Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / mscorlib / system / threading / Tasks / TaskContinuation.cs
1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
7 //
8 // TaskContinuation.cs
9 //
10 // <OWNER>[....]</OWNER>
11 //
12 // Implementation of task continuations, TaskContinuation, and its descendants.
13 //
14 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
15
16 using System.Security;
17 using System.Diagnostics.Contracts;
18 using System.Runtime.ExceptionServices;
19 using System.Runtime.CompilerServices;
20 using System.Threading;
21
22 #if FEATURE_COMINTEROP
23 using System.Runtime.InteropServices.WindowsRuntime;
24 #endif // FEATURE_COMINTEROP
25
26 namespace System.Threading.Tasks
27 {
28     // Task type used to implement: Task ContinueWith(Action<Task,...>)
29     internal sealed class ContinuationTaskFromTask : Task
30     {
31         private Task m_antecedent;
32
33         public ContinuationTaskFromTask(
34             Task antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) :
35             base(action, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null)
36         {
37             Contract.Requires(action is Action<Task> || action is Action<Task, object>, 
38                 "Invalid delegate type in ContinuationTaskFromTask");
39             m_antecedent = antecedent;
40             PossiblyCaptureContext(ref stackMark);
41         }
42
43         /// <summary>
44         /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
45         /// </summary>        
46         internal override void InnerInvoke()
47         {
48             // Get and null out the antecedent.  This is crucial to avoid a memory
49             // leak with long chains of continuations.
50             var antecedent = m_antecedent;
51             Contract.Assert(antecedent != null, 
52                 "No antecedent was set for the ContinuationTaskFromTask.");
53             m_antecedent = null;
54
55             // Notify the debugger we're completing an asynchronous wait on a task
56             antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
57
58             // Invoke the delegate
59             Contract.Assert(m_action != null);
60             var action = m_action as Action<Task>;
61             if (action != null)
62             {
63                 action(antecedent);
64                 return;
65             }
66             var actionWithState = m_action as Action<Task, object>;
67             if (actionWithState != null)
68             {
69                 actionWithState(antecedent, m_stateObject);
70                 return;
71             }
72             Contract.Assert(false, "Invalid m_action in ContinuationTaskFromTask");
73         }
74     }
75
76     // Task type used to implement: Task<TResult> ContinueWith(Func<Task,...>)
77     internal sealed class ContinuationResultTaskFromTask<TResult> : Task<TResult>
78     {
79         private Task m_antecedent;
80
81         public ContinuationResultTaskFromTask(
82             Task antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) :
83             base(function, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null)
84         {
85             Contract.Requires(function is Func<Task, TResult> || function is Func<Task, object, TResult>, 
86                 "Invalid delegate type in ContinuationResultTaskFromTask");
87             m_antecedent = antecedent;
88             PossiblyCaptureContext(ref stackMark);
89         }
90
91         /// <summary>
92         /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
93         /// </summary>        
94         internal override void InnerInvoke()
95         {
96             // Get and null out the antecedent.  This is crucial to avoid a memory
97             // leak with long chains of continuations.
98             var antecedent = m_antecedent;
99             Contract.Assert(antecedent != null, 
100                 "No antecedent was set for the ContinuationResultTaskFromTask.");
101             m_antecedent = null;
102
103             // Notify the debugger we're completing an asynchronous wait on a task
104             antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
105
106             // Invoke the delegate
107             Contract.Assert(m_action != null);
108             var func = m_action as Func<Task, TResult>;
109             if (func != null)
110             {
111                 m_result = func(antecedent);
112                 return;
113             }
114             var funcWithState = m_action as Func<Task, object, TResult>;
115             if (funcWithState != null)
116             {
117                 m_result = funcWithState(antecedent, m_stateObject);
118                 return;
119             }
120             Contract.Assert(false, "Invalid m_action in ContinuationResultTaskFromTask");
121         }
122     }
123
124     // Task type used to implement: Task ContinueWith(Action<Task<TAntecedentResult>,...>)
125     internal sealed class ContinuationTaskFromResultTask<TAntecedentResult> : Task
126     {
127         private Task<TAntecedentResult> m_antecedent;
128
129         public ContinuationTaskFromResultTask(
130             Task<TAntecedentResult> antecedent, Delegate action, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) :
131             base(action, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null)
132         {
133             Contract.Requires(action is Action<Task<TAntecedentResult>> || action is Action<Task<TAntecedentResult>, object>, 
134                 "Invalid delegate type in ContinuationTaskFromResultTask");
135             m_antecedent = antecedent;
136             PossiblyCaptureContext(ref stackMark);
137         }
138
139         /// <summary>
140         /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
141         /// </summary>
142         internal override void InnerInvoke()
143         {
144             // Get and null out the antecedent.  This is crucial to avoid a memory
145             // leak with long chains of continuations.
146             var antecedent = m_antecedent;
147             Contract.Assert(antecedent != null, 
148                 "No antecedent was set for the ContinuationTaskFromResultTask.");
149             m_antecedent = null;
150
151             // Notify the debugger we're completing an asynchronous wait on a task
152             antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
153
154             // Invoke the delegate
155             Contract.Assert(m_action != null);
156             var action = m_action as Action<Task<TAntecedentResult>>;
157             if (action != null)
158             {
159                 action(antecedent);
160                 return;
161             }
162             var actionWithState = m_action as Action<Task<TAntecedentResult>, object>;
163             if (actionWithState != null)
164             {
165                 actionWithState(antecedent, m_stateObject);
166                 return;
167             }
168             Contract.Assert(false, "Invalid m_action in ContinuationTaskFromResultTask");
169         }
170     }
171
172     // Task type used to implement: Task<TResult> ContinueWith(Func<Task<TAntecedentResult>,...>)
173     internal sealed class ContinuationResultTaskFromResultTask<TAntecedentResult, TResult> : Task<TResult>
174     {
175         private Task<TAntecedentResult> m_antecedent;
176
177         public ContinuationResultTaskFromResultTask(
178             Task<TAntecedentResult> antecedent, Delegate function, object state, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark) :
179             base(function, state, Task.InternalCurrentIfAttached(creationOptions), default(CancellationToken), creationOptions, internalOptions, null)
180         {
181             Contract.Requires(function is Func<Task<TAntecedentResult>, TResult> || function is Func<Task<TAntecedentResult>, object, TResult>,
182                 "Invalid delegate type in ContinuationResultTaskFromResultTask");
183             m_antecedent = antecedent;
184             PossiblyCaptureContext(ref stackMark);
185         }
186
187         /// <summary>
188         /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
189         /// </summary>
190         internal override void InnerInvoke()
191         {
192             // Get and null out the antecedent.  This is crucial to avoid a memory
193             // leak with long chains of continuations.
194             var antecedent = m_antecedent;
195             Contract.Assert(antecedent != null, 
196                 "No antecedent was set for the ContinuationResultTaskFromResultTask.");
197             m_antecedent = null;
198
199             // Notify the debugger we're completing an asynchronous wait on a task
200             antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
201
202             // Invoke the delegate
203             Contract.Assert(m_action != null);
204             var func = m_action as Func<Task<TAntecedentResult>, TResult>;
205             if (func != null)
206             {
207                 m_result = func(antecedent);
208                 return;
209             }
210             var funcWithState = m_action as Func<Task<TAntecedentResult>, object, TResult>;
211             if (funcWithState != null)
212             {
213                 m_result = funcWithState(antecedent, m_stateObject);
214                 return;
215             }
216             Contract.Assert(false, "Invalid m_action in ContinuationResultTaskFromResultTask");
217         }
218     }
219
220     // For performance reasons, we don't just have a single way of representing
221     // a continuation object.  Rather, we have a hierarchy of types:
222     // - TaskContinuation: abstract base that provides a virtual Run method
223     //     - StandardTaskContinuation: wraps a task,options,and scheduler, and overrides Run to process the task with that configuration
224     //     - AwaitTaskContinuation: base for continuations created through TaskAwaiter; targets default scheduler by default
225     //         - TaskSchedulerAwaitTaskContinuation: awaiting with a non-default TaskScheduler
226     //         - SynchronizationContextAwaitTaskContinuation: awaiting with a "current" [....] ctx
227
228     /// <summary>Represents a continuation.</summary>
229     internal abstract class TaskContinuation
230     {
231         /// <summary>Inlines or schedules the continuation.</summary>
232         /// <param name="completedTask">The antecedent task that has completed.</param>
233         /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
234         internal abstract void Run(Task completedTask, bool bCanInlineContinuationTask);
235
236         /// <summary>Tries to run the task on the current thread, if possible; otherwise, schedules it.</summary>
237         /// <param name="task">The task to run</param>
238         /// <param name="needsProtection">
239         /// true if we need to protect against multiple threads racing to start/cancel the task; otherwise, false.
240         /// </param>
241         [SecuritySafeCritical]
242         protected static void InlineIfPossibleOrElseQueue(Task task, bool needsProtection)
243         {
244             Contract.Requires(task != null);
245             Contract.Assert(task.m_taskScheduler != null);
246
247             // Set the TASK_STATE_STARTED flag.  This only needs to be done
248             // if the task may be canceled or if someone else has a reference to it
249             // that may try to execute it.
250             if (needsProtection)
251             {
252                 if (!task.MarkStarted())
253                     return; // task has been previously started or canceled.  Stop processing.
254             }
255             else
256             {
257                 task.m_stateFlags |= Task.TASK_STATE_STARTED;
258             }
259
260             // Try to inline it but queue if we can't
261             try
262             {
263                 if (!task.m_taskScheduler.TryRunInline(task, taskWasPreviouslyQueued: false))
264                 {
265                     task.m_taskScheduler.InternalQueueTask(task);
266                 }
267             }
268             catch (Exception e)
269             {
270                 // Either TryRunInline() or QueueTask() threw an exception. Record the exception, marking the task as Faulted.
271                 // However if it was a ThreadAbortException coming from TryRunInline we need to skip here, 
272                 // because it would already have been handled in Task.Execute()
273                 if (!(e is ThreadAbortException &&
274                       (task.m_stateFlags & Task.TASK_STATE_THREAD_WAS_ABORTED) != 0))    // this ensures TAEs from QueueTask will be wrapped in TSE
275                 {
276                     TaskSchedulerException tse = new TaskSchedulerException(e);
277                     task.AddException(tse);
278                     task.Finish(false);
279                 }
280
281                 // Don't re-throw.
282             }
283         }
284
285         internal abstract Delegate[] GetDelegateContinuationsForDebugger();
286
287     }
288
289     /// <summary>Provides the standard implementation of a task continuation.</summary>
290     internal class StandardTaskContinuation : TaskContinuation
291     {
292         /// <summary>The unstarted continuation task.</summary>
293         internal readonly Task m_task;
294         /// <summary>The options to use with the continuation task.</summary>
295         internal readonly TaskContinuationOptions m_options;
296         /// <summary>The task scheduler with which to run the continuation task.</summary>
297         private readonly TaskScheduler m_taskScheduler;
298
299         /// <summary>Initializes a new continuation.</summary>
300         /// <param name="task">The task to be activated.</param>
301         /// <param name="options">The continuation options.</param>
302         /// <param name="scheduler">The scheduler to use for the continuation.</param>
303         internal StandardTaskContinuation(Task task, TaskContinuationOptions options, TaskScheduler scheduler)
304         {
305             Contract.Requires(task != null, "TaskContinuation ctor: task is null");
306             Contract.Requires(scheduler != null, "TaskContinuation ctor: scheduler is null");
307             m_task = task;
308             m_options = options;
309             m_taskScheduler = scheduler;
310             if (AsyncCausalityTracer.LoggingOn)
311                 AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, m_task.Id, "Task.ContinueWith: " + ((Delegate)task.m_action).Method.Name, 0);
312
313             if (Task.s_asyncDebuggingEnabled)
314             {
315                 Task.AddToActiveTasks(m_task);
316             }
317         }
318
319         /// <summary>Invokes the continuation for the target completion task.</summary>
320         /// <param name="completedTask">The completed task.</param>
321         /// <param name="bCanInlineContinuationTask">Whether the continuation can be inlined.</param>
322         internal override void Run(Task completedTask, bool bCanInlineContinuationTask)
323         {
324             Contract.Assert(completedTask != null);
325             Contract.Assert(completedTask.IsCompleted, "ContinuationTask.Run(): completedTask not completed");
326
327             // Check if the completion status of the task works with the desired 
328             // activation criteria of the TaskContinuationOptions.
329             TaskContinuationOptions options = m_options;
330             bool isRightKind =
331                 completedTask.IsRanToCompletion ?
332                     (options & TaskContinuationOptions.NotOnRanToCompletion) == 0 :
333                     (completedTask.IsCanceled ?
334                         (options & TaskContinuationOptions.NotOnCanceled) == 0 :
335                         (options & TaskContinuationOptions.NotOnFaulted) == 0);
336
337             // If the completion status is allowed, run the continuation.
338             Task continuationTask = m_task;
339             if (isRightKind)
340             {
341                 //If the task was cancel before running (e.g a ContinueWhenAll with a cancelled caancelation token)
342                 //we will still flow it to ScheduleAndStart() were it will check the status before running
343                 //We check here to avoid faulty logs that contain a join event to an operation that was already set as completed.
344                 if (!continuationTask.IsCanceled && AsyncCausalityTracer.LoggingOn)
345                 {
346                     // Log now that we are sure that this continuation is being ran
347                     AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, continuationTask.Id, CausalityRelation.AssignDelegate);
348                 }
349                 continuationTask.m_taskScheduler = m_taskScheduler;
350
351                 // Either run directly or just queue it up for execution, depending
352                 // on whether synchronous or asynchronous execution is wanted.
353                 if (bCanInlineContinuationTask && // inlining is allowed by the caller
354                     (options & TaskContinuationOptions.ExecuteSynchronously) != 0) // synchronous execution was requested by the continuation's creator
355                 {
356                     InlineIfPossibleOrElseQueue(continuationTask, needsProtection: true);
357                 }
358                 else
359                 {
360                     try { continuationTask.ScheduleAndStart(needsProtection: true); }
361                     catch (TaskSchedulerException)
362                     {
363                         // No further action is necessary -- ScheduleAndStart() already transitioned the 
364                         // task to faulted.  But we want to make sure that no exception is thrown from here.
365                     }
366                 }
367             }
368             // Otherwise, the final state of this task does not match the desired
369             // continuation activation criteria; cancel it to denote this.
370             else continuationTask.InternalCancel(false);
371         }
372
373         internal override Delegate[] GetDelegateContinuationsForDebugger()
374         {
375             if (m_task.m_action == null)
376             {
377                 return m_task.GetDelegateContinuationsForDebugger();
378             }
379
380             return new Delegate[] { m_task.m_action as Delegate };
381         }
382     }
383
384     /// <summary>Task continuation for awaiting with a current synchronization context.</summary>
385     internal sealed class SynchronizationContextAwaitTaskContinuation : AwaitTaskContinuation
386     {
387         /// <summary>SendOrPostCallback delegate to invoke the action.</summary>
388         private readonly static SendOrPostCallback s_postCallback = state => ((Action)state)(); // can't use InvokeAction as it's SecurityCritical
389         /// <summary>Cached delegate for PostAction</summary>
390         [SecurityCritical]
391         private static ContextCallback s_postActionCallback;
392         /// <summary>The context with which to run the action.</summary>
393         private readonly SynchronizationContext m_syncContext;
394
395         /// <summary>Initializes the SynchronizationContextAwaitTaskContinuation.</summary>
396         /// <param name="context">The synchronization context with which to invoke the action.  Must not be null.</param>
397         /// <param name="action">The action to invoke. Must not be null.</param>
398         /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
399         /// <param name="stackMark">The captured stack mark.</param>
400         [SecurityCritical]
401         internal SynchronizationContextAwaitTaskContinuation(
402             SynchronizationContext context, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) :
403             base(action, flowExecutionContext, ref stackMark)
404         {
405             Contract.Assert(context != null);
406             m_syncContext = context;
407         }
408
409         /// <summary>Inlines or schedules the continuation.</summary>
410         /// <param name="ignored">The antecedent task, which is ignored.</param>
411         /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
412         [SecuritySafeCritical]
413         internal sealed override void Run(Task task, bool canInlineContinuationTask)
414         {
415             // If we're allowed to inline, run the action on this thread.
416             if (canInlineContinuationTask &&
417                 m_syncContext == SynchronizationContext.CurrentNoFlow)
418             {
419                 RunCallback(GetInvokeActionCallback(), m_action, ref Task.t_currentTask);
420             }
421             // Otherwise, Post the action back to the SynchronizationContext.
422             else
423             {
424                 TplEtwProvider etwLog = TplEtwProvider.Log;
425                 if (etwLog.IsEnabled())
426                 {
427                     m_continuationId = Task.NewId();
428                     etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
429                 }
430                 RunCallback(GetPostActionCallback(), this, ref Task.t_currentTask);
431             }
432             // Any exceptions will be handled by RunCallback.
433         }
434
435         /// <summary>Calls InvokeOrPostAction(false) on the supplied SynchronizationContextAwaitTaskContinuation.</summary>
436         /// <param name="state">The SynchronizationContextAwaitTaskContinuation.</param>
437         [SecurityCritical]
438         private static void PostAction(object state)
439         {
440             var c = (SynchronizationContextAwaitTaskContinuation)state;
441
442             TplEtwProvider etwLog = TplEtwProvider.Log;
443             if (etwLog.TasksSetActivityIds && c.m_continuationId != 0)
444             {
445                 c.m_syncContext.Post(s_postCallback, GetActionLogDelegate(c.m_continuationId, c.m_action));
446             }
447             else
448             {
449                 c.m_syncContext.Post(s_postCallback, c.m_action); // s_postCallback is manually cached, as the compiler won't in a SecurityCritical method
450             }
451         }
452
453         private static Action GetActionLogDelegate(int continuationId, Action action)
454         {
455             return () =>
456                 {
457                     Guid savedActivityId;
458                     Guid activityId = TplEtwProvider.CreateGuidForTaskID(continuationId);
459                     System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(activityId, out savedActivityId);
460                     try { action(); }
461                     finally { System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(savedActivityId); }
462                 };
463         }
464
465         /// <summary>Gets a cached delegate for the PostAction method.</summary>
466         /// <returns>
467         /// A delegate for PostAction, which expects a SynchronizationContextAwaitTaskContinuation 
468         /// to be passed as state.
469         /// </returns>
470         [MethodImpl(MethodImplOptions.AggressiveInlining)]
471         [SecurityCritical]
472         private static ContextCallback GetPostActionCallback()
473         {
474             ContextCallback callback = s_postActionCallback;
475             if (callback == null) { s_postActionCallback = callback = PostAction; } // lazily initialize SecurityCritical delegate
476             return callback;
477         }
478     }
479
480     /// <summary>Task continuation for awaiting with a task scheduler.</summary>
481     internal sealed class TaskSchedulerAwaitTaskContinuation : AwaitTaskContinuation
482     {
483         /// <summary>The scheduler on which to run the action.</summary>
484         private readonly TaskScheduler m_scheduler;
485
486         /// <summary>Initializes the TaskSchedulerAwaitTaskContinuation.</summary>
487         /// <param name="scheduler">The task scheduler with which to invoke the action.  Must not be null.</param>
488         /// <param name="action">The action to invoke. Must not be null.</param>
489         /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
490         /// <param name="stackMark">The captured stack mark.</param>
491         [SecurityCritical]
492         internal TaskSchedulerAwaitTaskContinuation(
493             TaskScheduler scheduler, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) :
494             base(action, flowExecutionContext, ref stackMark)
495         {
496             Contract.Assert(scheduler != null);
497             m_scheduler = scheduler;
498         }
499
500         /// <summary>Inlines or schedules the continuation.</summary>
501         /// <param name="ignored">The antecedent task, which is ignored.</param>
502         /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
503         internal sealed override void Run(Task ignored, bool canInlineContinuationTask)
504         {
505             // If we're targeting the default scheduler, we can use the faster path provided by the base class.
506             if (m_scheduler == TaskScheduler.Default)
507             {
508                 base.Run(ignored, canInlineContinuationTask);
509             }
510             else
511             {
512                 // We permit inlining if the caller allows us to, and 
513                 // either we're on a thread pool thread (in which case we're fine running arbitrary code)
514                 // or we're already on the target scheduler (in which case we'll just ask the scheduler
515                 // whether it's ok to run here).  We include the IsThreadPoolThread check here, whereas
516                 // we don't in AwaitTaskContinuation.Run, since here it expands what's allowed as opposed
517                 // to in AwaitTaskContinuation.Run where it restricts what's allowed.
518                 bool inlineIfPossible = canInlineContinuationTask &&
519                     (TaskScheduler.InternalCurrent == m_scheduler || Thread.CurrentThread.IsThreadPoolThread);
520
521                 // Create the continuation task task. If we're allowed to inline, try to do so.  
522                 // The target scheduler may still deny us from executing on this thread, in which case this'll be queued.
523                 var task = CreateTask(state => {
524                     try { ((Action)state)(); }
525                     catch (Exception exc) { ThrowAsyncIfNecessary(exc); }
526                 }, m_action, m_scheduler);
527
528                 if (inlineIfPossible)
529                 {
530                     InlineIfPossibleOrElseQueue(task, needsProtection: false);
531                 }
532                 else
533                 {
534                     // We need to run asynchronously, so just schedule the task.
535                     try { task.ScheduleAndStart(needsProtection: false); }
536                     catch (TaskSchedulerException) { } // No further action is necessary, as ScheduleAndStart already transitioned task to faulted
537                 }
538             }
539         }
540     }
541
542     /// <summary>Base task continuation class used for await continuations.</summary>
543     internal class AwaitTaskContinuation : TaskContinuation, IThreadPoolWorkItem
544     {
545         /// <summary>The ExecutionContext with which to run the continuation.</summary>
546         private readonly ExecutionContext m_capturedContext;
547         /// <summary>The action to invoke.</summary>
548         protected readonly Action m_action;
549
550         protected int m_continuationId;
551
552         /// <summary>Initializes the continuation.</summary>
553         /// <param name="action">The action to invoke. Must not be null.</param>
554         /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
555         /// <param name="stackMark">The captured stack mark with which to construct an ExecutionContext.</param>
556         [SecurityCritical]
557         internal AwaitTaskContinuation(Action action, bool flowExecutionContext, ref StackCrawlMark stackMark)
558         {
559             Contract.Requires(action != null);
560             m_action = action;
561             if (flowExecutionContext)
562             {
563                 m_capturedContext = ExecutionContext.Capture(
564                     ref stackMark, 
565                     ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
566             }
567         }
568
569         /// <summary>Initializes the continuation.</summary>
570         /// <param name="action">The action to invoke. Must not be null.</param>
571         /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
572         [SecurityCritical]
573         internal AwaitTaskContinuation(Action action, bool flowExecutionContext)
574         {
575             Contract.Requires(action != null);
576             m_action = action;
577             if (flowExecutionContext)
578             {
579                 m_capturedContext = ExecutionContext.FastCapture();
580             }
581         }
582
583         /// <summary>Creates a task to run the action with the specified state on the specified scheduler.</summary>
584         /// <param name="action">The action to run. Must not be null.</param>
585         /// <param name="state">The state to pass to the action. Must not be null.</param>
586         /// <param name="scheduler">The scheduler to target.</param>
587         /// <returns>The created task.</returns>
588         protected Task CreateTask(Action<object> action, object state, TaskScheduler scheduler)
589         {
590             Contract.Requires(action != null);
591             Contract.Requires(scheduler != null);
592
593             return new Task(
594                 action, state, null, default(CancellationToken), 
595                 TaskCreationOptions.None, InternalTaskOptions.QueuedByRuntime, scheduler)  
596             { 
597                 CapturedContext = m_capturedContext 
598             };
599         }
600
601         /// <summary>Inlines or schedules the continuation onto the default scheduler.</summary>
602         /// <param name="ignored">The antecedent task, which is ignored.</param>
603         /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
604         [SecuritySafeCritical]
605         internal override void Run(Task task, bool canInlineContinuationTask)
606         {
607             // For the base AwaitTaskContinuation, we allow inlining if our caller allows it
608             // and if we're in a "valid location" for it.  See the comments on 
609             // IsValidLocationForInlining for more about what's valid.  For performance
610             // reasons we would like to always inline, but we don't in some cases to avoid
611             // running arbitrary amounts of work in suspected "bad locations", like UI threads.
612             if (canInlineContinuationTask && IsValidLocationForInlining)
613             {
614                 RunCallback(GetInvokeActionCallback(), m_action, ref Task.t_currentTask); // any exceptions from m_action will be handled by s_callbackRunAction
615             }
616             else
617             {
618                 TplEtwProvider etwLog = TplEtwProvider.Log;
619                 if (etwLog.IsEnabled())
620                 {
621                     m_continuationId = Task.NewId();
622                     etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
623                 }
624
625                 // We couldn't inline, so now we need to schedule it
626                 ThreadPool.UnsafeQueueCustomWorkItem(this, forceGlobal: false);
627            }
628         }
629
630         /// <summary>
631         /// Gets whether the current thread is an appropriate location to inline a continuation's execution.
632         /// </summary>
633         /// <remarks>
634         /// Returns whether SynchronizationContext is null and we're in the default scheduler.
635         /// If the await had a SynchronizationContext/TaskScheduler where it began and the 
636         /// default/ConfigureAwait(true) was used, then we won't be on this path.  If, however, 
637         /// ConfigureAwait(false) was used, or the SynchronizationContext and TaskScheduler were 
638         /// naturally null/Default, then we might end up here.  If we do, we need to make sure
639         /// that we don't execute continuations in a place that isn't set up to handle them, e.g.
640         /// running arbitrary amounts of code on the UI thread.  It would be "correct", but very
641         /// expensive, to always run the continuations asynchronously, incurring lots of context
642         /// switches and allocations and locks and the like.  As such, we employ the heuristic
643         /// that if the current thread has a non-null SynchronizationContext or a non-default
644         /// scheduler, then we better not run arbitrary continuations here.
645         /// </remarks>
646         internal static bool IsValidLocationForInlining
647         {
648             get
649             {
650                 // If there's a SynchronizationContext, we'll be conservative and say 
651                 // this is a bad location to inline.
652                 var ctx = SynchronizationContext.CurrentNoFlow;
653                 if (ctx != null && ctx.GetType() != typeof(SynchronizationContext)) return false;
654
655                 // Similarly, if there's a non-default TaskScheduler, we'll be conservative
656                 // and say this is a bad location to inline.
657                 var sched = TaskScheduler.InternalCurrent;
658                 return sched == null || sched == TaskScheduler.Default;
659             }
660         }
661
662         /// <summary>IThreadPoolWorkItem override, which is the entry function for this when the ThreadPool scheduler decides to run it.</summary>
663         [SecurityCritical]
664         void ExecuteWorkItemHelper()
665         {
666             var etwLog = TplEtwProvider.Log;
667             Guid savedActivityId = Guid.Empty;
668             if (etwLog.TasksSetActivityIds && m_continuationId != 0)
669             {
670                 Guid activityId = TplEtwProvider.CreateGuidForTaskID(m_continuationId);
671                 System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(activityId, out savedActivityId);
672             }
673             try
674             {
675                 // We're not inside of a task, so t_currentTask doesn't need to be specially maintained.
676                 // We're on a thread pool thread with no higher-level callers, so exceptions can just propagate.
677
678                 // If there's no execution context, just invoke the delegate.
679                 if (m_capturedContext == null)
680                 {
681                     m_action();
682                 }
683                     // If there is an execution context, get the cached delegate and run the action under the context.
684                 else
685                 {
686                     try
687                     {
688                         ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action, true);
689                     }
690                     finally { m_capturedContext.Dispose(); }
691                 }
692             }
693             finally
694             {
695                 if (etwLog.TasksSetActivityIds && m_continuationId != 0)
696                 {
697                     System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(savedActivityId);
698                 }
699             }
700         }
701
702         [SecurityCritical]
703         void IThreadPoolWorkItem.ExecuteWorkItem()
704         {
705             // inline the fast path
706             if (m_capturedContext == null && !TplEtwProvider.Log.IsEnabled()
707             )
708             {
709                 m_action();
710             }
711             else
712             {
713                 ExecuteWorkItemHelper();
714             }
715         }
716
717         /// <summary>
718         /// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem.
719         /// </summary>
720         [SecurityCritical]
721         void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ }
722
723         /// <summary>Cached delegate that invokes an Action passed as an object parameter.</summary>
724         [SecurityCritical]
725         private static ContextCallback s_invokeActionCallback;
726
727         /// <summary>Runs an action provided as an object parameter.</summary>
728         /// <param name="state">The Action to invoke.</param>
729         [SecurityCritical]
730         private static void InvokeAction(object state) { ((Action)state)(); }
731
732         [MethodImpl(MethodImplOptions.AggressiveInlining)]
733         [SecurityCritical]
734         protected static ContextCallback GetInvokeActionCallback()
735         {
736             ContextCallback callback = s_invokeActionCallback;
737             if (callback == null) { s_invokeActionCallback = callback = InvokeAction; } // lazily initialize SecurityCritical delegate
738             return callback;
739         }
740
741         /// <summary>Runs the callback synchronously with the provided state.</summary>
742         /// <param name="callback">The callback to run.</param>
743         /// <param name="state">The state to pass to the callback.</param>
744         /// <param name="currentTask">A reference to Task.t_currentTask.</param>
745         [SecurityCritical]
746         protected void RunCallback(ContextCallback callback, object state, ref Task currentTask)
747         {
748             Contract.Requires(callback != null);
749             Contract.Assert(currentTask == Task.t_currentTask);
750
751             // Pretend there's no current task, so that no task is seen as a parent
752             // and TaskScheduler.Current does not reflect false information
753             var prevCurrentTask = currentTask;
754             try
755             {
756                 if (prevCurrentTask != null) currentTask = null;
757
758                 // If there's no captured context, just run the callback directly.
759                 if (m_capturedContext == null) callback(state);
760                 // Otherwise, use the captured context to do so.
761                 else ExecutionContext.Run(m_capturedContext, callback, state, true);
762             }
763             catch (Exception exc) // we explicitly do not request handling of dangerous exceptions like AVs
764             {
765                 ThrowAsyncIfNecessary(exc);
766             }
767             finally
768             {
769                 // Restore the current task information
770                 if (prevCurrentTask != null) currentTask = prevCurrentTask;
771
772                 // Clean up after the execution context, which is only usable once.
773                 if (m_capturedContext != null) m_capturedContext.Dispose();
774             }
775         }
776
777         /// <summary>Invokes or schedules the action to be executed.</summary>
778         /// <param name="action">The action to invoke or queue.</param>
779         /// <param name="allowInlining">
780         /// true to allow inlining, or false to force the action to run asynchronously.
781         /// </param>
782         /// <param name="currentTask">
783         /// A reference to the t_currentTask thread static value.
784         /// This is passed by-ref rather than accessed in the method in order to avoid
785         /// unnecessary thread-static writes.
786         /// </param>
787         /// <remarks>
788         /// No ExecutionContext work is performed used.  This method is only used in the
789         /// case where a raw Action continuation delegate was stored into the Task, which
790         /// only happens in Task.SetContinuationForAwait if execution context flow was disabled
791         /// via using TaskAwaiter.UnsafeOnCompleted or a similar path.
792         /// </remarks>
793         [SecurityCritical]
794         internal static void RunOrScheduleAction(Action action, bool allowInlining, ref Task currentTask)
795         {
796             Contract.Assert(currentTask == Task.t_currentTask);
797
798             // If we're not allowed to run here, schedule the action
799             if (!allowInlining || !IsValidLocationForInlining)
800             {
801                 UnsafeScheduleAction(action, currentTask);
802                 return;
803             }
804
805             // Otherwise, run it, making sure that t_currentTask is null'd out appropriately during the execution
806             Task prevCurrentTask = currentTask;
807             try
808             {
809                 if (prevCurrentTask != null) currentTask = null;
810                 action();
811             }
812             catch (Exception exception)
813             {
814                 ThrowAsyncIfNecessary(exception);
815             }
816             finally
817             {
818                 if (prevCurrentTask != null) currentTask = prevCurrentTask;
819             }
820         }
821
822         /// <summary>Schedules the action to be executed.  No ExecutionContext work is performed used.</summary>
823         /// <param name="action">The action to invoke or queue.</param>
824         [SecurityCritical]
825         internal static void UnsafeScheduleAction(Action action, Task task)
826         {
827             AwaitTaskContinuation atc = new AwaitTaskContinuation(action, flowExecutionContext: false);
828
829             var etwLog = TplEtwProvider.Log;
830             if (etwLog.IsEnabled() && task != null)
831             {
832                 atc.m_continuationId = Task.NewId();
833                 etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, atc.m_continuationId);
834             }
835
836             ThreadPool.UnsafeQueueCustomWorkItem(atc, forceGlobal: false);
837         }
838
839         /// <summary>Throws the exception asynchronously on the ThreadPool.</summary>
840         /// <param name="exc">The exception to throw.</param>
841         protected static void ThrowAsyncIfNecessary(Exception exc)
842         {
843             // Awaits should never experience an exception (other than an TAE or ADUE), 
844             // unless a malicious user is explicitly passing a throwing action into the TaskAwaiter. 
845             // We don't want to allow the exception to propagate on this stack, as it'll emerge in random places, 
846             // and we can't fail fast, as that would allow for elevation of privilege.
847             //
848             // If unhandled error reporting APIs are available use those, otherwise since this 
849             // would have executed on the thread pool otherwise, let it propagate there.
850
851             if (!(exc is ThreadAbortException || exc is AppDomainUnloadedException))
852             {
853 #if FEATURE_COMINTEROP
854                 if (!WindowsRuntimeMarshal.ReportUnhandledError(exc))
855 #endif // FEATURE_COMINTEROP
856                 {
857                     var edi = ExceptionDispatchInfo.Capture(exc);
858                     ThreadPool.QueueUserWorkItem(s => ((ExceptionDispatchInfo)s).Throw(), edi);
859                 }
860             }
861         }
862
863         internal override Delegate[] GetDelegateContinuationsForDebugger()
864         {
865             Contract.Assert(m_action != null);
866             return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(m_action) };
867         }
868     }
869
870 }