// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // // // As with TaskFactory, TaskFactory encodes common factory patterns into helper methods. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; using System.Security; using System.Runtime.CompilerServices; using System.Threading; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Runtime.Versioning; namespace System.Threading.Tasks { /// /// Provides support for creating and scheduling /// Task{TResult} objects. /// /// The type of the results that are available though /// the Task{TResult} objects that are associated with /// the methods in this class. /// /// /// There are many common patterns for which tasks are relevant. The /// class encodes some of these patterns into methods that pick up default settings, which are /// configurable through its constructors. /// /// /// A default instance of is available through the /// Task{TResult}.Factory property. /// /// public class TaskFactory { // Member variables, DefaultScheduler, other properties and ctors // copied right out of TaskFactory... Lots of duplication here... // Should we be thinking about a TaskFactoryBase class? // member variables private CancellationToken m_defaultCancellationToken; private TaskScheduler m_defaultScheduler; private TaskCreationOptions m_defaultCreationOptions; private TaskContinuationOptions m_defaultContinuationOptions; private TaskScheduler DefaultScheduler { get { if (m_defaultScheduler == null) return TaskScheduler.Current; else return m_defaultScheduler; } } // sister method to above property -- avoids a TLS lookup private TaskScheduler GetDefaultScheduler(Task currTask) { if (m_defaultScheduler != null) return m_defaultScheduler; else if ((currTask != null) && ((currTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0) ) return currTask.ExecutingTaskScheduler; else return TaskScheduler.Default; } /* Constructors */ /// /// Initializes a instance with the default configuration. /// /// /// This constructor creates a instance with a default configuration. The /// property is initialized to /// TaskCreationOptions.None, the /// property is initialized to TaskContinuationOptions.None, /// and the TaskScheduler property is /// initialized to the current scheduler (see TaskScheduler.Current). /// public TaskFactory() : this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, null) { } /// /// Initializes a instance with the default configuration. /// /// The default that will be assigned /// to tasks created by this unless another CancellationToken is explicitly specified /// while calling the factory methods. /// /// This constructor creates a instance with a default configuration. The /// property is initialized to /// TaskCreationOptions.None, the /// property is initialized to TaskContinuationOptions.None, /// and the TaskScheduler property is /// initialized to the current scheduler (see TaskScheduler.Current). /// public TaskFactory(CancellationToken cancellationToken) : this(cancellationToken, TaskCreationOptions.None, TaskContinuationOptions.None, null) { } /// /// Initializes a instance with the specified configuration. /// /// /// The /// TaskScheduler to use to schedule any tasks created with this TaskFactory{TResult}. A null value /// indicates that the current TaskScheduler should be used. /// /// /// With this constructor, the /// property is initialized to /// TaskCreationOptions.None, the /// property is initialized to TaskContinuationOptions.None, /// and the TaskScheduler property is /// initialized to , unless it's null, in which case the property is /// initialized to the current scheduler (see TaskScheduler.Current). /// public TaskFactory(TaskScheduler scheduler) // null means to use TaskScheduler.Current : this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, scheduler) { } /// /// Initializes a instance with the specified configuration. /// /// /// The default /// TaskCreationOptions to use when creating tasks with this TaskFactory{TResult}. /// /// /// The default /// TaskContinuationOptions to use when creating continuation tasks with this TaskFactory{TResult}. /// /// /// The exception that is thrown when the /// argument or the /// argument specifies an invalid value. /// /// /// With this constructor, the /// property is initialized to , /// the /// property is initialized to , and the TaskScheduler property is initialized to the /// current scheduler (see TaskScheduler.Current). /// public TaskFactory(TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions) : this(default(CancellationToken), creationOptions, continuationOptions, null) { } /// /// Initializes a instance with the specified configuration. /// /// The default that will be assigned /// to tasks created by this unless another CancellationToken is explicitly specified /// while calling the factory methods. /// /// The default /// TaskCreationOptions to use when creating tasks with this TaskFactory{TResult}. /// /// /// The default /// TaskContinuationOptions to use when creating continuation tasks with this TaskFactory{TResult}. /// /// /// The default /// TaskScheduler to use to schedule any Tasks created with this TaskFactory{TResult}. A null value /// indicates that TaskScheduler.Current should be used. /// /// /// The exception that is thrown when the /// argument or the /// argumentspecifies an invalid value. /// /// /// With this constructor, the /// property is initialized to , /// the /// property is initialized to , and the TaskScheduler property is initialized to /// , unless it's null, in which case the property is initialized to the /// current scheduler (see TaskScheduler.Current). /// public TaskFactory(CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); TaskFactory.CheckCreationOptions(creationOptions); m_defaultCancellationToken = cancellationToken; m_defaultScheduler = scheduler; m_defaultCreationOptions = creationOptions; m_defaultContinuationOptions = continuationOptions; } /* Properties */ /// /// Gets the default CancellationToken of this /// TaskFactory. /// /// /// This property returns the default that will be assigned to all /// tasks created by this factory unless another CancellationToken value is explicitly specified /// during the call to the factory methods. /// public CancellationToken CancellationToken { get { return m_defaultCancellationToken; } } /// /// Gets the TaskScheduler of this /// TaskFactory{TResult}. /// /// /// This property returns the default scheduler for this factory. It will be used to schedule all /// tasks unless another scheduler is explicitly specified during calls to this factory's methods. /// If null, TaskScheduler.Current /// will be used. /// public TaskScheduler Scheduler { get { return m_defaultScheduler; } } /// /// Gets the TaskCreationOptions /// value of this TaskFactory{TResult}. /// /// /// This property returns the default creation options for this factory. They will be used to create all /// tasks unless other options are explicitly specified during calls to this factory's methods. /// public TaskCreationOptions CreationOptions { get { return m_defaultCreationOptions; } } /// /// Gets the TaskContinuationOptions /// value of this TaskFactory{TResult}. /// /// /// This property returns the default continuation options for this factory. They will be used to create /// all continuation tasks unless other options are explicitly specified during calls to this factory's methods. /// public TaskContinuationOptions ContinuationOptions { get { return m_defaultContinuationOptions; } } /* StartNew */ /// /// Creates and starts a . /// /// A function delegate that returns the future result to be available through /// the . /// The started . /// The exception that is thrown when the /// argument is null. /// /// Calling StartNew is functionally equivalent to creating a using one /// of its constructors and then calling /// Start to schedule it for execution. /// However, unless creation and scheduling must be separated, StartNew is the recommended approach /// for both simplicity and performance. /// public Task StartNew(Func function) { Task currTask = Task.InternalCurrent; return Task.StartNew(currTask, function, m_defaultCancellationToken, m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// /// Creates and starts a . /// /// A function delegate that returns the future result to be available through /// the . /// The that will be assigned to the new task. /// The started . /// The exception that is thrown when the /// argument is null. /// The provided CancellationToken /// has already been disposed. /// /// /// Calling StartNew is functionally equivalent to creating a using one /// of its constructors and then calling /// Start to schedule it for execution. /// However, unless creation and scheduling must be separated, StartNew is the recommended approach /// for both simplicity and performance. /// public Task StartNew(Func function, CancellationToken cancellationToken) { Task currTask = Task.InternalCurrent; return Task.StartNew(currTask, function, cancellationToken, m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// /// Creates and starts a . /// /// A function delegate that returns the future result to be available through /// the . /// A TaskCreationOptions value that controls the behavior of the /// created /// . /// The started . /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// /// Calling StartNew is functionally equivalent to creating a using one /// of its constructors and then calling /// Start to schedule it for execution. /// However, unless creation and scheduling must be separated, StartNew is the recommended approach /// for both simplicity and performance. /// public Task StartNew(Func function, TaskCreationOptions creationOptions) { Task currTask = Task.InternalCurrent; return Task.StartNew(currTask, function, m_defaultCancellationToken, creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// /// Creates and starts a . /// /// A function delegate that returns the future result to be available through /// the . /// A TaskCreationOptions value that controls the behavior of the /// created /// . /// The that will be assigned to the new task. /// The TaskScheduler /// that is used to schedule the created /// Task{TResult}. /// The started . /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// The provided CancellationToken /// has already been disposed. /// /// /// Calling StartNew is functionally equivalent to creating a using one /// of its constructors and then calling /// Start to schedule it for execution. /// However, unless creation and scheduling must be separated, StartNew is the recommended approach /// for both simplicity and performance. /// public Task StartNew(Func function, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) { return Task.StartNew( Task.InternalCurrentIfAttached(creationOptions), function, cancellationToken, creationOptions, InternalTaskOptions.None, scheduler); } /// /// Creates and starts a . /// /// A function delegate that returns the future result to be available through /// the . /// An object containing data to be used by the /// delegate. /// The started . /// The exception that is thrown when the /// argument is null. /// /// Calling StartNew is functionally equivalent to creating a using one /// of its constructors and then calling /// Start to schedule it for execution. /// However, unless creation and scheduling must be separated, StartNew is the recommended approach /// for both simplicity and performance. /// public Task StartNew(Func function, Object state) { Task currTask = Task.InternalCurrent; return Task.StartNew(currTask, function, state, m_defaultCancellationToken, m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// /// Creates and starts a . /// /// A function delegate that returns the future result to be available through /// the . /// An object containing data to be used by the /// delegate. /// The that will be assigned to the new task. /// The started . /// The exception that is thrown when the /// argument is null. /// The provided CancellationToken /// has already been disposed. /// /// /// Calling StartNew is functionally equivalent to creating a using one /// of its constructors and then calling /// Start to schedule it for execution. /// However, unless creation and scheduling must be separated, StartNew is the recommended approach /// for both simplicity and performance. /// public Task StartNew(Func function, Object state, CancellationToken cancellationToken) { Task currTask = Task.InternalCurrent; return Task.StartNew(currTask, function, state, cancellationToken, m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// /// Creates and starts a . /// /// A function delegate that returns the future result to be available through /// the . /// An object containing data to be used by the /// delegate. /// A TaskCreationOptions value that controls the behavior of the /// created /// . /// The started . /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// /// Calling StartNew is functionally equivalent to creating a using one /// of its constructors and then calling /// Start to schedule it for execution. /// However, unless creation and scheduling must be separated, StartNew is the recommended approach /// for both simplicity and performance. /// public Task StartNew(Func function, Object state, TaskCreationOptions creationOptions) { Task currTask = Task.InternalCurrent; return Task.StartNew(currTask, function, state, m_defaultCancellationToken, creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask)); } /// /// Creates and starts a . /// /// A function delegate that returns the future result to be available through /// the . /// An object containing data to be used by the /// delegate. /// The that will be assigned to the new task. /// A TaskCreationOptions value that controls the behavior of the /// created /// . /// The TaskScheduler /// that is used to schedule the created /// Task{TResult}. /// The started . /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// The provided CancellationToken /// has already been disposed. /// /// /// Calling StartNew is functionally equivalent to creating a using one /// of its constructors and then calling /// Start to schedule it for execution. /// However, unless creation and scheduling must be separated, StartNew is the recommended approach /// for both simplicity and performance. /// public Task StartNew(Func function, Object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) { return Task.StartNew(Task.InternalCurrentIfAttached(creationOptions), function, state, cancellationToken, creationOptions, InternalTaskOptions.None, scheduler); } // // APM Factory methods // // Common core logic for FromAsync calls. This minimizes the chance of "drift" between overload implementations. private static void FromAsyncCoreLogic( IAsyncResult iar, Func endFunction, Action endAction, Task promise, bool requiresSynchronization) { Contract.Requires((endFunction != null) != (endAction != null), "Expected exactly one of endFunction/endAction to be non-null"); Exception ex = null; OperationCanceledException oce = null; TResult result = default(TResult); try { if (endFunction != null) { result = endFunction(iar); } else { endAction(iar); } } catch (OperationCanceledException _oce) { oce = _oce; } catch (Exception e) { ex = e; } finally { if (oce != null) { promise.TrySetCanceled(oce.CancellationToken, oce); } else if (ex != null) { bool bWonSetException = promise.TrySetException(ex); if (bWonSetException && ex is ThreadAbortException) { promise.m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false); } } else { if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Completed); if (Task.s_asyncDebuggingEnabled) { Task.RemoveFromActiveTasks(promise.Id); } if (requiresSynchronization) { promise.TrySetResult(result); } else { promise.DangerousSetResult(result); } } } } /// /// Creates a Task that executes an end /// method function when a specified IAsyncResult completes. /// /// The IAsyncResult whose completion should trigger the processing of the /// . /// The function delegate that processes the completed . /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// A Task that represents the /// asynchronous operation. public Task FromAsync(IAsyncResult asyncResult, Func endMethod) { return FromAsyncImpl(asyncResult, endMethod, null, m_defaultCreationOptions, DefaultScheduler); } /// /// Creates a Task that executes an end /// method function when a specified IAsyncResult completes. /// /// The IAsyncResult whose completion should trigger the processing of the /// . /// The function delegate that processes the completed . /// The TaskCreationOptions value that controls the behavior of the /// created Task. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// A Task that represents the /// asynchronous operation. public Task FromAsync( IAsyncResult asyncResult, Func endMethod, TaskCreationOptions creationOptions) { return FromAsyncImpl(asyncResult, endMethod, null, creationOptions, DefaultScheduler); } /// /// Creates a Task that executes an end /// method function when a specified IAsyncResult completes. /// /// The IAsyncResult whose completion should trigger the processing of the /// . /// The function delegate that processes the completed . /// The TaskScheduler /// that is used to schedule the task that executes the end method. /// The TaskCreationOptions value that controls the behavior of the /// created Task. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// A Task that represents the /// asynchronous operation. public Task FromAsync( IAsyncResult asyncResult, Func endMethod, TaskCreationOptions creationOptions, TaskScheduler scheduler) { return FromAsyncImpl(asyncResult, endMethod, null, creationOptions, scheduler); } // We need this logic broken out into a static method so that the similar TaskFactory.FromAsync() // method can access the logic w/o declaring a TaskFactory instance. internal static Task FromAsyncImpl( IAsyncResult asyncResult, Func endFunction, Action endAction, TaskCreationOptions creationOptions, TaskScheduler scheduler) { if (asyncResult == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.asyncResult); if (endFunction == null && endAction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod); Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null"); if (scheduler == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); Contract.EndContractBlock(); TaskFactory.CheckFromAsyncOptions(creationOptions, false); Task promise = new Task((object)null, creationOptions); if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync", 0); if (Task.s_asyncDebuggingEnabled) { Task.AddToActiveTasks(promise); } // Just specify this task as detached. No matter what happens, we want endMethod // to be called -- even if the parent is canceled. So we don't want to flow // RespectParentCancellation. Task t = new Task(new Action(delegate { FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization:true); }), (object)null, null, default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null); if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Verbose, t.Id, "TaskFactory.FromAsync Callback", 0); if (Task.s_asyncDebuggingEnabled) { Task.AddToActiveTasks(t); } if (asyncResult.IsCompleted) { try { t.InternalRunSynchronously(scheduler, waitForCompletion:false); } catch (Exception e) { promise.TrySetException(e); } // catch and log any scheduler exceptions } else { ThreadPool.RegisterWaitForSingleObject( asyncResult.AsyncWaitHandle, delegate { try { t.InternalRunSynchronously(scheduler, waitForCompletion: false); } catch (Exception e) { promise.TrySetException(e); } // catch and log any scheduler exceptions }, null, Timeout.Infinite, true); } return promise; } /// /// Creates a Task that represents a pair of /// begin and end methods that conform to the Asynchronous Programming Model pattern. /// /// The delegate that begins the asynchronous operation. /// The delegate that ends the asynchronous operation. /// An object containing data to be used by the /// delegate. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The created Task that /// represents the asynchronous operation. /// /// This method throws any exceptions thrown by the . /// public Task FromAsync( Func beginMethod, Func endMethod, object state) { return FromAsyncImpl(beginMethod, endMethod, null, state, m_defaultCreationOptions); } /// /// Creates a Task that represents a pair of /// begin and end methods that conform to the Asynchronous Programming Model pattern. /// /// The delegate that begins the asynchronous operation. /// The delegate that ends the asynchronous operation. /// The TaskCreationOptions value that controls the behavior of the /// created Task. /// An object containing data to be used by the /// delegate. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// The created Task that /// represents the asynchronous operation. /// /// This method throws any exceptions thrown by the . /// public Task FromAsync( Func beginMethod, Func endMethod, object state, TaskCreationOptions creationOptions) { return FromAsyncImpl(beginMethod, endMethod, null, state, creationOptions); } // We need this logic broken out into a static method so that the similar TaskFactory.FromAsync() // method can access the logic w/o declaring a TaskFactory instance. internal static Task FromAsyncImpl(Func beginMethod, Func endFunction, Action endAction, object state, TaskCreationOptions creationOptions) { if (beginMethod == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.beginMethod); if (endFunction == null && endAction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod); Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null"); TaskFactory.CheckFromAsyncOptions(creationOptions, true); Task promise = new Task(state, creationOptions); if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0); if (Task.s_asyncDebuggingEnabled) { Task.AddToActiveTasks(promise); } try { //This is 4.5 behaviour //if we don't require synchronization, a faster set result path is taken var asyncResult = beginMethod(iar => { if (!iar.CompletedSynchronously) FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true); }, state); if (asyncResult.CompletedSynchronously) { Debug.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false); } } catch { if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error); if (Task.s_asyncDebuggingEnabled) { Task.RemoveFromActiveTasks(promise.Id); } // Make sure we don't leave promise "dangling". promise.TrySetResult(default(TResult)); throw; } return promise; } /// /// Creates a Task that represents a pair of /// begin and end methods that conform to the Asynchronous Programming Model pattern. /// /// The type of the first argument passed to the delegate. /// The delegate that begins the asynchronous operation. /// The delegate that ends the asynchronous operation. /// The first argument passed to the /// delegate. /// An object containing data to be used by the /// delegate. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The created Task that /// represents the asynchronous operation. /// /// This method throws any exceptions thrown by the . /// public Task FromAsync( Func beginMethod, Func endMethod, TArg1 arg1, object state) { return FromAsyncImpl(beginMethod, endMethod, null, arg1, state, m_defaultCreationOptions); } /// /// Creates a Task that represents a pair of /// begin and end methods that conform to the Asynchronous Programming Model pattern. /// /// The type of the first argument passed to the delegate. /// The delegate that begins the asynchronous operation. /// The delegate that ends the asynchronous operation. /// The first argument passed to the /// delegate. /// The TaskCreationOptions value that controls the behavior of the /// created Task. /// An object containing data to be used by the /// delegate. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// The created Task that /// represents the asynchronous operation. /// /// This method throws any exceptions thrown by the . /// public Task FromAsync( Func beginMethod, Func endMethod, TArg1 arg1, object state, TaskCreationOptions creationOptions) { return FromAsyncImpl(beginMethod, endMethod, null, arg1, state, creationOptions); } // We need this logic broken out into a static method so that the similar TaskFactory.FromAsync() // method can access the logic w/o declaring a TaskFactory instance. internal static Task FromAsyncImpl(Func beginMethod, Func endFunction, Action endAction, TArg1 arg1, object state, TaskCreationOptions creationOptions) { if (beginMethod == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.beginMethod); if (endFunction == null && endAction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endFunction); Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null"); TaskFactory.CheckFromAsyncOptions(creationOptions, true); Task promise = new Task(state, creationOptions); if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0); if (Task.s_asyncDebuggingEnabled) { Task.AddToActiveTasks(promise); } try { //if we don't require synchronization, a faster set result path is taken var asyncResult = beginMethod(arg1, iar => { if (!iar.CompletedSynchronously) FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true); }, state); if (asyncResult.CompletedSynchronously) { Debug.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false); } } catch { if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error); if (Task.s_asyncDebuggingEnabled) { Task.RemoveFromActiveTasks(promise.Id); } // Make sure we don't leave promise "dangling". promise.TrySetResult(default(TResult)); throw; } return promise; } /// /// Creates a Task that represents a pair of /// begin and end methods that conform to the Asynchronous Programming Model pattern. /// /// The type of the first argument passed to the delegate. /// The type of the second argument passed to /// delegate. /// The delegate that begins the asynchronous operation. /// The delegate that ends the asynchronous operation. /// The first argument passed to the /// delegate. /// The second argument passed to the /// delegate. /// An object containing data to be used by the /// delegate. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The created Task that /// represents the asynchronous operation. /// /// This method throws any exceptions thrown by the . /// public Task FromAsync( Func beginMethod, Func endMethod, TArg1 arg1, TArg2 arg2, object state) { return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, state, m_defaultCreationOptions); } /// /// Creates a Task that represents a pair of /// begin and end methods that conform to the Asynchronous Programming Model pattern. /// /// The type of the first argument passed to the delegate. /// The type of the second argument passed to /// delegate. /// The delegate that begins the asynchronous operation. /// The delegate that ends the asynchronous operation. /// The first argument passed to the /// delegate. /// The second argument passed to the /// delegate. /// The TaskCreationOptions value that controls the behavior of the /// created Task. /// An object containing data to be used by the /// delegate. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// The created Task that /// represents the asynchronous operation. /// /// This method throws any exceptions thrown by the . /// public Task FromAsync( Func beginMethod, Func endMethod, TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions) { return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, state, creationOptions); } // We need this logic broken out into a static method so that the similar TaskFactory.FromAsync() // method can access the logic w/o declaring a TaskFactory instance. internal static Task FromAsyncImpl(Func beginMethod, Func endFunction, Action endAction, TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions) { if (beginMethod == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.beginMethod); if (endFunction == null && endAction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod); Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null"); TaskFactory.CheckFromAsyncOptions(creationOptions, true); Task promise = new Task(state, creationOptions); if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0); if (Task.s_asyncDebuggingEnabled) { Task.AddToActiveTasks(promise); } try { //if we don't require synchronization, a faster set result path is taken var asyncResult = beginMethod(arg1, arg2, iar => { if (!iar.CompletedSynchronously) FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true); }, state); if (asyncResult.CompletedSynchronously) { Debug.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false); } } catch { if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error); if (Task.s_asyncDebuggingEnabled) { Task.RemoveFromActiveTasks(promise.Id); } // Make sure we don't leave promise "dangling". promise.TrySetResult(default(TResult)); throw; } return promise; } /// /// Creates a Task that represents a pair of /// begin and end methods that conform to the Asynchronous Programming Model pattern. /// /// The type of the first argument passed to the delegate. /// The type of the second argument passed to /// delegate. /// The type of the third argument passed to /// delegate. /// The delegate that begins the asynchronous operation. /// The delegate that ends the asynchronous operation. /// The first argument passed to the /// delegate. /// The second argument passed to the /// delegate. /// The third argument passed to the /// delegate. /// An object containing data to be used by the /// delegate. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The created Task that /// represents the asynchronous operation. /// /// This method throws any exceptions thrown by the . /// public Task FromAsync( Func beginMethod, Func endMethod, TArg1 arg1, TArg2 arg2, TArg3 arg3, object state) { return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, arg3, state, m_defaultCreationOptions); } /// /// Creates a Task that represents a pair of /// begin and end methods that conform to the Asynchronous Programming Model pattern. /// /// The type of the first argument passed to the delegate. /// The type of the second argument passed to /// delegate. /// The type of the third argument passed to /// delegate. /// The delegate that begins the asynchronous operation. /// The delegate that ends the asynchronous operation. /// The first argument passed to the /// delegate. /// The second argument passed to the /// delegate. /// The third argument passed to the /// delegate. /// The TaskCreationOptions value that controls the behavior of the /// created Task. /// An object containing data to be used by the /// delegate. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument specifies an invalid TaskCreationOptions /// value. /// The created Task that /// represents the asynchronous operation. /// /// This method throws any exceptions thrown by the . /// public Task FromAsync( Func beginMethod, Func endMethod, TArg1 arg1, TArg2 arg2, TArg3 arg3, object state, TaskCreationOptions creationOptions) { return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, arg3, state, creationOptions); } // We need this logic broken out into a static method so that the similar TaskFactory.FromAsync() // method can access the logic w/o declaring a TaskFactory instance. internal static Task FromAsyncImpl(Func beginMethod, Func endFunction, Action endAction, TArg1 arg1, TArg2 arg2, TArg3 arg3, object state, TaskCreationOptions creationOptions) { if (beginMethod == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.beginMethod); if (endFunction == null && endAction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.endMethod); Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null"); TaskFactory.CheckFromAsyncOptions(creationOptions, true); Task promise = new Task(state, creationOptions); if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0); if (Task.s_asyncDebuggingEnabled) { Task.AddToActiveTasks(promise); } try { //if we don't require synchronization, a faster set result path is taken var asyncResult = beginMethod(arg1, arg2, arg3, iar => { if (!iar.CompletedSynchronously) FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true); }, state); if (asyncResult.CompletedSynchronously) { Debug.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false); } } catch { if (AsyncCausalityTracer.LoggingOn) AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error); if (Task.s_asyncDebuggingEnabled) { Task.RemoveFromActiveTasks(promise.Id); } // Make sure we don't leave the promise "dangling". promise.TrySetResult(default(TResult)); throw; } return promise; } /// /// Special internal-only FromAsync support used by System.IO to wrap /// APM implementations with minimal overhead, avoiding unnecessary closure /// and delegate allocations. /// /// Specifies the type of the instance on which the APM implementation lives. /// Specifies the type containing the arguments. /// The instance from which the begin and end methods are invoked. /// The begin method. /// The end method. /// The arguments. /// A task representing the asynchronous operation. internal static Task FromAsyncTrim( TInstance thisRef, TArgs args, Func beginMethod, Func endMethod) where TInstance : class { // Validate arguments, but only with asserts, as this is an internal only implementation. Debug.Assert(thisRef != null, "Expected a non-null thisRef"); Debug.Assert(beginMethod != null, "Expected a non-null beginMethod"); Debug.Assert(endMethod != null, "Expected a non-null endMethod"); // Create the promise and start the operation. // No try/catch is necessary here as we want exceptions to bubble out, and because // the task doesn't have AttachedToParent set on it, there's no need to complete it in // case of an exception occurring... we can just let it go unresolved. var promise = new FromAsyncTrimPromise(thisRef, endMethod); var asyncResult = beginMethod(thisRef, args, FromAsyncTrimPromise.s_completeFromAsyncResult, promise); // If the IAsyncResult completed asynchronously, completing the promise will be handled by the callback. // If it completed synchronously, we'll handle that here. if (asyncResult.CompletedSynchronously) { Debug.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization: false); } // Return the promise return promise; } /// /// A specialized task used by FromAsyncTrim. Stores relevant information as instance /// state so that we can avoid unnecessary closure/delegate allocations. /// /// Specifies the type of the instance on which the APM implementation lives. private sealed class FromAsyncTrimPromise : Task where TInstance : class { /// A cached delegate used as the callback for the BeginXx method. internal readonly static AsyncCallback s_completeFromAsyncResult = CompleteFromAsyncResult; /// A reference to the object on which the begin/end methods are invoked. private TInstance m_thisRef; /// The end method. private Func m_endMethod; /// Initializes the promise. /// A reference to the object on which the begin/end methods are invoked. /// The end method. internal FromAsyncTrimPromise(TInstance thisRef, Func endMethod) : base() { Contract.Requires(thisRef != null, "Expected a non-null thisRef"); Contract.Requires(endMethod != null, "Expected a non-null endMethod"); m_thisRef = thisRef; m_endMethod = endMethod; } /// /// Completes the asynchronous operation using information in the IAsyncResult. /// IAsyncResult.AsyncState neeeds to be the FromAsyncTrimPromise to complete. /// /// The IAsyncResult for the async operation. internal static void CompleteFromAsyncResult(IAsyncResult asyncResult) { // Validate argument if (asyncResult == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.asyncResult); Contract.EndContractBlock(); var promise = asyncResult.AsyncState as FromAsyncTrimPromise; if (promise == null) ThrowHelper.ThrowArgumentException(ExceptionResource.InvalidOperation_WrongAsyncResultOrEndCalledMultiple, ExceptionArgument.asyncResult); // Grab the relevant state and then null it out so that the task doesn't hold onto the state unnecessarily var thisRef = promise.m_thisRef; var endMethod = promise.m_endMethod; promise.m_thisRef = default(TInstance); promise.m_endMethod = null; if (endMethod == null) ThrowHelper.ThrowArgumentException(ExceptionResource.InvalidOperation_WrongAsyncResultOrEndCalledMultiple, ExceptionArgument.asyncResult); // Complete the promise. If the IAsyncResult completed synchronously, // we'll instead complete the promise at the call site. if (!asyncResult.CompletedSynchronously) { promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization:true); } } /// Completes the promise. /// /// true if synchronization is needed to completed the task; /// false if the task may be completed without synchronization /// because it hasn't been handed out. /// /// The target instance on which the end method should be called. /// The end method to call to retrieve the result. /// The IAsyncResult for the async operation. /// /// Whether completing the task requires synchronization. This should be true /// unless absolutely sure that the task has not yet been handed out to any consumers. /// internal void Complete( TInstance thisRef, Func endMethod, IAsyncResult asyncResult, bool requiresSynchronization) { Debug.Assert(!IsCompleted, "The task should not have been completed yet."); // Run the end method and complete the task bool successfullySet = false; try { var result = endMethod(thisRef, asyncResult); if (requiresSynchronization) { successfullySet = TrySetResult(result); } else { // If requiresSynchronization is false, we can use the DangerousSetResult // method, which uses no synchronization to complete the task. This is // only valid when the operation is completing synchronously such // that the task has not yet been handed out to any consumers. DangerousSetResult(result); successfullySet = true; } } catch (OperationCanceledException oce) { successfullySet = TrySetCanceled(oce.CancellationToken, oce); } catch (Exception exc) { successfullySet = TrySetException(exc); } Debug.Assert(successfullySet, "Expected the task to not yet be completed"); } } // Utility method to create a canceled future-style task. // Used by ContinueWhenAll/Any to bail out early on a pre-canceled token. private static Task CreateCanceledTask(TaskContinuationOptions continuationOptions, CancellationToken ct) { TaskCreationOptions tco; InternalTaskOptions dontcare; Task.CreationOptionsFromContinuationOptions(continuationOptions, out tco, out dontcare); return new Task(true, default(TResult), tco, ct); } // // ContinueWhenAll() methods // /// /// Creates a continuation Task /// that will be started upon the completion of a set of provided Tasks. /// /// The array of tasks from which to continue. /// The function delegate to execute when all tasks in /// the array have completed. /// The new continuation Task. /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. public Task ContinueWhenAll(Task[] tasks, Func continuationFunction) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of a set of provided Tasks. /// /// The array of tasks from which to continue. /// The function delegate to execute when all tasks in /// the array have completed. /// The CancellationToken /// that will be assigned to the new continuation task. /// The new continuation Task. /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The provided CancellationToken /// has already been disposed. /// public Task ContinueWhenAll(Task[] tasks, Func continuationFunction, CancellationToken cancellationToken) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of a set of provided Tasks. /// /// The array of tasks from which to continue. /// The function delegate to execute when all tasks in the array have completed. /// The /// TaskContinuationOptions value that controls the behavior of /// the created continuation Task. /// The new continuation Task. /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The exception that is thrown when the /// argument specifies an invalid TaskContinuationOptions /// value. /// /// The NotOn* and OnlyOn* TaskContinuationOptions, /// which constrain for which TaskStatus states a continuation /// will be executed, are illegal with ContinueWhenAll. /// public Task ContinueWhenAll(Task[] tasks, Func continuationFunction, TaskContinuationOptions continuationOptions) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of a set of provided Tasks. /// /// The array of tasks from which to continue. /// The function delegate to execute when all tasks in the array have completed. /// The CancellationToken /// that will be assigned to the new continuation task. /// The /// TaskContinuationOptions value that controls the behavior of /// the created continuation Task. /// The TaskScheduler /// that is used to schedule the created continuation Task. /// The new continuation Task. /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The exception that is thrown when the /// argument specifies an invalid TaskContinuationOptions /// value. /// The provided CancellationToken /// has already been disposed. /// /// /// The NotOn* and OnlyOn* TaskContinuationOptions, /// which constrain for which TaskStatus states a continuation /// will be executed, are illegal with ContinueWhenAll. /// public Task ContinueWhenAll(Task[] tasks, Func continuationFunction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of a set of provided Tasks. /// /// The type of the result of the antecedent . /// The array of tasks from which to continue. /// The function delegate to execute when all tasks in the /// array have completed. /// The new continuation . /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. public Task ContinueWhenAll(Task[] tasks, Func[], TResult> continuationFunction) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of a set of provided Tasks. /// /// The type of the result of the antecedent . /// The array of tasks from which to continue. /// The function delegate to execute when all tasks in the /// array have completed. /// The CancellationToken /// that will be assigned to the new continuation task. /// The new continuation . /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The provided CancellationToken /// has already been disposed. /// public Task ContinueWhenAll(Task[] tasks, Func[], TResult> continuationFunction, CancellationToken cancellationToken) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of a set of provided Tasks. /// /// The type of the result of the antecedent . /// The array of tasks from which to continue. /// The function delegate to execute when all tasks in the /// array have completed. /// The /// TaskContinuationOptions value that controls the behavior of /// the created continuation Task. /// The new continuation . /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The exception that is thrown when the /// argument specifies an invalid TaskContinuationOptions /// value. /// /// The NotOn* and OnlyOn* TaskContinuationOptions, /// which constrain for which TaskStatus states a continuation /// will be executed, are illegal with ContinueWhenAll. /// public Task ContinueWhenAll(Task[] tasks, Func[], TResult> continuationFunction, TaskContinuationOptions continuationOptions) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of a set of provided Tasks. /// /// The type of the result of the antecedent . /// The array of tasks from which to continue. /// The function delegate to execute when all tasks in the /// array have completed. /// The CancellationToken /// that will be assigned to the new continuation task. /// The /// TaskContinuationOptions value that controls the behavior of /// the created continuation Task. /// The TaskScheduler /// that is used to schedule the created continuation . /// The new continuation . /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The exception that is thrown when the /// argument specifies an invalid TaskContinuationOptions /// value. /// The provided CancellationToken /// has already been disposed. /// /// /// The NotOn* and OnlyOn* TaskContinuationOptions, /// which constrain for which TaskStatus states a continuation /// will be executed, are illegal with ContinueWhenAll. /// public Task ContinueWhenAll(Task[] tasks, Func[], TResult> continuationFunction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } // Core implementation of ContinueWhenAll -- the generic version // Note: if you make any changes to this method, please do the same to the non-generic version too. internal static Task ContinueWhenAllImpl(Task[] tasks, Func[], TResult> continuationFunction, Action[]> continuationAction, TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler) { // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); //ArgumentNullException of continuationFunction or continuationAction is checked by the caller Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null"); if (scheduler == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); Contract.EndContractBlock(); // Check tasks array and make defensive copy Task[] tasksCopy = TaskFactory.CheckMultiContinuationTasksAndCopy(tasks); // Bail early if cancellation has been requested. if (cancellationToken.IsCancellationRequested && ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0) ) { return CreateCanceledTask(continuationOptions, cancellationToken); } // Call common ContinueWhenAll() setup logic, extract starter task. var starter = TaskFactory.CommonCWAllLogic(tasksCopy); // returned continuation task, off of starter if (continuationFunction != null) { return starter.ContinueWith( // use a cached delegate GenericDelegateCache.CWAllFuncDelegate, continuationFunction, scheduler, cancellationToken, continuationOptions); } else { Debug.Assert(continuationAction != null); return starter.ContinueWith( // use a cached delegate GenericDelegateCache.CWAllActionDelegate, continuationAction, scheduler, cancellationToken, continuationOptions); } } // Core implementation of ContinueWhenAll -- the non-generic version // Note: if you make any changes to this method, please do the same to the generic version too. internal static Task ContinueWhenAllImpl(Task[] tasks, Func continuationFunction, Action continuationAction, TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler) { // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); //ArgumentNullException of continuationFunction or continuationAction is checked by the caller Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null"); if (scheduler == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); Contract.EndContractBlock(); // Check tasks array and make defensive copy Task[] tasksCopy = TaskFactory.CheckMultiContinuationTasksAndCopy(tasks); // Bail early if cancellation has been requested. if (cancellationToken.IsCancellationRequested && ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0) ) { return CreateCanceledTask(continuationOptions, cancellationToken); } // Perform common ContinueWhenAll() setup logic, extract starter task var starter = TaskFactory.CommonCWAllLogic(tasksCopy); // returned continuation task, off of starter if (continuationFunction != null) { return starter.ContinueWith( //the following delegate avoids closure capture as much as possible //completedTasks.Result == tasksCopy; //state == continuationFunction (completedTasks, state) => { completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary(); return ((Func)state)(completedTasks.Result); }, continuationFunction, scheduler, cancellationToken, continuationOptions); } else { Debug.Assert(continuationAction != null); return starter.ContinueWith( //the following delegate avoids closure capture as much as possible //completedTasks.Result == tasksCopy; //state == continuationAction (completedTasks, state) => { completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary(); ((Action)state)(completedTasks.Result); return default(TResult); }, continuationAction, scheduler, cancellationToken, continuationOptions); } } // // ContinueWhenAny() methods // /// /// Creates a continuation Task /// that will be started upon the completion of any Task in the provided set. /// /// The array of tasks from which to continue when one task completes. /// The function delegate to execute when one task in the array completes. /// The new continuation Task. /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. public Task ContinueWhenAny(Task[] tasks, Func continuationFunction) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of any Task in the provided set. /// /// The array of tasks from which to continue when one task completes. /// The function delegate to execute when one task in the array completes. /// The CancellationToken /// that will be assigned to the new continuation task. /// The new continuation Task. /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The provided CancellationToken /// has already been disposed. /// public Task ContinueWhenAny(Task[] tasks, Func continuationFunction, CancellationToken cancellationToken) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of any Task in the provided set. /// /// The array of tasks from which to continue when one task completes. /// The function delegate to execute when one task in the array completes. /// The /// TaskContinuationOptions value that controls the behavior of /// the created continuation Task. /// The new continuation Task. /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The exception that is thrown when the /// argument specifies an invalid TaskContinuationOptions /// value. /// /// The NotOn* and OnlyOn* TaskContinuationOptions, /// which constrain for which TaskStatus states a continuation /// will be executed, are illegal with ContinueWhenAny. /// public Task ContinueWhenAny(Task[] tasks, Func continuationFunction, TaskContinuationOptions continuationOptions) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of any Task in the provided set. /// /// The array of tasks from which to continue when one task completes. /// The function delegate to execute when one task in the array completes. /// The CancellationToken /// that will be assigned to the new continuation task. /// The /// TaskContinuationOptions value that controls the behavior of /// the created continuation Task. /// The TaskScheduler /// that is used to schedule the created continuation Task. /// The new continuation Task. /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The exception that is thrown when the /// argument specifies an invalid TaskContinuationOptions /// value. /// The provided CancellationToken /// has already been disposed. /// /// /// The NotOn* and OnlyOn* TaskContinuationOptions, /// which constrain for which TaskStatus states a continuation /// will be executed, are illegal with ContinueWhenAny. /// public Task ContinueWhenAny(Task[] tasks, Func continuationFunction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of any Task in the provided set. /// /// The type of the result of the antecedent . /// The array of tasks from which to continue when one task completes. /// The function delegate to execute when one task in the /// array completes. /// The new continuation . /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. public Task ContinueWhenAny(Task[] tasks, Func, TResult> continuationFunction) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of any Task in the provided set. /// /// The type of the result of the antecedent . /// The array of tasks from which to continue when one task completes. /// The function delegate to execute when one task in the /// array completes. /// The CancellationToken /// that will be assigned to the new continuation task. /// The new continuation . /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The provided CancellationToken /// has already been disposed. /// public Task ContinueWhenAny(Task[] tasks, Func, TResult> continuationFunction, CancellationToken cancellationToken) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of any Task in the provided set. /// /// The type of the result of the antecedent . /// The array of tasks from which to continue when one task completes. /// The function delegate to execute when one task in the /// array completes. /// The /// TaskContinuationOptions value that controls the behavior of /// the created continuation Task. /// The new continuation . /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The exception that is thrown when the /// argument specifies an invalid TaskContinuationOptions /// value. /// /// The NotOn* and OnlyOn* TaskContinuationOptions, /// which constrain for which TaskStatus states a continuation /// will be executed, are illegal with ContinueWhenAny. /// public Task ContinueWhenAny(Task[] tasks, Func, TResult> continuationFunction, TaskContinuationOptions continuationOptions) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler); } /// /// Creates a continuation Task /// that will be started upon the completion of any Task in the provided set. /// /// The type of the result of the antecedent . /// The array of tasks from which to continue when one task completes. /// The function delegate to execute when one task in the /// array completes. /// The CancellationToken /// that will be assigned to the new continuation task. /// The /// TaskContinuationOptions value that controls the behavior of /// the created continuation Task. /// The TaskScheduler /// that is used to schedule the created continuation . /// The new continuation . /// The exception that is thrown when the /// array is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// argument is null. /// The exception that is thrown when the /// array contains a null value. /// The exception that is thrown when the /// array is empty. /// The exception that is thrown when the /// argument specifies an invalid TaskContinuationOptions /// value. /// The provided CancellationToken /// has already been disposed. /// /// /// The NotOn* and OnlyOn* TaskContinuationOptions, /// which constrain for which TaskStatus states a continuation /// will be executed, are illegal with ContinueWhenAny. /// public Task ContinueWhenAny(Task[] tasks, Func, TResult> continuationFunction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) { if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); Contract.EndContractBlock(); return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler); } // Core implementation of ContinueWhenAny, non-generic version // Note: if you make any changes to this method, be sure to do the same to the generic version internal static Task ContinueWhenAnyImpl(Task[] tasks, Func continuationFunction, Action continuationAction, TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler) { // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); if(tasks.Length == 0) ThrowHelper.ThrowArgumentException( ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks); //ArgumentNullException of continuationFunction or continuationAction is checked by the caller Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null"); if (scheduler == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); Contract.EndContractBlock(); // Call common ContinueWhenAny() setup logic, extract starter Task starter = TaskFactory.CommonCWAnyLogic(tasks); // Bail early if cancellation has been requested. if (cancellationToken.IsCancellationRequested && ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0) ) { return CreateCanceledTask(continuationOptions, cancellationToken); } // returned continuation task, off of starter if (continuationFunction != null) { return starter.ContinueWith( //the following delegate avoids closure capture as much as possible //completedTask.Result is the winning task; state == continuationAction (completedTask, state) => { return ((Func)state)(completedTask.Result); }, continuationFunction, scheduler, cancellationToken, continuationOptions); } else { Debug.Assert(continuationAction != null); return starter.ContinueWith( //the following delegate avoids closure capture as much as possible //completedTask.Result is the winning task; state == continuationAction (completedTask, state) => { ((Action)state)(completedTask.Result); return default(TResult); }, continuationAction, scheduler, cancellationToken, continuationOptions); } } // Core implementation of ContinueWhenAny, generic version // Note: if you make any changes to this method, be sure to do the same to the non-generic version internal static Task ContinueWhenAnyImpl(Task[] tasks, Func, TResult> continuationFunction, Action> continuationAction, TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler) { // check arguments TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions); if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); if (tasks.Length == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_EmptyTaskList, ExceptionArgument.tasks); //ArgumentNullException of continuationFunction or continuationAction is checked by the caller Contract.Requires((continuationFunction != null) != (continuationAction != null), "Expected exactly one of endFunction/endAction to be non-null"); if (scheduler == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); Contract.EndContractBlock(); // Call common ContinueWhenAny setup logic, extract starter var starter = TaskFactory.CommonCWAnyLogic(tasks); // Bail early if cancellation has been requested. if (cancellationToken.IsCancellationRequested && ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0) ) { return CreateCanceledTask(continuationOptions, cancellationToken); } // returned continuation task, off of starter if (continuationFunction != null) { return starter.ContinueWith( // Use a cached delegate GenericDelegateCache.CWAnyFuncDelegate, continuationFunction, scheduler, cancellationToken, continuationOptions); } else { Debug.Assert(continuationAction != null); return starter.ContinueWith( // Use a cached delegate GenericDelegateCache.CWAnyActionDelegate, continuationAction, scheduler, cancellationToken, continuationOptions); } } } // For the ContinueWhenAnyImpl/ContinueWhenAllImpl methods that are generic on TAntecedentResult, // the compiler won't cache the internal ContinueWith delegate because it is generic on both // TAntecedentResult and TResult. The GenericDelegateCache serves as a cache for those delegates. internal static class GenericDelegateCache { // ContinueWith delegate for TaskFactory.ContinueWhenAnyImpl(non-null continuationFunction) internal static Func, object, TResult> CWAnyFuncDelegate = (Task wrappedWinner, object state) => { var func = (Func, TResult>)state; var arg = (Task)wrappedWinner.Result; return func(arg); }; // ContinueWith delegate for TaskFactory.ContinueWhenAnyImpl(non-null continuationAction) internal static Func, object, TResult> CWAnyActionDelegate = (Task wrappedWinner, object state) => { var action = (Action>)state; var arg = (Task)wrappedWinner.Result; action(arg); return default(TResult); }; // ContinueWith delegate for TaskFactory.ContinueWhenAllImpl(non-null continuationFunction) internal static Func[]>, object, TResult> CWAllFuncDelegate = (Task[]> wrappedAntecedents, object state) => { wrappedAntecedents.NotifyDebuggerOfWaitCompletionIfNecessary(); var func = (Func[], TResult>)state; return func(wrappedAntecedents.Result); }; // ContinueWith delegate for TaskFactory.ContinueWhenAllImpl(non-null continuationAction) internal static Func[]>, object, TResult> CWAllActionDelegate = (Task[]> wrappedAntecedents, object state) => { wrappedAntecedents.NotifyDebuggerOfWaitCompletionIfNecessary(); var action = (Action[]>)state; action(wrappedAntecedents.Result); return default(TResult); }; } }