Kill some unused code that is a leftover from when TPL had a managed scheduler.
[mono-project.git] / mcs / class / corlib / System.Threading.Tasks / Task.cs
blob54a9d9be322fb79ece08554225b26be7bd9dc117
1 //
2 // Task.cs
3 //
4 // Authors:
5 // Marek Safar <marek.safar@gmail.com>
6 // Jérémie Laval <jeremie dot laval at xamarin dot com>
7 //
8 // Copyright (c) 2008 Jérémie "Garuma" Laval
9 // Copyright 2011-2013 Xamarin Inc (http://www.xamarin.com).
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
31 #if NET_4_0
33 using System;
34 using System.Threading;
35 using System.Collections.Concurrent;
36 using System.Collections.Generic;
37 using System.Runtime.CompilerServices;
39 namespace System.Threading.Tasks
41 [System.Diagnostics.DebuggerDisplay ("Id = {Id}, Status = {Status}")]
42 [System.Diagnostics.DebuggerTypeProxy (typeof (TaskDebuggerView))]
43 public class Task : IDisposable, IAsyncResult
45 // With this attribute each thread has its own value so that it's correct for our Schedule code
46 // and for Parent property.
47 [System.ThreadStatic]
48 static Task current;
50 // parent is the outer task in which this task is created
51 readonly Task parent;
52 // contAncestor is the Task on which this continuation was setup
53 readonly Task contAncestor;
55 static int id = -1;
56 static readonly TaskFactory defaultFactory = new TaskFactory ();
58 CountdownEvent childTasks;
60 int taskId;
61 TaskCreationOptions creationOptions;
63 internal TaskScheduler scheduler;
65 TaskExceptionSlot exSlot;
66 ManualResetEvent wait_handle;
68 TaskStatus status;
70 TaskActionInvoker invoker;
71 object state;
72 internal AtomicBooleanValue executing;
74 TaskCompletionQueue<IContinuation> continuations;
76 CancellationToken token;
77 CancellationTokenRegistration? cancellationRegistration;
79 internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
81 const TaskCreationOptions MaxTaskCreationOptions =
82 #if NET_4_5
83 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler |
84 #endif
85 TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent;
87 public Task (Action action)
88 : this (action, TaskCreationOptions.None)
93 public Task (Action action, TaskCreationOptions creationOptions)
94 : this (action, CancellationToken.None, creationOptions)
99 public Task (Action action, CancellationToken cancellationToken)
100 : this (action, cancellationToken, TaskCreationOptions.None)
105 public Task (Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
106 : this (TaskActionInvoker.Create (action), null, cancellationToken, creationOptions, current)
108 if (action == null)
109 throw new ArgumentNullException ("action");
110 if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
111 throw new ArgumentOutOfRangeException ("creationOptions");
114 public Task (Action<object> action, object state)
115 : this (action, state, TaskCreationOptions.None)
119 public Task (Action<object> action, object state, TaskCreationOptions creationOptions)
120 : this (action, state, CancellationToken.None, creationOptions)
124 public Task (Action<object> action, object state, CancellationToken cancellationToken)
125 : this (action, state, cancellationToken, TaskCreationOptions.None)
129 public Task (Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
130 : this (TaskActionInvoker.Create (action), state, cancellationToken, creationOptions, current)
132 if (action == null)
133 throw new ArgumentNullException ("action");
134 if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
135 throw new ArgumentOutOfRangeException ("creationOptions");
138 internal Task (TaskActionInvoker invoker, object state, CancellationToken cancellationToken,
139 TaskCreationOptions creationOptions, Task parent = null, Task contAncestor = null, bool ignoreCancellation = false)
141 this.invoker = invoker;
142 this.creationOptions = creationOptions;
143 this.state = state;
144 this.taskId = Interlocked.Increment (ref id);
145 this.token = cancellationToken;
146 this.parent = parent = parent == null ? current : parent;
147 this.contAncestor = contAncestor;
148 this.status = cancellationToken.IsCancellationRequested && !ignoreCancellation ? TaskStatus.Canceled : TaskStatus.Created;
150 // Process creationOptions
151 #if NET_4_5
152 if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent)
153 && !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach))
154 #else
155 if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
156 #endif
157 parent.AddChild ();
159 if (token.CanBeCanceled && !ignoreCancellation)
160 cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
163 static bool HasFlag (TaskCreationOptions opt, TaskCreationOptions member)
165 return (opt & member) == member;
168 #region Start
169 public void Start ()
171 Start (TaskScheduler.Current);
174 public void Start (TaskScheduler scheduler)
176 if (scheduler == null)
177 throw new ArgumentNullException ("scheduler");
179 if (status >= TaskStatus.WaitingToRun)
180 throw new InvalidOperationException ("The Task is not in a valid state to be started.");
182 if (IsContinuation)
183 throw new InvalidOperationException ("Start may not be called on a continuation task");
185 SetupScheduler (scheduler);
186 Schedule ();
189 internal void SetupScheduler (TaskScheduler scheduler)
191 this.scheduler = scheduler;
192 Status = TaskStatus.WaitingForActivation;
195 public void RunSynchronously ()
197 RunSynchronously (TaskScheduler.Current);
200 public void RunSynchronously (TaskScheduler scheduler)
202 if (scheduler == null)
203 throw new ArgumentNullException ("scheduler");
205 if (Status > TaskStatus.WaitingForActivation)
206 throw new InvalidOperationException ("The task is not in a valid state to be started");
208 if (IsContinuation)
209 throw new InvalidOperationException ("RunSynchronously may not be called on a continuation task");
211 RunSynchronouslyCore (scheduler);
214 internal void RunSynchronouslyCore (TaskScheduler scheduler)
216 SetupScheduler (scheduler);
217 var saveStatus = status;
218 Status = TaskStatus.WaitingToRun;
220 try {
221 if (scheduler.RunInline (this, false))
222 return;
223 } catch (Exception inner) {
224 throw new TaskSchedulerException (inner);
227 Status = saveStatus;
228 Start (scheduler);
229 Wait ();
231 #endregion
233 #region ContinueWith
234 public Task ContinueWith (Action<Task> continuationAction)
236 return ContinueWith (continuationAction, TaskContinuationOptions.None);
239 public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
241 return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
244 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
246 return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
249 public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
251 return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
254 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
256 if (continuationAction == null)
257 throw new ArgumentNullException ("continuationAction");
258 if (scheduler == null)
259 throw new ArgumentNullException ("scheduler");
261 return ContinueWith (TaskActionInvoker.Create (continuationAction), cancellationToken, continuationOptions, scheduler);
264 internal Task ContinueWith (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
266 var lazyCancellation = false;
267 #if NET_4_5
268 lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
269 #endif
270 var continuation = new Task (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), null, this, lazyCancellation);
271 ContinueWithCore (continuation, continuationOptions, scheduler);
273 return continuation;
276 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction)
278 return ContinueWith<TResult> (continuationFunction, TaskContinuationOptions.None);
281 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
283 return ContinueWith<TResult> (continuationFunction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
286 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
288 return ContinueWith<TResult> (continuationFunction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
291 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
293 return ContinueWith<TResult> (continuationFunction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
296 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
297 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
299 if (continuationFunction == null)
300 throw new ArgumentNullException ("continuationFunction");
301 if (scheduler == null)
302 throw new ArgumentNullException ("scheduler");
304 return ContinueWith<TResult> (TaskActionInvoker.Create (continuationFunction), cancellationToken, continuationOptions, scheduler);
307 internal Task<TResult> ContinueWith<TResult> (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
309 var lazyCancellation = false;
310 #if NET_4_5
311 lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
312 #endif
313 var continuation = new Task<TResult> (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), parent, this, lazyCancellation);
314 ContinueWithCore (continuation, continuationOptions, scheduler);
316 return continuation;
319 internal void ContinueWithCore (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler)
321 const TaskContinuationOptions wrongRan = TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.OnlyOnRanToCompletion;
322 const TaskContinuationOptions wrongCanceled = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.OnlyOnCanceled;
323 const TaskContinuationOptions wrongFaulted = TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.OnlyOnFaulted;
325 if (((options & wrongRan) == wrongRan) || ((options & wrongCanceled) == wrongCanceled) || ((options & wrongFaulted) == wrongFaulted))
326 throw new ArgumentException ("continuationOptions", "Some options are mutually exclusive");
328 // Already set the scheduler so that user can call Wait and that sort of stuff
329 continuation.scheduler = scheduler;
330 continuation.Status = TaskStatus.WaitingForActivation;
332 ContinueWith (new TaskContinuation (continuation, options));
335 internal void ContinueWith (IContinuation continuation)
337 if (IsCompleted) {
338 continuation.Execute ();
339 return;
342 continuations.Add (continuation);
344 // Retry in case completion was achieved but event adding was too late
345 if (IsCompleted && continuations.Remove (continuation))
346 continuation.Execute ();
349 internal void RemoveContinuation (IContinuation continuation)
351 continuations.Remove (continuation);
354 static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
356 TaskCreationOptions options = TaskCreationOptions.None;
357 if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
358 options |= TaskCreationOptions.AttachedToParent;
359 if ((kind & TaskContinuationOptions.PreferFairness) > 0)
360 options |= TaskCreationOptions.PreferFairness;
361 if ((kind & TaskContinuationOptions.LongRunning) > 0)
362 options |= TaskCreationOptions.LongRunning;
364 return options;
366 #endregion
368 #region Internal and protected thingies
369 internal void Schedule ()
371 Status = TaskStatus.WaitingToRun;
372 scheduler.QueueTask (this);
375 void ThreadStart ()
377 /* Allow scheduler to break fairness of deque ordering without
378 * breaking its semantic (the task can be executed twice but the
379 * second time it will return immediately
381 if (!executing.TryRelaxedSet ())
382 return;
384 // Disable CancellationToken direct cancellation
385 if (cancellationRegistration != null) {
386 cancellationRegistration.Value.Dispose ();
387 cancellationRegistration = null;
390 // If Task are ran inline on the same thread we might trash these values
391 var saveCurrent = current;
392 var saveScheduler = TaskScheduler.Current;
394 current = this;
395 #if NET_4_5
396 TaskScheduler.Current = HasFlag (creationOptions, TaskCreationOptions.HideScheduler) ? TaskScheduler.Default : scheduler;
397 #else
398 TaskScheduler.Current = scheduler;
399 #endif
401 if (!token.IsCancellationRequested) {
403 status = TaskStatus.Running;
405 try {
406 InnerInvoke ();
407 } catch (OperationCanceledException oce) {
408 if (token != CancellationToken.None && oce.CancellationToken == token)
409 CancelReal ();
410 else
411 HandleGenericException (oce);
412 } catch (Exception e) {
413 HandleGenericException (e);
415 } else {
416 CancelReal ();
419 if (saveCurrent != null)
420 current = saveCurrent;
421 if (saveScheduler != null)
422 TaskScheduler.Current = saveScheduler;
423 Finish ();
426 internal bool TrySetCanceled ()
428 if (IsCompleted)
429 return false;
431 if (!executing.TryRelaxedSet ()) {
432 var sw = new SpinWait ();
433 while (!IsCompleted)
434 sw.SpinOnce ();
436 return false;
439 CancelReal ();
440 return true;
443 internal bool TrySetException (AggregateException aggregate)
445 if (IsCompleted)
446 return false;
448 if (!executing.TryRelaxedSet ()) {
449 var sw = new SpinWait ();
450 while (!IsCompleted)
451 sw.SpinOnce ();
453 return false;
456 HandleGenericException (aggregate);
457 return true;
460 internal bool TrySetExceptionObserved ()
462 if (exSlot != null) {
463 exSlot.Observed = true;
464 return true;
466 return false;
469 internal void Execute ()
471 ThreadStart ();
474 internal void AddChild ()
476 if (childTasks == null)
477 Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
478 childTasks.AddCount ();
481 internal void ChildCompleted (AggregateException childEx)
483 if (childEx != null) {
484 if (ExceptionSlot.ChildExceptions == null)
485 Interlocked.CompareExchange (ref ExceptionSlot.ChildExceptions, new ConcurrentQueue<AggregateException> (), null);
486 ExceptionSlot.ChildExceptions.Enqueue (childEx);
489 if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
490 ProcessChildExceptions ();
491 Status = exSlot == null ? TaskStatus.RanToCompletion : TaskStatus.Faulted;
492 ProcessCompleteDelegates ();
493 if (parent != null &&
494 #if NET_4_5
495 !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
496 #endif
497 HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
498 parent.ChildCompleted (this.Exception);
502 void InnerInvoke ()
504 if (IsContinuation) {
505 invoker.Invoke (contAncestor, state, this);
506 } else {
507 invoker.Invoke (this, state, this);
511 internal void Finish ()
513 // If there was children created and they all finished, we set the countdown
514 if (childTasks != null) {
515 if (childTasks.Signal ())
516 ProcessChildExceptions (true);
519 // Don't override Canceled or Faulted
520 if (status == TaskStatus.Running) {
521 if (childTasks == null || childTasks.IsSet)
522 Status = TaskStatus.RanToCompletion;
523 else
524 Status = TaskStatus.WaitingForChildrenToComplete;
527 if (wait_handle != null)
528 wait_handle.Set ();
530 // Tell parent that we are finished
531 if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) &&
532 #if NET_4_5
533 !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
534 #endif
535 status != TaskStatus.WaitingForChildrenToComplete) {
536 parent.ChildCompleted (this.Exception);
539 // Completions are already processed when task is canceled or faulted
540 if (status == TaskStatus.RanToCompletion)
541 ProcessCompleteDelegates ();
543 // Reset the current thingies
544 if (current == this)
545 current = null;
546 if (TaskScheduler.Current == scheduler)
547 TaskScheduler.Current = null;
549 if (cancellationRegistration.HasValue)
550 cancellationRegistration.Value.Dispose ();
553 void ProcessCompleteDelegates ()
555 if (continuations.HasElements) {
556 IContinuation continuation;
557 while (continuations.TryGetNextCompletion (out continuation))
558 continuation.Execute ();
562 void ProcessChildExceptions (bool isParent = false)
564 if (exSlot == null || exSlot.ChildExceptions == null)
565 return;
567 if (ExceptionSlot.Exception == null)
568 exSlot.Exception = new AggregateException ();
570 AggregateException childEx;
571 while (exSlot.ChildExceptions.TryDequeue (out childEx))
572 exSlot.Exception.AddChildException (childEx);
574 if (isParent) {
575 Status = TaskStatus.Faulted;
576 ProcessCompleteDelegates ();
579 #endregion
581 #region Cancel and Wait related method
583 internal void CancelReal ()
585 Status = TaskStatus.Canceled;
587 if (wait_handle != null)
588 wait_handle.Set ();
590 ProcessCompleteDelegates ();
593 void HandleGenericException (Exception e)
595 HandleGenericException (new AggregateException (e));
598 void HandleGenericException (AggregateException e)
600 ExceptionSlot.Exception = e;
601 Thread.MemoryBarrier ();
602 Status = TaskStatus.Faulted;
604 if (wait_handle != null)
605 wait_handle.Set ();
607 ProcessCompleteDelegates ();
610 internal bool WaitOnChildren ()
612 if (Status == TaskStatus.WaitingForChildrenToComplete && childTasks != null) {
613 childTasks.Wait ();
614 return true;
616 return false;
619 public void Wait ()
621 Wait (Timeout.Infinite, CancellationToken.None);
624 public void Wait (CancellationToken cancellationToken)
626 Wait (Timeout.Infinite, cancellationToken);
629 public bool Wait (TimeSpan timeout)
631 return Wait (CheckTimeout (timeout), CancellationToken.None);
634 public bool Wait (int millisecondsTimeout)
636 return Wait (millisecondsTimeout, CancellationToken.None);
639 public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
641 if (millisecondsTimeout < -1)
642 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
644 bool result = true;
646 if (!IsCompleted) {
647 // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
648 if (Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
649 scheduler.RunInline (this, true);
651 if (!IsCompleted) {
652 var continuation = new ManualResetContinuation ();
653 try {
654 ContinueWith (continuation);
655 result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
656 } finally {
657 if (!result)
658 RemoveContinuation (continuation);
659 continuation.Dispose ();
664 if (IsCanceled)
665 throw new AggregateException (new TaskCanceledException (this));
667 var exception = Exception;
668 if (exception != null)
669 throw exception;
671 return result;
674 public static void WaitAll (params Task[] tasks)
676 WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
679 public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
681 WaitAll (tasks, Timeout.Infinite, cancellationToken);
684 public static bool WaitAll (Task[] tasks, TimeSpan timeout)
686 return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
689 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
691 return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
694 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
696 if (tasks == null)
697 throw new ArgumentNullException ("tasks");
699 bool result = true;
700 foreach (var t in tasks) {
701 if (t == null)
702 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
704 result &= t.Status == TaskStatus.RanToCompletion;
707 if (!result) {
708 var continuation = new CountdownContinuation (tasks.Length);
709 try {
710 foreach (var t in tasks)
711 t.ContinueWith (continuation);
713 result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
714 } finally {
715 List<Exception> exceptions = null;
717 foreach (var t in tasks) {
718 if (result) {
719 if (t.Status == TaskStatus.RanToCompletion)
720 continue;
721 if (exceptions == null)
722 exceptions = new List<Exception> ();
723 if (t.Exception != null)
724 exceptions.AddRange (t.Exception.InnerExceptions);
725 else
726 exceptions.Add (new TaskCanceledException (t));
727 } else {
728 t.RemoveContinuation (continuation);
732 continuation.Dispose ();
734 if (exceptions != null)
735 throw new AggregateException (exceptions);
739 return result;
742 public static int WaitAny (params Task[] tasks)
744 return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
747 public static int WaitAny (Task[] tasks, TimeSpan timeout)
749 return WaitAny (tasks, CheckTimeout (timeout));
752 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
754 return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
757 public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
759 return WaitAny (tasks, Timeout.Infinite, cancellationToken);
762 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
764 if (tasks == null)
765 throw new ArgumentNullException ("tasks");
766 if (millisecondsTimeout < -1)
767 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
768 CheckForNullTasks (tasks);
770 if (tasks.Length > 0) {
771 var continuation = new ManualResetContinuation ();
772 bool result = false;
773 try {
774 for (int i = 0; i < tasks.Length; i++) {
775 var t = tasks[i];
776 if (t.IsCompleted)
777 return i;
778 t.ContinueWith (continuation);
781 if (!(result = continuation.Event.Wait (millisecondsTimeout, cancellationToken)))
782 return -1;
783 } finally {
784 if (!result)
785 foreach (var t in tasks)
786 t.RemoveContinuation (continuation);
787 continuation.Dispose ();
791 int firstFinished = -1;
792 for (int i = 0; i < tasks.Length; i++) {
793 var t = tasks[i];
794 if (t.IsCompleted) {
795 firstFinished = i;
796 break;
800 return firstFinished;
803 static int CheckTimeout (TimeSpan timeout)
805 try {
806 return checked ((int)timeout.TotalMilliseconds);
807 } catch (System.OverflowException) {
808 throw new ArgumentOutOfRangeException ("timeout");
812 static void CheckForNullTasks (Task[] tasks)
814 foreach (var t in tasks)
815 if (t == null)
816 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
818 #endregion
820 #region Dispose
821 public void Dispose ()
823 Dispose (true);
826 protected virtual void Dispose (bool disposing)
828 if (!IsCompleted)
829 throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
831 // Set action to null so that the GC can collect the delegate and thus
832 // any big object references that the user might have captured in a anonymous method
833 if (disposing) {
834 invoker = null;
835 state = null;
836 if (cancellationRegistration != null)
837 cancellationRegistration.Value.Dispose ();
838 if (wait_handle != null)
839 wait_handle.Dispose ();
842 #endregion
844 #if NET_4_5
845 public
846 #else
847 internal
848 #endif
849 Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken,
850 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
852 if (continuationAction == null)
853 throw new ArgumentNullException ("continuationAction");
854 if (scheduler == null)
855 throw new ArgumentNullException ("scheduler");
857 Task continuation = new Task (TaskActionInvoker.Create (continuationAction),
858 state, cancellationToken,
859 GetCreationOptions (continuationOptions),
860 parent,
861 this);
862 ContinueWithCore (continuation, continuationOptions, scheduler);
864 return continuation;
867 #if NET_4_5
869 public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext)
871 return new ConfiguredTaskAwaitable (this, continueOnCapturedContext);
874 public Task ContinueWith (Action<Task, object> continuationAction, object state)
876 return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
879 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken)
881 return ContinueWith (continuationAction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
884 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskContinuationOptions continuationOptions)
886 return ContinueWith (continuationAction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
889 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskScheduler scheduler)
891 return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
894 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
896 return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
899 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
901 return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
904 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
906 return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
909 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
911 return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
914 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
915 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
917 if (continuationFunction == null)
918 throw new ArgumentNullException ("continuationFunction");
919 if (scheduler == null)
920 throw new ArgumentNullException ("scheduler");
922 var t = new Task<TResult> (TaskActionInvoker.Create (continuationFunction),
923 state,
924 cancellationToken,
925 GetCreationOptions (continuationOptions),
926 parent,
927 this);
929 ContinueWithCore (t, continuationOptions, scheduler);
931 return t;
934 public static Task Delay (int millisecondsDelay)
936 return Delay (millisecondsDelay, CancellationToken.None);
939 public static Task Delay (TimeSpan delay)
941 return Delay (CheckTimeout (delay), CancellationToken.None);
944 public static Task Delay (TimeSpan delay, CancellationToken cancellationToken)
946 return Delay (CheckTimeout (delay), cancellationToken);
949 public static Task Delay (int millisecondsDelay, CancellationToken cancellationToken)
951 if (millisecondsDelay < -1)
952 throw new ArgumentOutOfRangeException ("millisecondsDelay");
954 var task = new Task (TaskActionInvoker.Delay, millisecondsDelay, cancellationToken, TaskCreationOptions.None, null, TaskConstants.Finished);
955 task.SetupScheduler (TaskScheduler.Current);
957 if (millisecondsDelay != Timeout.Infinite)
958 task.scheduler.QueueTask (task);
960 return task;
963 public static Task<TResult> FromResult<TResult> (TResult result)
965 var tcs = new TaskCompletionSource<TResult> ();
966 tcs.SetResult (result);
967 return tcs.Task;
970 public TaskAwaiter GetAwaiter ()
972 return new TaskAwaiter (this);
975 public static Task Run (Action action)
977 return Run (action, CancellationToken.None);
980 public static Task Run (Action action, CancellationToken cancellationToken)
982 if (cancellationToken.IsCancellationRequested)
983 return TaskConstants.Canceled;
985 return Task.Factory.StartNew (action, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
988 public static Task Run (Func<Task> function)
990 return Run (function, CancellationToken.None);
993 public static Task Run (Func<Task> function, CancellationToken cancellationToken)
995 if (cancellationToken.IsCancellationRequested)
996 return TaskConstants.Canceled;
998 return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
1001 public static Task<TResult> Run<TResult> (Func<TResult> function)
1003 return Run (function, CancellationToken.None);
1006 public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
1008 if (cancellationToken.IsCancellationRequested)
1009 return TaskConstants<TResult>.Canceled;
1011 return Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
1014 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
1016 return Run (function, CancellationToken.None);
1019 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
1021 if (cancellationToken.IsCancellationRequested)
1022 return TaskConstants<TResult>.Canceled;
1024 return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
1027 public static Task WhenAll (params Task[] tasks)
1029 if (tasks == null)
1030 throw new ArgumentNullException ("tasks");
1032 return WhenAllCore (tasks);
1035 public static Task WhenAll (IEnumerable<Task> tasks)
1037 if (tasks == null)
1038 throw new ArgumentNullException ("tasks");
1040 // Call ToList on input enumeration or we end up
1041 // enumerating it more than once
1042 return WhenAllCore (new List<Task> (tasks));
1045 public static Task<TResult[]> WhenAll<TResult> (params Task<TResult>[] tasks)
1047 if (tasks == null)
1048 throw new ArgumentNullException ("tasks");
1050 return WhenAllCore<TResult> (tasks);
1053 public static Task<TResult[]> WhenAll<TResult> (IEnumerable<Task<TResult>> tasks)
1055 if (tasks == null)
1056 throw new ArgumentNullException ("tasks");
1058 // Call ToList on input enumeration or we end up
1059 // enumerating it more than once
1060 return WhenAllCore<TResult> (new List<Task<TResult>> (tasks));
1063 internal static Task<TResult[]> WhenAllCore<TResult> (IList<Task<TResult>> tasks)
1065 foreach (var t in tasks) {
1066 if (t == null)
1067 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1070 var task = new Task<TResult[]> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1071 task.SetupScheduler (TaskScheduler.Current);
1073 var continuation = new WhenAllContinuation<TResult> (task, tasks);
1074 foreach (var t in tasks)
1075 t.ContinueWith (continuation);
1077 return task;
1080 public static Task<Task> WhenAny (params Task[] tasks)
1082 if (tasks == null)
1083 throw new ArgumentNullException ("tasks");
1085 return WhenAnyCore (tasks);
1088 public static Task<Task> WhenAny (IEnumerable<Task> tasks)
1090 if (tasks == null)
1091 throw new ArgumentNullException ("tasks");
1093 return WhenAnyCore (new List<Task> (tasks));
1096 public static Task<Task<TResult>> WhenAny<TResult> (params Task<TResult>[] tasks)
1098 if (tasks == null)
1099 throw new ArgumentNullException ("tasks");
1101 return WhenAnyCore<TResult> (tasks);
1104 public static Task<Task<TResult>> WhenAny<TResult> (IEnumerable<Task<TResult>> tasks)
1106 if (tasks == null)
1107 throw new ArgumentNullException ("tasks");
1109 return WhenAnyCore<TResult> (new List<Task<TResult>> (tasks));
1112 static Task<Task<TResult>> WhenAnyCore<TResult> (IList<Task<TResult>> tasks)
1114 if (tasks.Count == 0)
1115 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1117 int completed_index = -1;
1118 for (int i = 0; i < tasks.Count; ++i) {
1119 var t = tasks[i];
1120 if (t == null)
1121 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1123 if (t.IsCompleted && completed_index < 0)
1124 completed_index = i;
1127 var task = new Task<Task<TResult>> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1129 if (completed_index > 0) {
1130 task.TrySetResult (tasks[completed_index]);
1131 return task;
1134 task.SetupScheduler (TaskScheduler.Current);
1136 var continuation = new WhenAnyContinuation<Task<TResult>> (task, tasks);
1137 foreach (var t in tasks)
1138 t.ContinueWith (continuation);
1140 return task;
1143 public static YieldAwaitable Yield ()
1145 return new YieldAwaitable ();
1147 #endif
1149 internal static Task WhenAllCore (IList<Task> tasks)
1151 bool all_completed = true;
1152 foreach (var t in tasks) {
1153 if (t == null)
1154 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1156 all_completed &= t.Status == TaskStatus.RanToCompletion;
1159 if (all_completed)
1160 return TaskConstants.Finished;
1162 var task = new Task (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1163 task.SetupScheduler (TaskScheduler.Current);
1165 var continuation = new WhenAllContinuation (task, tasks);
1166 foreach (var t in tasks)
1167 t.ContinueWith (continuation);
1169 return task;
1172 internal static Task<Task> WhenAnyCore (IList<Task> tasks)
1174 if (tasks.Count == 0)
1175 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1177 int completed_index = -1;
1178 for (int i = 0; i < tasks.Count; ++i) {
1179 var t = tasks [i];
1180 if (t == null)
1181 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1183 if (t.IsCompleted && completed_index < 0)
1184 completed_index = i;
1187 var task = new Task<Task> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1189 if (completed_index > 0) {
1190 task.TrySetResult (tasks[completed_index]);
1191 return task;
1194 task.SetupScheduler (TaskScheduler.Current);
1196 var continuation = new WhenAnyContinuation<Task> (task, tasks);
1197 foreach (var t in tasks)
1198 t.ContinueWith (continuation);
1200 return task;
1202 #region Properties
1204 internal CancellationToken CancellationToken {
1205 get {
1206 return token;
1210 public static TaskFactory Factory {
1211 get {
1212 return defaultFactory;
1216 public static int? CurrentId {
1217 get {
1218 Task t = current;
1219 return t == null ? (int?)null : t.Id;
1223 public AggregateException Exception {
1224 get {
1225 if (exSlot == null)
1226 return null;
1227 exSlot.Observed = true;
1228 return exSlot.Exception;
1232 public bool IsCanceled {
1233 get {
1234 return status == TaskStatus.Canceled;
1238 public bool IsCompleted {
1239 get {
1240 return status >= TaskStatus.RanToCompletion;
1244 public bool IsFaulted {
1245 get {
1246 return status == TaskStatus.Faulted;
1250 public TaskCreationOptions CreationOptions {
1251 get {
1252 return creationOptions & MaxTaskCreationOptions;
1256 public TaskStatus Status {
1257 get {
1258 return status;
1260 internal set {
1261 status = value;
1262 Thread.MemoryBarrier ();
1266 TaskExceptionSlot ExceptionSlot {
1267 get {
1268 if (exSlot != null)
1269 return exSlot;
1270 Interlocked.CompareExchange (ref exSlot, new TaskExceptionSlot (this), null);
1271 return exSlot;
1275 public object AsyncState {
1276 get {
1277 return state;
1281 bool IAsyncResult.CompletedSynchronously {
1282 get {
1283 return true;
1287 WaitHandle IAsyncResult.AsyncWaitHandle {
1288 get {
1289 if (invoker == null)
1290 throw new ObjectDisposedException (GetType ().ToString ());
1292 if (wait_handle == null)
1293 Interlocked.CompareExchange (ref wait_handle, new ManualResetEvent (IsCompleted), null);
1295 return wait_handle;
1299 public int Id {
1300 get {
1301 return taskId;
1305 bool IsContinuation {
1306 get {
1307 return contAncestor != null;
1311 internal Task ContinuationAncestor {
1312 get {
1313 return contAncestor;
1317 internal string DisplayActionMethod {
1318 get {
1319 Delegate d = invoker.Action;
1320 return d == null ? "<none>" : d.Method.ToString ();
1324 #endregion
1327 #endif