1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 6 // 7 // 8 // 9 // TaskCompletionSource<TResult> is the producer end of an unbound future. Its 10 // Task member may be distributed as the consumer end of the future. 11 // 12 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 13 14 using System; 15 using System.Diagnostics; 16 using System.Collections.Generic; 17 using System.Runtime.CompilerServices; 18 using System.Runtime.ExceptionServices; 19 using System.Threading; 20 21 // Disable the "reference to volatile field not treated as volatile" error. 22 #pragma warning disable 0420 23 24 namespace System.Threading.Tasks 25 { 26 /// <summary> 27 /// Represents the producer side of a <see cref="T:System.Threading.Tasks.Task{TResult}"/> unbound to a 28 /// delegate, providing access to the consumer side through the <see cref="Task"/> property. 29 /// </summary> 30 /// <remarks> 31 /// <para> 32 /// It is often the case that a <see cref="T:System.Threading.Tasks.Task{TResult}"/> is desired to 33 /// represent another asynchronous operation. 34 /// <see cref="TaskCompletionSource{TResult}">TaskCompletionSource</see> is provided for this purpose. It enables 35 /// the creation of a task that can be handed out to consumers, and those consumers can use the members 36 /// of the task as they would any other. However, unlike most tasks, the state of a task created by a 37 /// TaskCompletionSource is controlled explicitly by the methods on TaskCompletionSource. This enables the 38 /// completion of the external asynchronous operation to be propagated to the underlying Task. The 39 /// separation also ensures that consumers are not able to transition the state without access to the 40 /// corresponding TaskCompletionSource. 41 /// </para> 42 /// <para> 43 /// All members of <see cref="TaskCompletionSource{TResult}"/> are thread-safe 44 /// and may be used from multiple threads concurrently. 45 /// </para> 46 /// </remarks> 47 /// <typeparam name="TResult">The type of the result value associated with this <see 48 /// cref="TaskCompletionSource{TResult}"/>.</typeparam> 49 public class TaskCompletionSource<TResult> 50 { 51 private readonly Task<TResult> _task; 52 53 /// <summary> 54 /// Creates a <see cref="TaskCompletionSource{TResult}"/>. 55 /// </summary> TaskCompletionSource()56 public TaskCompletionSource() 57 { 58 _task = new Task<TResult>(); 59 } 60 61 /// <summary> 62 /// Creates a <see cref="TaskCompletionSource{TResult}"/> 63 /// with the specified options. 64 /// </summary> 65 /// <remarks> 66 /// The <see cref="T:System.Threading.Tasks.Task{TResult}"/> created 67 /// by this instance and accessible through its <see cref="Task"/> property 68 /// will be instantiated using the specified <paramref name="creationOptions"/>. 69 /// </remarks> 70 /// <param name="creationOptions">The options to use when creating the underlying 71 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> 72 /// <exception cref="T:System.ArgumentOutOfRangeException"> 73 /// The <paramref name="creationOptions"/> represent options invalid for use 74 /// with a <see cref="TaskCompletionSource{TResult}"/>. 75 /// </exception> TaskCompletionSource(TaskCreationOptions creationOptions)76 public TaskCompletionSource(TaskCreationOptions creationOptions) 77 : this(null, creationOptions) 78 { 79 } 80 81 /// <summary> 82 /// Creates a <see cref="TaskCompletionSource{TResult}"/> 83 /// with the specified state. 84 /// </summary> 85 /// <param name="state">The state to use as the underlying 86 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>'s AsyncState.</param> TaskCompletionSource(object state)87 public TaskCompletionSource(object state) 88 : this(state, TaskCreationOptions.None) 89 { 90 } 91 92 /// <summary> 93 /// Creates a <see cref="TaskCompletionSource{TResult}"/> with 94 /// the specified state and options. 95 /// </summary> 96 /// <param name="creationOptions">The options to use when creating the underlying 97 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> 98 /// <param name="state">The state to use as the underlying 99 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>'s AsyncState.</param> 100 /// <exception cref="T:System.ArgumentOutOfRangeException"> 101 /// The <paramref name="creationOptions"/> represent options invalid for use 102 /// with a <see cref="TaskCompletionSource{TResult}"/>. 103 /// </exception> TaskCompletionSource(object state, TaskCreationOptions creationOptions)104 public TaskCompletionSource(object state, TaskCreationOptions creationOptions) 105 { 106 _task = new Task<TResult>(state, creationOptions); 107 } 108 109 110 /// <summary> 111 /// Gets the <see cref="T:System.Threading.Tasks.Task{TResult}"/> created 112 /// by this <see cref="TaskCompletionSource{TResult}"/>. 113 /// </summary> 114 /// <remarks> 115 /// This property enables a consumer access to the <see 116 /// cref="T:System.Threading.Tasks.Task{TResult}"/> that is controlled by this instance. 117 /// The <see cref="SetResult"/>, <see cref="SetException(System.Exception)"/>, 118 /// <see cref="SetException(System.Collections.Generic.IEnumerable{System.Exception})"/>, and <see cref="SetCanceled"/> 119 /// methods (and their "Try" variants) on this instance all result in the relevant state 120 /// transitions on this underlying Task. 121 /// </remarks> 122 public Task<TResult> Task => _task; 123 124 /// <summary>Spins until the underlying task is completed.</summary> 125 /// <remarks>This should only be called if the task is in the process of being completed by another thread.</remarks> SpinUntilCompleted()126 private void SpinUntilCompleted() 127 { 128 // Spin wait until the completion is finalized by another thread. 129 var sw = new SpinWait(); 130 while (!_task.IsCompleted) 131 sw.SpinOnce(); 132 } 133 134 /// <summary> 135 /// Attempts to transition the underlying 136 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the 137 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see> 138 /// state. 139 /// </summary> 140 /// <param name="exception">The exception to bind to this <see 141 /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> 142 /// <returns>True if the operation was successful; otherwise, false.</returns> 143 /// <remarks>This operation will return false if the 144 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one 145 /// of the three final states: 146 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>, 147 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or 148 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>. 149 /// </remarks> 150 /// <exception cref="T:System.ArgumentNullException">The <paramref name="exception"/> argument is null.</exception> 151 /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> TrySetException(Exception exception)152 public bool TrySetException(Exception exception) 153 { 154 if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); 155 156 bool rval = _task.TrySetException(exception); 157 if (!rval && !_task.IsCompleted) SpinUntilCompleted(); 158 return rval; 159 } 160 161 /// <summary> 162 /// Attempts to transition the underlying 163 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the 164 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see> 165 /// state. 166 /// </summary> 167 /// <param name="exceptions">The collection of exceptions to bind to this <see 168 /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> 169 /// <returns>True if the operation was successful; otherwise, false.</returns> 170 /// <remarks>This operation will return false if the 171 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one 172 /// of the three final states: 173 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>, 174 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or 175 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>. 176 /// </remarks> 177 /// <exception cref="T:System.ArgumentNullException">The <paramref name="exceptions"/> argument is null.</exception> 178 /// <exception cref="T:System.ArgumentException">There are one or more null elements in <paramref name="exceptions"/>.</exception> 179 /// <exception cref="T:System.ArgumentException">The <paramref name="exceptions"/> collection is empty.</exception> 180 /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> TrySetException(IEnumerable<Exception> exceptions)181 public bool TrySetException(IEnumerable<Exception> exceptions) 182 { 183 if (exceptions == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exceptions); 184 185 List<Exception> defensiveCopy = new List<Exception>(); 186 foreach (Exception e in exceptions) 187 { 188 if (e == null) 189 ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NullException, ExceptionArgument.exceptions); 190 defensiveCopy.Add(e); 191 } 192 193 if (defensiveCopy.Count == 0) 194 ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NoExceptions, ExceptionArgument.exceptions); 195 196 bool rval = _task.TrySetException(defensiveCopy); 197 if (!rval && !_task.IsCompleted) SpinUntilCompleted(); 198 return rval; 199 } 200 201 /// <summary> 202 /// Transitions the underlying 203 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the 204 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see> 205 /// state. 206 /// </summary> 207 /// <param name="exception">The exception to bind to this <see 208 /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> 209 /// <exception cref="T:System.ArgumentNullException">The <paramref name="exception"/> argument is null.</exception> 210 /// <exception cref="T:System.InvalidOperationException"> 211 /// The underlying <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one 212 /// of the three final states: 213 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>, 214 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or 215 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>. 216 /// </exception> 217 /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> SetException(Exception exception)218 public void SetException(Exception exception) 219 { 220 if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); 221 222 if (!TrySetException(exception)) 223 { 224 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); 225 } 226 } 227 228 /// <summary> 229 /// Transitions the underlying 230 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the 231 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see> 232 /// state. 233 /// </summary> 234 /// <param name="exceptions">The collection of exceptions to bind to this <see 235 /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> 236 /// <exception cref="T:System.ArgumentNullException">The <paramref name="exceptions"/> argument is null.</exception> 237 /// <exception cref="T:System.ArgumentException">There are one or more null elements in <paramref name="exceptions"/>.</exception> 238 /// <exception cref="T:System.InvalidOperationException"> 239 /// The underlying <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one 240 /// of the three final states: 241 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>, 242 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or 243 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>. 244 /// </exception> 245 /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> SetException(IEnumerable<Exception> exceptions)246 public void SetException(IEnumerable<Exception> exceptions) 247 { 248 if (!TrySetException(exceptions)) 249 { 250 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); 251 } 252 } 253 254 255 /// <summary> 256 /// Attempts to transition the underlying 257 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the 258 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see> 259 /// state. 260 /// </summary> 261 /// <param name="result">The result value to bind to this <see 262 /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> 263 /// <returns>True if the operation was successful; otherwise, false.</returns> 264 /// <remarks>This operation will return false if the 265 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one 266 /// of the three final states: 267 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>, 268 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or 269 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>. 270 /// </remarks> 271 /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> TrySetResult(TResult result)272 public bool TrySetResult(TResult result) 273 { 274 bool rval = _task.TrySetResult(result); 275 if (!rval) SpinUntilCompleted(); 276 return rval; 277 } 278 279 /// <summary> 280 /// Transitions the underlying 281 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the 282 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see> 283 /// state. 284 /// </summary> 285 /// <param name="result">The result value to bind to this <see 286 /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> 287 /// <exception cref="T:System.InvalidOperationException"> 288 /// The underlying <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one 289 /// of the three final states: 290 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>, 291 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or 292 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>. 293 /// </exception> 294 /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> SetResult(TResult result)295 public void SetResult(TResult result) 296 { 297 if (!TrySetResult(result)) 298 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); 299 } 300 301 /// <summary> 302 /// Attempts to transition the underlying 303 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the 304 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see> 305 /// state. 306 /// </summary> 307 /// <returns>True if the operation was successful; otherwise, false.</returns> 308 /// <remarks>This operation will return false if the 309 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one 310 /// of the three final states: 311 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>, 312 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or 313 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>. 314 /// </remarks> 315 /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> TrySetCanceled()316 public bool TrySetCanceled() 317 { 318 return TrySetCanceled(default(CancellationToken)); 319 } 320 321 // Enables a token to be stored into the canceled task TrySetCanceled(CancellationToken cancellationToken)322 public bool TrySetCanceled(CancellationToken cancellationToken) 323 { 324 bool rval = _task.TrySetCanceled(cancellationToken); 325 if (!rval && !_task.IsCompleted) SpinUntilCompleted(); 326 return rval; 327 } 328 329 /// <summary> 330 /// Transitions the underlying 331 /// <see cref="T:System.Threading.Tasks.Task{TResult}"/> into the 332 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see> 333 /// state. 334 /// </summary> 335 /// <exception cref="T:System.InvalidOperationException"> 336 /// The underlying <see cref="T:System.Threading.Tasks.Task{TResult}"/> is already in one 337 /// of the three final states: 338 /// <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>, 339 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or 340 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>. 341 /// </exception> 342 /// <exception cref="T:System.ObjectDisposedException">The <see cref="Task"/> was disposed.</exception> SetCanceled()343 public void SetCanceled() 344 { 345 if (!TrySetCanceled()) 346 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); 347 } 348 } 349 } 350