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)
978 var etwLog = TplEtwProvider.Log;
979 if (etwLog.IsEnabled() && (m_stateFlags & Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED) == 0)
981 m_stateFlags |= Task.TASK_STATE_TASKSCHEDULED_WAS_FIRED;
983 Task currentTask = Task.InternalCurrent;
984 Task parentTask = this.m_parent;
985 etwLog.TaskScheduled(ts.Id, currentTask == null ? 0 : currentTask.Id,
986 this.Id, parentTask == null ? 0 : parentTask.Id, (int)this.Options,
987 System.Threading.Thread.GetDomainID());
996 /// Internal function that will be called by a new child task to add itself to
997 /// the children list of the parent (this).
999 /// Since a child task can only be created from the thread executing the action delegate
1000 /// of this task, reentrancy is neither required nor supported. This should not be called from
1001 /// anywhere other than the task construction/initialization codepaths.
1003 internal void AddNewChild()
1005 Contract.Assert(Task.InternalCurrent == this || this.IsSelfReplicatingRoot, "Task.AddNewChild(): Called from an external context");
1007 var props = EnsureContingentPropertiesInitialized(needsProtection: true);
1009 if (props.m_completionCountdown == 1 && !IsSelfReplicatingRoot)
1011 // A count of 1 indicates so far there was only the parent, and this is the first child task
1012 // Single kid => no fuss about who else is accessing the count. Let's save ourselves 100 cycles
1013 // We exclude self replicating root tasks from this optimization, because further child creation can take place on
1014 // other cores and with bad enough timing this write may not be visible to them.
1015 props.m_completionCountdown++;
1019 // otherwise do it safely
1020 Interlocked.Increment(ref props.m_completionCountdown);
1024 // This is called in the case where a new child is added, but then encounters a CancellationToken-related exception.
1025 // We need to subtract that child from m_completionCountdown, or the parent will never complete.
1026 internal void DisregardChild()
1028 Contract.Assert(Task.InternalCurrent == this, "Task.DisregardChild(): Called from an external context");
1030 var props = EnsureContingentPropertiesInitialized(needsProtection: true);
1031 Contract.Assert(props.m_completionCountdown >= 2, "Task.DisregardChild(): Expected parent count to be >= 2");
1032 Interlocked.Decrement(ref props.m_completionCountdown);
1036 /// Starts the <see cref="Task"/>, scheduling it for execution to the current <see
1037 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1040 /// A task may only be started and run only once. Any attempts to schedule a task a second time
1041 /// will result in an exception.
1043 /// <exception cref="InvalidOperationException">
1044 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1045 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1050 Start(TaskScheduler.Current);
1054 /// Starts the <see cref="Task"/>, scheduling it for execution to the specified <see
1055 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1058 /// A task may only be started and run only once. Any attempts to schedule a task a second time will
1059 /// result in an exception.
1061 /// <param name="scheduler">
1062 /// The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> with which to associate
1063 /// and execute this task.
1065 /// <exception cref="ArgumentNullException">
1066 /// The <paramref name="scheduler"/> argument is null.
1068 /// <exception cref="InvalidOperationException">
1069 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1070 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1073 public void Start(TaskScheduler scheduler)
1075 // Read the volatile m_stateFlags field once and cache it for subsequent operations
1076 int flags = m_stateFlags;
1078 // Need to check this before (m_action == null) because completed tasks will
1079 // set m_action to null. We would want to know if this is the reason that m_action == null.
1080 if (IsCompletedMethod(flags))
1082 throw new InvalidOperationException(Environment.GetResourceString("Task_Start_TaskCompleted"));
1085 if (scheduler == null)
1087 throw new ArgumentNullException("scheduler");
1090 var options = OptionsMethod(flags);
1091 if ((options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
1093 throw new InvalidOperationException(Environment.GetResourceString("Task_Start_Promise"));
1095 if ((options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) != 0)
1097 throw new InvalidOperationException(Environment.GetResourceString("Task_Start_ContinuationTask"));
1100 // Make sure that Task only gets started once. Or else throw an exception.
1101 if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
1103 throw new InvalidOperationException(Environment.GetResourceString("Task_Start_AlreadyStarted"));
1106 ScheduleAndStart(true);
1110 /// Runs the <see cref="Task"/> synchronously on the current <see
1111 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1115 /// A task may only be started and run only once. Any attempts to schedule a task a second time will
1116 /// result in an exception.
1119 /// Tasks executed with <see cref="RunSynchronously()"/> will be associated with the current <see
1120 /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see>.
1123 /// If the target scheduler does not support running this Task on the current thread, the Task will
1124 /// be scheduled for execution on the scheduler, and the current thread will block until the
1125 /// Task has completed execution.
1128 /// <exception cref="InvalidOperationException">
1129 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1130 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1133 public void RunSynchronously()
1135 InternalRunSynchronously(TaskScheduler.Current, waitForCompletion: true);
1139 /// Runs the <see cref="Task"/> synchronously on the <see
1140 /// cref="System.Threading.Tasks.TaskScheduler">scheduler</see> provided.
1144 /// A task may only be started and run only once. Any attempts to schedule a task a second time will
1145 /// result in an exception.
1148 /// If the target scheduler does not support running this Task on the current thread, the Task will
1149 /// be scheduled for execution on the scheduler, and the current thread will block until the
1150 /// Task has completed execution.
1153 /// <exception cref="InvalidOperationException">
1154 /// The <see cref="Task"/> is not in a valid state to be started. It may have already been started,
1155 /// executed, or canceled, or it may have been created in a manner that doesn't support direct
1158 /// <exception cref="ArgumentNullException">The <paramref name="scheduler"/> parameter
1159 /// is null.</exception>
1160 /// <param name="scheduler">The scheduler on which to attempt to run this task inline.</param>
1161 public void RunSynchronously(TaskScheduler scheduler)
1163 if (scheduler == null)
1165 throw new ArgumentNullException("scheduler");
1167 Contract.EndContractBlock();
1169 InternalRunSynchronously(scheduler, waitForCompletion: true);
1173 // Internal version of RunSynchronously that allows not waiting for completion.
1175 [SecuritySafeCritical] // Needed for QueueTask
1176 internal void InternalRunSynchronously(TaskScheduler scheduler, bool waitForCompletion)
1178 Contract.Requires(scheduler != null, "Task.InternalRunSynchronously(): null TaskScheduler");
1180 // Read the volatile m_stateFlags field once and cache it for subsequent operations
1181 int flags = m_stateFlags;
1183 // Can't call this method on a continuation task
1184 var options = OptionsMethod(flags);
1185 if ((options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) != 0)
1187 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_Continuation"));
1190 // Can't call this method on a promise-style task
1191 if ((options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
1193 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_Promise"));
1196 // Can't call this method on a task that has already completed
1197 if (IsCompletedMethod(flags))
1199 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_TaskCompleted"));
1202 // Make sure that Task only gets started once. Or else throw an exception.
1203 if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
1205 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_AlreadyStarted"));
1208 // execute only if we win the ---- against concurrent cancel attempts.
1209 // otherwise throw an exception, because we've been canceled.
1212 bool taskQueued = false;
1215 // We wrap TryRunInline() in a try/catch block and move an excepted task to Faulted here,
1216 // but not in Wait()/WaitAll()/FastWaitAll(). Here, we know for sure that the
1217 // task will not be subsequently scheduled (assuming that the scheduler adheres
1218 // to the guideline that an exception implies that no state change took place),
1219 // so it is safe to catch the exception and move the task to a final state. The
1220 // same cannot be said for Wait()/WaitAll()/FastWaitAll().
1221 if (!scheduler.TryRunInline(this, false))
1223 scheduler.InternalQueueTask(this);
1224 taskQueued = true; // only mark this after successfully queuing the task.
1227 // A successful TryRunInline doesn't guarantee completion, as there may be unfinished children.
1228 // Also if we queued the task above, the task may not be done yet.
1229 if (waitForCompletion && !IsCompleted)
1231 SpinThenBlockingWait(Timeout.Infinite, default(CancellationToken));
1236 // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
1237 // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute
1238 if (!taskQueued && !(e is ThreadAbortException))
1240 // We had a problem with TryRunInline() or QueueTask().
1241 // Record the exception, marking ourselves as Completed/Faulted.
1242 TaskSchedulerException tse = new TaskSchedulerException(e);
1246 // Mark ourselves as "handled" to avoid crashing the finalizer thread if the caller neglects to
1247 // call Wait() on this task.
1248 // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
1250 (m_contingentProperties != null) &&
1251 (m_contingentProperties.m_exceptionsHolder != null) &&
1252 (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
1253 "Task.InternalRunSynchronously(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
1254 "and to have faults recorded.");
1255 m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
1260 // We had a problem with waiting or this is a thread abort. Just re-throw.
1266 Contract.Assert((m_stateFlags & TASK_STATE_CANCELED) != 0, "Task.RunSynchronously: expected TASK_STATE_CANCELED to be set");
1267 // Can't call this method on canceled task.
1268 throw new InvalidOperationException(Environment.GetResourceString("Task_RunSynchronously_TaskCompleted"));
1274 //// Helper methods for Factory StartNew methods.
1278 // Implicitly converts action to object and handles the meat of the StartNew() logic.
1279 internal static Task InternalStartNew(
1280 Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler,
1281 TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)
1283 // Validate arguments.
1284 if (scheduler == null)
1286 throw new ArgumentNullException("scheduler");
1288 Contract.EndContractBlock();
1290 // Create and schedule the task. This throws an InvalidOperationException if already shut down.
1291 // Here we add the InternalTaskOptions.QueuedByRuntime to the internalOptions, so that TaskConstructorCore can skip the cancellation token registration
1292 Task t = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
1293 t.PossiblyCaptureContext(ref stackMark);
1295 t.ScheduleAndStart(false);
1300 /// Gets a unique ID for a <see cref="Task">Task</see> or task continuation instance.
1302 internal static int NewId()
1305 // We need to repeat if Interlocked.Increment wraps around and returns 0.
1306 // Otherwise next time this task's Id is queried it will get a new value
1309 newId = Interlocked.Increment(ref s_taskIdCounter);
1313 TplEtwProvider.Log.NewID(newId);
1323 /// Gets a unique ID for this <see cref="Task">Task</see> instance.
1326 /// Task IDs are assigned on-demand and do not necessarily represent the order in the which Task
1327 /// instances were created.
1335 int newId = NewId();
1336 Interlocked.CompareExchange(ref m_taskId, newId, 0);
1344 /// Returns the unique ID of the currently executing <see cref="Task">Task</see>.
1346 public static int? CurrentId
1350 Task currentTask = InternalCurrent;
1351 if (currentTask != null)
1352 return currentTask.Id;
1359 /// Gets the <see cref="Task">Task</see> instance currently executing, or
1360 /// null if none exists.
1362 internal static Task InternalCurrent
1364 get { return t_currentTask; }
1368 /// Gets the Task instance currently executing if the specified creation options
1369 /// contain AttachedToParent.
1371 /// <param name="options">The options to check.</param>
1372 /// <returns>The current task if there is one and if AttachToParent is in the options; otherwise, null.</returns>
1373 internal static Task InternalCurrentIfAttached(TaskCreationOptions creationOptions)
1375 return (creationOptions & TaskCreationOptions.AttachedToParent) != 0 ? InternalCurrent : null;
1379 /// Gets the StackGuard object assigned to the current thread.
1381 internal static StackGuard CurrentStackGuard
1385 StackGuard sg = t_stackGuard;
1388 t_stackGuard = sg = new StackGuard();
1396 /// Gets the <see cref="T:System.AggregateException">Exception</see> that caused the <see
1397 /// cref="Task">Task</see> to end prematurely. If the <see
1398 /// cref="Task">Task</see> completed successfully or has not yet thrown any
1399 /// exceptions, this will return null.
1402 /// Tasks that throw unhandled exceptions store the resulting exception and propagate it wrapped in a
1403 /// <see cref="System.AggregateException"/> in calls to <see cref="Wait()">Wait</see>
1404 /// or in accesses to the <see cref="Exception"/> property. Any exceptions not observed by the time
1405 /// the Task instance is garbage collected will be propagated on the finalizer thread.
1407 public AggregateException Exception
1411 AggregateException e = null;
1413 // If you're faulted, retrieve the exception(s)
1414 if (IsFaulted) e = GetExceptions(false);
1416 // Only return an exception in faulted state (skip manufactured exceptions)
1417 // A "benevolent" race condition makes it possible to return null when IsFaulted is
1418 // true (i.e., if IsFaulted is set just after the check to IsFaulted above).
1419 Contract.Assert((e == null) || IsFaulted, "Task.Exception_get(): returning non-null value when not Faulted");
1426 /// Gets the <see cref="T:System.Threading.Tasks.TaskStatus">TaskStatus</see> of this Task.
1428 public TaskStatus Status
1434 // get a cached copy of the state flags. This should help us
1435 // to get a consistent view of the flags if they are changing during the
1436 // execution of this method.
1437 int sf = m_stateFlags;
1439 if ((sf & TASK_STATE_FAULTED) != 0) rval = TaskStatus.Faulted;
1440 else if ((sf & TASK_STATE_CANCELED) != 0) rval = TaskStatus.Canceled;
1441 else if ((sf & TASK_STATE_RAN_TO_COMPLETION) != 0) rval = TaskStatus.RanToCompletion;
1442 else if ((sf & TASK_STATE_WAITING_ON_CHILDREN) != 0) rval = TaskStatus.WaitingForChildrenToComplete;
1443 else if ((sf & TASK_STATE_DELEGATE_INVOKED) != 0) rval = TaskStatus.Running;
1444 else if ((sf & TASK_STATE_STARTED) != 0) rval = TaskStatus.WaitingToRun;
1445 else if ((sf & TASK_STATE_WAITINGFORACTIVATION) != 0) rval = TaskStatus.WaitingForActivation;
1446 else rval = TaskStatus.Created;
1453 /// Gets whether this <see cref="Task">Task</see> instance has completed
1454 /// execution due to being canceled.
1457 /// A <see cref="Task">Task</see> will complete in Canceled state either if its <see cref="CancellationToken">CancellationToken</see>
1458 /// was marked for cancellation before the task started executing, or if the task acknowledged the cancellation request on
1459 /// its already signaled CancellationToken by throwing an
1460 /// <see cref="System.OperationCanceledException">OperationCanceledException</see> that bears the same
1461 /// <see cref="System.Threading.CancellationToken">CancellationToken</see>.
1463 public bool IsCanceled
1467 // Return true if canceled bit is set and faulted bit is not set
1468 return (m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_FAULTED)) == TASK_STATE_CANCELED;
1473 /// Returns true if this task has a cancellation token and it was signaled.
1474 /// To be used internally in execute entry codepaths.
1476 internal bool IsCancellationRequested
1480 // check both the internal cancellation request flag and the CancellationToken attached to this task
1481 var props = m_contingentProperties;
1482 return props != null &&
1483 (props.m_internalCancellationRequested == CANCELLATION_REQUESTED ||
1484 props.m_cancellationToken.IsCancellationRequested);
1489 /// Ensures that the contingent properties field has been initialized.
1490 /// ASSUMES THAT m_stateFlags IS ALREADY SET!
1492 /// <param name="needsProtection">true if this needs to be done in a thread-safe manner; otherwise, false.</param>
1493 /// <returns>The initialized contingent properties object.</returns>
1494 internal ContingentProperties EnsureContingentPropertiesInitialized(bool needsProtection)
1496 var props = m_contingentProperties;
1497 return props != null ? props : EnsureContingentPropertiesInitializedCore(needsProtection);
1501 /// Initializes the contingent properties object. This assumes a check has already been done for nullness.
1503 /// <param name="needsProtection">true if this needs to be done in a thread-safe manner; otherwise, false.</param>
1504 /// <returns>The initialized contingent properties object.</returns>
1505 private ContingentProperties EnsureContingentPropertiesInitializedCore(bool needsProtection)
1507 if (needsProtection)
1509 return LazyInitializer.EnsureInitialized<ContingentProperties>(ref m_contingentProperties, s_createContingentProperties);
1513 Contract.Assert(m_contingentProperties == null, "Expected props to be null after checking and with needsProtection == false");
1514 return m_contingentProperties = new ContingentProperties();
1518 // Cached functions for lazily initializing contingent properties
1519 private static readonly Func<ContingentProperties> s_createContingentProperties = () => new ContingentProperties();
1522 /// This internal property provides access to the CancellationToken that was set on the task
1523 /// when it was constructed.
1525 internal CancellationToken CancellationToken
1529 var props = m_contingentProperties;
1530 return (props == null) ? default(CancellationToken) : props.m_cancellationToken;
1535 /// Gets whether this <see cref="Task"/> threw an OperationCanceledException while its CancellationToken was signaled.
1537 internal bool IsCancellationAcknowledged
1539 get { return (m_stateFlags & TASK_STATE_CANCELLATIONACKNOWLEDGED) != 0; }
1544 /// Gets whether this <see cref="Task">Task</see> has completed.
1547 /// <see cref="IsCompleted"/> will return true when the Task is in one of the three
1548 /// final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
1549 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
1550 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
1552 public bool IsCompleted
1556 int stateFlags = m_stateFlags; // enable inlining of IsCompletedMethod by "cast"ing away the volatility
1557 return IsCompletedMethod(stateFlags);
1561 // Similar to IsCompleted property, but allows for the use of a cached flags value
1562 // rather than reading the volatile m_stateFlags field.
1563 private static bool IsCompletedMethod(int flags)
1565 return (flags & TASK_STATE_COMPLETED_MASK) != 0;
1568 // For use in InternalWait -- marginally faster than (Task.Status == TaskStatus.RanToCompletion)
1569 internal bool IsRanToCompletion
1571 get { return (m_stateFlags & TASK_STATE_COMPLETED_MASK) == TASK_STATE_RAN_TO_COMPLETION; }
1575 /// Gets the <see cref="T:System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used
1576 /// to create this task.
1578 public TaskCreationOptions CreationOptions
1580 get { return Options & (TaskCreationOptions)(~InternalTaskOptions.InternalOptionsMask); }
1584 /// Gets a <see cref="T:System.Threading.WaitHandle"/> that can be used to wait for the task to
1588 /// Using the wait functionality provided by <see cref="Wait()"/>
1589 /// should be preferred over using <see cref="IAsyncResult.AsyncWaitHandle"/> for similar
1592 /// <exception cref="T:System.ObjectDisposedException">
1593 /// The <see cref="Task"/> has been disposed.
1595 WaitHandle IAsyncResult.AsyncWaitHandle
1597 // Although a slim event is used internally to avoid kernel resource allocation, this function
1598 // forces allocation of a true WaitHandle when called.
1601 bool isDisposed = (m_stateFlags & TASK_STATE_DISPOSED) != 0;
1604 throw new ObjectDisposedException(null, Environment.GetResourceString("Task_ThrowIfDisposed"));
1606 return CompletedEvent.WaitHandle;
1611 /// Gets the state object supplied when the <see cref="Task">Task</see> was created,
1612 /// or null if none was supplied.
1614 public object AsyncState
1616 get { return m_stateObject; }
1620 /// Gets an indication of whether the asynchronous operation completed synchronously.
1622 /// <value>true if the asynchronous operation completed synchronously; otherwise, false.</value>
1623 bool IAsyncResult.CompletedSynchronously
1632 /// Provides access to the TaskScheduler responsible for executing this Task.
1634 internal TaskScheduler ExecutingTaskScheduler
1636 get { return m_taskScheduler; }
1640 /// Provides access to factory methods for creating <see cref="Task"/> and <see cref="Task{TResult}"/> instances.
1643 /// The factory returned from <see cref="Factory"/> is a default instance
1644 /// of <see cref="System.Threading.Tasks.TaskFactory"/>, as would result from using
1645 /// the default constructor on TaskFactory.
1647 public static TaskFactory Factory { get { return s_factory; } }
1649 /// <summary>A task that's already been completed successfully.</summary>
1650 private static Task s_completedTask;
1652 /// <summary>Gets a task that's already been completed successfully.</summary>
1653 /// <remarks>May not always return the same instance.</remarks>
1654 public static Task CompletedTask
1658 var completedTask = s_completedTask;
1659 if (completedTask == null)
1660 s_completedTask = completedTask = new Task(false, (TaskCreationOptions)InternalTaskOptions.DoNotDispose, default(CancellationToken)); // benign initialization ----
1661 return completedTask;
1666 /// Provides an event that can be used to wait for completion.
1667 /// Only called by IAsyncResult.AsyncWaitHandle, which means that we really do need to instantiate a completion event.
1669 internal ManualResetEventSlim CompletedEvent
1673 var contingentProps = EnsureContingentPropertiesInitialized(needsProtection: true);
1674 if (contingentProps.m_completionEvent == null)
1676 bool wasCompleted = IsCompleted;
1677 ManualResetEventSlim newMre = new ManualResetEventSlim(wasCompleted);
1678 if (Interlocked.CompareExchange(ref contingentProps.m_completionEvent, newMre, null) != null)
1680 // We lost the ----, so we will just close the event right away.
1683 else if (!wasCompleted && IsCompleted)
1685 // We published the event as unset, but the task has subsequently completed.
1686 // Set the event's state properly so that callers don't deadlock.
1691 return contingentProps.m_completionEvent;
1696 /// Determines whether this is the root task of a self replicating group.
1698 internal bool IsSelfReplicatingRoot
1702 // Return true if self-replicating bit is set and child replica bit is not set
1703 return (Options & (TaskCreationOptions)(InternalTaskOptions.SelfReplicating | InternalTaskOptions.ChildReplica))
1704 == (TaskCreationOptions)InternalTaskOptions.SelfReplicating;
1709 /// Determines whether the task is a replica itself.
1711 internal bool IsChildReplica
1713 get { return (Options & (TaskCreationOptions)InternalTaskOptions.ChildReplica) != 0; }
1716 internal int ActiveChildCount
1720 var props = m_contingentProperties;
1721 return props != null ? props.m_completionCountdown - 1 : 0;
1726 /// The property formerly known as IsFaulted.
1728 internal bool ExceptionRecorded
1732 var props = m_contingentProperties;
1733 return (props != null) && (props.m_exceptionsHolder != null) && (props.m_exceptionsHolder.ContainsFaultList);
1738 /// Gets whether the <see cref="Task"/> completed due to an unhandled exception.
1741 /// If <see cref="IsFaulted"/> is true, the Task's <see cref="Status"/> will be equal to
1742 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">TaskStatus.Faulted</see>, and its
1743 /// <see cref="Exception"/> property will be non-null.
1745 public bool IsFaulted
1749 // Faulted is "king" -- if that bit is present (regardless of other bits), we are faulted.
1750 return ((m_stateFlags & TASK_STATE_FAULTED) != 0);
1755 /// The captured execution context for the current task to run inside
1756 /// If the TASK_STATE_EXECUTIONCONTEXT_IS_NULL flag is set, this means ExecutionContext.Capture returned null, otherwise
1757 /// If the captured context is the default, nothing is saved, otherwise the m_contingentProperties inflates to save the context
1759 internal ExecutionContext CapturedContext
1763 if ((m_stateFlags & TASK_STATE_EXECUTIONCONTEXT_IS_NULL) == TASK_STATE_EXECUTIONCONTEXT_IS_NULL)
1769 var props = m_contingentProperties;
1770 if (props != null && props.m_capturedContext != null) return props.m_capturedContext;
1771 else return ExecutionContext.PreAllocatedDefault;
1776 // 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
1779 m_stateFlags |= TASK_STATE_EXECUTIONCONTEXT_IS_NULL;
1781 else if (!value.IsPreAllocatedDefault) // not the default context, then inflate the contingent properties and set it
1783 EnsureContingentPropertiesInitialized(needsProtection: false).m_capturedContext = value;
1785 //else do nothing, this is the default context
1790 /// Static helper function to copy specific ExecutionContext
1792 /// <param name="capturedContext">The captured context</param>
1793 /// <returns>The copied context, null if the capturedContext is null</returns>
1794 private static ExecutionContext CopyExecutionContext(ExecutionContext capturedContext)
1796 if (capturedContext == null)
1798 if (capturedContext.IsPreAllocatedDefault)
1799 return ExecutionContext.PreAllocatedDefault;
1801 return capturedContext.CreateCopy();
1807 /// Retrieves an identifier for the task.
1809 internal int InternalId
1811 get { return GetHashCode(); }
1820 /// Disposes the <see cref="Task"/>, releasing all of its unmanaged resources.
1823 /// Unlike most of the members of <see cref="Task"/>, this method is not thread-safe.
1824 /// Also, <see cref="Dispose()"/> may only be called on a <see cref="Task"/> that is in one of
1825 /// the final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
1826 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
1827 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
1829 /// <exception cref="T:System.InvalidOperationException">
1830 /// The exception that is thrown if the <see cref="Task"/> is not in
1831 /// one of the final states: <see cref="System.Threading.Tasks.TaskStatus.RanToCompletion">RanToCompletion</see>,
1832 /// <see cref="System.Threading.Tasks.TaskStatus.Faulted">Faulted</see>, or
1833 /// <see cref="System.Threading.Tasks.TaskStatus.Canceled">Canceled</see>.
1835 public void Dispose()
1838 GC.SuppressFinalize(this);
1842 /// Disposes the <see cref="Task"/>, releasing all of its unmanaged resources.
1844 /// <param name="disposing">
1845 /// A Boolean value that indicates whether this method is being called due to a call to <see
1846 /// cref="Dispose()"/>.
1849 /// Unlike most of the members of <see cref="Task"/>, this method is not thread-safe.
1851 protected virtual void Dispose(bool disposing)
1855 // Dispose is a nop if this task was created with the DoNotDispose internal option.
1856 // This is done before the completed check, because if we're not touching any
1857 // state on the task, it's ok for it to happen before completion.
1858 if ((Options & (TaskCreationOptions)InternalTaskOptions.DoNotDispose) != 0)
1863 // Task must be completed to dispose
1866 throw new InvalidOperationException(Environment.GetResourceString("Task_Dispose_NotCompleted"));
1869 // Dispose of the underlying completion event if it exists
1870 var cp = m_contingentProperties;
1873 // Make a copy to protect against racing Disposes.
1874 // If we wanted to make this a bit safer, we could use an interlocked here,
1875 // but we state that Dispose is not thread safe.
1876 var ev = cp.m_completionEvent;
1879 // Null out the completion event in contingent props; we'll use our copy from here on out
1880 cp.m_completionEvent = null;
1882 // In the unlikely event that our completion event is inflated but not yet signaled,
1883 // go ahead and signal the event. If you dispose of an unsignaled MRES, then any waiters
1884 // will deadlock; an ensuing Set() will not wake them up. In the event of an AppDomainUnload,
1885 // there is no guarantee that anyone else is going to signal the event, and it does no harm to
1886 // call Set() twice on m_completionEvent.
1887 if (!ev.IsSet) ev.Set();
1889 // Finally, dispose of the event
1895 // We OR the flags to indicate the object has been disposed. The task
1896 // has already completed at this point, and the only conceivable ---- would
1897 // be with the unsetting of the TASK_STATE_WAIT_COMPLETION_NOTIFICATION flag, which
1898 // ---- is extremely unlikely and also benign. (Worst case: we hit a breakpoint
1899 // twice instead of once in the debugger. Weird, but not lethal.)
1900 m_stateFlags |= TASK_STATE_DISPOSED;
1908 /// Schedules the task for execution.
1910 /// <param name="needsProtection">If true, TASK_STATE_STARTED bit is turned on in
1911 /// an atomic fashion, making sure that TASK_STATE_CANCELED does not get set
1912 /// underneath us. If false, TASK_STATE_STARTED bit is OR-ed right in. This
1913 /// allows us to streamline things a bit for StartNew(), where competing cancellations
1914 /// are not a problem.</param>
1915 [SecuritySafeCritical] // Needed for QueueTask
1916 internal void ScheduleAndStart(bool needsProtection)
1918 Contract.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected");
1919 Contract.Assert((m_stateFlags & TASK_STATE_STARTED) == 0, "task has already started");
1921 // Set the TASK_STATE_STARTED bit
1922 if (needsProtection)
1926 // A cancel has snuck in before we could get started. Quietly exit.
1932 m_stateFlags |= TASK_STATE_STARTED;
1935 if (s_asyncDebuggingEnabled)
1937 AddToActiveTasks(this);
1940 if (AsyncCausalityTracer.LoggingOn && (Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
1942 //For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor
1943 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: "+((Delegate)m_action).Method.Name, 0);
1949 // Queue to the indicated scheduler.
1950 m_taskScheduler.InternalQueueTask(this);
1952 catch (ThreadAbortException tae)
1955 FinishThreadAbortedTask(true, false);
1959 // The scheduler had a problem queueing this task. Record the exception, leaving this task in
1961 TaskSchedulerException tse = new TaskSchedulerException(e);
1965 // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew()
1966 // or from the self replicating logic, because in both cases the exception is either propagated outside directly, or added
1967 // to an enclosing parent. However we won't do this for continuation tasks, because in that case we internally eat the exception
1968 // and therefore we need to make sure the user does later observe it explicitly or see it on the finalizer.
1970 if ((Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
1972 // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
1974 (m_contingentProperties != null) &&
1975 (m_contingentProperties.m_exceptionsHolder != null) &&
1976 (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
1977 "Task.ScheduleAndStart(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
1978 "and to have faults recorded.");
1980 m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
1982 // re-throw the exception wrapped as a TaskSchedulerException.
1988 /// Adds an exception to the list of exceptions this task has thrown.
1990 /// <param name="exceptionObject">An object representing either an Exception or a collection of Exceptions.</param>
1991 internal void AddException(object exceptionObject)
1993 Contract.Requires(exceptionObject != null, "Task.AddException: Expected a non-null exception object");
1994 AddException(exceptionObject, representsCancellation: false);
1998 /// Adds an exception to the list of exceptions this task has thrown.
2000 /// <param name="exceptionObject">An object representing either an Exception or a collection of Exceptions.</param>
2001 /// <param name="representsCancellation">Whether the exceptionObject is an OperationCanceledException representing cancellation.</param>
2002 internal void AddException(object exceptionObject, bool representsCancellation)
2004 Contract.Requires(exceptionObject != null, "Task.AddException: Expected a non-null exception object");
2007 var eoAsException = exceptionObject as Exception;
2008 var eoAsEnumerableException = exceptionObject as IEnumerable<Exception>;
2009 var eoAsEdi = exceptionObject as ExceptionDispatchInfo;
2010 var eoAsEnumerableEdi = exceptionObject as IEnumerable<ExceptionDispatchInfo>;
2013 eoAsException != null || eoAsEnumerableException != null || eoAsEdi != null || eoAsEnumerableEdi != null,
2014 "Task.AddException: Expected an Exception, ExceptionDispatchInfo, or an IEnumerable<> of one of those");
2016 var eoAsOce = exceptionObject as OperationCanceledException;
2019 !representsCancellation ||
2021 (eoAsEdi != null && eoAsEdi.SourceException is OperationCanceledException),
2022 "representsCancellation should be true only if an OCE was provided.");
2026 // WARNING: A great deal of care went into ensuring that
2027 // AddException() and GetExceptions() are never called
2028 // simultaneously. See comment at start of GetExceptions().
2031 // Lazily initialize the holder, ensuring only one thread wins.
2032 var props = EnsureContingentPropertiesInitialized(needsProtection: true);
2033 if (props.m_exceptionsHolder == null)
2035 TaskExceptionHolder holder = new TaskExceptionHolder(this);
2036 if (Interlocked.CompareExchange(ref props.m_exceptionsHolder, holder, null) != null)
2038 // If we lost the ----, suppress finalization.
2039 holder.MarkAsHandled(false);
2045 props.m_exceptionsHolder.Add(exceptionObject, representsCancellation);
2050 /// Returns a list of exceptions by aggregating the holder's contents. Or null if
2051 /// no exceptions have been thrown.
2053 /// <param name="includeTaskCanceledExceptions">Whether to include a TCE if cancelled.</param>
2054 /// <returns>An aggregate exception, or null if no exceptions have been caught.</returns>
2055 private AggregateException GetExceptions(bool includeTaskCanceledExceptions)
2058 // WARNING: The Task/Task<TResult>/TaskCompletionSource classes
2059 // have all been carefully crafted to insure that GetExceptions()
2060 // is never called while AddException() is being called. There
2061 // are locks taken on m_contingentProperties in several places:
2063 // -- Task<TResult>.TrySetException(): The lock allows the
2064 // task to be set to Faulted state, and all exceptions to
2065 // be recorded, in one atomic action.
2067 // -- Task.Exception_get(): The lock ensures that Task<TResult>.TrySetException()
2068 // is allowed to complete its operation before Task.Exception_get()
2069 // can access GetExceptions().
2071 // -- Task.ThrowIfExceptional(): The lock insures that Wait() will
2072 // not attempt to call GetExceptions() while Task<TResult>.TrySetException()
2073 // is in the process of calling AddException().
2075 // For "regular" tasks, we effectively keep AddException() and GetException()
2076 // from being called concurrently by the way that the state flows. Until
2077 // a Task is marked Faulted, Task.Exception_get() returns null. And
2078 // a Task is not marked Faulted until it and all of its children have
2079 // completed, which means that all exceptions have been recorded.
2081 // It might be a lot easier to follow all of this if we just required
2082 // that all calls to GetExceptions() and AddExceptions() were made
2083 // under a lock on m_contingentProperties. But that would also
2084 // increase our lock occupancy time and the frequency with which we
2085 // would need to take the lock.
2087 // If you add a call to GetExceptions() anywhere in the code,
2088 // please continue to maintain the invariant that it can't be
2089 // called when AddException() is being called.
2092 // We'll lazily create a TCE if the task has been canceled.
2093 Exception canceledException = null;
2094 if (includeTaskCanceledExceptions && IsCanceled)
2097 // Ideally we'd just use the cached OCE from this.GetCancellationExceptionDispatchInfo()
2098 // here. However, that would result in a potentially breaking change from .NET 4, which
2099 // has the code here that throws a new exception instead of the original, and the EDI
2100 // may not contain a TCE, but an OCE or any OCE-derived type, which would mean we'd be
2101 // propagating an exception of a different type.
2102 canceledException = new TaskCanceledException(this);
2105 if (ExceptionRecorded)
2107 // There are exceptions; get the aggregate and optionally add the canceled
2108 // exception to the aggregate (if applicable).
2109 Contract.Assert(m_contingentProperties != null); // ExceptionRecorded ==> m_contingentProperties != null
2111 // No need to lock around this, as other logic prevents the consumption of exceptions
2112 // before they have been completely processed.
2113 return m_contingentProperties.m_exceptionsHolder.CreateExceptionObject(false, canceledException);
2115 else if (canceledException != null)
2117 // No exceptions, but there was a cancelation. Aggregate and return it.
2118 return new AggregateException(canceledException);
2124 /// <summary>Gets the exception dispatch infos once the task has faulted.</summary>
2125 internal ReadOnlyCollection<ExceptionDispatchInfo> GetExceptionDispatchInfos()
2127 bool exceptionsAvailable = IsFaulted && ExceptionRecorded;
2128 Contract.Assert(exceptionsAvailable, "Must only be used when the task has faulted with exceptions.");
2129 return exceptionsAvailable ?
2130 m_contingentProperties.m_exceptionsHolder.GetExceptionDispatchInfos() :
2131 new ReadOnlyCollection<ExceptionDispatchInfo>(new ExceptionDispatchInfo[0]);
2134 /// <summary>Gets the ExceptionDispatchInfo containing the OperationCanceledException for this task.</summary>
2135 /// <returns>The ExceptionDispatchInfo. May be null if no OCE was stored for the task.</returns>
2136 internal ExceptionDispatchInfo GetCancellationExceptionDispatchInfo()
2138 Contract.Assert(IsCanceled, "Must only be used when the task has canceled.");
2139 var props = m_contingentProperties;
2140 if (props == null) return null;
2141 var holder = props.m_exceptionsHolder;
2142 if (holder == null) return null;
2143 return holder.GetCancellationExceptionDispatchInfo(); // may be null
2147 /// Throws an aggregate exception if the task contains exceptions.
2149 internal void ThrowIfExceptional(bool includeTaskCanceledExceptions)
2151 Contract.Requires(IsCompleted, "ThrowIfExceptional(): Expected IsCompleted == true");
2153 Exception exception = GetExceptions(includeTaskCanceledExceptions);
2154 if (exception != null)
2156 UpdateExceptionObservedStatus();
2162 /// Checks whether this is an attached task, and whether we are being called by the parent task.
2163 /// And sets the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag based on that.
2165 /// This is meant to be used internally when throwing an exception, and when WaitAll is gathering
2166 /// exceptions for tasks it waited on. If this flag gets set, the implicit wait on children
2167 /// will skip exceptions to prevent duplication.
2169 /// This should only be called when this task has completed with an exception
2172 internal void UpdateExceptionObservedStatus()
2174 if ((m_parent != null)
2175 && ((Options & TaskCreationOptions.AttachedToParent) != 0)
2176 && ((m_parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
2177 && Task.InternalCurrent == m_parent)
2179 m_stateFlags |= TASK_STATE_EXCEPTIONOBSERVEDBYPARENT;
2184 /// Checks whether the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag is set,
2185 /// This will only be used by the implicit wait to prevent double throws
2188 internal bool IsExceptionObservedByParent
2192 return (m_stateFlags & TASK_STATE_EXCEPTIONOBSERVEDBYPARENT) != 0;
2197 /// Checks whether the body was ever invoked. Used by task scheduler code to verify custom schedulers actually ran the task.
2199 internal bool IsDelegateInvoked
2203 return (m_stateFlags & TASK_STATE_DELEGATE_INVOKED) != 0;
2208 /// Signals completion of this particular task.
2210 /// The bUserDelegateExecuted parameter indicates whether this Finish() call comes following the
2211 /// full execution of the user delegate.
2213 /// If bUserDelegateExecuted is false, it mean user delegate wasn't invoked at all (either due to
2214 /// a cancellation request, or because this task is a promise style Task). In this case, the steps
2215 /// involving child tasks (i.e. WaitForChildren) will be skipped.
2218 internal void Finish(bool bUserDelegateExecuted)
2220 if (!bUserDelegateExecuted)
2222 // delegate didn't execute => no children. We can safely call the remaining finish stages
2227 var props = m_contingentProperties;
2229 if (props == null || // no contingent properties means no children, so it's safe to complete ourselves
2230 (props.m_completionCountdown == 1 && !IsSelfReplicatingRoot) ||
2231 // Count of 1 => either all children finished, or there were none. Safe to complete ourselves
2232 // without paying the price of an Interlocked.Decrement.
2233 // However we need to exclude self replicating root tasks from this optimization, because
2234 // they can have children joining in, or finishing even after the root task delegate is done.
2235 Interlocked.Decrement(ref props.m_completionCountdown) == 0) // Reaching this sub clause means there may be remaining active children,
2236 // and we could be racing with one of them to call FinishStageTwo().
2237 // So whoever does the final Interlocked.Dec is responsible to finish.
2243 // Apparently some children still remain. It will be up to the last one to process the completion of this task on their own thread.
2244 // We will now yield the thread back to ThreadPool. Mark our state appropriately before getting out.
2246 // We have to use an atomic update for this and make sure not to overwrite a final state,
2247 // because at this very moment the last child's thread may be concurrently completing us.
2248 // Otherwise we risk overwriting the TASK_STATE_RAN_TO_COMPLETION, _CANCELED or _FAULTED bit which may have been set by that child task.
2249 // Note that the concurrent update by the last child happening in FinishStageTwo could still wipe out the TASK_STATE_WAITING_ON_CHILDREN flag,
2250 // but it is not critical to maintain, therefore we dont' need to intruduce a full atomic update into FinishStageTwo
2252 AtomicStateUpdate(TASK_STATE_WAITING_ON_CHILDREN, TASK_STATE_FAULTED | TASK_STATE_CANCELED | TASK_STATE_RAN_TO_COMPLETION);
2255 // 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.
2256 // we use a local variable for exceptional children here because some other thread may be nulling out m_contingentProperties.m_exceptionalChildren
2257 List<Task> exceptionalChildren = props != null ? props.m_exceptionalChildren : null;
2259 if (exceptionalChildren != null)
2261 lock (exceptionalChildren)
2263 exceptionalChildren.RemoveAll(s_IsExceptionObservedByParentPredicate); // RemoveAll has better performance than doing it ourselves
2269 // statically allocated delegate for the removeall expression in Finish()
2270 private readonly static Predicate<Task> s_IsExceptionObservedByParentPredicate = new Predicate<Task>((t) => { return t.IsExceptionObservedByParent; });
2273 /// FinishStageTwo is to be executed as soon as we known there are no more children to complete.
2274 /// 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)
2275 /// ii) or on the thread that executed the last child.
2277 internal void FinishStageTwo()
2279 AddExceptionsFromChildren();
2281 // At this point, the task is done executing and waiting for its children,
2282 // we can transition our task to a completion state.
2283 int completionState;
2284 if (ExceptionRecorded)
2286 completionState = TASK_STATE_FAULTED;
2287 if (AsyncCausalityTracer.LoggingOn)
2288 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Error);
2290 if (Task.s_asyncDebuggingEnabled)
2292 RemoveFromActiveTasks(this.Id);
2295 else if (IsCancellationRequested && IsCancellationAcknowledged)
2297 // We transition into the TASK_STATE_CANCELED final state if the task's CT was signalled for cancellation,
2298 // and the user delegate acknowledged the cancellation request by throwing an OCE,
2299 // and the task hasn't otherwise transitioned into faulted state. (TASK_STATE_FAULTED trumps TASK_STATE_CANCELED)
2301 // If the task threw an OCE without cancellation being requestsed (while the CT not being in signaled state),
2302 // then we regard it as a regular exception
2304 completionState = TASK_STATE_CANCELED;
2305 if (AsyncCausalityTracer.LoggingOn)
2306 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Canceled);
2308 if (Task.s_asyncDebuggingEnabled)
2310 RemoveFromActiveTasks(this.Id);
2315 completionState = TASK_STATE_RAN_TO_COMPLETION;
2316 if (AsyncCausalityTracer.LoggingOn)
2317 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
2319 if (Task.s_asyncDebuggingEnabled)
2321 RemoveFromActiveTasks(this.Id);
2325 // Use Interlocked.Exchange() to effect a memory fence, preventing
2326 // any SetCompleted() (or later) instructions from sneak back before it.
2327 Interlocked.Exchange(ref m_stateFlags, m_stateFlags | completionState);
2329 // Set the completion event if it's been lazy allocated.
2330 // And if we made a cancellation registration, it's now unnecessary.
2331 var cp = m_contingentProperties;
2335 cp.DeregisterCancellationCallback();
2338 // ready to run continuations and notify parent.
2344 /// Final stage of the task completion code path. Notifies the parent (if any) that another of its childre are done, and runs continuations.
2345 /// This function is only separated out from FinishStageTwo because these two operations are also needed to be called from CancellationCleanupLogic()
2347 internal void FinishStageThree()
2349 // Release the action so that holding this task object alive doesn't also
2350 // hold alive the body of the task. We do this before notifying a parent,
2351 // so that if notifying the parent completes the parent and causes
2352 // its synchronous continuations to run, the GC can collect the state
2353 // in the interim. And we do it before finishing continuations, because
2354 // continuations hold onto the task, and therefore are keeping it alive.
2357 // Notify parent if this was an attached task
2358 if (m_parent != null
2359 && ((m_parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == 0)
2360 && (((TaskCreationOptions)(m_stateFlags & OptionsMask)) & TaskCreationOptions.AttachedToParent) != 0)
2362 m_parent.ProcessChildCompletion(this);
2365 // Activate continuations (if any).
2366 FinishContinuations();
2370 /// This is called by children of this task when they are completed.
2372 internal void ProcessChildCompletion(Task childTask)
2374 Contract.Requires(childTask != null);
2375 Contract.Requires(childTask.IsCompleted, "ProcessChildCompletion was called for an uncompleted task");
2377 Contract.Assert(childTask.m_parent == this, "ProcessChildCompletion should only be called for a child of this task");
2379 var props = m_contingentProperties;
2381 // if the child threw and we haven't observed it we need to save it for future reference
2382 if (childTask.IsFaulted && !childTask.IsExceptionObservedByParent)
2384 // Lazily initialize the child exception list
2385 if (props.m_exceptionalChildren == null)
2387 Interlocked.CompareExchange(ref props.m_exceptionalChildren, new List<Task>(), null);
2390 // In rare situations involving AppDomainUnload, it's possible (though unlikely) for FinishStageTwo() to be called
2391 // multiple times for the same task. In that case, AddExceptionsFromChildren() could be nulling m_exceptionalChildren
2392 // out at the same time that we're processing it, resulting in a NullReferenceException here. We'll protect
2393 // ourselves by caching m_exceptionChildren in a local variable.
2394 List<Task> tmp = props.m_exceptionalChildren;
2405 if (Interlocked.Decrement(ref props.m_completionCountdown) == 0)
2407 // This call came from the final child to complete, and apparently we have previously given up this task's right to complete itself.
2408 // So we need to invoke the final finish stage.
2415 /// This is to be called just before the task does its final state transition.
2416 /// It traverses the list of exceptional children, and appends their aggregate exceptions into this one's exception list
2418 internal void AddExceptionsFromChildren()
2420 // In rare occurences during AppDomainUnload() processing, it is possible for this method to be called
2421 // simultaneously on the same task from two different contexts. This can result in m_exceptionalChildren
2422 // being nulled out while it is being processed, which could lead to a NullReferenceException. To
2423 // protect ourselves, we'll cache m_exceptionalChildren in a local variable.
2424 var props = m_contingentProperties;
2425 List<Task> tmp = (props != null) ? props.m_exceptionalChildren : null;
2429 // This lock is necessary because even though AddExceptionsFromChildren is last to execute, it may still
2430 // be racing with the code segment at the bottom of Finish() that prunes the exceptional child array.
2433 foreach (Task task in tmp)
2435 // Ensure any exceptions thrown by children are added to the parent.
2436 // In doing this, we are implicitly marking children as being "handled".
2437 Contract.Assert(task.IsCompleted, "Expected all tasks in list to be completed");
2438 if (task.IsFaulted && !task.IsExceptionObservedByParent)
2440 TaskExceptionHolder exceptionHolder = task.m_contingentProperties.m_exceptionsHolder;
2441 Contract.Assert(exceptionHolder != null);
2443 // No locking necessary since child task is finished adding exceptions
2444 // and concurrent CreateExceptionObject() calls do not constitute
2445 // a concurrency hazard.
2446 AddException(exceptionHolder.CreateExceptionObject(false, null));
2451 // Reduce memory pressure by getting rid of the array
2452 props.m_exceptionalChildren = null;
2457 /// Special purpose Finish() entry point to be used when the task delegate throws a ThreadAbortedException
2458 /// This makes a note in the state flags so that we avoid any costly synchronous operations in the finish codepath
2459 /// such as inlined continuations
2461 /// <param name="bTAEAddedToExceptionHolder">
2462 /// Indicates whether the ThreadAbortException was added to this task's exception holder.
2463 /// This should always be true except for the case of non-root self replicating task copies.
2465 /// <param name="delegateRan">Whether the delegate was executed.</param>
2466 internal void FinishThreadAbortedTask(bool bTAEAddedToExceptionHolder, bool delegateRan)
2468 Contract.Assert(!bTAEAddedToExceptionHolder || (m_contingentProperties != null && m_contingentProperties.m_exceptionsHolder != null),
2469 "FinishThreadAbortedTask() called on a task whose exception holder wasn't initialized");
2471 // this will only be false for non-root self replicating task copies, because all of their exceptions go to the root task.
2472 if (bTAEAddedToExceptionHolder)
2473 m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
2475 // If this method has already been called for this task, or if this task has already completed, then
2476 // return before actually calling Finish().
2477 if (!AtomicStateUpdate(TASK_STATE_THREAD_WAS_ABORTED,
2478 TASK_STATE_THREAD_WAS_ABORTED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED))
2483 Finish(delegateRan);
2489 /// Executes the task. This method will only be called once, and handles bookeeping associated with
2490 /// self-replicating tasks, in addition to performing necessary exception marshaling.
2492 private void Execute()
2494 if (IsSelfReplicatingRoot)
2496 ExecuteSelfReplicating(this);
2504 catch (ThreadAbortException tae)
2506 // Don't record the TAE or call FinishThreadAbortedTask for a child replica task --
2507 // it's already been done downstream.
2508 if (!IsChildReplica)
2510 // Record this exception in the task's exception list
2511 HandleException(tae);
2513 // This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to
2514 // skip the regular Finish codepath. In order not to leave the task unfinished, we now call
2515 // FinishThreadAbortedTask here.
2516 FinishThreadAbortedTask(true, true);
2519 catch (Exception exn)
2521 // Record this exception in the task's exception list
2522 HandleException(exn);
2527 // Allows (internal) deriving classes to support limited replication.
2528 // (By default, replication is basically unlimited).
2529 internal virtual bool ShouldReplicate()
2534 // Allows (internal) deriving classes to instantiate the task replica as a Task super class of their choice
2535 // (By default, we create a regular Task instance)
2536 internal virtual Task CreateReplicaTask(Action<object> taskReplicaDelegate, Object stateObject, Task parentTask, TaskScheduler taskScheduler,
2537 TaskCreationOptions creationOptionsForReplica, InternalTaskOptions internalOptionsForReplica)
2539 return new Task(taskReplicaDelegate, stateObject, parentTask, default(CancellationToken),
2540 creationOptionsForReplica, internalOptionsForReplica, parentTask.ExecutingTaskScheduler);
2543 // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
2544 internal virtual Object SavedStateForNextReplica
2546 get { return null; }
2548 set { /*do nothing*/ }
2551 // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
2552 internal virtual Object SavedStateFromPreviousReplica
2554 get { return null; }
2556 set { /*do nothing*/ }
2559 // Allows internal deriving classes to support replicas that exit prematurely and want to hand over the child replica that they
2560 // had queued, so that the replacement replica can work with that child task instead of queuing up yet another one
2561 internal virtual Task HandedOverChildReplica
2563 get { return null; }
2565 set { /* do nothing*/ }
2568 private static void ExecuteSelfReplicating(Task root)
2570 TaskCreationOptions creationOptionsForReplicas = root.CreationOptions | TaskCreationOptions.AttachedToParent;
2571 InternalTaskOptions internalOptionsForReplicas =
2572 InternalTaskOptions.ChildReplica | // child replica flag disables self replication for the replicas themselves.
2573 InternalTaskOptions.SelfReplicating | // we still want to identify this as part of a self replicating group
2574 InternalTaskOptions.QueuedByRuntime; // we queue and cancel these tasks internally, so don't allow CT registration to take place
2577 // Important Note: The child replicas we launch from here will be attached the root replica (by virtue of the root.CreateReplicaTask call)
2578 // because we need the root task to receive all their exceptions, and to block until all of them return
2581 // This variable is captured in a closure and shared among all replicas.
2582 bool replicasAreQuitting = false;
2584 // Set up a delegate that will form the body of the root and all recursively created replicas.
2585 Action<object> taskReplicaDelegate = null;
2586 taskReplicaDelegate = delegate
2588 Task currentTask = Task.InternalCurrent;
2591 // Check if a child task has been handed over by a prematurely quiting replica that we might be a replacement for.
2592 Task childTask = currentTask.HandedOverChildReplica;
2594 if (childTask == null)
2596 // Apparently we are not a replacement task. This means we need to queue up a child task for replication to progress
2598 // Down-counts a counter in the root task.
2599 if (!root.ShouldReplicate()) return;
2601 // If any of the replicas have quit, we will do so ourselves.
2602 if (Volatile.Read(ref replicasAreQuitting))
2607 // Propagate a copy of the context from the root task. It may be null if flow was suppressed.
2608 ExecutionContext creatorContext = root.CapturedContext;
2611 childTask = root.CreateReplicaTask(taskReplicaDelegate, root.m_stateObject, root, root.ExecutingTaskScheduler,
2612 creationOptionsForReplicas, internalOptionsForReplicas);
2614 childTask.CapturedContext = CopyExecutionContext(creatorContext);
2616 childTask.ScheduleAndStart(false);
2621 // Finally invoke the meat of the task.
2622 // Note that we are directly calling root.InnerInvoke() even though we are currently be in the action delegate of a child replica
2623 // This is because the actual work was passed down in that delegate, and the action delegate of the child replica simply contains this
2624 // replication control logic.
2627 // passing in currentTask only so that the parallel debugger can find it
2628 root.InnerInvokeWithArg(currentTask);
2630 catch (Exception exn)
2632 // Record this exception in the root task's exception list
2633 root.HandleException(exn);
2635 if (exn is ThreadAbortException)
2637 // If this is a ThreadAbortException it will escape this catch clause, causing us to skip the regular Finish codepath
2638 // In order not to leave the task unfinished, we now call FinishThreadAbortedTask here
2639 currentTask.FinishThreadAbortedTask(false, true);
2643 Object savedState = currentTask.SavedStateForNextReplica;
2645 // check for premature exit
2646 if (savedState != null)
2648 // the replica decided to exit early
2649 // we need to queue up a replacement, attach the saved state, and yield the thread right away
2651 Task replacementReplica = root.CreateReplicaTask(taskReplicaDelegate, root.m_stateObject, root, root.ExecutingTaskScheduler,
2652 creationOptionsForReplicas, internalOptionsForReplicas);
2654 // Propagate a copy of the context from the root task to the replacement task
2655 ExecutionContext creatorContext = root.CapturedContext;
2656 replacementReplica.CapturedContext = CopyExecutionContext(creatorContext);
2658 replacementReplica.HandedOverChildReplica = childTask;
2659 replacementReplica.SavedStateFromPreviousReplica = savedState;
2661 replacementReplica.ScheduleAndStart(false);
2665 // The replica finished normally, which means it can't find more work to grab.
2666 // Time to mark replicas quitting
2668 replicasAreQuitting = true;
2670 // InternalCancel() could conceivably throw in the underlying scheduler's TryDequeue() method.
2671 // If it does, then make sure that we record it.
2674 childTask.InternalCancel(true);
2678 // Apparently TryDequeue threw an exception. Before propagating that exception, InternalCancel should have
2679 // attempted an atomic state transition and a call to CancellationCleanupLogic() on this task. So we know
2680 // the task was properly cleaned up if it was possible.
2682 // Now all we need to do is to Record the exception in the root task.
2684 root.HandleException(e);
2687 // No specific action needed if the child could not be canceled
2688 // because we attached it to the root task, which should therefore be receiving any exceptions from the child,
2689 // and root.wait will not return before this child finishes anyway.
2695 // Now we execute as the root task
2697 taskReplicaDelegate(null);
2701 /// IThreadPoolWorkItem override, which is the entry function for this task when the TP scheduler decides to run it.
2705 void IThreadPoolWorkItem.ExecuteWorkItem()
2707 ExecuteEntry(false);
2711 /// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem. This may occur
2712 /// before Task would otherwise be able to observe it.
2715 void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
2717 // If the task has marked itself as Completed, then it either a) already observed this exception (so we shouldn't handle it here)
2718 // or b) completed before the exception ocurred (in which case it shouldn't count against this Task).
2721 HandleException(tae);
2722 FinishThreadAbortedTask(true, false);
2727 /// Outermost entry function to execute this task. Handles all aspects of executing a task on the caller thread.
2728 /// Currently this is called by IThreadPoolWorkItem.ExecuteWorkItem(), and TaskManager.TryExecuteInline.
2731 /// <param name="bPreventDoubleExecution"> Performs atomic updates to prevent double execution. Should only be set to true
2732 /// in codepaths servicing user provided TaskSchedulers. The ConcRT or ThreadPool schedulers don't need this. </param>
2733 [SecuritySafeCritical]
2734 internal bool ExecuteEntry(bool bPreventDoubleExecution)
2736 if (bPreventDoubleExecution || ((Options & (TaskCreationOptions)InternalTaskOptions.SelfReplicating) != 0))
2738 int previousState = 0;
2740 // Do atomic state transition from queued to invoked. If we observe a task that's already invoked,
2741 // we will return false so that TaskScheduler.ExecuteTask can throw an exception back to the custom scheduler.
2742 // However we don't want this exception to be throw if the task was already canceled, because it's a
2743 // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler)
2744 if (!AtomicStateUpdate(TASK_STATE_DELEGATE_INVOKED,
2745 TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK,
2746 ref previousState) && (previousState & TASK_STATE_CANCELED) == 0)
2748 // This task has already been invoked. Don't invoke it again.
2754 // Remember that we started running the task delegate.
2755 m_stateFlags |= TASK_STATE_DELEGATE_INVOKED;
2758 if (!IsCancellationRequested && !IsCanceled)
2760 ExecuteWithThreadLocal(ref t_currentTask);
2762 else if (!IsCanceled)
2764 int prevState = Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED);
2765 if ((prevState & TASK_STATE_CANCELED) == 0)
2767 CancellationCleanupLogic();
2774 // A trick so we can refer to the TLS slot with a byref.
2776 private void ExecuteWithThreadLocal(ref Task currentTaskSlot)
2778 // Remember the current task so we can restore it after running, and then
2779 Task previousTask = currentTaskSlot;
2781 // ETW event for Task Started
2782 var etwLog = TplEtwProvider.Log;
2783 Guid savedActivityID = new Guid();
2784 bool etwIsEnabled = etwLog.IsEnabled();
2787 if (etwLog.TasksSetActivityIds)
2788 EventSource.SetCurrentThreadActivityId(TplEtwProvider.CreateGuidForTaskID(this.Id), out savedActivityID);
2789 // previousTask holds the actual "current task" we want to report in the event
2790 if (previousTask != null)
2791 etwLog.TaskStarted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id);
2793 etwLog.TaskStarted(TaskScheduler.Current.Id, 0, this.Id);
2796 if (AsyncCausalityTracer.LoggingOn)
2797 AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.Execution);
2802 // place the current task into TLS.
2803 currentTaskSlot = this;
2805 ExecutionContext ec = CapturedContext;
2808 // No context, just run the task directly.
2813 if (IsSelfReplicatingRoot || IsChildReplica)
2815 CapturedContext = CopyExecutionContext(ec);
2818 // Run the task. We need a simple shim that converts the
2819 // object back into a Task object, so that we can Execute it.
2821 // Lazily initialize the callback delegate; benign ----
2822 var callback = s_ecCallback;
2823 if (callback == null) s_ecCallback = callback = new ContextCallback(ExecutionContextCallback);
2825 ExecutionContext.Run(ec, callback, this);
2827 ExecutionContext.Run(ec, callback, this, true);
2831 if (AsyncCausalityTracer.LoggingOn)
2832 AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.Execution);
2838 currentTaskSlot = previousTask;
2840 // ETW event for Task Completed
2843 // previousTask holds the actual "current task" we want to report in the event
2844 if (previousTask != null)
2845 etwLog.TaskCompleted(previousTask.m_taskScheduler.Id, previousTask.Id, this.Id, IsFaulted);
2847 etwLog.TaskCompleted(TaskScheduler.Current.Id, 0, this.Id, IsFaulted);
2849 if (etwLog.TasksSetActivityIds)
2850 EventSource.SetCurrentThreadActivityId(savedActivityID);
2856 // Cached callback delegate that's lazily initialized due to ContextCallback being SecurityCritical
2858 private static ContextCallback s_ecCallback;
2861 private static void ExecutionContextCallback(object obj)
2863 Task task = obj as Task;
2864 Contract.Assert(task != null, "expected a task object");
2870 /// The actual code which invokes the body of the task. This can be overriden in derived types.
2872 internal virtual void InnerInvoke()
2874 // Invoke the delegate
2875 Contract.Assert(m_action != null, "Null action in InnerInvoke()");
2876 var action = m_action as Action;
2882 var actionWithState = m_action as Action<object>;
2883 if (actionWithState != null)
2885 actionWithState(m_stateObject);
2888 Contract.Assert(false, "Invalid m_action in Task");
2892 /// Alternate InnerInvoke prototype to be called from ExecuteSelfReplicating() so that
2893 /// the Parallel Debugger can discover the actual task being invoked.
2894 /// Details: Here, InnerInvoke is actually being called on the rootTask object while we are actually executing the
2895 /// childTask. And the debugger needs to discover the childTask, so we pass that down as an argument.
2896 /// The NoOptimization and NoInlining flags ensure that the childTask pointer is retained, and that this
2897 /// function appears on the callstack.
2899 /// <param name="childTask"></param>
2900 [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
2901 internal void InnerInvokeWithArg(Task childTask)
2907 /// Performs whatever handling is necessary for an unhandled exception. Normally
2908 /// this just entails adding the exception to the holder object.
2910 /// <param name="unhandledException">The exception that went unhandled.</param>
2911 private void HandleException(Exception unhandledException)
2913 Contract.Requires(unhandledException != null);
2915 OperationCanceledException exceptionAsOce = unhandledException as OperationCanceledException;
2916 if (exceptionAsOce != null && IsCancellationRequested &&
2917 m_contingentProperties.m_cancellationToken == exceptionAsOce.CancellationToken)
2919 // All conditions are satisfied for us to go into canceled state in Finish().
2920 // Mark the acknowledgement. The exception is also stored to enable it to be
2921 // the exception propagated from an await.
2923 SetCancellationAcknowledged();
2924 AddException(exceptionAsOce, representsCancellation: true);
2928 // Other exceptions, including any OCE from the task that doesn't match the tasks' own CT,
2929 // or that gets thrown without the CT being set will be treated as an ordinary exception
2930 // and added to the aggregate.
2932 AddException(unhandledException);
2936 #region Await Support
2937 /// <summary>Gets an awaiter used to await this <see cref="System.Threading.Tasks.Task"/>.</summary>
2938 /// <returns>An awaiter instance.</returns>
2939 /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks>
2940 public TaskAwaiter GetAwaiter()
2942 return new TaskAwaiter(this);
2945 /// <summary>Configures an awaiter used to await this <see cref="System.Threading.Tasks.Task"/>.</summary>
2946 /// <param name="continueOnCapturedContext">
2947 /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
2949 /// <returns>An object used to await this task.</returns>
2950 public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext)
2952 return new ConfiguredTaskAwaitable(this, continueOnCapturedContext);
2956 /// Sets a continuation onto the <see cref="System.Threading.Tasks.Task"/>.
2957 /// The continuation is scheduled to run in the current synchronization context is one exists,
2958 /// otherwise in the current task scheduler.
2960 /// <param name="continuationAction">The action to invoke when the <see cref="System.Threading.Tasks.Task"/> has completed.</param>
2961 /// <param name="continueOnCapturedContext">
2962 /// true to attempt to marshal the continuation back to the original context captured; otherwise, false.
2964 /// <param name="flowExecutionContext">Whether to flow ExecutionContext across the await.</param>
2965 /// <param name="stackMark">A stack crawl mark tied to execution context.</param>
2966 /// <exception cref="System.InvalidOperationException">The awaiter was not properly initialized.</exception>
2968 internal void SetContinuationForAwait(
2969 Action continuationAction, bool continueOnCapturedContext, bool flowExecutionContext, ref StackCrawlMark stackMark)
2971 Contract.Requires(continuationAction != null);
2973 // Create the best AwaitTaskContinuation object given the request.
2974 // If this remains null by the end of the function, we can use the
2975 // continuationAction directly without wrapping it.
2976 TaskContinuation tc = null;
2978 // If the user wants the continuation to run on the current "context" if there is one...
2979 if (continueOnCapturedContext)
2981 // First try getting the current synchronization context.
2982 // If the current context is really just the base SynchronizationContext type,
2983 // which is intended to be equivalent to not having a current SynchronizationContext at all,
2984 // then ignore it. This helps with performance by avoiding unnecessary posts and queueing
2985 // of work items, but more so it ensures that if code happens to publish the default context
2986 // as current, it won't prevent usage of a current task scheduler if there is one.
2987 var syncCtx = SynchronizationContext.CurrentNoFlow;
2988 if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
2990 tc = new SynchronizationContextAwaitTaskContinuation(syncCtx, continuationAction, flowExecutionContext, ref stackMark);
2994 // If there was no SynchronizationContext, then try for the current scheduler.
2995 // We only care about it if it's not the default.
2996 var scheduler = TaskScheduler.InternalCurrent;
2997 if (scheduler != null && scheduler != TaskScheduler.Default)
2999 tc = new TaskSchedulerAwaitTaskContinuation(scheduler, continuationAction, flowExecutionContext, ref stackMark);
3004 if (tc == null && flowExecutionContext)
3006 // We're targeting the default scheduler, so we can use the faster path
3007 // that assumes the default, and thus we don't need to store it. If we're flowing
3008 // ExecutionContext, we need to capture it and wrap it in an AwaitTaskContinuation.
3009 // Otherwise, we're targeting the default scheduler and we don't need to flow ExecutionContext, so
3010 // we don't actually need a continuation object. We can just store/queue the action itself.
3011 tc = new AwaitTaskContinuation(continuationAction, flowExecutionContext: true, stackMark: ref stackMark);
3014 // Now register the continuation, and if we couldn't register it because the task is already completing,
3015 // process the continuation directly (in which case make sure we schedule the continuation
3016 // rather than inlining it, the latter of which could result in a rare but possible stack overflow).
3019 if (!AddTaskContinuation(tc, addBeforeOthers: false))
3020 tc.Run(this, bCanInlineContinuationTask: false);
3024 Contract.Assert(!flowExecutionContext, "We already determined we're not required to flow context.");
3025 if (!AddTaskContinuation(continuationAction, addBeforeOthers: false))
3026 AwaitTaskContinuation.UnsafeScheduleAction(continuationAction, this);
3030 /// <summary>Creates an awaitable that asynchronously yields back to the current context when awaited.</summary>
3032 /// A context that, when awaited, will asynchronously transition back into the current context at the
3033 /// time of the await. If the current SynchronizationContext is non-null, that is treated as the current context.
3034 /// Otherwise, TaskScheduler.Current is treated as the current context.
3036 public static YieldAwaitable Yield()
3038 return new YieldAwaitable();
3043 /// Waits for the <see cref="Task"/> to complete execution.
3045 /// <exception cref="T:System.AggregateException">
3046 /// The <see cref="Task"/> was canceled -or- an exception was thrown during
3047 /// the execution of the <see cref="Task"/>.
3054 Wait(Timeout.Infinite, default(CancellationToken));
3057 Contract.Assert(waitResult, "expected wait to succeed");
3062 /// Waits for the <see cref="Task"/> to complete execution.
3064 /// <param name="timeout">
3065 /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
3066 /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
3069 /// true if the <see cref="Task"/> completed execution within the allotted time; otherwise, false.
3071 /// <exception cref="T:System.AggregateException">
3072 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
3075 /// <exception cref="T:System.ArgumentOutOfRangeException">
3076 /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
3077 /// infinite time-out -or- timeout is greater than
3078 /// <see cref="System.Int32.MaxValue"/>.
3080 public bool Wait(TimeSpan timeout)
3082 long totalMilliseconds = (long)timeout.TotalMilliseconds;
3083 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
3085 throw new ArgumentOutOfRangeException("timeout");
3088 return Wait((int)totalMilliseconds, default(CancellationToken));
3093 /// Waits for the <see cref="Task"/> to complete execution.
3095 /// <param name="cancellationToken">
3096 /// A <see cref="CancellationToken"/> to observe while waiting for the task to complete.
3098 /// <exception cref="T:System.OperationCanceledException">
3099 /// The <paramref name="cancellationToken"/> was canceled.
3101 /// <exception cref="T:System.AggregateException">
3102 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
3105 public void Wait(CancellationToken cancellationToken)
3107 Wait(Timeout.Infinite, cancellationToken);
3112 /// Waits for the <see cref="Task"/> to complete execution.
3114 /// <param name="millisecondsTimeout">
3115 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
3116 /// wait indefinitely.</param>
3117 /// <returns>true if the <see cref="Task"/> completed execution within the allotted time; otherwise,
3120 /// <exception cref="T:System.ArgumentOutOfRangeException">
3121 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
3122 /// infinite time-out.
3124 /// <exception cref="T:System.AggregateException">
3125 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
3128 public bool Wait(int millisecondsTimeout)
3130 return Wait(millisecondsTimeout, default(CancellationToken));
3135 /// Waits for the <see cref="Task"/> to complete execution.
3137 /// <param name="millisecondsTimeout">
3138 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
3139 /// wait indefinitely.
3141 /// <param name="cancellationToken">
3142 /// A <see cref="CancellationToken"/> to observe while waiting for the task to complete.
3145 /// true if the <see cref="Task"/> completed execution within the allotted time; otherwise, false.
3147 /// <exception cref="T:System.AggregateException">
3148 /// The <see cref="Task"/> was canceled -or- an exception was thrown during the execution of the <see
3151 /// <exception cref="T:System.ArgumentOutOfRangeException">
3152 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
3153 /// infinite time-out.
3155 /// <exception cref="T:System.OperationCanceledException">
3156 /// The <paramref name="cancellationToken"/> was canceled.
3158 public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
3160 if (millisecondsTimeout < -1)
3162 throw new ArgumentOutOfRangeException("millisecondsTimeout");
3164 Contract.EndContractBlock();
3166 // Return immediately if we know that we've completed "clean" -- no exceptions, no cancellations
3167 // and if no notification to the debugger is required
3168 if (!IsWaitNotificationEnabledOrNotRanToCompletion) // (!DebuggerBitSet && RanToCompletion)
3171 // Wait, and then return if we're still not done.
3172 if (!InternalWait(millisecondsTimeout, cancellationToken))
3175 if (IsWaitNotificationEnabledOrNotRanToCompletion) // avoid a few unnecessary volatile reads if we completed successfully
3177 // Notify the debugger of the wait completion if it's requested such a notification
3178 NotifyDebuggerOfWaitCompletionIfNecessary();
3180 // If cancellation was requested and the task was canceled, throw an
3181 // OperationCanceledException. This is prioritized ahead of the ThrowIfExceptional
3182 // call to bring more determinism to cases where the same token is used to
3183 // cancel the Wait and to cancel the Task. Otherwise, there's a ---- between
3184 // whether the Wait or the Task observes the cancellation request first,
3185 // and different exceptions result from the different cases.
3186 if (IsCanceled) cancellationToken.ThrowIfCancellationRequested();
3188 // If an exception occurred, or the task was cancelled, throw an exception.
3189 ThrowIfExceptional(true);
3192 Contract.Assert((m_stateFlags & TASK_STATE_FAULTED) == 0, "Task.Wait() completing when in Faulted state.");
3197 // Convenience method that wraps any scheduler exception in a TaskSchedulerException
3199 private bool WrappedTryRunInline()
3201 if (m_taskScheduler == null)
3206 return m_taskScheduler.TryRunInline(this, true);
3210 // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown
3211 // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute
3212 if (!(e is ThreadAbortException))
3214 TaskSchedulerException tse = new TaskSchedulerException(e);
3225 /// The core wait function, which is only accesible internally. It's meant to be used in places in TPL code where
3226 /// the current context is known or cached.
3228 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
3229 internal bool InternalWait(int millisecondsTimeout, CancellationToken cancellationToken)
3232 // ETW event for Task Wait Begin
3233 var etwLog = TplEtwProvider.Log;
3234 bool etwIsEnabled = etwLog.IsEnabled();
3237 Task currentTask = Task.InternalCurrent;
3238 etwLog.TaskWaitBegin(
3239 (currentTask != null ? currentTask.m_taskScheduler.Id : TaskScheduler.Default.Id), (currentTask != null ? currentTask.Id : 0),
3240 this.Id, TplEtwProvider.TaskWaitBehavior.Synchronous, 0, System.Threading.Thread.GetDomainID());
3243 bool returnValue = IsCompleted;
3245 // If the event hasn't already been set, we will wait.
3248 // Alert a listening debugger that we can't make forward progress unless it slips threads.
3249 // We call NOCTD for two reasons:
3250 // 1. If the task runs on another thread, then we'll be blocked here indefinitely.
3251 // 2. If the task runs inline but takes some time to complete, it will suffer ThreadAbort with possible state corruption,
3252 // and it is best to prevent this unless the user explicitly asks to view the value with thread-slipping enabled.
3253 Debugger.NotifyOfCrossThreadDependency();
3255 // We will attempt inline execution only if an infinite wait was requested
3256 // Inline execution doesn't make sense for finite timeouts and if a cancellation token was specified
3257 // because we don't know how long the task delegate will take.
3258 if (millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled &&
3259 WrappedTryRunInline() && IsCompleted) // TryRunInline doesn't guarantee completion, as there may be unfinished children.
3265 returnValue = SpinThenBlockingWait(millisecondsTimeout, cancellationToken);
3269 Contract.Assert(IsCompleted || millisecondsTimeout != Timeout.Infinite);
3271 // ETW event for Task Wait End
3274 Task currentTask = Task.InternalCurrent;
3275 if (currentTask != null)
3277 etwLog.TaskWaitEnd(currentTask.m_taskScheduler.Id, currentTask.Id, this.Id);
3281 etwLog.TaskWaitEnd(TaskScheduler.Default.Id, 0, this.Id);
3283 // logically the continuation is empty so we immediately fire
3284 etwLog.TaskWaitContinuationComplete(this.Id);
3290 // An MRES that gets set when Invoke is called. This replaces old logic that looked like this:
3291 // ManualResetEventSlim mres = new ManualResetEventSlim(false, 0);
3292 // Action<Task> completionAction = delegate {mres.Set();}
3293 // AddCompletionAction(completionAction);
3295 // SetOnInvokeMres mres = new SetOnInvokeMres();
3296 // AddCompletionAction(mres, addBeforeOthers: true);
3297 // which saves a couple of allocations.
3299 // Used in SpinThenBlockingWait (below), but could be seen as a general purpose mechanism.
3300 private sealed class SetOnInvokeMres : ManualResetEventSlim, ITaskCompletionAction
3302 internal SetOnInvokeMres() : base(false, 0) { }
3303 public void Invoke(Task completingTask) { Set(); }
3307 /// Waits for the task to complete, for a timeout to occur, or for cancellation to be requested.
3308 /// The method first spins and then falls back to blocking on a new event.
3310 /// <param name="millisecondsTimeout">The timeout.</param>
3311 /// <param name="cancellationToken">The token.</param>
3312 /// <returns>true if the task is completed; otherwise, false.</returns>
3313 private bool SpinThenBlockingWait(int millisecondsTimeout, CancellationToken cancellationToken)
3315 bool infiniteWait = millisecondsTimeout == Timeout.Infinite;
3316 uint startTimeTicks = infiniteWait ? 0 : (uint)Environment.TickCount;
3317 bool returnValue = SpinWait(millisecondsTimeout);
3320 var mres = new SetOnInvokeMres();
3323 AddCompletionAction(mres, addBeforeOthers: true);
3326 returnValue = mres.Wait(Timeout.Infinite, cancellationToken);
3330 uint elapsedTimeTicks = ((uint)Environment.TickCount) - startTimeTicks;
3331 if (elapsedTimeTicks < millisecondsTimeout)
3333 returnValue = mres.Wait((int)(millisecondsTimeout - elapsedTimeTicks), cancellationToken);
3339 if (!IsCompleted) RemoveContinuation(mres);
3340 // Don't Dispose of the MRES, because the continuation off of this task may
3341 // still be running. This is ok, however, as we never access the MRES' WaitHandle,
3342 // and thus no finalizable resources are actually allocated.
3349 /// Spins briefly while checking IsCompleted
3351 /// <param name="millisecondsTimeout">The timeout.</param>
3352 /// <returns>true if the task is completed; otherwise, false.</returns>
3353 /// <exception cref="System.OperationCanceledException">The wait was canceled.</exception>
3354 private bool SpinWait(int millisecondsTimeout)
3356 if (IsCompleted) return true;
3358 if (millisecondsTimeout == 0)
3360 // For 0-timeouts, we just return immediately.
3364 //This code is pretty similar to the custom spinning in MRES except there is no yieling after we exceed the spin count
3365 int spinCount = PlatformHelper.IsSingleProcessor ? 1 : System.Threading.SpinWait.YIELD_THRESHOLD; //spin only once if we are running on a single CPU
3366 for (int i = 0; i < spinCount; i++)
3373 if (i == spinCount / 2)
3379 Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i));
3388 /// Cancels the <see cref="Task"/>.
3390 /// <param name="bCancelNonExecutingOnly">
3391 /// Indicates whether we should only cancel non-invoked tasks.
3392 /// For the default scheduler this option will only be serviced through TryDequeue.
3393 /// For custom schedulers we also attempt an atomic state transition.
3395 /// <returns>true if the task was successfully canceled; otherwise, false.</returns>
3396 [SecuritySafeCritical]
3397 internal bool InternalCancel(bool bCancelNonExecutingOnly)
3399 Contract.Requires((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) == 0, "Task.InternalCancel() did not expect promise-style task");
3401 bool bPopSucceeded = false;
3402 bool mustCleanup = false;
3404 TaskSchedulerException tse = null;
3406 // If started, and running in a task context, we can try to pop the chore.
3407 if ((m_stateFlags & TASK_STATE_STARTED) != 0)
3409 TaskScheduler ts = m_taskScheduler;
3413 bPopSucceeded = (ts != null) && ts.TryDequeue(this);
3417 // TryDequeue threw. We don't know whether the task was properly dequeued or not. So we must let the rest of
3418 // the cancellation logic run its course (record the request, attempt atomic state transition and do cleanup where appropriate)
3419 // Here we will only record a TaskSchedulerException, which will later be thrown at function exit.
3421 if (!(e is ThreadAbortException))
3423 tse = new TaskSchedulerException(e);
3427 bool bRequiresAtomicStartTransition = (ts != null && ts.RequiresAtomicStartTransition) || ((Options & (TaskCreationOptions)InternalTaskOptions.SelfReplicating) != 0);
3429 if (!bPopSucceeded && bCancelNonExecutingOnly && bRequiresAtomicStartTransition)
3431 // The caller requested cancellation of non-invoked tasks only, and TryDequeue was one way of doing it...
3432 // Since that seems to have failed, we should now try an atomic state transition (from non-invoked state to canceled)
3433 // An atomic transition here is only safe if we know we're on a custom task scheduler, which also forces a CAS on ExecuteEntry
3435 // Even though this task can't have any children, we should be ready for handling any continuations that
3436 // may be attached to it (although currently
3437 // So we need to remeber whether we actually did the flip, so we can do clean up (finish continuations etc)
3438 mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_DELEGATE_INVOKED | TASK_STATE_CANCELED);
3441 // PS: This is slightly different from the regular cancellation codepath
3442 // since we record the cancellation request *after* doing the state transition.
3443 // However that shouldn't matter too much because the task was never invoked, thus can't have children
3448 if (!bCancelNonExecutingOnly || bPopSucceeded || mustCleanup)
3450 // Record the cancellation request.
3451 RecordInternalCancellationRequest();
3453 // Determine whether we need to clean up
3454 // This will be the case
3455 // 1) if we were able to pop, and we win the ---- to update task state to TASK_STATE_CANCELED
3456 // 2) if the task seems to be yet unstarted, and we win the ---- to transition to
3457 // TASK_STATE_CANCELED before anyone else can transition into _STARTED or _CANCELED or
3458 // _RAN_TO_COMPLETION or _FAULTED
3459 // Note that we do not check for TASK_STATE_COMPLETION_RESERVED. That only applies to promise-style
3460 // tasks, and a promise-style task should not enter into this codepath.
3463 // hitting this would mean something wrong with the AtomicStateUpdate above
3464 Contract.Assert(!mustCleanup, "Possibly an invalid state transition call was made in InternalCancel()");
3466 // Include TASK_STATE_DELEGATE_INVOKED in "illegal" bits to protect against the situation where
3467 // TS.TryDequeue() returns true but the task is still left on the queue.
3468 mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED, TASK_STATE_CANCELED | TASK_STATE_DELEGATE_INVOKED);
3470 else if (!mustCleanup && (m_stateFlags & TASK_STATE_STARTED) == 0)
3472 mustCleanup = AtomicStateUpdate(TASK_STATE_CANCELED,
3473 TASK_STATE_CANCELED | TASK_STATE_STARTED | TASK_STATE_RAN_TO_COMPLETION |
3474 TASK_STATE_FAULTED | TASK_STATE_DELEGATE_INVOKED);
3477 // do the cleanup (i.e. set completion event and finish continuations)
3480 CancellationCleanupLogic();
3487 return (mustCleanup);
3490 // Breaks out logic for recording a cancellation request
3491 internal void RecordInternalCancellationRequest()
3493 // Record the cancellation request.
3494 var props = EnsureContingentPropertiesInitialized(needsProtection: true);
3495 props.m_internalCancellationRequested = CANCELLATION_REQUESTED;
3499 // Breaks out logic for recording a cancellation request
3500 // This overload should only be used for promise tasks where no cancellation token
3501 // was supplied when the task was created.
3502 internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord)
3504 RecordInternalCancellationRequest();
3506 Contract.Assert((Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0, "Task.RecordInternalCancellationRequest(CancellationToken) only valid for promise-style task");
3507 Contract.Assert(m_contingentProperties.m_cancellationToken == default(CancellationToken));
3509 // Store the supplied cancellation token as this task's token.
3510 // Waiting on this task will then result in an OperationCanceledException containing this token.
3511 if (tokenToRecord != default(CancellationToken))
3513 m_contingentProperties.m_cancellationToken = tokenToRecord;
3517 // Breaks out logic for recording a cancellation request
3518 // This overload should only be used for promise tasks where no cancellation token
3519 // was supplied when the task was created.
3520 internal void RecordInternalCancellationRequest(CancellationToken tokenToRecord, object cancellationException)
3522 RecordInternalCancellationRequest(tokenToRecord);
3524 // Store the supplied cancellation exception
3525 if (cancellationException != null)
3528 var oce = cancellationException as OperationCanceledException;
3531 var edi = cancellationException as ExceptionDispatchInfo;
3532 Contract.Assert(edi != null, "Expected either an OCE or an EDI");
3533 oce = edi.SourceException as OperationCanceledException;
3534 Contract.Assert(oce != null, "Expected EDI to contain an OCE");
3536 Contract.Assert(oce.CancellationToken == tokenToRecord,
3537 "Expected OCE's token to match the provided token.");
3539 AddException(cancellationException, representsCancellation: true);
3543 // ASSUMES THAT A SUCCESSFUL CANCELLATION HAS JUST OCCURRED ON THIS TASK!!!
3544 // And this method should be called at most once per task.
3545 internal void CancellationCleanupLogic()
3547 Contract.Assert((m_stateFlags & (TASK_STATE_CANCELED | TASK_STATE_COMPLETION_RESERVED)) != 0, "Task.CancellationCleanupLogic(): Task not canceled or reserved.");
3548 // I'd like to do this, but there is a small window for a race condition. If someone calls Wait() between InternalCancel() and
3549 // here, that will set m_completionEvent, leading to a meaningless/harmless assertion.
3550 //Contract.Assert((m_completionEvent == null) || !m_completionEvent.IsSet, "Task.CancellationCleanupLogic(): Completion event already set.");
3552 // This may have been set already, but we need to make sure.
3553 Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_CANCELED);
3555 // Fire completion event if it has been lazily initialized
3556 var cp = m_contingentProperties;
3560 cp.DeregisterCancellationCallback();
3563 if (AsyncCausalityTracer.LoggingOn)
3564 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Canceled);
3566 if (Task.s_asyncDebuggingEnabled)
3568 RemoveFromActiveTasks(this.Id);
3571 // Notify parents, fire continuations, other cleanup.
3577 /// Sets the task's cancellation acknowledged flag.
3579 private void SetCancellationAcknowledged()
3581 Contract.Assert(this == Task.InternalCurrent, "SetCancellationAcknowledged() should only be called while this is still the current task");
3582 Contract.Assert(IsCancellationRequested, "SetCancellationAcknowledged() should not be called if the task's CT wasn't signaled");
3584 m_stateFlags |= TASK_STATE_CANCELLATIONACKNOWLEDGED;
3589 // Continuation passing functionality (aka ContinueWith)
3596 /// Runs all of the continuations, as appropriate.
3598 [SecuritySafeCritical] // for AwaitTaskContinuation.RunOrScheduleAction
3599 internal void FinishContinuations()
3601 // Atomically store the fact that this task is completing. From this point on, the adding of continuations will
3602 // result in the continuations being run/launched directly rather than being added to the continuation list.
3603 object continuationObject = Interlocked.Exchange(ref m_continuationObject, s_taskCompletionSentinel);
3605 TplEtwProvider.Log.RunningContinuation(Id, continuationObject);
3608 // If continuationObject == null, then we don't have any continuations to process
3609 if (continuationObject != null)
3612 if (AsyncCausalityTracer.LoggingOn)
3613 AsyncCausalityTracer.TraceSynchronousWorkStart(CausalityTraceLevel.Required, this.Id, CausalitySynchronousWork.CompletionNotification);
3615 // Skip synchronous execution of continuations if this task's thread was aborted
3616 bool bCanInlineContinuations = !(((m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED) != 0) ||
3617 (Thread.CurrentThread.ThreadState == ThreadState.AbortRequested) ||
3618 ((m_stateFlags & (int)TaskCreationOptions.RunContinuationsAsynchronously) != 0));
3620 // Handle the single-Action case
3621 Action singleAction = continuationObject as Action;
3622 if (singleAction != null)
3624 AwaitTaskContinuation.RunOrScheduleAction(singleAction, bCanInlineContinuations, ref t_currentTask);
3625 LogFinishCompletionNotification();
3629 // Handle the single-ITaskCompletionAction case
3630 ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction;
3631 if (singleTaskCompletionAction != null)
3633 singleTaskCompletionAction.Invoke(this);
3634 LogFinishCompletionNotification();
3638 // Handle the single-TaskContinuation case
3639 TaskContinuation singleTaskContinuation = continuationObject as TaskContinuation;
3640 if (singleTaskContinuation != null)
3642 singleTaskContinuation.Run(this, bCanInlineContinuations);
3643 LogFinishCompletionNotification();
3647 // Not a single; attempt to cast as list
3648 List<object> continuations = continuationObject as List<object>;
3650 if (continuations == null)
3652 LogFinishCompletionNotification();
3653 return; // Not a single or a list; just return
3657 // Begin processing of continuation list
3660 // Wait for any concurrent adds or removes to be retired
3661 lock (continuations) { }
3662 int continuationCount = continuations.Count;
3664 // Fire the asynchronous continuations first ...
3665 for (int i = 0; i < continuationCount; i++)
3667 // Synchronous continuation tasks will have the ExecuteSynchronously option,
3668 // and we're looking for asynchronous tasks...
3669 var tc = continuations[i] as StandardTaskContinuation;
3670 if (tc != null && (tc.m_options & TaskContinuationOptions.ExecuteSynchronously) == 0)
3673 TplEtwProvider.Log.RunningContinuationList(Id, i, tc);
3675 continuations[i] = null; // so that we can skip this later
3676 tc.Run(this, bCanInlineContinuations);
3680 // ... and then fire the synchronous continuations (if there are any).
3681 // This includes ITaskCompletionAction, AwaitTaskContinuations, and
3682 // Action delegates, which are all by default implicitly synchronous.
3683 for (int i = 0; i < continuationCount; i++)
3685 object currentContinuation = continuations[i];
3686 if (currentContinuation == null) continue;
3687 continuations[i] = null; // to enable free'ing up memory earlier
3689 TplEtwProvider.Log.RunningContinuationList(Id, i, currentContinuation);
3692 // If the continuation is an Action delegate, it came from an await continuation,
3693 // and we should use AwaitTaskContinuation to run it.
3694 Action ad = currentContinuation as Action;
3697 AwaitTaskContinuation.RunOrScheduleAction(ad, bCanInlineContinuations, ref t_currentTask);
3701 // If it's a TaskContinuation object of some kind, invoke it.
3702 TaskContinuation tc = currentContinuation as TaskContinuation;
3705 // We know that this is a synchronous continuation because the
3706 // asynchronous ones have been weeded out
3707 tc.Run(this, bCanInlineContinuations);
3709 // Otherwise, it must be an ITaskCompletionAction, so invoke it.
3712 Contract.Assert(currentContinuation is ITaskCompletionAction, "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction");
3713 var action = (ITaskCompletionAction)currentContinuation;
3714 action.Invoke(this);
3719 LogFinishCompletionNotification();
3723 private void LogFinishCompletionNotification()
3725 if (AsyncCausalityTracer.LoggingOn)
3726 AsyncCausalityTracer.TraceSynchronousWorkCompletion(CausalityTraceLevel.Required, CausalitySynchronousWork.CompletionNotification);
3729 #region Continuation methods
3731 #region Action<Task> continuation
3733 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3735 /// <param name="continuationAction">
3736 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3737 /// passed the completed task as an argument.
3739 /// <returns>A new continuation <see cref="Task"/>.</returns>
3741 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3742 /// completed, whether it completes due to running to completion successfully, faulting due to an
3743 /// unhandled exception, or exiting out early due to being canceled.
3745 /// <exception cref="T:System.ArgumentNullException">
3746 /// The <paramref name="continuationAction"/> argument is null.
3748 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3749 public Task ContinueWith(Action<Task> continuationAction)
3751 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3752 return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
3756 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3758 /// <param name="continuationAction">
3759 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3760 /// passed the completed task as an argument.
3762 /// <param name="cancellationToken"> The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3763 /// <returns>A new continuation <see cref="Task"/>.</returns>
3765 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3766 /// completed, whether it completes due to running to completion successfully, faulting due to an
3767 /// unhandled exception, or exiting out early due to being canceled.
3769 /// <exception cref="T:System.ArgumentNullException">
3770 /// The <paramref name="continuationAction"/> argument is null.
3772 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3773 /// has already been disposed.
3775 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3776 public Task ContinueWith(Action<Task> continuationAction, CancellationToken cancellationToken)
3778 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3779 return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
3783 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3785 /// <param name="continuationAction">
3786 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3787 /// passed the completed task as an argument.
3789 /// <param name="scheduler">
3790 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
3792 /// <returns>A new continuation <see cref="Task"/>.</returns>
3794 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3795 /// completed, whether it completes due to running to completion successfully, faulting due to an
3796 /// unhandled exception, or exiting out early due to being canceled.
3798 /// <exception cref="T:System.ArgumentNullException">
3799 /// The <paramref name="continuationAction"/> argument is null.
3801 /// <exception cref="T:System.ArgumentNullException">
3802 /// The <paramref name="scheduler"/> argument is null.
3804 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3805 public Task ContinueWith(Action<Task> continuationAction, TaskScheduler scheduler)
3807 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3808 return ContinueWith(continuationAction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
3812 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3814 /// <param name="continuationAction">
3815 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3816 /// passed the completed task as an argument.
3818 /// <param name="continuationOptions">
3819 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3821 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3822 /// well as execution options, such as <see
3823 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3825 /// <returns>A new continuation <see cref="Task"/>.</returns>
3827 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3828 /// completed. If the continuation criteria specified through the <paramref
3829 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
3830 /// instead of scheduled.
3832 /// <exception cref="T:System.ArgumentNullException">
3833 /// The <paramref name="continuationAction"/> argument is null.
3835 /// <exception cref="T:System.ArgumentOutOfRangeException">
3836 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3837 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3839 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3840 public Task ContinueWith(Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
3842 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3843 return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
3847 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3849 /// <param name="continuationAction">
3850 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3851 /// passed the completed task as an argument.
3853 /// <param name="continuationOptions">
3854 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
3856 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
3857 /// well as execution options, such as <see
3858 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
3860 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3861 /// <param name="scheduler">
3862 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
3865 /// <returns>A new continuation <see cref="Task"/>.</returns>
3867 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3868 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
3869 /// are not met, the continuation task will be canceled instead of scheduled.
3871 /// <exception cref="T:System.ArgumentNullException">
3872 /// The <paramref name="continuationAction"/> argument is null.
3874 /// <exception cref="T:System.ArgumentOutOfRangeException">
3875 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
3876 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
3878 /// <exception cref="T:System.ArgumentNullException">
3879 /// The <paramref name="scheduler"/> argument is null.
3881 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3882 /// has already been disposed.
3884 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3885 public Task ContinueWith(Action<Task> continuationAction, CancellationToken cancellationToken,
3886 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
3888 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3889 return ContinueWith(continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark);
3892 // Same as the above overload, just with a stack mark parameter.
3893 private Task ContinueWith(Action<Task> continuationAction, TaskScheduler scheduler,
3894 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
3896 // Throw on continuation with null action
3897 if (continuationAction == null)
3899 throw new ArgumentNullException("continuationAction");
3902 // Throw on continuation with null TaskScheduler
3903 if (scheduler == null)
3905 throw new ArgumentNullException("scheduler");
3907 Contract.EndContractBlock();
3909 TaskCreationOptions creationOptions;
3910 InternalTaskOptions internalOptions;
3911 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
3913 Task continuationTask = new ContinuationTaskFromTask(
3914 this, continuationAction, null,
3915 creationOptions, internalOptions,
3919 // Register the continuation. If synchronous execution is requested, this may
3920 // actually invoke the continuation before returning.
3921 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
3923 return continuationTask;
3927 #region Action<Task, Object> continuation
3930 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3932 /// <param name="continuationAction">
3933 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3934 /// passed the completed task as and the caller-supplied state object as arguments.
3936 /// <param name="state">An object representing data to be used by the continuation action.</param>
3937 /// <returns>A new continuation <see cref="Task"/>.</returns>
3939 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3940 /// completed, whether it completes due to running to completion successfully, faulting due to an
3941 /// unhandled exception, or exiting out early due to being canceled.
3943 /// <exception cref="T:System.ArgumentNullException">
3944 /// The <paramref name="continuationAction"/> argument is null.
3946 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3947 public Task ContinueWith(Action<Task, Object> continuationAction, Object state)
3949 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3950 return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
3954 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3956 /// <param name="continuationAction">
3957 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3958 /// passed the completed task and the caller-supplied state object as arguments.
3960 /// <param name="state">An object representing data to be used by the continuation action.</param>
3961 /// <param name="cancellationToken"> The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
3962 /// <returns>A new continuation <see cref="Task"/>.</returns>
3964 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3965 /// completed, whether it completes due to running to completion successfully, faulting due to an
3966 /// unhandled exception, or exiting out early due to being canceled.
3968 /// <exception cref="T:System.ArgumentNullException">
3969 /// The <paramref name="continuationAction"/> argument is null.
3971 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
3972 /// has already been disposed.
3974 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
3975 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, CancellationToken cancellationToken)
3977 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
3978 return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
3982 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
3984 /// <param name="continuationAction">
3985 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
3986 /// passed the completed task and the caller-supplied state object as arguments.
3988 /// <param name="state">An object representing data to be used by the continuation action.</param>
3989 /// <param name="scheduler">
3990 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
3992 /// <returns>A new continuation <see cref="Task"/>.</returns>
3994 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
3995 /// completed, whether it completes due to running to completion successfully, faulting due to an
3996 /// unhandled exception, or exiting out early due to being canceled.
3998 /// <exception cref="T:System.ArgumentNullException">
3999 /// The <paramref name="continuationAction"/> argument is null.
4001 /// <exception cref="T:System.ArgumentNullException">
4002 /// The <paramref name="scheduler"/> argument is null.
4004 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4005 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskScheduler scheduler)
4007 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4008 return ContinueWith(continuationAction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
4012 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4014 /// <param name="continuationAction">
4015 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
4016 /// passed the completed task and the caller-supplied state object as arguments.
4018 /// <param name="state">An object representing data to be used by the continuation action.</param>
4019 /// <param name="continuationOptions">
4020 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4022 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4023 /// well as execution options, such as <see
4024 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4026 /// <returns>A new continuation <see cref="Task"/>.</returns>
4028 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
4029 /// completed. If the continuation criteria specified through the <paramref
4030 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
4031 /// instead of scheduled.
4033 /// <exception cref="T:System.ArgumentNullException">
4034 /// The <paramref name="continuationAction"/> argument is null.
4036 /// <exception cref="T:System.ArgumentOutOfRangeException">
4037 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4038 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4040 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4041 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskContinuationOptions continuationOptions)
4043 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4044 return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
4048 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4050 /// <param name="continuationAction">
4051 /// An action to run when the <see cref="Task"/> completes. When run, the delegate will be
4052 /// passed the completed task and the caller-supplied state object as arguments.
4054 /// <param name="state">An object representing data to be used by the continuation action.</param>
4055 /// <param name="continuationOptions">
4056 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4058 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4059 /// well as execution options, such as <see
4060 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4062 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4063 /// <param name="scheduler">
4064 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
4067 /// <returns>A new continuation <see cref="Task"/>.</returns>
4069 /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has
4070 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
4071 /// are not met, the continuation task will be canceled instead of scheduled.
4073 /// <exception cref="T:System.ArgumentNullException">
4074 /// The <paramref name="continuationAction"/> argument is null.
4076 /// <exception cref="T:System.ArgumentOutOfRangeException">
4077 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4078 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4080 /// <exception cref="T:System.ArgumentNullException">
4081 /// The <paramref name="scheduler"/> argument is null.
4083 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4084 /// has already been disposed.
4086 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4087 public Task ContinueWith(Action<Task, Object> continuationAction, Object state, CancellationToken cancellationToken,
4088 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
4090 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4091 return ContinueWith(continuationAction, state, scheduler, cancellationToken, continuationOptions, ref stackMark);
4094 // Same as the above overload, just with a stack mark parameter.
4095 private Task ContinueWith(Action<Task, Object> continuationAction, Object state, TaskScheduler scheduler,
4096 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
4098 // Throw on continuation with null action
4099 if (continuationAction == null)
4101 throw new ArgumentNullException("continuationAction");
4104 // Throw on continuation with null TaskScheduler
4105 if (scheduler == null)
4107 throw new ArgumentNullException("scheduler");
4109 Contract.EndContractBlock();
4111 TaskCreationOptions creationOptions;
4112 InternalTaskOptions internalOptions;
4113 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
4115 Task continuationTask = new ContinuationTaskFromTask(
4116 this, continuationAction, state,
4117 creationOptions, internalOptions,
4121 // Register the continuation. If synchronous execution is requested, this may
4122 // actually invoke the continuation before returning.
4123 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
4125 return continuationTask;
4130 #region Func<Task, TResult> continuation
4133 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4135 /// <typeparam name="TResult">
4136 /// The type of the result produced by the continuation.
4138 /// <param name="continuationFunction">
4139 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4140 /// passed the completed task as an argument.
4142 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4144 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4145 /// completed, whether it completes due to running to completion successfully, faulting due to an
4146 /// unhandled exception, or exiting out early due to being canceled.
4148 /// <exception cref="T:System.ArgumentNullException">
4149 /// The <paramref name="continuationFunction"/> argument is null.
4151 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4152 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction)
4154 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4155 return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken),
4156 TaskContinuationOptions.None, ref stackMark);
4161 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4163 /// <typeparam name="TResult">
4164 /// The type of the result produced by the continuation.
4166 /// <param name="continuationFunction">
4167 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4168 /// passed the completed task as an argument.
4170 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4171 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4173 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4174 /// completed, whether it completes due to running to completion successfully, faulting due to an
4175 /// unhandled exception, or exiting out early due to being canceled.
4177 /// <exception cref="T:System.ArgumentNullException">
4178 /// The <paramref name="continuationFunction"/> argument is null.
4180 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4181 /// has already been disposed.
4183 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4184 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
4186 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4187 return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
4191 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4193 /// <typeparam name="TResult">
4194 /// The type of the result produced by the continuation.
4196 /// <param name="continuationFunction">
4197 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4198 /// passed the completed task as an argument.
4200 /// <param name="scheduler">
4201 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
4203 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4205 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4206 /// completed, whether it completes due to running to completion successfully, faulting due to an
4207 /// unhandled exception, or exiting out early due to being canceled.
4209 /// <exception cref="T:System.ArgumentNullException">
4210 /// The <paramref name="continuationFunction"/> argument is null.
4212 /// <exception cref="T:System.ArgumentNullException">
4213 /// The <paramref name="scheduler"/> argument is null.
4215 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4216 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
4218 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4219 return ContinueWith<TResult>(continuationFunction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
4223 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4225 /// <typeparam name="TResult">
4226 /// The type of the result produced by the continuation.
4228 /// <param name="continuationFunction">
4229 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4230 /// passed the completed task as an argument.
4232 /// <param name="continuationOptions">
4233 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4235 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4236 /// well as execution options, such as <see
4237 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4239 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4241 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4242 /// completed. If the continuation criteria specified through the <paramref
4243 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
4244 /// instead of scheduled.
4246 /// <exception cref="T:System.ArgumentNullException">
4247 /// The <paramref name="continuationFunction"/> argument is null.
4249 /// <exception cref="T:System.ArgumentOutOfRangeException">
4250 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4251 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4253 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4254 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
4256 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4257 return ContinueWith<TResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
4261 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4263 /// <typeparam name="TResult">
4264 /// The type of the result produced by the continuation.
4266 /// <param name="continuationFunction">
4267 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4268 /// passed the completed task as an argument.
4270 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4271 /// <param name="continuationOptions">
4272 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4274 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4275 /// well as execution options, such as <see
4276 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4278 /// <param name="scheduler">
4279 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
4282 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4284 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4285 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
4286 /// are not met, the continuation task will be canceled instead of scheduled.
4288 /// <exception cref="T:System.ArgumentNullException">
4289 /// The <paramref name="continuationFunction"/> argument is null.
4291 /// <exception cref="T:System.ArgumentOutOfRangeException">
4292 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4293 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4295 /// <exception cref="T:System.ArgumentNullException">
4296 /// The <paramref name="scheduler"/> argument is null.
4298 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4299 /// has already been disposed.
4301 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4302 public Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
4303 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
4305 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4306 return ContinueWith<TResult>(continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
4309 // Same as the above overload, just with a stack mark parameter.
4310 private Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, TaskScheduler scheduler,
4311 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
4313 // Throw on continuation with null function
4314 if (continuationFunction == null)
4316 throw new ArgumentNullException("continuationFunction");
4319 // Throw on continuation with null task scheduler
4320 if (scheduler == null)
4322 throw new ArgumentNullException("scheduler");
4324 Contract.EndContractBlock();
4326 TaskCreationOptions creationOptions;
4327 InternalTaskOptions internalOptions;
4328 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
4330 Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>(
4331 this, continuationFunction, null,
4332 creationOptions, internalOptions,
4336 // Register the continuation. If synchronous execution is requested, this may
4337 // actually invoke the continuation before returning.
4338 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
4340 return continuationTask;
4344 #region Func<Task, Object, TResult> continuation
4347 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4349 /// <typeparam name="TResult">
4350 /// The type of the result produced by the continuation.
4352 /// <param name="continuationFunction">
4353 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4354 /// passed the completed task and the caller-supplied state object as arguments.
4356 /// <param name="state">An object representing data to be used by the continuation function.</param>
4357 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4359 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4360 /// completed, whether it completes due to running to completion successfully, faulting due to an
4361 /// unhandled exception, or exiting out early due to being canceled.
4363 /// <exception cref="T:System.ArgumentNullException">
4364 /// The <paramref name="continuationFunction"/> argument is null.
4366 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4367 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state)
4369 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4370 return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken),
4371 TaskContinuationOptions.None, ref stackMark);
4376 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4378 /// <typeparam name="TResult">
4379 /// The type of the result produced by the continuation.
4381 /// <param name="continuationFunction">
4382 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4383 /// passed the completed task and the caller-supplied state object as arguments.
4385 /// <param name="state">An object representing data to be used by the continuation function.</param>
4386 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4387 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4389 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4390 /// completed, whether it completes due to running to completion successfully, faulting due to an
4391 /// unhandled exception, or exiting out early due to being canceled.
4393 /// <exception cref="T:System.ArgumentNullException">
4394 /// The <paramref name="continuationFunction"/> argument is null.
4396 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4397 /// has already been disposed.
4399 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4400 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, CancellationToken cancellationToken)
4402 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4403 return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark);
4407 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4409 /// <typeparam name="TResult">
4410 /// The type of the result produced by the continuation.
4412 /// <param name="continuationFunction">
4413 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4414 /// passed the completed task and the caller-supplied state object as arguments.
4416 /// <param name="state">An object representing data to be used by the continuation function.</param>
4417 /// <param name="scheduler">
4418 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution.
4420 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4422 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4423 /// completed, whether it completes due to running to completion successfully, faulting due to an
4424 /// unhandled exception, or exiting out early due to being canceled.
4426 /// <exception cref="T:System.ArgumentNullException">
4427 /// The <paramref name="continuationFunction"/> argument is null.
4429 /// <exception cref="T:System.ArgumentNullException">
4430 /// The <paramref name="scheduler"/> argument is null.
4432 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4433 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskScheduler scheduler)
4435 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4436 return ContinueWith<TResult>(continuationFunction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark);
4440 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4442 /// <typeparam name="TResult">
4443 /// The type of the result produced by the continuation.
4445 /// <param name="continuationFunction">
4446 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4447 /// passed the completed task and the caller-supplied state object as arguments.
4449 /// <param name="state">An object representing data to be used by the continuation function.</param>
4450 /// <param name="continuationOptions">
4451 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4453 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4454 /// well as execution options, such as <see
4455 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4457 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4459 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4460 /// completed. If the continuation criteria specified through the <paramref
4461 /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled
4462 /// instead of scheduled.
4464 /// <exception cref="T:System.ArgumentNullException">
4465 /// The <paramref name="continuationFunction"/> argument is null.
4467 /// <exception cref="T:System.ArgumentOutOfRangeException">
4468 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4469 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4471 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4472 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskContinuationOptions continuationOptions)
4474 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4475 return ContinueWith<TResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark);
4479 /// Creates a continuation that executes when the target <see cref="Task"/> completes.
4481 /// <typeparam name="TResult">
4482 /// The type of the result produced by the continuation.
4484 /// <param name="continuationFunction">
4485 /// A function to run when the <see cref="Task"/> completes. When run, the delegate will be
4486 /// passed the completed task and the caller-supplied state object as arguments.
4488 /// <param name="state">An object representing data to be used by the continuation function.</param>
4489 /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param>
4490 /// <param name="continuationOptions">
4491 /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such
4493 /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as
4494 /// well as execution options, such as <see
4495 /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>.
4497 /// <param name="scheduler">
4498 /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its
4501 /// <returns>A new continuation <see cref="Task{TResult}"/>.</returns>
4503 /// The returned <see cref="Task{TResult}"/> will not be scheduled for execution until the current task has
4504 /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter
4505 /// are not met, the continuation task will be canceled instead of scheduled.
4507 /// <exception cref="T:System.ArgumentNullException">
4508 /// The <paramref name="continuationFunction"/> argument is null.
4510 /// <exception cref="T:System.ArgumentOutOfRangeException">
4511 /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see
4512 /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>.
4514 /// <exception cref="T:System.ArgumentNullException">
4515 /// The <paramref name="scheduler"/> argument is null.
4517 /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see>
4518 /// has already been disposed.
4520 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
4521 public Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, CancellationToken cancellationToken,
4522 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
4524 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
4525 return ContinueWith<TResult>(continuationFunction, state, scheduler, cancellationToken, continuationOptions, ref stackMark);
4528 // Same as the above overload, just with a stack mark parameter.
4529 private Task<TResult> ContinueWith<TResult>(Func<Task, Object, TResult> continuationFunction, Object state, TaskScheduler scheduler,
4530 CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark)
4532 // Throw on continuation with null function
4533 if (continuationFunction == null)
4535 throw new ArgumentNullException("continuationFunction");
4538 // Throw on continuation with null task scheduler
4539 if (scheduler == null)
4541 throw new ArgumentNullException("scheduler");
4543 Contract.EndContractBlock();
4545 TaskCreationOptions creationOptions;
4546 InternalTaskOptions internalOptions;
4547 CreationOptionsFromContinuationOptions(continuationOptions, out creationOptions, out internalOptions);
4549 Task<TResult> continuationTask = new ContinuationResultTaskFromTask<TResult>(
4550 this, continuationFunction, state,
4551 creationOptions, internalOptions,
4555 // Register the continuation. If synchronous execution is requested, this may
4556 // actually invoke the continuation before returning.
4557 ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions);
4559 return continuationTask;
4564 /// Converts TaskContinuationOptions to TaskCreationOptions, and also does
4565 /// some validity checking along the way.
4567 /// <param name="continuationOptions">Incoming TaskContinuationOptions</param>
4568 /// <param name="creationOptions">Outgoing TaskCreationOptions</param>
4569 /// <param name="internalOptions">Outgoing InternalTaskOptions</param>
4570 internal static void CreationOptionsFromContinuationOptions(
4571 TaskContinuationOptions continuationOptions,
4572 out TaskCreationOptions creationOptions,
4573 out InternalTaskOptions internalOptions)
4575 // This is used a couple of times below
4576 TaskContinuationOptions NotOnAnything =
4577 TaskContinuationOptions.NotOnCanceled |
4578 TaskContinuationOptions.NotOnFaulted |
4579 TaskContinuationOptions.NotOnRanToCompletion;
4581 TaskContinuationOptions creationOptionsMask =
4582 TaskContinuationOptions.PreferFairness |
4583 TaskContinuationOptions.LongRunning |
4584 TaskContinuationOptions.DenyChildAttach |
4585 TaskContinuationOptions.HideScheduler |
4586 TaskContinuationOptions.AttachedToParent|
4587 TaskContinuationOptions.RunContinuationsAsynchronously;
4589 // Check that LongRunning and ExecuteSynchronously are not specified together
4590 TaskContinuationOptions illegalMask = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.LongRunning;
4591 if ((continuationOptions & illegalMask) == illegalMask)
4593 throw new ArgumentOutOfRangeException("continuationOptions", Environment.GetResourceString("Task_ContinueWith_ESandLR"));
4596 // Check that no illegal options were specified
4597 if ((continuationOptions &
4598 ~(creationOptionsMask | NotOnAnything |
4599 TaskContinuationOptions.LazyCancellation | TaskContinuationOptions.ExecuteSynchronously)) != 0)
4601 throw new ArgumentOutOfRangeException("continuationOptions");
4604 // Check that we didn't specify "not on anything"
4605 if ((continuationOptions & NotOnAnything) == NotOnAnything)
4607 throw new ArgumentOutOfRangeException("continuationOptions", Environment.GetResourceString("Task_ContinueWith_NotOnAnything"));
4610 // This passes over all but LazyCancellation, which has no representation in TaskCreationOptions
4611 creationOptions = (TaskCreationOptions)(continuationOptions & creationOptionsMask);
4613 // internalOptions has at least ContinuationTask ...
4614 internalOptions = InternalTaskOptions.ContinuationTask;
4616 // ... and possibly LazyCancellation
4617 if ((continuationOptions & TaskContinuationOptions.LazyCancellation) != 0)
4618 internalOptions |= InternalTaskOptions.LazyCancellation;
4623 /// Registers the continuation and possibly runs it (if the task is already finished).
4625 /// <param name="continuationTask">The continuation task itself.</param>
4626 /// <param name="scheduler">TaskScheduler with which to associate continuation task.</param>
4627 /// <param name="options">Restrictions on when the continuation becomes active.</param>
4628 internal void ContinueWithCore(Task continuationTask,
4629 TaskScheduler scheduler,
4630 CancellationToken cancellationToken,
4631 TaskContinuationOptions options)
4633 Contract.Requires(continuationTask != null, "Task.ContinueWithCore(): null continuationTask");
4634 Contract.Requires(scheduler != null, "Task.ContinueWithCore(): null scheduler");
4635 Contract.Requires(!continuationTask.IsCompleted, "Did not expect continuationTask to be completed");
4637 // Create a TaskContinuation
4638 TaskContinuation continuation = new StandardTaskContinuation(continuationTask, options, scheduler);
4640 // If cancellationToken is cancellable, then assign it.
4641 if (cancellationToken.CanBeCanceled)
4643 if (IsCompleted || cancellationToken.IsCancellationRequested)
4645 // If the antecedent has completed, then we will not be queuing up
4646 // the continuation in the antecedent's continuation list. Likewise,
4647 // if the cancellationToken has been canceled, continuationTask will
4648 // be completed in the AssignCancellationToken call below, and there
4649 // is no need to queue the continuation to the antecedent's continuation
4650 // list. In either of these two cases, we will pass "null" for the antecedent,
4651 // meaning "the cancellation callback should not attempt to remove the
4652 // continuation from its antecedent's continuation list".
4653 continuationTask.AssignCancellationToken(cancellationToken, null, null);
4657 // The antecedent is not yet complete, so there is a pretty good chance
4658 // that the continuation will be queued up in the antecedent. Assign the
4659 // cancellation token with information about the antecedent, so that the
4660 // continuation can be dequeued upon the signalling of the token.
4662 // It's possible that the antecedent completes before the call to AddTaskContinuation,
4663 // and that is a benign ----. It just means that the cancellation will result in
4664 // a futile search of the antecedent's continuation list.
4665 continuationTask.AssignCancellationToken(cancellationToken, this, continuation);
4669 // In the case of a pre-canceled token, continuationTask will have been completed
4670 // in a Canceled state by now. If such is the case, there is no need to go through
4671 // the motions of queuing up the continuation for eventual execution.
4672 if (!continuationTask.IsCompleted)
4674 // We need additional correlation produced here to ensure that at least the continuation
4675 // code will be correlatable to the currrent activity that initiated "this" task:
4676 // . when the antecendent ("this") is a promise we have very little control over where
4677 // the code for the promise will run (e.g. it can be a task from a user provided
4678 // TaskCompletionSource or from a classic Begin/End async operation); this user or
4679 // system code will likely not have stamped an activity id on the thread, so there's
4680 // generally no easy correlation that can be provided between the current activity
4681 // and the promise. Also the continuation code may run practically on any thread.
4682 // Since there may be no correlation between the current activity and the TCS's task
4683 // activity, we ensure we at least create a correlation from the current activity to
4684 // the continuation that runs when the promise completes.
4686 if ((this.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0 &&
4687 !(this is ITaskCompletionAction))
4689 var etwLog = TplEtwProvider.Log;
4690 if (etwLog.IsEnabled())
4692 etwLog.AwaitTaskContinuationScheduled(TaskScheduler.Current.Id, Task.CurrentId ?? 0, continuationTask.Id);
4696 // Attempt to enqueue the continuation
4697 bool continuationQueued = AddTaskContinuation(continuation, addBeforeOthers: false);
4699 // If the continuation was not queued (because the task completed), then run it now.
4700 if (!continuationQueued) continuation.Run(this, bCanInlineContinuationTask: true);
4705 // Adds a lightweight completion action to a task. This is similar to a continuation
4706 // task except that it is stored as an action, and thus does not require the allocation/
4707 // execution resources of a continuation task.
4709 // Used internally by ContinueWhenAll() and ContinueWhenAny().
4710 internal void AddCompletionAction(ITaskCompletionAction action)
4712 AddCompletionAction(action, addBeforeOthers: false);
4715 private void AddCompletionAction(ITaskCompletionAction action, bool addBeforeOthers)
4717 if (!AddTaskContinuation(action, addBeforeOthers))
4718 action.Invoke(this); // run the action directly if we failed to queue the continuation (i.e., the task completed)
4721 // Support method for AddTaskContinuation that takes care of multi-continuation logic.
4722 // Returns true if and only if the continuation was successfully queued.
4723 // THIS METHOD ASSUMES THAT m_continuationObject IS NOT NULL. That case was taken
4724 // care of in the calling method, AddTaskContinuation().
4725 private bool AddTaskContinuationComplex(object tc, bool addBeforeOthers)
4727 Contract.Requires(tc != null, "Expected non-null tc object in AddTaskContinuationComplex");
4729 object oldValue = m_continuationObject;
4731 // Logic for the case where we were previously storing a single continuation
4732 if ((oldValue != s_taskCompletionSentinel) && (!(oldValue is List<object>)))
4734 // Construct a new TaskContinuation list
4735 List<object> newList = new List<object>();
4737 // Add in the old single value
4738 newList.Add(oldValue);
4740 // Now CAS in the new list
4741 Interlocked.CompareExchange(ref m_continuationObject, newList, oldValue);
4743 // We might be racing against another thread converting the single into
4744 // a list, or we might be racing against task completion, so resample "list"
4748 // m_continuationObject is guaranteed at this point to be either a List or
4749 // s_taskCompletionSentinel.
4750 List<object> list = m_continuationObject as List<object>;
4751 Contract.Assert((list != null) || (m_continuationObject == s_taskCompletionSentinel),
4752 "Expected m_continuationObject to be list or sentinel");
4754 // If list is null, it can only mean that s_taskCompletionSentinel has been exchanged
4755 // into m_continuationObject. Thus, the task has completed and we should return false
4756 // from this method, as we will not be queuing up the continuation.
4761 // It is possible for the task to complete right after we snap the copy of
4762 // the list. If so, then fall through and return false without queuing the
4764 if (m_continuationObject != s_taskCompletionSentinel)
4766 // Before growing the list we remove possible null entries that are the
4767 // result from RemoveContinuations()
4768 if (list.Count == list.Capacity)
4770 list.RemoveAll(s_IsTaskContinuationNullPredicate);
4773 if (addBeforeOthers)
4778 return true; // continuation successfully queued, so return true.
4783 // We didn't succeed in queuing the continuation, so return false.
4787 // Record a continuation task or action.
4788 // Return true if and only if we successfully queued a continuation.
4789 private bool AddTaskContinuation(object tc, bool addBeforeOthers)
4791 Contract.Requires(tc != null);
4793 // Make sure that, if someone calls ContinueWith() right after waiting for the predecessor to complete,
4794 // we don't queue up a continuation.
4795 if (IsCompleted) return false;
4797 // Try to just jam tc into m_continuationObject
4798 if ((m_continuationObject != null) || (Interlocked.CompareExchange(ref m_continuationObject, tc, null) != null))
4800 // If we get here, it means that we failed to CAS tc into m_continuationObject.
4801 // Therefore, we must go the more complicated route.
4802 return AddTaskContinuationComplex(tc, addBeforeOthers);
4807 // Removes a continuation task from m_continuations
4808 internal void RemoveContinuation(object continuationObject) // could be TaskContinuation or Action<Task>
4810 // We need to snap a local reference to m_continuations since reading a volatile object is more costly.
4811 // Also to prevent the value to be changed as result of a race condition with another method.
4812 object continuationsLocalRef = m_continuationObject;
4814 // Task is completed. Nothing to do here.
4815 if (continuationsLocalRef == s_taskCompletionSentinel) return;
4817 List<object> continuationsLocalListRef = continuationsLocalRef as List<object>;
4819 if (continuationsLocalListRef == null)
4821 // 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.
4822 // Note we cannot go back to a null state, since it will mess up the AddTaskContinuation logic.
4823 if (Interlocked.CompareExchange(ref m_continuationObject, new List<object>(), continuationObject) != continuationObject)
4825 // If we fail it means that either AddContinuationComplex won the race condition and m_continuationObject is now a List
4826 // that contains the element we want to remove. Or FinishContinuations set the s_taskCompletionSentinel.
4827 // So we should try to get a list one more time
4828 continuationsLocalListRef = m_continuationObject as List<object>;
4832 // Exchange was successful so we can skip the last comparison
4837 // if continuationsLocalRef == null it means s_taskCompletionSentinel has been set already and there is nothing else to do.
4838 if (continuationsLocalListRef != null)
4840 lock (continuationsLocalListRef)
4842 // There is a small chance that this task completed since we took a local snapshot into
4843 // continuationsLocalRef. In that case, just return; we don't want to be manipulating the
4844 // continuation list as it is being processed.
4845 if (m_continuationObject == s_taskCompletionSentinel) return;
4847 // Find continuationObject in the continuation list
4848 int index = continuationsLocalListRef.IndexOf(continuationObject);
4852 // null out that TaskContinuation entry, which will be interpreted as "to be cleaned up"
4853 continuationsLocalListRef[index] = null;
4860 // statically allocated delegate for the RemoveAll expression in RemoveContinuations() and AddContinuationComplex()
4861 private readonly static Predicate<object> s_IsTaskContinuationNullPredicate =
4862 new Predicate<object>((tc) => { return (tc == null); });
4870 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4872 /// <param name="tasks">
4873 /// An array of <see cref="Task"/> instances on which to wait.
4875 /// <exception cref="T:System.ArgumentNullException">
4876 /// The <paramref name="tasks"/> argument is null.
4878 /// <exception cref="T:System.ArgumentNullException">
4879 /// The <paramref name="tasks"/> argument contains a null element.
4881 /// <exception cref="T:System.AggregateException">
4882 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4883 /// the execution of at least one of the <see cref="Task"/> instances.
4885 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4886 public static void WaitAll(params Task[] tasks)
4891 WaitAll(tasks, Timeout.Infinite);
4894 Contract.Assert(waitResult, "expected wait to succeed");
4899 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4902 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4903 /// otherwise, false.
4905 /// <param name="tasks">
4906 /// An array of <see cref="Task"/> instances on which to wait.
4908 /// <param name="timeout">
4909 /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
4910 /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
4912 /// <exception cref="T:System.ArgumentNullException">
4913 /// The <paramref name="tasks"/> argument is null.
4915 /// <exception cref="T:System.ArgumentException">
4916 /// The <paramref name="tasks"/> argument contains a null element.
4918 /// <exception cref="T:System.AggregateException">
4919 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4920 /// the execution of at least one of the <see cref="Task"/> instances.
4922 /// <exception cref="T:System.ArgumentOutOfRangeException">
4923 /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
4924 /// infinite time-out -or- timeout is greater than
4925 /// <see cref="System.Int32.MaxValue"/>.
4927 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4928 public static bool WaitAll(Task[] tasks, TimeSpan timeout)
4930 long totalMilliseconds = (long)timeout.TotalMilliseconds;
4931 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
4933 throw new ArgumentOutOfRangeException("timeout");
4936 return WaitAll(tasks, (int)totalMilliseconds);
4941 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4944 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4945 /// otherwise, false.
4947 /// <param name="millisecondsTimeout">
4948 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
4949 /// wait indefinitely.</param>
4950 /// <param name="tasks">An array of <see cref="Task"/> instances on which to wait.
4952 /// <exception cref="T:System.ArgumentNullException">
4953 /// The <paramref name="tasks"/> argument is null.
4955 /// <exception cref="T:System.ArgumentException">
4956 /// The <paramref name="tasks"/> argument contains a null element.
4958 /// <exception cref="T:System.AggregateException">
4959 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4960 /// the execution of at least one of the <see cref="Task"/> instances.
4962 /// <exception cref="T:System.ArgumentOutOfRangeException">
4963 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
4964 /// infinite time-out.
4966 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4967 public static bool WaitAll(Task[] tasks, int millisecondsTimeout)
4969 return WaitAll(tasks, millisecondsTimeout, default(CancellationToken));
4973 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
4976 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
4977 /// otherwise, false.
4979 /// <param name="tasks">
4980 /// An array of <see cref="Task"/> instances on which to wait.
4982 /// <param name="cancellationToken">
4983 /// A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.
4985 /// <exception cref="T:System.ArgumentNullException">
4986 /// The <paramref name="tasks"/> argument is null.
4988 /// <exception cref="T:System.ArgumentException">
4989 /// The <paramref name="tasks"/> argument contains a null element.
4991 /// <exception cref="T:System.AggregateException">
4992 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
4993 /// the execution of at least one of the <see cref="Task"/> instances.
4995 /// <exception cref="T:System.OperationCanceledException">
4996 /// The <paramref name="cancellationToken"/> was canceled.
4998 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
4999 public static void WaitAll(Task[] tasks, CancellationToken cancellationToken)
5001 WaitAll(tasks, Timeout.Infinite, cancellationToken);
5005 /// Waits for all of the provided <see cref="Task"/> objects to complete execution.
5008 /// true if all of the <see cref="Task"/> instances completed execution within the allotted time;
5009 /// otherwise, false.
5011 /// <param name="tasks">
5012 /// An array of <see cref="Task"/> instances on which to wait.
5014 /// <param name="millisecondsTimeout">
5015 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
5016 /// wait indefinitely.
5018 /// <param name="cancellationToken">
5019 /// A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.
5021 /// <exception cref="T:System.ArgumentNullException">
5022 /// The <paramref name="tasks"/> argument is null.
5024 /// <exception cref="T:System.ArgumentException">
5025 /// The <paramref name="tasks"/> argument contains a null element.
5027 /// <exception cref="T:System.AggregateException">
5028 /// At least one of the <see cref="Task"/> instances was canceled -or- an exception was thrown during
5029 /// the execution of at least one of the <see cref="Task"/> instances.
5031 /// <exception cref="T:System.ArgumentOutOfRangeException">
5032 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
5033 /// infinite time-out.
5035 /// <exception cref="T:System.OperationCanceledException">
5036 /// The <paramref name="cancellationToken"/> was canceled.
5038 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5039 public static bool WaitAll(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
5043 throw new ArgumentNullException("tasks");
5045 if (millisecondsTimeout < -1)
5047 throw new ArgumentOutOfRangeException("millisecondsTimeout");
5049 Contract.EndContractBlock();
5051 cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations
5054 // In this WaitAll() implementation we have 2 alternate code paths for a task to be handled:
5055 // CODEPATH1: skip an already completed task, CODEPATH2: actually wait on tasks
5056 // We make sure that the exception behavior of Task.Wait() is replicated the same for tasks handled in either of these codepaths
5059 List<Exception> exceptions = null;
5060 List<Task> waitedOnTaskList = null;
5061 List<Task> notificationTasks = null;
5063 // If any of the waited-upon tasks end as Faulted or Canceled, set these to true.
5064 bool exceptionSeen = false, cancellationSeen = false;
5066 bool returnValue = true;
5068 // Collects incomplete tasks in "waitedOnTaskList"
5069 for (int i = tasks.Length - 1; i >= 0; i--)
5071 Task task = tasks[i];
5075 throw new ArgumentException(Environment.GetResourceString("Task_WaitMulti_NullTask"), "tasks");
5078 bool taskIsCompleted = task.IsCompleted;
5079 if (!taskIsCompleted)
5081 // try inlining the task only if we have an infinite timeout and an empty cancellation token
5082 if (millisecondsTimeout != Timeout.Infinite || cancellationToken.CanBeCanceled)
5084 // We either didn't attempt inline execution because we had a non-infinite timeout or we had a cancellable token.
5085 // In all cases we need to do a full wait on the task (=> add its event into the list.)
5086 AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
5090 // We are eligible for inlining. If it doesn't work, we'll do a full wait.
5091 taskIsCompleted = task.WrappedTryRunInline() && task.IsCompleted; // A successful TryRunInline doesn't guarantee completion
5092 if (!taskIsCompleted) AddToList(task, ref waitedOnTaskList, initSize: tasks.Length);
5096 if (taskIsCompleted)
5098 if (task.IsFaulted) exceptionSeen = true;
5099 else if (task.IsCanceled) cancellationSeen = true;
5100 if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: 1);
5104 if (waitedOnTaskList != null)
5106 // Block waiting for the tasks to complete.
5107 returnValue = WaitAllBlockingCore(waitedOnTaskList, millisecondsTimeout, cancellationToken);
5109 // If the wait didn't time out, ensure exceptions are propagated, and if a debugger is
5110 // attached and one of these tasks requires it, that we notify the debugger of a wait completion.
5113 // Add any exceptions for this task to the collection, and if it's wait
5114 // notification bit is set, store it to operate on at the end.
5115 foreach (var task in waitedOnTaskList)
5117 if (task.IsFaulted) exceptionSeen = true;
5118 else if (task.IsCanceled) cancellationSeen = true;
5119 if (task.IsWaitNotificationEnabled) AddToList(task, ref notificationTasks, initSize: 1);
5123 // We need to prevent the tasks array from being GC'ed until we come out of the wait.
5124 // This is necessary so that the Parallel Debugger can traverse it during the long wait and
5125 // deduce waiter/waitee relationships
5126 GC.KeepAlive(tasks);
5129 // Now that we're done and about to exit, if the wait completed and if we have
5130 // any tasks with a notification bit set, signal the debugger if any requires it.
5131 if (returnValue && notificationTasks != null)
5133 // Loop through each task tha that had its bit set, and notify the debugger
5134 // about the first one that requires it. The debugger will reset the bit
5135 // for any tasks we don't notify of as soon as we break, so we only need to notify
5137 foreach (var task in notificationTasks)
5139 if (task.NotifyDebuggerOfWaitCompletionIfNecessary()) break;
5143 // If one or more threw exceptions, aggregate and throw them.
5144 if (returnValue && (exceptionSeen || cancellationSeen))
5146 // If the WaitAll was canceled and tasks were canceled but not faulted,
5147 // prioritize throwing an OCE for canceling the WaitAll over throwing an
5148 // AggregateException for all of the canceled Tasks. This helps
5149 // to bring determinism to an otherwise non-determistic case of using
5150 // the same token to cancel both the WaitAll and the Tasks.
5151 if (!exceptionSeen) cancellationToken.ThrowIfCancellationRequested();
5153 // Now gather up and throw all of the exceptions.
5154 foreach (var task in tasks) AddExceptionsForCompletedTask(ref exceptions, task);
5155 Contract.Assert(exceptions != null, "Should have seen at least one exception");
5156 throw new AggregateException(exceptions);
5162 /// <summary>Adds an element to the list, initializing the list if it's null.</summary>
5163 /// <typeparam name="T">Specifies the type of data stored in the list.</typeparam>
5164 /// <param name="item">The item to add.</param>
5165 /// <param name="list">The list.</param>
5166 /// <param name="initSize">The size to which to initialize the list if the list is null.</param>
5167 private static void AddToList<T>(T item, ref List<T> list, int initSize)
5169 if (list == null) list = new List<T>(initSize);
5173 /// <summary>Performs a blocking WaitAll on the vetted list of tasks.</summary>
5174 /// <param name="tasks">The tasks, which have already been checked and filtered for completion.</param>
5175 /// <param name="millisecondsTimeout">The timeout.</param>
5176 /// <param name="cancellationToken">The cancellation token.</param>
5177 /// <returns>true if all of the tasks completed; otherwise, false.</returns>
5178 private static bool WaitAllBlockingCore(List<Task> tasks, int millisecondsTimeout, CancellationToken cancellationToken)
5180 Contract.Assert(tasks != null, "Expected a non-null list of tasks");
5181 Contract.Assert(tasks.Count > 0, "Expected at least one task");
5183 bool waitCompleted = false;
5184 var mres = new SetOnCountdownMres(tasks.Count);
5187 foreach (var task in tasks)
5189 task.AddCompletionAction(mres, addBeforeOthers: true);
5191 waitCompleted = mres.Wait(millisecondsTimeout, cancellationToken);
5197 foreach (var task in tasks)
5199 if (!task.IsCompleted) task.RemoveContinuation(mres);
5202 // It's ok that we don't dispose of the MRES here, as we never
5203 // access the MRES' WaitHandle, and thus no finalizable resources
5204 // are actually created. We don't always just Dispose it because
5205 // a continuation that's accessing the MRES could still be executing.
5207 return waitCompleted;
5210 // A ManualResetEventSlim that will get Set after Invoke is called count times.
5211 // This allows us to replace this logic:
5212 // var mres = new ManualResetEventSlim(tasks.Count);
5213 // Action<Task> completionAction = delegate { if(Interlocked.Decrement(ref count) == 0) mres.Set(); };
5214 // foreach(var task in tasks) task.AddCompletionAction(completionAction);
5216 // var mres = new SetOnCountdownMres(tasks.Count);
5217 // foreach(var task in tasks) task.AddCompletionAction(mres);
5218 // which saves a couple of allocations.
5220 // Used in WaitAllBlockingCore (above).
5221 private sealed class SetOnCountdownMres : ManualResetEventSlim, ITaskCompletionAction
5225 internal SetOnCountdownMres(int count)
5227 Contract.Assert(count > 0, "Expected count > 0");
5231 public void Invoke(Task completingTask)
5233 if (Interlocked.Decrement(ref _count) == 0) Set();
5234 Contract.Assert(_count >= 0, "Count should never go below 0");
5239 /// Internal WaitAll implementation which is meant to be used with small number of tasks,
5240 /// optimized for Parallel.Invoke and other structured primitives.
5242 internal static void FastWaitAll(Task[] tasks)
5244 Contract.Requires(tasks != null);
5246 List<Exception> exceptions = null;
5248 // Collects incomplete tasks in "waitedOnTaskList" and their cooperative events in "cooperativeEventList"
5249 for (int i = tasks.Length - 1; i >= 0; i--)
5251 if (!tasks[i].IsCompleted)
5253 // Just attempting to inline here... result doesn't matter.
5254 // We'll do a second pass to do actual wait on each task, and to aggregate their exceptions.
5255 // If the task is inlined here, it will register as IsCompleted in the second pass
5256 // and will just give us the exception.
5257 tasks[i].WrappedTryRunInline();
5261 // Wait on the tasks.
5262 for (int i = tasks.Length - 1; i >= 0; i--)
5264 var task = tasks[i];
5265 task.SpinThenBlockingWait(Timeout.Infinite, default(CancellationToken));
5266 AddExceptionsForCompletedTask(ref exceptions, task);
5268 // Note that unlike other wait code paths, we do not check
5269 // task.NotifyDebuggerOfWaitCompletionIfNecessary() here, because this method is currently
5270 // only used from contexts where the tasks couldn't have that bit set, namely
5271 // Parallel.Invoke. If that ever changes, such checks should be added here.
5274 // If one or more threw exceptions, aggregate them.
5275 if (exceptions != null)
5277 throw new AggregateException(exceptions);
5282 /// This internal function is only meant to be called by WaitAll()
5283 /// If the completed task is canceled or it has other exceptions, here we will add those
5284 /// into the passed in exception list (which will be lazily initialized here).
5286 internal static void AddExceptionsForCompletedTask(ref List<Exception> exceptions, Task t)
5288 AggregateException ex = t.GetExceptions(true);
5291 // make sure the task's exception observed status is set appropriately
5292 // it's possible that WaitAll was called by the parent of an attached child,
5293 // this will make sure it won't throw again in the implicit wait
5294 t.UpdateExceptionObservedStatus();
5296 if (exceptions == null)
5298 exceptions = new List<Exception>(ex.InnerExceptions.Count);
5301 exceptions.AddRange(ex.InnerExceptions);
5307 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5309 /// <param name="tasks">
5310 /// An array of <see cref="Task"/> instances on which to wait.
5312 /// <returns>The index of the completed task in the <paramref name="tasks"/> array argument.</returns>
5313 /// <exception cref="T:System.ArgumentNullException">
5314 /// The <paramref name="tasks"/> argument is null.
5316 /// <exception cref="T:System.ArgumentException">
5317 /// The <paramref name="tasks"/> argument contains a null element.
5319 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5320 public static int WaitAny(params Task[] tasks)
5322 int waitResult = WaitAny(tasks, Timeout.Infinite);
5323 Contract.Assert(tasks.Length == 0 || waitResult != -1, "expected wait to succeed");
5328 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5330 /// <param name="tasks">
5331 /// An array of <see cref="Task"/> instances on which to wait.
5333 /// <param name="timeout">
5334 /// A <see cref="System.TimeSpan"/> that represents the number of milliseconds to wait, or a <see
5335 /// cref="System.TimeSpan"/> that represents -1 milliseconds to wait indefinitely.
5338 /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
5339 /// timeout occurred.
5341 /// <exception cref="T:System.ArgumentNullException">
5342 /// The <paramref name="tasks"/> argument is null.
5344 /// <exception cref="T:System.ArgumentException">
5345 /// The <paramref name="tasks"/> argument contains a null element.
5347 /// <exception cref="T:System.ArgumentOutOfRangeException">
5348 /// <paramref name="timeout"/> is a negative number other than -1 milliseconds, which represents an
5349 /// infinite time-out -or- timeout is greater than
5350 /// <see cref="System.Int32.MaxValue"/>.
5352 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5353 public static int WaitAny(Task[] tasks, TimeSpan timeout)
5355 long totalMilliseconds = (long)timeout.TotalMilliseconds;
5356 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
5358 throw new ArgumentOutOfRangeException("timeout");
5361 return WaitAny(tasks, (int)totalMilliseconds);
5365 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5367 /// <param name="tasks">
5368 /// An array of <see cref="Task"/> instances on which to wait.
5370 /// <param name="cancellationToken">
5371 /// A <see cref="CancellationToken"/> to observe while waiting for a task to complete.
5374 /// The index of the completed task in the <paramref name="tasks"/> array argument.
5376 /// <exception cref="T:System.ArgumentNullException">
5377 /// The <paramref name="tasks"/> argument is null.
5379 /// <exception cref="T:System.ArgumentException">
5380 /// The <paramref name="tasks"/> argument contains a null element.
5382 /// <exception cref="T:System.OperationCanceledException">
5383 /// The <paramref name="cancellationToken"/> was canceled.
5385 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5386 public static int WaitAny(Task[] tasks, CancellationToken cancellationToken)
5388 return WaitAny(tasks, Timeout.Infinite, cancellationToken);
5392 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5394 /// <param name="tasks">
5395 /// An array of <see cref="Task"/> instances on which to wait.
5397 /// <param name="millisecondsTimeout">
5398 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
5399 /// wait indefinitely.
5402 /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
5403 /// timeout occurred.
5405 /// <exception cref="T:System.ArgumentNullException">
5406 /// The <paramref name="tasks"/> argument is null.
5408 /// <exception cref="T:System.ArgumentException">
5409 /// The <paramref name="tasks"/> argument contains a null element.
5411 /// <exception cref="T:System.ArgumentOutOfRangeException">
5412 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
5413 /// infinite time-out.
5415 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5416 public static int WaitAny(Task[] tasks, int millisecondsTimeout)
5418 return WaitAny(tasks, millisecondsTimeout, default(CancellationToken));
5422 /// Waits for any of the provided <see cref="Task"/> objects to complete execution.
5424 /// <param name="tasks">
5425 /// An array of <see cref="Task"/> instances on which to wait.
5427 /// <param name="millisecondsTimeout">
5428 /// The number of milliseconds to wait, or <see cref="System.Threading.Timeout.Infinite"/> (-1) to
5429 /// wait indefinitely.
5431 /// <param name="cancellationToken">
5432 /// A <see cref="CancellationToken"/> to observe while waiting for a task to complete.
5435 /// The index of the completed task in the <paramref name="tasks"/> array argument, or -1 if the
5436 /// timeout occurred.
5438 /// <exception cref="T:System.ArgumentNullException">
5439 /// The <paramref name="tasks"/> argument is null.
5441 /// <exception cref="T:System.ArgumentException">
5442 /// The <paramref name="tasks"/> argument contains a null element.
5444 /// <exception cref="T:System.ArgumentOutOfRangeException">
5445 /// <paramref name="millisecondsTimeout"/> is a negative number other than -1, which represents an
5446 /// infinite time-out.
5448 /// <exception cref="T:System.OperationCanceledException">
5449 /// The <paramref name="cancellationToken"/> was canceled.
5451 [MethodImpl(MethodImplOptions.NoOptimization)] // this is needed for the parallel debugger
5452 public static int WaitAny(Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
5456 throw new ArgumentNullException("tasks");
5458 if (millisecondsTimeout < -1)
5460 throw new ArgumentOutOfRangeException("millisecondsTimeout");
5462 Contract.EndContractBlock();
5464 cancellationToken.ThrowIfCancellationRequested(); // early check before we make any allocations
5466 int signaledTaskIndex = -1;
5468 // Make a pass through the loop to check for any tasks that may have
5469 // already been completed, and to verify that no tasks are null.
5471 for (int taskIndex = 0; taskIndex < tasks.Length; taskIndex++)
5473 Task task = tasks[taskIndex];
5477 throw new ArgumentException(Environment.GetResourceString("Task_WaitMulti_NullTask"), "tasks");
5480 if (signaledTaskIndex == -1 && task.IsCompleted)
5482 // We found our first completed task. Store it, but we can't just return here,
5483 // as we still need to validate the whole array for nulls.
5484 signaledTaskIndex = taskIndex;
5488 if (signaledTaskIndex == -1 && tasks.Length != 0)
5490 Task<Task> firstCompleted = TaskFactory.CommonCWAnyLogic(tasks);
5491 bool waitCompleted = firstCompleted.Wait(millisecondsTimeout, cancellationToken);
5494 Contract.Assert(firstCompleted.Status == TaskStatus.RanToCompletion);
5495 signaledTaskIndex = Array.IndexOf(tasks, firstCompleted.Result);
5496 Contract.Assert(signaledTaskIndex >= 0);
5500 // We need to prevent the tasks array from being GC'ed until we come out of the wait.
5501 // This is necessary so that the Parallel Debugger can traverse it during the long wait
5502 // and deduce waiter/waitee relationships
5503 GC.KeepAlive(tasks);
5506 return signaledTaskIndex;
5509 #region FromResult / FromException / FromCancellation
5511 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed successfully with the specified result.</summary>
5512 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5513 /// <param name="result">The result to store into the completed task.</param>
5514 /// <returns>The successfully completed task.</returns>
5515 public static Task<TResult> FromResult<TResult>(TResult result)
5517 return new Task<TResult>(result);
5520 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed exceptionally with the specified exception.</summary>
5521 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5522 /// <param name="exception">The exception with which to complete the task.</param>
5523 /// <returns>The faulted task.</returns>
5524 public static Task FromException(Exception exception)
5526 return FromException<VoidTaskResult>(exception);
5529 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed exceptionally with the specified exception.</summary>
5530 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5531 /// <param name="exception">The exception with which to complete the task.</param>
5532 /// <returns>The faulted task.</returns>
5533 public static Task<TResult> FromException<TResult>(Exception exception)
5535 if (exception == null) throw new ArgumentNullException("exception");
5536 Contract.EndContractBlock();
5538 var task = new Task<TResult>();
5539 bool succeeded = task.TrySetException(exception);
5540 Contract.Assert(succeeded, "This should always succeed on a new task.");
5544 /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified token.</summary>
5545 /// <param name="cancellationToken">The token with which to complete the task.</param>
5546 /// <returns>The canceled task.</returns>
5547 [FriendAccessAllowed]
5548 internal static Task FromCancellation(CancellationToken cancellationToken)
5550 if (!cancellationToken.IsCancellationRequested) throw new ArgumentOutOfRangeException("cancellationToken");
5551 Contract.EndContractBlock();
5552 return new Task(true, TaskCreationOptions.None, cancellationToken);
5555 /// <summary>Creates a <see cref="Task"/> that's completed due to cancellation with the specified token.</summary>
5556 /// <param name="cancellationToken">The token with which to complete the task.</param>
5557 /// <returns>The canceled task.</returns>
5558 public static Task FromCanceled(CancellationToken cancellationToken)
5560 return FromCancellation(cancellationToken);
5563 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified token.</summary>
5564 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5565 /// <param name="cancellationToken">The token with which to complete the task.</param>
5566 /// <returns>The canceled task.</returns>
5567 [FriendAccessAllowed]
5568 internal static Task<TResult> FromCancellation<TResult>(CancellationToken cancellationToken)
5570 if (!cancellationToken.IsCancellationRequested) throw new ArgumentOutOfRangeException("cancellationToken");
5571 Contract.EndContractBlock();
5572 return new Task<TResult>(true, default(TResult), TaskCreationOptions.None, cancellationToken);
5575 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified token.</summary>
5576 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5577 /// <param name="cancellationToken">The token with which to complete the task.</param>
5578 /// <returns>The canceled task.</returns>
5579 public static Task<TResult> FromCanceled<TResult>(CancellationToken cancellationToken)
5581 return FromCancellation<TResult>(cancellationToken);
5584 /// <summary>Creates a <see cref="Task{TResult}"/> that's completed due to cancellation with the specified exception.</summary>
5585 /// <typeparam name="TResult">The type of the result returned by the task.</typeparam>
5586 /// <param name="exception">The exception with which to complete the task.</param>
5587 /// <returns>The canceled task.</returns>
5588 internal static Task<TResult> FromCancellation<TResult>(OperationCanceledException exception)
5590 if (exception == null) throw new ArgumentNullException("exception");
5591 Contract.EndContractBlock();
5593 var task = new Task<TResult>();
5594 bool succeeded = task.TrySetCanceled(exception.CancellationToken, exception);
5595 Contract.Assert(succeeded, "This should always succeed on a new task.");
5604 /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work.
5606 /// <param name="action">The work to execute asynchronously</param>
5607 /// <returns>A Task that represents the work queued to execute in the ThreadPool.</returns>
5608 /// <exception cref="T:System.ArgumentNullException">
5609 /// The <paramref name="action"/> parameter was null.
5611 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
5612 public static Task Run(Action action)
5614 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
5615 return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default,
5616 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
5620 /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work.
5622 /// <param name="action">The work to execute asynchronously</param>
5623 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5624 /// <returns>A Task that represents the work queued to execute in the ThreadPool.</returns>
5625 /// <exception cref="T:System.ArgumentNullException">
5626 /// The <paramref name="action"/> parameter was null.
5628 /// <exception cref="T:System.ObjectDisposedException">
5629 /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
5631 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
5632 public static Task Run(Action action, CancellationToken cancellationToken)
5634 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
5635 return Task.InternalStartNew(null, action, null, cancellationToken, TaskScheduler.Default,
5636 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
5640 /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work.
5642 /// <param name="function">The work to execute asynchronously</param>
5643 /// <returns>A Task(TResult) that represents the work queued to execute in the ThreadPool.</returns>
5644 /// <exception cref="T:System.ArgumentNullException">
5645 /// The <paramref name="function"/> parameter was null.
5647 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
5648 public static Task<TResult> Run<TResult>(Func<TResult> function)
5650 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
5651 return Task<TResult>.StartNew(null, function, default(CancellationToken),
5652 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default, ref stackMark);
5656 /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work.
5658 /// <param name="function">The work to execute asynchronously</param>
5659 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5660 /// <returns>A Task(TResult) that represents the work queued to execute in the ThreadPool.</returns>
5661 /// <exception cref="T:System.ArgumentNullException">
5662 /// The <paramref name="function"/> parameter was null.
5664 /// <exception cref="T:System.ObjectDisposedException">
5665 /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
5667 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
5668 public static Task<TResult> Run<TResult>(Func<TResult> function, CancellationToken cancellationToken)
5670 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
5671 return Task<TResult>.StartNew(null, function, cancellationToken,
5672 TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, TaskScheduler.Default, ref stackMark);
5676 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5677 /// Task returned by <paramref name="function"/>.
5679 /// <param name="function">The work to execute asynchronously</param>
5680 /// <returns>A Task that represents a proxy for the Task returned by <paramref name="function"/>.</returns>
5681 /// <exception cref="T:System.ArgumentNullException">
5682 /// The <paramref name="function"/> parameter was null.
5684 public static Task Run(Func<Task> function)
5686 return Run(function, default(CancellationToken));
5691 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5692 /// Task returned by <paramref name="function"/>.
5694 /// <param name="function">The work to execute asynchronously</param>
5695 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5696 /// <returns>A Task that represents a proxy for the Task returned by <paramref name="function"/>.</returns>
5697 /// <exception cref="T:System.ArgumentNullException">
5698 /// The <paramref name="function"/> parameter was null.
5700 /// <exception cref="T:System.ObjectDisposedException">
5701 /// The <see cref="T:System.CancellationTokenSource"/> associated with <paramref name="cancellationToken"/> was disposed.
5703 public static Task Run(Func<Task> function, CancellationToken cancellationToken)
5706 if (function == null) throw new ArgumentNullException("function");
5707 Contract.EndContractBlock();
5709 if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
5711 cancellationToken.ThrowIfSourceDisposed();
5714 // Short-circuit if we are given a pre-canceled token
5715 if (cancellationToken.IsCancellationRequested)
5716 return Task.FromCancellation(cancellationToken);
5718 // Kick off initial Task, which will call the user-supplied function and yield a Task.
5719 Task<Task> task1 = Task<Task>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
5721 // Create a promise-style Task to be used as a proxy for the operation
5722 // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation.
5723 UnwrapPromise<VoidTaskResult> promise = new UnwrapPromise<VoidTaskResult>(task1, lookForOce: true);
5729 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5730 /// Task(TResult) returned by <paramref name="function"/>.
5732 /// <typeparam name="TResult">The type of the result returned by the proxy Task.</typeparam>
5733 /// <param name="function">The work to execute asynchronously</param>
5734 /// <returns>A Task(TResult) that represents a proxy for the Task(TResult) returned by <paramref name="function"/>.</returns>
5735 /// <exception cref="T:System.ArgumentNullException">
5736 /// The <paramref name="function"/> parameter was null.
5738 public static Task<TResult> Run<TResult>(Func<Task<TResult>> function)
5740 return Run(function, default(CancellationToken));
5744 /// Queues the specified work to run on the ThreadPool and returns a proxy for the
5745 /// Task(TResult) returned by <paramref name="function"/>.
5747 /// <typeparam name="TResult">The type of the result returned by the proxy Task.</typeparam>
5748 /// <param name="function">The work to execute asynchronously</param>
5749 /// <param name="cancellationToken">A cancellation token that should be used to cancel the work</param>
5750 /// <returns>A Task(TResult) that represents a proxy for the Task(TResult) returned by <paramref name="function"/>.</returns>
5751 /// <exception cref="T:System.ArgumentNullException">
5752 /// The <paramref name="function"/> parameter was null.
5754 public static Task<TResult> Run<TResult>(Func<Task<TResult>> function, CancellationToken cancellationToken)
5757 if (function == null) throw new ArgumentNullException("function");
5758 Contract.EndContractBlock();
5760 if (AppContextSwitches.ThrowExceptionIfDisposedCancellationTokenSource)
5762 cancellationToken.ThrowIfSourceDisposed();
5765 // Short-circuit if we are given a pre-canceled token
5766 if (cancellationToken.IsCancellationRequested)
5767 return Task.FromCancellation<TResult>(cancellationToken);
5769 // Kick off initial Task, which will call the user-supplied function and yield a Task.
5770 Task<Task<TResult>> task1 = Task<Task<TResult>>.Factory.StartNew(function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
5772 // Create a promise-style Task to be used as a proxy for the operation
5773 // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation.
5774 UnwrapPromise<TResult> promise = new UnwrapPromise<TResult>(task1, lookForOce: true);
5782 #region Delay methods
5785 /// Creates a Task that will complete after a time delay.
5787 /// <param name="delay">The time span to wait before completing the returned Task</param>
5788 /// <returns>A Task that represents the time delay</returns>
5789 /// <exception cref="T:System.ArgumentOutOfRangeException">
5790 /// The <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
5793 /// After the specified time delay, the Task is completed in RanToCompletion state.
5795 public static Task Delay(TimeSpan delay)
5797 return Delay(delay, default(CancellationToken));
5801 /// Creates a Task that will complete after a time delay.
5803 /// <param name="delay">The time span to wait before completing the returned Task</param>
5804 /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned Task</param>
5805 /// <returns>A Task that represents the time delay</returns>
5806 /// <exception cref="T:System.ArgumentOutOfRangeException">
5807 /// The <paramref name="delay"/> is less than -1 or greater than Int32.MaxValue.
5809 /// <exception cref="T:System.ObjectDisposedException">
5810 /// The provided <paramref name="cancellationToken"/> has already been disposed.
5813 /// If the cancellation token is signaled before the specified time delay, then the Task is completed in
5814 /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time
5815 /// delay has expired.
5817 public static Task Delay(TimeSpan delay, CancellationToken cancellationToken)
5819 long totalMilliseconds = (long)delay.TotalMilliseconds;
5820 if (totalMilliseconds < -1 || totalMilliseconds > Int32.MaxValue)
5822 throw new ArgumentOutOfRangeException("delay", Environment.GetResourceString("Task_Delay_InvalidDelay"));
5825 return Delay((int)totalMilliseconds, cancellationToken);
5829 /// Creates a Task that will complete after a time delay.
5831 /// <param name="millisecondsDelay">The number of milliseconds to wait before completing the returned Task</param>
5832 /// <returns>A Task that represents the time delay</returns>
5833 /// <exception cref="T:System.ArgumentOutOfRangeException">
5834 /// The <paramref name="millisecondsDelay"/> is less than -1.
5837 /// After the specified time delay, the Task is completed in RanToCompletion state.
5839 public static Task Delay(int millisecondsDelay)
5841 return Delay(millisecondsDelay, default(CancellationToken));
5845 /// Creates a Task that will complete after a time delay.
5847 /// <param name="millisecondsDelay">The number of milliseconds to wait before completing the returned Task</param>
5848 /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned Task</param>
5849 /// <returns>A Task that represents the time delay</returns>
5850 /// <exception cref="T:System.ArgumentOutOfRangeException">
5851 /// The <paramref name="millisecondsDelay"/> is less than -1.
5853 /// <exception cref="T:System.ObjectDisposedException">
5854 /// The provided <paramref name="cancellationToken"/> has already been disposed.
5857 /// If the cancellation token is signaled before the specified time delay, then the Task is completed in
5858 /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time
5859 /// delay has expired.
5861 public static Task Delay(int millisecondsDelay, CancellationToken cancellationToken)
5863 // Throw on non-sensical time
5864 if (millisecondsDelay < -1)
5866 throw new ArgumentOutOfRangeException("millisecondsDelay", Environment.GetResourceString("Task_Delay_InvalidMillisecondsDelay"));
5868 Contract.EndContractBlock();
5870 // some short-cuts in case quick completion is in order
5871 if (cancellationToken.IsCancellationRequested)
5873 // return a Task created as already-Canceled
5874 return Task.FromCancellation(cancellationToken);
5876 else if (millisecondsDelay == 0)
5878 // return a Task created as already-RanToCompletion
5879 return Task.CompletedTask;
5882 // Construct a promise-style Task to encapsulate our return value
5883 var promise = new DelayPromise(cancellationToken);
5885 // Register our cancellation token, if necessary.
5886 if (cancellationToken.CanBeCanceled)
5888 promise.Registration = cancellationToken.InternalRegisterWithoutEC(state => ((DelayPromise)state).Complete(), promise);
5891 // ... and create our timer and make sure that it stays rooted.
5892 if (millisecondsDelay != Timeout.Infinite) // no need to create the timer if it's an infinite timeout
5894 promise.Timer = new Timer(state => ((DelayPromise)state).Complete(), promise, millisecondsDelay, Timeout.Infinite);
5895 promise.Timer.KeepRootedWhileScheduled();
5898 // Return the timer proxy task
5902 /// <summary>Task that also stores the completion closure and logic for Task.Delay implementation.</summary>
5903 private sealed class DelayPromise : Task<VoidTaskResult>
5905 internal DelayPromise(CancellationToken token)
5909 if (AsyncCausalityTracer.LoggingOn)
5910 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.Delay", 0);
5912 if (Task.s_asyncDebuggingEnabled)
5914 AddToActiveTasks(this);
5918 internal readonly CancellationToken Token;
5919 internal CancellationTokenRegistration Registration;
5920 internal Timer Timer;
5922 internal void Complete()
5924 // Transition the task to completed.
5927 if (Token.IsCancellationRequested)
5929 setSucceeded = TrySetCanceled(Token);
5933 if (AsyncCausalityTracer.LoggingOn)
5934 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
5936 if (Task.s_asyncDebuggingEnabled)
5938 RemoveFromActiveTasks(this.Id);
5940 setSucceeded = TrySetResult(default(VoidTaskResult));
5943 // If we won the ----, also clean up.
5946 if (Timer != null) Timer.Dispose();
5947 Registration.Dispose();
5955 /// Creates a task that will complete when all of the supplied tasks have completed.
5957 /// <param name="tasks">The tasks to wait on for completion.</param>
5958 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
5961 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
5962 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
5965 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
5968 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
5971 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
5972 /// state before it's returned to the caller.
5975 /// <exception cref="T:System.ArgumentNullException">
5976 /// The <paramref name="tasks"/> argument was null.
5978 /// <exception cref="T:System.ArgumentException">
5979 /// The <paramref name="tasks"/> collection contained a null task.
5981 public static Task WhenAll(IEnumerable<Task> tasks)
5983 // Take a more efficient path if tasks is actually an array
5984 Task[] taskArray = tasks as Task[];
5985 if (taskArray != null)
5987 return WhenAll(taskArray);
5990 // Skip a List allocation/copy if tasks is a collection
5991 ICollection<Task> taskCollection = tasks as ICollection<Task>;
5992 if (taskCollection != null)
5995 taskArray = new Task[taskCollection.Count];
5996 foreach (var task in tasks)
5998 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
5999 taskArray[index++] = task;
6001 return InternalWhenAll(taskArray);
6004 // Do some argument checking and convert tasks to a List (and later an array).
6005 if (tasks == null) throw new ArgumentNullException("tasks");
6006 List<Task> taskList = new List<Task>();
6007 foreach (Task task in tasks)
6009 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6013 // Delegate the rest to InternalWhenAll()
6014 return InternalWhenAll(taskList.ToArray());
6018 /// Creates a task that will complete when all of the supplied tasks have completed.
6020 /// <param name="tasks">The tasks to wait on for completion.</param>
6021 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
6024 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
6025 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
6028 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
6031 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
6034 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
6035 /// state before it's returned to the caller.
6038 /// <exception cref="T:System.ArgumentNullException">
6039 /// The <paramref name="tasks"/> argument was null.
6041 /// <exception cref="T:System.ArgumentException">
6042 /// The <paramref name="tasks"/> array contained a null task.
6044 public static Task WhenAll(params Task[] tasks)
6046 // Do some argument checking and make a defensive copy of the tasks array
6047 if (tasks == null) throw new ArgumentNullException("tasks");
6048 Contract.EndContractBlock();
6050 int taskCount = tasks.Length;
6051 if (taskCount == 0) return InternalWhenAll(tasks); // Small optimization in the case of an empty array.
6053 Task[] tasksCopy = new Task[taskCount];
6054 for (int i = 0; i < taskCount; i++)
6056 Task task = tasks[i];
6057 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6058 tasksCopy[i] = task;
6061 // The rest can be delegated to InternalWhenAll()
6062 return InternalWhenAll(tasksCopy);
6065 // Some common logic to support WhenAll() methods
6066 // tasks should be a defensive copy.
6067 private static Task InternalWhenAll(Task[] tasks)
6069 Contract.Requires(tasks != null, "Expected a non-null tasks array");
6070 return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
6071 Task.CompletedTask :
6072 new WhenAllPromise(tasks);
6075 // A Task<VoidTaskResult> that gets completed when all of its constituent tasks complete.
6076 // Completion logic will analyze the antecedents in order to choose completion status.
6077 // This type allows us to replace this logic:
6078 // Task<VoidTaskResult> promise = new Task<VoidTaskResult>(...);
6079 // Action<Task> completionAction = delegate { <completion logic>};
6080 // TaskFactory.CommonCWAllLogic(tasksCopy).AddCompletionAction(completionAction);
6082 // which involves several allocations, with this logic:
6083 // return new WhenAllPromise(tasksCopy);
6084 // which saves a couple of allocations and enables debugger notification specialization.
6086 // Used in InternalWhenAll(Task[])
6087 private sealed class WhenAllPromise : Task<VoidTaskResult>, ITaskCompletionAction
6090 /// Stores all of the constituent tasks. Tasks clear themselves out of this
6091 /// array as they complete, but only if they don't have their wait notification bit set.
6093 private readonly Task[] m_tasks;
6094 /// <summary>The number of tasks remaining to complete.</summary>
6095 private int m_count;
6097 internal WhenAllPromise(Task[] tasks) :
6100 Contract.Requires(tasks != null, "Expected a non-null task array");
6101 Contract.Requires(tasks.Length > 0, "Expected a non-zero length task array");
6103 if (AsyncCausalityTracer.LoggingOn)
6104 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.WhenAll", 0);
6106 if (s_asyncDebuggingEnabled)
6108 AddToActiveTasks(this);
6112 m_count = tasks.Length;
6114 foreach (var task in tasks)
6116 if (task.IsCompleted) this.Invoke(task); // short-circuit the completion action, if possible
6117 else task.AddCompletionAction(this); // simple completion action
6121 public void Invoke(Task completedTask)
6123 if (AsyncCausalityTracer.LoggingOn)
6124 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
6126 // Decrement the count, and only continue to complete the promise if we're the last one.
6127 if (Interlocked.Decrement(ref m_count) == 0)
6129 // Set up some accounting variables
6130 List<ExceptionDispatchInfo> observedExceptions = null;
6131 Task canceledTask = null;
6133 // Loop through antecedents:
6134 // If any one of them faults, the result will be faulted
6135 // If none fault, but at least one is canceled, the result will be canceled
6136 // If none fault or are canceled, then result will be RanToCompletion
6137 for (int i = 0; i < m_tasks.Length; i++)
6139 var task = m_tasks[i];
6140 Contract.Assert(task != null, "Constituent task in WhenAll should never be null");
6144 if (observedExceptions == null) observedExceptions = new List<ExceptionDispatchInfo>();
6145 observedExceptions.AddRange(task.GetExceptionDispatchInfos());
6147 else if (task.IsCanceled)
6149 if (canceledTask == null) canceledTask = task; // use the first task that's canceled
6152 // Regardless of completion state, if the task has its debug bit set, transfer it to the
6153 // WhenAll task. We must do this before we complete the task.
6154 if (task.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
6155 else m_tasks[i] = null; // avoid holding onto tasks unnecessarily
6158 if (observedExceptions != null)
6160 Contract.Assert(observedExceptions.Count > 0, "Expected at least one exception");
6162 //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there
6164 TrySetException(observedExceptions);
6166 else if (canceledTask != null)
6168 TrySetCanceled(canceledTask.CancellationToken, canceledTask.GetCancellationExceptionDispatchInfo());
6172 if (AsyncCausalityTracer.LoggingOn)
6173 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
6175 if (Task.s_asyncDebuggingEnabled)
6177 RemoveFromActiveTasks(this.Id);
6179 TrySetResult(default(VoidTaskResult));
6182 Contract.Assert(m_count >= 0, "Count should never go below 0");
6186 /// Returns whether we should notify the debugger of a wait completion. This returns
6187 /// true iff at least one constituent task has its bit set.
6189 internal override bool ShouldNotifyDebuggerOfWaitCompletion
6194 base.ShouldNotifyDebuggerOfWaitCompletion &&
6195 Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks);
6201 /// Creates a task that will complete when all of the supplied tasks have completed.
6203 /// <param name="tasks">The tasks to wait on for completion.</param>
6204 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
6207 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
6208 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
6211 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
6214 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
6215 /// The Result of the returned task will be set to an array containing all of the results of the
6216 /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output
6217 /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result).
6220 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
6221 /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements.
6224 /// <exception cref="T:System.ArgumentNullException">
6225 /// The <paramref name="tasks"/> argument was null.
6227 /// <exception cref="T:System.ArgumentException">
6228 /// The <paramref name="tasks"/> collection contained a null task.
6230 public static Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>> tasks)
6232 // Take a more efficient route if tasks is actually an array
6233 Task<TResult>[] taskArray = tasks as Task<TResult>[];
6234 if (taskArray != null)
6236 return WhenAll<TResult>(taskArray);
6239 // Skip a List allocation/copy if tasks is a collection
6240 ICollection<Task<TResult>> taskCollection = tasks as ICollection<Task<TResult>>;
6241 if (taskCollection != null)
6244 taskArray = new Task<TResult>[taskCollection.Count];
6245 foreach (var task in tasks)
6247 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6248 taskArray[index++] = task;
6250 return InternalWhenAll<TResult>(taskArray);
6253 // Do some argument checking and convert tasks into a List (later an array)
6254 if (tasks == null) throw new ArgumentNullException("tasks");
6255 List<Task<TResult>> taskList = new List<Task<TResult>>();
6256 foreach (Task<TResult> task in tasks)
6258 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6262 // Delegate the rest to InternalWhenAll<TResult>().
6263 return InternalWhenAll<TResult>(taskList.ToArray());
6267 /// Creates a task that will complete when all of the supplied tasks have completed.
6269 /// <param name="tasks">The tasks to wait on for completion.</param>
6270 /// <returns>A task that represents the completion of all of the supplied tasks.</returns>
6273 /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state,
6274 /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks.
6277 /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state.
6280 /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state.
6281 /// The Result of the returned task will be set to an array containing all of the results of the
6282 /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output
6283 /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result).
6286 /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion
6287 /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements.
6290 /// <exception cref="T:System.ArgumentNullException">
6291 /// The <paramref name="tasks"/> argument was null.
6293 /// <exception cref="T:System.ArgumentException">
6294 /// The <paramref name="tasks"/> array contained a null task.
6296 public static Task<TResult[]> WhenAll<TResult>(params Task<TResult>[] tasks)
6298 // Do some argument checking and make a defensive copy of the tasks array
6299 if (tasks == null) throw new ArgumentNullException("tasks");
6300 Contract.EndContractBlock();
6302 int taskCount = tasks.Length;
6303 if (taskCount == 0) return InternalWhenAll<TResult>(tasks); // small optimization in the case of an empty task array
6305 Task<TResult>[] tasksCopy = new Task<TResult>[taskCount];
6306 for (int i = 0; i < taskCount; i++)
6308 Task<TResult> task = tasks[i];
6309 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6310 tasksCopy[i] = task;
6313 // Delegate the rest to InternalWhenAll<TResult>()
6314 return InternalWhenAll<TResult>(tasksCopy);
6317 // Some common logic to support WhenAll<TResult> methods
6318 private static Task<TResult[]> InternalWhenAll<TResult>(Task<TResult>[] tasks)
6320 Contract.Requires(tasks != null, "Expected a non-null tasks array");
6321 return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait
6322 new Task<TResult[]>(false, new TResult[0], TaskCreationOptions.None, default(CancellationToken)) :
6323 new WhenAllPromise<TResult>(tasks);
6326 // A Task<T> that gets completed when all of its constituent tasks complete.
6327 // Completion logic will analyze the antecedents in order to choose completion status.
6328 // See comments for non-generic version of WhenAllPromise class.
6330 // Used in InternalWhenAll<TResult>(Task<TResult>[])
6331 private sealed class WhenAllPromise<T> : Task<T[]>, ITaskCompletionAction
6334 /// Stores all of the constituent tasks. Tasks clear themselves out of this
6335 /// array as they complete, but only if they don't have their wait notification bit set.
6337 private readonly Task<T>[] m_tasks;
6338 /// <summary>The number of tasks remaining to complete.</summary>
6339 private int m_count;
6341 internal WhenAllPromise(Task<T>[] tasks) :
6344 Contract.Requires(tasks != null, "Expected a non-null task array");
6345 Contract.Requires(tasks.Length > 0, "Expected a non-zero length task array");
6348 m_count = tasks.Length;
6350 if (AsyncCausalityTracer.LoggingOn)
6351 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.WhenAll", 0);
6353 if (s_asyncDebuggingEnabled)
6355 AddToActiveTasks(this);
6358 foreach (var task in tasks)
6360 if (task.IsCompleted) this.Invoke(task); // short-circuit the completion action, if possible
6361 else task.AddCompletionAction(this); // simple completion action
6365 public void Invoke(Task ignored)
6367 if (AsyncCausalityTracer.LoggingOn)
6368 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
6370 // Decrement the count, and only continue to complete the promise if we're the last one.
6371 if (Interlocked.Decrement(ref m_count) == 0)
6373 // Set up some accounting variables
6374 T[] results = new T[m_tasks.Length];
6375 List<ExceptionDispatchInfo> observedExceptions = null;
6376 Task canceledTask = null;
6378 // Loop through antecedents:
6379 // If any one of them faults, the result will be faulted
6380 // If none fault, but at least one is canceled, the result will be canceled
6381 // If none fault or are canceled, then result will be RanToCompletion
6382 for (int i = 0; i < m_tasks.Length; i++)
6384 Task<T> task = m_tasks[i];
6385 Contract.Assert(task != null, "Constituent task in WhenAll should never be null");
6389 if (observedExceptions == null) observedExceptions = new List<ExceptionDispatchInfo>();
6390 observedExceptions.AddRange(task.GetExceptionDispatchInfos());
6392 else if (task.IsCanceled)
6394 if (canceledTask == null) canceledTask = task; // use the first task that's canceled
6398 Contract.Assert(task.Status == TaskStatus.RanToCompletion);
6399 results[i] = task.GetResultCore(waitCompletionNotification: false); // avoid Result, which would triggering debug notification
6402 // Regardless of completion state, if the task has its debug bit set, transfer it to the
6403 // WhenAll task. We must do this before we complete the task.
6404 if (task.IsWaitNotificationEnabled) this.SetNotificationForWaitCompletion(enabled: true);
6405 else m_tasks[i] = null; // avoid holding onto tasks unnecessarily
6408 if (observedExceptions != null)
6410 Contract.Assert(observedExceptions.Count > 0, "Expected at least one exception");
6412 //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there
6414 TrySetException(observedExceptions);
6416 else if (canceledTask != null)
6418 TrySetCanceled(canceledTask.CancellationToken, canceledTask.GetCancellationExceptionDispatchInfo());
6422 if (AsyncCausalityTracer.LoggingOn)
6423 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
6425 if (Task.s_asyncDebuggingEnabled)
6427 RemoveFromActiveTasks(this.Id);
6429 TrySetResult(results);
6432 Contract.Assert(m_count >= 0, "Count should never go below 0");
6436 /// Returns whether we should notify the debugger of a wait completion. This returns true
6437 /// iff at least one constituent task has its bit set.
6439 internal override bool ShouldNotifyDebuggerOfWaitCompletion
6444 base.ShouldNotifyDebuggerOfWaitCompletion &&
6445 Task.AnyTaskRequiresNotifyDebuggerOfWaitCompletion(m_tasks);
6453 /// Creates a task that will complete when any of the supplied tasks have completed.
6455 /// <param name="tasks">The tasks to wait on for completion.</param>
6456 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6458 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6459 /// 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.
6461 /// <exception cref="T:System.ArgumentNullException">
6462 /// The <paramref name="tasks"/> argument was null.
6464 /// <exception cref="T:System.ArgumentException">
6465 /// The <paramref name="tasks"/> array contained a null task, or was empty.
6467 public static Task<Task> WhenAny(params Task[] tasks)
6469 if (tasks == null) throw new ArgumentNullException("tasks");
6470 if (tasks.Length == 0)
6472 throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), "tasks");
6474 Contract.EndContractBlock();
6476 // Make a defensive copy, as the user may manipulate the tasks array
6477 // after we return but before the WhenAny asynchronously completes.
6478 int taskCount = tasks.Length;
6479 Task[] tasksCopy = new Task[taskCount];
6480 for (int i = 0; i < taskCount; i++)
6482 Task task = tasks[i];
6483 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6484 tasksCopy[i] = task;
6487 // Previously implemented CommonCWAnyLogic() can handle the rest
6488 return TaskFactory.CommonCWAnyLogic(tasksCopy);
6492 /// Creates a task that will complete when any of the supplied tasks have completed.
6494 /// <param name="tasks">The tasks to wait on for completion.</param>
6495 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6497 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6498 /// 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.
6500 /// <exception cref="T:System.ArgumentNullException">
6501 /// The <paramref name="tasks"/> argument was null.
6503 /// <exception cref="T:System.ArgumentException">
6504 /// The <paramref name="tasks"/> collection contained a null task, or was empty.
6506 public static Task<Task> WhenAny(IEnumerable<Task> tasks)
6508 if (tasks == null) throw new ArgumentNullException("tasks");
6509 Contract.EndContractBlock();
6511 // Make a defensive copy, as the user may manipulate the tasks collection
6512 // after we return but before the WhenAny asynchronously completes.
6513 List<Task> taskList = new List<Task>();
6514 foreach (Task task in tasks)
6516 if (task == null) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_NullTask"), "tasks");
6520 if (taskList.Count == 0)
6522 throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), "tasks");
6525 // Previously implemented CommonCWAnyLogic() can handle the rest
6526 return TaskFactory.CommonCWAnyLogic(taskList);
6530 /// Creates a task that will complete when any of the supplied tasks have completed.
6532 /// <param name="tasks">The tasks to wait on for completion.</param>
6533 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6535 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6536 /// 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.
6538 /// <exception cref="T:System.ArgumentNullException">
6539 /// The <paramref name="tasks"/> argument was null.
6541 /// <exception cref="T:System.ArgumentException">
6542 /// The <paramref name="tasks"/> array contained a null task, or was empty.
6544 public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks)
6546 // We would just like to do this:
6547 // return (Task<Task<TResult>>) WhenAny( (Task[]) tasks);
6548 // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>.
6550 // Call WhenAny(Task[]) for basic functionality
6551 Task<Task> intermediate = WhenAny((Task[])tasks);
6553 // Return a continuation task with the correct result type
6554 return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default(CancellationToken),
6555 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
6559 /// Creates a task that will complete when any of the supplied tasks have completed.
6561 /// <param name="tasks">The tasks to wait on for completion.</param>
6562 /// <returns>A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed.</returns>
6564 /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state
6565 /// 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.
6567 /// <exception cref="T:System.ArgumentNullException">
6568 /// The <paramref name="tasks"/> argument was null.
6570 /// <exception cref="T:System.ArgumentException">
6571 /// The <paramref name="tasks"/> collection contained a null task, or was empty.
6573 public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks)
6575 // We would just like to do this:
6576 // return (Task<Task<TResult>>) WhenAny( (IEnumerable<Task>) tasks);
6577 // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>.
6579 // Call WhenAny(IEnumerable<Task>) for basic functionality
6580 Task<Task> intermediate = WhenAny((IEnumerable<Task>)tasks);
6582 // Return a continuation task with the correct result type
6583 return intermediate.ContinueWith(Task<TResult>.TaskWhenAnyCast, default(CancellationToken),
6584 TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default);
6588 [FriendAccessAllowed]
6589 internal static Task<TResult> CreateUnwrapPromise<TResult>(Task outerTask, bool lookForOce)
6591 Contract.Requires(outerTask != null);
6593 return new UnwrapPromise<TResult>(outerTask, lookForOce);
6596 internal virtual Delegate[] GetDelegateContinuationsForDebugger()
6598 //Avoid an infinite loop by making sure the continuation object is not a reference to istelf.
6599 if (this.m_continuationObject != this)
6600 return GetDelegatesFromContinuationObject(this.m_continuationObject);
6605 internal static Delegate[] GetDelegatesFromContinuationObject(object continuationObject)
6607 if (continuationObject != null)
6609 Action singleAction = continuationObject as Action;
6610 if (singleAction != null)
6612 return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(singleAction) };
6615 TaskContinuation taskContinuation = continuationObject as TaskContinuation;
6616 if (taskContinuation != null)
6618 return taskContinuation.GetDelegateContinuationsForDebugger();
6621 Task continuationTask = continuationObject as Task;
6622 if (continuationTask != null)
6624 Contract.Assert(continuationTask.m_action == null);
6625 Delegate[] delegates = continuationTask.GetDelegateContinuationsForDebugger();
6626 if (delegates != null)
6630 //We need this ITaskCompletionAction after the Task because in the case of UnwrapPromise
6631 //the VS debugger is more interested in the continuation than the internal invoke()
6632 ITaskCompletionAction singleCompletionAction = continuationObject as ITaskCompletionAction;
6633 if (singleCompletionAction != null)
6635 return new Delegate[] { new Action<Task>(singleCompletionAction.Invoke) };
6638 List<object> continuationList = continuationObject as List<object>;
6639 if (continuationList != null)
6641 List<Delegate> result = new List<Delegate>();
6642 foreach (object obj in continuationList)
6644 var innerDelegates = GetDelegatesFromContinuationObject(obj);
6645 if (innerDelegates != null)
6647 foreach (var del in innerDelegates)
6655 return result.ToArray();
6662 private static Task GetActiveTaskFromId(int taskId)
6665 s_currentActiveTasks.TryGetValue(taskId, out task);
6669 private static Task[] GetActiveTasks()
6672 return new List<Task>(s_currentActiveTasks.Values).ToArray();
6676 // Proxy class for better debugging experience
6677 internal class SystemThreadingTasks_TaskDebugView
6679 private Task m_task;
6681 public SystemThreadingTasks_TaskDebugView(Task task)
6686 public object AsyncState { get { return m_task.AsyncState; } }
6687 public TaskCreationOptions CreationOptions { get { return m_task.CreationOptions; } }
6688 public Exception Exception { get { return m_task.Exception; } }
6689 public int Id { get { return m_task.Id; } }
6690 public bool CancellationPending { get { return (m_task.Status == TaskStatus.WaitingToRun) && m_task.CancellationToken.IsCancellationRequested; } }
6691 public TaskStatus Status { get { return m_task.Status; } }
6694 // Special purpose derivation of Task that supports limited replication through
6695 // overriding the ShouldReplicate() method. This is used by the Parallel.For/ForEach
6697 internal class ParallelForReplicatingTask : Task
6700 private int m_replicationDownCount; // downcounter to control replication
6706 [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
6707 internal ParallelForReplicatingTask(
6708 ParallelOptions parallelOptions, Action action, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions)
6709 : base(action, null, Task.InternalCurrent, default(CancellationToken), creationOptions, internalOptions | InternalTaskOptions.SelfReplicating, null)
6711 // Compute the down count based on scheduler/DOP info in parallelOptions.
6712 m_replicationDownCount = parallelOptions.EffectiveMaxConcurrencyLevel;
6714 StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
6715 PossiblyCaptureContext(ref stackMark);
6719 // Controls degree of replication. If downcounter is initialized to -1, then
6720 // replication will be allowed to "run wild". Otherwise, this method decrements
6721 // the downcounter each time it is called, calling false when it is called with
6722 // a zero downcounter. This method returning false effectively ends the replication
6723 // of the associated ParallelForReplicatingTask.
6724 internal override bool ShouldReplicate()
6726 if (m_replicationDownCount == -1) return true; // "run wild"
6728 if (m_replicationDownCount > 0) // Decrement and return true if not called with 0 downcount
6730 m_replicationDownCount--;
6734 return false; // We're done replicating
6737 internal override Task CreateReplicaTask(Action<object> taskReplicaDelegate, Object stateObject, Task parentTask, TaskScheduler taskScheduler,
6738 TaskCreationOptions creationOptionsForReplica, InternalTaskOptions internalOptionsForReplica)
6740 return new ParallelForReplicaTask(taskReplicaDelegate, stateObject, parentTask, taskScheduler,
6741 creationOptionsForReplica, internalOptionsForReplica);
6747 internal class ParallelForReplicaTask : Task
6749 internal object m_stateForNextReplica; // some replicas may quit prematurely, in which case they will use this variable
6750 // to save state they want to be picked up by the next replica queued to the same thread
6752 internal object m_stateFromPreviousReplica; // some replicas may quit prematurely, in which case they will use this variable
6753 // to save state they want to be picked up by the next replica queued to the same thread
6755 internal Task m_handedOverChildReplica; // some replicas may quit prematurely, in which case they will use this variable
6756 // to hand over the child replica they had queued to the next task that will replace them
6758 internal ParallelForReplicaTask(Action<object> taskReplicaDelegate, Object stateObject, Task parentTask, TaskScheduler taskScheduler,
6759 TaskCreationOptions creationOptionsForReplica, InternalTaskOptions internalOptionsForReplica) :
6760 base(taskReplicaDelegate, stateObject, parentTask, default(CancellationToken), creationOptionsForReplica, internalOptionsForReplica, taskScheduler)
6764 // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
6765 internal override Object SavedStateForNextReplica
6767 get { return m_stateForNextReplica; }
6769 set { m_stateForNextReplica = value; }
6772 // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica
6773 internal override Object SavedStateFromPreviousReplica
6775 get { return m_stateFromPreviousReplica; }
6777 set { m_stateFromPreviousReplica = value; }
6780 // Allows internal deriving classes to support replicas that exit prematurely and want to hand over the child replica that they
6781 // had queued, so that the replacement replica can work with that child task instead of queuing up yet another one
6782 internal override Task HandedOverChildReplica
6784 get { return m_handedOverChildReplica; }
6786 set { m_handedOverChildReplica = value; }
6791 /// Specifies flags that control optional behavior for the creation and execution of tasks.
6793 // NOTE: These options are a subset of TaskContinuationsOptions, thus before adding a flag check it is
6794 // not already in use.
6797 public enum TaskCreationOptions
6800 /// Specifies that the default behavior should be used.
6805 /// A hint to a <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> to schedule a
6806 /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to
6807 /// be run sooner, and tasks scheduled later will be more likely to be run later.
6809 PreferFairness = 0x01,
6812 /// Specifies that a task will be a long-running, course-grained operation. It provides a hint to the
6813 /// <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> that oversubscription may be
6819 /// Specifies that a task is attached to a parent in the task hierarchy.
6821 AttachedToParent = 0x04,
6824 /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task.
6826 DenyChildAttach = 0x08,
6829 /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations
6830 /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler.
6832 HideScheduler = 0x10,
6834 // 0x20 is already being used in TaskContinuationOptions
6837 /// Forces continuations added to the current task to be executed asynchronously.
6838 /// This option has precedence over TaskContinuationOptions.ExecuteSynchronously
6840 RunContinuationsAsynchronously = 0x40
6845 /// Task creation flags which are only used internally.
6849 internal enum InternalTaskOptions
6851 /// <summary> Specifies "No internal task options" </summary>
6854 /// <summary>Used to filter out internal vs. public task creation options.</summary>
6855 InternalOptionsMask = 0x0000FF00,
6857 ChildReplica = 0x0100,
6858 ContinuationTask = 0x0200,
6859 PromiseTask = 0x0400,
6860 SelfReplicating = 0x0800,
6863 /// Store the presence of TaskContinuationOptions.LazyCancellation, since it does not directly
6864 /// translate into any TaskCreationOptions.
6866 LazyCancellation = 0x1000,
6868 /// <summary>Specifies that the task will be queued by the runtime before handing it over to the user.
6869 /// This flag will be used to skip the cancellationtoken registration step, which is only meant for unstarted tasks.</summary>
6870 QueuedByRuntime = 0x2000,
6873 /// Denotes that Dispose should be a complete nop for a Task. Used when constructing tasks that are meant to be cached/reused.
6875 DoNotDispose = 0x4000
6879 /// Specifies flags that control optional behavior for the creation and execution of continuation tasks.
6883 public enum TaskContinuationOptions
6886 /// Default = "Continue on any, no task options, run asynchronously"
6887 /// Specifies that the default behavior should be used. Continuations, by default, will
6888 /// be scheduled when the antecedent task completes, regardless of the task's final <see
6889 /// cref="System.Threading.Tasks.TaskStatus">TaskStatus</see>.
6893 // These are identical to their meanings and values in TaskCreationOptions
6896 /// A hint to a <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> to schedule a
6897 /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to
6898 /// be run sooner, and tasks scheduled later will be more likely to be run later.
6900 PreferFairness = 0x01,
6903 /// Specifies that a task will be a long-running, course-grained operation. It provides
6904 /// a hint to the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> that
6905 /// oversubscription may be warranted.
6909 /// Specifies that a task is attached to a parent in the task hierarchy.
6911 AttachedToParent = 0x04,
6914 /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task.
6916 DenyChildAttach = 0x08,
6918 /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations
6919 /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler.
6921 HideScheduler = 0x10,
6924 /// In the case of continuation cancellation, prevents completion of the continuation until the antecedent has completed.
6926 LazyCancellation = 0x20,
6928 RunContinuationsAsynchronously = 0x40,
6930 // These are specific to continuations
6933 /// Specifies that the continuation task should not be scheduled if its antecedent ran to completion.
6934 /// This option is not valid for multi-task continuations.
6936 NotOnRanToCompletion = 0x10000,
6938 /// Specifies that the continuation task should not be scheduled if its antecedent threw an unhandled
6939 /// exception. This option is not valid for multi-task continuations.
6941 NotOnFaulted = 0x20000,
6943 /// Specifies that the continuation task should not be scheduled if its antecedent was canceled. This
6944 /// option is not valid for multi-task continuations.
6946 NotOnCanceled = 0x40000,
6948 /// Specifies that the continuation task should be scheduled only if its antecedent ran to
6949 /// completion. This option is not valid for multi-task continuations.
6951 OnlyOnRanToCompletion = NotOnFaulted | NotOnCanceled,
6953 /// Specifies that the continuation task should be scheduled only if its antecedent threw an
6954 /// unhandled exception. This option is not valid for multi-task continuations.
6956 OnlyOnFaulted = NotOnRanToCompletion | NotOnCanceled,
6958 /// Specifies that the continuation task should be scheduled only if its antecedent was canceled.
6959 /// This option is not valid for multi-task continuations.
6961 OnlyOnCanceled = NotOnRanToCompletion | NotOnFaulted,
6963 /// Specifies that the continuation task should be executed synchronously. With this option
6964 /// specified, the continuation will be run on the same thread that causes the antecedent task to
6965 /// transition into its final state. If the antecedent is already complete when the continuation is
6966 /// created, the continuation will run on the thread creating the continuation. Only very
6967 /// short-running continuations should be executed synchronously.
6969 ExecuteSynchronously = 0x80000
6973 /// Internal helper class to keep track of stack depth and decide whether we should inline or not.
6975 internal class StackGuard
6977 // current thread's depth of nested inline task executions
6978 private int m_inliningDepth = 0;
6980 // For relatively small inlining depths we don't want to get into the business of stack probing etc.
6981 // This clearly leaves a window of opportunity for the user code to SO. However a piece of code
6982 // that can SO in 20 inlines on a typical 1MB stack size probably needs to be revisited anyway.
6983 private const int MAX_UNCHECKED_INLINING_DEPTH = 20;
6985 #if !FEATURE_PAL && !PFX_LEGACY_3_5
6987 private UInt64 m_lastKnownWatermark;
6988 private static int s_pageSize;
6990 // We are conservative here. We assume that the platform needs a whole 64KB to
6991 // respond to stack overflow. This means that for very small stacks (e.g. 128KB)
6992 // we'll fail a lot of stack checks incorrectly.
6993 private const long STACK_RESERVED_SPACE = 4096 * 16;
6995 #endif // !FEATURE_PAL && !PFX_LEGACY_3_5
6998 /// This method needs to be called before attempting inline execution on the current thread.
6999 /// If false is returned, it means we are too close to the end of the stack and should give up inlining.
7000 /// Each call to TryBeginInliningScope() that returns true must be matched with a
7001 /// call to EndInliningScope() regardless of whether inlining actually took place.
7003 [SecuritySafeCritical]
7004 internal bool TryBeginInliningScope()
7006 // If we're still under the 'safe' limit we'll just skip the stack probe to save p/invoke calls
7007 if (m_inliningDepth < MAX_UNCHECKED_INLINING_DEPTH || CheckForSufficientStack())
7017 /// This needs to be called once for each previous successful TryBeginInliningScope() call after
7018 /// inlining related logic runs.
7020 internal void EndInliningScope()
7023 Contract.Assert(m_inliningDepth >= 0, "Inlining depth count should never go negative.");
7025 // do the right thing just in case...
7026 if (m_inliningDepth < 0) m_inliningDepth = 0;
7030 private unsafe bool CheckForSufficientStack()
7032 #if !FEATURE_PAL && !PFX_LEGACY_3_5
7033 // see if we already have the system page size info recorded
7034 int pageSize = s_pageSize;
7037 // If not we need to query it from GetSystemInfo()
7038 // Note that this happens only once for the process lifetime
7039 Win32Native.SYSTEM_INFO sysInfo = new Win32Native.SYSTEM_INFO();
7040 Win32Native.GetSystemInfo(ref sysInfo);
7042 s_pageSize = pageSize = sysInfo.dwPageSize;
7045 Win32Native.MEMORY_BASIC_INFORMATION stackInfo = new Win32Native.MEMORY_BASIC_INFORMATION();
7047 // We subtract one page for our request. VirtualQuery rounds UP to the next page.
7048 // Unfortunately, the stack grows down. If we're on the first page (last page in the
7049 // VirtualAlloc), we'll be moved to the next page, which is off the stack!
7051 UIntPtr currentAddr = new UIntPtr(&stackInfo - pageSize);
7052 UInt64 current64 = currentAddr.ToUInt64();
7054 // Check whether we previously recorded a deeper stack than where we currently are,
7055 // If so we don't need to do the P/Invoke to VirtualQuery
7056 if (m_lastKnownWatermark != 0 && current64 > m_lastKnownWatermark)
7059 // Actual stack probe. P/Invoke to query for the current stack allocation information.
7060 Win32Native.VirtualQuery(currentAddr.ToPointer(), ref stackInfo, (UIntPtr)(sizeof(Win32Native.MEMORY_BASIC_INFORMATION)));
7062 // If the current address minus the base (remember: the stack grows downward in the
7063 // address space) is greater than the number of bytes requested plus the reserved
7064 // space at the end, the request has succeeded.
7066 if ((current64 - ((UIntPtr)stackInfo.AllocationBase).ToUInt64()) > STACK_RESERVED_SPACE)
7068 m_lastKnownWatermark = current64;
7074 #else // !FEATURE_PAL && !PFX_LEGACY_3_5
7076 // if we're being compiled with FEATURE_PAL or PFX_LEGACY_3_5 we simply allow unchecked inlining.
7082 // Special internal struct that we use to signify that we are not interested in
7083 // a Task<VoidTaskResult>'s result.
7084 internal struct VoidTaskResult { }
7086 // Interface to which all completion actions must conform.
7087 // This interface allows us to combine functionality and reduce allocations.
7088 // For example, see Task.SetOnInvokeMres, and its use in Task.SpinThenBlockingWait().
7090 // ManualResetEvent mres = new ManualResetEventSlim(false, 0);
7091 // Action<Task> completionAction = delegate { mres.Set() ; };
7092 // AddCompletionAction(completionAction);
7093 // gets replaced with this:
7094 // SetOnInvokeMres mres = new SetOnInvokeMres();
7095 // AddCompletionAction(mres);
7096 // For additional examples of where this is used, see internal classes Task.SignalOnInvokeCDE,
7097 // Task.WhenAllPromise, Task.WhenAllPromise<T>, TaskFactory.CompleteOnCountdownPromise,
7098 // TaskFactory.CompleteOnCountdownPromise<T>, and TaskFactory.CompleteOnInvokePromise.
7099 internal interface ITaskCompletionAction
7101 void Invoke(Task completingTask);
7104 // This class encapsulates all "unwrap" logic, and also implements ITaskCompletionAction,
7105 // which minimizes the allocations needed for queuing it to its antecedent. This
7106 // logic is used by both the Unwrap extension methods and the unwrap-style Task.Run methods.
7107 internal sealed class UnwrapPromise<TResult> : Task<TResult>, ITaskCompletionAction
7109 // The possible states for our UnwrapPromise, used by Invoke() to determine which logic to execute
7110 private const byte STATE_WAITING_ON_OUTER_TASK = 0; // Invoke() means "process completed outer task"
7111 private const byte STATE_WAITING_ON_INNER_TASK = 1; // Invoke() means "process completed inner task"
7112 private const byte STATE_DONE = 2; // Invoke() means "something went wrong and we are hosed!"
7114 // Keep track of our state; initialized to STATE_WAITING_ON_OUTER_TASK in the constructor
7115 private byte _state;
7117 // "Should we check for OperationCanceledExceptions on the outer task and interpret them as proxy cancellation?"
7118 // Unwrap() sets this to false, Run() sets it to true.
7119 private readonly bool _lookForOce;
7121 public UnwrapPromise(Task outerTask, bool lookForOce)
7122 : base((object)null, outerTask.CreationOptions & TaskCreationOptions.AttachedToParent)
7124 Contract.Requires(outerTask != null, "Expected non-null outerTask");
7125 _lookForOce = lookForOce;
7126 _state = STATE_WAITING_ON_OUTER_TASK;
7128 if (AsyncCausalityTracer.LoggingOn)
7129 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task.Unwrap", 0);
7131 if (Task.s_asyncDebuggingEnabled)
7133 AddToActiveTasks(this);
7136 // Link ourselves to the outer task.
7137 // If the outer task has already completed, take the fast path
7138 // of immediately transferring its results or processing the inner task.
7139 if (outerTask.IsCompleted)
7141 ProcessCompletedOuterTask(outerTask);
7143 else // Otherwise, process its completion asynchronously.
7145 outerTask.AddCompletionAction(this);
7149 // For ITaskCompletionAction
7150 public void Invoke(Task completingTask)
7152 // Check the current stack guard. If we're ok to inline,
7153 // process the task, and reset the guard when we're done.
7154 var sg = Task.CurrentStackGuard;
7155 if (sg.TryBeginInliningScope())
7157 try { InvokeCore(completingTask); }
7158 finally { sg.EndInliningScope(); }
7160 // Otherwise, we're too deep on the stack, and
7161 // we shouldn't run the continuation chain here, so queue a work
7162 // item to call back here to Invoke asynchronously.
7163 else InvokeCoreAsync(completingTask);
7167 /// Processes the completed task. InvokeCore could be called twice:
7168 /// once for the outer task, once for the inner task.
7170 /// <param name="completingTask">The completing outer or inner task.</param>
7171 private void InvokeCore(Task completingTask)
7175 case STATE_WAITING_ON_OUTER_TASK:
7176 ProcessCompletedOuterTask(completingTask);
7177 // We bump the state inside of ProcessCompletedOuterTask because it can also be called from the constructor.
7179 case STATE_WAITING_ON_INNER_TASK:
7180 bool result = TrySetFromTask(completingTask, lookForOce: false);
7181 _state = STATE_DONE; // bump the state
7182 Contract.Assert(result, "Expected TrySetFromTask from inner task to succeed");
7185 Contract.Assert(false, "UnwrapPromise in illegal state");
7190 // Calls InvokeCore asynchronously.
7191 [SecuritySafeCritical]
7192 private void InvokeCoreAsync(Task completingTask)
7194 // Queue a call to Invoke. If we're so deep on the stack that we're at risk of overflowing,
7195 // there's a high liklihood this thread is going to be doing lots more work before
7196 // returning to the thread pool (at the very least unwinding through thousands of
7197 // stack frames). So we queue to the global queue.
7198 ThreadPool.UnsafeQueueUserWorkItem(state =>
7200 // InvokeCore(completingTask);
7201 var tuple = (Tuple<UnwrapPromise<TResult>, Task>)state;
7202 tuple.Item1.InvokeCore(tuple.Item2);
7203 }, Tuple.Create<UnwrapPromise<TResult>, Task>(this, completingTask));
7206 /// <summary>Processes the outer task once it's completed.</summary>
7207 /// <param name="task">The now-completed outer task.</param>
7208 private void ProcessCompletedOuterTask(Task task)
7210 Contract.Requires(task != null && task.IsCompleted, "Expected non-null, completed outer task");
7211 Contract.Assert(_state == STATE_WAITING_ON_OUTER_TASK, "We're in the wrong state!");
7213 // Bump our state before proceeding any further
7214 _state = STATE_WAITING_ON_INNER_TASK;
7216 switch (task.Status)
7218 // If the outer task did not complete successfully, then record the
7219 // cancellation/fault information to tcs.Task.
7220 case TaskStatus.Canceled:
7221 case TaskStatus.Faulted:
7222 bool result = TrySetFromTask(task, _lookForOce);
7223 Contract.Assert(result, "Expected TrySetFromTask from outer task to succeed");
7226 // Otherwise, process the inner task it returned.
7227 case TaskStatus.RanToCompletion:
7228 var taskOfTaskOfTResult = task as Task<Task<TResult>>; // it's either a Task<Task> or Task<Task<TResult>>
7229 ProcessInnerTask(taskOfTaskOfTResult != null ?
7230 taskOfTaskOfTResult.Result : ((Task<Task>)task).Result);
7235 /// <summary>Transfer the completion status from "task" to ourself.</summary>
7236 /// <param name="task">The source task whose results should be transfered to <paramref name="promise"/>.</param>
7237 /// <param name="lookForOce">Whether or not to look for OperationCanceledExceptions in task's exceptions if it faults.</param>
7238 /// <returns>true if the transfer was successful; otherwise, false.</returns>
7239 private bool TrySetFromTask(Task task, bool lookForOce)
7241 Contract.Requires(task != null && task.IsCompleted, "TrySetFromTask: Expected task to have completed.");
7243 if (AsyncCausalityTracer.LoggingOn)
7244 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, this.Id, CausalityRelation.Join);
7246 bool result = false;
7247 switch (task.Status)
7249 case TaskStatus.Canceled:
7250 result = TrySetCanceled(task.CancellationToken, task.GetCancellationExceptionDispatchInfo());
7253 case TaskStatus.Faulted:
7254 var edis = task.GetExceptionDispatchInfos();
7255 ExceptionDispatchInfo oceEdi;
7256 OperationCanceledException oce;
7257 if (lookForOce && edis.Count > 0 &&
7258 (oceEdi = edis[0]) != null &&
7259 (oce = oceEdi.SourceException as OperationCanceledException) != null)
7261 result = TrySetCanceled(oce.CancellationToken, oceEdi);
7265 result = TrySetException(edis);
7269 case TaskStatus.RanToCompletion:
7270 var taskTResult = task as Task<TResult>;
7272 if (AsyncCausalityTracer.LoggingOn)
7273 AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, this.Id, AsyncCausalityStatus.Completed);
7275 if (Task.s_asyncDebuggingEnabled)
7277 RemoveFromActiveTasks(this.Id);
7280 result = TrySetResult(taskTResult != null ? taskTResult.Result : default(TResult));
7287 /// Processes the inner task of a Task{Task} or Task{Task{TResult}},
7288 /// transferring the appropriate results to ourself.
7290 /// <param name="task">The inner task returned by the task provided by the user.</param>
7291 private void ProcessInnerTask(Task task)
7293 // If the inner task is null, the proxy should be canceled.
7296 TrySetCanceled(default(CancellationToken));
7297 _state = STATE_DONE; // ... and record that we are done
7300 // Fast path for if the inner task is already completed
7301 else if (task.IsCompleted)
7303 TrySetFromTask(task, lookForOce: false);
7304 _state = STATE_DONE; // ... and record that we are done
7307 // The inner task exists but is not yet complete, so when it does complete,
7308 // take some action to set our completion state.
7311 task.AddCompletionAction(this);
7312 // We'll record that we are done when Invoke() is called.