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) { } } } }