[corlib] Keep original cancellation exception from awaited task
[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 bool ContinueWith (IContinuation continuation, bool canExecuteInline = true)
334                 {
335                         if (IsCompleted) {
336                                 if (!canExecuteInline)
337                                         return false;
338
339                                 continuation.Execute ();
340                                 return true;
341                         }
342                         
343                         continuations.Add (continuation);
344                         
345                         // Retry in case completion was achieved but event adding was too late
346                         if (IsCompleted) {
347                                 continuations.Remove (continuation);
348                                 if (!canExecuteInline)
349                                         return false;
350
351                                 continuation.Execute ();
352                         }
353
354                         return true;
355                 }
356
357                 internal void RemoveContinuation (IContinuation continuation)
358                 {
359                         continuations.Remove (continuation);
360                 }
361
362                 static internal TaskCreationOptions GetCreationOptions (TaskContinuationOptions kind)
363                 {
364                         TaskCreationOptions options = TaskCreationOptions.None;
365                         if ((kind & TaskContinuationOptions.AttachedToParent) > 0)
366                                 options |= TaskCreationOptions.AttachedToParent;
367                         if ((kind & TaskContinuationOptions.PreferFairness) > 0)
368                                 options |= TaskCreationOptions.PreferFairness;
369                         if ((kind & TaskContinuationOptions.LongRunning) > 0)
370                                 options |= TaskCreationOptions.LongRunning;
371                         
372                         return options;
373                 }
374                 #endregion
375                 
376                 #region Internal and protected thingies
377                 internal void Schedule ()
378                 {
379                         Status = TaskStatus.WaitingToRun;
380                         scheduler.QueueTask (this);
381                 }
382                 
383                 void ThreadStart ()
384                 {
385                         /* Allow scheduler to break fairness of deque ordering without
386                          * breaking its semantic (the task can be executed twice but the
387                          * second time it will return immediately
388                          */
389                         if (!executing.TryRelaxedSet ())
390                                 return;
391
392                         // Disable CancellationToken direct cancellation
393                         if (cancellationRegistration != null) {
394                                 cancellationRegistration.Value.Dispose ();
395                                 cancellationRegistration = null;
396                         }
397
398                         // If Task are ran inline on the same thread we might trash these values
399                         var saveCurrent = current;
400                         var saveScheduler = TaskScheduler.Current;
401
402                         current = this;
403 #if NET_4_5
404                         TaskScheduler.Current = HasFlag (creationOptions, TaskCreationOptions.HideScheduler) ? TaskScheduler.Default : scheduler;
405 #else
406                         TaskScheduler.Current = scheduler;
407 #endif
408                         
409                         if (!token.IsCancellationRequested) {
410                                 
411                                 status = TaskStatus.Running;
412                                 
413                                 try {
414                                         InnerInvoke ();
415                                 } catch (OperationCanceledException oce) {
416                                         if (token != CancellationToken.None && oce.CancellationToken == token)
417                                                 CancelReal ();
418                                         else
419                                                 HandleGenericException (oce);
420                                 } catch (Exception e) {
421                                         HandleGenericException (e);
422                                 }
423                         } else {
424                                 CancelReal ();
425                         }
426
427                         if (saveCurrent != null)
428                                 current = saveCurrent;
429                         if (saveScheduler != null)
430                                 TaskScheduler.Current = saveScheduler;
431                         Finish ();
432                 }
433
434                 internal bool TrySetCanceled ()
435                 {
436                         if (IsCompleted)
437                                 return false;
438                         
439                         if (!executing.TryRelaxedSet ()) {
440                                 var sw = new SpinWait ();
441                                 while (!IsCompleted)
442                                         sw.SpinOnce ();
443
444                                 return false;
445                         }
446                         
447                         CancelReal ();
448                         return true;
449                 }
450
451                 internal bool TrySetException (AggregateException aggregate, bool cancellation)
452                 {
453                         if (IsCompleted)
454                                 return false;
455                         
456                         if (!executing.TryRelaxedSet ()) {
457                                 var sw = new SpinWait ();
458                                 while (!IsCompleted)
459                                         sw.SpinOnce ();
460
461                                 return false;
462                         }
463
464                         if (cancellation) {
465                                 ExceptionSlot.Exception = aggregate;
466                                 Thread.MemoryBarrier ();
467
468                                 CancelReal ();
469                         } else {
470                                 HandleGenericException (aggregate);
471                         }
472
473                         return true;
474                 }
475
476                 internal bool TrySetExceptionObserved ()
477                 {
478                         if (exSlot != null) {
479                                 exSlot.Observed = true;
480                                 return true;
481                         }
482                         return false;
483                 }
484
485                 internal void Execute ()
486                 {
487                         ThreadStart ();
488                 }
489                 
490                 internal void AddChild ()
491                 {
492                         if (childTasks == null)
493                                 Interlocked.CompareExchange (ref childTasks, new CountdownEvent (1), null);
494                         childTasks.AddCount ();
495                 }
496
497                 internal void ChildCompleted (AggregateException childEx)
498                 {
499                         if (childEx != null) {
500                                 if (ExceptionSlot.ChildExceptions == null)
501                                         Interlocked.CompareExchange (ref ExceptionSlot.ChildExceptions, new ConcurrentQueue<AggregateException> (), null);
502                                 ExceptionSlot.ChildExceptions.Enqueue (childEx);
503                         }
504
505                         if (childTasks.Signal () && status == TaskStatus.WaitingForChildrenToComplete) {
506                                 ProcessChildExceptions ();
507                                 Status = exSlot == null ? TaskStatus.RanToCompletion : TaskStatus.Faulted;
508                                 ProcessCompleteDelegates ();
509                                 if (parent != null &&
510 #if NET_4_5
511                                     !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
512 #endif
513                                         HasFlag (creationOptions, TaskCreationOptions.AttachedToParent))
514                                         parent.ChildCompleted (this.Exception);
515                         }
516                 }
517
518                 void InnerInvoke ()
519                 {
520                         if (IsContinuation) {
521                                 var ancestor = contAncestor;
522                                 contAncestor = null;
523                                 invoker.Invoke (ancestor, state, this);
524                         } else {
525                                 invoker.Invoke (this, state, this);
526                         }
527                 }
528                 
529                 internal void Finish ()
530                 {
531                         // If there was children created and they all finished, we set the countdown
532                         if (childTasks != null) {
533                                 if (childTasks.Signal ())
534                                         ProcessChildExceptions (true);
535                         }
536                         
537                         // Don't override Canceled or Faulted
538                         if (status == TaskStatus.Running) {
539                                 if (childTasks == null || childTasks.IsSet)
540                                         Status = TaskStatus.RanToCompletion;
541                                 else
542                                         Status = TaskStatus.WaitingForChildrenToComplete;
543                         }
544
545                         if (wait_handle != null)
546                                 wait_handle.Set ();
547
548                         // Tell parent that we are finished
549                         if (parent != null && HasFlag (creationOptions, TaskCreationOptions.AttachedToParent) &&
550 #if NET_4_5
551                             !HasFlag (parent.CreationOptions, TaskCreationOptions.DenyChildAttach) &&
552 #endif
553                                 status != TaskStatus.WaitingForChildrenToComplete) {
554                                 parent.ChildCompleted (this.Exception);
555                         }
556
557                         // Completions are already processed when task is canceled or faulted
558                         if (status == TaskStatus.RanToCompletion)
559                                 ProcessCompleteDelegates ();
560
561                         // Reset the current thingies
562                         if (current == this)
563                                 current = null;
564                         if (TaskScheduler.Current == scheduler)
565                                 TaskScheduler.Current = null;
566
567                         if (cancellationRegistration.HasValue)
568                                 cancellationRegistration.Value.Dispose ();
569                 }
570
571                 void ProcessCompleteDelegates ()
572                 {
573                         if (continuations.HasElements) {
574                                 IContinuation continuation;
575                                 while (continuations.TryGetNextCompletion (out continuation))
576                                         continuation.Execute ();
577                         }
578                 }
579
580                 void ProcessChildExceptions (bool isParent = false)
581                 {
582                         if (exSlot == null || exSlot.ChildExceptions == null)
583                                 return;
584
585                         if (ExceptionSlot.Exception == null)
586                                 exSlot.Exception = new AggregateException ();
587
588                         AggregateException childEx;
589                         while (exSlot.ChildExceptions.TryDequeue (out childEx))
590                                 exSlot.Exception.AddChildException (childEx);
591
592                         if (isParent) {
593                                 Status = TaskStatus.Faulted;
594                                 ProcessCompleteDelegates ();                    
595                         }
596                 }
597                 #endregion
598                 
599                 #region Cancel and Wait related method
600                 
601                 internal void CancelReal ()
602                 {
603                         Status = TaskStatus.Canceled;
604
605                         if (wait_handle != null)
606                                 wait_handle.Set ();
607
608                         ProcessCompleteDelegates ();
609                 }
610
611                 void HandleGenericException (Exception e)
612                 {
613                         HandleGenericException (new AggregateException (e));
614                 }
615
616                 void HandleGenericException (AggregateException e)
617                 {
618                         ExceptionSlot.Exception = e;
619                         Thread.MemoryBarrier ();
620                         Status = TaskStatus.Faulted;
621
622                         if (wait_handle != null)
623                                 wait_handle.Set ();
624
625                         ProcessCompleteDelegates ();
626                 }
627
628                 internal bool WaitOnChildren ()
629                 {
630                         if (Status == TaskStatus.WaitingForChildrenToComplete && childTasks != null) {
631                                 childTasks.Wait ();
632                                 return true;
633                         }
634                         return false;
635                 }
636                 
637                 public void Wait ()
638                 {
639                         Wait (Timeout.Infinite, CancellationToken.None);
640                 }
641
642                 public void Wait (CancellationToken cancellationToken)
643                 {
644                         Wait (Timeout.Infinite, cancellationToken);
645                 }
646                 
647                 public bool Wait (TimeSpan timeout)
648                 {
649                         return Wait (CheckTimeout (timeout), CancellationToken.None);
650                 }
651                 
652                 public bool Wait (int millisecondsTimeout)
653                 {
654                         return Wait (millisecondsTimeout, CancellationToken.None);
655                 }
656
657                 public bool Wait (int millisecondsTimeout, CancellationToken cancellationToken)
658                 {
659                         if (millisecondsTimeout < -1)
660                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
661
662                         bool result = WaitCore (millisecondsTimeout, cancellationToken, true);
663
664                         if (IsCanceled)
665                                 throw new AggregateException (new TaskCanceledException (this));
666
667                         var exception = Exception;
668                         if (exception != null)
669                                 throw exception;
670
671                         return result;
672                 }
673
674                 internal bool WaitCore (int millisecondsTimeout, CancellationToken cancellationToken, bool runInline)
675                 {
676                         if (IsCompleted)
677                                 return true;
678
679                         // If the task is ready to be run and we were supposed to wait on it indefinitely without cancellation, just run it
680                         if (runInline && Status == TaskStatus.WaitingToRun && millisecondsTimeout == Timeout.Infinite && scheduler != null && !cancellationToken.CanBeCanceled)
681                                 scheduler.RunInline (this, true);
682
683                         bool result = true;
684
685                         if (!IsCompleted) {
686                                 var continuation = new ManualResetContinuation ();
687                                 try {
688                                         ContinueWith (continuation);
689                                         result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
690                                 } finally {
691                                         if (!result)
692                                                 RemoveContinuation (continuation);
693                                         continuation.Dispose ();
694                                 }
695                         }
696
697                         return result;
698                 }
699                 
700                 public static void WaitAll (params Task[] tasks)
701                 {
702                         WaitAll (tasks, Timeout.Infinite, CancellationToken.None);
703                 }
704
705                 public static void WaitAll (Task[] tasks, CancellationToken cancellationToken)
706                 {
707                         WaitAll (tasks, Timeout.Infinite, cancellationToken);
708                 }
709                 
710                 public static bool WaitAll (Task[] tasks, TimeSpan timeout)
711                 {
712                         return WaitAll (tasks, CheckTimeout (timeout), CancellationToken.None);
713                 }
714                 
715                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout)
716                 {
717                         return WaitAll (tasks, millisecondsTimeout, CancellationToken.None);
718                 }
719                 
720                 public static bool WaitAll (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
721                 {
722                         if (tasks == null)
723                                 throw new ArgumentNullException ("tasks");
724
725                         bool result = true;
726                         foreach (var t in tasks) {
727                                 if (t == null)
728                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
729
730                                 result &= t.Status == TaskStatus.RanToCompletion;
731                         }
732
733                         if (!result) {
734                                 var continuation = new CountdownContinuation (tasks.Length);
735                                 try {
736                                         foreach (var t in tasks)
737                                                 t.ContinueWith (continuation);
738
739                                         result = continuation.Event.Wait (millisecondsTimeout, cancellationToken);
740                                 } finally {
741                                         List<Exception> exceptions = null;
742
743                                         foreach (var t in tasks) {
744                                                 if (result) {
745                                                         if (t.Status == TaskStatus.RanToCompletion)
746                                                                 continue;
747                                                         if (exceptions == null)
748                                                                 exceptions = new List<Exception> ();
749                                                         if (t.Exception != null)
750                                                                 exceptions.AddRange (t.Exception.InnerExceptions);
751                                                         else
752                                                                 exceptions.Add (new TaskCanceledException (t));
753                                                 } else {
754                                                         t.RemoveContinuation (continuation);
755                                                 }
756                                         }
757
758                                         continuation.Dispose ();
759
760                                         if (exceptions != null)
761                                                 throw new AggregateException (exceptions);
762                                 }
763                         }
764
765                         return result;
766                 }
767                 
768                 public static int WaitAny (params Task[] tasks)
769                 {
770                         return WaitAny (tasks, Timeout.Infinite, CancellationToken.None);
771                 }
772
773                 public static int WaitAny (Task[] tasks, TimeSpan timeout)
774                 {
775                         return WaitAny (tasks, CheckTimeout (timeout));
776                 }
777                 
778                 public static int WaitAny (Task[] tasks, int millisecondsTimeout)
779                 {
780                         return WaitAny (tasks, millisecondsTimeout, CancellationToken.None);
781                 }
782
783                 public static int WaitAny (Task[] tasks, CancellationToken cancellationToken)
784                 {
785                         return WaitAny (tasks, Timeout.Infinite, cancellationToken);
786                 }
787
788                 public static int WaitAny (Task[] tasks, int millisecondsTimeout, CancellationToken cancellationToken)
789                 {
790                         if (tasks == null)
791                                 throw new ArgumentNullException ("tasks");
792                         if (millisecondsTimeout < -1)
793                                 throw new ArgumentOutOfRangeException ("millisecondsTimeout");
794                         CheckForNullTasks (tasks);
795
796                         if (tasks.Length > 0) {
797                                 var continuation = new ManualResetContinuation ();
798                                 bool result = false;
799                                 try {
800                                         for (int i = 0; i < tasks.Length; i++) {
801                                                 var t = tasks[i];
802                                                 if (t.IsCompleted)
803                                                         return i;
804                                                 t.ContinueWith (continuation);
805                                         }
806
807                                         if (!(result = continuation.Event.Wait (millisecondsTimeout, cancellationToken)))
808                                                 return -1;
809                                 } finally {
810                                         if (!result)
811                                                 foreach (var t in tasks)
812                                                         t.RemoveContinuation (continuation);
813                                         continuation.Dispose ();
814                                 }
815                         }
816
817                         int firstFinished = -1;
818                         for (int i = 0; i < tasks.Length; i++) {
819                                 var t = tasks[i];
820                                 if (t.IsCompleted) {
821                                         firstFinished = i;
822                                         break;
823                                 }
824                         }
825
826                         return firstFinished;
827                 }
828
829                 static int CheckTimeout (TimeSpan timeout)
830                 {
831                         try {
832                                 return checked ((int)timeout.TotalMilliseconds);
833                         } catch (System.OverflowException) {
834                                 throw new ArgumentOutOfRangeException ("timeout");
835                         }
836                 }
837
838                 static void CheckForNullTasks (Task[] tasks)
839                 {
840                         foreach (var t in tasks)
841                                 if (t == null)
842                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
843                 }
844                 #endregion
845                 
846                 #region Dispose
847                 public void Dispose ()
848                 {
849                         Dispose (true);                 
850                 }
851                 
852                 protected virtual void Dispose (bool disposing)
853                 {
854                         if (!IsCompleted)
855                                 throw new InvalidOperationException ("A task may only be disposed if it is in a completion state");
856
857                         // Set action to null so that the GC can collect the delegate and thus
858                         // any big object references that the user might have captured in a anonymous method
859                         if (disposing) {
860                                 invoker = null;
861                                 state = null;
862                                 if (cancellationRegistration != null)
863                                         cancellationRegistration.Value.Dispose ();
864                                 if (wait_handle != null)
865                                         wait_handle.Dispose ();
866                         }
867                 }
868                 #endregion
869
870 #if NET_4_5
871                 public
872 #else
873                 internal
874 #endif
875                 Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken,
876                                                                   TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
877                 {
878                         if (continuationAction == null)
879                                 throw new ArgumentNullException ("continuationAction");
880                         if (scheduler == null)
881                                 throw new ArgumentNullException ("scheduler");
882
883                         Task continuation = new Task (TaskActionInvoker.Create (continuationAction),
884                                                                                   state, cancellationToken,
885                                                                                   GetCreationOptions (continuationOptions),
886                                                       parent,
887                                                       this);
888                         ContinueWithCore (continuation, continuationOptions, scheduler);
889
890                         return continuation;
891                 }
892                 
893 #if NET_4_5
894
895                 public ConfiguredTaskAwaitable ConfigureAwait (bool continueOnCapturedContext)
896                 {
897                         return new ConfiguredTaskAwaitable (this, continueOnCapturedContext);
898                 }
899
900                 public Task ContinueWith (Action<Task, object> continuationAction, object state)
901                 {
902                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
903                 }
904
905                 public Task ContinueWith (Action<Task, object> continuationAction, object state, CancellationToken cancellationToken)
906                 {
907                         return ContinueWith (continuationAction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
908                 }
909
910                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskContinuationOptions continuationOptions)
911                 {
912                         return ContinueWith (continuationAction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
913                 }
914
915                 public Task ContinueWith (Action<Task, object> continuationAction, object state, TaskScheduler scheduler)
916                 {
917                         return ContinueWith (continuationAction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
918                 }
919
920                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state)
921                 {
922                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
923                 }
924
925                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskContinuationOptions continuationOptions)
926                 {
927                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, continuationOptions, TaskScheduler.Current);
928                 }
929
930                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken)
931                 {
932                         return ContinueWith<TResult> (continuationFunction, state, cancellationToken, TaskContinuationOptions.None, TaskScheduler.Current);
933                 }
934
935                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, TaskScheduler scheduler)
936                 {
937                         return ContinueWith<TResult> (continuationFunction, state, CancellationToken.None, TaskContinuationOptions.None, scheduler);
938                 }
939
940                 public Task<TResult> ContinueWith<TResult> (Func<Task, object, TResult> continuationFunction, object state, CancellationToken cancellationToken,
941                                                                                                         TaskContinuationOptions continuationOptions, TaskScheduler scheduler)
942                 {
943                         if (continuationFunction == null)
944                                 throw new ArgumentNullException ("continuationFunction");
945                         if (scheduler == null)
946                                 throw new ArgumentNullException ("scheduler");
947
948                         var t = new Task<TResult> (TaskActionInvoker.Create (continuationFunction),
949                                                    state,
950                                                    cancellationToken,
951                                                    GetCreationOptions (continuationOptions),
952                                                    parent,
953                                                    this);
954
955                         ContinueWithCore (t, continuationOptions, scheduler);
956
957                         return t;
958                 }
959
960                 public static Task Delay (int millisecondsDelay)
961                 {
962                         return Delay (millisecondsDelay, CancellationToken.None);
963                 }
964
965                 public static Task Delay (TimeSpan delay)
966                 {
967                         return Delay (CheckTimeout (delay), CancellationToken.None);
968                 }
969
970                 public static Task Delay (TimeSpan delay, CancellationToken cancellationToken)
971                 {
972                         return Delay (CheckTimeout (delay), cancellationToken);
973                 }
974
975                 public static Task Delay (int millisecondsDelay, CancellationToken cancellationToken)
976                 {
977                         if (millisecondsDelay < -1)
978                                 throw new ArgumentOutOfRangeException ("millisecondsDelay");
979
980                         if (cancellationToken.IsCancellationRequested)
981                                 return TaskConstants.Canceled;
982
983                         var task = new Task (TaskActionInvoker.Empty, null, cancellationToken, TaskCreationOptions.None, null, null);
984                         task.SetupScheduler (TaskScheduler.Default);
985
986                         if (millisecondsDelay != Timeout.Infinite) {
987                                 var timer = new Timer (delegate (object state) {
988                                         var t = (Task) state;
989                                         if (t.Status == TaskStatus.WaitingForActivation) {
990                                                 t.Status = TaskStatus.Running;
991                                                 t.Finish ();
992                                         }
993                                 }, task, millisecondsDelay, -1);
994
995                                 task.ContinueWith (new DisposeContinuation (timer));
996                         }
997
998                         return task;
999                 }
1000
1001                 public static Task<TResult> FromResult<TResult> (TResult result)
1002                 {
1003                         var tcs = new TaskCompletionSource<TResult> ();
1004                         tcs.SetResult (result);
1005                         return tcs.Task;
1006                 }
1007
1008                 public TaskAwaiter GetAwaiter ()
1009                 {
1010                         return new TaskAwaiter (this);
1011                 }
1012
1013                 public static Task Run (Action action)
1014                 {
1015                         return Run (action, CancellationToken.None);
1016                 }
1017
1018                 public static Task Run (Action action, CancellationToken cancellationToken)
1019                 {
1020                         if (cancellationToken.IsCancellationRequested)
1021                                 return TaskConstants.Canceled;
1022
1023                         return Task.Factory.StartNew (action, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
1024                 }
1025
1026                 public static Task Run (Func<Task> function)
1027                 {
1028                         return Run (function, CancellationToken.None);
1029                 }
1030
1031                 public static Task Run (Func<Task> function, CancellationToken cancellationToken)
1032                 {
1033                         if (cancellationToken.IsCancellationRequested)
1034                                 return TaskConstants.Canceled;
1035
1036                         return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
1037                 }
1038
1039                 public static Task<TResult> Run<TResult> (Func<TResult> function)
1040                 {
1041                         return Run (function, CancellationToken.None);
1042                 }
1043
1044                 public static Task<TResult> Run<TResult> (Func<TResult> function, CancellationToken cancellationToken)
1045                 {
1046                         if (cancellationToken.IsCancellationRequested)
1047                                 return TaskConstants<TResult>.Canceled;
1048
1049                         return Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
1050                 }
1051
1052                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function)
1053                 {
1054                         return Run (function, CancellationToken.None);
1055                 }
1056
1057                 public static Task<TResult> Run<TResult> (Func<Task<TResult>> function, CancellationToken cancellationToken)
1058                 {
1059                         if (cancellationToken.IsCancellationRequested)
1060                                 return TaskConstants<TResult>.Canceled;
1061
1062                         return TaskExtensionsImpl.Unwrap (Task.Factory.StartNew (function, cancellationToken, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
1063                 }
1064
1065                 public static Task WhenAll (params Task[] tasks)
1066                 {
1067                         if (tasks == null)
1068                                 throw new ArgumentNullException ("tasks");
1069
1070                         return WhenAllCore (tasks);
1071                 }
1072
1073                 public static Task WhenAll (IEnumerable<Task> 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 (new List<Task> (tasks));
1081                 }
1082
1083                 public static Task<TResult[]> WhenAll<TResult> (params Task<TResult>[] tasks)
1084                 {
1085                         if (tasks == null)
1086                                 throw new ArgumentNullException ("tasks");
1087
1088                         return WhenAllCore<TResult> (tasks);
1089                 }
1090
1091                 public static Task<TResult[]> WhenAll<TResult> (IEnumerable<Task<TResult>> tasks)
1092                 {
1093                         if (tasks == null)
1094                                 throw new ArgumentNullException ("tasks");
1095
1096                         // Call ToList on input enumeration or we end up
1097                         // enumerating it more than once
1098                         return WhenAllCore<TResult> (new List<Task<TResult>> (tasks));
1099                 }
1100
1101                 internal static Task<TResult[]> WhenAllCore<TResult> (IList<Task<TResult>> tasks)
1102                 {
1103                         if (tasks.Count == 0)
1104                                 return FromResult(new TResult[0]);
1105
1106                         foreach (var t in tasks) {
1107                                 if (t == null)
1108                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1109                         }
1110
1111                         var task = new Task<TResult[]> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1112                         task.SetupScheduler (TaskScheduler.Current);
1113
1114                         var continuation = new WhenAllContinuation<TResult> (task, tasks);
1115                         foreach (var t in tasks)
1116                                 t.ContinueWith (continuation);
1117
1118                         return task;
1119                 }
1120
1121                 public static Task<Task> WhenAny (params Task[] tasks)
1122                 {
1123                         if (tasks == null)
1124                                 throw new ArgumentNullException ("tasks");
1125
1126                         return WhenAnyCore (tasks);
1127                 }
1128
1129                 public static Task<Task> WhenAny (IEnumerable<Task> tasks)
1130                 {
1131                         if (tasks == null)
1132                                 throw new ArgumentNullException ("tasks");
1133
1134                         return WhenAnyCore (new List<Task> (tasks));
1135                 }
1136
1137                 public static Task<Task<TResult>> WhenAny<TResult> (params Task<TResult>[] tasks)
1138                 {
1139                         if (tasks == null)
1140                                 throw new ArgumentNullException ("tasks");
1141
1142                         return WhenAnyCore<TResult> (tasks);
1143                 }
1144
1145                 public static Task<Task<TResult>> WhenAny<TResult> (IEnumerable<Task<TResult>> tasks)
1146                 {
1147                         if (tasks == null)
1148                                 throw new ArgumentNullException ("tasks");
1149
1150                         return WhenAnyCore<TResult> (new List<Task<TResult>> (tasks));
1151                 }
1152
1153                 static Task<Task<TResult>> WhenAnyCore<TResult> (IList<Task<TResult>> tasks)
1154                 {
1155                         if (tasks.Count == 0)
1156                                 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1157
1158                         int completed_index = -1;
1159                         for (int i = 0; i < tasks.Count; ++i) {
1160                                 var t = tasks[i];
1161                                 if (t == null)
1162                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1163
1164                                 if (t.IsCompleted && completed_index < 0)
1165                                         completed_index = i;
1166                         }
1167
1168                         var task = new Task<Task<TResult>> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1169
1170                         if (completed_index > 0) {
1171                                 task.TrySetResult (tasks[completed_index]);
1172                                 return task;
1173                         }
1174
1175                         task.SetupScheduler (TaskScheduler.Current);
1176
1177                         var continuation = new WhenAnyContinuation<Task<TResult>> (task, tasks);
1178                         foreach (var t in tasks)
1179                                 t.ContinueWith (continuation);
1180
1181                         return task;
1182                 }
1183
1184                 public static YieldAwaitable Yield ()
1185                 {
1186                         return new YieldAwaitable ();
1187                 }
1188 #endif
1189
1190                 internal static Task WhenAllCore (IList<Task> tasks)
1191                 {
1192                         bool all_completed = true;
1193                         foreach (var t in tasks) {
1194                                 if (t == null)
1195                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1196
1197                                 all_completed &= t.Status == TaskStatus.RanToCompletion;
1198                         }
1199
1200                         if (all_completed)
1201                                 return TaskConstants.Finished;
1202
1203                         var task = new Task (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1204                         task.SetupScheduler (TaskScheduler.Current);
1205
1206                         var continuation = new WhenAllContinuation (task, tasks);
1207                         foreach (var t in tasks)
1208                                 t.ContinueWith (continuation);
1209
1210                         return task;
1211                 }
1212
1213                 internal static Task<Task> WhenAnyCore (IList<Task> tasks)
1214                 {
1215                         if (tasks.Count == 0)
1216                                 throw new ArgumentException ("The tasks argument contains no tasks", "tasks");
1217
1218                         int completed_index = -1;
1219                         for (int i = 0; i < tasks.Count; ++i) {
1220                                 var t = tasks [i];
1221                                 if (t == null)
1222                                         throw new ArgumentException ("tasks", "the tasks argument contains a null element");
1223
1224                                 if (t.IsCompleted && completed_index < 0)
1225                                         completed_index = i;
1226                         }
1227
1228                         var task = new Task<Task> (TaskActionInvoker.Empty, null, CancellationToken.None, TaskCreationOptions.None, null, TaskConstants.Finished);
1229
1230                         if (completed_index > 0) {
1231                                 task.TrySetResult (tasks[completed_index]);
1232                                 return task;
1233                         }
1234
1235                         task.SetupScheduler (TaskScheduler.Current);
1236
1237                         var continuation = new WhenAnyContinuation<Task> (task, tasks);
1238                         foreach (var t in tasks)
1239                                 t.ContinueWith (continuation);
1240
1241                         return task;
1242                 }
1243                 #region Properties
1244
1245                 internal CancellationToken CancellationToken {
1246                         get {
1247                                 return token;
1248                         }
1249                 }
1250
1251                 public static TaskFactory Factory {
1252                         get {
1253                                 return defaultFactory;
1254                         }
1255                 }
1256                 
1257                 public static int? CurrentId {
1258                         get {
1259                                 Task t = current;
1260                                 return t == null ? (int?)null : t.Id;
1261                         }
1262                 }
1263                 
1264                 public AggregateException Exception {
1265                         get {
1266                                 if (exSlot == null || !IsFaulted)
1267                                         return null;
1268                                 exSlot.Observed = true;
1269                                 return exSlot.Exception;
1270                         }
1271                 }
1272                 
1273                 public bool IsCanceled {
1274                         get {
1275                                 return status == TaskStatus.Canceled;
1276                         }
1277                 }
1278
1279                 public bool IsCompleted {
1280                         get {
1281                                 return status >= TaskStatus.RanToCompletion;
1282                         }
1283                 }
1284                 
1285                 public bool IsFaulted {
1286                         get {
1287                                 return status == TaskStatus.Faulted;
1288                         }
1289                 }
1290
1291                 public TaskCreationOptions CreationOptions {
1292                         get {
1293                                 return creationOptions & MaxTaskCreationOptions;
1294                         }
1295                 }
1296                 
1297                 public TaskStatus Status {
1298                         get {
1299                                 return status;
1300                         }
1301                         internal set {
1302                                 status = value;
1303                                 Thread.MemoryBarrier ();
1304                         }
1305                 }
1306
1307                 internal TaskExceptionSlot ExceptionSlot {
1308                         get {
1309                                 if (exSlot != null)
1310                                         return exSlot;
1311                                 Interlocked.CompareExchange (ref exSlot, new TaskExceptionSlot (this), null);
1312                                 return exSlot;
1313                         }
1314                 }
1315
1316                 public object AsyncState {
1317                         get {
1318                                 return state;
1319                         }
1320                 }
1321                 
1322                 bool IAsyncResult.CompletedSynchronously {
1323                         get {
1324                                 return true;
1325                         }
1326                 }
1327
1328                 WaitHandle IAsyncResult.AsyncWaitHandle {
1329                         get {
1330                                 if (invoker == null)
1331                                         throw new ObjectDisposedException (GetType ().ToString ());
1332
1333                                 if (wait_handle == null)
1334                                         Interlocked.CompareExchange (ref wait_handle, new ManualResetEvent (IsCompleted), null);
1335
1336                                 return wait_handle;
1337                         }
1338                 }
1339                 
1340                 public int Id {
1341                         get {
1342                                 return taskId;
1343                         }
1344                 }
1345
1346                 bool IsContinuation {
1347                         get {
1348                                 return contAncestor != null;
1349                         }
1350                 }
1351
1352                 internal Task ContinuationAncestor {
1353                         get {
1354                                 return contAncestor;
1355                         }
1356                 }
1357                 
1358                 internal string DisplayActionMethod {
1359                         get {
1360                                 Delegate d = invoker.Action;
1361                                 return d == null ? "<none>" : d.Method.ToString ();
1362                         }
1363                 }
1364                 
1365                 #endregion
1366         }
1367 }
1368 #endif