Skip to content

Instantly share code, notes, and snippets.

@atifaziz
Last active May 29, 2021 14:12
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atifaziz/0d97f72fe2c5d07861a3 to your computer and use it in GitHub Desktop.
Save atifaziz/0d97f72fe2c5d07861a3 to your computer and use it in GitHub Desktop.
Implementation of Task.WhenAll in C# and F#
// 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;
}
}
// 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