Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

1418 lines
41 KiB
C#

//
// Task.cs
//
// Authors:
// Marek Safar <marek.safar@gmail.com>
// Jérémie Laval <jeremie dot laval at xamarin dot com>
//
// Copyright (c) 2008 Jérémie "Garuma" Laval
// Copyright 2011-2013 Xamarin Inc (http://www.xamarin.com).
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
#if NET_4_0
using System;
using System.Threading;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace System.Threading.Tasks
{
[System.Diagnostics.DebuggerDisplay ("Id = {Id}, Status = {Status}")]
[System.Diagnostics.DebuggerTypeProxy (typeof (TaskDebuggerView))]
public class Task : IDisposable, IAsyncResult
{
// With this attribute each thread has its own value so that it's correct for our Schedule code
// and for Parent property.
[System.ThreadStatic]
static Task current;
// parent is the outer task in which this task is created
Task parent;
// A reference to a Task on which this continuation is attached to
Task contAncestor;
static int id = -1;
static readonly TaskFactory defaultFactory = new TaskFactory ();
CountdownEvent childTasks;
int taskId;
TaskCreationOptions creationOptions;
internal TaskScheduler scheduler;
TaskExceptionSlot exSlot;
ManualResetEvent wait_handle;
TaskStatus status;
TaskActionInvoker invoker;
object state;
internal AtomicBooleanValue executing;
TaskCompletionQueue<IContinuation> continuations;
CancellationToken token;
CancellationTokenRegistration? cancellationRegistration;
ExecutionContext ec;
internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
const TaskCreationOptions MaxTaskCreationOptions =
#if NET_4_5
TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler |
#endif
TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent;
public Task (Action action)
: this (action, TaskCreationOptions.None)
{
}
public Task (Action action, TaskCreationOptions creationOptions)
: this (action, CancellationToken.None, creationOptions)
{
}
public Task (Action action, CancellationToken cancellationToken)
: this (action, cancellationToken, TaskCreationOptions.None)
{
}
public Task (Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
: this (TaskActionInvoker.Create (action), null, cancellationToken, creationOptions, current)
{
if (action == null)
throw new ArgumentNullException ("action");
if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
throw new ArgumentOutOfRangeException ("creationOptions");
}
public Task (Action<object> action, object state)
: this (action, state, TaskCreationOptions.None)
{
}
public Task (Action<object> action, object state, TaskCreationOptions creationOptions)
: this (action, state, CancellationToken.None, creationOptions)
{
}
public Task (Action<object> action, object state, CancellationToken cancellationToken)
: this (action, state, cancellationToken, TaskCreationOptions.None)
{
}
public Task (Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
: this (TaskActionInvoker.Create (action), state, cancellationToken, creationOptions, current)
{
if (action == null)
throw new ArgumentNullException ("action");
if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
throw new ArgumentOutOfRangeException ("creationOptions");
}
internal Task (TaskActionInvoker invoker, object state, CancellationToken cancellationToken,
TaskCreationOptions creationOptions, Task parent = null, Task contAncestor = null, bool ignoreCancellation = false)
{
this.invoker = invoker;
this.creationOptions = creationOptions;
this.state = state;
this.taskId = Interlocked.Increment (ref id);
this.token = cancellationToken;
this.parent = parent = parent == null ? current : parent;
this.contAncestor = contAncestor;
this.status = cancellationToken.IsCancellationRequested && !ignoreCancellation ? TaskStatus.Canceled : TaskStatus.Created;
// Process creationOptions
#if NET_4_5
if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent)
&& !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach))
#else
if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
#endif
parent.AddChild ();
if (token.CanBeCanceled && !ignoreCancellation)
cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
ec = ExecutionContext.Capture (false, true);
}
static bool HasFlag (TaskCreationOptions opt, TaskCreationOptions member)
{
return (opt & member) == member;
}
#region Start
public void Start ()
{
Start (TaskScheduler.Current);
}
public void Start (TaskScheduler scheduler)
{
if (scheduler == null)
throw new ArgumentNullException ("scheduler");
if (status >= TaskStatus.WaitingToRun)
throw new InvalidOperationException ("The Task is not in a valid state to be started.");
if (IsContinuation)
throw new InvalidOperationException ("Start may not be called on a continuation task");
if (IsPromise)
throw new InvalidOperationException ("Start may not be called on a promise-style task");
SetupScheduler (scheduler);
Schedule (true);
}
internal void SetupScheduler (TaskScheduler scheduler)
{
this.scheduler = scheduler;
Status = TaskStatus.WaitingForActivation;
}
public void RunSynchronously ()
{
RunSynchronously (TaskScheduler.Current);
}
public void RunSynchronously (TaskScheduler scheduler)
{
if (scheduler == null)
throw new ArgumentNullException ("scheduler");
if (Status > TaskStatus.WaitingForActivation)
throw new InvalidOperationException ("The task is not in a valid state to be started");
if (IsContinuation)
throw new InvalidOperationException ("RunSynchronously may not be called on a continuation task");
if (IsPromise)
throw new InvalidOperationException ("RunSynchronously may not be called on a promise-style task");
RunSynchronouslyCore (scheduler, true);
}
internal void RunSynchronouslyCore (TaskScheduler scheduler, bool throwException)
{
SetupScheduler (scheduler);
Status = TaskStatus.WaitingToRun;
try {
if (scheduler.RunInline (this, false))
return;
} catch (Exception inner) {
var ex = new TaskSchedulerException (inner);
TrySetException (new AggregateException (ex), false, true);
if (throwException)
throw ex;
return;
}
Schedule (throwException);
WaitCore (Timeout.Infinite, CancellationToken.None, false);
}
#endregion
#region ContinueWith
public Task ContinueWith (Action<Task> continuationAction)
{
return ContinueWith (continuationAction, TaskContinuationOptions.None);
}
public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
{
return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
}
public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
{
return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
}
public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
{
return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
}
public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
if (continuationAction == null)
throw new ArgumentNullException ("continuationAction");
if (scheduler == null)
throw new ArgumentNullException ("scheduler");
return ContinueWith (TaskActionInvoker.Create (continuationAction), cancellationToken, continuationOptions, scheduler);
}
internal Task ContinueWith (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
var lazyCancellation = false;
#if NET_4_5
lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
#endif
var continuation = new Task (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), null, this, lazyCancellation);
ContinueWithCore (continuation, continuationOptions, scheduler);
return continuation;
}
public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction)
{
return ContinueWith<TResult> (continuationFunction, TaskContinuationOptions.None);
}
public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
{
return ContinueWith<TResult> (continuationFunction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
}
public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
{
return ContinueWith<TResult> (continuationFunction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
}
public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
{
return ContinueWith<TResult> (continuationFunction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
}
public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
if (continuationFunction == null)
throw new ArgumentNullException ("continuationFunction");
if (scheduler == null)
throw new ArgumentNullException ("scheduler");
return ContinueWith<TResult> (TaskActionInvoker.Create (continuationFunction), cancellationToken, continuationOptions, scheduler);
}
internal Task<TResult> ContinueWith<TResult> (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
var lazyCancellation = false;
#if NET_4_5
lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
#endif
var continuation = new Task<TResult> (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), parent, this, lazyCancellation);
ContinueWithCore (continuation, continuationOptions, scheduler);
return continuation;
}
internal void ContinueWithCore (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler)
{
const TaskContinuationOptions wrongRan = TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.OnlyOnRanToCompletion;
const TaskContinuationOptions wrongCanceled = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.OnlyOnCanceled;
const TaskContinuationOptions wrongFaulted = TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.OnlyOnFaulted;
if (((options & wrongRan) == wrongRan) || ((options & wrongCanceled) == wrongCanceled) || ((options & wrongFaulted) == wrongFaulted))
throw new ArgumentException ("continuationOptions", "Some options are mutually exclusive");
// Already set the scheduler so that user can call Wait and that sort of stuff
continuation.scheduler = scheduler;
continuation.Status = TaskStatus.WaitingForActivation;
ContinueWith (new TaskContinuation (continuation, options));
}
internal bool ContinueWith (IContinuation continuation, bool canExecuteInline = true)
{
if (IsCompleted) {
if (!canExecuteInline)
return false;
continuation.Execute ();
return true;
}
continuations.Add (continuation);
// Retry in case completion was achieved but event adding was too late
if (IsCompleted && continuations.Remove (continuation)) {
if (!canExecuteInline)
return false;
continuation.Execute ();
}
return true;
}
internal void RemoveContinuation (IContinuation continuation)
{
continuations.Remove (continuation);
}
static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
{
TaskCreationOptions options = TaskCreationOptions.None;
if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
options |= TaskCreationOptions.AttachedToParent;
if ((kind & TaskContinuationOptions.PreferFairness) > 0)
options |= TaskCreationOptions.PreferFairness;
if ((kind & TaskContinuationOptions.LongRunning) > 0)
options |= TaskCreationOptions.LongRunning;
return options;
}
#endregion
#region Internal and protected thingies
internal void Schedule (bool throwException)
{
Status = TaskStatus.WaitingToRun;
try {
scheduler.QueueTask (this);
} catch (Exception inner) {
var ex = new TaskSchedulerException (inner);
TrySetException (new AggregateException (ex), false, true);
if (throwException)
throw ex;
}
}
void ThreadStart ()
{
/* Allow scheduler to break fairness of deque ordering without
* breaking its semantic (the task can be executed twice but the
* second time it will return immediately
*/
if (!executing.TryRelaxedSet ())
return;
// Disable CancellationToken direct cancellation
if (cancellationRegistration != null) {
cancellationRegistration.Value.Dispose ();
cancellationRegistration = null;
}
// If Task are ran inline on the same thread we might trash these values
var saveCurrent = current;
var saveScheduler = TaskScheduler.Current;
current = this;
#if NET_4_5
TaskScheduler.Current = HasFlag (creationOptions, TaskCreationOptions.HideScheduler) ? TaskScheduler.Default : scheduler;
#else
TaskScheduler.Current = scheduler;
#endif
if (!token.IsCancellationRequested) {
status = TaskStatus.Running;
try {
if (ec != null)
ExecutionContext.Run (ec, l => ((Task) l).InnerInvoke (), this);
else
InnerInvoke ();
} catch (OperationCanceledException oce) {
if (token != CancellationToken.None && oce.CancellationToken == token)
CancelReal ();
else
HandleGenericException (oce);
} catch (Exception e) {
HandleGenericException (e);
}
} else {
CancelReal ();
}
if (saveCurrent != null)
current = saveCurrent;
if (saveScheduler != null)
TaskScheduler.Current = saveScheduler;
Finish ();
}
internal bool TrySetCanceled ()
{
if (IsCompleted)
return false;
if (!executing.TryRelaxedSet ()) {
var sw = new SpinWait ();
while (!IsCompleted)
sw.SpinOnce ();
return false;
}
CancelReal ();
return true;
}
internal bool TrySetException (AggregateException aggregate, bool cancellation, bool observed)
{
if (IsCompleted)
return false;
if (!executing.TryRelaxedSet ()) {
var sw = new SpinWait ();
while (!IsCompleted)
sw.SpinOnce ();
return false;
}
if (cancellation) {
ExceptionSlot.Exception = aggregate;
Thread.MemoryBarrier ();
CancelReal ();
} else {
HandleGenericException (aggregate);
}
if (observed)
exSlot.Observed = true;
return true;
}
internal bool TrySetExceptionObserved ()
{
if (exSlot != null) {
exSlot.Observed = true;
return true;
}
return false;
}
internal void Execute ()
{
ThreadStart ();
}
internal void AddChild ()
{
if (childTasks == null)
Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
childTasks.AddCount ();
}
internal void ChildCompleted (AggregateException childEx)
{
if (childEx != null) {
if (ExceptionSlot.ChildExceptions == null)
Interlocked.CompareExchange (ref ExceptionSlot.ChildExceptions, new ConcurrentQueue<AggregateException> (), null);
ExceptionSlot.ChildExceptions.Enqueue (childEx);
}
if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
ProcessChildExceptions ();
Status = exSlot == null ? TaskStatus.RanToCompletion : TaskStatus.Faulted;
ProcessCompleteDelegates ();
if (parent != null && NotifyParentOnFinish ())
parent = null;
}
}
void InnerInvoke ()
{
if (IsContinuation) {
var ancestor = contAncestor;
contAncestor = null;
invoker.Invoke (ancestor, state, this);
} else {
invoker.Invoke (this, state, this);
}
}
internal void Finish ()
{
// If there was children created and they all finished, we set the countdown
if (childTasks != null) {
if (childTasks.Signal ())
ProcessChildExceptions (true);
}
// Don't override Canceled or Faulted
if (status == TaskStatus.Running) {
if (childTasks == null || childTasks.IsSet)
Status = TaskStatus.RanToCompletion;
else
Status = TaskStatus.WaitingForChildrenToComplete;
}
if (wait_handle != null)
wait_handle.Set ();
// Tell parent that we are finished
if (parent != null && NotifyParentOnFinish ()) {
//
// Break the reference back to the parent, otherwise any Tasks created from another Task's thread of
// execution will create an undesired linked-list that the GC cannot free. See bug #18398.
//
parent = null;
}
// Completions are already processed when task is canceled or faulted
if (status == TaskStatus.RanToCompletion)
ProcessCompleteDelegates ();
// Reset the current thingies
if (current == this)
current = null;
if (TaskScheduler.Current == scheduler)
TaskScheduler.Current = null;
if (cancellationRegistration.HasValue)
cancellationRegistration.Value.Dispose ();
}
bool NotifyParentOnFinish ()
{
if (!HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
return true;
#if NET_4_5
if (HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach))
return true;
#endif
if (status != TaskStatus.WaitingForChildrenToComplete)
parent.ChildCompleted (Exception);
return false;
}
void ProcessCompleteDelegates ()
{
if (continuations.HasElements) {
IContinuation continuation;
while (continuations.TryGetNextCompletion (out continuation))
continuation.Execute ();
}
}
void ProcessChildExceptions (bool isParent = false)
{
if (exSlot == null || exSlot.ChildExceptions == null)
return;
if (ExceptionSlot.Exception == null)
exSlot.Exception = new AggregateException ();
AggregateException childEx;
while (exSlot.ChildExceptions.TryDequeue (out childEx))
exSlot.Exception.AddChildException (childEx);
if (isParent) {
Status = TaskStatus.Faulted;
ProcessCompleteDelegates ();
}
}
#endregion
#region Cancel and Wait related method
internal void CancelReal ()
{
Status = TaskStatus.Canceled;
if (wait_handle != null)
wait_handle.Set ();
ProcessCompleteDelegates ();
}
void HandleGenericException (Exception e)
{
HandleGenericException (new AggregateException (e));
}
void HandleGenericException (AggregateException e)
{
ExceptionSlot.Exception = e;
Thread.MemoryBarrier ();
Status = TaskStatus.Faulted;
if (wait_handle != null)
wait_handle.Set ();
ProcessCompleteDelegates ();
}
internal bool WaitOnChildren ()
{
if (Status == TaskStatus.WaitingForChildrenToComplete && childTasks != null) {
childTasks.Wait ();
return true;
}
return false;
}
public void Wait ()
{
Wait (Timeout.Infinite, CancellationToken.None);
}
public void Wait (CancellationToken cancellationToken)
{
Wait (Timeout.Infinite, cancellationToken);
}
public bool Wait (TimeSpan timeout)
{
return Wait (CheckTimeout (timeout), CancellationToken.None);
}
public bool Wait (int millisecondsTimeout)
{
return Wait (millisecondsTimeout, CancellationToken.None);
}
public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
{
if (millisecondsTimeout < -1)
throw new ArgumentOutOfRangeException ("millisecondsTimeout");
bool result = WaitCore (millisecondsTimeout, cancellationToken, true);
if (IsCanceled)
throw new AggregateException (new TaskCanceledException (this));
var exception = Exception;
if (exception != null)
throw exception;
return result;
}
internal bool WaitCore (int millisecondsTimeout, CancellationToken cancellationToken, bool runInline)
{
if (IsCompleted)
return true;
// If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
if (runInline && Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled) {
try {
scheduler.RunInline (this, true);
} catch (Exception e) {
throw new TaskSchedulerException (e);
}
}
bool result = true;
if (!IsCompleted) {
var continuation = new ManualResetContinuation ();
try {
ContinueWith (continuation);
result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
} finally {
if (!result)
RemoveContinuation (continuation);
continuation.Dispose ();
}
}
return result;
}
public static void WaitAll (params Task[] tasks)
{
WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
}
public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
{
WaitAll (tasks, Timeout.Infinite, cancellationToken);
}
public static bool WaitAll (Task[] tasks, TimeSpan timeout)
{
return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
}
public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
{
return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
}
public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
bool result = true;
foreach (var t in tasks) {
if (t == null)
throw new ArgumentException ("tasks", "the tasks argument contains a null element");
result &= t.Status == TaskStatus.RanToCompletion;
}
if (!result) {
var continuation = new CountdownContinuation (tasks.Length);
try {
foreach (var t in tasks)
t.ContinueWith (continuation);
result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
} finally {
List<Exception> exceptions = null;
foreach (var t in tasks) {
if (result) {
if (t.Status == TaskStatus.RanToCompletion)
continue;
if (exceptions == null)
exceptions = new List<Exception> ();
if (t.Exception != null)
exceptions.AddRange (t.Exception.InnerExceptions);
else
exceptions.Add (new TaskCanceledException (t));
} else {
t.RemoveContinuation (continuation);
}
}
continuation.Dispose ();
if (exceptions != null)
throw new AggregateException (exceptions);
}
}
return result;
}
public static int WaitAny (params Task[] tasks)
{
return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
}
public static int WaitAny (Task[] tasks, TimeSpan timeout)
{
return WaitAny (tasks, CheckTimeout (timeout));
}
public static int WaitAny (Task[] tasks, int millisecondsTimeout)
{
return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
}
public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
{
return WaitAny (tasks, Timeout.Infinite, cancellationToken);
}
public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
if (millisecondsTimeout < -1)
throw new ArgumentOutOfRangeException ("millisecondsTimeout");
CheckForNullTasks (tasks);
if (tasks.Length > 0) {
var continuation = new ManualResetContinuation ();
bool result = false;
try {
for (int i = 0; i < tasks.Length; i++) {
var t = tasks[i];
if (t.IsCompleted)
return i;
t.ContinueWith (continuation);
}
if (!(result = continuation.Event.Wait (millisecondsTimeout, cancellationToken)))
return -1;
} finally {
if (!result)
foreach (var t in tasks)
t.RemoveContinuation (continuation);
continuation.Dispose ();
}
}
int firstFinished = -1;
for (int i = 0; i < tasks.Length; i++) {
var t = tasks[i];
if (t.IsCompleted) {
firstFinished = i;
break;
}
}
return firstFinished;
}
static int CheckTimeout (TimeSpan timeout)
{
try {
return checked ((int)timeout.TotalMilliseconds);
} catch (System.OverflowException) {
throw new ArgumentOutOfRangeException ("timeout");
}
}
static void CheckForNullTasks (Task[] tasks)
{
foreach (var t in tasks)
if (t == null)
throw new ArgumentException ("tasks", "the tasks argument contains a null element");
}
#endregion
#region Dispose
public void Dispose ()
{
Dispose (true);
}
protected virtual void Dispose (bool disposing)
{
if (!IsCompleted)
throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
// Set action to null so that the GC can collect the delegate and thus
// any big object references that the user might have captured in a anonymous method
if (disposing) {
parent = null;
invoker = null;
state = null;
if (cancellationRegistration != null)
cancellationRegistration.Value.Dispose ();
if (wait_handle != null)
wait_handle.Dispose ();
}
}
#endregion
#if NET_4_5
public
#else
internal
#endif
Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken,
TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
if (continuationAction == null)
throw new ArgumentNullException ("continuationAction");
if (scheduler == null)
throw new ArgumentNullException ("scheduler");
Task continuation = new Task (TaskActionInvoker.Create (continuationAction),
state, cancellationToken,
GetCreationOptions (continuationOptions),
parent,
this);
ContinueWithCore (continuation, continuationOptions, scheduler);
return continuation;
}
#if NET_4_5
public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext)
{
return new ConfiguredTaskAwaitable (this, continueOnCapturedContext);
}
public Task ContinueWith (Action<Task, object> continuationAction, object state)
{
return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
}
public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken)
{
return ContinueWith (continuationAction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
}
public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskContinuationOptions continuationOptions)
{
return ContinueWith (continuationAction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
}
public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskScheduler scheduler)
{
return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
}
public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
{
return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
}
public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
{
return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
}
public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
{
return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
}
public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
{
return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
}
public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
if (continuationFunction == null)
throw new ArgumentNullException ("continuationFunction");
if (scheduler == null)
throw new ArgumentNullException ("scheduler");
var t = new Task<TResult> (TaskActionInvoker.Create (continuationFunction),
state,
cancellationToken,
GetCreationOptions (continuationOptions),
parent,
this);
ContinueWithCore (t, continuationOptions, scheduler);
return t;
}
public static Task Delay (int millisecondsDelay)
{
return Delay (millisecondsDelay, CancellationToken.None);
}
public static Task Delay (TimeSpan delay)
{
return Delay (CheckTimeout (delay), CancellationToken.None);
}
public static Task Delay (TimeSpan delay, CancellationToken cancellationToken)
{
return Delay (CheckTimeout (delay), cancellationToken);
}
public static Task Delay (int millisecondsDelay, CancellationToken cancellationToken)
{
if (millisecondsDelay < -1)
throw new ArgumentOutOfRangeException ("millisecondsDelay");
if (cancellationToken.IsCancellationRequested)
return TaskConstants.Canceled;
var task = new Task (TaskActionInvoker.Empty, null, cancellationToken, TaskCreationOptions.None, null, null);
task.SetupScheduler (TaskScheduler.Default);
if (millisecondsDelay != Timeout.Infinite) {
var timer = new Timer (delegate (object state) {
var t = (Task) state;
if (t.Status == TaskStatus.WaitingForActivation) {
t.Status = TaskStatus.Running;
t.Finish ();
}
}, task, millisecondsDelay, -1);
task.ContinueWith (new DisposeContinuation (timer));
}
return task;
}
public static Task<TResult> FromResult<TResult> (TResult result)
{
var tcs = new TaskCompletionSource<TResult> ();
tcs.SetResult (result);
return tcs.Task;
}
public TaskAwaiter GetAwaiter ()
{
return new TaskAwaiter (this);
}
public static Task Run (Action action)
{
return Run (action, CancellationToken.None);
}
public static Task Run (Action action, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
return TaskConstants.Canceled;
return Task.Factory.StartNew (action, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
public static Task Run (Func<Task> function)
{
return Run (function, CancellationToken.None);
}
public static Task Run (Func<Task> function, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
return TaskConstants.Canceled;
return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
}
public static Task<TResult> Run<TResult> (Func<TResult> function)
{
return Run (function, CancellationToken.None);
}
public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
return TaskConstants<TResult>.Canceled;
return Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
{
return Run (function, CancellationToken.None);
}
public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
return TaskConstants<TResult>.Canceled;
return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
}
public static Task WhenAll (params Task[] tasks)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
return WhenAllCore (tasks);
}
public static Task WhenAll (IEnumerable<Task> tasks)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
// Call ToList on input enumeration or we end up
// enumerating it more than once
return WhenAllCore (new List<Task> (tasks));
}
public static Task<TResult[]> WhenAll<TResult> (params Task<TResult>[] tasks)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
return WhenAllCore<TResult> (tasks);
}
public static Task<TResult[]> WhenAll<TResult> (IEnumerable<Task<TResult>> tasks)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
// Call ToList on input enumeration or we end up
// enumerating it more than once
return WhenAllCore<TResult> (new List<Task<TResult>> (tasks));
}
internal static Task<TResult[]> WhenAllCore<TResult> (IList<Task<TResult>> tasks)
{
if (tasks.Count == 0)
return FromResult(new TResult[0]);
foreach (var t in tasks) {
if (t == null)
throw new ArgumentException ("tasks", "the tasks argument contains a null element");
}
var task = new Task<TResult[]> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
task.SetupScheduler (TaskScheduler.Current);
var continuation = new WhenAllContinuation<TResult> (task, tasks);
foreach (var t in tasks)
t.ContinueWith (continuation);
return task;
}
public static Task<Task> WhenAny (params Task[] tasks)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
return WhenAnyCore (tasks);
}
public static Task<Task> WhenAny (IEnumerable<Task> tasks)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
return WhenAnyCore (new List<Task> (tasks));
}
public static Task<Task<TResult>> WhenAny<TResult> (params Task<TResult>[] tasks)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
return WhenAnyCore<TResult> (tasks);
}
public static Task<Task<TResult>> WhenAny<TResult> (IEnumerable<Task<TResult>> tasks)
{
if (tasks == null)
throw new ArgumentNullException ("tasks");
return WhenAnyCore<TResult> (new List<Task<TResult>> (tasks));
}
static Task<Task<TResult>> WhenAnyCore<TResult> (IList<Task<TResult>> tasks)
{
if (tasks.Count == 0)
throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
int completed_index = -1;
for (int i = 0; i < tasks.Count; ++i) {
var t = tasks[i];
if (t == null)
throw new ArgumentException ("tasks", "the tasks argument contains a null element");
if (t.IsCompleted && completed_index < 0)
completed_index = i;
}
var task = new Task<Task<TResult>> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
if (completed_index > 0) {
task.TrySetResult (tasks[completed_index]);
return task;
}
task.SetupScheduler (TaskScheduler.Current);
var continuation = new WhenAnyContinuation<Task<TResult>> (task, tasks);
foreach (var t in tasks)
t.ContinueWith (continuation);
return task;
}
public static YieldAwaitable Yield ()
{
return new YieldAwaitable ();
}
#endif
internal static Task WhenAllCore (IList<Task> tasks)
{
bool all_completed = true;
foreach (var t in tasks) {
if (t == null)
throw new ArgumentException ("tasks", "the tasks argument contains a null element");
all_completed &= t.Status == TaskStatus.RanToCompletion;
}
if (all_completed)
return TaskConstants.Finished;
var task = new Task (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
task.SetupScheduler (TaskScheduler.Current);
var continuation = new WhenAllContinuation (task, tasks);
foreach (var t in tasks)
t.ContinueWith (continuation);
return task;
}
internal static Task<Task> WhenAnyCore (IList<Task> tasks)
{
if (tasks.Count == 0)
throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
int completed_index = -1;
for (int i = 0; i < tasks.Count; ++i) {
var t = tasks [i];
if (t == null)
throw new ArgumentException ("tasks", "the tasks argument contains a null element");
if (t.IsCompleted && completed_index < 0)
completed_index = i;
}
var task = new Task<Task> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
if (completed_index > 0) {
task.TrySetResult (tasks[completed_index]);
return task;
}
task.SetupScheduler (TaskScheduler.Current);
var continuation = new WhenAnyContinuation<Task> (task, tasks);
foreach (var t in tasks)
t.ContinueWith (continuation);
return task;
}
#region Properties
internal CancellationToken CancellationToken {
get {
return token;
}
}
public static TaskFactory Factory {
get {
return defaultFactory;
}
}
public static int? CurrentId {
get {
Task t = current;
return t == null ? (int?)null : t.Id;
}
}
public AggregateException Exception {
get {
if (exSlot == null || !IsFaulted)
return null;
exSlot.Observed = true;
return exSlot.Exception;
}
}
public bool IsCanceled {
get {
return status == TaskStatus.Canceled;
}
}
public bool IsCompleted {
get {
return status >= TaskStatus.RanToCompletion;
}
}
public bool IsFaulted {
get {
return status == TaskStatus.Faulted;
}
}
public TaskCreationOptions CreationOptions {
get {
return creationOptions & MaxTaskCreationOptions;
}
}
public TaskStatus Status {
get {
return status;
}
internal set {
status = value;
Thread.MemoryBarrier ();
}
}
internal TaskExceptionSlot ExceptionSlot {
get {
if (exSlot != null)
return exSlot;
Interlocked.CompareExchange (ref exSlot, new TaskExceptionSlot (this), null);
return exSlot;
}
}
public object AsyncState {
get {
return state;
}
}
bool IAsyncResult.CompletedSynchronously {
get {
return true;
}
}
WaitHandle IAsyncResult.AsyncWaitHandle {
get {
if (invoker == null)
throw new ObjectDisposedException (GetType ().ToString ());
if (wait_handle == null)
Interlocked.CompareExchange (ref wait_handle, new ManualResetEvent (IsCompleted), null);
return wait_handle;
}
}
public int Id {
get {
return taskId;
}
}
bool IsContinuation {
get {
return contAncestor != null;
}
}
bool IsPromise {
get {
return invoker == TaskActionInvoker.Promise;
}
}
internal Task ContinuationAncestor {
get {
return contAncestor;
}
}
internal string DisplayActionMethod {
get {
Delegate d = invoker.Action;
return d == null ? "<none>" : d.Method.ToString ();
}
}
#endregion
}
}
#endif