src/common/Extensions/TaskUtilities.cs (82 lines of code) (raw):

// -------------------------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. // -------------------------------------------------------------------------------------------- namespace System.Threading.Tasks { internal static class TaskUtilities { /// <summary> /// Runs a task, but will immediately throw if cancellation is requested. /// Note that the Task will continue to run in the background even if cancellation is requested, hence the "Unsafe" name. /// </summary> /// <exception cref="OperationCanceledException"></exception> /// <param name="t"></param> /// <param name="token"></param> /// <returns></returns> public static Task RunWithUnsafeCancellationAsync(this Task t, CancellationToken token) { Func<Task<object>> func = async () => { await t; return null; }; return RunWithUnsafeCancellationAsync(func(), token); } /// <summary> /// Runs a task that returns a result, but will immediately throw if cancellation is requested. /// Note that the Task will continue to run in the background even if cancellation is requested, hence the "Unsafe" name. /// </summary> /// <exception cref="OperationCanceledException"></exception> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="token"></param> /// <returns></returns> public static async Task<T> RunWithUnsafeCancellationAsync<T>(this Task<T> t, CancellationToken token) { token.ThrowIfCancellationRequested(); TaskCompletionSource<object> source = new TaskCompletionSource<object>(); using (token.Register(() => source.SetResult(null))) { await Task.WhenAny(t, source.Task); token.ThrowIfCancellationRequested(); return t.Result; } } /// <summary> /// Schedules/returns another Task to run when, and only when, the given task runs to completion. Propagates exceptions. /// This overload is for async continuations that don't need the previous Task's Result. /// </summary> /// <param name="t"></param> /// <param name="next"></param> /// <returns></returns> public static Task WhenCompleteContinueWith(this Task t, Func<Task> next) { return t.ContinueWith(p => { if (p.Exception != null) { throw p.Exception; } return next(); }, TaskContinuationOptions.NotOnCanceled).Unwrap(); } /// <summary> /// Schedules/returns another Task to run when, and only when, the given task runs to completion. Propagates exceptions. /// This overload is for async continuations that use the previous Task's Result. /// </summary> /// <param name="t"></param> /// <param name="next"></param> /// <returns></returns> public static Task WhenCompleteContinueWith<T>(this Task<T> t, Func<T, Task> next) { return t.ContinueWith(p => { if (p.Exception != null) { throw p.Exception; } return next(p.Result); }, TaskContinuationOptions.NotOnCanceled).Unwrap(); } /// <summary> /// Schedules/returns another Task to run when, and only when, the given task runs to completion. Propagates exceptions. /// This overload is for async continuations that don't need the previous Task's Result, but themselves return a result /// </summary> /// <param name="t"></param> /// <param name="next"></param> /// <returns></returns> public static Task<T> WhenCompleteContinueWith<T>(this Task t, Func<Task<T>> next) { return t.ContinueWith(p => { if (p.Exception != null) { throw p.Exception; } return next(); }, TaskContinuationOptions.NotOnCanceled).Unwrap(); } /// <summary> /// Schedules/returns another Task to run when, and only when, the given task runs to completion. Propagates exceptions. /// This overload is for synchronous continuations that don't need the previous Task's Result. /// </summary> /// <param name="t"></param> /// <param name="next"></param> /// <returns></returns> public static Task WhenCompleteContinueWith(this Task t, Action next) { return t.ContinueWith(p => { if (p.Exception != null) { throw p.Exception; } next(); }, TaskContinuationOptions.NotOnCanceled); } /// <summary> /// Schedules/returns another Task to run when, and only when, the given task runs to completion. Propagates exceptions. /// This overload is for synchronous continuations that use the previous Task's Result. /// </summary> /// <param name="t"></param> /// <param name="next"></param> /// <returns></returns> public static Task WhenCompleteContinueWith<T>(this Task<T> t, Action<T> next) { return t.ContinueWith(p => { if (p.Exception != null) { throw p.Exception; } next(p.Result); }, TaskContinuationOptions.NotOnCanceled); } } }