@@ -47,6 +47,7 @@ open FSharp.Compiler.EditorServices
4747open FSharp.Compiler .DiagnosticsLogger
4848open FSharp.Compiler .Features
4949open FSharp.Compiler .IlxGen
50+ open FSharp.Compiler .Interactive
5051open FSharp.Compiler .InfoReader
5152open FSharp.Compiler .IO
5253open FSharp.Compiler .Lexhelp
@@ -2208,7 +2209,10 @@ type internal FsiInterruptControllerKillerThreadRequest =
22082209 | ExitRequest
22092210 | PrintInterruptRequest
22102211
2211- type internal FsiInterruptController ( fsiOptions : FsiCommandLineOptions , fsiConsoleOutput : FsiConsoleOutput ) =
2212+ type internal FsiInterruptController (
2213+ fsiOptions: FsiCommandLineOptions,
2214+ controlledExecution: ControlledExecution,
2215+ fsiConsoleOutput: FsiConsoleOutput) =
22122216
22132217 let mutable stdinInterruptState = StdinNormal
22142218 let CTRL_C = 0
@@ -2240,7 +2244,12 @@ type internal FsiInterruptController(fsiOptions: FsiCommandLineOptions, fsiConso
22402244
22412245 member _.EventHandlers = ctrlEventHandlers
22422246
2243- member controller.InstallKillThread ( threadToKill : Thread , pauseMilliseconds : int ) =
2247+ member _.ControlledExecution () = controlledExecution
2248+
2249+ member controller.InstallKillThread () =
2250+ // Compute how long to pause before a ThreadAbort is actually executed.
2251+ // A somewhat arbitrary choice.
2252+ let pauseMilliseconds = ( if fsiOptions.Gui then 400 else 100 )
22442253
22452254 // Fsi Interrupt handler
22462255 let raiseCtrlC () =
@@ -2259,8 +2268,11 @@ type internal FsiInterruptController(fsiOptions: FsiCommandLineOptions, fsiConso
22592268 if killThreadRequest = ThreadAbortRequest then
22602269 if progress then fsiConsoleOutput.uprintnfn " %s " ( FSIstrings.SR.fsiAbortingMainThread())
22612270 killThreadRequest <- NoRequest
2262- threadToKill.Abort()
2263- ()), Name= " ControlCAbortThread" )
2271+ let rec abortLoop n =
2272+ if n > 0 then
2273+ if not ( controlledExecution.TryAbort( TimeSpan.FromSeconds( 30 ))) then abortLoop ( n-1 )
2274+ abortLoop 3
2275+ ()), Name= " ControlCAbortThread" )
22642276 killerThread.IsBackground <- true
22652277 killerThread.Start()
22662278
@@ -2853,24 +2865,35 @@ type FsiInteractionProcessor
28532865 /// Execute a single parsed interaction on the parser/execute thread.
28542866 let mainThreadProcessAction ctok action istate =
28552867 try
2856- let tcConfig = TcConfig.Create( tcConfigB, validate= false )
2857- if progress then fprintfn fsiConsoleOutput.Out " In mainThreadProcessAction..." ;
2858- fsiInterruptController.InterruptAllowed <- InterruptCanRaiseException;
2859- let res = action ctok tcConfig istate
2860- fsiInterruptController.ClearInterruptRequest()
2861- fsiInterruptController.InterruptAllowed <- InterruptIgnored;
2862- res
2868+ let mutable result = Unchecked.defaultof< 'a * FsiInteractionStepStatus>
2869+ fsiInterruptController.ControlledExecution() .Run(
2870+ fun () ->
2871+ let tcConfig = TcConfig.Create( tcConfigB, validate= false )
2872+ if progress then fprintfn fsiConsoleOutput.Out " In mainThreadProcessAction..."
2873+ fsiInterruptController.InterruptAllowed <- InterruptCanRaiseException;
2874+ let res = action ctok tcConfig istate
2875+ fsiInterruptController.ClearInterruptRequest()
2876+ fsiInterruptController.InterruptAllowed <- InterruptIgnored
2877+ result <- res)
2878+ result
28632879 with
28642880 | :? ThreadAbortException ->
2865- fsiInterruptController.ClearInterruptRequest()
2866- fsiInterruptController.InterruptAllowed <- InterruptIgnored;
2867- ( try Thread.ResetAbort() with _ -> ());
2868- ( istate, CtrlC)
2881+ fsiInterruptController.ClearInterruptRequest()
2882+ fsiInterruptController.InterruptAllowed <- InterruptIgnored
2883+ fsiInterruptController.ControlledExecution() .ResetAbort()
2884+ ( istate, CtrlC)
2885+
2886+ | :? TargetInvocationException as e when ( ControlledExecution.StripTargetInvocationException( e)) .GetType() .Name = " ThreadAbortException" ->
2887+ fsiInterruptController.ClearInterruptRequest()
2888+ fsiInterruptController.InterruptAllowed <- InterruptIgnored
2889+ fsiInterruptController.ControlledExecution() .ResetAbort()
2890+ ( istate, CtrlC)
2891+
28692892 | e ->
2870- fsiInterruptController.ClearInterruptRequest()
2871- fsiInterruptController.InterruptAllowed <- InterruptIgnored;
2872- stopProcessingRecovery e range0;
2873- istate, CompletedWithReportedError e
2893+ fsiInterruptController.ClearInterruptRequest()
2894+ fsiInterruptController.InterruptAllowed <- InterruptIgnored;
2895+ stopProcessingRecovery e range0;
2896+ istate, CompletedWithReportedError e
28742897
28752898 let mainThreadProcessParsedInteractions ctok diagnosticsLogger ( action , istate ) cancellationToken =
28762899 istate |> mainThreadProcessAction ctok ( fun ctok tcConfig istate ->
@@ -3180,27 +3203,32 @@ let internal SpawnInteractiveServer
31803203/// Repeatedly drive the event loop (e.g. Application.Run()) but catching ThreadAbortException and re-running.
31813204///
31823205/// This gives us a last chance to catch an abort on the main execution thread.
3183- let internal DriveFsiEventLoop ( fsi : FsiEvaluationSessionHostConfig , fsiConsoleOutput : FsiConsoleOutput ) =
3206+ let internal DriveFsiEventLoop ( fsi : FsiEvaluationSessionHostConfig , fsiInterruptController : FsiInterruptController , fsiConsoleOutput : FsiConsoleOutput ) =
3207+
3208+ if progress then fprintfn fsiConsoleOutput.Out " GUI thread runLoop"
3209+ fsiInterruptController.InstallKillThread()
3210+
31843211 let rec runLoop () =
3185- if progress then fprintfn fsiConsoleOutput.Out " GUI thread runLoop " ;
3212+
31863213 let restart =
31873214 try
3188- // BLOCKING POINT: The GUI Thread spends most (all) of its time this event loop
3189- if progress then fprintfn fsiConsoleOutput.Out " MAIN: entering event loop..." ;
3190- fsi.EventLoopRun()
3215+ fsi.EventLoopRun()
31913216 with
3192- | :? ThreadAbortException ->
3217+ | :? TargetInvocationException as e when ( ControlledExecution.StripTargetInvocationException ( e )) .GetType () .Name = " ThreadAbortException" ->
31933218 // If this TAE handler kicks it's almost certainly too late to save the
31943219 // state of the process - the state of the message loop may have been corrupted
3195- fsiConsoleOutput.uprintnfn " %s " ( FSIstrings.SR.fsiUnexpectedThreadAbortException());
3196- ( try Thread.ResetAbort() with _ -> ());
3220+ fsiInterruptController.ControlledExecution() .ResetAbort()
31973221 true
3198- // Try again, just case we can restart
3199- | e ->
3200- stopProcessingRecovery e range0;
3222+ | :? ThreadAbortException ->
3223+ // If this TAE handler kicks it's almost certainly too late to save the
3224+ // state of the process - the state of the message loop may have been corrupted
3225+ fsiInterruptController.ControlledExecution() .ResetAbort()
32013226 true
3202- // Try again, just case we can restart
3203- if progress then fprintfn fsiConsoleOutput.Out " MAIN: exited event loop..." ;
3227+ | e ->
3228+ stopProcessingRecovery e range0
3229+ true
3230+ // Try again, just case we can restart
3231+ if progress then fprintfn fsiConsoleOutput.Out " MAIN: exited event loop..."
32043232 if restart then runLoop()
32053233
32063234 runLoop();
@@ -3380,7 +3408,9 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i
33803408
33813409 let fsiDynamicCompiler = FsiDynamicCompiler( fsi, timeReporter, tcConfigB, tcLockObject, outWriter, tcImports, tcGlobals, fsiOptions, fsiConsoleOutput, fsiCollectible, niceNameGen, resolveAssemblyRef)
33823410
3383- let fsiInterruptController = FsiInterruptController( fsiOptions, fsiConsoleOutput)
3411+ let controlledExecution = ControlledExecution( Thread.CurrentThread)
3412+
3413+ let fsiInterruptController = FsiInterruptController( fsiOptions, controlledExecution, fsiConsoleOutput)
33843414
33853415 let uninstallMagicAssemblyResolution = MagicAssemblyResolution.Install( tcConfigB, tcImports, fsiDynamicCompiler, fsiConsoleOutput)
33863416
@@ -3640,14 +3670,6 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i
36403670 if fsiOptions.Interact then
36413671 // page in the type check env
36423672 fsiInteractionProcessor.LoadDummyInteraction( ctokStartup, diagnosticsLogger)
3643- if progress then fprintfn fsiConsoleOutput.Out " MAIN: InstallKillThread!" ;
3644-
3645- // Compute how long to pause before a ThreadAbort is actually executed.
3646- // A somewhat arbitrary choice.
3647- let pauseMilliseconds = ( if fsiOptions.Gui then 400 else 100 )
3648-
3649- // Request that ThreadAbort interrupts be performed on this (current) thread
3650- fsiInterruptController.InstallKillThread( Thread.CurrentThread, pauseMilliseconds)
36513673 if progress then fprintfn fsiConsoleOutput.Out " MAIN: got initial state, creating form" ;
36523674
36533675#if ! FX_ NO_ APP_ DOMAINS
@@ -3657,12 +3679,10 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i
36573679 | :? System.Exception as err -> x.ReportUnhandledExceptionSafe false err
36583680 | _ -> ())
36593681#endif
3660-
36613682 fsiInteractionProcessor.LoadInitialFiles( ctokRun, diagnosticsLogger)
3662-
36633683 fsiInteractionProcessor.StartStdinReadAndProcessThread( diagnosticsLogger)
36643684
3665- DriveFsiEventLoop ( fsi, fsiConsoleOutput )
3685+ DriveFsiEventLoop ( fsi, fsiInterruptController , fsiConsoleOutput )
36663686
36673687 else // not interact
36683688 if progress then fprintfn fsiConsoleOutput.Out " Run: not interact, loading initial files..."
0 commit comments