X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mcs%2Fclass%2Fcorlib%2FSystem.Threading.Tasks%2FTask.cs;h=bb023257b4769978f0db08de9d5a5a4a03119811;hb=a0173a7e76ad48889ade46116e516731b170e7c5;hp=cf59c6138454b12d90b5eff711adf58a757da315;hpb=c1437f237f63608c7bc8d297dbadd7acccf4d104;p=mono.git diff --git a/mcs/class/corlib/System.Threading.Tasks/Task.cs b/mcs/class/corlib/System.Threading.Tasks/Task.cs index cf59c613845..bb023257b47 100644 --- a/mcs/class/corlib/System.Threading.Tasks/Task.cs +++ b/mcs/class/corlib/System.Threading.Tasks/Task.cs @@ -6,7 +6,7 @@ // Jérémie Laval // // 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 @@ -46,13 +46,11 @@ namespace System.Threading.Tasks // and for Parent property. [System.ThreadStatic] static Task current; - [System.ThreadStatic] - static Action 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 (); @@ -65,6 +63,7 @@ namespace System.Threading.Tasks internal TaskScheduler scheduler; TaskExceptionSlot exSlot; + ManualResetEvent wait_handle; TaskStatus status; @@ -183,8 +182,11 @@ namespace System.Threading.Tasks 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 (); + Schedule (true); } internal void SetupScheduler (TaskScheduler scheduler) @@ -209,25 +211,31 @@ namespace System.Threading.Tasks if (IsContinuation) throw new InvalidOperationException ("RunSynchronously may not be called on a continuation task"); - RunSynchronouslyCore (scheduler); + if (IsPromise) + throw new InvalidOperationException ("RunSynchronously may not be called on a promise-style task"); + + RunSynchronouslyCore (scheduler, true); } - internal void RunSynchronouslyCore (TaskScheduler scheduler) + internal void RunSynchronouslyCore (TaskScheduler scheduler, bool throwException) { SetupScheduler (scheduler); - var saveStatus = status; Status = TaskStatus.WaitingToRun; try { if (scheduler.RunInline (this, false)) return; } catch (Exception inner) { - throw new TaskSchedulerException (inner); + var ex = new TaskSchedulerException (inner); + TrySetException (new AggregateException (ex), false, true); + if (throwException) + throw ex; + + return; } - Status = saveStatus; - Start (scheduler); - Wait (); + Schedule (throwException); + WaitCore (Timeout.Infinite, CancellationToken.None, false); } #endregion @@ -333,21 +341,30 @@ namespace System.Threading.Tasks ContinueWith (new TaskContinuation (continuation, options)); } - internal void ContinueWith (IContinuation continuation) + internal bool ContinueWith (IContinuation continuation, bool canExecuteInline = true) { if (IsCompleted) { + if (!canExecuteInline) + return false; + continuation.Execute (); - return; + return true; } continuations.Add (continuation); // Retry in case completion was achieved but event adding was too late - if (IsCompleted && continuations.Remove (continuation)) + if (IsCompleted && continuations.Remove (continuation)) { + if (!canExecuteInline) + return false; + continuation.Execute (); + } + + return true; } - void RemoveContinuation (IContinuation continuation) + internal void RemoveContinuation (IContinuation continuation) { continuations.Remove (continuation); } @@ -367,20 +384,16 @@ namespace System.Threading.Tasks #endregion #region Internal and protected thingies - internal void Schedule () + internal void Schedule (bool throwException) { 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)) { + try { 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); + } catch (Exception inner) { + var ex = new TaskSchedulerException (inner); + TrySetException (new AggregateException (ex), false, true); + if (throwException) + throw ex; } } @@ -452,7 +465,7 @@ namespace System.Threading.Tasks return true; } - internal bool TrySetException (AggregateException aggregate) + internal bool TrySetException (AggregateException aggregate, bool cancellation, bool observed) { if (IsCompleted) return false; @@ -464,8 +477,19 @@ namespace System.Threading.Tasks return false; } - - HandleGenericException (aggregate); + + if (cancellation) { + ExceptionSlot.Exception = aggregate; + Thread.MemoryBarrier (); + + CancelReal (); + } else { + HandleGenericException (aggregate); + } + + if (observed) + exSlot.Observed = true; + return true; } @@ -514,7 +538,9 @@ namespace System.Threading.Tasks 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); } @@ -536,6 +562,9 @@ namespace System.Threading.Tasks 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 @@ -592,6 +621,10 @@ namespace System.Threading.Tasks internal void CancelReal () { Status = TaskStatus.Canceled; + + if (wait_handle != null) + wait_handle.Set (); + ProcessCompleteDelegates (); } @@ -605,6 +638,10 @@ namespace System.Threading.Tasks ExceptionSlot.Exception = e; Thread.MemoryBarrier (); Status = TaskStatus.Faulted; + + if (wait_handle != null) + wait_handle.Set (); + ProcessCompleteDelegates (); } @@ -642,25 +679,7 @@ namespace System.Threading.Tasks 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, true); if (IsCanceled) throw new AggregateException (new TaskCanceledException (this)); @@ -671,6 +690,37 @@ namespace System.Threading.Tasks 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) { @@ -821,7 +871,7 @@ namespace System.Threading.Tasks #region Dispose public void Dispose () { - Dispose (true); + Dispose (true); } protected virtual void Dispose (bool disposing) @@ -836,6 +886,8 @@ namespace System.Threading.Tasks state = null; if (cancellationRegistration != null) cancellationRegistration.Value.Dispose (); + if (wait_handle != null) + wait_handle.Dispose (); } } #endregion @@ -950,11 +1002,23 @@ namespace System.Threading.Tasks 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; } @@ -1061,6 +1125,9 @@ namespace System.Threading.Tasks internal static Task WhenAllCore (IList> 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"); @@ -1221,7 +1288,7 @@ namespace System.Threading.Tasks public AggregateException Exception { get { - if (exSlot == null) + if (exSlot == null || !IsFaulted) return null; exSlot.Observed = true; return exSlot.Exception; @@ -1262,7 +1329,7 @@ namespace System.Threading.Tasks } } - TaskExceptionSlot ExceptionSlot { + internal TaskExceptionSlot ExceptionSlot { get { if (exSlot != null) return exSlot; @@ -1285,7 +1352,13 @@ namespace System.Threading.Tasks 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; } } @@ -1301,6 +1374,12 @@ namespace System.Threading.Tasks } } + bool IsPromise { + get { + return invoker == TaskActionInvoker.Promise; + } + } + internal Task ContinuationAncestor { get { return contAncestor;