Skip to content

Commit 4054701

Browse files
OnurGumusKevinRansom
authored andcommitted
[RFC FS-1028] - Implement Async.StartImmediateAsTask (#2534)
Thank you for this contribution Kevin
1 parent 7228139 commit 4054701

3 files changed

Lines changed: 124 additions & 0 deletions

File tree

src/fsharp/FSharp.Core.Unittests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,102 @@ type AsyncType() =
275275
Assert.IsTrue(t.IsCanceled)
276276
Assert.IsTrue(!cancelled)
277277

278+
[<Test>]
279+
member this.CreateImmediateAsTask () =
280+
let s = "Hello tasks!"
281+
let a = async { return s }
282+
#if FSCORE_PORTABLE_NEW || coreclr
283+
let t : Task<string> =
284+
#else
285+
use t : Task<string> =
286+
#endif
287+
Async.StartImmediateAsTask a
288+
this.WaitASec t
289+
Assert.IsTrue (t.IsCompleted)
290+
Assert.AreEqual(s, t.Result)
291+
292+
[<Test>]
293+
member this.StartImmediateAsTask () =
294+
let s = "Hello tasks!"
295+
let a = async { return s }
296+
#if FSCORE_PORTABLE_NEW || coreclr
297+
let t =
298+
#else
299+
use t =
300+
#endif
301+
Async.StartImmediateAsTask a
302+
this.WaitASec t
303+
Assert.IsTrue (t.IsCompleted)
304+
Assert.AreEqual(s, t.Result)
305+
306+
307+
[<Test>]
308+
member this.ExceptionPropagatesToImmediateTask () =
309+
let a = async {
310+
do raise (Exception ())
311+
}
312+
#if FSCORE_PORTABLE_NEW || coreclr
313+
let t =
314+
#else
315+
use t =
316+
#endif
317+
Async.StartImmediateAsTask a
318+
let mutable exceptionThrown = false
319+
try
320+
this.WaitASec t
321+
with
322+
e -> exceptionThrown <- true
323+
Assert.IsTrue (t.IsFaulted)
324+
Assert.IsTrue(exceptionThrown)
325+
326+
[<Test>]
327+
member this.CancellationPropagatesToImmediateTask () =
328+
let a = async {
329+
while true do ()
330+
}
331+
#if FSCORE_PORTABLE_NEW || coreclr
332+
let t =
333+
#else
334+
use t =
335+
#endif
336+
Async.StartImmediateAsTask a
337+
Async.CancelDefaultToken ()
338+
let mutable exceptionThrown = false
339+
try
340+
this.WaitASec t
341+
with e -> exceptionThrown <- true
342+
Assert.IsTrue (exceptionThrown)
343+
Assert.IsTrue(t.IsCanceled)
344+
345+
[<Test>]
346+
member this.CancellationPropagatesToGroupImmediate () =
347+
let ewh = new ManualResetEvent(false)
348+
let cancelled = ref false
349+
let a = async {
350+
use! holder = Async.OnCancel (fun _ -> cancelled := true)
351+
ewh.Set() |> Assert.IsTrue
352+
while true do ()
353+
}
354+
let cts = new CancellationTokenSource()
355+
let token = cts.Token
356+
#if FSCORE_PORTABLE_NEW || coreclr
357+
let t =
358+
#else
359+
use t =
360+
#endif
361+
Async.StartImmediateAsTask(a, cancellationToken=token)
362+
// printfn "%A" t.Status
363+
ewh.WaitOne() |> Assert.IsTrue
364+
cts.Cancel()
365+
// printfn "%A" t.Status
366+
let mutable exceptionThrown = false
367+
try
368+
this.WaitASec t
369+
with e -> exceptionThrown <- true
370+
Assert.IsTrue (exceptionThrown)
371+
Assert.IsTrue(t.IsCanceled)
372+
Assert.IsTrue(!cancelled)
373+
278374

279375
[<Test>]
280376
member this.TaskAsyncValue () =

src/fsharp/FSharp.Core/control.fs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,17 @@ namespace Microsoft.FSharp.Control
12181218
static member StartWithContinuations(computation:Async<'T>, continuation, exceptionContinuation, cancellationContinuation, ?cancellationToken) : unit =
12191219
Async.StartWithContinuationsUsingDispatchInfo(computation, continuation, (fun edi -> exceptionContinuation (edi.GetAssociatedSourceException())), cancellationContinuation, ?cancellationToken=cancellationToken)
12201220

1221+
static member StartImmediateAsTask (computation : Async<'T>, ?cancellationToken ) : Task<'T>=
1222+
let token = defaultArg cancellationToken defaultCancellationTokenSource.Token
1223+
let ts = new TaskCompletionSource<'T>()
1224+
let task = ts.Task
1225+
Async.StartWithContinuations(
1226+
computation,
1227+
(fun (k) -> ts.SetResult(k)),
1228+
(fun exn -> ts.SetException(exn)),
1229+
(fun _ -> ts.SetCanceled()),
1230+
token)
1231+
task
12211232
static member StartImmediate(computation:Async<unit>, ?cancellationToken) : unit =
12221233
let token = defaultArg cancellationToken defaultCancellationTokenSource.Token
12231234
CancellationTokenOps.StartWithContinuations(token, computation, id, (fun edi -> edi.ThrowAny()), ignore)

src/fsharp/FSharp.Core/control.fsi

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,23 @@ namespace Microsoft.FSharp.Control
415415
computation:Async<unit> * ?cancellationToken:CancellationToken-> unit
416416

417417

418+
/// <summary>Runs an asynchronous computation, starting immediately on the current operating system,
419+
/// but also returns the execution as <c>System.Threading.Tasks.Task</c>
420+
/// </summary>
421+
/// <remarks>If no cancellation token is provided then the default cancellation token is used.
422+
/// You may prefer using this method if you want to achive a similar behviour to async await in C# as
423+
/// async computation starts on the current thread with an ability to return a result.
424+
/// </remarks>
425+
/// <param name="computation">The asynchronous computation to execute.</param>
426+
/// <param name="cancellationToken">The <c>CancellationToken</c> to associate with the computation.
427+
/// The default is used if this parameter is not provided.</param>
428+
/// <returns>A <c>System.Threading.Tasks.Task</c> that will be completed
429+
/// in the corresponding state once the computation terminates (produces the result, throws exception or gets canceled)</returns>
430+
/// </returns>
431+
static member StartImmediateAsTask:
432+
computation:Async<'T> * ?cancellationToken:CancellationToken-> Task<'T>
433+
434+
418435

419436
[<CompiledName("FSharpAsyncBuilder")>]
420437
[<Sealed>]

0 commit comments

Comments
 (0)