// 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
// 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 ();
internal TaskScheduler scheduler;
TaskExceptionSlot exSlot;
+ ManualResetEvent wait_handle;
TaskStatus status;
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 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 || HasFlag (creationOptions, 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 ()
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
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));
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;
+ }
public static void WaitAll (params Task[] tasks)
{
#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 (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;
}
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");
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;
}
}
}
}
+ class NonInlineableScheduler : TaskScheduler
+ {
+ protected override IEnumerable<Task> GetScheduledTasks ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ protected override void QueueTask (Task task)
+ {
+ if (!base.TryExecuteTask (task))
+ throw new ApplicationException ();
+ }
+
+ protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
+ {
+ return false;
+ }
+ }
+
Task[] tasks;
const int max = 6;
Assert.IsTrue (r2);
}
+ [Test]
+ public void AsyncWaitHandleSet ()
+ {
+ var task = new TaskFactory ().StartNew (() => { });
+ var ar = (IAsyncResult)task;
+ ar.AsyncWaitHandle.WaitOne ();
+ }
+
#if NET_4_5
[Test]
public void Delay_Invalid ()
}
}
+ [Test]
+ public void Delay_TimeManagement ()
+ {
+ var delay1 = Task.Delay(50);
+ var delay2 = Task.Delay(25);
+ Assert.IsTrue (Task.WhenAny(new[] { delay1, delay2 }).Wait (1000));
+ Assert.AreEqual (TaskStatus.RanToCompletion, delay2.Status);
+ }
+
[Test]
public void WaitAny_WithNull ()
{
}
}
+ [Test]
+ public void WhenAll_Empty ()
+ {
+ var tasks = new Task[0];
+
+ Task t = Task.WhenAll(tasks);
+
+ Assert.IsTrue(t.Wait(1000), "#1");
+ }
+
[Test]
public void WhenAll_WithNull ()
{
Assert.IsTrue (t.Wait (1000), "#2");
}
+ [Test]
+ public void WhenAllResult_Empty ()
+ {
+ var tasks = new Task<int>[0];
+
+ Task<int[]> t = Task.WhenAll(tasks);
+
+ Assert.IsTrue(t.Wait(1000), "#1");
+ Assert.IsNotNull(t.Result, "#2");
+ Assert.AreEqual(t.Result.Length, 0, "#3");
+ }
+
[Test]
public void WhenAllResult_WithNull ()
{
Assert.AreEqual ('d', d.Result, "#3r");
}
+ [Test]
+ public void ContinueWith_CustomScheduleRejected ()
+ {
+ var scheduler = new NonInlineableScheduler ();
+ var t = Task.Factory.StartNew (delegate { }).
+ ContinueWith (r => {}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler);
+
+ Assert.IsTrue (t.Wait (5000));
+ }
+
[Test]
public void FromResult ()
{