using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
namespace ZeroLevel.Services.Async
{
///
/// Provides extension methods for .
///
public static class TaskCompletionSourceExtensions
{
///
/// Attempts to complete a , propagating the completion of .
///
/// The type of the result of the target asynchronous operation.
/// The type of the result of the source asynchronous operation.
/// The task completion source. May not be null.
/// The task. May not be null.
/// true if this method completed the task completion source; false if it was already completed.
public static bool TryCompleteFromCompletedTask(this TaskCompletionSource @this, Task task) where TSourceResult : TResult
{
if (@this == null)
throw new ArgumentNullException(nameof(@this));
if (task == null)
throw new ArgumentNullException(nameof(task));
if (task.IsFaulted)
return @this.TrySetException(task.Exception.InnerExceptions);
if (task.IsCanceled)
{
try
{
task.WaitAndUnwrapException();
}
catch (OperationCanceledException exception)
{
var token = exception.CancellationToken;
return token.IsCancellationRequested ? @this.TrySetCanceled(token) : @this.TrySetCanceled();
}
}
return @this.TrySetResult(task.Result);
}
///
/// Attempts to complete a , propagating the completion of but using the result value from if the task completed successfully.
///
/// The type of the result of the target asynchronous operation.
/// The task completion source. May not be null.
/// The task. May not be null.
/// A delegate that returns the result with which to complete the task completion source, if the task completed successfully. May not be null.
/// true if this method completed the task completion source; false if it was already completed.
public static bool TryCompleteFromCompletedTask(this TaskCompletionSource @this, Task task, Func resultFunc)
{
if (@this == null)
throw new ArgumentNullException(nameof(@this));
if (task == null)
throw new ArgumentNullException(nameof(task));
if (resultFunc == null)
throw new ArgumentNullException(nameof(resultFunc));
if (task.IsFaulted)
return @this.TrySetException(task.Exception.InnerExceptions);
if (task.IsCanceled)
{
try
{
task.WaitAndUnwrapException();
}
catch (OperationCanceledException exception)
{
var token = exception.CancellationToken;
return token.IsCancellationRequested ? @this.TrySetCanceled(token) : @this.TrySetCanceled();
}
}
return @this.TrySetResult(resultFunc());
}
///
/// Attempts to complete a , propagating the completion of .
///
/// The type of the result of the asynchronous operation.
/// The task completion source. May not be null.
/// The event arguments passed to the completion event. May not be null.
/// The delegate used to retrieve the result. This is only invoked if indicates successful completion. May not be null.
/// true if this method completed the task completion source; false if it was already completed.
public static bool TryCompleteFromEventArgs(this TaskCompletionSource @this, AsyncCompletedEventArgs eventArgs, Func getResult)
{
if (eventArgs.Cancelled)
return @this.TrySetCanceled();
if (eventArgs.Error != null)
return @this.TrySetException(eventArgs.Error);
return @this.TrySetResult(getResult());
}
///
/// Attempts to complete a , propagating the completion of .
///
/// The task completion source. May not be null.
/// The task. May not be null.
/// true if this method completed the task completion source; false if it was already completed.
public static bool TryCompleteFromCompletedTask(this TaskCompletionSource @this, Task task)
{
if (task.IsFaulted)
return @this.TrySetException(task.Exception.InnerExceptions);
if (task.IsCanceled)
return @this.TrySetCanceled();
return @this.TrySetResult();
}
///
/// Attempts to complete a , propagating the completion of .
///
/// The task completion source. May not be null.
/// The event arguments passed to the completion event. May not be null.
/// true if this method completed the task completion source; false if it was already completed.
public static bool TryCompleteFromEventArgs(this TaskCompletionSource @this, AsyncCompletedEventArgs eventArgs)
{
if (eventArgs.Cancelled)
return @this.TrySetCanceled();
if (eventArgs.Error != null)
return @this.TrySetException(eventArgs.Error);
return @this.TrySetResult();
}
///
/// Attempts to complete a with the specified value, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
///
/// The type of the result of the asynchronous operation.
/// The task completion source. May not be null.
/// The result of the asynchronous operation.
public static void TrySetResultWithBackgroundContinuations(this TaskCompletionSource @this, TResult result)
{
// Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
TaskShim.Run(() => @this.TrySetResult(result));
// Wait for the TCS task to complete; note that the continuations may not be complete.
@this.Task.Wait();
}
///
/// Attempts to complete a , forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
///
/// The task completion source. May not be null.
public static void TrySetResultWithBackgroundContinuations(this TaskCompletionSource @this)
{
// Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
TaskShim.Run(() => @this.TrySetResult());
// Wait for the TCS task to complete; note that the continuations may not be complete.
@this.Task.Wait();
}
///
/// Attempts to complete a as canceled, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
///
/// The type of the result of the asynchronous operation.
/// The task completion source. May not be null.
public static void TrySetCanceledWithBackgroundContinuations(this TaskCompletionSource @this)
{
// Complete on a threadpool thread, so any synchronous continuations will execute in the background.
TaskShim.Run(() => @this.TrySetCanceled());
// Wait for the TCS task to complete; note that the continuations may not be complete.
try
{
@this.Task.Wait();
}
catch (AggregateException)
{
}
}
///
/// Creates a new TCS for use with async code, and which forces its continuations to execute asynchronously.
///
/// The type of the result of the TCS.
public static TaskCompletionSource CreateAsyncTaskSource()
{
return new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
}
///
/// Attempts to complete a as canceled, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
///
/// The task completion source. May not be null.
public static void TrySetCanceledWithBackgroundContinuations(this TaskCompletionSource @this)
{
// Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
TaskShim.Run(() => @this.TrySetCanceled());
// Wait for the TCS task to complete; note that the continuations may not be complete.
try
{
@this.Task.Wait();
}
catch (AggregateException)
{
}
}
///
/// Attempts to complete a as faulted, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
///
/// The type of the result of the asynchronous operation.
/// The task completion source. May not be null.
/// The exception to bind to the task.
public static void TrySetExceptionWithBackgroundContinuations(this TaskCompletionSource @this, Exception exception)
{
// Complete on a threadpool thread, so any synchronous continuations will execute in the background.
TaskShim.Run(() => @this.TrySetException(exception));
// Wait for the TCS task to complete; note that the continuations may not be complete.
try
{
@this.Task.Wait();
}
catch (AggregateException)
{
}
}
///
/// Attempts to complete a as faulted, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
///
/// The task completion source. May not be null.
/// The exception to bind to the task.
public static void TrySetExceptionWithBackgroundContinuations(this TaskCompletionSource @this, Exception exception)
{
// Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
TaskShim.Run(() => @this.TrySetException(exception));
// Wait for the TCS task to complete; note that the continuations may not be complete.
try
{
@this.Task.Wait();
}
catch (AggregateException)
{
}
}
///
/// Attempts to complete a as faulted, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
///
/// The type of the result of the asynchronous operation.
/// The task completion source. May not be null.
/// The exceptions to bind to the task.
public static void TrySetExceptionWithBackgroundContinuations(this TaskCompletionSource @this, IEnumerable exceptions)
{
// Complete on a threadpool thread, so any synchronous continuations will execute in the background.
TaskShim.Run(() => @this.TrySetException(exceptions));
// Wait for the TCS task to complete; note that the continuations may not be complete.
try
{
@this.Task.Wait();
}
catch (AggregateException)
{
}
}
///
/// Attempts to complete a as faulted, forcing all continuations onto a threadpool thread even if they specified ExecuteSynchronously.
///
/// The task completion source. May not be null.
/// The exceptions to bind to the task.
public static void TrySetExceptionWithBackgroundContinuations(this TaskCompletionSource @this, IEnumerable exceptions)
{
// Set the result on a threadpool thread, so any synchronous continuations will execute in the background.
TaskShim.Run(() => @this.TrySetException(exceptions));
// Wait for the TCS task to complete; note that the continuations may not be complete.
try
{
@this.Task.Wait();
}
catch (AggregateException)
{
}
}
}
}