// Jérémie Laval <jeremie dot laval at xamarin dot com>
//
// Copyright (c) 2008 Jérémie "Garuma" Laval
-// Copyright 2011 Xamarin Inc (http://www.xamarin.com).
+// 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
//
//
-#if NET_4_0 || MOBILE
+#if NET_4_0
using System;
using System.Threading;
// and for Parent property.
[System.ThreadStatic]
static Task current;
- [System.ThreadStatic]
- static Action<Task> childWorkAdder;
// parent is the outer task in which this task is created
readonly Task parent;
- // contAncestor is the Task on which this continuation was setup
- readonly Task contAncestor;
+ // 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 taskCreationOptions;
+ TaskCreationOptions creationOptions;
internal TaskScheduler scheduler;
TaskExceptionSlot exSlot;
+ ManualResetEvent wait_handle;
TaskStatus status;
}
internal Task (TaskActionInvoker invoker, object state, CancellationToken cancellationToken,
- TaskCreationOptions creationOptions, Task parent = null, Task contAncestor = null)
- {
- this.invoker = invoker;
- this.taskCreationOptions = creationOptions;
- this.state = state;
- this.taskId = Interlocked.Increment (ref id);
- this.status = cancellationToken.IsCancellationRequested ? TaskStatus.Canceled : TaskStatus.Created;
- this.token = cancellationToken;
- this.parent = parent = parent == null ? current : parent;
- this.contAncestor = contAncestor;
-
- // Process taskCreationOptions
- if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
+ 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)
+ if (token.CanBeCanceled && !ignoreCancellation)
cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
}
- static bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
+ static bool HasFlag (TaskCreationOptions opt, TaskCreationOptions member)
{
return (opt & member) == member;
}
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");
+
+ RunSynchronouslyCore (scheduler);
+ }
+
+ internal void RunSynchronouslyCore (TaskScheduler scheduler)
+ {
SetupScheduler (scheduler);
- var saveStatus = status;
Status = TaskStatus.WaitingToRun;
try {
throw new TaskSchedulerException (inner);
}
- Status = saveStatus;
- Start (scheduler);
+ Schedule ();
Wait ();
}
#endregion
internal Task ContinueWith (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
- var continuation = new Task (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), null, this);
+ 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;
internal Task<TResult> ContinueWith<TResult> (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
{
- var continuation = new Task<TResult> (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), parent, this);
+ 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;
continuation.Execute ();
}
- void RemoveContinuation (IContinuation continuation)
+ internal void RemoveContinuation (IContinuation continuation)
{
continuations.Remove (continuation);
}
internal void Schedule ()
{
Status = TaskStatus.WaitingToRun;
-
- // If worker is null it means it is a local one, revert to the old behavior
- // If TaskScheduler.Current is not being used, the scheduler was explicitly provided, so we must use that
- if (scheduler != TaskScheduler.Current || childWorkAdder == null || CheckTaskOptions (taskCreationOptions, TaskCreationOptions.PreferFairness)) {
- scheduler.QueueTask (this);
- } else {
- /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom
- * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
- * the correct Thread during the creation
- */
- childWorkAdder (this);
- }
+ scheduler.QueueTask (this);
}
void ThreadStart ()
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) {
return true;
}
+ internal bool TrySetExceptionObserved ()
+ {
+ if (exSlot != null) {
+ exSlot.Observed = true;
+ return true;
+ }
+ return false;
+ }
+
internal void Execute ()
{
ThreadStart ();
ProcessChildExceptions ();
Status = exSlot == null ? TaskStatus.RanToCompletion : TaskStatus.Faulted;
ProcessCompleteDelegates ();
- if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
+ if (parent != null &&
+#if NET_4_5
+ !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
+#endif
+ HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
parent.ChildCompleted (this.Exception);
}
}
void InnerInvoke ()
{
if (IsContinuation) {
- invoker.Invoke (contAncestor, state, this);
+ var ancestor = contAncestor;
+ contAncestor = null;
+ invoker.Invoke (ancestor, state, this);
} else {
invoker.Invoke (this, state, this);
}
Status = TaskStatus.WaitingForChildrenToComplete;
}
+ if (wait_handle != null)
+ wait_handle.Set ();
+
+ // Tell parent that we are finished
+ if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) &&
+#if NET_4_5
+ !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
+#endif
+ status != TaskStatus.WaitingForChildrenToComplete) {
+ parent.ChildCompleted (this.Exception);
+ }
+
// Completions are already processed when task is canceled or faulted
if (status == TaskStatus.RanToCompletion)
ProcessCompleteDelegates ();
if (cancellationRegistration.HasValue)
cancellationRegistration.Value.Dispose ();
-
- // Tell parent that we are finished
- if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null && status != TaskStatus.WaitingForChildrenToComplete) {
- parent.ChildCompleted (this.Exception);
- }
}
void ProcessCompleteDelegates ()
internal void CancelReal ()
{
Status = TaskStatus.Canceled;
+
+ if (wait_handle != null)
+ wait_handle.Set ();
+
ProcessCompleteDelegates ();
}
ExceptionSlot.Exception = e;
Thread.MemoryBarrier ();
Status = TaskStatus.Faulted;
+
+ if (wait_handle != null)
+ wait_handle.Set ();
+
ProcessCompleteDelegates ();
}
if (millisecondsTimeout < -1)
throw new ArgumentOutOfRangeException ("millisecondsTimeout");
- bool result = true;
-
- if (!IsCompleted) {
- // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
- if (Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
- scheduler.RunInline (this, true);
-
- if (!IsCompleted) {
- var continuation = new ManualResetContinuation ();
- try {
- ContinueWith (continuation);
- result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
- } finally {
- if (!result)
- RemoveContinuation (continuation);
- continuation.Dispose ();
- }
- }
- }
+ bool result = WaitCore (millisecondsTimeout, cancellationToken);
if (IsCanceled)
throw new AggregateException (new TaskCanceledException (this));
if (exception != null)
throw exception;
- if (childTasks != null)
- childTasks.Wait ();
+ return result;
+ }
+
+ internal bool WaitCore (int millisecondsTimeout, CancellationToken cancellationToken)
+ {
+ 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 (Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
+ scheduler.RunInline (this, true);
+
+ 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;
}
#region Dispose
public void Dispose ()
{
- Dispose (true);
+ Dispose (true);
}
protected virtual void Dispose (bool disposing)
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
return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
}
- public 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;
- }
-
public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
{
return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
if (millisecondsDelay < -1)
throw new ArgumentOutOfRangeException ("millisecondsDelay");
- var task = new Task (TaskActionInvoker.Delay, millisecondsDelay, cancellationToken, TaskCreationOptions.None, null, TaskConstants.Finished);
- task.SetupScheduler (TaskScheduler.Current);
-
- if (millisecondsDelay != Timeout.Infinite)
- task.scheduler.QueueTask (task);
+ 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;
}
if (cancellationToken.IsCancellationRequested)
return TaskConstants.Canceled;
- return Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap ();
+ return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
}
public static Task<TResult> Run<TResult> (Func<TResult> function)
if (cancellationToken.IsCancellationRequested)
return TaskConstants<TResult>.Canceled;
- return Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap ();
+ return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
}
public static Task WhenAll (params Task[] 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");
public TaskCreationOptions CreationOptions {
get {
- return taskCreationOptions & MaxTaskCreationOptions;
+ return creationOptions & MaxTaskCreationOptions;
}
}
WaitHandle IAsyncResult.AsyncWaitHandle {
get {
- return null;
+ if (invoker == null)
+ throw new ObjectDisposedException (GetType ().ToString ());
+
+ if (wait_handle == null)
+ Interlocked.CompareExchange (ref wait_handle, new ManualResetEvent (IsCompleted), null);
+
+ return wait_handle;
}
}