// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; namespace System.Threading.Tasks { static class TaskExt { public static readonly Task True; public static readonly Task False; static TaskExt() { True = Return(true); False = Return(false); } public static Task Return(T value) { var tcs = new TaskCompletionSource(); tcs.TrySetResult(value); return tcs.Task; } public static Task Throw(Exception exception) { var tcs = new TaskCompletionSource(); tcs.TrySetException(exception); return tcs.Task; } public static void Handle(this Task task, TaskCompletionSource tcs, Action success) { if (task.IsFaulted) tcs.TrySetException(task.Exception.InnerExceptions); else if (task.IsCanceled) tcs.TrySetCanceled(); else if (task.IsCompleted) success(task.Result); } public static void Handle(this Task task, TaskCompletionSource tcs, Action success, Action error) { if (task.IsFaulted) error(task.Exception); else if (task.IsCanceled) tcs.TrySetCanceled(); else if (task.IsCompleted) success(task.Result); } public static void Handle(this Task task, TaskCompletionSource tcs, Action success, Action error, Action canceled) { if (task.IsFaulted) error(task.Exception); else if (task.IsCanceled) canceled(); else if (task.IsCompleted) success(task.Result); } public static Task Then(this Task task, Action> continuation) { // // Central location to deal with continuations; allows for experimentation with flags. // Note that right now, we don't go for synchronous execution. Users can block on the // task returned from MoveNext, which can cause deadlocks (e.g. typical uses of GroupBy // involve some aggregate). We'd need deeper asynchrony to make this work with less // spawning of tasks. // return task.ContinueWith(continuation); } public static Task Then(this Task task, Func, R> continuation) { // // See comment on Then for rationale. // return task.ContinueWith(continuation); } public static Task UsingEnumerator(this Task task, IDisposable disposable) { return task.Finally(() => { if (task.IsFaulted || task.IsCanceled || !task.Result) disposable.Dispose(); }); } public static Task Finally(this Task task, Action action) { var tcs = new TaskCompletionSource(); task.ContinueWith(t => { try { action(); } finally { switch (t.Status) { case TaskStatus.Canceled: tcs.SetCanceled(); break; case TaskStatus.Faulted: tcs.SetException(t.Exception.InnerException); break; case TaskStatus.RanToCompletion: tcs.SetResult(t.Result); break; } } }, TaskContinuationOptions.ExecuteSynchronously); return tcs.Task; } public static Task Zip(this Task t1, Task t2, Func f) { var gate = new object(); var tcs = new TaskCompletionSource(); var i = 2; var complete = new Action(t => { if (Interlocked.Decrement(ref i) == 0) { var exs = new List(); if (t1.IsFaulted) exs.Add(t1.Exception); if (t2.IsFaulted) exs.Add(t2.Exception); if (exs.Count > 0) tcs.TrySetException(exs); else if (t1.IsCanceled || t2.IsCanceled) tcs.TrySetCanceled(); else { var res = default(V); try { res = f(t1.Result, t2.Result); } catch (Exception ex) { tcs.TrySetException(ex); return; } tcs.TrySetResult(res); } } }); t1.ContinueWith(complete); t2.ContinueWith(complete); return tcs.Task; } } }