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