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();
}
}
}
}