From 08376fb88ad1a78d82b80bf4ff1fa40a3673b4cd Mon Sep 17 00:00:00 2001 From: Onur Gumus Date: Sun, 5 Mar 2017 22:46:07 +0300 Subject: [PATCH] [RFC FS-1028] - Implement Async.StartImmediateAsTask --- .../Microsoft.FSharp.Control/AsyncType.fs | 97 +++++++++++++++++++ src/fsharp/FSharp.Core/control.fs | 11 +++ src/fsharp/FSharp.Core/control.fsi | 17 ++++ 3 files changed, 125 insertions(+) diff --git a/src/fsharp/FSharp.Core.Unittests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs b/src/fsharp/FSharp.Core.Unittests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs index a883b2ecf51..103ab540aca 100644 --- a/src/fsharp/FSharp.Core.Unittests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs +++ b/src/fsharp/FSharp.Core.Unittests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs @@ -162,6 +162,7 @@ type AsyncType() = this.WaitASec t Assert.IsTrue (t.IsCompleted) Assert.AreEqual(s, t.Result) + [] member this.ExceptionPropagatesToTask () = @@ -230,6 +231,102 @@ type AsyncType() = Assert.IsTrue(t.IsCanceled) Assert.IsTrue(!cancelled) + [] + member this.CreateImmediateAsTask () = + let s = "Hello tasks!" + let a = async { return s } +#if FSCORE_PORTABLE_NEW || coreclr + let t : Task = +#else + use t : Task = +#endif + Async.StartImmediateAsTask a + this.WaitASec t + Assert.IsTrue (t.IsCompleted) + Assert.AreEqual(s, t.Result) + + [] + member this.StartImmediateAsTask () = + let s = "Hello tasks!" + let a = async { return s } +#if FSCORE_PORTABLE_NEW || coreclr + let t = +#else + use t = +#endif + Async.StartImmediateAsTask a + this.WaitASec t + Assert.IsTrue (t.IsCompleted) + Assert.AreEqual(s, t.Result) + + + [] + member this.ExceptionPropagatesToImmediateTask () = + let a = async { + do raise (Exception ()) + } +#if FSCORE_PORTABLE_NEW || coreclr + let t = +#else + use t = +#endif + Async.StartImmediateAsTask a + let mutable exceptionThrown = false + try + this.WaitASec t + with + e -> exceptionThrown <- true + Assert.IsTrue (t.IsFaulted) + Assert.IsTrue(exceptionThrown) + + [] + member this.CancellationPropagatesToImmediateTask () = + let a = async { + while true do () + } +#if FSCORE_PORTABLE_NEW || coreclr + let t = +#else + use t = +#endif + Async.StartImmediateAsTask a + Async.CancelDefaultToken () + let mutable exceptionThrown = false + try + this.WaitASec t + with e -> exceptionThrown <- true + Assert.IsTrue (exceptionThrown) + Assert.IsTrue(t.IsCanceled) + + [] + member this.CancellationPropagatesToGroupImmediate () = + let ewh = new ManualResetEvent(false) + let cancelled = ref false + let a = async { + use! holder = Async.OnCancel (fun _ -> cancelled := true) + ewh.Set() |> Assert.IsTrue + while true do () + } + let cts = new CancellationTokenSource() + let token = cts.Token +#if FSCORE_PORTABLE_NEW || coreclr + let t = +#else + use t = +#endif + Async.StartImmediateAsTask(a, cancellationToken=token) +// printfn "%A" t.Status + ewh.WaitOne() |> Assert.IsTrue + cts.Cancel() +// printfn "%A" t.Status + let mutable exceptionThrown = false + try + this.WaitASec t + with e -> exceptionThrown <- true + Assert.IsTrue (exceptionThrown) + Assert.IsTrue(t.IsCanceled) + Assert.IsTrue(!cancelled) + [] member this.TaskAsyncValue () = diff --git a/src/fsharp/FSharp.Core/control.fs b/src/fsharp/FSharp.Core/control.fs index 316ba9a34d6..3fe52e57fd3 100644 --- a/src/fsharp/FSharp.Core/control.fs +++ b/src/fsharp/FSharp.Core/control.fs @@ -1595,6 +1595,17 @@ namespace Microsoft.FSharp.Control static member StartWithContinuations(computation:Async<'T>, continuation, exceptionContinuation, cancellationContinuation, ?cancellationToken) : unit = Async.StartWithContinuationsUsingDispatchInfo(computation, continuation, (fun edi -> exceptionContinuation (edi.GetAssociatedSourceException())), cancellationContinuation, ?cancellationToken=cancellationToken) + static member StartImmediateAsTask (computation : Async<'T>, ?cancellationToken ) : Task<'T>= + let token = defaultArg cancellationToken defaultCancellationTokenSource.Token + let ts = new TaskCompletionSource<'T>() + let task = ts.Task + Async.StartWithContinuations( + computation, + (fun (k) -> ts.SetResult(k)), + (fun exn -> ts.SetException(exn)), + (fun _ -> ts.SetCanceled()), + token) + task static member StartImmediate(computation:Async, ?cancellationToken) : unit = let token = defaultArg cancellationToken defaultCancellationTokenSource.Token CancellationTokenOps.StartWithContinuations(token, computation, id, (fun edi -> edi.ThrowAny()), ignore) diff --git a/src/fsharp/FSharp.Core/control.fsi b/src/fsharp/FSharp.Core/control.fsi index c96901e15f0..d4fc30d978b 100644 --- a/src/fsharp/FSharp.Core/control.fsi +++ b/src/fsharp/FSharp.Core/control.fsi @@ -521,6 +521,23 @@ namespace Microsoft.FSharp.Control computation:Async * ?cancellationToken:CancellationToken-> unit + /// Runs an asynchronous computation, starting immediately on the current operating system, + /// but also returns the execution as System.Threading.Tasks.Task + /// + /// 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 + /// async computation starts on the current thread with an ability to return a result. + /// + /// The asynchronous computation to execute. + /// The CancellationToken to associate with the computation. + /// The default is used if this parameter is not provided. + /// A System.Threading.Tasks.Task that will be completed + /// in the corresponding state once the computation terminates (produces the result, throws exception or gets canceled) + /// + static member StartImmediateAsTask: + computation:Async<'T> * ?cancellationToken:CancellationToken-> Task<'T> + + [] []