using System; using System.Threading; using System.Threading.Tasks; namespace ZeroLevel.Services.Async { /// /// Provides synchronous extension methods for tasks. /// public static class TaskExtensions { /// /// Asynchronously waits for the task to complete, or for the cancellation token to be canceled. /// /// The task to wait for. May not be null. /// The cancellation token that cancels the wait. public static Task WaitAsync(this Task @this, CancellationToken cancellationToken) { if (@this == null) throw new ArgumentNullException(nameof(@this)); if (!cancellationToken.CanBeCanceled) return @this; if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); return DoWaitAsync(@this, cancellationToken); } private static async Task DoWaitAsync(Task task, CancellationToken cancellationToken) { using (var cancelTaskSource = new CancellationTokenTaskSource(cancellationToken)) await await Task.WhenAny(task, cancelTaskSource.Task).ConfigureAwait(false); } /// /// Asynchronously waits for the task to complete, or for the cancellation token to be canceled. /// /// The type of the task result. /// The task to wait for. May not be null. /// The cancellation token that cancels the wait. public static Task WaitAsync(this Task @this, CancellationToken cancellationToken) { if (@this == null) throw new ArgumentNullException(nameof(@this)); if (!cancellationToken.CanBeCanceled) return @this; if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken); return DoWaitAsync(@this, cancellationToken); } private static async Task DoWaitAsync(Task task, CancellationToken cancellationToken) { using (var cancelTaskSource = new CancellationTokenTaskSource(cancellationToken)) return await await Task.WhenAny(task, cancelTaskSource.Task).ConfigureAwait(false); } /// /// Waits for the task to complete, unwrapping any exceptions. /// /// The task. May not be null. public static void WaitAndUnwrapException(this Task task) { task.GetAwaiter().GetResult(); } /// /// Waits for the task to complete, unwrapping any exceptions. /// /// The task. May not be null. /// A cancellation token to observe while waiting for the task to complete. /// The was cancelled before the completed, or the raised an . public static void WaitAndUnwrapException(this Task task, CancellationToken cancellationToken) { try { task.Wait(cancellationToken); } catch (AggregateException ex) { throw ExceptionHelpers.PrepareForRethrow(ex.InnerException); } } /// /// Waits for the task to complete, unwrapping any exceptions. /// /// The type of the result of the task. /// The task. May not be null. /// The result of the task. public static TResult WaitAndUnwrapException(this Task task) { return task.GetAwaiter().GetResult(); } /// /// Waits for the task to complete, unwrapping any exceptions. /// /// The type of the result of the task. /// The task. May not be null. /// A cancellation token to observe while waiting for the task to complete. /// The result of the task. /// The was cancelled before the completed, or the raised an . public static TResult WaitAndUnwrapException(this Task task, CancellationToken cancellationToken) { try { task.Wait(cancellationToken); return task.Result; } catch (AggregateException ex) { throw ExceptionHelpers.PrepareForRethrow(ex.InnerException); } } /// /// Waits for the task to complete, but does not raise task exceptions. The task exception (if any) is unobserved. /// /// The task. May not be null. public static void WaitWithoutException(this Task task) { // Check to see if it's completed first, so we don't cause unnecessary allocation of a WaitHandle. if (task.IsCompleted) { return; } var asyncResult = (IAsyncResult)task; asyncResult.AsyncWaitHandle.WaitOne(); } /// /// Waits for the task to complete, but does not raise task exceptions. The task exception (if any) is unobserved. /// /// The task. May not be null. /// A cancellation token to observe while waiting for the task to complete. /// The was cancelled before the completed. public static void WaitWithoutException(this Task task, CancellationToken cancellationToken) { // Check to see if it's completed first, so we don't cause unnecessary allocation of a WaitHandle. if (task.IsCompleted) { return; } cancellationToken.ThrowIfCancellationRequested(); var index = WaitHandle.WaitAny(new[] { ((IAsyncResult)task).AsyncWaitHandle, cancellationToken.WaitHandle }); if (index != 0) { cancellationToken.ThrowIfCancellationRequested(); } } } }