Add unit test for AggregateException.GetBaseException that works on .net but is broke...
[mono.git] / mcs / class / corlib / System.Threading.Tasks / Task.cs
1 //
2 // Task.cs
3 //
4 // Authors:
5 //    Marek Safar  <marek.safar@gmail.com>
6 //    Jérémie Laval <jeremie dot laval at xamarin dot com>
7 //
8 // Copyright (c) 2008 Jérémie "Garuma" Laval
9 // Copyright 2011-2013 Xamarin Inc (http://www.xamarin.com).
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 //
30
31 #if NET_4_0
32
33 using System;
34 using System.Threading;
35 using System.Collections.Concurrent;
36 using System.Collections.Generic;
37 using System.Runtime.CompilerServices;
38
39 namespace System.Threading.Tasks
40 {
41         [System.Diagnostics.DebuggerDisplay ("Id = {Id}, Status = {Status}")]
42         [System.Diagnostics.DebuggerTypeProxy (typeof (TaskDebuggerView))]
43         public class Task : IDisposable, IAsyncResult
44         {
45                 // With this attribute each thread has its own value so that it's correct for our Schedule code
46                 // and for Parent property.
47                 [System.ThreadStatic]
48                 static Task current;
49                 
50                 // parent is the outer task in which this task is created
51                 readonly Task parent;
52                 // A reference to a Task on which this continuation is attached to
53                 Task contAncestor;
54                 
55                 static int          id = -1;
56                 static readonly TaskFactory defaultFactory = new TaskFactory ();
57
58                 CountdownEvent childTasks;
59                 
60                 int                 taskId;
61                 TaskCreationOptions creationOptions;
62                 
63                 internal TaskScheduler       scheduler;
64
65                 TaskExceptionSlot exSlot;
66                 ManualResetEvent wait_handle;
67
68                 TaskStatus          status;
69
70                 TaskActionInvoker invoker;
71                 object         state;
72                 internal AtomicBooleanValue executing;
73
74                 TaskCompletionQueue<IContinuation> continuations;
75
76                 CancellationToken token;
77                 CancellationTokenRegistration? cancellationRegistration;
78
79                 internal const TaskCreationOptions WorkerTaskNotSupportedOptions = TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness;
80
81                 const TaskCreationOptions MaxTaskCreationOptions =
82 #if NET_4_5
83                         TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler |
84 #endif
85                         TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent;
86
87                 public Task (Action action)
88                         : this (action, TaskCreationOptions.None)
89                 {
90                         
91                 }
92                 
93                 public Task (Action action, TaskCreationOptions creationOptions)
94                         : this (action, CancellationToken.None, creationOptions)
95                 {
96                         
97                 }
98                 
99                 public Task (Action action, CancellationToken cancellationToken)
100                         : this (action, cancellationToken, TaskCreationOptions.None)
101                 {
102                         
103                 }
104                 
105                 public Task (Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
106                         : this (TaskActionInvoker.Create (action), null, cancellationToken, creationOptions, current)
107                 {
108                         if (action == null)
109                                 throw new ArgumentNullException ("action");
110                         if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
111                                 throw new ArgumentOutOfRangeException ("creationOptions");
112                 }
113                 
114                 public Task (Action<object> action, object state)
115                         : this (action, state, TaskCreationOptions.None)
116                 {       
117                 }
118                 
119                 public Task (Action<object> action, object state, TaskCreationOptions creationOptions)
120                         : this (action, state, CancellationToken.None, creationOptions)
121                 {
122                 }
123                 
124                 public Task (Action<object> action, object state, CancellationToken cancellationToken)
125                         : this (action, state, cancellationToken, TaskCreationOptions.None)
126                 {       
127                 }
128
129                 public Task (Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)
130                         : this (TaskActionInvoker.Create (action), state, cancellationToken, creationOptions, current)
131                 {
132                         if (action == null)
133                                 throw new ArgumentNullException ("action");
134                         if (creationOptions > MaxTaskCreationOptions || creationOptions < TaskCreationOptions.None)
135                                 throw new ArgumentOutOfRangeException ("creationOptions");
136                 }
137
138                 internal Task (TaskActionInvoker invoker, object state, CancellationToken cancellationToken,
139                                TaskCreationOptions creationOptions, Task parent = null, Task contAncestor = null, bool ignoreCancellation = false)
140                 {
141                         this.invoker         = invoker;
142                         this.creationOptions = creationOptions;
143                         this.state           = state;
144                         this.taskId          = Interlocked.Increment (ref id);
145                         this.token           = cancellationToken;
146                         this.parent          = parent = parent == null ? current : parent;
147                         this.contAncestor    = contAncestor;
148                         this.status          = cancellationToken.IsCancellationRequested && !ignoreCancellation ? TaskStatus.Canceled : TaskStatus.Created;
149
150                         // Process creationOptions
151 #if NET_4_5
152                         if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent)
153                             && !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach))
154 #else
155                         if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
156 #endif
157                                 parent.AddChild ();
158
159                         if (token.CanBeCanceled && !ignoreCancellation)
160                                 cancellationRegistration = token.Register (l => ((Task) l).CancelReal (), this);
161                 }
162
163                 static bool HasFlag (TaskCreationOptions opt, TaskCreationOptions member)
164                 {
165                         return (opt & member) == member;
166                 }
167
168                 #region Start
169                 public void Start ()
170                 {
171                         Start (TaskScheduler.Current);
172                 }
173                 
174                 public void Start (TaskScheduler scheduler)
175                 {
176                         if (scheduler == null)
177                                 throw new ArgumentNullException ("scheduler");
178
179                         if (status >= TaskStatus.WaitingToRun)
180                                 throw new InvalidOperationException ("The Task is not in a valid state to be started.");
181
182                         if (IsContinuation)
183                                 throw new InvalidOperationException ("Start may not be called on a continuation task");
184
185                         SetupScheduler (scheduler);
186                         Schedule ();
187                 }
188
189                 internal void SetupScheduler (TaskScheduler scheduler)
190                 {
191                         this.scheduler = scheduler;
192                         Status = TaskStatus.WaitingForActivation;
193                 }
194                 
195                 public void RunSynchronously ()
196                 {
197                         RunSynchronously (TaskScheduler.Current);
198                 }
199                 
200                 public void RunSynchronously (TaskScheduler scheduler)
201                 {
202                         if (scheduler == null)
203                                 throw new ArgumentNullException ("scheduler");
204
205                         if (Status > TaskStatus.WaitingForActivation)
206                                 throw new InvalidOperationException ("The task is not in a valid state to be started");
207
208                         if (IsContinuation)
209                                 throw new InvalidOperationException ("RunSynchronously may not be called on a continuation task");
210
211                         RunSynchronouslyCore (scheduler);
212                 }
213
214                 internal void RunSynchronouslyCore (TaskScheduler scheduler)
215                 {
216                         SetupScheduler (scheduler);
217                         Status = TaskStatus.WaitingToRun;
218
219                         try {
220                                 if (scheduler.RunInline (this, false))
221                                         return;
222                         } catch (Exception inner) {
223                                 throw new TaskSchedulerException (inner);
224                         }
225
226                         Schedule ();
227                         WaitCore (Timeout.Infinite, CancellationToken.None, false);
228                 }
229                 #endregion
230                 
231                 #region ContinueWith
232                 public Task ContinueWith (Action<Task> continuationAction)
233                 {
234                         return ContinueWith (continuationAction, TaskContinuationOptions.None);
235                 }
236                 
237                 public Task ContinueWith (Action<Task> continuationAction, TaskContinuationOptions continuationOptions)
238                 {
239                         return ContinueWith (continuationAction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
240                 }
241                 
242                 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken)
243                 {
244                         return ContinueWith (continuationAction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
245                 }
246                 
247                 public Task ContinueWith (Action<Task> continuationAction, TaskScheduler scheduler)
248                 {
249                         return ContinueWith (continuationAction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
250                 }
251                 
252                 public Task ContinueWith (Action<Task> continuationAction, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
253                 {
254                         if (continuationAction == null)
255                                 throw new ArgumentNullException ("continuationAction");
256                         if (scheduler == null)
257                                 throw new ArgumentNullException ("scheduler");
258
259                         return ContinueWith (TaskActionInvoker.Create (continuationAction), cancellationToken, continuationOptions, scheduler);
260                 }
261
262                 internal Task ContinueWith (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
263                 {
264                         var lazyCancellation = false;
265 #if NET_4_5
266                         lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
267 #endif
268                         var continuation = new Task (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), null, this, lazyCancellation);
269                         ContinueWithCore (continuation, continuationOptions, scheduler);
270
271                         return continuation;
272                 }
273                 
274                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction)
275                 {
276                         return ContinueWith<TResult> (continuationFunction, TaskContinuationOptions.None);
277                 }
278                 
279                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskContinuationOptions continuationOptions)
280                 {
281                         return ContinueWith<TResult> (continuationFunction, CancellationToken.None, continuationOptions, TaskScheduler.Current);
282                 }
283                 
284                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken)
285                 {
286                         return ContinueWith<TResult> (continuationFunction, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
287                 }
288                 
289                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, TaskScheduler scheduler)
290                 {
291                         return ContinueWith<TResult> (continuationFunction, CancellationToken.None, TaskContinuationOptions.None, scheduler);
292                 }
293                 
294                 public Task<TResult> ContinueWith<TResult> (Func<Task, TResult> continuationFunction, CancellationToken cancellationToken,
295                                                             TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
296                 {
297                         if (continuationFunction == null)
298                                 throw new ArgumentNullException ("continuationFunction");
299                         if (scheduler == null)
300                                 throw new ArgumentNullException ("scheduler");
301
302                         return ContinueWith<TResult> (TaskActionInvoker.Create (continuationFunction), cancellationToken, continuationOptions, scheduler);
303                 }
304
305                 internal Task<TResult> ContinueWith<TResult> (TaskActionInvoker invoker, CancellationToken cancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
306                 {
307                         var lazyCancellation = false;
308 #if NET_4_5
309                         lazyCancellation = (continuationOptions & TaskContinuationOptions.LazyCancellation) > 0;
310 #endif
311                         var continuation = new Task<TResult> (invoker, null, cancellationToken, GetCreationOptions (continuationOptions), parent, this, lazyCancellation);
312                         ContinueWithCore (continuation, continuationOptions, scheduler);
313
314                         return continuation;
315                 }
316         
317                 internal void ContinueWithCore (Task continuation, TaskContinuationOptions options, TaskScheduler scheduler)
318                 {
319                         const TaskContinuationOptions wrongRan = TaskContinuationOptions.NotOnRanToCompletion | TaskContinuationOptions.OnlyOnRanToCompletion;
320                         const TaskContinuationOptions wrongCanceled = TaskContinuationOptions.NotOnCanceled | TaskContinuationOptions.OnlyOnCanceled;
321                         const TaskContinuationOptions wrongFaulted = TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.OnlyOnFaulted;
322
323                         if (((options & wrongRan) == wrongRan) || ((options & wrongCanceled) == wrongCanceled) || ((options & wrongFaulted) == wrongFaulted))
324                                 throw new ArgumentException ("continuationOptions", "Some options are mutually exclusive");
325
326                         // Already set the scheduler so that user can call Wait and that sort of stuff
327                         continuation.scheduler = scheduler;
328                         continuation.Status = TaskStatus.WaitingForActivation;
329
330                         ContinueWith (new TaskContinuation (continuation, options));
331                 }
332                 
333                 internal void ContinueWith (IContinuation continuation)
334                 {
335                         if (IsCompleted) {
336                                 continuation.Execute ();
337                                 return;
338                         }
339                         
340                         continuations.Add (continuation);
341                         
342                         // Retry in case completion was achieved but event adding was too late
343                         if (IsCompleted && continuations.Remove (continuation))
344                                 continuation.Execute ();
345                 }
346
347                 internal void RemoveContinuation (IContinuation continuation)
348                 {
349                         continuations.Remove (continuation);
350                 }
351
352                 static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
353                 {
354                         TaskCreationOptions options = TaskCreationOptions.None;
355                         if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
356                                 options |= TaskCreationOptions.AttachedToParent;
357                         if ((kind & TaskContinuationOptions.PreferFairness) > 0)
358                                 options |= TaskCreationOptions.PreferFairness;
359                         if ((kind & TaskContinuationOptions.LongRunning) > 0)
360                                 options |= TaskCreationOptions.LongRunning;
361                         
362                         return options;
363                 }
364                 #endregion
365                 
366                 #region Internal and protected thingies
367                 internal void Schedule ()
368                 {
369                         Status = TaskStatus.WaitingToRun;
370                         scheduler.QueueTask (this);
371                 }
372                 
373                 void ThreadStart ()
374                 {
375                         /* Allow scheduler to break fairness of deque ordering without
376                          * breaking its semantic (the task can be executed twice but the
377                          * second time it will return immediately
378                          */
379                         if (!executing.TryRelaxedSet ())
380                                 return;
381
382                         // Disable CancellationToken direct cancellation
383                         if (cancellationRegistration != null) {
384                                 cancellationRegistration.Value.Dispose ();
385                                 cancellationRegistration = null;
386                         }
387
388                         // If Task are ran inline on the same thread we might trash these values
389                         var saveCurrent = current;
390                         var saveScheduler = TaskScheduler.Current;
391
392                         current = this;
393 #if NET_4_5
394                         TaskScheduler.Current = HasFlag (creationOptions, TaskCreationOptions.HideScheduler) ? TaskScheduler.Default : scheduler;
395 #else
396                         TaskScheduler.Current = scheduler;
397 #endif
398                         
399                         if (!token.IsCancellationRequested) {
400                                 
401                                 status = TaskStatus.Running;
402                                 
403                                 try {
404                                         InnerInvoke ();
405                                 } catch (OperationCanceledException oce) {
406                                         if (token != CancellationToken.None && oce.CancellationToken == token)
407                                                 CancelReal ();
408                                         else
409                                                 HandleGenericException (oce);
410                                 } catch (Exception e) {
411                                         HandleGenericException (e);
412                                 }
413                         } else {
414                                 CancelReal ();
415                         }
416
417                         if (saveCurrent != null)
418                                 current = saveCurrent;
419                         if (saveScheduler != null)
420                                 TaskScheduler.Current = saveScheduler;
421                         Finish ();
422                 }
423
424                 internal bool TrySetCanceled ()
425                 {
426                         if (IsCompleted)
427                                 return false;
428                         
429                         if (!executing.TryRelaxedSet ()) {
430                                 var sw = new SpinWait ();
431                                 while (!IsCompleted)
432                                         sw.SpinOnce ();
433
434                                 return false;
435                         }
436                         
437                         CancelReal ();
438                         return true;
439                 }
440
441                 internal bool TrySetException (AggregateException aggregate)
442                 {
443                         if (IsCompleted)
444                                 return false;
445                         
446                         if (!executing.TryRelaxedSet ()) {
447                                 var sw = new SpinWait ();
448                                 while (!IsCompleted)
449                                         sw.SpinOnce ();
450
451                                 return false;
452                         }
453                         
454                         HandleGenericException (aggregate);
455                         return true;
456                 }
457
458                 internal bool TrySetExceptionObserved ()
459                 {
460                         if (exSlot != null) {
461                                 exSlot.Observed = true;
462                                 return true;
463                         }
464                         return false;
465                 }
466
467                 internal void Execute ()
468                 {
469                         ThreadStart ();
470                 }
471                 
472                 internal void AddChild ()
473                 {
474                         if (childTasks == null)
475                                 Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
476                         childTasks.AddCount ();
477                 }
478
479                 internal void ChildCompleted (AggregateException childEx)
480                 {
481                         if (childEx != null) {
482                                 if (ExceptionSlot.ChildExceptions == null)
483                                         Interlocked.CompareExchange (ref ExceptionSlot.ChildExceptions, new ConcurrentQueue<AggregateException> (), null);
484                                 ExceptionSlot.ChildExceptions.Enqueue (childEx);
485                         }
486
487                         if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
488                                 ProcessChildExceptions ();
489                                 Status = exSlot == null ? TaskStatus.RanToCompletion : TaskStatus.Faulted;
490                                 ProcessCompleteDelegates ();
491                                 if (parent != null &&
492 #if NET_4_5
493                                     !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
494 #endif
495                                         HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
496                                         parent.ChildCompleted (this.Exception);
497                         }
498                 }
499
500                 void InnerInvoke ()
501                 {
502                         if (IsContinuation) {
503                                 var ancestor = contAncestor;
504                                 contAncestor = null;
505                                 invoker.Invoke (ancestor, state, this);
506                         } else {
507                                 invoker.Invoke (this, state, this);
508                         }
509                 }
510                 
511                 internal void Finish ()
512                 {
513                         // If there was children created and they all finished, we set the countdown
514                         if (childTasks != null) {
515                                 if (childTasks.Signal ())
516                                         ProcessChildExceptions (true);
517                         }
518                         
519                         // Don't override Canceled or Faulted
520                         if (status == TaskStatus.Running) {
521                                 if (childTasks == null || childTasks.IsSet)
522                                         Status = TaskStatus.RanToCompletion;
523                                 else
524                                         Status = TaskStatus.WaitingForChildrenToComplete;
525                         }
526
527                         if (wait_handle != null)
528                                 wait_handle.Set ();
529
530                         // Tell parent that we are finished
531                         if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) &&
532 #if NET_4_5
533                             !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
534 #endif
535                                 status != TaskStatus.WaitingForChildrenToComplete) {
536                                 parent.ChildCompleted (this.Exception);
537                         }
538
539                         // Completions are already processed when task is canceled or faulted
540                         if (status == TaskStatus.RanToCompletion)
541                                 ProcessCompleteDelegates ();
542
543                         // Reset the current thingies
544                         if (current == this)
545                                 current = null;
546                         if (TaskScheduler.Current == scheduler)
547                                 TaskScheduler.Current = null;
548
549                         if (cancellationRegistration.HasValue)
550                                 cancellationRegistration.Value.Dispose ();
551                 }
552
553                 void ProcessCompleteDelegates ()
554                 {
555                         if (continuations.HasElements) {
556                                 IContinuation continuation;
557                                 while (continuations.TryGetNextCompletion (out continuation))
558                                         continuation.Execute ();
559                         }
560                 }
561
562                 void ProcessChildExceptions (bool isParent = false)
563                 {
564                         if (exSlot == null || exSlot.ChildExceptions == null)
565                                 return;
566
567                         if (ExceptionSlot.Exception == null)
568                                 exSlot.Exception = new AggregateException ();
569
570                         AggregateException childEx;
571                         while (exSlot.ChildExceptions.TryDequeue (out childEx))
572                                 exSlot.Exception.AddChildException (childEx);
573
574                         if (isParent) {
575                                 Status = TaskStatus.Faulted;
576                                 ProcessCompleteDelegates ();                    
577                         }
578                 }
579                 #endregion
580                 
581                 #region Cancel and Wait related method
582                 
583                 internal void CancelReal ()
584                 {
585                         Status = TaskStatus.Canceled;
586
587                         if (wait_handle != null)
588                                 wait_handle.Set ();
589
590                         ProcessCompleteDelegates ();
591                 }
592
593                 void HandleGenericException (Exception e)
594                 {
595                         HandleGenericException (new AggregateException (e));
596                 }
597
598                 void HandleGenericException (AggregateException e)
599                 {
600                         ExceptionSlot.Exception = e;
601                         Thread.MemoryBarrier ();
602                         Status = TaskStatus.Faulted;
603
604                         if (wait_handle != null)
605                                 wait_handle.Set ();
606
607                         ProcessCompleteDelegates ();
608                 }
609
610                 internal bool WaitOnChildren ()
611                 {
612                         if (Status == TaskStatus.WaitingForChildrenToComplete && childTasks != null) {
613                                 childTasks.Wait ();
614                                 return true;
615                         }
616                         return false;
617                 }
618                 
619                 public void Wait ()
620                 {
621                         Wait (Timeout.Infinite, CancellationToken.None);
622                 }
623
624                 public void Wait (CancellationToken cancellationToken)
625                 {
626                         Wait (Timeout.Infinite, cancellationToken);
627                 }
628                 
629                 public bool Wait (TimeSpan timeout)
630                 {
631                         return Wait (CheckTimeout (timeout), CancellationToken.None);
632                 }
633                 
634                 public bool Wait (int millisecondsTimeout)
635                 {
636                         return Wait (millisecondsTimeout, CancellationToken.None);
637                 }
638
639                 public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
640                 {
641                         if (millisecondsTimeout < -1)
642                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
643
644                         bool result = WaitCore (millisecondsTimeout, cancellationToken, true);
645
646                         if (IsCanceled)
647                                 throw new AggregateException (new TaskCanceledException (this));
648
649                         var exception = Exception;
650                         if (exception != null)
651                                 throw exception;
652
653                         return result;
654                 }
655
656                 internal bool WaitCore (int millisecondsTimeout, CancellationToken cancellationToken, bool runInline)
657                 {
658                         if (IsCompleted)
659                                 return true;
660
661                         // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
662                         if (runInline && Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
663                                 scheduler.RunInline (this, true);
664
665                         bool result = true;
666
667                         if (!IsCompleted) {
668                                 var continuation = new ManualResetContinuation ();
669                                 try {
670                                         ContinueWith (continuation);
671                                         result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
672                                 } finally {
673                                         if (!result)
674                                                 RemoveContinuation (continuation);
675                                         continuation.Dispose ();
676                                 }
677                         }
678
679                         return result;
680                 }
681                 
682                 public static void WaitAll (params Task[] tasks)
683                 {
684                         WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
685                 }
686
687                 public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
688                 {
689                         WaitAll (tasks, Timeout.Infinite, cancellationToken);
690                 }
691                 
692                 public static bool WaitAll (Task[] tasks, TimeSpan timeout)
693                 {
694                         return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
695                 }
696                 
697                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
698                 {
699                         return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
700                 }
701                 
702                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
703                 {
704                         if (tasks == null)
705                                 throw new ArgumentNullException ("tasks");
706
707                         bool result = true;
708                         foreach (var t in tasks) {
709                                 if (t == null)
710                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
711
712                                 result &= t.Status == TaskStatus.RanToCompletion;
713                         }
714
715                         if (!result) {
716                                 var continuation = new CountdownContinuation (tasks.Length);
717                                 try {
718                                         foreach (var t in tasks)
719                                                 t.ContinueWith (continuation);
720
721                                         result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
722                                 } finally {
723                                         List<Exception> exceptions = null;
724
725                                         foreach (var t in tasks) {
726                                                 if (result) {
727                                                         if (t.Status == TaskStatus.RanToCompletion)
728                                                                 continue;
729                                                         if (exceptions == null)
730                                                                 exceptions = new List<Exception> ();
731                                                         if (t.Exception != null)
732                                                                 exceptions.AddRange (t.Exception.InnerExceptions);
733                                                         else
734                                                                 exceptions.Add (new TaskCanceledException (t));
735                                                 } else {
736                                                         t.RemoveContinuation (continuation);
737                                                 }
738                                         }
739
740                                         continuation.Dispose ();
741
742                                         if (exceptions != null)
743                                                 throw new AggregateException (exceptions);
744                                 }
745                         }
746
747                         return result;
748                 }
749                 
750                 public static int WaitAny (params Task[] tasks)
751                 {
752                         return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
753                 }
754
755                 public static int WaitAny (Task[] tasks, TimeSpan timeout)
756                 {
757                         return WaitAny (tasks, CheckTimeout (timeout));
758                 }
759                 
760                 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
761                 {
762                         return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
763                 }
764
765                 public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
766                 {
767                         return WaitAny (tasks, Timeout.Infinite, cancellationToken);
768                 }
769
770                 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
771                 {
772                         if (tasks == null)
773                                 throw new ArgumentNullException ("tasks");
774                         if (millisecondsTimeout < -1)
775                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
776                         CheckForNullTasks (tasks);
777
778                         if (tasks.Length > 0) {
779                                 var continuation = new ManualResetContinuation ();
780                                 bool result = false;
781                                 try {
782                                         for (int i = 0; i < tasks.Length; i++) {
783                                                 var t = tasks[i];
784                                                 if (t.IsCompleted)
785                                                         return i;
786                                                 t.ContinueWith (continuation);
787                                         }
788
789                                         if (!(result = continuation.Event.Wait (millisecondsTimeout, cancellationToken)))
790                                                 return -1;
791                                 } finally {
792                                         if (!result)
793                                                 foreach (var t in tasks)
794                                                         t.RemoveContinuation (continuation);
795                                         continuation.Dispose ();
796                                 }
797                         }
798
799                         int firstFinished = -1;
800                         for (int i = 0; i < tasks.Length; i++) {
801                                 var t = tasks[i];
802                                 if (t.IsCompleted) {
803                                         firstFinished = i;
804                                         break;
805                                 }
806                         }
807
808                         return firstFinished;
809                 }
810
811                 static int CheckTimeout (TimeSpan timeout)
812                 {
813                         try {
814                                 return checked ((int)timeout.TotalMilliseconds);
815                         } catch (System.OverflowException) {
816                                 throw new ArgumentOutOfRangeException ("timeout");
817                         }
818                 }
819
820                 static void CheckForNullTasks (Task[] tasks)
821                 {
822                         foreach (var t in tasks)
823                                 if (t == null)
824                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
825                 }
826                 #endregion
827                 
828                 #region Dispose
829                 public void Dispose ()
830                 {
831                         Dispose (true);                 
832                 }
833                 
834                 protected virtual void Dispose (bool disposing)
835                 {
836                         if (!IsCompleted)
837                                 throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
838
839                         // Set action to null so that the GC can collect the delegate and thus
840                         // any big object references that the user might have captured in a anonymous method
841                         if (disposing) {
842                                 invoker = null;
843                                 state = null;
844                                 if (cancellationRegistration != null)
845                                         cancellationRegistration.Value.Dispose ();
846                                 if (wait_handle != null)
847                                         wait_handle.Dispose ();
848                         }
849                 }
850                 #endregion
851
852 #if NET_4_5
853                 public
854 #else
855                 internal
856 #endif
857                 Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken,
858                                                                   TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
859                 {
860                         if (continuationAction == null)
861                                 throw new ArgumentNullException ("continuationAction");
862                         if (scheduler == null)
863                                 throw new ArgumentNullException ("scheduler");
864
865                         Task continuation = new Task (TaskActionInvoker.Create (continuationAction),
866                                                                                   state, cancellationToken,
867                                                                                   GetCreationOptions (continuationOptions),
868                                                       parent,
869                                                       this);
870                         ContinueWithCore (continuation, continuationOptions, scheduler);
871
872                         return continuation;
873                 }
874                 
875 #if NET_4_5
876
877                 public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext)
878                 {
879                         return new ConfiguredTaskAwaitable (this, continueOnCapturedContext);
880                 }
881
882                 public Task ContinueWith (Action<Task, object> continuationAction, object state)
883                 {
884                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
885                 }
886
887                 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken)
888                 {
889                         return ContinueWith (continuationAction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
890                 }
891
892                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskContinuationOptions continuationOptions)
893                 {
894                         return ContinueWith (continuationAction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
895                 }
896
897                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskScheduler scheduler)
898                 {
899                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
900                 }
901
902                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
903                 {
904                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
905                 }
906
907                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
908                 {
909                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
910                 }
911
912                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
913                 {
914                         return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
915                 }
916
917                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
918                 {
919                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
920                 }
921
922                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
923                                                                                                         TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
924                 {
925                         if (continuationFunction == null)
926                                 throw new ArgumentNullException ("continuationFunction");
927                         if (scheduler == null)
928                                 throw new ArgumentNullException ("scheduler");
929
930                         var t = new Task<TResult> (TaskActionInvoker.Create (continuationFunction),
931                                                    state,
932                                                    cancellationToken,
933                                                    GetCreationOptions (continuationOptions),
934                                                    parent,
935                                                    this);
936
937                         ContinueWithCore (t, continuationOptions, scheduler);
938
939                         return t;
940                 }
941
942                 public static Task Delay (int millisecondsDelay)
943                 {
944                         return Delay (millisecondsDelay, CancellationToken.None);
945                 }
946
947                 public static Task Delay (TimeSpan delay)
948                 {
949                         return Delay (CheckTimeout (delay), CancellationToken.None);
950                 }
951
952                 public static Task Delay (TimeSpan delay, CancellationToken cancellationToken)
953                 {
954                         return Delay (CheckTimeout (delay), cancellationToken);
955                 }
956
957                 public static Task Delay (int millisecondsDelay, CancellationToken cancellationToken)
958                 {
959                         if (millisecondsDelay < -1)
960                                 throw new ArgumentOutOfRangeException ("millisecondsDelay");
961
962                         if (cancellationToken.IsCancellationRequested)
963                                 return TaskConstants.Canceled;
964
965                         var task = new Task (TaskActionInvoker.Empty, null, cancellationToken, TaskCreationOptions.None, null, null);
966                         task.SetupScheduler (TaskScheduler.Default);
967
968                         if (millisecondsDelay != Timeout.Infinite) {
969                                 var timer = new Timer (delegate (object state) {
970                                         var t = (Task) state;
971                                         if (t.Status == TaskStatus.WaitingForActivation) {
972                                                 t.Status = TaskStatus.Running;
973                                                 t.Finish ();
974                                         }
975                                 }, task, millisecondsDelay, -1);
976
977                                 task.ContinueWith (new DisposeContinuation (timer));
978                         }
979
980                         return task;
981                 }
982
983                 public static Task<TResult> FromResult<TResult> (TResult result)
984                 {
985                         var tcs = new TaskCompletionSource<TResult> ();
986                         tcs.SetResult (result);
987                         return tcs.Task;
988                 }
989
990                 public TaskAwaiter GetAwaiter ()
991                 {
992                         return new TaskAwaiter (this);
993                 }
994
995                 public static Task Run (Action action)
996                 {
997                         return Run (action, CancellationToken.None);
998                 }
999
1000                 public static Task Run (Action action, CancellationToken cancellationToken)
1001                 {
1002                         if (cancellationToken.IsCancellationRequested)
1003                                 return TaskConstants.Canceled;
1004
1005                         return Task.Factory.StartNew (action, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
1006                 }
1007
1008                 public static Task Run (Func<Task> function)
1009                 {
1010                         return Run (function, CancellationToken.None);
1011                 }
1012
1013                 public static Task Run (Func<Task> function, CancellationToken cancellationToken)
1014                 {
1015                         if (cancellationToken.IsCancellationRequested)
1016                                 return TaskConstants.Canceled;
1017
1018                         return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
1019                 }
1020
1021                 public static Task<TResult> Run<TResult> (Func<TResult> function)
1022                 {
1023                         return Run (function, CancellationToken.None);
1024                 }
1025
1026                 public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
1027                 {
1028                         if (cancellationToken.IsCancellationRequested)
1029                                 return TaskConstants<TResult>.Canceled;
1030
1031                         return Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
1032                 }
1033
1034                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
1035                 {
1036                         return Run (function, CancellationToken.None);
1037                 }
1038
1039                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
1040                 {
1041                         if (cancellationToken.IsCancellationRequested)
1042                                 return TaskConstants<TResult>.Canceled;
1043
1044                         return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
1045                 }
1046
1047                 public static Task WhenAll (params Task[] tasks)
1048                 {
1049                         if (tasks == null)
1050                                 throw new ArgumentNullException ("tasks");
1051
1052                         return WhenAllCore (tasks);
1053                 }
1054
1055                 public static Task WhenAll (IEnumerable<Task> tasks)
1056                 {
1057                         if (tasks == null)
1058                                 throw new ArgumentNullException ("tasks");
1059
1060                         // Call ToList on input enumeration or we end up
1061                         // enumerating it more than once
1062                         return WhenAllCore (new List<Task> (tasks));
1063                 }
1064
1065                 public static Task<TResult[]> WhenAll<TResult> (params Task<TResult>[] tasks)
1066                 {
1067                         if (tasks == null)
1068                                 throw new ArgumentNullException ("tasks");
1069
1070                         return WhenAllCore<TResult> (tasks);
1071                 }
1072
1073                 public static Task<TResult[]> WhenAll<TResult> (IEnumerable<Task<TResult>> tasks)
1074                 {
1075                         if (tasks == null)
1076                                 throw new ArgumentNullException ("tasks");
1077
1078                         // Call ToList on input enumeration or we end up
1079                         // enumerating it more than once
1080                         return WhenAllCore<TResult> (new List<Task<TResult>> (tasks));
1081                 }
1082
1083                 internal static Task<TResult[]> WhenAllCore<TResult> (IList<Task<TResult>> tasks)
1084                 {
1085                         if (tasks.Count == 0)
1086                                 return FromResult(new TResult[0]);
1087
1088                         foreach (var t in tasks) {
1089                                 if (t == null)
1090                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1091                         }
1092
1093                         var task = new Task<TResult[]> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1094                         task.SetupScheduler (TaskScheduler.Current);
1095
1096                         var continuation = new WhenAllContinuation<TResult> (task, tasks);
1097                         foreach (var t in tasks)
1098                                 t.ContinueWith (continuation);
1099
1100                         return task;
1101                 }
1102
1103                 public static Task<Task> WhenAny (params Task[] tasks)
1104                 {
1105                         if (tasks == null)
1106                                 throw new ArgumentNullException ("tasks");
1107
1108                         return WhenAnyCore (tasks);
1109                 }
1110
1111                 public static Task<Task> WhenAny (IEnumerable<Task> tasks)
1112                 {
1113                         if (tasks == null)
1114                                 throw new ArgumentNullException ("tasks");
1115
1116                         return WhenAnyCore (new List<Task> (tasks));
1117                 }
1118
1119                 public static Task<Task<TResult>> WhenAny<TResult> (params Task<TResult>[] tasks)
1120                 {
1121                         if (tasks == null)
1122                                 throw new ArgumentNullException ("tasks");
1123
1124                         return WhenAnyCore<TResult> (tasks);
1125                 }
1126
1127                 public static Task<Task<TResult>> WhenAny<TResult> (IEnumerable<Task<TResult>> tasks)
1128                 {
1129                         if (tasks == null)
1130                                 throw new ArgumentNullException ("tasks");
1131
1132                         return WhenAnyCore<TResult> (new List<Task<TResult>> (tasks));
1133                 }
1134
1135                 static Task<Task<TResult>> WhenAnyCore<TResult> (IList<Task<TResult>> tasks)
1136                 {
1137                         if (tasks.Count == 0)
1138                                 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1139
1140                         int completed_index = -1;
1141                         for (int i = 0; i < tasks.Count; ++i) {
1142                                 var t = tasks[i];
1143                                 if (t == null)
1144                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1145
1146                                 if (t.IsCompleted && completed_index < 0)
1147                                         completed_index = i;
1148                         }
1149
1150                         var task = new Task<Task<TResult>> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1151
1152                         if (completed_index > 0) {
1153                                 task.TrySetResult (tasks[completed_index]);
1154                                 return task;
1155                         }
1156
1157                         task.SetupScheduler (TaskScheduler.Current);
1158
1159                         var continuation = new WhenAnyContinuation<Task<TResult>> (task, tasks);
1160                         foreach (var t in tasks)
1161                                 t.ContinueWith (continuation);
1162
1163                         return task;
1164                 }
1165
1166                 public static YieldAwaitable Yield ()
1167                 {
1168                         return new YieldAwaitable ();
1169                 }
1170 #endif
1171
1172                 internal static Task WhenAllCore (IList<Task> tasks)
1173                 {
1174                         bool all_completed = true;
1175                         foreach (var t in tasks) {
1176                                 if (t == null)
1177                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1178
1179                                 all_completed &= t.Status == TaskStatus.RanToCompletion;
1180                         }
1181
1182                         if (all_completed)
1183                                 return TaskConstants.Finished;
1184
1185                         var task = new Task (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1186                         task.SetupScheduler (TaskScheduler.Current);
1187
1188                         var continuation = new WhenAllContinuation (task, tasks);
1189                         foreach (var t in tasks)
1190                                 t.ContinueWith (continuation);
1191
1192                         return task;
1193                 }
1194
1195                 internal static Task<Task> WhenAnyCore (IList<Task> tasks)
1196                 {
1197                         if (tasks.Count == 0)
1198                                 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1199
1200                         int completed_index = -1;
1201                         for (int i = 0; i < tasks.Count; ++i) {
1202                                 var t = tasks [i];
1203                                 if (t == null)
1204                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1205
1206                                 if (t.IsCompleted && completed_index < 0)
1207                                         completed_index = i;
1208                         }
1209
1210                         var task = new Task<Task> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1211
1212                         if (completed_index > 0) {
1213                                 task.TrySetResult (tasks[completed_index]);
1214                                 return task;
1215                         }
1216
1217                         task.SetupScheduler (TaskScheduler.Current);
1218
1219                         var continuation = new WhenAnyContinuation<Task> (task, tasks);
1220                         foreach (var t in tasks)
1221                                 t.ContinueWith (continuation);
1222
1223                         return task;
1224                 }
1225                 #region Properties
1226
1227                 internal CancellationToken CancellationToken {
1228                         get {
1229                                 return token;
1230                         }
1231                 }
1232
1233                 public static TaskFactory Factory {
1234                         get {
1235                                 return defaultFactory;
1236                         }
1237                 }
1238                 
1239                 public static int? CurrentId {
1240                         get {
1241                                 Task t = current;
1242                                 return t == null ? (int?)null : t.Id;
1243                         }
1244                 }
1245                 
1246                 public AggregateException Exception {
1247                         get {
1248                                 if (exSlot == null)
1249                                         return null;
1250                                 exSlot.Observed = true;
1251                                 return exSlot.Exception;
1252                         }
1253                 }
1254                 
1255                 public bool IsCanceled {
1256                         get {
1257                                 return status == TaskStatus.Canceled;
1258                         }
1259                 }
1260
1261                 public bool IsCompleted {
1262                         get {
1263                                 return status >= TaskStatus.RanToCompletion;
1264                         }
1265                 }
1266                 
1267                 public bool IsFaulted {
1268                         get {
1269                                 return status == TaskStatus.Faulted;
1270                         }
1271                 }
1272
1273                 public TaskCreationOptions CreationOptions {
1274                         get {
1275                                 return creationOptions & MaxTaskCreationOptions;
1276                         }
1277                 }
1278                 
1279                 public TaskStatus Status {
1280                         get {
1281                                 return status;
1282                         }
1283                         internal set {
1284                                 status = value;
1285                                 Thread.MemoryBarrier ();
1286                         }
1287                 }
1288
1289                 TaskExceptionSlot ExceptionSlot {
1290                         get {
1291                                 if (exSlot != null)
1292                                         return exSlot;
1293                                 Interlocked.CompareExchange (ref exSlot, new TaskExceptionSlot (this), null);
1294                                 return exSlot;
1295                         }
1296                 }
1297
1298                 public object AsyncState {
1299                         get {
1300                                 return state;
1301                         }
1302                 }
1303                 
1304                 bool IAsyncResult.CompletedSynchronously {
1305                         get {
1306                                 return true;
1307                         }
1308                 }
1309
1310                 WaitHandle IAsyncResult.AsyncWaitHandle {
1311                         get {
1312                                 if (invoker == null)
1313                                         throw new ObjectDisposedException (GetType ().ToString ());
1314
1315                                 if (wait_handle == null)
1316                                         Interlocked.CompareExchange (ref wait_handle, new ManualResetEvent (IsCompleted), null);
1317
1318                                 return wait_handle;
1319                         }
1320                 }
1321                 
1322                 public int Id {
1323                         get {
1324                                 return taskId;
1325                         }
1326                 }
1327
1328                 bool IsContinuation {
1329                         get {
1330                                 return contAncestor != null;
1331                         }
1332                 }
1333
1334                 internal Task ContinuationAncestor {
1335                         get {
1336                                 return contAncestor;
1337                         }
1338                 }
1339                 
1340                 internal string DisplayActionMethod {
1341                         get {
1342                                 Delegate d = invoker.Action;
1343                                 return d == null ? "<none>" : d.Method.ToString ();
1344                         }
1345                 }
1346                 
1347                 #endregion
1348         }
1349 }
1350 #endif