Last active
May 29, 2021 14:12
-
-
Save atifaziz/0d97f72fe2c5d07861a3 to your computer and use it in GitHub Desktop.
Implementation of Task.WhenAll in C# and F#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.whenall.aspx | |
using System; | |
using System.Threading; | |
using System.Threading.Tasks; | |
static class TaskExtensions | |
{ | |
public static Task<T[]> WhenAll<T>(this Task<T>[] tasks) | |
{ | |
return WhenAll(tasks, CancellationToken.None); | |
} | |
public static Task<T[]> WhenAll<T>(this Task<T>[] tasks, CancellationToken cancellationToken) | |
{ | |
if (tasks == null) throw new ArgumentNullException("tasks"); | |
var tcs = new TaskCompletionSource<T[]>(); | |
cancellationToken.Register(() => tcs.TrySetCanceled()); | |
var results = new T[tasks.Length]; | |
var pending = results.Length; | |
for (var i = 0; i < tasks.Length; i++) | |
{ | |
var ti = i; | |
tasks[i].ContinueWith(t => | |
{ | |
if (t.IsCanceled) | |
tcs.TrySetCanceled(); | |
else if (t.IsFaulted) | |
tcs.TrySetException(t.Exception); | |
else | |
{ | |
results[ti] = t.Result; | |
if (0 == Interlocked.Decrement(ref pending)) | |
tcs.SetResult(results); | |
} | |
}, cancellationToken, | |
TaskContinuationOptions.ExecuteSynchronously, | |
TaskScheduler.Default); | |
} | |
return tcs.Task; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.whenall.aspx | |
open System | |
open System.Threading | |
open System.Threading.Tasks | |
let (|Canceled|Faulted|Completed|) (t : Task<'a>) = | |
if t.IsCanceled then Canceled | |
else if t.IsFaulted then Faulted(t.Exception) | |
else Completed(t.Result) | |
type Tasks = | |
static member WhenAll(tasks : Task<'a>[], ?cancellationToken : CancellationToken) = | |
let tcs = TaskCompletionSource<'a[]>() | |
let cancellationToken = defaultArg cancellationToken CancellationToken.None | |
cancellationToken.Register((fun () -> tcs.TrySetCanceled() |> ignore)) |> ignore | |
let results = Array.zeroCreate<'a>(tasks.Length) | |
let pending = ref results.Length | |
tasks | |
|> Seq.iteri (fun i t -> | |
let continuation = function | |
| Canceled -> tcs.TrySetCanceled() |> ignore | |
| Faulted(e) -> tcs.TrySetException(e) |> ignore | |
| Completed(r) -> | |
results.[i] <- r | |
if Interlocked.Decrement(pending) = 0 then | |
tcs.SetResult(results) | |
t.ContinueWith(continuation, cancellationToken, | |
TaskContinuationOptions.ExecuteSynchronously, | |
TaskScheduler.Default) |> ignore) | |
tcs.Task |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment