diff options
Diffstat (limited to 'src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs')
-rw-r--r-- | src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs | 2303 |
1 files changed, 2303 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs new file mode 100644 index 0000000000..b1f634c707 --- /dev/null +++ b/src/mscorlib/src/System/Threading/Tasks/FutureFactory.cs @@ -0,0 +1,2303 @@ +// 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<TResult> encodes common factory patterns into helper methods. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System; +using System.Security; +using System.Security.Permissions; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Diagnostics.Contracts; +using System.Runtime.Versioning; + +namespace System.Threading.Tasks +{ + /// <summary> + /// Provides support for creating and scheduling + /// <see cref="T:System.Threading.Tasks.Task{TResult}">Task{TResult}</see> objects. + /// </summary> + /// <typeparam name="TResult">The type of the results that are available though + /// the <see cref="T:System.Threading.Tasks.Task{TResult}">Task{TResult}</see> objects that are associated with + /// the methods in this class.</typeparam> + /// <remarks> + /// <para> + /// There are many common patterns for which tasks are relevant. The <see cref="TaskFactory{TResult}"/> + /// class encodes some of these patterns into methods that pick up default settings, which are + /// configurable through its constructors. + /// </para> + /// <para> + /// A default instance of <see cref="TaskFactory{TResult}"/> is available through the + /// <see cref="System.Threading.Tasks.Task{TResult}.Factory">Task{TResult}.Factory</see> property. + /// </para> + /// </remarks> + [HostProtection(Synchronization = true, ExternalThreading = true)] + public class TaskFactory<TResult> + { + // 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 */ + + /// <summary> + /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the default configuration. + /// </summary> + /// <remarks> + /// This constructor creates a <see cref="TaskFactory{TResult}"/> instance with a default configuration. The + /// <see cref="TaskCreationOptions"/> property is initialized to + /// <see cref="System.Threading.Tasks.TaskCreationOptions.None">TaskCreationOptions.None</see>, the + /// <see cref="TaskContinuationOptions"/> property is initialized to <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.None">TaskContinuationOptions.None</see>, + /// and the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is + /// initialized to the current scheduler (see <see + /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>). + /// </remarks> + public TaskFactory() + : this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, null) + { + } + + /// <summary> + /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the default configuration. + /// </summary> + /// <param name="cancellationToken">The default <see cref="CancellationToken"/> that will be assigned + /// to tasks created by this <see cref="TaskFactory"/> unless another CancellationToken is explicitly specified + /// while calling the factory methods.</param> + /// <remarks> + /// This constructor creates a <see cref="TaskFactory{TResult}"/> instance with a default configuration. The + /// <see cref="TaskCreationOptions"/> property is initialized to + /// <see cref="System.Threading.Tasks.TaskCreationOptions.None">TaskCreationOptions.None</see>, the + /// <see cref="TaskContinuationOptions"/> property is initialized to <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.None">TaskContinuationOptions.None</see>, + /// and the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is + /// initialized to the current scheduler (see <see + /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>). + /// </remarks> + public TaskFactory(CancellationToken cancellationToken) + : this(cancellationToken, TaskCreationOptions.None, TaskContinuationOptions.None, null) + { + } + + /// <summary> + /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the specified configuration. + /// </summary> + /// <param name="scheduler"> + /// The <see cref="System.Threading.Tasks.TaskScheduler"> + /// TaskScheduler</see> to use to schedule any tasks created with this TaskFactory{TResult}. A null value + /// indicates that the current TaskScheduler should be used. + /// </param> + /// <remarks> + /// With this constructor, the + /// <see cref="TaskCreationOptions"/> property is initialized to + /// <see cref="System.Threading.Tasks.TaskCreationOptions.None">TaskCreationOptions.None</see>, the + /// <see cref="TaskContinuationOptions"/> property is initialized to <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.None">TaskContinuationOptions.None</see>, + /// and the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is + /// initialized to <paramref name="scheduler"/>, unless it's null, in which case the property is + /// initialized to the current scheduler (see <see + /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>). + /// </remarks> + public TaskFactory(TaskScheduler scheduler) // null means to use TaskScheduler.Current + : this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, scheduler) + { + } + + /// <summary> + /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the specified configuration. + /// </summary> + /// <param name="creationOptions"> + /// The default <see cref="System.Threading.Tasks.TaskCreationOptions"> + /// TaskCreationOptions</see> to use when creating tasks with this TaskFactory{TResult}. + /// </param> + /// <param name="continuationOptions"> + /// The default <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> to use when creating continuation tasks with this TaskFactory{TResult}. + /// </param> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The exception that is thrown when the + /// <paramref name="creationOptions"/> argument or the <paramref name="continuationOptions"/> + /// argument specifies an invalid value. + /// </exception> + /// <remarks> + /// With this constructor, the + /// <see cref="TaskCreationOptions"/> property is initialized to <paramref name="creationOptions"/>, + /// the + /// <see cref="TaskContinuationOptions"/> property is initialized to <paramref + /// name="continuationOptions"/>, and the <see + /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is initialized to the + /// current scheduler (see <see + /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>). + /// </remarks> + public TaskFactory(TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions) + : this(default(CancellationToken), creationOptions, continuationOptions, null) + { + } + + /// <summary> + /// Initializes a <see cref="TaskFactory{TResult}"/> instance with the specified configuration. + /// </summary> + /// <param name="cancellationToken">The default <see cref="CancellationToken"/> that will be assigned + /// to tasks created by this <see cref="TaskFactory"/> unless another CancellationToken is explicitly specified + /// while calling the factory methods.</param> + /// <param name="creationOptions"> + /// The default <see cref="System.Threading.Tasks.TaskCreationOptions"> + /// TaskCreationOptions</see> to use when creating tasks with this TaskFactory{TResult}. + /// </param> + /// <param name="continuationOptions"> + /// The default <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> to use when creating continuation tasks with this TaskFactory{TResult}. + /// </param> + /// <param name="scheduler"> + /// The default <see cref="System.Threading.Tasks.TaskScheduler"> + /// TaskScheduler</see> to use to schedule any Tasks created with this TaskFactory{TResult}. A null value + /// indicates that TaskScheduler.Current should be used. + /// </param> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The exception that is thrown when the + /// <paramref name="creationOptions"/> argument or the <paramref name="continuationOptions"/> + /// argumentspecifies an invalid value. + /// </exception> + /// <remarks> + /// With this constructor, the + /// <see cref="TaskCreationOptions"/> property is initialized to <paramref name="creationOptions"/>, + /// the + /// <see cref="TaskContinuationOptions"/> property is initialized to <paramref + /// name="continuationOptions"/>, and the <see + /// cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> property is initialized to + /// <paramref name="scheduler"/>, unless it's null, in which case the property is initialized to the + /// current scheduler (see <see + /// cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see>). + /// </remarks> + 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 */ + + /// <summary> + /// Gets the default <see cref="System.Threading.CancellationToken">CancellationToken</see> of this + /// TaskFactory. + /// </summary> + /// <remarks> + /// This property returns the default <see cref="CancellationToken"/> 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. + /// </remarks> + public CancellationToken CancellationToken { get { return m_defaultCancellationToken; } } + + /// <summary> + /// Gets the <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> of this + /// TaskFactory{TResult}. + /// </summary> + /// <remarks> + /// 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, <see cref="System.Threading.Tasks.TaskScheduler.Current">TaskScheduler.Current</see> + /// will be used. + /// </remarks> + public TaskScheduler Scheduler { get { return m_defaultScheduler; } } + + /// <summary> + /// Gets the <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions + /// </see> value of this TaskFactory{TResult}. + /// </summary> + /// <remarks> + /// 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. + /// </remarks> + public TaskCreationOptions CreationOptions { get { return m_defaultCreationOptions; } } + + /// <summary> + /// Gets the <see cref="System.Threading.Tasks.TaskCreationOptions">TaskContinuationOptions + /// </see> value of this TaskFactory{TResult}. + /// </summary> + /// <remarks> + /// 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. + /// </remarks> + public TaskContinuationOptions ContinuationOptions { get { return m_defaultContinuationOptions; } } + + + /* StartNew */ + + /// <summary> + /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>. + /// </summary> + /// <param name="function">A function delegate that returns the future result to be available through + /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="function"/> + /// argument is null.</exception> + /// <remarks> + /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one + /// of its constructors and then calling + /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. + /// However, unless creation and scheduling must be separated, StartNew is the recommended approach + /// for both simplicity and performance. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> StartNew(Func<TResult> function) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + Task currTask = Task.InternalCurrent; + return Task<TResult>.StartNew(currTask, function, m_defaultCancellationToken, + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark); + } + + /// <summary> + /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>. + /// </summary> + /// <param name="function">A function delegate that returns the future result to be available through + /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param> + /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="function"/> + /// argument is null.</exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + /// <remarks> + /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one + /// of its constructors and then calling + /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. + /// However, unless creation and scheduling must be separated, StartNew is the recommended approach + /// for both simplicity and performance. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> StartNew(Func<TResult> function, CancellationToken cancellationToken) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + Task currTask = Task.InternalCurrent; + return Task<TResult>.StartNew(currTask, function, cancellationToken, + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark); + } + + /// <summary> + /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>. + /// </summary> + /// <param name="function">A function delegate that returns the future result to be available through + /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the + /// created + /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="function"/> + /// argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <remarks> + /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one + /// of its constructors and then calling + /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. + /// However, unless creation and scheduling must be separated, StartNew is the recommended approach + /// for both simplicity and performance. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> StartNew(Func<TResult> function, TaskCreationOptions creationOptions) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + Task currTask = Task.InternalCurrent; + return Task<TResult>.StartNew(currTask, function, m_defaultCancellationToken, + creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark); + } + + /// <summary> + /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>. + /// </summary> + /// <param name="function">A function delegate that returns the future result to be available through + /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the + /// created + /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param> + /// <param name="scheduler">The <see + /// cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see> + /// that is used to schedule the created <see cref="T:System.Threading.Tasks.Task{TResult}"> + /// Task{TResult}</see>.</param> + /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="function"/> + /// argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="scheduler"/> + /// argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + /// <remarks> + /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one + /// of its constructors and then calling + /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. + /// However, unless creation and scheduling must be separated, StartNew is the recommended approach + /// for both simplicity and performance. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> StartNew(Func<TResult> function, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return Task<TResult>.StartNew( + Task.InternalCurrentIfAttached(creationOptions), function, cancellationToken, + creationOptions, InternalTaskOptions.None, scheduler, ref stackMark); + } + + /// <summary> + /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>. + /// </summary> + /// <param name="function">A function delegate that returns the future result to be available through + /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="state">An object containing data to be used by the <paramref name="function"/> + /// delegate.</param> + /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="function"/> + /// argument is null.</exception> + /// <remarks> + /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one + /// of its constructors and then calling + /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. + /// However, unless creation and scheduling must be separated, StartNew is the recommended approach + /// for both simplicity and performance. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> StartNew(Func<Object, TResult> function, Object state) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + Task currTask = Task.InternalCurrent; + return Task<TResult>.StartNew(currTask, function, state, m_defaultCancellationToken, + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark); + } + + /// <summary> + /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>. + /// </summary> + /// <param name="function">A function delegate that returns the future result to be available through + /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="state">An object containing data to be used by the <paramref name="function"/> + /// delegate.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param> + /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="function"/> + /// argument is null.</exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + /// <remarks> + /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one + /// of its constructors and then calling + /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. + /// However, unless creation and scheduling must be separated, StartNew is the recommended approach + /// for both simplicity and performance. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> StartNew(Func<Object, TResult> function, Object state, CancellationToken cancellationToken) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + Task currTask = Task.InternalCurrent; + return Task<TResult>.StartNew(currTask, function, state, cancellationToken, + m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark); + } + + /// <summary> + /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>. + /// </summary> + /// <param name="function">A function delegate that returns the future result to be available through + /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="state">An object containing data to be used by the <paramref name="function"/> + /// delegate.</param> + /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the + /// created + /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="function"/> + /// argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <remarks> + /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one + /// of its constructors and then calling + /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. + /// However, unless creation and scheduling must be separated, StartNew is the recommended approach + /// for both simplicity and performance. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> StartNew(Func<Object, TResult> function, Object state, TaskCreationOptions creationOptions) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + Task currTask = Task.InternalCurrent; + return Task<TResult>.StartNew(currTask, function, state, m_defaultCancellationToken, + creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark); + } + + /// <summary> + /// Creates and starts a <see cref="T:System.Threading.Tasks.Task{TResult}"/>. + /// </summary> + /// <param name="function">A function delegate that returns the future result to be available through + /// the <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="state">An object containing data to be used by the <paramref name="function"/> + /// delegate.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param> + /// <param name="creationOptions">A TaskCreationOptions value that controls the behavior of the + /// created + /// <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <param name="scheduler">The <see + /// cref="T:System.Threading.Tasks.TaskScheduler">TaskScheduler</see> + /// that is used to schedule the created <see cref="T:System.Threading.Tasks.Task{TResult}"> + /// Task{TResult}</see>.</param> + /// <returns>The started <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="function"/> + /// argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the <paramref + /// name="scheduler"/> + /// argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + /// <remarks> + /// Calling StartNew is functionally equivalent to creating a <see cref="Task{TResult}"/> using one + /// of its constructors and then calling + /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> to schedule it for execution. + /// However, unless creation and scheduling must be separated, StartNew is the recommended approach + /// for both simplicity and performance. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> StartNew(Func<Object, TResult> function, Object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return Task<TResult>.StartNew(Task.InternalCurrentIfAttached(creationOptions), function, state, cancellationToken, + creationOptions, InternalTaskOptions.None, scheduler, ref stackMark); + } + + // + // 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<IAsyncResult, TResult> endFunction, + Action<IAsyncResult> endAction, + Task<TResult> 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); + } + } + + + } + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that executes an end + /// method function when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes. + /// </summary> + /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the + /// <paramref name="endMethod"/>.</param> + /// <param name="endMethod">The function delegate that processes the completed <paramref + /// name="asyncResult"/>.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="asyncResult"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <returns>A <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents the + /// asynchronous operation.</returns> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> FromAsync(IAsyncResult asyncResult, Func<IAsyncResult, TResult> endMethod) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return FromAsyncImpl(asyncResult, endMethod, null, m_defaultCreationOptions, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that executes an end + /// method function when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes. + /// </summary> + /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the + /// <paramref name="endMethod"/>.</param> + /// <param name="endMethod">The function delegate that processes the completed <paramref + /// name="asyncResult"/>.</param> + /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the + /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="asyncResult"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <returns>A <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents the + /// asynchronous operation.</returns> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> FromAsync( + IAsyncResult asyncResult, + Func<IAsyncResult, TResult> endMethod, + TaskCreationOptions creationOptions) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return FromAsyncImpl(asyncResult, endMethod, null, creationOptions, DefaultScheduler, ref stackMark); + } + + + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that executes an end + /// method function when a specified <see cref="T:System.IAsyncResult">IAsyncResult</see> completes. + /// </summary> + /// <param name="asyncResult">The IAsyncResult whose completion should trigger the processing of the + /// <paramref name="endMethod"/>.</param> + /// <param name="endMethod">The function delegate that processes the completed <paramref + /// name="asyncResult"/>.</param> + /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> + /// that is used to schedule the task that executes the end method.</param> + /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the + /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="asyncResult"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="scheduler"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <returns>A <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents the + /// asynchronous operation.</returns> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> FromAsync( + IAsyncResult asyncResult, + Func<IAsyncResult, TResult> endMethod, + TaskCreationOptions creationOptions, + TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return FromAsyncImpl(asyncResult, endMethod, null, creationOptions, scheduler, ref stackMark); + } + + // internal overload that supports StackCrawlMark + // We also 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<TResult> instance. + internal static Task<TResult> FromAsyncImpl( + IAsyncResult asyncResult, + Func<IAsyncResult, TResult> endFunction, + Action<IAsyncResult> endAction, + TaskCreationOptions creationOptions, + TaskScheduler scheduler, + ref StackCrawlMark stackMark) + { + 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<TResult> promise = new Task<TResult>((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(delegate + { + FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization:true); + }, + (object)null, null, + default(CancellationToken), TaskCreationOptions.None, InternalTaskOptions.None, null, ref stackMark); + + 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; + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of + /// begin and end methods that conform to the Asynchronous Programming Model pattern. + /// </summary> + /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param> + /// <param name="endMethod">The delegate that ends the asynchronous operation.</param> + /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/> + /// delegate.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="beginMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that + /// represents the asynchronous operation.</returns> + /// <remarks> + /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>. + /// </remarks> + public Task<TResult> FromAsync( + Func<AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> endMethod, object state) + { + return FromAsyncImpl(beginMethod, endMethod, null, state, m_defaultCreationOptions); + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of + /// begin and end methods that conform to the Asynchronous Programming Model pattern. + /// </summary> + /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param> + /// <param name="endMethod">The delegate that ends the asynchronous operation.</param> + /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the + /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/> + /// delegate.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="beginMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that + /// represents the asynchronous operation.</returns> + /// <remarks> + /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>. + /// </remarks> + public Task<TResult> FromAsync( + Func<AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> 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<TResult> instance. + internal static Task<TResult> FromAsyncImpl(Func<AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> endFunction, Action<IAsyncResult> 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<TResult> promise = new Task<TResult>(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 + { + // Do NOT change the code below. + // 4.5 relies on the fact that IAsyncResult CompletedSynchronously flag needs to be set correctly, + // sadly this has not been the case that is why the behaviour from 4.5 broke 4.0 buggy apps. Any other + // change will likely brake 4.5 behavior so if possible never touch this code again. + if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) + { + //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) + { + Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); + FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false); + } + } + else + { + //This is the original 4.0 behaviour + var asyncResult = beginMethod(iar => + { + FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true); + }, state); + } + } + 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; + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of + /// begin and end methods that conform to the Asynchronous Programming Model pattern. + /// </summary> + /// <typeparam name="TArg1">The type of the first argument passed to the <paramref + /// name="beginMethod"/> delegate.</typeparam> + /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param> + /// <param name="endMethod">The delegate that ends the asynchronous operation.</param> + /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/> + /// delegate.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="beginMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that + /// represents the asynchronous operation.</returns> + /// <remarks> + /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>. + /// </remarks> + public Task<TResult> FromAsync<TArg1>( + Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> endMethod, + TArg1 arg1, object state) + { + return FromAsyncImpl(beginMethod, endMethod, null, arg1, state, m_defaultCreationOptions); + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of + /// begin and end methods that conform to the Asynchronous Programming Model pattern. + /// </summary> + /// <typeparam name="TArg1">The type of the first argument passed to the <paramref + /// name="beginMethod"/> delegate.</typeparam> + /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param> + /// <param name="endMethod">The delegate that ends the asynchronous operation.</param> + /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the + /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/> + /// delegate.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="beginMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that + /// represents the asynchronous operation.</returns> + /// <remarks> + /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>. + /// </remarks> + public Task<TResult> FromAsync<TArg1>( + Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> 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<TResult> instance. + internal static Task<TResult> FromAsyncImpl<TArg1>(Func<TArg1, AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> endFunction, Action<IAsyncResult> 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<TResult> promise = new Task<TResult>(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 + { + // Do NOT change the code below. + // 4.5 relies on the fact that IAsyncResult CompletedSynchronously flag needs to be set correctly, + // sadly this has not been the case that is why the behaviour from 4.5 broke 4.0 buggy apps. Any other + // change will likely brake 4.5 behavior so if possible never touch this code again. + if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) + { + //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) + { + Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); + FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false); + } + } + else + { + //quirk for previous versions + var asyncResult = beginMethod(arg1, iar => + { + FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true); + }, state); + } + } + 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; + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of + /// begin and end methods that conform to the Asynchronous Programming Model pattern. + /// </summary> + /// <typeparam name="TArg1">The type of the first argument passed to the <paramref + /// name="beginMethod"/> delegate.</typeparam> + /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/> + /// delegate.</typeparam> + /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param> + /// <param name="endMethod">The delegate that ends the asynchronous operation.</param> + /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/> + /// delegate.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="beginMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that + /// represents the asynchronous operation.</returns> + /// <remarks> + /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>. + /// </remarks> + public Task<TResult> FromAsync<TArg1, TArg2>( + Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> endMethod, + TArg1 arg1, TArg2 arg2, object state) + { + return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, state, m_defaultCreationOptions); + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of + /// begin and end methods that conform to the Asynchronous Programming Model pattern. + /// </summary> + /// <typeparam name="TArg1">The type of the first argument passed to the <paramref + /// name="beginMethod"/> delegate.</typeparam> + /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/> + /// delegate.</typeparam> + /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param> + /// <param name="endMethod">The delegate that ends the asynchronous operation.</param> + /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the + /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/> + /// delegate.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="beginMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that + /// represents the asynchronous operation.</returns> + /// <remarks> + /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>. + /// </remarks> + public Task<TResult> FromAsync<TArg1, TArg2>( + Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> 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<TResult> instance. + internal static Task<TResult> FromAsyncImpl<TArg1, TArg2>(Func<TArg1, TArg2, AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> endFunction, Action<IAsyncResult> 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<TResult> promise = new Task<TResult>(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 + { + // Do NOT change the code below. + // 4.5 relies on the fact that IAsyncResult CompletedSynchronously flag needs to be set correctly, + // sadly this has not been the case that is why the behaviour from 4.5 broke 4.0 buggy apps. Any other + // change will likely brake 4.5 behavior so if possible never touch this code again. + if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) + { + //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) + { + Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); + FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false); + } + } + else + { + //quirk for previous versions + var asyncResult = beginMethod(arg1, arg2, iar => + { + FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true); + }, state); + } + } + 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; + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of + /// begin and end methods that conform to the Asynchronous Programming Model pattern. + /// </summary> + /// <typeparam name="TArg1">The type of the first argument passed to the <paramref + /// name="beginMethod"/> delegate.</typeparam> + /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/> + /// delegate.</typeparam> + /// <typeparam name="TArg3">The type of the third argument passed to <paramref name="beginMethod"/> + /// delegate.</typeparam> + /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param> + /// <param name="endMethod">The delegate that ends the asynchronous operation.</param> + /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="arg3">The third argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/> + /// delegate.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="beginMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that + /// represents the asynchronous operation.</returns> + /// <remarks> + /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>. + /// </remarks> + public Task<TResult> FromAsync<TArg1, TArg2, TArg3>( + Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> endMethod, + TArg1 arg1, TArg2 arg2, TArg3 arg3, object state) + { + return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, arg3, state, m_defaultCreationOptions); + } + + /// <summary> + /// Creates a <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that represents a pair of + /// begin and end methods that conform to the Asynchronous Programming Model pattern. + /// </summary> + /// <typeparam name="TArg1">The type of the first argument passed to the <paramref + /// name="beginMethod"/> delegate.</typeparam> + /// <typeparam name="TArg2">The type of the second argument passed to <paramref name="beginMethod"/> + /// delegate.</typeparam> + /// <typeparam name="TArg3">The type of the third argument passed to <paramref name="beginMethod"/> + /// delegate.</typeparam> + /// <param name="beginMethod">The delegate that begins the asynchronous operation.</param> + /// <param name="endMethod">The delegate that ends the asynchronous operation.</param> + /// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="arg3">The third argument passed to the <paramref name="beginMethod"/> + /// delegate.</param> + /// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the + /// created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/> + /// delegate.</param> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="beginMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="endMethod"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="creationOptions"/> argument specifies an invalid TaskCreationOptions + /// value.</exception> + /// <returns>The created <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> that + /// represents the asynchronous operation.</returns> + /// <remarks> + /// This method throws any exceptions thrown by the <paramref name="beginMethod"/>. + /// </remarks> + public Task<TResult> FromAsync<TArg1, TArg2, TArg3>( + Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> 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<TResult> instance. + internal static Task<TResult> FromAsyncImpl<TArg1, TArg2, TArg3>(Func<TArg1, TArg2, TArg3, AsyncCallback, object, IAsyncResult> beginMethod, + Func<IAsyncResult, TResult> endFunction, Action<IAsyncResult> 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<TResult> promise = new Task<TResult>(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 + { + // Do NOT change the code below. + // 4.5 relies on the fact that IAsyncResult CompletedSynchronously flag needs to be set correctly, + // sadly this has not been the case that is why the behaviour from 4.5 broke 4.0 buggy apps. Any other + // change will likely brake 4.5 behavior so if possible never touch this code again. + if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) + { + //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) + { + Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); + FromAsyncCoreLogic(asyncResult, endFunction, endAction, promise, requiresSynchronization: false); + } + } + else + { + //quirk for previous versions + var asyncResult = beginMethod(arg1, arg2, arg3, iar => + { + FromAsyncCoreLogic(iar, endFunction, endAction, promise, requiresSynchronization: true); + }, state); + } + } + 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; + } + + /// <summary> + /// Special internal-only FromAsync support used by System.IO to wrap + /// APM implementations with minimal overhead, avoiding unnecessary closure + /// and delegate allocations. + /// </summary> + /// <typeparam name="TInstance">Specifies the type of the instance on which the APM implementation lives.</typeparam> + /// <typeparam name="TArg1">Specifies the type containing the arguments.</typeparam> + /// <param name="thisRef">The instance from which the begin and end methods are invoked.</param> + /// <param name="beginMethod">The begin method.</param> + /// <param name="endMethod">The end method.</param> + /// <param name="args">The arguments.</param> + /// <returns>A task representing the asynchronous operation.</returns> + internal static Task<TResult> FromAsyncTrim<TInstance, TArgs>( + TInstance thisRef, TArgs args, + Func<TInstance, TArgs, AsyncCallback, object, IAsyncResult> beginMethod, + Func<TInstance, IAsyncResult, TResult> endMethod) + where TInstance : class + { + // Validate arguments, but only with asserts, as this is an internal only implementation. + Contract.Assert(thisRef != null, "Expected a non-null thisRef"); + Contract.Assert(beginMethod != null, "Expected a non-null beginMethod"); + Contract.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<TInstance>(thisRef, endMethod); + var asyncResult = beginMethod(thisRef, args, FromAsyncTrimPromise<TInstance>.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) + { + Contract.Assert(asyncResult.IsCompleted, "If the operation completed synchronously, it must be completed."); + promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization: false); + } + + // Return the promise + return promise; + } + + /// <summary> + /// A specialized task used by FromAsyncTrim. Stores relevant information as instance + /// state so that we can avoid unnecessary closure/delegate allocations. + /// </summary> + /// <typeparam name="TInstance">Specifies the type of the instance on which the APM implementation lives.</typeparam> + private sealed class FromAsyncTrimPromise<TInstance> : Task<TResult> where TInstance : class + { + /// <summary>A cached delegate used as the callback for the BeginXx method.</summary> + internal readonly static AsyncCallback s_completeFromAsyncResult = CompleteFromAsyncResult; + + /// <summary>A reference to the object on which the begin/end methods are invoked.</summary> + private TInstance m_thisRef; + /// <summary>The end method.</summary> + private Func<TInstance, IAsyncResult, TResult> m_endMethod; + + /// <summary>Initializes the promise.</summary> + /// <param name="thisRef">A reference to the object on which the begin/end methods are invoked.</param> + /// <param name="endMethod">The end method.</param> + internal FromAsyncTrimPromise(TInstance thisRef, Func<TInstance, IAsyncResult, TResult> 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; + } + + /// <summary> + /// Completes the asynchronous operation using information in the IAsyncResult. + /// IAsyncResult.AsyncState neeeds to be the FromAsyncTrimPromise to complete. + /// </summary> + /// <param name="asyncResult">The IAsyncResult for the async operation.</param> + internal static void CompleteFromAsyncResult(IAsyncResult asyncResult) + { + // Validate argument + if (asyncResult == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.asyncResult); + Contract.EndContractBlock(); + + var promise = asyncResult.AsyncState as FromAsyncTrimPromise<TInstance>; + 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); + } + } + + /// <summary>Completes the promise.</summary> + /// <param name="requiresSynchronization"> + /// 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. + /// </param> + /// <param name="thisRef">The target instance on which the end method should be called.</param> + /// <param name="endMethod">The end method to call to retrieve the result.</param> + /// <param name="asyncResult">The IAsyncResult for the async operation.</param> + /// <permission cref="requiresSynchronization"> + /// 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. + /// </permission> + internal void Complete( + TInstance thisRef, Func<TInstance, IAsyncResult, TResult> endMethod, IAsyncResult asyncResult, + bool requiresSynchronization) + { + Contract.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); + } + Contract.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<TResult> CreateCanceledTask(TaskContinuationOptions continuationOptions, CancellationToken ct) + { + TaskCreationOptions tco; + InternalTaskOptions dontcare; + Task.CreationOptionsFromContinuationOptions(continuationOptions, out tco, out dontcare); + return new Task<TResult>(true, default(TResult), tco, ct); + } + + // + // ContinueWhenAll() methods + // + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of a set of provided Tasks. + /// </summary> + /// <param name="tasks">The array of tasks from which to continue.</param> + /// <param name="continuationFunction">The function delegate to execute when all tasks in + /// the <paramref name="tasks"/> array have completed.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAll(Task[] tasks, Func<Task[], TResult> continuationFunction) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of a set of provided Tasks. + /// </summary> + /// <param name="tasks">The array of tasks from which to continue.</param> + /// <param name="continuationFunction">The function delegate to execute when all tasks in + /// the <paramref name="tasks"/> array have completed.</param> + /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// that will be assigned to the new continuation task.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</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> ContinueWhenAll(Task[] tasks, Func<Task[], TResult> continuationFunction, CancellationToken cancellationToken) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of a set of provided Tasks. + /// </summary> + /// <param name="tasks">The array of tasks from which to continue.</param> + /// <param name="continuationFunction">The function delegate to execute when all tasks in the <paramref + /// name="tasks"/> array have completed.</param> + /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> value that controls the behavior of + /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions + /// value.</exception> + /// <remarks> + /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>, + /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation + /// will be executed, are illegal with ContinueWhenAll. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAll(Task[] tasks, Func<Task[], TResult> continuationFunction, TaskContinuationOptions continuationOptions) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of a set of provided Tasks. + /// </summary> + /// <param name="tasks">The array of tasks from which to continue.</param> + /// <param name="continuationFunction">The function delegate to execute when all tasks in the <paramref + /// name="tasks"/> array have completed.</param> + /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// that will be assigned to the new continuation task.</param> + /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> value that controls the behavior of + /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> + /// that is used to schedule the created continuation <see + /// cref="T:System.Threading.Tasks.Task">Task</see>.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="scheduler"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions + /// value.</exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + /// <remarks> + /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>, + /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation + /// will be executed, are illegal with ContinueWhenAll. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAll(Task[] tasks, Func<Task[], TResult> continuationFunction, + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of a set of provided Tasks. + /// </summary> + /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam> + /// <param name="tasks">The array of tasks from which to continue.</param> + /// <param name="continuationFunction">The function delegate to execute when all tasks in the + /// <paramref name="tasks"/> array have completed.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of a set of provided Tasks. + /// </summary> + /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam> + /// <param name="tasks">The array of tasks from which to continue.</param> + /// <param name="continuationFunction">The function delegate to execute when all tasks in the + /// <paramref name="tasks"/> array have completed.</param> + /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// that will be assigned to the new continuation task.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</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> ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction, + CancellationToken cancellationToken) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of a set of provided Tasks. + /// </summary> + /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam> + /// <param name="tasks">The array of tasks from which to continue.</param> + /// <param name="continuationFunction">The function delegate to execute when all tasks in the + /// <paramref name="tasks"/> array have completed.</param> + /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> value that controls the behavior of + /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions + /// value.</exception> + /// <remarks> + /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>, + /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation + /// will be executed, are illegal with ContinueWhenAll. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction, + TaskContinuationOptions continuationOptions) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of a set of provided Tasks. + /// </summary> + /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam> + /// <param name="tasks">The array of tasks from which to continue.</param> + /// <param name="continuationFunction">The function delegate to execute when all tasks in the + /// <paramref name="tasks"/> array have completed.</param> + /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// that will be assigned to the new continuation task.</param> + /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> value that controls the behavior of + /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> + /// that is used to schedule the created continuation <see + /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="scheduler"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions + /// value.</exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + /// <remarks> + /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>, + /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation + /// will be executed, are illegal with ContinueWhenAll. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAll<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>[], TResult> continuationFunction, + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAllImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark); + } + + + // 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<TResult> ContinueWhenAllImpl<TAntecedentResult>(Task<TAntecedentResult>[] tasks, + Func<Task<TAntecedentResult>[], TResult> continuationFunction, Action<Task<TAntecedentResult>[]> continuationAction, + TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark) + { + // 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<TAntecedentResult>[] tasksCopy = TaskFactory.CheckMultiContinuationTasksAndCopy<TAntecedentResult>(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<TResult>( + // use a cached delegate + GenericDelegateCache<TAntecedentResult, TResult>.CWAllFuncDelegate, + continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + else + { + Contract.Assert(continuationAction != null); + + return starter.ContinueWith<TResult>( + // use a cached delegate + GenericDelegateCache<TAntecedentResult, TResult>.CWAllActionDelegate, + continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + } + + // 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<TResult> ContinueWhenAllImpl(Task[] tasks, + Func<Task[], TResult> continuationFunction, Action<Task[]> continuationAction, + TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark) + { + // 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<Task[], TResult>)state)(completedTasks.Result); + }, + continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + else + { + Contract.Assert(continuationAction != null); + return starter.ContinueWith<TResult>( + //the following delegate avoids closure capture as much as possible + //completedTasks.Result == tasksCopy; + //state == continuationAction + (completedTasks, state) => + { + completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary(); + ((Action<Task[]>)state)(completedTasks.Result); return default(TResult); + }, + continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + } + + // + // ContinueWhenAny() methods + // + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of any Task in the provided set. + /// </summary> + /// <param name="tasks">The array of tasks from which to continue when one task completes.</param> + /// <param name="continuationFunction">The function delegate to execute when one task in the <paramref + /// name="tasks"/> array completes.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAny(Task[] tasks, Func<Task, TResult> continuationFunction) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of any Task in the provided set. + /// </summary> + /// <param name="tasks">The array of tasks from which to continue when one task completes.</param> + /// <param name="continuationFunction">The function delegate to execute when one task in the <paramref + /// name="tasks"/> array completes.</param> + /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// that will be assigned to the new continuation task.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</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> ContinueWhenAny(Task[] tasks, Func<Task, TResult> continuationFunction, CancellationToken cancellationToken) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of any Task in the provided set. + /// </summary> + /// <param name="tasks">The array of tasks from which to continue when one task completes.</param> + /// <param name="continuationFunction">The function delegate to execute when one task in the <paramref + /// name="tasks"/> array completes.</param> + /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> value that controls the behavior of + /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions + /// value.</exception> + /// <remarks> + /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>, + /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation + /// will be executed, are illegal with ContinueWhenAny. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAny(Task[] tasks, Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of any Task in the provided set. + /// </summary> + /// <param name="tasks">The array of tasks from which to continue when one task completes.</param> + /// <param name="continuationFunction">The function delegate to execute when one task in the <paramref + /// name="tasks"/> array completes.</param> + /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// that will be assigned to the new continuation task.</param> + /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> value that controls the behavior of + /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> + /// that is used to schedule the created continuation <see + /// cref="T:System.Threading.Tasks.Task">Task</see>.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="scheduler"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions + /// value.</exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + /// <remarks> + /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>, + /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation + /// will be executed, are illegal with ContinueWhenAny. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAny(Task[] tasks, Func<Task, TResult> continuationFunction, + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of any Task in the provided set. + /// </summary> + /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam> + /// <param name="tasks">The array of tasks from which to continue when one task completes.</param> + /// <param name="continuationFunction">The function delegate to execute when one task in the + /// <paramref name="tasks"/> array completes.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of any Task in the provided set. + /// </summary> + /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam> + /// <param name="tasks">The array of tasks from which to continue when one task completes.</param> + /// <param name="continuationFunction">The function delegate to execute when one task in the + /// <paramref name="tasks"/> array completes.</param> + /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// that will be assigned to the new continuation task.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</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> ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction, + CancellationToken cancellationToken) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of any Task in the provided set. + /// </summary> + /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam> + /// <param name="tasks">The array of tasks from which to continue when one task completes.</param> + /// <param name="continuationFunction">The function delegate to execute when one task in the + /// <paramref name="tasks"/> array completes.</param> + /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> value that controls the behavior of + /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions + /// value.</exception> + /// <remarks> + /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>, + /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation + /// will be executed, are illegal with ContinueWhenAny. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction, + TaskContinuationOptions continuationOptions) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark); + } + + /// <summary> + /// Creates a continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see> + /// that will be started upon the completion of any Task in the provided set. + /// </summary> + /// <typeparam name="TAntecedentResult">The type of the result of the antecedent <paramref name="tasks"/>.</typeparam> + /// <param name="tasks">The array of tasks from which to continue when one task completes.</param> + /// <param name="continuationFunction">The function delegate to execute when one task in the + /// <paramref name="tasks"/> array completes.</param> + /// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// that will be assigned to the new continuation task.</param> + /// <param name="continuationOptions">The <see cref="System.Threading.Tasks.TaskContinuationOptions"> + /// TaskContinuationOptions</see> value that controls the behavior of + /// the created continuation <see cref="T:System.Threading.Tasks.Task{TResult}">Task</see>.</param> + /// <param name="scheduler">The <see cref="System.Threading.Tasks.TaskScheduler">TaskScheduler</see> + /// that is used to schedule the created continuation <see + /// cref="T:System.Threading.Tasks.Task{TResult}"/>.</param> + /// <returns>The new continuation <see cref="T:System.Threading.Tasks.Task{TResult}"/>.</returns> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="tasks"/> array is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="continuationFunction"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentNullException">The exception that is thrown when the + /// <paramref name="scheduler"/> argument is null.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array contains a null value.</exception> + /// <exception cref="T:System.ArgumentException">The exception that is thrown when the + /// <paramref name="tasks"/> array is empty.</exception> + /// <exception cref="T:System.ArgumentOutOfRangeException">The exception that is thrown when the + /// <paramref name="continuationOptions"/> argument specifies an invalid TaskContinuationOptions + /// value.</exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + /// <remarks> + /// The NotOn* and OnlyOn* <see cref="System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>, + /// which constrain for which <see cref="System.Threading.Tasks.TaskStatus">TaskStatus</see> states a continuation + /// will be executed, are illegal with ContinueWhenAny. + /// </remarks> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TResult> ContinueWhenAny<TAntecedentResult>(Task<TAntecedentResult>[] tasks, Func<Task<TAntecedentResult>, TResult> continuationFunction, + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) + { + if (continuationFunction == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + Contract.EndContractBlock(); + + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWhenAnyImpl<TAntecedentResult>(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark); + } + + // 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<TResult> ContinueWhenAnyImpl(Task[] tasks, + Func<Task, TResult> continuationFunction, Action<Task> continuationAction, + TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark) + { + // 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<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<Task, TResult>)state)(completedTask.Result); }, + continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + else + { + Contract.Assert(continuationAction != null); + return starter.ContinueWith<TResult>( + //the following delegate avoids closure capture as much as possible + //completedTask.Result is the winning task; state == continuationAction + (completedTask, state) => { ((Action<Task>)state)(completedTask.Result); return default(TResult); }, + continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + } + + + // 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<TResult> ContinueWhenAnyImpl<TAntecedentResult>(Task<TAntecedentResult>[] tasks, + Func<Task<TAntecedentResult>, TResult> continuationFunction, Action<Task<TAntecedentResult>> continuationAction, + TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark) + { + // 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<TResult>( + // Use a cached delegate + GenericDelegateCache<TAntecedentResult, TResult>.CWAnyFuncDelegate, + continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + else + { + Contract.Assert(continuationAction != null); + return starter.ContinueWith<TResult>( + // Use a cached delegate + GenericDelegateCache<TAntecedentResult,TResult>.CWAnyActionDelegate, + continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + } + } + + // 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<TAntecedentResult, TResult> + { + // ContinueWith delegate for TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(non-null continuationFunction) + internal static Func<Task<Task>, object, TResult> CWAnyFuncDelegate = + (Task<Task> wrappedWinner, object state) => + { + var func = (Func<Task<TAntecedentResult>, TResult>)state; + var arg = (Task<TAntecedentResult>)wrappedWinner.Result; + return func(arg); + }; + + // ContinueWith delegate for TaskFactory<TResult>.ContinueWhenAnyImpl<TAntecedentResult>(non-null continuationAction) + internal static Func<Task<Task>, object, TResult> CWAnyActionDelegate = + (Task<Task> wrappedWinner, object state) => + { + var action = (Action<Task<TAntecedentResult>>)state; + var arg = (Task<TAntecedentResult>)wrappedWinner.Result; + action(arg); + return default(TResult); + }; + + // ContinueWith delegate for TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(non-null continuationFunction) + internal static Func<Task<Task<TAntecedentResult>[]>, object, TResult> CWAllFuncDelegate = + (Task<Task<TAntecedentResult>[]> wrappedAntecedents, object state) => + { + wrappedAntecedents.NotifyDebuggerOfWaitCompletionIfNecessary(); + var func = (Func<Task<TAntecedentResult>[], TResult>)state; + return func(wrappedAntecedents.Result); + }; + + // ContinueWith delegate for TaskFactory<TResult>.ContinueWhenAllImpl<TAntecedentResult>(non-null continuationAction) + internal static Func<Task<Task<TAntecedentResult>[]>, object, TResult> CWAllActionDelegate = + (Task<Task<TAntecedentResult>[]> wrappedAntecedents, object state) => + { + wrappedAntecedents.NotifyDebuggerOfWaitCompletionIfNecessary(); + var action = (Action<Task<TAntecedentResult>[]>)state; + action(wrappedAntecedents.Result); + return default(TResult); + }; + + } + +} |