/ParallelExtensionsExtras/Extensions/TaskExtrasExtensions.cs

# · C# · 224 lines · 129 code · 24 blank · 71 comment · 20 complexity · ad311c1fddf9da956307207731ce044a MD5 · raw file

  1. //--------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. //
  5. // File: TaskExtensions.cs
  6. //
  7. //--------------------------------------------------------------------------
  8. using System.Linq;
  9. namespace System.Threading.Tasks
  10. {
  11. /// <summary>Extensions methods for Task.</summary>
  12. public static class TaskExtrasExtensions
  13. {
  14. #region ContinueWith accepting TaskFactory
  15. /// <summary>Creates a continuation task using the specified TaskFactory.</summary>
  16. /// <param name="task">The antecedent Task.</param>
  17. /// <param name="continuationAction">The continuation action.</param>
  18. /// <param name="factory">The TaskFactory.</param>
  19. /// <returns>A continuation task.</returns>
  20. public static Task ContinueWith(
  21. this Task task, Action<Task> continuationAction, TaskFactory factory)
  22. {
  23. return task.ContinueWith(continuationAction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler);
  24. }
  25. /// <summary>Creates a continuation task using the specified TaskFactory.</summary>
  26. /// <param name="task">The antecedent Task.</param>
  27. /// <param name="continuationFunction">The continuation function.</param>
  28. /// <param name="factory">The TaskFactory.</param>
  29. /// <returns>A continuation task.</returns>
  30. public static Task<TResult> ContinueWith<TResult>(
  31. this Task task, Func<Task, TResult> continuationFunction, TaskFactory factory)
  32. {
  33. return task.ContinueWith(continuationFunction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler);
  34. }
  35. #endregion
  36. #region ContinueWith accepting TaskFactory<TResult>
  37. /// <summary>Creates a continuation task using the specified TaskFactory.</summary>
  38. /// <param name="task">The antecedent Task.</param>
  39. /// <param name="continuationAction">The continuation action.</param>
  40. /// <param name="factory">The TaskFactory.</param>
  41. /// <returns>A continuation task.</returns>
  42. public static Task ContinueWith<TResult>(
  43. this Task<TResult> task, Action<Task<TResult>> continuationAction, TaskFactory<TResult> factory)
  44. {
  45. return task.ContinueWith(continuationAction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler);
  46. }
  47. /// <summary>Creates a continuation task using the specified TaskFactory.</summary>
  48. /// <param name="task">The antecedent Task.</param>
  49. /// <param name="continuationFunction">The continuation function.</param>
  50. /// <param name="factory">The TaskFactory.</param>
  51. /// <returns>A continuation task.</returns>
  52. public static Task<TNewResult> ContinueWith<TResult, TNewResult>(
  53. this Task<TResult> task, Func<Task<TResult>, TNewResult> continuationFunction, TaskFactory<TResult> factory)
  54. {
  55. return task.ContinueWith(continuationFunction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler);
  56. }
  57. #endregion
  58. #region WithAsyncCallback(AsyncCallback, object)
  59. /// <summary>
  60. /// Creates a Task that represents the completion of another Task, and
  61. /// that schedules an AsyncCallback to run upon completion.
  62. /// </summary>
  63. /// <param name="task">The antecedent Task.</param>
  64. /// <param name="callback">The AsyncCallback to run.</param>
  65. /// <param name="state">The object state to use with the AsyncCallback.</param>
  66. /// <returns>The new task.</returns>
  67. public static Task WithAsyncCallback(this Task task, AsyncCallback callback, object state)
  68. {
  69. if (task == null) throw new ArgumentNullException("task");
  70. var tcs = new TaskCompletionSource<object>(state);
  71. task.ContinueWith(_ =>
  72. {
  73. tcs.SetFromTask(task);
  74. if (callback != null) callback(tcs.Task);
  75. });
  76. return tcs.Task;
  77. }
  78. /// <summary>
  79. /// Creates a Task that represents the completion of another Task, and
  80. /// that schedules an AsyncCallback to run upon completion.
  81. /// </summary>
  82. /// <param name="task">The antecedent Task.</param>
  83. /// <param name="callback">The AsyncCallback to run.</param>
  84. /// <param name="state">The object state to use with the AsyncCallback.</param>
  85. /// <returns>The new task.</returns>
  86. public static Task<TResult> WithAsyncCallback<TResult>(this Task<TResult> task, AsyncCallback callback, object state)
  87. {
  88. if (task == null) throw new ArgumentNullException("task");
  89. var tcs = new TaskCompletionSource<TResult>(state);
  90. task.ContinueWith(_ =>
  91. {
  92. tcs.SetFromTask(task);
  93. if (callback != null) callback(tcs.Task);
  94. });
  95. return tcs.Task;
  96. }
  97. #endregion
  98. #region Exception Handling
  99. /// <summary>Suppresses default exception handling of a Task that would otherwise reraise the exception on the finalizer thread.</summary>
  100. /// <param name="task">The Task to be monitored.</param>
  101. /// <returns>The original Task.</returns>
  102. public static Task IgnoreExceptions(this Task task)
  103. {
  104. task.ContinueWith(t => { var ignored = t.Exception; },
  105. TaskContinuationOptions.ExecuteSynchronously |
  106. TaskContinuationOptions.OnlyOnFaulted);
  107. return task;
  108. }
  109. /// <summary>Suppresses default exception handling of a Task that would otherwise reraise the exception on the finalizer thread.</summary>
  110. /// <param name="task">The Task to be monitored.</param>
  111. /// <returns>The original Task.</returns>
  112. public static Task<T> IgnoreExceptions<T>(this Task<T> task)
  113. {
  114. return (Task<T>)((Task)task).IgnoreExceptions();
  115. }
  116. /// <summary>Fails immediately when an exception is encountered.</summary>
  117. /// <param name="task">The Task to be monitored.</param>
  118. /// <returns>The original Task.</returns>
  119. public static Task FailFastOnException(this Task task)
  120. {
  121. task.ContinueWith(t => Environment.FailFast("A task faulted.", t.Exception),
  122. TaskContinuationOptions.ExecuteSynchronously |
  123. TaskContinuationOptions.OnlyOnFaulted);
  124. return task;
  125. }
  126. /// <summary>Fails immediately when an exception is encountered.</summary>
  127. /// <param name="task">The Task to be monitored.</param>
  128. /// <returns>The original Task.</returns>
  129. public static Task<T> FailFastOnException<T>(this Task<T> task)
  130. {
  131. return (Task<T>)((Task)task).FailFastOnException();
  132. }
  133. /// <summary>Propagates any exceptions that occurred on the specified task.</summary>
  134. /// <param name="task">The Task whose exceptions are to be propagated.</param>
  135. public static void PropagateExceptions(this Task task)
  136. {
  137. if (!task.IsCompleted) throw new InvalidOperationException("The task has not completed.");
  138. if (task.IsFaulted) task.Wait();
  139. }
  140. /// <summary>Propagates any exceptions that occurred on the specified tasks.</summary>
  141. /// <param name="task">The Tassk whose exceptions are to be propagated.</param>
  142. public static void PropagateExceptions(this Task [] tasks)
  143. {
  144. if (tasks == null) throw new ArgumentNullException("tasks");
  145. if (tasks.Any(t => t == null)) throw new ArgumentException("tasks");
  146. if (tasks.Any(t => !t.IsCompleted)) throw new InvalidOperationException("A task has not completed.");
  147. Task.WaitAll(tasks);
  148. }
  149. #endregion
  150. #region Observables
  151. /// <summary>Creates an IObservable that represents the completion of a Task.</summary>
  152. /// <typeparam name="TResult">Specifies the type of data returned by the Task.</typeparam>
  153. /// <param name="task">The Task to be represented as an IObservable.</param>
  154. /// <returns>An IObservable that represents the completion of the Task.</returns>
  155. public static IObservable<TResult> ToObservable<TResult>(this Task<TResult> task)
  156. {
  157. if (task == null) throw new ArgumentNullException("task");
  158. return new TaskObservable<TResult> { _task = task };
  159. }
  160. /// <summary>An implementation of IObservable that wraps a Task.</summary>
  161. /// <typeparam name="TResult">The type of data returned by the task.</typeparam>
  162. private class TaskObservable<TResult> : IObservable<TResult>
  163. {
  164. internal Task<TResult> _task;
  165. public IDisposable Subscribe(IObserver<TResult> observer)
  166. {
  167. // Validate arguments
  168. if (observer == null) throw new ArgumentNullException("observer");
  169. // Support cancelling the continuation if the observer is unsubscribed
  170. var cts = new CancellationTokenSource();
  171. // Create a continuation to pass data along to the observer
  172. _task.ContinueWith(t =>
  173. {
  174. switch (t.Status)
  175. {
  176. case TaskStatus.RanToCompletion:
  177. observer.OnNext(_task.Result);
  178. observer.OnCompleted();
  179. break;
  180. case TaskStatus.Faulted:
  181. observer.OnError(_task.Exception);
  182. break;
  183. case TaskStatus.Canceled:
  184. observer.OnError(new OperationCanceledException());
  185. break;
  186. }
  187. }, cts.Token);
  188. // Support unsubscribe simply by canceling the continuation if it hasn't yet run
  189. return new CancelOnDispose { Source = cts };
  190. }
  191. }
  192. /// <summary>Translate a call to IDisposable.Dispose to a CancellationTokenSource.Cancel.</summary>
  193. private class CancelOnDispose : IDisposable
  194. {
  195. internal CancellationTokenSource Source;
  196. void IDisposable.Dispose() { Source.Cancel(); }
  197. }
  198. #endregion
  199. }
  200. }