Merge pull request #487 from mayerwin/patch-1
[mono.git] / mcs / class / corlib / System.Threading.Tasks / Task.cs
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 Xamarin Inc (http://www.xamarin.com).
10 //
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:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
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.
28 //
29 //
30
31 #if NET_4_0 || MOBILE
32
33 using System;
34 using System.Threading;
35 using System.Collections.Concurrent;
36 using System.Collections.Generic;
37 using System.Runtime.CompilerServices;
38
39 namespace System.Threading.Tasks
40 {
41         [System.Diagnostics.DebuggerDisplay ("Id = {Id}, Status = {Status}")]
42         [System.Diagnostics.DebuggerTypeProxy (typeof (TaskDebuggerView))]
43         public class Task : IDisposable, IAsyncResult
44         {
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;
49                 [System.ThreadStatic]
50                 static Action<Task> childWorkAdder;
51                 
52                 // parent is the outer task in which this task is created
53                 readonly Task parent;
54                 // contAncestor is the Task on which this continuation was setup
55                 readonly Task contAncestor;
56                 
57                 static int          id = -1;
58                 static readonly TaskFactory defaultFactory = new TaskFactory ();
59
60                 CountdownEvent childTasks;
61                 
62                 int                 taskId;
63                 TaskCreationOptions creationOptions;
64                 
65                 internal TaskScheduler       scheduler;
66
67                 TaskExceptionSlot exSlot;
68
69                 TaskStatus          status;
70
71                 TaskActionInvoker invoker;
72                 object         state;
73                 internal AtomicBooleanValue executing;
74
75                 TaskCompletionQueue<IContinuation> continuations;
76
77                 CancellationToken token;
78                 CancellationTokenRegistration? cancellationRegistration;
79
80                 internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
81
82                 const TaskCreationOptions MaxTaskCreationOptions =
83 #if NET_4_5
84                         TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler |
85 #endif
86                         TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent;
87
88                 public Task (Action action)
89                         : this (action, TaskCreationOptions.None)
90                 {
91                         
92                 }
93                 
94                 public Task (Action action, TaskCreationOptions creationOptions)
95                         : this (action, CancellationToken.None, creationOptions)
96                 {
97                         
98                 }
99                 
100                 public Task (Action action, CancellationToken cancellationToken)
101                         : this (action, cancellationToken, TaskCreationOptions.None)
102                 {
103                         
104                 }
105                 
106                 public Task (Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
107                         : this (TaskActionInvoker.Create (action), null, cancellationToken, creationOptions, current)
108                 {
109                         if (action == null)
110                                 throw new ArgumentNullException ("action");
111                         if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
112                                 throw new ArgumentOutOfRangeException ("creationOptions");
113                 }
114                 
115                 public Task (Action<object> action, object state)
116                         : this (action, state, TaskCreationOptions.None)
117                 {       
118                 }
119                 
120                 public Task (Action<object> action, object state, TaskCreationOptions creationOptions)
121                         : this (action, state, CancellationToken.None, creationOptions)
122                 {
123                 }
124                 
125                 public Task (Action<object> action, object state, CancellationToken cancellationToken)
126                         : this (action, state, cancellationToken, TaskCreationOptions.None)
127                 {       
128                 }
129
130                 public Task (Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
131                         : this (TaskActionInvoker.Create (action), state, cancellationToken, creationOptions, current)
132                 {
133                         if (action == null)
134                                 throw new ArgumentNullException ("action");
135                         if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
136                                 throw new ArgumentOutOfRangeException ("creationOptions");
137                 }
138
139                 internal Task (TaskActionInvoker invoker, object state, CancellationToken cancellationToken,
140                                TaskCreationOptions creationOptions, Task parent = null, Task contAncestor = null, bool ignoreCancellation = false)
141                 {
142                         this.invoker         = invoker;
143                         this.creationOptions = creationOptions;
144                         this.state           = state;
145                         this.taskId          = Interlocked.Increment (ref id);
146                         this.token           = cancellationToken;
147                         this.parent          = parent = parent == null ? current : parent;
148                         this.contAncestor    = contAncestor;
149                         this.status          = cancellationToken.IsCancellationRequested && !ignoreCancellation ? TaskStatus.Canceled : TaskStatus.Created;
150
151                         // Process creationOptions
152 #if NET_4_5
153                         if (HasFlag (creationOptions, TaskCreationOptions.AttachedToParent)
154                             && parent != null && !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach))
155 #else
156                         if (HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
157 #endif
158                                 parent.AddChild ();
159
160                         if (token.CanBeCanceled && !ignoreCancellation)
161                                 cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
162                 }
163
164                 static bool HasFlag (TaskCreationOptions opt, TaskCreationOptions member)
165                 {
166                         return (opt & member) == member;
167                 }
168
169                 #region Start
170                 public void Start ()
171                 {
172                         Start (TaskScheduler.Current);
173                 }
174                 
175                 public void Start (TaskScheduler scheduler)
176                 {
177                         if (scheduler == null)
178                                 throw new ArgumentNullException ("scheduler");
179
180                         if (status >= TaskStatus.WaitingToRun)
181                                 throw new InvalidOperationException ("The Task is not in a valid state to be started.");
182
183                         if (IsContinuation)
184                                 throw new InvalidOperationException ("Start may not be called on a continuation task");
185
186                         SetupScheduler (scheduler);
187                         Schedule ();
188                 }
189
190                 internal void SetupScheduler (TaskScheduler scheduler)
191                 {
192                         this.scheduler = scheduler;
193                         Status = TaskStatus.WaitingForActivation;
194                 }
195                 
196                 public void RunSynchronously ()
197                 {
198                         RunSynchronously (TaskScheduler.Current);
199                 }
200                 
201                 public void RunSynchronously (TaskScheduler scheduler)
202                 {
203                         if (scheduler == null)
204                                 throw new ArgumentNullException ("scheduler");
205
206                         if (Status > TaskStatus.WaitingForActivation)
207                                 throw new InvalidOperationException ("The task is not in a valid state to be started");
208
209                         SetupScheduler (scheduler);
210                         var saveStatus = status;
211                         Status = TaskStatus.WaitingToRun;
212
213                         try {
214                                 if (scheduler.RunInline (this, false))
215                                         return;
216                         } catch (Exception inner) {
217                                 throw new TaskSchedulerException (inner);
218                         }
219
220                         Status = saveStatus;
221                         Start (scheduler);
222                         Wait ();
223                 }
224                 #endregion
225                 
226                 #region ContinueWith
227                 public Task ContinueWith (Action<Task> continuationAction)
228                 {
229                         return ContinueWith (continuationAction, TaskContinuationOptions.None);
230                 }
231                 
232                 public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
233                 {
234                         return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
235                 }
236                 
237                 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
238                 {
239                         return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
240                 }
241                 
242                 public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
243                 {
244                         return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
245                 }
246                 
247                 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
248                 {
249                         if (continuationAction == null)
250                                 throw new ArgumentNullException ("continuationAction");
251                         if (scheduler == null)
252                                 throw new ArgumentNullException ("scheduler");
253
254                         return ContinueWith (TaskActionInvoker.Create (continuationAction), cancellationToken, continuationOptions, scheduler);
255                 }
256
257                 internal Task ContinueWith (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
258                 {
259                         var lazyCancellation = false;
260 #if NET_4_5
261                         lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
262 #endif
263                         var continuation = new Task (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), null, this, lazyCancellation);
264                         ContinueWithCore (continuation, continuationOptions, scheduler);
265
266                         return continuation;
267                 }
268                 
269                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction)
270                 {
271                         return ContinueWith<TResult> (continuationFunction, TaskContinuationOptions.None);
272                 }
273                 
274                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
275                 {
276                         return ContinueWith<TResult> (continuationFunction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
277                 }
278                 
279                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
280                 {
281                         return ContinueWith<TResult> (continuationFunction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
282                 }
283                 
284                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
285                 {
286                         return ContinueWith<TResult> (continuationFunction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
287                 }
288                 
289                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
290                                                             TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
291                 {
292                         if (continuationFunction == null)
293                                 throw new ArgumentNullException ("continuationFunction");
294                         if (scheduler == null)
295                                 throw new ArgumentNullException ("scheduler");
296
297                         return ContinueWith<TResult> (TaskActionInvoker.Create (continuationFunction), cancellationToken, continuationOptions, scheduler);
298                 }
299
300                 internal Task<TResult> ContinueWith<TResult> (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
301                 {
302                         var lazyCancellation = false;
303 #if NET_4_5
304                         lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
305 #endif
306                         var continuation = new Task<TResult> (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), parent, this, lazyCancellation);
307                         ContinueWithCore (continuation, continuationOptions, scheduler);
308
309                         return continuation;
310                 }
311         
312                 internal void ContinueWithCore (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler)
313                 {
314                         const TaskContinuationOptions wrongRan = TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.OnlyOnRanToCompletion;
315                         const TaskContinuationOptions wrongCanceled = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.OnlyOnCanceled;
316                         const TaskContinuationOptions wrongFaulted = TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.OnlyOnFaulted;
317
318                         if (((options & wrongRan) == wrongRan) || ((options & wrongCanceled) == wrongCanceled) || ((options & wrongFaulted) == wrongFaulted))
319                                 throw new ArgumentException ("continuationOptions", "Some options are mutually exclusive");
320
321                         // Already set the scheduler so that user can call Wait and that sort of stuff
322                         continuation.scheduler = scheduler;
323                         continuation.Status = TaskStatus.WaitingForActivation;
324
325                         ContinueWith (new TaskContinuation (continuation, options));
326                 }
327                 
328                 internal void ContinueWith (IContinuation continuation)
329                 {
330                         if (IsCompleted) {
331                                 continuation.Execute ();
332                                 return;
333                         }
334                         
335                         continuations.Add (continuation);
336                         
337                         // Retry in case completion was achieved but event adding was too late
338                         if (IsCompleted && continuations.Remove (continuation))
339                                 continuation.Execute ();
340                 }
341
342                 void RemoveContinuation (IContinuation continuation)
343                 {
344                         continuations.Remove (continuation);
345                 }
346
347                 static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
348                 {
349                         TaskCreationOptions options = TaskCreationOptions.None;
350                         if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
351                                 options |= TaskCreationOptions.AttachedToParent;
352                         if ((kind & TaskContinuationOptions.PreferFairness) > 0)
353                                 options |= TaskCreationOptions.PreferFairness;
354                         if ((kind & TaskContinuationOptions.LongRunning) > 0)
355                                 options |= TaskCreationOptions.LongRunning;
356                         
357                         return options;
358                 }
359                 #endregion
360                 
361                 #region Internal and protected thingies
362                 internal void Schedule ()
363                 {
364                         Status = TaskStatus.WaitingToRun;
365                         
366                         // If worker is null it means it is a local one, revert to the old behavior
367                         // If TaskScheduler.Current is not being used, the scheduler was explicitly provided, so we must use that
368                         if (scheduler != TaskScheduler.Current || childWorkAdder == null || HasFlag (creationOptions, TaskCreationOptions.PreferFairness)) {
369                                 scheduler.QueueTask (this);
370                         } else {
371                                 /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom 
372                                  * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
373                                  * the correct Thread during the creation
374                                  */
375                                 childWorkAdder (this);
376                         }
377                 }
378                 
379                 void ThreadStart ()
380                 {
381                         /* Allow scheduler to break fairness of deque ordering without
382                          * breaking its semantic (the task can be executed twice but the
383                          * second time it will return immediately
384                          */
385                         if (!executing.TryRelaxedSet ())
386                                 return;
387
388                         // Disable CancellationToken direct cancellation
389                         if (cancellationRegistration != null) {
390                                 cancellationRegistration.Value.Dispose ();
391                                 cancellationRegistration = null;
392                         }
393
394                         // If Task are ran inline on the same thread we might trash these values
395                         var saveCurrent = current;
396                         var saveScheduler = TaskScheduler.Current;
397
398                         current = this;
399 #if NET_4_5
400                         TaskScheduler.Current = HasFlag (creationOptions, TaskCreationOptions.HideScheduler) ? TaskScheduler.Default : scheduler;
401 #else
402                         TaskScheduler.Current = scheduler;
403 #endif
404                         
405                         if (!token.IsCancellationRequested) {
406                                 
407                                 status = TaskStatus.Running;
408                                 
409                                 try {
410                                         InnerInvoke ();
411                                 } catch (OperationCanceledException oce) {
412                                         if (token != CancellationToken.None && oce.CancellationToken == token)
413                                                 CancelReal ();
414                                         else
415                                                 HandleGenericException (oce);
416                                 } catch (Exception e) {
417                                         HandleGenericException (e);
418                                 }
419                         } else {
420                                 CancelReal ();
421                         }
422
423                         if (saveCurrent != null)
424                                 current = saveCurrent;
425                         if (saveScheduler != null)
426                                 TaskScheduler.Current = saveScheduler;
427                         Finish ();
428                 }
429
430                 internal bool TrySetCanceled ()
431                 {
432                         if (IsCompleted)
433                                 return false;
434                         
435                         if (!executing.TryRelaxedSet ()) {
436                                 var sw = new SpinWait ();
437                                 while (!IsCompleted)
438                                         sw.SpinOnce ();
439
440                                 return false;
441                         }
442                         
443                         CancelReal ();
444                         return true;
445                 }
446
447                 internal bool TrySetException (AggregateException aggregate)
448                 {
449                         if (IsCompleted)
450                                 return false;
451                         
452                         if (!executing.TryRelaxedSet ()) {
453                                 var sw = new SpinWait ();
454                                 while (!IsCompleted)
455                                         sw.SpinOnce ();
456
457                                 return false;
458                         }
459                         
460                         HandleGenericException (aggregate);
461                         return true;
462                 }
463
464                 internal bool TrySetExceptionObserved ()
465                 {
466                         if (exSlot != null) {
467                                 exSlot.Observed = true;
468                                 return true;
469                         }
470                         return false;
471                 }
472
473                 internal void Execute ()
474                 {
475                         ThreadStart ();
476                 }
477                 
478                 internal void AddChild ()
479                 {
480                         if (childTasks == null)
481                                 Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
482                         childTasks.AddCount ();
483                 }
484
485                 internal void ChildCompleted (AggregateException childEx)
486                 {
487                         if (childEx != null) {
488                                 if (ExceptionSlot.ChildExceptions == null)
489                                         Interlocked.CompareExchange (ref ExceptionSlot.ChildExceptions, new ConcurrentQueue<AggregateException> (), null);
490                                 ExceptionSlot.ChildExceptions.Enqueue (childEx);
491                         }
492
493                         if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
494                                 ProcessChildExceptions ();
495                                 Status = exSlot == null ? TaskStatus.RanToCompletion : TaskStatus.Faulted;
496                                 ProcessCompleteDelegates ();
497                                 if (HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
498                                         parent.ChildCompleted (this.Exception);
499                         }
500                 }
501
502                 void InnerInvoke ()
503                 {
504                         if (IsContinuation) {
505                                 invoker.Invoke (contAncestor, state, this);
506                         } else {
507                                 invoker.Invoke (this, state, this);
508                         }
509                 }
510                 
511                 internal void Finish ()
512                 {
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);
517                         }
518                         
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;
525                         }
526
527                         // Completions are already processed when task is canceled or faulted
528                         if (status == TaskStatus.RanToCompletion)
529                                 ProcessCompleteDelegates ();
530
531                         // Reset the current thingies
532                         if (current == this)
533                                 current = null;
534                         if (TaskScheduler.Current == scheduler)
535                                 TaskScheduler.Current = null;
536
537                         if (cancellationRegistration.HasValue)
538                                 cancellationRegistration.Value.Dispose ();
539                         
540                         // Tell parent that we are finished
541                         if (HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) && parent != null && status != TaskStatus.WaitingForChildrenToComplete) {
542                                 parent.ChildCompleted (this.Exception);
543                         }
544                 }
545
546                 void ProcessCompleteDelegates ()
547                 {
548                         if (continuations.HasElements) {
549                                 IContinuation continuation;
550                                 while (continuations.TryGetNextCompletion (out continuation))
551                                         continuation.Execute ();
552                         }
553                 }
554
555                 void ProcessChildExceptions (bool isParent = false)
556                 {
557                         if (exSlot == null || exSlot.ChildExceptions == null)
558                                 return;
559
560                         if (ExceptionSlot.Exception == null)
561                                 exSlot.Exception = new AggregateException ();
562
563                         AggregateException childEx;
564                         while (exSlot.ChildExceptions.TryDequeue (out childEx))
565                                 exSlot.Exception.AddChildException (childEx);
566
567                         if (isParent) {
568                                 Status = TaskStatus.Faulted;
569                                 ProcessCompleteDelegates ();                    
570                         }
571                 }
572                 #endregion
573                 
574                 #region Cancel and Wait related method
575                 
576                 internal void CancelReal ()
577                 {
578                         Status = TaskStatus.Canceled;
579                         ProcessCompleteDelegates ();
580                 }
581
582                 void HandleGenericException (Exception e)
583                 {
584                         HandleGenericException (new AggregateException (e));
585                 }
586
587                 void HandleGenericException (AggregateException e)
588                 {
589                         ExceptionSlot.Exception = e;
590                         Thread.MemoryBarrier ();
591                         Status = TaskStatus.Faulted;
592                         ProcessCompleteDelegates ();
593                 }
594
595                 internal bool WaitOnChildren ()
596                 {
597                         if (Status == TaskStatus.WaitingForChildrenToComplete && childTasks != null) {
598                                 childTasks.Wait ();
599                                 return true;
600                         }
601                         return false;
602                 }
603                 
604                 public void Wait ()
605                 {
606                         Wait (Timeout.Infinite, CancellationToken.None);
607                 }
608
609                 public void Wait (CancellationToken cancellationToken)
610                 {
611                         Wait (Timeout.Infinite, cancellationToken);
612                 }
613                 
614                 public bool Wait (TimeSpan timeout)
615                 {
616                         return Wait (CheckTimeout (timeout), CancellationToken.None);
617                 }
618                 
619                 public bool Wait (int millisecondsTimeout)
620                 {
621                         return Wait (millisecondsTimeout, CancellationToken.None);
622                 }
623
624                 public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
625                 {
626                         if (millisecondsTimeout < -1)
627                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
628
629                         bool result = true;
630
631                         if (!IsCompleted) {
632                                 // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
633                                 if (Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
634                                         scheduler.RunInline (this, true);
635
636                                 if (!IsCompleted) {
637                                         var continuation = new ManualResetContinuation ();
638                                         try {
639                                                 ContinueWith (continuation);
640                                                 result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
641                                         } finally {
642                                                 if (!result)
643                                                         RemoveContinuation (continuation);
644                                                 continuation.Dispose ();
645                                         }
646                                 }
647                         }
648
649                         if (IsCanceled)
650                                 throw new AggregateException (new TaskCanceledException (this));
651
652                         var exception = Exception;
653                         if (exception != null)
654                                 throw exception;
655
656                         if (childTasks != null)
657                                 childTasks.Wait ();
658
659                         return result;
660                 }
661                 
662                 public static void WaitAll (params Task[] tasks)
663                 {
664                         WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
665                 }
666
667                 public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
668                 {
669                         WaitAll (tasks, Timeout.Infinite, cancellationToken);
670                 }
671                 
672                 public static bool WaitAll (Task[] tasks, TimeSpan timeout)
673                 {
674                         return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
675                 }
676                 
677                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
678                 {
679                         return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
680                 }
681                 
682                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
683                 {
684                         if (tasks == null)
685                                 throw new ArgumentNullException ("tasks");
686
687                         bool result = true;
688                         foreach (var t in tasks) {
689                                 if (t == null)
690                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
691
692                                 result &= t.Status == TaskStatus.RanToCompletion;
693                         }
694
695                         if (!result) {
696                                 var continuation = new CountdownContinuation (tasks.Length);
697                                 try {
698                                         foreach (var t in tasks)
699                                                 t.ContinueWith (continuation);
700
701                                         result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
702                                 } finally {
703                                         List<Exception> exceptions = null;
704
705                                         foreach (var t in tasks) {
706                                                 if (result) {
707                                                         if (t.Status == TaskStatus.RanToCompletion)
708                                                                 continue;
709                                                         if (exceptions == null)
710                                                                 exceptions = new List<Exception> ();
711                                                         if (t.Exception != null)
712                                                                 exceptions.AddRange (t.Exception.InnerExceptions);
713                                                         else
714                                                                 exceptions.Add (new TaskCanceledException (t));
715                                                 } else {
716                                                         t.RemoveContinuation (continuation);
717                                                 }
718                                         }
719
720                                         continuation.Dispose ();
721
722                                         if (exceptions != null)
723                                                 throw new AggregateException (exceptions);
724                                 }
725                         }
726
727                         return result;
728                 }
729                 
730                 public static int WaitAny (params Task[] tasks)
731                 {
732                         return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
733                 }
734
735                 public static int WaitAny (Task[] tasks, TimeSpan timeout)
736                 {
737                         return WaitAny (tasks, CheckTimeout (timeout));
738                 }
739                 
740                 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
741                 {
742                         return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
743                 }
744
745                 public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
746                 {
747                         return WaitAny (tasks, Timeout.Infinite, cancellationToken);
748                 }
749
750                 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
751                 {
752                         if (tasks == null)
753                                 throw new ArgumentNullException ("tasks");
754                         if (millisecondsTimeout < -1)
755                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
756                         CheckForNullTasks (tasks);
757
758                         if (tasks.Length > 0) {
759                                 var continuation = new ManualResetContinuation ();
760                                 bool result = false;
761                                 try {
762                                         for (int i = 0; i < tasks.Length; i++) {
763                                                 var t = tasks[i];
764                                                 if (t.IsCompleted)
765                                                         return i;
766                                                 t.ContinueWith (continuation);
767                                         }
768
769                                         if (!(result = continuation.Event.Wait (millisecondsTimeout, cancellationToken)))
770                                                 return -1;
771                                 } finally {
772                                         if (!result)
773                                                 foreach (var t in tasks)
774                                                         t.RemoveContinuation (continuation);
775                                         continuation.Dispose ();
776                                 }
777                         }
778
779                         int firstFinished = -1;
780                         for (int i = 0; i < tasks.Length; i++) {
781                                 var t = tasks[i];
782                                 if (t.IsCompleted) {
783                                         firstFinished = i;
784                                         break;
785                                 }
786                         }
787
788                         return firstFinished;
789                 }
790
791                 static int CheckTimeout (TimeSpan timeout)
792                 {
793                         try {
794                                 return checked ((int)timeout.TotalMilliseconds);
795                         } catch (System.OverflowException) {
796                                 throw new ArgumentOutOfRangeException ("timeout");
797                         }
798                 }
799
800                 static void CheckForNullTasks (Task[] tasks)
801                 {
802                         foreach (var t in tasks)
803                                 if (t == null)
804                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
805                 }
806                 #endregion
807                 
808                 #region Dispose
809                 public void Dispose ()
810                 {
811                         Dispose (true);
812                 }
813                 
814                 protected virtual void Dispose (bool disposing)
815                 {
816                         if (!IsCompleted)
817                                 throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
818
819                         // Set action to null so that the GC can collect the delegate and thus
820                         // any big object references that the user might have captured in a anonymous method
821                         if (disposing) {
822                                 invoker = null;
823                                 state = null;
824                                 if (cancellationRegistration != null)
825                                         cancellationRegistration.Value.Dispose ();
826                         }
827                 }
828                 #endregion
829                 
830 #if NET_4_5
831
832                 public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext)
833                 {
834                         return new ConfiguredTaskAwaitable (this, continueOnCapturedContext);
835                 }
836
837                 public Task ContinueWith (Action<Task, object> continuationAction, object state)
838                 {
839                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
840                 }
841
842                 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken)
843                 {
844                         return ContinueWith (continuationAction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
845                 }
846
847                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskContinuationOptions continuationOptions)
848                 {
849                         return ContinueWith (continuationAction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
850                 }
851
852                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskScheduler scheduler)
853                 {
854                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
855                 }
856
857                 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken,
858                                                                   TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
859                 {
860                         if (continuationAction == null)
861                                 throw new ArgumentNullException ("continuationAction");
862                         if (scheduler == null)
863                                 throw new ArgumentNullException ("scheduler");
864
865                         Task continuation = new Task (TaskActionInvoker.Create (continuationAction),
866                                                                                   state, cancellationToken,
867                                                                                   GetCreationOptions (continuationOptions),
868                                                       parent,
869                                                       this);
870                         ContinueWithCore (continuation, continuationOptions, scheduler);
871
872                         return continuation;
873                 }
874
875                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
876                 {
877                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
878                 }
879
880                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
881                 {
882                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
883                 }
884
885                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
886                 {
887                         return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
888                 }
889
890                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
891                 {
892                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
893                 }
894
895                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
896                                                                                                         TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
897                 {
898                         if (continuationFunction == null)
899                                 throw new ArgumentNullException ("continuationFunction");
900                         if (scheduler == null)
901                                 throw new ArgumentNullException ("scheduler");
902
903                         var t = new Task<TResult> (TaskActionInvoker.Create (continuationFunction),
904                                                    state,
905                                                    cancellationToken,
906                                                    GetCreationOptions (continuationOptions),
907                                                    parent,
908                                                    this);
909
910                         ContinueWithCore (t, continuationOptions, scheduler);
911
912                         return t;
913                 }
914
915                 public static Task Delay (int millisecondsDelay)
916                 {
917                         return Delay (millisecondsDelay, CancellationToken.None);
918                 }
919
920                 public static Task Delay (TimeSpan delay)
921                 {
922                         return Delay (CheckTimeout (delay), CancellationToken.None);
923                 }
924
925                 public static Task Delay (TimeSpan delay, CancellationToken cancellationToken)
926                 {
927                         return Delay (CheckTimeout (delay), cancellationToken);
928                 }
929
930                 public static Task Delay (int millisecondsDelay, CancellationToken cancellationToken)
931                 {
932                         if (millisecondsDelay < -1)
933                                 throw new ArgumentOutOfRangeException ("millisecondsDelay");
934
935                         var task = new Task (TaskActionInvoker.Delay, millisecondsDelay, cancellationToken, TaskCreationOptions.None, null, TaskConstants.Finished);
936                         task.SetupScheduler (TaskScheduler.Current);
937                         
938                         if (millisecondsDelay != Timeout.Infinite)
939                                 task.scheduler.QueueTask (task);
940
941                         return task;
942                 }
943
944                 public static Task<TResult> FromResult<TResult> (TResult result)
945                 {
946                         var tcs = new TaskCompletionSource<TResult> ();
947                         tcs.SetResult (result);
948                         return tcs.Task;
949                 }
950
951                 public TaskAwaiter GetAwaiter ()
952                 {
953                         return new TaskAwaiter (this);
954                 }
955
956                 public static Task Run (Action action)
957                 {
958                         return Run (action, CancellationToken.None);
959                 }
960
961                 public static Task Run (Action action, CancellationToken cancellationToken)
962                 {
963                         if (cancellationToken.IsCancellationRequested)
964                                 return TaskConstants.Canceled;
965
966                         return Task.Factory.StartNew (action, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
967                 }
968
969                 public static Task Run (Func<Task> function)
970                 {
971                         return Run (function, CancellationToken.None);
972                 }
973
974                 public static Task Run (Func<Task> function, CancellationToken cancellationToken)
975                 {
976                         if (cancellationToken.IsCancellationRequested)
977                                 return TaskConstants.Canceled;
978
979                         return TaskExtensions.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
980                 }
981
982                 public static Task<TResult> Run<TResult> (Func<TResult> function)
983                 {
984                         return Run (function, CancellationToken.None);
985                 }
986
987                 public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
988                 {
989                         if (cancellationToken.IsCancellationRequested)
990                                 return TaskConstants<TResult>.Canceled;
991
992                         return Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
993                 }
994
995                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
996                 {
997                         return Run (function, CancellationToken.None);
998                 }
999
1000                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
1001                 {
1002                         if (cancellationToken.IsCancellationRequested)
1003                                 return TaskConstants<TResult>.Canceled;
1004
1005                         return TaskExtensions.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
1006                 }
1007
1008                 public static Task WhenAll (params Task[] tasks)
1009                 {
1010                         if (tasks == null)
1011                                 throw new ArgumentNullException ("tasks");
1012
1013                         return WhenAllCore (tasks);
1014                 }
1015
1016                 public static Task WhenAll (IEnumerable<Task> tasks)
1017                 {
1018                         if (tasks == null)
1019                                 throw new ArgumentNullException ("tasks");
1020
1021                         // Call ToList on input enumeration or we end up
1022                         // enumerating it more than once
1023                         return WhenAllCore (new List<Task> (tasks));
1024                 }
1025
1026                 public static Task<TResult[]> WhenAll<TResult> (params Task<TResult>[] tasks)
1027                 {
1028                         if (tasks == null)
1029                                 throw new ArgumentNullException ("tasks");
1030
1031                         return WhenAllCore<TResult> (tasks);
1032                 }
1033
1034                 public static Task<TResult[]> WhenAll<TResult> (IEnumerable<Task<TResult>> tasks)
1035                 {
1036                         if (tasks == null)
1037                                 throw new ArgumentNullException ("tasks");
1038
1039                         // Call ToList on input enumeration or we end up
1040                         // enumerating it more than once
1041                         return WhenAllCore<TResult> (new List<Task<TResult>> (tasks));
1042                 }
1043
1044                 internal static Task<TResult[]> WhenAllCore<TResult> (IList<Task<TResult>> tasks)
1045                 {
1046                         foreach (var t in tasks) {
1047                                 if (t == null)
1048                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1049                         }
1050
1051                         var task = new Task<TResult[]> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1052                         task.SetupScheduler (TaskScheduler.Current);
1053
1054                         var continuation = new WhenAllContinuation<TResult> (task, tasks);
1055                         foreach (var t in tasks)
1056                                 t.ContinueWith (continuation);
1057
1058                         return task;
1059                 }
1060
1061                 public static Task<Task> WhenAny (params Task[] tasks)
1062                 {
1063                         if (tasks == null)
1064                                 throw new ArgumentNullException ("tasks");
1065
1066                         return WhenAnyCore (tasks);
1067                 }
1068
1069                 public static Task<Task> WhenAny (IEnumerable<Task> tasks)
1070                 {
1071                         if (tasks == null)
1072                                 throw new ArgumentNullException ("tasks");
1073
1074                         return WhenAnyCore (new List<Task> (tasks));
1075                 }
1076
1077                 public static Task<Task<TResult>> WhenAny<TResult> (params Task<TResult>[] tasks)
1078                 {
1079                         if (tasks == null)
1080                                 throw new ArgumentNullException ("tasks");
1081
1082                         return WhenAnyCore<TResult> (tasks);
1083                 }
1084
1085                 public static Task<Task<TResult>> WhenAny<TResult> (IEnumerable<Task<TResult>> tasks)
1086                 {
1087                         if (tasks == null)
1088                                 throw new ArgumentNullException ("tasks");
1089
1090                         return WhenAnyCore<TResult> (new List<Task<TResult>> (tasks));
1091                 }
1092
1093                 static Task<Task<TResult>> WhenAnyCore<TResult> (IList<Task<TResult>> tasks)
1094                 {
1095                         if (tasks.Count == 0)
1096                                 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1097
1098                         int completed_index = -1;
1099                         for (int i = 0; i < tasks.Count; ++i) {
1100                                 var t = tasks[i];
1101                                 if (t == null)
1102                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1103
1104                                 if (t.IsCompleted && completed_index < 0)
1105                                         completed_index = i;
1106                         }
1107
1108                         var task = new Task<Task<TResult>> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1109
1110                         if (completed_index > 0) {
1111                                 task.TrySetResult (tasks[completed_index]);
1112                                 return task;
1113                         }
1114
1115                         task.SetupScheduler (TaskScheduler.Current);
1116
1117                         var continuation = new WhenAnyContinuation<Task<TResult>> (task, tasks);
1118                         foreach (var t in tasks)
1119                                 t.ContinueWith (continuation);
1120
1121                         return task;
1122                 }
1123
1124                 public static YieldAwaitable Yield ()
1125                 {
1126                         return new YieldAwaitable ();
1127                 }
1128 #endif
1129
1130                 internal static Task WhenAllCore (IList<Task> tasks)
1131                 {
1132                         bool all_completed = true;
1133                         foreach (var t in tasks) {
1134                                 if (t == null)
1135                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1136
1137                                 all_completed &= t.Status == TaskStatus.RanToCompletion;
1138                         }
1139
1140                         if (all_completed)
1141                                 return TaskConstants.Finished;
1142
1143                         var task = new Task (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1144                         task.SetupScheduler (TaskScheduler.Current);
1145
1146                         var continuation = new WhenAllContinuation (task, tasks);
1147                         foreach (var t in tasks)
1148                                 t.ContinueWith (continuation);
1149
1150                         return task;
1151                 }
1152
1153                 internal static Task<Task> WhenAnyCore (IList<Task> tasks)
1154                 {
1155                         if (tasks.Count == 0)
1156                                 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1157
1158                         int completed_index = -1;
1159                         for (int i = 0; i < tasks.Count; ++i) {
1160                                 var t = tasks [i];
1161                                 if (t == null)
1162                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1163
1164                                 if (t.IsCompleted && completed_index < 0)
1165                                         completed_index = i;
1166                         }
1167
1168                         var task = new Task<Task> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1169
1170                         if (completed_index > 0) {
1171                                 task.TrySetResult (tasks[completed_index]);
1172                                 return task;
1173                         }
1174
1175                         task.SetupScheduler (TaskScheduler.Current);
1176
1177                         var continuation = new WhenAnyContinuation<Task> (task, tasks);
1178                         foreach (var t in tasks)
1179                                 t.ContinueWith (continuation);
1180
1181                         return task;
1182                 }
1183                 #region Properties
1184
1185                 internal CancellationToken CancellationToken {
1186                         get {
1187                                 return token;
1188                         }
1189                 }
1190
1191                 public static TaskFactory Factory {
1192                         get {
1193                                 return defaultFactory;
1194                         }
1195                 }
1196                 
1197                 public static int? CurrentId {
1198                         get {
1199                                 Task t = current;
1200                                 return t == null ? (int?)null : t.Id;
1201                         }
1202                 }
1203                 
1204                 public AggregateException Exception {
1205                         get {
1206                                 if (exSlot == null)
1207                                         return null;
1208                                 exSlot.Observed = true;
1209                                 return exSlot.Exception;
1210                         }
1211                 }
1212                 
1213                 public bool IsCanceled {
1214                         get {
1215                                 return status == TaskStatus.Canceled;
1216                         }
1217                 }
1218
1219                 public bool IsCompleted {
1220                         get {
1221                                 return status >= TaskStatus.RanToCompletion;
1222                         }
1223                 }
1224                 
1225                 public bool IsFaulted {
1226                         get {
1227                                 return status == TaskStatus.Faulted;
1228                         }
1229                 }
1230
1231                 public TaskCreationOptions CreationOptions {
1232                         get {
1233                                 return creationOptions & MaxTaskCreationOptions;
1234                         }
1235                 }
1236                 
1237                 public TaskStatus Status {
1238                         get {
1239                                 return status;
1240                         }
1241                         internal set {
1242                                 status = value;
1243                                 Thread.MemoryBarrier ();
1244                         }
1245                 }
1246
1247                 TaskExceptionSlot ExceptionSlot {
1248                         get {
1249                                 if (exSlot != null)
1250                                         return exSlot;
1251                                 Interlocked.CompareExchange (ref exSlot, new TaskExceptionSlot (this), null);
1252                                 return exSlot;
1253                         }
1254                 }
1255
1256                 public object AsyncState {
1257                         get {
1258                                 return state;
1259                         }
1260                 }
1261                 
1262                 bool IAsyncResult.CompletedSynchronously {
1263                         get {
1264                                 return true;
1265                         }
1266                 }
1267
1268                 WaitHandle IAsyncResult.AsyncWaitHandle {
1269                         get {
1270                                 return null;
1271                         }
1272                 }
1273                 
1274                 public int Id {
1275                         get {
1276                                 return taskId;
1277                         }
1278                 }
1279
1280                 bool IsContinuation {
1281                         get {
1282                                 return contAncestor != null;
1283                         }
1284                 }
1285
1286                 internal Task ContinuationAncestor {
1287                         get {
1288                                 return contAncestor;
1289                         }
1290                 }
1291                 
1292                 internal string DisplayActionMethod {
1293                         get {
1294                                 Delegate d = invoker.Action;
1295                                 return d == null ? "<none>" : d.Method.ToString ();
1296                         }
1297                 }
1298                 
1299                 #endregion
1300         }
1301 }
1302 #endif