Skip to content

Commit b0a8397

Browse files
brettfobaronfel
authored andcommitted
allow fsi evaluations to be cancelled (#7736)
1 parent c20ef80 commit b0a8397

File tree

2 files changed

+20
-16
lines changed

2 files changed

+20
-16
lines changed

src/fsharp/fsi/fsi.fs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,7 +1888,8 @@ type internal FsiInteractionProcessor
18881888
///
18891889
/// #directive comes through with other definitions as a SynModuleDecl.HashDirective.
18901890
/// We split these out for individual processing.
1891-
let rec execParsedInteractions (ctok, tcConfig, istate, action, errorLogger: ErrorLogger, lastResult:option<FsiInteractionStepStatus>) =
1891+
let rec execParsedInteractions (ctok, tcConfig, istate, action, errorLogger: ErrorLogger, lastResult:option<FsiInteractionStepStatus>, cancellationToken: CancellationToken) =
1892+
cancellationToken.ThrowIfCancellationRequested()
18921893
let action,nextAction,istate =
18931894
match action with
18941895
| None -> None,None,istate
@@ -1935,7 +1936,7 @@ type internal FsiInteractionProcessor
19351936
| Some action, _ ->
19361937
let istate,cont = ExecInteraction (ctok, tcConfig, istate, action, errorLogger)
19371938
match cont with
1938-
| Completed _ -> execParsedInteractions (ctok, tcConfig, istate, nextAction, errorLogger, Some cont)
1939+
| Completed _ -> execParsedInteractions (ctok, tcConfig, istate, nextAction, errorLogger, Some cont, cancellationToken)
19391940
| CompletedWithReportedError e -> istate,CompletedWithReportedError e (* drop nextAction on error *)
19401941
| EndOfFile -> istate,defaultArg lastResult (Completed None) (* drop nextAction on EOF *)
19411942
| CtrlC -> istate,CtrlC (* drop nextAction on CtrlC *)
@@ -1962,9 +1963,9 @@ type internal FsiInteractionProcessor
19621963
stopProcessingRecovery e range0;
19631964
istate, CompletedWithReportedError e
19641965

1965-
let mainThreadProcessParsedInteractions ctok errorLogger (action, istate) =
1966+
let mainThreadProcessParsedInteractions ctok errorLogger (action, istate) cancellationToken =
19661967
istate |> mainThreadProcessAction ctok (fun ctok tcConfig istate ->
1967-
execParsedInteractions (ctok, tcConfig, istate, action, errorLogger, None))
1968+
execParsedInteractions (ctok, tcConfig, istate, action, errorLogger, None, cancellationToken))
19681969

19691970
let parseExpression (tokenizer:LexFilter.LexFilter) =
19701971
reusingLexbufForParsing tokenizer.LexBuffer (fun () ->
@@ -1997,8 +1998,8 @@ type internal FsiInteractionProcessor
19971998
/// During processing of startup scripts, this runs on the main thread.
19981999
///
19992000
/// This is blocking: it reads until one chunk of input have been received, unless IsPastEndOfStream is true
2000-
member __.ParseAndExecOneSetOfInteractionsFromLexbuf (runCodeOnMainThread, istate:FsiDynamicCompilerState, tokenizer:LexFilter.LexFilter, errorLogger) =
2001-
2001+
member __.ParseAndExecOneSetOfInteractionsFromLexbuf (runCodeOnMainThread, istate:FsiDynamicCompilerState, tokenizer:LexFilter.LexFilter, errorLogger, ?cancellationToken: CancellationToken) =
2002+
let cancellationToken = defaultArg cancellationToken CancellationToken.None
20022003
if tokenizer.LexBuffer.IsPastEndOfStream then
20032004
let stepStatus =
20042005
if fsiInterruptController.FsiInterruptStdinState = StdinEOFPermittedBecauseCtrlCRecentlyPressed then
@@ -2022,7 +2023,7 @@ type internal FsiInteractionProcessor
20222023

20232024
// After we've unblocked and got something to run we switch
20242025
// over to the run-thread (e.g. the GUI thread)
2025-
let res = istate |> runCodeOnMainThread (fun ctok istate -> mainThreadProcessParsedInteractions ctok errorLogger (action, istate))
2026+
let res = istate |> runCodeOnMainThread (fun ctok istate -> mainThreadProcessParsedInteractions ctok errorLogger (action, istate) cancellationToken)
20262027

20272028
if !progress then fprintfn fsiConsoleOutput.Out "Just called runCodeOnMainThread, res = %O..." res;
20282029
res)
@@ -2093,7 +2094,8 @@ type internal FsiInteractionProcessor
20932094
member __.LoadDummyInteraction(ctok, errorLogger) =
20942095
setCurrState (currState |> InteractiveCatch errorLogger (fun istate -> fsiDynamicCompiler.EvalParsedDefinitions (ctok, errorLogger, istate, true, false, []) |> fst, Completed None) |> fst)
20952096

2096-
member __.EvalInteraction(ctok, sourceText, scriptFileName, errorLogger) =
2097+
member __.EvalInteraction(ctok, sourceText, scriptFileName, errorLogger, ?cancellationToken) =
2098+
let cancellationToken = defaultArg cancellationToken CancellationToken.None
20972099
use _unwind1 = ErrorLogger.PushThreadBuildPhaseUntilUnwind(ErrorLogger.BuildPhase.Interactive)
20982100
use _unwind2 = ErrorLogger.PushErrorLoggerPhaseUntilUnwind(fun _ -> errorLogger)
20992101
use _scope = SetCurrentUICultureForThread fsiOptions.FsiLCID
@@ -2102,7 +2104,7 @@ type internal FsiInteractionProcessor
21022104
currState
21032105
|> InteractiveCatch errorLogger (fun istate ->
21042106
let expr = ParseInteraction tokenizer
2105-
mainThreadProcessParsedInteractions ctok errorLogger (expr, istate) )
2107+
mainThreadProcessParsedInteractions ctok errorLogger (expr, istate) cancellationToken)
21062108
|> commitResult
21072109

21082110
member this.EvalScript (ctok, scriptPath, errorLogger) =
@@ -2592,25 +2594,26 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i
25922594
fsiInteractionProcessor.EvalExpression(ctok, sourceText, dummyScriptFileName, errorLogger)
25932595
|> commitResultNonThrowing errorOptions dummyScriptFileName errorLogger
25942596

2595-
member x.EvalInteraction(sourceText) : unit =
2597+
member x.EvalInteraction(sourceText, ?cancellationToken) : unit =
25962598
// Explanation: When the user of the FsiInteractiveSession object calls this method, the
25972599
// code is parsed, checked and evaluated on the calling thread. This means EvalExpression
25982600
// is not safe to call concurrently.
25992601
let ctok = AssumeCompilationThreadWithoutEvidence()
2600-
2601-
fsiInteractionProcessor.EvalInteraction(ctok, sourceText, dummyScriptFileName, errorLogger)
2602+
let cancellationToken = defaultArg cancellationToken CancellationToken.None
2603+
fsiInteractionProcessor.EvalInteraction(ctok, sourceText, dummyScriptFileName, errorLogger, cancellationToken)
26022604
|> commitResult
26032605
|> ignore
26042606

2605-
member x.EvalInteractionNonThrowing(sourceText) =
2607+
member x.EvalInteractionNonThrowing(sourceText, ?cancellationToken) =
26062608
// Explanation: When the user of the FsiInteractiveSession object calls this method, the
26072609
// code is parsed, checked and evaluated on the calling thread. This means EvalExpression
26082610
// is not safe to call concurrently.
26092611
let ctok = AssumeCompilationThreadWithoutEvidence()
2612+
let cancellationToken = defaultArg cancellationToken CancellationToken.None
26102613

26112614
let errorOptions = TcConfig.Create(tcConfigB,validate = false).errorSeverityOptions
26122615
let errorLogger = CompilationErrorLogger("EvalInteraction", errorOptions)
2613-
fsiInteractionProcessor.EvalInteraction(ctok, sourceText, dummyScriptFileName, errorLogger)
2616+
fsiInteractionProcessor.EvalInteraction(ctok, sourceText, dummyScriptFileName, errorLogger, cancellationToken)
26142617
|> commitResultNonThrowing errorOptions "input.fsx" errorLogger
26152618

26162619
member x.EvalScript(scriptPath) : unit =

src/fsharp/fsi/fsi.fsi

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module public FSharp.Compiler.Interactive.Shell
55

66
open System.IO
7+
open System.Threading
78
open FSharp.Compiler
89
open FSharp.Compiler.SourceCodeServices
910

@@ -146,7 +147,7 @@ type FsiEvaluationSession =
146147
///
147148
/// Due to a current limitation, it is not fully thread-safe to run this operation concurrently with evaluation triggered
148149
/// by input from 'stdin'.
149-
member EvalInteraction : code: string -> unit
150+
member EvalInteraction : code: string * ?cancellationToken: CancellationToken -> unit
150151

151152
/// Execute the code as if it had been entered as one or more interactions, with an
152153
/// implicit termination at the end of the input. Stop on first error, discarding the rest
@@ -155,7 +156,7 @@ type FsiEvaluationSession =
155156
///
156157
/// Due to a current limitation, it is not fully thread-safe to run this operation concurrently with evaluation triggered
157158
/// by input from 'stdin'.
158-
member EvalInteractionNonThrowing : code: string -> Choice<FsiValue option, exn> * FSharpErrorInfo[]
159+
member EvalInteractionNonThrowing : code: string * ?cancellationToken: CancellationToken -> Choice<FsiValue option, exn> * FSharpErrorInfo[]
159160

160161
/// Execute the given script. Stop on first error, discarding the rest
161162
/// of the script. Errors are sent to the output writer, a 'true' return value indicates there

0 commit comments

Comments
 (0)