Skip to content

Commit

Permalink
[RFC FS-1028] - Implement Async.StartImmediateAsTask (#2534)
Browse files Browse the repository at this point in the history
Thank you for this contribution

Kevin
  • Loading branch information
OnurGumus authored and KevinRansom committed Dec 15, 2017
1 parent 7228139 commit 4054701
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,102 @@ type AsyncType() =
Assert.IsTrue(t.IsCanceled)
Assert.IsTrue(!cancelled)

[<Test>]
member this.CreateImmediateAsTask () =
let s = "Hello tasks!"
let a = async { return s }
#if FSCORE_PORTABLE_NEW || coreclr
let t : Task<string> =
#else
use t : Task<string> =
#endif
Async.StartImmediateAsTask a
this.WaitASec t
Assert.IsTrue (t.IsCompleted)
Assert.AreEqual(s, t.Result)

[<Test>]
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)


[<Test>]
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)

[<Test>]
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)

[<Test>]
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)


[<Test>]
member this.TaskAsyncValue () =
Expand Down
11 changes: 11 additions & 0 deletions src/fsharp/FSharp.Core/control.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,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<unit>, ?cancellationToken) : unit =
let token = defaultArg cancellationToken defaultCancellationTokenSource.Token
CancellationTokenOps.StartWithContinuations(token, computation, id, (fun edi -> edi.ThrowAny()), ignore)
Expand Down
17 changes: 17 additions & 0 deletions src/fsharp/FSharp.Core/control.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,23 @@ namespace Microsoft.FSharp.Control
computation:Async<unit> * ?cancellationToken:CancellationToken-> unit


/// <summary>Runs an asynchronous computation, starting immediately on the current operating system,
/// but also returns the execution as <c>System.Threading.Tasks.Task</c>
/// </summary>
/// <remarks>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.
/// </remarks>
/// <param name="computation">The asynchronous computation to execute.</param>
/// <param name="cancellationToken">The <c>CancellationToken</c> to associate with the computation.
/// The default is used if this parameter is not provided.</param>
/// <returns>A <c>System.Threading.Tasks.Task</c> that will be completed
/// in the corresponding state once the computation terminates (produces the result, throws exception or gets canceled)</returns>
/// </returns>
static member StartImmediateAsTask:
computation:Async<'T> * ?cancellationToken:CancellationToken-> Task<'T>



[<CompiledName("FSharpAsyncBuilder")>]
[<Sealed>]
Expand Down

0 comments on commit 4054701

Please sign in to comment.