diff --git a/src/Compiler/Utilities/Cancellable.fs b/src/Compiler/Utilities/Cancellable.fs index 9e40de9dd2d..20e220b7e9f 100644 --- a/src/Compiler/Utilities/Cancellable.fs +++ b/src/Compiler/Utilities/Cancellable.fs @@ -3,6 +3,16 @@ namespace FSharp.Compiler open System open System.Threading +// This code provides two methods for handling cancellation in synchronous code: +// 1. Explicitly, by calling Cancellable.CheckAndThrow(). +// 2. Implicitly, by wrapping the code in a cancellable computation. +// The cancellable computation propagates the CancellationToken and checks for cancellation implicitly. +// When it is impractical to use the cancellable computation, such as in deeply nested functions, Cancellable.CheckAndThrow() can be used. +// It checks a CancellationToken local to the current async execution context, held in AsyncLocal. +// Before calling Cancellable.CheckAndThrow(), this token must be set. +// The token is guaranteed to be set during execution of cancellable computation. +// Otherwise, it can be passed explicitly from the ambient async computation using Cancellable.UseToken(). + [] type Cancellable = static let tokenHolder = AsyncLocal() @@ -47,9 +57,7 @@ open System open System.Threading open FSharp.Compiler -#if !FSHARPCORE_USE_PACKAGE open FSharp.Core.CompilerServices.StateMachineHelpers -#endif [] type ValueOrCancelled<'TResult> = @@ -65,12 +73,7 @@ module Cancellable = if ct.IsCancellationRequested then ValueOrCancelled.Cancelled(OperationCanceledException ct) else - try - use _ = Cancellable.UsingToken(ct) - oper ct - with - | :? OperationCanceledException as e when ct.IsCancellationRequested -> ValueOrCancelled.Cancelled e - | :? OperationCanceledException as e -> InvalidOperationException("Wrong cancellation token", e) |> raise + oper ct let fold f acc seq = Cancellable(fun ct -> @@ -84,6 +87,7 @@ module Cancellable = acc) let runWithoutCancellation comp = + use _ = Cancellable.UsingToken CancellationToken.None let res = run CancellationToken.None comp match res with @@ -92,14 +96,19 @@ module Cancellable = let toAsync c = async { + use! _holder = Cancellable.UseToken() + let! ct = Async.CancellationToken - let res = run ct c return! - Async.FromContinuations(fun (cont, _econt, ccont) -> - match res with - | ValueOrCancelled.Value v -> cont v - | ValueOrCancelled.Cancelled ce -> ccont ce) + Async.FromContinuations(fun (cont, econt, ccont) -> + try + match run ct c with + | ValueOrCancelled.Value v -> cont v + | ValueOrCancelled.Cancelled ce -> ccont ce + with + | :? OperationCanceledException as ce when ct.IsCancellationRequested -> ccont ce + | :? OperationCanceledException as e -> InvalidOperationException("Wrong cancellation token", e) |> econt) } let token () = Cancellable(ValueOrCancelled.Value) @@ -113,9 +122,8 @@ type CancellableBuilder() = member inline _.Bind(comp, [] k) = Cancellable(fun ct -> -#if !FSHARPCORE_USE_PACKAGE + __debugPoint "" -#endif match Cancellable.run ct comp with | ValueOrCancelled.Value v1 -> Cancellable.run ct (k v1) @@ -123,9 +131,8 @@ type CancellableBuilder() = member inline _.BindReturn(comp, [] k) = Cancellable(fun ct -> -#if !FSHARPCORE_USE_PACKAGE + __debugPoint "" -#endif match Cancellable.run ct comp with | ValueOrCancelled.Value v1 -> ValueOrCancelled.Value(k v1) @@ -133,9 +140,8 @@ type CancellableBuilder() = member inline _.Combine(comp1, comp2) = Cancellable(fun ct -> -#if !FSHARPCORE_USE_PACKAGE + __debugPoint "" -#endif match Cancellable.run ct comp1 with | ValueOrCancelled.Value() -> Cancellable.run ct comp2 @@ -143,9 +149,8 @@ type CancellableBuilder() = member inline _.TryWith(comp, [] handler) = Cancellable(fun ct -> -#if !FSHARPCORE_USE_PACKAGE + __debugPoint "" -#endif let compRes = try @@ -164,9 +169,9 @@ type CancellableBuilder() = member inline _.Using(resource: _ MaybeNull, [] comp) = Cancellable(fun ct -> -#if !FSHARPCORE_USE_PACKAGE + __debugPoint "" -#endif + let body = comp resource let compRes = @@ -188,9 +193,8 @@ type CancellableBuilder() = member inline _.TryFinally(comp, [] compensation) = Cancellable(fun ct -> -#if !FSHARPCORE_USE_PACKAGE + __debugPoint "" -#endif let compRes = try diff --git a/src/Compiler/Utilities/Cancellable.fsi b/src/Compiler/Utilities/Cancellable.fsi index 72c098d2234..e74404c7eec 100644 --- a/src/Compiler/Utilities/Cancellable.fsi +++ b/src/Compiler/Utilities/Cancellable.fsi @@ -7,9 +7,6 @@ open System.Threading type Cancellable = static member internal UseToken: unit -> Async - /// For use in testing only. Cancellable.token should be set only by the cancellable computation. - static member internal UsingToken: CancellationToken -> IDisposable - static member HasCancellationToken: bool static member Token: CancellationToken