Fix a race in Task.WaitAny
[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 //
7 // Copyright (c) 2008 Jérémie "Garuma" Laval
8 // Copyright 2011 Xamarin Inc (http://www.xamarin.com).
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27 //
28 //
29
30 #if NET_4_0 || MOBILE
31
32 using System;
33 using System.Threading;
34 using System.Collections.Concurrent;
35 using System.Collections.Generic;
36 using System.Runtime.CompilerServices;
37
38 namespace System.Threading.Tasks
39 {
40         [System.Diagnostics.DebuggerDisplay ("Id = {Id}, Status = {Status}")]
41         [System.Diagnostics.DebuggerTypeProxy (typeof (TaskDebuggerView))]
42         public class Task : IDisposable, IAsyncResult
43         {
44                 // With this attribute each thread has its own value so that it's correct for our Schedule code
45                 // and for Parent property.
46                 [System.ThreadStatic]
47                 static Task         current;
48                 [System.ThreadStatic]
49                 static Action<Task> childWorkAdder;
50                 
51                 Task parent;
52                 
53                 static int          id = -1;
54                 static readonly TaskFactory defaultFactory = new TaskFactory ();
55                 
56                 CountdownEvent childTasks;
57                 
58                 int                 taskId;
59                 TaskCreationOptions taskCreationOptions;
60                 
61                 TaskScheduler       scheduler;
62
63                 ManualResetEventSlim schedWait = new ManualResetEventSlim (false);
64                 
65                 volatile AggregateException  exception;
66                 volatile bool                exceptionObserved;
67                 ConcurrentQueue<AggregateException> childExceptions;
68
69                 TaskStatus          status;
70                 
71                 Action<object> action;
72                 Action         simpleAction;
73                 object         state;
74                 AtomicBooleanValue executing;
75
76                 TaskCompletionQueue completed;
77                 // If this task is a continuation, this stuff gets filled
78                 CompletionSlot Slot;
79
80                 CancellationToken token;
81                 CancellationTokenRegistration? cancelation_registration;
82
83                 internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
84
85                 const TaskCreationOptions MaxTaskCreationOptions =
86 #if NET_4_5
87                         TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler |
88 #endif
89                         TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent;
90
91                 public Task (Action action) : this (action, TaskCreationOptions.None)
92                 {
93                         
94                 }
95                 
96                 public Task (Action action, TaskCreationOptions creationOptions) : this (action, CancellationToken.None, creationOptions)
97                 {
98                         
99                 }
100                 
101                 public Task (Action action, CancellationToken cancellationToken) : this (action, cancellationToken, TaskCreationOptions.None)
102                 {
103                         
104                 }
105                 
106                 public Task (Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
107                         : this (null, 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                         this.simpleAction = action;
114                 }
115                 
116                 public Task (Action<object> action, object state) : 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 (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 (Action<object> action,
140                                object state,
141                                CancellationToken cancellationToken,
142                                TaskCreationOptions creationOptions,
143                                Task parent)
144                 {
145                         this.taskCreationOptions = creationOptions;
146                         this.action              = action;
147                         this.state               = state;
148                         this.taskId              = Interlocked.Increment (ref id);
149                         this.status              = cancellationToken.IsCancellationRequested ? TaskStatus.Canceled : TaskStatus.Created;
150                         this.token               = cancellationToken;
151                         this.parent              = parent;
152
153                         // Process taskCreationOptions
154                         if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null)
155                                 parent.AddChild ();
156
157                         if (token.CanBeCanceled) {
158                                 cancelation_registration = token.Register (l => ((Task) l).CancelReal (), this);
159                         }
160                 }
161
162                 ~Task ()
163                 {
164                         if (exception != null && !exceptionObserved)
165                                 throw exception;
166                 }
167
168                 bool CheckTaskOptions (TaskCreationOptions opt, TaskCreationOptions member)
169                 {
170                         return (opt & member) == member;
171                 }
172
173                 #region Start
174                 public void Start ()
175                 {
176                         Start (TaskScheduler.Current);
177                 }
178                 
179                 public void Start (TaskScheduler scheduler)
180                 {
181                         if (scheduler == null)
182                                 throw new ArgumentNullException ("scheduler");
183
184                         if (status >= TaskStatus.WaitingToRun)
185                                 throw new InvalidOperationException ("The Task is not in a valid state to be started.");
186
187                         if (Slot.Initialized)
188                                 throw new InvalidOperationException ("Start may not be called on a continuation task");
189
190                         SetupScheduler (scheduler);
191                         Schedule ();
192                 }
193
194                 internal void SetupScheduler (TaskScheduler scheduler)
195                 {
196                         this.scheduler = scheduler;
197                         status = TaskStatus.WaitingForActivation;
198                         schedWait.Set ();
199                 }
200                 
201                 public void RunSynchronously ()
202                 {
203                         RunSynchronously (TaskScheduler.Current);
204                 }
205                 
206                 public void RunSynchronously (TaskScheduler scheduler)
207                 {
208                         if (scheduler == null)
209                                 throw new ArgumentNullException ("scheduler");
210
211                         if (Status > TaskStatus.WaitingForActivation)
212                                 throw new InvalidOperationException ("The task is not in a valid state to be started");
213
214                         SetupScheduler (scheduler);
215                         var saveStatus = status;
216                         status = TaskStatus.WaitingToRun;
217
218                         try {
219                                 if (scheduler.RunInline (this))
220                                         return;
221                         } catch (Exception inner) {
222                                 throw new TaskSchedulerException (inner);
223                         }
224
225                         status = saveStatus;
226                         Start (scheduler);
227                         Wait ();
228                 }
229                 #endregion
230                 
231                 #region ContinueWith
232                 public Task ContinueWith (Action<Task> continuationAction)
233                 {
234                         return ContinueWith (continuationAction, TaskContinuationOptions.None);
235                 }
236                 
237                 public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
238                 {
239                         return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
240                 }
241                 
242                 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
243                 {
244                         return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
245                 }
246                 
247                 public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
248                 {
249                         return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
250                 }
251                 
252                 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
253                 {
254                         if (continuationAction == null)
255                                 throw new ArgumentNullException ("continuationAction");
256                         if (scheduler == null)
257                                 throw new ArgumentNullException ("scheduler");
258
259                         Task continuation = new Task (l => continuationAction ((Task)l),
260                                                       this,
261                                                       cancellationToken,
262                                                       GetCreationOptions (continuationOptions),
263                                                       this);
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                         Task<TResult> t = new Task<TResult> ((o) => continuationFunction ((Task)o),
298                                                              this,
299                                                              cancellationToken,
300                                                              GetCreationOptions (continuationOptions),
301                                                              this);
302                         
303                         ContinueWithCore (t, continuationOptions, scheduler);
304                         
305                         return t;
306                 }
307         
308                 internal void ContinueWithCore (Task continuation, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
309                 {
310                         ContinueWithCore (continuation, continuationOptions, scheduler, null);
311                 }
312                 
313                 internal void ContinueWithCore (Task continuation, TaskContinuationOptions kind,
314                                                 TaskScheduler scheduler, Func<bool> predicate)
315                 {
316                         // Already set the scheduler so that user can call Wait and that sort of stuff
317                         continuation.scheduler = scheduler;
318                         continuation.schedWait.Set ();
319                         continuation.status = TaskStatus.WaitingForActivation;
320                         continuation.Slot = new CompletionSlot (kind, predicate);
321
322                         if (IsCompleted) {
323                                 CompletionTaskExecutor (continuation);
324                                 return;
325                         }
326                         
327                         completed.Add (continuation);
328                         
329                         // Retry in case completion was achieved but event adding was too late
330                         if (IsCompleted)
331                                 CompletionTaskExecutor (continuation);
332                 }
333
334                 
335                 bool ContinuationStatusCheck (TaskContinuationOptions kind)
336                 {
337                         if (kind == TaskContinuationOptions.None)
338                                 return true;
339                         
340                         int kindCode = (int)kind;
341                         
342                         if (kindCode >= ((int)TaskContinuationOptions.NotOnRanToCompletion)) {
343                                 // Remove other options
344                                 kind &= ~(TaskContinuationOptions.PreferFairness
345                                           | TaskContinuationOptions.LongRunning
346                                           | TaskContinuationOptions.AttachedToParent
347                                           | TaskContinuationOptions.ExecuteSynchronously);
348
349                                 if (status == TaskStatus.Canceled) {
350                                         if (kind == TaskContinuationOptions.NotOnCanceled)
351                                                 return false;
352                                         if (kind == TaskContinuationOptions.OnlyOnFaulted)
353                                                 return false;
354                                         if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
355                                                 return false;
356                                 } else if (status == TaskStatus.Faulted) {
357                                         if (kind == TaskContinuationOptions.NotOnFaulted)
358                                                 return false;
359                                         if (kind == TaskContinuationOptions.OnlyOnCanceled)
360                                                 return false;
361                                         if (kind == TaskContinuationOptions.OnlyOnRanToCompletion)
362                                                 return false;
363                                 } else if (status == TaskStatus.RanToCompletion) {
364                                         if (kind == TaskContinuationOptions.NotOnRanToCompletion)
365                                                 return false;
366                                         if (kind == TaskContinuationOptions.OnlyOnFaulted)
367                                                 return false;
368                                         if (kind == TaskContinuationOptions.OnlyOnCanceled)
369                                                 return false;
370                                 }
371                         }
372                         
373                         return true;
374                 }
375                         
376                 static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
377                 {
378                         TaskCreationOptions options = TaskCreationOptions.None;
379                         if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
380                                 options |= TaskCreationOptions.AttachedToParent;
381                         if ((kind & TaskContinuationOptions.PreferFairness) > 0)
382                                 options |= TaskCreationOptions.PreferFairness;
383                         if ((kind & TaskContinuationOptions.LongRunning) > 0)
384                                 options |= TaskCreationOptions.LongRunning;
385                         
386                         return options;
387                 }
388                 #endregion
389                 
390                 #region Internal and protected thingies
391                 internal void Schedule ()
392                 {
393                         status = TaskStatus.WaitingToRun;
394                         
395                         // If worker is null it means it is a local one, revert to the old behavior
396                         // If TaskScheduler.Current is not being used, the scheduler was explicitly provided, so we must use that
397                         if (scheduler != TaskScheduler.Current || childWorkAdder == null || CheckTaskOptions (taskCreationOptions, TaskCreationOptions.PreferFairness)) {
398                                 scheduler.QueueTask (this);
399                         } else {
400                                 /* Like the semantic of the ABP paper describe it, we add ourselves to the bottom 
401                                  * of our Parent Task's ThreadWorker deque. It's ok to do that since we are in
402                                  * the correct Thread during the creation
403                                  */
404                                 childWorkAdder (this);
405                         }
406                 }
407                 
408                 void ThreadStart ()
409                 {
410                         /* Allow scheduler to break fairness of deque ordering without
411                          * breaking its semantic (the task can be executed twice but the
412                          * second time it will return immediately
413                          */
414                         if (!executing.TryRelaxedSet ())
415                                 return;
416
417                         current = this;
418                         TaskScheduler.Current = scheduler;
419                         
420                         if (!token.IsCancellationRequested) {
421                                 
422                                 status = TaskStatus.Running;
423                                 
424                                 try {
425                                         InnerInvoke ();
426                                 } catch (OperationCanceledException oce) {
427                                         if (token != CancellationToken.None && oce.CancellationToken == token)
428                                                 CancelReal ();
429                                         else
430                                                 HandleGenericException (oce);
431                                 } catch (Exception e) {
432                                         HandleGenericException (e);
433                                 }
434                         } else {
435                                 CancelReal ();
436                         }
437                         
438                         Finish ();
439                 }
440                 
441                 internal void Execute (Action<Task> childAdder)
442                 {
443                         childWorkAdder = childAdder;
444                         ThreadStart ();
445                 }
446                 
447                 internal void AddChild ()
448                 {
449                         if (childTasks == null)
450                                 Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
451                         childTasks.AddCount ();
452                 }
453
454                 internal void ChildCompleted (AggregateException childEx)
455                 {
456                         if (childEx != null) {
457                                 if (childExceptions == null)
458                                         Interlocked.CompareExchange (ref childExceptions, new ConcurrentQueue<AggregateException> (), null);
459                                 childExceptions.Enqueue (childEx);
460                         }
461
462                         if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
463                                 status = TaskStatus.RanToCompletion;
464                                 ProcessChildExceptions ();
465                                 ProcessCompleteDelegates ();
466                         }
467                 }
468
469                 internal virtual void InnerInvoke ()
470                 {
471                         if (action == null && simpleAction != null)
472                                 simpleAction ();
473                         else if (action != null)
474                                 action (state);
475                         // Set action to null so that the GC can collect the delegate and thus
476                         // any big object references that the user might have captured in an anonymous method
477                         action = null;
478                         simpleAction = null;
479                         state = null;
480                 }
481                 
482                 internal void Finish ()
483                 {
484                         // If there was children created and they all finished, we set the countdown
485                         if (childTasks != null)
486                                 childTasks.Signal ();
487                         
488                         // Don't override Canceled or Faulted
489                         if (status == TaskStatus.Running) {
490                                 if (childTasks == null || childTasks.IsSet)
491                                         status = TaskStatus.RanToCompletion;
492                                 else
493                                         status = TaskStatus.WaitingForChildrenToComplete;
494                         }
495                 
496                         if (status != TaskStatus.WaitingForChildrenToComplete)
497                                 ProcessCompleteDelegates ();
498
499                         // Reset the current thingies
500                         current = null;
501                         TaskScheduler.Current = null;
502
503                         if (cancelation_registration.HasValue)
504                                 cancelation_registration.Value.Dispose ();
505                         
506                         // Tell parent that we are finished
507                         if (CheckTaskOptions (taskCreationOptions, TaskCreationOptions.AttachedToParent) && parent != null) {
508                                 parent.ChildCompleted (this.Exception);
509                         }
510                 }
511
512                 void ProcessCompleteDelegates ()
513                 {
514                         if (!completed.HasElements)
515                                 return;
516
517                         object value;
518                         while (completed.TryGetNext (out value)) {
519                                 var t = value as Task;
520                                 if (t != null) {
521                                         CompletionTaskExecutor (t);
522                                         continue;
523                                 }
524
525                                 var mre = value as ManualResetEventSlim;
526                                 if (mre != null) {
527                                         mre.Set ();
528                                         continue;
529                                 }
530
531                                 throw new NotImplementedException ("Unknown completition type " + t.GetType ());
532                         }
533                 }
534
535                 void CompletionTaskExecutor (Task cont)
536                 {
537                         if (cont.Slot.Predicate != null && !cont.Slot.Predicate ())
538                                 return;
539
540                         if (!cont.Slot.Launched.TryRelaxedSet ())
541                                 return;
542
543                         if (!ContinuationStatusCheck (cont.Slot.Kind)) {
544                                 cont.CancelReal ();
545                                 cont.Dispose ();
546
547                                 return;
548                         }
549                         
550                         if ((cont.Slot.Kind & TaskContinuationOptions.ExecuteSynchronously) != 0)
551                                 cont.RunSynchronously (cont.scheduler);
552                         else
553                                 cont.Schedule ();
554                 }
555
556                 void ProcessChildExceptions ()
557                 {
558                         if (childExceptions == null)
559                                 return;
560
561                         if (exception == null)
562                                 exception = new AggregateException ();
563
564                         AggregateException childEx;
565                         while (childExceptions.TryDequeue (out childEx))
566                                 exception.AddChildException (childEx);
567                 }
568                 #endregion
569                 
570                 #region Cancel and Wait related method
571                 
572                 internal void CancelReal ()
573                 {
574                         status = TaskStatus.Canceled;
575                         ProcessCompleteDelegates ();
576                 }
577
578                 internal void HandleGenericException (Exception e)
579                 {
580                         HandleGenericException (new AggregateException (e));
581                 }
582
583                 internal void HandleGenericException (AggregateException e)
584                 {
585                         exception = e;
586                         Thread.MemoryBarrier ();
587                         status = TaskStatus.Faulted;
588                         if (scheduler != null && scheduler.FireUnobservedEvent (exception).Observed)
589                                 exceptionObserved = true;
590                 }
591                 
592                 public void Wait ()
593                 {
594                         Wait (Timeout.Infinite, CancellationToken.None);
595                 }
596
597                 public void Wait (CancellationToken cancellationToken)
598                 {
599                         Wait (Timeout.Infinite, cancellationToken);
600                 }
601                 
602                 public bool Wait (TimeSpan timeout)
603                 {
604                         return Wait (CheckTimeout (timeout), CancellationToken.None);
605                 }
606                 
607                 public bool Wait (int millisecondsTimeout)
608                 {
609                         return Wait (millisecondsTimeout, CancellationToken.None);
610                 }
611
612                 public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
613                 {
614                         if (millisecondsTimeout < -1)
615                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
616
617                         bool result = IsCompleted;
618                         if (!result) {
619                                 CancellationTokenRegistration? registration = null;
620                                 var completed_event =  new ManualResetEventSlim (false);
621
622                                 try {
623                                         if (cancellationToken.CanBeCanceled) {
624                                                 registration = cancellationToken.Register (completed_event.Set);
625                                         }
626
627                                         completed.Add (completed_event);
628
629                                         // Task could complete while we were setting things up
630                                         if (IsCompleted) {
631                                                 // Don't bother removing completed_event, GC can handle it
632                                                 result = true;
633                                         } else {
634                                                 result = completed_event.Wait (millisecondsTimeout);
635                                         }
636                                 } finally {
637                                         if (registration.HasValue)
638                                                 registration.Value.Dispose ();
639
640                                         // Try to remove completition event when timeout expired
641                                         if (!result)
642                                                 completed.TryRemove (completed_event);
643
644                                         completed_event.Dispose ();
645                                 }
646                         }
647
648                         if (IsCanceled)
649                                 throw new AggregateException (new TaskCanceledException (this));
650
651                         if (exception != null)
652                                 throw exception;
653
654                         return result;
655                 }
656                 
657                 public static void WaitAll (params Task[] tasks)
658                 {
659                         WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
660                 }
661
662                 public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
663                 {
664                         WaitAll (tasks, Timeout.Infinite, cancellationToken);
665                 }
666                 
667                 public static bool WaitAll (Task[] tasks, TimeSpan timeout)
668                 {
669                         return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
670                 }
671                 
672                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
673                 {
674                         return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
675                 }
676                 
677                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
678                 {
679                         if (tasks == null)
680                                 throw new ArgumentNullException ("tasks");
681
682                         if (millisecondsTimeout < -1)
683                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
684                         
685                         bool result = true;
686                         bool simple_run = millisecondsTimeout == Timeout.Infinite || tasks.Length == 1;
687                         List<Exception> exceptions = null;
688
689                         foreach (var t in tasks) {
690                                 if (t == null)
691                                         throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
692
693                                 if (simple_run) {
694                                         try {
695                                                 result &= t.Wait (millisecondsTimeout, cancellationToken);
696                                         } catch (AggregateException e) {
697                                                 if (exceptions == null)
698                                                         exceptions = new List<Exception> ();
699
700                                                 exceptions.AddRange (e.InnerExceptions);
701                                         }
702                                 }
703                         }
704
705                         // FIXME: Wrong implementation, millisecondsTimeout is total time not time per task
706                         if (!simple_run) {
707                                 foreach (var t in tasks) {
708                                         try {
709                                                 result &= t.Wait (millisecondsTimeout, cancellationToken);
710                                         } catch (AggregateException e) {
711                                                 if (exceptions == null)
712                                                         exceptions = new List<Exception> ();
713
714                                                 exceptions.AddRange (e.InnerExceptions);
715                                         }
716                                 }
717                         }
718
719                         if (exceptions != null)
720                                 throw new AggregateException (exceptions);
721
722                         return result;
723                 }
724                 
725                 public static int WaitAny (params Task[] tasks)
726                 {
727                         return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
728                 }
729
730                 public static int WaitAny (Task[] tasks, TimeSpan timeout)
731                 {
732                         return WaitAny (tasks, CheckTimeout (timeout));
733                 }
734                 
735                 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
736                 {
737                         return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
738                 }
739
740                 public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
741                 {
742                         return WaitAny (tasks, Timeout.Infinite, cancellationToken);
743                 }
744
745                 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
746                 {
747                         if (tasks == null)
748                                 throw new ArgumentNullException ("tasks");
749
750                         int first_finished = -1;
751                         for (int i = 0; i < tasks.Length; ++i) {
752                                 var t = tasks [i];
753
754                                 if (t == null)
755                                         throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
756
757                                 if (first_finished < 0 && t.IsCompleted)
758                                         first_finished = i;
759                         }
760
761                         if (first_finished >= 0 || tasks.Length == 0)
762                                 return first_finished;
763
764                         using (var completed_event = new ManualResetEventSlim (false)) {
765
766                                 for (int i = 0; i < tasks.Length; ++i) {
767                                         var t = tasks[i];
768                                         t.completed.Add (completed_event);
769
770                                         // completed.Add could happen while task was finishing and 
771                                         // we could miss continuation execution where completed_event is set
772                                         if (t.IsCompleted) {
773                                                 first_finished = i;
774                                                 break;
775                                         }
776                                 }
777
778                                 if (first_finished < 0) {
779                                         completed_event.Wait (millisecondsTimeout, cancellationToken);
780                                 }
781
782                                 for (int i = 0; i < tasks.Length; ++i) {
783                                         var t = tasks[i];
784                                         if (first_finished < 0 && t.IsCompleted)
785                                                 first_finished = i;
786
787                                         t.completed.TryRemove (completed_event);
788                                 }
789                         }
790
791                         return first_finished;
792                 }
793
794                 static int CheckTimeout (TimeSpan timeout)
795                 {
796                         try {
797                                 return checked ((int)timeout.TotalMilliseconds);
798                         } catch (System.OverflowException) {
799                                 throw new ArgumentOutOfRangeException ("timeout");
800                         }
801                 }
802
803                 static int ComputeTimeout (int millisecondsTimeout, Watch watch)
804                 {
805                         return millisecondsTimeout == -1 ? -1 : (int)Math.Max (watch.ElapsedMilliseconds - millisecondsTimeout, 1);
806                 }
807
808                 #endregion
809                 
810                 #region Dispose
811                 public void Dispose ()
812                 {
813                         Dispose (true);
814                 }
815                 
816                 protected virtual void Dispose (bool disposing)
817                 {
818                         if (!IsCompleted)
819                                 throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
820
821                         // Set action to null so that the GC can collect the delegate and thus
822                         // any big object references that the user might have captured in a anonymous method
823                         if (disposing) {
824                                 action = null;
825                                 state = null;
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 (l => continuationAction (this, l), state,
866                                                                                   cancellationToken,
867                                                                                   GetCreationOptions (continuationOptions),
868                                                                                   this);
869                         ContinueWithCore (continuation, continuationOptions, scheduler);
870
871                         return continuation;
872                 }
873
874                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
875                 {
876                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
877                 }
878
879                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
880                 {
881                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
882                 }
883
884                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
885                 {
886                         return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
887                 }
888
889                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
890                 {
891                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
892                 }
893
894                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
895                                                                                                         TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
896                 {
897                         if (continuationFunction == null)
898                                 throw new ArgumentNullException ("continuationFunction");
899                         if (scheduler == null)
900                                 throw new ArgumentNullException ("scheduler");
901
902                         var t = new Task<TResult> (l => continuationFunction (this, l),
903                                                                                                  state,
904                                                                                                  cancellationToken,
905                                                                                                  GetCreationOptions (continuationOptions),
906                                                                                                  this);
907
908                         ContinueWithCore (t, continuationOptions, scheduler);
909
910                         return t;
911                 }
912
913                 public static Task<TResult> FromResult<TResult> (TResult result)
914                 {
915                         var tcs = new TaskCompletionSource<TResult> ();
916                         tcs.SetResult (result);
917                         return tcs.Task;
918                 }
919
920                 public TaskAwaiter GetAwaiter ()
921                 {
922                         return new TaskAwaiter (this);
923                 }
924
925                 public static Task Run (Action action)
926                 {
927                         return Run (action, CancellationToken.None);
928                 }
929
930                 public static Task Run (Action action, CancellationToken cancellationToken)
931                 {
932                         if (cancellationToken.IsCancellationRequested)
933                                 return TaskConstants.Canceled;
934
935                         var t = new Task (action, cancellationToken, TaskCreationOptions.DenyChildAttach);
936                         t.Start ();
937                         return t;
938                 }
939
940                 public static Task Run (Func<Task> function)
941                 {
942                         return Run (function, CancellationToken.None);
943                 }
944
945                 public static Task Run (Func<Task> function, CancellationToken cancellationToken)
946                 {
947                         if (cancellationToken.IsCancellationRequested)
948                                 return TaskConstants.Canceled;
949
950                         var t = new Task<Task> (function, cancellationToken);
951                         t.Start ();
952                         return t;
953                 }
954
955                 public static Task<TResult> Run<TResult> (Func<TResult> function)
956                 {
957                         return Run (function, CancellationToken.None);
958                 }
959
960                 public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
961                 {
962                         if (cancellationToken.IsCancellationRequested)
963                                 return TaskConstants<TResult>.Canceled;
964
965                         var t = new Task<TResult> (function, cancellationToken, TaskCreationOptions.DenyChildAttach);
966                         t.Start ();
967                         return t;
968                 }
969
970                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
971                 {
972                         return Run (function, CancellationToken.None);
973                 }
974
975                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
976                 {
977                         if (cancellationToken.IsCancellationRequested)
978                                 return TaskConstants<TResult>.Canceled;
979
980                         var t = Task<Task<TResult>>.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
981                         return GetTaskResult (t);
982                 }
983
984                 async static Task<TResult> GetTaskResult<TResult> (Task<Task<TResult>> task)
985                 {
986                         var r = await task.ConfigureAwait (false);
987                         return r.Result;
988                 }
989                 
990                 public static YieldAwaitable Yield ()
991                 {
992                         return new YieldAwaitable ();
993                 }
994 #endif
995
996                 #region Properties
997                 public static TaskFactory Factory {
998                         get {
999                                 return defaultFactory;
1000                         }
1001                 }
1002                 
1003                 public static int? CurrentId {
1004                         get {
1005                                 Task t = current;
1006                                 return t == null ? (int?)null : t.Id;
1007                         }
1008                 }
1009                 
1010                 public AggregateException Exception {
1011                         get {
1012                                 exceptionObserved = true;
1013                                 
1014                                 return exception;       
1015                         }
1016                         internal set {
1017                                 exception = value;
1018                         }
1019                 }
1020                 
1021                 public bool IsCanceled {
1022                         get {
1023                                 return status == TaskStatus.Canceled;
1024                         }
1025                 }
1026
1027                 public bool IsCompleted {
1028                         get {
1029                                 return status == TaskStatus.RanToCompletion ||
1030                                         status == TaskStatus.Canceled || status == TaskStatus.Faulted;
1031                         }
1032                 }
1033                 
1034                 public bool IsFaulted {
1035                         get {
1036                                 return status == TaskStatus.Faulted;
1037                         }
1038                 }
1039
1040                 public TaskCreationOptions CreationOptions {
1041                         get {
1042                                 return taskCreationOptions;
1043                         }
1044                 }
1045                 
1046                 public TaskStatus Status {
1047                         get {
1048                                 return status;
1049                         }
1050                         internal set {
1051                                 status = value;
1052                         }
1053                 }
1054
1055                 public object AsyncState {
1056                         get {
1057                                 return state;
1058                         }
1059                 }
1060                 
1061                 bool IAsyncResult.CompletedSynchronously {
1062                         get {
1063                                 return true;
1064                         }
1065                 }
1066
1067                 WaitHandle IAsyncResult.AsyncWaitHandle {
1068                         get {
1069                                 return null;
1070                         }
1071                 }
1072                 
1073                 public int Id {
1074                         get {
1075                                 return taskId;
1076                         }
1077                 }
1078
1079                 internal Task Parent {
1080                         get {
1081                                 return parent;
1082                         }
1083                 }
1084                 
1085                 internal string DisplayActionMethod {
1086                         get {
1087                                 Delegate d = simpleAction ?? (Delegate) action;
1088                                 return d == null ? "<none>" : d.Method.ToString ();
1089                         }
1090                 }
1091                 
1092                 #endregion
1093         }
1094 }
1095 #endif