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.
5 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
9 // A schedulable unit of work.
11 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
13 using System.Collections.Generic;
14 using System.Collections.ObjectModel;
15 using System.Diagnostics;
16 using System.Diagnostics.Tracing;
17 using System.Runtime.CompilerServices;
18 using System.Runtime.ExceptionServices;
19 using Internal.Runtime.Augments;
21 // Disable the "reference to volatile field not treated as volatile" error.
22 #pragma warning disable 0420
24 namespace System.Threading.Tasks
27 /// Utility class for allocating structs as heap variables
29 internal class Shared<T>
33 internal Shared(T value)
40 /// Represents the current stage in the lifecycle of a <see cref="Task"/>.
42 public enum TaskStatus
45 /// The task has been initialized but has not yet been scheduled.
49 /// The task is waiting to be activated and scheduled internally by the .NET Framework infrastructure.
53 /// The task has been scheduled for execution but has not yet begun executing.
57 /// The task is running but has not yet completed.
61 // /// The task is currently blocked in a wait state.
65 /// The task has finished executing and is implicitly waiting for
66 /// attached child tasks to complete.
68 WaitingForChildrenToComplete,
70 /// The task completed execution successfully.
74 /// The task acknowledged cancellation by throwing an OperationCanceledException with its own CancellationToken
75 /// while the token was in signaled state, or the task's CancellationToken was already signaled before the
76 /// task started executing.
80 /// The task completed due to an unhandled exception.
86 /// Represents an asynchronous operation.
90 /// <see cref="Task"/> instances may be created in a variety of ways. The most common approach is by
91 /// using the Task type's <see cref="Factory"/> property to retrieve a <see
92 /// cref="System.Threading.Tasks.TaskFactory"/> instance that can be used to create tasks for several
93 /// purposes. For example, to create a <see cref="Task"/> that runs an action, the factory's StartNew
94 /// method may be used:
97 /// var t = Task.Factory.StartNew(() => DoAction());
100 /// Dim t = Task.Factory.StartNew(Function() DoAction())
104 /// The <see cref="Task"/> class also provides constructors that initialize the Task but that do not
105 /// schedule it for execution. For performance reasons, TaskFactory's StartNew method should be the
106 /// preferred mechanism for creating and scheduling computational tasks, but for scenarios where creation
107 /// and scheduling must be separated, the constructors may be used, and the task's <see cref="Start()"/>
108 /// method may then be used to schedule the task for execution at a later time.
111 /// All members of <see cref="Task"/>, except for <see cref="Dispose()"/>, are thread-safe
112 /// and may be used from multiple threads concurrently.
115 /// For operations that return values, the <see cref="System.Threading.Tasks.Task{TResult}"/> class
119 /// For developers implementing custom debuggers, several internal and private members of Task may be
120 /// useful (these may change from release to release). The Int32 m_taskId field serves as the backing
121 /// store for the <see cref="Id"/> property, however accessing this field directly from a debugger may be
122 /// more efficient than accessing the same value through the property's getter method (the
123 /// s_taskIdCounter Int32 counter is used to retrieve the next available ID for a Task). Similarly, the
124 /// Int32 m_stateFlags field stores information about the current lifecycle stage of the Task,
125 /// information also accessible through the <see cref="Status"/> property. The m_action System.Object
126 /// field stores a reference to the Task's delegate, and the m_stateObject System.Object field stores the
127 /// async state passed to the Task by the developer. Finally, for debuggers that parse stack frames, the
128 /// InternalWait method serves a potential marker for when a Task is entering a wait operation.
131 [DebuggerTypeProxy(typeof(SystemThreadingTasks_TaskDebugView))]
132 [DebuggerDisplay("Id = {Id}, Status = {Status}, Method = {DebuggerDisplayMethodDescription}")]
133 public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable
136 internal static Task t_currentTask; // The currently executing task.
138 private static StackGuard t_stackGuard; // The stack guard object for this thread
140 internal static int s_taskIdCounter; //static counter used to generate unique task IDs
142 private volatile int m_taskId; // this task's unique ID. initialized only if it is ever requested
144 internal Delegate m_action; // The body of the task. Might be Action<object>, Action<TState> or Action. Or possibly a Func.
145 // If m_action is set to null it will indicate that we operate in the
146 // "externally triggered completion" mode, which is exclusively meant
147 // for the signalling Task<TResult> (aka. promise). In this mode,
148 // we don't call InnerInvoke() in response to a Wait(), but simply wait on
149 // the completion event which will be set when the Future class calls Finish().
150 // But the event would now be signalled if Cancel() is called
153 internal object m_stateObject; // A state object that can be optionally supplied, passed to action.
154 internal TaskScheduler m_taskScheduler; // The task scheduler this task runs under.
156 internal volatile int m_stateFlags;
158 private Task ParentForDebugger => m_contingentProperties?.m_parent; // Private property used by a debugger to access this Task's parent
159 private int StateFlagsForDebugger => m_stateFlags; // Private property used by a debugger to access this Task's state flags
161 // State constants for m_stateFlags;
162 // The bits of m_stateFlags are allocated as follows:
163 // 0x40000000 - TaskBase state flag
164 // 0x3FFF0000 - Task state flags
165 // 0x0000FF00 - internal TaskCreationOptions flags
166 // 0x000000FF - publicly exposed TaskCreationOptions flags
168 // See TaskCreationOptions for bit values associated with TaskCreationOptions
170 private const int OptionsMask = 0xFFFF; // signifies the Options portion of m_stateFlags bin: 0000 0000 0000 0000 1111 1111 1111 1111
171 internal const int TASK_STATE_STARTED = 0x10000; //bin: 0000 0000 0000 0001 0000 0000 0000 0000
172 internal const int TASK_STATE_DELEGATE_INVOKED = 0x20000; //bin: 0000 0000 0000 0010 0000 0000 0000 0000
173 internal const int TASK_STATE_DISPOSED = 0x40000; //bin: 0000 0000 0000 0100 0000 0000 0000 0000
174 internal const int TASK_STATE_EXCEPTIONOBSERVEDBYPARENT = 0x80000; //bin: 0000 0000 0000 1000 0000 0000 0000 0000
175 internal const int TASK_STATE_CANCELLATIONACKNOWLEDGED = 0x100000; //bin: 0000 0000 0001 0000 0000 0000 0000 0000
176 internal const int TASK_STATE_FAULTED = 0x200000; //bin: 0000 0000 0010 0000 0000 0000 0000 0000
177 internal const int TASK_STATE_CANCELED = 0x400000; //bin: 0000 0000 0100 0000 0000 0000 0000 0000
178 internal const int TASK_STATE_WAITING_ON_CHILDREN = 0x800000; //bin: 0000 0000 1000 0000 0000 0000 0000 0000
179 internal const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000; //bin: 0000 0001 0000 0000 0000 0000 0000 0000
180 internal const int TASK_STATE_WAITINGFORACTIVATION = 0x2000000; //bin: 0000 0010 0000 0000 0000 0000 0000 0000
181 internal const int TASK_STATE_COMPLETION_RESERVED = 0x4000000; //bin: 0000 0100 0000 0000 0000 0000 0000 0000
182 internal const int TASK_STATE_THREAD_WAS_ABORTED = 0x8000000; //bin: 0000 1000 0000 0000 0000 0000 0000 0000
183 internal const int TASK_STATE_WAIT_COMPLETION_NOTIFICATION = 0x10000000; //bin: 0001 0000 0000 0000 0000 0000 0000 0000
184 //This could be moved to InternalTaskOptions enum
185 internal const int TASK_STATE_EXECUTIONCONTEXT_IS_NULL = 0x20000000; //bin: 0010 0000 0000 0000 0000 0000 0000 0000
186 internal const int TASK_STATE_TASKSCHEDULED_WAS_FIRED = 0x40000000; //bin: 0100 0000 0000 0000 0000 0000 0000 0000
188 // A mask for all of the final states a task may be in
189 private const int TASK_STATE_COMPLETED_MASK = TASK_STATE_CANCELED | TASK_STATE_FAULTED | TASK_STATE_RAN_TO_COMPLETION;
191 // Values for ContingentProperties.m_internalCancellationRequested.
192 private const int CANCELLATION_REQUESTED = 0x1;
194 // Can be null, a single continuation, a list of continuations, or s_taskCompletionSentinel,
195 // in that order. The logic arround this object assumes it will never regress to a previous state.
196 private volatile object m_continuationObject = null;
198 // m_continuationObject is set to this when the task completes.
199 private static readonly object s_taskCompletionSentinel = new object();
201 // A private flag that would be set (only) by the debugger
202 // When true the Async Causality logging trace is enabled as well as a dictionary to relate operation ids with Tasks
203 // [FriendAccessAllowed]
204 internal static bool s_asyncDebuggingEnabled; //false by default
206 // This dictonary relates the task id, from an operation id located in the Async Causality log to the actual
207 // task. This is to be used by the debugger ONLY. Task in this dictionary represent current active tasks.
208 private static readonly Dictionary<int, Task> s_currentActiveTasks = new Dictionary<int, Task>();
209 private static readonly Object s_activeTasksLock = new Object();
211 // These methods are a way to access the dictionary both from this class and for other classes that also
212 // activate dummy tasks. Specifically the AsyncTaskMethodBuilder and AsyncTaskMethodBuilder<>
213 // [FriendAccessAllowed]
214 internal static bool AddToActiveTasks(Task task)
216 Debug.Assert(task != null, "Null Task objects can't be added to the ActiveTasks collection");
217 lock (s_activeTasksLock)
219 s_currentActiveTasks[task.Id] = task;
221 //always return true to keep signature as bool for backwards compatibility
225 // [FriendAccessAllowed]
226 internal static void RemoveFromActiveTasks(int taskId)
228 lock (s_activeTasksLock)
230 s_currentActiveTasks.Remove(taskId);
234 // We moved a number of Task properties into this class. The idea is that in most cases, these properties never
235 // need to be accessed during the life cycle of a Task, so we don't want to instantiate them every time. Once
236 // one of these properties needs to be written, we will instantiate a ContingentProperties object and set
237 // the appropriate property.
238 internal class ContingentProperties
240 // Additional context
242 internal ExecutionContext m_capturedContext; // The execution context to run the task within, if any. Only set from non-concurrent contexts.
244 // Completion fields (exceptions and event)
246 internal volatile ManualResetEventSlim m_completionEvent; // Lazily created if waiting is required.
247 internal volatile TaskExceptionHolder m_exceptionsHolder; // Tracks exceptions, if any have occurred
249 // Cancellation fields (token, registration, and internally requested)
251 internal CancellationToken m_cancellationToken; // Task's cancellation token, if it has one
252 internal Shared<CancellationTokenRegistration> m_cancellationRegistration; // Task's registration with the cancellation token
253 internal volatile int m_internalCancellationRequested; // Its own field because multiple threads legally try to set it.
257 // # of active children + 1 (for this task itself).
258 // Used for ensuring all children are done before this task can complete
259 // The extra count helps prevent the race condition for executing the final state transition
260 // (i.e. whether the last child or this task itself should call FinishStageTwo())
261 internal volatile int m_completionCountdown = 1;
262 // A list of child tasks that threw an exception (TCEs don't count),
263 // but haven't yet been waited on by the parent, lazily initialized.
264 internal volatile List<Task> m_exceptionalChildren;
265 // A task's parent, or null if parent-less. Only set during Task construction.
266 internal Task m_parent;
269 /// Sets the internal completion event.
271 internal void SetCompleted()
273 var mres = m_completionEvent;
274 if (mres != null) mres.Set();
278 /// Checks if we registered a CT callback during construction, and deregisters it.
279 /// This should be called when we know the registration isn't useful anymore. Specifically from Finish() if the task has completed
280 /// successfully or with an exception.
282 internal void DeregisterCancellationCallback()
284 if (m_cancellationRegistration != null)
286 // Harden against ODEs thrown from disposing of the CTR.
287 // Since the task has already been put into a final state by the time this
288 // is called, all we can do here is suppress the exception.
289 try { m_cancellationRegistration.Value.Dispose(); }
290 catch (ObjectDisposedException) { }
291 m_cancellationRegistration = null;
297 // This field will only be instantiated to some non-null value if any ContingentProperties need to be set.
298 // This will be a ContingentProperties instance or a type derived from it
299 internal ContingentProperties m_contingentProperties;
301 // Special internal constructor to create an already-completed task.
302 // if canceled==true, create a Canceled task, or else create a RanToCompletion task.
303 // Constructs the task as already completed
304 internal Task(bool canceled, TaskCreationOptions creationOptions, CancellationToken ct)
306 int optionFlags = (int)creationOptions;
309 m_stateFlags = TASK_STATE_CANCELED | TASK_STATE_CANCELLATIONACKNOWLEDGED | optionFlags;
310 m_contingentProperties = new ContingentProperties() // can't have children, so just instantiate directly
312 m_cancellationToken = ct,
313 m_internalCancellationRequested = CANCELLATION_REQUESTED,
317 m_stateFlags = TASK_STATE_RAN_TO_COMPLETION | optionFlags;
320 /// <summary>Constructor for use with promise-style tasks that aren't configurable.</summary>
323 m_stateFlags = TASK_STATE_WAITINGFORACTIVATION | (int)InternalTaskOptions.PromiseTask;
326 // Special constructor for use with promise-style tasks.
327 // Added promiseStyle parameter as an aid to the compiler to distinguish between (state,TCO) and
328 // (action,TCO). It should always be true.
329 internal Task(object state, TaskCreationOptions creationOptions, bool promiseStyle)
331 Debug.Assert(promiseStyle, "Promise CTOR: promiseStyle was false");
333 // Check the creationOptions. We allow the AttachedToParent option to be specified for promise tasks.
334 // Also allow RunContinuationsAsynchronously because this is the constructor called by TCS
335 if ((creationOptions & ~(TaskCreationOptions.AttachedToParent | TaskCreationOptions.RunContinuationsAsynchronously)) != 0)
337 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions);
340 // Only set a parent if AttachedToParent is specified.
341 if ((creationOptions & TaskCreationOptions.AttachedToParent) != 0)
343 Task parent = Task.InternalCurrent;
346 EnsureContingentPropertiesInitializedUnsafe().m_parent = parent;
350 TaskConstructorCore(null, state, default(CancellationToken), creationOptions, InternalTaskOptions.PromiseTask, null);
354 /// Initializes a new <see cref="Task"/> with the specified action.
356 /// <param name="action">The delegate that represents the code to execute in the Task.</param>
357 /// <exception cref="T:System.ArgumentNullException">The <paramref name="action"/> argument is null.</exception>
358 public Task(Action action)
359 : this(action, null, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null)
364 /// Initializes a new <see cref="Task"/> with the specified action and <see cref="System.Threading.CancellationToken">CancellationToken</see>.
366 /// <param name="action">The delegate that represents the code to execute in the Task.</param>
367 /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
368 /// that will be assigned to the new Task.</param>
369 /// <exception cref="T:System.ArgumentNullException">The <paramref name="action"/> argument is null.</exception>
370 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
371 /// has already been disposed.
373 public Task(Action action, CancellationToken cancellationToken)
374 : this(action, null, null, cancellationToken, TaskCreationOptions.None, InternalTaskOptions.None, null)
379 /// Initializes a new <see cref="Task"/> with the specified action and creation options.
381 /// <param name="action">The delegate that represents the code to execute in the task.</param>
382 /// <param name="creationOptions">
383 /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
384 /// customize the Task's behavior.
386 /// <exception cref="T:System.ArgumentNullException">
387 /// The <paramref name="action"/> argument is null.
389 /// <exception cref="T:System.ArgumentOutOfRangeException">
390 /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
391 /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
393 public Task(Action action, TaskCreationOptions creationOptions)
394 : this(action, null, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, InternalTaskOptions.None, null)
399 /// Initializes a new <see cref="Task"/> with the specified action and creation options.
401 /// <param name="action">The delegate that represents the code to execute in the task.</param>
402 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
403 /// <param name="creationOptions">
404 /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
405 /// customize the Task's behavior.
407 /// <exception cref="T:System.ArgumentNullException">
408 /// The <paramref name="action"/> argument is null.
410 /// <exception cref="T:System.ArgumentOutOfRangeException">
411 /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
412 /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
414 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
415 /// has already been disposed.
417 public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
418 : this(action, null, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, creationOptions, InternalTaskOptions.None, null)
424 /// Initializes a new <see cref="Task"/> with the specified action and state.
426 /// <param name="action">The delegate that represents the code to execute in the task.</param>
427 /// <param name="state">An object representing data to be used by the action.</param>
428 /// <exception cref="T:System.ArgumentNullException">
429 /// The <paramref name="action"/> argument is null.
431 public Task(Action<object> action, object state)
432 : this(action, state, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null)
437 /// Initializes a new <see cref="Task"/> with the specified action, state, and options.
439 /// <param name="action">The delegate that represents the code to execute in the task.</param>
440 /// <param name="state">An object representing data to be used by the action.</param>
441 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
442 /// <exception cref="T:System.ArgumentNullException">
443 /// The <paramref name="action"/> argument is null.
445 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
446 /// has already been disposed.
448 public Task(Action<object> action, object state, CancellationToken cancellationToken)
449 : this(action, state, null, cancellationToken, TaskCreationOptions.None, InternalTaskOptions.None, null)
454 /// Initializes a new <see cref="Task"/> with the specified action, state, and options.
456 /// <param name="action">The delegate that represents the code to execute in the task.</param>
457 /// <param name="state">An object representing data to be used by the action.</param>
458 /// <param name="creationOptions">
459 /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
460 /// customize the Task's behavior.
462 /// <exception cref="T:System.ArgumentNullException">
463 /// The <paramref name="action"/> argument is null.
465 /// <exception cref="T:System.ArgumentOutOfRangeException">
466 /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
467 /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
469 public Task(Action<object> action, object state, TaskCreationOptions creationOptions)
470 : this(action, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, InternalTaskOptions.None, null)
475 /// Initializes a new <see cref="Task"/> with the specified action, state, and options.
477 /// <param name="action">The delegate that represents the code to execute in the task.</param>
478 /// <param name="state">An object representing data to be used by the action.</param>
479 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
480 /// <param name="creationOptions">
481 /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
482 /// customize the Task's behavior.
484 /// <exception cref="T:System.ArgumentNullException">
485 /// The <paramref name="action"/> argument is null.
487 /// <exception cref="T:System.ArgumentOutOfRangeException">
488 /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
489 /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
491 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
492 /// has already been disposed.
494 public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
495 : this(action, state, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, creationOptions, InternalTaskOptions.None, null)
500 /// An internal constructor used by the factory methods on task and its descendent(s).
501 /// This variant does not capture the ExecutionContext; it is up to the caller to do that.
503 /// <param name="action">An action to execute.</param>
504 /// <param name="state">Optional state to pass to the action.</param>
505 /// <param name="parent">Parent of Task.</param>
506 /// <param name="cancellationToken">A CancellationToken for the task.</param>
507 /// <param name="scheduler">A task scheduler under which the task will run.</param>
508 /// <param name="creationOptions">Options to control its execution.</param>
509 /// <param name="internalOptions">Internal options to control its execution</param>
510 internal Task(Delegate action, object state, Task parent, CancellationToken cancellationToken,
511 TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
515 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action);
518 // Keep a link to the parent if attached
519 if (parent != null && (creationOptions & TaskCreationOptions.AttachedToParent) != 0)
521 EnsureContingentPropertiesInitializedUnsafe().m_parent = parent;
524 TaskConstructorCore(action, state, cancellationToken, creationOptions, internalOptions, scheduler);
528 /// Common logic used by the following internal ctors:
530 /// Task(object action, object state, Task parent, TaskCreationOptions options, TaskScheduler taskScheduler)
532 /// <param name="action">Action for task to execute.</param>
533 /// <param name="state">Object to which to pass to action (may be null)</param>
534 /// <param name="scheduler">Task scheduler on which to run thread (only used by continuation tasks).</param>
535 /// <param name="cancellationToken">A CancellationToken for the Task.</param>
536 /// <param name="creationOptions">Options to customize behavior of Task.</param>
537 /// <param name="internalOptions">Internal options to customize behavior of Task.</param>
538 internal void TaskConstructorCore(Delegate action, object state, CancellationToken cancellationToken,
539 TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
542 m_stateObject = state;
543 m_taskScheduler = scheduler;
545 Debug.Assert(m_contingentProperties == null || m_contingentProperties.m_capturedContext == null,
546 "Captured an ExecutionContext when one was already captured.");
547 CapturedContext = ExecutionContext.Capture();
549 // Check for validity of options
550 if ((creationOptions &
551 ~(TaskCreationOptions.AttachedToParent |
552 TaskCreationOptions.LongRunning |
553 TaskCreationOptions.DenyChildAttach |
554 TaskCreationOptions.HideScheduler |
555 TaskCreationOptions.PreferFairness |
556 TaskCreationOptions.RunContinuationsAsynchronously)) != 0)
558 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions);
562 // Check the validity of internalOptions
563 int illegalInternalOptions =
564 (int)(internalOptions &
565 ~(InternalTaskOptions.PromiseTask |
566 InternalTaskOptions.ContinuationTask |
567 InternalTaskOptions.LazyCancellation |
568 InternalTaskOptions.QueuedByRuntime));
569 Debug.Assert(illegalInternalOptions == 0, "TaskConstructorCore: Illegal internal options");
572 // Assign options to m_stateAndOptionsFlag.
573 Debug.Assert(m_stateFlags == 0, "TaskConstructorCore: non-zero m_stateFlags");
574 Debug.Assert((((int)creationOptions) | OptionsMask) == OptionsMask, "TaskConstructorCore: options take too many bits");
575 int tmpFlags = (int)creationOptions | (int)internalOptions; // one write to the volatile m_stateFlags instead of two when setting the above options
576 m_stateFlags = m_action == null || (internalOptions & InternalTaskOptions.ContinuationTask) != 0 ?
577 tmpFlags | TASK_STATE_WAITINGFORACTIVATION :
580 // Now is the time to add the new task to the children list
581 // of the creating task if the options call for it.
582 // We can safely call the creator task's AddNewChild() method to register it,
583 // because at this point we are already on its thread of execution.
585 ContingentProperties props = m_contingentProperties;
588 Task parent = props.m_parent;
590 && ((creationOptions & TaskCreationOptions.AttachedToParent) != 0)
591 && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0))
593 parent.AddNewChild();
597 // if we have a non-null cancellationToken, allocate the contingent properties to save it
598 // we need to do this as the very last thing in the construction path, because the CT registration could modify m_stateFlags
599 if (cancellationToken.CanBeCanceled)
601 Debug.Assert((internalOptions & InternalTaskOptions.ContinuationTask) == 0, "TaskConstructorCore: Did not expect to see cancelable token for continuation task.");
603 AssignCancellationToken(cancellationToken, null, null);
608 /// Handles everything needed for associating a CancellationToken with a task which is being constructed.
609 /// This method is meant to be called either from the TaskConstructorCore or from ContinueWithCore.
611 private void AssignCancellationToken(CancellationToken cancellationToken, Task antecedent, TaskContinuation continuation)
613 // There is no need to worry about concurrency issues here because we are in the constructor path of the task --
614 // there should not be any race conditions to set m_contingentProperties at this point.
615 ContingentProperties props = EnsureContingentPropertiesInitializedUnsafe();
616 props.m_cancellationToken = cancellationToken;
620 // If an unstarted task has a valid CancellationToken that gets signalled while the task is still not queued
621 // we need to proactively cancel it, because it may never execute to transition itself.
622 // The only way to accomplish this is to register a callback on the CT.
623 // We exclude Promise tasks from this, because TaskCompletionSource needs to fully control the inner tasks's lifetime (i.e. not allow external cancellations)
624 if ((((InternalTaskOptions)Options &
625 (InternalTaskOptions.QueuedByRuntime | InternalTaskOptions.PromiseTask | InternalTaskOptions.LazyCancellation)) == 0))
627 if (cancellationToken.IsCancellationRequested)
629 // Fast path for an already-canceled cancellationToken
630 this.InternalCancel(false);
634 // Regular path for an uncanceled cancellationToken
635 CancellationTokenRegistration ctr;
636 if (antecedent == null)
638 // if no antecedent was specified, use this task's reference as the cancellation state object
639 ctr = cancellationToken.InternalRegisterWithoutEC(s_taskCancelCallback, this);
643 // If an antecedent was specified, pack this task, its antecedent and the TaskContinuation together as a tuple
644 // and use it as the cancellation state object. This will be unpacked in the cancellation callback so that
645 // antecedent.RemoveCancellation(continuation) can be invoked.
646 ctr = cancellationToken.InternalRegisterWithoutEC(s_taskCancelCallback,
647 new Tuple<Task, Task, TaskContinuation>(this, antecedent, continuation));
650 props.m_cancellationRegistration = new Shared<CancellationTokenRegistration>(ctr);
656 // If we have an exception related to our CancellationToken, then we need to subtract ourselves
657 // from our parent before throwing it.
658 Task parent = m_contingentProperties?.m_parent;
659 if ((parent != null) &&
660 ((Options & TaskCreationOptions.AttachedToParent) != 0)
661 && ((parent.Options & TaskCreationOptions.DenyChildAttach) == 0))
663 parent.DisregardChild();
670 // Static delegate to be used as a cancellation callback on unstarted tasks that have a valid cancellation token.
671 // This is necessary to transition them into canceled state if their cancellation token is signalled while they are still not queued
672 private readonly static Action<Object> s_taskCancelCallback = new Action<Object>(TaskCancelCallback);
673 private static void TaskCancelCallback(Object o)
675 var targetTask = o as Task;
676 if (targetTask == null)
678 var tuple = o as Tuple<Task, Task, TaskContinuation>;
681 targetTask = tuple.Item1;
683 Task antecedentTask = tuple.Item2;
684 TaskContinuation continuation = tuple.Item3;
685 antecedentTask.RemoveContinuation(continuation);
688 Debug.Assert(targetTask != null,
689 "targetTask should have been non-null, with the supplied argument being a task or a tuple containing one");
690 targetTask.InternalCancel(false);
694 private string DebuggerDisplayMethodDescription
698 Delegate d = m_action;
699 return d != null ? d.Method.ToString() : "{null}";
703 // Internal property to process TaskCreationOptions access and mutation.
704 internal TaskCreationOptions Options => OptionsMethod(m_stateFlags);
706 // Similar to Options property, but allows for the use of a cached flags value rather than
707 // a read of the volatile m_stateFlags field.
708 internal static TaskCreationOptions OptionsMethod(int flags)
710 Debug.Assert((OptionsMask & 1) == 1, "OptionsMask needs a shift in Options.get");
711 return (TaskCreationOptions)(flags & OptionsMask);
714 // Atomically OR-in newBits to m_stateFlags, while making sure that
715 // no illegalBits are set. Returns true on success, false on failure.
716 internal bool AtomicStateUpdate(int newBits, int illegalBits)
718 int oldFlags = m_stateFlags;
720 (oldFlags & illegalBits) == 0 &&
721 (Interlocked.CompareExchange(ref m_stateFlags, oldFlags | newBits, oldFlags) == oldFlags ||
722 AtomicStateUpdateSlow(newBits, illegalBits));
725 private bool AtomicStateUpdateSlow(int newBits, int illegalBits)
727 var sw = new SpinWait();
730 int oldFlags = m_stateFlags;
731 if ((oldFlags & illegalBits) != 0) return false;
732 if (Interlocked.CompareExchange(ref m_stateFlags, oldFlags | newBits, oldFlags) == oldFlags)
740 internal bool AtomicStateUpdate(int newBits, int illegalBits, ref int oldFlags)
742 SpinWait sw = new SpinWait();
745 oldFlags = m_stateFlags;
746 if ((oldFlags & illegalBits) != 0) return false;
747 if (Interlocked.CompareExchange(ref m_stateFlags, oldFlags | newBits, oldFlags) == oldFlags)
756 /// Sets or clears the TASK_STATE_WAIT_COMPLETION_NOTIFICATION state bit.
757 /// The debugger sets this bit to aid it in "stepping out" of an async method body.
758 /// If enabled is true, this must only be called on a task that has not yet been completed.
759 /// If enabled is false, this may be called on completed tasks.
760 /// Either way, it should only be used for promise-style tasks.
762 /// <param name="enabled">true to set the bit; false to unset the bit.</param>
763 internal void SetNotificationForWaitCompletion(bool enabled)
765 Debug.Assert((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0,
766 "Should only be used for promise-style tasks"); // hasn't been vetted on other kinds as there hasn't been a need
770 // Atomically set the END_AWAIT_NOTIFICATION bit
771 bool success = AtomicStateUpdate(TASK_STATE_WAIT_COMPLETION_NOTIFICATION,
772 TASK_STATE_COMPLETED_MASK | TASK_STATE_COMPLETION_RESERVED);
773 Debug.Assert(success, "Tried to set enabled on completed Task");
777 // Atomically clear the END_AWAIT_NOTIFICATION bit
778 SpinWait sw = new SpinWait();
781 int oldFlags = m_stateFlags;
782 int newFlags = oldFlags & (~TASK_STATE_WAIT_COMPLETION_NOTIFICATION);
783 if (Interlocked.CompareExchange(ref m_stateFlags, newFlags, oldFlags) == oldFlags) break;
790 /// Calls the debugger notification method if the right bit is set and if
791 /// the task itself allows for the notification to proceed.
793 /// <returns>true if the debugger was notified; otherwise, false.</returns>
794 internal bool NotifyDebuggerOfWaitCompletionIfNecessary()
796 // Notify the debugger if of any of the tasks we've waited on requires notification
797 if (IsWaitNotificationEnabled && ShouldNotifyDebuggerOfWaitCompletion)
799 NotifyDebuggerOfWaitCompletion();
805 /// <summary>Returns true if any of the supplied tasks require wait notification.</summary>
806 /// <param name="tasks">The tasks to check.</param>
807 /// <returns>true if any of the tasks require notification; otherwise, false.</returns>
808 internal static bool AnyTaskRequiresNotifyDebuggerOfWaitCompletion(Task[] tasks)
810 Debug.Assert(tasks != null, "Expected non-null array of tasks");
811 foreach (var task in tasks)
814 task.IsWaitNotificationEnabled &&
815 task.ShouldNotifyDebuggerOfWaitCompletion) // potential recursion
823 /// <summary>Gets whether either the end await bit is set or (not xor) the task has not completed successfully.</summary>
824 /// <returns>(DebuggerBitSet || !RanToCompletion)</returns>
825 internal bool IsWaitNotificationEnabledOrNotRanToCompletion
827 [MethodImpl(MethodImplOptions.AggressiveInlining)]
830 return (m_stateFlags & (Task.TASK_STATE_WAIT_COMPLETION_NOTIFICATION | Task.TASK_STATE_RAN_TO_COMPLETION))
831 != Task.TASK_STATE_RAN_TO_COMPLETION;
836 /// Determines whether we should inform the debugger that we're ending a join with a task.
837 /// This should only be called if the debugger notification bit is set, as it is has some cost,
838 /// namely it is a virtual call (however calling it if the bit is not set is not functionally
839 /// harmful). Derived implementations may choose to only conditionally call down to this base
842 internal virtual bool ShouldNotifyDebuggerOfWaitCompletion // ideally would be familyAndAssembly, but that can't be done in C#
846 // It's theoretically possible but extremely rare that this assert could fire because the
847 // bit was unset between the time that it was checked and this method was called.
848 // It's so remote a chance that it's worth having the assert to protect against misuse.
849 bool isWaitNotificationEnabled = IsWaitNotificationEnabled;
850 Debug.Assert(isWaitNotificationEnabled, "Should only be called if the wait completion bit is set.");
851 return isWaitNotificationEnabled;
855 /// <summary>Gets whether the task's debugger notification for wait completion bit is set.</summary>
856 /// <returns>true if the bit is set; false if it's not set.</returns>
857 internal bool IsWaitNotificationEnabled // internal only to enable unit tests; would otherwise be private
859 get { return (m_stateFlags & TASK_STATE_WAIT_COMPLETION_NOTIFICATION) != 0; }
862 /// <summary>Placeholder method used as a breakpoint target by the debugger. Must not be inlined or optimized.</summary>
863 /// <remarks>All joins with a task should end up calling this if their debugger notification bit is set.</remarks>
864 [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
865 private void NotifyDebuggerOfWaitCompletion()
867 // It's theoretically possible but extremely rare that this assert could fire because the
868 // bit was unset between the time that it was checked and this method was called.
869 // It's so remote a chance that it's worth having the assert to protect against misuse.
870 Debug.Assert(IsWaitNotificationEnabled, "Should only be called if the wait completion bit is set.");
872 // Now that we're notifying the debugger, clear the bit. The debugger should do this anyway,
873 // but this adds a bit of protection in case it fails to, and given that the debugger is involved,
874 // the overhead here for the interlocked is negligable. We do still rely on the debugger
875 // to clear bits, as this doesn't recursively clear bits in the case of, for example, WhenAny.
876 SetNotificationForWaitCompletion(enabled: false);
880 // Atomically mark a Task as started while making sure that it is not canceled.
881 internal bool MarkStarted()
883 return AtomicStateUpdate(TASK_STATE_STARTED, TASK_STATE_CANCELED | TASK_STATE_STARTED);
886 internal bool FireTaskScheduledIfNeeded(TaskScheduler ts)
888 var etwLog = TplEtwProvider.Log;
889 if (etwLog.IsEnabled() && (m_stateFlags & Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED) == 0)
891 m_stateFlags |= Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED;
893 Task currentTask = Task.InternalCurrent;
894 Task parentTask = m_contingentProperties?.m_parent;
895 etwLog.TaskScheduled(ts.Id, currentTask == null ? 0 : currentTask.Id,
896 this.Id, parentTask == null ? 0 : parentTask.Id, (int)this.Options,
897 System.Threading.Thread.GetDomainID());
905 /// Internal function that will be called by a new child task to add itself to
906 /// the children list of the parent (this).
908 /// Since a child task can only be created from the thread executing the action delegate
909 /// of this task, reentrancy is neither required nor supported. This should not be called from
910 /// anywhere other than the task construction/initialization codepaths.
912 internal void AddNewChild()
914 Debug.Assert(Task.InternalCurrent == this, "Task.AddNewChild(): Called from an external context");
916 var props = EnsureContingentPropertiesInitialized();
918 if (props.m_completionCountdown == 1)
920 // A count of 1 indicates so far there was only the parent, and this is the first child task
921 // Single kid => no fuss about who else is accessing the count. Let's save ourselves 100 cycles
922 props.m_completionCountdown++;
926 // otherwise do it safely
927 Interlocked.Increment(ref props.m_completionCountdown);
931 // This is called in the case where a new child is added, but then encounters a CancellationToken-related exception.
932 // We need to subtract that child from m_completionCountdown, or the parent will never complete.
933 internal void DisregardChild()
935 Debug.Assert(Task.InternalCurrent == this, "Task.DisregardChild(): Called from an external context");
937 var props = EnsureContingentPropertiesInitialized();
938 Debug.Assert(props.m_completionCountdown >= 2, "Task.DisregardChild(): Expected parent count to be >= 2");
939 Interlocked.Decrement(ref props.m_completionCountdown);
943 /// Starts the <see cref="Task"/>, scheduling it for execution to the current <see
944 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
947 /// A task may only be started and run only once. Any attempts to schedule a task a second time
948 /// will result in an exception.
950 /// <exception cref="InvalidOperationException">
951 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
952 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
957 Start(TaskScheduler.Current);
961 /// Starts the <see cref="Task"/>, scheduling it for execution to the specified <see
962 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
965 /// A task may only be started and run only once. Any attempts to schedule a task a second time will
966 /// result in an exception.
968 /// <param name="scheduler">
969 /// The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> with which to associate
970 /// and execute this task.
972 /// <exception cref="ArgumentNullException">
973 /// The <paramref name="scheduler"/> argument is null.
975 /// <exception cref="InvalidOperationException">
976 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
977 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
980 public void Start(TaskScheduler scheduler)
982 // Read the volatile m_stateFlags field once and cache it for subsequent operations
983 int flags = m_stateFlags;
985 // Need to check this before (m_action == null) because completed tasks will
986 // set m_action to null. We would want to know if this is the reason that m_action == null.
987 if (IsCompletedMethod(flags))
989 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Start_TaskCompleted);
992 if (scheduler == null)
994 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
997 var options = OptionsMethod(flags);
998 if ((options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
1000 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Start_Promise);
1002 if ((options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) != 0)
1004 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Start_ContinuationTask);
1007 // Make sure that Task only gets started once. Or else throw an exception.
1008 if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
1010 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Start_AlreadyStarted);
1013 ScheduleAndStart(true);
1017 /// Runs the <see cref="Task"/> synchronously on the current <see
1018 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1022 /// A task may only be started and run only once. Any attempts to schedule a task a second time will
1023 /// result in an exception.
1026 /// Tasks executed with <see cref="RunSynchronously()"/> will be associated with the current <see
1027 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1030 /// If the target scheduler does not support running this Task on the current thread, the Task will
1031 /// be scheduled for execution on the scheduler, and the current thread will block until the
1032 /// Task has completed execution.
1035 /// <exception cref="InvalidOperationException">
1036 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1037 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1040 public void RunSynchronously()
1042 InternalRunSynchronously(TaskScheduler.Current, waitForCompletion: true);
1046 /// Runs the <see cref="Task"/> synchronously on the <see
1047 /// cref="System.Threading.Tasks.TaskScheduler">scheduler</see> provided.
1051 /// A task may only be started and run only once. Any attempts to schedule a task a second time will
1052 /// result in an exception.
1055 /// If the target scheduler does not support running this Task on the current thread, the Task will
1056 /// be scheduled for execution on the scheduler, and the current thread will block until the
1057 /// Task has completed execution.
1060 /// <exception cref="InvalidOperationException">
1061 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1062 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1065 /// <exception cref="ArgumentNullException">The <paramref name="scheduler"/> parameter
1066 /// is null.</exception>
1067 /// <param name="scheduler">The scheduler on which to attempt to run this task inline.</param>
1068 public void RunSynchronously(TaskScheduler scheduler)
1070 if (scheduler == null)
1072 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
1075 InternalRunSynchronously(scheduler, waitForCompletion: true);
1079 // Internal version of RunSynchronously that allows not waiting for completion.
1081 internal void InternalRunSynchronously(TaskScheduler scheduler, bool waitForCompletion)
1083 Debug.Assert(scheduler != null, "Task.InternalRunSynchronously(): null TaskScheduler");
1085 // Read the volatile m_stateFlags field once and cache it for subsequent operations
1086 int flags = m_stateFlags;
1088 // Can't call this method on a continuation task
1089 var options = OptionsMethod(flags);
1090 if ((options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) != 0)
1092 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_Continuation);
1095 // Can't call this method on a promise-style task
1096 if ((options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
1098 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_Promise);
1101 // Can't call this method on a task that has already completed
1102 if (IsCompletedMethod(flags))
1104 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_TaskCompleted);
1107 // Make sure that Task only gets started once. Or else throw an exception.
1108 if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
1110 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_AlreadyStarted);
1113 // execute only if we successfully cancel when concurrent cancel attempts are made.
1114 // otherwise throw an exception, because we've been canceled.
1117 bool taskQueued = false;
1120 // We wrap TryRunInline() in a try/catch block and move an excepted task to Faulted here,
1121 // but not in Wait()/WaitAll()/FastWaitAll(). Here, we know for sure that the
1122 // task will not be subsequently scheduled (assuming that the scheduler adheres
1123 // to the guideline that an exception implies that no state change took place),
1124 // so it is safe to catch the exception and move the task to a final state. The
1125 // same cannot be said for Wait()/WaitAll()/FastWaitAll().
1126 if (!scheduler.TryRunInline(this, false))
1128 scheduler.InternalQueueTask(this);
1129 taskQueued = true; // only mark this after successfully queuing the task.
1132 // A successful TryRunInline doesn't guarantee completion, as there may be unfinished children.
1133 // Also if we queued the task above, the task may not be done yet.
1134 if (waitForCompletion && !IsCompleted)
1136 SpinThenBlockingWait(Timeout.Infinite, default(CancellationToken));
1141 // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
1142 // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute
1143 if (!taskQueued && !(e is ThreadAbortException))
1145 // We had a problem with TryRunInline() or QueueTask().
1146 // Record the exception, marking ourselves as Completed/Faulted.
1147 TaskSchedulerException tse = new TaskSchedulerException(e);
1151 // Mark ourselves as "handled" to avoid crashing the finalizer thread if the caller neglects to
1152 // call Wait() on this task.
1153 // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
1155 (m_contingentProperties != null) &&
1156 (m_contingentProperties.m_exceptionsHolder != null) &&
1157 (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
1158 "Task.InternalRunSynchronously(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
1159 "and to have faults recorded.");
1160 m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
1165 // We had a problem with waiting or this is a thread abort. Just re-throw.
1171 Debug.Assert((m_stateFlags & TASK_STATE_CANCELED) != 0, "Task.RunSynchronously: expected TASK_STATE_CANCELED to be set");
1172 // Can't call this method on canceled task.
1173 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_RunSynchronously_TaskCompleted);
1179 //// Helper methods for Factory StartNew methods.
1183 // Implicitly converts action to object and handles the meat of the StartNew() logic.
1184 internal static Task InternalStartNew(
1185 Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler,
1186 TaskCreationOptions options, InternalTaskOptions internalOptions)
1188 // Validate arguments.
1189 if (scheduler == null)
1191 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
1194 // Create and schedule the task. This throws an InvalidOperationException if already shut down.
1195 // Here we add the InternalTaskOptions.QueuedByRuntime to the internalOptions, so that TaskConstructorCore can skip the cancellation token registration
1196 Task t = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
1198 t.ScheduleAndStart(false);
1203 /// Gets a unique ID for a <see cref="Task">Task</see> or task continuation instance.
1205 internal static int NewId()
1208 // We need to repeat if Interlocked.Increment wraps around and returns 0.
1209 // Otherwise next time this task's Id is queried it will get a new value
1212 newId = Interlocked.Increment(ref s_taskIdCounter);
1215 TplEtwProvider.Log.NewID(newId);
1224 /// Gets a unique ID for this <see cref="Task">Task</see> instance.
1227 /// Task IDs are assigned on-demand and do not necessarily represent the order in the which Task
1228 /// instances were created.
1236 int newId = NewId();
1237 Interlocked.CompareExchange(ref m_taskId, newId, 0);
1245 /// Returns the unique ID of the currently executing <see cref="Task">Task</see>.
1247 public static int? CurrentId
1251 Task currentTask = InternalCurrent;
1252 if (currentTask != null)
1253 return currentTask.Id;
1260 /// Gets the <see cref="Task">Task</see> instance currently executing, or
1261 /// null if none exists.
1263 internal static Task InternalCurrent
1265 get { return t_currentTask; }
1269 /// Gets the Task instance currently executing if the specified creation options
1270 /// contain AttachedToParent.
1272 /// <param name="options">The options to check.</param>
1273 /// <returns>The current task if there is one and if AttachToParent is in the options; otherwise, null.</returns>
1274 internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions)
1276 return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
1280 /// Gets the StackGuard object assigned to the current thread.
1282 internal static StackGuard CurrentStackGuard
1286 StackGuard sg = t_stackGuard;
1289 t_stackGuard = sg = new StackGuard();
1297 /// Gets the <see cref="T:System.AggregateException">Exception</see> that caused the <see
1298 /// cref="Task">Task</see> to end prematurely. If the <see
1299 /// cref="Task">Task</see> completed successfully or has not yet thrown any
1300 /// exceptions, this will return null.
1303 /// Tasks that throw unhandled exceptions store the resulting exception and propagate it wrapped in a
1304 /// <see cref="System.AggregateException"/> in calls to <see cref="Wait()">Wait</see>
1305 /// or in accesses to the <see cref="Exception"/> property. Any exceptions not observed by the time
1306 /// the Task instance is garbage collected will be propagated on the finalizer thread.
1308 public AggregateException Exception
1312 AggregateException e = null;
1314 // If you're faulted, retrieve the exception(s)
1315 if (IsFaulted) e = GetExceptions(false);
1317 // Only return an exception in faulted state (skip manufactured exceptions)
1318 // A "benevolent" race condition makes it possible to return null when IsFaulted is
1319 // true (i.e., if IsFaulted is set just after the check to IsFaulted above).
1320 Debug.Assert((e == null) || IsFaulted, "Task.Exception_get(): returning non-null value when not Faulted");
1327 /// Gets the <see cref="T:System.Threading.Tasks.TaskStatus">TaskStatus</see> of this Task.
1329 public TaskStatus Status
1335 // get a cached copy of the state flags. This should help us
1336 // to get a consistent view of the flags if they are changing during the
1337 // execution of this method.
1338 int sf = m_stateFlags;
1340 if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted;
1341 else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled;
1342 else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion;
1343 else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete;
1344 else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running;
1345 else if ((sf & TASK_STATE_STARTED) != 0) rval = TaskStatus.WaitingToRun;
1346 else if ((sf & TASK_STATE_WAITINGFORACTIVATION) != 0) rval = TaskStatus.WaitingForActivation;
1347 else rval = TaskStatus.Created;
1354 /// Gets whether this <see cref="Task">Task</see> instance has completed
1355 /// execution due to being canceled.
1358 /// A <see cref="Task">Task</see> will complete in Canceled state either if its <see cref="CancellationToken">CancellationToken</see>
1359 /// was marked for cancellation before the task started executing, or if the task acknowledged the cancellation request on
1360 /// its already signaled CancellationToken by throwing an
1361 /// <see cref="System.OperationCanceledException">OperationCanceledException</see> that bears the same
1362 /// <see cref="System.Threading.CancellationToken">CancellationToken</see>.
1364 public bool IsCanceled
1368 // Return true if canceled bit is set and faulted bit is not set
1369 return (m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_FAULTED)) == TASK_STATE_CANCELED;
1374 /// Returns true if this task has a cancellation token and it was signaled.
1375 /// To be used internally in execute entry codepaths.
1377 internal bool IsCancellationRequested
1381 // check both the internal cancellation request flag and the CancellationToken attached to this task
1382 var props = Volatile.Read(ref m_contingentProperties);
1383 return props != null &&
1384 (props.m_internalCancellationRequested == CANCELLATION_REQUESTED ||
1385 props.m_cancellationToken.IsCancellationRequested);
1390 /// Ensures that the contingent properties field has been initialized.
1391 /// ASSUMES THAT m_stateFlags IS ALREADY SET!
1393 /// <returns>The initialized contingent properties object.</returns>
1394 internal ContingentProperties EnsureContingentPropertiesInitialized()
1396 return LazyInitializer.EnsureInitialized(ref m_contingentProperties, () => new ContingentProperties());
1400 /// Without synchronization, ensures that the contingent properties field has been initialized.
1401 /// ASSUMES THAT m_stateFlags IS ALREADY SET!
1403 /// <returns>The initialized contingent properties object.</returns>
1404 internal ContingentProperties EnsureContingentPropertiesInitializedUnsafe()
1406 return m_contingentProperties ?? (m_contingentProperties = new ContingentProperties());
1410 /// This internal property provides access to the CancellationToken that was set on the task
1411 /// when it was constructed.
1413 internal CancellationToken CancellationToken
1417 var props = Volatile.Read(ref m_contingentProperties);
1418 return (props == null) ? default(CancellationToken) : props.m_cancellationToken;
1423 /// Gets whether this <see cref="Task"/> threw an OperationCanceledException while its CancellationToken was signaled.
1425 internal bool IsCancellationAcknowledged
1427 get { return (m_stateFlags & TASK_STATE_CANCELLATIONACKNOWLEDGED) != 0; }
1432 /// Gets whether this <see cref="Task">Task</see> has completed.
1435 /// <see cref="IsCompleted"/> will return true when the Task is in one of the three
1436 /// final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
1437 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
1438 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
1440 public bool IsCompleted
1444 int stateFlags = m_stateFlags; // enable inlining of IsCompletedMethod by "cast"ing away the volatility
1445 return IsCompletedMethod(stateFlags);
1449 // Similar to IsCompleted property, but allows for the use of a cached flags value
1450 // rather than reading the volatile m_stateFlags field.
1451 private static bool IsCompletedMethod(int flags)
1453 return (flags & TASK_STATE_COMPLETED_MASK) != 0;
1456 public bool IsCompletedSuccessfully
1458 get { return (m_stateFlags & TASK_STATE_COMPLETED_MASK) == TASK_STATE_RAN_TO_COMPLETION; }
1462 /// Gets the <see cref="T:System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used
1463 /// to create this task.
1465 public TaskCreationOptions CreationOptions
1467 get { return Options & (TaskCreationOptions)(~InternalTaskOptions.InternalOptionsMask); }
1471 /// Gets a <see cref="T:System.Threading.WaitHandle"/> that can be used to wait for the task to
1475 /// Using the wait functionality provided by <see cref="Wait()"/>
1476 /// should be preferred over using <see cref="IAsyncResult.AsyncWaitHandle"/> for similar
1479 /// <exception cref="T:System.ObjectDisposedException">
1480 /// The <see cref="Task"/> has been disposed.
1482 WaitHandle IAsyncResult.AsyncWaitHandle
1484 // Although a slim event is used internally to avoid kernel resource allocation, this function
1485 // forces allocation of a true WaitHandle when called.
1488 bool isDisposed = (m_stateFlags & TASK_STATE_DISPOSED) != 0;
1491 ThrowHelper.ThrowObjectDisposedException(ExceptionResource.Task_ThrowIfDisposed);
1493 return CompletedEvent.WaitHandle;
1498 /// Gets the state object supplied when the <see cref="Task">Task</see> was created,
1499 /// or null if none was supplied.
1501 public object AsyncState
1503 get { return m_stateObject; }
1507 /// Gets an indication of whether the asynchronous operation completed synchronously.
1509 /// <value>true if the asynchronous operation completed synchronously; otherwise, false.</value>
1510 bool IAsyncResult.CompletedSynchronously
1519 /// Provides access to the TaskScheduler responsible for executing this Task.
1521 internal TaskScheduler ExecutingTaskScheduler
1523 get { return m_taskScheduler; }
1527 /// Provides access to factory methods for creating <see cref="Task"/> and <see cref="Task{TResult}"/> instances.
1530 /// The factory returned from <see cref="Factory"/> is a default instance
1531 /// of <see cref="System.Threading.Tasks.TaskFactory"/>, as would result from using
1532 /// the default constructor on TaskFactory.
1534 public static TaskFactory Factory { get; } = new TaskFactory();
1536 /// <summary>Gets a task that's already been completed successfully.</summary>
1537 public static Task CompletedTask { get; } = new Task(false, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken));
1540 /// Provides an event that can be used to wait for completion.
1541 /// Only called by IAsyncResult.AsyncWaitHandle, which means that we really do need to instantiate a completion event.
1543 internal ManualResetEventSlim CompletedEvent
1547 var contingentProps = EnsureContingentPropertiesInitialized();
1548 if (contingentProps.m_completionEvent == null)
1550 bool wasCompleted = IsCompleted;
1551 ManualResetEventSlim newMre = new ManualResetEventSlim(wasCompleted);
1552 if (Interlocked.CompareExchange(ref contingentProps.m_completionEvent, newMre, null) != null)
1554 // Someone else already set the value, so we will just close the event right away.
1557 else if (!wasCompleted && IsCompleted)
1559 // We published the event as unset, but the task has subsequently completed.
1560 // Set the event's state properly so that callers don't deadlock.
1565 return contingentProps.m_completionEvent;
1571 /// The property formerly known as IsFaulted.
1573 internal bool ExceptionRecorded
1577 var props = Volatile.Read(ref m_contingentProperties);
1578 return (props != null) && (props.m_exceptionsHolder != null) && (props.m_exceptionsHolder.ContainsFaultList);
1583 /// Gets whether the <see cref="Task"/> completed due to an unhandled exception.
1586 /// If <see cref="IsFaulted"/> is true, the Task's <see cref="Status"/> will be equal to
1587 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">TaskStatus.Faulted</see>, and its
1588 /// <see cref="Exception"/> property will be non-null.
1590 public bool IsFaulted
1594 // Faulted is "king" -- if that bit is present (regardless of other bits), we are faulted.
1595 return ((m_stateFlags & TASK_STATE_FAULTED) != 0);
1600 /// The captured execution context for the current task to run inside
1601 /// If the TASK_STATE_EXECUTIONCONTEXT_IS_NULL flag is set, this means ExecutionContext.Capture returned null, otherwise
1602 /// If the captured context is the default, nothing is saved, otherwise the m_contingentProperties inflates to save the context
1604 internal ExecutionContext CapturedContext
1608 if ((m_stateFlags & TASK_STATE_EXECUTIONCONTEXT_IS_NULL) == TASK_STATE_EXECUTIONCONTEXT_IS_NULL)
1614 return m_contingentProperties?.m_capturedContext ?? ExecutionContext.Default;
1619 // There is no need to atomically set this bit because this set() method is only called during construction, and therefore there should be no contending accesses to m_stateFlags
1622 m_stateFlags |= TASK_STATE_EXECUTIONCONTEXT_IS_NULL;
1624 else if (value != ExecutionContext.Default) // not the default context, then inflate the contingent properties and set it
1626 EnsureContingentPropertiesInitializedUnsafe().m_capturedContext = value;
1628 //else do nothing, this is the default context
1637 /// Disposes the <see cref="Task"/>, releasing all of its unmanaged resources.
1640 /// Unlike most of the members of <see cref="Task"/>, this method is not thread-safe.
1641 /// Also, <see cref="Dispose()"/> may only be called on a <see cref="Task"/> that is in one of
1642 /// the final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
1643 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
1644 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
1646 /// <exception cref="T:System.InvalidOperationException">
1647 /// The exception that is thrown if the <see cref="Task"/> is not in
1648 /// one of the final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
1649 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
1650 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
1652 public void Dispose()
1655 GC.SuppressFinalize(this);
1659 /// Disposes the <see cref="Task"/>, releasing all of its unmanaged resources.
1661 /// <param name="disposing">
1662 /// A Boolean value that indicates whether this method is being called due to a call to <see
1663 /// cref="Dispose()"/>.
1666 /// Unlike most of the members of <see cref="Task"/>, this method is not thread-safe.
1668 protected virtual void Dispose(bool disposing)
1672 // Dispose is a nop if this task was created with the DoNotDispose internal option.
1673 // This is done before the completed check, because if we're not touching any
1674 // state on the task, it's ok for it to happen before completion.
1675 if ((Options & (TaskCreationOptions)InternalTaskOptions.DoNotDispose) != 0)
1680 // Task must be completed to dispose
1683 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.Task_Dispose_NotCompleted);
1686 // Dispose of the underlying completion event if it exists
1687 var cp = Volatile.Read(ref m_contingentProperties);
1690 // Make a copy to protect against racing Disposes.
1691 // If we wanted to make this a bit safer, we could use an interlocked here,
1692 // but we state that Dispose is not thread safe.
1693 var ev = cp.m_completionEvent;
1696 // Null out the completion event in contingent props; we'll use our copy from here on out
1697 cp.m_completionEvent = null;
1699 // In the unlikely event that our completion event is inflated but not yet signaled,
1700 // go ahead and signal the event. If you dispose of an unsignaled MRES, then any waiters
1701 // will deadlock; an ensuing Set() will not wake them up. In the event of an AppDomainUnload,
1702 // there is no guarantee that anyone else is going to signal the event, and it does no harm to
1703 // call Set() twice on m_completionEvent.
1704 if (!ev.IsSet) ev.Set();
1706 // Finally, dispose of the event
1712 // We OR the flags to indicate the object has been disposed. The task
1713 // has already completed at this point, and the only conceivable race condition would
1714 // be with the unsetting of the TASK_STATE_WAIT_COMPLETION_NOTIFICATION flag, which
1715 // is extremely unlikely and also benign. (Worst case: we hit a breakpoint
1716 // twice instead of once in the debugger. Weird, but not lethal.)
1717 m_stateFlags |= TASK_STATE_DISPOSED;
1725 /// Schedules the task for execution.
1727 /// <param name="needsProtection">If true, TASK_STATE_STARTED bit is turned on in
1728 /// an atomic fashion, making sure that TASK_STATE_CANCELED does not get set
1729 /// underneath us. If false, TASK_STATE_STARTED bit is OR-ed right in. This
1730 /// allows us to streamline things a bit for StartNew(), where competing cancellations
1731 /// are not a problem.</param>
1732 internal void ScheduleAndStart(bool needsProtection)
1734 Debug.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected");
1735 Debug.Assert((m_stateFlags & TASK_STATE_STARTED) == 0, "task has already started");
1737 // Set the TASK_STATE_STARTED bit
1738 if (needsProtection)
1742 // A cancel has snuck in before we could get started. Quietly exit.
1748 m_stateFlags |= TASK_STATE_STARTED;
1751 if (s_asyncDebuggingEnabled)
1753 AddToActiveTasks(this);
1756 if (AsyncCausalityTracer.LoggingOn && (Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
1758 //For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor
1759 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: " + m_action.Method.Name, 0);
1765 // Queue to the indicated scheduler.
1766 m_taskScheduler.InternalQueueTask(this);
1768 catch (ThreadAbortException tae)
1771 FinishThreadAbortedTask(delegateRan: false);
1775 // The scheduler had a problem queueing this task. Record the exception, leaving this task in
1777 TaskSchedulerException tse = new TaskSchedulerException(e);
1781 // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew(),
1782 // because the exception is either propagated outside directly, or added to an enclosing parent. However we won't do this for
1783 // continuation tasks, because in that case we internally eat the exception and therefore we need to make sure the user does
1784 // later observe it explicitly or see it on the finalizer.
1786 if ((Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
1788 // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
1790 (m_contingentProperties != null) &&
1791 (m_contingentProperties.m_exceptionsHolder != null) &&
1792 (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
1793 "Task.ScheduleAndStart(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
1794 "and to have faults recorded.");
1796 m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
1798 // re-throw the exception wrapped as a TaskSchedulerException.
1804 /// Adds an exception to the list of exceptions this task has thrown.
1806 /// <param name="exceptionObject">An object representing either an Exception or a collection of Exceptions.</param>
1807 internal void AddException(object exceptionObject)
1809 Debug.Assert(exceptionObject != null, "Task.AddException: Expected a non-null exception object");
1810 AddException(exceptionObject, representsCancellation: false);
1814 /// Adds an exception to the list of exceptions this task has thrown.
1816 /// <param name="exceptionObject">An object representing either an Exception or a collection of Exceptions.</param>
1817 /// <param name="representsCancellation">Whether the exceptionObject is an OperationCanceledException representing cancellation.</param>
1818 internal void AddException(object exceptionObject, bool representsCancellation)
1820 Debug.Assert(exceptionObject != null, "Task.AddException: Expected a non-null exception object");
1823 var eoAsException = exceptionObject as Exception;
1824 var eoAsEnumerableException = exceptionObject as IEnumerable<Exception>;
1825 var eoAsEdi = exceptionObject as ExceptionDispatchInfo;
1826 var eoAsEnumerableEdi = exceptionObject as IEnumerable<ExceptionDispatchInfo>;
1829 eoAsException != null || eoAsEnumerableException != null || eoAsEdi != null || eoAsEnumerableEdi != null,
1830 "Task.AddException: Expected an Exception, ExceptionDispatchInfo, or an IEnumerable<> of one of those");
1832 var eoAsOce = exceptionObject as OperationCanceledException;
1835 !representsCancellation ||
1837 (eoAsEdi != null && eoAsEdi.SourceException is OperationCanceledException),
1838 "representsCancellation should be true only if an OCE was provided.");
1842 // WARNING: A great deal of care went into ensuring that
1843 // AddException() and GetExceptions() are never called
1844 // simultaneously. See comment at start of GetExceptions().
1847 // Lazily initialize the holder, ensuring only one thread wins.
1848 var props = EnsureContingentPropertiesInitialized();
1849 if (props.m_exceptionsHolder == null)
1851 TaskExceptionHolder holder = new TaskExceptionHolder(this);
1852 if (Interlocked.CompareExchange(ref props.m_exceptionsHolder, holder, null) != null)
1854 // If someone else already set the value, suppress finalization.
1855 holder.MarkAsHandled(false);
1861 props.m_exceptionsHolder.Add(exceptionObject, representsCancellation);
1866 /// Returns a list of exceptions by aggregating the holder's contents. Or null if
1867 /// no exceptions have been thrown.
1869 /// <param name="includeTaskCanceledExceptions">Whether to include a TCE if cancelled.</param>
1870 /// <returns>An aggregate exception, or null if no exceptions have been caught.</returns>
1871 private AggregateException GetExceptions(bool includeTaskCanceledExceptions)
1874 // WARNING: The Task/Task<TResult>/TaskCompletionSource classes
1875 // have all been carefully crafted to insure that GetExceptions()
1876 // is never called while AddException() is being called. There
1877 // are locks taken on m_contingentProperties in several places:
1879 // -- Task<TResult>.TrySetException(): The lock allows the
1880 // task to be set to Faulted state, and all exceptions to
1881 // be recorded, in one atomic action.
1883 // -- Task.Exception_get(): The lock ensures that Task<TResult>.TrySetException()
1884 // is allowed to complete its operation before Task.Exception_get()
1885 // can access GetExceptions().
1887 // -- Task.ThrowIfExceptional(): The lock insures that Wait() will
1888 // not attempt to call GetExceptions() while Task<TResult>.TrySetException()
1889 // is in the process of calling AddException().
1891 // For "regular" tasks, we effectively keep AddException() and GetException()
1892 // from being called concurrently by the way that the state flows. Until
1893 // a Task is marked Faulted, Task.Exception_get() returns null. And
1894 // a Task is not marked Faulted until it and all of its children have
1895 // completed, which means that all exceptions have been recorded.
1897 // It might be a lot easier to follow all of this if we just required
1898 // that all calls to GetExceptions() and AddExceptions() were made
1899 // under a lock on m_contingentProperties. But that would also
1900 // increase our lock occupancy time and the frequency with which we
1901 // would need to take the lock.
1903 // If you add a call to GetExceptions() anywhere in the code,
1904 // please continue to maintain the invariant that it can't be
1905 // called when AddException() is being called.
1908 // We'll lazily create a TCE if the task has been canceled.
1909 Exception canceledException = null;
1910 if (includeTaskCanceledExceptions && IsCanceled)
1913 // Ideally we'd just use the cached OCE from this.GetCancellationExceptionDispatchInfo()
1914 // here. However, that would result in a potentially breaking change from .NET 4, which
1915 // has the code here that throws a new exception instead of the original, and the EDI
1916 // may not contain a TCE, but an OCE or any OCE-derived type, which would mean we'd be
1917 // propagating an exception of a different type.
1918 canceledException = new TaskCanceledException(this);
1921 if (ExceptionRecorded)
1923 // There are exceptions; get the aggregate and optionally add the canceled
1924 // exception to the aggregate (if applicable).
1925 Debug.Assert(m_contingentProperties != null); // ExceptionRecorded ==> m_contingentProperties != null
1927 // No need to lock around this, as other logic prevents the consumption of exceptions
1928 // before they have been completely processed.
1929 return m_contingentProperties.m_exceptionsHolder.CreateExceptionObject(false, canceledException);
1931 else if (canceledException != null)
1933 // No exceptions, but there was a cancelation. Aggregate and return it.
1934 return new AggregateException(canceledException);
1940 /// <summary>Gets the exception dispatch infos once the task has faulted.</summary>
1941 internal ReadOnlyCollection<ExceptionDispatchInfo> GetExceptionDispatchInfos()
1943 bool exceptionsAvailable = IsFaulted && ExceptionRecorded;
1944 Debug.Assert(exceptionsAvailable, "Must only be used when the task has faulted with exceptions.");
1945 return exceptionsAvailable ?
1946 m_contingentProperties.m_exceptionsHolder.GetExceptionDispatchInfos() :
1947 new ReadOnlyCollection<ExceptionDispatchInfo>(new ExceptionDispatchInfo[0]);
1950 /// <summary>Gets the ExceptionDispatchInfo containing the OperationCanceledException for this task.</summary>
1951 /// <returns>The ExceptionDispatchInfo. May be null if no OCE was stored for the task.</returns>
1952 internal ExceptionDispatchInfo GetCancellationExceptionDispatchInfo()
1954 Debug.Assert(IsCanceled, "Must only be used when the task has canceled.");
1955 return Volatile.Read(ref m_contingentProperties)?.m_exceptionsHolder?.GetCancellationExceptionDispatchInfo(); // may be null
1959 /// Throws an aggregate exception if the task contains exceptions.
1961 internal void ThrowIfExceptional(bool includeTaskCanceledExceptions)
1963 Debug.Assert(IsCompleted, "ThrowIfExceptional(): Expected IsCompleted == true");
1965 Exception exception = GetExceptions(includeTaskCanceledExceptions);
1966 if (exception != null)
1968 UpdateExceptionObservedStatus();
1974 /// Checks whether this is an attached task, and whether we are being called by the parent task.
1975 /// And sets the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag based on that.
1977 /// This is meant to be used internally when throwing an exception, and when WaitAll is gathering
1978 /// exceptions for tasks it waited on. If this flag gets set, the implicit wait on children
1979 /// will skip exceptions to prevent duplication.
1981 /// This should only be called when this task has completed with an exception
1984 internal void UpdateExceptionObservedStatus()
1986 Task parent = m_contingentProperties?.m_parent;
1987 if ((parent != null)
1988 && ((Options & TaskCreationOptions.AttachedToParent) != 0)
1989 && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
1990 && Task.InternalCurrent == parent)
1992 m_stateFlags |= TASK_STATE_EXCEPTIONOBSERVEDBYPARENT;
1997 /// Checks whether the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag is set,
1998 /// This will only be used by the implicit wait to prevent double throws
2001 internal bool IsExceptionObservedByParent
2005 return (m_stateFlags & TASK_STATE_EXCEPTIONOBSERVEDBYPARENT) != 0;
2010 /// Checks whether the body was ever invoked. Used by task scheduler code to verify custom schedulers actually ran the task.
2012 internal bool IsDelegateInvoked
2016 return (m_stateFlags & TASK_STATE_DELEGATE_INVOKED) != 0;
2021 /// Signals completion of this particular task.
2023 /// The userDelegateExecute parameter indicates whether this Finish() call comes following the
2024 /// full execution of the user delegate.
2026 /// If userDelegateExecute is false, it mean user delegate wasn't invoked at all (either due to
2027 /// a cancellation request, or because this task is a promise style Task). In this case, the steps
2028 /// involving child tasks (i.e. WaitForChildren) will be skipped.
2031 internal void Finish(bool userDelegateExecute)
2033 if (m_contingentProperties == null)
2039 FinishSlow(userDelegateExecute);
2043 private void FinishSlow(bool userDelegateExecute)
2045 Debug.Assert(userDelegateExecute || m_contingentProperties != null);
2047 if (!userDelegateExecute)
2049 // delegate didn't execute => no children. We can safely call the remaining finish stages
2054 ContingentProperties props = m_contingentProperties;
2056 // Count of 1 => either all children finished, or there were none. Safe to complete ourselves
2057 // without paying the price of an Interlocked.Decrement.
2058 if ((props.m_completionCountdown == 1) ||
2059 Interlocked.Decrement(ref props.m_completionCountdown) == 0) // Reaching this sub clause means there may be remaining active children,
2060 // and we could be racing with one of them to call FinishStageTwo().
2061 // So whoever does the final Interlocked.Dec is responsible to finish.
2067 // Apparently some children still remain. It will be up to the last one to process the completion of this task on their own thread.
2068 // We will now yield the thread back to ThreadPool. Mark our state appropriately before getting out.
2070 // We have to use an atomic update for this and make sure not to overwrite a final state,
2071 // because at this very moment the last child's thread may be concurrently completing us.
2072 // Otherwise we risk overwriting the TASK_STATE_RAN_TO_COMPLETION, _CANCELED or _FAULTED bit which may have been set by that child task.
2073 // Note that the concurrent update by the last child happening in FinishStageTwo could still wipe out the TASK_STATE_WAITING_ON_CHILDREN flag,
2074 // but it is not critical to maintain, therefore we dont' need to intruduce a full atomic update into FinishStageTwo
2076 AtomicStateUpdate(TASK_STATE_WAITING_ON_CHILDREN, TASK_STATE_FAULTED | TASK_STATE_CANCELED | TASK_STATE_RAN_TO_COMPLETION);
2079 // Now is the time to prune exceptional children. We'll walk the list and removes the ones whose exceptions we might have observed after they threw.
2080 // we use a local variable for exceptional children here because some other thread may be nulling out m_contingentProperties.m_exceptionalChildren
2081 List<Task> exceptionalChildren = props.m_exceptionalChildren;
2082 if (exceptionalChildren != null)
2084 lock (exceptionalChildren)
2086 exceptionalChildren.RemoveAll(s_IsExceptionObservedByParentPredicate); // RemoveAll has better performance than doing it ourselves
2092 // statically allocated delegate for the removeall expression in Finish()
2093 private readonly static Predicate<Task> s_IsExceptionObservedByParentPredicate = new Predicate<Task>((t) => { return t.IsExceptionObservedByParent; });
2096 /// FinishStageTwo is to be executed as soon as we known there are no more children to complete.
2097 /// It can happen i) either on the thread that originally executed this task (if no children were spawned, or they all completed by the time this task's delegate quit)
2098 /// ii) or on the thread that executed the last child.
2100 private void FinishStageTwo()
2102 // At this point, the task is done executing and waiting for its children,
2103 // we can transition our task to a completion state.
2105 ContingentProperties cp = Volatile.Read(ref m_contingentProperties);
2108 AddExceptionsFromChildren(cp);
2111 int completionState;
2112 if (ExceptionRecorded)
2114 completionState = TASK_STATE_FAULTED;
2115 if (AsyncCausalityTracer.LoggingOn)
2116 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Error);
2118 if (Task.s_asyncDebuggingEnabled)
2120 RemoveFromActiveTasks(this.Id);
2123 else if (IsCancellationRequested && IsCancellationAcknowledged)
2125 // We transition into the TASK_STATE_CANCELED final state if the task's CT was signalled for cancellation,
2126 // and the user delegate acknowledged the cancellation request by throwing an OCE,
2127 // and the task hasn't otherwise transitioned into faulted state. (TASK_STATE_FAULTED trumps TASK_STATE_CANCELED)
2129 // If the task threw an OCE without cancellation being requestsed (while the CT not being in signaled state),
2130 // then we regard it as a regular exception
2132 completionState = TASK_STATE_CANCELED;
2133 if (AsyncCausalityTracer.LoggingOn)
2134 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Canceled);
2136 if (Task.s_asyncDebuggingEnabled)
2138 RemoveFromActiveTasks(this.Id);
2143 completionState = TASK_STATE_RAN_TO_COMPLETION;
2144 if (AsyncCausalityTracer.LoggingOn)
2145 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
2147 if (Task.s_asyncDebuggingEnabled)
2149 RemoveFromActiveTasks(this.Id);
2153 // Use Interlocked.Exchange() to effect a memory fence, preventing
2154 // any SetCompleted() (or later) instructions from sneak back before it.
2155 Interlocked.Exchange(ref m_stateFlags, m_stateFlags | completionState);
2157 // Set the completion event if it's been lazy allocated.
2158 // And if we made a cancellation registration, it's now unnecessary.
2159 cp = Volatile.Read(ref m_contingentProperties); // need to re-read after updating state
2163 cp.DeregisterCancellationCallback();
2166 // ready to run continuations and notify parent.
2172 /// Final stage of the task completion code path. Notifies the parent (if any) that another of its children are done, and runs continuations.
2173 /// This function is only separated out from FinishStageTwo because these two operations are also needed to be called from CancellationCleanupLogic()
2175 internal void FinishStageThree()
2177 // Release the action so that holding this task object alive doesn't also
2178 // hold alive the body of the task. We do this before notifying a parent,
2179 // so that if notifying the parent completes the parent and causes
2180 // its synchronous continuations to run, the GC can collect the state
2181 // in the interim. And we do it before finishing continuations, because
2182 // continuations hold onto the task, and therefore are keeping it alive.
2185 // Notify parent if this was an attached task
2186 if (m_contingentProperties != null)
2188 NotifyParentIfPotentiallyAttachedTask();
2191 // Activate continuations (if any).
2192 FinishContinuations();
2195 internal void NotifyParentIfPotentiallyAttachedTask()
2197 Task parent = m_contingentProperties?.m_parent;
2199 && ((parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
2200 && (((TaskCreationOptions)(m_stateFlags & OptionsMask)) & TaskCreationOptions.AttachedToParent) != 0)
2202 parent.ProcessChildCompletion(this);
2207 /// This is called by children of this task when they are completed.
2209 internal void ProcessChildCompletion(Task childTask)
2211 Debug.Assert(childTask != null);
2212 Debug.Assert(childTask.IsCompleted, "ProcessChildCompletion was called for an uncompleted task");
2214 Debug.Assert(childTask.m_contingentProperties?.m_parent == this, "ProcessChildCompletion should only be called for a child of this task");
2216 var props = Volatile.Read(ref m_contingentProperties);
2218 // if the child threw and we haven't observed it we need to save it for future reference
2219 if (childTask.IsFaulted && !childTask.IsExceptionObservedByParent)
2221 // Lazily initialize the child exception list
2222 if (props.m_exceptionalChildren == null)
2224 Interlocked.CompareExchange(ref props.m_exceptionalChildren, new List<Task>(), null);
2227 // In rare situations involving AppDomainUnload, it's possible (though unlikely) for FinishStageTwo() to be called
2228 // multiple times for the same task. In that case, AddExceptionsFromChildren() could be nulling m_exceptionalChildren
2229 // out at the same time that we're processing it, resulting in a NullReferenceException here. We'll protect
2230 // ourselves by caching m_exceptionChildren in a local variable.
2231 List<Task> tmp = props.m_exceptionalChildren;
2241 if (Interlocked.Decrement(ref props.m_completionCountdown) == 0)
2243 // This call came from the final child to complete, and apparently we have previously given up this task's right to complete itself.
2244 // So we need to invoke the final finish stage.
2251 /// This is to be called just before the task does its final state transition.
2252 /// It traverses the list of exceptional children, and appends their aggregate exceptions into this one's exception list
2254 internal void AddExceptionsFromChildren(ContingentProperties props)
2256 Debug.Assert(props != null);
2258 // In rare occurences during AppDomainUnload() processing, it is possible for this method to be called
2259 // simultaneously on the same task from two different contexts. This can result in m_exceptionalChildren
2260 // being nulled out while it is being processed, which could lead to a NullReferenceException. To
2261 // protect ourselves, we'll cache m_exceptionalChildren in a local variable.
2262 List<Task> exceptionalChildren = props.m_exceptionalChildren;
2264 if (exceptionalChildren != null)
2266 // This lock is necessary because even though AddExceptionsFromChildren is last to execute, it may still
2267 // be racing with the code segment at the bottom of Finish() that prunes the exceptional child array.
2268 lock (exceptionalChildren)
2270 foreach (Task task in exceptionalChildren)
2272 // Ensure any exceptions thrown by children are added to the parent.
2273 // In doing this, we are implicitly marking children as being "handled".
2274 Debug.Assert(task.IsCompleted, "Expected all tasks in list to be completed");
2275 if (task.IsFaulted && !task.IsExceptionObservedByParent)
2277 TaskExceptionHolder exceptionHolder = Volatile.Read(ref task.m_contingentProperties).m_exceptionsHolder;
2278 Debug.Assert(exceptionHolder != null);
2280 // No locking necessary since child task is finished adding exceptions
2281 // and concurrent CreateExceptionObject() calls do not constitute
2282 // a concurrency hazard.
2283 AddException(exceptionHolder.CreateExceptionObject(false, null));
2288 // Reduce memory pressure by getting rid of the array
2289 props.m_exceptionalChildren = null;
2294 /// Special purpose Finish() entry point to be used when the task delegate throws a ThreadAbortedException
2295 /// This makes a note in the state flags so that we avoid any costly synchronous operations in the finish codepath
2296 /// such as inlined continuations
2298 /// <param name="delegateRan">Whether the delegate was executed.</param>
2299 internal void FinishThreadAbortedTask(bool delegateRan)
2301 Debug.Assert(m_contingentProperties?.m_exceptionsHolder != null,
2302 "FinishThreadAbortedTask() called on a task whose exception holder wasn't initialized");
2304 m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
2306 // If this method has already been called for this task, or if this task has already completed, then
2307 // return before actually calling Finish().
2308 if (!AtomicStateUpdate(TASK_STATE_THREAD_WAS_ABORTED,
2309 TASK_STATE_THREAD_WAS_ABORTED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED))
2314 Finish(delegateRan);
2318 /// IThreadPoolWorkItem override, which is the entry function for this task when the TP scheduler decides to run it.
2321 void IThreadPoolWorkItem.ExecuteWorkItem()
2323 ExecuteEntryUnsafe();
2327 /// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem. This may occur
2328 /// before Task would otherwise be able to observe it.
2330 void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
2332 // If the task has marked itself as Completed, then it either a) already observed this exception (so we shouldn't handle it here)
2333 // or b) completed before the exception ocurred (in which case it shouldn't count against this Task).
2336 HandleException(tae);
2337 FinishThreadAbortedTask(delegateRan: false);
2342 /// Outermost entry function to execute this task. Handles all aspects of executing a task on the caller thread.
2344 internal bool ExecuteEntry()
2346 // Do atomic state transition from queued to invoked. If we observe a task that's already invoked,
2347 // we will return false so that TaskScheduler.ExecuteTask can throw an exception back to the custom scheduler.
2348 // However we don't want this exception to be throw if the task was already canceled, because it's a
2349 // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler)
2350 int previousState = 0;
2351 if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED,
2352 TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK,
2353 ref previousState) && (previousState & TASK_STATE_CANCELED) == 0)
2355 // This task has already been invoked. Don't invoke it again.
2359 if (!IsCancellationRequested & !IsCanceled)
2361 ExecuteWithThreadLocal(ref t_currentTask);
2365 ExecuteEntryCancellationRequestedOrCanceled();
2371 internal void ExecuteEntryUnsafe() // used instead of ExecuteEntry() when we don't have to worry about double-execution prevent
2373 // Remember that we started running the task delegate.
2374 m_stateFlags |= TASK_STATE_DELEGATE_INVOKED;
2376 if (!IsCancellationRequested & !IsCanceled)
2378 ExecuteWithThreadLocal(ref t_currentTask);
2382 ExecuteEntryCancellationRequestedOrCanceled();
2386 internal void ExecuteEntryCancellationRequestedOrCanceled()
2390 int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED);
2391 if ((prevState & TASK_STATE_CANCELED) == 0)
2393 CancellationCleanupLogic();
2398 // A trick so we can refer to the TLS slot with a byref.
2399 private void ExecuteWithThreadLocal(ref Task currentTaskSlot)
2401 // Remember the current task so we can restore it after running, and then
2402 Task previousTask = currentTaskSlot;
2404 // ETW event for Task Started
2405 var etwLog = TplEtwProvider.Log;
2406 Guid savedActivityID = new Guid();
2407 bool etwIsEnabled = etwLog.IsEnabled();
2410 if (etwLog.TasksSetActivityIds)
2411 EventSource.SetCurrentThreadActivityId(TplEtwProvider.CreateGuidForTaskID(this.Id), out savedActivityID);
2412 // previousTask holds the actual "current task" we want to report in the event
2413 if (previousTask != null)
2414 etwLog.TaskStarted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id);
2416 etwLog.TaskStarted(TaskScheduler.Current.Id, 0, this.Id);
2419 bool loggingOn = AsyncCausalityTracer.LoggingOn;
2421 AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.Execution);
2425 // place the current task into TLS.
2426 currentTaskSlot = this;
2428 // Execute the task body
2431 ExecutionContext ec = CapturedContext;
2434 // No context, just run the task directly.
2439 // Invoke it under the captured ExecutionContext
2440 ExecutionContext.RunInternal(ec, s_ecCallback, this);
2443 catch (Exception exn)
2445 // Record this exception in the task's exception list
2446 HandleException(exn);
2447 if (exn is ThreadAbortException)
2449 // This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to
2450 // skip the regular Finish codepath. In order not to leave the task unfinished, we now call
2451 // FinishThreadAbortedTask here.
2452 FinishThreadAbortedTask(delegateRan: true);
2457 AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.Execution);
2463 currentTaskSlot = previousTask;
2465 // ETW event for Task Completed
2468 // previousTask holds the actual "current task" we want to report in the event
2469 if (previousTask != null)
2470 etwLog.TaskCompleted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id, IsFaulted);
2472 etwLog.TaskCompleted(TaskScheduler.Current.Id, 0, this.Id, IsFaulted);
2474 if (etwLog.TasksSetActivityIds)
2475 EventSource.SetCurrentThreadActivityId(savedActivityID);
2480 private static readonly ContextCallback s_ecCallback = obj => ((Task)obj).InnerInvoke();
2483 /// The actual code which invokes the body of the task. This can be overridden in derived types.
2485 internal virtual void InnerInvoke()
2487 // Invoke the delegate
2488 Debug.Assert(m_action != null, "Null action in InnerInvoke()");
2489 var action = m_action as Action;
2495 var actionWithState = m_action as Action<object>;
2496 if (actionWithState != null)
2498 actionWithState(m_stateObject);
2501 Debug.Fail("Invalid m_action in Task");
2505 /// Performs whatever handling is necessary for an unhandled exception. Normally
2506 /// this just entails adding the exception to the holder object.
2508 /// <param name="unhandledException">The exception that went unhandled.</param>
2509 private void HandleException(Exception unhandledException)
2511 Debug.Assert(unhandledException != null);
2513 OperationCanceledException exceptionAsOce = unhandledException as OperationCanceledException;
2514 if (exceptionAsOce != null && IsCancellationRequested &&
2515 m_contingentProperties.m_cancellationToken == exceptionAsOce.CancellationToken)
2517 // All conditions are satisfied for us to go into canceled state in Finish().
2518 // Mark the acknowledgement. The exception is also stored to enable it to be
2519 // the exception propagated from an await.
2521 SetCancellationAcknowledged();
2522 AddException(exceptionAsOce, representsCancellation: true);
2526 // Other exceptions, including any OCE from the task that doesn't match the tasks' own CT,
2527 // or that gets thrown without the CT being set will be treated as an ordinary exception
2528 // and added to the aggregate.
2530 AddException(unhandledException);
2534 #region Await Support
2535 /// <summary>Gets an awaiter used to await this <see cref="System.Threading.Tasks.Task"/>.</summary>
2536 /// <returns>An awaiter instance.</returns>
2537 /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
2538 public TaskAwaiter GetAwaiter()
2540 return new TaskAwaiter(this);
2543 /// <summary>Configures an awaiter used to await this <see cref="System.Threading.Tasks.Task"/>.</summary>
2544 /// <param name="continueOnCapturedContext">
2545 /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
2547 /// <returns>An object used to await this task.</returns>
2548 public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
2550 return new ConfiguredTaskAwaitable(this, continueOnCapturedContext);
2554 /// Sets a continuation onto the <see cref="System.Threading.Tasks.Task"/>.
2555 /// The continuation is scheduled to run in the current synchronization context is one exists,
2556 /// otherwise in the current task scheduler.
2558 /// <param name="continuationAction">The action to invoke when the <see cref="System.Threading.Tasks.Task"/> has completed.</param>
2559 /// <param name="continueOnCapturedContext">
2560 /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
2562 /// <param name="flowExecutionContext">Whether to flow ExecutionContext across the await.</param>
2563 /// <exception cref="System.InvalidOperationException">The awaiter was not properly initialized.</exception>
2564 internal void SetContinuationForAwait(
2565 Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext)
2567 Debug.Assert(continuationAction != null);
2569 // Create the best AwaitTaskContinuation object given the request.
2570 // If this remains null by the end of the function, we can use the
2571 // continuationAction directly without wrapping it.
2572 TaskContinuation tc = null;
2574 // If the user wants the continuation to run on the current "context" if there is one...
2575 if (continueOnCapturedContext)
2577 // First try getting the current synchronization context.
2578 // If the current context is really just the base SynchronizationContext type,
2579 // which is intended to be equivalent to not having a current SynchronizationContext at all,
2580 // then ignore it. This helps with performance by avoiding unnecessary posts and queueing
2581 // of work items, but more so it ensures that if code happens to publish the default context
2582 // as current, it won't prevent usage of a current task scheduler if there is one.
2583 var syncCtx = SynchronizationContext.Current;
2584 if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
2586 tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext);
2590 // If there was no SynchronizationContext, then try for the current scheduler.
2591 // We only care about it if it's not the default.
2592 var scheduler = TaskScheduler.InternalCurrent;
2593 if (scheduler != null && scheduler != TaskScheduler.Default)
2595 tc = new TaskSchedulerAwaitTaskContinuation(scheduler, continuationAction, flowExecutionContext);
2600 if (tc == null && flowExecutionContext)
2602 // We're targeting the default scheduler, so we can use the faster path
2603 // that assumes the default, and thus we don't need to store it. If we're flowing
2604 // ExecutionContext, we need to capture it and wrap it in an AwaitTaskContinuation.
2605 // Otherwise, we're targeting the default scheduler and we don't need to flow ExecutionContext, so
2606 // we don't actually need a continuation object. We can just store/queue the action itself.
2607 tc = new AwaitTaskContinuation(continuationAction, flowExecutionContext: true);
2610 // Now register the continuation, and if we couldn't register it because the task is already completing,
2611 // process the continuation directly (in which case make sure we schedule the continuation
2612 // rather than inlining it, the latter of which could result in a rare but possible stack overflow).
2615 if (!AddTaskContinuation(tc, addBeforeOthers: false))
2616 tc.Run(this, bCanInlineContinuationTask: false);
2620 Debug.Assert(!flowExecutionContext, "We already determined we're not required to flow context.");
2621 if (!AddTaskContinuation(continuationAction, addBeforeOthers: false))
2622 AwaitTaskContinuation.UnsafeScheduleAction(continuationAction, this);
2627 /// Sets a continuation onto the <see cref="System.Threading.Tasks.Task"/>.
2628 /// The continuation is scheduled to run in the current synchronization context is one exists,
2629 /// otherwise in the current task scheduler.
2631 /// <param name="stateMachineBox">The action to invoke when the <see cref="System.Threading.Tasks.Task"/> has completed.</param>
2632 /// <param name="continueOnCapturedContext">
2633 /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
2635 /// <exception cref="System.InvalidOperationException">The awaiter was not properly initialized.</exception>
2636 internal void UnsafeSetContinuationForAwait(IAsyncStateMachineBox stateMachineBox, bool continueOnCapturedContext)
2638 Debug.Assert(stateMachineBox != null);
2640 // If the caller wants to continue on the current context/scheduler and there is one,
2641 // fall back to using the state machine's delegate.
2642 if (continueOnCapturedContext)
2644 SynchronizationContext syncCtx = SynchronizationContext.Current;
2645 if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
2647 var tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, stateMachineBox.MoveNextAction, flowExecutionContext: false);
2648 if (!AddTaskContinuation(tc, addBeforeOthers: false))
2650 tc.Run(this, canInlineContinuationTask: false);
2656 TaskScheduler scheduler = TaskScheduler.InternalCurrent;
2657 if (scheduler != null && scheduler != TaskScheduler.Default)
2659 var tc = new TaskSchedulerAwaitTaskContinuation(scheduler, stateMachineBox.MoveNextAction, flowExecutionContext: false);
2660 if (!AddTaskContinuation(tc, addBeforeOthers: false))
2662 tc.Run(this, canInlineContinuationTask: false);
2669 // Otherwise, add the state machine box directly as the ITaskCompletionAction continuation.
2670 // If we're unable to because the task has already completed, queue the delegate.
2671 if (!AddTaskContinuation(stateMachineBox, addBeforeOthers: false))
2673 AwaitTaskContinuation.UnsafeScheduleAction(stateMachineBox.MoveNextAction, this);
2677 /// <summary>Creates an awaitable that asynchronously yields back to the current context when awaited.</summary>
2679 /// A context that, when awaited, will asynchronously transition back into the current context at the
2680 /// time of the await. If the current SynchronizationContext is non-null, that is treated as the current context.
2681 /// Otherwise, TaskScheduler.Current is treated as the current context.
2683 public static YieldAwaitable Yield()
2685 return new YieldAwaitable();
2690 /// Waits for the <see cref="Task"/> to complete execution.
2692 /// <exception cref="T:System.AggregateException">
2693 /// The <see cref="Task"/> was canceled -or- an exception was thrown during
2694 /// the execution of the <see cref="Task"/>.
2701 Wait(Timeout.Infinite, default(CancellationToken));
2704 Debug.Assert(waitResult, "expected wait to succeed");
2709 /// Waits for the <see cref="Task"/> to complete execution.
2711 /// <param name="timeout">
2712 /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
2713 /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
2716 /// true if the <see cref="Task"/> completed execution within the allotted time; otherwise, false.
2718 /// <exception cref="T:System.AggregateException">
2719 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
2722 /// <exception cref="T:System.ArgumentOutOfRangeException">
2723 /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
2724 /// infinite time-out -or- timeout is greater than
2725 /// <see cref="System.Int32.MaxValue"/>.
2727 public bool Wait(TimeSpan timeout)
2729 long totalMilliseconds = (long)timeout.TotalMilliseconds;
2730 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
2732 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout);
2735 return Wait((int)totalMilliseconds, default(CancellationToken));
2740 /// Waits for the <see cref="Task"/> to complete execution.
2742 /// <param name="cancellationToken">
2743 /// A <see cref="CancellationToken"/> to observe while waiting for the task to complete.
2745 /// <exception cref="T:System.OperationCanceledException">
2746 /// The <paramref name="cancellationToken"/> was canceled.
2748 /// <exception cref="T:System.AggregateException">
2749 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
2752 public void Wait(CancellationToken cancellationToken)
2754 Wait(Timeout.Infinite, cancellationToken);
2759 /// Waits for the <see cref="Task"/> to complete execution.
2761 /// <param name="millisecondsTimeout">
2762 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
2763 /// wait indefinitely.</param>
2764 /// <returns>true if the <see cref="Task"/> completed execution within the allotted time; otherwise,
2767 /// <exception cref="T:System.ArgumentOutOfRangeException">
2768 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
2769 /// infinite time-out.
2771 /// <exception cref="T:System.AggregateException">
2772 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
2775 public bool Wait(int millisecondsTimeout)
2777 return Wait(millisecondsTimeout, default(CancellationToken));
2782 /// Waits for the <see cref="Task"/> to complete execution.
2784 /// <param name="millisecondsTimeout">
2785 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
2786 /// wait indefinitely.
2788 /// <param name="cancellationToken">
2789 /// A <see cref="CancellationToken"/> to observe while waiting for the task to complete.
2792 /// true if the <see cref="Task"/> completed execution within the allotted time; otherwise, false.
2794 /// <exception cref="T:System.AggregateException">
2795 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
2798 /// <exception cref="T:System.ArgumentOutOfRangeException">
2799 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
2800 /// infinite time-out.
2802 /// <exception cref="T:System.OperationCanceledException">
2803 /// The <paramref name="cancellationToken"/> was canceled.
2805 public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
2807 if (millisecondsTimeout < -1)
2809 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.millisecondsTimeout);
2812 // Return immediately if we know that we've completed "clean" -- no exceptions, no cancellations
2813 // and if no notification to the debugger is required
2814 if (!IsWaitNotificationEnabledOrNotRanToCompletion) // (!DebuggerBitSet && RanToCompletion)
2817 // Wait, and then return if we're still not done.
2818 if (!InternalWait(millisecondsTimeout, cancellationToken))
2821 if (IsWaitNotificationEnabledOrNotRanToCompletion) // avoid a few unnecessary volatile reads if we completed successfully
2823 // Notify the debugger of the wait completion if it's requested such a notification
2824 NotifyDebuggerOfWaitCompletionIfNecessary();
2826 // If cancellation was requested and the task was canceled, throw an
2827 // OperationCanceledException. This is prioritized ahead of the ThrowIfExceptional
2828 // call to bring more determinism to cases where the same token is used to
2829 // cancel the Wait and to cancel the Task. Otherwise, there's a race condition between
2830 // whether the Wait or the Task observes the cancellation request first,
2831 // and different exceptions result from the different cases.
2832 if (IsCanceled) cancellationToken.ThrowIfCancellationRequested();
2834 // If an exception occurred, or the task was cancelled, throw an exception.
2835 ThrowIfExceptional(true);
2838 Debug.Assert((m_stateFlags & TASK_STATE_FAULTED) == 0, "Task.Wait() completing when in Faulted state.");
2843 // Convenience method that wraps any scheduler exception in a TaskSchedulerException
2845 private bool WrappedTryRunInline()
2847 if (m_taskScheduler == null)
2852 return m_taskScheduler.TryRunInline(this, true);
2856 // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
2857 // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute
2858 if (!(e is ThreadAbortException))
2860 TaskSchedulerException tse = new TaskSchedulerException(e);
2871 /// The core wait function, which is only accessible internally. It's meant to be used in places in TPL code where
2872 /// the current context is known or cached.
2874 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
2875 internal bool InternalWait(int millisecondsTimeout, CancellationToken cancellationToken) =>
2876 InternalWaitCore(millisecondsTimeout, cancellationToken);
2878 // Separated out to allow it to be optimized (caller is marked NoOptimization for VS parallel debugger
2879 // to be able to see the method on the stack and inspect arguments).
2880 private bool InternalWaitCore(int millisecondsTimeout, CancellationToken cancellationToken)
2882 // If the task has already completed, there's nothing to wait for.
2883 bool returnValue = IsCompleted;
2889 // ETW event for Task Wait Begin
2890 var etwLog = TplEtwProvider.Log;
2891 bool etwIsEnabled = etwLog.IsEnabled();
2894 Task currentTask = Task.InternalCurrent;
2895 etwLog.TaskWaitBegin(
2896 (currentTask != null ? currentTask.m_taskScheduler.Id : TaskScheduler.Default.Id), (currentTask != null ? currentTask.Id : 0),
2897 this.Id, TplEtwProvider.TaskWaitBehavior.Synchronous, 0);
2900 // Alert a listening debugger that we can't make forward progress unless it slips threads.
2901 // We call NOCTD for two reasons:
2902 // 1. If the task runs on another thread, then we'll be blocked here indefinitely.
2903 // 2. If the task runs inline but takes some time to complete, it will suffer ThreadAbort with possible state corruption,
2904 // and it is best to prevent this unless the user explicitly asks to view the value with thread-slipping enabled.
2905 Debugger.NotifyOfCrossThreadDependency();
2907 // We will attempt inline execution only if an infinite wait was requested
2908 // Inline execution doesn't make sense for finite timeouts and if a cancellation token was specified
2909 // because we don't know how long the task delegate will take.
2910 if (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled &&
2911 WrappedTryRunInline() && IsCompleted) // TryRunInline doesn't guarantee completion, as there may be unfinished children.
2917 returnValue = SpinThenBlockingWait(millisecondsTimeout, cancellationToken);
2920 Debug.Assert(IsCompleted || millisecondsTimeout != Timeout.Infinite);
2922 // ETW event for Task Wait End
2925 Task currentTask = Task.InternalCurrent;
2926 if (currentTask != null)
2928 etwLog.TaskWaitEnd(currentTask.m_taskScheduler.Id, currentTask.Id, this.Id);
2932 etwLog.TaskWaitEnd(TaskScheduler.Default.Id, 0, this.Id);
2934 // logically the continuation is empty so we immediately fire
2935 etwLog.TaskWaitContinuationComplete(this.Id);
2941 // An MRES that gets set when Invoke is called. This replaces old logic that looked like this:
2942 // ManualResetEventSlim mres = new ManualResetEventSlim(false, 0);
2943 // Action<Task> completionAction = delegate {mres.Set();}
2944 // AddCompletionAction(completionAction);
2946 // SetOnInvokeMres mres = new SetOnInvokeMres();
2947 // AddCompletionAction(mres, addBeforeOthers: true);
2948 // which saves a couple of allocations.
2950 // Used in SpinThenBlockingWait (below), but could be seen as a general purpose mechanism.
2951 private sealed class SetOnInvokeMres : ManualResetEventSlim, ITaskCompletionAction
2953 internal SetOnInvokeMres() : base(false, 0) { }
2954 public void Invoke(Task completingTask) { Set(); }
2955 public bool InvokeMayRunArbitraryCode { get { return false; } }
2959 /// Waits for the task to complete, for a timeout to occur, or for cancellation to be requested.
2960 /// The method first spins and then falls back to blocking on a new event.
2962 /// <param name="millisecondsTimeout">The timeout.</param>
2963 /// <param name="cancellationToken">The token.</param>
2964 /// <returns>true if the task is completed; otherwise, false.</returns>
2965 private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken)
2967 bool infiniteWait = millisecondsTimeout == Timeout.Infinite;
2968 uint startTimeTicks = infiniteWait ? 0 : (uint)Environment.TickCount;
2969 bool returnValue = SpinWait(millisecondsTimeout);
2972 var mres = new SetOnInvokeMres();
2975 AddCompletionAction(mres, addBeforeOthers: true);
2978 returnValue = mres.Wait(Timeout.Infinite, cancellationToken);
2982 uint elapsedTimeTicks = ((uint)Environment.TickCount) - startTimeTicks;
2983 if (elapsedTimeTicks < millisecondsTimeout)
2985 returnValue = mres.Wait((int)(millisecondsTimeout - elapsedTimeTicks), cancellationToken);
2991 if (!IsCompleted) RemoveContinuation(mres);
2992 // Don't Dispose of the MRES, because the continuation off of this task may
2993 // still be running. This is ok, however, as we never access the MRES' WaitHandle,
2994 // and thus no finalizable resources are actually allocated.
3001 /// Spins briefly while checking IsCompleted
3003 /// <param name="millisecondsTimeout">The timeout.</param>
3004 /// <returns>true if the task is completed; otherwise, false.</returns>
3005 /// <exception cref="System.OperationCanceledException">The wait was canceled.</exception>
3006 private bool SpinWait(int millisecondsTimeout)
3008 if (IsCompleted) return true;
3010 if (millisecondsTimeout == 0)
3012 // For 0-timeouts, we just return immediately.
3016 int spinCount = Threading.SpinWait.SpinCountforSpinBeforeWait;
3017 var spinner = new SpinWait();
3018 while (spinner.Count < spinCount)
3020 spinner.SpinOnce(Threading.SpinWait.Sleep1ThresholdForSpinBeforeWait);
3032 /// Cancels the <see cref="Task"/>.
3034 /// <param name="bCancelNonExecutingOnly">
3035 /// Indicates whether we should only cancel non-invoked tasks.
3036 /// For the default scheduler this option will only be serviced through TryDequeue.
3037 /// For custom schedulers we also attempt an atomic state transition.
3039 /// <returns>true if the task was successfully canceled; otherwise, false.</returns>
3040 internal bool InternalCancel(bool bCancelNonExecutingOnly)
3042 Debug.Assert((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) == 0, "Task.InternalCancel() did not expect promise-style task");
3044 bool bPopSucceeded = false;
3045 bool mustCleanup = false;
3047 TaskSchedulerException tse = null;
3049 // If started, and running in a task context, we can try to pop the chore.
3050 if ((m_stateFlags & TASK_STATE_STARTED) != 0)
3052 TaskScheduler ts = m_taskScheduler;
3056 bPopSucceeded = (ts != null) && ts.TryDequeue(this);
3060 // TryDequeue threw. We don't know whether the task was properly dequeued or not. So we must let the rest of
3061 // the cancellation logic run its course (record the request, attempt atomic state transition and do cleanup where appropriate)
3062 // Here we will only record a TaskSchedulerException, which will later be thrown at function exit.
3064 if (!(e is ThreadAbortException))
3066 tse = new TaskSchedulerException(e);
3070 bool bRequiresAtomicStartTransition = ts != null && ts.RequiresAtomicStartTransition;
3072 if (!bPopSucceeded && bCancelNonExecutingOnly && bRequiresAtomicStartTransition)
3074 // The caller requested cancellation of non-invoked tasks only, and TryDequeue was one way of doing it...
3075 // Since that seems to have failed, we should now try an atomic state transition (from non-invoked state to canceled)
3076 // An atomic transition here is only safe if we know we're on a custom task scheduler, which also forces a CAS on ExecuteEntry
3078 // Even though this task can't have any children, we should be ready for handling any continuations that
3079 // may be attached to it (although currently
3080 // So we need to remeber whether we actually did the flip, so we can do clean up (finish continuations etc)
3081 mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_DELEGATE_INVOKED | TASK_STATE_CANCELED);
3083 // PS: This is slightly different from the regular cancellation codepath
3084 // since we record the cancellation request *after* doing the state transition.
3085 // However that shouldn't matter too much because the task was never invoked, thus can't have children
3089 if (!bCancelNonExecutingOnly || bPopSucceeded || mustCleanup)
3091 // Record the cancellation request.
3092 RecordInternalCancellationRequest();
3094 // Determine whether we need to clean up
3095 // This will be the case
3096 // 1) if we were able to pop, and we are able to update task state to TASK_STATE_CANCELED
3097 // 2) if the task seems to be yet unstarted, and we can transition to
3098 // TASK_STATE_CANCELED before anyone else can transition into _STARTED or _CANCELED or
3099 // _RAN_TO_COMPLETION or _FAULTED
3100 // Note that we do not check for TASK_STATE_COMPLETION_RESERVED. That only applies to promise-style
3101 // tasks, and a promise-style task should not enter into this codepath.
3104 // hitting this would mean something wrong with the AtomicStateUpdate above
3105 Debug.Assert(!mustCleanup, "Possibly an invalid state transition call was made in InternalCancel()");
3107 // Include TASK_STATE_DELEGATE_INVOKED in "illegal" bits to protect against the situation where
3108 // TS.TryDequeue() returns true but the task is still left on the queue.
3109 mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_CANCELED | TASK_STATE_DELEGATE_INVOKED);
3111 else if (!mustCleanup && (m_stateFlags & TASK_STATE_STARTED) == 0)
3113 mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED,
3114 TASK_STATE_CANCELED | TASK_STATE_STARTED | TASK_STATE_RAN_TO_COMPLETION |
3115 TASK_STATE_FAULTED | TASK_STATE_DELEGATE_INVOKED);
3118 // do the cleanup (i.e. set completion event and finish continuations)
3121 CancellationCleanupLogic();
3128 return (mustCleanup);
3131 // Breaks out logic for recording a cancellation request
3132 internal void RecordInternalCancellationRequest()
3134 // Record the cancellation request.
3135 EnsureContingentPropertiesInitialized().m_internalCancellationRequested = CANCELLATION_REQUESTED;
3138 // Breaks out logic for recording a cancellation request
3139 // This overload should only be used for promise tasks where no cancellation token
3140 // was supplied when the task was created.
3141 internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord)
3143 RecordInternalCancellationRequest();
3145 Debug.Assert((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0, "Task.RecordInternalCancellationRequest(CancellationToken) only valid for promise-style task");
3146 Debug.Assert(m_contingentProperties.m_cancellationToken == default(CancellationToken));
3148 // Store the supplied cancellation token as this task's token.
3149 // Waiting on this task will then result in an OperationCanceledException containing this token.
3150 if (tokenToRecord != default(CancellationToken))
3152 m_contingentProperties.m_cancellationToken = tokenToRecord;
3156 // Breaks out logic for recording a cancellation request
3157 // This overload should only be used for promise tasks where no cancellation token
3158 // was supplied when the task was created.
3159 internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord, object cancellationException)
3161 RecordInternalCancellationRequest(tokenToRecord);
3163 // Store the supplied cancellation exception
3164 if (cancellationException != null)
3167 var oce = cancellationException as OperationCanceledException;
3170 var edi = cancellationException as ExceptionDispatchInfo;
3171 Debug.Assert(edi != null, "Expected either an OCE or an EDI");
3172 oce = edi.SourceException as OperationCanceledException;
3173 Debug.Assert(oce != null, "Expected EDI to contain an OCE");
3175 Debug.Assert(oce.CancellationToken == tokenToRecord,
3176 "Expected OCE's token to match the provided token.");
3178 AddException(cancellationException, representsCancellation: true);
3182 // ASSUMES THAT A SUCCESSFUL CANCELLATION HAS JUST OCCURRED ON THIS TASK!!!
3183 // And this method should be called at most once per task.
3184 internal void CancellationCleanupLogic()
3186 Debug.Assert((m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_COMPLETION_RESERVED)) != 0, "Task.CancellationCleanupLogic(): Task not canceled or reserved.");
3187 // I'd like to do this, but there is a small window for a race condition. If someone calls Wait() between InternalCancel() and
3188 // here, that will set m_completionEvent, leading to a meaningless/harmless assertion.
3189 //Debug.Assert((m_completionEvent == null) || !m_completionEvent.IsSet, "Task.CancellationCleanupLogic(): Completion event already set.");
3191 // This may have been set already, but we need to make sure.
3192 Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED);
3194 // Fire completion event if it has been lazily initialized
3195 var cp = Volatile.Read(ref m_contingentProperties);
3199 cp.DeregisterCancellationCallback();
3202 if (AsyncCausalityTracer.LoggingOn)
3203 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Canceled);
3205 if (Task.s_asyncDebuggingEnabled)
3207 RemoveFromActiveTasks(this.Id);
3210 // Notify parents, fire continuations, other cleanup.
3216 /// Sets the task's cancellation acknowledged flag.
3218 private void SetCancellationAcknowledged()
3220 Debug.Assert(this == Task.InternalCurrent, "SetCancellationAcknowledged() should only be called while this is still the current task");
3221 Debug.Assert(IsCancellationRequested, "SetCancellationAcknowledged() should not be called if the task's CT wasn't signaled");
3223 m_stateFlags |= TASK_STATE_CANCELLATIONACKNOWLEDGED;
3228 // Continuation passing functionality (aka ContinueWith)
3235 /// Runs all of the continuations, as appropriate.
3237 internal void FinishContinuations()
3239 // Atomically store the fact that this task is completing. From this point on, the adding of continuations will
3240 // result in the continuations being run/launched directly rather than being added to the continuation list.
3241 // Then if we grabbed any continuations, run them.
3242 object continuationObject = Interlocked.Exchange(ref m_continuationObject, s_taskCompletionSentinel);
3243 if (continuationObject != null)
3245 RunContinuations(continuationObject);
3249 private void RunContinuations(object continuationObject) // separated out of FinishContinuations to enable it to be inlined
3251 Debug.Assert(continuationObject != null);
3253 TplEtwProvider etw = TplEtwProvider.Log;
3254 bool tplEtwProviderLoggingEnabled = etw.IsEnabled();
3255 if (tplEtwProviderLoggingEnabled)
3257 etw.RunningContinuation(Id, continuationObject);
3260 if (AsyncCausalityTracer.LoggingOn)
3261 AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.CompletionNotification);
3263 // Skip synchronous execution of continuations if this task's thread was aborted
3264 bool bCanInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) ||
3265 (RuntimeThread.CurrentThread.ThreadState == ThreadState.AbortRequested) ||
3266 ((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0));
3268 // Handle the single-Action case
3269 Action singleAction = continuationObject as Action;
3270 if (singleAction != null)
3272 AwaitTaskContinuation.RunOrScheduleAction(singleAction, bCanInlineContinuations, ref t_currentTask);
3273 LogFinishCompletionNotification();
3277 // Handle the single-ITaskCompletionAction case
3278 ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction;
3279 if (singleTaskCompletionAction != null)
3281 if (bCanInlineContinuations || !singleTaskCompletionAction.InvokeMayRunArbitraryCode)
3283 singleTaskCompletionAction.Invoke(this);
3287 ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(singleTaskCompletionAction, this), forceGlobal: false);
3289 LogFinishCompletionNotification();
3293 // Handle the single-TaskContinuation case
3294 TaskContinuation singleTaskContinuation = continuationObject as TaskContinuation;
3295 if (singleTaskContinuation != null)
3297 singleTaskContinuation.Run(this, bCanInlineContinuations);
3298 LogFinishCompletionNotification();
3302 // Not a single; it must be a list.
3303 List<object> continuations = (List<object>)continuationObject;
3306 // Begin processing of continuation list
3309 // Wait for any concurrent adds or removes to be retired
3310 lock (continuations) { }
3311 int continuationCount = continuations.Count;
3313 // Fire the asynchronous continuations first ...
3314 for (int i = 0; i < continuationCount; i++)
3316 // Synchronous continuation tasks will have the ExecuteSynchronously option,
3317 // and we're looking for asynchronous tasks...
3318 var tc = continuations[i] as StandardTaskContinuation;
3319 if (tc != null && (tc.m_options & TaskContinuationOptions.ExecuteSynchronously) == 0)
3321 if (tplEtwProviderLoggingEnabled)
3323 etw.RunningContinuationList(Id, i, tc);
3325 continuations[i] = null; // so that we can skip this later
3326 tc.Run(this, bCanInlineContinuations);
3330 // ... and then fire the synchronous continuations (if there are any).
3331 // This includes ITaskCompletionAction, AwaitTaskContinuations, and
3332 // Action delegates, which are all by default implicitly synchronous.
3333 for (int i = 0; i < continuationCount; i++)
3335 object currentContinuation = continuations[i];
3336 if (currentContinuation == null) continue;
3337 continuations[i] = null; // to enable free'ing up memory earlier
3338 if (tplEtwProviderLoggingEnabled)
3340 etw.RunningContinuationList(Id, i, currentContinuation);
3343 // If the continuation is an Action delegate, it came from an await continuation,
3344 // and we should use AwaitTaskContinuation to run it.
3345 Action ad = currentContinuation as Action;
3348 AwaitTaskContinuation.RunOrScheduleAction(ad, bCanInlineContinuations, ref t_currentTask);
3352 // If it's a TaskContinuation object of some kind, invoke it.
3353 TaskContinuation tc = currentContinuation as TaskContinuation;
3356 // We know that this is a synchronous continuation because the
3357 // asynchronous ones have been weeded out
3358 tc.Run(this, bCanInlineContinuations);
3360 // Otherwise, it must be an ITaskCompletionAction, so invoke it.
3363 Debug.Assert(currentContinuation is ITaskCompletionAction, "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction");
3364 var action = (ITaskCompletionAction)currentContinuation;
3366 if (bCanInlineContinuations || !action.InvokeMayRunArbitraryCode)
3368 action.Invoke(this);
3372 ThreadPool.UnsafeQueueCustomWorkItem(new CompletionActionInvoker(action, this), forceGlobal: false);
3378 LogFinishCompletionNotification();
3381 private void LogFinishCompletionNotification()
3383 if (AsyncCausalityTracer.LoggingOn)
3384 AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.CompletionNotification);
3387 #region Continuation methods
3389 #region Action<Task> continuation
3391 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3393 /// <param name="continuationAction">
3394 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3395 /// passed the completed task as an argument.
3397 /// <returns>A new continuation <see cref="Task"/>.</returns>
3399 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3400 /// completed, whether it completes due to running to completion successfully, faulting due to an
3401 /// unhandled exception, or exiting out early due to being canceled.
3403 /// <exception cref="T:System.ArgumentNullException">
3404 /// The <paramref name="continuationAction"/> argument is null.
3406 public Task ContinueWith(Action<Task> continuationAction)
3408 return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None);
3412 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3414 /// <param name="continuationAction">
3415 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3416 /// passed the completed task as an argument.
3418 /// <param name="cancellationToken"> The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3419 /// <returns>A new continuation <see cref="Task"/>.</returns>
3421 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3422 /// completed, whether it completes due to running to completion successfully, faulting due to an
3423 /// unhandled exception, or exiting out early due to being canceled.
3425 /// <exception cref="T:System.ArgumentNullException">
3426 /// The <paramref name="continuationAction"/> argument is null.
3428 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3429 /// has already been disposed.
3431 public Task ContinueWith(Action<Task> continuationAction, CancellationToken cancellationToken)
3433 return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None);
3437 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3439 /// <param name="continuationAction">
3440 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3441 /// passed the completed task as an argument.
3443 /// <param name="scheduler">
3444 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
3446 /// <returns>A new continuation <see cref="Task"/>.</returns>
3448 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3449 /// completed, whether it completes due to running to completion successfully, faulting due to an
3450 /// unhandled exception, or exiting out early due to being canceled.
3452 /// <exception cref="T:System.ArgumentNullException">
3453 /// The <paramref name="continuationAction"/> argument is null.
3455 /// <exception cref="T:System.ArgumentNullException">
3456 /// The <paramref name="scheduler"/> argument is null.
3458 public Task ContinueWith(Action<Task> continuationAction, TaskScheduler scheduler)
3460 return ContinueWith(continuationAction, scheduler, default(CancellationToken), TaskContinuationOptions.None);
3464 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3466 /// <param name="continuationAction">
3467 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3468 /// passed the completed task as an argument.
3470 /// <param name="continuationOptions">
3471 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3473 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3474 /// well as execution options, such as <see
3475 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3477 /// <returns>A new continuation <see cref="Task"/>.</returns>
3479 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3480 /// completed. If the continuation criteria specified through the <paramref
3481 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
3482 /// instead of scheduled.
3484 /// <exception cref="T:System.ArgumentNullException">
3485 /// The <paramref name="continuationAction"/> argument is null.
3487 /// <exception cref="T:System.ArgumentOutOfRangeException">
3488 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3489 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3491 public Task ContinueWith(Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
3493 return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), continuationOptions);
3497 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3499 /// <param name="continuationAction">
3500 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3501 /// passed the completed task as an argument.
3503 /// <param name="continuationOptions">
3504 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3506 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3507 /// well as execution options, such as <see
3508 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3510 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3511 /// <param name="scheduler">
3512 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
3515 /// <returns>A new continuation <see cref="Task"/>.</returns>
3517 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3518 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
3519 /// are not met, the continuation task will be canceled instead of scheduled.
3521 /// <exception cref="T:System.ArgumentNullException">
3522 /// The <paramref name="continuationAction"/> argument is null.
3524 /// <exception cref="T:System.ArgumentOutOfRangeException">
3525 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3526 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3528 /// <exception cref="T:System.ArgumentNullException">
3529 /// The <paramref name="scheduler"/> argument is null.
3531 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3532 /// has already been disposed.
3534 public Task ContinueWith(Action<Task> continuationAction, CancellationToken cancellationToken,
3535 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
3537 return ContinueWith(continuationAction, scheduler, cancellationToken, continuationOptions);
3540 // Same as the above overload, just with a stack mark parameter.
3541 private Task ContinueWith(Action<Task> continuationAction, TaskScheduler scheduler,
3542 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions)
3544 // Throw on continuation with null action
3545 if (continuationAction == null)
3547 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationAction);
3550 // Throw on continuation with null TaskScheduler
3551 if (scheduler == null)
3553 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
3556 TaskCreationOptions creationOptions;
3557 InternalTaskOptions internalOptions;
3558 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
3560 Task continuationTask = new ContinuationTaskFromTask(
3561 this, continuationAction, null,
3562 creationOptions, internalOptions
3565 // Register the continuation. If synchronous execution is requested, this may
3566 // actually invoke the continuation before returning.
3567 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
3569 return continuationTask;
3573 #region Action<Task, Object> continuation
3576 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3578 /// <param name="continuationAction">
3579 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3580 /// passed the completed task as and the caller-supplied state object as arguments.
3582 /// <param name="state">An object representing data to be used by the continuation action.</param>
3583 /// <returns>A new continuation <see cref="Task"/>.</returns>
3585 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3586 /// completed, whether it completes due to running to completion successfully, faulting due to an
3587 /// unhandled exception, or exiting out early due to being canceled.
3589 /// <exception cref="T:System.ArgumentNullException">
3590 /// The <paramref name="continuationAction"/> argument is null.
3592 public Task ContinueWith(Action<Task, Object> continuationAction, Object state)
3594 return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None);
3598 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3600 /// <param name="continuationAction">
3601 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3602 /// passed the completed task and the caller-supplied state object as arguments.
3604 /// <param name="state">An object representing data to be used by the continuation action.</param>
3605 /// <param name="cancellationToken"> The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3606 /// <returns>A new continuation <see cref="Task"/>.</returns>
3608 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3609 /// completed, whether it completes due to running to completion successfully, faulting due to an
3610 /// unhandled exception, or exiting out early due to being canceled.
3612 /// <exception cref="T:System.ArgumentNullException">
3613 /// The <paramref name="continuationAction"/> argument is null.
3615 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3616 /// has already been disposed.
3618 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, CancellationToken cancellationToken)
3620 return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None);
3624 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3626 /// <param name="continuationAction">
3627 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3628 /// passed the completed task and the caller-supplied state object as arguments.
3630 /// <param name="state">An object representing data to be used by the continuation action.</param>
3631 /// <param name="scheduler">
3632 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
3634 /// <returns>A new continuation <see cref="Task"/>.</returns>
3636 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3637 /// completed, whether it completes due to running to completion successfully, faulting due to an
3638 /// unhandled exception, or exiting out early due to being canceled.
3640 /// <exception cref="T:System.ArgumentNullException">
3641 /// The <paramref name="continuationAction"/> argument is null.
3643 /// <exception cref="T:System.ArgumentNullException">
3644 /// The <paramref name="scheduler"/> argument is null.
3646 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskScheduler scheduler)
3648 return ContinueWith(continuationAction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None);
3652 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3654 /// <param name="continuationAction">
3655 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3656 /// passed the completed task and the caller-supplied state object as arguments.
3658 /// <param name="state">An object representing data to be used by the continuation action.</param>
3659 /// <param name="continuationOptions">
3660 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3662 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3663 /// well as execution options, such as <see
3664 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3666 /// <returns>A new continuation <see cref="Task"/>.</returns>
3668 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3669 /// completed. If the continuation criteria specified through the <paramref
3670 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
3671 /// instead of scheduled.
3673 /// <exception cref="T:System.ArgumentNullException">
3674 /// The <paramref name="continuationAction"/> argument is null.
3676 /// <exception cref="T:System.ArgumentOutOfRangeException">
3677 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3678 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3680 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskContinuationOptions continuationOptions)
3682 return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions);
3686 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3688 /// <param name="continuationAction">
3689 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3690 /// passed the completed task and the caller-supplied state object as arguments.
3692 /// <param name="state">An object representing data to be used by the continuation action.</param>
3693 /// <param name="continuationOptions">
3694 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3696 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3697 /// well as execution options, such as <see
3698 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3700 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3701 /// <param name="scheduler">
3702 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
3705 /// <returns>A new continuation <see cref="Task"/>.</returns>
3707 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3708 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
3709 /// are not met, the continuation task will be canceled instead of scheduled.
3711 /// <exception cref="T:System.ArgumentNullException">
3712 /// The <paramref name="continuationAction"/> argument is null.
3714 /// <exception cref="T:System.ArgumentOutOfRangeException">
3715 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3716 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3718 /// <exception cref="T:System.ArgumentNullException">
3719 /// The <paramref name="scheduler"/> argument is null.
3721 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3722 /// has already been disposed.
3724 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, CancellationToken cancellationToken,
3725 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
3727 return ContinueWith(continuationAction, state, scheduler, cancellationToken, continuationOptions);
3730 // Same as the above overload, just with a stack mark parameter.
3731 private Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskScheduler scheduler,
3732 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions)
3734 // Throw on continuation with null action
3735 if (continuationAction == null)
3737 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationAction);
3740 // Throw on continuation with null TaskScheduler
3741 if (scheduler == null)
3743 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
3746 TaskCreationOptions creationOptions;
3747 InternalTaskOptions internalOptions;
3748 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
3750 Task continuationTask = new ContinuationTaskFromTask(
3751 this, continuationAction, state,
3752 creationOptions, internalOptions
3755 // Register the continuation. If synchronous execution is requested, this may
3756 // actually invoke the continuation before returning.
3757 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
3759 return continuationTask;
3764 #region Func<Task, TResult> continuation
3767 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3769 /// <typeparam name="TResult">
3770 /// The type of the result produced by the continuation.
3772 /// <param name="continuationFunction">
3773 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
3774 /// passed the completed task as an argument.
3776 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
3778 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
3779 /// completed, whether it completes due to running to completion successfully, faulting due to an
3780 /// unhandled exception, or exiting out early due to being canceled.
3782 /// <exception cref="T:System.ArgumentNullException">
3783 /// The <paramref name="continuationFunction"/> argument is null.
3785 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction)
3787 return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken),
3788 TaskContinuationOptions.None);
3793 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3795 /// <typeparam name="TResult">
3796 /// The type of the result produced by the continuation.
3798 /// <param name="continuationFunction">
3799 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
3800 /// passed the completed task as an argument.
3802 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3803 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
3805 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
3806 /// completed, whether it completes due to running to completion successfully, faulting due to an
3807 /// unhandled exception, or exiting out early due to being canceled.
3809 /// <exception cref="T:System.ArgumentNullException">
3810 /// The <paramref name="continuationFunction"/> argument is null.
3812 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3813 /// has already been disposed.
3815 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
3817 return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None);
3821 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3823 /// <typeparam name="TResult">
3824 /// The type of the result produced by the continuation.
3826 /// <param name="continuationFunction">
3827 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
3828 /// passed the completed task as an argument.
3830 /// <param name="scheduler">
3831 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
3833 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
3835 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
3836 /// completed, whether it completes due to running to completion successfully, faulting due to an
3837 /// unhandled exception, or exiting out early due to being canceled.
3839 /// <exception cref="T:System.ArgumentNullException">
3840 /// The <paramref name="continuationFunction"/> argument is null.
3842 /// <exception cref="T:System.ArgumentNullException">
3843 /// The <paramref name="scheduler"/> argument is null.
3845 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
3847 return ContinueWith<TResult>(continuationFunction, scheduler, default(CancellationToken), TaskContinuationOptions.None);
3851 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3853 /// <typeparam name="TResult">
3854 /// The type of the result produced by the continuation.
3856 /// <param name="continuationFunction">
3857 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
3858 /// passed the completed task as an argument.
3860 /// <param name="continuationOptions">
3861 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3863 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3864 /// well as execution options, such as <see
3865 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3867 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
3869 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
3870 /// completed. If the continuation criteria specified through the <paramref
3871 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
3872 /// instead of scheduled.
3874 /// <exception cref="T:System.ArgumentNullException">
3875 /// The <paramref name="continuationFunction"/> argument is null.
3877 /// <exception cref="T:System.ArgumentOutOfRangeException">
3878 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3879 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3881 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
3883 return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), continuationOptions);
3887 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3889 /// <typeparam name="TResult">
3890 /// The type of the result produced by the continuation.
3892 /// <param name="continuationFunction">
3893 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
3894 /// passed the completed task as an argument.
3896 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3897 /// <param name="continuationOptions">
3898 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3900 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3901 /// well as execution options, such as <see
3902 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3904 /// <param name="scheduler">
3905 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
3908 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
3910 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
3911 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
3912 /// are not met, the continuation task will be canceled instead of scheduled.
3914 /// <exception cref="T:System.ArgumentNullException">
3915 /// The <paramref name="continuationFunction"/> argument is null.
3917 /// <exception cref="T:System.ArgumentOutOfRangeException">
3918 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3919 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3921 /// <exception cref="T:System.ArgumentNullException">
3922 /// The <paramref name="scheduler"/> argument is null.
3924 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3925 /// has already been disposed.
3927 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
3928 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
3930 return ContinueWith<TResult>(continuationFunction, scheduler, cancellationToken, continuationOptions);
3933 // Same as the above overload, just with a stack mark parameter.
3934 private Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskScheduler scheduler,
3935 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions)
3937 // Throw on continuation with null function
3938 if (continuationFunction == null)
3940 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
3943 // Throw on continuation with null task scheduler
3944 if (scheduler == null)
3946 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
3949 TaskCreationOptions creationOptions;
3950 InternalTaskOptions internalOptions;
3951 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
3953 Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>(
3954 this, continuationFunction, null,
3955 creationOptions, internalOptions
3958 // Register the continuation. If synchronous execution is requested, this may
3959 // actually invoke the continuation before returning.
3960 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
3962 return continuationTask;
3966 #region Func<Task, Object, TResult> continuation
3969 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3971 /// <typeparam name="TResult">
3972 /// The type of the result produced by the continuation.
3974 /// <param name="continuationFunction">
3975 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
3976 /// passed the completed task and the caller-supplied state object as arguments.
3978 /// <param name="state">An object representing data to be used by the continuation function.</param>
3979 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
3981 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
3982 /// completed, whether it completes due to running to completion successfully, faulting due to an
3983 /// unhandled exception, or exiting out early due to being canceled.
3985 /// <exception cref="T:System.ArgumentNullException">
3986 /// The <paramref name="continuationFunction"/> argument is null.
3988 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state)
3990 return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken),
3991 TaskContinuationOptions.None);
3996 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3998 /// <typeparam name="TResult">
3999 /// The type of the result produced by the continuation.
4001 /// <param name="continuationFunction">
4002 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4003 /// passed the completed task and the caller-supplied state object as arguments.
4005 /// <param name="state">An object representing data to be used by the continuation function.</param>
4006 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4007 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4009 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4010 /// completed, whether it completes due to running to completion successfully, faulting due to an
4011 /// unhandled exception, or exiting out early due to being canceled.
4013 /// <exception cref="T:System.ArgumentNullException">
4014 /// The <paramref name="continuationFunction"/> argument is null.
4016 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4017 /// has already been disposed.
4019 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, CancellationToken cancellationToken)
4021 return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None);
4025 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4027 /// <typeparam name="TResult">
4028 /// The type of the result produced by the continuation.
4030 /// <param name="continuationFunction">
4031 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4032 /// passed the completed task and the caller-supplied state object as arguments.
4034 /// <param name="state">An object representing data to be used by the continuation function.</param>
4035 /// <param name="scheduler">
4036 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
4038 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4040 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4041 /// completed, whether it completes due to running to completion successfully, faulting due to an
4042 /// unhandled exception, or exiting out early due to being canceled.
4044 /// <exception cref="T:System.ArgumentNullException">
4045 /// The <paramref name="continuationFunction"/> argument is null.
4047 /// <exception cref="T:System.ArgumentNullException">
4048 /// The <paramref name="scheduler"/> argument is null.
4050 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskScheduler scheduler)
4052 return ContinueWith<TResult>(continuationFunction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None);
4056 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4058 /// <typeparam name="TResult">
4059 /// The type of the result produced by the continuation.
4061 /// <param name="continuationFunction">
4062 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4063 /// passed the completed task and the caller-supplied state object as arguments.
4065 /// <param name="state">An object representing data to be used by the continuation function.</param>
4066 /// <param name="continuationOptions">
4067 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4069 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4070 /// well as execution options, such as <see
4071 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4073 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4075 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4076 /// completed. If the continuation criteria specified through the <paramref
4077 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
4078 /// instead of scheduled.
4080 /// <exception cref="T:System.ArgumentNullException">
4081 /// The <paramref name="continuationFunction"/> argument is null.
4083 /// <exception cref="T:System.ArgumentOutOfRangeException">
4084 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4085 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4087 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskContinuationOptions continuationOptions)
4089 return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions);
4093 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4095 /// <typeparam name="TResult">
4096 /// The type of the result produced by the continuation.
4098 /// <param name="continuationFunction">
4099 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4100 /// passed the completed task and the caller-supplied state object as arguments.
4102 /// <param name="state">An object representing data to be used by the continuation function.</param>
4103 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4104 /// <param name="continuationOptions">
4105 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4107 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4108 /// well as execution options, such as <see
4109 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4111 /// <param name="scheduler">
4112 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
4115 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4117 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4118 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
4119 /// are not met, the continuation task will be canceled instead of scheduled.
4121 /// <exception cref="T:System.ArgumentNullException">
4122 /// The <paramref name="continuationFunction"/> argument is null.
4124 /// <exception cref="T:System.ArgumentOutOfRangeException">
4125 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4126 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4128 /// <exception cref="T:System.ArgumentNullException">
4129 /// The <paramref name="scheduler"/> argument is null.
4131 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4132 /// has already been disposed.
4134 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, CancellationToken cancellationToken,
4135 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
4137 return ContinueWith<TResult>(continuationFunction, state, scheduler, cancellationToken, continuationOptions);
4140 // Same as the above overload, just with a stack mark parameter.
4141 private Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskScheduler scheduler,
4142 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions)
4144 // Throw on continuation with null function
4145 if (continuationFunction == null)
4147 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction);
4150 // Throw on continuation with null task scheduler
4151 if (scheduler == null)
4153 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler);
4156 TaskCreationOptions creationOptions;
4157 InternalTaskOptions internalOptions;
4158 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
4160 Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>(
4161 this, continuationFunction, state,
4162 creationOptions, internalOptions
4165 // Register the continuation. If synchronous execution is requested, this may
4166 // actually invoke the continuation before returning.
4167 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
4169 return continuationTask;
4174 /// Converts TaskContinuationOptions to TaskCreationOptions, and also does
4175 /// some validity checking along the way.
4177 /// <param name="continuationOptions">Incoming TaskContinuationOptions</param>
4178 /// <param name="creationOptions">Outgoing TaskCreationOptions</param>
4179 /// <param name="internalOptions">Outgoing InternalTaskOptions</param>
4180 internal static void CreationOptionsFromContinuationOptions(
4181 TaskContinuationOptions continuationOptions,
4182 out TaskCreationOptions creationOptions,
4183 out InternalTaskOptions internalOptions)
4185 // This is used a couple of times below
4186 const TaskContinuationOptions NotOnAnything =
4187 TaskContinuationOptions.NotOnCanceled |
4188 TaskContinuationOptions.NotOnFaulted |
4189 TaskContinuationOptions.NotOnRanToCompletion;
4191 const TaskContinuationOptions CreationOptionsMask =
4192 TaskContinuationOptions.PreferFairness |
4193 TaskContinuationOptions.LongRunning |
4194 TaskContinuationOptions.DenyChildAttach |
4195 TaskContinuationOptions.HideScheduler |
4196 TaskContinuationOptions.AttachedToParent |
4197 TaskContinuationOptions.RunContinuationsAsynchronously;
4199 // Check that LongRunning and ExecuteSynchronously are not specified together
4200 const TaskContinuationOptions IllegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning;
4201 if ((continuationOptions & IllegalMask) == IllegalMask)
4203 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions, ExceptionResource.Task_ContinueWith_ESandLR);
4206 // Check that no illegal options were specified
4207 if ((continuationOptions &
4208 ~(CreationOptionsMask | NotOnAnything |
4209 TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously)) != 0)
4211 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions);
4214 // Check that we didn't specify "not on anything"
4215 if ((continuationOptions & NotOnAnything) == NotOnAnything)
4217 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.continuationOptions, ExceptionResource.Task_ContinueWith_NotOnAnything);
4220 // This passes over all but LazyCancellation, which has no representation in TaskCreationOptions
4221 creationOptions = (TaskCreationOptions)(continuationOptions & CreationOptionsMask);
4223 // internalOptions has at least ContinuationTask and possibly LazyCancellation
4224 internalOptions = (continuationOptions & TaskContinuationOptions.LazyCancellation) != 0 ?
4225 InternalTaskOptions.ContinuationTask | InternalTaskOptions.LazyCancellation :
4226 InternalTaskOptions.ContinuationTask;
4231 /// Registers the continuation and possibly runs it (if the task is already finished).
4233 /// <param name="continuationTask">The continuation task itself.</param>
4234 /// <param name="scheduler">TaskScheduler with which to associate continuation task.</param>
4235 /// <param name="options">Restrictions on when the continuation becomes active.</param>
4236 internal void ContinueWithCore(Task continuationTask,
4237 TaskScheduler scheduler,
4238 CancellationToken cancellationToken,
4239 TaskContinuationOptions options)
4241 Debug.Assert(continuationTask != null, "Task.ContinueWithCore(): null continuationTask");
4242 Debug.Assert(scheduler != null, "Task.ContinueWithCore(): null scheduler");
4243 Debug.Assert(!continuationTask.IsCompleted, "Did not expect continuationTask to be completed");
4245 // Create a TaskContinuation
4246 TaskContinuation continuation = new StandardTaskContinuation(continuationTask, options, scheduler);
4248 // If cancellationToken is cancellable, then assign it.
4249 if (cancellationToken.CanBeCanceled)
4251 if (IsCompleted || cancellationToken.IsCancellationRequested)
4253 // If the antecedent has completed, then we will not be queuing up
4254 // the continuation in the antecedent's continuation list. Likewise,
4255 // if the cancellationToken has been canceled, continuationTask will
4256 // be completed in the AssignCancellationToken call below, and there
4257 // is no need to queue the continuation to the antecedent's continuation
4258 // list. In either of these two cases, we will pass "null" for the antecedent,
4259 // meaning "the cancellation callback should not attempt to remove the
4260 // continuation from its antecedent's continuation list".
4261 continuationTask.AssignCancellationToken(cancellationToken, null, null);
4265 // The antecedent is not yet complete, so there is a pretty good chance
4266 // that the continuation will be queued up in the antecedent. Assign the
4267 // cancellation token with information about the antecedent, so that the
4268 // continuation can be dequeued upon the signalling of the token.
4270 // It's possible that the antecedent completes before the call to AddTaskContinuation,
4271 // and that is a benign race condition. It just means that the cancellation will result in
4272 // a futile search of the antecedent's continuation list.
4273 continuationTask.AssignCancellationToken(cancellationToken, this, continuation);
4277 // In the case of a pre-canceled token, continuationTask will have been completed
4278 // in a Canceled state by now. If such is the case, there is no need to go through
4279 // the motions of queuing up the continuation for eventual execution.
4280 if (!continuationTask.IsCompleted)
4282 // We need additional correlation produced here to ensure that at least the continuation
4283 // code will be correlatable to the currrent activity that initiated "this" task:
4284 // . when the antecendent ("this") is a promise we have very little control over where
4285 // the code for the promise will run (e.g. it can be a task from a user provided
4286 // TaskCompletionSource or from a classic Begin/End async operation); this user or
4287 // system code will likely not have stamped an activity id on the thread, so there's
4288 // generally no easy correlation that can be provided between the current activity
4289 // and the promise. Also the continuation code may run practically on any thread.
4290 // Since there may be no correlation between the current activity and the TCS's task
4291 // activity, we ensure we at least create a correlation from the current activity to
4292 // the continuation that runs when the promise completes.
4293 if ((this.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0 &&
4294 !(this is ITaskCompletionAction))
4296 var etwLog = TplEtwProvider.Log;
4297 if (etwLog.IsEnabled())
4299 etwLog.AwaitTaskContinuationScheduled(TaskScheduler.Current.Id, Task.CurrentId ?? 0, continuationTask.Id);
4303 // Attempt to enqueue the continuation
4304 bool continuationQueued = AddTaskContinuation(continuation, addBeforeOthers: false);
4306 // If the continuation was not queued (because the task completed), then run it now.
4307 if (!continuationQueued) continuation.Run(this, bCanInlineContinuationTask: true);
4312 // Adds a lightweight completion action to a task. This is similar to a continuation
4313 // task except that it is stored as an action, and thus does not require the allocation/
4314 // execution resources of a continuation task.
4316 // Used internally by ContinueWhenAll() and ContinueWhenAny().
4317 internal void AddCompletionAction(ITaskCompletionAction action)
4319 AddCompletionAction(action, addBeforeOthers: false);
4322 private void AddCompletionAction(ITaskCompletionAction action, bool addBeforeOthers)
4324 if (!AddTaskContinuation(action, addBeforeOthers))
4325 action.Invoke(this); // run the action directly if we failed to queue the continuation (i.e., the task completed)
4328 // Support method for AddTaskContinuation that takes care of multi-continuation logic.
4329 // Returns true if and only if the continuation was successfully queued.
4330 // THIS METHOD ASSUMES THAT m_continuationObject IS NOT NULL. That case was taken
4331 // care of in the calling method, AddTaskContinuation().
4332 private bool AddTaskContinuationComplex(object tc, bool addBeforeOthers)
4334 Debug.Assert(tc != null, "Expected non-null tc object in AddTaskContinuationComplex");
4336 object oldValue = m_continuationObject;
4338 // Logic for the case where we were previously storing a single continuation
4339 if ((oldValue != s_taskCompletionSentinel) && (!(oldValue is List<object>)))
4341 // Construct a new TaskContinuation list
4342 List<object> newList = new List<object>();
4344 // Add in the old single value
4345 newList.Add(oldValue);
4347 // Now CAS in the new list
4348 Interlocked.CompareExchange(ref m_continuationObject, newList, oldValue);
4350 // We might be racing against another thread converting the single into
4351 // a list, or we might be racing against task completion, so resample "list"
4355 // m_continuationObject is guaranteed at this point to be either a List or
4356 // s_taskCompletionSentinel.
4357 List<object> list = m_continuationObject as List<object>;
4358 Debug.Assert((list != null) || (m_continuationObject == s_taskCompletionSentinel),
4359 "Expected m_continuationObject to be list or sentinel");
4361 // If list is null, it can only mean that s_taskCompletionSentinel has been exchanged
4362 // into m_continuationObject. Thus, the task has completed and we should return false
4363 // from this method, as we will not be queuing up the continuation.
4368 // It is possible for the task to complete right after we snap the copy of
4369 // the list. If so, then fall through and return false without queuing the
4371 if (m_continuationObject != s_taskCompletionSentinel)
4373 // Before growing the list we remove possible null entries that are the
4374 // result from RemoveContinuations()
4375 if (list.Count == list.Capacity)
4377 list.RemoveAll(s_IsTaskContinuationNullPredicate);
4380 if (addBeforeOthers)
4385 return true; // continuation successfully queued, so return true.
4390 // We didn't succeed in queuing the continuation, so return false.
4394 // Record a continuation task or action.
4395 // Return true if and only if we successfully queued a continuation.
4396 private bool AddTaskContinuation(object tc, bool addBeforeOthers)
4398 Debug.Assert(tc != null);
4400 // Make sure that, if someone calls ContinueWith() right after waiting for the predecessor to complete,
4401 // we don't queue up a continuation.
4402 if (IsCompleted) return false;
4404 // Try to just jam tc into m_continuationObject
4405 if ((m_continuationObject != null) || (Interlocked.CompareExchange(ref m_continuationObject, tc, null) != null))
4407 // If we get here, it means that we failed to CAS tc into m_continuationObject.
4408 // Therefore, we must go the more complicated route.
4409 return AddTaskContinuationComplex(tc, addBeforeOthers);
4414 // Removes a continuation task from m_continuations
4415 internal void RemoveContinuation(object continuationObject) // could be TaskContinuation or Action<Task>
4417 // We need to snap a local reference to m_continuations since reading a volatile object is more costly.
4418 // Also to prevent the value to be changed as result of a race condition with another method.
4419 object continuationsLocalRef = m_continuationObject;
4421 // Task is completed. Nothing to do here.
4422 if (continuationsLocalRef == s_taskCompletionSentinel) return;
4424 List<object> continuationsLocalListRef = continuationsLocalRef as List<object>;
4426 if (continuationsLocalListRef == null)
4428 // This is not a list. If we have a single object (the one we want to remove) we try to replace it with an empty list.
4429 // Note we cannot go back to a null state, since it will mess up the AddTaskContinuation logic.
4430 if (Interlocked.CompareExchange(ref m_continuationObject, new List<object>(), continuationObject) != continuationObject)
4432 // If we fail it means that either AddContinuationComplex won the race condition and m_continuationObject is now a List
4433 // that contains the element we want to remove. Or FinishContinuations set the s_taskCompletionSentinel.
4434 // So we should try to get a list one more time
4435 continuationsLocalListRef = m_continuationObject as List<object>;
4439 // Exchange was successful so we can skip the last comparison
4444 // if continuationsLocalRef == null it means s_taskCompletionSentinel has been set already and there is nothing else to do.
4445 if (continuationsLocalListRef != null)
4447 lock (continuationsLocalListRef)
4449 // There is a small chance that this task completed since we took a local snapshot into
4450 // continuationsLocalRef. In that case, just return; we don't want to be manipulating the
4451 // continuation list as it is being processed.
4452 if (m_continuationObject == s_taskCompletionSentinel) return;
4454 // Find continuationObject in the continuation list
4455 int index = continuationsLocalListRef.IndexOf(continuationObject);
4459 // null out that TaskContinuation entry, which will be interpreted as "to be cleaned up"
4460 continuationsLocalListRef[index] = null;
4466 // statically allocated delegate for the RemoveAll expression in RemoveContinuations() and AddContinuationComplex()
4467 private readonly static Predicate<object> s_IsTaskContinuationNullPredicate =
4468 new Predicate<object>((tc) => { return (tc == null); });
4476 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4478 /// <param name="tasks">
4479 /// An array of <see cref="Task"/> instances on which to wait.
4481 /// <exception cref="T:System.ArgumentNullException">
4482 /// The <paramref name="tasks"/> argument is null.
4484 /// <exception cref="T:System.ArgumentNullException">
4485 /// The <paramref name="tasks"/> argument contains a null element.
4487 /// <exception cref="T:System.AggregateException">
4488 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4489 /// the execution of at least one of the <see cref="Task"/> instances.
4491 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4492 public static void WaitAll(params Task[] tasks)
4497 WaitAllCore(tasks, Timeout.Infinite, default(CancellationToken));
4500 Debug.Assert(waitResult, "expected wait to succeed");
4505 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4508 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4509 /// otherwise, false.
4511 /// <param name="tasks">
4512 /// An array of <see cref="Task"/> instances on which to wait.
4514 /// <param name="timeout">
4515 /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
4516 /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
4518 /// <exception cref="T:System.ArgumentNullException">
4519 /// The <paramref name="tasks"/> argument is null.
4521 /// <exception cref="T:System.ArgumentException">
4522 /// The <paramref name="tasks"/> argument contains a null element.
4524 /// <exception cref="T:System.AggregateException">
4525 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4526 /// the execution of at least one of the <see cref="Task"/> instances.
4528 /// <exception cref="T:System.ArgumentOutOfRangeException">
4529 /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
4530 /// infinite time-out -or- timeout is greater than
4531 /// <see cref="System.Int32.MaxValue"/>.
4533 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4534 public static bool WaitAll(Task[] tasks, TimeSpan timeout)
4536 long totalMilliseconds = (long)timeout.TotalMilliseconds;
4537 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
4539 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout);
4542 return WaitAllCore(tasks, (int)totalMilliseconds, default(CancellationToken));
4546 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4549 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4550 /// otherwise, false.
4552 /// <param name="millisecondsTimeout">
4553 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
4554 /// wait indefinitely.</param>
4555 /// <param name="tasks">An array of <see cref="Task"/> instances on which to wait.
4557 /// <exception cref="T:System.ArgumentNullException">
4558 /// The <paramref name="tasks"/> argument is null.
4560 /// <exception cref="T:System.ArgumentException">
4561 /// The <paramref name="tasks"/> argument contains a null element.
4563 /// <exception cref="T:System.AggregateException">
4564 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4565 /// the execution of at least one of the <see cref="Task"/> instances.
4567 /// <exception cref="T:System.ArgumentOutOfRangeException">
4568 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
4569 /// infinite time-out.
4571 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4572 public static bool WaitAll(Task[] tasks, int millisecondsTimeout)
4574 return WaitAllCore(tasks, millisecondsTimeout, default(CancellationToken));
4578 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4581 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4582 /// otherwise, false.
4584 /// <param name="tasks">
4585 /// An array of <see cref="Task"/> instances on which to wait.
4587 /// <param name="cancellationToken">
4588 /// A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.
4590 /// <exception cref="T:System.ArgumentNullException">
4591 /// The <paramref name="tasks"/> argument is null.
4593 /// <exception cref="T:System.ArgumentException">
4594 /// The <paramref name="tasks"/> argument contains a null element.
4596 /// <exception cref="T:System.AggregateException">
4597 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4598 /// the execution of at least one of the <see cref="Task"/> instances.
4600 /// <exception cref="T:System.OperationCanceledException">
4601 /// The <paramref name="cancellationToken"/> was canceled.
4603 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4604 public static void WaitAll(Task[] tasks, CancellationToken cancellationToken)
4606 WaitAllCore(tasks, Timeout.Infinite, cancellationToken);
4610 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4613 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4614 /// otherwise, false.
4616 /// <param name="tasks">
4617 /// An array of <see cref="Task"/> instances on which to wait.
4619 /// <param name="millisecondsTimeout">
4620 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
4621 /// wait indefinitely.
4623 /// <param name="cancellationToken">
4624 /// A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.
4626 /// <exception cref="T:System.ArgumentNullException">
4627 /// The <paramref name="tasks"/> argument is null.
4629 /// <exception cref="T:System.ArgumentException">
4630 /// The <paramref name="tasks"/> argument contains a null element.
4632 /// <exception cref="T:System.AggregateException">
4633 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4634 /// the execution of at least one of the <see cref="Task"/> instances.
4636 /// <exception cref="T:System.ArgumentOutOfRangeException">
4637 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
4638 /// infinite time-out.
4640 /// <exception cref="T:System.OperationCanceledException">
4641 /// The <paramref name="cancellationToken"/> was canceled.
4643 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4644 public static bool WaitAll(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken) =>
4645 WaitAllCore(tasks, millisecondsTimeout, cancellationToken);
4647 // Separated out to allow it to be optimized (caller is marked NoOptimization for VS parallel debugger
4648 // to be able to see the method on the stack and inspect arguments).
4649 private static bool WaitAllCore(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
4653 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
4655 if (millisecondsTimeout < -1)
4657 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.millisecondsTimeout);
4660 cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations
4663 // In this WaitAll() implementation we have 2 alternate code paths for a task to be handled:
4664 // CODEPATH1: skip an already completed task, CODEPATH2: actually wait on tasks
4665 // We make sure that the exception behavior of Task.Wait() is replicated the same for tasks handled in either of these codepaths
4668 List<Exception> exceptions = null;
4669 List<Task> waitedOnTaskList = null;
4670 List<Task> notificationTasks = null;
4672 // If any of the waited-upon tasks end as Faulted or Canceled, set these to true.
4673 bool exceptionSeen = false, cancellationSeen = false;
4675 bool returnValue = true;
4677 // Collects incomplete tasks in "waitedOnTaskList"
4678 for (int i = tasks.Length - 1; i >= 0; i--)
4680 Task task = tasks[i];
4684 ThrowHelper.ThrowArgumentException(ExceptionResource.Task_WaitMulti_NullTask, ExceptionArgument.tasks);
4687 bool taskIsCompleted = task.IsCompleted;
4688 if (!taskIsCompleted)
4690 // try inlining the task only if we have an infinite timeout and an empty cancellation token
4691 if (millisecondsTimeout != Timeout.Infinite || cancellationToken.CanBeCanceled)
4693 // We either didn't attempt inline execution because we had a non-infinite timeout or we had a cancellable token.
4694 // In all cases we need to do a full wait on the task (=> add its event into the list.)
4695 AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
4699 // We are eligible for inlining. If it doesn't work, we'll do a full wait.
4700 taskIsCompleted = task.WrappedTryRunInline() && task.IsCompleted; // A successful TryRunInline doesn't guarantee completion
4701 if (!taskIsCompleted) AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
4705 if (taskIsCompleted)
4707 if (task.IsFaulted) exceptionSeen = true;
4708 else if (task.IsCanceled) cancellationSeen = true;
4709 if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: 1);
4713 if (waitedOnTaskList != null)
4715 // Block waiting for the tasks to complete.
4716 returnValue = WaitAllBlockingCore(waitedOnTaskList, millisecondsTimeout, cancellationToken);
4718 // If the wait didn't time out, ensure exceptions are propagated, and if a debugger is
4719 // attached and one of these tasks requires it, that we notify the debugger of a wait completion.
4722 // Add any exceptions for this task to the collection, and if it's wait
4723 // notification bit is set, store it to operate on at the end.
4724 foreach (var task in waitedOnTaskList)
4726 if (task.IsFaulted) exceptionSeen = true;
4727 else if (task.IsCanceled) cancellationSeen = true;
4728 if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: 1);
4732 // We need to prevent the tasks array from being GC'ed until we come out of the wait.
4733 // This is necessary so that the Parallel Debugger can traverse it during the long wait and
4734 // deduce waiter/waitee relationships
4735 GC.KeepAlive(tasks);
4738 // Now that we're done and about to exit, if the wait completed and if we have
4739 // any tasks with a notification bit set, signal the debugger if any requires it.
4740 if (returnValue && notificationTasks != null)
4742 // Loop through each task tha that had its bit set, and notify the debugger
4743 // about the first one that requires it. The debugger will reset the bit
4744 // for any tasks we don't notify of as soon as we break, so we only need to notify
4746 foreach (var task in notificationTasks)
4748 if (task.NotifyDebuggerOfWaitCompletionIfNecessary()) break;
4752 // If one or more threw exceptions, aggregate and throw them.
4753 if (returnValue && (exceptionSeen || cancellationSeen))
4755 // If the WaitAll was canceled and tasks were canceled but not faulted,
4756 // prioritize throwing an OCE for canceling the WaitAll over throwing an
4757 // AggregateException for all of the canceled Tasks. This helps
4758 // to bring determinism to an otherwise non-determistic case of using
4759 // the same token to cancel both the WaitAll and the Tasks.
4760 if (!exceptionSeen) cancellationToken.ThrowIfCancellationRequested();
4762 // Now gather up and throw all of the exceptions.
4763 foreach (var task in tasks) AddExceptionsForCompletedTask(ref exceptions, task);
4764 Debug.Assert(exceptions != null, "Should have seen at least one exception");
4765 ThrowHelper.ThrowAggregateException(exceptions);
4771 /// <summary>Adds an element to the list, initializing the list if it's null.</summary>
4772 /// <typeparam name="T">Specifies the type of data stored in the list.</typeparam>
4773 /// <param name="item">The item to add.</param>
4774 /// <param name="list">The list.</param>
4775 /// <param name="initSize">The size to which to initialize the list if the list is null.</param>
4776 private static void AddToList<T>(T item, ref List<T> list, int initSize)
4778 if (list == null) list = new List<T>(initSize);
4782 /// <summary>Performs a blocking WaitAll on the vetted list of tasks.</summary>
4783 /// <param name="tasks">The tasks, which have already been checked and filtered for completion.</param>
4784 /// <param name="millisecondsTimeout">The timeout.</param>
4785 /// <param name="cancellationToken">The cancellation token.</param>
4786 /// <returns>true if all of the tasks completed; otherwise, false.</returns>
4787 private static bool WaitAllBlockingCore(List<Task> tasks, int millisecondsTimeout, CancellationToken cancellationToken)
4789 Debug.Assert(tasks != null, "Expected a non-null list of tasks");
4790 Debug.Assert(tasks.Count > 0, "Expected at least one task");
4792 bool waitCompleted = false;
4793 var mres = new SetOnCountdownMres(tasks.Count);
4796 foreach (var task in tasks)
4798 task.AddCompletionAction(mres, addBeforeOthers: true);
4800 waitCompleted = mres.Wait(millisecondsTimeout, cancellationToken);
4806 foreach (var task in tasks)
4808 if (!task.IsCompleted) task.RemoveContinuation(mres);
4811 // It's ok that we don't dispose of the MRES here, as we never
4812 // access the MRES' WaitHandle, and thus no finalizable resources
4813 // are actually created. We don't always just Dispose it because
4814 // a continuation that's accessing the MRES could still be executing.
4816 return waitCompleted;
4819 // A ManualResetEventSlim that will get Set after Invoke is called count times.
4820 // This allows us to replace this logic:
4821 // var mres = new ManualResetEventSlim(tasks.Count);
4822 // Action<Task> completionAction = delegate { if(Interlocked.Decrement(ref count) == 0) mres.Set(); };
4823 // foreach(var task in tasks) task.AddCompletionAction(completionAction);
4825 // var mres = new SetOnCountdownMres(tasks.Count);
4826 // foreach(var task in tasks) task.AddCompletionAction(mres);
4827 // which saves a couple of allocations.
4829 // Used in WaitAllBlockingCore (above).
4830 private sealed class SetOnCountdownMres : ManualResetEventSlim, ITaskCompletionAction
4834 internal SetOnCountdownMres(int count)
4836 Debug.Assert(count > 0, "Expected count > 0");
4840 public void Invoke(Task completingTask)
4842 if (Interlocked.Decrement(ref _count) == 0) Set();
4843 Debug.Assert(_count >= 0, "Count should never go below 0");
4846 public bool InvokeMayRunArbitraryCode { get { return false; } }
4850 /// This internal function is only meant to be called by WaitAll()
4851 /// If the completed task is canceled or it has other exceptions, here we will add those
4852 /// into the passed in exception list (which will be lazily initialized here).
4854 internal static void AddExceptionsForCompletedTask(ref List<Exception> exceptions, Task t)
4856 AggregateException ex = t.GetExceptions(true);
4859 // make sure the task's exception observed status is set appropriately
4860 // it's possible that WaitAll was called by the parent of an attached child,
4861 // this will make sure it won't throw again in the implicit wait
4862 t.UpdateExceptionObservedStatus();
4864 if (exceptions == null)
4866 exceptions = new List<Exception>(ex.InnerExceptions.Count);
4869 exceptions.AddRange(ex.InnerExceptions);
4875 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
4877 /// <param name="tasks">
4878 /// An array of <see cref="Task"/> instances on which to wait.
4880 /// <returns>The index of the completed task in the <paramref name="tasks"/> array argument.</returns>
4881 /// <exception cref="T:System.ArgumentNullException">
4882 /// The <paramref name="tasks"/> argument is null.
4884 /// <exception cref="T:System.ArgumentException">
4885 /// The <paramref name="tasks"/> argument contains a null element.
4887 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4888 public static int WaitAny(params Task[] tasks)
4890 int waitResult = WaitAnyCore(tasks, Timeout.Infinite, default(CancellationToken));
4891 Debug.Assert(tasks.Length == 0 || waitResult != -1, "expected wait to succeed");
4896 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
4898 /// <param name="tasks">
4899 /// An array of <see cref="Task"/> instances on which to wait.
4901 /// <param name="timeout">
4902 /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
4903 /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
4906 /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
4907 /// timeout occurred.
4909 /// <exception cref="T:System.ArgumentNullException">
4910 /// The <paramref name="tasks"/> argument is null.
4912 /// <exception cref="T:System.ArgumentException">
4913 /// The <paramref name="tasks"/> argument contains a null element.
4915 /// <exception cref="T:System.ArgumentOutOfRangeException">
4916 /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
4917 /// infinite time-out -or- timeout is greater than
4918 /// <see cref="System.Int32.MaxValue"/>.
4920 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4921 public static int WaitAny(Task[] tasks, TimeSpan timeout)
4923 long totalMilliseconds = (long)timeout.TotalMilliseconds;
4924 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
4926 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout);
4929 return WaitAnyCore(tasks, (int)totalMilliseconds, default(CancellationToken));
4933 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
4935 /// <param name="tasks">
4936 /// An array of <see cref="Task"/> instances on which to wait.
4938 /// <param name="cancellationToken">
4939 /// A <see cref="CancellationToken"/> to observe while waiting for a task to complete.
4942 /// The index of the completed task in the <paramref name="tasks"/> array argument.
4944 /// <exception cref="T:System.ArgumentNullException">
4945 /// The <paramref name="tasks"/> argument is null.
4947 /// <exception cref="T:System.ArgumentException">
4948 /// The <paramref name="tasks"/> argument contains a null element.
4950 /// <exception cref="T:System.OperationCanceledException">
4951 /// The <paramref name="cancellationToken"/> was canceled.
4953 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4954 public static int WaitAny(Task[] tasks, CancellationToken cancellationToken)
4956 return WaitAnyCore(tasks, Timeout.Infinite, cancellationToken);
4960 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
4962 /// <param name="tasks">
4963 /// An array of <see cref="Task"/> instances on which to wait.
4965 /// <param name="millisecondsTimeout">
4966 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
4967 /// wait indefinitely.
4970 /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
4971 /// timeout occurred.
4973 /// <exception cref="T:System.ArgumentNullException">
4974 /// The <paramref name="tasks"/> argument is null.
4976 /// <exception cref="T:System.ArgumentException">
4977 /// The <paramref name="tasks"/> argument contains a null element.
4979 /// <exception cref="T:System.ArgumentOutOfRangeException">
4980 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
4981 /// infinite time-out.
4983 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4984 public static int WaitAny(Task[] tasks, int millisecondsTimeout)
4986 return WaitAnyCore(tasks, millisecondsTimeout, default(CancellationToken));
4990 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
4992 /// <param name="tasks">
4993 /// An array of <see cref="Task"/> instances on which to wait.
4995 /// <param name="millisecondsTimeout">
4996 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
4997 /// wait indefinitely.
4999 /// <param name="cancellationToken">
5000 /// A <see cref="CancellationToken"/> to observe while waiting for a task to complete.
5003 /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
5004 /// timeout occurred.
5006 /// <exception cref="T:System.ArgumentNullException">
5007 /// The <paramref name="tasks"/> argument is null.
5009 /// <exception cref="T:System.ArgumentException">
5010 /// The <paramref name="tasks"/> argument contains a null element.
5012 /// <exception cref="T:System.ArgumentOutOfRangeException">
5013 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
5014 /// infinite time-out.
5016 /// <exception cref="T:System.OperationCanceledException">
5017 /// The <paramref name="cancellationToken"/> was canceled.
5019 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5020 public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken) =>
5021 WaitAnyCore(tasks, millisecondsTimeout, cancellationToken);
5023 // Separated out to allow it to be optimized (caller is marked NoOptimization for VS parallel debugger
5024 // to be able to inspect arguments).
5025 private static int WaitAnyCore(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
5029 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
5031 if (millisecondsTimeout < -1)
5033 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.millisecondsTimeout);
5036 cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations
5038 int signaledTaskIndex = -1;
5040 // Make a pass through the loop to check for any tasks that may have
5041 // already been completed, and to verify that no tasks are null.
5043 for (int taskIndex = 0; taskIndex < tasks.Length; taskIndex++)
5045 Task task = tasks[taskIndex];
5049 ThrowHelper.ThrowArgumentException(ExceptionResource.Task_WaitMulti_NullTask, ExceptionArgument.tasks);
5052 if (signaledTaskIndex == -1 && task.IsCompleted)
5054 // We found our first completed task. Store it, but we can't just return here,
5055 // as we still need to validate the whole array for nulls.
5056 signaledTaskIndex = taskIndex;
5060 if (signaledTaskIndex == -1 && tasks.Length != 0)
5062 Task<Task> firstCompleted = TaskFactory.CommonCWAnyLogic(tasks);
5063 bool waitCompleted = firstCompleted.Wait(millisecondsTimeout, cancellationToken);
5066 Debug.Assert(firstCompleted.Status == TaskStatus.RanToCompletion);
5067 signaledTaskIndex = Array.IndexOf(tasks, firstCompleted.Result);
5068 Debug.Assert(signaledTaskIndex >= 0);
5072 TaskFactory.CommonCWAnyLogicCleanup(firstCompleted);
5076 // We need to prevent the tasks array from being GC'ed until we come out of the wait.
5077 // This is necessary so that the Parallel Debugger can traverse it during the long wait
5078 // and deduce waiter/waitee relationships
5079 GC.KeepAlive(tasks);
5082 return signaledTaskIndex;
5085 #region FromResult / FromException / FromCanceled
5087 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed successfully with the specified result.</summary>
5088 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5089 /// <param name="result">The result to store into the completed task.</param>
5090 /// <returns>The successfully completed task.</returns>
5091 public static Task<TResult> FromResult<TResult>(TResult result)
5093 return new Task<TResult>(result);
5096 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed exceptionally with the specified exception.</summary>
5097 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5098 /// <param name="exception">The exception with which to complete the task.</param>
5099 /// <returns>The faulted task.</returns>
5100 public static Task FromException(Exception exception)
5102 return FromException<VoidTaskResult>(exception);
5105 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed exceptionally with the specified exception.</summary>
5106 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5107 /// <param name="exception">The exception with which to complete the task.</param>
5108 /// <returns>The faulted task.</returns>
5109 public static Task<TResult> FromException<TResult>(Exception exception)
5111 if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception);
5113 var task = new Task<TResult>();
5114 bool succeeded = task.TrySetException(exception);
5115 Debug.Assert(succeeded, "This should always succeed on a new task.");
5119 /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified token.</summary>
5120 /// <param name="cancellationToken">The token with which to complete the task.</param>
5121 /// <returns>The canceled task.</returns>
5122 public static Task FromCanceled(CancellationToken cancellationToken)
5124 if (!cancellationToken.IsCancellationRequested)
5125 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.cancellationToken);
5126 return new Task(true, TaskCreationOptions.None, cancellationToken);
5129 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified token.</summary>
5130 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5131 /// <param name="cancellationToken">The token with which to complete the task.</param>
5132 /// <returns>The canceled task.</returns>
5133 public static Task<TResult> FromCanceled<TResult>(CancellationToken cancellationToken)
5135 if (!cancellationToken.IsCancellationRequested)
5136 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.cancellationToken);
5137 return new Task<TResult>(true, default(TResult), TaskCreationOptions.None, cancellationToken);
5140 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified exception.</summary>
5141 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5142 /// <param name="exception">The exception with which to complete the task.</param>
5143 /// <returns>The canceled task.</returns>
5144 internal static Task<TResult> FromCancellation<TResult>(OperationCanceledException exception)
5146 if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception);
5148 var task = new Task<TResult>();
5149 bool succeeded = task.TrySetCanceled(exception.CancellationToken, exception);
5150 Debug.Assert(succeeded, "This should always succeed on a new task.");
5160 /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work.
5162 /// <param name="action">The work to execute asynchronously</param>
5163 /// <returns>A Task that represents the work queued to execute in the ThreadPool.</returns>
5164 /// <exception cref="T:System.ArgumentNullException">
5165 /// The <paramref name="action"/> parameter was null.
5167 public static Task Run(Action action)
5169 return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default,
5170 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None);
5174 /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work.
5176 /// <param name="action">The work to execute asynchronously</param>
5177 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5178 /// <returns>A Task that represents the work queued to execute in the ThreadPool.</returns>
5179 /// <exception cref="T:System.ArgumentNullException">
5180 /// The <paramref name="action"/> parameter was null.
5182 /// <exception cref="T:System.ObjectDisposedException">
5183 /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
5185 public static Task Run(Action action, CancellationToken cancellationToken)
5187 return Task.InternalStartNew(null, action, null, cancellationToken, TaskScheduler.Default,
5188 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None);
5192 /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work.
5194 /// <param name="function">The work to execute asynchronously</param>
5195 /// <returns>A Task(TResult) that represents the work queued to execute in the ThreadPool.</returns>
5196 /// <exception cref="T:System.ArgumentNullException">
5197 /// The <paramref name="function"/> parameter was null.
5199 public static Task<TResult> Run<TResult>(Func<TResult> function)
5201 return Task<TResult>.StartNew(null, function, default(CancellationToken),
5202 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default);
5206 /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work.
5208 /// <param name="function">The work to execute asynchronously</param>
5209 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5210 /// <returns>A Task(TResult) that represents the work queued to execute in the ThreadPool.</returns>
5211 /// <exception cref="T:System.ArgumentNullException">
5212 /// The <paramref name="function"/> parameter was null.
5214 /// <exception cref="T:System.ObjectDisposedException">
5215 /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
5217 public static Task<TResult> Run<TResult>(Func<TResult> function, CancellationToken cancellationToken)
5219 return Task<TResult>.StartNew(null, function, cancellationToken,
5220 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default);
5224 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5225 /// Task returned by <paramref name="function"/>.
5227 /// <param name="function">The work to execute asynchronously</param>
5228 /// <returns>A Task that represents a proxy for the Task returned by <paramref name="function"/>.</returns>
5229 /// <exception cref="T:System.ArgumentNullException">
5230 /// The <paramref name="function"/> parameter was null.
5232 public static Task Run(Func<Task> function)
5234 return Run(function, default(CancellationToken));
5239 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5240 /// Task returned by <paramref name="function"/>.
5242 /// <param name="function">The work to execute asynchronously</param>
5243 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5244 /// <returns>A Task that represents a proxy for the Task returned by <paramref name="function"/>.</returns>
5245 /// <exception cref="T:System.ArgumentNullException">
5246 /// The <paramref name="function"/> parameter was null.
5248 /// <exception cref="T:System.ObjectDisposedException">
5249 /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
5251 public static Task Run(Func<Task> function, CancellationToken cancellationToken)
5254 if (function == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function);
5256 // Short-circuit if we are given a pre-canceled token
5257 if (cancellationToken.IsCancellationRequested)
5258 return Task.FromCanceled(cancellationToken);
5260 // Kick off initial Task, which will call the user-supplied function and yield a Task.
5261 Task<Task> task1 = Task<Task>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
5263 // Create a promise-style Task to be used as a proxy for the operation
5264 // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation.
5265 UnwrapPromise<VoidTaskResult> promise = new UnwrapPromise<VoidTaskResult>(task1, lookForOce: true);
5271 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5272 /// Task(TResult) returned by <paramref name="function"/>.
5274 /// <typeparam name="TResult">The type of the result returned by the proxy Task.</typeparam>
5275 /// <param name="function">The work to execute asynchronously</param>
5276 /// <returns>A Task(TResult) that represents a proxy for the Task(TResult) returned by <paramref name="function"/>.</returns>
5277 /// <exception cref="T:System.ArgumentNullException">
5278 /// The <paramref name="function"/> parameter was null.
5280 public static Task<TResult> Run<TResult>(Func<Task<TResult>> function)
5282 return Run(function, default(CancellationToken));
5286 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5287 /// Task(TResult) returned by <paramref name="function"/>.
5289 /// <typeparam name="TResult">The type of the result returned by the proxy Task.</typeparam>
5290 /// <param name="function">The work to execute asynchronously</param>
5291 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5292 /// <returns>A Task(TResult) that represents a proxy for the Task(TResult) returned by <paramref name="function"/>.</returns>
5293 /// <exception cref="T:System.ArgumentNullException">
5294 /// The <paramref name="function"/> parameter was null.
5296 public static Task<TResult> Run<TResult>(Func<Task<TResult>> function, CancellationToken cancellationToken)
5299 if (function == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function);
5301 // Short-circuit if we are given a pre-canceled token
5302 if (cancellationToken.IsCancellationRequested)
5303 return Task.FromCanceled<TResult>(cancellationToken);
5305 // Kick off initial Task, which will call the user-supplied function and yield a Task.
5306 Task<Task<TResult>> task1 = Task<Task<TResult>>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
5308 // Create a promise-style Task to be used as a proxy for the operation
5309 // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation.
5310 UnwrapPromise<TResult> promise = new UnwrapPromise<TResult>(task1, lookForOce: true);
5318 #region Delay methods
5321 /// Creates a Task that will complete after a time delay.
5323 /// <param name="delay">The time span to wait before completing the returned Task</param>
5324 /// <returns>A Task that represents the time delay</returns>
5325 /// <exception cref="T:System.ArgumentOutOfRangeException">
5326 /// The <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
5329 /// After the specified time delay, the Task is completed in RanToCompletion state.
5331 public static Task Delay(TimeSpan delay)
5333 return Delay(delay, default(CancellationToken));
5337 /// Creates a Task that will complete after a time delay.
5339 /// <param name="delay">The time span to wait before completing the returned Task</param>
5340 /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned Task</param>
5341 /// <returns>A Task that represents the time delay</returns>
5342 /// <exception cref="T:System.ArgumentOutOfRangeException">
5343 /// The <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
5345 /// <exception cref="T:System.ObjectDisposedException">
5346 /// The provided <paramref name="cancellationToken"/> has already been disposed.
5349 /// If the cancellation token is signaled before the specified time delay, then the Task is completed in
5350 /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time
5351 /// delay has expired.
5353 public static Task Delay(TimeSpan delay, CancellationToken cancellationToken)
5355 long totalMilliseconds = (long)delay.TotalMilliseconds;
5356 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
5358 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.delay, ExceptionResource.Task_Delay_InvalidDelay);
5361 return Delay((int)totalMilliseconds, cancellationToken);
5365 /// Creates a Task that will complete after a time delay.
5367 /// <param name="millisecondsDelay">The number of milliseconds to wait before completing the returned Task</param>
5368 /// <returns>A Task that represents the time delay</returns>
5369 /// <exception cref="T:System.ArgumentOutOfRangeException">
5370 /// The <paramref name="millisecondsDelay"/> is less than -1.
5373 /// After the specified time delay, the Task is completed in RanToCompletion state.
5375 public static Task Delay(int millisecondsDelay)
5377 return Delay(millisecondsDelay, default(CancellationToken));
5381 /// Creates a Task that will complete after a time delay.
5383 /// <param name="millisecondsDelay">The number of milliseconds to wait before completing the returned Task</param>
5384 /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned Task</param>
5385 /// <returns>A Task that represents the time delay</returns>
5386 /// <exception cref="T:System.ArgumentOutOfRangeException">
5387 /// The <paramref name="millisecondsDelay"/> is less than -1.
5389 /// <exception cref="T:System.ObjectDisposedException">
5390 /// The provided <paramref name="cancellationToken"/> has already been disposed.
5393 /// If the cancellation token is signaled before the specified time delay, then the Task is completed in
5394 /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time
5395 /// delay has expired.
5397 public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken)
5399 // Throw on non-sensical time
5400 if (millisecondsDelay < -1)
5402 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.millisecondsDelay, ExceptionResource.Task_Delay_InvalidMillisecondsDelay);
5405 // some short-cuts in case quick completion is in order
5406 if (cancellationToken.IsCancellationRequested)
5408 // return a Task created as already-Canceled
5409 return Task.FromCanceled(cancellationToken);
5411 else if (millisecondsDelay == 0)
5413 // return a Task created as already-RanToCompletion
5414 return Task.CompletedTask;
5417 // Construct a promise-style Task to encapsulate our return value
5418 var promise = new DelayPromise(cancellationToken);
5420 // Register our cancellation token, if necessary.
5421 if (cancellationToken.CanBeCanceled)
5423 promise.Registration = cancellationToken.InternalRegisterWithoutEC(state => ((DelayPromise)state).Complete(), promise);
5426 // ... and create our timer and make sure that it stays rooted.
5427 if (millisecondsDelay != Timeout.Infinite) // no need to create the timer if it's an infinite timeout
5429 promise.Timer = new TimerQueueTimer(state => ((DelayPromise)state).Complete(), promise, (uint)millisecondsDelay, Timeout.UnsignedInfinite);
5432 // Return the timer proxy task
5436 /// <summary>Task that also stores the completion closure and logic for Task.Delay implementation.</summary>
5437 private sealed class DelayPromise : Task<VoidTaskResult>
5439 internal DelayPromise(CancellationToken token)
5443 if (AsyncCausalityTracer.LoggingOn)
5444 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.Delay", 0);
5446 if (Task.s_asyncDebuggingEnabled)
5448 AddToActiveTasks(this);
5452 internal readonly CancellationToken Token;
5453 internal CancellationTokenRegistration Registration;
5454 internal TimerQueueTimer Timer;
5456 internal void Complete()
5458 // Transition the task to completed.
5461 if (Token.IsCancellationRequested)
5463 setSucceeded = TrySetCanceled(Token);
5467 if (AsyncCausalityTracer.LoggingOn)
5468 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
5470 if (Task.s_asyncDebuggingEnabled)
5472 RemoveFromActiveTasks(this.Id);
5474 setSucceeded = TrySetResult(default(VoidTaskResult));
5477 // If we set the value, also clean up.
5481 Registration.Dispose();
5489 /// Creates a task that will complete when all of the supplied tasks have completed.
5491 /// <param name="tasks">The tasks to wait on for completion.</param>
5492 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
5495 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
5496 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
5499 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
5502 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
5505 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
5506 /// state before it's returned to the caller.
5509 /// <exception cref="T:System.ArgumentNullException">
5510 /// The <paramref name="tasks"/> argument was null.
5512 /// <exception cref="T:System.ArgumentException">
5513 /// The <paramref name="tasks"/> collection contained a null task.
5515 public static Task WhenAll(IEnumerable<Task> tasks)
5517 // Take a more efficient path if tasks is actually an array
5518 Task[] taskArray = tasks as Task[];
5519 if (taskArray != null)
5521 return WhenAll(taskArray);
5524 // Skip a List allocation/copy if tasks is a collection
5525 ICollection<Task> taskCollection = tasks as ICollection<Task>;
5526 if (taskCollection != null)
5529 taskArray = new Task[taskCollection.Count];
5530 foreach (var task in tasks)
5532 if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
5533 taskArray[index++] = task;
5535 return InternalWhenAll(taskArray);
5538 // Do some argument checking and convert tasks to a List (and later an array).
5539 if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
5540 List<Task> taskList = new List<Task>();
5541 foreach (Task task in tasks)
5543 if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
5547 // Delegate the rest to InternalWhenAll()
5548 return InternalWhenAll(taskList.ToArray());
5552 /// Creates a task that will complete when all of the supplied tasks have completed.
5554 /// <param name="tasks">The tasks to wait on for completion.</param>
5555 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
5558 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
5559 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
5562 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
5565 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
5568 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
5569 /// state before it's returned to the caller.
5572 /// <exception cref="T:System.ArgumentNullException">
5573 /// The <paramref name="tasks"/> argument was null.
5575 /// <exception cref="T:System.ArgumentException">
5576 /// The <paramref name="tasks"/> array contained a null task.
5578 public static Task WhenAll(params Task[] tasks)
5580 // Do some argument checking and make a defensive copy of the tasks array
5581 if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
5583 int taskCount = tasks.Length;
5584 if (taskCount == 0) return InternalWhenAll(tasks); // Small optimization in the case of an empty array.
5586 Task[] tasksCopy = new Task[taskCount];
5587 for (int i = 0; i < taskCount; i++)
5589 Task task = tasks[i];
5590 if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
5591 tasksCopy[i] = task;
5594 // The rest can be delegated to InternalWhenAll()
5595 return InternalWhenAll(tasksCopy);
5598 // Some common logic to support WhenAll() methods
5599 // tasks should be a defensive copy.
5600 private static Task InternalWhenAll(Task[] tasks)
5602 Debug.Assert(tasks != null, "Expected a non-null tasks array");
5603 return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
5604 Task.CompletedTask :
5605 new WhenAllPromise(tasks);
5608 // A Task<VoidTaskResult> that gets completed when all of its constituent tasks complete.
5609 // Completion logic will analyze the antecedents in order to choose completion status.
5610 // This type allows us to replace this logic:
5611 // Task<VoidTaskResult> promise = new Task<VoidTaskResult>(...);
5612 // Action<Task> completionAction = delegate { <completion logic>};
5613 // TaskFactory.CommonCWAllLogic(tasksCopy).AddCompletionAction(completionAction);
5615 // which involves several allocations, with this logic:
5616 // return new WhenAllPromise(tasksCopy);
5617 // which saves a couple of allocations and enables debugger notification specialization.
5619 // Used in InternalWhenAll(Task[])
5620 private sealed class WhenAllPromise : Task<VoidTaskResult>, ITaskCompletionAction
5623 /// Stores all of the constituent tasks. Tasks clear themselves out of this
5624 /// array as they complete, but only if they don't have their wait notification bit set.
5626 private readonly Task[] m_tasks;
5627 /// <summary>The number of tasks remaining to complete.</summary>
5628 private int m_count;
5630 internal WhenAllPromise(Task[] tasks) :
5633 Debug.Assert(tasks != null, "Expected a non-null task array");
5634 Debug.Assert(tasks.Length > 0, "Expected a non-zero length task array");
5636 if (AsyncCausalityTracer.LoggingOn)
5637 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.WhenAll", 0);
5639 if (s_asyncDebuggingEnabled)
5641 AddToActiveTasks(this);
5645 m_count = tasks.Length;
5647 foreach (var task in tasks)
5649 if (task.IsCompleted) this.Invoke(task); // short-circuit the completion action, if possible
5650 else task.AddCompletionAction(this); // simple completion action
5654 public void Invoke(Task completedTask)
5656 if (AsyncCausalityTracer.LoggingOn)
5657 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
5659 // Decrement the count, and only continue to complete the promise if we're the last one.
5660 if (Interlocked.Decrement(ref m_count) == 0)
5662 // Set up some accounting variables
5663 List<ExceptionDispatchInfo> observedExceptions = null;
5664 Task canceledTask = null;
5666 // Loop through antecedents:
5667 // If any one of them faults, the result will be faulted
5668 // If none fault, but at least one is canceled, the result will be canceled
5669 // If none fault or are canceled, then result will be RanToCompletion
5670 for (int i = 0; i < m_tasks.Length; i++)
5672 var task = m_tasks[i];
5673 Debug.Assert(task != null, "Constituent task in WhenAll should never be null");
5677 if (observedExceptions == null) observedExceptions = new List<ExceptionDispatchInfo>();
5678 observedExceptions.AddRange(task.GetExceptionDispatchInfos());
5680 else if (task.IsCanceled)
5682 if (canceledTask == null) canceledTask = task; // use the first task that's canceled
5685 // Regardless of completion state, if the task has its debug bit set, transfer it to the
5686 // WhenAll task. We must do this before we complete the task.
5687 if (task.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
5688 else m_tasks[i] = null; // avoid holding onto tasks unnecessarily
5691 if (observedExceptions != null)
5693 Debug.Assert(observedExceptions.Count > 0, "Expected at least one exception");
5695 //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there
5697 TrySetException(observedExceptions);
5699 else if (canceledTask != null)
5701 TrySetCanceled(canceledTask.CancellationToken, canceledTask.GetCancellationExceptionDispatchInfo());
5705 if (AsyncCausalityTracer.LoggingOn)
5706 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
5708 if (Task.s_asyncDebuggingEnabled)
5710 RemoveFromActiveTasks(this.Id);
5712 TrySetResult(default(VoidTaskResult));
5715 Debug.Assert(m_count >= 0, "Count should never go below 0");
5718 public bool InvokeMayRunArbitraryCode { get { return true; } }
5721 /// Returns whether we should notify the debugger of a wait completion. This returns
5722 /// true iff at least one constituent task has its bit set.
5724 internal override bool ShouldNotifyDebuggerOfWaitCompletion
5729 base.ShouldNotifyDebuggerOfWaitCompletion &&
5730 Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks);
5736 /// Creates a task that will complete when all of the supplied tasks have completed.
5738 /// <param name="tasks">The tasks to wait on for completion.</param>
5739 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
5742 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
5743 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
5746 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
5749 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
5750 /// The Result of the returned task will be set to an array containing all of the results of the
5751 /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output
5752 /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result).
5755 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
5756 /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements.
5759 /// <exception cref="T:System.ArgumentNullException">
5760 /// The <paramref name="tasks"/> argument was null.
5762 /// <exception cref="T:System.ArgumentException">
5763 /// The <paramref name="tasks"/> collection contained a null task.
5765 public static Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks)
5767 // Take a more efficient route if tasks is actually an array
5768 Task<TResult>[] taskArray = tasks as Task<TResult>[];
5769 if (taskArray != null)
5771 return WhenAll<TResult>(taskArray);
5774 // Skip a List allocation/copy if tasks is a collection
5775 ICollection<Task<TResult>> taskCollection = tasks as ICollection<Task<TResult>>;
5776 if (taskCollection != null)
5779 taskArray = new Task<TResult>[taskCollection.Count];
5780 foreach (var task in tasks)
5782 if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
5783 taskArray[index++] = task;
5785 return InternalWhenAll<TResult>(taskArray);
5788 // Do some argument checking and convert tasks into a List (later an array)
5789 if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
5790 List<Task<TResult>> taskList = new List<Task<TResult>>();
5791 foreach (Task<TResult> task in tasks)
5793 if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
5797 // Delegate the rest to InternalWhenAll<TResult>().
5798 return InternalWhenAll<TResult>(taskList.ToArray());
5802 /// Creates a task that will complete when all of the supplied tasks have completed.
5804 /// <param name="tasks">The tasks to wait on for completion.</param>
5805 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
5808 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
5809 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
5812 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
5815 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
5816 /// The Result of the returned task will be set to an array containing all of the results of the
5817 /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output
5818 /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result).
5821 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
5822 /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements.
5825 /// <exception cref="T:System.ArgumentNullException">
5826 /// The <paramref name="tasks"/> argument was null.
5828 /// <exception cref="T:System.ArgumentException">
5829 /// The <paramref name="tasks"/> array contained a null task.
5831 public static Task<TResult[]> WhenAll<TResult>(params Task<TResult>[] tasks)
5833 // Do some argument checking and make a defensive copy of the tasks array
5834 if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
5836 int taskCount = tasks.Length;
5837 if (taskCount == 0) return InternalWhenAll<TResult>(tasks); // small optimization in the case of an empty task array
5839 Task<TResult>[] tasksCopy = new Task<TResult>[taskCount];
5840 for (int i = 0; i < taskCount; i++)
5842 Task<TResult> task = tasks[i];
5843 if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
5844 tasksCopy[i] = task;
5847 // Delegate the rest to InternalWhenAll<TResult>()
5848 return InternalWhenAll<TResult>(tasksCopy);
5851 // Some common logic to support WhenAll<TResult> methods
5852 private static Task<TResult[]> InternalWhenAll<TResult>(Task<TResult>[] tasks)
5854 Debug.Assert(tasks != null, "Expected a non-null tasks array");
5855 return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
5856 new Task<TResult[]>(false, new TResult[0], TaskCreationOptions.None, default(CancellationToken)) :
5857 new WhenAllPromise<TResult>(tasks);
5860 // A Task<T> that gets completed when all of its constituent tasks complete.
5861 // Completion logic will analyze the antecedents in order to choose completion status.
5862 // See comments for non-generic version of WhenAllPromise class.
5864 // Used in InternalWhenAll<TResult>(Task<TResult>[])
5865 private sealed class WhenAllPromise<T> : Task<T[]>, ITaskCompletionAction
5868 /// Stores all of the constituent tasks. Tasks clear themselves out of this
5869 /// array as they complete, but only if they don't have their wait notification bit set.
5871 private readonly Task<T>[] m_tasks;
5872 /// <summary>The number of tasks remaining to complete.</summary>
5873 private int m_count;
5875 internal WhenAllPromise(Task<T>[] tasks) :
5878 Debug.Assert(tasks != null, "Expected a non-null task array");
5879 Debug.Assert(tasks.Length > 0, "Expected a non-zero length task array");
5882 m_count = tasks.Length;
5884 if (AsyncCausalityTracer.LoggingOn)
5885 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.WhenAll", 0);
5887 if (s_asyncDebuggingEnabled)
5889 AddToActiveTasks(this);
5892 foreach (var task in tasks)
5894 if (task.IsCompleted) this.Invoke(task); // short-circuit the completion action, if possible
5895 else task.AddCompletionAction(this); // simple completion action
5899 public void Invoke(Task ignored)
5901 if (AsyncCausalityTracer.LoggingOn)
5902 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
5904 // Decrement the count, and only continue to complete the promise if we're the last one.
5905 if (Interlocked.Decrement(ref m_count) == 0)
5907 // Set up some accounting variables
5908 T[] results = new T[m_tasks.Length];
5909 List<ExceptionDispatchInfo> observedExceptions = null;
5910 Task canceledTask = null;
5912 // Loop through antecedents:
5913 // If any one of them faults, the result will be faulted
5914 // If none fault, but at least one is canceled, the result will be canceled
5915 // If none fault or are canceled, then result will be RanToCompletion
5916 for (int i = 0; i < m_tasks.Length; i++)
5918 Task<T> task = m_tasks[i];
5919 Debug.Assert(task != null, "Constituent task in WhenAll should never be null");
5923 if (observedExceptions == null) observedExceptions = new List<ExceptionDispatchInfo>();
5924 observedExceptions.AddRange(task.GetExceptionDispatchInfos());
5926 else if (task.IsCanceled)
5928 if (canceledTask == null) canceledTask = task; // use the first task that's canceled
5932 Debug.Assert(task.Status == TaskStatus.RanToCompletion);
5933 results[i] = task.GetResultCore(waitCompletionNotification: false); // avoid Result, which would triggering debug notification
5936 // Regardless of completion state, if the task has its debug bit set, transfer it to the
5937 // WhenAll task. We must do this before we complete the task.
5938 if (task.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
5939 else m_tasks[i] = null; // avoid holding onto tasks unnecessarily
5942 if (observedExceptions != null)
5944 Debug.Assert(observedExceptions.Count > 0, "Expected at least one exception");
5946 //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there
5948 TrySetException(observedExceptions);
5950 else if (canceledTask != null)
5952 TrySetCanceled(canceledTask.CancellationToken, canceledTask.GetCancellationExceptionDispatchInfo());
5956 if (AsyncCausalityTracer.LoggingOn)
5957 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
5959 if (Task.s_asyncDebuggingEnabled)
5961 RemoveFromActiveTasks(this.Id);
5963 TrySetResult(results);
5966 Debug.Assert(m_count >= 0, "Count should never go below 0");
5969 public bool InvokeMayRunArbitraryCode { get { return true; } }
5972 /// Returns whether we should notify the debugger of a wait completion. This returns true
5973 /// iff at least one constituent task has its bit set.
5975 internal override bool ShouldNotifyDebuggerOfWaitCompletion
5980 base.ShouldNotifyDebuggerOfWaitCompletion &&
5981 Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks);
5989 /// Creates a task that will complete when any of the supplied tasks have completed.
5991 /// <param name="tasks">The tasks to wait on for completion.</param>
5992 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
5994 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
5995 /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
5997 /// <exception cref="T:System.ArgumentNullException">
5998 /// The <paramref name="tasks"/> argument was null.
6000 /// <exception cref="T:System.ArgumentException">
6001 /// The <paramref name="tasks"/> array contained a null task, or was empty.
6003 public static Task<Task> WhenAny(params Task[] tasks)
6005 if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
6006 if (tasks.Length == 0)
6008 ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks);
6011 // Make a defensive copy, as the user may manipulate the tasks array
6012 // after we return but before the WhenAny asynchronously completes.
6013 int taskCount = tasks.Length;
6014 Task[] tasksCopy = new Task[taskCount];
6015 for (int i = 0; i < taskCount; i++)
6017 Task task = tasks[i];
6018 if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
6019 tasksCopy[i] = task;
6022 // Previously implemented CommonCWAnyLogic() can handle the rest
6023 return TaskFactory.CommonCWAnyLogic(tasksCopy);
6027 /// Creates a task that will complete when any of the supplied tasks have completed.
6029 /// <param name="tasks">The tasks to wait on for completion.</param>
6030 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6032 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6033 /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
6035 /// <exception cref="T:System.ArgumentNullException">
6036 /// The <paramref name="tasks"/> argument was null.
6038 /// <exception cref="T:System.ArgumentException">
6039 /// The <paramref name="tasks"/> collection contained a null task, or was empty.
6041 public static Task<Task> WhenAny(IEnumerable<Task> tasks)
6043 if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks);
6045 // Make a defensive copy, as the user may manipulate the tasks collection
6046 // after we return but before the WhenAny asynchronously completes.
6047 List<Task> taskList = new List<Task>();
6048 foreach (Task task in tasks)
6050 if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks);
6054 if (taskList.Count == 0)
6056 ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks);
6059 // Previously implemented CommonCWAnyLogic() can handle the rest
6060 return TaskFactory.CommonCWAnyLogic(taskList);
6064 /// Creates a task that will complete when any of the supplied tasks have completed.
6066 /// <param name="tasks">The tasks to wait on for completion.</param>
6067 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6069 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6070 /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
6072 /// <exception cref="T:System.ArgumentNullException">
6073 /// The <paramref name="tasks"/> argument was null.
6075 /// <exception cref="T:System.ArgumentException">
6076 /// The <paramref name="tasks"/> array contained a null task, or was empty.
6078 public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks)
6080 // We would just like to do this:
6081 // return (Task<Task<TResult>>) WhenAny( (Task[]) tasks);
6082 // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>.
6084 // Call WhenAny(Task[]) for basic functionality
6085 Task<Task> intermediate = WhenAny((Task[])tasks);
6087 // Return a continuation task with the correct result type
6088 return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default(CancellationToken),
6089 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
6093 /// Creates a task that will complete when any of the supplied tasks have completed.
6095 /// <param name="tasks">The tasks to wait on for completion.</param>
6096 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6098 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6099 /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
6101 /// <exception cref="T:System.ArgumentNullException">
6102 /// The <paramref name="tasks"/> argument was null.
6104 /// <exception cref="T:System.ArgumentException">
6105 /// The <paramref name="tasks"/> collection contained a null task, or was empty.
6107 public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks)
6109 // We would just like to do this:
6110 // return (Task<Task<TResult>>) WhenAny( (IEnumerable<Task>) tasks);
6111 // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>.
6113 // Call WhenAny(IEnumerable<Task>) for basic functionality
6114 Task<Task> intermediate = WhenAny((IEnumerable<Task>)tasks);
6116 // Return a continuation task with the correct result type
6117 return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default(CancellationToken),
6118 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
6122 internal static Task<TResult> CreateUnwrapPromise<TResult>(Task outerTask, bool lookForOce)
6124 Debug.Assert(outerTask != null);
6126 return new UnwrapPromise<TResult>(outerTask, lookForOce);
6129 internal virtual Delegate[] GetDelegateContinuationsForDebugger()
6131 //Avoid an infinite loop by making sure the continuation object is not a reference to istelf.
6132 if (m_continuationObject != this)
6133 return GetDelegatesFromContinuationObject(m_continuationObject);
6138 internal static Delegate[] GetDelegatesFromContinuationObject(object continuationObject)
6140 if (continuationObject != null)
6142 Action singleAction = continuationObject as Action;
6143 if (singleAction != null)
6145 return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(singleAction) };
6148 TaskContinuation taskContinuation = continuationObject as TaskContinuation;
6149 if (taskContinuation != null)
6151 return taskContinuation.GetDelegateContinuationsForDebugger();
6154 Task continuationTask = continuationObject as Task;
6155 if (continuationTask != null)
6157 Debug.Assert(continuationTask.m_action == null);
6158 Delegate[] delegates = continuationTask.GetDelegateContinuationsForDebugger();
6159 if (delegates != null)
6163 //We need this ITaskCompletionAction after the Task because in the case of UnwrapPromise
6164 //the VS debugger is more interested in the continuation than the internal invoke()
6165 ITaskCompletionAction singleCompletionAction = continuationObject as ITaskCompletionAction;
6166 if (singleCompletionAction != null)
6168 return new Delegate[] { new Action<Task>(singleCompletionAction.Invoke) };
6171 List<object> continuationList = continuationObject as List<object>;
6172 if (continuationList != null)
6174 List<Delegate> result = new List<Delegate>();
6175 foreach (object obj in continuationList)
6177 var innerDelegates = GetDelegatesFromContinuationObject(obj);
6178 if (innerDelegates != null)
6180 foreach (var del in innerDelegates)
6188 return result.ToArray();
6195 private static Task GetActiveTaskFromId(int taskId)
6198 s_currentActiveTasks.TryGetValue(taskId, out task);
6202 private static Task[] GetActiveTasks()
6204 return new List<Task>(s_currentActiveTasks.Values).ToArray();
6208 internal sealed class CompletionActionInvoker : IThreadPoolWorkItem
6210 private readonly ITaskCompletionAction m_action;
6211 private readonly Task m_completingTask;
6213 internal CompletionActionInvoker(ITaskCompletionAction action, Task completingTask)
6216 m_completingTask = completingTask;
6219 void IThreadPoolWorkItem.ExecuteWorkItem()
6221 m_action.Invoke(m_completingTask);
6224 void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
6230 // Proxy class for better debugging experience
6231 internal class SystemThreadingTasks_TaskDebugView
6233 private Task m_task;
6235 public SystemThreadingTasks_TaskDebugView(Task task)
6240 public object AsyncState { get { return m_task.AsyncState; } }
6241 public TaskCreationOptions CreationOptions { get { return m_task.CreationOptions; } }
6242 public Exception Exception { get { return m_task.Exception; } }
6243 public int Id { get { return m_task.Id; } }
6244 public bool CancellationPending { get { return (m_task.Status == TaskStatus.WaitingToRun) && m_task.CancellationToken.IsCancellationRequested; } }
6245 public TaskStatus Status { get { return m_task.Status; } }
6249 /// Specifies flags that control optional behavior for the creation and execution of tasks.
6251 // NOTE: These options are a subset of TaskContinuationsOptions, thus before adding a flag check it is
6252 // not already in use.
6254 public enum TaskCreationOptions
6257 /// Specifies that the default behavior should be used.
6262 /// A hint to a <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> to schedule a
6263 /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to
6264 /// be run sooner, and tasks scheduled later will be more likely to be run later.
6266 PreferFairness = 0x01,
6269 /// Specifies that a task will be a long-running, course-grained operation. It provides a hint to the
6270 /// <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> that oversubscription may be
6276 /// Specifies that a task is attached to a parent in the task hierarchy.
6278 AttachedToParent = 0x04,
6281 /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task.
6283 DenyChildAttach = 0x08,
6286 /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations
6287 /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler.
6289 HideScheduler = 0x10,
6291 // 0x20 is already being used in TaskContinuationOptions
6294 /// Forces continuations added to the current task to be executed asynchronously.
6295 /// This option has precedence over TaskContinuationOptions.ExecuteSynchronously
6297 RunContinuationsAsynchronously = 0x40
6302 /// Task creation flags which are only used internally.
6305 internal enum InternalTaskOptions
6307 /// <summary> Specifies "No internal task options" </summary>
6310 /// <summary>Used to filter out internal vs. public task creation options.</summary>
6311 InternalOptionsMask = 0x0000FF00,
6313 ContinuationTask = 0x0200,
6314 PromiseTask = 0x0400,
6317 /// Store the presence of TaskContinuationOptions.LazyCancellation, since it does not directly
6318 /// translate into any TaskCreationOptions.
6320 LazyCancellation = 0x1000,
6322 /// <summary>Specifies that the task will be queued by the runtime before handing it over to the user.
6323 /// This flag will be used to skip the cancellationtoken registration step, which is only meant for unstarted tasks.</summary>
6324 QueuedByRuntime = 0x2000,
6327 /// Denotes that Dispose should be a complete nop for a Task. Used when constructing tasks that are meant to be cached/reused.
6329 DoNotDispose = 0x4000
6333 /// Specifies flags that control optional behavior for the creation and execution of continuation tasks.
6336 public enum TaskContinuationOptions
6339 /// Default = "Continue on any, no task options, run asynchronously"
6340 /// Specifies that the default behavior should be used. Continuations, by default, will
6341 /// be scheduled when the antecedent task completes, regardless of the task's final <see
6342 /// cref="System.Threading.Tasks.TaskStatus">TaskStatus</see>.
6346 // These are identical to their meanings and values in TaskCreationOptions
6349 /// A hint to a <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> to schedule a
6350 /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to
6351 /// be run sooner, and tasks scheduled later will be more likely to be run later.
6353 PreferFairness = 0x01,
6356 /// Specifies that a task will be a long-running, course-grained operation. It provides
6357 /// a hint to the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> that
6358 /// oversubscription may be warranted.
6362 /// Specifies that a task is attached to a parent in the task hierarchy.
6364 AttachedToParent = 0x04,
6367 /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task.
6369 DenyChildAttach = 0x08,
6371 /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations
6372 /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler.
6374 HideScheduler = 0x10,
6377 /// In the case of continuation cancellation, prevents completion of the continuation until the antecedent has completed.
6379 LazyCancellation = 0x20,
6381 RunContinuationsAsynchronously = 0x40,
6383 // These are specific to continuations
6386 /// Specifies that the continuation task should not be scheduled if its antecedent ran to completion.
6387 /// This option is not valid for multi-task continuations.
6389 NotOnRanToCompletion = 0x10000,
6391 /// Specifies that the continuation task should not be scheduled if its antecedent threw an unhandled
6392 /// exception. This option is not valid for multi-task continuations.
6394 NotOnFaulted = 0x20000,
6396 /// Specifies that the continuation task should not be scheduled if its antecedent was canceled. This
6397 /// option is not valid for multi-task continuations.
6399 NotOnCanceled = 0x40000,
6401 /// Specifies that the continuation task should be scheduled only if its antecedent ran to
6402 /// completion. This option is not valid for multi-task continuations.
6404 OnlyOnRanToCompletion = NotOnFaulted | NotOnCanceled,
6406 /// Specifies that the continuation task should be scheduled only if its antecedent threw an
6407 /// unhandled exception. This option is not valid for multi-task continuations.
6409 OnlyOnFaulted = NotOnRanToCompletion | NotOnCanceled,
6411 /// Specifies that the continuation task should be scheduled only if its antecedent was canceled.
6412 /// This option is not valid for multi-task continuations.
6414 OnlyOnCanceled = NotOnRanToCompletion | NotOnFaulted,
6416 /// Specifies that the continuation task should be executed synchronously. With this option
6417 /// specified, the continuation will be run on the same thread that causes the antecedent task to
6418 /// transition into its final state. If the antecedent is already complete when the continuation is
6419 /// created, the continuation will run on the thread creating the continuation. Only very
6420 /// short-running continuations should be executed synchronously.
6422 ExecuteSynchronously = 0x80000
6426 /// Internal helper class to keep track of stack depth and decide whether we should inline or not.
6428 internal class StackGuard
6430 // current thread's depth of nested inline task executions
6431 private int m_inliningDepth = 0;
6433 // For relatively small inlining depths we don't want to get into the business of stack probing etc.
6434 // This clearly leaves a window of opportunity for the user code to SO. However a piece of code
6435 // that can SO in 20 inlines on a typical 1MB stack size probably needs to be revisited anyway.
6436 private const int MAX_UNCHECKED_INLINING_DEPTH = 20;
6439 /// This method needs to be called before attempting inline execution on the current thread.
6440 /// If false is returned, it means we are too close to the end of the stack and should give up inlining.
6441 /// Each call to TryBeginInliningScope() that returns true must be matched with a
6442 /// call to EndInliningScope() regardless of whether inlining actually took place.
6444 internal bool TryBeginInliningScope()
6446 // If we're still under the 'safe' limit we'll just skip the stack probe to save p/invoke calls
6447 if (m_inliningDepth < MAX_UNCHECKED_INLINING_DEPTH || CheckForSufficientStack())
6457 /// This needs to be called once for each previous successful TryBeginInliningScope() call after
6458 /// inlining related logic runs.
6460 internal void EndInliningScope()
6463 Debug.Assert(m_inliningDepth >= 0, "Inlining depth count should never go negative.");
6465 // do the right thing just in case...
6466 if (m_inliningDepth < 0) m_inliningDepth = 0;
6469 private unsafe bool CheckForSufficientStack()
6471 return RuntimeHelpers.TryEnsureSufficientExecutionStack();
6475 // Special internal struct that we use to signify that we are not interested in
6476 // a Task<VoidTaskResult>'s result.
6477 internal struct VoidTaskResult { }
6479 // Interface to which all completion actions must conform.
6480 // This interface allows us to combine functionality and reduce allocations.
6481 // For example, see Task.SetOnInvokeMres, and its use in Task.SpinThenBlockingWait().
6483 // ManualResetEvent mres = new ManualResetEventSlim(false, 0);
6484 // Action<Task> completionAction = delegate { mres.Set() ; };
6485 // AddCompletionAction(completionAction);
6486 // gets replaced with this:
6487 // SetOnInvokeMres mres = new SetOnInvokeMres();
6488 // AddCompletionAction(mres);
6489 // For additional examples of where this is used, see internal classes Task.SignalOnInvokeCDE,
6490 // Task.WhenAllPromise, Task.WhenAllPromise<T>, TaskFactory.CompleteOnCountdownPromise,
6491 // TaskFactory.CompleteOnCountdownPromise<T>, and TaskFactory.CompleteOnInvokePromise.
6492 internal interface ITaskCompletionAction
6494 /// <summary>Invoked to run the completion action.</summary>
6495 void Invoke(Task completingTask);
6498 /// Some completion actions are considered internal implementation details of tasks,
6499 /// using the continuation mechanism only for performance reasons. Such actions perform
6500 /// known quantities and types of work, and can be invoked safely as a continuation even
6501 /// if the system wants to prevent arbitrary continuations from running synchronously.
6502 /// This should only return false for a limited set of implementations where a small amount
6503 /// of work is guaranteed to be performed, e.g. setting a ManualResetEventSlim.
6505 bool InvokeMayRunArbitraryCode { get; }
6508 // This class encapsulates all "unwrap" logic, and also implements ITaskCompletionAction,
6509 // which minimizes the allocations needed for queuing it to its antecedent. This
6510 // logic is used by both the Unwrap extension methods and the unwrap-style Task.Run methods.
6511 internal sealed class UnwrapPromise<TResult> : Task<TResult>, ITaskCompletionAction
6513 // The possible states for our UnwrapPromise, used by Invoke() to determine which logic to execute
6514 private const byte STATE_WAITING_ON_OUTER_TASK = 0; // Invoke() means "process completed outer task"
6515 private const byte STATE_WAITING_ON_INNER_TASK = 1; // Invoke() means "process completed inner task"
6516 private const byte STATE_DONE = 2; // Invoke() means "something went wrong and we are hosed!"
6518 // Keep track of our state; initialized to STATE_WAITING_ON_OUTER_TASK in the constructor
6519 private byte _state;
6521 // "Should we check for OperationCanceledExceptions on the outer task and interpret them as proxy cancellation?"
6522 // Unwrap() sets this to false, Run() sets it to true.
6523 private readonly bool _lookForOce;
6525 public UnwrapPromise(Task outerTask, bool lookForOce)
6526 : base((object)null, outerTask.CreationOptions & TaskCreationOptions.AttachedToParent)
6528 Debug.Assert(outerTask != null, "Expected non-null outerTask");
6529 _lookForOce = lookForOce;
6530 _state = STATE_WAITING_ON_OUTER_TASK;
6532 if (AsyncCausalityTracer.LoggingOn)
6533 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.Unwrap", 0);
6535 if (Task.s_asyncDebuggingEnabled)
6537 AddToActiveTasks(this);
6540 // Link ourselves to the outer task.
6541 // If the outer task has already completed, take the fast path
6542 // of immediately transferring its results or processing the inner task.
6543 if (outerTask.IsCompleted)
6545 ProcessCompletedOuterTask(outerTask);
6547 else // Otherwise, process its completion asynchronously.
6549 outerTask.AddCompletionAction(this);
6553 // For ITaskCompletionAction
6554 public void Invoke(Task completingTask)
6556 // Check the current stack guard. If we're ok to inline,
6557 // process the task, and reset the guard when we're done.
6558 var sg = Task.CurrentStackGuard;
6559 if (sg.TryBeginInliningScope())
6561 try { InvokeCore(completingTask); }
6562 finally { sg.EndInliningScope(); }
6564 // Otherwise, we're too deep on the stack, and
6565 // we shouldn't run the continuation chain here, so queue a work
6566 // item to call back here to Invoke asynchronously.
6567 else InvokeCoreAsync(completingTask);
6571 /// Processes the completed task. InvokeCore could be called twice:
6572 /// once for the outer task, once for the inner task.
6574 /// <param name="completingTask">The completing outer or inner task.</param>
6575 private void InvokeCore(Task completingTask)
6579 case STATE_WAITING_ON_OUTER_TASK:
6580 ProcessCompletedOuterTask(completingTask);
6581 // We bump the state inside of ProcessCompletedOuterTask because it can also be called from the constructor.
6583 case STATE_WAITING_ON_INNER_TASK:
6584 bool result = TrySetFromTask(completingTask, lookForOce: false);
6585 _state = STATE_DONE; // bump the state
6586 Debug.Assert(result, "Expected TrySetFromTask from inner task to succeed");
6589 Debug.Fail("UnwrapPromise in illegal state");
6594 // Calls InvokeCore asynchronously.
6595 private void InvokeCoreAsync(Task completingTask)
6597 // Queue a call to Invoke. If we're so deep on the stack that we're at risk of overflowing,
6598 // there's a high liklihood this thread is going to be doing lots more work before
6599 // returning to the thread pool (at the very least unwinding through thousands of
6600 // stack frames). So we queue to the global queue.
6601 ThreadPool.UnsafeQueueUserWorkItem(state =>
6603 // InvokeCore(completingTask);
6604 var tuple = (Tuple<UnwrapPromise<TResult>, Task>)state;
6605 tuple.Item1.InvokeCore(tuple.Item2);
6606 }, Tuple.Create<UnwrapPromise<TResult>, Task>(this, completingTask));
6609 /// <summary>Processes the outer task once it's completed.</summary>
6610 /// <param name="task">The now-completed outer task.</param>
6611 private void ProcessCompletedOuterTask(Task task)
6613 Debug.Assert(task != null && task.IsCompleted, "Expected non-null, completed outer task");
6614 Debug.Assert(_state == STATE_WAITING_ON_OUTER_TASK, "We're in the wrong state!");
6616 // Bump our state before proceeding any further
6617 _state = STATE_WAITING_ON_INNER_TASK;
6619 switch (task.Status)
6621 // If the outer task did not complete successfully, then record the
6622 // cancellation/fault information to tcs.Task.
6623 case TaskStatus.Canceled:
6624 case TaskStatus.Faulted:
6625 bool result = TrySetFromTask(task, _lookForOce);
6626 Debug.Assert(result, "Expected TrySetFromTask from outer task to succeed");
6629 // Otherwise, process the inner task it returned.
6630 case TaskStatus.RanToCompletion:
6631 var taskOfTaskOfTResult = task as Task<Task<TResult>>; // it's either a Task<Task> or Task<Task<TResult>>
6632 ProcessInnerTask(taskOfTaskOfTResult != null ?
6633 taskOfTaskOfTResult.Result : ((Task<Task>)task).Result);
6638 /// <summary>Transfer the completion status from "task" to ourself.</summary>
6639 /// <param name="task">The source task whose results should be transfered to <paramref name="promise"/>.</param>
6640 /// <param name="lookForOce">Whether or not to look for OperationCanceledExceptions in task's exceptions if it faults.</param>
6641 /// <returns>true if the transfer was successful; otherwise, false.</returns>
6642 private bool TrySetFromTask(Task task, bool lookForOce)
6644 Debug.Assert(task != null && task.IsCompleted, "TrySetFromTask: Expected task to have completed.");
6646 if (AsyncCausalityTracer.LoggingOn)
6647 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
6649 bool result = false;
6650 switch (task.Status)
6652 case TaskStatus.Canceled:
6653 result = TrySetCanceled(task.CancellationToken, task.GetCancellationExceptionDispatchInfo());
6656 case TaskStatus.Faulted:
6657 var edis = task.GetExceptionDispatchInfos();
6658 ExceptionDispatchInfo oceEdi;
6659 OperationCanceledException oce;
6660 if (lookForOce && edis.Count > 0 &&
6661 (oceEdi = edis[0]) != null &&
6662 (oce = oceEdi.SourceException as OperationCanceledException) != null)
6664 result = TrySetCanceled(oce.CancellationToken, oceEdi);
6668 result = TrySetException(edis);
6672 case TaskStatus.RanToCompletion:
6673 var taskTResult = task as Task<TResult>;
6675 if (AsyncCausalityTracer.LoggingOn)
6676 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
6678 if (Task.s_asyncDebuggingEnabled)
6680 RemoveFromActiveTasks(this.Id);
6683 result = TrySetResult(taskTResult != null ? taskTResult.Result : default(TResult));
6690 /// Processes the inner task of a Task{Task} or Task{Task{TResult}},
6691 /// transferring the appropriate results to ourself.
6693 /// <param name="task">The inner task returned by the task provided by the user.</param>
6694 private void ProcessInnerTask(Task task)
6696 // If the inner task is null, the proxy should be canceled.
6699 TrySetCanceled(default(CancellationToken));
6700 _state = STATE_DONE; // ... and record that we are done
6703 // Fast path for if the inner task is already completed
6704 else if (task.IsCompleted)
6706 TrySetFromTask(task, lookForOce: false);
6707 _state = STATE_DONE; // ... and record that we are done
6710 // The inner task exists but is not yet complete, so when it does complete,
6711 // take some action to set our completion state.
6714 task.AddCompletionAction(this);
6715 // We'll record that we are done when Invoke() is called.
6719 public bool InvokeMayRunArbitraryCode { get { return true; } }