// 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. // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // // // TaskCompletionSource is the producer end of an unbound future. Its // Task member may be distributed as the consumer end of the future. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; using System.Diagnostics; using System.Diagnostics.Contracts; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Threading; // Disable the "reference to volatile field not treated as volatile" error. #pragma warning disable 0420 namespace System.Threading.Tasks { /// /// Represents the producer side of a unbound to a /// delegate, providing access to the consumer side through the property. /// /// /// /// It is often the case that a is desired to /// represent another asynchronous operation. /// TaskCompletionSource is provided for this purpose. It enables /// the creation of a task that can be handed out to consumers, and those consumers can use the members /// of the task as they would any other. However, unlike most tasks, the state of a task created by a /// TaskCompletionSource is controlled explicitly by the methods on TaskCompletionSource. This enables the /// completion of the external asynchronous operation to be propagated to the underlying Task. The /// separation also ensures that consumers are not able to transition the state without access to the /// corresponding TaskCompletionSource. /// /// /// All members of are thread-safe /// and may be used from multiple threads concurrently. /// /// /// The type of the result value assocatied with this . public class TaskCompletionSource { private readonly Task m_task; /// /// Creates a . /// public TaskCompletionSource() { m_task = new Task(); } /// /// Creates a /// with the specified options. /// /// /// The created /// by this instance and accessible through its property /// will be instantiated using the specified . /// /// The options to use when creating the underlying /// . /// /// The represent options invalid for use /// with a . /// public TaskCompletionSource(TaskCreationOptions creationOptions) : this(null, creationOptions) { } /// /// Creates a /// with the specified state. /// /// The state to use as the underlying /// 's AsyncState. public TaskCompletionSource(object state) : this(state, TaskCreationOptions.None) { } /// /// Creates a with /// the specified state and options. /// /// The options to use when creating the underlying /// . /// The state to use as the underlying /// 's AsyncState. /// /// The represent options invalid for use /// with a . /// public TaskCompletionSource(object state, TaskCreationOptions creationOptions) { m_task = new Task(state, creationOptions); } /// /// Gets the created /// by this . /// /// /// This property enables a consumer access to the that is controlled by this instance. /// The , , /// , and /// methods (and their "Try" variants) on this instance all result in the relevant state /// transitions on this underlying Task. /// public Task Task { get { return m_task; } } /// Spins until the underlying task is completed. /// This should only be called if the task is in the process of being completed by another thread. private void SpinUntilCompleted() { // Spin wait until the completion is finalized by another thread. var sw = new SpinWait(); while (!m_task.IsCompleted) sw.SpinOnce(); } /// /// Attempts to transition the underlying /// into the /// Faulted /// state. /// /// The exception to bind to this . /// True if the operation was successful; otherwise, false. /// This operation will return false if the /// is already in one /// of the three final states: /// RanToCompletion, /// Faulted, or /// Canceled. /// /// The argument is null. /// The was disposed. public bool TrySetException(Exception exception) { if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); bool rval = m_task.TrySetException(exception); if (!rval && !m_task.IsCompleted) SpinUntilCompleted(); return rval; } /// /// Attempts to transition the underlying /// into the /// Faulted /// state. /// /// The collection of exceptions to bind to this . /// True if the operation was successful; otherwise, false. /// This operation will return false if the /// is already in one /// of the three final states: /// RanToCompletion, /// Faulted, or /// Canceled. /// /// The argument is null. /// There are one or more null elements in . /// The collection is empty. /// The was disposed. public bool TrySetException(IEnumerable exceptions) { if (exceptions == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exceptions); List defensiveCopy = new List(); foreach (Exception e in exceptions) { if (e == null) ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NullException, ExceptionArgument.exceptions); defensiveCopy.Add(e); } if (defensiveCopy.Count == 0) ThrowHelper.ThrowArgumentException(ExceptionResource.TaskCompletionSourceT_TrySetException_NoExceptions, ExceptionArgument.exceptions); bool rval = m_task.TrySetException(defensiveCopy); if (!rval && !m_task.IsCompleted) SpinUntilCompleted(); return rval; } /// /// Transitions the underlying /// into the /// Faulted /// state. /// /// The exception to bind to this . /// The argument is null. /// /// The underlying is already in one /// of the three final states: /// RanToCompletion, /// Faulted, or /// Canceled. /// /// The was disposed. public void SetException(Exception exception) { if (exception == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.exception); if (!TrySetException(exception)) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } } /// /// Transitions the underlying /// into the /// Faulted /// state. /// /// The collection of exceptions to bind to this . /// The argument is null. /// There are one or more null elements in . /// /// The underlying is already in one /// of the three final states: /// RanToCompletion, /// Faulted, or /// Canceled. /// /// The was disposed. public void SetException(IEnumerable exceptions) { if (!TrySetException(exceptions)) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } } /// /// Attempts to transition the underlying /// into the /// RanToCompletion /// state. /// /// The result value to bind to this . /// True if the operation was successful; otherwise, false. /// This operation will return false if the /// is already in one /// of the three final states: /// RanToCompletion, /// Faulted, or /// Canceled. /// /// The was disposed. public bool TrySetResult(TResult result) { bool rval = m_task.TrySetResult(result); if (!rval && !m_task.IsCompleted) SpinUntilCompleted(); return rval; } /// /// Transitions the underlying /// into the /// RanToCompletion /// state. /// /// The result value to bind to this . /// /// The underlying is already in one /// of the three final states: /// RanToCompletion, /// Faulted, or /// Canceled. /// /// The was disposed. public void SetResult(TResult result) { if (!TrySetResult(result)) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } /// /// Attempts to transition the underlying /// into the /// Canceled /// state. /// /// True if the operation was successful; otherwise, false. /// This operation will return false if the /// is already in one /// of the three final states: /// RanToCompletion, /// Faulted, or /// Canceled. /// /// The was disposed. public bool TrySetCanceled() { return TrySetCanceled(default(CancellationToken)); } // Enables a token to be stored into the canceled task public bool TrySetCanceled(CancellationToken cancellationToken) { bool rval = m_task.TrySetCanceled(cancellationToken); if (!rval && !m_task.IsCompleted) SpinUntilCompleted(); return rval; } /// /// Transitions the underlying /// into the /// Canceled /// state. /// /// /// The underlying is already in one /// of the three final states: /// RanToCompletion, /// Faulted, or /// Canceled. /// /// The was disposed. public void SetCanceled() { if(!TrySetCanceled()) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.TaskT_TransitionToFinal_AlreadyCompleted); } } }