#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