diff --git a/src/FSharp.Core/async.fs b/src/FSharp.Core/async.fs index e907040c94c..ce9b05445b8 100644 --- a/src/FSharp.Core/async.fs +++ b/src/FSharp.Core/async.fs @@ -1501,6 +1501,10 @@ type Async = computation.Invoke newCtxt) + static member RunImmediate(computation: Async<'T>, ?cancellationToken: CancellationToken) = + let cancellationToken = defaultArg cancellationToken Async.DefaultCancellationToken + AsyncPrimitives.RunImmediate cancellationToken computation + static member RunSynchronously(computation: Async<'T>, ?timeout, ?cancellationToken: CancellationToken) = let timeout, cancellationToken = match cancellationToken with diff --git a/src/FSharp.Core/async.fsi b/src/FSharp.Core/async.fsi index 3aa0403bb19..c1dc9bd953c 100644 --- a/src/FSharp.Core/async.fsi +++ b/src/FSharp.Core/async.fsi @@ -13,25 +13,25 @@ namespace Microsoft.FSharp.Control /// /// An asynchronous computation, which, when run, will eventually produce a value of type T, or else raises an exception. - /// + /// /// /// /// This type has no members. Asynchronous computations are normally specified either by using an async expression /// or the static methods in the type. /// /// See also F# Language Guide - Async Workflows. - /// + /// /// /// /// Library functionality for asynchronous programming, events and agents. See also - /// Asynchronous Programming, + /// Asynchronous Programming, /// Events and /// Lazy Expressions in the /// F# Language Guide. /// /// /// Async Programming - + [] type Async<'T> @@ -47,6 +47,42 @@ namespace Microsoft.FSharp.Control [] type Async = + /// Runs the asynchronous computation and await its result. + /// + /// If an exception occurs in the asynchronous computation then an exception is re-raised by this + /// function. + /// + /// If no cancellation token is provided then the default cancellation token is used. + /// + /// The computation always starts on current thread. + /// + /// + /// + /// The computation to run. + /// The cancellation token to be associated with the computation. + /// If one is not supplied, the default cancellation token is used. + /// + /// The result of the computation. + /// + /// Starting Async Computations + /// + /// + /// + /// printfn "A" + /// + /// let result = async { + /// printfn "B" + /// do! Async.Sleep(1000) + /// printfn "C" + /// 17 + /// } |> Async.RunImmediate + /// + /// printfn "D" + /// + /// Prints "A", "B" immediately, then "C", "D" in 1 second. result is set to 17. + /// + static member RunImmediate : computation: Async<'T> * ?cancellationToken: CancellationToken -> 'T + /// Runs the asynchronous computation and await its result. /// /// If an exception occurs in the asynchronous computation then an exception is re-raised by this @@ -90,7 +126,7 @@ namespace Microsoft.FSharp.Control /// Prints "A", "B" immediately, then "C", "D" in 1 second. result is set to 17. /// static member RunSynchronously : computation:Async<'T> * ?timeout : int * ?cancellationToken:CancellationToken-> 'T - + /// Starts the asynchronous computation in the thread pool. Do not await its result. /// /// If no cancellation token is provided then the default cancellation token is used. @@ -170,7 +206,7 @@ namespace Microsoft.FSharp.Control /// match DateTime.Today with /// | dt when dt.DayOfWeek = DayOfWeek.Monday -> failwith "Not compatible with Mondays" /// | dt -> dt - /// + /// /// async { return someRiskyBusiness() } /// |> Async.Catch /// |> Async.RunSynchronously @@ -183,7 +219,7 @@ namespace Microsoft.FSharp.Control static member Catch : computation:Async<'T> -> Async> /// Creates an asynchronous computation that executes computation. - /// If this computation is cancelled before it completes then the computation generated by + /// If this computation is cancelled before it completes then the computation generated by /// running compensation is executed. /// /// The input asynchronous computation. @@ -218,11 +254,11 @@ namespace Microsoft.FSharp.Control /// Generates a scoped, cooperative cancellation handler for use within an asynchronous workflow. /// /// For example, - /// async { use! holder = Async.OnCancel interruption ... } - /// generates an asynchronous computation where, if a cancellation happens any time during - /// the execution of the asynchronous computation in the scope of holder, then action - /// interruption is executed on the thread that is performing the cancellation. This can - /// be used to arrange for a computation to be asynchronously notified that a cancellation + /// async { use! holder = Async.OnCancel interruption ... } + /// generates an asynchronous computation where, if a cancellation happens any time during + /// the execution of the asynchronous computation in the scope of holder, then action + /// interruption is executed on the thread that is performing the cancellation. This can + /// be used to arrange for a computation to be asynchronously notified that a cancellation /// has occurred, e.g. by setting a flag, or deregistering a pending I/O action. /// /// The function that is executed on the thread performing the @@ -252,11 +288,11 @@ namespace Microsoft.FSharp.Control /// and then print "Computation Cancelled: 7", "Computation Cancelled: 11" and "Tasks Finished" in any order. /// static member OnCancel : interruption: (unit -> unit) -> Async - - /// Creates an asynchronous computation that returns the CancellationToken governing the execution + + /// Creates an asynchronous computation that returns the CancellationToken governing the execution /// of the computation. /// - /// In async { let! token = Async.CancellationToken ...} token can be used to initiate other + /// In async { let! token = Async.CancellationToken ...} token can be used to initiate other /// asynchronous operations that will cancel cooperatively with this workflow. /// /// An asynchronous computation capable of retrieving the CancellationToken from a computation @@ -267,9 +303,9 @@ namespace Microsoft.FSharp.Control /// static member CancellationToken : Async - /// Raises the cancellation condition for the most recent set of asynchronous computations started - /// without any specific CancellationToken. Replaces the global CancellationTokenSource with a new - /// global token source for any asynchronous computations created after this point without any + /// Raises the cancellation condition for the most recent set of asynchronous computations started + /// without any specific CancellationToken. Replaces the global CancellationTokenSource with a new + /// global token source for any asynchronous computations created after this point without any /// specific CancellationToken. /// /// Cancellation and Exceptions @@ -299,7 +335,7 @@ namespace Microsoft.FSharp.Control /// This will print "2" 2 seconds from start, "3" 3 seconds from start, "5" 5 seconds from start, cease computation and /// then print "Tasks Not Finished: One or more errors occurred. (A task was canceled.)". /// - static member CancelDefaultToken : unit -> unit + static member CancelDefaultToken : unit -> unit /// Gets the default cancellation token for executing asynchronous computations. /// @@ -330,22 +366,22 @@ namespace Microsoft.FSharp.Control //---------- Parallelism - /// Starts a child computation within an asynchronous workflow. + /// Starts a child computation within an asynchronous workflow. /// This allows multiple asynchronous computations to be executed simultaneously. /// - /// This method should normally be used as the immediate + /// This method should normally be used as the immediate /// right-hand-side of a let! binding in an F# asynchronous workflow, that is, /// /// async { ... - /// let! completor1 = childComputation1 |> Async.StartChild - /// let! completor2 = childComputation2 |> Async.StartChild - /// ... - /// let! result1 = completor1 - /// let! result2 = completor2 + /// let! completor1 = childComputation1 |> Async.StartChild + /// let! completor2 = childComputation2 |> Async.StartChild + /// ... + /// let! result1 = completor1 + /// let! result2 = completor2 /// ... } /// /// - /// When used in this way, each use of StartChild starts an instance of childComputation + /// When used in this way, each use of StartChild starts an instance of childComputation /// and returns a completor object representing a computation to wait for the completion of the operation. /// When executed, the completor awaits the completion of childComputation. /// @@ -386,14 +422,14 @@ namespace Microsoft.FSharp.Control /// Will throw a System.TimeoutException if called with a timeout less than 2000, otherwise will print "Result: 3". /// static member StartChild : computation:Async<'T> * ?millisecondsTimeout : int -> Async> - - /// Creates an asynchronous computation that executes all the given asynchronous computations, + + /// Creates an asynchronous computation that executes all the given asynchronous computations, /// initially queueing each as work items and using a fork/join pattern. /// /// If all child computations succeed, an array of results is passed to the success continuation. /// - /// If any child computation raises an exception, then the overall computation will trigger an - /// exception, and cancel the others. + /// If any child computation raises an exception, then the overall computation will trigger an + /// exception, and cancel the others. /// /// The overall computation will respond to cancellation while executing the child computations. /// If cancelled, the computation will cancel any remaining child computations but will still wait @@ -515,7 +551,7 @@ namespace Microsoft.FSharp.Control /// |> Async.StartAsTask /// /// t.Wait() - /// printfn $"%A{t.Result}" + /// printfn $"%A{t.Result}" /// /// This will print "3", "5", "7", "11" with ~1-2 seconds between them except for pauses where even numbers would be and then /// prints [| false; true; true; true; false; true |]. @@ -523,14 +559,14 @@ namespace Microsoft.FSharp.Control static member Sequential : computations:seq> -> Async<'T[]> /// - /// Creates an asynchronous computation that executes all given asynchronous computations in parallel, + /// Creates an asynchronous computation that executes all given asynchronous computations in parallel, /// returning the result of the first succeeding computation (one whose result is 'Some x'). /// If all child computations complete with None, the parent computation also returns None. /// /// /// - /// If any child computation raises an exception, then the overall computation will trigger an - /// exception, and cancel the others. + /// If any child computation raises an exception, then the overall computation will trigger an + /// exception, and cancel the others. /// /// The overall computation will respond to cancellation while executing the child computations. /// If cancelled, the computation will cancel any remaining child computations but will still wait @@ -594,7 +630,7 @@ namespace Microsoft.FSharp.Control static member Choice : computations:seq> -> Async<'T option> //---------- Thread Control - + /// Creates an asynchronous computation that creates a new thread and runs /// its continuation in that thread. /// @@ -611,8 +647,8 @@ namespace Microsoft.FSharp.Control /// /// This will run someLongRunningComputation() without blocking the threads in the threadpool. /// - static member SwitchToNewThread : unit -> Async - + static member SwitchToNewThread : unit -> Async + /// Creates an asynchronous computation that queues a work item that runs /// its continuation. /// @@ -634,10 +670,10 @@ namespace Microsoft.FSharp.Control /// This will run someLongRunningComputation() without blocking the threads in the threadpool, and then switch to the /// threadpool for shorter computations. /// - static member SwitchToThreadPool : unit -> Async + static member SwitchToThreadPool : unit -> Async /// Creates an asynchronous computation that runs - /// its continuation using syncContext.Post. If syncContext is null + /// its continuation using syncContext.Post. If syncContext is null /// then the asynchronous computation is equivalent to SwitchToThreadPool(). /// /// The synchronization context to accept the posted computation. @@ -647,10 +683,10 @@ namespace Microsoft.FSharp.Control /// Threads and Contexts /// /// - static member SwitchToContext : syncContext:System.Threading.SynchronizationContext -> Async + static member SwitchToContext : syncContext:System.Threading.SynchronizationContext -> Async /// Creates an asynchronous computation that captures the current - /// success, exception and cancellation continuations. The callback must + /// success, exception and cancellation continuations. The callback must /// eventually call exactly one of the given continuations. /// /// The function that accepts the current success, exception, and cancellation @@ -666,7 +702,7 @@ namespace Microsoft.FSharp.Control /// match DateTime.Today with /// | dt when dt.DayOfWeek = DayOfWeek.Monday -> failwith "Not compatible with Mondays" /// | dt -> dt - /// + /// /// let computation = /// (fun (successCont, exceptionCont, cancellationCont) -> /// try @@ -688,12 +724,12 @@ namespace Microsoft.FSharp.Control /// static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> - /// Creates an asynchronous computation that waits for a single invocation of a CLI - /// event by adding a handler to the event. Once the computation completes or is + /// Creates an asynchronous computation that waits for a single invocation of a CLI + /// event by adding a handler to the event. Once the computation completes or is /// cancelled, the handler is removed from the event. /// - /// The computation will respond to cancellation while waiting for the event. If a - /// cancellation occurs, and cancelAction is specified, then it is executed, and + /// The computation will respond to cancellation while waiting for the event. If a + /// cancellation occurs, and cancelAction is specified, then it is executed, and /// the computation continues to wait for the event. /// /// If cancelAction is not specified, then cancellation causes the computation @@ -708,7 +744,7 @@ namespace Microsoft.FSharp.Control /// Awaiting Results /// /// - static member AwaitEvent: event:IEvent<'Del,'T> * ?cancelAction : (unit -> unit) -> Async<'T> when 'Del : delegate<'T,unit> and 'Del :> System.Delegate + static member AwaitEvent: event:IEvent<'Del,'T> * ?cancelAction : (unit -> unit) -> Async<'T> when 'Del : delegate<'T,unit> and 'Del :> System.Delegate /// Creates an asynchronous computation that will wait on the given WaitHandle. /// @@ -839,13 +875,13 @@ namespace Microsoft.FSharp.Control static member Sleep: dueTime:TimeSpan -> Async /// - /// Creates an asynchronous computation in terms of a Begin/End pair of actions in + /// Creates an asynchronous computation in terms of a Begin/End pair of actions in /// the style used in CLI APIs. /// /// /// /// The computation will respond to cancellation while waiting for the completion - /// of the operation. If a cancellation occurs, and cancelAction is specified, then it is + /// of the operation. If a cancellation occurs, and cancelAction is specified, then it is /// executed, and the computation continues to wait for the completion of the operation. /// /// If cancelAction is not specified, then cancellation causes the computation @@ -863,12 +899,12 @@ namespace Microsoft.FSharp.Control static member FromBeginEnd : beginAction:(System.AsyncCallback * obj -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T> /// - /// Creates an asynchronous computation in terms of a Begin/End pair of actions in + /// Creates an asynchronous computation in terms of a Begin/End pair of actions in /// the style used in .NET 2.0 APIs. /// /// /// The computation will respond to cancellation while waiting for the completion - /// of the operation. If a cancellation occurs, and cancelAction is specified, then it is + /// of the operation. If a cancellation occurs, and cancelAction is specified, then it is /// executed, and the computation continues to wait for the completion of the operation. /// /// If cancelAction is not specified, then cancellation causes the computation @@ -888,11 +924,11 @@ namespace Microsoft.FSharp.Control static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * System.AsyncCallback * obj -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T> /// - /// Creates an asynchronous computation in terms of a Begin/End pair of actions in + /// Creates an asynchronous computation in terms of a Begin/End pair of actions in /// the style used in .NET 2.0 APIs. /// /// The computation will respond to cancellation while waiting for the completion - /// of the operation. If a cancellation occurs, and cancelAction is specified, then it is + /// of the operation. If a cancellation occurs, and cancelAction is specified, then it is /// executed, and the computation continues to wait for the completion of the operation. /// /// If cancelAction is not specified, then cancellation causes the computation @@ -911,11 +947,11 @@ namespace Microsoft.FSharp.Control /// static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * System.AsyncCallback * obj -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T> - /// Creates an asynchronous computation in terms of a Begin/End pair of actions in + /// Creates an asynchronous computation in terms of a Begin/End pair of actions in /// the style used in .NET 2.0 APIs. /// /// The computation will respond to cancellation while waiting for the completion - /// of the operation. If a cancellation occurs, and cancelAction is specified, then it is + /// of the operation. If a cancellation occurs, and cancelAction is specified, then it is /// executed, and the computation continues to wait for the completion of the operation. /// /// If cancelAction is not specified, then cancellation causes the computation @@ -935,7 +971,7 @@ namespace Microsoft.FSharp.Control /// static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * System.AsyncCallback * obj -> System.IAsyncResult) * endAction:(System.IAsyncResult -> 'T) * ?cancelAction : (unit -> unit) -> Async<'T> - /// Creates three functions that can be used to implement the .NET 1.0 Asynchronous + /// Creates three functions that can be used to implement the .NET 1.0 Asynchronous /// Programming Model (APM) for a given asynchronous computation. /// /// A function generating the asynchronous computation to split into the traditional @@ -946,15 +982,15 @@ namespace Microsoft.FSharp.Control /// Legacy .NET Async Interoperability /// /// - static member AsBeginEnd : computation:('Arg -> Async<'T>) -> + static member AsBeginEnd : computation:('Arg -> Async<'T>) -> // The 'Begin' member - ('Arg * System.AsyncCallback * obj -> System.IAsyncResult) * + ('Arg * System.AsyncCallback * obj -> System.IAsyncResult) * // The 'End' member - (System.IAsyncResult -> 'T) * + (System.IAsyncResult -> 'T) * // The 'Cancel' member (System.IAsyncResult -> unit) - /// Creates an asynchronous computation that runs the given computation and ignores + /// Creates an asynchronous computation that runs the given computation and ignores /// its result. /// /// The input computation. @@ -995,16 +1031,16 @@ namespace Microsoft.FSharp.Control /// Starting Async Computations /// /// - static member StartWithContinuations: - computation:Async<'T> * - continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * + static member StartWithContinuations: + computation:Async<'T> * + continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken-> unit /// /// - static member internal StartWithContinuationsUsingDispatchInfo: - computation:Async<'T> * - continuation:('T -> unit) * exceptionContinuation:(ExceptionDispatchInfo -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * + static member internal StartWithContinuationsUsingDispatchInfo: + computation:Async<'T> * + continuation:('T -> unit) * exceptionContinuation:(ExceptionDispatchInfo -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken-> unit /// Runs an asynchronous computation, starting immediately on the current operating system @@ -1032,7 +1068,7 @@ namespace Microsoft.FSharp.Control /// /// Prints "A", "B", "D" immediately, then "C" in 1 second /// - static member StartImmediate: + static member StartImmediate: computation:Async * ?cancellationToken:CancellationToken-> unit /// Runs an asynchronous computation, starting immediately on the current operating system @@ -1040,7 +1076,7 @@ namespace Microsoft.FSharp.Control /// /// /// If no cancellation token is provided then the default cancellation token is used. - /// You may prefer using this method if you want to achive a similar behviour to async await in C# as + /// You may prefer using this method if you want to achive a similar behviour to async await in C# as /// async computation starts on the current thread with an ability to return a result. /// /// @@ -1070,7 +1106,7 @@ namespace Microsoft.FSharp.Control /// /// Prints "A", "B", "D" immediately, then "C", "E" in 1 second. /// - static member StartImmediateAsTask: + static member StartImmediateAsTask: computation:Async<'T> * ?cancellationToken:CancellationToken-> Task<'T> @@ -1188,7 +1224,7 @@ namespace Microsoft.FSharp.Control /// A value indicating asynchronous execution. val TryWith: ctxt:AsyncActivation<'T> -> computation: Async<'T> -> catchFunction: (Exception -> Async<'T> option) -> AsyncReturn - [] + [] // Internals used by MailboxProcessor type internal ResultCell<'T> = new : unit -> ResultCell<'T> @@ -1215,7 +1251,7 @@ namespace Microsoft.FSharp.Control /// /// A cancellation check is performed on each iteration of the loop. /// - /// The existence of this method permits the use of for in the + /// The existence of this method permits the use of for in the /// async { ... } computation expression syntax. /// /// The sequence to enumerate. @@ -1232,19 +1268,19 @@ namespace Microsoft.FSharp.Control /// /// A cancellation check is performed when the computation is executed. /// - /// The existence of this method permits the use of empty else branches in the + /// The existence of this method permits the use of empty else branches in the /// async { ... } computation expression syntax. /// An asynchronous computation that returns (). /// /// - member Zero : unit -> Async + member Zero : unit -> Async /// Creates an asynchronous computation that first runs computation1 /// and then runs computation2, returning the result of computation2. /// /// A cancellation check is performed when the computation is executed. /// - /// The existence of this method permits the use of expression sequencing in the + /// The existence of this method permits the use of expression sequencing in the /// async { ... } computation expression syntax. /// /// The first part of the sequenced computation. @@ -1255,12 +1291,12 @@ namespace Microsoft.FSharp.Control /// member inline Combine : computation1:Async * computation2:Async<'T> -> Async<'T> - /// Creates an asynchronous computation that runs computation repeatedly + /// Creates an asynchronous computation that runs computation repeatedly /// until guard() becomes false. /// /// A cancellation check is performed whenever the computation is executed. /// - /// The existence of this method permits the use of while in the + /// The existence of this method permits the use of while in the /// async { ... } computation expression syntax. /// /// The function to determine when to stop executing computation. @@ -1276,7 +1312,7 @@ namespace Microsoft.FSharp.Control /// /// A cancellation check is performed when the computation is executed. /// - /// The existence of this method permits the use of return in the + /// The existence of this method permits the use of return in the /// async { ... } computation expression syntax. /// /// The value to return from the computation. @@ -1288,7 +1324,7 @@ namespace Microsoft.FSharp.Control /// Delegates to the input computation. /// - /// The existence of this method permits the use of return! in the + /// The existence of this method permits the use of return! in the /// async { ... } computation expression syntax. /// /// The input computation. @@ -1309,13 +1345,13 @@ namespace Microsoft.FSharp.Control /// member Delay : generator:(unit -> Async<'T>) -> Async<'T> - /// Creates an asynchronous computation that runs binder(resource). + /// Creates an asynchronous computation that runs binder(resource). /// The action resource.Dispose() is executed as this computation yields its result /// or if the asynchronous computation exits by an exception or by cancellation. /// /// A cancellation check is performed when the computation is executed. /// - /// The existence of this method permits the use of use and use! in the + /// The existence of this method permits the use of use and use! in the /// async { ... } computation expression syntax. /// /// The resource to be used and disposed. @@ -1327,12 +1363,12 @@ namespace Microsoft.FSharp.Control /// member Using: resource:'T * binder:('T -> Async<'U>) -> Async<'U> when 'T :> System.IDisposable - /// Creates an asynchronous computation that runs computation, and when + /// Creates an asynchronous computation that runs computation, and when /// computation generates a result T, runs binder res. /// /// A cancellation check is performed when the computation is executed. /// - /// The existence of this method permits the use of let! in the + /// The existence of this method permits the use of let! in the /// async { ... } computation expression syntax. /// /// The computation to provide an unbound result. @@ -1343,14 +1379,14 @@ namespace Microsoft.FSharp.Control /// /// member inline Bind: computation: Async<'T> * binder: ('T -> Async<'U>) -> Async<'U> - - /// Creates an asynchronous computation that runs computation. The action compensation is executed + + /// Creates an asynchronous computation that runs computation. The action compensation is executed /// after computation completes, whether computation exits normally or by an exception. If compensation raises an exception itself /// the original exception is discarded and the new exception becomes the overall result of the computation. /// /// A cancellation check is performed when the computation is executed. /// - /// The existence of this method permits the use of try/finally in the + /// The existence of this method permits the use of try/finally in the /// async { ... } computation expression syntax. /// /// The input computation. @@ -1368,7 +1404,7 @@ namespace Microsoft.FSharp.Control /// /// A cancellation check is performed when the computation is executed. /// - /// The existence of this method permits the use of try/with in the + /// The existence of this method permits the use of try/with in the /// async { ... } computation expression syntax. /// /// The input computation. @@ -1393,9 +1429,9 @@ namespace Microsoft.FSharp.Control /// Async Programming [] module CommonExtensions = - - type System.IO.Stream with - + + type System.IO.Stream with + /// Returns an asynchronous computation that will read from the stream into the given buffer. /// The buffer to read into. /// An optional offset as a number of bytes in the stream. @@ -1406,21 +1442,21 @@ namespace Microsoft.FSharp.Control /// Thrown when the sum of offset and count is longer than /// the buffer length. /// Thrown when offset or count is negative. - /// + /// /// [] // give the extension member a nice, unmangled compiled name, unique within this module member AsyncRead : buffer:byte[] * ?offset:int * ?count:int -> Async - + /// Returns an asynchronous computation that will read the given number of bytes from the stream. /// /// The number of bytes to read. /// - /// An asynchronous computation that returns the read byte[] when run. - /// + /// An asynchronous computation that returns the read byte[] when run. + /// /// [] // give the extension member a nice, unmangled compiled name, unique within this module member AsyncRead : count:int -> Async - + /// Returns an asynchronous computation that will write the given bytes to the stream. /// /// The buffer to write from. @@ -1432,7 +1468,7 @@ namespace Microsoft.FSharp.Control /// Thrown when the sum of offset and count is longer than /// the buffer length. /// Thrown when offset or count is negative. - /// + /// /// [] // give the extension member a nice, unmangled compiled name, unique within this module member AsyncWrite : buffer:byte[] * ?offset:int * ?count:int -> Async @@ -1444,7 +1480,7 @@ namespace Microsoft.FSharp.Control /// be invoked for each observation. /// /// The function to be called for each observation. - /// + /// /// [] // give the extension member a nice, unmangled compiled name, unique within this module member Add: callback:('T -> unit) -> unit @@ -1456,7 +1492,7 @@ namespace Microsoft.FSharp.Control /// The function to be called for each observation. /// /// An object that will remove the listener if disposed. - /// + /// /// [] // give the extension member a nice, unmangled compiled name, unique within this module member Subscribe: callback:('T -> unit) -> System.IDisposable @@ -1465,12 +1501,12 @@ namespace Microsoft.FSharp.Control /// /// Async Programming [] - module WebExtensions = + module WebExtensions = - type System.Net.WebRequest with + type System.Net.WebRequest with /// Returns an asynchronous computation that, when run, will wait for a response to the given WebRequest. /// An asynchronous computation that waits for response to the WebRequest. - /// + /// /// /// /// open System.Net @@ -1493,7 +1529,7 @@ namespace Microsoft.FSharp.Control /// The URI to retrieve. /// /// An asynchronous computation that will wait for the download of the URI. - /// + /// /// /// /// open System @@ -1510,14 +1546,14 @@ namespace Microsoft.FSharp.Control /// The URI to retrieve. /// /// An asynchronous computation that will wait for the download of the URI. - /// + /// /// /// /// open System.Net /// open System.Text /// open System /// let client = new WebClient() - /// client.AsyncDownloadData(Uri("https://www.w3.org")) |> Async.RunSynchronously |> Encoding.ASCII.GetString + /// client.AsyncDownloadData(Uri("https://www.w3.org")) |> Async.RunSynchronously |> Encoding.ASCII.GetString /// /// /// Downloads the data in bytes and decodes it to a string. @@ -1530,7 +1566,7 @@ namespace Microsoft.FSharp.Control /// The file name to save download to. /// /// An asynchronous computation that will wait for the download of the URI to specified file. - /// + /// /// /// /// open System.Net @@ -1544,6 +1580,6 @@ namespace Microsoft.FSharp.Control member AsyncDownloadFile : address:System.Uri * fileName: string -> Async // Internals used by MailboxProcessor - module internal AsyncBuilderImpl = + module internal AsyncBuilderImpl = val async : AsyncBuilder diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs index 2edf022ac75..3f01fcb953e 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs @@ -80,13 +80,13 @@ module ChoiceUtils = let result = let cancellationToken = match cancelAfter with - | Some ca -> + | Some ca -> let cts = new CancellationTokenSource() cts.CancelAfter(ca) Some cts.Token | None -> None - try Async.RunSynchronously(choiceWorkflow, ?cancellationToken = cancellationToken) |> Choice1Of2 + try Async.RunSynchronously(choiceWorkflow, ?cancellationToken = cancellationToken) |> Choice1Of2 with e -> Choice2Of2 e // Step 3. check that results are up to spec @@ -94,7 +94,7 @@ module ChoiceUtils = seq { yield Int32.MaxValue // "infinity": avoid exceptions if list is empty - for op in ops do + for op in ops do match op with | NoneResultAfter _ -> () | op -> yield op.Timeout @@ -105,7 +105,7 @@ module ChoiceUtils = let verifyIndex index = if index < 0 || index >= ops.Length then Assert.Fail "Returned choice index is out of bounds." - + // Step 3a. check that output is up to spec match result with | Choice1Of2 (Some index) -> @@ -142,36 +142,36 @@ module LeakUtils = // We also need something non trivial to dissuade the compiler from inlining in Release builds. type ToRun<'a>(f : unit -> 'a) = member _.Invoke() = f() - + let run (toRun : ToRun<'a>) = toRun.Invoke() // --------------------------------------------------- type AsyncModule() = - + /// Simple asynchronous task that delays 200ms and returns a list of the current tick count let getTicksTask = async { do! Async.SwitchToThreadPool() let mutable tickstamps = [] // like timestamps but for ticks :) - + for i = 1 to 10 do tickstamps <- DateTime.UtcNow.Ticks :: tickstamps do! Async.Sleep(20) - + return tickstamps } - let wait (wh : System.Threading.WaitHandle) (timeoutMilliseconds : int) = + let wait (wh : System.Threading.WaitHandle) (timeoutMilliseconds : int) = wh.WaitOne(timeoutMilliseconds, exitContext=false) let dispose(d : #IDisposable) = d.Dispose() - let testErrorAndCancelRace testCaseName computation = + let testErrorAndCancelRace testCaseName computation = for i in 1..20 do let cts = new System.Threading.CancellationTokenSource() use barrier = new System.Threading.ManualResetEvent(false) - async { cts.Cancel() } + async { cts.Cancel() } |> Async.Start let c = ref 0 @@ -189,6 +189,25 @@ type AsyncModule() = |> ignore if c.Value = 2 then Assert.Fail("both error and cancel continuations were called") + [] + member _.``Async.RunImmediate should execute on the same thread`` () = + let t = + async { + let a = Thread.CurrentThread.ManagedThreadId + let b = + async { + return Thread.CurrentThread.ManagedThreadId + } |> Async.RunImmediate + let c = Thread.CurrentThread.ManagedThreadId + return $"Before: {a}, in async: {b}, after async: {c}" + } |> Async.RunImmediate + + let d = Thread.CurrentThread.ManagedThreadId + let actual = $"{t}, after task: {d}" + + if not (actual = $"Before: {d}, in async: {d}, after async: {d}, after task: {d}") then + failwith actual + [] member _.AwaitIAsyncResult() = @@ -203,8 +222,8 @@ type AsyncModule() = // When the operation has already completed let operationIAR = beginOp ((), new AsyncCallback(fun iar -> ()), null) sleep(250) - - let result = Async.AwaitIAsyncResult(operationIAR) |> Async.RunSynchronously + + let result = Async.AwaitIAsyncResult(operationIAR) |> Async.RunSynchronously match result with | true -> () | false -> Assert.Fail("Timed out. Expected to succeed.") @@ -217,11 +236,11 @@ type AsyncModule() = | false -> () [] - member _.``AwaitWaitHandle.Timeout``() = + member _.``AwaitWaitHandle.Timeout``() = use waitHandle = new System.Threading.ManualResetEvent(false) let startTime = DateTime.UtcNow - let r = + let r = Async.AwaitWaitHandle(waitHandle, 500) |> Async.RunSynchronously @@ -232,19 +251,19 @@ type AsyncModule() = Assert.True(delta.TotalMilliseconds < 1100.0, sprintf "Expected faster timeout than %.0f ms" delta.TotalMilliseconds) [] - member _.``AwaitWaitHandle.TimeoutWithCancellation``() = + member _.``AwaitWaitHandle.TimeoutWithCancellation``() = use barrier = new System.Threading.ManualResetEvent(false) use waitHandle = new System.Threading.ManualResetEvent(false) let cts = new System.Threading.CancellationTokenSource() Async.AwaitWaitHandle(waitHandle, 5000) |> Async.Ignore - |> fun c -> + |> fun c -> Async.StartWithContinuations( - c, - (failwithf "Unexpected success %A"), - (failwithf "Unexpected error %A"), - (fun _ -> barrier.Set() |> ignore), + c, + (failwithf "Unexpected success %A"), + (failwithf "Unexpected error %A"), + (fun _ -> barrier.Set() |> ignore), cts.Token ) @@ -259,9 +278,9 @@ type AsyncModule() = if not ok then Assert.Fail("Async computation was not completed in given time") [] - member _.``AwaitWaitHandle.DisposedWaitHandle1``() = + member _.``AwaitWaitHandle.DisposedWaitHandle1``() = let wh = new System.Threading.ManualResetEvent(false) - + dispose wh let test = async { try @@ -275,8 +294,8 @@ type AsyncModule() = // test is flaky: https://github.com/dotnet/fsharp/issues/11586 //[] - member _.``OnCancel.RaceBetweenCancellationHandlerAndDisposingHandlerRegistration``() = - let test() = + member _.``OnCancel.RaceBetweenCancellationHandlerAndDisposingHandlerRegistration``() = + let test() = use flag = new ManualResetEvent(false) use cancelHandlerRegistered = new ManualResetEvent(false) let cts = new System.Threading.CancellationTokenSource() @@ -299,7 +318,7 @@ type AsyncModule() = // test is flaky: https://github.com/dotnet/fsharp/issues/11586 //[] - member _.``OnCancel.RaceBetweenCancellationAndDispose``() = + member _.``OnCancel.RaceBetweenCancellationAndDispose``() = let mutable flag = 0 let cts = new System.Threading.CancellationTokenSource() let go = async { @@ -318,8 +337,8 @@ type AsyncModule() = // test is flaky: https://github.com/dotnet/fsharp/issues/11586 //[] - member _.``OnCancel.CancelThatWasSignalledBeforeRunningTheComputation``() = - let test() = + member _.``OnCancel.CancelThatWasSignalledBeforeRunningTheComputation``() = + let test() = let cts = new System.Threading.CancellationTokenSource() let go e (flag : bool ref) = async { let! _ = Async.AwaitWaitHandle e @@ -347,13 +366,13 @@ type AsyncModule() = // This test checks that AwaitWaitHandle does not leak continuations (described in #131), // We only test the worst case - when the AwaitWaitHandle is already set. use manualResetEvent = new System.Threading.ManualResetEvent(true) - - let tryToLeak() = - let resource = + + let tryToLeak() = + let resource = LeakUtils.ToRun (fun () -> let resource = obj() - let work = - async { + let work = + async { let! _ = Async.AwaitWaitHandle manualResetEvent GC.KeepAlive(resource) return () @@ -372,13 +391,13 @@ type AsyncModule() = GC.Collect() Assert.False(resource.IsAlive) - + // The leak hangs on a race condition which is really hard to trigger in F# 3.0, hence the 100000 runs... for _ in 1..10 do tryToLeak() #endif [] - member _.``AwaitWaitHandle.DisposedWaitHandle2``() = + member _.``AwaitWaitHandle.DisposedWaitHandle2``() = let wh = new System.Threading.ManualResetEvent(false) let barrier = new System.Threading.ManualResetEvent(false) @@ -393,12 +412,12 @@ type AsyncModule() = let timeout = wait barrier 3000 Assert.False(timeout, "Barrier was reached too early") dispose wh - + let ok = wait barrier 10000 if not ok then Assert.Fail("Async computation was not completed in given time") [] - member _.``RunSynchronously.NoThreadJumpsAndTimeout``() = + member _.``RunSynchronously.NoThreadJumpsAndTimeout``() = let longRunningTask = async { sleep(5000) } try Async.RunSynchronously(longRunningTask, timeout = 500) @@ -407,7 +426,7 @@ type AsyncModule() = :? System.TimeoutException -> () [] - member _.``RunSynchronously.NoThreadJumpsAndTimeout.DifferentSyncContexts``() = + member _.``RunSynchronously.NoThreadJumpsAndTimeout.DifferentSyncContexts``() = let run syncContext = let old = SynchronizationContext.Current SynchronizationContext.SetSynchronizationContext(syncContext) @@ -425,7 +444,7 @@ type AsyncModule() = [] // See https://github.com/dotnet/fsharp/issues/12637#issuecomment-1020199383 - member _.``RunSynchronously.ThreadJump.IfSyncCtxtNonNull``() = + member _.``RunSynchronously.ThreadJump.IfSyncCtxtNonNull``() = async { do! Async.SwitchToThreadPool() let old = SynchronizationContext.Current @@ -444,7 +463,7 @@ type AsyncModule() = |> Async.RunSynchronously [] - member _.``RaceBetweenCancellationAndError.AwaitWaitHandle``() = + member _.``RaceBetweenCancellationAndError.AwaitWaitHandle``() = let disposedEvent = new System.Threading.ManualResetEvent(false) dispose disposedEvent testErrorAndCancelRace "RaceBetweenCancellationAndError.AwaitWaitHandle" (Async.AwaitWaitHandle disposedEvent) @@ -476,13 +495,13 @@ type AsyncModule() = [] member _.``error on one workflow should cancel all others``() = - let counter = + let counter = async { let mutable counter = 0 - let job i = async { - if i = 55 then failwith "boom" - else - do! Async.Sleep 1000 + let job i = async { + if i = 55 then failwith "boom" + else + do! Async.Sleep 1000 counter <- counter + 1 } @@ -494,7 +513,7 @@ type AsyncModule() = Assert.AreEqual(0, counter) [] - member _.``AwaitWaitHandle.ExceptionsAfterTimeout``() = + member _.``AwaitWaitHandle.ExceptionsAfterTimeout``() = let wh = new System.Threading.ManualResetEvent(false) let test = async { try @@ -506,40 +525,40 @@ type AsyncModule() = :? InvalidOperationException as e when e.Message = "EXPECTED" -> return () } Async.RunSynchronously(test) - + [] - member _.``FromContinuationsCanTailCallCurrentThread``() = + member _.``FromContinuationsCanTailCallCurrentThread``() = let mutable cnt = 0 - let origTid = System.Threading.Thread.CurrentThread.ManagedThreadId + let origTid = System.Threading.Thread.CurrentThread.ManagedThreadId let mutable finalTid = -1 let rec f n = if n = 0 then - async { + async { finalTid <- System.Threading.Thread.CurrentThread.ManagedThreadId return () } else async { cnt <- cnt + 1 do! Async.FromContinuations(fun (k,_,_) -> k()) - do! f (n-1) + do! f (n-1) } // 5000 is big enough that does-not-stackoverflow means we are tailcalling thru FromContinuations - f 5000 |> Async.StartImmediate + f 5000 |> Async.StartImmediate Assert.AreEqual(origTid, finalTid) Assert.AreEqual(5000, cnt) [] - member _.``AwaitWaitHandle With Cancellation``() = + member _.``AwaitWaitHandle With Cancellation``() = let run wh = async { let! r = Async.AwaitWaitHandle wh Assert.True(r, "Timeout not expected") - return() + return() } - let test () = + let test () = let wh = new System.Threading.ManualResetEvent(false) let cts = new System.Threading.CancellationTokenSource() let asyncs = - [ + [ yield! List.init 100 (fun _ -> run wh) yield async { cts.Cancel() } yield async { wh.Set() |> ignore } @@ -554,7 +573,7 @@ type AsyncModule() = for _ in 1..1000 do test() [] - member _.``StartWithContinuationsVersusDoBang``() = + member _.``StartWithContinuationsVersusDoBang``() = // worthwhile to note these three // case 1 let mutable r = "" @@ -563,8 +582,8 @@ type AsyncModule() = do! Async.FromContinuations(fun (s, _, _) -> s()) return failwith "boom" with - e-> r <- e.Message - } |> Async.RunSynchronously + e-> r <- e.Message + } |> Async.RunSynchronously Assert.AreEqual("boom", r) // case 2 r <- "" @@ -581,37 +600,37 @@ type AsyncModule() = #if IGNORED [] - member _.``SleepContinuations``() = + member _.``SleepContinuations``() = let okCount = ref 0 let errCount = ref 0 - let test() = - let cts = new System.Threading.CancellationTokenSource() - - System.Threading.ThreadPool.QueueUserWorkItem(fun _-> - System.Threading.Thread.Sleep 50 - try - Async.StartWithContinuations( - Async.Sleep(1000), - (fun _ -> printfn "ok"; incr okCount), - (fun _ -> printfn "error"; incr errCount), - (fun _ -> printfn "cancel"; failwith "BOOM!"), - cancellationToken = cts.Token - ) - with _ -> () - ) |> ignore - System.Threading.Thread.Sleep 50 - try cts.Cancel() with _ -> () - System.Threading.Thread.Sleep 1500 - printfn "====" + let test() = + let cts = new System.Threading.CancellationTokenSource() + + System.Threading.ThreadPool.QueueUserWorkItem(fun _-> + System.Threading.Thread.Sleep 50 + try + Async.StartWithContinuations( + Async.Sleep(1000), + (fun _ -> printfn "ok"; incr okCount), + (fun _ -> printfn "error"; incr errCount), + (fun _ -> printfn "cancel"; failwith "BOOM!"), + cancellationToken = cts.Token + ) + with _ -> () + ) |> ignore + System.Threading.Thread.Sleep 50 + try cts.Cancel() with _ -> () + System.Threading.Thread.Sleep 1500 + printfn "====" for i = 1 to 3 do test() Assert.AreEqual(0, !okCount) Assert.AreEqual(0, !errCount) #endif [] - member _.``Async caching should work``() = + member _.``Async caching should work``() = let mutable x = 0 - let someSlowFunc _mykey = async { + let someSlowFunc _mykey = async { Console.WriteLine "Simulated downloading..." do! Async.Sleep 400 Console.WriteLine "Simulated downloading Done." @@ -625,7 +644,7 @@ type AsyncModule() = do! memFunc "a" |> Async.Ignore do! memFunc "a" |> Async.Ignore do! memFunc "a" |> Async.Ignore - do! [|1 .. 30|] |> Seq.map(fun _ -> (memFunc "a")) + do! [|1 .. 30|] |> Seq.map(fun _ -> (memFunc "a")) |> Async.Parallel |> Async.Ignore Console.WriteLine "Still more ways...." @@ -640,7 +659,7 @@ type AsyncModule() = Async.Start( memFunc "a" |> Async.Ignore ) Console.WriteLine "Still more ways again again...." - do! [|1 .. 30|] |> Seq.map(fun _ -> (memFunc "a")) + do! [|1 .. 30|] |> Seq.map(fun _ -> (memFunc "a")) |> Async.Parallel |> Async.Ignore } |> Async.RunSynchronously Console.WriteLine "Checking result...." @@ -737,16 +756,16 @@ type AsyncModule() = // index 1 will enter the try/finally quickly, call failwith and cancel the other tasks // One of index 2 and index 3 will be stuck here before the try/finally. But having got // this far it should enter the try/finally before cancellation takes effect - do + do lock gate <| fun () -> printfn "[%i] Acquired semaphore" index Interlocked.Increment(&acquiredCount) |> ignore - if index <> 0 then + if index <> 0 then lock gate <| fun () -> printfn "[%i] Slowly entering try/finally" index System.Threading.Thread.Sleep(100) try lock gate <| fun () -> printfn "[%i] Within try-finally" index - if index = 0 then + if index = 0 then lock gate <| fun () -> printfn "[%i] Error" index // The failure will cause others to cancel failwith "Something bad happened!" diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.fs index fd0fe854332..2cb17b58b8f 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.fs @@ -606,6 +606,7 @@ Microsoft.FSharp.Control.FSharpAsync: System.Threading.CancellationToken get_Def Microsoft.FSharp.Control.FSharpAsync: System.Threading.Tasks.Task`1[T] StartAsTask[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.Tasks.TaskCreationOptions], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: System.Threading.Tasks.Task`1[T] StartImmediateAsTask[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: System.Tuple`3[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[TArg,System.AsyncCallback,System.Object],System.IAsyncResult],Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T],Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,Microsoft.FSharp.Core.Unit]] AsBeginEnd[TArg,T](Microsoft.FSharp.Core.FSharpFunc`2[TArg,Microsoft.FSharp.Control.FSharpAsync`1[T]]) +Microsoft.FSharp.Control.FSharpAsync: T RunImmediate[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: T RunSynchronously[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: Void CancelDefaultToken() Microsoft.FSharp.Control.FSharpAsync: Void Start(Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken])