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