添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
// ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // Task.cs // <OWNER>Microsoft</OWNER> // A schedulable unit of work. // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.ExceptionServices; using System.Security; using System.Security.Permissions; using System.Threading; using System.Diagnostics; using System.Diagnostics.Contracts; using Microsoft.Win32; using System.Diagnostics.Tracing; // Disable the "reference to volatile field not treated as volatile" error. # pragma warning disable 0420 namespace System.Threading.Tasks /// < summary > /// Utility class for allocating structs as heap variables /// </ summary > internal class Shared < T > internal T Value ; internal Shared ( T value ) this . Value = value ; /// < summary > /// Represents the current stage in the lifecycle of a < see cref = " Task " /> . /// </ summary > public enum TaskStatus /// < summary > /// The task has been initialized but has not yet been scheduled. /// </ summary > Created , /// < summary > /// The task is waiting to be activated and scheduled internally by the .NET Framework infrastructure. /// </ summary > WaitingForActivation , /// < summary > /// The task has been scheduled for execution but has not yet begun executing. /// </ summary > WaitingToRun , /// < summary > /// The task is running but has not yet completed. /// </ summary > Running , // /// <summary> // /// The task is currently blocked in a wait state. // /// </summary> // Blocked, /// < summary > /// The task has finished executing and is implicitly waiting for /// attached child tasks to complete. /// </ summary > WaitingForChildrenToComplete , /// < summary > /// The task completed execution successfully. /// </ summary > RanToCompletion , /// < summary > /// The task acknowledged cancellation by throwing an OperationCanceledException with its own CancellationToken /// while the token was in signaled state, or the task's CancellationToken was already signaled before the /// task started executing. /// </ summary > Canceled , /// < summary > /// The task completed due to an unhandled exception. /// </ summary > Faulted /// < summary > /// Represents an asynchronous operation. /// </ summary > /// < remarks > /// < para > /// < see cref = " Task " /> instances may be created in a variety of ways. The most common approach is by /// using the Task type's < see cref = " Factory " /> property to retrieve a < see /// cref = " System.Threading.Tasks. TaskFactory " /> instance that can be used to create tasks for several /// purposes. For example, to create a < see cref = " Task " /> that runs an action, the factory's StartNew /// method may be used: /// < code > /// // C# /// var t = Task.Factory.StartNew(() => DoAction()); /// ' Visual Basic /// Dim t = Task.Factory.StartNew(Function() DoAction()) /// </ code > /// </ para > /// < para > /// The < see cref = " Task " /> class also provides constructors that initialize the Task but that do not /// schedule it for execution. For performance reasons, TaskFactory's StartNew method should be the /// preferred mechanism for creating and scheduling computational tasks, but for scenarios where creation /// and scheduling must be separated, the constructors may be used, and the task's < see cref = " Start () " /> /// method may then be used to schedule the task for execution at a later time. /// </ para > /// < para > /// All members of < see cref = " Task " /> , except for < see cref = " Dispose () " /> , are thread-safe /// and may be used from multiple threads concurrently. /// </ para > /// < para > /// For operations that return values, the < see cref = " System.Threading.Tasks. Task { TResult } " /> class /// should be used. /// </ para > /// < para > /// For developers implementing custom debuggers, several internal and private members of Task may be /// useful (these may change from release to release). The Int32 m_taskId field serves as the backing /// store for the < see cref = " Id " /> property, however accessing this field directly from a debugger may be /// more efficient than accessing the same value through the property's getter method (the /// s_taskIdCounter Int32 counter is used to retrieve the next available ID for a Task). Similarly, the /// Int32 m_stateFlags field stores information about the current lifecycle stage of the Task, /// information also accessible through the < see cref = " Status " /> property. The m_action System.Object /// field stores a reference to the Task's delegate, and the m_stateObject System.Object field stores the /// async state passed to the Task by the developer. Finally, for debuggers that parse stack frames, the /// InternalWait method serves a potential marker for when a Task is entering a wait operation. /// </ para > /// </ remarks > [ HostProtection ( Synchronization = true , ExternalThreading = true )] [ DebuggerTypeProxy ( typeof ( SystemThreadingTasks_TaskDebugView ))] [ DebuggerDisplay ( "Id = {Id}, Status = {Status}, Method = {DebuggerDisplayMethodDescription}" )] public class Task : IThreadPoolWorkItem , IAsyncResult , IDisposable [ ThreadStatic ] internal static Task t_currentTask ; // The currently executing task. [ ThreadStatic ] private static StackGuard t_stackGuard ; // The stack guard object for this thread internal static int s_taskIdCounter ; //static counter used to generate unique task IDs private readonly static TaskFactory s_factory = new TaskFactory (); private volatile int m_taskId ; // this task's unique ID. initialized only if it is ever requested internal object m_action ; // The body of the task. Might be Action<object>, Action<TState> or Action. Or possibly a Func. // If m_action is set to null it will indicate that we operate in the // "externally triggered completion" mode, which is exclusively meant // for the signalling Task<TResult> (aka. promise). In this mode, // we don't call InnerInvoke() in response to a Wait(), but simply wait on // the completion event which will be set when the Future class calls Finish(). // But the event would now be signalled if Cancel() is called internal object m_stateObject ; // A state object that can be optionally supplied, passed to action. internal TaskScheduler m_taskScheduler ; // The task scheduler this task runs under. internal readonly Task m_parent ; // A task's parent, or null if parent-less. internal volatile int m_stateFlags ; // State constants for m_stateFlags; // The bits of m_stateFlags are allocated as follows: // 0x40000000 - TaskBase state flag // 0x3FFF0000 - Task state flags // 0x0000FF00 - internal TaskCreationOptions flags // 0x000000FF - publicly exposed TaskCreationOptions flags // See TaskCreationOptions for bit values associated with TaskCreationOptions private const int OptionsMask = 0xFFFF; // signifies the Options portion of m_stateFlags bin: 0000 0000 0000 0000 1111 1111 1111 1111 internal const int TASK_STATE_STARTED = 0x10000; //bin: 0000 0000 0000 0001 0000 0000 0000 0000 internal const int TASK_STATE_DELEGATE_INVOKED = 0x20000; //bin: 0000 0000 0000 0010 0000 0000 0000 0000 internal const int TASK_STATE_DISPOSED = 0x40000; //bin: 0000 0000 0000 0100 0000 0000 0000 0000 internal const int TASK_STATE_EXCEPTIONOBSERVEDBYPARENT = 0x80000; //bin: 0000 0000 0000 1000 0000 0000 0000 0000 internal const int TASK_STATE_CANCELLATIONACKNOWLEDGED = 0x100000; //bin: 0000 0000 0001 0000 0000 0000 0000 0000 internal const int TASK_STATE_FAULTED = 0x200000; //bin: 0000 0000 0010 0000 0000 0000 0000 0000 internal const int TASK_STATE_CANCELED = 0x400000; //bin: 0000 0000 0100 0000 0000 0000 0000 0000 internal const int TASK_STATE_WAITING_ON_CHILDREN = 0x800000; //bin: 0000 0000 1000 0000 0000 0000 0000 0000 internal const int TASK_STATE_RAN_TO_COMPLETION = 0x1000000; //bin: 0000 0001 0000 0000 0000 0000 0000 0000 internal const int TASK_STATE_WAITINGFORACTIVATION = 0x2000000; //bin: 0000 0010 0000 0000 0000 0000 0000 0000 internal const int TASK_STATE_COMPLETION_RESERVED = 0x4000000; //bin: 0000 0100 0000 0000 0000 0000 0000 0000 internal const int TASK_STATE_THREAD_WAS_ABORTED = 0x8000000; //bin: 0000 1000 0000 0000 0000 0000 0000 0000 internal const int TASK_STATE_WAIT_COMPLETION_NOTIFICATION = 0x10000000; //bin: 0001 0000 0000 0000 0000 0000 0000 0000 //This could be moved to InternalTaskOptions enum internal const int TASK_STATE_EXECUTIONCONTEXT_IS_NULL = 0x20000000; //bin: 0010 0000 0000 0000 0000 0000 0000 0000 internal const int TASK_STATE_TASKSCHEDULED_WAS_FIRED = 0x40000000; //bin: 0100 0000 0000 0000 0000 0000 0000 0000 // A mask for all of the final states a task may be in private const int TASK_STATE_COMPLETED_MASK = TASK_STATE_CANCELED | TASK_STATE_FAULTED | TASK_STATE_RAN_TO_COMPLETION ; // Values for ContingentProperties.m_internalCancellationRequested. private const int CANCELLATION_REQUESTED = 0x1; // Can be null, a single continuation, a list of continuations, or s_taskCompletionSentinel, // in that order. The logic arround this object assumes it will never regress to a previous state. private volatile object m_continuationObject = null ; // m_continuationObject is set to this when the task completes. private static readonly object s_taskCompletionSentinel = new object (); // A private flag that would be set (only) by the debugger // When true the Async Causality logging trace is enabled as well as a dictionary to relate operation ids with Tasks [ FriendAccessAllowed ] internal static bool s_asyncDebuggingEnabled ; //false by default // This dictonary relates the task id, from an operation id located in the Async Causality log to the actual // task. This is to be used by the debugger ONLY. Task in this dictionary represent current active tasks. private static readonly Dictionary < int , Task > s_currentActiveTasks = new Dictionary < int , Task >(); private static readonly Object s_activeTasksLock = new Object (); // These methods are a way to access the dictionary both from this class and for other classes that also // activate dummy tasks. Specifically the AsyncTaskMethodBuilder and AsyncTaskMethodBuilder<> [ FriendAccessAllowed ] internal static bool AddToActiveTasks ( Task task ) Contract . Requires ( task != null , "Null Task objects can't be added to the ActiveTasks collection" ); lock ( s_activeTasksLock ) s_currentActiveTasks [ task . Id ] = task ; //always return true to keep signature as bool for backwards compatibility return true ; [ FriendAccessAllowed ] internal static void RemoveFromActiveTasks ( int taskId ) lock ( s_activeTasksLock ) s_currentActiveTasks . Remove ( taskId ); // We moved a number of Task properties into this class. The idea is that in most cases, these properties never // need to be accessed during the life cycle of a Task, so we don't want to instantiate them every time. Once // one of these properties needs to be written, we will instantiate a ContingentProperties object and set // the appropriate property. internal class ContingentProperties // Additional context internal ExecutionContext m_capturedContext ; // The execution context to run the task within, if any. // Completion fields (exceptions and event) internal volatile ManualResetEventSlim m_completionEvent ; // Lazily created if waiting is required. internal volatile TaskExceptionHolder m_exceptionsHolder ; // Tracks exceptions, if any have occurred // Cancellation fields (token, registration, and internally requested) internal CancellationToken m_cancellationToken ; // Task's cancellation token, if it has one internal Shared < CancellationTokenRegistration > m_cancellationRegistration ; // Task's registration with the cancellation token internal volatile int m_internalCancellationRequested ; // Its own field because threads legally ---- to set it. // Parenting fields // # of active children + 1 (for this task itself). // Used for ensuring all children are done before this task can complete // The extra count helps prevent the ---- for executing the final state transition // (i.e. whether the last child or this task itself should call FinishStageTwo()) internal volatile int m_completionCountdown = 1; // A list of child tasks that threw an exception (TCEs don't count), // but haven't yet been waited on by the parent, lazily initialized. internal volatile List < Task > m_exceptionalChildren ; /// < summary > /// Sets the internal completion event. /// </ summary > internal void SetCompleted () var mres = m_completionEvent ; if ( mres != null ) mres . Set (); /// < summary > /// Checks if we registered a CT callback during construction, and deregisters it. /// This should be called when we know the registration isn't useful anymore. Specifically from Finish() if the task has completed /// successfully or with an exception. /// </ summary > internal void DeregisterCancellationCallback () if ( m_cancellationRegistration != null ) // Harden against ODEs thrown from disposing of the CTR. // Since the task has already been put into a final state by the time this // is called, all we can do here is suppress the exception. try { m_cancellationRegistration . Value . Dispose (); } catch ( ObjectDisposedException ) { } m_cancellationRegistration = null ; // This field will only be instantiated to some non-null value if any ContingentProperties need to be set. // This will be a ContingentProperties instance or a type derived from it internal volatile ContingentProperties m_contingentProperties ; // Special internal constructor to create an already-completed task. // if canceled==true, create a Canceled task, or else create a RanToCompletion task. // Constructs the task as already completed internal Task ( bool canceled , TaskCreationOptions creationOptions , CancellationToken ct ) int optionFlags = ( int ) creationOptions ; if ( canceled ) m_stateFlags = TASK_STATE_CANCELED | TASK_STATE_CANCELLATIONACKNOWLEDGED | optionFlags ; ContingentProperties props ; m_contingentProperties = props = new ContingentProperties (); // can't have children, so just instantiate directly props . m_cancellationToken = ct ; props . m_internalCancellationRequested = CANCELLATION_REQUESTED ; m_stateFlags = TASK_STATE_RAN_TO_COMPLETION | optionFlags ; // Uncomment if/when we want Task.FromException //// Special internal constructor to create an already-Faulted task. //internal Task(Exception exception) // Contract.Assert(exception != null); // ContingentProperties props; // m_contingentProperties = props = new ContingentProperties(); // can't have children, so just instantiate directly // props.m_exceptionsHolder.Add(exception); // m_stateFlags = TASK_STATE_FAULTED; /// < summary > Constructor for use with promise-style tasks that aren't configurable. </ summary > internal Task () m_stateFlags = TASK_STATE_WAITINGFORACTIVATION | ( int ) InternalTaskOptions . PromiseTask ; // Special constructor for use with promise-style tasks. // Added promiseStyle parameter as an aid to the compiler to distinguish between (state,TCO) and // (action,TCO). It should always be true. internal Task ( object state , TaskCreationOptions creationOptions , bool promiseStyle ) Contract . Assert ( promiseStyle , "Promise CTOR: promiseStyle was false" ); // Check the creationOptions. We allow the AttachedToParent option to be specified for promise tasks. // Also allow RunContinuationsAsynchronously because this is the constructor called by TCS if (( creationOptions & ~( TaskCreationOptions . AttachedToParent | TaskCreationOptions . RunContinuationsAsynchronously )) != 0) throw new ArgumentOutOfRangeException ( "creationOptions" ); // m_parent is readonly, and so must be set in the constructor. // Only set a parent if AttachedToParent is specified. if (( creationOptions & TaskCreationOptions . AttachedToParent ) != 0) m_parent = Task . InternalCurrent ; TaskConstructorCore ( null , state , default ( CancellationToken ), creationOptions , InternalTaskOptions . PromiseTask , null ); /// < summary > /// Initializes a new < see cref = " Task " /> with the specified action. /// </ summary > /// < param name = " action " > The delegate that represents the code to execute in the Task. </ param > /// < exception cref = " T:System.ArgumentNullException " > The < paramref name = " action " /> argument is null. </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ( Action action ) : this ( action , null , null , default ( CancellationToken ), TaskCreationOptions . None , InternalTaskOptions . None , null ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; PossiblyCaptureContext ( ref stackMark ); /// < summary > /// Initializes a new < see cref = " Task " /> with the specified action and < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > . /// </ summary > /// < param name = " action " > The delegate that represents the code to execute in the Task. </ param > /// < param name = " cancellationToken " > The < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// that will be assigned to the new Task. </ param > /// < exception cref = " T:System.ArgumentNullException " > The < paramref name = " action " /> argument is null. </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ( Action action , CancellationToken cancellationToken ) : this ( action , null , null , cancellationToken , TaskCreationOptions . None , InternalTaskOptions . None , null ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; PossiblyCaptureContext ( ref stackMark ); /// < summary > /// Initializes a new < see cref = " Task " /> with the specified action and creation options. /// </ summary > /// < param name = " action " > The delegate that represents the code to execute in the task. </ param > /// < param name = " creationOptions " > /// The < see cref = " System.Threading.Tasks. TaskCreationOptions " > TaskCreationOptions </ see > used to /// customize the Task's behavior. /// </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " action " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " creationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskCreationOptions " /> . /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ( Action action , TaskCreationOptions creationOptions ) : this ( action , null , Task . InternalCurrentIfAttached ( creationOptions ), default ( CancellationToken ), creationOptions , InternalTaskOptions . None , null ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; PossiblyCaptureContext ( ref stackMark ); /// < summary > /// Initializes a new < see cref = " Task " /> with the specified action and creation options. /// </ summary > /// < param name = " action " > The delegate that represents the code to execute in the task. </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new task. </ param > /// < param name = " creationOptions " > /// The < see cref = " System.Threading.Tasks. TaskCreationOptions " > TaskCreationOptions </ see > used to /// customize the Task's behavior. /// </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " action " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " creationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskCreationOptions " /> . /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ( Action action , CancellationToken cancellationToken , TaskCreationOptions creationOptions ) : this ( action , null , Task . InternalCurrentIfAttached ( creationOptions ), cancellationToken , creationOptions , InternalTaskOptions . None , null ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; PossiblyCaptureContext ( ref stackMark ); /// < summary > /// Initializes a new < see cref = " Task " /> with the specified action and state. /// </ summary > /// < param name = " action " > The delegate that represents the code to execute in the task. </ param > /// < param name = " state " > An object representing data to be used by the action. </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " action " /> argument is null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ( Action < object > action , object state ) : this ( action , state , null , default ( CancellationToken ), TaskCreationOptions . None , InternalTaskOptions . None , null ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; PossiblyCaptureContext ( ref stackMark ); /// < summary > /// Initializes a new < see cref = " Task " /> with the specified action, state, snd options. /// </ summary > /// < param name = " action " > The delegate that represents the code to execute in the task. </ param > /// < param name = " state " > An object representing data to be used by the action. </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new task. </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " action " /> argument is null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ( Action < object > action , object state , CancellationToken cancellationToken ) : this ( action , state , null , cancellationToken , TaskCreationOptions . None , InternalTaskOptions . None , null ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; PossiblyCaptureContext ( ref stackMark ); /// < summary > /// Initializes a new < see cref = " Task " /> with the specified action, state, snd options. /// </ summary > /// < param name = " action " > The delegate that represents the code to execute in the task. </ param > /// < param name = " state " > An object representing data to be used by the action. </ param > /// < param name = " creationOptions " > /// The < see cref = " System.Threading.Tasks. TaskCreationOptions " > TaskCreationOptions </ see > used to /// customize the Task's behavior. /// </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " action " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " creationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskCreationOptions " /> . /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ( Action < object > action , object state , TaskCreationOptions creationOptions ) : this ( action , state , Task . InternalCurrentIfAttached ( creationOptions ), default ( CancellationToken ), creationOptions , InternalTaskOptions . None , null ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; PossiblyCaptureContext ( ref stackMark ); /// < summary > /// Initializes a new < see cref = " Task " /> with the specified action, state, snd options. /// </ summary > /// < param name = " action " > The delegate that represents the code to execute in the task. </ param > /// < param name = " state " > An object representing data to be used by the action. </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new task. </ param > /// < param name = " creationOptions " > /// The < see cref = " System.Threading.Tasks. TaskCreationOptions " > TaskCreationOptions </ see > used to /// customize the Task's behavior. /// </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " action " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " creationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskCreationOptions " /> . /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ( Action < object > action , object state , CancellationToken cancellationToken , TaskCreationOptions creationOptions ) : this ( action , state , Task . InternalCurrentIfAttached ( creationOptions ), cancellationToken , creationOptions , InternalTaskOptions . None , null ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; PossiblyCaptureContext ( ref stackMark ); internal Task ( Action < object > action , object state , Task parent , CancellationToken cancellationToken , TaskCreationOptions creationOptions , InternalTaskOptions internalOptions , TaskScheduler scheduler , ref StackCrawlMark stackMark ) : this ( action , state , parent , cancellationToken , creationOptions , internalOptions , scheduler ) PossiblyCaptureContext ( ref stackMark ); /// < summary > /// An internal constructor used by the factory methods on task and its descendent(s). /// This variant does not capture the ExecutionContext; it is up to the caller to do that. /// </ summary > /// < param name = " action " > An action to execute. </ param > /// < param name = " state " > Optional state to pass to the action. </ param > /// < param name = " parent " > Parent of Task. </ param > /// < param name = " cancellationToken " > A CancellationToken for the task. </ param > /// < param name = " scheduler " > A task scheduler under which the task will run. </ param > /// < param name = " creationOptions " > Options to control its execution. </ param > /// < param name = " internalOptions " > Internal options to control its execution </ param > internal Task ( Delegate action , object state , Task parent , CancellationToken cancellationToken , TaskCreationOptions creationOptions , InternalTaskOptions internalOptions , TaskScheduler scheduler ) if ( action == null ) throw new ArgumentNullException ( "action" ); Contract . EndContractBlock (); // This is readonly, and so must be set in the constructor // Keep a link to your parent if: (A) You are attached, or (B) you are self-replicating. if ((( creationOptions & TaskCreationOptions . AttachedToParent ) != 0) || (( internalOptions & InternalTaskOptions . SelfReplicating ) != 0) m_parent = parent ; TaskConstructorCore ( action , state , cancellationToken , creationOptions , internalOptions , scheduler ); /// < summary > /// Common logic used by the following internal ctors: /// Task() /// Task(object action, object state, Task parent, TaskCreationOptions options, TaskScheduler taskScheduler) /// </ summary > /// < param name = " action " > Action for task to execute. </ param > /// < param name = " state " > Object to which to pass to action (may be null) </ param > /// < param name = " scheduler " > Task scheduler on which to run thread (only used by continuation tasks). </ param > /// < param name = " cancellationToken " > A CancellationToken for the Task. </ param > /// < param name = " creationOptions " > Options to customize behavior of Task. </ param > /// < param name = " internalOptions " > Internal options to customize behavior of Task. </ param > internal void TaskConstructorCore ( object action , object state , CancellationToken cancellationToken , TaskCreationOptions creationOptions , InternalTaskOptions internalOptions , TaskScheduler scheduler ) m_action = action ; m_stateObject = state ; m_taskScheduler = scheduler ; // Check for validity of options if (( creationOptions & ~( TaskCreationOptions . AttachedToParent | TaskCreationOptions . LongRunning | TaskCreationOptions . DenyChildAttach | TaskCreationOptions . HideScheduler | TaskCreationOptions . PreferFairness | TaskCreationOptions . RunContinuationsAsynchronously )) != 0) throw new ArgumentOutOfRangeException ( "creationOptions" ); # if DEBUG // Check the validity of internalOptions int illegalInternalOptions = (int) (internalOptions & ~(InternalTaskOptions.SelfReplicating | InternalTaskOptions.ChildReplica | InternalTaskOptions.PromiseTask | InternalTaskOptions.ContinuationTask | InternalTaskOptions.LazyCancellation | InternalTaskOptions.QueuedByRuntime)); Contract.Assert(illegalInternalOptions == 0, "TaskConstructorCore: Illegal internal options"); # endif // Throw exception if the user specifies both LongRunning and SelfReplicating if ((( creationOptions & TaskCreationOptions . LongRunning ) != 0) && (( internalOptions & InternalTaskOptions . SelfReplicating ) != 0)) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_ctor_LRandSR" )); // Assign options to m_stateAndOptionsFlag. Contract . Assert ( m_stateFlags == 0, "TaskConstructorCore: non-zero m_stateFlags" ); Contract . Assert (((( int ) creationOptions ) | OptionsMask ) == OptionsMask , "TaskConstructorCore: options take too many bits" ); var tmpFlags = ( int ) creationOptions | ( int ) internalOptions ; if (( m_action == null ) || (( internalOptions & InternalTaskOptions . ContinuationTask ) != 0)) // For continuation tasks or TaskCompletionSource.Tasks, begin life in the // WaitingForActivation state rather than the Created state. tmpFlags |= TASK_STATE_WAITINGFORACTIVATION ; m_stateFlags = tmpFlags ; // one write to the volatile m_stateFlags instead of two when setting the above options // Now is the time to add the new task to the children list // of the creating task if the options call for it. // We can safely call the creator task's AddNewChild() method to register it, // because at this point we are already on its thread of execution. if ( m_parent != null && (( creationOptions & TaskCreationOptions . AttachedToParent ) != 0) && (( m_parent . CreationOptions & TaskCreationOptions . DenyChildAttach ) == 0) m_parent . AddNewChild (); // if we have a non-null cancellationToken, allocate the contingent properties to save it // we need to do this as the very last thing in the construction path, because the CT registration could modify m_stateFlags if ( cancellationToken . CanBeCanceled ) Contract . Assert (( internalOptions & ( InternalTaskOptions . ChildReplica | InternalTaskOptions . SelfReplicating | InternalTaskOptions . ContinuationTask )) == 0, "TaskConstructorCore: Did not expect to see cancelable token for replica/replicating or continuation task." ); AssignCancellationToken ( cancellationToken , null , null ); /// < summary > /// Handles everything needed for associating a CancellationToken with a task which is being constructed. /// This method is meant to be be called either from the TaskConstructorCore or from ContinueWithCore. /// </ summary > private void AssignCancellationToken ( CancellationToken cancellationToken , Task antecedent , TaskContinuation continuation ) // There is no need to worry about concurrency issues here because we are in the constructor path of the task -- // there should not be any ----s to set m_contingentProperties at this point. ContingentProperties props = EnsureContingentPropertiesInitialized ( needsProtection : false ); props . m_cancellationToken = cancellationToken ; if ( AppContextSwitches . ThrowExceptionIfDisposedCancellationTokenSource ) cancellationToken . ThrowIfSourceDisposed (); // If an unstarted task has a valid CancellationToken that gets signalled while the task is still not queued // we need to proactively cancel it, because it may never execute to transition itself. // The only way to accomplish this is to register a callback on the CT. // We exclude Promise tasks from this, because TaskCompletionSource needs to fully control the inner tasks's lifetime (i.e. not allow external cancellations) if (((( InternalTaskOptions ) Options & ( InternalTaskOptions . QueuedByRuntime | InternalTaskOptions . PromiseTask | InternalTaskOptions . LazyCancellation )) == 0)) if ( cancellationToken . IsCancellationRequested ) // Fast path for an already-canceled cancellationToken this . InternalCancel ( false ); // Regular path for an uncanceled cancellationToken CancellationTokenRegistration ctr ; if ( antecedent == null ) // if no antecedent was specified, use this task's reference as the cancellation state object ctr = cancellationToken . InternalRegisterWithoutEC ( s_taskCancelCallback , this ); // If an antecedent was specified, pack this task, its antecedent and the TaskContinuation together as a tuple // and use it as the cancellation state object. This will be unpacked in the cancellation callback so that // antecedent.RemoveCancellation(continuation) can be invoked. ctr = cancellationToken . InternalRegisterWithoutEC ( s_taskCancelCallback , new Tuple < Task , Task , TaskContinuation >( this , antecedent , continuation )); props . m_cancellationRegistration = new Shared < CancellationTokenRegistration >( ctr ); catch // If we have an exception related to our CancellationToken, then we need to subtract ourselves // from our parent before throwing it. if (( m_parent != null ) && (( Options & TaskCreationOptions . AttachedToParent ) != 0) && (( m_parent . Options & TaskCreationOptions . DenyChildAttach ) == 0)) m_parent . DisregardChild (); throw ; // Static delegate to be used as a cancellation callback on unstarted tasks that have a valid cancellation token. // This is necessary to transition them into canceled state if their cancellation token is signalled while they are still not queued private readonly static Action < Object > s_taskCancelCallback = new Action < Object >( TaskCancelCallback ); private static void TaskCancelCallback ( Object o ) var targetTask = o as Task ; if ( targetTask == null ) var tuple = o as Tuple < Task , Task , TaskContinuation >; if ( tuple != null ) targetTask = tuple . Item1 ; Task antecedentTask = tuple . Item2 ; TaskContinuation continuation = tuple . Item3 ; antecedentTask . RemoveContinuation ( continuation ); Contract . Assert ( targetTask != null , "targetTask should have been non-null, with the supplied argument being a task or a tuple containing one" ); targetTask . InternalCancel ( false ); // Debugger support private string DebuggerDisplayMethodDescription Delegate d = ( Delegate ) m_action ; return d != null ? d . Method . ToString () : "{null}" ; /// < summary > /// Captures the ExecutionContext so long as flow isn't suppressed. /// </ summary > /// < param name = " stackMark " > A stack crawl mark pointing to the frame of the caller. </ param > [ SecuritySafeCritical ] internal void PossiblyCaptureContext ( ref StackCrawlMark stackMark ) Contract . Assert ( m_contingentProperties == null || m_contingentProperties . m_capturedContext == null , "Captured an ExecutionContext when one was already captured." ); // In the legacy .NET 3.5 build, we don't have the optimized overload of Capture() // available, so we call the parameterless overload. # if PFX_LEGACY_3_5 CapturedContext = ExecutionContext.Capture(); # else CapturedContext = ExecutionContext . Capture ( ref stackMark , ExecutionContext . CaptureOptions . IgnoreSyncCtx | ExecutionContext . CaptureOptions . OptimizeDefaultCase ); # endif // Internal property to process TaskCreationOptions access and mutation. internal TaskCreationOptions Options int stateFlags = m_stateFlags ; // "cast away" volatility to enable inlining of OptionsMethod return OptionsMethod ( stateFlags ); // Similar to Options property, but allows for the use of a cached flags value rather than // a read of the volatile m_stateFlags field. internal static TaskCreationOptions OptionsMethod ( int flags ) Contract . Assert (( OptionsMask & 1) == 1, "OptionsMask needs a shift in Options.get" ); return ( TaskCreationOptions )( flags & OptionsMask ); // Atomically OR-in newBits to m_stateFlags, while making sure that // no illegalBits are set. Returns true on success, false on failure. internal bool AtomicStateUpdate ( int newBits , int illegalBits ) // This could be implemented in terms of: // internal bool AtomicStateUpdate(int newBits, int illegalBits, ref int oldFlags); // but for high-throughput perf, that delegation's cost is noticeable. SpinWait sw = new SpinWait (); int oldFlags = m_stateFlags ; if (( oldFlags & illegalBits ) != 0) return false ; if ( Interlocked . CompareExchange ( ref m_stateFlags , oldFlags | newBits , oldFlags ) == oldFlags ) return true ; sw . SpinOnce (); } while ( true ); internal bool AtomicStateUpdate ( int newBits , int illegalBits , ref int oldFlags ) SpinWait sw = new SpinWait (); oldFlags = m_stateFlags ; if (( oldFlags & illegalBits ) != 0) return false ; if ( Interlocked . CompareExchange ( ref m_stateFlags , oldFlags | newBits , oldFlags ) == oldFlags ) return true ; sw . SpinOnce (); } while ( true ); /// < summary > /// Sets or clears the TASK_STATE_WAIT_COMPLETION_NOTIFICATION state bit. /// The debugger sets this bit to aid it in "stepping out" of an async method body. /// If enabled is true, this must only be called on a task that has not yet been completed. /// If enabled is false, this may be called on completed tasks. /// Either way, it should only be used for promise-style tasks. /// </ summary > /// < param name = " enabled " > true to set the bit; false to unset the bit. </ param > internal void SetNotificationForWaitCompletion ( bool enabled ) Contract . Assert (( Options & ( TaskCreationOptions ) InternalTaskOptions . PromiseTask ) != 0, "Should only be used for promise-style tasks" ); // hasn't been vetted on other kinds as there hasn't been a need if ( enabled ) // Atomically set the END_AWAIT_NOTIFICATION bit bool success = AtomicStateUpdate ( TASK_STATE_WAIT_COMPLETION_NOTIFICATION , TASK_STATE_COMPLETED_MASK | TASK_STATE_COMPLETION_RESERVED ); Contract . Assert ( success , "Tried to set enabled on completed Task" ); // Atomically clear the END_AWAIT_NOTIFICATION bit SpinWait sw = new SpinWait (); while ( true ) int oldFlags = m_stateFlags ; int newFlags = oldFlags & (~ TASK_STATE_WAIT_COMPLETION_NOTIFICATION ); if ( Interlocked . CompareExchange ( ref m_stateFlags , newFlags , oldFlags ) == oldFlags ) break ; sw . SpinOnce (); /// < summary > /// Calls the debugger notification method if the right bit is set and if /// the task itself allows for the notification to proceed. /// </ summary > /// < returns > true if the debugger was notified; otherwise, false. </ returns > internal bool NotifyDebuggerOfWaitCompletionIfNecessary () // Notify the debugger if of any of the tasks we've waited on requires notification if ( IsWaitNotificationEnabled && ShouldNotifyDebuggerOfWaitCompletion ) NotifyDebuggerOfWaitCompletion (); return true ; return false ; /// < summary > Returns true if any of the supplied tasks require wait notification. </ summary > /// < param name = " tasks " > The tasks to check. </ param > /// < returns > true if any of the tasks require notification; otherwise, false. </ returns > internal static bool AnyTaskRequiresNotifyDebuggerOfWaitCompletion ( Task [] tasks ) Contract . Assert ( tasks != null , "Expected non-null array of tasks" ); foreach ( var task in tasks ) if ( task != null && task . IsWaitNotificationEnabled && task . ShouldNotifyDebuggerOfWaitCompletion ) // potential recursion return true ; return false ; /// < summary > Gets whether either the end await bit is set or (not xor) the task has not completed successfully. </ summary > /// < returns > (DebuggerBitSet || !RanToCompletion) </ returns > internal bool IsWaitNotificationEnabledOrNotRanToCompletion [ MethodImpl ( MethodImplOptions . AggressiveInlining )] return ( m_stateFlags & ( Task . TASK_STATE_WAIT_COMPLETION_NOTIFICATION | Task . TASK_STATE_RAN_TO_COMPLETION )) != Task . TASK_STATE_RAN_TO_COMPLETION ; /// < summary > /// Determines whether we should inform the debugger that we're ending a join with a task. /// This should only be called if the debugger notification bit is set, as it is has some cost, /// namely it is a virtual call (however calling it if the bit is not set is not functionally /// harmful). Derived implementations may choose to only conditionally call down to this base /// implementation. /// </ summary > internal virtual bool ShouldNotifyDebuggerOfWaitCompletion // ideally would be familyAndAssembly, but that can't be done in C# // It's theoretically possible but extremely rare that this assert could fire because the // bit was unset between the time that it was checked and this method was called. // It's so remote a chance that it's worth having the assert to protect against misuse. bool isWaitNotificationEnabled = IsWaitNotificationEnabled ; Contract . Assert ( isWaitNotificationEnabled , "Should only be called if the wait completion bit is set." ); return isWaitNotificationEnabled ; /// < summary > Gets whether the task's debugger notification for wait completion bit is set. </ summary > /// < returns > true if the bit is set; false if it's not set. </ returns > internal bool IsWaitNotificationEnabled // internal only to enable unit tests; would otherwise be private get { return ( m_stateFlags & TASK_STATE_WAIT_COMPLETION_NOTIFICATION ) != 0; } /// < summary > Placeholder method used as a breakpoint target by the debugger. Must not be inlined or optimized. </ summary > /// < remarks > All joins with a task should end up calling this if their debugger notification bit is set. </ remarks > [ MethodImpl ( MethodImplOptions . NoOptimization | MethodImplOptions . NoInlining )] private void NotifyDebuggerOfWaitCompletion () // It's theoretically possible but extremely rare that this assert could fire because the // bit was unset between the time that it was checked and this method was called. // It's so remote a chance that it's worth having the assert to protect against misuse. Contract . Assert ( IsWaitNotificationEnabled , "Should only be called if the wait completion bit is set." ); // Now that we're notifying the debugger, clear the bit. The debugger should do this anyway, // but this adds a bit of protection in case it fails to, and given that the debugger is involved, // the overhead here for the interlocked is negligable. We do still rely on the debugger // to clear bits, as this doesn't recursively clear bits in the case of, for example, WhenAny. SetNotificationForWaitCompletion ( enabled : false ); // Atomically mark a Task as started while making sure that it is not canceled. internal bool MarkStarted () return AtomicStateUpdate ( TASK_STATE_STARTED , TASK_STATE_CANCELED | TASK_STATE_STARTED ); [ MethodImpl ( MethodImplOptions . AggressiveInlining )] internal bool FireTaskScheduledIfNeeded ( TaskScheduler ts ) var etwLog = TplEtwProvider . Log ; if ( etwLog . IsEnabled () && ( m_stateFlags & Task . TASK_STATE_TASKSCHEDULED_WAS_FIRED ) == 0) m_stateFlags |= Task . TASK_STATE_TASKSCHEDULED_WAS_FIRED ; Task currentTask = Task . InternalCurrent ; Task parentTask = this . m_parent ; etwLog . TaskScheduled ( ts . Id , currentTask == null ? 0 : currentTask . Id , this . Id , parentTask == null ? 0 : parentTask . Id , ( int ) this . Options , System.Threading. Thread . GetDomainID ()); return true ; return false; /// < summary > /// Internal function that will be called by a new child task to add itself to /// the children list of the parent (this). /// Since a child task can only be created from the thread executing the action delegate /// of this task, reentrancy is neither required nor supported. This should not be called from /// anywhere other than the task construction/initialization codepaths. /// </ summary > internal void AddNewChild () Contract . Assert ( Task . InternalCurrent == this || this . IsSelfReplicatingRoot , "Task.AddNewChild(): Called from an external context" ); var props = EnsureContingentPropertiesInitialized ( needsProtection : true ); if ( props . m_completionCountdown == 1 && ! IsSelfReplicatingRoot ) // A count of 1 indicates so far there was only the parent, and this is the first child task // Single kid => no fuss about who else is accessing the count. Let's save ourselves 100 cycles // We exclude self replicating root tasks from this optimization, because further child creation can take place on // other cores and with bad enough timing this write may not be visible to them. props . m_completionCountdown ++; // otherwise do it safely Interlocked . Increment ( ref props . m_completionCountdown ); // This is called in the case where a new child is added, but then encounters a CancellationToken-related exception. // We need to subtract that child from m_completionCountdown, or the parent will never complete. internal void DisregardChild () Contract . Assert ( Task . InternalCurrent == this , "Task.DisregardChild(): Called from an external context" ); var props = EnsureContingentPropertiesInitialized ( needsProtection : true ); Contract . Assert ( props . m_completionCountdown >= 2, "Task.DisregardChild(): Expected parent count to be >= 2" ); Interlocked . Decrement ( ref props . m_completionCountdown ); /// < summary > /// Starts the < see cref = " Task " /> , scheduling it for execution to the current < see /// cref = " System.Threading.Tasks. TaskScheduler " > TaskScheduler </ see > . /// </ summary > /// < remarks > /// A task may only be started and run only once. Any attempts to schedule a task a second time /// will result in an exception. /// </ remarks > /// < exception cref = " InvalidOperationException " > /// The < see cref = " Task " /> is not in a valid state to be started. It may have already been started, /// executed, or canceled, or it may have been created in a manner that doesn't support direct /// scheduling. /// </ exception > public void Start () Start ( TaskScheduler . Current ); /// < summary > /// Starts the < see cref = " Task " /> , scheduling it for execution to the specified < see /// cref = " System.Threading.Tasks. TaskScheduler " > TaskScheduler </ see > . /// </ summary > /// < remarks > /// A task may only be started and run only once. Any attempts to schedule a task a second time will /// result in an exception. /// </ remarks > /// < param name = " scheduler " > /// The < see cref = " System.Threading.Tasks. TaskScheduler " > TaskScheduler </ see > with which to associate /// and execute this task. /// </ param > /// < exception cref = " ArgumentNullException " > /// The < paramref name = " scheduler " /> argument is null. /// </ exception > /// < exception cref = " InvalidOperationException " > /// The < see cref = " Task " /> is not in a valid state to be started. It may have already been started, /// executed, or canceled, or it may have been created in a manner that doesn't support direct /// scheduling. /// </ exception > public void Start ( TaskScheduler scheduler ) // Read the volatile m_stateFlags field once and cache it for subsequent operations int flags = m_stateFlags ; // Need to check this before (m_action == null) because completed tasks will // set m_action to null. We would want to know if this is the reason that m_action == null. if ( IsCompletedMethod ( flags )) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_Start_TaskCompleted" )); if ( scheduler == null ) throw new ArgumentNullException ( "scheduler" ); var options = OptionsMethod ( flags ); if (( options & ( TaskCreationOptions ) InternalTaskOptions . PromiseTask ) != 0) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_Start_Promise" )); if (( options & ( TaskCreationOptions ) InternalTaskOptions . ContinuationTask ) != 0) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_Start_ContinuationTask" )); // Make sure that Task only gets started once. Or else throw an exception. if ( Interlocked . CompareExchange ( ref m_taskScheduler , scheduler , null ) != null ) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_Start_AlreadyStarted" )); ScheduleAndStart ( true ); /// < summary > /// Runs the < see cref = " Task " /> synchronously on the current < see /// cref = " System.Threading.Tasks. TaskScheduler " > TaskScheduler </ see > . /// </ summary > /// < remarks > /// < para > /// A task may only be started and run only once. Any attempts to schedule a task a second time will /// result in an exception. /// </ para > /// < para > /// Tasks executed with < see cref = " RunSynchronously () " /> will be associated with the current < see /// cref = " System.Threading.Tasks. TaskScheduler " > TaskScheduler </ see > . /// </ para > /// < para > /// If the target scheduler does not support running this Task on the current thread, the Task will /// be scheduled for execution on the scheduler, and the current thread will block until the /// Task has completed execution. /// </ para > /// </ remarks > /// < exception cref = " InvalidOperationException " > /// The < see cref = " Task " /> is not in a valid state to be started. It may have already been started, /// executed, or canceled, or it may have been created in a manner that doesn't support direct /// scheduling. /// </ exception > public void RunSynchronously () InternalRunSynchronously ( TaskScheduler . Current , waitForCompletion : true ); /// < summary > /// Runs the < see cref = " Task " /> synchronously on the < see /// cref = " System.Threading.Tasks. TaskScheduler " > scheduler </ see > provided. /// </ summary > /// < remarks > /// < para > /// A task may only be started and run only once. Any attempts to schedule a task a second time will /// result in an exception. /// </ para > /// < para > /// If the target scheduler does not support running this Task on the current thread, the Task will /// be scheduled for execution on the scheduler, and the current thread will block until the /// Task has completed execution. /// </ para > /// </ remarks > /// < exception cref = " InvalidOperationException " > /// The < see cref = " Task " /> is not in a valid state to be started. It may have already been started, /// executed, or canceled, or it may have been created in a manner that doesn't support direct /// scheduling. /// </ exception > /// < exception cref = " ArgumentNullException " > The < paramref name = " scheduler " /> parameter /// is null. </ exception > /// < param name = " scheduler " > The scheduler on which to attempt to run this task inline. </ param > public void RunSynchronously ( TaskScheduler scheduler ) if ( scheduler == null ) throw new ArgumentNullException ( "scheduler" ); Contract . EndContractBlock (); InternalRunSynchronously ( scheduler , waitForCompletion : true ); // Internal version of RunSynchronously that allows not waiting for completion. [ SecuritySafeCritical ] // Needed for QueueTask internal void InternalRunSynchronously ( TaskScheduler scheduler , bool waitForCompletion ) Contract . Requires ( scheduler != null , "Task.InternalRunSynchronously(): null TaskScheduler" ); // Read the volatile m_stateFlags field once and cache it for subsequent operations int flags = m_stateFlags ; // Can't call this method on a continuation task var options = OptionsMethod ( flags ); if (( options & ( TaskCreationOptions ) InternalTaskOptions . ContinuationTask ) != 0) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_RunSynchronously_Continuation" )); // Can't call this method on a promise-style task if (( options & ( TaskCreationOptions ) InternalTaskOptions . PromiseTask ) != 0) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_RunSynchronously_Promise" )); // Can't call this method on a task that has already completed if ( IsCompletedMethod ( flags )) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_RunSynchronously_TaskCompleted" )); // Make sure that Task only gets started once. Or else throw an exception. if ( Interlocked . CompareExchange ( ref m_taskScheduler , scheduler , null ) != null ) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_RunSynchronously_AlreadyStarted" )); // execute only if we win the ---- against concurrent cancel attempts. // otherwise throw an exception, because we've been canceled. if ( MarkStarted ()) bool taskQueued = false ; // We wrap TryRunInline() in a try/catch block and move an excepted task to Faulted here, // but not in Wait()/WaitAll()/FastWaitAll(). Here, we know for sure that the // task will not be subsequently scheduled (assuming that the scheduler adheres // to the guideline that an exception implies that no state change took place), // so it is safe to catch the exception and move the task to a final state. The // same cannot be said for Wait()/WaitAll()/FastWaitAll(). if (! scheduler . TryRunInline ( this , false )) scheduler . InternalQueueTask ( this ); taskQueued = true ; // only mark this after successfully queuing the task. // A successful TryRunInline doesn't guarantee completion, as there may be unfinished children. // Also if we queued the task above, the task may not be done yet. if ( waitForCompletion && ! IsCompleted ) SpinThenBlockingWait ( Timeout . Infinite , default ( CancellationToken )); catch ( Exception e ) // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute if (! taskQueued && !( e is ThreadAbortException )) // We had a problem with TryRunInline() or QueueTask(). // Record the exception, marking ourselves as Completed/Faulted. TaskSchedulerException tse = new TaskSchedulerException ( e ); AddException ( tse ); Finish ( false ); // Mark ourselves as "handled" to avoid crashing the finalizer thread if the caller neglects to // call Wait() on this task. // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException() Contract . Assert ( ( m_contingentProperties != null ) && ( m_contingentProperties . m_exceptionsHolder != null ) && ( m_contingentProperties . m_exceptionsHolder . ContainsFaultList ), "Task.InternalRunSynchronously(): Expected m_contingentProperties.m_exceptionsHolder to exist " + "and to have faults recorded." ); m_contingentProperties . m_exceptionsHolder . MarkAsHandled ( false ); // And re-throw. throw tse ; // We had a problem with waiting or this is a thread abort. Just re-throw. else throw ; Contract . Assert (( m_stateFlags & TASK_STATE_CANCELED ) != 0, "Task.RunSynchronously: expected TASK_STATE_CANCELED to be set" ); // Can't call this method on canceled task. throw new InvalidOperationException ( Environment . GetResourceString ( "Task_RunSynchronously_TaskCompleted" )); //// Helper methods for Factory StartNew methods. // Implicitly converts action to object and handles the meat of the StartNew() logic. internal static Task InternalStartNew ( Task creatingTask , Delegate action , object state , CancellationToken cancellationToken , TaskScheduler scheduler , TaskCreationOptions options , InternalTaskOptions internalOptions , ref StackCrawlMark stackMark ) // Validate arguments. if ( scheduler == null ) throw new ArgumentNullException ( "scheduler" ); Contract . EndContractBlock (); // Create and schedule the task. This throws an InvalidOperationException if already shut down. // Here we add the InternalTaskOptions.QueuedByRuntime to the internalOptions, so that TaskConstructorCore can skip the cancellation token registration Task t = new Task ( action , state , creatingTask , cancellationToken , options , internalOptions | InternalTaskOptions . QueuedByRuntime , scheduler ); t . PossiblyCaptureContext ( ref stackMark ); t . ScheduleAndStart ( false ); return t ; /// < summary > /// Gets a unique ID for a < see cref = " Task " > Task </ see > or task continuation instance. /// </ summary > internal static int NewId () int newId = 0; // We need to repeat if Interlocked.Increment wraps around and returns 0. // Otherwise next time this task's Id is queried it will get a new value newId = Interlocked . Increment ( ref s_taskIdCounter ); while ( newId == 0); TplEtwProvider . Log . NewID ( newId ); return newId ; ///////////// // properties /// < summary > /// Gets a unique ID for this < see cref = " Task " > Task </ see > instance. /// </ summary > /// < remarks > /// Task IDs are assigned on-demand and do not necessarily represent the order in the which Task /// instances were created. /// </ remarks > public int Id if ( m_taskId == 0) int newId = NewId (); Interlocked . CompareExchange ( ref m_taskId , newId , 0); return m_taskId ; /// < summary > /// Returns the unique ID of the currently executing < see cref = " Task " > Task </ see > . /// </ summary > public static int ? CurrentId Task currentTask = InternalCurrent ; if ( currentTask != null ) return currentTask . Id ; return null; /// < summary > /// Gets the < see cref = " Task " > Task </ see > instance currently executing, or /// null if none exists. /// </ summary > internal static Task InternalCurrent get { return t_currentTask ; } /// < summary > /// Gets the Task instance currently executing if the specified creation options /// contain AttachedToParent. /// </ summary > /// < param name = " options " > The options to check. </ param > /// < returns > The current task if there is one and if AttachToParent is in the options; otherwise, null. </ returns > internal static Task InternalCurrentIfAttached ( TaskCreationOptions creationOptions ) return ( creationOptions & TaskCreationOptions . AttachedToParent ) != 0 ? InternalCurrent : null ; /// < summary > /// Gets the StackGuard object assigned to the current thread. /// </ summary > internal static StackGuard CurrentStackGuard StackGuard sg = t_stackGuard ; if ( sg == null ) t_stackGuard = sg = new StackGuard (); return sg ; /// < summary > /// Gets the < see cref = " T:System.AggregateException " > Exception </ see > that caused the < see /// cref = " Task " > Task </ see > to end prematurely. If the < see /// cref = " Task " > Task </ see > completed successfully or has not yet thrown any /// exceptions, this will return null. /// </ summary > /// < remarks > /// Tasks that throw unhandled exceptions store the resulting exception and propagate it wrapped in a /// < see cref = " System. AggregateException " /> in calls to < see cref = " Wait () " > Wait </ see > /// or in accesses to the < see cref = " Exception " /> property. Any exceptions not observed by the time /// the Task instance is garbage collected will be propagated on the finalizer thread. /// </ remarks > public AggregateException Exception AggregateException e = null ; // If you're faulted, retrieve the exception(s) if ( IsFaulted ) e = GetExceptions ( false ); // Only return an exception in faulted state (skip manufactured exceptions) // A "benevolent" race condition makes it possible to return null when IsFaulted is // true (i.e., if IsFaulted is set just after the check to IsFaulted above). Contract . Assert (( e == null ) || IsFaulted , "Task.Exception_get(): returning non-null value when not Faulted" ); return e ; /// < summary > /// Gets the < see cref = " T:System.Threading.Tasks.TaskStatus " > TaskStatus </ see > of this Task. /// </ summary > public TaskStatus Status TaskStatus rval ; // get a cached copy of the state flags. This should help us // to get a consistent view of the flags if they are changing during the // execution of this method. int sf = m_stateFlags ; if (( sf & TASK_STATE_FAULTED ) != 0) rval = TaskStatus . Faulted ; else if (( sf & TASK_STATE_CANCELED ) != 0) rval = TaskStatus . Canceled ; else if (( sf & TASK_STATE_RAN_TO_COMPLETION ) != 0) rval = TaskStatus . RanToCompletion ; else if (( sf & TASK_STATE_WAITING_ON_CHILDREN ) != 0) rval = TaskStatus . WaitingForChildrenToComplete ; else if (( sf & TASK_STATE_DELEGATE_INVOKED ) != 0) rval = TaskStatus . Running ; else if (( sf & TASK_STATE_STARTED ) != 0) rval = TaskStatus . WaitingToRun ; else if (( sf & TASK_STATE_WAITINGFORACTIVATION ) != 0) rval = TaskStatus . WaitingForActivation ; else rval = TaskStatus . Created ; return rval ; /// < summary > /// Gets whether this < see cref = " Task " > Task </ see > instance has completed /// execution due to being canceled. /// </ summary > /// < remarks > /// A < see cref = " Task " > Task </ see > will complete in Canceled state either if its < see cref = " CancellationToken " > CancellationToken </ see > /// was marked for cancellation before the task started executing, or if the task acknowledged the cancellation request on /// its already signaled CancellationToken by throwing an /// < see cref = " System. OperationCanceledException " > OperationCanceledException </ see > that bears the same /// < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > . /// </ remarks > public bool IsCanceled // Return true if canceled bit is set and faulted bit is not set return ( m_stateFlags & ( TASK_STATE_CANCELED | TASK_STATE_FAULTED )) == TASK_STATE_CANCELED ; /// < summary > /// Returns true if this task has a cancellation token and it was signaled. /// To be used internally in execute entry codepaths. /// </ summary > internal bool IsCancellationRequested // check both the internal cancellation request flag and the CancellationToken attached to this task var props = m_contingentProperties ; return props != null && ( props . m_internalCancellationRequested == CANCELLATION_REQUESTED || props . m_cancellationToken . IsCancellationRequested ); /// < summary > /// Ensures that the contingent properties field has been initialized. /// ASSUMES THAT m_stateFlags IS ALREADY SET! /// </ summary > /// < param name = " needsProtection " > true if this needs to be done in a thread-safe manner; otherwise, false. </ param > /// < returns > The initialized contingent properties object. </ returns > internal ContingentProperties EnsureContingentPropertiesInitialized ( bool needsProtection ) var props = m_contingentProperties ; return props != null ? props : EnsureContingentPropertiesInitializedCore ( needsProtection ); /// < summary > /// Initializes the contingent properties object. This assumes a check has already been done for nullness. /// </ summary > /// < param name = " needsProtection " > true if this needs to be done in a thread-safe manner; otherwise, false. </ param > /// < returns > The initialized contingent properties object. </ returns > private ContingentProperties EnsureContingentPropertiesInitializedCore ( bool needsProtection ) if ( needsProtection ) return LazyInitializer . EnsureInitialized < ContingentProperties >( ref m_contingentProperties , s_createContingentProperties ); Contract . Assert ( m_contingentProperties == null , "Expected props to be null after checking and with needsProtection == false" ); return m_contingentProperties = new ContingentProperties (); // Cached functions for lazily initializing contingent properties private static readonly Func < ContingentProperties > s_createContingentProperties = () => new ContingentProperties (); /// < summary > /// This internal property provides access to the CancellationToken that was set on the task /// when it was constructed. /// </ summary > internal CancellationToken CancellationToken var props = m_contingentProperties ; return ( props == null ) ? default ( CancellationToken ) : props . m_cancellationToken ; /// < summary > /// Gets whether this < see cref = " Task " /> threw an OperationCanceledException while its CancellationToken was signaled. /// </ summary > internal bool IsCancellationAcknowledged get { return ( m_stateFlags & TASK_STATE_CANCELLATIONACKNOWLEDGED ) != 0; } /// < summary > /// Gets whether this < see cref = " Task " > Task </ see > has completed. /// </ summary > /// < remarks > /// < see cref = " IsCompleted " /> will return true when the Task is in one of the three /// final states: < see cref = " System.Threading.Tasks. TaskStatus . RanToCompletion " > RanToCompletion </ see > , /// < see cref = " System.Threading.Tasks. TaskStatus . Faulted " > Faulted </ see > , or /// < see cref = " System.Threading.Tasks. TaskStatus . Canceled " > Canceled </ see > . /// </ remarks > public bool IsCompleted int stateFlags = m_stateFlags ; // enable inlining of IsCompletedMethod by "cast"ing away the volatility return IsCompletedMethod ( stateFlags ); // Similar to IsCompleted property, but allows for the use of a cached flags value // rather than reading the volatile m_stateFlags field. private static bool IsCompletedMethod ( int flags ) return ( flags & TASK_STATE_COMPLETED_MASK ) != 0; // For use in InternalWait -- marginally faster than (Task.Status == TaskStatus.RanToCompletion) internal bool IsRanToCompletion get { return ( m_stateFlags & TASK_STATE_COMPLETED_MASK ) == TASK_STATE_RAN_TO_COMPLETION ; } /// < summary > /// Gets the < see cref = " T:System.Threading.Tasks.TaskCreationOptions " > TaskCreationOptions </ see > used /// to create this task. /// </ summary > public TaskCreationOptions CreationOptions get { return Options & ( TaskCreationOptions )(~ InternalTaskOptions . InternalOptionsMask ); } /// < summary > /// Gets a < see cref = " T:System.Threading.WaitHandle " /> that can be used to wait for the task to /// complete. /// </ summary > /// < remarks > /// Using the wait functionality provided by < see cref = " Wait () " /> /// should be preferred over using < see cref = " IAsyncResult . AsyncWaitHandle " /> for similar /// functionality. /// </ remarks > /// < exception cref = " T:System.ObjectDisposedException " > /// The < see cref = " Task " /> has been disposed. /// </ exception > WaitHandle IAsyncResult . AsyncWaitHandle // Although a slim event is used internally to avoid kernel resource allocation, this function // forces allocation of a true WaitHandle when called. bool isDisposed = ( m_stateFlags & TASK_STATE_DISPOSED ) != 0; if ( isDisposed ) throw new ObjectDisposedException ( null , Environment . GetResourceString ( "Task_ThrowIfDisposed" )); return CompletedEvent . WaitHandle ; /// < summary > /// Gets the state object supplied when the < see cref = " Task " > Task </ see > was created, /// or null if none was supplied. /// </ summary > public object AsyncState get { return m_stateObject ; } /// < summary > /// Gets an indication of whether the asynchronous operation completed synchronously. /// </ summary > /// < value > true if the asynchronous operation completed synchronously; otherwise, false. </ value > bool IAsyncResult . CompletedSynchronously return false ; /// < summary > /// Provides access to the TaskScheduler responsible for executing this Task. /// </ summary > internal TaskScheduler ExecutingTaskScheduler get { return m_taskScheduler ; } /// < summary > /// Provides access to factory methods for creating < see cref = " Task " /> and < see cref = " Task { TResult } " /> instances. /// </ summary > /// < remarks > /// The factory returned from < see cref = " Factory " /> is a default instance /// of < see cref = " System.Threading.Tasks. TaskFactory " /> , as would result from using /// the default constructor on TaskFactory. /// </ remarks > public static TaskFactory Factory { get { return s_factory ; } } /// < summary > A task that's already been completed successfully. </ summary > private static Task s_completedTask ; /// < summary > Gets a task that's already been completed successfully. </ summary > /// < remarks > May not always return the same instance. </ remarks > public static Task CompletedTask var completedTask = s_completedTask ; if ( completedTask == null ) s_completedTask = completedTask = new Task ( false , ( TaskCreationOptions ) InternalTaskOptions . DoNotDispose , default ( CancellationToken )); // benign initialization ---- return completedTask ; /// < summary > /// Provides an event that can be used to wait for completion. /// Only called by IAsyncResult.AsyncWaitHandle, which means that we really do need to instantiate a completion event. /// </ summary > internal ManualResetEventSlim CompletedEvent var contingentProps = EnsureContingentPropertiesInitialized ( needsProtection : true ); if ( contingentProps . m_completionEvent == null ) bool wasCompleted = IsCompleted ; ManualResetEventSlim newMre = new ManualResetEventSlim ( wasCompleted ); if ( Interlocked . CompareExchange ( ref contingentProps . m_completionEvent , newMre , null ) != null ) // We lost the ----, so we will just close the event right away. newMre . Dispose (); else if (! wasCompleted && IsCompleted ) // We published the event as unset, but the task has subsequently completed. // Set the event's state properly so that callers don't deadlock. newMre . Set (); return contingentProps . m_completionEvent ; /// < summary > /// Determines whether this is the root task of a self replicating group. /// </ summary > internal bool IsSelfReplicatingRoot // Return true if self-replicating bit is set and child replica bit is not set return ( Options & ( TaskCreationOptions )( InternalTaskOptions . SelfReplicating | InternalTaskOptions . ChildReplica )) == ( TaskCreationOptions ) InternalTaskOptions . SelfReplicating ; /// < summary > /// Determines whether the task is a replica itself. /// </ summary > internal bool IsChildReplica get { return ( Options & ( TaskCreationOptions ) InternalTaskOptions . ChildReplica ) != 0; } internal int ActiveChildCount var props = m_contingentProperties ; return props != null ? props . m_completionCountdown - 1 : 0; /// < summary > /// The property formerly known as IsFaulted. /// </ summary > internal bool ExceptionRecorded var props = m_contingentProperties ; return ( props != null ) && ( props . m_exceptionsHolder != null ) && ( props . m_exceptionsHolder . ContainsFaultList ); /// < summary > /// Gets whether the < see cref = " Task " /> completed due to an unhandled exception. /// </ summary > /// < remarks > /// If < see cref = " IsFaulted " /> is true, the Task's < see cref = " Status " /> will be equal to /// < see cref = " System.Threading.Tasks. TaskStatus . Faulted " > TaskStatus.Faulted </ see > , and its /// < see cref = " Exception " /> property will be non-null. /// </ remarks > public bool IsFaulted // Faulted is "king" -- if that bit is present (regardless of other bits), we are faulted. return (( m_stateFlags & TASK_STATE_FAULTED ) != 0); /// < summary > /// The captured execution context for the current task to run inside /// If the TASK_STATE_EXECUTIONCONTEXT_IS_NULL flag is set, this means ExecutionContext.Capture returned null, otherwise /// If the captured context is the default, nothing is saved, otherwise the m_contingentProperties inflates to save the context /// </ summary > internal ExecutionContext CapturedContext if (( m_stateFlags & TASK_STATE_EXECUTIONCONTEXT_IS_NULL ) == TASK_STATE_EXECUTIONCONTEXT_IS_NULL ) return null ; var props = m_contingentProperties ; if ( props != null && props . m_capturedContext != null ) return props . m_capturedContext ; else return ExecutionContext . PreAllocatedDefault ; // 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 if ( value == null ) m_stateFlags |= TASK_STATE_EXECUTIONCONTEXT_IS_NULL ; else if (! value . IsPreAllocatedDefault ) // not the default context, then inflate the contingent properties and set it EnsureContingentPropertiesInitialized ( needsProtection : false ). m_capturedContext = value ; //else do nothing, this is the default context /// < summary > /// Static helper function to copy specific ExecutionContext /// </ summary > /// < param name = " capturedContext " > The captured context </ param > /// < returns > The copied context, null if the capturedContext is null </ returns > private static ExecutionContext CopyExecutionContext ( ExecutionContext capturedContext ) if ( capturedContext == null ) return null ; if ( capturedContext . IsPreAllocatedDefault ) return ExecutionContext . PreAllocatedDefault ; return capturedContext . CreateCopy (); # if DEBUG /// <summary> /// Retrieves an identifier for the task. /// </summary> internal int InternalId get { return GetHashCode(); } # endif ///////////// // methods /// < summary > /// Disposes the < see cref = " Task " /> , releasing all of its unmanaged resources. /// </ summary > /// < remarks > /// Unlike most of the members of < see cref = " Task " /> , this method is not thread-safe. /// Also, < see cref = " Dispose () " /> may only be called on a < see cref = " Task " /> that is in one of /// the final states: < see cref = " System.Threading.Tasks. TaskStatus . RanToCompletion " > RanToCompletion </ see > , /// < see cref = " System.Threading.Tasks. TaskStatus . Faulted " > Faulted </ see > , or /// < see cref = " System.Threading.Tasks. TaskStatus . Canceled " > Canceled </ see > . /// </ remarks > /// < exception cref = " T:System.InvalidOperationException " > /// The exception that is thrown if the < see cref = " Task " /> is not in /// one of the final states: < see cref = " System.Threading.Tasks. TaskStatus . RanToCompletion " > RanToCompletion </ see > , /// < see cref = " System.Threading.Tasks. TaskStatus . Faulted " > Faulted </ see > , or /// < see cref = " System.Threading.Tasks. TaskStatus . Canceled " > Canceled </ see > . /// </ exception > public void Dispose () Dispose ( true ); GC . SuppressFinalize ( this ); /// < summary > /// Disposes the < see cref = " Task " /> , releasing all of its unmanaged resources. /// </ summary > /// < param name = " disposing " > /// A Boolean value that indicates whether this method is being called due to a call to < see /// cref = " Dispose () " /> . /// </ param > /// < remarks > /// Unlike most of the members of < see cref = " Task " /> , this method is not thread-safe. /// </ remarks > protected virtual void Dispose ( bool disposing ) if ( disposing ) // Dispose is a nop if this task was created with the DoNotDispose internal option. // This is done before the completed check, because if we're not touching any // state on the task, it's ok for it to happen before completion. if (( Options & ( TaskCreationOptions ) InternalTaskOptions . DoNotDispose ) != 0) return ; // Task must be completed to dispose if (! IsCompleted ) throw new InvalidOperationException ( Environment . GetResourceString ( "Task_Dispose_NotCompleted" )); // Dispose of the underlying completion event if it exists var cp = m_contingentProperties ; if ( cp != null ) // Make a copy to protect against racing Disposes. // If we wanted to make this a bit safer, we could use an interlocked here, // but we state that Dispose is not thread safe. var ev = cp . m_completionEvent ; if ( ev != null ) // Null out the completion event in contingent props; we'll use our copy from here on out cp . m_completionEvent = null ; // In the unlikely event that our completion event is inflated but not yet signaled, // go ahead and signal the event. If you dispose of an unsignaled MRES, then any waiters // will deadlock; an ensuing Set() will not wake them up. In the event of an AppDomainUnload, // there is no guarantee that anyone else is going to signal the event, and it does no harm to // call Set() twice on m_completionEvent. if (! ev . IsSet ) ev . Set (); // Finally, dispose of the event ev . Dispose (); // We OR the flags to indicate the object has been disposed. The task // has already completed at this point, and the only conceivable ---- would // be with the unsetting of the TASK_STATE_WAIT_COMPLETION_NOTIFICATION flag, which // ---- is extremely unlikely and also benign. (Worst case: we hit a breakpoint // twice instead of once in the debugger. Weird, but not lethal.) m_stateFlags |= TASK_STATE_DISPOSED ; ///////////// // internal helpers /// < summary > /// Schedules the task for execution. /// </ summary > /// < param name = " needsProtection " > If true, TASK_STATE_STARTED bit is turned on in /// an atomic fashion, making sure that TASK_STATE_CANCELED does not get set /// underneath us. If false, TASK_STATE_STARTED bit is OR-ed right in. This /// allows us to streamline things a bit for StartNew(), where competing cancellations /// are not a problem. </ param > [ SecuritySafeCritical ] // Needed for QueueTask internal void ScheduleAndStart ( bool needsProtection ) Contract . Assert ( m_taskScheduler != null , "expected a task scheduler to have been selected" ); Contract . Assert (( m_stateFlags & TASK_STATE_STARTED ) == 0, "task has already started" ); // Set the TASK_STATE_STARTED bit if ( needsProtection ) if (! MarkStarted ()) // A cancel has snuck in before we could get started. Quietly exit. return ; m_stateFlags |= TASK_STATE_STARTED ; if ( s_asyncDebuggingEnabled ) AddToActiveTasks ( this ); if ( AsyncCausalityTracer . LoggingOn && ( Options & ( TaskCreationOptions ) InternalTaskOptions . ContinuationTask ) == 0) //For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor AsyncCausalityTracer . TraceOperationCreation ( CausalityTraceLevel . Required , this . Id , "Task: " +(( Delegate ) m_action ). Method . Name , 0); // Queue to the indicated scheduler. m_taskScheduler . InternalQueueTask ( this ); catch ( ThreadAbortException tae ) AddException ( tae ); FinishThreadAbortedTask ( true , false ); catch ( Exception e ) // The scheduler had a problem queueing this task. Record the exception, leaving this task in // a Faulted state. TaskSchedulerException tse = new TaskSchedulerException ( e ); AddException ( tse ); Finish ( false ); // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew() // or from the self replicating logic, because in both cases the exception is either propagated outside directly, or added // to an enclosing parent. However we won't do this for continuation tasks, because in that case we internally eat the exception // and therefore we need to make sure the user does later observe it explicitly or see it on the finalizer. if (( Options & ( TaskCreationOptions ) InternalTaskOptions . ContinuationTask ) == 0) // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException() Contract . Assert ( ( m_contingentProperties != null ) && ( m_contingentProperties . m_exceptionsHolder != null ) && ( m_contingentProperties . m_exceptionsHolder . ContainsFaultList ), "Task.ScheduleAndStart(): Expected m_contingentProperties.m_exceptionsHolder to exist " + "and to have faults recorded." ); m_contingentProperties . m_exceptionsHolder . MarkAsHandled ( false ); // re-throw the exception wrapped as a TaskSchedulerException. throw tse ; /// < summary > /// Adds an exception to the list of exceptions this task has thrown. /// </ summary > /// < param name = " exceptionObject " > An object representing either an Exception or a collection of Exceptions. </ param > internal void AddException ( object exceptionObject ) Contract . Requires ( exceptionObject != null , "Task.AddException: Expected a non-null exception object" ); AddException ( exceptionObject , representsCancellation : false ); /// < summary > /// Adds an exception to the list of exceptions this task has thrown. /// </ summary > /// < param name = " exceptionObject " > An object representing either an Exception or a collection of Exceptions. </ param > /// < param name = " representsCancellation " > Whether the exceptionObject is an OperationCanceledException representing cancellation. </ param > internal void AddException ( object exceptionObject , bool representsCancellation ) Contract . Requires ( exceptionObject != null , "Task.AddException: Expected a non-null exception object" ); # if DEBUG var eoAsException = exceptionObject as Exception; var eoAsEnumerableException = exceptionObject as IEnumerable<Exception>; var eoAsEdi = exceptionObject as ExceptionDispatchInfo; var eoAsEnumerableEdi = exceptionObject as IEnumerable<ExceptionDispatchInfo>; Contract.Assert( eoAsException != null || eoAsEnumerableException != null || eoAsEdi != null || eoAsEnumerableEdi != null, "Task.AddException: Expected an Exception, ExceptionDispatchInfo, or an IEnumerable<> of one of those"); var eoAsOce = exceptionObject as OperationCanceledException; Contract.Assert( !representsCancellation || eoAsOce != null || (eoAsEdi != null && eoAsEdi.SourceException is OperationCanceledException), "representsCancellation should be true only if an OCE was provided."); # endif // WARNING: A great deal of care went into ensuring that // AddException() and GetExceptions() are never called // simultaneously. See comment at start of GetExceptions(). // Lazily initialize the holder, ensuring only one thread wins. var props = EnsureContingentPropertiesInitialized ( needsProtection : true ); if ( props . m_exceptionsHolder == null ) TaskExceptionHolder holder = new TaskExceptionHolder ( this ); if ( Interlocked . CompareExchange ( ref props . m_exceptionsHolder , holder , null ) != null ) // If we lost the ----, suppress finalization. holder . MarkAsHandled ( false ); lock ( props ) props . m_exceptionsHolder . Add ( exceptionObject , representsCancellation ); /// < summary > /// Returns a list of exceptions by aggregating the holder's contents. Or null if /// no exceptions have been thrown. /// </ summary > /// < param name = " includeTaskCanceledExceptions " > Whether to include a TCE if cancelled. </ param > /// < returns > An aggregate exception, or null if no exceptions have been caught. </ returns > private AggregateException GetExceptions ( bool includeTaskCanceledExceptions ) // WARNING: The Task/Task<TResult>/TaskCompletionSource classes // have all been carefully crafted to insure that GetExceptions() // is never called while AddException() is being called. There // are locks taken on m_contingentProperties in several places: // -- Task<TResult>.TrySetException(): The lock allows the // task to be set to Faulted state, and all exceptions to // be recorded, in one atomic action. // -- Task.Exception_get(): The lock ensures that Task<TResult>.TrySetException() // is allowed to complete its operation before Task.Exception_get() // can access GetExceptions(). // -- Task.ThrowIfExceptional(): The lock insures that Wait() will // not attempt to call GetExceptions() while Task<TResult>.TrySetException() // is in the process of calling AddException(). // For "regular" tasks, we effectively keep AddException() and GetException() // from being called concurrently by the way that the state flows. Until // a Task is marked Faulted, Task.Exception_get() returns null. And // a Task is not marked Faulted until it and all of its children have // completed, which means that all exceptions have been recorded. // It might be a lot easier to follow all of this if we just required // that all calls to GetExceptions() and AddExceptions() were made // under a lock on m_contingentProperties. But that would also // increase our lock occupancy time and the frequency with which we // would need to take the lock. // If you add a call to GetExceptions() anywhere in the code, // please continue to maintain the invariant that it can't be // called when AddException() is being called. // We'll lazily create a TCE if the task has been canceled. Exception canceledException = null ; if ( includeTaskCanceledExceptions && IsCanceled ) // Backcompat: // Ideally we'd just use the cached OCE from this.GetCancellationExceptionDispatchInfo() // here. However, that would result in a potentially breaking change from .NET 4, which // has the code here that throws a new exception instead of the original, and the EDI // may not contain a TCE, but an OCE or any OCE-derived type, which would mean we'd be // propagating an exception of a different type. canceledException = new TaskCanceledException ( this ); if ( ExceptionRecorded ) // There are exceptions; get the aggregate and optionally add the canceled // exception to the aggregate (if applicable). Contract . Assert ( m_contingentProperties != null ); // ExceptionRecorded ==> m_contingentProperties != null // No need to lock around this, as other logic prevents the consumption of exceptions // before they have been completely processed. return m_contingentProperties . m_exceptionsHolder . CreateExceptionObject ( false , canceledException ); else if ( canceledException != null ) // No exceptions, but there was a cancelation. Aggregate and return it. return new AggregateException ( canceledException ); return null ; /// < summary > Gets the exception dispatch infos once the task has faulted. </ summary > internal ReadOnlyCollection < ExceptionDispatchInfo > GetExceptionDispatchInfos () bool exceptionsAvailable = IsFaulted && ExceptionRecorded ; Contract . Assert ( exceptionsAvailable , "Must only be used when the task has faulted with exceptions." ); return exceptionsAvailable ? m_contingentProperties . m_exceptionsHolder . GetExceptionDispatchInfos () : new ReadOnlyCollection < ExceptionDispatchInfo >( new ExceptionDispatchInfo [0]); /// < summary > Gets the ExceptionDispatchInfo containing the OperationCanceledException for this task. </ summary > /// < returns > The ExceptionDispatchInfo. May be null if no OCE was stored for the task. </ returns > internal ExceptionDispatchInfo GetCancellationExceptionDispatchInfo () Contract . Assert ( IsCanceled , "Must only be used when the task has canceled." ); var props = m_contingentProperties ; if ( props == null ) return null ; var holder = props . m_exceptionsHolder ; if ( holder == null ) return null ; return holder . GetCancellationExceptionDispatchInfo (); // may be null /// < summary > /// Throws an aggregate exception if the task contains exceptions. /// </ summary > internal void ThrowIfExceptional ( bool includeTaskCanceledExceptions ) Contract . Requires ( IsCompleted , "ThrowIfExceptional(): Expected IsCompleted == true" ); Exception exception = GetExceptions ( includeTaskCanceledExceptions ); if ( exception != null ) UpdateExceptionObservedStatus (); throw exception ; /// < summary > /// Checks whether this is an attached task, and whether we are being called by the parent task. /// And sets the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag based on that. /// This is meant to be used internally when throwing an exception, and when WaitAll is gathering /// exceptions for tasks it waited on. If this flag gets set, the implicit wait on children /// will skip exceptions to prevent duplication. /// This should only be called when this task has completed with an exception /// </ summary > internal void UpdateExceptionObservedStatus () if (( m_parent != null ) && (( Options & TaskCreationOptions . AttachedToParent ) != 0) && (( m_parent . CreationOptions & TaskCreationOptions . DenyChildAttach ) == 0) && Task . InternalCurrent == m_parent ) m_stateFlags |= TASK_STATE_EXCEPTIONOBSERVEDBYPARENT ; /// < summary > /// Checks whether the TASK_STATE_EXCEPTIONOBSERVEDBYPARENT status flag is set, /// This will only be used by the implicit wait to prevent double throws /// </ summary > internal bool IsExceptionObservedByParent return ( m_stateFlags & TASK_STATE_EXCEPTIONOBSERVEDBYPARENT ) != 0; /// < summary > /// Checks whether the body was ever invoked. Used by task scheduler code to verify custom schedulers actually ran the task. /// </ summary > internal bool IsDelegateInvoked return ( m_stateFlags & TASK_STATE_DELEGATE_INVOKED ) != 0; /// < summary > /// Signals completion of this particular task. /// The bUserDelegateExecuted parameter indicates whether this Finish() call comes following the /// full execution of the user delegate. /// If bUserDelegateExecuted is false, it mean user delegate wasn't invoked at all (either due to /// a cancellation request, or because this task is a promise style Task). In this case, the steps /// involving child tasks (i.e. WaitForChildren) will be skipped. /// </ summary > internal void Finish ( bool bUserDelegateExecuted ) if (! bUserDelegateExecuted ) // delegate didn't execute => no children. We can safely call the remaining finish stages FinishStageTwo (); var props = m_contingentProperties ; if ( props == null || // no contingent properties means no children, so it's safe to complete ourselves ( props . m_completionCountdown == 1 && ! IsSelfReplicatingRoot ) || // Count of 1 => either all children finished, or there were none. Safe to complete ourselves // without paying the price of an Interlocked.Decrement. // However we need to exclude self replicating root tasks from this optimization, because // they can have children joining in, or finishing even after the root task delegate is done. Interlocked . Decrement ( ref props . m_completionCountdown ) == 0) // Reaching this sub clause means there may be remaining active children, // and we could be racing with one of them to call FinishStageTwo(). // So whoever does the final Interlocked.Dec is responsible to finish. FinishStageTwo (); // Apparently some children still remain. It will be up to the last one to process the completion of this task on their own thread. // We will now yield the thread back to ThreadPool. Mark our state appropriately before getting out. // We have to use an atomic update for this and make sure not to overwrite a final state, // because at this very moment the last child's thread may be concurrently completing us. // Otherwise we risk overwriting the TASK_STATE_RAN_TO_COMPLETION, _CANCELED or _FAULTED bit which may have been set by that child task. // Note that the concurrent update by the last child happening in FinishStageTwo could still wipe out the TASK_STATE_WAITING_ON_CHILDREN flag, // but it is not critical to maintain, therefore we dont' need to intruduce a full atomic update into FinishStageTwo AtomicStateUpdate ( TASK_STATE_WAITING_ON_CHILDREN , TASK_STATE_FAULTED | TASK_STATE_CANCELED | TASK_STATE_RAN_TO_COMPLETION ); // 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. // we use a local variable for exceptional children here because some other thread may be nulling out m_contingentProperties.m_exceptionalChildren List < Task > exceptionalChildren = props != null ? props . m_exceptionalChildren : null ; if ( exceptionalChildren != null ) lock ( exceptionalChildren ) exceptionalChildren . RemoveAll ( s_IsExceptionObservedByParentPredicate ); // RemoveAll has better performance than doing it ourselves // statically allocated delegate for the removeall expression in Finish() private readonly static Predicate < Task > s_IsExceptionObservedByParentPredicate = new Predicate < Task >(( t ) => { return t . IsExceptionObservedByParent ; }); /// < summary > /// FinishStageTwo is to be executed as soon as we known there are no more children to complete. /// 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) /// ii) or on the thread that executed the last child. /// </ summary > internal void FinishStageTwo () AddExceptionsFromChildren (); // At this point, the task is done executing and waiting for its children, // we can transition our task to a completion state. int completionState ; if ( ExceptionRecorded ) completionState = TASK_STATE_FAULTED ; if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCompletion ( CausalityTraceLevel . Required , this . Id , AsyncCausalityStatus . Error ); if ( Task . s_asyncDebuggingEnabled ) RemoveFromActiveTasks ( this . Id ); else if ( IsCancellationRequested && IsCancellationAcknowledged ) // We transition into the TASK_STATE_CANCELED final state if the task's CT was signalled for cancellation, // and the user delegate acknowledged the cancellation request by throwing an OCE, // and the task hasn't otherwise transitioned into faulted state. (TASK_STATE_FAULTED trumps TASK_STATE_CANCELED) // If the task threw an OCE without cancellation being requestsed (while the CT not being in signaled state), // then we regard it as a regular exception completionState = TASK_STATE_CANCELED ; if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCompletion ( CausalityTraceLevel . Required , this . Id , AsyncCausalityStatus . Canceled ); if ( Task . s_asyncDebuggingEnabled ) RemoveFromActiveTasks ( this . Id ); completionState = TASK_STATE_RAN_TO_COMPLETION ; if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCompletion ( CausalityTraceLevel . Required , this . Id , AsyncCausalityStatus . Completed ); if ( Task . s_asyncDebuggingEnabled ) RemoveFromActiveTasks ( this . Id ); // Use Interlocked.Exchange() to effect a memory fence, preventing // any SetCompleted() (or later) instructions from sneak back before it. Interlocked . Exchange ( ref m_stateFlags , m_stateFlags | completionState ); // Set the completion event if it's been lazy allocated. // And if we made a cancellation registration, it's now unnecessary. var cp = m_contingentProperties ; if ( cp != null ) cp . SetCompleted (); cp . DeregisterCancellationCallback (); // ready to run continuations and notify parent. FinishStageThree (); /// < summary > /// Final stage of the task completion code path. Notifies the parent (if any) that another of its childre are done, and runs continuations. /// This function is only separated out from FinishStageTwo because these two operations are also needed to be called from CancellationCleanupLogic() /// </ summary > internal void FinishStageThree () // Release the action so that holding this task object alive doesn't also // hold alive the body of the task. We do this before notifying a parent, // so that if notifying the parent completes the parent and causes // its synchronous continuations to run, the GC can collect the state // in the interim. And we do it before finishing continuations, because // continuations hold onto the task, and therefore are keeping it alive. m_action = null ; // Notify parent if this was an attached task if ( m_parent != null && (( m_parent . CreationOptions & TaskCreationOptions . DenyChildAttach ) == 0) && ((( TaskCreationOptions )( m_stateFlags & OptionsMask )) & TaskCreationOptions . AttachedToParent ) != 0) m_parent . ProcessChildCompletion ( this ); // Activate continuations (if any). FinishContinuations (); /// < summary > /// This is called by children of this task when they are completed. /// </ summary > internal void ProcessChildCompletion ( Task childTask ) Contract . Requires ( childTask != null ); Contract . Requires ( childTask . IsCompleted , "ProcessChildCompletion was called for an uncompleted task" ); Contract . Assert ( childTask . m_parent == this , "ProcessChildCompletion should only be called for a child of this task" ); var props = m_contingentProperties ; // if the child threw and we haven't observed it we need to save it for future reference if ( childTask . IsFaulted && ! childTask . IsExceptionObservedByParent ) // Lazily initialize the child exception list if ( props . m_exceptionalChildren == null ) Interlocked . CompareExchange ( ref props . m_exceptionalChildren , new List < Task >(), null ); // In rare situations involving AppDomainUnload, it's possible (though unlikely) for FinishStageTwo() to be called // multiple times for the same task. In that case, AddExceptionsFromChildren() could be nulling m_exceptionalChildren // out at the same time that we're processing it, resulting in a NullReferenceException here. We'll protect // ourselves by caching m_exceptionChildren in a local variable. List < Task > tmp = props . m_exceptionalChildren ; if ( tmp != null ) lock ( tmp ) tmp . Add ( childTask ); if ( Interlocked . Decrement ( ref props . m_completionCountdown ) == 0) // This call came from the final child to complete, and apparently we have previously given up this task's right to complete itself. // So we need to invoke the final finish stage. FinishStageTwo (); /// < summary > /// This is to be called just before the task does its final state transition. /// It traverses the list of exceptional children, and appends their aggregate exceptions into this one's exception list /// </ summary > internal void AddExceptionsFromChildren () // In rare occurences during AppDomainUnload() processing, it is possible for this method to be called // simultaneously on the same task from two different contexts. This can result in m_exceptionalChildren // being nulled out while it is being processed, which could lead to a NullReferenceException. To // protect ourselves, we'll cache m_exceptionalChildren in a local variable. var props = m_contingentProperties ; List < Task > tmp = ( props != null ) ? props . m_exceptionalChildren : null ; if ( tmp != null ) // This lock is necessary because even though AddExceptionsFromChildren is last to execute, it may still // be racing with the code segment at the bottom of Finish() that prunes the exceptional child array. lock ( tmp ) foreach ( Task task in tmp ) // Ensure any exceptions thrown by children are added to the parent. // In doing this, we are implicitly marking children as being "handled". Contract . Assert ( task . IsCompleted , "Expected all tasks in list to be completed" ); if ( task . IsFaulted && ! task . IsExceptionObservedByParent ) TaskExceptionHolder exceptionHolder = task . m_contingentProperties . m_exceptionsHolder ; Contract . Assert ( exceptionHolder != null ); // No locking necessary since child task is finished adding exceptions // and concurrent CreateExceptionObject() calls do not constitute // a concurrency hazard. AddException ( exceptionHolder . CreateExceptionObject ( false , null )); // Reduce memory pressure by getting rid of the array props . m_exceptionalChildren = null ; /// < summary > /// Special purpose Finish() entry point to be used when the task delegate throws a ThreadAbortedException /// This makes a note in the state flags so that we avoid any costly synchronous operations in the finish codepath /// such as inlined continuations /// </ summary > /// < param name = " bTAEAddedToExceptionHolder " > /// Indicates whether the ThreadAbortException was added to this task's exception holder. /// This should always be true except for the case of non-root self replicating task copies. /// </ param > /// < param name = " delegateRan " > Whether the delegate was executed. </ param > internal void FinishThreadAbortedTask ( bool bTAEAddedToExceptionHolder , bool delegateRan ) Contract . Assert (! bTAEAddedToExceptionHolder || ( m_contingentProperties != null && m_contingentProperties . m_exceptionsHolder != null ), "FinishThreadAbortedTask() called on a task whose exception holder wasn't initialized" ); // this will only be false for non-root self replicating task copies, because all of their exceptions go to the root task. if ( bTAEAddedToExceptionHolder ) m_contingentProperties . m_exceptionsHolder . MarkAsHandled ( false ); // If this method has already been called for this task, or if this task has already completed, then // return before actually calling Finish(). if (! AtomicStateUpdate ( TASK_STATE_THREAD_WAS_ABORTED , TASK_STATE_THREAD_WAS_ABORTED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED )) return ; Finish ( delegateRan ); /// < summary > /// Executes the task. This method will only be called once, and handles bookeeping associated with /// self-replicating tasks, in addition to performing necessary exception marshaling. /// </ summary > private void Execute () if ( IsSelfReplicatingRoot ) ExecuteSelfReplicating ( this ); InnerInvoke (); catch ( ThreadAbortException tae ) // Don't record the TAE or call FinishThreadAbortedTask for a child replica task -- // it's already been done downstream. if (! IsChildReplica ) // Record this exception in the task's exception list HandleException ( tae ); // This is a ThreadAbortException and it will be rethrown from this catch clause, causing us to // skip the regular Finish codepath. In order not to leave the task unfinished, we now call // FinishThreadAbortedTask here. FinishThreadAbortedTask ( true , true ); catch ( Exception exn ) // Record this exception in the task's exception list HandleException ( exn ); // Allows (internal) deriving classes to support limited replication. // (By default, replication is basically unlimited). internal virtual bool ShouldReplicate () return true ; // Allows (internal) deriving classes to instantiate the task replica as a Task super class of their choice // (By default, we create a regular Task instance) internal virtual Task CreateReplicaTask ( Action < object > taskReplicaDelegate , Object stateObject , Task parentTask , TaskScheduler taskScheduler , TaskCreationOptions creationOptionsForReplica , InternalTaskOptions internalOptionsForReplica ) return new Task ( taskReplicaDelegate , stateObject , parentTask , default ( CancellationToken ), creationOptionsForReplica , internalOptionsForReplica , parentTask . ExecutingTaskScheduler ); // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica internal virtual Object SavedStateForNextReplica get { return null ; } set { /*do nothing*/ } // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica internal virtual Object SavedStateFromPreviousReplica get { return null ; } set { /*do nothing*/ } // Allows internal deriving classes to support replicas that exit prematurely and want to hand over the child replica that they // had queued, so that the replacement replica can work with that child task instead of queuing up yet another one internal virtual Task HandedOverChildReplica get { return null ; } set { /* do nothing*/ } private static void ExecuteSelfReplicating ( Task root ) TaskCreationOptions creationOptionsForReplicas = root . CreationOptions | TaskCreationOptions . AttachedToParent ; InternalTaskOptions internalOptionsForReplicas = InternalTaskOptions . ChildReplica | // child replica flag disables self replication for the replicas themselves. InternalTaskOptions . SelfReplicating | // we still want to identify this as part of a self replicating group InternalTaskOptions . QueuedByRuntime ; // we queue and cancel these tasks internally, so don't allow CT registration to take place // Important Note: The child replicas we launch from here will be attached the root replica (by virtue of the root.CreateReplicaTask call) // because we need the root task to receive all their exceptions, and to block until all of them return // This variable is captured in a closure and shared among all replicas. bool replicasAreQuitting = false ; // Set up a delegate that will form the body of the root and all recursively created replicas. Action < object > taskReplicaDelegate = null ; taskReplicaDelegate = delegate Task currentTask = Task . InternalCurrent ; // Check if a child task has been handed over by a prematurely quiting replica that we might be a replacement for. Task childTask = currentTask . HandedOverChildReplica ; if ( childTask == null ) // Apparently we are not a replacement task. This means we need to queue up a child task for replication to progress // Down-counts a counter in the root task. if (! root . ShouldReplicate ()) return ; // If any of the replicas have quit, we will do so ourselves. if ( Volatile . Read ( ref replicasAreQuitting )) return ; // Propagate a copy of the context from the root task. It may be null if flow was suppressed. ExecutionContext creatorContext = root . CapturedContext ; childTask = root . CreateReplicaTask ( taskReplicaDelegate , root . m_stateObject , root , root . ExecutingTaskScheduler , creationOptionsForReplicas , internalOptionsForReplicas ); childTask . CapturedContext = CopyExecutionContext ( creatorContext ); childTask . ScheduleAndStart ( false ); // Finally invoke the meat of the task. // Note that we are directly calling root.InnerInvoke() even though we are currently be in the action delegate of a child replica // This is because the actual work was passed down in that delegate, and the action delegate of the child replica simply contains this // replication control logic. // passing in currentTask only so that the parallel debugger can find it root . InnerInvokeWithArg ( currentTask ); catch ( Exception exn ) // Record this exception in the root task's exception list root . HandleException ( exn ); if ( exn is ThreadAbortException ) // If this is a ThreadAbortException it will escape this catch clause, causing us to skip the regular Finish codepath // In order not to leave the task unfinished, we now call FinishThreadAbortedTask here currentTask . FinishThreadAbortedTask ( false , true ); Object savedState = currentTask . SavedStateForNextReplica ; // check for premature exit if ( savedState != null ) // the replica decided to exit early // we need to queue up a replacement, attach the saved state, and yield the thread right away Task replacementReplica = root . CreateReplicaTask ( taskReplicaDelegate , root . m_stateObject , root , root . ExecutingTaskScheduler , creationOptionsForReplicas , internalOptionsForReplicas ); // Propagate a copy of the context from the root task to the replacement task ExecutionContext creatorContext = root . CapturedContext ; replacementReplica . CapturedContext = CopyExecutionContext ( creatorContext ); replacementReplica . HandedOverChildReplica = childTask ; replacementReplica . SavedStateFromPreviousReplica = savedState ; replacementReplica . ScheduleAndStart ( false ); // The replica finished normally, which means it can't find more work to grab. // Time to mark replicas quitting replicasAreQuitting = true ; // InternalCancel() could conceivably throw in the underlying scheduler's TryDequeue() method. // If it does, then make sure that we record it. childTask . InternalCancel ( true ); catch ( Exception e ) // Apparently TryDequeue threw an exception. Before propagating that exception, InternalCancel should have // attempted an atomic state transition and a call to CancellationCleanupLogic() on this task. So we know // the task was properly cleaned up if it was possible. // Now all we need to do is to Record the exception in the root task. root . HandleException ( e ); // No specific action needed if the child could not be canceled // because we attached it to the root task, which should therefore be receiving any exceptions from the child, // and root.wait will not return before this child finishes anyway. // Now we execute as the root task taskReplicaDelegate ( null ); /// < summary > /// IThreadPoolWorkItem override, which is the entry function for this task when the TP scheduler decides to run it. /// </ summary > [ SecurityCritical ] void IThreadPoolWorkItem . ExecuteWorkItem () ExecuteEntry ( false ); /// < summary > /// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem. This may occur /// before Task would otherwise be able to observe it. /// </ summary > [ SecurityCritical ] void IThreadPoolWorkItem . MarkAborted ( ThreadAbortException tae ) // If the task has marked itself as Completed, then it either a) already observed this exception (so we shouldn't handle it here) // or b) completed before the exception ocurred (in which case it shouldn't count against this Task). if (! IsCompleted ) HandleException ( tae ); FinishThreadAbortedTask ( true , false ); /// < summary > /// Outermost entry function to execute this task. Handles all aspects of executing a task on the caller thread. /// Currently this is called by IThreadPoolWorkItem.ExecuteWorkItem(), and TaskManager.TryExecuteInline. /// </ summary > /// < param name = " bPreventDoubleExecution " > Performs atomic updates to prevent double execution. Should only be set to true /// in codepaths servicing user provided TaskSchedulers. The ConcRT or ThreadPool schedulers don't need this. </ param > [ SecuritySafeCritical ] internal bool ExecuteEntry ( bool bPreventDoubleExecution ) if ( bPreventDoubleExecution || (( Options & ( TaskCreationOptions ) InternalTaskOptions . SelfReplicating ) != 0)) int previousState = 0; // Do atomic state transition from queued to invoked. If we observe a task that's already invoked, // we will return false so that TaskScheduler.ExecuteTask can throw an exception back to the custom scheduler. // However we don't want this exception to be throw if the task was already canceled, because it's a // legitimate scenario for custom schedulers to dequeue a task and mark it as canceled (example: throttling scheduler) if (! AtomicStateUpdate ( TASK_STATE_DELEGATE_INVOKED , TASK_STATE_DELEGATE_INVOKED | TASK_STATE_COMPLETED_MASK , ref previousState ) && ( previousState & TASK_STATE_CANCELED ) == 0) // This task has already been invoked. Don't invoke it again. return false ; // Remember that we started running the task delegate. m_stateFlags |= TASK_STATE_DELEGATE_INVOKED ; if (! IsCancellationRequested && ! IsCanceled ) ExecuteWithThreadLocal ( ref t_currentTask ); else if (! IsCanceled ) int prevState = Interlocked . Exchange ( ref m_stateFlags , m_stateFlags | TASK_STATE_CANCELED ); if (( prevState & TASK_STATE_CANCELED ) == 0) CancellationCleanupLogic (); return true ; // A trick so we can refer to the TLS slot with a byref. [ SecurityCritical ] private void ExecuteWithThreadLocal ( ref Task currentTaskSlot ) // Remember the current task so we can restore it after running, and then Task previousTask = currentTaskSlot ; // ETW event for Task Started var etwLog = TplEtwProvider . Log ; Guid savedActivityID = new Guid (); bool etwIsEnabled = etwLog . IsEnabled (); if ( etwIsEnabled ) if ( etwLog . TasksSetActivityIds ) EventSource . SetCurrentThreadActivityId ( TplEtwProvider . CreateGuidForTaskID ( this . Id ), out savedActivityID ); // previousTask holds the actual "current task" we want to report in the event if ( previousTask != null ) etwLog . TaskStarted ( previousTask . m_taskScheduler . Id , previousTask . Id , this . Id ); etwLog . TaskStarted ( TaskScheduler . Current . Id , 0, this . Id ); if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceSynchronousWorkStart ( CausalityTraceLevel . Required , this . Id , CausalitySynchronousWork . Execution ); // place the current task into TLS. currentTaskSlot = this ; ExecutionContext ec = CapturedContext ; if ( ec == null ) // No context, just run the task directly. Execute (); if ( IsSelfReplicatingRoot || IsChildReplica ) CapturedContext = CopyExecutionContext ( ec ); // Run the task. We need a simple shim that converts the // object back into a Task object, so that we can Execute it. // Lazily initialize the callback delegate; benign ---- var callback = s_ecCallback ; if ( callback == null ) s_ecCallback = callback = new ContextCallback ( ExecutionContextCallback ); # if PFX_LEGACY_3_5 ExecutionContext.Run(ec, callback, this); # else ExecutionContext . Run ( ec , callback , this , true ); # endif if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceSynchronousWorkCompletion ( CausalityTraceLevel . Required , CausalitySynchronousWork . Execution ); Finish ( true ); finally currentTaskSlot = previousTask ; // ETW event for Task Completed if ( etwIsEnabled ) // previousTask holds the actual "current task" we want to report in the event if ( previousTask != null ) etwLog . TaskCompleted ( previousTask . m_taskScheduler . Id , previousTask . Id , this . Id , IsFaulted ); etwLog . TaskCompleted ( TaskScheduler . Current . Id , 0, this . Id , IsFaulted ); if ( etwLog . TasksSetActivityIds ) EventSource . SetCurrentThreadActivityId ( savedActivityID ); // Cached callback delegate that's lazily initialized due to ContextCallback being SecurityCritical [ SecurityCritical ] private static ContextCallback s_ecCallback ; [ SecurityCritical ] private static void ExecutionContextCallback ( object obj ) Task task = obj as Task ; Contract . Assert ( task != null , "expected a task object" ); task . Execute (); /// < summary > /// The actual code which invokes the body of the task. This can be overriden in derived types. /// </ summary > internal virtual void InnerInvoke () // Invoke the delegate Contract . Assert ( m_action != null , "Null action in InnerInvoke()" ); var action = m_action as Action ; if ( action != null ) action (); return ; var actionWithState = m_action as Action < object >; if ( actionWithState != null ) actionWithState ( m_stateObject ); return ; Contract . Assert ( false , "Invalid m_action in Task" ); /// < summary > /// Alternate InnerInvoke prototype to be called from ExecuteSelfReplicating() so that /// the Parallel Debugger can discover the actual task being invoked. /// Details: Here, InnerInvoke is actually being called on the rootTask object while we are actually executing the /// childTask. And the debugger needs to discover the childTask, so we pass that down as an argument. /// The NoOptimization and NoInlining flags ensure that the childTask pointer is retained, and that this /// function appears on the callstack. /// </ summary > /// < param name = " childTask " > </ param > [ MethodImpl ( MethodImplOptions . NoOptimization | MethodImplOptions . NoInlining )] internal void InnerInvokeWithArg ( Task childTask ) InnerInvoke (); /// < summary > /// Performs whatever handling is necessary for an unhandled exception. Normally /// this just entails adding the exception to the holder object. /// </ summary > /// < param name = " unhandledException " > The exception that went unhandled. </ param > private void HandleException ( Exception unhandledException ) Contract . Requires ( unhandledException != null ); OperationCanceledException exceptionAsOce = unhandledException as OperationCanceledException ; if ( exceptionAsOce != null && IsCancellationRequested && m_contingentProperties . m_cancellationToken == exceptionAsOce . CancellationToken ) // All conditions are satisfied for us to go into canceled state in Finish(). // Mark the acknowledgement. The exception is also stored to enable it to be // the exception propagated from an await. SetCancellationAcknowledged (); AddException ( exceptionAsOce , representsCancellation : true ); // Other exceptions, including any OCE from the task that doesn't match the tasks' own CT, // or that gets thrown without the CT being set will be treated as an ordinary exception // and added to the aggregate. AddException ( unhandledException ); # region Await Support /// < summary > Gets an awaiter used to await this < see cref = " System.Threading.Tasks. Task " /> . </ summary > /// < returns > An awaiter instance. </ returns > /// < remarks > This method is intended for compiler user rather than use directly in code. </ remarks > public TaskAwaiter GetAwaiter () return new TaskAwaiter ( this ); /// < summary > Configures an awaiter used to await this < see cref = " System.Threading.Tasks. Task " /> . </ summary > /// < param name = " continueOnCapturedContext " > /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. /// </ param > /// < returns > An object used to await this task. </ returns > public ConfiguredTaskAwaitable ConfigureAwait ( bool continueOnCapturedContext ) return new ConfiguredTaskAwaitable ( this , continueOnCapturedContext ); /// < summary > /// Sets a continuation onto the < see cref = " System.Threading.Tasks. Task " /> . /// The continuation is scheduled to run in the current synchronization context is one exists, /// otherwise in the current task scheduler. /// </ summary > /// < param name = " continuationAction " > The action to invoke when the < see cref = " System.Threading.Tasks. Task " /> has completed. </ param > /// < param name = " continueOnCapturedContext " > /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. /// </ param > /// < param name = " flowExecutionContext " > Whether to flow ExecutionContext across the await. </ param > /// < param name = " stackMark " > A stack crawl mark tied to execution context. </ param > /// < exception cref = " System. InvalidOperationException " > The awaiter was not properly initialized. </ exception > [ SecurityCritical ] internal void SetContinuationForAwait ( Action continuationAction , bool continueOnCapturedContext , bool flowExecutionContext , ref StackCrawlMark stackMark ) Contract . Requires ( continuationAction != null ); // Create the best AwaitTaskContinuation object given the request. // If this remains null by the end of the function, we can use the // continuationAction directly without wrapping it. TaskContinuation tc = null ; // If the user wants the continuation to run on the current "context" if there is one... if ( continueOnCapturedContext ) // First try getting the current synchronization context. // If the current context is really just the base SynchronizationContext type, // which is intended to be equivalent to not having a current SynchronizationContext at all, // then ignore it. This helps with performance by avoiding unnecessary posts and queueing // of work items, but more so it ensures that if code happens to publish the default context // as current, it won't prevent usage of a current task scheduler if there is one. var syncCtx = SynchronizationContext . CurrentNoFlow ; if ( syncCtx != null && syncCtx . GetType () != typeof ( SynchronizationContext )) tc = new SynchronizationContextAwaitTaskContinuation ( syncCtx , continuationAction , flowExecutionContext , ref stackMark ); // If there was no SynchronizationContext, then try for the current scheduler. // We only care about it if it's not the default. var scheduler = TaskScheduler . InternalCurrent ; if ( scheduler != null && scheduler != TaskScheduler . Default ) tc = new TaskSchedulerAwaitTaskContinuation ( scheduler , continuationAction , flowExecutionContext , ref stackMark ); if ( tc == null && flowExecutionContext ) // We're targeting the default scheduler, so we can use the faster path // that assumes the default, and thus we don't need to store it. If we're flowing // ExecutionContext, we need to capture it and wrap it in an AwaitTaskContinuation. // Otherwise, we're targeting the default scheduler and we don't need to flow ExecutionContext, so // we don't actually need a continuation object. We can just store/queue the action itself. tc = new AwaitTaskContinuation ( continuationAction , flowExecutionContext : true , stackMark : ref stackMark ); // Now register the continuation, and if we couldn't register it because the task is already completing, // process the continuation directly (in which case make sure we schedule the continuation // rather than inlining it, the latter of which could result in a rare but possible stack overflow). if ( tc != null ) if (! AddTaskContinuation ( tc , addBeforeOthers : false )) tc . Run ( this , bCanInlineContinuationTask : false ); Contract . Assert (! flowExecutionContext , "We already determined we're not required to flow context." ); if (! AddTaskContinuation ( continuationAction , addBeforeOthers : false )) AwaitTaskContinuation . UnsafeScheduleAction ( continuationAction , this ); /// < summary > Creates an awaitable that asynchronously yields back to the current context when awaited. </ summary > /// < returns > /// A context that, when awaited, will asynchronously transition back into the current context at the /// time of the await. If the current SynchronizationContext is non-null, that is treated as the current context. /// Otherwise, TaskScheduler.Current is treated as the current context. /// </ returns > public static YieldAwaitable Yield () return new YieldAwaitable (); # endregion /// < summary > /// Waits for the < see cref = " Task " /> to complete execution. /// </ summary > /// < exception cref = " T:System.AggregateException " > /// The < see cref = " Task " /> was canceled -or- an exception was thrown during /// the execution of the < see cref = " Task " /> . /// </ exception > public void Wait () # if DEBUG bool waitResult = # endif Wait ( Timeout . Infinite , default ( CancellationToken )); # if DEBUG Contract.Assert(waitResult, "expected wait to succeed"); # endif /// < summary > /// Waits for the < see cref = " Task " /> to complete execution. /// </ summary > /// < param name = " timeout " > /// A < see cref = " System. TimeSpan " /> that represents the number of milliseconds to wait, or a < see /// cref = " System. TimeSpan " /> that represents -1 milliseconds to wait indefinitely. /// </ param > /// < returns > /// true if the < see cref = " Task " /> completed execution within the allotted time; otherwise, false. /// </ returns > /// < exception cref = " T:System.AggregateException " > /// The < see cref = " Task " /> was canceled -or- an exception was thrown during the execution of the < see /// cref = " Task " /> . /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// < paramref name = " timeout " /> is a negative number other than -1 milliseconds, which represents an /// infinite time-out -or- timeout is greater than /// < see cref = " System. Int32 . MaxValue " /> . /// </ exception > public bool Wait ( TimeSpan timeout ) long totalMilliseconds = ( long ) timeout . TotalMilliseconds ; if ( totalMilliseconds < -1 || totalMilliseconds > Int32 . MaxValue ) throw new ArgumentOutOfRangeException ( "timeout" ); return Wait (( int ) totalMilliseconds , default ( CancellationToken )); /// < summary > /// Waits for the < see cref = " Task " /> to complete execution. /// </ summary > /// < param name = " cancellationToken " > /// A < see cref = " CancellationToken " /> to observe while waiting for the task to complete. /// </ param > /// < exception cref = " T:System.OperationCanceledException " > /// The < paramref name = " cancellationToken " /> was canceled. /// </ exception > /// < exception cref = " T:System.AggregateException " > /// The < see cref = " Task " /> was canceled -or- an exception was thrown during the execution of the < see /// cref = " Task " /> . /// </ exception > public void Wait ( CancellationToken cancellationToken ) Wait ( Timeout . Infinite , cancellationToken ); /// < summary > /// Waits for the < see cref = " Task " /> to complete execution. /// </ summary > /// < param name = " millisecondsTimeout " > /// The number of milliseconds to wait, or < see cref = " System.Threading. Timeout . Infinite " /> (-1) to /// wait indefinitely. </ param > /// < returns > true if the < see cref = " Task " /> completed execution within the allotted time; otherwise, /// false. /// </ returns > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// < paramref name = " millisecondsTimeout " /> is a negative number other than -1, which represents an /// infinite time-out. /// </ exception > /// < exception cref = " T:System.AggregateException " > /// The < see cref = " Task " /> was canceled -or- an exception was thrown during the execution of the < see /// cref = " Task " /> . /// </ exception > public bool Wait ( int millisecondsTimeout ) return Wait ( millisecondsTimeout , default ( CancellationToken )); /// < summary > /// Waits for the < see cref = " Task " /> to complete execution. /// </ summary > /// < param name = " millisecondsTimeout " > /// The number of milliseconds to wait, or < see cref = " System.Threading. Timeout . Infinite " /> (-1) to /// wait indefinitely. /// </ param > /// < param name = " cancellationToken " > /// A < see cref = " CancellationToken " /> to observe while waiting for the task to complete. /// </ param > /// < returns > /// true if the < see cref = " Task " /> completed execution within the allotted time; otherwise, false. /// </ returns > /// < exception cref = " T:System.AggregateException " > /// The < see cref = " Task " /> was canceled -or- an exception was thrown during the execution of the < see /// cref = " Task " /> . /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// < paramref name = " millisecondsTimeout " /> is a negative number other than -1, which represents an /// infinite time-out. /// </ exception > /// < exception cref = " T:System.OperationCanceledException " > /// The < paramref name = " cancellationToken " /> was canceled. /// </ exception > public bool Wait ( int millisecondsTimeout , CancellationToken cancellationToken ) if ( millisecondsTimeout < -1) throw new ArgumentOutOfRangeException ( "millisecondsTimeout" ); Contract . EndContractBlock (); // Return immediately if we know that we've completed "clean" -- no exceptions, no cancellations // and if no notification to the debugger is required if (! IsWaitNotificationEnabledOrNotRanToCompletion ) // (!DebuggerBitSet && RanToCompletion) return true ; // Wait, and then return if we're still not done. if (! InternalWait ( millisecondsTimeout , cancellationToken )) return false ; if ( IsWaitNotificationEnabledOrNotRanToCompletion ) // avoid a few unnecessary volatile reads if we completed successfully // Notify the debugger of the wait completion if it's requested such a notification NotifyDebuggerOfWaitCompletionIfNecessary (); // If cancellation was requested and the task was canceled, throw an // OperationCanceledException. This is prioritized ahead of the ThrowIfExceptional // call to bring more determinism to cases where the same token is used to // cancel the Wait and to cancel the Task. Otherwise, there's a ---- between // whether the Wait or the Task observes the cancellation request first, // and different exceptions result from the different cases. if ( IsCanceled ) cancellationToken . ThrowIfCancellationRequested (); // If an exception occurred, or the task was cancelled, throw an exception. ThrowIfExceptional ( true ); Contract . Assert (( m_stateFlags & TASK_STATE_FAULTED ) == 0, "Task.Wait() completing when in Faulted state." ); return true ; // Convenience method that wraps any scheduler exception in a TaskSchedulerException // and rethrows it. private bool WrappedTryRunInline () if ( m_taskScheduler == null ) return false ; return m_taskScheduler . TryRunInline ( this , true ); catch ( Exception e ) // we 1) either received an unexpected exception originating from a custom scheduler, which needs to be wrapped in a TSE and thrown // 2) or a a ThreadAbortException, which we need to skip here, because it would already have been handled in Task.Execute if (!( e is ThreadAbortException )) TaskSchedulerException tse = new TaskSchedulerException ( e ); throw tse ; throw ; /// < summary > /// The core wait function, which is only accesible internally. It's meant to be used in places in TPL code where /// the current context is known or cached. /// </ summary > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger internal bool InternalWait ( int millisecondsTimeout , CancellationToken cancellationToken ) // ETW event for Task Wait Begin var etwLog = TplEtwProvider . Log ; bool etwIsEnabled = etwLog . IsEnabled (); if ( etwIsEnabled ) Task currentTask = Task . InternalCurrent ; etwLog . TaskWaitBegin ( ( currentTask != null ? currentTask . m_taskScheduler . Id : TaskScheduler . Default . Id ), ( currentTask != null ? currentTask . Id : 0), this . Id , TplEtwProvider . TaskWaitBehavior . Synchronous , 0, System.Threading. Thread . GetDomainID ()); bool returnValue = IsCompleted ; // If the event hasn't already been set, we will wait. if (! returnValue ) // Alert a listening debugger that we can't make forward progress unless it slips threads. // We call NOCTD for two reasons: // 1. If the task runs on another thread, then we'll be blocked here indefinitely. // 2. If the task runs inline but takes some time to complete, it will suffer ThreadAbort with possible state corruption, // and it is best to prevent this unless the user explicitly asks to view the value with thread-slipping enabled. Debugger . NotifyOfCrossThreadDependency (); // We will attempt inline execution only if an infinite wait was requested // Inline execution doesn't make sense for finite timeouts and if a cancellation token was specified // because we don't know how long the task delegate will take. if ( millisecondsTimeout == Timeout . Infinite && ! cancellationToken . CanBeCanceled && WrappedTryRunInline () && IsCompleted ) // TryRunInline doesn't guarantee completion, as there may be unfinished children. returnValue = true ; returnValue = SpinThenBlockingWait ( millisecondsTimeout , cancellationToken ); Contract . Assert ( IsCompleted || millisecondsTimeout != Timeout . Infinite ); // ETW event for Task Wait End if ( etwIsEnabled ) Task currentTask = Task . InternalCurrent ; if ( currentTask != null ) etwLog . TaskWaitEnd ( currentTask . m_taskScheduler . Id , currentTask . Id , this . Id ); etwLog . TaskWaitEnd ( TaskScheduler . Default . Id , 0, this . Id ); // logically the continuation is empty so we immediately fire etwLog . TaskWaitContinuationComplete ( this . Id ); return returnValue ; // An MRES that gets set when Invoke is called. This replaces old logic that looked like this: // ManualResetEventSlim mres = new ManualResetEventSlim(false, 0); // Action<Task> completionAction = delegate {mres.Set();} // AddCompletionAction(completionAction); // with this: // SetOnInvokeMres mres = new SetOnInvokeMres(); // AddCompletionAction(mres, addBeforeOthers: true); // which saves a couple of allocations. // Used in SpinThenBlockingWait (below), but could be seen as a general purpose mechanism. private sealed class SetOnInvokeMres : ManualResetEventSlim , ITaskCompletionAction internal SetOnInvokeMres () : base ( false , 0) { } public void Invoke ( Task completingTask ) { Set (); } /// < summary > /// Waits for the task to complete, for a timeout to occur, or for cancellation to be requested. /// The method first spins and then falls back to blocking on a new event. /// </ summary > /// < param name = " millisecondsTimeout " > The timeout. </ param > /// < param name = " cancellationToken " > The token. </ param > /// < returns > true if the task is completed; otherwise, false. </ returns > private bool SpinThenBlockingWait ( int millisecondsTimeout , CancellationToken cancellationToken ) bool infiniteWait = millisecondsTimeout == Timeout . Infinite ; uint startTimeTicks = infiniteWait ? 0 : ( uint ) Environment . TickCount ; bool returnValue = SpinWait ( millisecondsTimeout ); if (! returnValue ) var mres = new SetOnInvokeMres (); AddCompletionAction ( mres , addBeforeOthers : true ); if ( infiniteWait ) returnValue = mres . Wait ( Timeout . Infinite , cancellationToken ); uint elapsedTimeTicks = (( uint ) Environment . TickCount ) - startTimeTicks ; if ( elapsedTimeTicks < millisecondsTimeout ) returnValue = mres . Wait (( int )( millisecondsTimeout - elapsedTimeTicks ), cancellationToken ); finally if (! IsCompleted ) RemoveContinuation ( mres ); // Don't Dispose of the MRES, because the continuation off of this task may // still be running. This is ok, however, as we never access the MRES' WaitHandle, // and thus no finalizable resources are actually allocated. return returnValue ; /// < summary > /// Spins briefly while checking IsCompleted /// </ summary > /// < param name = " millisecondsTimeout " > The timeout. </ param > /// < returns > true if the task is completed; otherwise, false. </ returns > /// < exception cref = " System. OperationCanceledException " > The wait was canceled. </ exception > private bool SpinWait ( int millisecondsTimeout ) if ( IsCompleted ) return true ; if ( millisecondsTimeout == 0) // For 0-timeouts, we just return immediately. return false ; //This code is pretty similar to the custom spinning in MRES except there is no yieling after we exceed the spin count int spinCount = PlatformHelper . IsSingleProcessor ? 1 : System.Threading. SpinWait . YIELD_THRESHOLD ; //spin only once if we are running on a single CPU for ( int i = 0; i < spinCount ; i ++) if ( IsCompleted ) return true ; if ( i == spinCount / 2) Thread . Yield (); Thread . SpinWait (4 << i ); return IsCompleted ; /// < summary > /// Cancels the < see cref = " Task " /> . /// </ summary > /// < param name = " bCancelNonExecutingOnly " > /// Indicates whether we should only cancel non-invoked tasks. /// For the default scheduler this option will only be serviced through TryDequeue. /// For custom schedulers we also attempt an atomic state transition. /// </ param > /// < returns > true if the task was successfully canceled; otherwise, false. </ returns > [ SecuritySafeCritical ] internal bool InternalCancel ( bool bCancelNonExecutingOnly ) Contract . Requires (( Options & ( TaskCreationOptions ) InternalTaskOptions . PromiseTask ) == 0, "Task.InternalCancel() did not expect promise-style task" ); bool bPopSucceeded = false ; bool mustCleanup = false ; TaskSchedulerException tse = null ; // If started, and running in a task context, we can try to pop the chore. if (( m_stateFlags & TASK_STATE_STARTED ) != 0) TaskScheduler ts = m_taskScheduler ; bPopSucceeded = ( ts != null ) && ts . TryDequeue ( this ); catch ( Exception e ) // TryDequeue threw. We don't know whether the task was properly dequeued or not. So we must let the rest of // the cancellation logic run its course (record the request, attempt atomic state transition and do cleanup where appropriate) // Here we will only record a TaskSchedulerException, which will later be thrown at function exit. if (!( e is ThreadAbortException )) tse = new TaskSchedulerException ( e ); bool bRequiresAtomicStartTransition = ( ts != null && ts . RequiresAtomicStartTransition ) || (( Options & ( TaskCreationOptions ) InternalTaskOptions . SelfReplicating ) != 0); if (! bPopSucceeded && bCancelNonExecutingOnly && bRequiresAtomicStartTransition ) // The caller requested cancellation of non-invoked tasks only, and TryDequeue was one way of doing it... // Since that seems to have failed, we should now try an atomic state transition (from non-invoked state to canceled) // An atomic transition here is only safe if we know we're on a custom task scheduler, which also forces a CAS on ExecuteEntry // Even though this task can't have any children, we should be ready for handling any continuations that // may be attached to it (although currently // So we need to remeber whether we actually did the flip, so we can do clean up (finish continuations etc) mustCleanup = AtomicStateUpdate ( TASK_STATE_CANCELED , TASK_STATE_DELEGATE_INVOKED | TASK_STATE_CANCELED ); // PS: This is slightly different from the regular cancellation codepath // since we record the cancellation request *after* doing the state transition. // However that shouldn't matter too much because the task was never invoked, thus can't have children if (! bCancelNonExecutingOnly || bPopSucceeded || mustCleanup ) // Record the cancellation request. RecordInternalCancellationRequest (); // Determine whether we need to clean up // This will be the case // 1) if we were able to pop, and we win the ---- to update task state to TASK_STATE_CANCELED // 2) if the task seems to be yet unstarted, and we win the ---- to transition to // TASK_STATE_CANCELED before anyone else can transition into _STARTED or _CANCELED or // _RAN_TO_COMPLETION or _FAULTED // Note that we do not check for TASK_STATE_COMPLETION_RESERVED. That only applies to promise-style // tasks, and a promise-style task should not enter into this codepath. if ( bPopSucceeded ) // hitting this would mean something wrong with the AtomicStateUpdate above Contract . Assert (! mustCleanup , "Possibly an invalid state transition call was made in InternalCancel()" ); // Include TASK_STATE_DELEGATE_INVOKED in "illegal" bits to protect against the situation where // TS.TryDequeue() returns true but the task is still left on the queue. mustCleanup = AtomicStateUpdate ( TASK_STATE_CANCELED , TASK_STATE_CANCELED | TASK_STATE_DELEGATE_INVOKED ); else if (! mustCleanup && ( m_stateFlags & TASK_STATE_STARTED ) == 0) mustCleanup = AtomicStateUpdate ( TASK_STATE_CANCELED , TASK_STATE_CANCELED | TASK_STATE_STARTED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_DELEGATE_INVOKED ); // do the cleanup (i.e. set completion event and finish continuations) if ( mustCleanup ) CancellationCleanupLogic (); if ( tse != null ) throw tse ; return ( mustCleanup ); // Breaks out logic for recording a cancellation request internal void RecordInternalCancellationRequest () // Record the cancellation request. var props = EnsureContingentPropertiesInitialized ( needsProtection : true ); props . m_internalCancellationRequested = CANCELLATION_REQUESTED ; // Breaks out logic for recording a cancellation request // This overload should only be used for promise tasks where no cancellation token // was supplied when the task was created. internal void RecordInternalCancellationRequest ( CancellationToken tokenToRecord ) RecordInternalCancellationRequest (); Contract . Assert (( Options & ( TaskCreationOptions ) InternalTaskOptions . PromiseTask ) != 0, "Task.RecordInternalCancellationRequest(CancellationToken) only valid for promise-style task" ); Contract . Assert ( m_contingentProperties . m_cancellationToken == default ( CancellationToken )); // Store the supplied cancellation token as this task's token. // Waiting on this task will then result in an OperationCanceledException containing this token. if ( tokenToRecord != default ( CancellationToken )) m_contingentProperties . m_cancellationToken = tokenToRecord ; // Breaks out logic for recording a cancellation request // This overload should only be used for promise tasks where no cancellation token // was supplied when the task was created. internal void RecordInternalCancellationRequest ( CancellationToken tokenToRecord , object cancellationException ) RecordInternalCancellationRequest ( tokenToRecord ); // Store the supplied cancellation exception if ( cancellationException != null ) # if DEBUG var oce = cancellationException as OperationCanceledException; if (oce == null) var edi = cancellationException as ExceptionDispatchInfo; Contract.Assert(edi != null, "Expected either an OCE or an EDI"); oce = edi.SourceException as OperationCanceledException; Contract.Assert(oce != null, "Expected EDI to contain an OCE"); Contract.Assert(oce.CancellationToken == tokenToRecord, "Expected OCE's token to match the provided token."); # endif AddException ( cancellationException , representsCancellation : true ); // ASSUMES THAT A SUCCESSFUL CANCELLATION HAS JUST OCCURRED ON THIS TASK!!! // And this method should be called at most once per task. internal void CancellationCleanupLogic () Contract . Assert (( m_stateFlags & ( TASK_STATE_CANCELED | TASK_STATE_COMPLETION_RESERVED )) != 0, "Task.CancellationCleanupLogic(): Task not canceled or reserved." ); // I'd like to do this, but there is a small window for a race condition. If someone calls Wait() between InternalCancel() and // here, that will set m_completionEvent, leading to a meaningless/harmless assertion. //Contract.Assert((m_completionEvent == null) || !m_completionEvent.IsSet, "Task.CancellationCleanupLogic(): Completion event already set."); // This may have been set already, but we need to make sure. Interlocked . Exchange ( ref m_stateFlags , m_stateFlags | TASK_STATE_CANCELED ); // Fire completion event if it has been lazily initialized var cp = m_contingentProperties ; if ( cp != null ) cp . SetCompleted (); cp . DeregisterCancellationCallback (); if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCompletion ( CausalityTraceLevel . Required , this . Id , AsyncCausalityStatus . Canceled ); if ( Task . s_asyncDebuggingEnabled ) RemoveFromActiveTasks ( this . Id ); // Notify parents, fire continuations, other cleanup. FinishStageThree (); /// < summary > /// Sets the task's cancellation acknowledged flag. /// </ summary > private void SetCancellationAcknowledged () Contract . Assert ( this == Task . InternalCurrent , "SetCancellationAcknowledged() should only be called while this is still the current task" ); Contract . Assert ( IsCancellationRequested , "SetCancellationAcknowledged() should not be called if the task's CT wasn't signaled" ); m_stateFlags |= TASK_STATE_CANCELLATIONACKNOWLEDGED ; // Continuation passing functionality (aka ContinueWith) /// Runs all of the continuations, as appropriate. /// </ summary > [ SecuritySafeCritical ] // for AwaitTaskContinuation.RunOrScheduleAction internal void FinishContinuations () // Atomically store the fact that this task is completing. From this point on, the adding of continuations will // result in the continuations being run/launched directly rather than being added to the continuation list. object continuationObject = Interlocked . Exchange ( ref m_continuationObject , s_taskCompletionSentinel ); TplEtwProvider . Log . RunningContinuation ( Id , continuationObject ); // If continuationObject == null, then we don't have any continuations to process if ( continuationObject != null ) if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceSynchronousWorkStart ( CausalityTraceLevel . Required , this . Id , CausalitySynchronousWork . CompletionNotification ); // Skip synchronous execution of continuations if this task's thread was aborted bool bCanInlineContinuations = !((( m_stateFlags & TASK_STATE_THREAD_WAS_ABORTED ) != 0) || ( Thread . CurrentThread . ThreadState == ThreadState . AbortRequested ) || (( m_stateFlags & ( int ) TaskCreationOptions . RunContinuationsAsynchronously ) != 0)); // Handle the single-Action case Action singleAction = continuationObject as Action ; if ( singleAction != null ) AwaitTaskContinuation . RunOrScheduleAction ( singleAction , bCanInlineContinuations , ref t_currentTask ); LogFinishCompletionNotification (); return ; // Handle the single-ITaskCompletionAction case ITaskCompletionAction singleTaskCompletionAction = continuationObject as ITaskCompletionAction ; if ( singleTaskCompletionAction != null ) if ( bCanInlineContinuations ) singleTaskCompletionAction . Invoke ( this ); ThreadPool . UnsafeQueueCustomWorkItem ( new CompletionActionInvoker ( singleTaskCompletionAction , this ), forceGlobal : false ); LogFinishCompletionNotification (); return ; // Handle the single-TaskContinuation case TaskContinuation singleTaskContinuation = continuationObject as TaskContinuation ; if ( singleTaskContinuation != null ) singleTaskContinuation . Run ( this , bCanInlineContinuations ); LogFinishCompletionNotification (); return ; // Not a single; attempt to cast as list List < object > continuations = continuationObject as List < object >; if ( continuations == null ) LogFinishCompletionNotification (); return ; // Not a single or a list; just return // Begin processing of continuation list // Wait for any concurrent adds or removes to be retired lock ( continuations ) { } int continuationCount = continuations . Count ; // Fire the asynchronous continuations first ... for ( int i = 0; i < continuationCount ; i ++) // Synchronous continuation tasks will have the ExecuteSynchronously option, // and we're looking for asynchronous tasks... var tc = continuations [ i ] as StandardTaskContinuation ; if ( tc != null && ( tc . m_options & TaskContinuationOptions . ExecuteSynchronously ) == 0) TplEtwProvider . Log . RunningContinuationList ( Id , i , tc ); continuations [ i ] = null ; // so that we can skip this later tc . Run ( this , bCanInlineContinuations ); // ... and then fire the synchronous continuations (if there are any). // This includes ITaskCompletionAction, AwaitTaskContinuations, and // Action delegates, which are all by default implicitly synchronous. for ( int i = 0; i < continuationCount ; i ++) object currentContinuation = continuations [ i ]; if ( currentContinuation == null ) continue ; continuations [ i ] = null ; // to enable free'ing up memory earlier TplEtwProvider . Log . RunningContinuationList ( Id , i , currentContinuation ); // If the continuation is an Action delegate, it came from an await continuation, // and we should use AwaitTaskContinuation to run it. Action ad = currentContinuation as Action ; if ( ad != null ) AwaitTaskContinuation . RunOrScheduleAction ( ad , bCanInlineContinuations , ref t_currentTask ); // If it's a TaskContinuation object of some kind, invoke it. TaskContinuation tc = currentContinuation as TaskContinuation ; if ( tc != null ) // We know that this is a synchronous continuation because the // asynchronous ones have been weeded out tc . Run ( this , bCanInlineContinuations ); // Otherwise, it must be an ITaskCompletionAction, so invoke it. Contract . Assert ( currentContinuation is ITaskCompletionAction , "Expected continuation element to be Action, TaskContinuation, or ITaskContinuationAction" ); var action = ( ITaskCompletionAction ) currentContinuation ; if ( bCanInlineContinuations ) action . Invoke ( this ); ThreadPool . UnsafeQueueCustomWorkItem ( new CompletionActionInvoker ( action , this ), forceGlobal : false ); LogFinishCompletionNotification (); private void LogFinishCompletionNotification () if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceSynchronousWorkCompletion ( CausalityTraceLevel . Required , CausalitySynchronousWork . CompletionNotification ); # region Continuation methods # region Action<Task> continuation /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task > continuationAction ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , TaskScheduler . Current , default ( CancellationToken ), TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new continuation task. </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task > continuationAction , CancellationToken cancellationToken ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , TaskScheduler . Current , cancellationToken , TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < param name = " scheduler " > /// The < see cref = " TaskScheduler " /> to associate with the continuation task and to use for its execution. /// </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " scheduler " /> argument is null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task > continuationAction , TaskScheduler scheduler ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , scheduler , default ( CancellationToken ), TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < param name = " continuationOptions " > /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such /// as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . OnlyOnCanceled " > OnlyOnCanceled </ see > , as /// well as execution options, such as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . ExecuteSynchronously " > ExecuteSynchronously </ see > . /// </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed. If the continuation criteria specified through the < paramref /// name = " continuationOptions " /> parameter are not met, the continuation task will be canceled /// instead of scheduled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " continuationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskContinuationOptions " > TaskContinuationOptions </ see > . /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task > continuationAction , TaskContinuationOptions continuationOptions ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , TaskScheduler . Current , default ( CancellationToken ), continuationOptions , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < param name = " continuationOptions " > /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such /// as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . OnlyOnCanceled " > OnlyOnCanceled </ see > , as /// well as execution options, such as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . ExecuteSynchronously " > ExecuteSynchronously </ see > . /// </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new continuation task. </ param > /// < param name = " scheduler " > /// The < see cref = " TaskScheduler " /> to associate with the continuation task and to use for its /// execution. /// </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed. If the criteria specified through the < paramref name = " continuationOptions " /> parameter /// are not met, the continuation task will be canceled instead of scheduled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " continuationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskContinuationOptions " > TaskContinuationOptions </ see > . /// </ exception > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " scheduler " /> argument is null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task > continuationAction , CancellationToken cancellationToken , TaskContinuationOptions continuationOptions , TaskScheduler scheduler ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , scheduler , cancellationToken , continuationOptions , ref stackMark ); // Same as the above overload, just with a stack mark parameter. private Task ContinueWith ( Action < Task > continuationAction , TaskScheduler scheduler , CancellationToken cancellationToken , TaskContinuationOptions continuationOptions , ref StackCrawlMark stackMark ) // Throw on continuation with null action if ( continuationAction == null ) throw new ArgumentNullException ( "continuationAction" ); // Throw on continuation with null TaskScheduler if ( scheduler == null ) throw new ArgumentNullException ( "scheduler" ); Contract . EndContractBlock (); TaskCreationOptions creationOptions ; InternalTaskOptions internalOptions ; CreationOptionsFromContinuationOptions ( continuationOptions , out creationOptions , out internalOptions ); Task continuationTask = new ContinuationTaskFromTask ( this , continuationAction , null , creationOptions , internalOptions , ref stackMark // Register the continuation. If synchronous execution is requested, this may // actually invoke the continuation before returning. ContinueWithCore ( continuationTask , scheduler , cancellationToken , continuationOptions ); return continuationTask ; # endregion # region Action<Task, Object> continuation /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation action. </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task , Object > continuationAction , Object state ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , state , TaskScheduler . Current , default ( CancellationToken ), TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation action. </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new continuation task. </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task , Object > continuationAction , Object state , CancellationToken cancellationToken ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , state , TaskScheduler . Current , cancellationToken , TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation action. </ param > /// < param name = " scheduler " > /// The < see cref = " TaskScheduler " /> to associate with the continuation task and to use for its execution. /// </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " scheduler " /> argument is null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task , Object > continuationAction , Object state , TaskScheduler scheduler ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , state , scheduler , default ( CancellationToken ), TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation action. </ param > /// < param name = " continuationOptions " > /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such /// as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . OnlyOnCanceled " > OnlyOnCanceled </ see > , as /// well as execution options, such as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . ExecuteSynchronously " > ExecuteSynchronously </ see > . /// </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed. If the continuation criteria specified through the < paramref /// name = " continuationOptions " /> parameter are not met, the continuation task will be canceled /// instead of scheduled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " continuationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskContinuationOptions " > TaskContinuationOptions </ see > . /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task , Object > continuationAction , Object state , TaskContinuationOptions continuationOptions ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , state , TaskScheduler . Current , default ( CancellationToken ), continuationOptions , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < param name = " continuationAction " > /// An action to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation action. </ param > /// < param name = " continuationOptions " > /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such /// as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . OnlyOnCanceled " > OnlyOnCanceled </ see > , as /// well as execution options, such as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . ExecuteSynchronously " > ExecuteSynchronously </ see > . /// </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new continuation task. </ param > /// < param name = " scheduler " > /// The < see cref = " TaskScheduler " /> to associate with the continuation task and to use for its /// execution. /// </ param > /// < returns > A new continuation < see cref = " Task " /> . </ returns > /// < remarks > /// The returned < see cref = " Task " /> will not be scheduled for execution until the current task has /// completed. If the criteria specified through the < paramref name = " continuationOptions " /> parameter /// are not met, the continuation task will be canceled instead of scheduled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationAction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " continuationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskContinuationOptions " > TaskContinuationOptions </ see > . /// </ exception > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " scheduler " /> argument is null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task ContinueWith ( Action < Task , Object > continuationAction , Object state , CancellationToken cancellationToken , TaskContinuationOptions continuationOptions , TaskScheduler scheduler ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith ( continuationAction , state , scheduler , cancellationToken , continuationOptions , ref stackMark ); // Same as the above overload, just with a stack mark parameter. private Task ContinueWith ( Action < Task , Object > continuationAction , Object state , TaskScheduler scheduler , CancellationToken cancellationToken , TaskContinuationOptions continuationOptions , ref StackCrawlMark stackMark ) // Throw on continuation with null action if ( continuationAction == null ) throw new ArgumentNullException ( "continuationAction" ); // Throw on continuation with null TaskScheduler if ( scheduler == null ) throw new ArgumentNullException ( "scheduler" ); Contract . EndContractBlock (); TaskCreationOptions creationOptions ; InternalTaskOptions internalOptions ; CreationOptionsFromContinuationOptions ( continuationOptions , out creationOptions , out internalOptions ); Task continuationTask = new ContinuationTaskFromTask ( this , continuationAction , state , creationOptions , internalOptions , ref stackMark // Register the continuation. If synchronous execution is requested, this may // actually invoke the continuation before returning. ContinueWithCore ( continuationTask , scheduler , cancellationToken , continuationOptions ); return continuationTask ; # endregion # region Func<Task, TResult> continuation /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , TResult > continuationFunction ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , TaskScheduler . Current , default ( CancellationToken ), TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new continuation task. </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , TResult > continuationFunction , CancellationToken cancellationToken ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , TaskScheduler . Current , cancellationToken , TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < param name = " scheduler " > /// The < see cref = " TaskScheduler " /> to associate with the continuation task and to use for its execution. /// </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " scheduler " /> argument is null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , TResult > continuationFunction , TaskScheduler scheduler ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , scheduler , default ( CancellationToken ), TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < param name = " continuationOptions " > /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such /// as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . OnlyOnCanceled " > OnlyOnCanceled </ see > , as /// well as execution options, such as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . ExecuteSynchronously " > ExecuteSynchronously </ see > . /// </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed. If the continuation criteria specified through the < paramref /// name = " continuationOptions " /> parameter are not met, the continuation task will be canceled /// instead of scheduled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " continuationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskContinuationOptions " > TaskContinuationOptions </ see > . /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , TResult > continuationFunction , TaskContinuationOptions continuationOptions ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , TaskScheduler . Current , default ( CancellationToken ), continuationOptions , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task as an argument. /// </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new continuation task. </ param > /// < param name = " continuationOptions " > /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such /// as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . OnlyOnCanceled " > OnlyOnCanceled </ see > , as /// well as execution options, such as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . ExecuteSynchronously " > ExecuteSynchronously </ see > . /// </ param > /// < param name = " scheduler " > /// The < see cref = " TaskScheduler " /> to associate with the continuation task and to use for its /// execution. /// </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed. If the criteria specified through the < paramref name = " continuationOptions " /> parameter /// are not met, the continuation task will be canceled instead of scheduled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " continuationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskContinuationOptions " > TaskContinuationOptions </ see > . /// </ exception > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " scheduler " /> argument is null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , TResult > continuationFunction , CancellationToken cancellationToken , TaskContinuationOptions continuationOptions , TaskScheduler scheduler ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , scheduler , cancellationToken , continuationOptions , ref stackMark ); // Same as the above overload, just with a stack mark parameter. private Task < TResult > ContinueWith < TResult >( Func < Task , TResult > continuationFunction , TaskScheduler scheduler , CancellationToken cancellationToken , TaskContinuationOptions continuationOptions , ref StackCrawlMark stackMark ) // Throw on continuation with null function if ( continuationFunction == null ) throw new ArgumentNullException ( "continuationFunction" ); // Throw on continuation with null task scheduler if ( scheduler == null ) throw new ArgumentNullException ( "scheduler" ); Contract . EndContractBlock (); TaskCreationOptions creationOptions ; InternalTaskOptions internalOptions ; CreationOptionsFromContinuationOptions ( continuationOptions , out creationOptions , out internalOptions ); Task < TResult > continuationTask = new ContinuationResultTaskFromTask < TResult >( this , continuationFunction , null , creationOptions , internalOptions , ref stackMark // Register the continuation. If synchronous execution is requested, this may // actually invoke the continuation before returning. ContinueWithCore ( continuationTask , scheduler , cancellationToken , continuationOptions ); return continuationTask ; # endregion # region Func<Task, Object, TResult> continuation /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation function. </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , Object , TResult > continuationFunction , Object state ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , state , TaskScheduler . Current , default ( CancellationToken ), TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation function. </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new continuation task. </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , Object , TResult > continuationFunction , Object state , CancellationToken cancellationToken ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , state , TaskScheduler . Current , cancellationToken , TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation function. </ param > /// < param name = " scheduler " > /// The < see cref = " TaskScheduler " /> to associate with the continuation task and to use for its execution. /// </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed, whether it completes due to running to completion successfully, faulting due to an /// unhandled exception, or exiting out early due to being canceled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " scheduler " /> argument is null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , Object , TResult > continuationFunction , Object state , TaskScheduler scheduler ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , state , scheduler , default ( CancellationToken ), TaskContinuationOptions . None , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation function. </ param > /// < param name = " continuationOptions " > /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such /// as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . OnlyOnCanceled " > OnlyOnCanceled </ see > , as /// well as execution options, such as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . ExecuteSynchronously " > ExecuteSynchronously </ see > . /// </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed. If the continuation criteria specified through the < paramref /// name = " continuationOptions " /> parameter are not met, the continuation task will be canceled /// instead of scheduled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " continuationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskContinuationOptions " > TaskContinuationOptions </ see > . /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , Object , TResult > continuationFunction , Object state , TaskContinuationOptions continuationOptions ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , state , TaskScheduler . Current , default ( CancellationToken ), continuationOptions , ref stackMark ); /// < summary > /// Creates a continuation that executes when the target < see cref = " Task " /> completes. /// </ summary > /// < typeparam name = " TResult " > /// The type of the result produced by the continuation. /// </ typeparam > /// < param name = " continuationFunction " > /// A function to run when the < see cref = " Task " /> completes. When run, the delegate will be /// passed the completed task and the caller-supplied state object as arguments. /// </ param > /// < param name = " state " > An object representing data to be used by the continuation function. </ param > /// < param name = " cancellationToken " > The < see cref = " CancellationToken " /> that will be assigned to the new continuation task. </ param > /// < param name = " continuationOptions " > /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such /// as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . OnlyOnCanceled " > OnlyOnCanceled </ see > , as /// well as execution options, such as < see /// cref = " System.Threading.Tasks. TaskContinuationOptions . ExecuteSynchronously " > ExecuteSynchronously </ see > . /// </ param > /// < param name = " scheduler " > /// The < see cref = " TaskScheduler " /> to associate with the continuation task and to use for its /// execution. /// </ param > /// < returns > A new continuation < see cref = " Task { TResult } " /> . </ returns > /// < remarks > /// The returned < see cref = " Task { TResult } " /> will not be scheduled for execution until the current task has /// completed. If the criteria specified through the < paramref name = " continuationOptions " /> parameter /// are not met, the continuation task will be canceled instead of scheduled. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " continuationFunction " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " continuationOptions " /> argument specifies an invalid value for < see /// cref = " T:System.Threading.Tasks.TaskContinuationOptions " > TaskContinuationOptions </ see > . /// </ exception > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " scheduler " /> argument is null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > The provided < see cref = " System.Threading. CancellationToken " > CancellationToken </ see > /// has already been disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public Task < TResult > ContinueWith < TResult >( Func < Task , Object , TResult > continuationFunction , Object state , CancellationToken cancellationToken , TaskContinuationOptions continuationOptions , TaskScheduler scheduler ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return ContinueWith < TResult >( continuationFunction , state , scheduler , cancellationToken , continuationOptions , ref stackMark ); // Same as the above overload, just with a stack mark parameter. private Task < TResult > ContinueWith < TResult >( Func < Task , Object , TResult > continuationFunction , Object state , TaskScheduler scheduler , CancellationToken cancellationToken , TaskContinuationOptions continuationOptions , ref StackCrawlMark stackMark ) // Throw on continuation with null function if ( continuationFunction == null ) throw new ArgumentNullException ( "continuationFunction" ); // Throw on continuation with null task scheduler if ( scheduler == null ) throw new ArgumentNullException ( "scheduler" ); Contract . EndContractBlock (); TaskCreationOptions creationOptions ; InternalTaskOptions internalOptions ; CreationOptionsFromContinuationOptions ( continuationOptions , out creationOptions , out internalOptions ); Task < TResult > continuationTask = new ContinuationResultTaskFromTask < TResult >( this , continuationFunction , state , creationOptions , internalOptions , ref stackMark // Register the continuation. If synchronous execution is requested, this may // actually invoke the continuation before returning. ContinueWithCore ( continuationTask , scheduler , cancellationToken , continuationOptions ); return continuationTask ; # endregion /// < summary > /// Converts TaskContinuationOptions to TaskCreationOptions, and also does /// some validity checking along the way. /// </ summary > /// < param name = " continuationOptions " > Incoming TaskContinuationOptions </ param > /// < param name = " creationOptions " > Outgoing TaskCreationOptions </ param > /// < param name = " internalOptions " > Outgoing InternalTaskOptions </ param > internal static void CreationOptionsFromContinuationOptions ( TaskContinuationOptions continuationOptions , out TaskCreationOptions creationOptions , out InternalTaskOptions internalOptions ) // This is used a couple of times below TaskContinuationOptions NotOnAnything = TaskContinuationOptions . NotOnCanceled | TaskContinuationOptions . NotOnFaulted | TaskContinuationOptions . NotOnRanToCompletion ; TaskContinuationOptions creationOptionsMask = TaskContinuationOptions . PreferFairness | TaskContinuationOptions . LongRunning | TaskContinuationOptions . DenyChildAttach | TaskContinuationOptions . HideScheduler | TaskContinuationOptions . AttachedToParent | TaskContinuationOptions . RunContinuationsAsynchronously ; // Check that LongRunning and ExecuteSynchronously are not specified together TaskContinuationOptions illegalMask = TaskContinuationOptions . ExecuteSynchronously | TaskContinuationOptions . LongRunning ; if (( continuationOptions & illegalMask ) == illegalMask ) throw new ArgumentOutOfRangeException ( "continuationOptions" , Environment . GetResourceString ( "Task_ContinueWith_ESandLR" )); // Check that no illegal options were specified if (( continuationOptions & ~( creationOptionsMask | NotOnAnything | TaskContinuationOptions . LazyCancellation | TaskContinuationOptions . ExecuteSynchronously )) != 0) throw new ArgumentOutOfRangeException ( "continuationOptions" ); // Check that we didn't specify "not on anything" if (( continuationOptions & NotOnAnything ) == NotOnAnything ) throw new ArgumentOutOfRangeException ( "continuationOptions" , Environment . GetResourceString ( "Task_ContinueWith_NotOnAnything" )); // This passes over all but LazyCancellation, which has no representation in TaskCreationOptions creationOptions = ( TaskCreationOptions )( continuationOptions & creationOptionsMask ); // internalOptions has at least ContinuationTask ... internalOptions = InternalTaskOptions . ContinuationTask ; // ... and possibly LazyCancellation if (( continuationOptions & TaskContinuationOptions . LazyCancellation ) != 0) internalOptions |= InternalTaskOptions . LazyCancellation ; /// < summary > /// Registers the continuation and possibly runs it (if the task is already finished). /// </ summary > /// < param name = " continuationTask " > The continuation task itself. </ param > /// < param name = " scheduler " > TaskScheduler with which to associate continuation task. </ param > /// < param name = " options " > Restrictions on when the continuation becomes active. </ param > internal void ContinueWithCore ( Task continuationTask , TaskScheduler scheduler , CancellationToken cancellationToken , TaskContinuationOptions options ) Contract . Requires ( continuationTask != null , "Task.ContinueWithCore(): null continuationTask" ); Contract . Requires ( scheduler != null , "Task.ContinueWithCore(): null scheduler" ); Contract . Requires (! continuationTask . IsCompleted , "Did not expect continuationTask to be completed" ); // Create a TaskContinuation TaskContinuation continuation = new StandardTaskContinuation ( continuationTask , options , scheduler ); // If cancellationToken is cancellable, then assign it. if ( cancellationToken . CanBeCanceled ) if ( IsCompleted || cancellationToken . IsCancellationRequested ) // If the antecedent has completed, then we will not be queuing up // the continuation in the antecedent's continuation list. Likewise, // if the cancellationToken has been canceled, continuationTask will // be completed in the AssignCancellationToken call below, and there // is no need to queue the continuation to the antecedent's continuation // list. In either of these two cases, we will pass "null" for the antecedent, // meaning "the cancellation callback should not attempt to remove the // continuation from its antecedent's continuation list". continuationTask . AssignCancellationToken ( cancellationToken , null , null ); // The antecedent is not yet complete, so there is a pretty good chance // that the continuation will be queued up in the antecedent. Assign the // cancellation token with information about the antecedent, so that the // continuation can be dequeued upon the signalling of the token. // It's possible that the antecedent completes before the call to AddTaskContinuation, // and that is a benign ----. It just means that the cancellation will result in // a futile search of the antecedent's continuation list. continuationTask . AssignCancellationToken ( cancellationToken , this , continuation ); // In the case of a pre-canceled token, continuationTask will have been completed // in a Canceled state by now. If such is the case, there is no need to go through // the motions of queuing up the continuation for eventual execution. if (! continuationTask . IsCompleted ) // We need additional correlation produced here to ensure that at least the continuation // code will be correlatable to the currrent activity that initiated "this" task: // . when the antecendent ("this") is a promise we have very little control over where // the code for the promise will run (e.g. it can be a task from a user provided // TaskCompletionSource or from a classic Begin/End async operation); this user or // system code will likely not have stamped an activity id on the thread, so there's // generally no easy correlation that can be provided between the current activity // and the promise. Also the continuation code may run practically on any thread. // Since there may be no correlation between the current activity and the TCS's task // activity, we ensure we at least create a correlation from the current activity to // the continuation that runs when the promise completes. if (( this . Options & ( TaskCreationOptions ) InternalTaskOptions . PromiseTask ) != 0 && !( this is ITaskCompletionAction )) var etwLog = TplEtwProvider . Log ; if ( etwLog . IsEnabled ()) etwLog . AwaitTaskContinuationScheduled ( TaskScheduler . Current . Id , Task . CurrentId ?? 0, continuationTask . Id ); // Attempt to enqueue the continuation bool continuationQueued = AddTaskContinuation ( continuation , addBeforeOthers : false ); // If the continuation was not queued (because the task completed), then run it now. if (! continuationQueued ) continuation . Run ( this , bCanInlineContinuationTask : true ); # endregion // Adds a lightweight completion action to a task. This is similar to a continuation // task except that it is stored as an action, and thus does not require the allocation/ // execution resources of a continuation task. // Used internally by ContinueWhenAll() and ContinueWhenAny(). internal void AddCompletionAction ( ITaskCompletionAction action ) AddCompletionAction ( action , addBeforeOthers : false ); private void AddCompletionAction ( ITaskCompletionAction action , bool addBeforeOthers ) if (! AddTaskContinuation ( action , addBeforeOthers )) action . Invoke ( this ); // run the action directly if we failed to queue the continuation (i.e., the task completed) // Support method for AddTaskContinuation that takes care of multi-continuation logic. // Returns true if and only if the continuation was successfully queued. // THIS METHOD ASSUMES THAT m_continuationObject IS NOT NULL. That case was taken // care of in the calling method, AddTaskContinuation(). private bool AddTaskContinuationComplex ( object tc , bool addBeforeOthers ) Contract . Requires ( tc != null , "Expected non-null tc object in AddTaskContinuationComplex" ); object oldValue = m_continuationObject ; // Logic for the case where we were previously storing a single continuation if (( oldValue != s_taskCompletionSentinel ) && (!( oldValue is List < object >))) // Construct a new TaskContinuation list List < object > newList = new List < object >(); // Add in the old single value newList . Add ( oldValue ); // Now CAS in the new list Interlocked . CompareExchange ( ref m_continuationObject , newList , oldValue ); // We might be racing against another thread converting the single into // a list, or we might be racing against task completion, so resample "list" // below. // m_continuationObject is guaranteed at this point to be either a List or // s_taskCompletionSentinel. List < object > list = m_continuationObject as List < object >; Contract . Assert (( list != null ) || ( m_continuationObject == s_taskCompletionSentinel ), "Expected m_continuationObject to be list or sentinel" ); // If list is null, it can only mean that s_taskCompletionSentinel has been exchanged // into m_continuationObject. Thus, the task has completed and we should return false // from this method, as we will not be queuing up the continuation. if ( list != null ) lock ( list ) // It is possible for the task to complete right after we snap the copy of // the list. If so, then fall through and return false without queuing the // continuation. if ( m_continuationObject != s_taskCompletionSentinel ) // Before growing the list we remove possible null entries that are the // result from RemoveContinuations() if ( list . Count == list . Capacity ) list . RemoveAll ( s_IsTaskContinuationNullPredicate ); if ( addBeforeOthers ) list . Insert (0, tc ); list . Add ( tc ); return true ; // continuation successfully queued, so return true. // We didn't succeed in queuing the continuation, so return false. return false ; // Record a continuation task or action. // Return true if and only if we successfully queued a continuation. private bool AddTaskContinuation ( object tc , bool addBeforeOthers ) Contract . Requires ( tc != null ); // Make sure that, if someone calls ContinueWith() right after waiting for the predecessor to complete, // we don't queue up a continuation. if ( IsCompleted ) return false ; // Try to just jam tc into m_continuationObject if (( m_continuationObject != null ) || ( Interlocked . CompareExchange ( ref m_continuationObject , tc , null ) != null )) // If we get here, it means that we failed to CAS tc into m_continuationObject. // Therefore, we must go the more complicated route. return AddTaskContinuationComplex ( tc , addBeforeOthers ); else return true ; // Removes a continuation task from m_continuations internal void RemoveContinuation ( object continuationObject ) // could be TaskContinuation or Action<Task> // We need to snap a local reference to m_continuations since reading a volatile object is more costly. // Also to prevent the value to be changed as result of a race condition with another method. object continuationsLocalRef = m_continuationObject ; // Task is completed. Nothing to do here. if ( continuationsLocalRef == s_taskCompletionSentinel ) return ; List < object > continuationsLocalListRef = continuationsLocalRef as List < object >; if ( continuationsLocalListRef == null ) // 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. // Note we cannot go back to a null state, since it will mess up the AddTaskContinuation logic. if ( Interlocked . CompareExchange ( ref m_continuationObject , new List < object >(), continuationObject ) != continuationObject ) // If we fail it means that either AddContinuationComplex won the race condition and m_continuationObject is now a List // that contains the element we want to remove. Or FinishContinuations set the s_taskCompletionSentinel. // So we should try to get a list one more time continuationsLocalListRef = m_continuationObject as List < object >; // Exchange was successful so we can skip the last comparison return ; // if continuationsLocalRef == null it means s_taskCompletionSentinel has been set already and there is nothing else to do. if ( continuationsLocalListRef != null ) lock ( continuationsLocalListRef ) // There is a small chance that this task completed since we took a local snapshot into // continuationsLocalRef. In that case, just return; we don't want to be manipulating the // continuation list as it is being processed. if ( m_continuationObject == s_taskCompletionSentinel ) return ; // Find continuationObject in the continuation list int index = continuationsLocalListRef . IndexOf ( continuationObject ); if ( index != -1) // null out that TaskContinuation entry, which will be interpreted as "to be cleaned up" continuationsLocalListRef [ index ] = null ; // statically allocated delegate for the RemoveAll expression in RemoveContinuations() and AddContinuationComplex() private readonly static Predicate < object > s_IsTaskContinuationNullPredicate = new Predicate < object >(( tc ) => { return ( tc == null ); }); // Wait methods /// < summary > /// Waits for all of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < param name = " tasks " > /// An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > /// < exception cref = " T:System.AggregateException " > /// At least one of the < see cref = " Task " /> instances was canceled -or- an exception was thrown during /// the execution of at least one of the < see cref = " Task " /> instances. /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static void WaitAll ( params Task [] tasks ) # if DEBUG bool waitResult = # endif WaitAll ( tasks , Timeout . Infinite ); # if DEBUG Contract.Assert(waitResult, "expected wait to succeed"); # endif /// < summary > /// Waits for all of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < returns > /// true if all of the < see cref = " Task " /> instances completed execution within the allotted time; /// otherwise, false. /// </ returns > /// < param name = " tasks " > /// An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < param name = " timeout " > /// A < see cref = " System. TimeSpan " /> that represents the number of milliseconds to wait, or a < see /// cref = " System. TimeSpan " /> that represents -1 milliseconds to wait indefinitely. /// </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > /// < exception cref = " T:System.AggregateException " > /// At least one of the < see cref = " Task " /> instances was canceled -or- an exception was thrown during /// the execution of at least one of the < see cref = " Task " /> instances. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// < paramref name = " timeout " /> is a negative number other than -1 milliseconds, which represents an /// infinite time-out -or- timeout is greater than /// < see cref = " System. Int32 . MaxValue " /> . /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static bool WaitAll ( Task [] tasks , TimeSpan timeout ) long totalMilliseconds = ( long ) timeout . TotalMilliseconds ; if ( totalMilliseconds < -1 || totalMilliseconds > Int32 . MaxValue ) throw new ArgumentOutOfRangeException ( "timeout" ); return WaitAll ( tasks , ( int ) totalMilliseconds ); /// < summary > /// Waits for all of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < returns > /// true if all of the < see cref = " Task " /> instances completed execution within the allotted time; /// otherwise, false. /// </ returns > /// < param name = " millisecondsTimeout " > /// The number of milliseconds to wait, or < see cref = " System.Threading. Timeout . Infinite " /> (-1) to /// wait indefinitely. </ param > /// < param name = " tasks " > An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > /// < exception cref = " T:System.AggregateException " > /// At least one of the < see cref = " Task " /> instances was canceled -or- an exception was thrown during /// the execution of at least one of the < see cref = " Task " /> instances. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// < paramref name = " millisecondsTimeout " /> is a negative number other than -1, which represents an /// infinite time-out. /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static bool WaitAll ( Task [] tasks , int millisecondsTimeout ) return WaitAll ( tasks , millisecondsTimeout , default ( CancellationToken )); /// < summary > /// Waits for all of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < returns > /// true if all of the < see cref = " Task " /> instances completed execution within the allotted time; /// otherwise, false. /// </ returns > /// < param name = " tasks " > /// An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < param name = " cancellationToken " > /// A < see cref = " CancellationToken " /> to observe while waiting for the tasks to complete. /// </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > /// < exception cref = " T:System.AggregateException " > /// At least one of the < see cref = " Task " /> instances was canceled -or- an exception was thrown during /// the execution of at least one of the < see cref = " Task " /> instances. /// </ exception > /// < exception cref = " T:System.OperationCanceledException " > /// The < paramref name = " cancellationToken " /> was canceled. /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static void WaitAll ( Task [] tasks , CancellationToken cancellationToken ) WaitAll ( tasks , Timeout . Infinite , cancellationToken ); /// < summary > /// Waits for all of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < returns > /// true if all of the < see cref = " Task " /> instances completed execution within the allotted time; /// otherwise, false. /// </ returns > /// < param name = " tasks " > /// An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < param name = " millisecondsTimeout " > /// The number of milliseconds to wait, or < see cref = " System.Threading. Timeout . Infinite " /> (-1) to /// wait indefinitely. /// </ param > /// < param name = " cancellationToken " > /// A < see cref = " CancellationToken " /> to observe while waiting for the tasks to complete. /// </ param > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > /// < exception cref = " T:System.AggregateException " > /// At least one of the < see cref = " Task " /> instances was canceled -or- an exception was thrown during /// the execution of at least one of the < see cref = " Task " /> instances. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// < paramref name = " millisecondsTimeout " /> is a negative number other than -1, which represents an /// infinite time-out. /// </ exception > /// < exception cref = " T:System.OperationCanceledException " > /// The < paramref name = " cancellationToken " /> was canceled. /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static bool WaitAll ( Task [] tasks , int millisecondsTimeout , CancellationToken cancellationToken ) if ( tasks == null ) throw new ArgumentNullException ( "tasks" ); if ( millisecondsTimeout < -1) throw new ArgumentOutOfRangeException ( "millisecondsTimeout" ); Contract . EndContractBlock (); cancellationToken . ThrowIfCancellationRequested (); // early check before we make any allocations // In this WaitAll() implementation we have 2 alternate code paths for a task to be handled: // CODEPATH1: skip an already completed task, CODEPATH2: actually wait on tasks // We make sure that the exception behavior of Task.Wait() is replicated the same for tasks handled in either of these codepaths List < Exception > exceptions = null ; List < Task > waitedOnTaskList = null ; List < Task > notificationTasks = null ; // If any of the waited-upon tasks end as Faulted or Canceled, set these to true. bool exceptionSeen = false , cancellationSeen = false ; bool returnValue = true ; // Collects incomplete tasks in "waitedOnTaskList" for ( int i = tasks . Length - 1; i >= 0; i --) Task task = tasks [ i ]; if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_WaitMulti_NullTask" ), "tasks" ); bool taskIsCompleted = task . IsCompleted ; if (! taskIsCompleted ) // try inlining the task only if we have an infinite timeout and an empty cancellation token if ( millisecondsTimeout != Timeout . Infinite || cancellationToken . CanBeCanceled ) // We either didn't attempt inline execution because we had a non-infinite timeout or we had a cancellable token. // In all cases we need to do a full wait on the task (=> add its event into the list.) AddToList ( task , ref waitedOnTaskList , initSize : tasks . Length ); // We are eligible for inlining. If it doesn't work, we'll do a full wait. taskIsCompleted = task . WrappedTryRunInline () && task . IsCompleted ; // A successful TryRunInline doesn't guarantee completion if (! taskIsCompleted ) AddToList ( task , ref waitedOnTaskList , initSize : tasks . Length ); if ( taskIsCompleted ) if ( task . IsFaulted ) exceptionSeen = true ; else if ( task . IsCanceled ) cancellationSeen = true ; if ( task . IsWaitNotificationEnabled ) AddToList ( task , ref notificationTasks , initSize : 1); if ( waitedOnTaskList != null ) // Block waiting for the tasks to complete. returnValue = WaitAllBlockingCore ( waitedOnTaskList , millisecondsTimeout , cancellationToken ); // If the wait didn't time out, ensure exceptions are propagated, and if a debugger is // attached and one of these tasks requires it, that we notify the debugger of a wait completion. if ( returnValue ) // Add any exceptions for this task to the collection, and if it's wait // notification bit is set, store it to operate on at the end. foreach ( var task in waitedOnTaskList ) if ( task . IsFaulted ) exceptionSeen = true ; else if ( task . IsCanceled ) cancellationSeen = true ; if ( task . IsWaitNotificationEnabled ) AddToList ( task , ref notificationTasks , initSize : 1); // We need to prevent the tasks array from being GC'ed until we come out of the wait. // This is necessary so that the Parallel Debugger can traverse it during the long wait and // deduce waiter/waitee relationships GC . KeepAlive ( tasks ); // Now that we're done and about to exit, if the wait completed and if we have // any tasks with a notification bit set, signal the debugger if any requires it. if ( returnValue && notificationTasks != null ) // Loop through each task tha that had its bit set, and notify the debugger // about the first one that requires it. The debugger will reset the bit // for any tasks we don't notify of as soon as we break, so we only need to notify // for one. foreach ( var task in notificationTasks ) if ( task . NotifyDebuggerOfWaitCompletionIfNecessary ()) break ; // If one or more threw exceptions, aggregate and throw them. if ( returnValue && ( exceptionSeen || cancellationSeen )) // If the WaitAll was canceled and tasks were canceled but not faulted, // prioritize throwing an OCE for canceling the WaitAll over throwing an // AggregateException for all of the canceled Tasks. This helps // to bring determinism to an otherwise non-determistic case of using // the same token to cancel both the WaitAll and the Tasks. if (! exceptionSeen ) cancellationToken . ThrowIfCancellationRequested (); // Now gather up and throw all of the exceptions. foreach ( var task in tasks ) AddExceptionsForCompletedTask ( ref exceptions , task ); Contract . Assert ( exceptions != null , "Should have seen at least one exception" ); throw new AggregateException ( exceptions ); return returnValue ; /// < summary > Adds an element to the list, initializing the list if it's null. </ summary > /// < typeparam name = " T " > Specifies the type of data stored in the list. </ typeparam > /// < param name = " item " > The item to add. </ param > /// < param name = " list " > The list. </ param > /// < param name = " initSize " > The size to which to initialize the list if the list is null. </ param > private static void AddToList < T >( T item , ref List < T > list , int initSize ) if ( list == null ) list = new List < T >( initSize ); list . Add ( item ); /// < summary > Performs a blocking WaitAll on the vetted list of tasks. </ summary > /// < param name = " tasks " > The tasks, which have already been checked and filtered for completion. </ param > /// < param name = " millisecondsTimeout " > The timeout. </ param > /// < param name = " cancellationToken " > The cancellation token. </ param > /// < returns > true if all of the tasks completed; otherwise, false. </ returns > private static bool WaitAllBlockingCore ( List < Task > tasks , int millisecondsTimeout , CancellationToken cancellationToken ) Contract . Assert ( tasks != null , "Expected a non-null list of tasks" ); Contract . Assert ( tasks . Count > 0, "Expected at least one task" ); bool waitCompleted = false ; var mres = new SetOnCountdownMres ( tasks . Count ); foreach ( var task in tasks ) task . AddCompletionAction ( mres , addBeforeOthers : true ); waitCompleted = mres . Wait ( millisecondsTimeout , cancellationToken ); finally if (! waitCompleted ) foreach ( var task in tasks ) if (! task . IsCompleted ) task . RemoveContinuation ( mres ); // It's ok that we don't dispose of the MRES here, as we never // access the MRES' WaitHandle, and thus no finalizable resources // are actually created. We don't always just Dispose it because // a continuation that's accessing the MRES could still be executing. return waitCompleted ; // A ManualResetEventSlim that will get Set after Invoke is called count times. // This allows us to replace this logic: // var mres = new ManualResetEventSlim(tasks.Count); // Action<Task> completionAction = delegate { if(Interlocked.Decrement(ref count) == 0) mres.Set(); }; // foreach(var task in tasks) task.AddCompletionAction(completionAction); // with this logic: // var mres = new SetOnCountdownMres(tasks.Count); // foreach(var task in tasks) task.AddCompletionAction(mres); // which saves a couple of allocations. // Used in WaitAllBlockingCore (above). private sealed class SetOnCountdownMres : ManualResetEventSlim , ITaskCompletionAction private int _count ; internal SetOnCountdownMres ( int count ) Contract . Assert ( count > 0, "Expected count > 0" ); _count = count ; public void Invoke ( Task completingTask ) if ( Interlocked . Decrement ( ref _count ) == 0) Set (); Contract . Assert ( _count >= 0, "Count should never go below 0" ); /// < summary > /// Internal WaitAll implementation which is meant to be used with small number of tasks, /// optimized for Parallel.Invoke and other structured primitives. /// </ summary > internal static void FastWaitAll ( Task [] tasks ) Contract . Requires ( tasks != null ); List < Exception > exceptions = null ; // Collects incomplete tasks in "waitedOnTaskList" and their cooperative events in "cooperativeEventList" for ( int i = tasks . Length - 1; i >= 0; i --) if (! tasks [ i ]. IsCompleted ) // Just attempting to inline here... result doesn't matter. // We'll do a second pass to do actual wait on each task, and to aggregate their exceptions. // If the task is inlined here, it will register as IsCompleted in the second pass // and will just give us the exception. tasks [ i ]. WrappedTryRunInline (); // Wait on the tasks. for ( int i = tasks . Length - 1; i >= 0; i --) var task = tasks [ i ]; task . SpinThenBlockingWait ( Timeout . Infinite , default ( CancellationToken )); AddExceptionsForCompletedTask ( ref exceptions , task ); // Note that unlike other wait code paths, we do not check // task.NotifyDebuggerOfWaitCompletionIfNecessary() here, because this method is currently // only used from contexts where the tasks couldn't have that bit set, namely // Parallel.Invoke. If that ever changes, such checks should be added here. // If one or more threw exceptions, aggregate them. if ( exceptions != null ) throw new AggregateException ( exceptions ); /// < summary > /// This internal function is only meant to be called by WaitAll() /// If the completed task is canceled or it has other exceptions, here we will add those /// into the passed in exception list (which will be lazily initialized here). /// </ summary > internal static void AddExceptionsForCompletedTask ( ref List < Exception > exceptions , Task t ) AggregateException ex = t . GetExceptions ( true ); if ( ex != null ) // make sure the task's exception observed status is set appropriately // it's possible that WaitAll was called by the parent of an attached child, // this will make sure it won't throw again in the implicit wait t . UpdateExceptionObservedStatus (); if ( exceptions == null ) exceptions = new List < Exception >( ex . InnerExceptions . Count ); exceptions . AddRange ( ex . InnerExceptions ); /// < summary > /// Waits for any of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < param name = " tasks " > /// An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < returns > The index of the completed task in the < paramref name = " tasks " /> array argument. </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static int WaitAny ( params Task [] tasks ) int waitResult = WaitAny ( tasks , Timeout . Infinite ); Contract . Assert ( tasks . Length == 0 || waitResult != -1, "expected wait to succeed" ); return waitResult ; /// < summary > /// Waits for any of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < param name = " tasks " > /// An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < param name = " timeout " > /// A < see cref = " System. TimeSpan " /> that represents the number of milliseconds to wait, or a < see /// cref = " System. TimeSpan " /> that represents -1 milliseconds to wait indefinitely. /// </ param > /// < returns > /// The index of the completed task in the < paramref name = " tasks " /> array argument, or -1 if the /// timeout occurred. /// </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// < paramref name = " timeout " /> is a negative number other than -1 milliseconds, which represents an /// infinite time-out -or- timeout is greater than /// < see cref = " System. Int32 . MaxValue " /> . /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static int WaitAny ( Task [] tasks , TimeSpan timeout ) long totalMilliseconds = ( long ) timeout . TotalMilliseconds ; if ( totalMilliseconds < -1 || totalMilliseconds > Int32 . MaxValue ) throw new ArgumentOutOfRangeException ( "timeout" ); return WaitAny ( tasks , ( int ) totalMilliseconds ); /// < summary > /// Waits for any of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < param name = " tasks " > /// An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < param name = " cancellationToken " > /// A < see cref = " CancellationToken " /> to observe while waiting for a task to complete. /// </ param > /// < returns > /// The index of the completed task in the < paramref name = " tasks " /> array argument. /// </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > /// < exception cref = " T:System.OperationCanceledException " > /// The < paramref name = " cancellationToken " /> was canceled. /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static int WaitAny ( Task [] tasks , CancellationToken cancellationToken ) return WaitAny ( tasks , Timeout . Infinite , cancellationToken ); /// < summary > /// Waits for any of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < param name = " tasks " > /// An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < param name = " millisecondsTimeout " > /// The number of milliseconds to wait, or < see cref = " System.Threading. Timeout . Infinite " /> (-1) to /// wait indefinitely. /// </ param > /// < returns > /// The index of the completed task in the < paramref name = " tasks " /> array argument, or -1 if the /// timeout occurred. /// </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// < paramref name = " millisecondsTimeout " /> is a negative number other than -1, which represents an /// infinite time-out. /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static int WaitAny ( Task [] tasks , int millisecondsTimeout ) return WaitAny ( tasks , millisecondsTimeout , default ( CancellationToken )); /// < summary > /// Waits for any of the provided < see cref = " Task " /> objects to complete execution. /// </ summary > /// < param name = " tasks " > /// An array of < see cref = " Task " /> instances on which to wait. /// </ param > /// < param name = " millisecondsTimeout " > /// The number of milliseconds to wait, or < see cref = " System.Threading. Timeout . Infinite " /> (-1) to /// wait indefinitely. /// </ param > /// < param name = " cancellationToken " > /// A < see cref = " CancellationToken " /> to observe while waiting for a task to complete. /// </ param > /// < returns > /// The index of the completed task in the < paramref name = " tasks " /> array argument, or -1 if the /// timeout occurred. /// </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument is null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> argument contains a null element. /// </ exception > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// < paramref name = " millisecondsTimeout " /> is a negative number other than -1, which represents an /// infinite time-out. /// </ exception > /// < exception cref = " T:System.OperationCanceledException " > /// The < paramref name = " cancellationToken " /> was canceled. /// </ exception > [ MethodImpl ( MethodImplOptions . NoOptimization )] // this is needed for the parallel debugger public static int WaitAny ( Task [] tasks , int millisecondsTimeout , CancellationToken cancellationToken ) if ( tasks == null ) throw new ArgumentNullException ( "tasks" ); if ( millisecondsTimeout < -1) throw new ArgumentOutOfRangeException ( "millisecondsTimeout" ); Contract . EndContractBlock (); cancellationToken . ThrowIfCancellationRequested (); // early check before we make any allocations int signaledTaskIndex = -1; // Make a pass through the loop to check for any tasks that may have // already been completed, and to verify that no tasks are null. for ( int taskIndex = 0; taskIndex < tasks . Length ; taskIndex ++) Task task = tasks [ taskIndex ]; if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_WaitMulti_NullTask" ), "tasks" ); if ( signaledTaskIndex == -1 && task . IsCompleted ) // We found our first completed task. Store it, but we can't just return here, // as we still need to validate the whole array for nulls. signaledTaskIndex = taskIndex ; if ( signaledTaskIndex == -1 && tasks . Length != 0) Task < Task > firstCompleted = TaskFactory . CommonCWAnyLogic ( tasks ); bool waitCompleted = firstCompleted . Wait ( millisecondsTimeout , cancellationToken ); if ( waitCompleted ) Contract . Assert ( firstCompleted . Status == TaskStatus . RanToCompletion ); signaledTaskIndex = Array . IndexOf ( tasks , firstCompleted . Result ); Contract . Assert ( signaledTaskIndex >= 0); // We need to prevent the tasks array from being GC'ed until we come out of the wait. // This is necessary so that the Parallel Debugger can traverse it during the long wait // and deduce waiter/waitee relationships GC . KeepAlive ( tasks ); // Return the index return signaledTaskIndex ; # region FromResult / FromException / FromCancellation /// < summary > Creates a < see cref = " Task { TResult } " /> that's completed successfully with the specified result. </ summary > /// < typeparam name = " TResult " > The type of the result returned by the task. </ typeparam > /// < param name = " result " > The result to store into the completed task. </ param > /// < returns > The successfully completed task. </ returns > public static Task < TResult > FromResult < TResult >( TResult result ) return new Task < TResult >( result ); /// < summary > Creates a < see cref = " Task { TResult } " /> that's completed exceptionally with the specified exception. </ summary > /// < typeparam name = " TResult " > The type of the result returned by the task. </ typeparam > /// < param name = " exception " > The exception with which to complete the task. </ param > /// < returns > The faulted task. </ returns > public static Task FromException ( Exception exception ) return FromException < VoidTaskResult >( exception ); /// < summary > Creates a < see cref = " Task { TResult } " /> that's completed exceptionally with the specified exception. </ summary > /// < typeparam name = " TResult " > The type of the result returned by the task. </ typeparam > /// < param name = " exception " > The exception with which to complete the task. </ param > /// < returns > The faulted task. </ returns > public static Task < TResult > FromException < TResult >( Exception exception ) if ( exception == null ) throw new ArgumentNullException ( "exception" ); Contract . EndContractBlock (); var task = new Task < TResult >(); bool succeeded = task . TrySetException ( exception ); Contract . Assert ( succeeded , "This should always succeed on a new task." ); return task ; /// < summary > Creates a < see cref = " Task " /> that's completed due to cancellation with the specified token. </ summary > /// < param name = " cancellationToken " > The token with which to complete the task. </ param > /// < returns > The canceled task. </ returns > [ FriendAccessAllowed ] internal static Task FromCancellation ( CancellationToken cancellationToken ) if (! cancellationToken . IsCancellationRequested ) throw new ArgumentOutOfRangeException ( "cancellationToken" ); Contract . EndContractBlock (); return new Task ( true , TaskCreationOptions . None , cancellationToken ); /// < summary > Creates a < see cref = " Task " /> that's completed due to cancellation with the specified token. </ summary > /// < param name = " cancellationToken " > The token with which to complete the task. </ param > /// < returns > The canceled task. </ returns > public static Task FromCanceled ( CancellationToken cancellationToken ) return FromCancellation ( cancellationToken ); /// < summary > Creates a < see cref = " Task { TResult } " /> that's completed due to cancellation with the specified token. </ summary > /// < typeparam name = " TResult " > The type of the result returned by the task. </ typeparam > /// < param name = " cancellationToken " > The token with which to complete the task. </ param > /// < returns > The canceled task. </ returns > [ FriendAccessAllowed ] internal static Task < TResult > FromCancellation < TResult >( CancellationToken cancellationToken ) if (! cancellationToken . IsCancellationRequested ) throw new ArgumentOutOfRangeException ( "cancellationToken" ); Contract . EndContractBlock (); return new Task < TResult >( true , default ( TResult ), TaskCreationOptions . None , cancellationToken ); /// < summary > Creates a < see cref = " Task { TResult } " /> that's completed due to cancellation with the specified token. </ summary > /// < typeparam name = " TResult " > The type of the result returned by the task. </ typeparam > /// < param name = " cancellationToken " > The token with which to complete the task. </ param > /// < returns > The canceled task. </ returns > public static Task < TResult > FromCanceled < TResult >( CancellationToken cancellationToken ) return FromCancellation < TResult >( cancellationToken ); /// < summary > Creates a < see cref = " Task { TResult } " /> that's completed due to cancellation with the specified exception. </ summary > /// < typeparam name = " TResult " > The type of the result returned by the task. </ typeparam > /// < param name = " exception " > The exception with which to complete the task. </ param > /// < returns > The canceled task. </ returns > internal static Task < TResult > FromCancellation < TResult >( OperationCanceledException exception ) if ( exception == null ) throw new ArgumentNullException ( "exception" ); Contract . EndContractBlock (); var task = new Task < TResult >(); bool succeeded = task . TrySetCanceled ( exception . CancellationToken , exception ); Contract . Assert ( succeeded , "This should always succeed on a new task." ); return task ; # endregion # region Run methods /// < summary > /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work. /// </ summary > /// < param name = " action " > The work to execute asynchronously </ param > /// < returns > A Task that represents the work queued to execute in the ThreadPool. </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " action " /> parameter was null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public static Task Run ( Action action ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return Task . InternalStartNew ( null , action , null , default ( CancellationToken ), TaskScheduler . Default , TaskCreationOptions . DenyChildAttach , InternalTaskOptions . None , ref stackMark ); /// < summary > /// Queues the specified work to run on the ThreadPool and returns a Task handle for that work. /// </ summary > /// < param name = " action " > The work to execute asynchronously </ param > /// < param name = " cancellationToken " > A cancellation token that should be used to cancel the work </ param > /// < returns > A Task that represents the work queued to execute in the ThreadPool. </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " action " /> parameter was null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > /// The < see cref = " T:System.CancellationTokenSource " /> associated with < paramref name = " cancellationToken " /> was disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public static Task Run ( Action action , CancellationToken cancellationToken ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return Task . InternalStartNew ( null , action , null , cancellationToken , TaskScheduler . Default , TaskCreationOptions . DenyChildAttach , InternalTaskOptions . None , ref stackMark ); /// < summary > /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work. /// </ summary > /// < param name = " function " > The work to execute asynchronously </ param > /// < returns > A Task(TResult) that represents the work queued to execute in the ThreadPool. </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " function " /> parameter was null. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public static Task < TResult > Run < TResult >( Func < TResult > function ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return Task < TResult >. StartNew ( null , function , default ( CancellationToken ), TaskCreationOptions . DenyChildAttach , InternalTaskOptions . None , TaskScheduler . Default , ref stackMark ); /// < summary > /// Queues the specified work to run on the ThreadPool and returns a Task(TResult) handle for that work. /// </ summary > /// < param name = " function " > The work to execute asynchronously </ param > /// < param name = " cancellationToken " > A cancellation token that should be used to cancel the work </ param > /// < returns > A Task(TResult) that represents the work queued to execute in the ThreadPool. </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " function " /> parameter was null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > /// The < see cref = " T:System.CancellationTokenSource " /> associated with < paramref name = " cancellationToken " /> was disposed. /// </ exception > [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable public static Task < TResult > Run < TResult >( Func < TResult > function , CancellationToken cancellationToken ) StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; return Task < TResult >. StartNew ( null , function , cancellationToken , TaskCreationOptions . DenyChildAttach , InternalTaskOptions . None , TaskScheduler . Default , ref stackMark ); /// < summary > /// Queues the specified work to run on the ThreadPool and returns a proxy for the /// Task returned by < paramref name = " function " /> . /// </ summary > /// < param name = " function " > The work to execute asynchronously </ param > /// < returns > A Task that represents a proxy for the Task returned by < paramref name = " function " /> . </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " function " /> parameter was null. /// </ exception > public static Task Run ( Func < Task > function ) return Run ( function , default ( CancellationToken )); /// < summary > /// Queues the specified work to run on the ThreadPool and returns a proxy for the /// Task returned by < paramref name = " function " /> . /// </ summary > /// < param name = " function " > The work to execute asynchronously </ param > /// < param name = " cancellationToken " > A cancellation token that should be used to cancel the work </ param > /// < returns > A Task that represents a proxy for the Task returned by < paramref name = " function " /> . </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " function " /> parameter was null. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > /// The < see cref = " T:System.CancellationTokenSource " /> associated with < paramref name = " cancellationToken " /> was disposed. /// </ exception > public static Task Run ( Func < Task > function , CancellationToken cancellationToken ) // Check arguments if ( function == null ) throw new ArgumentNullException ( "function" ); Contract . EndContractBlock (); if ( AppContextSwitches . ThrowExceptionIfDisposedCancellationTokenSource ) cancellationToken . ThrowIfSourceDisposed (); // Short-circuit if we are given a pre-canceled token if ( cancellationToken . IsCancellationRequested ) return Task . FromCancellation ( cancellationToken ); // Kick off initial Task, which will call the user-supplied function and yield a Task. Task < Task > task1 = Task < Task >. Factory . StartNew ( function , cancellationToken , TaskCreationOptions . DenyChildAttach , TaskScheduler . Default ); // Create a promise-style Task to be used as a proxy for the operation // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation. UnwrapPromise < VoidTaskResult > promise = new UnwrapPromise < VoidTaskResult >( task1 , lookForOce : true ); return promise ; /// < summary > /// Queues the specified work to run on the ThreadPool and returns a proxy for the /// Task(TResult) returned by < paramref name = " function " /> . /// </ summary > /// < typeparam name = " TResult " > The type of the result returned by the proxy Task. </ typeparam > /// < param name = " function " > The work to execute asynchronously </ param > /// < returns > A Task(TResult) that represents a proxy for the Task(TResult) returned by < paramref name = " function " /> . </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " function " /> parameter was null. /// </ exception > public static Task < TResult > Run < TResult >( Func < Task < TResult >> function ) return Run ( function , default ( CancellationToken )); /// < summary > /// Queues the specified work to run on the ThreadPool and returns a proxy for the /// Task(TResult) returned by < paramref name = " function " /> . /// </ summary > /// < typeparam name = " TResult " > The type of the result returned by the proxy Task. </ typeparam > /// < param name = " function " > The work to execute asynchronously </ param > /// < param name = " cancellationToken " > A cancellation token that should be used to cancel the work </ param > /// < returns > A Task(TResult) that represents a proxy for the Task(TResult) returned by < paramref name = " function " /> . </ returns > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " function " /> parameter was null. /// </ exception > public static Task < TResult > Run < TResult >( Func < Task < TResult >> function , CancellationToken cancellationToken ) // Check arguments if ( function == null ) throw new ArgumentNullException ( "function" ); Contract . EndContractBlock (); if ( AppContextSwitches . ThrowExceptionIfDisposedCancellationTokenSource ) cancellationToken . ThrowIfSourceDisposed (); // Short-circuit if we are given a pre-canceled token if ( cancellationToken . IsCancellationRequested ) return Task . FromCancellation < TResult >( cancellationToken ); // Kick off initial Task, which will call the user-supplied function and yield a Task. Task < Task < TResult >> task1 = Task < Task < TResult >>. Factory . StartNew ( function , cancellationToken , TaskCreationOptions . DenyChildAttach , TaskScheduler . Default ); // Create a promise-style Task to be used as a proxy for the operation // Set lookForOce == true so that unwrap logic can be on the lookout for OCEs thrown as faults from task1, to support in-delegate cancellation. UnwrapPromise < TResult > promise = new UnwrapPromise < TResult >( task1 , lookForOce : true ); return promise ; # endregion # region Delay methods /// < summary > /// Creates a Task that will complete after a time delay. /// </ summary > /// < param name = " delay " > The time span to wait before completing the returned Task </ param > /// < returns > A Task that represents the time delay </ returns > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " delay " /> is less than -1 or greater than Int32.MaxValue. /// </ exception > /// < remarks > /// After the specified time delay, the Task is completed in RanToCompletion state. /// </ remarks > public static Task Delay ( TimeSpan delay ) return Delay ( delay , default ( CancellationToken )); /// < summary > /// Creates a Task that will complete after a time delay. /// </ summary > /// < param name = " delay " > The time span to wait before completing the returned Task </ param > /// < param name = " cancellationToken " > The cancellation token that will be checked prior to completing the returned Task </ param > /// < returns > A Task that represents the time delay </ returns > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " delay " /> is less than -1 or greater than Int32.MaxValue. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > /// The provided < paramref name = " cancellationToken " /> has already been disposed. /// </ exception > /// < remarks > /// If the cancellation token is signaled before the specified time delay, then the Task is completed in /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time /// delay has expired. /// </ remarks > public static Task Delay ( TimeSpan delay , CancellationToken cancellationToken ) long totalMilliseconds = ( long ) delay . TotalMilliseconds ; if ( totalMilliseconds < -1 || totalMilliseconds > Int32 . MaxValue ) throw new ArgumentOutOfRangeException ( "delay" , Environment . GetResourceString ( "Task_Delay_InvalidDelay" )); return Delay (( int ) totalMilliseconds , cancellationToken ); /// < summary > /// Creates a Task that will complete after a time delay. /// </ summary > /// < param name = " millisecondsDelay " > The number of milliseconds to wait before completing the returned Task </ param > /// < returns > A Task that represents the time delay </ returns > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " millisecondsDelay " /> is less than -1. /// </ exception > /// < remarks > /// After the specified time delay, the Task is completed in RanToCompletion state. /// </ remarks > public static Task Delay ( int millisecondsDelay ) return Delay ( millisecondsDelay , default ( CancellationToken )); /// < summary > /// Creates a Task that will complete after a time delay. /// </ summary > /// < param name = " millisecondsDelay " > The number of milliseconds to wait before completing the returned Task </ param > /// < param name = " cancellationToken " > The cancellation token that will be checked prior to completing the returned Task </ param > /// < returns > A Task that represents the time delay </ returns > /// < exception cref = " T:System.ArgumentOutOfRangeException " > /// The < paramref name = " millisecondsDelay " /> is less than -1. /// </ exception > /// < exception cref = " T:System.ObjectDisposedException " > /// The provided < paramref name = " cancellationToken " /> has already been disposed. /// </ exception > /// < remarks > /// If the cancellation token is signaled before the specified time delay, then the Task is completed in /// Canceled state. Otherwise, the Task is completed in RanToCompletion state once the specified time /// delay has expired. /// </ remarks > public static Task Delay ( int millisecondsDelay , CancellationToken cancellationToken ) // Throw on non-sensical time if ( millisecondsDelay < -1) throw new ArgumentOutOfRangeException ( "millisecondsDelay" , Environment . GetResourceString ( "Task_Delay_InvalidMillisecondsDelay" )); Contract . EndContractBlock (); // some short-cuts in case quick completion is in order if ( cancellationToken . IsCancellationRequested ) // return a Task created as already-Canceled return Task . FromCancellation ( cancellationToken ); else if ( millisecondsDelay == 0) // return a Task created as already-RanToCompletion return Task . CompletedTask ; // Construct a promise-style Task to encapsulate our return value var promise = new DelayPromise ( cancellationToken ); // Register our cancellation token, if necessary. if ( cancellationToken . CanBeCanceled ) promise . Registration = cancellationToken . InternalRegisterWithoutEC ( state => (( DelayPromise ) state ). Complete (), promise ); // ... and create our timer and make sure that it stays rooted. if ( millisecondsDelay != Timeout . Infinite ) // no need to create the timer if it's an infinite timeout promise . Timer = new Timer ( state => (( DelayPromise ) state ). Complete (), promise , millisecondsDelay , Timeout . Infinite ); promise . Timer . KeepRootedWhileScheduled (); // Return the timer proxy task return promise ; /// < summary > Task that also stores the completion closure and logic for Task.Delay implementation. </ summary > private sealed class DelayPromise : Task < VoidTaskResult > internal DelayPromise ( CancellationToken token ) : base () this . Token = token ; if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCreation ( CausalityTraceLevel . Required , this . Id , "Task.Delay" , 0); if ( Task . s_asyncDebuggingEnabled ) AddToActiveTasks ( this ); internal readonly CancellationToken Token ; internal CancellationTokenRegistration Registration ; internal Timer Timer ; internal void Complete () // Transition the task to completed. bool setSucceeded ; if ( Token . IsCancellationRequested ) setSucceeded = TrySetCanceled ( Token ); if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCompletion ( CausalityTraceLevel . Required , this . Id , AsyncCausalityStatus . Completed ); if ( Task . s_asyncDebuggingEnabled ) RemoveFromActiveTasks ( this . Id ); setSucceeded = TrySetResult ( default ( VoidTaskResult )); // If we won the ----, also clean up. if ( setSucceeded ) if ( Timer != null ) Timer . Dispose (); Registration . Dispose (); # endregion # region WhenAll /// < summary > /// Creates a task that will complete when all of the supplied tasks have completed. /// </ summary > /// < param name = " tasks " > The tasks to wait on for completion. </ param > /// < returns > A task that represents the completion of all of the supplied tasks. </ returns > /// < remarks > /// < para > /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks. /// </ para > /// < para > /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state. /// </ para > /// < para > /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. /// </ para > /// < para > /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion /// state before it's returned to the caller. /// </ para > /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument was null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> collection contained a null task. /// </ exception > public static Task WhenAll ( IEnumerable < Task > tasks ) // Take a more efficient path if tasks is actually an array Task [] taskArray = tasks as Task []; if ( taskArray != null ) return WhenAll ( taskArray ); // Skip a List allocation/copy if tasks is a collection ICollection < Task > taskCollection = tasks as ICollection < Task >; if ( taskCollection != null ) int index = 0; taskArray = new Task [ taskCollection . Count ]; foreach ( var task in tasks ) if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_NullTask" ), "tasks" ); taskArray [ index ++] = task ; return InternalWhenAll ( taskArray ); // Do some argument checking and convert tasks to a List (and later an array). if ( tasks == null ) throw new ArgumentNullException ( "tasks" ); List < Task > taskList = new List < Task >(); foreach ( Task task in tasks ) if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_NullTask" ), "tasks" ); taskList . Add ( task ); // Delegate the rest to InternalWhenAll() return InternalWhenAll ( taskList . ToArray ()); /// < summary > /// Creates a task that will complete when all of the supplied tasks have completed. /// </ summary > /// < param name = " tasks " > The tasks to wait on for completion. </ param > /// < returns > A task that represents the completion of all of the supplied tasks. </ returns > /// < remarks > /// < para > /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks. /// </ para > /// < para > /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state. /// </ para > /// < para > /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. /// </ para > /// < para > /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion /// state before it's returned to the caller. /// </ para > /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument was null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> array contained a null task. /// </ exception > public static Task WhenAll ( params Task [] tasks ) // Do some argument checking and make a defensive copy of the tasks array if ( tasks == null ) throw new ArgumentNullException ( "tasks" ); Contract . EndContractBlock (); int taskCount = tasks . Length ; if ( taskCount == 0) return InternalWhenAll ( tasks ); // Small optimization in the case of an empty array. Task [] tasksCopy = new Task [ taskCount ]; for ( int i = 0; i < taskCount ; i ++) Task task = tasks [ i ]; if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_NullTask" ), "tasks" ); tasksCopy [ i ] = task ; // The rest can be delegated to InternalWhenAll() return InternalWhenAll ( tasksCopy ); // Some common logic to support WhenAll() methods // tasks should be a defensive copy. private static Task InternalWhenAll ( Task [] tasks ) Contract . Requires ( tasks != null , "Expected a non-null tasks array" ); return ( tasks . Length == 0) ? // take shortcut if there are no tasks upon which to wait Task . CompletedTask : new WhenAllPromise ( tasks ); // A Task<VoidTaskResult> that gets completed when all of its constituent tasks complete. // Completion logic will analyze the antecedents in order to choose completion status. // This type allows us to replace this logic: // Task<VoidTaskResult> promise = new Task<VoidTaskResult>(...); // Action<Task> completionAction = delegate { <completion logic>}; // TaskFactory.CommonCWAllLogic(tasksCopy).AddCompletionAction(completionAction); // return promise; // which involves several allocations, with this logic: // return new WhenAllPromise(tasksCopy); // which saves a couple of allocations and enables debugger notification specialization. // Used in InternalWhenAll(Task[]) private sealed class WhenAllPromise : Task < VoidTaskResult >, ITaskCompletionAction /// < summary > /// Stores all of the constituent tasks. Tasks clear themselves out of this /// array as they complete, but only if they don't have their wait notification bit set. /// </ summary > private readonly Task [] m_tasks ; /// < summary > The number of tasks remaining to complete. </ summary > private int m_count ; internal WhenAllPromise ( Task [] tasks ) : base () Contract . Requires ( tasks != null , "Expected a non-null task array" ); Contract . Requires ( tasks . Length > 0, "Expected a non-zero length task array" ); if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCreation ( CausalityTraceLevel . Required , this . Id , "Task.WhenAll" , 0); if ( s_asyncDebuggingEnabled ) AddToActiveTasks ( this ); m_tasks = tasks ; m_count = tasks . Length ; foreach ( var task in tasks ) if ( task . IsCompleted ) this . Invoke ( task ); // short-circuit the completion action, if possible else task . AddCompletionAction ( this ); // simple completion action public void Invoke ( Task completedTask ) if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationRelation ( CausalityTraceLevel . Important , this . Id , CausalityRelation . Join ); // Decrement the count, and only continue to complete the promise if we're the last one. if ( Interlocked . Decrement ( ref m_count ) == 0) // Set up some accounting variables List < ExceptionDispatchInfo > observedExceptions = null ; Task canceledTask = null ; // Loop through antecedents: // If any one of them faults, the result will be faulted // If none fault, but at least one is canceled, the result will be canceled // If none fault or are canceled, then result will be RanToCompletion for ( int i = 0; i < m_tasks . Length ; i ++) var task = m_tasks [ i ]; Contract . Assert ( task != null , "Constituent task in WhenAll should never be null" ); if ( task . IsFaulted ) if ( observedExceptions == null ) observedExceptions = new List < ExceptionDispatchInfo >(); observedExceptions . AddRange ( task . GetExceptionDispatchInfos ()); else if ( task . IsCanceled ) if ( canceledTask == null ) canceledTask = task ; // use the first task that's canceled // Regardless of completion state, if the task has its debug bit set, transfer it to the // WhenAll task. We must do this before we complete the task. if ( task . IsWaitNotificationEnabled ) this . SetNotificationForWaitCompletion ( enabled : true ); else m_tasks [ i ] = null ; // avoid holding onto tasks unnecessarily if ( observedExceptions != null ) Contract . Assert ( observedExceptions . Count > 0, "Expected at least one exception" ); //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there TrySetException ( observedExceptions ); else if ( canceledTask != null ) TrySetCanceled ( canceledTask . CancellationToken , canceledTask . GetCancellationExceptionDispatchInfo ()); if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCompletion ( CausalityTraceLevel . Required , this . Id , AsyncCausalityStatus . Completed ); if ( Task . s_asyncDebuggingEnabled ) RemoveFromActiveTasks ( this . Id ); TrySetResult ( default ( VoidTaskResult )); Contract . Assert ( m_count >= 0, "Count should never go below 0" ); /// < summary > /// Returns whether we should notify the debugger of a wait completion. This returns /// true iff at least one constituent task has its bit set. /// </ summary > internal override bool ShouldNotifyDebuggerOfWaitCompletion return base . ShouldNotifyDebuggerOfWaitCompletion && Task . AnyTaskRequiresNotifyDebuggerOfWaitCompletion ( m_tasks ); /// < summary > /// Creates a task that will complete when all of the supplied tasks have completed. /// </ summary > /// < param name = " tasks " > The tasks to wait on for completion. </ param > /// < returns > A task that represents the completion of all of the supplied tasks. </ returns > /// < remarks > /// < para > /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks. /// </ para > /// < para > /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state. /// </ para > /// < para > /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. /// The Result of the returned task will be set to an array containing all of the results of the /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result). /// </ para > /// < para > /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements. /// </ para > /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument was null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> collection contained a null task. /// </ exception > public static Task < TResult []> WhenAll < TResult >( IEnumerable < Task < TResult >> tasks ) // Take a more efficient route if tasks is actually an array Task < TResult >[] taskArray = tasks as Task < TResult >[]; if ( taskArray != null ) return WhenAll < TResult >( taskArray ); // Skip a List allocation/copy if tasks is a collection ICollection < Task < TResult >> taskCollection = tasks as ICollection < Task < TResult >>; if ( taskCollection != null ) int index = 0; taskArray = new Task < TResult >[ taskCollection . Count ]; foreach ( var task in tasks ) if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_NullTask" ), "tasks" ); taskArray [ index ++] = task ; return InternalWhenAll < TResult >( taskArray ); // Do some argument checking and convert tasks into a List (later an array) if ( tasks == null ) throw new ArgumentNullException ( "tasks" ); List < Task < TResult >> taskList = new List < Task < TResult >>(); foreach ( Task < TResult > task in tasks ) if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_NullTask" ), "tasks" ); taskList . Add ( task ); // Delegate the rest to InternalWhenAll<TResult>(). return InternalWhenAll < TResult >( taskList . ToArray ()); /// < summary > /// Creates a task that will complete when all of the supplied tasks have completed. /// </ summary > /// < param name = " tasks " > The tasks to wait on for completion. </ param > /// < returns > A task that represents the completion of all of the supplied tasks. </ returns > /// < remarks > /// < para > /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks. /// </ para > /// < para > /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state. /// </ para > /// < para > /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. /// The Result of the returned task will be set to an array containing all of the results of the /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result). /// </ para > /// < para > /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements. /// </ para > /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument was null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> array contained a null task. /// </ exception > public static Task < TResult []> WhenAll < TResult >( params Task < TResult >[] tasks ) // Do some argument checking and make a defensive copy of the tasks array if ( tasks == null ) throw new ArgumentNullException ( "tasks" ); Contract . EndContractBlock (); int taskCount = tasks . Length ; if ( taskCount == 0) return InternalWhenAll < TResult >( tasks ); // small optimization in the case of an empty task array Task < TResult >[] tasksCopy = new Task < TResult >[ taskCount ]; for ( int i = 0; i < taskCount ; i ++) Task < TResult > task = tasks [ i ]; if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_NullTask" ), "tasks" ); tasksCopy [ i ] = task ; // Delegate the rest to InternalWhenAll<TResult>() return InternalWhenAll < TResult >( tasksCopy ); // Some common logic to support WhenAll<TResult> methods private static Task < TResult []> InternalWhenAll < TResult >( Task < TResult >[] tasks ) Contract . Requires ( tasks != null , "Expected a non-null tasks array" ); return ( tasks . Length == 0) ? // take shortcut if there are no tasks upon which to wait new Task < TResult []>( false , new TResult [0], TaskCreationOptions . None , default ( CancellationToken )) : new WhenAllPromise < TResult >( tasks ); // A Task<T> that gets completed when all of its constituent tasks complete. // Completion logic will analyze the antecedents in order to choose completion status. // See comments for non-generic version of WhenAllPromise class. // Used in InternalWhenAll<TResult>(Task<TResult>[]) private sealed class WhenAllPromise < T > : Task < T []>, ITaskCompletionAction /// < summary > /// Stores all of the constituent tasks. Tasks clear themselves out of this /// array as they complete, but only if they don't have their wait notification bit set. /// </ summary > private readonly Task < T >[] m_tasks ; /// < summary > The number of tasks remaining to complete. </ summary > private int m_count ; internal WhenAllPromise ( Task < T >[] tasks ) : base () Contract . Requires ( tasks != null , "Expected a non-null task array" ); Contract . Requires ( tasks . Length > 0, "Expected a non-zero length task array" ); m_tasks = tasks ; m_count = tasks . Length ; if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCreation ( CausalityTraceLevel . Required , this . Id , "Task.WhenAll" , 0); if ( s_asyncDebuggingEnabled ) AddToActiveTasks ( this ); foreach ( var task in tasks ) if ( task . IsCompleted ) this . Invoke ( task ); // short-circuit the completion action, if possible else task . AddCompletionAction ( this ); // simple completion action public void Invoke ( Task ignored ) if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationRelation ( CausalityTraceLevel . Important , this . Id , CausalityRelation . Join ); // Decrement the count, and only continue to complete the promise if we're the last one. if ( Interlocked . Decrement ( ref m_count ) == 0) // Set up some accounting variables T [] results = new T [ m_tasks . Length ]; List < ExceptionDispatchInfo > observedExceptions = null ; Task canceledTask = null ; // Loop through antecedents: // If any one of them faults, the result will be faulted // If none fault, but at least one is canceled, the result will be canceled // If none fault or are canceled, then result will be RanToCompletion for ( int i = 0; i < m_tasks . Length ; i ++) Task < T > task = m_tasks [ i ]; Contract . Assert ( task != null , "Constituent task in WhenAll should never be null" ); if ( task . IsFaulted ) if ( observedExceptions == null ) observedExceptions = new List < ExceptionDispatchInfo >(); observedExceptions . AddRange ( task . GetExceptionDispatchInfos ()); else if ( task . IsCanceled ) if ( canceledTask == null ) canceledTask = task ; // use the first task that's canceled Contract . Assert ( task . Status == TaskStatus . RanToCompletion ); results [ i ] = task . GetResultCore ( waitCompletionNotification : false ); // avoid Result, which would triggering debug notification // Regardless of completion state, if the task has its debug bit set, transfer it to the // WhenAll task. We must do this before we complete the task. if ( task . IsWaitNotificationEnabled ) this . SetNotificationForWaitCompletion ( enabled : true ); else m_tasks [ i ] = null ; // avoid holding onto tasks unnecessarily if ( observedExceptions != null ) Contract . Assert ( observedExceptions . Count > 0, "Expected at least one exception" ); //We don't need to TraceOperationCompleted here because TrySetException will call Finish and we'll log it there TrySetException ( observedExceptions ); else if ( canceledTask != null ) TrySetCanceled ( canceledTask . CancellationToken , canceledTask . GetCancellationExceptionDispatchInfo ()); if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCompletion ( CausalityTraceLevel . Required , this . Id , AsyncCausalityStatus . Completed ); if ( Task . s_asyncDebuggingEnabled ) RemoveFromActiveTasks ( this . Id ); TrySetResult ( results ); Contract . Assert ( m_count >= 0, "Count should never go below 0" ); /// < summary > /// Returns whether we should notify the debugger of a wait completion. This returns true /// iff at least one constituent task has its bit set. /// </ summary > internal override bool ShouldNotifyDebuggerOfWaitCompletion return base . ShouldNotifyDebuggerOfWaitCompletion && Task . AnyTaskRequiresNotifyDebuggerOfWaitCompletion ( m_tasks ); # endregion # region WhenAny /// < summary > /// Creates a task that will complete when any of the supplied tasks have completed. /// </ summary > /// < param name = " tasks " > The tasks to wait on for completion. </ param > /// < returns > A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. </ returns > /// < remarks > /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state /// 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. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument was null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> array contained a null task, or was empty. /// </ exception > public static Task < Task > WhenAny ( params Task [] tasks ) if ( tasks == null ) throw new ArgumentNullException ( "tasks" ); if ( tasks . Length == 0) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_EmptyTaskList" ), "tasks" ); Contract . EndContractBlock (); // Make a defensive copy, as the user may manipulate the tasks array // after we return but before the WhenAny asynchronously completes. int taskCount = tasks . Length ; Task [] tasksCopy = new Task [ taskCount ]; for ( int i = 0; i < taskCount ; i ++) Task task = tasks [ i ]; if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_NullTask" ), "tasks" ); tasksCopy [ i ] = task ; // Previously implemented CommonCWAnyLogic() can handle the rest return TaskFactory . CommonCWAnyLogic ( tasksCopy ); /// < summary > /// Creates a task that will complete when any of the supplied tasks have completed. /// </ summary > /// < param name = " tasks " > The tasks to wait on for completion. </ param > /// < returns > A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. </ returns > /// < remarks > /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state /// 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. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument was null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> collection contained a null task, or was empty. /// </ exception > public static Task < Task > WhenAny ( IEnumerable < Task > tasks ) if ( tasks == null ) throw new ArgumentNullException ( "tasks" ); Contract . EndContractBlock (); // Make a defensive copy, as the user may manipulate the tasks collection // after we return but before the WhenAny asynchronously completes. List < Task > taskList = new List < Task >(); foreach ( Task task in tasks ) if ( task == null ) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_NullTask" ), "tasks" ); taskList . Add ( task ); if ( taskList . Count == 0) throw new ArgumentException ( Environment . GetResourceString ( "Task_MultiTaskContinuation_EmptyTaskList" ), "tasks" ); // Previously implemented CommonCWAnyLogic() can handle the rest return TaskFactory . CommonCWAnyLogic ( taskList ); /// < summary > /// Creates a task that will complete when any of the supplied tasks have completed. /// </ summary > /// < param name = " tasks " > The tasks to wait on for completion. </ param > /// < returns > A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. </ returns > /// < remarks > /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state /// 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. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument was null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> array contained a null task, or was empty. /// </ exception > public static Task < Task < TResult >> WhenAny < TResult >( params Task < TResult >[] tasks ) // We would just like to do this: // return (Task<Task<TResult>>) WhenAny( (Task[]) tasks); // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>. // Call WhenAny(Task[]) for basic functionality Task < Task > intermediate = WhenAny (( Task []) tasks ); // Return a continuation task with the correct result type return intermediate . ContinueWith ( Task < TResult >. TaskWhenAnyCast , default ( CancellationToken ), TaskContinuationOptions . ExecuteSynchronously | TaskContinuationOptions . DenyChildAttach , TaskScheduler . Default ); /// < summary > /// Creates a task that will complete when any of the supplied tasks have completed. /// </ summary > /// < param name = " tasks " > The tasks to wait on for completion. </ param > /// < returns > A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. </ returns > /// < remarks > /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state /// 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. /// </ remarks > /// < exception cref = " T:System.ArgumentNullException " > /// The < paramref name = " tasks " /> argument was null. /// </ exception > /// < exception cref = " T:System.ArgumentException " > /// The < paramref name = " tasks " /> collection contained a null task, or was empty. /// </ exception > public static Task < Task < TResult >> WhenAny < TResult >( IEnumerable < Task < TResult >> tasks ) // We would just like to do this: // return (Task<Task<TResult>>) WhenAny( (IEnumerable<Task>) tasks); // but classes are not covariant to enable casting Task<TResult> to Task<Task<TResult>>. // Call WhenAny(IEnumerable<Task>) for basic functionality Task < Task > intermediate = WhenAny (( IEnumerable < Task >) tasks ); // Return a continuation task with the correct result type return intermediate . ContinueWith ( Task < TResult >. TaskWhenAnyCast , default ( CancellationToken ), TaskContinuationOptions . ExecuteSynchronously | TaskContinuationOptions . DenyChildAttach , TaskScheduler . Default ); # endregion [ FriendAccessAllowed ] internal static Task < TResult > CreateUnwrapPromise < TResult >( Task outerTask , bool lookForOce ) Contract . Requires ( outerTask != null ); return new UnwrapPromise < TResult >( outerTask , lookForOce ); internal virtual Delegate [] GetDelegateContinuationsForDebugger () //Avoid an infinite loop by making sure the continuation object is not a reference to istelf. if ( this . m_continuationObject != this ) return GetDelegatesFromContinuationObject ( this . m_continuationObject ); return null; internal static Delegate [] GetDelegatesFromContinuationObject ( object continuationObject ) if ( continuationObject != null ) Action singleAction = continuationObject as Action ; if ( singleAction != null ) return new Delegate [] { AsyncMethodBuilderCore . TryGetStateMachineForDebugger ( singleAction ) }; TaskContinuation taskContinuation = continuationObject as TaskContinuation ; if ( taskContinuation != null ) return taskContinuation . GetDelegateContinuationsForDebugger (); Task continuationTask = continuationObject as Task ; if ( continuationTask != null ) Contract . Assert ( continuationTask . m_action == null ); Delegate [] delegates = continuationTask . GetDelegateContinuationsForDebugger (); if ( delegates != null ) return delegates ; //We need this ITaskCompletionAction after the Task because in the case of UnwrapPromise //the VS debugger is more interested in the continuation than the internal invoke() ITaskCompletionAction singleCompletionAction = continuationObject as ITaskCompletionAction ; if ( singleCompletionAction != null ) return new Delegate [] { new Action < Task >( singleCompletionAction . Invoke ) }; List < object > continuationList = continuationObject as List < object >; if ( continuationList != null ) List < Delegate > result = new List < Delegate >(); foreach ( object obj in continuationList ) var innerDelegates = GetDelegatesFromContinuationObject ( obj ); if ( innerDelegates != null ) foreach ( var del in innerDelegates ) if ( del != null ) result . Add ( del ); return result . ToArray (); return null ; private static Task GetActiveTaskFromId ( int taskId ) Task task = null ; s_currentActiveTasks . TryGetValue ( taskId , out task ); return task ; private static Task [] GetActiveTasks () return new List < Task >( s_currentActiveTasks . Values ). ToArray (); internal sealed class CompletionActionInvoker : IThreadPoolWorkItem private readonly ITaskCompletionAction m_action ; private readonly Task m_completingTask ; internal CompletionActionInvoker ( ITaskCompletionAction action , Task completingTask ) m_action = action ; m_completingTask = completingTask ; [ SecurityCritical ] public void ExecuteWorkItem () m_action . Invoke ( m_completingTask ); [ SecurityCritical ] public void MarkAborted ( ThreadAbortException tae ) /* NOP */ // Proxy class for better debugging experience internal class SystemThreadingTasks_TaskDebugView private Task m_task ; public SystemThreadingTasks_TaskDebugView ( Task task ) m_task = task ; public object AsyncState { get { return m_task . AsyncState ; } } public TaskCreationOptions CreationOptions { get { return m_task . CreationOptions ; } } public Exception Exception { get { return m_task . Exception ; } } public int Id { get { return m_task . Id ; } } public bool CancellationPending { get { return ( m_task . Status == TaskStatus . WaitingToRun ) && m_task . CancellationToken . IsCancellationRequested ; } } public TaskStatus Status { get { return m_task . Status ; } } // Special purpose derivation of Task that supports limited replication through // overriding the ShouldReplicate() method. This is used by the Parallel.For/ForEach // methods. internal class ParallelForReplicatingTask : Task // Member variables private int m_replicationDownCount ; // downcounter to control replication // Constructors [ MethodImplAttribute ( MethodImplOptions . NoInlining )] // Methods containing StackCrawlMark local var have to be marked non-inlineable internal ParallelForReplicatingTask ( ParallelOptions parallelOptions , Action action , TaskCreationOptions creationOptions , InternalTaskOptions internalOptions ) : base ( action , null , Task . InternalCurrent , default ( CancellationToken ), creationOptions , internalOptions | InternalTaskOptions . SelfReplicating , null ) // Compute the down count based on scheduler/DOP info in parallelOptions. m_replicationDownCount = parallelOptions . EffectiveMaxConcurrencyLevel ; StackCrawlMark stackMark = StackCrawlMark . LookForMyCaller ; PossiblyCaptureContext ( ref stackMark ); // Controls degree of replication. If downcounter is initialized to -1, then // replication will be allowed to "run wild". Otherwise, this method decrements // the downcounter each time it is called, calling false when it is called with // a zero downcounter. This method returning false effectively ends the replication // of the associated ParallelForReplicatingTask. internal override bool ShouldReplicate () if ( m_replicationDownCount == -1) return true ; // "run wild" if ( m_replicationDownCount > 0) // Decrement and return true if not called with 0 downcount m_replicationDownCount --; return true ; return false ; // We're done replicating internal override Task CreateReplicaTask ( Action < object > taskReplicaDelegate , Object stateObject , Task parentTask , TaskScheduler taskScheduler , TaskCreationOptions creationOptionsForReplica , InternalTaskOptions internalOptionsForReplica ) return new ParallelForReplicaTask ( taskReplicaDelegate , stateObject , parentTask , taskScheduler , creationOptionsForReplica , internalOptionsForReplica ); internal class ParallelForReplicaTask : Task internal object m_stateForNextReplica ; // some replicas may quit prematurely, in which case they will use this variable // to save state they want to be picked up by the next replica queued to the same thread internal object m_stateFromPreviousReplica ; // some replicas may quit prematurely, in which case they will use this variable // to save state they want to be picked up by the next replica queued to the same thread internal Task m_handedOverChildReplica ; // some replicas may quit prematurely, in which case they will use this variable // to hand over the child replica they had queued to the next task that will replace them internal ParallelForReplicaTask ( Action < object > taskReplicaDelegate , Object stateObject , Task parentTask , TaskScheduler taskScheduler , TaskCreationOptions creationOptionsForReplica , InternalTaskOptions internalOptionsForReplica ) : base ( taskReplicaDelegate , stateObject , parentTask , default ( CancellationToken ), creationOptionsForReplica , internalOptionsForReplica , taskScheduler ) // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica internal override Object SavedStateForNextReplica get { return m_stateForNextReplica ; } set { m_stateForNextReplica = value ; } // Allows internal deriving classes to support replicas that exit prematurely and want to pass on state to the next replica internal override Object SavedStateFromPreviousReplica get { return m_stateFromPreviousReplica ; } set { m_stateFromPreviousReplica = value ; } // Allows internal deriving classes to support replicas that exit prematurely and want to hand over the child replica that they // had queued, so that the replacement replica can work with that child task instead of queuing up yet another one internal override Task HandedOverChildReplica get { return m_handedOverChildReplica ; } set { m_handedOverChildReplica = value ; } /// < summary > /// Specifies flags that control optional behavior for the creation and execution of tasks. /// </ summary > // NOTE: These options are a subset of TaskContinuationsOptions, thus before adding a flag check it is // not already in use. [ Flags ] [ Serializable ] public enum TaskCreationOptions /// < summary > /// Specifies that the default behavior should be used. /// </ summary > None = 0x0, /// < summary > /// A hint to a < see cref = " System.Threading.Tasks. TaskScheduler " > TaskScheduler </ see > to schedule a /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to /// be run sooner, and tasks scheduled later will be more likely to be run later. /// </ summary > PreferFairness = 0x01, /// < summary > /// Specifies that a task will be a long-running, course-grained operation. It provides a hint to the /// < see cref = " System.Threading.Tasks. TaskScheduler " > TaskScheduler </ see > that oversubscription may be /// warranted. /// </ summary > LongRunning = 0x02, /// < summary > /// Specifies that a task is attached to a parent in the task hierarchy. /// </ summary > AttachedToParent = 0x04, /// < summary > /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task. /// </ summary > DenyChildAttach = 0x08, /// < summary > /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler. /// </ summary > HideScheduler = 0x10, // 0x20 is already being used in TaskContinuationOptions /// < summary > /// Forces continuations added to the current task to be executed asynchronously. /// This option has precedence over TaskContinuationOptions.ExecuteSynchronously /// </ summary > RunContinuationsAsynchronously = 0x40 /// < summary > /// Task creation flags which are only used internally. /// </ summary > [ Flags ] [ Serializable ] internal enum InternalTaskOptions /// < summary > Specifies "No internal task options" </ summary > None , /// < summary > Used to filter out internal vs. public task creation options. </ summary > InternalOptionsMask = 0x0000FF00, ChildReplica = 0x0100, ContinuationTask = 0x0200, PromiseTask = 0x0400, SelfReplicating = 0x0800, /// < summary > /// Store the presence of TaskContinuationOptions.LazyCancellation, since it does not directly /// translate into any TaskCreationOptions. /// </ summary > LazyCancellation = 0x1000, /// < summary > Specifies that the task will be queued by the runtime before handing it over to the user. /// This flag will be used to skip the cancellationtoken registration step, which is only meant for unstarted tasks. </ summary > QueuedByRuntime = 0x2000, /// < summary > /// Denotes that Dispose should be a complete nop for a Task. Used when constructing tasks that are meant to be cached/reused. /// </ summary > DoNotDispose = 0x4000 /// < summary > /// Specifies flags that control optional behavior for the creation and execution of continuation tasks. /// </ summary > [ Flags ] [ Serializable ] public enum TaskContinuationOptions /// < summary > /// Default = "Continue on any, no task options, run asynchronously" /// Specifies that the default behavior should be used. Continuations, by default, will /// be scheduled when the antecedent task completes, regardless of the task's final < see /// cref = " System.Threading.Tasks. TaskStatus " > TaskStatus </ see > . /// </ summary > None = 0, // These are identical to their meanings and values in TaskCreationOptions /// < summary > /// A hint to a < see cref = " System.Threading.Tasks. TaskScheduler " > TaskScheduler </ see > to schedule a /// task in as fair a manner as possible, meaning that tasks scheduled sooner will be more likely to /// be run sooner, and tasks scheduled later will be more likely to be run later. /// </ summary > PreferFairness = 0x01, /// < summary > /// Specifies that a task will be a long-running, course-grained operation. It provides /// a hint to the < see cref = " System.Threading.Tasks. TaskScheduler " > TaskScheduler </ see > that /// oversubscription may be warranted. /// </ summary > LongRunning = 0x02, /// < summary > /// Specifies that a task is attached to a parent in the task hierarchy. /// </ summary > AttachedToParent = 0x04, /// < summary > /// Specifies that an InvalidOperationException will be thrown if an attempt is made to attach a child task to the created task. /// </ summary > DenyChildAttach = 0x08, /// < summary > /// Prevents the ambient scheduler from being seen as the current scheduler in the created task. This means that operations /// like StartNew or ContinueWith that are performed in the created task will see TaskScheduler.Default as the current scheduler. /// </ summary > HideScheduler = 0x10, /// < summary > /// In the case of continuation cancellation, prevents completion of the continuation until the antecedent has completed. /// </ summary > LazyCancellation = 0x20, RunContinuationsAsynchronously = 0x40, // These are specific to continuations /// < summary > /// Specifies that the continuation task should not be scheduled if its antecedent ran to completion. /// This option is not valid for multi-task continuations. /// </ summary > NotOnRanToCompletion = 0x10000, /// < summary > /// Specifies that the continuation task should not be scheduled if its antecedent threw an unhandled /// exception. This option is not valid for multi-task continuations. /// </ summary > NotOnFaulted = 0x20000, /// < summary > /// Specifies that the continuation task should not be scheduled if its antecedent was canceled. This /// option is not valid for multi-task continuations. /// </ summary > NotOnCanceled = 0x40000, /// < summary > /// Specifies that the continuation task should be scheduled only if its antecedent ran to /// completion. This option is not valid for multi-task continuations. /// </ summary > OnlyOnRanToCompletion = NotOnFaulted | NotOnCanceled , /// < summary > /// Specifies that the continuation task should be scheduled only if its antecedent threw an /// unhandled exception. This option is not valid for multi-task continuations. /// </ summary > OnlyOnFaulted = NotOnRanToCompletion | NotOnCanceled , /// < summary > /// Specifies that the continuation task should be scheduled only if its antecedent was canceled. /// This option is not valid for multi-task continuations. /// </ summary > OnlyOnCanceled = NotOnRanToCompletion | NotOnFaulted , /// < summary > /// Specifies that the continuation task should be executed synchronously. With this option /// specified, the continuation will be run on the same thread that causes the antecedent task to /// transition into its final state. If the antecedent is already complete when the continuation is /// created, the continuation will run on the thread creating the continuation. Only very /// short-running continuations should be executed synchronously. /// </ summary > ExecuteSynchronously = 0x80000 /// < summary > /// Internal helper class to keep track of stack depth and decide whether we should inline or not. /// </ summary > internal class StackGuard // current thread's depth of nested inline task executions private int m_inliningDepth = 0; // For relatively small inlining depths we don't want to get into the business of stack probing etc. // This clearly leaves a window of opportunity for the user code to SO. However a piece of code // that can SO in 20 inlines on a typical 1MB stack size probably needs to be revisited anyway. private const int MAX_UNCHECKED_INLINING_DEPTH = 20; # if !FEATURE_PAL && !PFX_LEGACY_3_5 private UInt64 m_lastKnownWatermark ; private static int s_pageSize ; // We are conservative here. We assume that the platform needs a whole 64KB to // respond to stack overflow. This means that for very small stacks (e.g. 128KB) // we'll fail a lot of stack checks incorrectly. private const long STACK_RESERVED_SPACE = 4096 * 16; # endif // !FEATURE_PAL && !PFX_LEGACY_3_5 /// < summary > /// This method needs to be called before attempting inline execution on the current thread. /// If false is returned, it means we are too close to the end of the stack and should give up inlining. /// Each call to TryBeginInliningScope() that returns true must be matched with a /// call to EndInliningScope() regardless of whether inlining actually took place. /// </ summary > [ SecuritySafeCritical ] internal bool TryBeginInliningScope () // If we're still under the 'safe' limit we'll just skip the stack probe to save p/invoke calls if ( m_inliningDepth < MAX_UNCHECKED_INLINING_DEPTH || CheckForSufficientStack ()) m_inliningDepth ++; return true ; return false; /// < summary > /// This needs to be called once for each previous successful TryBeginInliningScope() call after /// inlining related logic runs. /// </ summary > internal void EndInliningScope () m_inliningDepth --; Contract . Assert ( m_inliningDepth >= 0, "Inlining depth count should never go negative." ); // do the right thing just in case... if ( m_inliningDepth < 0) m_inliningDepth = 0; [ SecurityCritical ] private unsafe bool CheckForSufficientStack () # if !FEATURE_PAL && !PFX_LEGACY_3_5 // see if we already have the system page size info recorded int pageSize = s_pageSize ; if ( pageSize == 0) // If not we need to query it from GetSystemInfo() // Note that this happens only once for the process lifetime Win32Native . SYSTEM_INFO sysInfo = new Win32Native . SYSTEM_INFO (); Win32Native . GetSystemInfo ( ref sysInfo ); s_pageSize = pageSize = sysInfo . dwPageSize ; Win32Native . MEMORY_BASIC_INFORMATION stackInfo = new Win32Native . MEMORY_BASIC_INFORMATION (); // We subtract one page for our request. VirtualQuery rounds UP to the next page. // Unfortunately, the stack grows down. If we're on the first page (last page in the // VirtualAlloc), we'll be moved to the next page, which is off the stack! UIntPtr currentAddr = new UIntPtr (& stackInfo - pageSize ); UInt64 current64 = currentAddr . ToUInt64 (); // Check whether we previously recorded a deeper stack than where we currently are, // If so we don't need to do the P/Invoke to VirtualQuery if ( m_lastKnownWatermark != 0 && current64 > m_lastKnownWatermark ) return true ; // Actual stack probe. P/Invoke to query for the current stack allocation information. Win32Native . VirtualQuery ( currentAddr . ToPointer (), ref stackInfo , ( UIntPtr )( sizeof ( Win32Native . MEMORY_BASIC_INFORMATION ))); // If the current address minus the base (remember: the stack grows downward in the // address space) is greater than the number of bytes requested plus the reserved // space at the end, the request has succeeded. if (( current64 - (( UIntPtr ) stackInfo . AllocationBase ). ToUInt64 ()) > STACK_RESERVED_SPACE ) m_lastKnownWatermark = current64 ; return true ; return false ; # else // !FEATURE_PAL && !PFX_LEGACY_3_5 // if we're being compiled with FEATURE_PAL or PFX_LEGACY_3_5 we simply allow unchecked inlining. return true; # endif // Special internal struct that we use to signify that we are not interested in // a Task<VoidTaskResult>'s result. internal struct VoidTaskResult { } // Interface to which all completion actions must conform. // This interface allows us to combine functionality and reduce allocations. // For example, see Task.SetOnInvokeMres, and its use in Task.SpinThenBlockingWait(). // This code: // ManualResetEvent mres = new ManualResetEventSlim(false, 0); // Action<Task> completionAction = delegate { mres.Set() ; }; // AddCompletionAction(completionAction); // gets replaced with this: // SetOnInvokeMres mres = new SetOnInvokeMres(); // AddCompletionAction(mres); // For additional examples of where this is used, see internal classes Task.SignalOnInvokeCDE, // Task.WhenAllPromise, Task.WhenAllPromise<T>, TaskFactory.CompleteOnCountdownPromise, // TaskFactory.CompleteOnCountdownPromise<T>, and TaskFactory.CompleteOnInvokePromise. internal interface ITaskCompletionAction void Invoke ( Task completingTask ); // This class encapsulates all "unwrap" logic, and also implements ITaskCompletionAction, // which minimizes the allocations needed for queuing it to its antecedent. This // logic is used by both the Unwrap extension methods and the unwrap-style Task.Run methods. internal sealed class UnwrapPromise < TResult > : Task < TResult >, ITaskCompletionAction // The possible states for our UnwrapPromise, used by Invoke() to determine which logic to execute private const byte STATE_WAITING_ON_OUTER_TASK = 0; // Invoke() means "process completed outer task" private const byte STATE_WAITING_ON_INNER_TASK = 1; // Invoke() means "process completed inner task" private const byte STATE_DONE = 2; // Invoke() means "something went wrong and we are hosed!" // Keep track of our state; initialized to STATE_WAITING_ON_OUTER_TASK in the constructor private byte _state ; // "Should we check for OperationCanceledExceptions on the outer task and interpret them as proxy cancellation?" // Unwrap() sets this to false, Run() sets it to true. private readonly bool _lookForOce ; public UnwrapPromise ( Task outerTask , bool lookForOce ) : base (( object ) null , outerTask . CreationOptions & TaskCreationOptions . AttachedToParent ) Contract . Requires ( outerTask != null , "Expected non-null outerTask" ); _lookForOce = lookForOce ; _state = STATE_WAITING_ON_OUTER_TASK ; if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCreation ( CausalityTraceLevel . Required , this . Id , "Task.Unwrap" , 0); if ( Task . s_asyncDebuggingEnabled ) AddToActiveTasks ( this ); // Link ourselves to the outer task. // If the outer task has already completed, take the fast path // of immediately transferring its results or processing the inner task. if ( outerTask . IsCompleted ) ProcessCompletedOuterTask ( outerTask ); else // Otherwise, process its completion asynchronously. outerTask . AddCompletionAction ( this ); // For ITaskCompletionAction public void Invoke ( Task completingTask ) // Check the current stack guard. If we're ok to inline, // process the task, and reset the guard when we're done. var sg = Task . CurrentStackGuard ; if ( sg . TryBeginInliningScope ()) try { InvokeCore ( completingTask ); } finally { sg . EndInliningScope (); } // Otherwise, we're too deep on the stack, and // we shouldn't run the continuation chain here, so queue a work // item to call back here to Invoke asynchronously. else InvokeCoreAsync ( completingTask ); /// < summary > /// Processes the completed task. InvokeCore could be called twice: /// once for the outer task, once for the inner task. /// </ summary > /// < param name = " completingTask " > The completing outer or inner task. </ param > private void InvokeCore ( Task completingTask ) switch ( _state ) case STATE_WAITING_ON_OUTER_TASK : ProcessCompletedOuterTask ( completingTask ); // We bump the state inside of ProcessCompletedOuterTask because it can also be called from the constructor. break ; case STATE_WAITING_ON_INNER_TASK : bool result = TrySetFromTask ( completingTask , lookForOce : false ); _state = STATE_DONE ; // bump the state Contract . Assert ( result , "Expected TrySetFromTask from inner task to succeed" ); break ; default : Contract . Assert ( false , "UnwrapPromise in illegal state" ); break ; // Calls InvokeCore asynchronously. [ SecuritySafeCritical ] private void InvokeCoreAsync ( Task completingTask ) // Queue a call to Invoke. If we're so deep on the stack that we're at risk of overflowing, // there's a high liklihood this thread is going to be doing lots more work before // returning to the thread pool (at the very least unwinding through thousands of // stack frames). So we queue to the global queue. ThreadPool . UnsafeQueueUserWorkItem ( state => // InvokeCore(completingTask); var tuple = ( Tuple < UnwrapPromise < TResult >, Task >) state ; tuple . Item1 . InvokeCore ( tuple . Item2 ); }, Tuple . Create < UnwrapPromise < TResult >, Task >( this , completingTask )); /// < summary > Processes the outer task once it's completed. </ summary > /// < param name = " task " > The now-completed outer task. </ param > private void ProcessCompletedOuterTask ( Task task ) Contract . Requires ( task != null && task . IsCompleted , "Expected non-null, completed outer task" ); Contract . Assert ( _state == STATE_WAITING_ON_OUTER_TASK , "We're in the wrong state!" ); // Bump our state before proceeding any further _state = STATE_WAITING_ON_INNER_TASK ; switch ( task . Status ) // If the outer task did not complete successfully, then record the // cancellation/fault information to tcs.Task. case TaskStatus . Canceled : case TaskStatus . Faulted : bool result = TrySetFromTask ( task , _lookForOce ); Contract . Assert ( result , "Expected TrySetFromTask from outer task to succeed" ); break ; // Otherwise, process the inner task it returned. case TaskStatus . RanToCompletion : var taskOfTaskOfTResult = task as Task < Task < TResult >>; // it's either a Task<Task> or Task<Task<TResult>> ProcessInnerTask ( taskOfTaskOfTResult != null ? taskOfTaskOfTResult . Result : (( Task < Task >) task ). Result ); break ; /// < summary > Transfer the completion status from "task" to ourself. </ summary > /// < param name = " task " > The source task whose results should be transfered to < paramref name = " promise " /> . </ param > /// < param name = " lookForOce " > Whether or not to look for OperationCanceledExceptions in task's exceptions if it faults. </ param > /// < returns > true if the transfer was successful; otherwise, false. </ returns > private bool TrySetFromTask ( Task task , bool lookForOce ) Contract . Requires ( task != null && task . IsCompleted , "TrySetFromTask: Expected task to have completed." ); if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationRelation ( CausalityTraceLevel . Important , this . Id , CausalityRelation . Join ); bool result = false ; switch ( task . Status ) case TaskStatus . Canceled : result = TrySetCanceled ( task . CancellationToken , task . GetCancellationExceptionDispatchInfo ()); break ; case TaskStatus . Faulted : var edis = task . GetExceptionDispatchInfos (); ExceptionDispatchInfo oceEdi ; OperationCanceledException oce ; if ( lookForOce && edis . Count > 0 && ( oceEdi = edis [0]) != null && ( oce = oceEdi . SourceException as OperationCanceledException ) != null ) result = TrySetCanceled ( oce . CancellationToken , oceEdi ); result = TrySetException ( edis ); break ; case TaskStatus . RanToCompletion : var taskTResult = task as Task < TResult >; if ( AsyncCausalityTracer . LoggingOn ) AsyncCausalityTracer . TraceOperationCompletion ( CausalityTraceLevel . Required , this . Id , AsyncCausalityStatus . Completed ); if ( Task . s_asyncDebuggingEnabled ) RemoveFromActiveTasks ( this . Id ); result = TrySetResult ( taskTResult != null ? taskTResult . Result : default ( TResult )); break ; return result ; /// < summary > /// Processes the inner task of a Task{Task} or Task{Task{TResult}}, /// transferring the appropriate results to ourself. /// </ summary > /// < param name = " task " > The inner task returned by the task provided by the user. </ param > private void ProcessInnerTask ( Task task ) // If the inner task is null, the proxy should be canceled. if ( task == null ) TrySetCanceled ( default ( CancellationToken )); _state = STATE_DONE ; // ... and record that we are done // Fast path for if the inner task is already completed else if ( task . IsCompleted ) TrySetFromTask ( task , lookForOce : false ); _state = STATE_DONE ; // ... and record that we are done // The inner task exists but is not yet complete, so when it does complete, // take some action to set our completion state. task . AddCompletionAction ( this ); // We'll record that we are done when Invoke() is called.