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