// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// FutureFactory.cs
//
// [....]
//
// As with TaskFactory, TaskFactory 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
{
///
/// Provides support for creating and scheduling
/// Task{TResult} objects.
///
/// The type of the results that are available though
/// the Task{TResult} objects that are associated with
/// the methods in this class.
///
///
/// There are many common patterns for which tasks are relevant. The
/// class encodes some of these patterns into methods that pick up default settings, which are
/// configurable through its constructors.
///
///
/// A default instance of is available through the
/// Task{TResult}.Factory property.
///
///
[HostProtection(Synchronization = true, ExternalThreading = true)]
public class TaskFactory
{
// Member variables, DefaultScheduler, other properties and ctors
// copied right out of TaskFactory... Lots of duplication here...
// Should we be thinking about a TaskFactoryBase class?
// member variables
private CancellationToken m_defaultCancellationToken;
private TaskScheduler m_defaultScheduler;
private TaskCreationOptions m_defaultCreationOptions;
private TaskContinuationOptions m_defaultContinuationOptions;
private TaskScheduler DefaultScheduler
{
get
{
if (m_defaultScheduler == null) return TaskScheduler.Current;
else return m_defaultScheduler;
}
}
// sister method to above property -- avoids a TLS lookup
private TaskScheduler GetDefaultScheduler(Task currTask)
{
if (m_defaultScheduler != null) return m_defaultScheduler;
else if ((currTask != null)
#if !FEATURE_CORECLR || FEATURE_NETCORE
&& ((currTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0)
#endif
)
return currTask.ExecutingTaskScheduler;
else return TaskScheduler.Default;
}
/* Constructors */
///
/// Initializes a instance with the default configuration.
///
///
/// This constructor creates a instance with a default configuration. The
/// property is initialized to
/// TaskCreationOptions.None , the
/// property is initialized to TaskContinuationOptions.None ,
/// and the TaskScheduler property is
/// initialized to the current scheduler (see TaskScheduler.Current ).
///
public TaskFactory()
: this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, null)
{
}
///
/// Initializes a instance with the default configuration.
///
/// The default that will be assigned
/// to tasks created by this unless another CancellationToken is explicitly specified
/// while calling the factory methods.
///
/// This constructor creates a instance with a default configuration. The
/// property is initialized to
/// TaskCreationOptions.None , the
/// property is initialized to TaskContinuationOptions.None ,
/// and the TaskScheduler property is
/// initialized to the current scheduler (see TaskScheduler.Current ).
///
public TaskFactory(CancellationToken cancellationToken)
: this(cancellationToken, TaskCreationOptions.None, TaskContinuationOptions.None, null)
{
}
///
/// Initializes a instance with the specified configuration.
///
///
/// The
/// TaskScheduler to use to schedule any tasks created with this TaskFactory{TResult}. A null value
/// indicates that the current TaskScheduler should be used.
///
///
/// With this constructor, the
/// property is initialized to
/// TaskCreationOptions.None , the
/// property is initialized to TaskContinuationOptions.None ,
/// and the TaskScheduler property is
/// initialized to , unless it's null, in which case the property is
/// initialized to the current scheduler (see TaskScheduler.Current ).
///
public TaskFactory(TaskScheduler scheduler) // null means to use TaskScheduler.Current
: this(default(CancellationToken), TaskCreationOptions.None, TaskContinuationOptions.None, scheduler)
{
}
///
/// Initializes a instance with the specified configuration.
///
///
/// The default
/// TaskCreationOptions to use when creating tasks with this TaskFactory{TResult}.
///
///
/// The default
/// TaskContinuationOptions to use when creating continuation tasks with this TaskFactory{TResult}.
///
///
/// The exception that is thrown when the
/// argument or the
/// argument specifies an invalid value.
///
///
/// With this constructor, the
/// property is initialized to ,
/// the
/// property is initialized to , and the TaskScheduler property is initialized to the
/// current scheduler (see TaskScheduler.Current ).
///
public TaskFactory(TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions)
: this(default(CancellationToken), creationOptions, continuationOptions, null)
{
}
///
/// Initializes a instance with the specified configuration.
///
/// The default that will be assigned
/// to tasks created by this unless another CancellationToken is explicitly specified
/// while calling the factory methods.
///
/// The default
/// TaskCreationOptions to use when creating tasks with this TaskFactory{TResult}.
///
///
/// The default
/// TaskContinuationOptions to use when creating continuation tasks with this TaskFactory{TResult}.
///
///
/// The default
/// TaskScheduler to use to schedule any Tasks created with this TaskFactory{TResult}. A null value
/// indicates that TaskScheduler.Current should be used.
///
///
/// The exception that is thrown when the
/// argument or the
/// argumentspecifies an invalid value.
///
///
/// With this constructor, the
/// property is initialized to ,
/// the
/// property is initialized to , and the TaskScheduler property is initialized to
/// , unless it's null, in which case the property is initialized to the
/// current scheduler (see TaskScheduler.Current ).
///
public TaskFactory(CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
TaskFactory.CheckCreationOptions(creationOptions);
m_defaultCancellationToken = cancellationToken;
m_defaultScheduler = scheduler;
m_defaultCreationOptions = creationOptions;
m_defaultContinuationOptions = continuationOptions;
}
/* Properties */
///
/// Gets the default CancellationToken of this
/// TaskFactory.
///
///
/// This property returns the default that will be assigned to all
/// tasks created by this factory unless another CancellationToken value is explicitly specified
/// during the call to the factory methods.
///
public CancellationToken CancellationToken { get { return m_defaultCancellationToken; } }
///
/// Gets the TaskScheduler of this
/// TaskFactory{TResult}.
///
///
/// This property returns the default scheduler for this factory. It will be used to schedule all
/// tasks unless another scheduler is explicitly specified during calls to this factory's methods.
/// If null, TaskScheduler.Current
/// will be used.
///
public TaskScheduler Scheduler { get { return m_defaultScheduler; } }
///
/// Gets the TaskCreationOptions
/// value of this TaskFactory{TResult}.
///
///
/// This property returns the default creation options for this factory. They will be used to create all
/// tasks unless other options are explicitly specified during calls to this factory's methods.
///
public TaskCreationOptions CreationOptions { get { return m_defaultCreationOptions; } }
///
/// Gets the TaskContinuationOptions
/// value of this TaskFactory{TResult}.
///
///
/// This property returns the default continuation options for this factory. They will be used to create
/// all continuation tasks unless other options are explicitly specified during calls to this factory's methods.
///
public TaskContinuationOptions ContinuationOptions { get { return m_defaultContinuationOptions; } }
/* StartNew */
///
/// Creates and starts a .
///
/// A function delegate that returns the future result to be available through
/// the .
/// The started .
/// The exception that is thrown when the
/// argument is null.
///
/// Calling StartNew is functionally equivalent to creating a using one
/// of its constructors and then calling
/// Start to schedule it for execution.
/// However, unless creation and scheduling must be separated, StartNew is the recommended approach
/// for both simplicity and performance.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task StartNew(Func function)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
Task currTask = Task.InternalCurrent;
return Task.StartNew(currTask, function, m_defaultCancellationToken,
m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
}
///
/// Creates and starts a .
///
/// A function delegate that returns the future result to be available through
/// the .
/// The that will be assigned to the new task.
/// The started .
/// The exception that is thrown when the
/// argument is null.
/// The provided CancellationToken
/// has already been disposed.
///
///
/// Calling StartNew is functionally equivalent to creating a using one
/// of its constructors and then calling
/// Start to schedule it for execution.
/// However, unless creation and scheduling must be separated, StartNew is the recommended approach
/// for both simplicity and performance.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task StartNew(Func function, CancellationToken cancellationToken)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
Task currTask = Task.InternalCurrent;
return Task.StartNew(currTask, function, cancellationToken,
m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
}
///
/// Creates and starts a .
///
/// A function delegate that returns the future result to be available through
/// the .
/// A TaskCreationOptions value that controls the behavior of the
/// created
/// .
/// The started .
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
///
/// Calling StartNew is functionally equivalent to creating a using one
/// of its constructors and then calling
/// Start to schedule it for execution.
/// However, unless creation and scheduling must be separated, StartNew is the recommended approach
/// for both simplicity and performance.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task StartNew(Func function, TaskCreationOptions creationOptions)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
Task currTask = Task.InternalCurrent;
return Task.StartNew(currTask, function, m_defaultCancellationToken,
creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
}
///
/// Creates and starts a .
///
/// A function delegate that returns the future result to be available through
/// the .
/// A TaskCreationOptions value that controls the behavior of the
/// created
/// .
/// The that will be assigned to the new task.
/// The TaskScheduler
/// that is used to schedule the created
/// Task{TResult} .
/// The started .
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
/// The provided CancellationToken
/// has already been disposed.
///
///
/// Calling StartNew is functionally equivalent to creating a using one
/// of its constructors and then calling
/// Start to schedule it for execution.
/// However, unless creation and scheduling must be separated, StartNew is the recommended approach
/// for both simplicity and performance.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task StartNew(Func function, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return Task.StartNew(
Task.InternalCurrentIfAttached(creationOptions), function, cancellationToken,
creationOptions, InternalTaskOptions.None, scheduler, ref stackMark);
}
///
/// Creates and starts a .
///
/// A function delegate that returns the future result to be available through
/// the .
/// An object containing data to be used by the
/// delegate.
/// The started .
/// The exception that is thrown when the
/// argument is null.
///
/// Calling StartNew is functionally equivalent to creating a using one
/// of its constructors and then calling
/// Start to schedule it for execution.
/// However, unless creation and scheduling must be separated, StartNew is the recommended approach
/// for both simplicity and performance.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task StartNew(Func function, Object state)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
Task currTask = Task.InternalCurrent;
return Task.StartNew(currTask, function, state, m_defaultCancellationToken,
m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
}
///
/// Creates and starts a .
///
/// A function delegate that returns the future result to be available through
/// the .
/// An object containing data to be used by the
/// delegate.
/// The that will be assigned to the new task.
/// The started .
/// The exception that is thrown when the
/// argument is null.
/// The provided CancellationToken
/// has already been disposed.
///
///
/// Calling StartNew is functionally equivalent to creating a using one
/// of its constructors and then calling
/// Start to schedule it for execution.
/// However, unless creation and scheduling must be separated, StartNew is the recommended approach
/// for both simplicity and performance.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task StartNew(Func function, Object state, CancellationToken cancellationToken)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
Task currTask = Task.InternalCurrent;
return Task.StartNew(currTask, function, state, cancellationToken,
m_defaultCreationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
}
///
/// Creates and starts a .
///
/// A function delegate that returns the future result to be available through
/// the .
/// An object containing data to be used by the
/// delegate.
/// A TaskCreationOptions value that controls the behavior of the
/// created
/// .
/// The started .
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
///
/// Calling StartNew is functionally equivalent to creating a using one
/// of its constructors and then calling
/// Start to schedule it for execution.
/// However, unless creation and scheduling must be separated, StartNew is the recommended approach
/// for both simplicity and performance.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task StartNew(Func function, Object state, TaskCreationOptions creationOptions)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
Task currTask = Task.InternalCurrent;
return Task.StartNew(currTask, function, state, m_defaultCancellationToken,
creationOptions, InternalTaskOptions.None, GetDefaultScheduler(currTask), ref stackMark);
}
///
/// Creates and starts a .
///
/// A function delegate that returns the future result to be available through
/// the .
/// An object containing data to be used by the
/// delegate.
/// The that will be assigned to the new task.
/// A TaskCreationOptions value that controls the behavior of the
/// created
/// .
/// The TaskScheduler
/// that is used to schedule the created
/// Task{TResult} .
/// The started .
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
/// The provided CancellationToken
/// has already been disposed.
///
///
/// Calling StartNew is functionally equivalent to creating a using one
/// of its constructors and then calling
/// Start to schedule it for execution.
/// However, unless creation and scheduling must be separated, StartNew is the recommended approach
/// for both simplicity and performance.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task StartNew(Func function, Object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return Task.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 endFunction,
Action endAction,
Task promise,
bool requiresSynchronization)
{
Contract.Requires((endFunction != null) != (endAction != null), "Expected exactly one of endFunction/endAction to be non-null");
Exception ex = null;
OperationCanceledException oce = null;
TResult result = default(TResult);
try
{
if (endFunction != null)
{
result = endFunction(iar);
}
else
{
endAction(iar);
}
}
catch (OperationCanceledException _oce) { oce = _oce; }
catch (Exception e) { ex = e; }
finally
{
if (oce != null)
{
promise.TrySetCanceled(oce.CancellationToken, oce);
}
else if (ex != null)
{
bool bWonSetException = promise.TrySetException(ex);
if (bWonSetException && ex is ThreadAbortException)
{
promise.m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
}
}
else
{
#if !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Completed);
if (Task.s_asyncDebuggingEnabled)
{
Task.RemoveFromActiveTasks(promise.Id);
}
#endif
if (requiresSynchronization)
{
promise.TrySetResult(result);
}
else
{
promise.DangerousSetResult(result);
}
}
}
}
///
/// Creates a Task that executes an end
/// method function when a specified IAsyncResult completes.
///
/// The IAsyncResult whose completion should trigger the processing of the
/// .
/// The function delegate that processes the completed .
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// A Task that represents the
/// asynchronous operation.
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task FromAsync(IAsyncResult asyncResult, Func endMethod)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return FromAsyncImpl(asyncResult, endMethod, null, m_defaultCreationOptions, DefaultScheduler, ref stackMark);
}
///
/// Creates a Task that executes an end
/// method function when a specified IAsyncResult completes.
///
/// The IAsyncResult whose completion should trigger the processing of the
/// .
/// The function delegate that processes the completed .
/// The TaskCreationOptions value that controls the behavior of the
/// created Task .
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
/// A Task that represents the
/// asynchronous operation.
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task FromAsync(
IAsyncResult asyncResult,
Func endMethod,
TaskCreationOptions creationOptions)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return FromAsyncImpl(asyncResult, endMethod, null, creationOptions, DefaultScheduler, ref stackMark);
}
///
/// Creates a Task that executes an end
/// method function when a specified IAsyncResult completes.
///
/// The IAsyncResult whose completion should trigger the processing of the
/// .
/// The function delegate that processes the completed .
/// The TaskScheduler
/// that is used to schedule the task that executes the end method.
/// The TaskCreationOptions value that controls the behavior of the
/// created Task .
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
/// A Task that represents the
/// asynchronous operation.
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task FromAsync(
IAsyncResult asyncResult,
Func 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 instance.
internal static Task FromAsyncImpl(
IAsyncResult asyncResult,
Func endFunction,
Action endAction,
TaskCreationOptions creationOptions,
TaskScheduler scheduler,
ref StackCrawlMark stackMark)
{
if (asyncResult == null)
throw new ArgumentNullException("asyncResult");
if (endFunction == null && endAction == null)
throw new ArgumentNullException("endMethod");
Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
if (scheduler == null)
throw new ArgumentNullException("scheduler");
Contract.EndContractBlock();
TaskFactory.CheckFromAsyncOptions(creationOptions, false);
Task promise = new Task((object)null, creationOptions);
#if !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync", 0);
if (Task.s_asyncDebuggingEnabled)
{
Task.AddToActiveTasks(promise);
}
#endif
// 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 !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Verbose, t.Id, "TaskFactory.FromAsync Callback", 0);
if (Task.s_asyncDebuggingEnabled)
{
Task.AddToActiveTasks(t);
}
#endif
if (asyncResult.IsCompleted)
{
try { t.InternalRunSynchronously(scheduler, waitForCompletion:false); }
catch (Exception e) { promise.TrySetException(e); } // catch and log any scheduler exceptions
}
else
{
ThreadPool.RegisterWaitForSingleObject(
asyncResult.AsyncWaitHandle,
delegate
{
try { t.InternalRunSynchronously(scheduler, waitForCompletion: false); }
catch (Exception e) { promise.TrySetException(e); } // catch and log any scheduler exceptions
},
null,
Timeout.Infinite,
true);
}
return promise;
}
///
/// Creates a Task that represents a pair of
/// begin and end methods that conform to the Asynchronous Programming Model pattern.
///
/// The delegate that begins the asynchronous operation.
/// The delegate that ends the asynchronous operation.
/// An object containing data to be used by the
/// delegate.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The created Task that
/// represents the asynchronous operation.
///
/// This method throws any exceptions thrown by the .
///
public Task FromAsync(
Func beginMethod,
Func endMethod, object state)
{
return FromAsyncImpl(beginMethod, endMethod, null, state, m_defaultCreationOptions);
}
///
/// Creates a Task that represents a pair of
/// begin and end methods that conform to the Asynchronous Programming Model pattern.
///
/// The delegate that begins the asynchronous operation.
/// The delegate that ends the asynchronous operation.
/// The TaskCreationOptions value that controls the behavior of the
/// created Task .
/// An object containing data to be used by the
/// delegate.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
/// The created Task that
/// represents the asynchronous operation.
///
/// This method throws any exceptions thrown by the .
///
public Task FromAsync(
Func beginMethod,
Func endMethod, object state, TaskCreationOptions creationOptions)
{
return FromAsyncImpl(beginMethod, endMethod, null, state, creationOptions);
}
// We need this logic broken out into a static method so that the similar TaskFactory.FromAsync()
// method can access the logic w/o declaring a TaskFactory instance.
internal static Task FromAsyncImpl(Func beginMethod,
Func endFunction, Action endAction,
object state, TaskCreationOptions creationOptions)
{
if (beginMethod == null)
throw new ArgumentNullException("beginMethod");
if (endFunction == null && endAction == null)
throw new ArgumentNullException("endMethod");
Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
TaskFactory.CheckFromAsyncOptions(creationOptions, true);
Task promise = new Task(state, creationOptions);
#if !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
if (Task.s_asyncDebuggingEnabled)
{
Task.AddToActiveTasks(promise);
}
#endif
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 !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error);
if (Task.s_asyncDebuggingEnabled)
{
Task.RemoveFromActiveTasks(promise.Id);
}
#endif
// Make sure we don't leave promise "dangling".
promise.TrySetResult(default(TResult));
throw;
}
return promise;
}
///
/// Creates a Task that represents a pair of
/// begin and end methods that conform to the Asynchronous Programming Model pattern.
///
/// The type of the first argument passed to the delegate.
/// The delegate that begins the asynchronous operation.
/// The delegate that ends the asynchronous operation.
/// The first argument passed to the
/// delegate.
/// An object containing data to be used by the
/// delegate.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The created Task that
/// represents the asynchronous operation.
///
/// This method throws any exceptions thrown by the .
///
public Task FromAsync(
Func beginMethod,
Func endMethod,
TArg1 arg1, object state)
{
return FromAsyncImpl(beginMethod, endMethod, null, arg1, state, m_defaultCreationOptions);
}
///
/// Creates a Task that represents a pair of
/// begin and end methods that conform to the Asynchronous Programming Model pattern.
///
/// The type of the first argument passed to the delegate.
/// The delegate that begins the asynchronous operation.
/// The delegate that ends the asynchronous operation.
/// The first argument passed to the
/// delegate.
/// The TaskCreationOptions value that controls the behavior of the
/// created Task .
/// An object containing data to be used by the
/// delegate.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
/// The created Task that
/// represents the asynchronous operation.
///
/// This method throws any exceptions thrown by the .
///
public Task FromAsync(
Func beginMethod,
Func endMethod,
TArg1 arg1, object state, TaskCreationOptions creationOptions)
{
return FromAsyncImpl(beginMethod, endMethod, null, arg1, state, creationOptions);
}
// We need this logic broken out into a static method so that the similar TaskFactory.FromAsync()
// method can access the logic w/o declaring a TaskFactory instance.
internal static Task FromAsyncImpl(Func beginMethod,
Func endFunction, Action endAction,
TArg1 arg1, object state, TaskCreationOptions creationOptions)
{
if (beginMethod == null)
throw new ArgumentNullException("beginMethod");
if (endFunction == null && endAction == null)
throw new ArgumentNullException("endFunction");
Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
TaskFactory.CheckFromAsyncOptions(creationOptions, true);
Task promise = new Task(state, creationOptions);
#if !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
if (Task.s_asyncDebuggingEnabled)
{
Task.AddToActiveTasks(promise);
}
#endif
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 !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error);
if (Task.s_asyncDebuggingEnabled)
{
Task.RemoveFromActiveTasks(promise.Id);
}
#endif
// Make sure we don't leave promise "dangling".
promise.TrySetResult(default(TResult));
throw;
}
return promise;
}
///
/// Creates a Task that represents a pair of
/// begin and end methods that conform to the Asynchronous Programming Model pattern.
///
/// The type of the first argument passed to the delegate.
/// The type of the second argument passed to
/// delegate.
/// The delegate that begins the asynchronous operation.
/// The delegate that ends the asynchronous operation.
/// The first argument passed to the
/// delegate.
/// The second argument passed to the
/// delegate.
/// An object containing data to be used by the
/// delegate.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The created Task that
/// represents the asynchronous operation.
///
/// This method throws any exceptions thrown by the .
///
public Task FromAsync(
Func beginMethod,
Func endMethod,
TArg1 arg1, TArg2 arg2, object state)
{
return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, state, m_defaultCreationOptions);
}
///
/// Creates a Task that represents a pair of
/// begin and end methods that conform to the Asynchronous Programming Model pattern.
///
/// The type of the first argument passed to the delegate.
/// The type of the second argument passed to
/// delegate.
/// The delegate that begins the asynchronous operation.
/// The delegate that ends the asynchronous operation.
/// The first argument passed to the
/// delegate.
/// The second argument passed to the
/// delegate.
/// The TaskCreationOptions value that controls the behavior of the
/// created Task .
/// An object containing data to be used by the
/// delegate.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
/// The created Task that
/// represents the asynchronous operation.
///
/// This method throws any exceptions thrown by the .
///
public Task FromAsync(
Func beginMethod,
Func endMethod,
TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions)
{
return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, state, creationOptions);
}
// We need this logic broken out into a static method so that the similar TaskFactory.FromAsync()
// method can access the logic w/o declaring a TaskFactory instance.
internal static Task FromAsyncImpl(Func beginMethod,
Func endFunction, Action endAction,
TArg1 arg1, TArg2 arg2, object state, TaskCreationOptions creationOptions)
{
if (beginMethod == null)
throw new ArgumentNullException("beginMethod");
if (endFunction == null && endAction == null)
throw new ArgumentNullException("endMethod");
Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
TaskFactory.CheckFromAsyncOptions(creationOptions, true);
Task promise = new Task(state, creationOptions);
#if !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
if (Task.s_asyncDebuggingEnabled)
{
Task.AddToActiveTasks(promise);
}
#endif
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 !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error);
if (Task.s_asyncDebuggingEnabled)
{
Task.RemoveFromActiveTasks(promise.Id);
}
#endif
// Make sure we don't leave promise "dangling".
promise.TrySetResult(default(TResult));
throw;
}
return promise;
}
///
/// Creates a Task that represents a pair of
/// begin and end methods that conform to the Asynchronous Programming Model pattern.
///
/// The type of the first argument passed to the delegate.
/// The type of the second argument passed to
/// delegate.
/// The type of the third argument passed to
/// delegate.
/// The delegate that begins the asynchronous operation.
/// The delegate that ends the asynchronous operation.
/// The first argument passed to the
/// delegate.
/// The second argument passed to the
/// delegate.
/// The third argument passed to the
/// delegate.
/// An object containing data to be used by the
/// delegate.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The created Task that
/// represents the asynchronous operation.
///
/// This method throws any exceptions thrown by the .
///
public Task FromAsync(
Func beginMethod,
Func endMethod,
TArg1 arg1, TArg2 arg2, TArg3 arg3, object state)
{
return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, arg3, state, m_defaultCreationOptions);
}
///
/// Creates a Task that represents a pair of
/// begin and end methods that conform to the Asynchronous Programming Model pattern.
///
/// The type of the first argument passed to the delegate.
/// The type of the second argument passed to
/// delegate.
/// The type of the third argument passed to
/// delegate.
/// The delegate that begins the asynchronous operation.
/// The delegate that ends the asynchronous operation.
/// The first argument passed to the
/// delegate.
/// The second argument passed to the
/// delegate.
/// The third argument passed to the
/// delegate.
/// The TaskCreationOptions value that controls the behavior of the
/// created Task .
/// An object containing data to be used by the
/// delegate.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument specifies an invalid TaskCreationOptions
/// value.
/// The created Task that
/// represents the asynchronous operation.
///
/// This method throws any exceptions thrown by the .
///
public Task FromAsync(
Func beginMethod,
Func endMethod,
TArg1 arg1, TArg2 arg2, TArg3 arg3, object state, TaskCreationOptions creationOptions)
{
return FromAsyncImpl(beginMethod, endMethod, null, arg1, arg2, arg3, state, creationOptions);
}
// We need this logic broken out into a static method so that the similar TaskFactory.FromAsync()
// method can access the logic w/o declaring a TaskFactory instance.
internal static Task FromAsyncImpl(Func beginMethod,
Func endFunction, Action endAction,
TArg1 arg1, TArg2 arg2, TArg3 arg3, object state, TaskCreationOptions creationOptions)
{
if (beginMethod == null)
throw new ArgumentNullException("beginMethod");
if (endFunction == null && endAction == null)
throw new ArgumentNullException("endMethod");
Contract.Requires((endFunction != null) != (endAction != null), "Both endFunction and endAction were non-null");
TaskFactory.CheckFromAsyncOptions(creationOptions, true);
Task promise = new Task(state, creationOptions);
#if !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, promise.Id, "TaskFactory.FromAsync: " + beginMethod.Method.Name, 0);
if (Task.s_asyncDebuggingEnabled)
{
Task.AddToActiveTasks(promise);
}
#endif
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 !FEATURE_PAL && !FEATURE_CORECLR
if (AsyncCausalityTracer.LoggingOn)
AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, promise.Id, AsyncCausalityStatus.Error);
if (Task.s_asyncDebuggingEnabled)
{
Task.RemoveFromActiveTasks(promise.Id);
}
#endif
// Make sure we don't leave the promise "dangling".
promise.TrySetResult(default(TResult));
throw;
}
return promise;
}
///
/// Special internal-only FromAsync support used by System.IO to wrap
/// APM implementations with minimal overhead, avoiding unnecessary closure
/// and delegate allocations.
///
/// Specifies the type of the instance on which the APM implementation lives.
/// Specifies the type containing the arguments.
/// The instance from which the begin and end methods are invoked.
/// The begin method.
/// The end method.
/// The arguments.
/// A task representing the asynchronous operation.
internal static Task FromAsyncTrim(
TInstance thisRef, TArgs args,
Func beginMethod,
Func endMethod)
where TInstance : class
{
// Validate arguments, but only with asserts, as this is an internal only implementation.
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(thisRef, endMethod);
var asyncResult = beginMethod(thisRef, args, FromAsyncTrimPromise.s_completeFromAsyncResult, promise);
// If the IAsyncResult completed asynchronously, completing the promise will be handled by the callback.
// If it completed synchronously, we'll handle that here.
if (asyncResult.CompletedSynchronously)
{
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;
}
///
/// A specialized task used by FromAsyncTrim. Stores relevant information as instance
/// state so that we can avoid unnecessary closure/delegate allocations.
///
/// Specifies the type of the instance on which the APM implementation lives.
private sealed class FromAsyncTrimPromise : Task where TInstance : class
{
/// A cached delegate used as the callback for the BeginXx method.
internal readonly static AsyncCallback s_completeFromAsyncResult = CompleteFromAsyncResult;
/// A reference to the object on which the begin/end methods are invoked.
private TInstance m_thisRef;
/// The end method.
private Func m_endMethod;
/// Initializes the promise.
/// A reference to the object on which the begin/end methods are invoked.
/// The end method.
internal FromAsyncTrimPromise(TInstance thisRef, Func endMethod) : base()
{
Contract.Requires(thisRef != null, "Expected a non-null thisRef");
Contract.Requires(endMethod != null, "Expected a non-null endMethod");
m_thisRef = thisRef;
m_endMethod = endMethod;
}
///
/// Completes the asynchronous operation using information in the IAsyncResult.
/// IAsyncResult.AsyncState neeeds to be the FromAsyncTrimPromise to complete.
///
/// The IAsyncResult for the async operation.
internal static void CompleteFromAsyncResult(IAsyncResult asyncResult)
{
// Validate argument
if (asyncResult == null) throw new ArgumentNullException("asyncResult");
Contract.EndContractBlock();
var promise = asyncResult.AsyncState as FromAsyncTrimPromise;
if (promise == null) throw new ArgumentException(Environment.GetResourceString("InvalidOperation_WrongAsyncResultOrEndCalledMultiple"), "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) throw new ArgumentException(Environment.GetResourceString("InvalidOperation_WrongAsyncResultOrEndCalledMultiple"), "asyncResult");
// Complete the promise. If the IAsyncResult completed synchronously,
// we'll instead complete the promise at the call site.
if (!asyncResult.CompletedSynchronously)
{
promise.Complete(thisRef, endMethod, asyncResult, requiresSynchronization:true);
}
}
/// Completes the promise.
///
/// true if synchronization is needed to completed the task;
/// false if the task may be completed without synchronization
/// because it hasn't been handed out.
///
/// The target instance on which the end method should be called.
/// The end method to call to retrieve the result.
/// The IAsyncResult for the async operation.
///
/// Whether completing the task requires synchronization. This should be true
/// unless absolutely sure that the task has not yet been handed out to any consumers.
///
internal void Complete(
TInstance thisRef, Func endMethod, IAsyncResult asyncResult,
bool requiresSynchronization)
{
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 CreateCanceledTask(TaskContinuationOptions continuationOptions, CancellationToken ct)
{
TaskCreationOptions tco;
InternalTaskOptions dontcare;
Task.CreationOptionsFromContinuationOptions(continuationOptions, out tco, out dontcare);
return new Task(true, default(TResult), tco, ct);
}
//
// ContinueWhenAll() methods
//
///
/// Creates a continuation Task
/// that will be started upon the completion of a set of provided Tasks.
///
/// The array of tasks from which to continue.
/// The function delegate to execute when all tasks in
/// the array have completed.
/// The new continuation Task .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAll(Task[] tasks, Func continuationFunction)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of a set of provided Tasks.
///
/// The array of tasks from which to continue.
/// The function delegate to execute when all tasks in
/// the array have completed.
/// The CancellationToken
/// that will be assigned to the new continuation task.
/// The new continuation Task .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The provided CancellationToken
/// has already been disposed.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAll(Task[] tasks, Func continuationFunction, CancellationToken cancellationToken)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of a set of provided Tasks.
///
/// The array of tasks from which to continue.
/// The function delegate to execute when all tasks in the array have completed.
/// The
/// TaskContinuationOptions value that controls the behavior of
/// the created continuation Task .
/// The new continuation Task .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The exception that is thrown when the
/// argument specifies an invalid TaskContinuationOptions
/// value.
///
/// The NotOn* and OnlyOn* TaskContinuationOptions ,
/// which constrain for which TaskStatus states a continuation
/// will be executed, are illegal with ContinueWhenAll.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAll(Task[] tasks, Func continuationFunction, TaskContinuationOptions continuationOptions)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of a set of provided Tasks.
///
/// The array of tasks from which to continue.
/// The function delegate to execute when all tasks in the array have completed.
/// The CancellationToken
/// that will be assigned to the new continuation task.
/// The
/// TaskContinuationOptions value that controls the behavior of
/// the created continuation Task .
/// The TaskScheduler
/// that is used to schedule the created continuation Task .
/// The new continuation Task .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The exception that is thrown when the
/// argument specifies an invalid TaskContinuationOptions
/// value.
/// The provided CancellationToken
/// has already been disposed.
///
///
/// The NotOn* and OnlyOn* TaskContinuationOptions ,
/// which constrain for which TaskStatus states a continuation
/// will be executed, are illegal with ContinueWhenAll.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAll(Task[] tasks, Func continuationFunction,
CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of a set of provided Tasks.
///
/// The type of the result of the antecedent .
/// The array of tasks from which to continue.
/// The function delegate to execute when all tasks in the
/// array have completed.
/// The new continuation .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAll(Task[] tasks, Func[], TResult> continuationFunction)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of a set of provided Tasks.
///
/// The type of the result of the antecedent .
/// The array of tasks from which to continue.
/// The function delegate to execute when all tasks in the
/// array have completed.
/// The CancellationToken
/// that will be assigned to the new continuation task.
/// The new continuation .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The provided CancellationToken
/// has already been disposed.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAll(Task[] tasks, Func[], TResult> continuationFunction,
CancellationToken cancellationToken)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAllImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of a set of provided Tasks.
///
/// The type of the result of the antecedent .
/// The array of tasks from which to continue.
/// The function delegate to execute when all tasks in the
/// array have completed.
/// The
/// TaskContinuationOptions value that controls the behavior of
/// the created continuation Task .
/// The new continuation .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The exception that is thrown when the
/// argument specifies an invalid TaskContinuationOptions
/// value.
///
/// The NotOn* and OnlyOn* TaskContinuationOptions ,
/// which constrain for which TaskStatus states a continuation
/// will be executed, are illegal with ContinueWhenAll.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAll(Task[] tasks, Func[], TResult> continuationFunction,
TaskContinuationOptions continuationOptions)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAllImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of a set of provided Tasks.
///
/// The type of the result of the antecedent .
/// The array of tasks from which to continue.
/// The function delegate to execute when all tasks in the
/// array have completed.
/// The CancellationToken
/// that will be assigned to the new continuation task.
/// The
/// TaskContinuationOptions value that controls the behavior of
/// the created continuation Task .
/// The TaskScheduler
/// that is used to schedule the created continuation .
/// The new continuation .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The exception that is thrown when the
/// argument specifies an invalid TaskContinuationOptions
/// value.
/// The provided CancellationToken
/// has already been disposed.
///
///
/// The NotOn* and OnlyOn* TaskContinuationOptions ,
/// which constrain for which TaskStatus states a continuation
/// will be executed, are illegal with ContinueWhenAll.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAll(Task[] tasks, Func[], TResult> continuationFunction,
CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAllImpl(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 ContinueWhenAllImpl(Task[] tasks,
Func[], TResult> continuationFunction, Action[]> continuationAction,
TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark)
{
// check arguments
TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
if (tasks == null) throw new ArgumentNullException("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) throw new ArgumentNullException("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
#if !FEATURE_CORECLR || FEATURE_NETCORE
&& ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0)
#endif
)
{
return CreateCanceledTask(continuationOptions, cancellationToken);
}
// Call common ContinueWhenAll() setup logic, extract starter task.
var starter = TaskFactory.CommonCWAllLogic(tasksCopy);
// returned continuation task, off of starter
if (continuationFunction != null)
{
return starter.ContinueWith(
// use a cached delegate
GenericDelegateCache.CWAllFuncDelegate,
continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
}
else
{
Contract.Assert(continuationAction != null);
return starter.ContinueWith(
// use a cached delegate
GenericDelegateCache.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 ContinueWhenAllImpl(Task[] tasks,
Func continuationFunction, Action continuationAction,
TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark)
{
// check arguments
TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
if (tasks == null) throw new ArgumentNullException("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) throw new ArgumentNullException("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
#if !FEATURE_CORECLR || FEATURE_NETCORE
&& ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0)
#endif
)
{
return CreateCanceledTask(continuationOptions, cancellationToken);
}
// Perform common ContinueWhenAll() setup logic, extract starter task
var starter = TaskFactory.CommonCWAllLogic(tasksCopy);
// returned continuation task, off of starter
if (continuationFunction != null)
{
return starter.ContinueWith(
//the following delegate avoids closure capture as much as possible
//completedTasks.Result == tasksCopy;
//state == continuationFunction
(completedTasks, state) =>
{
completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary();
return ((Func)state)(completedTasks.Result);
},
continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
}
else
{
Contract.Assert(continuationAction != null);
return starter.ContinueWith(
//the following delegate avoids closure capture as much as possible
//completedTasks.Result == tasksCopy;
//state == continuationAction
(completedTasks, state) =>
{
completedTasks.NotifyDebuggerOfWaitCompletionIfNecessary();
((Action)state)(completedTasks.Result); return default(TResult);
},
continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark);
}
}
//
// ContinueWhenAny() methods
//
///
/// Creates a continuation Task
/// that will be started upon the completion of any Task in the provided set.
///
/// The array of tasks from which to continue when one task completes.
/// The function delegate to execute when one task in the array completes.
/// The new continuation Task .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAny(Task[] tasks, Func continuationFunction)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of any Task in the provided set.
///
/// The array of tasks from which to continue when one task completes.
/// The function delegate to execute when one task in the array completes.
/// The CancellationToken
/// that will be assigned to the new continuation task.
/// The new continuation Task .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The provided CancellationToken
/// has already been disposed.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAny(Task[] tasks, Func continuationFunction, CancellationToken cancellationToken)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of any Task in the provided set.
///
/// The array of tasks from which to continue when one task completes.
/// The function delegate to execute when one task in the array completes.
/// The
/// TaskContinuationOptions value that controls the behavior of
/// the created continuation Task .
/// The new continuation Task .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The exception that is thrown when the
/// argument specifies an invalid TaskContinuationOptions
/// value.
///
/// The NotOn* and OnlyOn* TaskContinuationOptions ,
/// which constrain for which TaskStatus states a continuation
/// will be executed, are illegal with ContinueWhenAny.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAny(Task[] tasks, Func continuationFunction, TaskContinuationOptions continuationOptions)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of any Task in the provided set.
///
/// The array of tasks from which to continue when one task completes.
/// The function delegate to execute when one task in the array completes.
/// The CancellationToken
/// that will be assigned to the new continuation task.
/// The
/// TaskContinuationOptions value that controls the behavior of
/// the created continuation Task .
/// The TaskScheduler
/// that is used to schedule the created continuation Task .
/// The new continuation Task .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The exception that is thrown when the
/// argument specifies an invalid TaskContinuationOptions
/// value.
/// The provided CancellationToken
/// has already been disposed.
///
///
/// The NotOn* and OnlyOn* TaskContinuationOptions ,
/// which constrain for which TaskStatus states a continuation
/// will be executed, are illegal with ContinueWhenAny.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAny(Task[] tasks, Func continuationFunction,
CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, cancellationToken, scheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of any Task in the provided set.
///
/// The type of the result of the antecedent .
/// The array of tasks from which to continue when one task completes.
/// The function delegate to execute when one task in the
/// array completes.
/// The new continuation .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAny(Task[] tasks, Func, TResult> continuationFunction)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of any Task in the provided set.
///
/// The type of the result of the antecedent .
/// The array of tasks from which to continue when one task completes.
/// The function delegate to execute when one task in the
/// array completes.
/// The CancellationToken
/// that will be assigned to the new continuation task.
/// The new continuation .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The provided CancellationToken
/// has already been disposed.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAny(Task[] tasks, Func, TResult> continuationFunction,
CancellationToken cancellationToken)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAnyImpl(tasks, continuationFunction, null, m_defaultContinuationOptions, cancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of any Task in the provided set.
///
/// The type of the result of the antecedent .
/// The array of tasks from which to continue when one task completes.
/// The function delegate to execute when one task in the
/// array completes.
/// The
/// TaskContinuationOptions value that controls the behavior of
/// the created continuation Task .
/// The new continuation .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The exception that is thrown when the
/// argument specifies an invalid TaskContinuationOptions
/// value.
///
/// The NotOn* and OnlyOn* TaskContinuationOptions ,
/// which constrain for which TaskStatus states a continuation
/// will be executed, are illegal with ContinueWhenAny.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAny(Task[] tasks, Func, TResult> continuationFunction,
TaskContinuationOptions continuationOptions)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAnyImpl(tasks, continuationFunction, null, continuationOptions, m_defaultCancellationToken, DefaultScheduler, ref stackMark);
}
///
/// Creates a continuation Task
/// that will be started upon the completion of any Task in the provided set.
///
/// The type of the result of the antecedent .
/// The array of tasks from which to continue when one task completes.
/// The function delegate to execute when one task in the
/// array completes.
/// The CancellationToken
/// that will be assigned to the new continuation task.
/// The
/// TaskContinuationOptions value that controls the behavior of
/// the created continuation Task .
/// The TaskScheduler
/// that is used to schedule the created continuation .
/// The new continuation .
/// The exception that is thrown when the
/// array is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// argument is null.
/// The exception that is thrown when the
/// array contains a null value.
/// The exception that is thrown when the
/// array is empty.
/// The exception that is thrown when the
/// argument specifies an invalid TaskContinuationOptions
/// value.
/// The provided CancellationToken
/// has already been disposed.
///
///
/// The NotOn* and OnlyOn* TaskContinuationOptions ,
/// which constrain for which TaskStatus states a continuation
/// will be executed, are illegal with ContinueWhenAny.
///
[MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable
public Task ContinueWhenAny(Task[] tasks, Func, TResult> continuationFunction,
CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
if (continuationFunction == null) throw new ArgumentNullException("continuationFunction");
Contract.EndContractBlock();
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return ContinueWhenAnyImpl(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 ContinueWhenAnyImpl(Task[] tasks,
Func continuationFunction, Action continuationAction,
TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark)
{
// check arguments
TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
if (tasks == null) throw new ArgumentNullException("tasks");
if(tasks.Length == 0) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), "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) throw new ArgumentNullException("scheduler");
Contract.EndContractBlock();
// Call common ContinueWhenAny() setup logic, extract starter
Task starter = TaskFactory.CommonCWAnyLogic(tasks);
// Bail early if cancellation has been requested.
if (cancellationToken.IsCancellationRequested
#if !FEATURE_CORECLR || FEATURE_NETCORE
&& ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0)
#endif
)
{
return CreateCanceledTask(continuationOptions, cancellationToken);
}
// returned continuation task, off of starter
if (continuationFunction != null)
{
return starter.ContinueWith(
//the following delegate avoids closure capture as much as possible
//completedTask.Result is the winning task; state == continuationAction
(completedTask, state) => { return ((Func)state)(completedTask.Result); },
continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
}
else
{
Contract.Assert(continuationAction != null);
return starter.ContinueWith(
//the following delegate avoids closure capture as much as possible
//completedTask.Result is the winning task; state == continuationAction
(completedTask, state) => { ((Action)state)(completedTask.Result); return default(TResult); },
continuationAction, scheduler, cancellationToken, continuationOptions, 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 ContinueWhenAnyImpl(Task[] tasks,
Func, TResult> continuationFunction, Action> continuationAction,
TaskContinuationOptions continuationOptions, CancellationToken cancellationToken, TaskScheduler scheduler, ref StackCrawlMark stackMark)
{
// check arguments
TaskFactory.CheckMultiTaskContinuationOptions(continuationOptions);
if (tasks == null) throw new ArgumentNullException("tasks");
if (tasks.Length == 0) throw new ArgumentException(Environment.GetResourceString("Task_MultiTaskContinuation_EmptyTaskList"), "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) throw new ArgumentNullException("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
#if !FEATURE_CORECLR || FEATURE_NETCORE
&& ((continuationOptions & TaskContinuationOptions.LazyCancellation) == 0)
#endif
)
{
return CreateCanceledTask(continuationOptions, cancellationToken);
}
// returned continuation task, off of starter
if (continuationFunction != null)
{
return starter.ContinueWith(
// Use a cached delegate
GenericDelegateCache.CWAnyFuncDelegate,
continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark);
}
else
{
Contract.Assert(continuationAction != null);
return starter.ContinueWith(
// Use a cached delegate
GenericDelegateCache.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
{
// ContinueWith delegate for TaskFactory.ContinueWhenAnyImpl(non-null continuationFunction)
internal static Func, object, TResult> CWAnyFuncDelegate =
(Task wrappedWinner, object state) =>
{
var func = (Func, TResult>)state;
var arg = (Task)wrappedWinner.Result;
return func(arg);
};
// ContinueWith delegate for TaskFactory.ContinueWhenAnyImpl(non-null continuationAction)
internal static Func, object, TResult> CWAnyActionDelegate =
(Task wrappedWinner, object state) =>
{
var action = (Action>)state;
var arg = (Task)wrappedWinner.Result;
action(arg);
return default(TResult);
};
// ContinueWith delegate for TaskFactory.ContinueWhenAllImpl(non-null continuationFunction)
internal static Func[]>, object, TResult> CWAllFuncDelegate =
(Task[]> wrappedAntecedents, object state) =>
{
wrappedAntecedents.NotifyDebuggerOfWaitCompletionIfNecessary();
var func = (Func[], TResult>)state;
return func(wrappedAntecedents.Result);
};
// ContinueWith delegate for TaskFactory.ContinueWhenAllImpl(non-null continuationAction)
internal static Func[]>, object, TResult> CWAllActionDelegate =
(Task[]> wrappedAntecedents, object state) =>
{
wrappedAntecedents.NotifyDebuggerOfWaitCompletionIfNecessary();
var action = (Action[]>)state;
action(wrappedAntecedents.Result);
return default(TResult);
};
}
}