Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/Compiler/Checking/CheckExpressions.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ val TcFieldInit: range -> ILFieldInit -> Const
val LightweightTcValForUsingInBuildMethodCall:
g: TcGlobals -> vref: ValRef -> vrefFlags: ValUseFlag -> vrefTypeInst: TTypes -> m: range -> Expr * TType


/// Indicates whether a syntactic type is allowed to include new type variables
/// not declared anywhere, e.g. `let f (x: 'T option) = x.Value`
type ImplicitlyBoundTyparsAllowed =
Expand Down
61 changes: 25 additions & 36 deletions src/Compiler/Interactive/ControlledExecution.fs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

// This wraps System.Runtime.CompilerServices.ControlledExecution
// This wraps System.Runtime.ControlledExecution
// This class enables scripting engines such as Fsi to abort threads safely in the coreclr
// This functionality will be introduced in .net 7.0.
// because we continue to dupport older coreclrs and the windows desktop framework through netstandard2.0
// because we continue to support older coreclrs and the windows desktop framework through netstandard2.0
// we access the features using reflection

namespace FSharp.Compiler.Interactive
Expand All @@ -14,54 +14,43 @@ open System.Threading

open Internal.Utilities.FSharpEnvironment

type internal ControlledExecution (thread:Thread) =
open Unchecked

type internal ControlledExecution () =

let mutable cts: CancellationTokenSource voption = ValueNone
let mutable thread: Thread voption = ValueNone

static let ceType: Type option =
Option.ofObj (Type.GetType("System.Runtime.CompilerServices.ControlledExecution, System.Private.CoreLib", false))
Option.ofObj (Type.GetType("System.Runtime.ControlledExecution, System.Private.CoreLib", false))

static let threadType: Type option =
Option.ofObj (typeof<Threading.Thread>)

static let ceConstructor: ConstructorInfo option =
match ceType with
| None -> None
| Some t -> Option.ofObj (t.GetConstructor([|typeof<Action>|]))

static let ceRun: MethodInfo option =
match ceType with
| None -> None
| Some t -> Option.ofObj (t.GetMethod("Run", [||]) )

static let ceTryAbort: MethodInfo option =
match ceType with
| None -> None
| Some t -> Option.ofObj (t.GetMethod("TryAbort", [|typeof<TimeSpan>|]))
| Some t -> Option.ofObj (t.GetMethod("Run", BindingFlags.Static ||| BindingFlags.Public, defaultof<Binder>, [|typeof<System.Action>; typeof<System.Threading.CancellationToken>|], [||] ))

static let threadResetAbort: MethodInfo option =
match isRunningOnCoreClr, threadType with
| false, Some t -> Option.ofObj (t.GetMethod("ResetAbort", [||]))
| _ -> None

let newInstance (action: Action) =
match ceConstructor with
| None -> None
| Some c -> Option.ofObj (c.Invoke([|action|]))

let mutable instance = Unchecked.defaultof<obj option>

member this.Run(action: Action) =
let newinstance = newInstance(action)
match newinstance, ceRun with
| Some inst, Some ceRun ->
instance <- newinstance
ceRun.Invoke(inst, [||]) |> ignore
| _ -> action.Invoke()

member _.TryAbort(timeout: TimeSpan): bool =
match isRunningOnCoreClr, instance, ceTryAbort with
| _, Some instance, Some tryAbort -> tryAbort.Invoke(instance, [|timeout|]) :?> bool
| false, _, _ -> thread.Abort(); true
| true, _, _ -> true
member _.Run (action: Action) =
match ceRun with
| Some run ->
cts <- ValueSome (new CancellationTokenSource())
run.Invoke(null, [|action; cts.Value.Token|]) |> ignore
| _ ->
thread <- ValueSome (Thread.CurrentThread)
action.Invoke()

member _.TryAbort(): unit =
match isRunningOnCoreClr, cts, thread with
| true, ValueSome cts, _ -> cts.Cancel()
| false, _, ValueSome thread -> thread.Abort(); ()
| _ -> ()

member _.ResetAbort() =
match thread, threadResetAbort with
Expand All @@ -72,4 +61,4 @@ type internal ControlledExecution (thread:Thread) =
match exn with
| :? TargetInvocationException as e when not(isNull e.InnerException) ->
ControlledExecution.StripTargetInvocationException(e.InnerException)
| _ -> exn
| _ -> exn
10 changes: 4 additions & 6 deletions src/Compiler/Interactive/fsi.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2262,10 +2262,7 @@ type internal FsiInterruptController(
if killThreadRequest = ThreadAbortRequest then
if progress then fsiConsoleOutput.uprintnfn "%s" (FSIstrings.SR.fsiAbortingMainThread())
killThreadRequest <- NoRequest
let rec abortLoop n =
if n > 0 then
if not (controlledExecution.TryAbort(TimeSpan.FromSeconds(30))) then abortLoop (n-1)
abortLoop 3
controlledExecution.TryAbort()
()), Name="ControlCAbortThread")
killerThread.IsBackground <- true
killerThread.Start()
Expand Down Expand Up @@ -2888,7 +2885,8 @@ type FsiInteractionProcessor
fsiInterruptController.ControlledExecution().ResetAbort()
(istate,CtrlC)

| :? TargetInvocationException as e when (ControlledExecution.StripTargetInvocationException(e)).GetType().Name = "ThreadAbortException" ->
| :? TargetInvocationException as e when (ControlledExecution.StripTargetInvocationException(e)).GetType().Name = "ThreadAbortException" ||
(ControlledExecution.StripTargetInvocationException(e)).GetType().Name = "OperationCanceledException" ->
fsiInterruptController.ClearInterruptRequest()
fsiInterruptController.InterruptAllowed <- InterruptIgnored
fsiInterruptController.ControlledExecution().ResetAbort()
Expand Down Expand Up @@ -3386,7 +3384,7 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i

let fsiDynamicCompiler = FsiDynamicCompiler(fsi, timeReporter, tcConfigB, tcLockObject, outWriter, tcImports, tcGlobals, fsiOptions, fsiConsoleOutput, fsiCollectible, niceNameGen, resolveAssemblyRef)

let controlledExecution = ControlledExecution(Thread.CurrentThread)
let controlledExecution = ControlledExecution()

let fsiInterruptController = FsiInterruptController(fsiOptions, controlledExecution, fsiConsoleOutput)

Expand Down