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