Skip to content

Commit 5b12870

Browse files
KevinRansombaronfel
authored andcommitted
Fix ctrl - c handling on the coreclr (#7495)
* fix ctrlchandler * remove nativemethods
1 parent c8b5712 commit 5b12870

1 file changed

Lines changed: 45 additions & 138 deletions

File tree

src/fsharp/fsi/fsi.fs

Lines changed: 45 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -787,14 +787,12 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: s
787787
member __.Gui = gui
788788

789789
/// Set the current ui culture for the current thread.
790-
#if FX_LCIDFROMCODEPAGE
791790
let internal SetCurrentUICultureForThread (lcid : int option) =
792791
let culture = Thread.CurrentThread.CurrentUICulture
793792
match lcid with
794793
| Some n -> Thread.CurrentThread.CurrentUICulture <- new CultureInfo(n)
795794
| None -> ()
796795
{ new IDisposable with member x.Dispose() = Thread.CurrentThread.CurrentUICulture <- culture }
797-
#endif
798796

799797
//----------------------------------------------------------------------------
800798
// Reporting - warnings, errors
@@ -1336,22 +1334,18 @@ type internal FsiDynamicCompiler
13361334
member __.FormatValue(obj:obj, objTy) =
13371335
valuePrinter.FormatValue(obj, objTy)
13381336

1339-
13401337
//----------------------------------------------------------------------------
13411338
// ctrl-c handling
13421339
//----------------------------------------------------------------------------
13431340

1344-
module internal NativeMethods =
1345-
1346-
type ControlEventHandler = delegate of int -> bool
1341+
type ControlEventHandler = delegate of int -> bool
13471342

1348-
[<DllImport("kernel32.dll")>]
1349-
extern bool SetConsoleCtrlHandler(ControlEventHandler _callback,bool _add)
13501343

13511344
// One strange case: when a TAE happens a strange thing
13521345
// occurs the next read from stdin always returns
13531346
// 0 bytes, i.e. the channel will look as if it has been closed. So we check
13541347
// for this condition explicitly. We also recreate the lexbuf whenever CtrlC kicks.
1348+
13551349
type internal FsiInterruptStdinState =
13561350
| StdinEOFPermittedBecauseCtrlCRecentlyPressed
13571351
| StdinNormal
@@ -1366,149 +1360,74 @@ type internal FsiInterruptControllerKillerThreadRequest =
13661360
| ExitRequest
13671361
| PrintInterruptRequest
13681362

1369-
type internal FsiInterruptController(fsiOptions : FsiCommandLineOptions,
1370-
fsiConsoleOutput: FsiConsoleOutput) =
1363+
type internal FsiInterruptController(fsiOptions: FsiCommandLineOptions, fsiConsoleOutput: FsiConsoleOutput) =
13711364

13721365
let mutable stdinInterruptState = StdinNormal
13731366
let CTRL_C = 0
13741367
let mutable interruptAllowed = InterruptIgnored
13751368
let mutable killThreadRequest = NoRequest
1376-
let mutable ctrlEventHandlers = [] : NativeMethods.ControlEventHandler list
1377-
let mutable ctrlEventActions = [] : (unit -> unit) list
1369+
1370+
let mutable ctrlEventHandlers = []: ControlEventHandler list
1371+
let mutable ctrlEventActions = []: (unit -> unit) list
13781372
let mutable exitViaKillThread = false
13791373

13801374
let mutable posixReinstate = (fun () -> ())
13811375

1382-
member __.Exit() =
1383-
if exitViaKillThread then
1376+
member __.Exit() =
1377+
if exitViaKillThread then
13841378
killThreadRequest <- ExitRequest
13851379
Thread.Sleep(1000)
13861380
exit 0
13871381

1388-
member __.FsiInterruptStdinState with get () = stdinInterruptState and set v = stdinInterruptState <- v
1382+
member __.FsiInterruptStdinState
1383+
with get () = stdinInterruptState
1384+
and set v = stdinInterruptState <- v
13891385

13901386
member __.ClearInterruptRequest() = killThreadRequest <- NoRequest
1391-
1392-
member __.InterruptAllowed with set v = interruptAllowed <- v
1393-
1387+
1388+
member __.InterruptAllowed
1389+
with set v = interruptAllowed <- v
1390+
13941391
member __.Interrupt() = ctrlEventActions |> List.iter (fun act -> act())
1395-
1392+
13961393
member __.EventHandlers = ctrlEventHandlers
13971394

1398-
// REVIEW: streamline all this code to use the same code on Windows and Posix.
1399-
member controller.InstallKillThread(threadToKill:Thread, pauseMilliseconds:int) =
1400-
#if DYNAMIC_CODE_EMITS_INTERRUPT_CHECKS
1401-
let action() =
1402-
Microsoft.FSharp.Silverlight.InterruptThread(threadToKill.ManagedThreadId)
1395+
member controller.InstallKillThread(threadToKill:Thread, pauseMilliseconds:int) =
14031396

1404-
ctrlEventActions <- action :: ctrlEventActions;
1405-
#else
1406-
#if FX_NO_THREADABORT
1407-
ignore threadToKill
1408-
ignore pauseMilliseconds
1409-
ignore fsiConsoleOutput
1410-
ignore CTRL_C
1411-
ignore fsiOptions
1412-
exitViaKillThread <- false
1413-
#else
1414-
if !progress then fprintfn fsiConsoleOutput.Out "installing CtrlC handler"
1415-
// WINDOWS TECHNIQUE: .NET has more safe points, and you can do more when a safe point.
1416-
// Hence we actually start up the killer thread within the handler.
1417-
try
1418-
let raiseCtrlC() =
1419-
#if FX_LCIDFROMCODEPAGE
1420-
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
1421-
#else
1422-
ignore fsiOptions
1423-
#endif
1424-
fprintf fsiConsoleOutput.Error "%s" (FSIstrings.SR.fsiInterrupt())
1425-
stdinInterruptState <- StdinEOFPermittedBecauseCtrlCRecentlyPressed
1426-
if (interruptAllowed = InterruptCanRaiseException) then
1427-
killThreadRequest <- ThreadAbortRequest
1428-
let killerThread =
1429-
new Thread(new ThreadStart(fun () ->
1430-
#if FX_LCIDFROMCODEPAGE
1431-
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
1432-
#endif
1433-
// sleep long enough to allow ControlEventHandler handler on main thread to return
1434-
// Also sleep to give computations a bit of time to terminate
1435-
Thread.Sleep(pauseMilliseconds)
1436-
if (killThreadRequest = ThreadAbortRequest) then
1437-
if !progress then fsiConsoleOutput.uprintnfn "%s" (FSIstrings.SR.fsiAbortingMainThread())
1438-
killThreadRequest <- NoRequest
1439-
threadToKill.Abort()
1440-
()),Name="ControlCAbortThread")
1441-
killerThread.IsBackground <- true
1442-
killerThread.Start()
1443-
1444-
let ctrlEventHandler = new NativeMethods.ControlEventHandler(fun i -> if i = CTRL_C then (raiseCtrlC(); true) else false )
1445-
ctrlEventHandlers <- ctrlEventHandler :: ctrlEventHandlers
1446-
ctrlEventActions <- raiseCtrlC :: ctrlEventActions
1447-
let _resultOK = NativeMethods.SetConsoleCtrlHandler(ctrlEventHandler,true)
1448-
exitViaKillThread <- false // don't exit via kill thread
1449-
with e ->
1450-
if !progress then fprintfn fsiConsoleOutput.Error "Failed to install ctrl-c handler using Windows technique - trying to install one using Unix signal handling...";
1451-
// UNIX TECHNIQUE: We start up a killer thread, and it watches the mutable reference location.
1452-
// We can't have a dependency on Mono DLLs (indeed we don't even have them!)
1453-
// So SOFT BIND the following code:
1454-
// Mono.Unix.Native.Stdlib.signal(Mono.Unix.Native.Signum.SIGINT,new Mono.Unix.Native.SignalHandler(fun n -> PosixSignalProcessor.PosixInvoke(n))) |> ignore;
1455-
match (try Choice1Of2(Assembly.Load(new System.Reflection.AssemblyName("Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756"))) with e -> Choice2Of2 e) with
1456-
| Choice1Of2(monoPosix) ->
1457-
try
1458-
if !progress then fprintfn fsiConsoleOutput.Error "loading type Mono.Unix.Native.Stdlib..."
1459-
let monoUnixStdlib = monoPosix.GetType("Mono.Unix.Native.Stdlib")
1460-
if !progress then fprintfn fsiConsoleOutput.Error "loading type Mono.Unix.Native.SignalHandler..."
1461-
let monoUnixSignalHandler = monoPosix.GetType("Mono.Unix.Native.SignalHandler")
1462-
if !progress then fprintfn fsiConsoleOutput.Error "creating delegate..."
1463-
controller.PosixInvoke(-1)
1464-
let monoHandler = System.Delegate.CreateDelegate(monoUnixSignalHandler,controller,"PosixInvoke")
1465-
if !progress then fprintfn fsiConsoleOutput.Error "registering signal handler..."
1466-
let monoSignalNumber = System.Enum.Parse(monoPosix.GetType("Mono.Unix.Native.Signum"),"SIGINT")
1467-
let register () = Utilities.callStaticMethod monoUnixStdlib "signal" [ monoSignalNumber; box monoHandler ] |> ignore
1468-
posixReinstate <- register
1469-
register()
1397+
// Fsi Interupt handler
1398+
let raiseCtrlC() =
1399+
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
1400+
fprintf fsiConsoleOutput.Error "%s" (FSIstrings.SR.fsiInterrupt())
1401+
1402+
stdinInterruptState <- StdinEOFPermittedBecauseCtrlCRecentlyPressed
1403+
if (interruptAllowed = InterruptCanRaiseException) then
1404+
killThreadRequest <- ThreadAbortRequest
14701405
let killerThread =
14711406
new Thread(new ThreadStart(fun () ->
1472-
#if FX_LCIDFROMCODEPAGE
14731407
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
1474-
#endif
1475-
while true do
1476-
//fprintf fsiConsoleOutput.Error "\n- kill thread loop...\n"; errorWriter.Flush();
1477-
Thread.Sleep(pauseMilliseconds*2)
1478-
match killThreadRequest with
1479-
| PrintInterruptRequest ->
1480-
fprintf fsiConsoleOutput.Error "%s" (FSIstrings.SR.fsiInterrupt()); fsiConsoleOutput.Error.Flush()
1481-
killThreadRequest <- NoRequest
1482-
| ThreadAbortRequest ->
1483-
fprintf fsiConsoleOutput.Error "%s" (FSIstrings.SR.fsiInterrupt()); fsiConsoleOutput.Error.Flush()
1484-
if !progress then fsiConsoleOutput.uprintnfn "%s" (FSIstrings.SR.fsiAbortingMainThread())
1485-
killThreadRequest <- NoRequest
1486-
threadToKill.Abort()
1487-
| ExitRequest ->
1488-
// Mono has some weird behaviour where it blocks on exit
1489-
// once CtrlC has ever been pressed. Who knows why? Perhaps something
1490-
// to do with having a signal handler installed, but it only happens _after_
1491-
// at least one CtrLC has been pressed. Maybe raising a ThreadAbort causes
1492-
// exiting to have problems.
1493-
//
1494-
// Anyway, we make "#q" work this case by setting ExitRequest and brutally calling
1495-
// the process-wide 'exit'
1496-
fprintf fsiConsoleOutput.Error "%s" (FSIstrings.SR.fsiExit()); fsiConsoleOutput.Error.Flush()
1497-
Utilities.callStaticMethod monoUnixStdlib "exit" [ box 0 ] |> ignore
1498-
| _ -> ()
1499-
done),Name="ControlCAbortAlternativeThread")
1408+
// sleep long enough to allow ControlEventHandler handler on main thread to return
1409+
// Also sleep to give computations a bit of time to terminate
1410+
Thread.Sleep(pauseMilliseconds)
1411+
if (killThreadRequest = ThreadAbortRequest) then
1412+
if !progress then fsiConsoleOutput.uprintnfn "%s" (FSIstrings.SR.fsiAbortingMainThread())
1413+
killThreadRequest <- NoRequest
1414+
threadToKill.Abort()
1415+
()),Name="ControlCAbortThread")
15001416
killerThread.IsBackground <- true
15011417
killerThread.Start()
1502-
// exit via kill thread to workaround block-on-exit bugs with Mono once a CtrlC has been pressed
1503-
exitViaKillThread <- true
1504-
with e ->
1505-
fprintf fsiConsoleOutput.Error "%s" (FSIstrings.SR.fsiCouldNotInstallCtrlCHandler(e.Message))
1506-
exitViaKillThread <- false
1507-
| Choice2Of2 e ->
1508-
fprintf fsiConsoleOutput.Error "%s" (FSIstrings.SR.fsiCouldNotInstallCtrlCHandler(e.Message))
1509-
exitViaKillThread <- false
1510-
#endif
15111418

1419+
let fsiInterruptHandler (args:ConsoleCancelEventArgs) =
1420+
args.Cancel <- true
1421+
ctrlEventHandlers |> List.iter(fun handler -> handler.Invoke(CTRL_C) |> ignore)
1422+
1423+
do Console.CancelKeyPress.Add(fsiInterruptHandler)
1424+
1425+
// WINDOWS TECHNIQUE: .NET has more safe points, and you can do more when a safe point.
1426+
// Hence we actually start up the killer thread within the handler.
1427+
let ctrlEventHandler = new ControlEventHandler(fun i -> if i = CTRL_C then (raiseCtrlC(); true) else false )
1428+
ctrlEventHandlers <- ctrlEventHandler :: ctrlEventHandlers
1429+
ctrlEventActions <- raiseCtrlC :: ctrlEventActions
1430+
exitViaKillThread <- false // don't exit via kill thread
15121431

15131432
member x.PosixInvoke(n:int) =
15141433
// we run this code once with n = -1 to make sure it is JITted before execution begins
@@ -1519,8 +1438,6 @@ type internal FsiInterruptController(fsiOptions : FsiCommandLineOptions,
15191438
stdinInterruptState <- StdinEOFPermittedBecauseCtrlCRecentlyPressed
15201439
killThreadRequest <- if (interruptAllowed = InterruptCanRaiseException) then ThreadAbortRequest else PrintInterruptRequest
15211440

1522-
#endif
1523-
15241441
//----------------------------------------------------------------------------
15251442
// assembly finder
15261443
//----------------------------------------------------------------------------
@@ -1809,9 +1726,7 @@ type internal FsiInteractionProcessor
18091726

18101727
// FSI error logging on switched to thread
18111728
InstallErrorLoggingOnThisThread errorLogger
1812-
#if FX_LCIDFROMCODEPAGE
18131729
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
1814-
#endif
18151730
f ctok istate)
18161731
with _ ->
18171732
(istate,Completed None)
@@ -2185,9 +2100,7 @@ type internal FsiInteractionProcessor
21852100
member __.EvalInteraction(ctok, sourceText, scriptFileName, errorLogger) =
21862101
use _unwind1 = ErrorLogger.PushThreadBuildPhaseUntilUnwind(ErrorLogger.BuildPhase.Interactive)
21872102
use _unwind2 = ErrorLogger.PushErrorLoggerPhaseUntilUnwind(fun _ -> errorLogger)
2188-
#if FX_LCIDFROMCODEPAGE
21892103
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
2190-
#endif
21912104
let lexbuf = UnicodeLexing.StringAsLexbuf(sourceText)
21922105
let tokenizer = fsiStdinLexerProvider.CreateBufferLexer(scriptFileName, lexbuf, errorLogger)
21932106
currState
@@ -2204,9 +2117,7 @@ type internal FsiInteractionProcessor
22042117
member __.EvalExpression (ctok, sourceText, scriptFileName, errorLogger) =
22052118
use _unwind1 = ErrorLogger.PushThreadBuildPhaseUntilUnwind(ErrorLogger.BuildPhase.Interactive)
22062119
use _unwind2 = ErrorLogger.PushErrorLoggerPhaseUntilUnwind(fun _ -> errorLogger)
2207-
#if FX_LCIDFROMCODEPAGE
22082120
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
2209-
#endif
22102121
let lexbuf = UnicodeLexing.StringAsLexbuf(sourceText)
22112122
let tokenizer = fsiStdinLexerProvider.CreateBufferLexer(scriptFileName, lexbuf, errorLogger)
22122123
currState
@@ -2235,9 +2146,7 @@ type internal FsiInteractionProcessor
22352146
let stdinReaderThread =
22362147
new Thread(new ThreadStart(fun () ->
22372148
InstallErrorLoggingOnThisThread errorLogger // FSI error logging on stdinReaderThread, e.g. parse errors.
2238-
#if FX_LCIDFROMCODEPAGE
22392149
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
2240-
#endif
22412150
try
22422151
try
22432152
let initialTokenizer = fsiStdinLexerProvider.CreateStdinLexer(errorLogger)
@@ -2341,9 +2250,7 @@ let internal SpawnInteractiveServer
23412250
fsiConsoleOutput: FsiConsoleOutput) =
23422251
//printf "Spawning fsi server on channel '%s'" !fsiServerName;
23432252
SpawnThread "ServerThread" (fun () ->
2344-
#if FX_LCIDFROMCODEPAGE
23452253
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
2346-
#endif
23472254
try
23482255
fsi.StartServer(fsiOptions.FsiServerName)
23492256
with e ->

0 commit comments

Comments
 (0)