3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
10 // <OWNER>[....]</OWNER>
12 // A schedulable unit of work.
14 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
20 using System.Runtime.CompilerServices;
21 using System.Runtime.InteropServices;
22 using System.Runtime.ExceptionServices;
23 using System.Security;
24 using System.Security.Permissions;
25 using System.Threading;
26 using System.Diagnostics;
27 using System.Diagnostics.Contracts;
28 using Microsoft.Win32;
29 using System.Diagnostics.Tracing;
31 // Disable the "reference to volatile field not treated as volatile" error.
32 #pragma warning disable 0420
34 namespace System.Threading.Tasks
38 /// Utility class for allocating structs as heap variables
40 internal class Shared<T>
44 internal Shared(T value)
52 /// Represents the current stage in the lifecycle of a <see cref="Task"/>.
54 public enum TaskStatus
57 /// The task has been initialized but has not yet been scheduled.
61 /// The task is waiting to be activated and scheduled internally by the .NET Framework infrastructure.
65 /// The task has been scheduled for execution but has not yet begun executing.
69 /// The task is running but has not yet completed.
73 // /// The task is currently blocked in a wait state.
77 /// The task has finished executing and is implicitly waiting for
78 /// attached child tasks to complete.
80 WaitingForChildrenToComplete,
82 /// The task completed execution successfully.
86 /// The task acknowledged cancellation by throwing an OperationCanceledException with its own CancellationToken
87 /// while the token was in signaled state, or the task's CancellationToken was already signaled before the
88 /// task started executing.
92 /// The task completed due to an unhandled exception.
98 /// Represents an asynchronous operation.
102 /// <see cref="Task"/> instances may be created in a variety of ways. The most common approach is by
103 /// using the Task type's <see cref="Factory"/> property to retrieve a <see
104 /// cref="System.Threading.Tasks.TaskFactory"/> instance that can be used to create tasks for several
105 /// purposes. For example, to create a <see cref="Task"/> that runs an action, the factory's StartNew
106 /// method may be used:
109 /// var t = Task.Factory.StartNew(() => DoAction());
112 /// Dim t = Task.Factory.StartNew(Function() DoAction())
116 /// The <see cref="Task"/> class also provides constructors that initialize the Task but that do not
117 /// schedule it for execution. For performance reasons, TaskFactory's StartNew method should be the
118 /// preferred mechanism for creating and scheduling computational tasks, but for scenarios where creation
119 /// and scheduling must be separated, the constructors may be used, and the task's <see cref="Start()"/>
120 /// method may then be used to schedule the task for execution at a later time.
123 /// All members of <see cref="Task"/>, except for <see cref="Dispose()"/>, are thread-safe
124 /// and may be used from multiple threads concurrently.
127 /// For operations that return values, the <see cref="System.Threading.Tasks.Task{TResult}"/> class
131 /// For developers implementing custom debuggers, several internal and private members of Task may be
132 /// useful (these may change from release to release). The Int32 m_taskId field serves as the backing
133 /// store for the <see cref="Id"/> property, however accessing this field directly from a debugger may be
134 /// more efficient than accessing the same value through the property's getter method (the
135 /// s_taskIdCounter Int32 counter is used to retrieve the next available ID for a Task). Similarly, the
136 /// Int32 m_stateFlags field stores information about the current lifecycle stage of the Task,
137 /// information also accessible through the <see cref="Status"/> property. The m_action System.Object
138 /// field stores a reference to the Task's delegate, and the m_stateObject System.Object field stores the
139 /// async state passed to the Task by the developer. Finally, for debuggers that parse stack frames, the
140 /// InternalWait method serves a potential marker for when a Task is entering a wait operation.
143 [HostProtection(Synchronization = true, ExternalThreading = true)]
144 [DebuggerTypeProxy(typeof(SystemThreadingTasks_TaskDebugView))]
145 [DebuggerDisplay("Id = {Id}, Status = {Status}, Method = {DebuggerDisplayMethodDescription}")]
146 public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable
149 internal static Task t_currentTask; // The currently executing task.
151 private static StackGuard t_stackGuard; // The stack guard object for this thread
153 internal static int s_taskIdCounter; //static counter used to generate unique task IDs
154 private readonly static TaskFactory s_factory = new TaskFactory();
156 private volatile int m_taskId; // this task's unique ID. initialized only if it is ever requested
158 internal object m_action; // The body of the task. Might be Action<object>, Action<TState> or Action. Or possibly a Func.
159 // If m_action is set to null it will indicate that we operate in the
160 // "externally triggered completion" mode, which is exclusively meant
161 // for the signalling Task<TResult> (aka. promise). In this mode,
162 // we don't call InnerInvoke() in response to a Wait(), but simply wait on
163 // the completion event which will be set when the Future class calls Finish().
164 // But the event would now be signalled if Cancel() is called
167 internal object m_stateObject; // A state object that can be optionally supplied, passed to action.
168 internal TaskScheduler m_taskScheduler; // The task scheduler this task runs under.
170 internal readonly Task m_parent; // A task's parent, or null if parent-less.
173 internal volatile int m_stateFlags;
175 // State constants for m_stateFlags;
176 // The bits of m_stateFlags are allocated as follows:
177 // 0x40000000 - TaskBase state flag
178 // 0x3FFF0000 - Task state flags
179 // 0x0000FF00 - internal TaskCreationOptions flags
180 // 0x000000FF - publicly exposed TaskCreationOptions flags
182 // See TaskCreationOptions for bit values associated with TaskCreationOptions
184 private const int OptionsMask = 0xFFFF; // signifies the Options portion of m_stateFlags bin: 0000 0000 0000 0000 1111 1111 1111 1111
185 internal const int TASK_STATE_STARTED = 0x10000; //bin: 0000 0000 0000 0001 0000 0000 0000 0000
186 internal const int TASK_STATE_DELEGATE_INVOKED = 0x20000; //bin: 0000 0000 0000 0010 0000 0000 0000 0000
187 internal const int TASK_STATE_DISPOSED = 0x40000; //bin: 0000 0000 0000 0100 0000 0000 0000 0000
188 internal const int TASK_STATE_EXCEPTIONOBSERVEDBYPARENT = 0x80000; //bin: 0000 0000 0000 1000 0000 0000 0000 0000
189 internal const int TASK_STATE_CANCELLATIONACKNOWLEDGED = 0x100000; //bin: 0000 0000 0001 0000 0000 0000 0000 0000
190 internal const int TASK_STATE_FAULTED = 0x200000; //bin: 0000 0000 0010 0000 0000 0000 0000 0000
191 internal const int TASK_STATE_CANCELED = 0x400000; //bin: 0000 0000 0100 0000 0000 0000 0000 0000
192 internal const int TASK_STATE_WAITING_ON_CHILDREN = 0x800000; //bin: 0000 0000 1000 0000 0000 0000 0000 0000
193 internal const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000; //bin: 0000 0001 0000 0000 0000 0000 0000 0000
194 internal const int TASK_STATE_WAITINGFORACTIVATION = 0x2000000; //bin: 0000 0010 0000 0000 0000 0000 0000 0000
195 internal const int TASK_STATE_COMPLETION_RESERVED = 0x4000000; //bin: 0000 0100 0000 0000 0000 0000 0000 0000
196 internal const int TASK_STATE_THREAD_WAS_ABORTED = 0x8000000; //bin: 0000 1000 0000 0000 0000 0000 0000 0000
197 internal const int TASK_STATE_WAIT_COMPLETION_NOTIFICATION = 0x10000000; //bin: 0001 0000 0000 0000 0000 0000 0000 0000
198 //This could be moved to InternalTaskOptions enum
199 internal const int TASK_STATE_EXECUTIONCONTEXT_IS_NULL = 0x20000000; //bin: 0010 0000 0000 0000 0000 0000 0000 0000
200 internal const int TASK_STATE_TASKSCHEDULED_WAS_FIRED = 0x40000000; //bin: 0100 0000 0000 0000 0000 0000 0000 0000
202 // A mask for all of the final states a task may be in
203 private const int TASK_STATE_COMPLETED_MASK = TASK_STATE_CANCELED | TASK_STATE_FAULTED | TASK_STATE_RAN_TO_COMPLETION;
205 // Values for ContingentProperties.m_internalCancellationRequested.
206 private const int CANCELLATION_REQUESTED = 0x1;
208 // Can be null, a single continuation, a list of continuations, or s_taskCompletionSentinel,
209 // in that order. The logic arround this object assumes it will never regress to a previous state.
210 private volatile object m_continuationObject = null;
212 // m_continuationObject is set to this when the task completes.
213 private static readonly object s_taskCompletionSentinel = new object();
215 // A private flag that would be set (only) by the debugger
216 // When true the Async Causality logging trace is enabled as well as a dictionary to relate operation ids with Tasks
217 [FriendAccessAllowed]
218 internal static bool s_asyncDebuggingEnabled; //false by default
220 // This dictonary relates the task id, from an operation id located in the Async Causality log to the actual
221 // task. This is to be used by the debugger ONLY. Task in this dictionary represent current active tasks.
222 private static readonly Dictionary<int, Task> s_currentActiveTasks = new Dictionary<int, Task>();
223 private static readonly Object s_activeTasksLock = new Object();
225 // These methods are a way to access the dictionary both from this class and for other classes that also
226 // activate dummy tasks. Specifically the AsyncTaskMethodBuilder and AsyncTaskMethodBuilder<>
227 [FriendAccessAllowed]
228 internal static bool AddToActiveTasks(Task task)
230 Contract.Requires(task != null, "Null Task objects can't be added to the ActiveTasks collection");
231 lock (s_activeTasksLock)
233 s_currentActiveTasks[task.Id] = task;
235 //always return true to keep signature as bool for backwards compatibility
239 [FriendAccessAllowed]
240 internal static void RemoveFromActiveTasks(int taskId)
242 lock (s_activeTasksLock)
244 s_currentActiveTasks.Remove(taskId);
248 // We moved a number of Task properties into this class. The idea is that in most cases, these properties never
249 // need to be accessed during the life cycle of a Task, so we don't want to instantiate them every time. Once
250 // one of these properties needs to be written, we will instantiate a ContingentProperties object and set
251 // the appropriate property.
252 internal class ContingentProperties
254 // Additional context
256 internal ExecutionContext m_capturedContext; // The execution context to run the task within, if any.
258 // Completion fields (exceptions and event)
260 internal volatile ManualResetEventSlim m_completionEvent; // Lazily created if waiting is required.
261 internal volatile TaskExceptionHolder m_exceptionsHolder; // Tracks exceptions, if any have occurred
263 // Cancellation fields (token, registration, and internally requested)
265 internal CancellationToken m_cancellationToken; // Task's cancellation token, if it has one
266 internal Shared<CancellationTokenRegistration> m_cancellationRegistration; // Task's registration with the cancellation token
267 internal volatile int m_internalCancellationRequested; // Its own field because threads legally ---- to set it.
271 // # of active children + 1 (for this task itself).
272 // Used for ensuring all children are done before this task can complete
273 // The extra count helps prevent the ---- for executing the final state transition
274 // (i.e. whether the last child or this task itself should call FinishStageTwo())
275 internal volatile int m_completionCountdown = 1;
276 // A list of child tasks that threw an exception (TCEs don't count),
277 // but haven't yet been waited on by the parent, lazily initialized.
278 internal volatile List<Task> m_exceptionalChildren;
281 /// Sets the internal completion event.
283 internal void SetCompleted()
285 var mres = m_completionEvent;
286 if (mres != null) mres.Set();
290 /// Checks if we registered a CT callback during construction, and deregisters it.
291 /// This should be called when we know the registration isn't useful anymore. Specifically from Finish() if the task has completed
292 /// successfully or with an exception.
294 internal void DeregisterCancellationCallback()
296 if (m_cancellationRegistration != null)
298 // Harden against ODEs thrown from disposing of the CTR.
299 // Since the task has already been put into a final state by the time this
300 // is called, all we can do here is suppress the exception.
301 try { m_cancellationRegistration.Value.Dispose(); }
302 catch (ObjectDisposedException) { }
303 m_cancellationRegistration = null;
309 // This field will only be instantiated to some non-null value if any ContingentProperties need to be set.
310 // This will be a ContingentProperties instance or a type derived from it
311 internal volatile ContingentProperties m_contingentProperties;
313 // Special internal constructor to create an already-completed task.
314 // if canceled==true, create a Canceled task, or else create a RanToCompletion task.
315 // Constructs the task as already completed
316 internal Task(bool canceled, TaskCreationOptions creationOptions, CancellationToken ct)
318 int optionFlags = (int)creationOptions;
321 m_stateFlags = TASK_STATE_CANCELED | TASK_STATE_CANCELLATIONACKNOWLEDGED | optionFlags;
322 ContingentProperties props;
323 m_contingentProperties = props = new ContingentProperties(); // can't have children, so just instantiate directly
324 props.m_cancellationToken = ct;
325 props.m_internalCancellationRequested = CANCELLATION_REQUESTED;
328 m_stateFlags = TASK_STATE_RAN_TO_COMPLETION | optionFlags;
331 // Uncomment if/when we want Task.FromException
332 //// Special internal constructor to create an already-Faulted task.
333 //internal Task(Exception exception)
335 // Contract.Assert(exception != null);
336 // ContingentProperties props;
337 // m_contingentProperties = props = new ContingentProperties(); // can't have children, so just instantiate directly
338 // props.m_exceptionsHolder.Add(exception);
339 // m_stateFlags = TASK_STATE_FAULTED;
342 /// <summary>Constructor for use with promise-style tasks that aren't configurable.</summary>
345 m_stateFlags = TASK_STATE_WAITINGFORACTIVATION | (int)InternalTaskOptions.PromiseTask;
348 // Special constructor for use with promise-style tasks.
349 // Added promiseStyle parameter as an aid to the compiler to distinguish between (state,TCO) and
350 // (action,TCO). It should always be true.
351 internal Task(object state, TaskCreationOptions creationOptions, bool promiseStyle)
353 Contract.Assert(promiseStyle, "Promise CTOR: promiseStyle was false");
355 // Check the creationOptions. We allow the AttachedToParent option to be specified for promise tasks.
356 // Also allow RunContinuationsAsynchronously because this is the constructor called by TCS
357 if ((creationOptions & ~(TaskCreationOptions.AttachedToParent | TaskCreationOptions.RunContinuationsAsynchronously)) != 0)
359 throw new ArgumentOutOfRangeException("creationOptions");
362 // m_parent is readonly, and so must be set in the constructor.
363 // Only set a parent if AttachedToParent is specified.
364 if ((creationOptions & TaskCreationOptions.AttachedToParent) != 0)
365 m_parent = Task.InternalCurrent;
367 TaskConstructorCore(null, state, default(CancellationToken), creationOptions, InternalTaskOptions.PromiseTask, null);
371 /// Initializes a new <see cref="Task"/> with the specified action.
373 /// <param name="action">The delegate that represents the code to execute in the Task.</param>
374 /// <exception cref="T:System.ArgumentNullException">The <paramref name="action"/> argument is null.</exception>
375 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
376 public Task(Action action)
377 : this(action, null, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null)
379 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
380 PossiblyCaptureContext(ref stackMark);
384 /// Initializes a new <see cref="Task"/> with the specified action and <see cref="System.Threading.CancellationToken">CancellationToken</see>.
386 /// <param name="action">The delegate that represents the code to execute in the Task.</param>
387 /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see>
388 /// that will be assigned to the new Task.</param>
389 /// <exception cref="T:System.ArgumentNullException">The <paramref name="action"/> argument is null.</exception>
390 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
391 /// has already been disposed.
393 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
394 public Task(Action action, CancellationToken cancellationToken)
395 : this(action, null, null, cancellationToken, TaskCreationOptions.None, InternalTaskOptions.None, null)
397 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
398 PossiblyCaptureContext(ref stackMark);
402 /// Initializes a new <see cref="Task"/> with the specified action and creation options.
404 /// <param name="action">The delegate that represents the code to execute in the task.</param>
405 /// <param name="creationOptions">
406 /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
407 /// customize the Task's behavior.
409 /// <exception cref="T:System.ArgumentNullException">
410 /// The <paramref name="action"/> argument is null.
412 /// <exception cref="T:System.ArgumentOutOfRangeException">
413 /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
414 /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
416 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
417 public Task(Action action, TaskCreationOptions creationOptions)
418 : this(action, null, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, InternalTaskOptions.None, null)
420 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
421 PossiblyCaptureContext(ref stackMark);
425 /// Initializes a new <see cref="Task"/> with the specified action and creation options.
427 /// <param name="action">The delegate that represents the code to execute in the task.</param>
428 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
429 /// <param name="creationOptions">
430 /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
431 /// customize the Task's behavior.
433 /// <exception cref="T:System.ArgumentNullException">
434 /// The <paramref name="action"/> argument is null.
436 /// <exception cref="T:System.ArgumentOutOfRangeException">
437 /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
438 /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
440 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
441 /// has already been disposed.
443 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
444 public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
445 : this(action, null, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, creationOptions, InternalTaskOptions.None, null)
447 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
448 PossiblyCaptureContext(ref stackMark);
453 /// Initializes a new <see cref="Task"/> with the specified action and state.
455 /// <param name="action">The delegate that represents the code to execute in the task.</param>
456 /// <param name="state">An object representing data to be used by the action.</param>
457 /// <exception cref="T:System.ArgumentNullException">
458 /// The <paramref name="action"/> argument is null.
460 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
461 public Task(Action<object> action, object state)
462 : this(action, state, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null)
464 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
465 PossiblyCaptureContext(ref stackMark);
469 /// Initializes a new <see cref="Task"/> with the specified action, state, snd options.
471 /// <param name="action">The delegate that represents the code to execute in the task.</param>
472 /// <param name="state">An object representing data to be used by the action.</param>
473 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
474 /// <exception cref="T:System.ArgumentNullException">
475 /// The <paramref name="action"/> argument is null.
477 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
478 /// has already been disposed.
480 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
481 public Task(Action<object> action, object state, CancellationToken cancellationToken)
482 : this(action, state, null, cancellationToken, TaskCreationOptions.None, InternalTaskOptions.None, null)
484 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
485 PossiblyCaptureContext(ref stackMark);
489 /// Initializes a new <see cref="Task"/> with the specified action, state, snd options.
491 /// <param name="action">The delegate that represents the code to execute in the task.</param>
492 /// <param name="state">An object representing data to be used by the action.</param>
493 /// <param name="creationOptions">
494 /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
495 /// customize the Task's behavior.
497 /// <exception cref="T:System.ArgumentNullException">
498 /// The <paramref name="action"/> argument is null.
500 /// <exception cref="T:System.ArgumentOutOfRangeException">
501 /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
502 /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
504 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
505 public Task(Action<object> action, object state, TaskCreationOptions creationOptions)
506 : this(action, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, InternalTaskOptions.None, null)
508 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
509 PossiblyCaptureContext(ref stackMark);
513 /// Initializes a new <see cref="Task"/> with the specified action, state, snd options.
515 /// <param name="action">The delegate that represents the code to execute in the task.</param>
516 /// <param name="state">An object representing data to be used by the action.</param>
517 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param>
518 /// <param name="creationOptions">
519 /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to
520 /// customize the Task's behavior.
522 /// <exception cref="T:System.ArgumentNullException">
523 /// The <paramref name="action"/> argument is null.
525 /// <exception cref="T:System.ArgumentOutOfRangeException">
526 /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see
527 /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>.
529 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
530 /// has already been disposed.
532 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
533 public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
534 : this(action, state, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, creationOptions, InternalTaskOptions.None, null)
536 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
537 PossiblyCaptureContext(ref stackMark);
540 internal Task(Action<object> action, object state, Task parent, CancellationToken cancellationToken,
541 TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler, ref StackCrawlMark stackMark)
542 : this(action, state, parent, cancellationToken, creationOptions, internalOptions, scheduler)
544 PossiblyCaptureContext(ref stackMark);
548 /// An internal constructor used by the factory methods on task and its descendent(s).
549 /// This variant does not capture the ExecutionContext; it is up to the caller to do that.
551 /// <param name="action">An action to execute.</param>
552 /// <param name="state">Optional state to pass to the action.</param>
553 /// <param name="parent">Parent of Task.</param>
554 /// <param name="cancellationToken">A CancellationToken for the task.</param>
555 /// <param name="scheduler">A task scheduler under which the task will run.</param>
556 /// <param name="creationOptions">Options to control its execution.</param>
557 /// <param name="internalOptions">Internal options to control its execution</param>
558 internal Task(Delegate action, object state, Task parent, CancellationToken cancellationToken,
559 TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
563 throw new ArgumentNullException("action");
565 Contract.EndContractBlock();
567 // This is readonly, and so must be set in the constructor
568 // Keep a link to your parent if: (A) You are attached, or (B) you are self-replicating.
569 if (((creationOptions & TaskCreationOptions.AttachedToParent) != 0) ||
570 ((internalOptions & InternalTaskOptions.SelfReplicating) != 0)
576 TaskConstructorCore(action, state, cancellationToken, creationOptions, internalOptions, scheduler);
580 /// Common logic used by the following internal ctors:
582 /// Task(object action, object state, Task parent, TaskCreationOptions options, TaskScheduler taskScheduler)
584 /// <param name="action">Action for task to execute.</param>
585 /// <param name="state">Object to which to pass to action (may be null)</param>
586 /// <param name="scheduler">Task scheduler on which to run thread (only used by continuation tasks).</param>
587 /// <param name="cancellationToken">A CancellationToken for the Task.</param>
588 /// <param name="creationOptions">Options to customize behavior of Task.</param>
589 /// <param name="internalOptions">Internal options to customize behavior of Task.</param>
590 internal void TaskConstructorCore(object action, object state, CancellationToken cancellationToken,
591 TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
594 m_stateObject = state;
595 m_taskScheduler = scheduler;
597 // Check for validity of options
598 if ((creationOptions &
599 ~(TaskCreationOptions.AttachedToParent |
600 TaskCreationOptions.LongRunning |
601 TaskCreationOptions.DenyChildAttach |
602 TaskCreationOptions.HideScheduler |
603 TaskCreationOptions.PreferFairness |
604 TaskCreationOptions.RunContinuationsAsynchronously)) != 0)
606 throw new ArgumentOutOfRangeException("creationOptions");
610 // Check the validity of internalOptions
611 int illegalInternalOptions =
612 (int) (internalOptions &
613 ~(InternalTaskOptions.SelfReplicating |
614 InternalTaskOptions.ChildReplica |
615 InternalTaskOptions.PromiseTask |
616 InternalTaskOptions.ContinuationTask |
617 InternalTaskOptions.LazyCancellation |
618 InternalTaskOptions.QueuedByRuntime));
619 Contract.Assert(illegalInternalOptions == 0, "TaskConstructorCore: Illegal internal options");
622 // Throw exception if the user specifies both LongRunning and SelfReplicating
623 if (((creationOptions & TaskCreationOptions.LongRunning) != 0) &&
624 ((internalOptions & InternalTaskOptions.SelfReplicating) != 0))
626 throw new InvalidOperationException(Environment.GetResourceString("Task_ctor_LRandSR"));
629 // Assign options to m_stateAndOptionsFlag.
630 Contract.Assert(m_stateFlags == 0, "TaskConstructorCore: non-zero m_stateFlags");
631 Contract.Assert((((int)creationOptions) | OptionsMask) == OptionsMask, "TaskConstructorCore: options take too many bits");
632 var tmpFlags = (int)creationOptions | (int)internalOptions;
633 if ((m_action == null) || ((internalOptions & InternalTaskOptions.ContinuationTask) != 0))
635 // For continuation tasks or TaskCompletionSource.Tasks, begin life in the
636 // WaitingForActivation state rather than the Created state.
637 tmpFlags |= TASK_STATE_WAITINGFORACTIVATION;
639 m_stateFlags = tmpFlags; // one write to the volatile m_stateFlags instead of two when setting the above options
641 // Now is the time to add the new task to the children list
642 // of the creating task if the options call for it.
643 // We can safely call the creator task's AddNewChild() method to register it,
644 // because at this point we are already on its thread of execution.
647 && ((creationOptions & TaskCreationOptions.AttachedToParent) != 0)
648 && ((m_parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
651 m_parent.AddNewChild();
654 // if we have a non-null cancellationToken, allocate the contingent properties to save it
655 // we need to do this as the very last thing in the construction path, because the CT registration could modify m_stateFlags
656 if (cancellationToken.CanBeCanceled)
658 Contract.Assert((internalOptions &
659 (InternalTaskOptions.ChildReplica | InternalTaskOptions.SelfReplicating | InternalTaskOptions.ContinuationTask)) == 0,
660 "TaskConstructorCore: Did not expect to see cancelable token for replica/replicating or continuation task.");
662 AssignCancellationToken(cancellationToken, null, null);
667 /// Handles everything needed for associating a CancellationToken with a task which is being constructed.
668 /// This method is meant to be be called either from the TaskConstructorCore or from ContinueWithCore.
670 private void AssignCancellationToken(CancellationToken cancellationToken, Task antecedent, TaskContinuation continuation)
672 // There is no need to worry about concurrency issues here because we are in the constructor path of the task --
673 // there should not be any ----s to set m_contingentProperties at this point.
674 ContingentProperties props = EnsureContingentPropertiesInitialized(needsProtection: false);
675 props.m_cancellationToken = cancellationToken;
679 if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
681 cancellationToken.ThrowIfSourceDisposed();
684 // If an unstarted task has a valid CancellationToken that gets signalled while the task is still not queued
685 // we need to proactively cancel it, because it may never execute to transition itself.
686 // The only way to accomplish this is to register a callback on the CT.
687 // We exclude Promise tasks from this, because TaskCompletionSource needs to fully control the inner tasks's lifetime (i.e. not allow external cancellations)
688 if ((((InternalTaskOptions)Options &
689 (InternalTaskOptions.QueuedByRuntime | InternalTaskOptions.PromiseTask | InternalTaskOptions.LazyCancellation)) == 0))
691 if (cancellationToken.IsCancellationRequested)
693 // Fast path for an already-canceled cancellationToken
694 this.InternalCancel(false);
698 // Regular path for an uncanceled cancellationToken
699 CancellationTokenRegistration ctr;
700 if (antecedent == null)
702 // if no antecedent was specified, use this task's reference as the cancellation state object
703 ctr = cancellationToken.InternalRegisterWithoutEC(s_taskCancelCallback, this);
707 // If an antecedent was specified, pack this task, its antecedent and the TaskContinuation together as a tuple
708 // and use it as the cancellation state object. This will be unpacked in the cancellation callback so that
709 // antecedent.RemoveCancellation(continuation) can be invoked.
710 ctr = cancellationToken.InternalRegisterWithoutEC(s_taskCancelCallback,
711 new Tuple<Task, Task, TaskContinuation>(this, antecedent, continuation));
714 props.m_cancellationRegistration = new Shared<CancellationTokenRegistration>(ctr);
720 // If we have an exception related to our CancellationToken, then we need to subtract ourselves
721 // from our parent before throwing it.
722 if ((m_parent != null) &&
723 ((Options & TaskCreationOptions.AttachedToParent) != 0)
724 && ((m_parent.Options & TaskCreationOptions.DenyChildAttach) == 0))
726 m_parent.DisregardChild();
733 // Static delegate to be used as a cancellation callback on unstarted tasks that have a valid cancellation token.
734 // This is necessary to transition them into canceled state if their cancellation token is signalled while they are still not queued
735 private readonly static Action<Object> s_taskCancelCallback = new Action<Object>(TaskCancelCallback);
736 private static void TaskCancelCallback(Object o)
738 var targetTask = o as Task;
739 if (targetTask == null)
741 var tuple = o as Tuple<Task, Task, TaskContinuation>;
744 targetTask = tuple.Item1;
746 Task antecedentTask = tuple.Item2;
747 TaskContinuation continuation = tuple.Item3;
748 antecedentTask.RemoveContinuation(continuation);
751 Contract.Assert(targetTask != null,
752 "targetTask should have been non-null, with the supplied argument being a task or a tuple containing one");
753 targetTask.InternalCancel(false);
757 private string DebuggerDisplayMethodDescription
761 Delegate d = (Delegate)m_action;
762 return d != null ? d.Method.ToString() : "{null}";
768 /// Captures the ExecutionContext so long as flow isn't suppressed.
770 /// <param name="stackMark">A stack crawl mark pointing to the frame of the caller.</param>
772 [SecuritySafeCritical]
773 internal void PossiblyCaptureContext(ref StackCrawlMark stackMark)
775 Contract.Assert(m_contingentProperties == null || m_contingentProperties.m_capturedContext == null,
776 "Captured an ExecutionContext when one was already captured.");
778 // In the legacy .NET 3.5 build, we don't have the optimized overload of Capture()
779 // available, so we call the parameterless overload.
781 CapturedContext = ExecutionContext.Capture();
783 CapturedContext = ExecutionContext.Capture(
785 ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
789 // Internal property to process TaskCreationOptions access and mutation.
790 internal TaskCreationOptions Options
794 int stateFlags = m_stateFlags; // "cast away" volatility to enable inlining of OptionsMethod
795 return OptionsMethod(stateFlags);
799 // Similar to Options property, but allows for the use of a cached flags value rather than
800 // a read of the volatile m_stateFlags field.
801 internal static TaskCreationOptions OptionsMethod(int flags)
803 Contract.Assert((OptionsMask & 1) == 1, "OptionsMask needs a shift in Options.get");
804 return (TaskCreationOptions)(flags & OptionsMask);
807 // Atomically OR-in newBits to m_stateFlags, while making sure that
808 // no illegalBits are set. Returns true on success, false on failure.
809 internal bool AtomicStateUpdate(int newBits, int illegalBits)
811 // This could be implemented in terms of:
812 // internal bool AtomicStateUpdate(int newBits, int illegalBits, ref int oldFlags);
813 // but for high-throughput perf, that delegation's cost is noticeable.
815 SpinWait sw = new SpinWait();
818 int oldFlags = m_stateFlags;
819 if ((oldFlags & illegalBits) != 0) return false;
820 if (Interlocked.CompareExchange(ref m_stateFlags, oldFlags | newBits, oldFlags) == oldFlags)
828 internal bool AtomicStateUpdate(int newBits, int illegalBits, ref int oldFlags)
830 SpinWait sw = new SpinWait();
833 oldFlags = m_stateFlags;
834 if ((oldFlags & illegalBits) != 0) return false;
835 if (Interlocked.CompareExchange(ref m_stateFlags, oldFlags | newBits, oldFlags) == oldFlags)
844 /// Sets or clears the TASK_STATE_WAIT_COMPLETION_NOTIFICATION state bit.
845 /// The debugger sets this bit to aid it in "stepping out" of an async method body.
846 /// If enabled is true, this must only be called on a task that has not yet been completed.
847 /// If enabled is false, this may be called on completed tasks.
848 /// Either way, it should only be used for promise-style tasks.
850 /// <param name="enabled">true to set the bit; false to unset the bit.</param>
851 internal void SetNotificationForWaitCompletion(bool enabled)
853 Contract.Assert((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0,
854 "Should only be used for promise-style tasks"); // hasn't been vetted on other kinds as there hasn't been a need
858 // Atomically set the END_AWAIT_NOTIFICATION bit
859 bool success = AtomicStateUpdate(TASK_STATE_WAIT_COMPLETION_NOTIFICATION,
860 TASK_STATE_COMPLETED_MASK | TASK_STATE_COMPLETION_RESERVED);
861 Contract.Assert(success, "Tried to set enabled on completed Task");
865 // Atomically clear the END_AWAIT_NOTIFICATION bit
866 SpinWait sw = new SpinWait();
869 int oldFlags = m_stateFlags;
870 int newFlags = oldFlags & (~TASK_STATE_WAIT_COMPLETION_NOTIFICATION);
871 if (Interlocked.CompareExchange(ref m_stateFlags, newFlags, oldFlags) == oldFlags) break;
878 /// Calls the debugger notification method if the right bit is set and if
879 /// the task itself allows for the notification to proceed.
881 /// <returns>true if the debugger was notified; otherwise, false.</returns>
882 internal bool NotifyDebuggerOfWaitCompletionIfNecessary()
884 // Notify the debugger if of any of the tasks we've waited on requires notification
885 if (IsWaitNotificationEnabled && ShouldNotifyDebuggerOfWaitCompletion)
887 NotifyDebuggerOfWaitCompletion();
893 /// <summary>Returns true if any of the supplied tasks require wait notification.</summary>
894 /// <param name="tasks">The tasks to check.</param>
895 /// <returns>true if any of the tasks require notification; otherwise, false.</returns>
896 internal static bool AnyTaskRequiresNotifyDebuggerOfWaitCompletion(Task[] tasks)
898 Contract.Assert(tasks != null, "Expected non-null array of tasks");
899 foreach (var task in tasks)
902 task.IsWaitNotificationEnabled &&
903 task.ShouldNotifyDebuggerOfWaitCompletion) // potential recursion
911 /// <summary>Gets whether either the end await bit is set or (not xor) the task has not completed successfully.</summary>
912 /// <returns>(DebuggerBitSet || !RanToCompletion)</returns>
913 internal bool IsWaitNotificationEnabledOrNotRanToCompletion
915 [MethodImpl(MethodImplOptions.AggressiveInlining)]
918 return (m_stateFlags & (Task.TASK_STATE_WAIT_COMPLETION_NOTIFICATION | Task.TASK_STATE_RAN_TO_COMPLETION))
919 != Task.TASK_STATE_RAN_TO_COMPLETION;
924 /// Determines whether we should inform the debugger that we're ending a join with a task.
925 /// This should only be called if the debugger notification bit is set, as it is has some cost,
926 /// namely it is a virtual call (however calling it if the bit is not set is not functionally
927 /// harmful). Derived implementations may choose to only conditionally call down to this base
930 internal virtual bool ShouldNotifyDebuggerOfWaitCompletion // ideally would be familyAndAssembly, but that can't be done in C#
934 // It's theoretically possible but extremely rare that this assert could fire because the
935 // bit was unset between the time that it was checked and this method was called.
936 // It's so remote a chance that it's worth having the assert to protect against misuse.
937 bool isWaitNotificationEnabled = IsWaitNotificationEnabled;
938 Contract.Assert(isWaitNotificationEnabled, "Should only be called if the wait completion bit is set.");
939 return isWaitNotificationEnabled;
943 /// <summary>Gets whether the task's debugger notification for wait completion bit is set.</summary>
944 /// <returns>true if the bit is set; false if it's not set.</returns>
945 internal bool IsWaitNotificationEnabled // internal only to enable unit tests; would otherwise be private
947 get { return (m_stateFlags & TASK_STATE_WAIT_COMPLETION_NOTIFICATION) != 0; }
950 /// <summary>Placeholder method used as a breakpoint target by the debugger. Must not be inlined or optimized.</summary>
951 /// <remarks>All joins with a task should end up calling this if their debugger notification bit is set.</remarks>
952 [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
953 private void NotifyDebuggerOfWaitCompletion()
955 // It's theoretically possible but extremely rare that this assert could fire because the
956 // bit was unset between the time that it was checked and this method was called.
957 // It's so remote a chance that it's worth having the assert to protect against misuse.
958 Contract.Assert(IsWaitNotificationEnabled, "Should only be called if the wait completion bit is set.");
960 // Now that we're notifying the debugger, clear the bit. The debugger should do this anyway,
961 // but this adds a bit of protection in case it fails to, and given that the debugger is involved,
962 // the overhead here for the interlocked is negligable. We do still rely on the debugger
963 // to clear bits, as this doesn't recursively clear bits in the case of, for example, WhenAny.
964 SetNotificationForWaitCompletion(enabled: false);
968 // Atomically mark a Task as started while making sure that it is not canceled.
969 internal bool MarkStarted()
971 return AtomicStateUpdate(TASK_STATE_STARTED, TASK_STATE_CANCELED | TASK_STATE_STARTED);
974 [MethodImpl(MethodImplOptions.AggressiveInlining)]
975 internal bool FireTaskScheduledIfNeeded(TaskScheduler ts)
977 var etwLog = TplEtwProvider.Log;
978 if (etwLog.IsEnabled() && (m_stateFlags & Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED) == 0)
980 m_stateFlags |= Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED;
982 Task currentTask = Task.InternalCurrent;
983 Task parentTask = this.m_parent;
984 etwLog.TaskScheduled(ts.Id, currentTask == null ? 0 : currentTask.Id,
985 this.Id, parentTask == null ? 0 : parentTask.Id, (int)this.Options,
986 System.Threading.Thread.GetDomainID());
994 /// Internal function that will be called by a new child task to add itself to
995 /// the children list of the parent (this).
997 /// Since a child task can only be created from the thread executing the action delegate
998 /// of this task, reentrancy is neither required nor supported. This should not be called from
999 /// anywhere other than the task construction/initialization codepaths.
1001 internal void AddNewChild()
1003 Contract.Assert(Task.InternalCurrent == this || this.IsSelfReplicatingRoot, "Task.AddNewChild(): Called from an external context");
1005 var props = EnsureContingentPropertiesInitialized(needsProtection: true);
1007 if (props.m_completionCountdown == 1 && !IsSelfReplicatingRoot)
1009 // A count of 1 indicates so far there was only the parent, and this is the first child task
1010 // Single kid => no fuss about who else is accessing the count. Let's save ourselves 100 cycles
1011 // We exclude self replicating root tasks from this optimization, because further child creation can take place on
1012 // other cores and with bad enough timing this write may not be visible to them.
1013 props.m_completionCountdown++;
1017 // otherwise do it safely
1018 Interlocked.Increment(ref props.m_completionCountdown);
1022 // This is called in the case where a new child is added, but then encounters a CancellationToken-related exception.
1023 // We need to subtract that child from m_completionCountdown, or the parent will never complete.
1024 internal void DisregardChild()
1026 Contract.Assert(Task.InternalCurrent == this, "Task.DisregardChild(): Called from an external context");
1028 var props = EnsureContingentPropertiesInitialized(needsProtection: true);
1029 Contract.Assert(props.m_completionCountdown >= 2, "Task.DisregardChild(): Expected parent count to be >= 2");
1030 Interlocked.Decrement(ref props.m_completionCountdown);
1034 /// Starts the <see cref="Task"/>, scheduling it for execution to the current <see
1035 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1038 /// A task may only be started and run only once. Any attempts to schedule a task a second time
1039 /// will result in an exception.
1041 /// <exception cref="InvalidOperationException">
1042 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1043 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1048 Start(TaskScheduler.Current);
1052 /// Starts the <see cref="Task"/>, scheduling it for execution to the specified <see
1053 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1056 /// A task may only be started and run only once. Any attempts to schedule a task a second time will
1057 /// result in an exception.
1059 /// <param name="scheduler">
1060 /// The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> with which to associate
1061 /// and execute this task.
1063 /// <exception cref="ArgumentNullException">
1064 /// The <paramref name="scheduler"/> argument is null.
1066 /// <exception cref="InvalidOperationException">
1067 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1068 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1071 public void Start(TaskScheduler scheduler)
1073 // Read the volatile m_stateFlags field once and cache it for subsequent operations
1074 int flags = m_stateFlags;
1076 // Need to check this before (m_action == null) because completed tasks will
1077 // set m_action to null. We would want to know if this is the reason that m_action == null.
1078 if (IsCompletedMethod(flags))
1080 throw new InvalidOperationException(Environment.GetResourceString("Task_Start_TaskCompleted"));
1083 if (scheduler == null)
1085 throw new ArgumentNullException("scheduler");
1088 var options = OptionsMethod(flags);
1089 if ((options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
1091 throw new InvalidOperationException(Environment.GetResourceString("Task_Start_Promise"));
1093 if ((options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) != 0)
1095 throw new InvalidOperationException(Environment.GetResourceString("Task_Start_ContinuationTask"));
1098 // Make sure that Task only gets started once. Or else throw an exception.
1099 if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
1101 throw new InvalidOperationException(Environment.GetResourceString("Task_Start_AlreadyStarted"));
1104 ScheduleAndStart(true);
1108 /// Runs the <see cref="Task"/> synchronously on the current <see
1109 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1113 /// A task may only be started and run only once. Any attempts to schedule a task a second time will
1114 /// result in an exception.
1117 /// Tasks executed with <see cref="RunSynchronously()"/> will be associated with the current <see
1118 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1121 /// If the target scheduler does not support running this Task on the current thread, the Task will
1122 /// be scheduled for execution on the scheduler, and the current thread will block until the
1123 /// Task has completed execution.
1126 /// <exception cref="InvalidOperationException">
1127 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1128 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1131 public void RunSynchronously()
1133 InternalRunSynchronously(TaskScheduler.Current, waitForCompletion: true);
1137 /// Runs the <see cref="Task"/> synchronously on the <see
1138 /// cref="System.Threading.Tasks.TaskScheduler">scheduler</see> provided.
1142 /// A task may only be started and run only once. Any attempts to schedule a task a second time will
1143 /// result in an exception.
1146 /// If the target scheduler does not support running this Task on the current thread, the Task will
1147 /// be scheduled for execution on the scheduler, and the current thread will block until the
1148 /// Task has completed execution.
1151 /// <exception cref="InvalidOperationException">
1152 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1153 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1156 /// <exception cref="ArgumentNullException">The <paramref name="scheduler"/> parameter
1157 /// is null.</exception>
1158 /// <param name="scheduler">The scheduler on which to attempt to run this task inline.</param>
1159 public void RunSynchronously(TaskScheduler scheduler)
1161 if (scheduler == null)
1163 throw new ArgumentNullException("scheduler");
1165 Contract.EndContractBlock();
1167 InternalRunSynchronously(scheduler, waitForCompletion: true);
1171 // Internal version of RunSynchronously that allows not waiting for completion.
1173 [SecuritySafeCritical] // Needed for QueueTask
1174 internal void InternalRunSynchronously(TaskScheduler scheduler, bool waitForCompletion)
1176 Contract.Requires(scheduler != null, "Task.InternalRunSynchronously(): null TaskScheduler");
1178 // Read the volatile m_stateFlags field once and cache it for subsequent operations
1179 int flags = m_stateFlags;
1181 // Can't call this method on a continuation task
1182 var options = OptionsMethod(flags);
1183 if ((options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) != 0)
1185 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_Continuation"));
1188 // Can't call this method on a promise-style task
1189 if ((options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
1191 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_Promise"));
1194 // Can't call this method on a task that has already completed
1195 if (IsCompletedMethod(flags))
1197 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_TaskCompleted"));
1200 // Make sure that Task only gets started once. Or else throw an exception.
1201 if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
1203 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_AlreadyStarted"));
1206 // execute only if we win the ---- against concurrent cancel attempts.
1207 // otherwise throw an exception, because we've been canceled.
1210 bool taskQueued = false;
1213 // We wrap TryRunInline() in a try/catch block and move an excepted task to Faulted here,
1214 // but not in Wait()/WaitAll()/FastWaitAll(). Here, we know for sure that the
1215 // task will not be subsequently scheduled (assuming that the scheduler adheres
1216 // to the guideline that an exception implies that no state change took place),
1217 // so it is safe to catch the exception and move the task to a final state. The
1218 // same cannot be said for Wait()/WaitAll()/FastWaitAll().
1219 if (!scheduler.TryRunInline(this, false))
1221 scheduler.InternalQueueTask(this);
1222 taskQueued = true; // only mark this after successfully queuing the task.
1225 // A successful TryRunInline doesn't guarantee completion, as there may be unfinished children.
1226 // Also if we queued the task above, the task may not be done yet.
1227 if (waitForCompletion && !IsCompleted)
1229 SpinThenBlockingWait(Timeout.Infinite, default(CancellationToken));
1234 // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
1235 // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute
1236 if (!taskQueued && !(e is ThreadAbortException))
1238 // We had a problem with TryRunInline() or QueueTask().
1239 // Record the exception, marking ourselves as Completed/Faulted.
1240 TaskSchedulerException tse = new TaskSchedulerException(e);
1244 // Mark ourselves as "handled" to avoid crashing the finalizer thread if the caller neglects to
1245 // call Wait() on this task.
1246 // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
1248 (m_contingentProperties != null) &&
1249 (m_contingentProperties.m_exceptionsHolder != null) &&
1250 (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
1251 "Task.InternalRunSynchronously(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
1252 "and to have faults recorded.");
1253 m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
1258 // We had a problem with waiting or this is a thread abort. Just re-throw.
1264 Contract.Assert((m_stateFlags & TASK_STATE_CANCELED) != 0, "Task.RunSynchronously: expected TASK_STATE_CANCELED to be set");
1265 // Can't call this method on canceled task.
1266 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_TaskCompleted"));
1272 //// Helper methods for Factory StartNew methods.
1276 // Implicitly converts action to object and handles the meat of the StartNew() logic.
1277 internal static Task InternalStartNew(
1278 Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler,
1279 TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)
1281 // Validate arguments.
1282 if (scheduler == null)
1284 throw new ArgumentNullException("scheduler");
1286 Contract.EndContractBlock();
1288 // Create and schedule the task. This throws an InvalidOperationException if already shut down.
1289 // Here we add the InternalTaskOptions.QueuedByRuntime to the internalOptions, so that TaskConstructorCore can skip the cancellation token registration
1290 Task t = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
1291 t.PossiblyCaptureContext(ref stackMark);
1293 t.ScheduleAndStart(false);
1298 /// Gets a unique ID for a <see cref="Task">Task</see> or task continuation instance.
1300 internal static int NewId()
1303 // We need to repeat if Interlocked.Increment wraps around and returns 0.
1304 // Otherwise next time this task's Id is queried it will get a new value
1307 newId = Interlocked.Increment(ref s_taskIdCounter);
1310 TplEtwProvider.Log.NewID(newId);
1319 /// Gets a unique ID for this <see cref="Task">Task</see> instance.
1322 /// Task IDs are assigned on-demand and do not necessarily represent the order in the which Task
1323 /// instances were created.
1331 int newId = NewId();
1332 Interlocked.CompareExchange(ref m_taskId, newId, 0);
1340 /// Returns the unique ID of the currently executing <see cref="Task">Task</see>.
1342 public static int? CurrentId
1346 Task currentTask = InternalCurrent;
1347 if (currentTask != null)
1348 return currentTask.Id;
1355 /// Gets the <see cref="Task">Task</see> instance currently executing, or
1356 /// null if none exists.
1358 internal static Task InternalCurrent
1360 get { return t_currentTask; }
1364 /// Gets the Task instance currently executing if the specified creation options
1365 /// contain AttachedToParent.
1367 /// <param name="options">The options to check.</param>
1368 /// <returns>The current task if there is one and if AttachToParent is in the options; otherwise, null.</returns>
1369 internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions)
1371 return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
1375 /// Gets the StackGuard object assigned to the current thread.
1377 internal static StackGuard CurrentStackGuard
1381 StackGuard sg = t_stackGuard;
1384 t_stackGuard = sg = new StackGuard();
1392 /// Gets the <see cref="T:System.AggregateException">Exception</see> that caused the <see
1393 /// cref="Task">Task</see> to end prematurely. If the <see
1394 /// cref="Task">Task</see> completed successfully or has not yet thrown any
1395 /// exceptions, this will return null.
1398 /// Tasks that throw unhandled exceptions store the resulting exception and propagate it wrapped in a
1399 /// <see cref="System.AggregateException"/> in calls to <see cref="Wait()">Wait</see>
1400 /// or in accesses to the <see cref="Exception"/> property. Any exceptions not observed by the time
1401 /// the Task instance is garbage collected will be propagated on the finalizer thread.
1403 public AggregateException Exception
1407 AggregateException e = null;
1409 // If you're faulted, retrieve the exception(s)
1410 if (IsFaulted) e = GetExceptions(false);
1412 // Only return an exception in faulted state (skip manufactured exceptions)
1413 // A "benevolent" race condition makes it possible to return null when IsFaulted is
1414 // true (i.e., if IsFaulted is set just after the check to IsFaulted above).
1415 Contract.Assert((e == null) || IsFaulted, "Task.Exception_get(): returning non-null value when not Faulted");
1422 /// Gets the <see cref="T:System.Threading.Tasks.TaskStatus">TaskStatus</see> of this Task.
1424 public TaskStatus Status
1430 // get a cached copy of the state flags. This should help us
1431 // to get a consistent view of the flags if they are changing during the
1432 // execution of this method.
1433 int sf = m_stateFlags;
1435 if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted;
1436 else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled;
1437 else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion;
1438 else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete;
1439 else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running;
1440 else if ((sf & TASK_STATE_STARTED) != 0) rval = TaskStatus.WaitingToRun;
1441 else if ((sf & TASK_STATE_WAITINGFORACTIVATION) != 0) rval = TaskStatus.WaitingForActivation;
1442 else rval = TaskStatus.Created;
1449 /// Gets whether this <see cref="Task">Task</see> instance has completed
1450 /// execution due to being canceled.
1453 /// A <see cref="Task">Task</see> will complete in Canceled state either if its <see cref="CancellationToken">CancellationToken</see>
1454 /// was marked for cancellation before the task started executing, or if the task acknowledged the cancellation request on
1455 /// its already signaled CancellationToken by throwing an
1456 /// <see cref="System.OperationCanceledException">OperationCanceledException</see> that bears the same
1457 /// <see cref="System.Threading.CancellationToken">CancellationToken</see>.
1459 public bool IsCanceled
1463 // Return true if canceled bit is set and faulted bit is not set
1464 return (m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_FAULTED)) == TASK_STATE_CANCELED;
1469 /// Returns true if this task has a cancellation token and it was signaled.
1470 /// To be used internally in execute entry codepaths.
1472 internal bool IsCancellationRequested
1476 // check both the internal cancellation request flag and the CancellationToken attached to this task
1477 var props = m_contingentProperties;
1478 return props != null &&
1479 (props.m_internalCancellationRequested == CANCELLATION_REQUESTED ||
1480 props.m_cancellationToken.IsCancellationRequested);
1485 /// Ensures that the contingent properties field has been initialized.
1486 /// ASSUMES THAT m_stateFlags IS ALREADY SET!
1488 /// <param name="needsProtection">true if this needs to be done in a thread-safe manner; otherwise, false.</param>
1489 /// <returns>The initialized contingent properties object.</returns>
1490 internal ContingentProperties EnsureContingentPropertiesInitialized(bool needsProtection)
1492 var props = m_contingentProperties;
1493 return props != null ? props : EnsureContingentPropertiesInitializedCore(needsProtection);
1497 /// Initializes the contingent properties object. This assumes a check has already been done for nullness.
1499 /// <param name="needsProtection">true if this needs to be done in a thread-safe manner; otherwise, false.</param>
1500 /// <returns>The initialized contingent properties object.</returns>
1501 private ContingentProperties EnsureContingentPropertiesInitializedCore(bool needsProtection)
1503 if (needsProtection)
1505 return LazyInitializer.EnsureInitialized<ContingentProperties>(ref m_contingentProperties, s_createContingentProperties);
1509 Contract.Assert(m_contingentProperties == null, "Expected props to be null after checking and with needsProtection == false");
1510 return m_contingentProperties = new ContingentProperties();
1514 // Cached functions for lazily initializing contingent properties
1515 private static readonly Func<ContingentProperties> s_createContingentProperties = () => new ContingentProperties();
1518 /// This internal property provides access to the CancellationToken that was set on the task
1519 /// when it was constructed.
1521 internal CancellationToken CancellationToken
1525 var props = m_contingentProperties;
1526 return (props == null) ? default(CancellationToken) : props.m_cancellationToken;
1531 /// Gets whether this <see cref="Task"/> threw an OperationCanceledException while its CancellationToken was signaled.
1533 internal bool IsCancellationAcknowledged
1535 get { return (m_stateFlags & TASK_STATE_CANCELLATIONACKNOWLEDGED) != 0; }
1540 /// Gets whether this <see cref="Task">Task</see> has completed.
1543 /// <see cref="IsCompleted"/> will return true when the Task is in one of the three
1544 /// final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
1545 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
1546 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
1548 public bool IsCompleted
1552 int stateFlags = m_stateFlags; // enable inlining of IsCompletedMethod by "cast"ing away the volatility
1553 return IsCompletedMethod(stateFlags);
1557 // Similar to IsCompleted property, but allows for the use of a cached flags value
1558 // rather than reading the volatile m_stateFlags field.
1559 private static bool IsCompletedMethod(int flags)
1561 return (flags & TASK_STATE_COMPLETED_MASK) != 0;
1564 // For use in InternalWait -- marginally faster than (Task.Status == TaskStatus.RanToCompletion)
1565 internal bool IsRanToCompletion
1567 get { return (m_stateFlags & TASK_STATE_COMPLETED_MASK) == TASK_STATE_RAN_TO_COMPLETION; }
1571 /// Gets the <see cref="T:System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used
1572 /// to create this task.
1574 public TaskCreationOptions CreationOptions
1576 get { return Options & (TaskCreationOptions)(~InternalTaskOptions.InternalOptionsMask); }
1580 /// Gets a <see cref="T:System.Threading.WaitHandle"/> that can be used to wait for the task to
1584 /// Using the wait functionality provided by <see cref="Wait()"/>
1585 /// should be preferred over using <see cref="IAsyncResult.AsyncWaitHandle"/> for similar
1588 /// <exception cref="T:System.ObjectDisposedException">
1589 /// The <see cref="Task"/> has been disposed.
1591 WaitHandle IAsyncResult.AsyncWaitHandle
1593 // Although a slim event is used internally to avoid kernel resource allocation, this function
1594 // forces allocation of a true WaitHandle when called.
1597 bool isDisposed = (m_stateFlags & TASK_STATE_DISPOSED) != 0;
1600 throw new ObjectDisposedException(null, Environment.GetResourceString("Task_ThrowIfDisposed"));
1602 return CompletedEvent.WaitHandle;
1607 /// Gets the state object supplied when the <see cref="Task">Task</see> was created,
1608 /// or null if none was supplied.
1610 public object AsyncState
1612 get { return m_stateObject; }
1616 /// Gets an indication of whether the asynchronous operation completed synchronously.
1618 /// <value>true if the asynchronous operation completed synchronously; otherwise, false.</value>
1619 bool IAsyncResult.CompletedSynchronously
1628 /// Provides access to the TaskScheduler responsible for executing this Task.
1630 internal TaskScheduler ExecutingTaskScheduler
1632 get { return m_taskScheduler; }
1636 /// Provides access to factory methods for creating <see cref="Task"/> and <see cref="Task{TResult}"/> instances.
1639 /// The factory returned from <see cref="Factory"/> is a default instance
1640 /// of <see cref="System.Threading.Tasks.TaskFactory"/>, as would result from using
1641 /// the default constructor on TaskFactory.
1643 public static TaskFactory Factory { get { return s_factory; } }
1645 /// <summary>A task that's already been completed successfully.</summary>
1646 private static Task s_completedTask;
1648 /// <summary>Gets a task that's already been completed successfully.</summary>
1649 /// <remarks>May not always return the same instance.</remarks>
1650 public static Task CompletedTask
1654 var completedTask = s_completedTask;
1655 if (completedTask == null)
1656 s_completedTask = completedTask = new Task(false, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken)); // benign initialization ----
1657 return completedTask;
1662 /// Provides an event that can be used to wait for completion.
1663 /// Only called by IAsyncResult.AsyncWaitHandle, which means that we really do need to instantiate a completion event.
1665 internal ManualResetEventSlim CompletedEvent
1669 var contingentProps = EnsureContingentPropertiesInitialized(needsProtection: true);
1670 if (contingentProps.m_completionEvent == null)
1672 bool wasCompleted = IsCompleted;
1673 ManualResetEventSlim newMre = new ManualResetEventSlim(wasCompleted);
1674 if (Interlocked.CompareExchange(ref contingentProps.m_completionEvent, newMre, null) != null)
1676 // We lost the ----, so we will just close the event right away.
1679 else if (!wasCompleted && IsCompleted)
1681 // We published the event as unset, but the task has subsequently completed.
1682 // Set the event's state properly so that callers don't deadlock.
1687 return contingentProps.m_completionEvent;
1692 /// Determines whether this is the root task of a self replicating group.
1694 internal bool IsSelfReplicatingRoot
1698 // Return true if self-replicating bit is set and child replica bit is not set
1699 return (Options & (TaskCreationOptions)(InternalTaskOptions.SelfReplicating | InternalTaskOptions.ChildReplica))
1700 == (TaskCreationOptions)InternalTaskOptions.SelfReplicating;
1705 /// Determines whether the task is a replica itself.
1707 internal bool IsChildReplica
1709 get { return (Options & (TaskCreationOptions)InternalTaskOptions.ChildReplica) != 0; }
1712 internal int ActiveChildCount
1716 var props = m_contingentProperties;
1717 return props != null ? props.m_completionCountdown - 1 : 0;
1722 /// The property formerly known as IsFaulted.
1724 internal bool ExceptionRecorded
1728 var props = m_contingentProperties;
1729 return (props != null) && (props.m_exceptionsHolder != null) && (props.m_exceptionsHolder.ContainsFaultList);
1734 /// Gets whether the <see cref="Task"/> completed due to an unhandled exception.
1737 /// If <see cref="IsFaulted"/> is true, the Task's <see cref="Status"/> will be equal to
1738 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">TaskStatus.Faulted</see>, and its
1739 /// <see cref="Exception"/> property will be non-null.
1741 public bool IsFaulted
1745 // Faulted is "king" -- if that bit is present (regardless of other bits), we are faulted.
1746 return ((m_stateFlags & TASK_STATE_FAULTED) != 0);
1751 /// The captured execution context for the current task to run inside
1752 /// If the TASK_STATE_EXECUTIONCONTEXT_IS_NULL flag is set, this means ExecutionContext.Capture returned null, otherwise
1753 /// If the captured context is the default, nothing is saved, otherwise the m_contingentProperties inflates to save the context
1755 internal ExecutionContext CapturedContext
1759 if ((m_stateFlags & TASK_STATE_EXECUTIONCONTEXT_IS_NULL) == TASK_STATE_EXECUTIONCONTEXT_IS_NULL)
1765 var props = m_contingentProperties;
1766 if (props != null && props.m_capturedContext != null) return props.m_capturedContext;
1767 else return ExecutionContext.PreAllocatedDefault;
1772 // 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
1775 m_stateFlags |= TASK_STATE_EXECUTIONCONTEXT_IS_NULL;
1777 else if (!value.IsPreAllocatedDefault) // not the default context, then inflate the contingent properties and set it
1779 EnsureContingentPropertiesInitialized(needsProtection: false).m_capturedContext = value;
1781 //else do nothing, this is the default context
1786 /// Static helper function to copy specific ExecutionContext
1788 /// <param name="capturedContext">The captured context</param>
1789 /// <returns>The copied context, null if the capturedContext is null</returns>
1790 private static ExecutionContext CopyExecutionContext(ExecutionContext capturedContext)
1792 if (capturedContext == null)
1794 if (capturedContext.IsPreAllocatedDefault)
1795 return ExecutionContext.PreAllocatedDefault;
1797 return capturedContext.CreateCopy();
1803 /// Retrieves an identifier for the task.
1805 internal int InternalId
1807 get { return GetHashCode(); }
1816 /// Disposes the <see cref="Task"/>, releasing all of its unmanaged resources.
1819 /// Unlike most of the members of <see cref="Task"/>, this method is not thread-safe.
1820 /// Also, <see cref="Dispose()"/> may only be called on a <see cref="Task"/> that is in one of
1821 /// the final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
1822 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
1823 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
1825 /// <exception cref="T:System.InvalidOperationException">
1826 /// The exception that is thrown if the <see cref="Task"/> is not in
1827 /// one of the final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
1828 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
1829 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
1831 public void Dispose()
1834 GC.SuppressFinalize(this);
1838 /// Disposes the <see cref="Task"/>, releasing all of its unmanaged resources.
1840 /// <param name="disposing">
1841 /// A Boolean value that indicates whether this method is being called due to a call to <see
1842 /// cref="Dispose()"/>.
1845 /// Unlike most of the members of <see cref="Task"/>, this method is not thread-safe.
1847 protected virtual void Dispose(bool disposing)
1851 // Dispose is a nop if this task was created with the DoNotDispose internal option.
1852 // This is done before the completed check, because if we're not touching any
1853 // state on the task, it's ok for it to happen before completion.
1854 if ((Options & (TaskCreationOptions)InternalTaskOptions.DoNotDispose) != 0)
1859 // Task must be completed to dispose
1862 throw new InvalidOperationException(Environment.GetResourceString("Task_Dispose_NotCompleted"));
1865 // Dispose of the underlying completion event if it exists
1866 var cp = m_contingentProperties;
1869 // Make a copy to protect against racing Disposes.
1870 // If we wanted to make this a bit safer, we could use an interlocked here,
1871 // but we state that Dispose is not thread safe.
1872 var ev = cp.m_completionEvent;
1875 // Null out the completion event in contingent props; we'll use our copy from here on out
1876 cp.m_completionEvent = null;
1878 // In the unlikely event that our completion event is inflated but not yet signaled,
1879 // go ahead and signal the event. If you dispose of an unsignaled MRES, then any waiters
1880 // will deadlock; an ensuing Set() will not wake them up. In the event of an AppDomainUnload,
1881 // there is no guarantee that anyone else is going to signal the event, and it does no harm to
1882 // call Set() twice on m_completionEvent.
1883 if (!ev.IsSet) ev.Set();
1885 // Finally, dispose of the event
1891 // We OR the flags to indicate the object has been disposed. The task
1892 // has already completed at this point, and the only conceivable ---- would
1893 // be with the unsetting of the TASK_STATE_WAIT_COMPLETION_NOTIFICATION flag, which
1894 // ---- is extremely unlikely and also benign. (Worst case: we hit a breakpoint
1895 // twice instead of once in the debugger. Weird, but not lethal.)
1896 m_stateFlags |= TASK_STATE_DISPOSED;
1904 /// Schedules the task for execution.
1906 /// <param name="needsProtection">If true, TASK_STATE_STARTED bit is turned on in
1907 /// an atomic fashion, making sure that TASK_STATE_CANCELED does not get set
1908 /// underneath us. If false, TASK_STATE_STARTED bit is OR-ed right in. This
1909 /// allows us to streamline things a bit for StartNew(), where competing cancellations
1910 /// are not a problem.</param>
1911 [SecuritySafeCritical] // Needed for QueueTask
1912 internal void ScheduleAndStart(bool needsProtection)
1914 Contract.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected");
1915 Contract.Assert((m_stateFlags & TASK_STATE_STARTED) == 0, "task has already started");
1917 // Set the TASK_STATE_STARTED bit
1918 if (needsProtection)
1922 // A cancel has snuck in before we could get started. Quietly exit.
1928 m_stateFlags |= TASK_STATE_STARTED;
1931 if (s_asyncDebuggingEnabled)
1933 AddToActiveTasks(this);
1936 if (AsyncCausalityTracer.LoggingOn && (Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
1938 //For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor
1939 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: "+((Delegate)m_action).Method.Name, 0);
1945 // Queue to the indicated scheduler.
1946 m_taskScheduler.InternalQueueTask(this);
1948 catch (ThreadAbortException tae)
1951 FinishThreadAbortedTask(true, false);
1955 // The scheduler had a problem queueing this task. Record the exception, leaving this task in
1957 TaskSchedulerException tse = new TaskSchedulerException(e);
1961 // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew()
1962 // or from the self replicating logic, because in both cases the exception is either propagated outside directly, or added
1963 // to an enclosing parent. However we won't do this for continuation tasks, because in that case we internally eat the exception
1964 // and therefore we need to make sure the user does later observe it explicitly or see it on the finalizer.
1966 if ((Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
1968 // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
1970 (m_contingentProperties != null) &&
1971 (m_contingentProperties.m_exceptionsHolder != null) &&
1972 (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
1973 "Task.ScheduleAndStart(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
1974 "and to have faults recorded.");
1976 m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
1978 // re-throw the exception wrapped as a TaskSchedulerException.
1984 /// Adds an exception to the list of exceptions this task has thrown.
1986 /// <param name="exceptionObject">An object representing either an Exception or a collection of Exceptions.</param>
1987 internal void AddException(object exceptionObject)
1989 Contract.Requires(exceptionObject != null, "Task.AddException: Expected a non-null exception object");
1990 AddException(exceptionObject, representsCancellation: false);
1994 /// Adds an exception to the list of exceptions this task has thrown.
1996 /// <param name="exceptionObject">An object representing either an Exception or a collection of Exceptions.</param>
1997 /// <param name="representsCancellation">Whether the exceptionObject is an OperationCanceledException representing cancellation.</param>
1998 internal void AddException(object exceptionObject, bool representsCancellation)
2000 Contract.Requires(exceptionObject != null, "Task.AddException: Expected a non-null exception object");
2003 var eoAsException = exceptionObject as Exception;
2004 var eoAsEnumerableException = exceptionObject as IEnumerable<Exception>;
2005 var eoAsEdi = exceptionObject as ExceptionDispatchInfo;
2006 var eoAsEnumerableEdi = exceptionObject as IEnumerable<ExceptionDispatchInfo>;
2009 eoAsException != null || eoAsEnumerableException != null || eoAsEdi != null || eoAsEnumerableEdi != null,
2010 "Task.AddException: Expected an Exception, ExceptionDispatchInfo, or an IEnumerable<> of one of those");
2012 var eoAsOce = exceptionObject as OperationCanceledException;
2015 !representsCancellation ||
2017 (eoAsEdi != null && eoAsEdi.SourceException is OperationCanceledException),
2018 "representsCancellation should be true only if an OCE was provided.");
2022 // WARNING: A great deal of care went into ensuring that
2023 // AddException() and GetExceptions() are never called
2024 // simultaneously. See comment at start of GetExceptions().
2027 // Lazily initialize the holder, ensuring only one thread wins.
2028 var props = EnsureContingentPropertiesInitialized(needsProtection: true);
2029 if (props.m_exceptionsHolder == null)
2031 TaskExceptionHolder holder = new TaskExceptionHolder(this);
2032 if (Interlocked.CompareExchange(ref props.m_exceptionsHolder, holder, null) != null)
2034 // If we lost the ----, suppress finalization.
2035 holder.MarkAsHandled(false);
2041 props.m_exceptionsHolder.Add(exceptionObject, representsCancellation);
2046 /// Returns a list of exceptions by aggregating the holder's contents. Or null if
2047 /// no exceptions have been thrown.
2049 /// <param name="includeTaskCanceledExceptions">Whether to include a TCE if cancelled.</param>
2050 /// <returns>An aggregate exception, or null if no exceptions have been caught.</returns>
2051 private AggregateException GetExceptions(bool includeTaskCanceledExceptions)
2054 // WARNING: The Task/Task<TResult>/TaskCompletionSource classes
2055 // have all been carefully crafted to insure that GetExceptions()
2056 // is never called while AddException() is being called. There
2057 // are locks taken on m_contingentProperties in several places:
2059 // -- Task<TResult>.TrySetException(): The lock allows the
2060 // task to be set to Faulted state, and all exceptions to
2061 // be recorded, in one atomic action.
2063 // -- Task.Exception_get(): The lock ensures that Task<TResult>.TrySetException()
2064 // is allowed to complete its operation before Task.Exception_get()
2065 // can access GetExceptions().
2067 // -- Task.ThrowIfExceptional(): The lock insures that Wait() will
2068 // not attempt to call GetExceptions() while Task<TResult>.TrySetException()
2069 // is in the process of calling AddException().
2071 // For "regular" tasks, we effectively keep AddException() and GetException()
2072 // from being called concurrently by the way that the state flows. Until
2073 // a Task is marked Faulted, Task.Exception_get() returns null. And
2074 // a Task is not marked Faulted until it and all of its children have
2075 // completed, which means that all exceptions have been recorded.
2077 // It might be a lot easier to follow all of this if we just required
2078 // that all calls to GetExceptions() and AddExceptions() were made
2079 // under a lock on m_contingentProperties. But that would also
2080 // increase our lock occupancy time and the frequency with which we
2081 // would need to take the lock.
2083 // If you add a call to GetExceptions() anywhere in the code,
2084 // please continue to maintain the invariant that it can't be
2085 // called when AddException() is being called.
2088 // We'll lazily create a TCE if the task has been canceled.
2089 Exception canceledException = null;
2090 if (includeTaskCanceledExceptions && IsCanceled)
2093 // Ideally we'd just use the cached OCE from this.GetCancellationExceptionDispatchInfo()
2094 // here. However, that would result in a potentially breaking change from .NET 4, which
2095 // has the code here that throws a new exception instead of the original, and the EDI
2096 // may not contain a TCE, but an OCE or any OCE-derived type, which would mean we'd be
2097 // propagating an exception of a different type.
2098 canceledException = new TaskCanceledException(this);
2101 if (ExceptionRecorded)
2103 // There are exceptions; get the aggregate and optionally add the canceled
2104 // exception to the aggregate (if applicable).
2105 Contract.Assert(m_contingentProperties != null); // ExceptionRecorded ==> m_contingentProperties != null
2107 // No need to lock around this, as other logic prevents the consumption of exceptions
2108 // before they have been completely processed.
2109 return m_contingentProperties.m_exceptionsHolder.CreateExceptionObject(false, canceledException);
2111 else if (canceledException != null)
2113 // No exceptions, but there was a cancelation. Aggregate and return it.
2114 return new AggregateException(canceledException);
2120 /// <summary>Gets the exception dispatch infos once the task has faulted.</summary>
2121 internal ReadOnlyCollection<ExceptionDispatchInfo> GetExceptionDispatchInfos()
2123 bool exceptionsAvailable = IsFaulted && ExceptionRecorded;
2124 Contract.Assert(exceptionsAvailable, "Must only be used when the task has faulted with exceptions.");
2125 return exceptionsAvailable ?
2126 m_contingentProperties.m_exceptionsHolder.GetExceptionDispatchInfos() :
2127 new ReadOnlyCollection<ExceptionDispatchInfo>(new ExceptionDispatchInfo[0]);
2130 /// <summary>Gets the ExceptionDispatchInfo containing the OperationCanceledException for this task.</summary>
2131 /// <returns>The ExceptionDispatchInfo. May be null if no OCE was stored for the task.</returns>
2132 internal ExceptionDispatchInfo GetCancellationExceptionDispatchInfo()
2134 Contract.Assert(IsCanceled, "Must only be used when the task has canceled.");
2135 var props = m_contingentProperties;
2136 if (props == null) return null;
2137 var holder = props.m_exceptionsHolder;
2138 if (holder == null) return null;
2139 return holder.GetCancellationExceptionDispatchInfo(); // may be null
2143 /// Throws an aggregate exception if the task contains exceptions.
2145 internal void ThrowIfExceptional(bool includeTaskCanceledExceptions)
2147 Contract.Requires(IsCompleted, "ThrowIfExceptional(): Expected IsCompleted == true");
2149 Exception exception = GetExceptions(includeTaskCanceledExceptions);
2150 if (exception != null)
2152 UpdateExceptionObservedStatus();
2158 /// Checks whether this is an attached task, and whether we are being called by the parent task.
2159 /// And sets the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag based on that.
2161 /// This is meant to be used internally when throwing an exception, and when WaitAll is gathering
2162 /// exceptions for tasks it waited on. If this flag gets set, the implicit wait on children
2163 /// will skip exceptions to prevent duplication.
2165 /// This should only be called when this task has completed with an exception
2168 internal void UpdateExceptionObservedStatus()
2170 if ((m_parent != null)
2171 && ((Options & TaskCreationOptions.AttachedToParent) != 0)
2172 && ((m_parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
2173 && Task.InternalCurrent == m_parent)
2175 m_stateFlags |= TASK_STATE_EXCEPTIONOBSERVEDBYPARENT;
2180 /// Checks whether the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag is set,
2181 /// This will only be used by the implicit wait to prevent double throws
2184 internal bool IsExceptionObservedByParent
2188 return (m_stateFlags & TASK_STATE_EXCEPTIONOBSERVEDBYPARENT) != 0;
2193 /// Checks whether the body was ever invoked. Used by task scheduler code to verify custom schedulers actually ran the task.
2195 internal bool IsDelegateInvoked
2199 return (m_stateFlags & TASK_STATE_DELEGATE_INVOKED) != 0;
2204 /// Signals completion of this particular task.
2206 /// The bUserDelegateExecuted parameter indicates whether this Finish() call comes following the
2207 /// full execution of the user delegate.
2209 /// If bUserDelegateExecuted is false, it mean user delegate wasn't invoked at all (either due to
2210 /// a cancellation request, or because this task is a promise style Task). In this case, the steps
2211 /// involving child tasks (i.e. WaitForChildren) will be skipped.
2214 internal void Finish(bool bUserDelegateExecuted)
2216 if (!bUserDelegateExecuted)
2218 // delegate didn't execute => no children. We can safely call the remaining finish stages
2223 var props = m_contingentProperties;
2225 if (props == null || // no contingent properties means no children, so it's safe to complete ourselves
2226 (props.m_completionCountdown == 1 && !IsSelfReplicatingRoot) ||
2227 // Count of 1 => either all children finished, or there were none. Safe to complete ourselves
2228 // without paying the price of an Interlocked.Decrement.
2229 // However we need to exclude self replicating root tasks from this optimization, because
2230 // they can have children joining in, or finishing even after the root task delegate is done.
2231 Interlocked.Decrement(ref props.m_completionCountdown) == 0) // Reaching this sub clause means there may be remaining active children,
2232 // and we could be racing with one of them to call FinishStageTwo().
2233 // So whoever does the final Interlocked.Dec is responsible to finish.
2239 // Apparently some children still remain. It will be up to the last one to process the completion of this task on their own thread.
2240 // We will now yield the thread back to ThreadPool. Mark our state appropriately before getting out.
2242 // We have to use an atomic update for this and make sure not to overwrite a final state,
2243 // because at this very moment the last child's thread may be concurrently completing us.
2244 // Otherwise we risk overwriting the TASK_STATE_RAN_TO_COMPLETION, _CANCELED or _FAULTED bit which may have been set by that child task.
2245 // Note that the concurrent update by the last child happening in FinishStageTwo could still wipe out the TASK_STATE_WAITING_ON_CHILDREN flag,
2246 // but it is not critical to maintain, therefore we dont' need to intruduce a full atomic update into FinishStageTwo
2248 AtomicStateUpdate(TASK_STATE_WAITING_ON_CHILDREN, TASK_STATE_FAULTED | TASK_STATE_CANCELED | TASK_STATE_RAN_TO_COMPLETION);
2251 // 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.
2252 // we use a local variable for exceptional children here because some other thread may be nulling out m_contingentProperties.m_exceptionalChildren
2253 List<Task> exceptionalChildren = props != null ? props.m_exceptionalChildren : null;
2255 if (exceptionalChildren != null)
2257 lock (exceptionalChildren)
2259 exceptionalChildren.RemoveAll(s_IsExceptionObservedByParentPredicate); // RemoveAll has better performance than doing it ourselves
2265 // statically allocated delegate for the removeall expression in Finish()
2266 private readonly static Predicate<Task> s_IsExceptionObservedByParentPredicate = new Predicate<Task>((t) => { return t.IsExceptionObservedByParent; });
2269 /// FinishStageTwo is to be executed as soon as we known there are no more children to complete.
2270 /// 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)
2271 /// ii) or on the thread that executed the last child.
2273 internal void FinishStageTwo()
2275 AddExceptionsFromChildren();
2277 // At this point, the task is done executing and waiting for its children,
2278 // we can transition our task to a completion state.
2279 int completionState;
2280 if (ExceptionRecorded)
2282 completionState = TASK_STATE_FAULTED;
2283 if (AsyncCausalityTracer.LoggingOn)
2284 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Error);
2286 if (Task.s_asyncDebuggingEnabled)
2288 RemoveFromActiveTasks(this.Id);
2291 else if (IsCancellationRequested && IsCancellationAcknowledged)
2293 // We transition into the TASK_STATE_CANCELED final state if the task's CT was signalled for cancellation,
2294 // and the user delegate acknowledged the cancellation request by throwing an OCE,
2295 // and the task hasn't otherwise transitioned into faulted state. (TASK_STATE_FAULTED trumps TASK_STATE_CANCELED)
2297 // If the task threw an OCE without cancellation being requestsed (while the CT not being in signaled state),
2298 // then we regard it as a regular exception
2300 completionState = TASK_STATE_CANCELED;
2301 if (AsyncCausalityTracer.LoggingOn)
2302 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Canceled);
2304 if (Task.s_asyncDebuggingEnabled)
2306 RemoveFromActiveTasks(this.Id);
2311 completionState = TASK_STATE_RAN_TO_COMPLETION;
2312 if (AsyncCausalityTracer.LoggingOn)
2313 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
2315 if (Task.s_asyncDebuggingEnabled)
2317 RemoveFromActiveTasks(this.Id);
2321 // Use Interlocked.Exchange() to effect a memory fence, preventing
2322 // any SetCompleted() (or later) instructions from sneak back before it.
2323 Interlocked.Exchange(ref m_stateFlags, m_stateFlags | completionState);
2325 // Set the completion event if it's been lazy allocated.
2326 // And if we made a cancellation registration, it's now unnecessary.
2327 var cp = m_contingentProperties;
2331 cp.DeregisterCancellationCallback();
2334 // ready to run continuations and notify parent.
2340 /// Final stage of the task completion code path. Notifies the parent (if any) that another of its childre are done, and runs continuations.
2341 /// This function is only separated out from FinishStageTwo because these two operations are also needed to be called from CancellationCleanupLogic()
2343 internal void FinishStageThree()
2345 // Release the action so that holding this task object alive doesn't also
2346 // hold alive the body of the task. We do this before notifying a parent,
2347 // so that if notifying the parent completes the parent and causes
2348 // its synchronous continuations to run, the GC can collect the state
2349 // in the interim. And we do it before finishing continuations, because
2350 // continuations hold onto the task, and therefore are keeping it alive.
2353 // Notify parent if this was an attached task
2354 if (m_parent != null
2355 && ((m_parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
2356 && (((TaskCreationOptions)(m_stateFlags & OptionsMask)) & TaskCreationOptions.AttachedToParent) != 0)
2358 m_parent.ProcessChildCompletion(this);
2361 // Activate continuations (if any).
2362 FinishContinuations();
2366 /// This is called by children of this task when they are completed.
2368 internal void ProcessChildCompletion(Task childTask)
2370 Contract.Requires(childTask != null);
2371 Contract.Requires(childTask.IsCompleted, "ProcessChildCompletion was called for an uncompleted task");
2373 Contract.Assert(childTask.m_parent == this, "ProcessChildCompletion should only be called for a child of this task");
2375 var props = m_contingentProperties;
2377 // if the child threw and we haven't observed it we need to save it for future reference
2378 if (childTask.IsFaulted && !childTask.IsExceptionObservedByParent)
2380 // Lazily initialize the child exception list
2381 if (props.m_exceptionalChildren == null)
2383 Interlocked.CompareExchange(ref props.m_exceptionalChildren, new List<Task>(), null);
2386 // In rare situations involving AppDomainUnload, it's possible (though unlikely) for FinishStageTwo() to be called
2387 // multiple times for the same task. In that case, AddExceptionsFromChildren() could be nulling m_exceptionalChildren
2388 // out at the same time that we're processing it, resulting in a NullReferenceException here. We'll protect
2389 // ourselves by caching m_exceptionChildren in a local variable.
2390 List<Task> tmp = props.m_exceptionalChildren;
2401 if (Interlocked.Decrement(ref props.m_completionCountdown) == 0)
2403 // This call came from the final child to complete, and apparently we have previously given up this task's right to complete itself.
2404 // So we need to invoke the final finish stage.
2411 /// This is to be called just before the task does its final state transition.
2412 /// It traverses the list of exceptional children, and appends their aggregate exceptions into this one's exception list
2414 internal void AddExceptionsFromChildren()
2416 // In rare occurences during AppDomainUnload() processing, it is possible for this method to be called
2417 // simultaneously on the same task from two different contexts. This can result in m_exceptionalChildren
2418 // being nulled out while it is being processed, which could lead to a NullReferenceException. To
2419 // protect ourselves, we'll cache m_exceptionalChildren in a local variable.
2420 var props = m_contingentProperties;
2421 List<Task> tmp = (props != null) ? props.m_exceptionalChildren : null;
2425 // This lock is necessary because even though AddExceptionsFromChildren is last to execute, it may still
2426 // be racing with the code segment at the bottom of Finish() that prunes the exceptional child array.
2429 foreach (Task task in tmp)
2431 // Ensure any exceptions thrown by children are added to the parent.
2432 // In doing this, we are implicitly marking children as being "handled".
2433 Contract.Assert(task.IsCompleted, "Expected all tasks in list to be completed");
2434 if (task.IsFaulted && !task.IsExceptionObservedByParent)
2436 TaskExceptionHolder exceptionHolder = task.m_contingentProperties.m_exceptionsHolder;
2437 Contract.Assert(exceptionHolder != null);
2439 // No locking necessary since child task is finished adding exceptions
2440 // and concurrent CreateExceptionObject() calls do not constitute
2441 // a concurrency hazard.
2442 AddException(exceptionHolder.CreateExceptionObject(false, null));
2447 // Reduce memory pressure by getting rid of the array
2448 props.m_exceptionalChildren = null;
2453 /// Special purpose Finish() entry point to be used when the task delegate throws a ThreadAbortedException
2454 /// This makes a note in the state flags so that we avoid any costly synchronous operations in the finish codepath
2455 /// such as inlined continuations
2457 /// <param name="bTAEAddedToExceptionHolder">
2458 /// Indicates whether the ThreadAbortException was added to this task's exception holder.
2459 /// This should always be true except for the case of non-root self replicating task copies.
2461 /// <param name="delegateRan">Whether the delegate was executed.</param>
2462 internal void FinishThreadAbortedTask(bool bTAEAddedToExceptionHolder, bool delegateRan)
2464 Contract.Assert(!bTAEAddedToExceptionHolder || (m_contingentProperties != null && m_contingentProperties.m_exceptionsHolder != null),
2465 "FinishThreadAbortedTask() called on a task whose exception holder wasn't initialized");
2467 // this will only be false for non-root self replicating task copies, because all of their exceptions go to the root task.
2468 if (bTAEAddedToExceptionHolder)
2469 m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
2471 // If this method has already been called for this task, or if this task has already completed, then
2472 // return before actually calling Finish().
2473 if (!AtomicStateUpdate(TASK_STATE_THREAD_WAS_ABORTED,
2474 TASK_STATE_THREAD_WAS_ABORTED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED))
2479 Finish(delegateRan);
2485 /// Executes the task. This method will only be called once, and handles bookeeping associated with
2486 /// self-replicating tasks, in addition to performing necessary exception marshaling.
2488 private void Execute()
2490 if (IsSelfReplicatingRoot)
2492 ExecuteSelfReplicating(this);
2500 catch (ThreadAbortException tae)
2502 // Don't record the TAE or call FinishThreadAbortedTask for a child replica task --
2503 // it's already been done downstream.
2504 if (!IsChildReplica)
2506 // Record this exception in the task's exception list
2507 HandleException(tae);
2509 // This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to
2510 // skip the regular Finish codepath. In order not to leave the task unfinished, we now call
2511 // FinishThreadAbortedTask here.
2512 FinishThreadAbortedTask(true, true);
2515 catch (Exception exn)
2517 // Record this exception in the task's exception list
2518 HandleException(exn);
2523 // Allows (internal) deriving classes to support limited replication.
2524 // (By default, replication is basically unlimited).
2525 internal virtual bool ShouldReplicate()
2530 // Allows (internal) deriving classes to instantiate the task replica as a Task super class of their choice
2531 // (By default, we create a regular Task instance)
2532 internal virtual Task CreateReplicaTask(Action<object> taskReplicaDelegate, Object stateObject, Task parentTask, TaskScheduler taskScheduler,
2533 TaskCreationOptions creationOptionsForReplica, InternalTaskOptions internalOptionsForReplica)
2535 return new Task(taskReplicaDelegate, stateObject, parentTask, default(CancellationToken),
2536 creationOptionsForReplica, internalOptionsForReplica, parentTask.ExecutingTaskScheduler);
2539 // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
2540 internal virtual Object SavedStateForNextReplica
2542 get { return null; }
2544 set { /*do nothing*/ }
2547 // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
2548 internal virtual Object SavedStateFromPreviousReplica
2550 get { return null; }
2552 set { /*do nothing*/ }
2555 // Allows internal deriving classes to support replicas that exit prematurely and want to hand over the child replica that they
2556 // had queued, so that the replacement replica can work with that child task instead of queuing up yet another one
2557 internal virtual Task HandedOverChildReplica
2559 get { return null; }
2561 set { /* do nothing*/ }
2564 private static void ExecuteSelfReplicating(Task root)
2566 TaskCreationOptions creationOptionsForReplicas = root.CreationOptions | TaskCreationOptions.AttachedToParent;
2567 InternalTaskOptions internalOptionsForReplicas =
2568 InternalTaskOptions.ChildReplica | // child replica flag disables self replication for the replicas themselves.
2569 InternalTaskOptions.SelfReplicating | // we still want to identify this as part of a self replicating group
2570 InternalTaskOptions.QueuedByRuntime; // we queue and cancel these tasks internally, so don't allow CT registration to take place
2573 // Important Note: The child replicas we launch from here will be attached the root replica (by virtue of the root.CreateReplicaTask call)
2574 // because we need the root task to receive all their exceptions, and to block until all of them return
2577 // This variable is captured in a closure and shared among all replicas.
2578 bool replicasAreQuitting = false;
2580 // Set up a delegate that will form the body of the root and all recursively created replicas.
2581 Action<object> taskReplicaDelegate = null;
2582 taskReplicaDelegate = delegate
2584 Task currentTask = Task.InternalCurrent;
2587 // Check if a child task has been handed over by a prematurely quiting replica that we might be a replacement for.
2588 Task childTask = currentTask.HandedOverChildReplica;
2590 if (childTask == null)
2592 // Apparently we are not a replacement task. This means we need to queue up a child task for replication to progress
2594 // Down-counts a counter in the root task.
2595 if (!root.ShouldReplicate()) return;
2597 // If any of the replicas have quit, we will do so ourselves.
2598 if (Volatile.Read(ref replicasAreQuitting))
2603 // Propagate a copy of the context from the root task. It may be null if flow was suppressed.
2604 ExecutionContext creatorContext = root.CapturedContext;
2607 childTask = root.CreateReplicaTask(taskReplicaDelegate, root.m_stateObject, root, root.ExecutingTaskScheduler,
2608 creationOptionsForReplicas, internalOptionsForReplicas);
2610 childTask.CapturedContext = CopyExecutionContext(creatorContext);
2612 childTask.ScheduleAndStart(false);
2617 // Finally invoke the meat of the task.
2618 // Note that we are directly calling root.InnerInvoke() even though we are currently be in the action delegate of a child replica
2619 // This is because the actual work was passed down in that delegate, and the action delegate of the child replica simply contains this
2620 // replication control logic.
2623 // passing in currentTask only so that the parallel debugger can find it
2624 root.InnerInvokeWithArg(currentTask);
2626 catch (Exception exn)
2628 // Record this exception in the root task's exception list
2629 root.HandleException(exn);
2631 if (exn is ThreadAbortException)
2633 // If this is a ThreadAbortException it will escape this catch clause, causing us to skip the regular Finish codepath
2634 // In order not to leave the task unfinished, we now call FinishThreadAbortedTask here
2635 currentTask.FinishThreadAbortedTask(false, true);
2639 Object savedState = currentTask.SavedStateForNextReplica;
2641 // check for premature exit
2642 if (savedState != null)
2644 // the replica decided to exit early
2645 // we need to queue up a replacement, attach the saved state, and yield the thread right away
2647 Task replacementReplica = root.CreateReplicaTask(taskReplicaDelegate, root.m_stateObject, root, root.ExecutingTaskScheduler,
2648 creationOptionsForReplicas, internalOptionsForReplicas);
2650 // Propagate a copy of the context from the root task to the replacement task
2651 ExecutionContext creatorContext = root.CapturedContext;
2652 replacementReplica.CapturedContext = CopyExecutionContext(creatorContext);
2654 replacementReplica.HandedOverChildReplica = childTask;
2655 replacementReplica.SavedStateFromPreviousReplica = savedState;
2657 replacementReplica.ScheduleAndStart(false);
2661 // The replica finished normally, which means it can't find more work to grab.
2662 // Time to mark replicas quitting
2664 replicasAreQuitting = true;
2666 // InternalCancel() could conceivably throw in the underlying scheduler's TryDequeue() method.
2667 // If it does, then make sure that we record it.
2670 childTask.InternalCancel(true);
2674 // Apparently TryDequeue threw an exception. Before propagating that exception, InternalCancel should have
2675 // attempted an atomic state transition and a call to CancellationCleanupLogic() on this task. So we know
2676 // the task was properly cleaned up if it was possible.
2678 // Now all we need to do is to Record the exception in the root task.
2680 root.HandleException(e);
2683 // No specific action needed if the child could not be canceled
2684 // because we attached it to the root task, which should therefore be receiving any exceptions from the child,
2685 // and root.wait will not return before this child finishes anyway.
2691 // Now we execute as the root task
2693 taskReplicaDelegate(null);
2697 /// IThreadPoolWorkItem override, which is the entry function for this task when the TP scheduler decides to run it.
2701 void IThreadPoolWorkItem.ExecuteWorkItem()
2703 ExecuteEntry(false);
2707 /// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem. This may occur
2708 /// before Task would otherwise be able to observe it.
2711 void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
2713 // If the task has marked itself as Completed, then it either a) already observed this exception (so we shouldn't handle it here)
2714 // or b) completed before the exception ocurred (in which case it shouldn't count against this Task).
2717 HandleException(tae);
2718 FinishThreadAbortedTask(true, false);
2723 /// Outermost entry function to execute this task. Handles all aspects of executing a task on the caller thread.
2724 /// Currently this is called by IThreadPoolWorkItem.ExecuteWorkItem(), and TaskManager.TryExecuteInline.
2727 /// <param name="bPreventDoubleExecution"> Performs atomic updates to prevent double execution. Should only be set to true
2728 /// in codepaths servicing user provided TaskSchedulers. The ConcRT or ThreadPool schedulers don't need this. </param>
2729 [SecuritySafeCritical]
2730 internal bool ExecuteEntry(bool bPreventDoubleExecution)
2732 if (bPreventDoubleExecution || ((Options & (TaskCreationOptions)InternalTaskOptions.SelfReplicating) != 0))
2734 int previousState = 0;
2736 // Do atomic state transition from queued to invoked. If we observe a task that's already invoked,
2737 // we will return false so that TaskScheduler.ExecuteTask can throw an exception back to the custom scheduler.
2738 // However we don't want this exception to be throw if the task was already canceled, because it's a
2739 // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler)
2740 if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED,
2741 TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK,
2742 ref previousState) && (previousState & TASK_STATE_CANCELED) == 0)
2744 // This task has already been invoked. Don't invoke it again.
2750 // Remember that we started running the task delegate.
2751 m_stateFlags |= TASK_STATE_DELEGATE_INVOKED;
2754 if (!IsCancellationRequested && !IsCanceled)
2756 ExecuteWithThreadLocal(ref t_currentTask);
2758 else if (!IsCanceled)
2760 int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED);
2761 if ((prevState & TASK_STATE_CANCELED) == 0)
2763 CancellationCleanupLogic();
2770 // A trick so we can refer to the TLS slot with a byref.
2772 private void ExecuteWithThreadLocal(ref Task currentTaskSlot)
2774 // Remember the current task so we can restore it after running, and then
2775 Task previousTask = currentTaskSlot;
2777 // ETW event for Task Started
2778 var etwLog = TplEtwProvider.Log;
2779 Guid savedActivityID = new Guid();
2780 bool etwIsEnabled = etwLog.IsEnabled();
2783 if (etwLog.TasksSetActivityIds)
2784 EventSource.SetCurrentThreadActivityId(TplEtwProvider.CreateGuidForTaskID(this.Id), out savedActivityID);
2785 // previousTask holds the actual "current task" we want to report in the event
2786 if (previousTask != null)
2787 etwLog.TaskStarted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id);
2789 etwLog.TaskStarted(TaskScheduler.Current.Id, 0, this.Id);
2792 if (AsyncCausalityTracer.LoggingOn)
2793 AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.Execution);
2798 // place the current task into TLS.
2799 currentTaskSlot = this;
2801 ExecutionContext ec = CapturedContext;
2804 // No context, just run the task directly.
2809 if (IsSelfReplicatingRoot || IsChildReplica)
2811 CapturedContext = CopyExecutionContext(ec);
2814 // Run the task. We need a simple shim that converts the
2815 // object back into a Task object, so that we can Execute it.
2817 // Lazily initialize the callback delegate; benign ----
2818 var callback = s_ecCallback;
2819 if (callback == null) s_ecCallback = callback = new ContextCallback(ExecutionContextCallback);
2821 ExecutionContext.Run(ec, callback, this);
2823 ExecutionContext.Run(ec, callback, this, true);
2827 if (AsyncCausalityTracer.LoggingOn)
2828 AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.Execution);
2834 currentTaskSlot = previousTask;
2836 // ETW event for Task Completed
2839 // previousTask holds the actual "current task" we want to report in the event
2840 if (previousTask != null)
2841 etwLog.TaskCompleted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id, IsFaulted);
2843 etwLog.TaskCompleted(TaskScheduler.Current.Id, 0, this.Id, IsFaulted);
2845 if (etwLog.TasksSetActivityIds)
2846 EventSource.SetCurrentThreadActivityId(savedActivityID);
2851 // Cached callback delegate that's lazily initialized due to ContextCallback being SecurityCritical
2853 private static ContextCallback s_ecCallback;
2856 private static void ExecutionContextCallback(object obj)
2858 Task task = obj as Task;
2859 Contract.Assert(task != null, "expected a task object");
2865 /// The actual code which invokes the body of the task. This can be overriden in derived types.
2867 internal virtual void InnerInvoke()
2869 // Invoke the delegate
2870 Contract.Assert(m_action != null, "Null action in InnerInvoke()");
2871 var action = m_action as Action;
2877 var actionWithState = m_action as Action<object>;
2878 if (actionWithState != null)
2880 actionWithState(m_stateObject);
2883 Contract.Assert(false, "Invalid m_action in Task");
2887 /// Alternate InnerInvoke prototype to be called from ExecuteSelfReplicating() so that
2888 /// the Parallel Debugger can discover the actual task being invoked.
2889 /// Details: Here, InnerInvoke is actually being called on the rootTask object while we are actually executing the
2890 /// childTask. And the debugger needs to discover the childTask, so we pass that down as an argument.
2891 /// The NoOptimization and NoInlining flags ensure that the childTask pointer is retained, and that this
2892 /// function appears on the callstack.
2894 /// <param name="childTask"></param>
2895 [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
2896 internal void InnerInvokeWithArg(Task childTask)
2902 /// Performs whatever handling is necessary for an unhandled exception. Normally
2903 /// this just entails adding the exception to the holder object.
2905 /// <param name="unhandledException">The exception that went unhandled.</param>
2906 private void HandleException(Exception unhandledException)
2908 Contract.Requires(unhandledException != null);
2910 OperationCanceledException exceptionAsOce = unhandledException as OperationCanceledException;
2911 if (exceptionAsOce != null && IsCancellationRequested &&
2912 m_contingentProperties.m_cancellationToken == exceptionAsOce.CancellationToken)
2914 // All conditions are satisfied for us to go into canceled state in Finish().
2915 // Mark the acknowledgement. The exception is also stored to enable it to be
2916 // the exception propagated from an await.
2918 SetCancellationAcknowledged();
2919 AddException(exceptionAsOce, representsCancellation: true);
2923 // Other exceptions, including any OCE from the task that doesn't match the tasks' own CT,
2924 // or that gets thrown without the CT being set will be treated as an ordinary exception
2925 // and added to the aggregate.
2927 AddException(unhandledException);
2931 #region Await Support
2932 /// <summary>Gets an awaiter used to await this <see cref="System.Threading.Tasks.Task"/>.</summary>
2933 /// <returns>An awaiter instance.</returns>
2934 /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
2935 public TaskAwaiter GetAwaiter()
2937 return new TaskAwaiter(this);
2940 /// <summary>Configures an awaiter used to await this <see cref="System.Threading.Tasks.Task"/>.</summary>
2941 /// <param name="continueOnCapturedContext">
2942 /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
2944 /// <returns>An object used to await this task.</returns>
2945 public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
2947 return new ConfiguredTaskAwaitable(this, continueOnCapturedContext);
2951 /// Sets a continuation onto the <see cref="System.Threading.Tasks.Task"/>.
2952 /// The continuation is scheduled to run in the current synchronization context is one exists,
2953 /// otherwise in the current task scheduler.
2955 /// <param name="continuationAction">The action to invoke when the <see cref="System.Threading.Tasks.Task"/> has completed.</param>
2956 /// <param name="continueOnCapturedContext">
2957 /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
2959 /// <param name="flowExecutionContext">Whether to flow ExecutionContext across the await.</param>
2960 /// <param name="stackMark">A stack crawl mark tied to execution context.</param>
2961 /// <exception cref="System.InvalidOperationException">The awaiter was not properly initialized.</exception>
2963 internal void SetContinuationForAwait(
2964 Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext, ref StackCrawlMark stackMark)
2966 Contract.Requires(continuationAction != null);
2968 // Create the best AwaitTaskContinuation object given the request.
2969 // If this remains null by the end of the function, we can use the
2970 // continuationAction directly without wrapping it.
2971 TaskContinuation tc = null;
2973 // If the user wants the continuation to run on the current "context" if there is one...
2974 if (continueOnCapturedContext)
2976 // First try getting the current synchronization context.
2977 // If the current context is really just the base SynchronizationContext type,
2978 // which is intended to be equivalent to not having a current SynchronizationContext at all,
2979 // then ignore it. This helps with performance by avoiding unnecessary posts and queueing
2980 // of work items, but more so it ensures that if code happens to publish the default context
2981 // as current, it won't prevent usage of a current task scheduler if there is one.
2982 var syncCtx = SynchronizationContext.CurrentNoFlow;
2983 if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
2985 tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext, ref stackMark);
2989 // If there was no SynchronizationContext, then try for the current scheduler.
2990 // We only care about it if it's not the default.
2991 var scheduler = TaskScheduler.InternalCurrent;
2992 if (scheduler != null && scheduler != TaskScheduler.Default)
2994 tc = new TaskSchedulerAwaitTaskContinuation(scheduler, continuationAction, flowExecutionContext, ref stackMark);
2999 if (tc == null && flowExecutionContext)
3001 // We're targeting the default scheduler, so we can use the faster path
3002 // that assumes the default, and thus we don't need to store it. If we're flowing
3003 // ExecutionContext, we need to capture it and wrap it in an AwaitTaskContinuation.
3004 // Otherwise, we're targeting the default scheduler and we don't need to flow ExecutionContext, so
3005 // we don't actually need a continuation object. We can just store/queue the action itself.
3006 tc = new AwaitTaskContinuation(continuationAction, flowExecutionContext: true, stackMark: ref stackMark);
3009 // Now register the continuation, and if we couldn't register it because the task is already completing,
3010 // process the continuation directly (in which case make sure we schedule the continuation
3011 // rather than inlining it, the latter of which could result in a rare but possible stack overflow).
3014 if (!AddTaskContinuation(tc, addBeforeOthers: false))
3015 tc.Run(this, bCanInlineContinuationTask: false);
3019 Contract.Assert(!flowExecutionContext, "We already determined we're not required to flow context.");
3020 if (!AddTaskContinuation(continuationAction, addBeforeOthers: false))
3021 AwaitTaskContinuation.UnsafeScheduleAction(continuationAction, this);
3025 /// <summary>Creates an awaitable that asynchronously yields back to the current context when awaited.</summary>
3027 /// A context that, when awaited, will asynchronously transition back into the current context at the
3028 /// time of the await. If the current SynchronizationContext is non-null, that is treated as the current context.
3029 /// Otherwise, TaskScheduler.Current is treated as the current context.
3031 public static YieldAwaitable Yield()
3033 return new YieldAwaitable();
3038 /// Waits for the <see cref="Task"/> to complete execution.
3040 /// <exception cref="T:System.AggregateException">
3041 /// The <see cref="Task"/> was canceled -or- an exception was thrown during
3042 /// the execution of the <see cref="Task"/>.
3049 Wait(Timeout.Infinite, default(CancellationToken));
3052 Contract.Assert(waitResult, "expected wait to succeed");
3057 /// Waits for the <see cref="Task"/> to complete execution.
3059 /// <param name="timeout">
3060 /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
3061 /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
3064 /// true if the <see cref="Task"/> completed execution within the allotted time; otherwise, false.
3066 /// <exception cref="T:System.AggregateException">
3067 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
3070 /// <exception cref="T:System.ArgumentOutOfRangeException">
3071 /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
3072 /// infinite time-out -or- timeout is greater than
3073 /// <see cref="System.Int32.MaxValue"/>.
3075 public bool Wait(TimeSpan timeout)
3077 long totalMilliseconds = (long)timeout.TotalMilliseconds;
3078 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
3080 throw new ArgumentOutOfRangeException("timeout");
3083 return Wait((int)totalMilliseconds, default(CancellationToken));
3088 /// Waits for the <see cref="Task"/> to complete execution.
3090 /// <param name="cancellationToken">
3091 /// A <see cref="CancellationToken"/> to observe while waiting for the task to complete.
3093 /// <exception cref="T:System.OperationCanceledException">
3094 /// The <paramref name="cancellationToken"/> was canceled.
3096 /// <exception cref="T:System.AggregateException">
3097 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
3100 public void Wait(CancellationToken cancellationToken)
3102 Wait(Timeout.Infinite, cancellationToken);
3107 /// Waits for the <see cref="Task"/> to complete execution.
3109 /// <param name="millisecondsTimeout">
3110 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
3111 /// wait indefinitely.</param>
3112 /// <returns>true if the <see cref="Task"/> completed execution within the allotted time; otherwise,
3115 /// <exception cref="T:System.ArgumentOutOfRangeException">
3116 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
3117 /// infinite time-out.
3119 /// <exception cref="T:System.AggregateException">
3120 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
3123 public bool Wait(int millisecondsTimeout)
3125 return Wait(millisecondsTimeout, default(CancellationToken));
3130 /// Waits for the <see cref="Task"/> to complete execution.
3132 /// <param name="millisecondsTimeout">
3133 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
3134 /// wait indefinitely.
3136 /// <param name="cancellationToken">
3137 /// A <see cref="CancellationToken"/> to observe while waiting for the task to complete.
3140 /// true if the <see cref="Task"/> completed execution within the allotted time; otherwise, false.
3142 /// <exception cref="T:System.AggregateException">
3143 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
3146 /// <exception cref="T:System.ArgumentOutOfRangeException">
3147 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
3148 /// infinite time-out.
3150 /// <exception cref="T:System.OperationCanceledException">
3151 /// The <paramref name="cancellationToken"/> was canceled.
3153 public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
3155 if (millisecondsTimeout < -1)
3157 throw new ArgumentOutOfRangeException("millisecondsTimeout");
3159 Contract.EndContractBlock();
3161 // Return immediately if we know that we've completed "clean" -- no exceptions, no cancellations
3162 // and if no notification to the debugger is required
3163 if (!IsWaitNotificationEnabledOrNotRanToCompletion) // (!DebuggerBitSet && RanToCompletion)
3166 // Wait, and then return if we're still not done.
3167 if (!InternalWait(millisecondsTimeout, cancellationToken))
3170 if (IsWaitNotificationEnabledOrNotRanToCompletion) // avoid a few unnecessary volatile reads if we completed successfully
3172 // Notify the debugger of the wait completion if it's requested such a notification
3173 NotifyDebuggerOfWaitCompletionIfNecessary();
3175 // If cancellation was requested and the task was canceled, throw an
3176 // OperationCanceledException. This is prioritized ahead of the ThrowIfExceptional
3177 // call to bring more determinism to cases where the same token is used to
3178 // cancel the Wait and to cancel the Task. Otherwise, there's a ---- between
3179 // whether the Wait or the Task observes the cancellation request first,
3180 // and different exceptions result from the different cases.
3181 if (IsCanceled) cancellationToken.ThrowIfCancellationRequested();
3183 // If an exception occurred, or the task was cancelled, throw an exception.
3184 ThrowIfExceptional(true);
3187 Contract.Assert((m_stateFlags & TASK_STATE_FAULTED) == 0, "Task.Wait() completing when in Faulted state.");
3192 // Convenience method that wraps any scheduler exception in a TaskSchedulerException
3194 private bool WrappedTryRunInline()
3196 if (m_taskScheduler == null)
3201 return m_taskScheduler.TryRunInline(this, true);
3205 // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
3206 // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute
3207 if (!(e is ThreadAbortException))
3209 TaskSchedulerException tse = new TaskSchedulerException(e);
3220 /// The core wait function, which is only accesible internally. It's meant to be used in places in TPL code where
3221 /// the current context is known or cached.
3223 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
3224 internal bool InternalWait(int millisecondsTimeout, CancellationToken cancellationToken)
3226 // ETW event for Task Wait Begin
3227 var etwLog = TplEtwProvider.Log;
3228 bool etwIsEnabled = etwLog.IsEnabled();
3231 Task currentTask = Task.InternalCurrent;
3232 etwLog.TaskWaitBegin(
3233 (currentTask != null ? currentTask.m_taskScheduler.Id : TaskScheduler.Default.Id), (currentTask != null ? currentTask.Id : 0),
3234 this.Id, TplEtwProvider.TaskWaitBehavior.Synchronous, 0, System.Threading.Thread.GetDomainID());
3237 bool returnValue = IsCompleted;
3239 // If the event hasn't already been set, we will wait.
3242 // Alert a listening debugger that we can't make forward progress unless it slips threads.
3243 // We call NOCTD for two reasons:
3244 // 1. If the task runs on another thread, then we'll be blocked here indefinitely.
3245 // 2. If the task runs inline but takes some time to complete, it will suffer ThreadAbort with possible state corruption,
3246 // and it is best to prevent this unless the user explicitly asks to view the value with thread-slipping enabled.
3247 Debugger.NotifyOfCrossThreadDependency();
3249 // We will attempt inline execution only if an infinite wait was requested
3250 // Inline execution doesn't make sense for finite timeouts and if a cancellation token was specified
3251 // because we don't know how long the task delegate will take.
3252 if (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled &&
3253 WrappedTryRunInline() && IsCompleted) // TryRunInline doesn't guarantee completion, as there may be unfinished children.
3259 returnValue = SpinThenBlockingWait(millisecondsTimeout, cancellationToken);
3263 Contract.Assert(IsCompleted || millisecondsTimeout != Timeout.Infinite);
3265 // ETW event for Task Wait End
3268 Task currentTask = Task.InternalCurrent;
3269 if (currentTask != null)
3271 etwLog.TaskWaitEnd(currentTask.m_taskScheduler.Id, currentTask.Id, this.Id);
3275 etwLog.TaskWaitEnd(TaskScheduler.Default.Id, 0, this.Id);
3277 // logically the continuation is empty so we immediately fire
3278 etwLog.TaskWaitContinuationComplete(this.Id);
3284 // An MRES that gets set when Invoke is called. This replaces old logic that looked like this:
3285 // ManualResetEventSlim mres = new ManualResetEventSlim(false, 0);
3286 // Action<Task> completionAction = delegate {mres.Set();}
3287 // AddCompletionAction(completionAction);
3289 // SetOnInvokeMres mres = new SetOnInvokeMres();
3290 // AddCompletionAction(mres, addBeforeOthers: true);
3291 // which saves a couple of allocations.
3293 // Used in SpinThenBlockingWait (below), but could be seen as a general purpose mechanism.
3294 private sealed class SetOnInvokeMres : ManualResetEventSlim, ITaskCompletionAction
3296 internal SetOnInvokeMres() : base(false, 0) { }
3297 public void Invoke(Task completingTask) { Set(); }
3301 /// Waits for the task to complete, for a timeout to occur, or for cancellation to be requested.
3302 /// The method first spins and then falls back to blocking on a new event.
3304 /// <param name="millisecondsTimeout">The timeout.</param>
3305 /// <param name="cancellationToken">The token.</param>
3306 /// <returns>true if the task is completed; otherwise, false.</returns>
3307 private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken)
3309 bool infiniteWait = millisecondsTimeout == Timeout.Infinite;
3310 uint startTimeTicks = infiniteWait ? 0 : (uint)Environment.TickCount;
3311 bool returnValue = SpinWait(millisecondsTimeout);
3314 var mres = new SetOnInvokeMres();
3317 AddCompletionAction(mres, addBeforeOthers: true);
3320 returnValue = mres.Wait(Timeout.Infinite, cancellationToken);
3324 uint elapsedTimeTicks = ((uint)Environment.TickCount) - startTimeTicks;
3325 if (elapsedTimeTicks < millisecondsTimeout)
3327 returnValue = mres.Wait((int)(millisecondsTimeout - elapsedTimeTicks), cancellationToken);
3333 if (!IsCompleted) RemoveContinuation(mres);
3334 // Don't Dispose of the MRES, because the continuation off of this task may
3335 // still be running. This is ok, however, as we never access the MRES' WaitHandle,
3336 // and thus no finalizable resources are actually allocated.
3343 /// Spins briefly while checking IsCompleted
3345 /// <param name="millisecondsTimeout">The timeout.</param>
3346 /// <returns>true if the task is completed; otherwise, false.</returns>
3347 /// <exception cref="System.OperationCanceledException">The wait was canceled.</exception>
3348 private bool SpinWait(int millisecondsTimeout)
3350 if (IsCompleted) return true;
3352 if (millisecondsTimeout == 0)
3354 // For 0-timeouts, we just return immediately.
3358 //This code is pretty similar to the custom spinning in MRES except there is no yieling after we exceed the spin count
3359 int spinCount = PlatformHelper.IsSingleProcessor ? 1 : System.Threading.SpinWait.YIELD_THRESHOLD; //spin only once if we are running on a single CPU
3360 for (int i = 0; i < spinCount; i++)
3367 if (i == spinCount / 2)
3373 Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i));
3382 /// Cancels the <see cref="Task"/>.
3384 /// <param name="bCancelNonExecutingOnly">
3385 /// Indicates whether we should only cancel non-invoked tasks.
3386 /// For the default scheduler this option will only be serviced through TryDequeue.
3387 /// For custom schedulers we also attempt an atomic state transition.
3389 /// <returns>true if the task was successfully canceled; otherwise, false.</returns>
3390 [SecuritySafeCritical]
3391 internal bool InternalCancel(bool bCancelNonExecutingOnly)
3393 Contract.Requires((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) == 0, "Task.InternalCancel() did not expect promise-style task");
3395 bool bPopSucceeded = false;
3396 bool mustCleanup = false;
3398 TaskSchedulerException tse = null;
3400 // If started, and running in a task context, we can try to pop the chore.
3401 if ((m_stateFlags & TASK_STATE_STARTED) != 0)
3403 TaskScheduler ts = m_taskScheduler;
3407 bPopSucceeded = (ts != null) && ts.TryDequeue(this);
3411 // TryDequeue threw. We don't know whether the task was properly dequeued or not. So we must let the rest of
3412 // the cancellation logic run its course (record the request, attempt atomic state transition and do cleanup where appropriate)
3413 // Here we will only record a TaskSchedulerException, which will later be thrown at function exit.
3415 if (!(e is ThreadAbortException))
3417 tse = new TaskSchedulerException(e);
3421 bool bRequiresAtomicStartTransition = (ts != null && ts.RequiresAtomicStartTransition) || ((Options & (TaskCreationOptions)InternalTaskOptions.SelfReplicating) != 0);
3423 if (!bPopSucceeded && bCancelNonExecutingOnly && bRequiresAtomicStartTransition)
3425 // The caller requested cancellation of non-invoked tasks only, and TryDequeue was one way of doing it...
3426 // Since that seems to have failed, we should now try an atomic state transition (from non-invoked state to canceled)
3427 // An atomic transition here is only safe if we know we're on a custom task scheduler, which also forces a CAS on ExecuteEntry
3429 // Even though this task can't have any children, we should be ready for handling any continuations that
3430 // may be attached to it (although currently
3431 // So we need to remeber whether we actually did the flip, so we can do clean up (finish continuations etc)
3432 mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_DELEGATE_INVOKED | TASK_STATE_CANCELED);
3435 // PS: This is slightly different from the regular cancellation codepath
3436 // since we record the cancellation request *after* doing the state transition.
3437 // However that shouldn't matter too much because the task was never invoked, thus can't have children
3442 if (!bCancelNonExecutingOnly || bPopSucceeded || mustCleanup)
3444 // Record the cancellation request.
3445 RecordInternalCancellationRequest();
3447 // Determine whether we need to clean up
3448 // This will be the case
3449 // 1) if we were able to pop, and we win the ---- to update task state to TASK_STATE_CANCELED
3450 // 2) if the task seems to be yet unstarted, and we win the ---- to transition to
3451 // TASK_STATE_CANCELED before anyone else can transition into _STARTED or _CANCELED or
3452 // _RAN_TO_COMPLETION or _FAULTED
3453 // Note that we do not check for TASK_STATE_COMPLETION_RESERVED. That only applies to promise-style
3454 // tasks, and a promise-style task should not enter into this codepath.
3457 // hitting this would mean something wrong with the AtomicStateUpdate above
3458 Contract.Assert(!mustCleanup, "Possibly an invalid state transition call was made in InternalCancel()");
3460 // Include TASK_STATE_DELEGATE_INVOKED in "illegal" bits to protect against the situation where
3461 // TS.TryDequeue() returns true but the task is still left on the queue.
3462 mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_CANCELED | TASK_STATE_DELEGATE_INVOKED);
3464 else if (!mustCleanup && (m_stateFlags & TASK_STATE_STARTED) == 0)
3466 mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED,
3467 TASK_STATE_CANCELED | TASK_STATE_STARTED | TASK_STATE_RAN_TO_COMPLETION |
3468 TASK_STATE_FAULTED | TASK_STATE_DELEGATE_INVOKED);
3471 // do the cleanup (i.e. set completion event and finish continuations)
3474 CancellationCleanupLogic();
3481 return (mustCleanup);
3484 // Breaks out logic for recording a cancellation request
3485 internal void RecordInternalCancellationRequest()
3487 // Record the cancellation request.
3488 var props = EnsureContingentPropertiesInitialized(needsProtection: true);
3489 props.m_internalCancellationRequested = CANCELLATION_REQUESTED;
3493 // Breaks out logic for recording a cancellation request
3494 // This overload should only be used for promise tasks where no cancellation token
3495 // was supplied when the task was created.
3496 internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord)
3498 RecordInternalCancellationRequest();
3500 Contract.Assert((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0, "Task.RecordInternalCancellationRequest(CancellationToken) only valid for promise-style task");
3501 Contract.Assert(m_contingentProperties.m_cancellationToken == default(CancellationToken));
3503 // Store the supplied cancellation token as this task's token.
3504 // Waiting on this task will then result in an OperationCanceledException containing this token.
3505 if (tokenToRecord != default(CancellationToken))
3507 m_contingentProperties.m_cancellationToken = tokenToRecord;
3511 // Breaks out logic for recording a cancellation request
3512 // This overload should only be used for promise tasks where no cancellation token
3513 // was supplied when the task was created.
3514 internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord, object cancellationException)
3516 RecordInternalCancellationRequest(tokenToRecord);
3518 // Store the supplied cancellation exception
3519 if (cancellationException != null)
3522 var oce = cancellationException as OperationCanceledException;
3525 var edi = cancellationException as ExceptionDispatchInfo;
3526 Contract.Assert(edi != null, "Expected either an OCE or an EDI");
3527 oce = edi.SourceException as OperationCanceledException;
3528 Contract.Assert(oce != null, "Expected EDI to contain an OCE");
3530 Contract.Assert(oce.CancellationToken == tokenToRecord,
3531 "Expected OCE's token to match the provided token.");
3533 AddException(cancellationException, representsCancellation: true);
3537 // ASSUMES THAT A SUCCESSFUL CANCELLATION HAS JUST OCCURRED ON THIS TASK!!!
3538 // And this method should be called at most once per task.
3539 internal void CancellationCleanupLogic()
3541 Contract.Assert((m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_COMPLETION_RESERVED)) != 0, "Task.CancellationCleanupLogic(): Task not canceled or reserved.");
3542 // I'd like to do this, but there is a small window for a race condition. If someone calls Wait() between InternalCancel() and
3543 // here, that will set m_completionEvent, leading to a meaningless/harmless assertion.
3544 //Contract.Assert((m_completionEvent == null) || !m_completionEvent.IsSet, "Task.CancellationCleanupLogic(): Completion event already set.");
3546 // This may have been set already, but we need to make sure.
3547 Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED);
3549 // Fire completion event if it has been lazily initialized
3550 var cp = m_contingentProperties;
3554 cp.DeregisterCancellationCallback();
3557 if (AsyncCausalityTracer.LoggingOn)
3558 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Canceled);
3560 if (Task.s_asyncDebuggingEnabled)
3562 RemoveFromActiveTasks(this.Id);
3565 // Notify parents, fire continuations, other cleanup.
3571 /// Sets the task's cancellation acknowledged flag.
3573 private void SetCancellationAcknowledged()
3575 Contract.Assert(this == Task.InternalCurrent, "SetCancellationAcknowledged() should only be called while this is still the current task");
3576 Contract.Assert(IsCancellationRequested, "SetCancellationAcknowledged() should not be called if the task's CT wasn't signaled");
3578 m_stateFlags |= TASK_STATE_CANCELLATIONACKNOWLEDGED;
3583 // Continuation passing functionality (aka ContinueWith)
3590 /// Runs all of the continuations, as appropriate.
3592 [SecuritySafeCritical] // for AwaitTaskContinuation.RunOrScheduleAction
3593 internal void FinishContinuations()
3595 // Atomically store the fact that this task is completing. From this point on, the adding of continuations will
3596 // result in the continuations being run/launched directly rather than being added to the continuation list.
3597 object continuationObject = Interlocked.Exchange(ref m_continuationObject, s_taskCompletionSentinel);
3598 TplEtwProvider.Log.RunningContinuation(Id, continuationObject);
3600 // If continuationObject == null, then we don't have any continuations to process
3601 if (continuationObject != null)
3604 if (AsyncCausalityTracer.LoggingOn)
3605 AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.CompletionNotification);
3607 // Skip synchronous execution of continuations if this task's thread was aborted
3608 bool bCanInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) ||
3609 (Thread.CurrentThread.ThreadState == ThreadState.AbortRequested) ||
3610 ((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0));
3612 // Handle the single-Action case
3613 Action singleAction = continuationObject as Action;
3614 if (singleAction != null)
3616 AwaitTaskContinuation.RunOrScheduleAction(singleAction, bCanInlineContinuations, ref t_currentTask);
3617 LogFinishCompletionNotification();
3621 // Handle the single-ITaskCompletionAction case
3622 ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction;
3623 if (singleTaskCompletionAction != null)
3625 singleTaskCompletionAction.Invoke(this);
3626 LogFinishCompletionNotification();
3630 // Handle the single-TaskContinuation case
3631 TaskContinuation singleTaskContinuation = continuationObject as TaskContinuation;
3632 if (singleTaskContinuation != null)
3634 singleTaskContinuation.Run(this, bCanInlineContinuations);
3635 LogFinishCompletionNotification();
3639 // Not a single; attempt to cast as list
3640 List<object> continuations = continuationObject as List<object>;
3642 if (continuations == null)
3644 LogFinishCompletionNotification();
3645 return; // Not a single or a list; just return
3649 // Begin processing of continuation list
3652 // Wait for any concurrent adds or removes to be retired
3653 lock (continuations) { }
3654 int continuationCount = continuations.Count;
3656 // Fire the asynchronous continuations first ...
3657 for (int i = 0; i < continuationCount; i++)
3659 // Synchronous continuation tasks will have the ExecuteSynchronously option,
3660 // and we're looking for asynchronous tasks...
3661 var tc = continuations[i] as StandardTaskContinuation;
3662 if (tc != null && (tc.m_options & TaskContinuationOptions.ExecuteSynchronously) == 0)
3664 TplEtwProvider.Log.RunningContinuationList(Id, i, tc);
3665 continuations[i] = null; // so that we can skip this later
3666 tc.Run(this, bCanInlineContinuations);
3670 // ... and then fire the synchronous continuations (if there are any).
3671 // This includes ITaskCompletionAction, AwaitTaskContinuations, and
3672 // Action delegates, which are all by default implicitly synchronous.
3673 for (int i = 0; i < continuationCount; i++)
3675 object currentContinuation = continuations[i];
3676 if (currentContinuation == null) continue;
3677 continuations[i] = null; // to enable free'ing up memory earlier
3678 TplEtwProvider.Log.RunningContinuationList(Id, i, currentContinuation);
3680 // If the continuation is an Action delegate, it came from an await continuation,
3681 // and we should use AwaitTaskContinuation to run it.
3682 Action ad = currentContinuation as Action;
3685 AwaitTaskContinuation.RunOrScheduleAction(ad, bCanInlineContinuations, ref t_currentTask);
3689 // If it's a TaskContinuation object of some kind, invoke it.
3690 TaskContinuation tc = currentContinuation as TaskContinuation;
3693 // We know that this is a synchronous continuation because the
3694 // asynchronous ones have been weeded out
3695 tc.Run(this, bCanInlineContinuations);
3697 // Otherwise, it must be an ITaskCompletionAction, so invoke it.
3700 Contract.Assert(currentContinuation is ITaskCompletionAction, "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction");
3701 var action = (ITaskCompletionAction)currentContinuation;
3702 action.Invoke(this);
3707 LogFinishCompletionNotification();
3711 private void LogFinishCompletionNotification()
3713 if (AsyncCausalityTracer.LoggingOn)
3714 AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.CompletionNotification);
3717 #region Continuation methods
3719 #region Action<Task> continuation
3721 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3723 /// <param name="continuationAction">
3724 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3725 /// passed the completed task as an argument.
3727 /// <returns>A new continuation <see cref="Task"/>.</returns>
3729 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3730 /// completed, whether it completes due to running to completion successfully, faulting due to an
3731 /// unhandled exception, or exiting out early due to being canceled.
3733 /// <exception cref="T:System.ArgumentNullException">
3734 /// The <paramref name="continuationAction"/> argument is null.
3736 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3737 public Task ContinueWith(Action<Task> continuationAction)
3739 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3740 return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
3744 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3746 /// <param name="continuationAction">
3747 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3748 /// passed the completed task as an argument.
3750 /// <param name="cancellationToken"> The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3751 /// <returns>A new continuation <see cref="Task"/>.</returns>
3753 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3754 /// completed, whether it completes due to running to completion successfully, faulting due to an
3755 /// unhandled exception, or exiting out early due to being canceled.
3757 /// <exception cref="T:System.ArgumentNullException">
3758 /// The <paramref name="continuationAction"/> argument is null.
3760 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3761 /// has already been disposed.
3763 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3764 public Task ContinueWith(Action<Task> continuationAction, CancellationToken cancellationToken)
3766 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3767 return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
3771 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3773 /// <param name="continuationAction">
3774 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3775 /// passed the completed task as an argument.
3777 /// <param name="scheduler">
3778 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
3780 /// <returns>A new continuation <see cref="Task"/>.</returns>
3782 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3783 /// completed, whether it completes due to running to completion successfully, faulting due to an
3784 /// unhandled exception, or exiting out early due to being canceled.
3786 /// <exception cref="T:System.ArgumentNullException">
3787 /// The <paramref name="continuationAction"/> argument is null.
3789 /// <exception cref="T:System.ArgumentNullException">
3790 /// The <paramref name="scheduler"/> argument is null.
3792 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3793 public Task ContinueWith(Action<Task> continuationAction, TaskScheduler scheduler)
3795 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3796 return ContinueWith(continuationAction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
3800 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3802 /// <param name="continuationAction">
3803 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3804 /// passed the completed task as an argument.
3806 /// <param name="continuationOptions">
3807 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3809 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3810 /// well as execution options, such as <see
3811 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3813 /// <returns>A new continuation <see cref="Task"/>.</returns>
3815 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3816 /// completed. If the continuation criteria specified through the <paramref
3817 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
3818 /// instead of scheduled.
3820 /// <exception cref="T:System.ArgumentNullException">
3821 /// The <paramref name="continuationAction"/> argument is null.
3823 /// <exception cref="T:System.ArgumentOutOfRangeException">
3824 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3825 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3827 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3828 public Task ContinueWith(Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
3830 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3831 return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
3835 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3837 /// <param name="continuationAction">
3838 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3839 /// passed the completed task as an argument.
3841 /// <param name="continuationOptions">
3842 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3844 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3845 /// well as execution options, such as <see
3846 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3848 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3849 /// <param name="scheduler">
3850 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
3853 /// <returns>A new continuation <see cref="Task"/>.</returns>
3855 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3856 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
3857 /// are not met, the continuation task will be canceled instead of scheduled.
3859 /// <exception cref="T:System.ArgumentNullException">
3860 /// The <paramref name="continuationAction"/> argument is null.
3862 /// <exception cref="T:System.ArgumentOutOfRangeException">
3863 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3864 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3866 /// <exception cref="T:System.ArgumentNullException">
3867 /// The <paramref name="scheduler"/> argument is null.
3869 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3870 /// has already been disposed.
3872 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3873 public Task ContinueWith(Action<Task> continuationAction, CancellationToken cancellationToken,
3874 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
3876 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3877 return ContinueWith(continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark);
3880 // Same as the above overload, just with a stack mark parameter.
3881 private Task ContinueWith(Action<Task> continuationAction, TaskScheduler scheduler,
3882 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
3884 // Throw on continuation with null action
3885 if (continuationAction == null)
3887 throw new ArgumentNullException("continuationAction");
3890 // Throw on continuation with null TaskScheduler
3891 if (scheduler == null)
3893 throw new ArgumentNullException("scheduler");
3895 Contract.EndContractBlock();
3897 TaskCreationOptions creationOptions;
3898 InternalTaskOptions internalOptions;
3899 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
3901 Task continuationTask = new ContinuationTaskFromTask(
3902 this, continuationAction, null,
3903 creationOptions, internalOptions,
3907 // Register the continuation. If synchronous execution is requested, this may
3908 // actually invoke the continuation before returning.
3909 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
3911 return continuationTask;
3915 #region Action<Task, Object> continuation
3918 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3920 /// <param name="continuationAction">
3921 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3922 /// passed the completed task as and the caller-supplied state object as arguments.
3924 /// <param name="state">An object representing data to be used by the continuation action.</param>
3925 /// <returns>A new continuation <see cref="Task"/>.</returns>
3927 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3928 /// completed, whether it completes due to running to completion successfully, faulting due to an
3929 /// unhandled exception, or exiting out early due to being canceled.
3931 /// <exception cref="T:System.ArgumentNullException">
3932 /// The <paramref name="continuationAction"/> argument is null.
3934 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3935 public Task ContinueWith(Action<Task, Object> continuationAction, Object state)
3937 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3938 return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
3942 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3944 /// <param name="continuationAction">
3945 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3946 /// passed the completed task and the caller-supplied state object as arguments.
3948 /// <param name="state">An object representing data to be used by the continuation action.</param>
3949 /// <param name="cancellationToken"> The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3950 /// <returns>A new continuation <see cref="Task"/>.</returns>
3952 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3953 /// completed, whether it completes due to running to completion successfully, faulting due to an
3954 /// unhandled exception, or exiting out early due to being canceled.
3956 /// <exception cref="T:System.ArgumentNullException">
3957 /// The <paramref name="continuationAction"/> argument is null.
3959 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3960 /// has already been disposed.
3962 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3963 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, CancellationToken cancellationToken)
3965 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3966 return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
3970 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3972 /// <param name="continuationAction">
3973 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3974 /// passed the completed task and the caller-supplied state object as arguments.
3976 /// <param name="state">An object representing data to be used by the continuation action.</param>
3977 /// <param name="scheduler">
3978 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
3980 /// <returns>A new continuation <see cref="Task"/>.</returns>
3982 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3983 /// completed, whether it completes due to running to completion successfully, faulting due to an
3984 /// unhandled exception, or exiting out early due to being canceled.
3986 /// <exception cref="T:System.ArgumentNullException">
3987 /// The <paramref name="continuationAction"/> argument is null.
3989 /// <exception cref="T:System.ArgumentNullException">
3990 /// The <paramref name="scheduler"/> argument is null.
3992 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3993 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskScheduler scheduler)
3995 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3996 return ContinueWith(continuationAction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
4000 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4002 /// <param name="continuationAction">
4003 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
4004 /// passed the completed task and the caller-supplied state object as arguments.
4006 /// <param name="state">An object representing data to be used by the continuation action.</param>
4007 /// <param name="continuationOptions">
4008 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4010 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4011 /// well as execution options, such as <see
4012 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4014 /// <returns>A new continuation <see cref="Task"/>.</returns>
4016 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
4017 /// completed. If the continuation criteria specified through the <paramref
4018 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
4019 /// instead of scheduled.
4021 /// <exception cref="T:System.ArgumentNullException">
4022 /// The <paramref name="continuationAction"/> argument is null.
4024 /// <exception cref="T:System.ArgumentOutOfRangeException">
4025 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4026 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4028 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4029 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskContinuationOptions continuationOptions)
4031 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4032 return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
4036 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4038 /// <param name="continuationAction">
4039 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
4040 /// passed the completed task and the caller-supplied state object as arguments.
4042 /// <param name="state">An object representing data to be used by the continuation action.</param>
4043 /// <param name="continuationOptions">
4044 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4046 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4047 /// well as execution options, such as <see
4048 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4050 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4051 /// <param name="scheduler">
4052 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
4055 /// <returns>A new continuation <see cref="Task"/>.</returns>
4057 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
4058 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
4059 /// are not met, the continuation task will be canceled instead of scheduled.
4061 /// <exception cref="T:System.ArgumentNullException">
4062 /// The <paramref name="continuationAction"/> argument is null.
4064 /// <exception cref="T:System.ArgumentOutOfRangeException">
4065 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4066 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4068 /// <exception cref="T:System.ArgumentNullException">
4069 /// The <paramref name="scheduler"/> argument is null.
4071 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4072 /// has already been disposed.
4074 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4075 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, CancellationToken cancellationToken,
4076 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
4078 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4079 return ContinueWith(continuationAction, state, scheduler, cancellationToken, continuationOptions, ref stackMark);
4082 // Same as the above overload, just with a stack mark parameter.
4083 private Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskScheduler scheduler,
4084 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
4086 // Throw on continuation with null action
4087 if (continuationAction == null)
4089 throw new ArgumentNullException("continuationAction");
4092 // Throw on continuation with null TaskScheduler
4093 if (scheduler == null)
4095 throw new ArgumentNullException("scheduler");
4097 Contract.EndContractBlock();
4099 TaskCreationOptions creationOptions;
4100 InternalTaskOptions internalOptions;
4101 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
4103 Task continuationTask = new ContinuationTaskFromTask(
4104 this, continuationAction, state,
4105 creationOptions, internalOptions,
4109 // Register the continuation. If synchronous execution is requested, this may
4110 // actually invoke the continuation before returning.
4111 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
4113 return continuationTask;
4118 #region Func<Task, TResult> continuation
4121 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4123 /// <typeparam name="TResult">
4124 /// The type of the result produced by the continuation.
4126 /// <param name="continuationFunction">
4127 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4128 /// passed the completed task as an argument.
4130 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4132 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4133 /// completed, whether it completes due to running to completion successfully, faulting due to an
4134 /// unhandled exception, or exiting out early due to being canceled.
4136 /// <exception cref="T:System.ArgumentNullException">
4137 /// The <paramref name="continuationFunction"/> argument is null.
4139 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4140 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction)
4142 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4143 return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken),
4144 TaskContinuationOptions.None, ref stackMark);
4149 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4151 /// <typeparam name="TResult">
4152 /// The type of the result produced by the continuation.
4154 /// <param name="continuationFunction">
4155 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4156 /// passed the completed task as an argument.
4158 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4159 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4161 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4162 /// completed, whether it completes due to running to completion successfully, faulting due to an
4163 /// unhandled exception, or exiting out early due to being canceled.
4165 /// <exception cref="T:System.ArgumentNullException">
4166 /// The <paramref name="continuationFunction"/> argument is null.
4168 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4169 /// has already been disposed.
4171 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4172 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
4174 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4175 return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
4179 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4181 /// <typeparam name="TResult">
4182 /// The type of the result produced by the continuation.
4184 /// <param name="continuationFunction">
4185 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4186 /// passed the completed task as an argument.
4188 /// <param name="scheduler">
4189 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
4191 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4193 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4194 /// completed, whether it completes due to running to completion successfully, faulting due to an
4195 /// unhandled exception, or exiting out early due to being canceled.
4197 /// <exception cref="T:System.ArgumentNullException">
4198 /// The <paramref name="continuationFunction"/> argument is null.
4200 /// <exception cref="T:System.ArgumentNullException">
4201 /// The <paramref name="scheduler"/> argument is null.
4203 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4204 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
4206 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4207 return ContinueWith<TResult>(continuationFunction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
4211 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4213 /// <typeparam name="TResult">
4214 /// The type of the result produced by the continuation.
4216 /// <param name="continuationFunction">
4217 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4218 /// passed the completed task as an argument.
4220 /// <param name="continuationOptions">
4221 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4223 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4224 /// well as execution options, such as <see
4225 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4227 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4229 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4230 /// completed. If the continuation criteria specified through the <paramref
4231 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
4232 /// instead of scheduled.
4234 /// <exception cref="T:System.ArgumentNullException">
4235 /// The <paramref name="continuationFunction"/> argument is null.
4237 /// <exception cref="T:System.ArgumentOutOfRangeException">
4238 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4239 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4241 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4242 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
4244 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4245 return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
4249 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4251 /// <typeparam name="TResult">
4252 /// The type of the result produced by the continuation.
4254 /// <param name="continuationFunction">
4255 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4256 /// passed the completed task as an argument.
4258 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4259 /// <param name="continuationOptions">
4260 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4262 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4263 /// well as execution options, such as <see
4264 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4266 /// <param name="scheduler">
4267 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
4270 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4272 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4273 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
4274 /// are not met, the continuation task will be canceled instead of scheduled.
4276 /// <exception cref="T:System.ArgumentNullException">
4277 /// The <paramref name="continuationFunction"/> argument is null.
4279 /// <exception cref="T:System.ArgumentOutOfRangeException">
4280 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4281 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4283 /// <exception cref="T:System.ArgumentNullException">
4284 /// The <paramref name="scheduler"/> argument is null.
4286 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4287 /// has already been disposed.
4289 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4290 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
4291 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
4293 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4294 return ContinueWith<TResult>(continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
4297 // Same as the above overload, just with a stack mark parameter.
4298 private Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskScheduler scheduler,
4299 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
4301 // Throw on continuation with null function
4302 if (continuationFunction == null)
4304 throw new ArgumentNullException("continuationFunction");
4307 // Throw on continuation with null task scheduler
4308 if (scheduler == null)
4310 throw new ArgumentNullException("scheduler");
4312 Contract.EndContractBlock();
4314 TaskCreationOptions creationOptions;
4315 InternalTaskOptions internalOptions;
4316 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
4318 Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>(
4319 this, continuationFunction, null,
4320 creationOptions, internalOptions,
4324 // Register the continuation. If synchronous execution is requested, this may
4325 // actually invoke the continuation before returning.
4326 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
4328 return continuationTask;
4332 #region Func<Task, Object, TResult> continuation
4335 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4337 /// <typeparam name="TResult">
4338 /// The type of the result produced by the continuation.
4340 /// <param name="continuationFunction">
4341 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4342 /// passed the completed task and the caller-supplied state object as arguments.
4344 /// <param name="state">An object representing data to be used by the continuation function.</param>
4345 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4347 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4348 /// completed, whether it completes due to running to completion successfully, faulting due to an
4349 /// unhandled exception, or exiting out early due to being canceled.
4351 /// <exception cref="T:System.ArgumentNullException">
4352 /// The <paramref name="continuationFunction"/> argument is null.
4354 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4355 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state)
4357 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4358 return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken),
4359 TaskContinuationOptions.None, ref stackMark);
4364 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4366 /// <typeparam name="TResult">
4367 /// The type of the result produced by the continuation.
4369 /// <param name="continuationFunction">
4370 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4371 /// passed the completed task and the caller-supplied state object as arguments.
4373 /// <param name="state">An object representing data to be used by the continuation function.</param>
4374 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4375 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4377 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4378 /// completed, whether it completes due to running to completion successfully, faulting due to an
4379 /// unhandled exception, or exiting out early due to being canceled.
4381 /// <exception cref="T:System.ArgumentNullException">
4382 /// The <paramref name="continuationFunction"/> argument is null.
4384 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4385 /// has already been disposed.
4387 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4388 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, CancellationToken cancellationToken)
4390 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4391 return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
4395 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4397 /// <typeparam name="TResult">
4398 /// The type of the result produced by the continuation.
4400 /// <param name="continuationFunction">
4401 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4402 /// passed the completed task and the caller-supplied state object as arguments.
4404 /// <param name="state">An object representing data to be used by the continuation function.</param>
4405 /// <param name="scheduler">
4406 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
4408 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4410 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4411 /// completed, whether it completes due to running to completion successfully, faulting due to an
4412 /// unhandled exception, or exiting out early due to being canceled.
4414 /// <exception cref="T:System.ArgumentNullException">
4415 /// The <paramref name="continuationFunction"/> argument is null.
4417 /// <exception cref="T:System.ArgumentNullException">
4418 /// The <paramref name="scheduler"/> argument is null.
4420 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4421 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskScheduler scheduler)
4423 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4424 return ContinueWith<TResult>(continuationFunction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
4428 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4430 /// <typeparam name="TResult">
4431 /// The type of the result produced by the continuation.
4433 /// <param name="continuationFunction">
4434 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4435 /// passed the completed task and the caller-supplied state object as arguments.
4437 /// <param name="state">An object representing data to be used by the continuation function.</param>
4438 /// <param name="continuationOptions">
4439 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4441 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4442 /// well as execution options, such as <see
4443 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4445 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4447 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4448 /// completed. If the continuation criteria specified through the <paramref
4449 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
4450 /// instead of scheduled.
4452 /// <exception cref="T:System.ArgumentNullException">
4453 /// The <paramref name="continuationFunction"/> argument is null.
4455 /// <exception cref="T:System.ArgumentOutOfRangeException">
4456 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4457 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4459 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4460 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskContinuationOptions continuationOptions)
4462 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4463 return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
4467 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4469 /// <typeparam name="TResult">
4470 /// The type of the result produced by the continuation.
4472 /// <param name="continuationFunction">
4473 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4474 /// passed the completed task and the caller-supplied state object as arguments.
4476 /// <param name="state">An object representing data to be used by the continuation function.</param>
4477 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4478 /// <param name="continuationOptions">
4479 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4481 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4482 /// well as execution options, such as <see
4483 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4485 /// <param name="scheduler">
4486 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
4489 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4491 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4492 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
4493 /// are not met, the continuation task will be canceled instead of scheduled.
4495 /// <exception cref="T:System.ArgumentNullException">
4496 /// The <paramref name="continuationFunction"/> argument is null.
4498 /// <exception cref="T:System.ArgumentOutOfRangeException">
4499 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4500 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4502 /// <exception cref="T:System.ArgumentNullException">
4503 /// The <paramref name="scheduler"/> argument is null.
4505 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4506 /// has already been disposed.
4508 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4509 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, CancellationToken cancellationToken,
4510 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
4512 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4513 return ContinueWith<TResult>(continuationFunction, state, scheduler, cancellationToken, continuationOptions, ref stackMark);
4516 // Same as the above overload, just with a stack mark parameter.
4517 private Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskScheduler scheduler,
4518 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
4520 // Throw on continuation with null function
4521 if (continuationFunction == null)
4523 throw new ArgumentNullException("continuationFunction");
4526 // Throw on continuation with null task scheduler
4527 if (scheduler == null)
4529 throw new ArgumentNullException("scheduler");
4531 Contract.EndContractBlock();
4533 TaskCreationOptions creationOptions;
4534 InternalTaskOptions internalOptions;
4535 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
4537 Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>(
4538 this, continuationFunction, state,
4539 creationOptions, internalOptions,
4543 // Register the continuation. If synchronous execution is requested, this may
4544 // actually invoke the continuation before returning.
4545 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
4547 return continuationTask;
4552 /// Converts TaskContinuationOptions to TaskCreationOptions, and also does
4553 /// some validity checking along the way.
4555 /// <param name="continuationOptions">Incoming TaskContinuationOptions</param>
4556 /// <param name="creationOptions">Outgoing TaskCreationOptions</param>
4557 /// <param name="internalOptions">Outgoing InternalTaskOptions</param>
4558 internal static void CreationOptionsFromContinuationOptions(
4559 TaskContinuationOptions continuationOptions,
4560 out TaskCreationOptions creationOptions,
4561 out InternalTaskOptions internalOptions)
4563 // This is used a couple of times below
4564 TaskContinuationOptions NotOnAnything =
4565 TaskContinuationOptions.NotOnCanceled |
4566 TaskContinuationOptions.NotOnFaulted |
4567 TaskContinuationOptions.NotOnRanToCompletion;
4569 TaskContinuationOptions creationOptionsMask =
4570 TaskContinuationOptions.PreferFairness |
4571 TaskContinuationOptions.LongRunning |
4572 TaskContinuationOptions.DenyChildAttach |
4573 TaskContinuationOptions.HideScheduler |
4574 TaskContinuationOptions.AttachedToParent|
4575 TaskContinuationOptions.RunContinuationsAsynchronously;
4577 // Check that LongRunning and ExecuteSynchronously are not specified together
4578 TaskContinuationOptions illegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning;
4579 if ((continuationOptions & illegalMask) == illegalMask)
4581 throw new ArgumentOutOfRangeException("continuationOptions", Environment.GetResourceString("Task_ContinueWith_ESandLR"));
4584 // Check that no illegal options were specified
4585 if ((continuationOptions &
4586 ~(creationOptionsMask | NotOnAnything |
4587 TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously)) != 0)
4589 throw new ArgumentOutOfRangeException("continuationOptions");
4592 // Check that we didn't specify "not on anything"
4593 if ((continuationOptions & NotOnAnything) == NotOnAnything)
4595 throw new ArgumentOutOfRangeException("continuationOptions", Environment.GetResourceString("Task_ContinueWith_NotOnAnything"));
4598 // This passes over all but LazyCancellation, which has no representation in TaskCreationOptions
4599 creationOptions = (TaskCreationOptions)(continuationOptions & creationOptionsMask);
4601 // internalOptions has at least ContinuationTask ...
4602 internalOptions = InternalTaskOptions.ContinuationTask;
4604 // ... and possibly LazyCancellation
4605 if ((continuationOptions & TaskContinuationOptions.LazyCancellation) != 0)
4606 internalOptions |= InternalTaskOptions.LazyCancellation;
4611 /// Registers the continuation and possibly runs it (if the task is already finished).
4613 /// <param name="continuationTask">The continuation task itself.</param>
4614 /// <param name="scheduler">TaskScheduler with which to associate continuation task.</param>
4615 /// <param name="options">Restrictions on when the continuation becomes active.</param>
4616 internal void ContinueWithCore(Task continuationTask,
4617 TaskScheduler scheduler,
4618 CancellationToken cancellationToken,
4619 TaskContinuationOptions options)
4621 Contract.Requires(continuationTask != null, "Task.ContinueWithCore(): null continuationTask");
4622 Contract.Requires(scheduler != null, "Task.ContinueWithCore(): null scheduler");
4623 Contract.Requires(!continuationTask.IsCompleted, "Did not expect continuationTask to be completed");
4625 // Create a TaskContinuation
4626 TaskContinuation continuation = new StandardTaskContinuation(continuationTask, options, scheduler);
4628 // If cancellationToken is cancellable, then assign it.
4629 if (cancellationToken.CanBeCanceled)
4631 if (IsCompleted || cancellationToken.IsCancellationRequested)
4633 // If the antecedent has completed, then we will not be queuing up
4634 // the continuation in the antecedent's continuation list. Likewise,
4635 // if the cancellationToken has been canceled, continuationTask will
4636 // be completed in the AssignCancellationToken call below, and there
4637 // is no need to queue the continuation to the antecedent's continuation
4638 // list. In either of these two cases, we will pass "null" for the antecedent,
4639 // meaning "the cancellation callback should not attempt to remove the
4640 // continuation from its antecedent's continuation list".
4641 continuationTask.AssignCancellationToken(cancellationToken, null, null);
4645 // The antecedent is not yet complete, so there is a pretty good chance
4646 // that the continuation will be queued up in the antecedent. Assign the
4647 // cancellation token with information about the antecedent, so that the
4648 // continuation can be dequeued upon the signalling of the token.
4650 // It's possible that the antecedent completes before the call to AddTaskContinuation,
4651 // and that is a benign ----. It just means that the cancellation will result in
4652 // a futile search of the antecedent's continuation list.
4653 continuationTask.AssignCancellationToken(cancellationToken, this, continuation);
4657 // In the case of a pre-canceled token, continuationTask will have been completed
4658 // in a Canceled state by now. If such is the case, there is no need to go through
4659 // the motions of queuing up the continuation for eventual execution.
4660 if (!continuationTask.IsCompleted)
4662 // We need additional correlation produced here to ensure that at least the continuation
4663 // code will be correlatable to the currrent activity that initiated "this" task:
4664 // . when the antecendent ("this") is a promise we have very little control over where
4665 // the code for the promise will run (e.g. it can be a task from a user provided
4666 // TaskCompletionSource or from a classic Begin/End async operation); this user or
4667 // system code will likely not have stamped an activity id on the thread, so there's
4668 // generally no easy correlation that can be provided between the current activity
4669 // and the promise. Also the continuation code may run practically on any thread.
4670 // Since there may be no correlation between the current activity and the TCS's task
4671 // activity, we ensure we at least create a correlation from the current activity to
4672 // the continuation that runs when the promise completes.
4673 if ((this.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0 &&
4674 !(this is ITaskCompletionAction))
4676 var etwLog = TplEtwProvider.Log;
4677 if (etwLog.IsEnabled())
4679 etwLog.AwaitTaskContinuationScheduled(TaskScheduler.Current.Id, Task.CurrentId ?? 0, continuationTask.Id);
4683 // Attempt to enqueue the continuation
4684 bool continuationQueued = AddTaskContinuation(continuation, addBeforeOthers: false);
4686 // If the continuation was not queued (because the task completed), then run it now.
4687 if (!continuationQueued) continuation.Run(this, bCanInlineContinuationTask: true);
4692 // Adds a lightweight completion action to a task. This is similar to a continuation
4693 // task except that it is stored as an action, and thus does not require the allocation/
4694 // execution resources of a continuation task.
4696 // Used internally by ContinueWhenAll() and ContinueWhenAny().
4697 internal void AddCompletionAction(ITaskCompletionAction action)
4699 AddCompletionAction(action, addBeforeOthers: false);
4702 private void AddCompletionAction(ITaskCompletionAction action, bool addBeforeOthers)
4704 if (!AddTaskContinuation(action, addBeforeOthers))
4705 action.Invoke(this); // run the action directly if we failed to queue the continuation (i.e., the task completed)
4708 // Support method for AddTaskContinuation that takes care of multi-continuation logic.
4709 // Returns true if and only if the continuation was successfully queued.
4710 // THIS METHOD ASSUMES THAT m_continuationObject IS NOT NULL. That case was taken
4711 // care of in the calling method, AddTaskContinuation().
4712 private bool AddTaskContinuationComplex(object tc, bool addBeforeOthers)
4714 Contract.Requires(tc != null, "Expected non-null tc object in AddTaskContinuationComplex");
4716 object oldValue = m_continuationObject;
4718 // Logic for the case where we were previously storing a single continuation
4719 if ((oldValue != s_taskCompletionSentinel) && (!(oldValue is List<object>)))
4721 // Construct a new TaskContinuation list
4722 List<object> newList = new List<object>();
4724 // Add in the old single value
4725 newList.Add(oldValue);
4727 // Now CAS in the new list
4728 Interlocked.CompareExchange(ref m_continuationObject, newList, oldValue);
4730 // We might be racing against another thread converting the single into
4731 // a list, or we might be racing against task completion, so resample "list"
4735 // m_continuationObject is guaranteed at this point to be either a List or
4736 // s_taskCompletionSentinel.
4737 List<object> list = m_continuationObject as List<object>;
4738 Contract.Assert((list != null) || (m_continuationObject == s_taskCompletionSentinel),
4739 "Expected m_continuationObject to be list or sentinel");
4741 // If list is null, it can only mean that s_taskCompletionSentinel has been exchanged
4742 // into m_continuationObject. Thus, the task has completed and we should return false
4743 // from this method, as we will not be queuing up the continuation.
4748 // It is possible for the task to complete right after we snap the copy of
4749 // the list. If so, then fall through and return false without queuing the
4751 if (m_continuationObject != s_taskCompletionSentinel)
4753 // Before growing the list we remove possible null entries that are the
4754 // result from RemoveContinuations()
4755 if (list.Count == list.Capacity)
4757 list.RemoveAll(s_IsTaskContinuationNullPredicate);
4760 if (addBeforeOthers)
4765 return true; // continuation successfully queued, so return true.
4770 // We didn't succeed in queuing the continuation, so return false.
4774 // Record a continuation task or action.
4775 // Return true if and only if we successfully queued a continuation.
4776 private bool AddTaskContinuation(object tc, bool addBeforeOthers)
4778 Contract.Requires(tc != null);
4780 // Make sure that, if someone calls ContinueWith() right after waiting for the predecessor to complete,
4781 // we don't queue up a continuation.
4782 if (IsCompleted) return false;
4784 // Try to just jam tc into m_continuationObject
4785 if ((m_continuationObject != null) || (Interlocked.CompareExchange(ref m_continuationObject, tc, null) != null))
4787 // If we get here, it means that we failed to CAS tc into m_continuationObject.
4788 // Therefore, we must go the more complicated route.
4789 return AddTaskContinuationComplex(tc, addBeforeOthers);
4794 // Removes a continuation task from m_continuations
4795 internal void RemoveContinuation(object continuationObject) // could be TaskContinuation or Action<Task>
4797 // We need to snap a local reference to m_continuations since reading a volatile object is more costly.
4798 // Also to prevent the value to be changed as result of a race condition with another method.
4799 object continuationsLocalRef = m_continuationObject;
4801 // Task is completed. Nothing to do here.
4802 if (continuationsLocalRef == s_taskCompletionSentinel) return;
4804 List<object> continuationsLocalListRef = continuationsLocalRef as List<object>;
4806 if (continuationsLocalListRef == null)
4808 // 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.
4809 // Note we cannot go back to a null state, since it will mess up the AddTaskContinuation logic.
4810 if (Interlocked.CompareExchange(ref m_continuationObject, new List<object>(), continuationObject) != continuationObject)
4812 // If we fail it means that either AddContinuationComplex won the race condition and m_continuationObject is now a List
4813 // that contains the element we want to remove. Or FinishContinuations set the s_taskCompletionSentinel.
4814 // So we should try to get a list one more time
4815 continuationsLocalListRef = m_continuationObject as List<object>;
4819 // Exchange was successful so we can skip the last comparison
4824 // if continuationsLocalRef == null it means s_taskCompletionSentinel has been set already and there is nothing else to do.
4825 if (continuationsLocalListRef != null)
4827 lock (continuationsLocalListRef)
4829 // There is a small chance that this task completed since we took a local snapshot into
4830 // continuationsLocalRef. In that case, just return; we don't want to be manipulating the
4831 // continuation list as it is being processed.
4832 if (m_continuationObject == s_taskCompletionSentinel) return;
4834 // Find continuationObject in the continuation list
4835 int index = continuationsLocalListRef.IndexOf(continuationObject);
4839 // null out that TaskContinuation entry, which will be interpreted as "to be cleaned up"
4840 continuationsLocalListRef[index] = null;
4847 // statically allocated delegate for the RemoveAll expression in RemoveContinuations() and AddContinuationComplex()
4848 private readonly static Predicate<object> s_IsTaskContinuationNullPredicate =
4849 new Predicate<object>((tc) => { return (tc == null); });
4857 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4859 /// <param name="tasks">
4860 /// An array of <see cref="Task"/> instances on which to wait.
4862 /// <exception cref="T:System.ArgumentNullException">
4863 /// The <paramref name="tasks"/> argument is null.
4865 /// <exception cref="T:System.ArgumentNullException">
4866 /// The <paramref name="tasks"/> argument contains a null element.
4868 /// <exception cref="T:System.AggregateException">
4869 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4870 /// the execution of at least one of the <see cref="Task"/> instances.
4872 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4873 public static void WaitAll(params Task[] tasks)
4878 WaitAll(tasks, Timeout.Infinite);
4881 Contract.Assert(waitResult, "expected wait to succeed");
4886 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4889 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4890 /// otherwise, false.
4892 /// <param name="tasks">
4893 /// An array of <see cref="Task"/> instances on which to wait.
4895 /// <param name="timeout">
4896 /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
4897 /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
4899 /// <exception cref="T:System.ArgumentNullException">
4900 /// The <paramref name="tasks"/> argument is null.
4902 /// <exception cref="T:System.ArgumentException">
4903 /// The <paramref name="tasks"/> argument contains a null element.
4905 /// <exception cref="T:System.AggregateException">
4906 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4907 /// the execution of at least one of the <see cref="Task"/> instances.
4909 /// <exception cref="T:System.ArgumentOutOfRangeException">
4910 /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
4911 /// infinite time-out -or- timeout is greater than
4912 /// <see cref="System.Int32.MaxValue"/>.
4914 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4915 public static bool WaitAll(Task[] tasks, TimeSpan timeout)
4917 long totalMilliseconds = (long)timeout.TotalMilliseconds;
4918 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
4920 throw new ArgumentOutOfRangeException("timeout");
4923 return WaitAll(tasks, (int)totalMilliseconds);
4928 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4931 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4932 /// otherwise, false.
4934 /// <param name="millisecondsTimeout">
4935 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
4936 /// wait indefinitely.</param>
4937 /// <param name="tasks">An array of <see cref="Task"/> instances on which to wait.
4939 /// <exception cref="T:System.ArgumentNullException">
4940 /// The <paramref name="tasks"/> argument is null.
4942 /// <exception cref="T:System.ArgumentException">
4943 /// The <paramref name="tasks"/> argument contains a null element.
4945 /// <exception cref="T:System.AggregateException">
4946 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4947 /// the execution of at least one of the <see cref="Task"/> instances.
4949 /// <exception cref="T:System.ArgumentOutOfRangeException">
4950 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
4951 /// infinite time-out.
4953 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4954 public static bool WaitAll(Task[] tasks, int millisecondsTimeout)
4956 return WaitAll(tasks, millisecondsTimeout, default(CancellationToken));
4960 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4963 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4964 /// otherwise, false.
4966 /// <param name="tasks">
4967 /// An array of <see cref="Task"/> instances on which to wait.
4969 /// <param name="cancellationToken">
4970 /// A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.
4972 /// <exception cref="T:System.ArgumentNullException">
4973 /// The <paramref name="tasks"/> argument is null.
4975 /// <exception cref="T:System.ArgumentException">
4976 /// The <paramref name="tasks"/> argument contains a null element.
4978 /// <exception cref="T:System.AggregateException">
4979 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4980 /// the execution of at least one of the <see cref="Task"/> instances.
4982 /// <exception cref="T:System.OperationCanceledException">
4983 /// The <paramref name="cancellationToken"/> was canceled.
4985 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4986 public static void WaitAll(Task[] tasks, CancellationToken cancellationToken)
4988 WaitAll(tasks, Timeout.Infinite, cancellationToken);
4992 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4995 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4996 /// otherwise, false.
4998 /// <param name="tasks">
4999 /// An array of <see cref="Task"/> instances on which to wait.
5001 /// <param name="millisecondsTimeout">
5002 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
5003 /// wait indefinitely.
5005 /// <param name="cancellationToken">
5006 /// A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.
5008 /// <exception cref="T:System.ArgumentNullException">
5009 /// The <paramref name="tasks"/> argument is null.
5011 /// <exception cref="T:System.ArgumentException">
5012 /// The <paramref name="tasks"/> argument contains a null element.
5014 /// <exception cref="T:System.AggregateException">
5015 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
5016 /// the execution of at least one of the <see cref="Task"/> instances.
5018 /// <exception cref="T:System.ArgumentOutOfRangeException">
5019 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
5020 /// infinite time-out.
5022 /// <exception cref="T:System.OperationCanceledException">
5023 /// The <paramref name="cancellationToken"/> was canceled.
5025 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5026 public static bool WaitAll(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
5030 throw new ArgumentNullException("tasks");
5032 if (millisecondsTimeout < -1)
5034 throw new ArgumentOutOfRangeException("millisecondsTimeout");
5036 Contract.EndContractBlock();
5038 cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations
5041 // In this WaitAll() implementation we have 2 alternate code paths for a task to be handled:
5042 // CODEPATH1: skip an already completed task, CODEPATH2: actually wait on tasks
5043 // We make sure that the exception behavior of Task.Wait() is replicated the same for tasks handled in either of these codepaths
5046 List<Exception> exceptions = null;
5047 List<Task> waitedOnTaskList = null;
5048 List<Task> notificationTasks = null;
5050 // If any of the waited-upon tasks end as Faulted or Canceled, set these to true.
5051 bool exceptionSeen = false, cancellationSeen = false;
5053 bool returnValue = true;
5055 // Collects incomplete tasks in "waitedOnTaskList"
5056 for (int i = tasks.Length - 1; i >= 0; i--)
5058 Task task = tasks[i];
5062 throw new ArgumentException(Environment.GetResourceString("Task_WaitMulti_NullTask"), "tasks");
5065 bool taskIsCompleted = task.IsCompleted;
5066 if (!taskIsCompleted)
5068 // try inlining the task only if we have an infinite timeout and an empty cancellation token
5069 if (millisecondsTimeout != Timeout.Infinite || cancellationToken.CanBeCanceled)
5071 // We either didn't attempt inline execution because we had a non-infinite timeout or we had a cancellable token.
5072 // In all cases we need to do a full wait on the task (=> add its event into the list.)
5073 AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
5077 // We are eligible for inlining. If it doesn't work, we'll do a full wait.
5078 taskIsCompleted = task.WrappedTryRunInline() && task.IsCompleted; // A successful TryRunInline doesn't guarantee completion
5079 if (!taskIsCompleted) AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
5083 if (taskIsCompleted)
5085 if (task.IsFaulted) exceptionSeen = true;
5086 else if (task.IsCanceled) cancellationSeen = true;
5087 if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: 1);
5091 if (waitedOnTaskList != null)
5093 // Block waiting for the tasks to complete.
5094 returnValue = WaitAllBlockingCore(waitedOnTaskList, millisecondsTimeout, cancellationToken);
5096 // If the wait didn't time out, ensure exceptions are propagated, and if a debugger is
5097 // attached and one of these tasks requires it, that we notify the debugger of a wait completion.
5100 // Add any exceptions for this task to the collection, and if it's wait
5101 // notification bit is set, store it to operate on at the end.
5102 foreach (var task in waitedOnTaskList)
5104 if (task.IsFaulted) exceptionSeen = true;
5105 else if (task.IsCanceled) cancellationSeen = true;
5106 if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: 1);
5110 // We need to prevent the tasks array from being GC'ed until we come out of the wait.
5111 // This is necessary so that the Parallel Debugger can traverse it during the long wait and
5112 // deduce waiter/waitee relationships
5113 GC.KeepAlive(tasks);
5116 // Now that we're done and about to exit, if the wait completed and if we have
5117 // any tasks with a notification bit set, signal the debugger if any requires it.
5118 if (returnValue && notificationTasks != null)
5120 // Loop through each task tha that had its bit set, and notify the debugger
5121 // about the first one that requires it. The debugger will reset the bit
5122 // for any tasks we don't notify of as soon as we break, so we only need to notify
5124 foreach (var task in notificationTasks)
5126 if (task.NotifyDebuggerOfWaitCompletionIfNecessary()) break;
5130 // If one or more threw exceptions, aggregate and throw them.
5131 if (returnValue && (exceptionSeen || cancellationSeen))
5133 // If the WaitAll was canceled and tasks were canceled but not faulted,
5134 // prioritize throwing an OCE for canceling the WaitAll over throwing an
5135 // AggregateException for all of the canceled Tasks. This helps
5136 // to bring determinism to an otherwise non-determistic case of using
5137 // the same token to cancel both the WaitAll and the Tasks.
5138 if (!exceptionSeen) cancellationToken.ThrowIfCancellationRequested();
5140 // Now gather up and throw all of the exceptions.
5141 foreach (var task in tasks) AddExceptionsForCompletedTask(ref exceptions, task);
5142 Contract.Assert(exceptions != null, "Should have seen at least one exception");
5143 throw new AggregateException(exceptions);
5149 /// <summary>Adds an element to the list, initializing the list if it's null.</summary>
5150 /// <typeparam name="T">Specifies the type of data stored in the list.</typeparam>
5151 /// <param name="item">The item to add.</param>
5152 /// <param name="list">The list.</param>
5153 /// <param name="initSize">The size to which to initialize the list if the list is null.</param>
5154 private static void AddToList<T>(T item, ref List<T> list, int initSize)
5156 if (list == null) list = new List<T>(initSize);
5160 /// <summary>Performs a blocking WaitAll on the vetted list of tasks.</summary>
5161 /// <param name="tasks">The tasks, which have already been checked and filtered for completion.</param>
5162 /// <param name="millisecondsTimeout">The timeout.</param>
5163 /// <param name="cancellationToken">The cancellation token.</param>
5164 /// <returns>true if all of the tasks completed; otherwise, false.</returns>
5165 private static bool WaitAllBlockingCore(List<Task> tasks, int millisecondsTimeout, CancellationToken cancellationToken)
5167 Contract.Assert(tasks != null, "Expected a non-null list of tasks");
5168 Contract.Assert(tasks.Count > 0, "Expected at least one task");
5170 bool waitCompleted = false;
5171 var mres = new SetOnCountdownMres(tasks.Count);
5174 foreach (var task in tasks)
5176 task.AddCompletionAction(mres, addBeforeOthers: true);
5178 waitCompleted = mres.Wait(millisecondsTimeout, cancellationToken);
5184 foreach (var task in tasks)
5186 if (!task.IsCompleted) task.RemoveContinuation(mres);
5189 // It's ok that we don't dispose of the MRES here, as we never
5190 // access the MRES' WaitHandle, and thus no finalizable resources
5191 // are actually created. We don't always just Dispose it because
5192 // a continuation that's accessing the MRES could still be executing.
5194 return waitCompleted;
5197 // A ManualResetEventSlim that will get Set after Invoke is called count times.
5198 // This allows us to replace this logic:
5199 // var mres = new ManualResetEventSlim(tasks.Count);
5200 // Action<Task> completionAction = delegate { if(Interlocked.Decrement(ref count) == 0) mres.Set(); };
5201 // foreach(var task in tasks) task.AddCompletionAction(completionAction);
5203 // var mres = new SetOnCountdownMres(tasks.Count);
5204 // foreach(var task in tasks) task.AddCompletionAction(mres);
5205 // which saves a couple of allocations.
5207 // Used in WaitAllBlockingCore (above).
5208 private sealed class SetOnCountdownMres : ManualResetEventSlim, ITaskCompletionAction
5212 internal SetOnCountdownMres(int count)
5214 Contract.Assert(count > 0, "Expected count > 0");
5218 public void Invoke(Task completingTask)
5220 if (Interlocked.Decrement(ref _count) == 0) Set();
5221 Contract.Assert(_count >= 0, "Count should never go below 0");
5226 /// Internal WaitAll implementation which is meant to be used with small number of tasks,
5227 /// optimized for Parallel.Invoke and other structured primitives.
5229 internal static void FastWaitAll(Task[] tasks)
5231 Contract.Requires(tasks != null);
5233 List<Exception> exceptions = null;
5235 // Collects incomplete tasks in "waitedOnTaskList" and their cooperative events in "cooperativeEventList"
5236 for (int i = tasks.Length - 1; i >= 0; i--)
5238 if (!tasks[i].IsCompleted)
5240 // Just attempting to inline here... result doesn't matter.
5241 // We'll do a second pass to do actual wait on each task, and to aggregate their exceptions.
5242 // If the task is inlined here, it will register as IsCompleted in the second pass
5243 // and will just give us the exception.
5244 tasks[i].WrappedTryRunInline();
5248 // Wait on the tasks.
5249 for (int i = tasks.Length - 1; i >= 0; i--)
5251 var task = tasks[i];
5252 task.SpinThenBlockingWait(Timeout.Infinite, default(CancellationToken));
5253 AddExceptionsForCompletedTask(ref exceptions, task);
5255 // Note that unlike other wait code paths, we do not check
5256 // task.NotifyDebuggerOfWaitCompletionIfNecessary() here, because this method is currently
5257 // only used from contexts where the tasks couldn't have that bit set, namely
5258 // Parallel.Invoke. If that ever changes, such checks should be added here.
5261 // If one or more threw exceptions, aggregate them.
5262 if (exceptions != null)
5264 throw new AggregateException(exceptions);
5269 /// This internal function is only meant to be called by WaitAll()
5270 /// If the completed task is canceled or it has other exceptions, here we will add those
5271 /// into the passed in exception list (which will be lazily initialized here).
5273 internal static void AddExceptionsForCompletedTask(ref List<Exception> exceptions, Task t)
5275 AggregateException ex = t.GetExceptions(true);
5278 // make sure the task's exception observed status is set appropriately
5279 // it's possible that WaitAll was called by the parent of an attached child,
5280 // this will make sure it won't throw again in the implicit wait
5281 t.UpdateExceptionObservedStatus();
5283 if (exceptions == null)
5285 exceptions = new List<Exception>(ex.InnerExceptions.Count);
5288 exceptions.AddRange(ex.InnerExceptions);
5294 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5296 /// <param name="tasks">
5297 /// An array of <see cref="Task"/> instances on which to wait.
5299 /// <returns>The index of the completed task in the <paramref name="tasks"/> array argument.</returns>
5300 /// <exception cref="T:System.ArgumentNullException">
5301 /// The <paramref name="tasks"/> argument is null.
5303 /// <exception cref="T:System.ArgumentException">
5304 /// The <paramref name="tasks"/> argument contains a null element.
5306 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5307 public static int WaitAny(params Task[] tasks)
5309 int waitResult = WaitAny(tasks, Timeout.Infinite);
5310 Contract.Assert(tasks.Length == 0 || waitResult != -1, "expected wait to succeed");
5315 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5317 /// <param name="tasks">
5318 /// An array of <see cref="Task"/> instances on which to wait.
5320 /// <param name="timeout">
5321 /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
5322 /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
5325 /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
5326 /// timeout occurred.
5328 /// <exception cref="T:System.ArgumentNullException">
5329 /// The <paramref name="tasks"/> argument is null.
5331 /// <exception cref="T:System.ArgumentException">
5332 /// The <paramref name="tasks"/> argument contains a null element.
5334 /// <exception cref="T:System.ArgumentOutOfRangeException">
5335 /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
5336 /// infinite time-out -or- timeout is greater than
5337 /// <see cref="System.Int32.MaxValue"/>.
5339 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5340 public static int WaitAny(Task[] tasks, TimeSpan timeout)
5342 long totalMilliseconds = (long)timeout.TotalMilliseconds;
5343 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
5345 throw new ArgumentOutOfRangeException("timeout");
5348 return WaitAny(tasks, (int)totalMilliseconds);
5352 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5354 /// <param name="tasks">
5355 /// An array of <see cref="Task"/> instances on which to wait.
5357 /// <param name="cancellationToken">
5358 /// A <see cref="CancellationToken"/> to observe while waiting for a task to complete.
5361 /// The index of the completed task in the <paramref name="tasks"/> array argument.
5363 /// <exception cref="T:System.ArgumentNullException">
5364 /// The <paramref name="tasks"/> argument is null.
5366 /// <exception cref="T:System.ArgumentException">
5367 /// The <paramref name="tasks"/> argument contains a null element.
5369 /// <exception cref="T:System.OperationCanceledException">
5370 /// The <paramref name="cancellationToken"/> was canceled.
5372 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5373 public static int WaitAny(Task[] tasks, CancellationToken cancellationToken)
5375 return WaitAny(tasks, Timeout.Infinite, cancellationToken);
5379 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5381 /// <param name="tasks">
5382 /// An array of <see cref="Task"/> instances on which to wait.
5384 /// <param name="millisecondsTimeout">
5385 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
5386 /// wait indefinitely.
5389 /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
5390 /// timeout occurred.
5392 /// <exception cref="T:System.ArgumentNullException">
5393 /// The <paramref name="tasks"/> argument is null.
5395 /// <exception cref="T:System.ArgumentException">
5396 /// The <paramref name="tasks"/> argument contains a null element.
5398 /// <exception cref="T:System.ArgumentOutOfRangeException">
5399 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
5400 /// infinite time-out.
5402 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5403 public static int WaitAny(Task[] tasks, int millisecondsTimeout)
5405 return WaitAny(tasks, millisecondsTimeout, default(CancellationToken));
5409 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5411 /// <param name="tasks">
5412 /// An array of <see cref="Task"/> instances on which to wait.
5414 /// <param name="millisecondsTimeout">
5415 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
5416 /// wait indefinitely.
5418 /// <param name="cancellationToken">
5419 /// A <see cref="CancellationToken"/> to observe while waiting for a task to complete.
5422 /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
5423 /// timeout occurred.
5425 /// <exception cref="T:System.ArgumentNullException">
5426 /// The <paramref name="tasks"/> argument is null.
5428 /// <exception cref="T:System.ArgumentException">
5429 /// The <paramref name="tasks"/> argument contains a null element.
5431 /// <exception cref="T:System.ArgumentOutOfRangeException">
5432 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
5433 /// infinite time-out.
5435 /// <exception cref="T:System.OperationCanceledException">
5436 /// The <paramref name="cancellationToken"/> was canceled.
5438 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5439 public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
5443 throw new ArgumentNullException("tasks");
5445 if (millisecondsTimeout < -1)
5447 throw new ArgumentOutOfRangeException("millisecondsTimeout");
5449 Contract.EndContractBlock();
5451 cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations
5453 int signaledTaskIndex = -1;
5455 // Make a pass through the loop to check for any tasks that may have
5456 // already been completed, and to verify that no tasks are null.
5458 for (int taskIndex = 0; taskIndex < tasks.Length; taskIndex++)
5460 Task task = tasks[taskIndex];
5464 throw new ArgumentException(Environment.GetResourceString("Task_WaitMulti_NullTask"), "tasks");
5467 if (signaledTaskIndex == -1 && task.IsCompleted)
5469 // We found our first completed task. Store it, but we can't just return here,
5470 // as we still need to validate the whole array for nulls.
5471 signaledTaskIndex = taskIndex;
5475 if (signaledTaskIndex == -1 && tasks.Length != 0)
5477 Task<Task> firstCompleted = TaskFactory.CommonCWAnyLogic(tasks);
5478 bool waitCompleted = firstCompleted.Wait(millisecondsTimeout, cancellationToken);
5481 Contract.Assert(firstCompleted.Status == TaskStatus.RanToCompletion);
5482 signaledTaskIndex = Array.IndexOf(tasks, firstCompleted.Result);
5483 Contract.Assert(signaledTaskIndex >= 0);
5487 // We need to prevent the tasks array from being GC'ed until we come out of the wait.
5488 // This is necessary so that the Parallel Debugger can traverse it during the long wait
5489 // and deduce waiter/waitee relationships
5490 GC.KeepAlive(tasks);
5493 return signaledTaskIndex;
5496 #region FromResult / FromException / FromCancellation
5498 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed successfully with the specified result.</summary>
5499 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5500 /// <param name="result">The result to store into the completed task.</param>
5501 /// <returns>The successfully completed task.</returns>
5502 public static Task<TResult> FromResult<TResult>(TResult result)
5504 return new Task<TResult>(result);
5507 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed exceptionally with the specified exception.</summary>
5508 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5509 /// <param name="exception">The exception with which to complete the task.</param>
5510 /// <returns>The faulted task.</returns>
5511 public static Task FromException(Exception exception)
5513 return FromException<VoidTaskResult>(exception);
5516 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed exceptionally with the specified exception.</summary>
5517 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5518 /// <param name="exception">The exception with which to complete the task.</param>
5519 /// <returns>The faulted task.</returns>
5520 public static Task<TResult> FromException<TResult>(Exception exception)
5522 if (exception == null) throw new ArgumentNullException("exception");
5523 Contract.EndContractBlock();
5525 var task = new Task<TResult>();
5526 bool succeeded = task.TrySetException(exception);
5527 Contract.Assert(succeeded, "This should always succeed on a new task.");
5531 /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified token.</summary>
5532 /// <param name="cancellationToken">The token with which to complete the task.</param>
5533 /// <returns>The canceled task.</returns>
5534 [FriendAccessAllowed]
5535 internal static Task FromCancellation(CancellationToken cancellationToken)
5537 if (!cancellationToken.IsCancellationRequested) throw new ArgumentOutOfRangeException("cancellationToken");
5538 Contract.EndContractBlock();
5539 return new Task(true, TaskCreationOptions.None, cancellationToken);
5543 public static Task FromCanceled(CancellationToken cancellationToken)
5545 return FromCancellation(cancellationToken);
5549 /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified token.</summary>
5550 /// <param name="cancellationToken">The token with which to complete the task.</param>
5551 /// <returns>The canceled task.</returns>
5552 public static Task FromCanceled(CancellationToken cancellationToken)
5554 return FromCancellation(cancellationToken);
5557 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified token.</summary>
5558 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5559 /// <param name="cancellationToken">The token with which to complete the task.</param>
5560 /// <returns>The canceled task.</returns>
5561 [FriendAccessAllowed]
5562 internal static Task<TResult> FromCancellation<TResult>(CancellationToken cancellationToken)
5564 if (!cancellationToken.IsCancellationRequested) throw new ArgumentOutOfRangeException("cancellationToken");
5565 Contract.EndContractBlock();
5566 return new Task<TResult>(true, default(TResult), TaskCreationOptions.None, cancellationToken);
5570 public static Task<TResult> FromCanceled<TResult>(CancellationToken cancellationToken)
5572 return FromCancellation<TResult>(cancellationToken);
5576 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified token.</summary>
5577 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5578 /// <param name="cancellationToken">The token with which to complete the task.</param>
5579 /// <returns>The canceled task.</returns>
5580 public static Task<TResult> FromCanceled<TResult>(CancellationToken cancellationToken)
5582 return FromCancellation<TResult>(cancellationToken);
5585 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified exception.</summary>
5586 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5587 /// <param name="exception">The exception with which to complete the task.</param>
5588 /// <returns>The canceled task.</returns>
5589 internal static Task<TResult> FromCancellation<TResult>(OperationCanceledException exception)
5591 if (exception == null) throw new ArgumentNullException("exception");
5592 Contract.EndContractBlock();
5594 var task = new Task<TResult>();
5595 bool succeeded = task.TrySetCanceled(exception.CancellationToken, exception);
5596 Contract.Assert(succeeded, "This should always succeed on a new task.");
5605 /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work.
5607 /// <param name="action">The work to execute asynchronously</param>
5608 /// <returns>A Task that represents the work queued to execute in the ThreadPool.</returns>
5609 /// <exception cref="T:System.ArgumentNullException">
5610 /// The <paramref name="action"/> parameter was null.
5612 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
5613 public static Task Run(Action action)
5615 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
5616 return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default,
5617 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
5621 /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work.
5623 /// <param name="action">The work to execute asynchronously</param>
5624 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5625 /// <returns>A Task that represents the work queued to execute in the ThreadPool.</returns>
5626 /// <exception cref="T:System.ArgumentNullException">
5627 /// The <paramref name="action"/> parameter was null.
5629 /// <exception cref="T:System.ObjectDisposedException">
5630 /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
5632 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
5633 public static Task Run(Action action, CancellationToken cancellationToken)
5635 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
5636 return Task.InternalStartNew(null, action, null, cancellationToken, TaskScheduler.Default,
5637 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
5641 /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work.
5643 /// <param name="function">The work to execute asynchronously</param>
5644 /// <returns>A Task(TResult) that represents the work queued to execute in the ThreadPool.</returns>
5645 /// <exception cref="T:System.ArgumentNullException">
5646 /// The <paramref name="function"/> parameter was null.
5648 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
5649 public static Task<TResult> Run<TResult>(Func<TResult> function)
5651 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
5652 return Task<TResult>.StartNew(null, function, default(CancellationToken),
5653 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default, ref stackMark);
5657 /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work.
5659 /// <param name="function">The work to execute asynchronously</param>
5660 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5661 /// <returns>A Task(TResult) that represents the work queued to execute in the ThreadPool.</returns>
5662 /// <exception cref="T:System.ArgumentNullException">
5663 /// The <paramref name="function"/> parameter was null.
5665 /// <exception cref="T:System.ObjectDisposedException">
5666 /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
5668 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
5669 public static Task<TResult> Run<TResult>(Func<TResult> function, CancellationToken cancellationToken)
5671 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
5672 return Task<TResult>.StartNew(null, function, cancellationToken,
5673 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default, ref stackMark);
5677 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5678 /// Task returned by <paramref name="function"/>.
5680 /// <param name="function">The work to execute asynchronously</param>
5681 /// <returns>A Task that represents a proxy for the Task returned by <paramref name="function"/>.</returns>
5682 /// <exception cref="T:System.ArgumentNullException">
5683 /// The <paramref name="function"/> parameter was null.
5685 public static Task Run(Func<Task> function)
5687 return Run(function, default(CancellationToken));
5692 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5693 /// Task returned by <paramref name="function"/>.
5695 /// <param name="function">The work to execute asynchronously</param>
5696 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5697 /// <returns>A Task that represents a proxy for the Task returned by <paramref name="function"/>.</returns>
5698 /// <exception cref="T:System.ArgumentNullException">
5699 /// The <paramref name="function"/> parameter was null.
5701 /// <exception cref="T:System.ObjectDisposedException">
5702 /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
5704 public static Task Run(Func<Task> function, CancellationToken cancellationToken)
5707 if (function == null) throw new ArgumentNullException("function");
5708 Contract.EndContractBlock();
5710 if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
5712 cancellationToken.ThrowIfSourceDisposed();
5715 // Short-circuit if we are given a pre-canceled token
5716 if (cancellationToken.IsCancellationRequested)
5717 return Task.FromCancellation(cancellationToken);
5719 // Kick off initial Task, which will call the user-supplied function and yield a Task.
5720 Task<Task> task1 = Task<Task>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
5722 // Create a promise-style Task to be used as a proxy for the operation
5723 // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation.
5724 UnwrapPromise<VoidTaskResult> promise = new UnwrapPromise<VoidTaskResult>(task1, lookForOce: true);
5730 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5731 /// Task(TResult) returned by <paramref name="function"/>.
5733 /// <typeparam name="TResult">The type of the result returned by the proxy Task.</typeparam>
5734 /// <param name="function">The work to execute asynchronously</param>
5735 /// <returns>A Task(TResult) that represents a proxy for the Task(TResult) returned by <paramref name="function"/>.</returns>
5736 /// <exception cref="T:System.ArgumentNullException">
5737 /// The <paramref name="function"/> parameter was null.
5739 public static Task<TResult> Run<TResult>(Func<Task<TResult>> function)
5741 return Run(function, default(CancellationToken));
5745 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5746 /// Task(TResult) returned by <paramref name="function"/>.
5748 /// <typeparam name="TResult">The type of the result returned by the proxy Task.</typeparam>
5749 /// <param name="function">The work to execute asynchronously</param>
5750 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5751 /// <returns>A Task(TResult) that represents a proxy for the Task(TResult) returned by <paramref name="function"/>.</returns>
5752 /// <exception cref="T:System.ArgumentNullException">
5753 /// The <paramref name="function"/> parameter was null.
5755 public static Task<TResult> Run<TResult>(Func<Task<TResult>> function, CancellationToken cancellationToken)
5758 if (function == null) throw new ArgumentNullException("function");
5759 Contract.EndContractBlock();
5761 if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
5763 cancellationToken.ThrowIfSourceDisposed();
5766 // Short-circuit if we are given a pre-canceled token
5767 if (cancellationToken.IsCancellationRequested)
5768 return Task.FromCancellation<TResult>(cancellationToken);
5770 // Kick off initial Task, which will call the user-supplied function and yield a Task.
5771 Task<Task<TResult>> task1 = Task<Task<TResult>>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
5773 // Create a promise-style Task to be used as a proxy for the operation
5774 // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation.
5775 UnwrapPromise<TResult> promise = new UnwrapPromise<TResult>(task1, lookForOce: true);
5783 #region Delay methods
5786 /// Creates a Task that will complete after a time delay.
5788 /// <param name="delay">The time span to wait before completing the returned Task</param>
5789 /// <returns>A Task that represents the time delay</returns>
5790 /// <exception cref="T:System.ArgumentOutOfRangeException">
5791 /// The <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
5794 /// After the specified time delay, the Task is completed in RanToCompletion state.
5796 public static Task Delay(TimeSpan delay)
5798 return Delay(delay, default(CancellationToken));
5802 /// Creates a Task that will complete after a time delay.
5804 /// <param name="delay">The time span to wait before completing the returned Task</param>
5805 /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned Task</param>
5806 /// <returns>A Task that represents the time delay</returns>
5807 /// <exception cref="T:System.ArgumentOutOfRangeException">
5808 /// The <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
5810 /// <exception cref="T:System.ObjectDisposedException">
5811 /// The provided <paramref name="cancellationToken"/> has already been disposed.
5814 /// If the cancellation token is signaled before the specified time delay, then the Task is completed in
5815 /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time
5816 /// delay has expired.
5818 public static Task Delay(TimeSpan delay, CancellationToken cancellationToken)
5820 long totalMilliseconds = (long)delay.TotalMilliseconds;
5821 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
5823 throw new ArgumentOutOfRangeException("delay", Environment.GetResourceString("Task_Delay_InvalidDelay"));
5826 return Delay((int)totalMilliseconds, cancellationToken);
5830 /// Creates a Task that will complete after a time delay.
5832 /// <param name="millisecondsDelay">The number of milliseconds to wait before completing the returned Task</param>
5833 /// <returns>A Task that represents the time delay</returns>
5834 /// <exception cref="T:System.ArgumentOutOfRangeException">
5835 /// The <paramref name="millisecondsDelay"/> is less than -1.
5838 /// After the specified time delay, the Task is completed in RanToCompletion state.
5840 public static Task Delay(int millisecondsDelay)
5842 return Delay(millisecondsDelay, default(CancellationToken));
5846 /// Creates a Task that will complete after a time delay.
5848 /// <param name="millisecondsDelay">The number of milliseconds to wait before completing the returned Task</param>
5849 /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned Task</param>
5850 /// <returns>A Task that represents the time delay</returns>
5851 /// <exception cref="T:System.ArgumentOutOfRangeException">
5852 /// The <paramref name="millisecondsDelay"/> is less than -1.
5854 /// <exception cref="T:System.ObjectDisposedException">
5855 /// The provided <paramref name="cancellationToken"/> has already been disposed.
5858 /// If the cancellation token is signaled before the specified time delay, then the Task is completed in
5859 /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time
5860 /// delay has expired.
5862 public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken)
5864 // Throw on non-sensical time
5865 if (millisecondsDelay < -1)
5867 throw new ArgumentOutOfRangeException("millisecondsDelay", Environment.GetResourceString("Task_Delay_InvalidMillisecondsDelay"));
5869 Contract.EndContractBlock();
5871 // some short-cuts in case quick completion is in order
5872 if (cancellationToken.IsCancellationRequested)
5874 // return a Task created as already-Canceled
5875 return Task.FromCancellation(cancellationToken);
5877 else if (millisecondsDelay == 0)
5879 // return a Task created as already-RanToCompletion
5880 return Task.CompletedTask;
5883 // Construct a promise-style Task to encapsulate our return value
5884 var promise = new DelayPromise(cancellationToken);
5886 // Register our cancellation token, if necessary.
5887 if (cancellationToken.CanBeCanceled)
5889 promise.Registration = cancellationToken.InternalRegisterWithoutEC(state => ((DelayPromise)state).Complete(), promise);
5892 // ... and create our timer and make sure that it stays rooted.
5893 if (millisecondsDelay != Timeout.Infinite) // no need to create the timer if it's an infinite timeout
5895 promise.Timer = new Timer(state => ((DelayPromise)state).Complete(), promise, millisecondsDelay, Timeout.Infinite);
5896 promise.Timer.KeepRootedWhileScheduled();
5899 // Return the timer proxy task
5903 /// <summary>Task that also stores the completion closure and logic for Task.Delay implementation.</summary>
5904 private sealed class DelayPromise : Task<VoidTaskResult>
5906 internal DelayPromise(CancellationToken token)
5910 if (AsyncCausalityTracer.LoggingOn)
5911 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.Delay", 0);
5913 if (Task.s_asyncDebuggingEnabled)
5915 AddToActiveTasks(this);
5919 internal readonly CancellationToken Token;
5920 internal CancellationTokenRegistration Registration;
5921 internal Timer Timer;
5923 internal void Complete()
5925 // Transition the task to completed.
5928 if (Token.IsCancellationRequested)
5930 setSucceeded = TrySetCanceled(Token);
5934 if (AsyncCausalityTracer.LoggingOn)
5935 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
5937 if (Task.s_asyncDebuggingEnabled)
5939 RemoveFromActiveTasks(this.Id);
5941 setSucceeded = TrySetResult(default(VoidTaskResult));
5944 // If we won the ----, also clean up.
5947 if (Timer != null) Timer.Dispose();
5948 Registration.Dispose();
5956 /// Creates a task that will complete when all of the supplied tasks have completed.
5958 /// <param name="tasks">The tasks to wait on for completion.</param>
5959 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
5962 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
5963 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
5966 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
5969 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
5972 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
5973 /// state before it's returned to the caller.
5976 /// <exception cref="T:System.ArgumentNullException">
5977 /// The <paramref name="tasks"/> argument was null.
5979 /// <exception cref="T:System.ArgumentException">
5980 /// The <paramref name="tasks"/> collection contained a null task.
5982 public static Task WhenAll(IEnumerable<Task> tasks)
5984 // Take a more efficient path if tasks is actually an array
5985 Task[] taskArray = tasks as Task[];
5986 if (taskArray != null)
5988 return WhenAll(taskArray);
5991 // Skip a List allocation/copy if tasks is a collection
5992 ICollection<Task> taskCollection = tasks as ICollection<Task>;
5993 if (taskCollection != null)
5996 taskArray = new Task[taskCollection.Count];
5997 foreach (var task in tasks)
5999 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6000 taskArray[index++] = task;
6002 return InternalWhenAll(taskArray);
6005 // Do some argument checking and convert tasks to a List (and later an array).
6006 if (tasks == null) throw new ArgumentNullException("tasks");
6007 List<Task> taskList = new List<Task>();
6008 foreach (Task task in tasks)
6010 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6014 // Delegate the rest to InternalWhenAll()
6015 return InternalWhenAll(taskList.ToArray());
6019 /// Creates a task that will complete when all of the supplied tasks have completed.
6021 /// <param name="tasks">The tasks to wait on for completion.</param>
6022 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
6025 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
6026 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
6029 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
6032 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
6035 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
6036 /// state before it's returned to the caller.
6039 /// <exception cref="T:System.ArgumentNullException">
6040 /// The <paramref name="tasks"/> argument was null.
6042 /// <exception cref="T:System.ArgumentException">
6043 /// The <paramref name="tasks"/> array contained a null task.
6045 public static Task WhenAll(params Task[] tasks)
6047 // Do some argument checking and make a defensive copy of the tasks array
6048 if (tasks == null) throw new ArgumentNullException("tasks");
6049 Contract.EndContractBlock();
6051 int taskCount = tasks.Length;
6052 if (taskCount == 0) return InternalWhenAll(tasks); // Small optimization in the case of an empty array.
6054 Task[] tasksCopy = new Task[taskCount];
6055 for (int i = 0; i < taskCount; i++)
6057 Task task = tasks[i];
6058 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6059 tasksCopy[i] = task;
6062 // The rest can be delegated to InternalWhenAll()
6063 return InternalWhenAll(tasksCopy);
6066 // Some common logic to support WhenAll() methods
6067 // tasks should be a defensive copy.
6068 private static Task InternalWhenAll(Task[] tasks)
6070 Contract.Requires(tasks != null, "Expected a non-null tasks array");
6071 return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
6072 Task.CompletedTask :
6073 new WhenAllPromise(tasks);
6076 // A Task<VoidTaskResult> that gets completed when all of its constituent tasks complete.
6077 // Completion logic will analyze the antecedents in order to choose completion status.
6078 // This type allows us to replace this logic:
6079 // Task<VoidTaskResult> promise = new Task<VoidTaskResult>(...);
6080 // Action<Task> completionAction = delegate { <completion logic>};
6081 // TaskFactory.CommonCWAllLogic(tasksCopy).AddCompletionAction(completionAction);
6083 // which involves several allocations, with this logic:
6084 // return new WhenAllPromise(tasksCopy);
6085 // which saves a couple of allocations and enables debugger notification specialization.
6087 // Used in InternalWhenAll(Task[])
6088 private sealed class WhenAllPromise : Task<VoidTaskResult>, ITaskCompletionAction
6091 /// Stores all of the constituent tasks. Tasks clear themselves out of this
6092 /// array as they complete, but only if they don't have their wait notification bit set.
6094 private readonly Task[] m_tasks;
6095 /// <summary>The number of tasks remaining to complete.</summary>
6096 private int m_count;
6098 internal WhenAllPromise(Task[] tasks) :
6101 Contract.Requires(tasks != null, "Expected a non-null task array");
6102 Contract.Requires(tasks.Length > 0, "Expected a non-zero length task array");
6104 if (AsyncCausalityTracer.LoggingOn)
6105 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.WhenAll", 0);
6107 if (s_asyncDebuggingEnabled)
6109 AddToActiveTasks(this);
6113 m_count = tasks.Length;
6115 foreach (var task in tasks)
6117 if (task.IsCompleted) this.Invoke(task); // short-circuit the completion action, if possible
6118 else task.AddCompletionAction(this); // simple completion action
6122 public void Invoke(Task completedTask)
6124 if (AsyncCausalityTracer.LoggingOn)
6125 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
6127 // Decrement the count, and only continue to complete the promise if we're the last one.
6128 if (Interlocked.Decrement(ref m_count) == 0)
6130 // Set up some accounting variables
6131 List<ExceptionDispatchInfo> observedExceptions = null;
6132 Task canceledTask = null;
6134 // Loop through antecedents:
6135 // If any one of them faults, the result will be faulted
6136 // If none fault, but at least one is canceled, the result will be canceled
6137 // If none fault or are canceled, then result will be RanToCompletion
6138 for (int i = 0; i < m_tasks.Length; i++)
6140 var task = m_tasks[i];
6141 Contract.Assert(task != null, "Constituent task in WhenAll should never be null");
6145 if (observedExceptions == null) observedExceptions = new List<ExceptionDispatchInfo>();
6146 observedExceptions.AddRange(task.GetExceptionDispatchInfos());
6148 else if (task.IsCanceled)
6150 if (canceledTask == null) canceledTask = task; // use the first task that's canceled
6153 // Regardless of completion state, if the task has its debug bit set, transfer it to the
6154 // WhenAll task. We must do this before we complete the task.
6155 if (task.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
6156 else m_tasks[i] = null; // avoid holding onto tasks unnecessarily
6159 if (observedExceptions != null)
6161 Contract.Assert(observedExceptions.Count > 0, "Expected at least one exception");
6163 //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there
6165 TrySetException(observedExceptions);
6167 else if (canceledTask != null)
6169 TrySetCanceled(canceledTask.CancellationToken, canceledTask.GetCancellationExceptionDispatchInfo());
6173 if (AsyncCausalityTracer.LoggingOn)
6174 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
6176 if (Task.s_asyncDebuggingEnabled)
6178 RemoveFromActiveTasks(this.Id);
6180 TrySetResult(default(VoidTaskResult));
6183 Contract.Assert(m_count >= 0, "Count should never go below 0");
6187 /// Returns whether we should notify the debugger of a wait completion. This returns
6188 /// true iff at least one constituent task has its bit set.
6190 internal override bool ShouldNotifyDebuggerOfWaitCompletion
6195 base.ShouldNotifyDebuggerOfWaitCompletion &&
6196 Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks);
6202 /// Creates a task that will complete when all of the supplied tasks have completed.
6204 /// <param name="tasks">The tasks to wait on for completion.</param>
6205 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
6208 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
6209 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
6212 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
6215 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
6216 /// The Result of the returned task will be set to an array containing all of the results of the
6217 /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output
6218 /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result).
6221 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
6222 /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements.
6225 /// <exception cref="T:System.ArgumentNullException">
6226 /// The <paramref name="tasks"/> argument was null.
6228 /// <exception cref="T:System.ArgumentException">
6229 /// The <paramref name="tasks"/> collection contained a null task.
6231 public static Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks)
6233 // Take a more efficient route if tasks is actually an array
6234 Task<TResult>[] taskArray = tasks as Task<TResult>[];
6235 if (taskArray != null)
6237 return WhenAll<TResult>(taskArray);
6240 // Skip a List allocation/copy if tasks is a collection
6241 ICollection<Task<TResult>> taskCollection = tasks as ICollection<Task<TResult>>;
6242 if (taskCollection != null)
6245 taskArray = new Task<TResult>[taskCollection.Count];
6246 foreach (var task in tasks)
6248 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6249 taskArray[index++] = task;
6251 return InternalWhenAll<TResult>(taskArray);
6254 // Do some argument checking and convert tasks into a List (later an array)
6255 if (tasks == null) throw new ArgumentNullException("tasks");
6256 List<Task<TResult>> taskList = new List<Task<TResult>>();
6257 foreach (Task<TResult> task in tasks)
6259 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6263 // Delegate the rest to InternalWhenAll<TResult>().
6264 return InternalWhenAll<TResult>(taskList.ToArray());
6268 /// Creates a task that will complete when all of the supplied tasks have completed.
6270 /// <param name="tasks">The tasks to wait on for completion.</param>
6271 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
6274 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
6275 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
6278 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
6281 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
6282 /// The Result of the returned task will be set to an array containing all of the results of the
6283 /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output
6284 /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result).
6287 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
6288 /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements.
6291 /// <exception cref="T:System.ArgumentNullException">
6292 /// The <paramref name="tasks"/> argument was null.
6294 /// <exception cref="T:System.ArgumentException">
6295 /// The <paramref name="tasks"/> array contained a null task.
6297 public static Task<TResult[]> WhenAll<TResult>(params Task<TResult>[] tasks)
6299 // Do some argument checking and make a defensive copy of the tasks array
6300 if (tasks == null) throw new ArgumentNullException("tasks");
6301 Contract.EndContractBlock();
6303 int taskCount = tasks.Length;
6304 if (taskCount == 0) return InternalWhenAll<TResult>(tasks); // small optimization in the case of an empty task array
6306 Task<TResult>[] tasksCopy = new Task<TResult>[taskCount];
6307 for (int i = 0; i < taskCount; i++)
6309 Task<TResult> task = tasks[i];
6310 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6311 tasksCopy[i] = task;
6314 // Delegate the rest to InternalWhenAll<TResult>()
6315 return InternalWhenAll<TResult>(tasksCopy);
6318 // Some common logic to support WhenAll<TResult> methods
6319 private static Task<TResult[]> InternalWhenAll<TResult>(Task<TResult>[] tasks)
6321 Contract.Requires(tasks != null, "Expected a non-null tasks array");
6322 return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
6323 new Task<TResult[]>(false, new TResult[0], TaskCreationOptions.None, default(CancellationToken)) :
6324 new WhenAllPromise<TResult>(tasks);
6327 // A Task<T> that gets completed when all of its constituent tasks complete.
6328 // Completion logic will analyze the antecedents in order to choose completion status.
6329 // See comments for non-generic version of WhenAllPromise class.
6331 // Used in InternalWhenAll<TResult>(Task<TResult>[])
6332 private sealed class WhenAllPromise<T> : Task<T[]>, ITaskCompletionAction
6335 /// Stores all of the constituent tasks. Tasks clear themselves out of this
6336 /// array as they complete, but only if they don't have their wait notification bit set.
6338 private readonly Task<T>[] m_tasks;
6339 /// <summary>The number of tasks remaining to complete.</summary>
6340 private int m_count;
6342 internal WhenAllPromise(Task<T>[] tasks) :
6345 Contract.Requires(tasks != null, "Expected a non-null task array");
6346 Contract.Requires(tasks.Length > 0, "Expected a non-zero length task array");
6349 m_count = tasks.Length;
6351 if (AsyncCausalityTracer.LoggingOn)
6352 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.WhenAll", 0);
6354 if (s_asyncDebuggingEnabled)
6356 AddToActiveTasks(this);
6359 foreach (var task in tasks)
6361 if (task.IsCompleted) this.Invoke(task); // short-circuit the completion action, if possible
6362 else task.AddCompletionAction(this); // simple completion action
6366 public void Invoke(Task ignored)
6368 if (AsyncCausalityTracer.LoggingOn)
6369 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
6371 // Decrement the count, and only continue to complete the promise if we're the last one.
6372 if (Interlocked.Decrement(ref m_count) == 0)
6374 // Set up some accounting variables
6375 T[] results = new T[m_tasks.Length];
6376 List<ExceptionDispatchInfo> observedExceptions = null;
6377 Task canceledTask = null;
6379 // Loop through antecedents:
6380 // If any one of them faults, the result will be faulted
6381 // If none fault, but at least one is canceled, the result will be canceled
6382 // If none fault or are canceled, then result will be RanToCompletion
6383 for (int i = 0; i < m_tasks.Length; i++)
6385 Task<T> task = m_tasks[i];
6386 Contract.Assert(task != null, "Constituent task in WhenAll should never be null");
6390 if (observedExceptions == null) observedExceptions = new List<ExceptionDispatchInfo>();
6391 observedExceptions.AddRange(task.GetExceptionDispatchInfos());
6393 else if (task.IsCanceled)
6395 if (canceledTask == null) canceledTask = task; // use the first task that's canceled
6399 Contract.Assert(task.Status == TaskStatus.RanToCompletion);
6400 results[i] = task.GetResultCore(waitCompletionNotification: false); // avoid Result, which would triggering debug notification
6403 // Regardless of completion state, if the task has its debug bit set, transfer it to the
6404 // WhenAll task. We must do this before we complete the task.
6405 if (task.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
6406 else m_tasks[i] = null; // avoid holding onto tasks unnecessarily
6409 if (observedExceptions != null)
6411 Contract.Assert(observedExceptions.Count > 0, "Expected at least one exception");
6413 //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there
6415 TrySetException(observedExceptions);
6417 else if (canceledTask != null)
6419 TrySetCanceled(canceledTask.CancellationToken, canceledTask.GetCancellationExceptionDispatchInfo());
6423 if (AsyncCausalityTracer.LoggingOn)
6424 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
6426 if (Task.s_asyncDebuggingEnabled)
6428 RemoveFromActiveTasks(this.Id);
6430 TrySetResult(results);
6433 Contract.Assert(m_count >= 0, "Count should never go below 0");
6437 /// Returns whether we should notify the debugger of a wait completion. This returns true
6438 /// iff at least one constituent task has its bit set.
6440 internal override bool ShouldNotifyDebuggerOfWaitCompletion
6445 base.ShouldNotifyDebuggerOfWaitCompletion &&
6446 Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks);
6454 /// Creates a task that will complete when any of the supplied tasks have completed.
6456 /// <param name="tasks">The tasks to wait on for completion.</param>
6457 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6459 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6460 /// 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.
6462 /// <exception cref="T:System.ArgumentNullException">
6463 /// The <paramref name="tasks"/> argument was null.
6465 /// <exception cref="T:System.ArgumentException">
6466 /// The <paramref name="tasks"/> array contained a null task, or was empty.
6468 public static Task<Task> WhenAny(params Task[] tasks)
6470 if (tasks == null) throw new ArgumentNullException("tasks");
6471 if (tasks.Length == 0)
6473 throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), "tasks");
6475 Contract.EndContractBlock();
6477 // Make a defensive copy, as the user may manipulate the tasks array
6478 // after we return but before the WhenAny asynchronously completes.
6479 int taskCount = tasks.Length;
6480 Task[] tasksCopy = new Task[taskCount];
6481 for (int i = 0; i < taskCount; i++)
6483 Task task = tasks[i];
6484 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6485 tasksCopy[i] = task;
6488 // Previously implemented CommonCWAnyLogic() can handle the rest
6489 return TaskFactory.CommonCWAnyLogic(tasksCopy);
6493 /// Creates a task that will complete when any of the supplied tasks have completed.
6495 /// <param name="tasks">The tasks to wait on for completion.</param>
6496 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6498 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6499 /// 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.
6501 /// <exception cref="T:System.ArgumentNullException">
6502 /// The <paramref name="tasks"/> argument was null.
6504 /// <exception cref="T:System.ArgumentException">
6505 /// The <paramref name="tasks"/> collection contained a null task, or was empty.
6507 public static Task<Task> WhenAny(IEnumerable<Task> tasks)
6509 if (tasks == null) throw new ArgumentNullException("tasks");
6510 Contract.EndContractBlock();
6512 // Make a defensive copy, as the user may manipulate the tasks collection
6513 // after we return but before the WhenAny asynchronously completes.
6514 List<Task> taskList = new List<Task>();
6515 foreach (Task task in tasks)
6517 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6521 if (taskList.Count == 0)
6523 throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), "tasks");
6526 // Previously implemented CommonCWAnyLogic() can handle the rest
6527 return TaskFactory.CommonCWAnyLogic(taskList);
6531 /// Creates a task that will complete when any of the supplied tasks have completed.
6533 /// <param name="tasks">The tasks to wait on for completion.</param>
6534 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6536 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6537 /// 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.
6539 /// <exception cref="T:System.ArgumentNullException">
6540 /// The <paramref name="tasks"/> argument was null.
6542 /// <exception cref="T:System.ArgumentException">
6543 /// The <paramref name="tasks"/> array contained a null task, or was empty.
6545 public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks)
6547 // We would just like to do this:
6548 // return (Task<Task<TResult>>) WhenAny( (Task[]) tasks);
6549 // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>.
6551 // Call WhenAny(Task[]) for basic functionality
6552 Task<Task> intermediate = WhenAny((Task[])tasks);
6554 // Return a continuation task with the correct result type
6555 return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default(CancellationToken),
6556 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
6560 /// Creates a task that will complete when any of the supplied tasks have completed.
6562 /// <param name="tasks">The tasks to wait on for completion.</param>
6563 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6565 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6566 /// 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.
6568 /// <exception cref="T:System.ArgumentNullException">
6569 /// The <paramref name="tasks"/> argument was null.
6571 /// <exception cref="T:System.ArgumentException">
6572 /// The <paramref name="tasks"/> collection contained a null task, or was empty.
6574 public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks)
6576 // We would just like to do this:
6577 // return (Task<Task<TResult>>) WhenAny( (IEnumerable<Task>) tasks);
6578 // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>.
6580 // Call WhenAny(IEnumerable<Task>) for basic functionality
6581 Task<Task> intermediate = WhenAny((IEnumerable<Task>)tasks);
6583 // Return a continuation task with the correct result type
6584 return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default(CancellationToken),
6585 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
6589 [FriendAccessAllowed]
6590 internal static Task<TResult> CreateUnwrapPromise<TResult>(Task outerTask, bool lookForOce)
6592 Contract.Requires(outerTask != null);
6594 return new UnwrapPromise<TResult>(outerTask, lookForOce);
6597 internal virtual Delegate[] GetDelegateContinuationsForDebugger()
6599 //Avoid an infinite loop by making sure the continuation object is not a reference to istelf.
6600 if (this.m_continuationObject != this)
6601 return GetDelegatesFromContinuationObject(this.m_continuationObject);
6606 internal static Delegate[] GetDelegatesFromContinuationObject(object continuationObject)
6608 if (continuationObject != null)
6610 Action singleAction = continuationObject as Action;
6611 if (singleAction != null)
6613 return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(singleAction) };
6616 TaskContinuation taskContinuation = continuationObject as TaskContinuation;
6617 if (taskContinuation != null)
6619 return taskContinuation.GetDelegateContinuationsForDebugger();
6622 Task continuationTask = continuationObject as Task;
6623 if (continuationTask != null)
6625 Contract.Assert(continuationTask.m_action == null);
6626 Delegate[] delegates = continuationTask.GetDelegateContinuationsForDebugger();
6627 if (delegates != null)
6631 //We need this ITaskCompletionAction after the Task because in the case of UnwrapPromise
6632 //the VS debugger is more interested in the continuation than the internal invoke()
6633 ITaskCompletionAction singleCompletionAction = continuationObject as ITaskCompletionAction;
6634 if (singleCompletionAction != null)
6636 return new Delegate[] { new Action<Task>(singleCompletionAction.Invoke) };
6639 List<object> continuationList = continuationObject as List<object>;
6640 if (continuationList != null)
6642 List<Delegate> result = new List<Delegate>();
6643 foreach (object obj in continuationList)
6645 var innerDelegates = GetDelegatesFromContinuationObject(obj);
6646 if (innerDelegates != null)
6648 foreach (var del in innerDelegates)
6656 return result.ToArray();
6663 private static Task GetActiveTaskFromId(int taskId)
6666 s_currentActiveTasks.TryGetValue(taskId, out task);
6670 private static Task[] GetActiveTasks()
6673 return new List<Task>(s_currentActiveTasks.Values).ToArray();
6677 // Proxy class for better debugging experience
6678 internal class SystemThreadingTasks_TaskDebugView
6680 private Task m_task;
6682 public SystemThreadingTasks_TaskDebugView(Task task)
6687 public object AsyncState { get { return m_task.AsyncState; } }
6688 public TaskCreationOptions CreationOptions { get { return m_task.CreationOptions; } }
6689 public Exception Exception { get { return m_task.Exception; } }
6690 public int Id { get { return m_task.Id; } }
6691 public bool CancellationPending { get { return (m_task.Status == TaskStatus.WaitingToRun) && m_task.CancellationToken.IsCancellationRequested; } }
6692 public TaskStatus Status { get { return m_task.Status; } }
6695 // Special purpose derivation of Task that supports limited replication through
6696 // overriding the ShouldReplicate() method. This is used by the Parallel.For/ForEach
6698 internal class ParallelForReplicatingTask : Task
6701 private int m_replicationDownCount; // downcounter to control replication
6707 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
6708 internal ParallelForReplicatingTask(
6709 ParallelOptions parallelOptions, Action action, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions)
6710 : base(action, null, Task.InternalCurrent, default(CancellationToken), creationOptions, internalOptions | InternalTaskOptions.SelfReplicating, null)
6712 // Compute the down count based on scheduler/DOP info in parallelOptions.
6713 m_replicationDownCount = parallelOptions.EffectiveMaxConcurrencyLevel;
6715 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
6716 PossiblyCaptureContext(ref stackMark);
6720 // Controls degree of replication. If downcounter is initialized to -1, then
6721 // replication will be allowed to "run wild". Otherwise, this method decrements
6722 // the downcounter each time it is called, calling false when it is called with
6723 // a zero downcounter. This method returning false effectively ends the replication
6724 // of the associated ParallelForReplicatingTask.
6725 internal override bool ShouldReplicate()
6727 if (m_replicationDownCount == -1) return true; // "run wild"
6729 if (m_replicationDownCount > 0) // Decrement and return true if not called with 0 downcount
6731 m_replicationDownCount--;
6735 return false; // We're done replicating
6738 internal override Task CreateReplicaTask(Action<object> taskReplicaDelegate, Object stateObject, Task parentTask, TaskScheduler taskScheduler,
6739 TaskCreationOptions creationOptionsForReplica, InternalTaskOptions internalOptionsForReplica)
6741 return new ParallelForReplicaTask(taskReplicaDelegate, stateObject, parentTask, taskScheduler,
6742 creationOptionsForReplica, internalOptionsForReplica);
6748 internal class ParallelForReplicaTask : Task
6750 internal object m_stateForNextReplica; // some replicas may quit prematurely, in which case they will use this variable
6751 // to save state they want to be picked up by the next replica queued to the same thread
6753 internal object m_stateFromPreviousReplica; // some replicas may quit prematurely, in which case they will use this variable
6754 // to save state they want to be picked up by the next replica queued to the same thread
6756 internal Task m_handedOverChildReplica; // some replicas may quit prematurely, in which case they will use this variable
6757 // to hand over the child replica they had queued to the next task that will replace them
6759 internal ParallelForReplicaTask(Action<object> taskReplicaDelegate, Object stateObject, Task parentTask, TaskScheduler taskScheduler,
6760 TaskCreationOptions creationOptionsForReplica, InternalTaskOptions internalOptionsForReplica) :
6761 base(taskReplicaDelegate, stateObject, parentTask, default(CancellationToken), creationOptionsForReplica, internalOptionsForReplica, taskScheduler)
6765 // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
6766 internal override Object SavedStateForNextReplica
6768 get { return m_stateForNextReplica; }
6770 set { m_stateForNextReplica = value; }
6773 // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
6774 internal override Object SavedStateFromPreviousReplica
6776 get { return m_stateFromPreviousReplica; }
6778 set { m_stateFromPreviousReplica = value; }
6781 // Allows internal deriving classes to support replicas that exit prematurely and want to hand over the child replica that they
6782 // had queued, so that the replacement replica can work with that child task instead of queuing up yet another one
6783 internal override Task HandedOverChildReplica
6785 get { return m_handedOverChildReplica; }
6787 set { m_handedOverChildReplica = value; }
6792 /// Specifies flags that control optional behavior for the creation and execution of tasks.
6794 // NOTE: These options are a subset of TaskContinuationsOptions, thus before adding a flag check it is
6795 // not already in use.
6798 public enum TaskCreationOptions
6801 /// Specifies that the default behavior should be used.
6806 /// A hint to a <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> to schedule a
6807 /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to
6808 /// be run sooner, and tasks scheduled later will be more likely to be run later.
6810 PreferFairness = 0x01,
6813 /// Specifies that a task will be a long-running, course-grained operation. It provides a hint to the
6814 /// <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> that oversubscription may be
6820 /// Specifies that a task is attached to a parent in the task hierarchy.
6822 AttachedToParent = 0x04,
6825 /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task.
6827 DenyChildAttach = 0x08,
6830 /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations
6831 /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler.
6833 HideScheduler = 0x10,
6835 // 0x20 is already being used in TaskContinuationOptions
6838 /// Forces continuations added to the current task to be executed asynchronously.
6839 /// This option has precedence over TaskContinuationOptions.ExecuteSynchronously
6841 RunContinuationsAsynchronously = 0x40
6846 /// Task creation flags which are only used internally.
6850 internal enum InternalTaskOptions
6852 /// <summary> Specifies "No internal task options" </summary>
6855 /// <summary>Used to filter out internal vs. public task creation options.</summary>
6856 InternalOptionsMask = 0x0000FF00,
6858 ChildReplica = 0x0100,
6859 ContinuationTask = 0x0200,
6860 PromiseTask = 0x0400,
6861 SelfReplicating = 0x0800,
6864 /// Store the presence of TaskContinuationOptions.LazyCancellation, since it does not directly
6865 /// translate into any TaskCreationOptions.
6867 LazyCancellation = 0x1000,
6869 /// <summary>Specifies that the task will be queued by the runtime before handing it over to the user.
6870 /// This flag will be used to skip the cancellationtoken registration step, which is only meant for unstarted tasks.</summary>
6871 QueuedByRuntime = 0x2000,
6874 /// Denotes that Dispose should be a complete nop for a Task. Used when constructing tasks that are meant to be cached/reused.
6876 DoNotDispose = 0x4000
6880 /// Specifies flags that control optional behavior for the creation and execution of continuation tasks.
6884 public enum TaskContinuationOptions
6887 /// Default = "Continue on any, no task options, run asynchronously"
6888 /// Specifies that the default behavior should be used. Continuations, by default, will
6889 /// be scheduled when the antecedent task completes, regardless of the task's final <see
6890 /// cref="System.Threading.Tasks.TaskStatus">TaskStatus</see>.
6894 // These are identical to their meanings and values in TaskCreationOptions
6897 /// A hint to a <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> to schedule a
6898 /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to
6899 /// be run sooner, and tasks scheduled later will be more likely to be run later.
6901 PreferFairness = 0x01,
6904 /// Specifies that a task will be a long-running, course-grained operation. It provides
6905 /// a hint to the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> that
6906 /// oversubscription may be warranted.
6910 /// Specifies that a task is attached to a parent in the task hierarchy.
6912 AttachedToParent = 0x04,
6915 /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task.
6917 DenyChildAttach = 0x08,
6919 /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations
6920 /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler.
6922 HideScheduler = 0x10,
6925 /// In the case of continuation cancellation, prevents completion of the continuation until the antecedent has completed.
6927 LazyCancellation = 0x20,
6929 RunContinuationsAsynchronously = 0x40,
6931 // These are specific to continuations
6934 /// Specifies that the continuation task should not be scheduled if its antecedent ran to completion.
6935 /// This option is not valid for multi-task continuations.
6937 NotOnRanToCompletion = 0x10000,
6939 /// Specifies that the continuation task should not be scheduled if its antecedent threw an unhandled
6940 /// exception. This option is not valid for multi-task continuations.
6942 NotOnFaulted = 0x20000,
6944 /// Specifies that the continuation task should not be scheduled if its antecedent was canceled. This
6945 /// option is not valid for multi-task continuations.
6947 NotOnCanceled = 0x40000,
6949 /// Specifies that the continuation task should be scheduled only if its antecedent ran to
6950 /// completion. This option is not valid for multi-task continuations.
6952 OnlyOnRanToCompletion = NotOnFaulted | NotOnCanceled,
6954 /// Specifies that the continuation task should be scheduled only if its antecedent threw an
6955 /// unhandled exception. This option is not valid for multi-task continuations.
6957 OnlyOnFaulted = NotOnRanToCompletion | NotOnCanceled,
6959 /// Specifies that the continuation task should be scheduled only if its antecedent was canceled.
6960 /// This option is not valid for multi-task continuations.
6962 OnlyOnCanceled = NotOnRanToCompletion | NotOnFaulted,
6964 /// Specifies that the continuation task should be executed synchronously. With this option
6965 /// specified, the continuation will be run on the same thread that causes the antecedent task to
6966 /// transition into its final state. If the antecedent is already complete when the continuation is
6967 /// created, the continuation will run on the thread creating the continuation. Only very
6968 /// short-running continuations should be executed synchronously.
6970 ExecuteSynchronously = 0x80000
6974 /// Internal helper class to keep track of stack depth and decide whether we should inline or not.
6976 internal class StackGuard
6978 // current thread's depth of nested inline task executions
6979 private int m_inliningDepth = 0;
6981 // For relatively small inlining depths we don't want to get into the business of stack probing etc.
6982 // This clearly leaves a window of opportunity for the user code to SO. However a piece of code
6983 // that can SO in 20 inlines on a typical 1MB stack size probably needs to be revisited anyway.
6984 private const int MAX_UNCHECKED_INLINING_DEPTH = 20;
6986 #if !FEATURE_PAL && !PFX_LEGACY_3_5
6988 private UInt64 m_lastKnownWatermark;
6989 private static int s_pageSize;
6991 // We are conservative here. We assume that the platform needs a whole 64KB to
6992 // respond to stack overflow. This means that for very small stacks (e.g. 128KB)
6993 // we'll fail a lot of stack checks incorrectly.
6994 private const long STACK_RESERVED_SPACE = 4096 * 16;
6996 #endif // !FEATURE_PAL && !PFX_LEGACY_3_5
6999 /// This method needs to be called before attempting inline execution on the current thread.
7000 /// If false is returned, it means we are too close to the end of the stack and should give up inlining.
7001 /// Each call to TryBeginInliningScope() that returns true must be matched with a
7002 /// call to EndInliningScope() regardless of whether inlining actually took place.
7004 [SecuritySafeCritical]
7005 internal bool TryBeginInliningScope()
7007 // If we're still under the 'safe' limit we'll just skip the stack probe to save p/invoke calls
7008 if (m_inliningDepth < MAX_UNCHECKED_INLINING_DEPTH || CheckForSufficientStack())
7018 /// This needs to be called once for each previous successful TryBeginInliningScope() call after
7019 /// inlining related logic runs.
7021 internal void EndInliningScope()
7024 Contract.Assert(m_inliningDepth >= 0, "Inlining depth count should never go negative.");
7026 // do the right thing just in case...
7027 if (m_inliningDepth < 0) m_inliningDepth = 0;
7031 private unsafe bool CheckForSufficientStack()
7033 #if !FEATURE_PAL && !PFX_LEGACY_3_5
7034 // see if we already have the system page size info recorded
7035 int pageSize = s_pageSize;
7038 // If not we need to query it from GetSystemInfo()
7039 // Note that this happens only once for the process lifetime
7040 Win32Native.SYSTEM_INFO sysInfo = new Win32Native.SYSTEM_INFO();
7041 Win32Native.GetSystemInfo(ref sysInfo);
7043 s_pageSize = pageSize = sysInfo.dwPageSize;
7046 Win32Native.MEMORY_BASIC_INFORMATION stackInfo = new Win32Native.MEMORY_BASIC_INFORMATION();
7048 // We subtract one page for our request. VirtualQuery rounds UP to the next page.
7049 // Unfortunately, the stack grows down. If we're on the first page (last page in the
7050 // VirtualAlloc), we'll be moved to the next page, which is off the stack!
7052 UIntPtr currentAddr = new UIntPtr(&stackInfo - pageSize);
7053 UInt64 current64 = currentAddr.ToUInt64();
7055 // Check whether we previously recorded a deeper stack than where we currently are,
7056 // If so we don't need to do the P/Invoke to VirtualQuery
7057 if (m_lastKnownWatermark != 0 && current64 > m_lastKnownWatermark)
7060 // Actual stack probe. P/Invoke to query for the current stack allocation information.
7061 Win32Native.VirtualQuery(currentAddr.ToPointer(), ref stackInfo, (UIntPtr)(sizeof(Win32Native.MEMORY_BASIC_INFORMATION)));
7063 // If the current address minus the base (remember: the stack grows downward in the
7064 // address space) is greater than the number of bytes requested plus the reserved
7065 // space at the end, the request has succeeded.
7067 if ((current64 - ((UIntPtr)stackInfo.AllocationBase).ToUInt64()) > STACK_RESERVED_SPACE)
7069 m_lastKnownWatermark = current64;
7075 #else // !FEATURE_PAL && !PFX_LEGACY_3_5
7077 // if we're being compiled with FEATURE_PAL or PFX_LEGACY_3_5 we simply allow unchecked inlining.
7083 // Special internal struct that we use to signify that we are not interested in
7084 // a Task<VoidTaskResult>'s result.
7085 internal struct VoidTaskResult { }
7087 // Interface to which all completion actions must conform.
7088 // This interface allows us to combine functionality and reduce allocations.
7089 // For example, see Task.SetOnInvokeMres, and its use in Task.SpinThenBlockingWait().
7091 // ManualResetEvent mres = new ManualResetEventSlim(false, 0);
7092 // Action<Task> completionAction = delegate { mres.Set() ; };
7093 // AddCompletionAction(completionAction);
7094 // gets replaced with this:
7095 // SetOnInvokeMres mres = new SetOnInvokeMres();
7096 // AddCompletionAction(mres);
7097 // For additional examples of where this is used, see internal classes Task.SignalOnInvokeCDE,
7098 // Task.WhenAllPromise, Task.WhenAllPromise<T>, TaskFactory.CompleteOnCountdownPromise,
7099 // TaskFactory.CompleteOnCountdownPromise<T>, and TaskFactory.CompleteOnInvokePromise.
7100 internal interface ITaskCompletionAction
7102 void Invoke(Task completingTask);
7105 // This class encapsulates all "unwrap" logic, and also implements ITaskCompletionAction,
7106 // which minimizes the allocations needed for queuing it to its antecedent. This
7107 // logic is used by both the Unwrap extension methods and the unwrap-style Task.Run methods.
7108 internal sealed class UnwrapPromise<TResult> : Task<TResult>, ITaskCompletionAction
7110 // The possible states for our UnwrapPromise, used by Invoke() to determine which logic to execute
7111 private const byte STATE_WAITING_ON_OUTER_TASK = 0; // Invoke() means "process completed outer task"
7112 private const byte STATE_WAITING_ON_INNER_TASK = 1; // Invoke() means "process completed inner task"
7113 private const byte STATE_DONE = 2; // Invoke() means "something went wrong and we are hosed!"
7115 // Keep track of our state; initialized to STATE_WAITING_ON_OUTER_TASK in the constructor
7116 private byte _state;
7118 // "Should we check for OperationCanceledExceptions on the outer task and interpret them as proxy cancellation?"
7119 // Unwrap() sets this to false, Run() sets it to true.
7120 private readonly bool _lookForOce;
7122 public UnwrapPromise(Task outerTask, bool lookForOce)
7123 : base((object)null, outerTask.CreationOptions & TaskCreationOptions.AttachedToParent)
7125 Contract.Requires(outerTask != null, "Expected non-null outerTask");
7126 _lookForOce = lookForOce;
7127 _state = STATE_WAITING_ON_OUTER_TASK;
7129 if (AsyncCausalityTracer.LoggingOn)
7130 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.Unwrap", 0);
7132 if (Task.s_asyncDebuggingEnabled)
7134 AddToActiveTasks(this);
7137 // Link ourselves to the outer task.
7138 // If the outer task has already completed, take the fast path
7139 // of immediately transferring its results or processing the inner task.
7140 if (outerTask.IsCompleted)
7142 ProcessCompletedOuterTask(outerTask);
7144 else // Otherwise, process its completion asynchronously.
7146 outerTask.AddCompletionAction(this);
7150 // For ITaskCompletionAction
7151 public void Invoke(Task completingTask)
7153 // Check the current stack guard. If we're ok to inline,
7154 // process the task, and reset the guard when we're done.
7155 var sg = Task.CurrentStackGuard;
7156 if (sg.TryBeginInliningScope())
7158 try { InvokeCore(completingTask); }
7159 finally { sg.EndInliningScope(); }
7161 // Otherwise, we're too deep on the stack, and
7162 // we shouldn't run the continuation chain here, so queue a work
7163 // item to call back here to Invoke asynchronously.
7164 else InvokeCoreAsync(completingTask);
7168 /// Processes the completed task. InvokeCore could be called twice:
7169 /// once for the outer task, once for the inner task.
7171 /// <param name="completingTask">The completing outer or inner task.</param>
7172 private void InvokeCore(Task completingTask)
7176 case STATE_WAITING_ON_OUTER_TASK:
7177 ProcessCompletedOuterTask(completingTask);
7178 // We bump the state inside of ProcessCompletedOuterTask because it can also be called from the constructor.
7180 case STATE_WAITING_ON_INNER_TASK:
7181 bool result = TrySetFromTask(completingTask, lookForOce: false);
7182 _state = STATE_DONE; // bump the state
7183 Contract.Assert(result, "Expected TrySetFromTask from inner task to succeed");
7186 Contract.Assert(false, "UnwrapPromise in illegal state");
7191 // Calls InvokeCore asynchronously.
7192 [SecuritySafeCritical]
7193 private void InvokeCoreAsync(Task completingTask)
7195 // Queue a call to Invoke. If we're so deep on the stack that we're at risk of overflowing,
7196 // there's a high liklihood this thread is going to be doing lots more work before
7197 // returning to the thread pool (at the very least unwinding through thousands of
7198 // stack frames). So we queue to the global queue.
7199 ThreadPool.UnsafeQueueUserWorkItem(state =>
7201 // InvokeCore(completingTask);
7202 var tuple = (Tuple<UnwrapPromise<TResult>, Task>)state;
7203 tuple.Item1.InvokeCore(tuple.Item2);
7204 }, Tuple.Create<UnwrapPromise<TResult>, Task>(this, completingTask));
7207 /// <summary>Processes the outer task once it's completed.</summary>
7208 /// <param name="task">The now-completed outer task.</param>
7209 private void ProcessCompletedOuterTask(Task task)
7211 Contract.Requires(task != null && task.IsCompleted, "Expected non-null, completed outer task");
7212 Contract.Assert(_state == STATE_WAITING_ON_OUTER_TASK, "We're in the wrong state!");
7214 // Bump our state before proceeding any further
7215 _state = STATE_WAITING_ON_INNER_TASK;
7217 switch (task.Status)
7219 // If the outer task did not complete successfully, then record the
7220 // cancellation/fault information to tcs.Task.
7221 case TaskStatus.Canceled:
7222 case TaskStatus.Faulted:
7223 bool result = TrySetFromTask(task, _lookForOce);
7224 Contract.Assert(result, "Expected TrySetFromTask from outer task to succeed");
7227 // Otherwise, process the inner task it returned.
7228 case TaskStatus.RanToCompletion:
7229 var taskOfTaskOfTResult = task as Task<Task<TResult>>; // it's either a Task<Task> or Task<Task<TResult>>
7230 ProcessInnerTask(taskOfTaskOfTResult != null ?
7231 taskOfTaskOfTResult.Result : ((Task<Task>)task).Result);
7236 /// <summary>Transfer the completion status from "task" to ourself.</summary>
7237 /// <param name="task">The source task whose results should be transfered to <paramref name="promise"/>.</param>
7238 /// <param name="lookForOce">Whether or not to look for OperationCanceledExceptions in task's exceptions if it faults.</param>
7239 /// <returns>true if the transfer was successful; otherwise, false.</returns>
7240 private bool TrySetFromTask(Task task, bool lookForOce)
7242 Contract.Requires(task != null && task.IsCompleted, "TrySetFromTask: Expected task to have completed.");
7244 if (AsyncCausalityTracer.LoggingOn)
7245 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
7247 bool result = false;
7248 switch (task.Status)
7250 case TaskStatus.Canceled:
7251 result = TrySetCanceled(task.CancellationToken, task.GetCancellationExceptionDispatchInfo());
7254 case TaskStatus.Faulted:
7255 var edis = task.GetExceptionDispatchInfos();
7256 ExceptionDispatchInfo oceEdi;
7257 OperationCanceledException oce;
7258 if (lookForOce && edis.Count > 0 &&
7259 (oceEdi = edis[0]) != null &&
7260 (oce = oceEdi.SourceException as OperationCanceledException) != null)
7262 result = TrySetCanceled(oce.CancellationToken, oceEdi);
7266 result = TrySetException(edis);
7270 case TaskStatus.RanToCompletion:
7271 var taskTResult = task as Task<TResult>;
7273 if (AsyncCausalityTracer.LoggingOn)
7274 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
7276 if (Task.s_asyncDebuggingEnabled)
7278 RemoveFromActiveTasks(this.Id);
7281 result = TrySetResult(taskTResult != null ? taskTResult.Result : default(TResult));
7288 /// Processes the inner task of a Task{Task} or Task{Task{TResult}},
7289 /// transferring the appropriate results to ourself.
7291 /// <param name="task">The inner task returned by the task provided by the user.</param>
7292 private void ProcessInnerTask(Task task)
7294 // If the inner task is null, the proxy should be canceled.
7297 TrySetCanceled(default(CancellationToken));
7298 _state = STATE_DONE; // ... and record that we are done
7301 // Fast path for if the inner task is already completed
7302 else if (task.IsCompleted)
7304 TrySetFromTask(task, lookForOce: false);
7305 _state = STATE_DONE; // ... and record that we are done
7308 // The inner task exists but is not yet complete, so when it does complete,
7309 // take some action to set our completion state.
7312 task.AddCompletionAction(this);
7313 // We'll record that we are done when Invoke() is called.