Implement ContinueWhenAny/ContinueWhenAll with less temporary tasks
[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                 const TaskCreationOptions TaskCreationOptionsContinuation = (TaskCreationOptions) (1 << 10);
46
47                 // With this attribute each thread has its own value so that it's correct for our Schedule code
48                 // and for Parent property.
49                 [System.ThreadStatic]
50                 static Task         current;
51                 [System.ThreadStatic]
52                 static Action<Task> childWorkAdder;
53                 
54                 Task parent;
55                 
56                 static int          id = -1;
57                 static readonly TaskFactory defaultFactory = new TaskFactory ();
58
59                 CountdownEvent childTasks;
60                 
61                 int                 taskId;
62                 TaskCreationOptions taskCreationOptions;
63                 
64                 internal TaskScheduler       scheduler;
65
66                 volatile AggregateException  exception;
67                 volatile bool                exceptionObserved;
68                 ConcurrentQueue<AggregateException> childExceptions;
69
70                 TaskStatus          status;
71
72                 TaskActionInvoker invoker;
73                 object         state;
74                 AtomicBooleanValue executing;
75
76                 TaskCompletionQueue<IContinuation> continuations;
77                 TaskCompletionQueue<IEventSlot> registeredEvts;
78
79                 CancellationToken token;
80                 CancellationTokenRegistration? cancellationRegistration;
81
82                 internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
83
84                 const TaskCreationOptions MaxTaskCreationOptions =
85 #if NET_4_5
86                         TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler |
87 #endif
88                         TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent;
89
90                 public Task (Action action)
91                         : this (action, TaskCreationOptions.None)
92                 {
93                         
94                 }
95                 
96                 public Task (Action action, TaskCreationOptions creationOptions)
97                         : this (action, CancellationToken.None, creationOptions)
98                 {
99                         
100                 }
101                 
102                 public Task (Action action, CancellationToken cancellationToken)
103                         : this (action, cancellationToken, TaskCreationOptions.None)
104                 {
105                         
106                 }
107                 
108                 public Task (Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
109                         : this (TaskActionInvoker.Create (action), null, cancellationToken, creationOptions, current)
110                 {
111                         if (action == null)
112                                 throw new ArgumentNullException ("action");
113                         if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
114                                 throw new ArgumentOutOfRangeException ("creationOptions");
115                 }
116                 
117                 public Task (Action<object> action, object state)
118                         : this (action, state, TaskCreationOptions.None)
119                 {       
120                 }
121                 
122                 public Task (Action<object> action, object state, TaskCreationOptions creationOptions)
123                         : this (action, state, CancellationToken.None, creationOptions)
124                 {
125                 }
126                 
127                 public Task (Action<object> action, object state, CancellationToken cancellationToken)
128                         : this (action, state, cancellationToken, TaskCreationOptions.None)
129                 {       
130                 }
131
132                 public Task (Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
133                         : this (TaskActionInvoker.Create (action), state, cancellationToken, creationOptions, current)
134                 {
135                         if (action == null)
136                                 throw new ArgumentNullException ("action");
137                         if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
138                                 throw new ArgumentOutOfRangeException ("creationOptions");
139                 }
140
141                 internal Task (TaskActionInvoker invoker, object state, CancellationToken cancellationToken,
142                                TaskCreationOptions creationOptions, Task parent)
143                 {
144                         this.invoker = invoker;
145                         this.taskCreationOptions = creationOptions;
146                         this.state               = state;
147                         this.taskId              = Interlocked.Increment (ref id);
148                         this.status              = cancellationToken.IsCancellationRequested ? TaskStatus.Canceled : TaskStatus.Created;
149                         this.token               = cancellationToken;
150                         this.parent              = parent;
151
152                         // Process taskCreationOptions
153                         if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
154                                 parent.AddChild ();
155
156                         if (token.CanBeCanceled)
157                                 cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
158                 }
159
160                 ~Task ()
161                 {
162                         if (exception != null && !exceptionObserved)
163                                 throw exception;
164                 }
165
166                 bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
167                 {
168                         return (opt & member) == member;
169                 }
170
171                 #region Start
172                 public void Start ()
173                 {
174                         Start (TaskScheduler.Current);
175                 }
176                 
177                 public void Start (TaskScheduler scheduler)
178                 {
179                         if (scheduler == null)
180                                 throw new ArgumentNullException ("scheduler");
181
182                         if (status >= TaskStatus.WaitingToRun)
183                                 throw new InvalidOperationException ("The Task is not in a valid state to be started.");
184
185                         if (IsContinuation)
186                                 throw new InvalidOperationException ("Start may not be called on a continuation task");
187
188                         SetupScheduler (scheduler);
189                         Schedule ();
190                 }
191
192                 internal void SetupScheduler (TaskScheduler scheduler)
193                 {
194                         this.scheduler = scheduler;
195                         Status = TaskStatus.WaitingForActivation;
196                 }
197                 
198                 public void RunSynchronously ()
199                 {
200                         RunSynchronously (TaskScheduler.Current);
201                 }
202                 
203                 public void RunSynchronously (TaskScheduler scheduler)
204                 {
205                         if (scheduler == null)
206                                 throw new ArgumentNullException ("scheduler");
207
208                         if (Status > TaskStatus.WaitingForActivation)
209                                 throw new InvalidOperationException ("The task is not in a valid state to be started");
210
211                         SetupScheduler (scheduler);
212                         var saveStatus = status;
213                         Status = TaskStatus.WaitingToRun;
214
215                         try {
216                                 if (scheduler.RunInline (this))
217                                         return;
218                         } catch (Exception inner) {
219                                 throw new TaskSchedulerException (inner);
220                         }
221
222                         Status = saveStatus;
223                         Start (scheduler);
224                         Wait ();
225                 }
226                 #endregion
227                 
228                 #region ContinueWith
229                 public Task ContinueWith (Action<Task> continuationAction)
230                 {
231                         return ContinueWith (continuationAction, TaskContinuationOptions.None);
232                 }
233                 
234                 public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
235                 {
236                         return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
237                 }
238                 
239                 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
240                 {
241                         return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
242                 }
243                 
244                 public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
245                 {
246                         return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
247                 }
248                 
249                 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
250                 {
251                         if (continuationAction == null)
252                                 throw new ArgumentNullException ("continuationAction");
253                         if (scheduler == null)
254                                 throw new ArgumentNullException ("scheduler");
255
256                         return ContinueWith (TaskActionInvoker.Create (continuationAction), cancellationToken, continuationOptions, scheduler);
257                 }
258
259                 internal Task ContinueWith (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
260                 {
261                         var continuation = new Task (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), this);
262                         ContinueWithCore (continuation, continuationOptions, scheduler);
263
264                         return continuation;
265                 }
266                 
267                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction)
268                 {
269                         return ContinueWith<TResult> (continuationFunction, TaskContinuationOptions.None);
270                 }
271                 
272                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
273                 {
274                         return ContinueWith<TResult> (continuationFunction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
275                 }
276                 
277                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
278                 {
279                         return ContinueWith<TResult> (continuationFunction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
280                 }
281                 
282                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
283                 {
284                         return ContinueWith<TResult> (continuationFunction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
285                 }
286                 
287                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
288                                                             TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
289                 {
290                         if (continuationFunction == null)
291                                 throw new ArgumentNullException ("continuationFunction");
292                         if (scheduler == null)
293                                 throw new ArgumentNullException ("scheduler");
294
295                         return ContinueWith<TResult> (TaskActionInvoker.Create (continuationFunction), cancellationToken, continuationOptions, scheduler);
296                 }
297
298                 internal Task<TResult> ContinueWith<TResult> (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
299                 {
300                         var continuation = new Task<TResult> (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), this);
301                         ContinueWithCore (continuation, continuationOptions, scheduler);
302
303                         return continuation;
304                 }
305         
306                 internal void ContinueWithCore (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler)
307                 {
308                         const TaskContinuationOptions wrongRan = TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.OnlyOnRanToCompletion;
309                         const TaskContinuationOptions wrongCanceled = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.OnlyOnCanceled;
310                         const TaskContinuationOptions wrongFaulted = TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.OnlyOnFaulted;
311
312                         if (((options & wrongRan) == wrongRan) || ((options & wrongCanceled) == wrongCanceled) || ((options & wrongFaulted) == wrongFaulted))
313                                 throw new ArgumentException ("continuationOptions", "Some options are mutually exclusive");
314
315                         // Already set the scheduler so that user can call Wait and that sort of stuff
316                         continuation.scheduler = scheduler;
317                         continuation.Status = TaskStatus.WaitingForActivation;
318                         continuation.taskCreationOptions |= TaskCreationOptionsContinuation;
319
320                         ContinueWith (new TaskContinuation (continuation, options));
321                 }
322                 
323                 internal void ContinueWith (IContinuation continuation)
324                 {
325                         if (IsCompleted) {
326                                 continuation.Execute ();
327                                 return;
328                         }
329                         
330                         continuations.Add (continuation);
331                         
332                         // Retry in case completion was achieved but event adding was too late
333                         if (IsCompleted)
334                                 continuation.Execute ();
335                 }
336
337                 static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
338                 {
339                         TaskCreationOptions options = TaskCreationOptions.None;
340                         if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
341                                 options |= TaskCreationOptions.AttachedToParent;
342                         if ((kind & TaskContinuationOptions.PreferFairness) > 0)
343                                 options |= TaskCreationOptions.PreferFairness;
344                         if ((kind & TaskContinuationOptions.LongRunning) > 0)
345                                 options |= TaskCreationOptions.LongRunning;
346                         
347                         return options;
348                 }
349
350                 void RegisterWaitEvent (IEventSlot slot)
351                 {
352                         if (IsCompleted) {
353                                 slot.Set ();
354                                 return;
355                         }
356
357                         registeredEvts.Add (slot);
358                         if (IsCompleted && registeredEvts.Remove (slot))
359                                 slot.Set ();
360                 }
361
362                 void UnregisterWaitEvent (IEventSlot slot)
363                 {
364                         registeredEvts.Remove (slot);
365                 }
366                 #endregion
367                 
368                 #region Internal and protected thingies
369                 internal void Schedule ()
370                 {
371                         Status = TaskStatus.WaitingToRun;
372                         
373                         // If worker is null it means it is a local one, revert to the old behavior
374                         // If TaskScheduler.Current is not being used, the scheduler was explicitly provided, so we must use that
375                         if (scheduler != TaskScheduler.Current || childWorkAdder == null || CheckTaskOptions (taskCreationOptions, TaskCreationOptions.PreferFairness)) {
376                                 scheduler.QueueTask (this);
377                         } else {
378                                 /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom 
379                                  * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
380                                  * the correct Thread during the creation
381                                  */
382                                 childWorkAdder (this);
383                         }
384                 }
385                 
386                 void ThreadStart ()
387                 {
388                         /* Allow scheduler to break fairness of deque ordering without
389                          * breaking its semantic (the task can be executed twice but the
390                          * second time it will return immediately
391                          */
392                         if (!executing.TryRelaxedSet ())
393                                 return;
394
395                         // Disable CancellationToken direct cancellation
396                         if (cancellationRegistration != null) {
397                                 cancellationRegistration.Value.Dispose ();
398                                 cancellationRegistration = null;
399                         }
400                         current = this;
401                         TaskScheduler.Current = scheduler;
402                         
403                         if (!token.IsCancellationRequested) {
404                                 
405                                 status = TaskStatus.Running;
406                                 
407                                 try {
408                                         InnerInvoke ();
409                                 } catch (OperationCanceledException oce) {
410                                         if (token != CancellationToken.None && oce.CancellationToken == token)
411                                                 CancelReal ();
412                                         else
413                                                 HandleGenericException (oce);
414                                 } catch (Exception e) {
415                                         HandleGenericException (e);
416                                 }
417                         } else {
418                                 CancelReal ();
419                         }
420                         
421                         Finish ();
422                 }
423                 
424                 internal void Execute (Action<Task> childAdder)
425                 {
426                         childWorkAdder = childAdder;
427                         ThreadStart ();
428                 }
429                 
430                 internal void AddChild ()
431                 {
432                         if (childTasks == null)
433                                 Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
434                         childTasks.AddCount ();
435                 }
436
437                 internal void ChildCompleted (AggregateException childEx)
438                 {
439                         if (childEx != null) {
440                                 if (childExceptions == null)
441                                         Interlocked.CompareExchange (ref childExceptions, new ConcurrentQueue<AggregateException> (), null);
442                                 childExceptions.Enqueue (childEx);
443                         }
444
445                         if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
446                                 Status = TaskStatus.RanToCompletion;
447                                 ProcessChildExceptions ();
448                                 ProcessCompleteDelegates ();
449                         }
450                 }
451
452                 void InnerInvoke ()
453                 {
454                         if (IsContinuation) {
455                                 invoker.Invoke (parent, state, this);
456                         } else {
457                                 invoker.Invoke (this, state, this);
458                         }
459                 }
460                 
461                 internal void Finish ()
462                 {
463                         // If there was children created and they all finished, we set the countdown
464                         if (childTasks != null)
465                                 childTasks.Signal ();
466                         
467                         // Don't override Canceled or Faulted
468                         if (status == TaskStatus.Running) {
469                                 if (childTasks == null || childTasks.IsSet)
470                                         Status = TaskStatus.RanToCompletion;
471                                 else
472                                         Status = TaskStatus.WaitingForChildrenToComplete;
473                         }
474                 
475                         // Completions are already processed when task is canceled
476                         if (status == TaskStatus.RanToCompletion || status == TaskStatus.Faulted)
477                                 ProcessCompleteDelegates ();
478
479                         // Reset the current thingies
480                         current = null;
481                         TaskScheduler.Current = null;
482
483                         if (cancellationRegistration.HasValue)
484                                 cancellationRegistration.Value.Dispose ();
485                         
486                         // Tell parent that we are finished
487                         if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null) {
488                                 parent.ChildCompleted (this.Exception);
489                         }
490                 }
491
492                 void ProcessCompleteDelegates ()
493                 {
494                         if (continuations.HasElements) {
495                                 IContinuation continuation;
496                                 while (continuations.TryGetNextCompletion (out continuation))
497                                         continuation.Execute ();
498                         }
499                         if (registeredEvts.HasElements) {
500                                 IEventSlot evt;
501                                 while (registeredEvts.TryGetNextCompletion (out evt))
502                                         evt.Set ();
503                         }
504                 }
505
506                 void ProcessChildExceptions ()
507                 {
508                         if (childExceptions == null)
509                                 return;
510
511                         if (exception == null)
512                                 exception = new AggregateException ();
513
514                         AggregateException childEx;
515                         while (childExceptions.TryDequeue (out childEx))
516                                 exception.AddChildException (childEx);
517                 }
518                 #endregion
519                 
520                 #region Cancel and Wait related method
521                 
522                 internal void CancelReal ()
523                 {
524                         Status = TaskStatus.Canceled;
525                         ProcessCompleteDelegates ();
526                 }
527
528                 internal void HandleGenericException (Exception e)
529                 {
530                         HandleGenericException (new AggregateException (e));
531                 }
532
533                 internal void HandleGenericException (AggregateException e)
534                 {
535                         exception = e;
536                         Thread.MemoryBarrier ();
537                         Status = TaskStatus.Faulted;
538                         if (scheduler != null && scheduler.FireUnobservedEvent (exception).Observed)
539                                 exceptionObserved = true;
540                 }
541                 
542                 public void Wait ()
543                 {
544                         Wait (Timeout.Infinite, CancellationToken.None);
545                 }
546
547                 public void Wait (CancellationToken cancellationToken)
548                 {
549                         Wait (Timeout.Infinite, cancellationToken);
550                 }
551                 
552                 public bool Wait (TimeSpan timeout)
553                 {
554                         return Wait (CheckTimeout (timeout), CancellationToken.None);
555                 }
556                 
557                 public bool Wait (int millisecondsTimeout)
558                 {
559                         return Wait (millisecondsTimeout, CancellationToken.None);
560                 }
561
562                 public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
563                 {
564                         if (millisecondsTimeout < -1)
565                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
566
567                         bool result = true;
568
569                         if (!IsCompleted) {
570                                 // If the task is ready to be run and we were supposed to wait on it indefinitely, just run it
571                                 if (Status == TaskStatus.WaitingToRun && millisecondsTimeout == -1 && scheduler != null)
572                                         Execute (null);
573
574                                 if (!IsCompleted) {
575                                         var evt = new ManualResetEventSlim ();
576                                         var slot = new ManualEventSlot (evt);
577                                         try {
578                                                 RegisterWaitEvent (slot);
579                                                 result = evt.Wait (millisecondsTimeout, cancellationToken);
580                                         } finally {
581                                                 if (!result)
582                                                         UnregisterWaitEvent (slot);
583                                                 evt.Dispose ();
584                                         }
585                                 }
586                         }
587
588                         if (IsCanceled)
589                                 throw new AggregateException (new TaskCanceledException (this));
590
591                         if (exception != null)
592                                 throw exception;
593
594                         return result;
595                 }
596                 
597                 public static void WaitAll (params Task[] tasks)
598                 {
599                         WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
600                 }
601
602                 public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
603                 {
604                         WaitAll (tasks, Timeout.Infinite, cancellationToken);
605                 }
606                 
607                 public static bool WaitAll (Task[] tasks, TimeSpan timeout)
608                 {
609                         return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
610                 }
611                 
612                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
613                 {
614                         return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
615                 }
616                 
617                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
618                 {
619                         if (tasks == null)
620                                 throw new ArgumentNullException ("tasks");
621
622                         bool result = true;
623                         foreach (var t in tasks) {
624                                 if (t == null)
625                                         throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
626
627                                 result &= t.Status == TaskStatus.RanToCompletion;
628                         }
629
630                         if (!result) {
631                                 var evt = new CountdownEvent (tasks.Length);
632                                 var slot = new CountdownEventSlot (evt);
633                                 try {
634                                         foreach (var t in tasks)
635                                                 t.RegisterWaitEvent (slot);
636
637                                         result = evt.Wait (millisecondsTimeout, cancellationToken);
638                                 } finally {
639                                         List<Exception> exceptions = null;
640
641                                         foreach (var t in tasks) {
642                                                 if (result) {
643                                                         if (t.Status == TaskStatus.RanToCompletion)
644                                                                 continue;
645                                                         if (exceptions == null)
646                                                                 exceptions = new List<Exception> ();
647                                                         if (t.Exception != null)
648                                                                 exceptions.AddRange (t.Exception.InnerExceptions);
649                                                         else
650                                                                 exceptions.Add (new TaskCanceledException (t));
651                                                 } else {
652                                                         t.UnregisterWaitEvent (slot);
653                                                 }
654                                         }
655
656                                         evt.Dispose ();
657
658                                         if (exceptions != null)
659                                                 throw new AggregateException (exceptions);
660                                 }
661                         }
662
663                         return result;
664                 }
665                 
666                 public static int WaitAny (params Task[] tasks)
667                 {
668                         return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
669                 }
670
671                 public static int WaitAny (Task[] tasks, TimeSpan timeout)
672                 {
673                         return WaitAny (tasks, CheckTimeout (timeout));
674                 }
675                 
676                 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
677                 {
678                         return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
679                 }
680
681                 public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
682                 {
683                         return WaitAny (tasks, Timeout.Infinite, cancellationToken);
684                 }
685
686                 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
687                 {
688                         if (tasks == null)
689                                 throw new ArgumentNullException ("tasks");
690                         if (millisecondsTimeout < -1)
691                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
692                         CheckForNullTasks (tasks);
693
694                         if (tasks.Length > 0) {
695                                 var evt = new ManualResetEventSlim ();
696                                 var slot = new ManualEventSlot (evt);
697                                 bool result = false;
698                                 try {
699                                         for (int i = 0; i < tasks.Length; i++) {
700                                                 var t = tasks[i];
701                                                 if (t.IsCompleted)
702                                                         return i;
703                                                 t.RegisterWaitEvent (slot);
704                                         }
705
706                                         if (!(result = evt.Wait (millisecondsTimeout, cancellationToken)))
707                                                 return -1;
708                                 } finally {
709                                         if (!result)
710                                                 foreach (var t in tasks)
711                                                         t.UnregisterWaitEvent (slot);
712                                         evt.Dispose ();
713                                 }
714                         }
715
716                         int firstFinished = -1;
717                         for (int i = 0; i < tasks.Length; i++) {
718                                 var t = tasks[i];
719                                 if (t.IsCompleted) {
720                                         firstFinished = i;
721                                         break;
722                                 }
723                         }
724
725                         return firstFinished;
726                 }
727
728                 static int CheckTimeout (TimeSpan timeout)
729                 {
730                         try {
731                                 return checked ((int)timeout.TotalMilliseconds);
732                         } catch (System.OverflowException) {
733                                 throw new ArgumentOutOfRangeException ("timeout");
734                         }
735                 }
736
737                 static void CheckForNullTasks (Task[] tasks)
738                 {
739                         foreach (var t in tasks)
740                                 if (t == null)
741                                         throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
742                 }
743                 #endregion
744                 
745                 #region Dispose
746                 public void Dispose ()
747                 {
748                         Dispose (true);
749                 }
750                 
751                 protected virtual void Dispose (bool disposing)
752                 {
753                         if (!IsCompleted)
754                                 throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
755
756                         // Set action to null so that the GC can collect the delegate and thus
757                         // any big object references that the user might have captured in a anonymous method
758                         if (disposing) {
759                                 invoker = null;
760                                 state = null;
761                                 if (cancellationRegistration != null)
762                                         cancellationRegistration.Value.Dispose ();
763                         }
764                 }
765                 #endregion
766                 
767 #if NET_4_5
768
769                 public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext)
770                 {
771                         return new ConfiguredTaskAwaitable (this, continueOnCapturedContext);
772                 }
773
774                 public Task ContinueWith (Action<Task, object> continuationAction, object state)
775                 {
776                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
777                 }
778
779                 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken)
780                 {
781                         return ContinueWith (continuationAction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
782                 }
783
784                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskContinuationOptions continuationOptions)
785                 {
786                         return ContinueWith (continuationAction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
787                 }
788
789                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskScheduler scheduler)
790                 {
791                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
792                 }
793
794                 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken,
795                                                                   TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
796                 {
797                         if (continuationAction == null)
798                                 throw new ArgumentNullException ("continuationAction");
799                         if (scheduler == null)
800                                 throw new ArgumentNullException ("scheduler");
801
802                         Task continuation = new Task (TaskActionInvoker.Create (continuationAction),
803                                                                                   state, cancellationToken,
804                                                                                   GetCreationOptions (continuationOptions),
805                                                                                   this);
806                         ContinueWithCore (continuation, continuationOptions, scheduler);
807
808                         return continuation;
809                 }
810
811                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
812                 {
813                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
814                 }
815
816                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
817                 {
818                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
819                 }
820
821                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
822                 {
823                         return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
824                 }
825
826                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
827                 {
828                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
829                 }
830
831                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
832                                                                                                         TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
833                 {
834                         if (continuationFunction == null)
835                                 throw new ArgumentNullException ("continuationFunction");
836                         if (scheduler == null)
837                                 throw new ArgumentNullException ("scheduler");
838
839                         var t = new Task<TResult> (TaskActionInvoker.Create (continuationFunction),
840                                                                                                  state,
841                                                                                                  cancellationToken,
842                                                                                                  GetCreationOptions (continuationOptions),
843                                                                                                  this);
844
845                         ContinueWithCore (t, continuationOptions, scheduler);
846
847                         return t;
848                 }
849
850                 public static Task<TResult> FromResult<TResult> (TResult result)
851                 {
852                         var tcs = new TaskCompletionSource<TResult> ();
853                         tcs.SetResult (result);
854                         return tcs.Task;
855                 }
856
857                 public TaskAwaiter GetAwaiter ()
858                 {
859                         return new TaskAwaiter (this);
860                 }
861
862                 public static Task Run (Action action)
863                 {
864                         return Run (action, CancellationToken.None);
865                 }
866
867                 public static Task Run (Action action, CancellationToken cancellationToken)
868                 {
869                         if (cancellationToken.IsCancellationRequested)
870                                 return TaskConstants.Canceled;
871
872                         var t = new Task (action, cancellationToken, TaskCreationOptions.DenyChildAttach);
873                         t.Start ();
874                         return t;
875                 }
876
877                 public static Task Run (Func<Task> function)
878                 {
879                         return Run (function, CancellationToken.None);
880                 }
881
882                 public static Task Run (Func<Task> function, CancellationToken cancellationToken)
883                 {
884                         if (cancellationToken.IsCancellationRequested)
885                                 return TaskConstants.Canceled;
886
887                         var t = new Task<Task> (function, cancellationToken);
888                         t.Start ();
889                         return t;
890                 }
891
892                 public static Task<TResult> Run<TResult> (Func<TResult> function)
893                 {
894                         return Run (function, CancellationToken.None);
895                 }
896
897                 public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
898                 {
899                         if (cancellationToken.IsCancellationRequested)
900                                 return TaskConstants<TResult>.Canceled;
901
902                         var t = new Task<TResult> (function, cancellationToken, TaskCreationOptions.DenyChildAttach);
903                         t.Start ();
904                         return t;
905                 }
906
907                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
908                 {
909                         return Run (function, CancellationToken.None);
910                 }
911
912                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
913                 {
914                         if (cancellationToken.IsCancellationRequested)
915                                 return TaskConstants<TResult>.Canceled;
916
917                         var t = Task<Task<TResult>>.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
918                         return GetTaskResult (t);
919                 }
920
921                 async static Task<TResult> GetTaskResult<TResult> (Task<Task<TResult>> task)
922                 {
923                         var r = await task.ConfigureAwait (false);
924                         return r.Result;
925                 }
926                 
927                 public static YieldAwaitable Yield ()
928                 {
929                         return new YieldAwaitable ();
930                 }
931 #endif
932
933                 #region Properties
934                 public static TaskFactory Factory {
935                         get {
936                                 return defaultFactory;
937                         }
938                 }
939                 
940                 public static int? CurrentId {
941                         get {
942                                 Task t = current;
943                                 return t == null ? (int?)null : t.Id;
944                         }
945                 }
946                 
947                 public AggregateException Exception {
948                         get {
949                                 exceptionObserved = true;
950                                 
951                                 return exception;       
952                         }
953                         internal set {
954                                 exception = value;
955                         }
956                 }
957                 
958                 public bool IsCanceled {
959                         get {
960                                 return status == TaskStatus.Canceled;
961                         }
962                 }
963
964                 public bool IsCompleted {
965                         get {
966                                 return status >= TaskStatus.RanToCompletion;
967                         }
968                 }
969                 
970                 public bool IsFaulted {
971                         get {
972                                 return status == TaskStatus.Faulted;
973                         }
974                 }
975
976                 public TaskCreationOptions CreationOptions {
977                         get {
978                                 return taskCreationOptions & MaxTaskCreationOptions;
979                         }
980                 }
981                 
982                 public TaskStatus Status {
983                         get {
984                                 return status;
985                         }
986                         internal set {
987                                 status = value;
988                                 Thread.MemoryBarrier ();
989                         }
990                 }
991
992                 public object AsyncState {
993                         get {
994                                 return state;
995                         }
996                 }
997                 
998                 bool IAsyncResult.CompletedSynchronously {
999                         get {
1000                                 return true;
1001                         }
1002                 }
1003
1004                 WaitHandle IAsyncResult.AsyncWaitHandle {
1005                         get {
1006                                 return null;
1007                         }
1008                 }
1009                 
1010                 public int Id {
1011                         get {
1012                                 return taskId;
1013                         }
1014                 }
1015
1016                 bool IsContinuation {
1017                         get {
1018                                 return (taskCreationOptions & TaskCreationOptionsContinuation) != 0;
1019                         }
1020                 }
1021
1022                 internal Task Parent {
1023                         get {
1024                                 return parent;
1025                         }
1026                 }
1027                 
1028                 internal string DisplayActionMethod {
1029                         get {
1030                                 Delegate d = invoker.Action;
1031                                 return d == null ? "<none>" : d.Method.ToString ();
1032                         }
1033                 }
1034                 
1035                 #endregion
1036         }
1037 }
1038 #endif