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