#if NET20 || NET30 || NET35 || !NET_4_6 // 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. using System.Collections.Generic; using System.Security.Permissions; using LinqInternal.Threading.Needles; 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 . [HostProtection(Synchronization = true, ExternalThreading = true)] public class TaskCompletionSource { private readonly StructNeedle> _task; /// /// Creates a . /// public TaskCompletionSource() { _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) { // Empty } /// /// Creates a /// with the specified state. /// /// The state to use as the underlying /// 's AsyncState. public TaskCompletionSource(object state) : this(state, TaskCreationOptions.None) { // Empty } /// /// 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) { _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 _task.Value; } } /// 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 (!_task.Value.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) { throw new ArgumentNullException("exception"); } var rval = _task.Value.TrySetException(exception); if (!rval && !_task.Value.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) { throw new ArgumentNullException("exceptions"); } var defensiveCopy = new List(); foreach (var e in exceptions) { if (e == null) { throw new ArgumentException("The exceptions collection included at least one null element.", "exceptions"); } defensiveCopy.Add(e); } if (defensiveCopy.Count == 0) { throw new ArgumentException("The exceptions collection was empty.", "exceptions"); } var rval = _task.Value.TrySetException(defensiveCopy); if (!rval && !_task.Value.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) { throw new ArgumentNullException("exception"); } if (!TrySetException(exception)) { throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } } /// /// 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)) { throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } } /// /// 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) { var rval = _task.Value.TrySetResult(result); if (!rval && !_task.Value.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)) { throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } } /// /// 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(CancellationTokenSource.CanceledSource.Token); } // Enables a token to be stored into the canceled task internal bool TrySetCanceled(CancellationToken tokenToRecord) { var rval = _task.Value.TrySetCanceled(tokenToRecord); if (!rval && !_task.Value.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()) { throw new InvalidOperationException("An attempt was made to transition a task to a final state when it had already completed."); } } } } #endif