diff options
Diffstat (limited to 'src/mscorlib/src/System/Threading/Tasks/future.cs')
-rw-r--r-- | src/mscorlib/src/System/Threading/Tasks/future.cs | 1667 |
1 files changed, 1667 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Threading/Tasks/future.cs b/src/mscorlib/src/System/Threading/Tasks/future.cs new file mode 100644 index 0000000000..39e6ca1d45 --- /dev/null +++ b/src/mscorlib/src/System/Threading/Tasks/future.cs @@ -0,0 +1,1667 @@ +// 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. + +// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// +// +// +// A task that produces a value. +// +// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +using System; +using System.Collections.Generic; +using System.Runtime; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; +using System.Security; +using System.Security.Permissions; +using System.Threading; +using System.Diagnostics; +using System.Diagnostics.Contracts; + +// Disable the "reference to volatile field not treated as volatile" error. +#pragma warning disable 0420 + +namespace System.Threading.Tasks +{ + /// <summary> + /// Represents an asynchronous operation that produces a result at some time in the future. + /// </summary> + /// <typeparam name="TResult"> + /// The type of the result produced by this <see cref="Task{TResult}"/>. + /// </typeparam> + /// <remarks> + /// <para> + /// <see cref="Task{TResult}"/> instances may be created in a variety of ways. The most common approach is by + /// using the task's <see cref="Factory"/> property to retrieve a <see + /// cref="System.Threading.Tasks.TaskFactory{TResult}"/> instance that can be used to create tasks for several + /// purposes. For example, to create a <see cref="Task{TResult}"/> that runs a function, the factory's StartNew + /// method may be used: + /// <code> + /// // C# + /// var t = Task<int>.Factory.StartNew(() => GenerateResult()); + /// - or - + /// var t = Task.Factory.StartNew(() => GenerateResult()); + /// + /// ' Visual Basic + /// Dim t = Task<int>.Factory.StartNew(Function() GenerateResult()) + /// - or - + /// Dim t = Task.Factory.StartNew(Function() GenerateResult()) + /// </code> + /// </para> + /// <para> + /// The <see cref="Task{TResult}"/> class also provides constructors that initialize the task but that do not + /// schedule it for execution. For performance reasons, the StartNew method should be the + /// preferred mechanism for creating and scheduling computational tasks, but for scenarios where creation + /// and scheduling must be separated, the constructors may be used, and the task's + /// <see cref="System.Threading.Tasks.Task.Start()">Start</see> + /// method may then be used to schedule the task for execution at a later time. + /// </para> + /// <para> + /// All members of <see cref="Task{TResult}"/>, except for + /// <see cref="System.Threading.Tasks.Task.Dispose()">Dispose</see>, are thread-safe + /// and may be used from multiple threads concurrently. + /// </para> + /// </remarks> + [HostProtection(Synchronization = true, ExternalThreading = true)] + [DebuggerTypeProxy(typeof(SystemThreadingTasks_FutureDebugView<>))] + [DebuggerDisplay("Id = {Id}, Status = {Status}, Method = {DebuggerDisplayMethodDescription}, Result = {DebuggerDisplayResultDescription}")] + public class Task<TResult> : Task +#if SUPPORT_IOBSERVABLE + , IObservable<TResult> +#endif + { + internal TResult m_result; // The value itself, if set. + + private static readonly TaskFactory<TResult> s_Factory = new TaskFactory<TResult>(); + + // Delegate used by: + // public static Task<Task<TResult>> WhenAny<TResult>(IEnumerable<Task<TResult>> tasks); + // public static Task<Task<TResult>> WhenAny<TResult>(params Task<TResult>[] tasks); + // Used to "cast" from Task<Task> to Task<Task<TResult>>. + internal static readonly Func<Task<Task>, Task<TResult>> TaskWhenAnyCast = completed => (Task<TResult>)completed.Result; + + // Construct a promise-style task without any options. + internal Task() : + base() + { + } + + // Construct a promise-style task with state and options. + internal Task(object state, TaskCreationOptions options) : + base(state, options, promiseStyle:true) + { + } + + + // Construct a pre-completed Task<TResult> + internal Task(TResult result) : + base(false, TaskCreationOptions.None, default(CancellationToken)) + { + m_result = result; + } + + internal Task(bool canceled, TResult result, TaskCreationOptions creationOptions, CancellationToken ct) + : base(canceled, creationOptions, ct) + { + if (!canceled) + { + m_result = result; + } + } + + // Uncomment if/when we want Task.FromException + //// Construct a pre-faulted Task<TResult> + //internal Task(Exception exception) + // : base(exception) + //{ + //} + + /// <summary> + /// Initializes a new <see cref="Task{TResult}"/> with the specified function. + /// </summary> + /// <param name="function"> + /// The delegate that represents the code to execute in the task. When the function has completed, + /// the task's <see cref="Result"/> property will be set to return the result value of the function. + /// </param> + /// <exception cref="T:System.ArgumentException"> + /// The <paramref name="function"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task(Func<TResult> function) + : this(function, null, default(CancellationToken), + TaskCreationOptions.None, InternalTaskOptions.None, null) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + PossiblyCaptureContext(ref stackMark); + } + + + /// <summary> + /// Initializes a new <see cref="Task{TResult}"/> with the specified function. + /// </summary> + /// <param name="function"> + /// The delegate that represents the code to execute in the task. When the function has completed, + /// the task's <see cref="Result"/> property will be set to return the result value of the function. + /// </param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> to be assigned to this task.</param> + /// <exception cref="T:System.ArgumentException"> + /// 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> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task(Func<TResult> function, CancellationToken cancellationToken) + : this(function, null, cancellationToken, + TaskCreationOptions.None, InternalTaskOptions.None, null) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + PossiblyCaptureContext(ref stackMark); + } + + /// <summary> + /// Initializes a new <see cref="Task{TResult}"/> with the specified function and creation options. + /// </summary> + /// <param name="function"> + /// The delegate that represents the code to execute in the task. When the function has completed, + /// the task's <see cref="Result"/> property will be set to return the result value of the function. + /// </param> + /// <param name="creationOptions"> + /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to + /// customize the task's behavior. + /// </param> + /// <exception cref="T:System.ArgumentException"> + /// The <paramref name="function"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task(Func<TResult> function, TaskCreationOptions creationOptions) + : this(function, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, InternalTaskOptions.None, null) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + PossiblyCaptureContext(ref stackMark); + } + + /// <summary> + /// Initializes a new <see cref="Task{TResult}"/> with the specified function and creation options. + /// </summary> + /// <param name="function"> + /// The delegate that represents the code to execute in the task. When the function has completed, + /// the task's <see cref="Result"/> property will be set to return the result value of the function. + /// </param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param> + /// <param name="creationOptions"> + /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to + /// customize the task's behavior. + /// </param> + /// <exception cref="T:System.ArgumentException"> + /// The <paramref name="function"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task(Func<TResult> function, CancellationToken cancellationToken, TaskCreationOptions creationOptions) + : this(function, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, creationOptions, InternalTaskOptions.None, null) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + PossiblyCaptureContext(ref stackMark); + } + + /// <summary> + /// Initializes a new <see cref="Task{TResult}"/> with the specified function and state. + /// </summary> + /// <param name="function"> + /// The delegate that represents the code to execute in the task. When the function has completed, + /// the task's <see cref="Result"/> property will be set to return the result value of the function. + /// </param> + /// <param name="state">An object representing data to be used by the action.</param> + /// <exception cref="T:System.ArgumentException"> + /// The <paramref name="function"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task(Func<object, TResult> function, object state) + : this(function, state, null, default(CancellationToken), + TaskCreationOptions.None, InternalTaskOptions.None, null) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + PossiblyCaptureContext(ref stackMark); + } + + /// <summary> + /// Initializes a new <see cref="Task{TResult}"/> with the specified action, state, and options. + /// </summary> + /// <param name="function"> + /// The delegate that represents the code to execute in the task. When the function has completed, + /// the task's <see cref="Result"/> property will be set to return the result value of the function. + /// </param> + /// <param name="state">An object representing data to be used by the function.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> to be assigned to the new task.</param> + /// <exception cref="T:System.ArgumentException"> + /// 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> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task(Func<object, TResult> function, object state, CancellationToken cancellationToken) + : this(function, state, null, cancellationToken, + TaskCreationOptions.None, InternalTaskOptions.None, null) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + PossiblyCaptureContext(ref stackMark); + } + + /// <summary> + /// Initializes a new <see cref="Task{TResult}"/> with the specified action, state, and options. + /// </summary> + /// <param name="function"> + /// The delegate that represents the code to execute in the task. When the function has completed, + /// the task's <see cref="Result"/> property will be set to return the result value of the function. + /// </param> + /// <param name="state">An object representing data to be used by the function.</param> + /// <param name="creationOptions"> + /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to + /// customize the task's behavior. + /// </param> + /// <exception cref="T:System.ArgumentException"> + /// The <paramref name="function"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task(Func<object, TResult> function, object state, TaskCreationOptions creationOptions) + : this(function, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), + creationOptions, InternalTaskOptions.None, null) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + PossiblyCaptureContext(ref stackMark); + } + + + /// <summary> + /// Initializes a new <see cref="Task{TResult}"/> with the specified action, state, and options. + /// </summary> + /// <param name="function"> + /// The delegate that represents the code to execute in the task. When the function has completed, + /// the task's <see cref="Result"/> property will be set to return the result value of the function. + /// </param> + /// <param name="state">An object representing data to be used by the function.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> to be assigned to the new task.</param> + /// <param name="creationOptions"> + /// The <see cref="System.Threading.Tasks.TaskCreationOptions">TaskCreationOptions</see> used to + /// customize the task's behavior. + /// </param> + /// <exception cref="T:System.ArgumentException"> + /// The <paramref name="function"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="creationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskCreationOptions"/>. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task(Func<object, TResult> function, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions) + : this(function, state, Task.InternalCurrentIfAttached(creationOptions), cancellationToken, + creationOptions, InternalTaskOptions.None, null) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + PossiblyCaptureContext(ref stackMark); + } + + internal Task( + Func<TResult> valueSelector, Task parent, CancellationToken cancellationToken, + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler, + ref StackCrawlMark stackMark) : + this(valueSelector, parent, cancellationToken, + creationOptions, internalOptions, scheduler) + { + PossiblyCaptureContext(ref stackMark); + } + + /// <summary> + /// Creates a new future object. + /// </summary> + /// <param name="parent">The parent task for this future.</param> + /// <param name="valueSelector">A function that yields the future value.</param> + /// <param name="scheduler">The task scheduler which will be used to execute the future.</param> + /// <param name="cancellationToken">The CancellationToken for the task.</param> + /// <param name="creationOptions">Options to control the future's behavior.</param> + /// <param name="internalOptions">Internal options to control the future's behavior.</param> + /// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="creationOptions"/> argument specifies + /// a SelfReplicating <see cref="Task{TResult}"/>, which is illegal."/>.</exception> + internal Task(Func<TResult> valueSelector, Task parent, CancellationToken cancellationToken, + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler) : + base(valueSelector, null, parent, cancellationToken, creationOptions, internalOptions, scheduler) + { + if ((internalOptions & InternalTaskOptions.SelfReplicating) != 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions, ExceptionResource.TaskT_ctor_SelfReplicating); + } + } + + internal Task( + Func<object, TResult> valueSelector, object state, Task parent, CancellationToken cancellationToken, + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler, ref StackCrawlMark stackMark) : + this(valueSelector, state, parent, cancellationToken, creationOptions, internalOptions, scheduler) + { + PossiblyCaptureContext(ref stackMark); + } + + /// <summary> + /// Creates a new future object. + /// </summary> + /// <param name="parent">The parent task for this future.</param> + /// <param name="state">An object containing data to be used by the action; may be null.</param> + /// <param name="valueSelector">A function that yields the future value.</param> + /// <param name="cancellationToken">The CancellationToken for the task.</param> + /// <param name="scheduler">The task scheduler which will be used to execute the future.</param> + /// <param name="creationOptions">Options to control the future's behavior.</param> + /// <param name="internalOptions">Internal options to control the future's behavior.</param> + /// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="creationOptions"/> argument specifies + /// a SelfReplicating <see cref="Task{TResult}"/>, which is illegal."/>.</exception> + internal Task(Delegate valueSelector, object state, Task parent, CancellationToken cancellationToken, + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler) : + base(valueSelector, state, parent, cancellationToken, creationOptions, internalOptions, scheduler) + { + if ((internalOptions & InternalTaskOptions.SelfReplicating) != 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions, ExceptionResource.TaskT_ctor_SelfReplicating); + } + } + + + // Internal method used by TaskFactory<TResult>.StartNew() methods + internal static Task<TResult> StartNew(Task parent, Func<TResult> function, CancellationToken cancellationToken, + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler, ref StackCrawlMark stackMark) + { + if (function == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function); + } + if (scheduler == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); + } + if ((internalOptions & InternalTaskOptions.SelfReplicating) != 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions, ExceptionResource.TaskT_ctor_SelfReplicating); + } + + // Create and schedule the future. + Task<TResult> f = new Task<TResult>(function, parent, cancellationToken, creationOptions, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler, ref stackMark); + + f.ScheduleAndStart(false); + return f; + } + + // Internal method used by TaskFactory<TResult>.StartNew() methods + internal static Task<TResult> StartNew(Task parent, Func<object, TResult> function, object state, CancellationToken cancellationToken, + TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler, ref StackCrawlMark stackMark) + { + if (function == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.function); + } + if (scheduler == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); + } + if ((internalOptions & InternalTaskOptions.SelfReplicating) != 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.creationOptions, ExceptionResource.TaskT_ctor_SelfReplicating); + } + + // Create and schedule the future. + Task<TResult> f = new Task<TResult>(function, state, parent, cancellationToken, creationOptions, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler, ref stackMark); + + f.ScheduleAndStart(false); + return f; + } + + // Debugger support + private string DebuggerDisplayResultDescription + { + get + { + return IsRanToCompletion ? "" + m_result : Environment.GetResourceString("TaskT_DebuggerNoResult"); + } + } + + // Debugger support + private string DebuggerDisplayMethodDescription + { + get + { + Delegate d = (Delegate)m_action; + return d != null ? d.Method.ToString() : "{null}"; + } + } + + + // internal helper function breaks out logic used by TaskCompletionSource + internal bool TrySetResult(TResult result) + { + if (IsCompleted) return false; + Contract.Assert(m_action == null, "Task<T>.TrySetResult(): non-null m_action"); + + // "Reserve" the completion for this task, while making sure that: (1) No prior reservation + // has been made, (2) The result has not already been set, (3) An exception has not previously + // been recorded, and (4) Cancellation has not been requested. + // + // If the reservation is successful, then set the result and finish completion processing. + if (AtomicStateUpdate(TASK_STATE_COMPLETION_RESERVED, + TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED)) + { + m_result = result; + + // Signal completion, for waiting tasks + + // This logic used to be: + // Finish(false); + // However, that goes through a windy code path, involves many non-inlineable functions + // and which can be summarized more concisely with the following snippet from + // FinishStageTwo, omitting everything that doesn't pertain to TrySetResult. + Interlocked.Exchange(ref m_stateFlags, m_stateFlags | TASK_STATE_RAN_TO_COMPLETION); + + var cp = m_contingentProperties; + if (cp != null) cp.SetCompleted(); + + FinishStageThree(); + + return true; + } + + return false; + } + + // Transitions the promise task into a successfully completed state with the specified result. + // This is dangerous, as no synchronization is used, and thus must only be used + // before this task is handed out to any consumers, before any continuations are hooked up, + // before its wait handle is accessed, etc. It's use is limited to places like in FromAsync + // where the operation completes synchronously, and thus we know we can forcefully complete + // the task, avoiding expensive completion paths, before the task is actually given to anyone. + internal void DangerousSetResult(TResult result) + { + Contract.Assert(!IsCompleted, "The promise must not yet be completed."); + + // If we have a parent, we need to notify it of the completion. Take the slow path to handle that. + if (m_contingentProperties?.m_parent != null) + { + bool success = TrySetResult(result); + + // Nobody else has had a chance to complete this Task yet, so we should succeed. + Contract.Assert(success); + } + else + { + m_result = result; + m_stateFlags |= TASK_STATE_RAN_TO_COMPLETION; + } + } + + /// <summary> + /// Gets the result value of this <see cref="Task{TResult}"/>. + /// </summary> + /// <remarks> + /// The get accessor for this property ensures that the asynchronous operation is complete before + /// returning. Once the result of the computation is available, it is stored and will be returned + /// immediately on later calls to <see cref="Result"/>. + /// </remarks> + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public TResult Result + { + get { return IsWaitNotificationEnabledOrNotRanToCompletion ? GetResultCore(waitCompletionNotification: true) : m_result; } + } + + /// <summary> + /// Gets the result value of this <see cref="Task{TResult}"/> once the task has completed successfully. + /// </summary> + /// <remarks> + /// This version of Result should only be used if the task completed successfully and if there's + /// no debugger wait notification enabled for this task. + /// </remarks> + internal TResult ResultOnSuccess + { + get + { + Contract.Assert(!IsWaitNotificationEnabledOrNotRanToCompletion, + "Should only be used when the task completed successfully and there's no wait notification enabled"); + return m_result; + } + } + + // Implements Result. Result delegates to this method if the result isn't already available. + internal TResult GetResultCore(bool waitCompletionNotification) + { + // If the result has not been calculated yet, wait for it. + if (!IsCompleted) InternalWait(Timeout.Infinite, default(CancellationToken)); // won't throw if task faulted or canceled; that's handled below + + // Notify the debugger of the wait completion if it's requested such a notification + if (waitCompletionNotification) NotifyDebuggerOfWaitCompletionIfNecessary(); + + // Throw an exception if appropriate. + if (!IsRanToCompletion) ThrowIfExceptional(includeTaskCanceledExceptions: true); + + // We shouldn't be here if the result has not been set. + Contract.Assert(IsRanToCompletion, "Task<T>.Result getter: Expected result to have been set."); + + return m_result; + } + + // Allow multiple exceptions to be assigned to a promise-style task. + // This is useful when a TaskCompletionSource<T> stands in as a proxy + // for a "real" task (as we do in Unwrap(), ContinueWhenAny() and ContinueWhenAll()) + // and the "real" task ends up with multiple exceptions, which is possible when + // a task has children. + // + // Called from TaskCompletionSource<T>.SetException(IEnumerable<Exception>). + internal bool TrySetException(object exceptionObject) + { + Contract.Assert(m_action == null, "Task<T>.TrySetException(): non-null m_action"); + + // TCS.{Try}SetException() should have checked for this + Contract.Assert(exceptionObject != null, "Expected non-null exceptionObject argument"); + + // Only accept these types. + Contract.Assert( + (exceptionObject is Exception) || (exceptionObject is IEnumerable<Exception>) || + (exceptionObject is ExceptionDispatchInfo) || (exceptionObject is IEnumerable<ExceptionDispatchInfo>), + "Expected exceptionObject to be either Exception, ExceptionDispatchInfo, or IEnumerable<> of one of those"); + + bool returnValue = false; + + // "Reserve" the completion for this task, while making sure that: (1) No prior reservation + // has been made, (2) The result has not already been set, (3) An exception has not previously + // been recorded, and (4) Cancellation has not been requested. + // + // If the reservation is successful, then add the exception(s) and finish completion processing. + // + // The lazy initialization may not be strictly necessary, but I'd like to keep it here + // anyway. Some downstream logic may depend upon an inflated m_contingentProperties. + EnsureContingentPropertiesInitialized(); + if (AtomicStateUpdate(TASK_STATE_COMPLETION_RESERVED, + TASK_STATE_COMPLETION_RESERVED | TASK_STATE_RAN_TO_COMPLETION | TASK_STATE_FAULTED | TASK_STATE_CANCELED)) + { + AddException(exceptionObject); // handles singleton exception or exception collection + Finish(false); + returnValue = true; + } + + return returnValue; + + } + + // internal helper function breaks out logic used by TaskCompletionSource and AsyncMethodBuilder + // If the tokenToRecord is not None, it will be stored onto the task. + // This method is only valid for promise tasks. + internal bool TrySetCanceled(CancellationToken tokenToRecord) + { + return TrySetCanceled(tokenToRecord, null); + } + + // internal helper function breaks out logic used by TaskCompletionSource and AsyncMethodBuilder + // If the tokenToRecord is not None, it will be stored onto the task. + // If the OperationCanceledException is not null, it will be stored into the task's exception holder. + // This method is only valid for promise tasks. + internal bool TrySetCanceled(CancellationToken tokenToRecord, object cancellationException) + { + Contract.Assert(m_action == null, "Task<T>.TrySetCanceled(): non-null m_action"); +#if DEBUG + var ceAsEdi = cancellationException as ExceptionDispatchInfo; + Contract.Assert( + cancellationException == null || + cancellationException is OperationCanceledException || + (ceAsEdi != null && ceAsEdi.SourceException is OperationCanceledException), + "Expected null or an OperationCanceledException"); +#endif + + bool returnValue = false; + + // "Reserve" the completion for this task, while making sure that: (1) No prior reservation + // has been made, (2) The result has not already been set, (3) An exception has not previously + // been recorded, and (4) Cancellation has not been requested. + // + // If the reservation is successful, then record the cancellation and finish completion processing. + // + // Note: I had to access static Task variables through Task<object> + // instead of Task, because I have a property named Task and that + // was confusing the compiler. + if (AtomicStateUpdate(Task<object>.TASK_STATE_COMPLETION_RESERVED, + Task<object>.TASK_STATE_COMPLETION_RESERVED | Task<object>.TASK_STATE_CANCELED | + Task<object>.TASK_STATE_FAULTED | Task<object>.TASK_STATE_RAN_TO_COMPLETION)) + { + RecordInternalCancellationRequest(tokenToRecord, cancellationException); + CancellationCleanupLogic(); // perform cancellation cleanup actions + returnValue = true; + } + + return returnValue; + } + + /// <summary> + /// Provides access to factory methods for creating <see cref="Task{TResult}"/> instances. + /// </summary> + /// <remarks> + /// The factory returned from <see cref="Factory"/> is a default instance + /// of <see cref="System.Threading.Tasks.TaskFactory{TResult}"/>, as would result from using + /// the default constructor on the factory type. + /// </remarks> + public new static TaskFactory<TResult> Factory { get { return s_Factory; } } + + /// <summary> + /// Evaluates the value selector of the Task which is passed in as an object and stores the result. + /// </summary> + internal override void InnerInvoke() + { + // Invoke the delegate + Contract.Assert(m_action != null); + var func = m_action as Func<TResult>; + if (func != null) + { + m_result = func(); + return; + } + var funcWithState = m_action as Func<object, TResult>; + if (funcWithState != null) + { + m_result = funcWithState(m_stateObject); + return; + } + Contract.Assert(false, "Invalid m_action in Task<TResult>"); + } + + #region Await Support + + /// <summary>Gets an awaiter used to await this <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary> + /// <returns>An awaiter instance.</returns> + /// <remarks>This method is intended for compiler user rather than use directly in code.</remarks> + public new TaskAwaiter<TResult> GetAwaiter() + { + return new TaskAwaiter<TResult>(this); + } + + /// <summary>Configures an awaiter used to await this <see cref="System.Threading.Tasks.Task{TResult}"/>.</summary> + /// <param name="continueOnCapturedContext"> + /// true to attempt to marshal the continuation back to the original context captured; otherwise, false. + /// </param> + /// <returns>An object used to await this task.</returns> + public new ConfiguredTaskAwaitable<TResult> ConfigureAwait(bool continueOnCapturedContext) + { + return new ConfiguredTaskAwaitable<TResult>(this, continueOnCapturedContext); + } + + #endregion + + #region Continuation methods + + #region Action<Task<TResult>> continuations + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task as an argument. + /// </param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>> continuationAction) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark); + } + + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task as an argument. + /// </param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>> continuationAction, CancellationToken cancellationToken) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark); + } + + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task as an argument. + /// </param> + /// <param name="scheduler"> + /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution. + /// </param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="scheduler"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>> continuationAction, TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task as an argument. + /// </param> + /// <param name="continuationOptions"> + /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such + /// as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as + /// well as execution options, such as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>. + /// </param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed. If the continuation criteria specified through the <paramref + /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled + /// instead of scheduled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>> continuationAction, TaskContinuationOptions continuationOptions) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task as an argument. + /// </param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param> + /// <param name="continuationOptions"> + /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such + /// as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as + /// well as execution options, such as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>. + /// </param> + /// <param name="scheduler"> + /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its + /// execution. + /// </param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter + /// are not met, the continuation task will be canceled instead of scheduled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. + /// </exception> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="scheduler"/> argument is null. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>> continuationAction, CancellationToken cancellationToken, + TaskContinuationOptions continuationOptions, TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + + // Same as the above overload, only with a stack mark. + internal Task ContinueWith(Action<Task<TResult>> continuationAction, TaskScheduler scheduler, CancellationToken cancellationToken, + TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark) + { + if (continuationAction == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationAction); + } + + if (scheduler == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); + } + + TaskCreationOptions creationOptions; + InternalTaskOptions internalOptions; + CreationOptionsFromContinuationOptions( + continuationOptions, + out creationOptions, + out internalOptions); + + Task continuationTask = new ContinuationTaskFromResultTask<TResult>( + this, continuationAction, null, + creationOptions, internalOptions, + ref stackMark + ); + + // Register the continuation. If synchronous execution is requested, this may + // actually invoke the continuation before returning. + ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions); + + return continuationTask; + } + #endregion + + #region Action<Task<TResult>, Object> continuations + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation action.</param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark); + } + + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation action.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state,CancellationToken cancellationToken) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark); + } + + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation action.</param> + /// <param name="scheduler"> + /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution. + /// </param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="scheduler"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation action.</param> + /// <param name="continuationOptions"> + /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such + /// as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as + /// well as execution options, such as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>. + /// </param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed. If the continuation criteria specified through the <paramref + /// name="continuationOptions"/> parameter are not met, the continuation task will be canceled + /// instead of scheduled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state,TaskContinuationOptions continuationOptions) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <param name="continuationAction"> + /// An action to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation action.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new continuation task.</param> + /// <param name="continuationOptions"> + /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such + /// as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as + /// well as execution options, such as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>. + /// </param> + /// <param name="scheduler"> + /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its + /// execution. + /// </param> + /// <returns>A new continuation <see cref="Task"/>.</returns> + /// <remarks> + /// The returned <see cref="Task"/> will not be scheduled for execution until the current task has + /// completed. If the criteria specified through the <paramref name="continuationOptions"/> parameter + /// are not met, the continuation task will be canceled instead of scheduled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationAction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. + /// </exception> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="scheduler"/> argument is null. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, CancellationToken cancellationToken, + TaskContinuationOptions continuationOptions, TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith(continuationAction, state, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + + // Same as the above overload, only with a stack mark. + internal Task ContinueWith(Action<Task<TResult>, Object> continuationAction, Object state, TaskScheduler scheduler, CancellationToken cancellationToken, + TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark) + { + if (continuationAction == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationAction); + } + + if (scheduler == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); + } + + TaskCreationOptions creationOptions; + InternalTaskOptions internalOptions; + CreationOptionsFromContinuationOptions( + continuationOptions, + out creationOptions, + out internalOptions); + + Task continuationTask = new ContinuationTaskFromResultTask<TResult>( + this, continuationAction, state, + creationOptions, internalOptions, + ref stackMark + ); + + // Register the continuation. If synchronous execution is requested, this may + // actually invoke the continuation before returning. + ContinueWithCore(continuationTask, scheduler, cancellationToken, continuationOptions); + + return continuationTask; + } + + #endregion + + #region Func<Task<TResult>,TNewResult> continuations + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task as an argument. + /// </param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current + /// task has completed, whether it completes due to running to completion successfully, faulting due + /// to an unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark); + } + + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task as an argument. + /// </param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current + /// task has completed, whether it completes due to running to completion successfully, faulting due + /// to an unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, CancellationToken cancellationToken) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task as an argument. + /// </param> + /// <param name="scheduler"> + /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution. + /// </param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="scheduler"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task as an argument. + /// </param> + /// <param name="continuationOptions"> + /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such + /// as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as + /// well as execution options, such as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>. + /// </param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// <para> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current + /// task has completed, whether it completes due to running to completion successfully, faulting due + /// to an unhandled exception, or exiting out early due to being canceled. + /// </para> + /// <para> + /// The <paramref name="continuationFunction"/>, when executed, should return a <see + /// cref="Task{TNewResult}"/>. This task's completion state will be transferred to the task returned + /// from the ContinueWith call. + /// </para> + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, TaskContinuationOptions continuationOptions) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be passed as + /// an argument this completed task. + /// </param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param> + /// <param name="continuationOptions"> + /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such + /// as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as + /// well as execution options, such as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>. + /// </param> + /// <param name="scheduler"> + /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its + /// execution. + /// </param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// <para> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </para> + /// <para> + /// The <paramref name="continuationFunction"/>, when executed, should return a <see cref="Task{TNewResult}"/>. + /// This task's completion state will be transferred to the task returned from the + /// ContinueWith call. + /// </para> + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. + /// </exception> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="scheduler"/> argument is null. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, CancellationToken cancellationToken, + TaskContinuationOptions continuationOptions, TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + + // Same as the above overload, just with a stack mark. + internal Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, TNewResult> continuationFunction, TaskScheduler scheduler, + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark) + { + if (continuationFunction == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + } + + if (scheduler == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); + } + + TaskCreationOptions creationOptions; + InternalTaskOptions internalOptions; + CreationOptionsFromContinuationOptions( + continuationOptions, + out creationOptions, + out internalOptions); + + Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>( + this, continuationFunction, null, + creationOptions, internalOptions, + ref stackMark + ); + + // Register the continuation. If synchronous execution is requested, this may + // actually invoke the continuation before returning. + ContinueWithCore(continuationFuture, scheduler, cancellationToken, continuationOptions); + + return continuationFuture; + } + #endregion + + #region Func<Task<TResult>, Object,TNewResult> continuations + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation function.</param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current + /// task has completed, whether it completes due to running to completion successfully, faulting due + /// to an unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), TaskContinuationOptions.None, ref stackMark); + } + + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation function.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current + /// task has completed, whether it completes due to running to completion successfully, faulting due + /// to an unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state, + CancellationToken cancellationToken) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, cancellationToken, TaskContinuationOptions.None, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation function.</param> + /// <param name="scheduler"> + /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its execution. + /// </param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="scheduler"/> argument is null. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state, + TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, state, scheduler, default(CancellationToken), TaskContinuationOptions.None, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation function.</param> + /// <param name="continuationOptions"> + /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such + /// as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as + /// well as execution options, such as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>. + /// </param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// <para> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current + /// task has completed, whether it completes due to running to completion successfully, faulting due + /// to an unhandled exception, or exiting out early due to being canceled. + /// </para> + /// <para> + /// The <paramref name="continuationFunction"/>, when executed, should return a <see + /// cref="Task{TNewResult}"/>. This task's completion state will be transferred to the task returned + /// from the ContinueWith call. + /// </para> + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state, + TaskContinuationOptions continuationOptions) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, state, TaskScheduler.Current, default(CancellationToken), continuationOptions, ref stackMark); + } + + /// <summary> + /// Creates a continuation that executes when the target <see cref="Task{TResult}"/> completes. + /// </summary> + /// <typeparam name="TNewResult"> + /// The type of the result produced by the continuation. + /// </typeparam> + /// <param name="continuationFunction"> + /// A function to run when the <see cref="Task{TResult}"/> completes. When run, the delegate will be + /// passed the completed task and the caller-supplied state object as arguments. + /// </param> + /// <param name="state">An object representing data to be used by the continuation function.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> that will be assigned to the new task.</param> + /// <param name="continuationOptions"> + /// Options for when the continuation is scheduled and how it behaves. This includes criteria, such + /// as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.OnlyOnCanceled">OnlyOnCanceled</see>, as + /// well as execution options, such as <see + /// cref="System.Threading.Tasks.TaskContinuationOptions.ExecuteSynchronously">ExecuteSynchronously</see>. + /// </param> + /// <param name="scheduler"> + /// The <see cref="TaskScheduler"/> to associate with the continuation task and to use for its + /// execution. + /// </param> + /// <returns>A new continuation <see cref="Task{TNewResult}"/>.</returns> + /// <remarks> + /// <para> + /// The returned <see cref="Task{TNewResult}"/> will not be scheduled for execution until the current task has + /// completed, whether it completes due to running to completion successfully, faulting due to an + /// unhandled exception, or exiting out early due to being canceled. + /// </para> + /// <para> + /// The <paramref name="continuationFunction"/>, when executed, should return a <see cref="Task{TNewResult}"/>. + /// This task's completion state will be transferred to the task returned from the + /// ContinueWith call. + /// </para> + /// </remarks> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="continuationFunction"/> argument is null. + /// </exception> + /// <exception cref="T:System.ArgumentOutOfRangeException"> + /// The <paramref name="continuationOptions"/> argument specifies an invalid value for <see + /// cref="T:System.Threading.Tasks.TaskContinuationOptions">TaskContinuationOptions</see>. + /// </exception> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="scheduler"/> argument is null. + /// </exception> + /// <exception cref="T:System.ObjectDisposedException">The provided <see cref="System.Threading.CancellationToken">CancellationToken</see> + /// has already been disposed. + /// </exception> + [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var have to be marked non-inlineable + public Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state, + CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler) + { + StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller; + return ContinueWith<TNewResult>(continuationFunction, state, scheduler, cancellationToken, continuationOptions, ref stackMark); + } + + // Same as the above overload, just with a stack mark. + internal Task<TNewResult> ContinueWith<TNewResult>(Func<Task<TResult>, Object, TNewResult> continuationFunction, Object state, + TaskScheduler scheduler, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, ref StackCrawlMark stackMark) + { + if (continuationFunction == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.continuationFunction); + } + + if (scheduler == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.scheduler); + } + + TaskCreationOptions creationOptions; + InternalTaskOptions internalOptions; + CreationOptionsFromContinuationOptions( + continuationOptions, + out creationOptions, + out internalOptions); + + Task<TNewResult> continuationFuture = new ContinuationResultTaskFromResultTask<TResult,TNewResult>( + this, continuationFunction, state, + creationOptions, internalOptions, + ref stackMark + ); + + // Register the continuation. If synchronous execution is requested, this may + // actually invoke the continuation before returning. + ContinueWithCore(continuationFuture, scheduler, cancellationToken, continuationOptions); + + return continuationFuture; + } + + #endregion + + #endregion + + /// <summary> + /// Subscribes an <see cref="IObserver{TResult}"/> to receive notification of the final state of this <see cref="Task{TResult}"/>. + /// </summary> + /// <param name="observer"> + /// The <see cref="IObserver{TResult}"/> to call on task completion. If this Task throws an exception, + /// observer.OnError is called with this Task's AggregateException. If this Task RanToCompletion, + /// observer.OnNext is called with this Task's result, followed by a call to observer.OnCompleted. + /// If this Task is Canceled, observer.OnError is called with a TaskCanceledException + /// containing this Task's CancellationToken + /// </param> + /// <returns>An IDisposable object <see cref="Task"/>.</returns> + /// <exception cref="T:System.ArgumentNullException"> + /// The <paramref name="observer"/> argument is null. + /// </exception> +#if SUPPORT_IOBSERVABLE + IDisposable IObservable<TResult>.Subscribe(IObserver<TResult> observer) + { + if (observer == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.observer); + + + var continuationTask = + this.ContinueWith(delegate(Task<TResult> observedTask, object taskObserverObject) + { + IObserver<TResult> taskObserver = (IObserver<TResult>)taskObserverObject; + if (observedTask.IsFaulted) + taskObserver.OnError(observedTask.Exception); + else if (observedTask.IsCanceled) + taskObserver.OnError(new TaskCanceledException(observedTask)); + else + { + taskObserver.OnNext(observedTask.Result); + taskObserver.OnCompleted(); + } + + }, observer, TaskScheduler.Default); + + return new DisposableSubscription(this, continuationTask); + } +#endif + } + +#if SUPPORT_IOBSERVABLE + // Class that calls RemoveContinuation if Dispose() is called before task completion + internal class DisposableSubscription : IDisposable + { + private Task _notifyObserverContinuationTask; + private Task _observedTask; + + internal DisposableSubscription(Task observedTask, Task notifyObserverContinuationTask) + { + _observedTask = observedTask; + _notifyObserverContinuationTask = notifyObserverContinuationTask; + } + void IDisposable.Dispose() + { + Task localObservedTask = _observedTask; + Task localNotifyingContinuationTask = _notifyObserverContinuationTask; + if (localObservedTask != null && localNotifyingContinuationTask != null && !localObservedTask.IsCompleted) + { + localObservedTask.RemoveContinuation(localNotifyingContinuationTask); + } + _observedTask = null; + _notifyObserverContinuationTask = null; + } + } +#endif + + // Proxy class for better debugging experience + internal class SystemThreadingTasks_FutureDebugView<TResult> + { + private Task<TResult> m_task; + + public SystemThreadingTasks_FutureDebugView(Task<TResult> task) + { + m_task = task; + } + + public TResult Result { get { return m_task.Status == TaskStatus.RanToCompletion ? m_task.Result : default(TResult); } } + public object AsyncState { get { return m_task.AsyncState; } } + public TaskCreationOptions CreationOptions { get { return m_task.CreationOptions; } } + public Exception Exception { get { return m_task.Exception; } } + public int Id { get { return m_task.Id; } } + public bool CancellationPending { get { return (m_task.Status == TaskStatus.WaitingToRun) && m_task.CancellationToken.IsCancellationRequested; } } + public TaskStatus Status { get { return m_task.Status; } } + + + } +} |