e53cc77dd458be7fdd319f8d095d89ebb9dfe616
[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                 CompletionContainer 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                                 CompletionExecutor (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                                 CompletionExecutor (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 CompletionExecutor (Task cont)
513                 {
514                         if (cont.Slot.Predicate != null && !cont.Slot.Predicate ())
515                                 return;
516
517                         if (!cont.Slot.Launched.TryRelaxedSet ())
518                                 return;
519
520                         if (!ContinuationStatusCheck (cont.Slot.Kind)) {
521                                 cont.CancelReal ();
522                                 cont.Dispose ();
523
524                                 return;
525                         }
526                         
527                         if ((cont.Slot.Kind & TaskContinuationOptions.ExecuteSynchronously) != 0)
528                                 cont.RunSynchronously (cont.scheduler);
529                         else
530                                 cont.Schedule ();
531                 }
532
533                 void ProcessCompleteDelegates ()
534                 {
535                         if (!completed.HasElements)
536                                 return;
537
538                         Task continuation;
539                         while (completed.TryGetNextCompletion (out continuation))
540                                 CompletionExecutor (continuation);
541                 }
542
543                 void ProcessChildExceptions ()
544                 {
545                         if (childExceptions == null)
546                                 return;
547
548                         if (exception == null)
549                                 exception = new AggregateException ();
550
551                         AggregateException childEx;
552                         while (childExceptions.TryDequeue (out childEx))
553                                 exception.AddChildException (childEx);
554                 }
555                 #endregion
556                 
557                 #region Cancel and Wait related method
558                 
559                 internal void CancelReal ()
560                 {
561                         status = TaskStatus.Canceled;
562                         ProcessCompleteDelegates ();
563                 }
564
565                 internal void HandleGenericException (Exception e)
566                 {
567                         HandleGenericException (new AggregateException (e));
568                 }
569
570                 internal void HandleGenericException (AggregateException e)
571                 {
572                         exception = e;
573                         Thread.MemoryBarrier ();
574                         status = TaskStatus.Faulted;
575                         if (scheduler != null && scheduler.FireUnobservedEvent (exception).Observed)
576                                 exceptionObserved = true;
577                 }
578                 
579                 public void Wait ()
580                 {
581                         Wait (Timeout.Infinite, CancellationToken.None);
582                 }
583
584                 public void Wait (CancellationToken cancellationToken)
585                 {
586                         Wait (Timeout.Infinite, cancellationToken);
587                 }
588                 
589                 public bool Wait (TimeSpan timeout)
590                 {
591                         return Wait (CheckTimeout (timeout), CancellationToken.None);
592                 }
593                 
594                 public bool Wait (int millisecondsTimeout)
595                 {
596                         return Wait (millisecondsTimeout, CancellationToken.None);
597                 }
598
599                 public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
600                 {
601                         if (millisecondsTimeout < -1)
602                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
603
604                         bool result = IsCompleted;
605                         if (!result) {
606                                 if (scheduler == null) {
607                                         Watch watch = Watch.StartNew ();
608
609                                         schedWait.Wait (millisecondsTimeout, cancellationToken);
610                                         millisecondsTimeout = ComputeTimeout (millisecondsTimeout, watch);
611                                 }
612
613                                 var wait_event = new ManualResetEventSlim (false);
614                                 CancellationTokenRegistration? registration = null;
615
616                                 try {
617                                         if (cancellationToken.CanBeCanceled) {
618                                                 registration = cancellationToken.Register (wait_event.Set);
619                                         }
620
621                                         // FIXME: The implementation is wrong and slow
622                                         // It adds a continuation to the task which is then
623                                         // returned to parent causing all sort of problems when
624                                         // timeout is reached before task is finished
625                                         result = !scheduler.ParticipateUntil (this, wait_event, millisecondsTimeout);
626                                 } finally {
627                                         if (registration.HasValue)
628                                                 registration.Value.Dispose ();
629                                 }
630                         }
631
632                         if (IsCanceled)
633                                 throw new AggregateException (new TaskCanceledException (this));
634
635                         if (exception != null)
636                                 throw exception;
637
638                         return result;
639                 }
640                 
641                 public static void WaitAll (params Task[] tasks)
642                 {
643                         WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
644                 }
645
646                 public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
647                 {
648                         WaitAll (tasks, Timeout.Infinite, cancellationToken);
649                 }
650                 
651                 public static bool WaitAll (Task[] tasks, TimeSpan timeout)
652                 {
653                         return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
654                 }
655                 
656                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
657                 {
658                         return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
659                 }
660                 
661                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
662                 {
663                         if (tasks == null)
664                                 throw new ArgumentNullException ("tasks");
665                         
666                         bool result = true;
667                         bool simple_run = millisecondsTimeout == Timeout.Infinite || tasks.Length == 1;
668                         List<Exception> exceptions = null;
669
670                         foreach (var t in tasks) {
671                                 if (t == null)
672                                         throw new ArgumentNullException ("tasks", "the tasks argument contains a null element");
673
674                                 if (simple_run) {
675                                         try {
676                                                 result &= t.Wait (millisecondsTimeout, cancellationToken);
677                                         } catch (AggregateException e) {
678                                                 if (exceptions == null)
679                                                         exceptions = new List<Exception> ();
680
681                                                 exceptions.AddRange (e.InnerExceptions);
682                                         }
683                                 }
684                         }
685
686                         // FIXME: Wrong implementation, millisecondsTimeout is total time not time per task
687                         if (!simple_run) {
688                                 foreach (var t in tasks) {
689                                         try {
690                                                 result &= t.Wait (millisecondsTimeout, cancellationToken);
691                                         } catch (AggregateException e) {
692                                                 if (exceptions == null)
693                                                         exceptions = new List<Exception> ();
694
695                                                 exceptions.AddRange (e.InnerExceptions);
696                                         }
697                                 }
698                         }
699
700                         if (exceptions != null)
701                                 throw new AggregateException (exceptions);
702
703                         return result;
704                 }
705                 
706                 public static int WaitAny (params Task[] tasks)
707                 {
708                         return WaitAny (tasks, -1, CancellationToken.None);
709                 }
710
711                 public static int WaitAny (Task[] tasks, TimeSpan timeout)
712                 {
713                         return WaitAny (tasks, CheckTimeout (timeout));
714                 }
715                 
716                 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
717                 {
718                         if (millisecondsTimeout < -1)
719                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
720
721                         if (millisecondsTimeout == -1)
722                                 return WaitAny (tasks);
723
724                         return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
725                 }
726
727                 public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
728                 {
729                         return WaitAny (tasks, -1, cancellationToken);
730                 }
731
732                 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
733                 {
734                         if (tasks == null)
735                                 throw new ArgumentNullException ("tasks");
736                         if (tasks.Length == 0)
737                                 throw new ArgumentException ("tasks is empty", "tasks");
738                         if (tasks.Length == 1) {
739                                 tasks[0].Wait (millisecondsTimeout, cancellationToken);
740                                 return 0;
741                         }
742                         
743                         int numFinished = 0;
744                         int indexFirstFinished = -1;
745                         int index = 0;
746                         TaskScheduler sched = null;
747                         Task task = null;
748                         Watch watch = Watch.StartNew ();
749                         ManualResetEventSlim predicateEvt = new ManualResetEventSlim (false);
750
751                         foreach (Task t in tasks) {
752                                 int indexResult = index++;
753                                 t.ContinueWith (delegate {
754                                         if (numFinished >= 1)
755                                                 return;
756                                         int result = Interlocked.Increment (ref numFinished);
757
758                                         // Check if we are the first to have finished
759                                         if (result == 1)
760                                                 indexFirstFinished = indexResult;
761
762                                         // Stop waiting
763                                         predicateEvt.Set ();
764                                 }, TaskContinuationOptions.ExecuteSynchronously);
765
766                                 if (sched == null && t.scheduler != null) {
767                                         task = t;
768                                         sched = t.scheduler;
769                                 }
770                         }
771
772                         // If none of task have a scheduler we are forced to wait for at least one to start
773                         if (sched == null) {
774                                 var handles = Array.ConvertAll (tasks, t => t.schedWait.WaitHandle);
775                                 int shandle = -1;
776                                 if ((shandle = WaitHandle.WaitAny (handles, millisecondsTimeout)) == WaitHandle.WaitTimeout)
777                                         return -1;
778                                 sched = tasks[shandle].scheduler;
779                                 task = tasks[shandle];
780                                 millisecondsTimeout = ComputeTimeout (millisecondsTimeout, watch);
781                         }
782
783                         // One task already finished
784                         if (indexFirstFinished != -1)
785                                 return indexFirstFinished;
786
787                         if (cancellationToken != CancellationToken.None) {
788                                 cancellationToken.Register (predicateEvt.Set);
789                                 cancellationToken.ThrowIfCancellationRequested ();
790                         }
791
792                         sched.ParticipateUntil (task, predicateEvt, millisecondsTimeout);
793
794                         // Index update is still not done
795                         if (indexFirstFinished == -1) {
796                                 SpinWait wait = new SpinWait ();
797                                 while (indexFirstFinished == -1)
798                                         wait.SpinOnce ();
799                         }
800
801                         return indexFirstFinished;
802                 }
803
804                 static int CheckTimeout (TimeSpan timeout)
805                 {
806                         try {
807                                 return checked ((int)timeout.TotalMilliseconds);
808                         } catch (System.OverflowException) {
809                                 throw new ArgumentOutOfRangeException ("timeout");
810                         }
811                 }
812
813                 static int ComputeTimeout (int millisecondsTimeout, Watch watch)
814                 {
815                         return millisecondsTimeout == -1 ? -1 : (int)Math.Max (watch.ElapsedMilliseconds - millisecondsTimeout, 1);
816                 }
817
818                 #endregion
819                 
820                 #region Dispose
821                 public void Dispose ()
822                 {
823                         Dispose (true);
824                 }
825                 
826                 protected virtual void Dispose (bool disposing)
827                 {
828                         if (!IsCompleted)
829                                 throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
830
831                         // Set action to null so that the GC can collect the delegate and thus
832                         // any big object references that the user might have captured in a anonymous method
833                         if (disposing) {
834                                 action = null;
835                                 state = null;
836                         }
837                 }
838                 #endregion
839                 
840 #if NET_4_5
841
842                 public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext)
843                 {
844                         return new ConfiguredTaskAwaitable (this, continueOnCapturedContext);
845                 }
846
847                 public Task ContinueWith (Action<Task, object> continuationAction, object state)
848                 {
849                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
850                 }
851
852                 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken)
853                 {
854                         return ContinueWith (continuationAction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
855                 }
856
857                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskContinuationOptions continuationOptions)
858                 {
859                         return ContinueWith (continuationAction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
860                 }
861
862                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskScheduler scheduler)
863                 {
864                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
865                 }
866
867                 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken,
868                                                                   TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
869                 {
870                         if (continuationAction == null)
871                                 throw new ArgumentNullException ("continuationAction");
872                         if (scheduler == null)
873                                 throw new ArgumentNullException ("scheduler");
874
875                         Task continuation = new Task (l => continuationAction (this, l), state,
876                                                                                   cancellationToken,
877                                                                                   GetCreationOptions (continuationOptions),
878                                                                                   this);
879                         ContinueWithCore (continuation, continuationOptions, scheduler);
880
881                         return continuation;
882                 }
883
884                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
885                 {
886                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
887                 }
888
889                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
890                 {
891                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
892                 }
893
894                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
895                 {
896                         return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
897                 }
898
899                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
900                 {
901                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
902                 }
903
904                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
905                                                                                                         TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
906                 {
907                         if (continuationFunction == null)
908                                 throw new ArgumentNullException ("continuationFunction");
909                         if (scheduler == null)
910                                 throw new ArgumentNullException ("scheduler");
911
912                         var t = new Task<TResult> (l => continuationFunction (this, l),
913                                                                                                  state,
914                                                                                                  cancellationToken,
915                                                                                                  GetCreationOptions (continuationOptions),
916                                                                                                  this);
917
918                         ContinueWithCore (t, continuationOptions, scheduler);
919
920                         return t;
921                 }
922
923                 public static Task<TResult> FromResult<TResult> (TResult result)
924                 {
925                         var tcs = new TaskCompletionSource<TResult> ();
926                         tcs.SetResult (result);
927                         return tcs.Task;
928                 }
929
930                 public TaskAwaiter GetAwaiter ()
931                 {
932                         return new TaskAwaiter (this);
933                 }
934
935                 public static Task Run (Action action)
936                 {
937                         return Run (action, CancellationToken.None);
938                 }
939
940                 public static Task Run (Action action, CancellationToken cancellationToken)
941                 {
942                         if (cancellationToken.IsCancellationRequested)
943                                 return TaskConstants.Canceled;
944
945                         var t = new Task (action, cancellationToken, TaskCreationOptions.DenyChildAttach);
946                         t.Start ();
947                         return t;
948                 }
949
950                 public static Task Run (Func<Task> function)
951                 {
952                         return Run (function, CancellationToken.None);
953                 }
954
955                 public static Task Run (Func<Task> function, CancellationToken cancellationToken)
956                 {
957                         if (cancellationToken.IsCancellationRequested)
958                                 return TaskConstants.Canceled;
959
960                         var t = new Task<Task> (function, cancellationToken);
961                         t.Start ();
962                         return t;
963                 }
964
965                 public static Task<TResult> Run<TResult> (Func<TResult> function)
966                 {
967                         return Run (function, CancellationToken.None);
968                 }
969
970                 public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
971                 {
972                         if (cancellationToken.IsCancellationRequested)
973                                 return TaskConstants<TResult>.Canceled;
974
975                         var t = new Task<TResult> (function, cancellationToken, TaskCreationOptions.DenyChildAttach);
976                         t.Start ();
977                         return t;
978                 }
979
980                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
981                 {
982                         return Run (function, CancellationToken.None);
983                 }
984
985                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
986                 {
987                         if (cancellationToken.IsCancellationRequested)
988                                 return TaskConstants<TResult>.Canceled;
989
990                         var t = Task<Task<TResult>>.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
991                         return GetTaskResult (t);
992                 }
993
994                 async static Task<TResult> GetTaskResult<TResult> (Task<Task<TResult>> task)
995                 {
996                         var r = await task.ConfigureAwait (false);
997                         return r.Result;
998                 }
999                 
1000                 public static YieldAwaitable Yield ()
1001                 {
1002                         return new YieldAwaitable ();
1003                 }
1004 #endif
1005
1006                 #region Properties
1007                 public static TaskFactory Factory {
1008                         get {
1009                                 return defaultFactory;
1010                         }
1011                 }
1012                 
1013                 public static int? CurrentId {
1014                         get {
1015                                 Task t = current;
1016                                 return t == null ? (int?)null : t.Id;
1017                         }
1018                 }
1019                 
1020                 public AggregateException Exception {
1021                         get {
1022                                 exceptionObserved = true;
1023                                 
1024                                 return exception;       
1025                         }
1026                         internal set {
1027                                 exception = value;
1028                         }
1029                 }
1030                 
1031                 public bool IsCanceled {
1032                         get {
1033                                 return status == TaskStatus.Canceled;
1034                         }
1035                 }
1036
1037                 public bool IsCompleted {
1038                         get {
1039                                 return status == TaskStatus.RanToCompletion ||
1040                                         status == TaskStatus.Canceled || status == TaskStatus.Faulted;
1041                         }
1042                 }
1043                 
1044                 public bool IsFaulted {
1045                         get {
1046                                 return status == TaskStatus.Faulted;
1047                         }
1048                 }
1049
1050                 public TaskCreationOptions CreationOptions {
1051                         get {
1052                                 return taskCreationOptions;
1053                         }
1054                 }
1055                 
1056                 public TaskStatus Status {
1057                         get {
1058                                 return status;
1059                         }
1060                         internal set {
1061                                 status = value;
1062                         }
1063                 }
1064
1065                 public object AsyncState {
1066                         get {
1067                                 return state;
1068                         }
1069                 }
1070                 
1071                 bool IAsyncResult.CompletedSynchronously {
1072                         get {
1073                                 return true;
1074                         }
1075                 }
1076
1077                 WaitHandle IAsyncResult.AsyncWaitHandle {
1078                         get {
1079                                 return null;
1080                         }
1081                 }
1082                 
1083                 public int Id {
1084                         get {
1085                                 return taskId;
1086                         }
1087                 }
1088
1089                 internal Task Parent {
1090                         get {
1091                                 return parent;
1092                         }
1093                 }
1094                 
1095                 internal string DisplayActionMethod {
1096                         get {
1097                                 Delegate d = simpleAction ?? (Delegate) action;
1098                                 return d == null ? "<none>" : d.Method.ToString ();
1099                         }
1100                 }
1101                 
1102                 #endregion
1103         }
1104 }
1105 #endif