5 // Marek Safar <marek.safar@gmail.com>
6 // Jérémie Laval <jeremie dot laval at xamarin dot com>
8 // Copyright (c) 2008 Jérémie "Garuma" Laval
9 // Copyright 2011 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
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.
50 static Action<Task> childWorkAdder;
52 // parent is the outer task in which this task is created
53 internal readonly Task parent;
54 // contAncestor is the Task on which this continuation was setup
55 readonly Task contAncestor;
58 static readonly TaskFactory defaultFactory = new TaskFactory ();
60 CountdownEvent childTasks;
63 TaskCreationOptions taskCreationOptions;
65 internal TaskScheduler scheduler;
67 TaskExceptionSlot exSlot;
71 TaskActionInvoker invoker;
73 internal AtomicBooleanValue executing;
75 TaskCompletionQueue<IContinuation> continuations;
77 CancellationToken token;
78 CancellationTokenRegistration? cancellationRegistration;
80 internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
82 const TaskCreationOptions MaxTaskCreationOptions =
84 TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler |
86 TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent;
88 public Task (Action action)
89 : this (action, TaskCreationOptions.None)
94 public Task (Action action, TaskCreationOptions creationOptions)
95 : this (action, CancellationToken.None, creationOptions)
100 public Task (Action action, CancellationToken cancellationToken)
101 : this (action, cancellationToken, TaskCreationOptions.None)
106 public Task (Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
107 : this (TaskActionInvoker.Create (action), null, cancellationToken, creationOptions, current)
110 throw new ArgumentNullException ("action");
111 if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
112 throw new ArgumentOutOfRangeException ("creationOptions");
115 public Task (Action<object> action, object state)
116 : this (action, state, TaskCreationOptions.None)
120 public Task (Action<object> action, object state, TaskCreationOptions creationOptions)
121 : this (action, state, CancellationToken.None, creationOptions)
125 public Task (Action<object> action, object state, CancellationToken cancellationToken)
126 : this (action, state, cancellationToken, TaskCreationOptions.None)
130 public Task (Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
131 : this (TaskActionInvoker.Create (action), state, cancellationToken, creationOptions, current)
134 throw new ArgumentNullException ("action");
135 if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
136 throw new ArgumentOutOfRangeException ("creationOptions");
139 internal Task (TaskActionInvoker invoker, object state, CancellationToken cancellationToken,
140 TaskCreationOptions creationOptions, Task parent, Task contAncestor = null)
142 this.invoker = invoker;
143 this.taskCreationOptions = creationOptions;
145 this.taskId = Interlocked.Increment (ref id);
146 this.status = cancellationToken.IsCancellationRequested ? TaskStatus.Canceled : TaskStatus.Created;
147 this.token = cancellationToken;
148 this.parent = parent;
149 this.contAncestor = contAncestor;
151 // Process taskCreationOptions
152 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
155 if (token.CanBeCanceled)
156 cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
159 static bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
161 return (opt & member) == member;
167 Start (TaskScheduler.Current);
170 public void Start (TaskScheduler scheduler)
172 if (scheduler == null)
173 throw new ArgumentNullException ("scheduler");
175 if (status >= TaskStatus.WaitingToRun)
176 throw new InvalidOperationException ("The Task is not in a valid state to be started.");
179 throw new InvalidOperationException ("Start may not be called on a continuation task");
181 SetupScheduler (scheduler);
185 internal void SetupScheduler (TaskScheduler scheduler)
187 this.scheduler = scheduler;
188 Status = TaskStatus.WaitingForActivation;
191 public void RunSynchronously ()
193 RunSynchronously (TaskScheduler.Current);
196 public void RunSynchronously (TaskScheduler scheduler)
198 if (scheduler == null)
199 throw new ArgumentNullException ("scheduler");
201 if (Status > TaskStatus.WaitingForActivation)
202 throw new InvalidOperationException ("The task is not in a valid state to be started");
204 SetupScheduler (scheduler);
205 var saveStatus = status;
206 Status = TaskStatus.WaitingToRun;
209 if (scheduler.RunInline (this))
211 } catch (Exception inner) {
212 throw new TaskSchedulerException (inner);
222 public Task ContinueWith (Action<Task> continuationAction)
224 return ContinueWith (continuationAction, TaskContinuationOptions.None);
227 public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
229 return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
232 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
234 return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
237 public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
239 return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
242 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
244 if (continuationAction == null)
245 throw new ArgumentNullException ("continuationAction");
246 if (scheduler == null)
247 throw new ArgumentNullException ("scheduler");
249 return ContinueWith (TaskActionInvoker.Create (continuationAction), cancellationToken, continuationOptions, scheduler);
252 internal Task ContinueWith (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
254 var continuation = new Task (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), parent, this);
255 ContinueWithCore (continuation, continuationOptions, scheduler);
260 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction)
262 return ContinueWith<TResult> (continuationFunction, TaskContinuationOptions.None);
265 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
267 return ContinueWith<TResult> (continuationFunction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
270 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
272 return ContinueWith<TResult> (continuationFunction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
275 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
277 return ContinueWith<TResult> (continuationFunction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
280 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
281 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
283 if (continuationFunction == null)
284 throw new ArgumentNullException ("continuationFunction");
285 if (scheduler == null)
286 throw new ArgumentNullException ("scheduler");
288 return ContinueWith<TResult> (TaskActionInvoker.Create (continuationFunction), cancellationToken, continuationOptions, scheduler);
291 internal Task<TResult> ContinueWith<TResult> (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
293 var continuation = new Task<TResult> (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), parent, this);
294 ContinueWithCore (continuation, continuationOptions, scheduler);
299 internal void ContinueWithCore (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler)
301 const TaskContinuationOptions wrongRan = TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.OnlyOnRanToCompletion;
302 const TaskContinuationOptions wrongCanceled = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.OnlyOnCanceled;
303 const TaskContinuationOptions wrongFaulted = TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.OnlyOnFaulted;
305 if (((options & wrongRan) == wrongRan) || ((options & wrongCanceled) == wrongCanceled) || ((options & wrongFaulted) == wrongFaulted))
306 throw new ArgumentException ("continuationOptions", "Some options are mutually exclusive");
308 // Already set the scheduler so that user can call Wait and that sort of stuff
309 continuation.scheduler = scheduler;
310 continuation.Status = TaskStatus.WaitingForActivation;
312 ContinueWith (new TaskContinuation (continuation, options));
315 internal void ContinueWith (IContinuation continuation)
318 continuation.Execute ();
322 continuations.Add (continuation);
324 // Retry in case completion was achieved but event adding was too late
325 if (IsCompleted && continuations.Remove (continuation))
326 continuation.Execute ();
329 void RemoveContinuation (IContinuation continuation)
331 continuations.Remove (continuation);
334 static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
336 TaskCreationOptions options = TaskCreationOptions.None;
337 if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
338 options |= TaskCreationOptions.AttachedToParent;
339 if ((kind & TaskContinuationOptions.PreferFairness) > 0)
340 options |= TaskCreationOptions.PreferFairness;
341 if ((kind & TaskContinuationOptions.LongRunning) > 0)
342 options |= TaskCreationOptions.LongRunning;
348 #region Internal and protected thingies
349 internal void Schedule ()
351 Status = TaskStatus.WaitingToRun;
353 // If worker is null it means it is a local one, revert to the old behavior
354 // If TaskScheduler.Current is not being used, the scheduler was explicitly provided, so we must use that
355 if (scheduler != TaskScheduler.Current || childWorkAdder == null || CheckTaskOptions (taskCreationOptions, TaskCreationOptions.PreferFairness)) {
356 scheduler.QueueTask (this);
358 /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom
359 * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
360 * the correct Thread during the creation
362 childWorkAdder (this);
368 /* Allow scheduler to break fairness of deque ordering without
369 * breaking its semantic (the task can be executed twice but the
370 * second time it will return immediately
372 if (!executing.TryRelaxedSet ())
375 // Disable CancellationToken direct cancellation
376 if (cancellationRegistration != null) {
377 cancellationRegistration.Value.Dispose ();
378 cancellationRegistration = null;
381 TaskScheduler.Current = scheduler;
383 if (!token.IsCancellationRequested) {
385 status = TaskStatus.Running;
389 } catch (OperationCanceledException oce) {
390 if (token != CancellationToken.None && oce.CancellationToken == token)
393 HandleGenericException (oce);
394 } catch (Exception e) {
395 HandleGenericException (e);
403 internal bool TrySetCanceled ()
408 if (!executing.TryRelaxedSet ()) {
409 var sw = new SpinWait ();
420 internal bool TrySetException (AggregateException aggregate)
425 if (!executing.TryRelaxedSet ()) {
426 var sw = new SpinWait ();
433 HandleGenericException (aggregate);
437 internal void Execute ()
442 internal void AddChild ()
444 if (childTasks == null)
445 Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
446 childTasks.AddCount ();
449 internal void ChildCompleted (AggregateException childEx)
451 if (childEx != null) {
452 if (ExceptionSlot.ChildExceptions == null)
453 Interlocked.CompareExchange (ref ExceptionSlot.ChildExceptions, new ConcurrentQueue<AggregateException> (), null);
454 ExceptionSlot.ChildExceptions.Enqueue (childEx);
457 if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
458 Status = TaskStatus.RanToCompletion;
459 ProcessChildExceptions ();
460 ProcessCompleteDelegates ();
461 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
462 parent.ChildCompleted (this.Exception);
468 if (IsContinuation) {
469 invoker.Invoke (contAncestor, state, this);
471 invoker.Invoke (this, state, this);
475 internal void Finish ()
477 // If there was children created and they all finished, we set the countdown
478 if (childTasks != null)
479 childTasks.Signal ();
481 // Don't override Canceled or Faulted
482 if (status == TaskStatus.Running) {
483 if (childTasks == null || childTasks.IsSet)
484 Status = TaskStatus.RanToCompletion;
486 Status = TaskStatus.WaitingForChildrenToComplete;
489 // Completions are already processed when task is canceled or faulted
490 if (status == TaskStatus.RanToCompletion)
491 ProcessCompleteDelegates ();
493 // Reset the current thingies
495 TaskScheduler.Current = null;
497 if (cancellationRegistration.HasValue)
498 cancellationRegistration.Value.Dispose ();
500 // Tell parent that we are finished
501 if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null && status != TaskStatus.WaitingForChildrenToComplete) {
502 parent.ChildCompleted (this.Exception);
506 void ProcessCompleteDelegates ()
508 if (continuations.HasElements) {
509 IContinuation continuation;
510 while (continuations.TryGetNextCompletion (out continuation))
511 continuation.Execute ();
515 void ProcessChildExceptions ()
517 if (exSlot == null || exSlot.ChildExceptions == null)
520 if (ExceptionSlot.Exception == null)
521 exSlot.Exception = new AggregateException ();
523 AggregateException childEx;
524 while (exSlot.ChildExceptions.TryDequeue (out childEx))
525 exSlot.Exception.AddChildException (childEx);
529 #region Cancel and Wait related method
531 internal void CancelReal ()
533 Status = TaskStatus.Canceled;
534 ProcessCompleteDelegates ();
537 void HandleGenericException (Exception e)
539 HandleGenericException (new AggregateException (e));
542 void HandleGenericException (AggregateException e)
544 ExceptionSlot.Exception = e;
545 Thread.MemoryBarrier ();
546 Status = TaskStatus.Faulted;
547 ProcessCompleteDelegates ();
550 internal void WaitOnChildren ()
552 if (Status == TaskStatus.WaitingForChildrenToComplete && childTasks != null)
558 Wait (Timeout.Infinite, CancellationToken.None);
561 public void Wait (CancellationToken cancellationToken)
563 Wait (Timeout.Infinite, cancellationToken);
566 public bool Wait (TimeSpan timeout)
568 return Wait (CheckTimeout (timeout), CancellationToken.None);
571 public bool Wait (int millisecondsTimeout)
573 return Wait (millisecondsTimeout, CancellationToken.None);
576 public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
578 if (millisecondsTimeout < -1)
579 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
584 // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
585 if (Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
589 var continuation = new ManualResetContinuation ();
591 ContinueWith (continuation);
592 result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
595 RemoveContinuation (continuation);
596 continuation.Dispose ();
602 throw new AggregateException (new TaskCanceledException (this));
604 var exception = Exception;
605 if (exception != null)
608 if (childTasks != null)
614 public static void WaitAll (params Task[] tasks)
616 WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
619 public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
621 WaitAll (tasks, Timeout.Infinite, cancellationToken);
624 public static bool WaitAll (Task[] tasks, TimeSpan timeout)
626 return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
629 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
631 return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
634 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
637 throw new ArgumentNullException ("tasks");
640 foreach (var t in tasks) {
642 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
644 result &= t.Status == TaskStatus.RanToCompletion;
648 var continuation = new CountdownContinuation (tasks.Length);
650 foreach (var t in tasks)
651 t.ContinueWith (continuation);
653 result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
655 List<Exception> exceptions = null;
657 foreach (var t in tasks) {
659 if (t.Status == TaskStatus.RanToCompletion)
661 if (exceptions == null)
662 exceptions = new List<Exception> ();
663 if (t.Exception != null)
664 exceptions.AddRange (t.Exception.InnerExceptions);
666 exceptions.Add (new TaskCanceledException (t));
668 t.RemoveContinuation (continuation);
672 continuation.Dispose ();
674 if (exceptions != null)
675 throw new AggregateException (exceptions);
682 public static int WaitAny (params Task[] tasks)
684 return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
687 public static int WaitAny (Task[] tasks, TimeSpan timeout)
689 return WaitAny (tasks, CheckTimeout (timeout));
692 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
694 return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
697 public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
699 return WaitAny (tasks, Timeout.Infinite, cancellationToken);
702 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
705 throw new ArgumentNullException ("tasks");
706 if (millisecondsTimeout < -1)
707 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
708 CheckForNullTasks (tasks);
710 if (tasks.Length > 0) {
711 var continuation = new ManualResetContinuation ();
714 for (int i = 0; i < tasks.Length; i++) {
718 t.ContinueWith (continuation);
721 if (!(result = continuation.Event.Wait (millisecondsTimeout, cancellationToken)))
725 foreach (var t in tasks)
726 t.RemoveContinuation (continuation);
727 continuation.Dispose ();
731 int firstFinished = -1;
732 for (int i = 0; i < tasks.Length; i++) {
740 return firstFinished;
743 static int CheckTimeout (TimeSpan timeout)
746 return checked ((int)timeout.TotalMilliseconds);
747 } catch (System.OverflowException) {
748 throw new ArgumentOutOfRangeException ("timeout");
752 static void CheckForNullTasks (Task[] tasks)
754 foreach (var t in tasks)
756 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
761 public void Dispose ()
766 protected virtual void Dispose (bool disposing)
769 throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
771 // Set action to null so that the GC can collect the delegate and thus
772 // any big object references that the user might have captured in a anonymous method
776 if (cancellationRegistration != null)
777 cancellationRegistration.Value.Dispose ();
784 public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext)
786 return new ConfiguredTaskAwaitable (this, continueOnCapturedContext);
789 public Task ContinueWith (Action<Task, object> continuationAction, object state)
791 return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
794 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken)
796 return ContinueWith (continuationAction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
799 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskContinuationOptions continuationOptions)
801 return ContinueWith (continuationAction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
804 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskScheduler scheduler)
806 return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
809 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken,
810 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
812 if (continuationAction == null)
813 throw new ArgumentNullException ("continuationAction");
814 if (scheduler == null)
815 throw new ArgumentNullException ("scheduler");
817 Task continuation = new Task (TaskActionInvoker.Create (continuationAction),
818 state, cancellationToken,
819 GetCreationOptions (continuationOptions),
822 ContinueWithCore (continuation, continuationOptions, scheduler);
827 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
829 return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
832 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
834 return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
837 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
839 return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
842 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
844 return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
847 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
848 TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
850 if (continuationFunction == null)
851 throw new ArgumentNullException ("continuationFunction");
852 if (scheduler == null)
853 throw new ArgumentNullException ("scheduler");
855 var t = new Task<TResult> (TaskActionInvoker.Create (continuationFunction),
858 GetCreationOptions (continuationOptions),
862 ContinueWithCore (t, continuationOptions, scheduler);
867 public static Task Delay (int millisecondsDelay)
869 return Delay (millisecondsDelay, CancellationToken.None);
872 public static Task Delay (TimeSpan delay)
874 return Delay (CheckTimeout (delay), CancellationToken.None);
877 public static Task Delay (TimeSpan delay, CancellationToken cancellationToken)
879 return Delay (CheckTimeout (delay), cancellationToken);
882 public static Task Delay (int millisecondsDelay, CancellationToken cancellationToken)
884 if (millisecondsDelay < -1)
885 throw new ArgumentOutOfRangeException ("millisecondsDelay");
887 var task = new Task (TaskActionInvoker.Delay, millisecondsDelay, cancellationToken, TaskCreationOptions.None, null, TaskConstants.Finished);
888 task.SetupScheduler (TaskScheduler.Current);
889 // TODO: Does not guarantee the task will run
890 task.scheduler.QueueTask (task);
894 public static Task<TResult> FromResult<TResult> (TResult result)
896 var tcs = new TaskCompletionSource<TResult> ();
897 tcs.SetResult (result);
901 public TaskAwaiter GetAwaiter ()
903 return new TaskAwaiter (this);
906 public static Task Run (Action action)
908 return Run (action, CancellationToken.None);
911 public static Task Run (Action action, CancellationToken cancellationToken)
913 if (cancellationToken.IsCancellationRequested)
914 return TaskConstants.Canceled;
916 var t = new Task (action, cancellationToken, TaskCreationOptions.DenyChildAttach);
921 public static Task Run (Func<Task> function)
923 return Run (function, CancellationToken.None);
926 public static Task Run (Func<Task> function, CancellationToken cancellationToken)
928 if (cancellationToken.IsCancellationRequested)
929 return TaskConstants.Canceled;
931 var t = new Task<Task> (function, cancellationToken);
936 public static Task<TResult> Run<TResult> (Func<TResult> function)
938 return Run (function, CancellationToken.None);
941 public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
943 if (cancellationToken.IsCancellationRequested)
944 return TaskConstants<TResult>.Canceled;
946 var t = new Task<TResult> (function, cancellationToken, TaskCreationOptions.DenyChildAttach);
951 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
953 return Run (function, CancellationToken.None);
956 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
958 if (cancellationToken.IsCancellationRequested)
959 return TaskConstants<TResult>.Canceled;
961 var t = Task<Task<TResult>>.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
962 return GetTaskResult (t);
965 async static Task<TResult> GetTaskResult<TResult> (Task<Task<TResult>> task)
967 var r = await task.ConfigureAwait (false);
971 public static Task WhenAll (params Task[] tasks)
974 throw new ArgumentNullException ("tasks");
976 return WhenAllCore (tasks);
979 public static Task WhenAll (IEnumerable<Task> tasks)
982 throw new ArgumentNullException ("tasks");
984 // Call ToList on input enumeration or we end up
985 // enumerating it more than once
986 return WhenAllCore (new List<Task> (tasks));
989 static Task WhenAllCore (IList<Task> tasks)
991 bool all_completed = true;
992 foreach (var t in tasks) {
994 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
996 all_completed &= t.Status == TaskStatus.RanToCompletion;
1000 return TaskConstants.Finished;
1002 var task = new Task (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1003 task.SetupScheduler (TaskScheduler.Current);
1005 var continuation = new WhenAllContinuation (task, tasks);
1006 foreach (var t in tasks)
1007 t.ContinueWith (continuation);
1012 public static Task<TResult[]> WhenAll<TResult> (params Task<TResult>[] tasks)
1015 throw new ArgumentNullException ("tasks");
1017 return WhenAllCore<TResult> (tasks);
1020 public static Task<TResult[]> WhenAll<TResult> (IEnumerable<Task<TResult>> tasks)
1023 throw new ArgumentNullException ("tasks");
1025 // Call ToList on input enumeration or we end up
1026 // enumerating it more than once
1027 return WhenAllCore<TResult> (new List<Task<TResult>> (tasks));
1030 static Task<TResult[]> WhenAllCore<TResult> (IList<Task<TResult>> tasks)
1032 foreach (var t in tasks) {
1034 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1037 var task = new Task<TResult[]> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1038 task.SetupScheduler (TaskScheduler.Current);
1040 var continuation = new WhenAllContinuation<TResult> (task, tasks);
1041 foreach (var t in tasks)
1042 t.ContinueWith (continuation);
1047 public static Task<Task> WhenAny (params Task[] tasks)
1050 throw new ArgumentNullException ("tasks");
1052 return WhenAnyCore (tasks);
1055 public static Task<Task> WhenAny (IEnumerable<Task> tasks)
1058 throw new ArgumentNullException ("tasks");
1060 return WhenAnyCore (new List<Task> (tasks));
1063 static Task<Task> WhenAnyCore (IList<Task> tasks)
1065 if (tasks.Count == 0)
1066 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1068 int completed_index = -1;
1069 for (int i = 0; i < tasks.Count; ++i) {
1072 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1074 if (t.IsCompleted && completed_index < 0)
1075 completed_index = i;
1078 var task = new Task<Task> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1080 if (completed_index > 0) {
1081 task.TrySetResult (tasks[completed_index]);
1085 task.SetupScheduler (TaskScheduler.Current);
1087 var continuation = new WhenAnyContinuation<Task> (task, tasks);
1088 foreach (var t in tasks)
1089 t.ContinueWith (continuation);
1094 public static Task<Task<TResult>> WhenAny<TResult> (params Task<TResult>[] tasks)
1097 throw new ArgumentNullException ("tasks");
1099 return WhenAnyCore<TResult> (tasks);
1102 public static Task<Task<TResult>> WhenAny<TResult> (IEnumerable<Task<TResult>> tasks)
1105 throw new ArgumentNullException ("tasks");
1107 return WhenAnyCore<TResult> (new List<Task<TResult>> (tasks));
1110 static Task<Task<TResult>> WhenAnyCore<TResult> (IList<Task<TResult>> tasks)
1112 if (tasks.Count == 0)
1113 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1115 int completed_index = -1;
1116 for (int i = 0; i < tasks.Count; ++i) {
1119 throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1121 if (t.IsCompleted && completed_index < 0)
1122 completed_index = i;
1125 var task = new Task<Task<TResult>> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1127 if (completed_index > 0) {
1128 task.TrySetResult (tasks[completed_index]);
1132 task.SetupScheduler (TaskScheduler.Current);
1134 var continuation = new WhenAnyContinuation<Task<TResult>> (task, tasks);
1135 foreach (var t in tasks)
1136 t.ContinueWith (continuation);
1141 public static YieldAwaitable Yield ()
1143 return new YieldAwaitable ();
1149 internal CancellationToken CancellationToken {
1155 public static TaskFactory Factory {
1157 return defaultFactory;
1161 public static int? CurrentId {
1164 return t == null ? (int?)null : t.Id;
1168 public AggregateException Exception {
1172 exSlot.Observed = true;
1173 return exSlot.Exception;
1177 public bool IsCanceled {
1179 return status == TaskStatus.Canceled;
1183 public bool IsCompleted {
1185 return status >= TaskStatus.RanToCompletion;
1189 public bool IsFaulted {
1191 return status == TaskStatus.Faulted;
1195 public TaskCreationOptions CreationOptions {
1197 return taskCreationOptions & MaxTaskCreationOptions;
1201 public TaskStatus Status {
1207 Thread.MemoryBarrier ();
1211 TaskExceptionSlot ExceptionSlot {
1215 Interlocked.CompareExchange (ref exSlot, new TaskExceptionSlot (this), null);
1220 public object AsyncState {
1226 bool IAsyncResult.CompletedSynchronously {
1232 WaitHandle IAsyncResult.AsyncWaitHandle {
1244 bool IsContinuation {
1246 return contAncestor != null;
1250 internal Task ContinuationAncestor {
1252 return contAncestor;
1256 internal string DisplayActionMethod {
1258 Delegate d = invoker.Action;
1259 return d == null ? "<none>" : d.Method.ToString ();