Merge pull request #3522 from henricm/fix-csharp-compiler-path-windows
[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 #if !MONO                
425                 TplEtwProvider etwLog = TplEtwProvider.Log;
426                 if (etwLog.IsEnabled())
427                 {
428                     m_continuationId = Task.NewId();
429                     etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
430                 }
431 #endif
432                 RunCallback(GetPostActionCallback(), this, ref Task.t_currentTask);
433             }
434             // Any exceptions will be handled by RunCallback.
435         }
436
437         /// <summary>Calls InvokeOrPostAction(false) on the supplied SynchronizationContextAwaitTaskContinuation.</summary>
438         /// <param name="state">The SynchronizationContextAwaitTaskContinuation.</param>
439         [SecurityCritical]
440         private static void PostAction(object state)
441         {
442             var c = (SynchronizationContextAwaitTaskContinuation)state;
443 #if !MONO
444             TplEtwProvider etwLog = TplEtwProvider.Log;
445             if (etwLog.TasksSetActivityIds && c.m_continuationId != 0)
446             {
447                 c.m_syncContext.Post(s_postCallback, GetActionLogDelegate(c.m_continuationId, c.m_action));
448             }
449             else
450 #endif            
451             {
452                 c.m_syncContext.Post(s_postCallback, c.m_action); // s_postCallback is manually cached, as the compiler won't in a SecurityCritical method
453             }
454         }
455 #if !MONO
456         private static Action GetActionLogDelegate(int continuationId, Action action)
457         {
458             return () =>
459                 {
460                     Guid savedActivityId;
461                     Guid activityId = TplEtwProvider.CreateGuidForTaskID(continuationId);
462                     System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(activityId, out savedActivityId);
463                     try { action(); }
464                     finally { System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(savedActivityId); }
465                 };
466         }
467 #endif
468         /// <summary>Gets a cached delegate for the PostAction method.</summary>
469         /// <returns>
470         /// A delegate for PostAction, which expects a SynchronizationContextAwaitTaskContinuation 
471         /// to be passed as state.
472         /// </returns>
473         [MethodImpl(MethodImplOptions.AggressiveInlining)]
474         [SecurityCritical]
475         private static ContextCallback GetPostActionCallback()
476         {
477             ContextCallback callback = s_postActionCallback;
478             if (callback == null) { s_postActionCallback = callback = PostAction; } // lazily initialize SecurityCritical delegate
479             return callback;
480         }
481     }
482
483     /// <summary>Task continuation for awaiting with a task scheduler.</summary>
484     internal sealed class TaskSchedulerAwaitTaskContinuation : AwaitTaskContinuation
485     {
486         /// <summary>The scheduler on which to run the action.</summary>
487         private readonly TaskScheduler m_scheduler;
488
489         /// <summary>Initializes the TaskSchedulerAwaitTaskContinuation.</summary>
490         /// <param name="scheduler">The task scheduler with which to invoke the action.  Must not be null.</param>
491         /// <param name="action">The action to invoke. Must not be null.</param>
492         /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
493         /// <param name="stackMark">The captured stack mark.</param>
494         [SecurityCritical]
495         internal TaskSchedulerAwaitTaskContinuation(
496             TaskScheduler scheduler, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) :
497             base(action, flowExecutionContext, ref stackMark)
498         {
499             Contract.Assert(scheduler != null);
500             m_scheduler = scheduler;
501         }
502
503         /// <summary>Inlines or schedules the continuation.</summary>
504         /// <param name="ignored">The antecedent task, which is ignored.</param>
505         /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
506         internal sealed override void Run(Task ignored, bool canInlineContinuationTask)
507         {
508             // If we're targeting the default scheduler, we can use the faster path provided by the base class.
509             if (m_scheduler == TaskScheduler.Default)
510             {
511                 base.Run(ignored, canInlineContinuationTask);
512             }
513             else
514             {
515                 // We permit inlining if the caller allows us to, and 
516                 // either we're on a thread pool thread (in which case we're fine running arbitrary code)
517                 // or we're already on the target scheduler (in which case we'll just ask the scheduler
518                 // whether it's ok to run here).  We include the IsThreadPoolThread check here, whereas
519                 // we don't in AwaitTaskContinuation.Run, since here it expands what's allowed as opposed
520                 // to in AwaitTaskContinuation.Run where it restricts what's allowed.
521                 bool inlineIfPossible = canInlineContinuationTask &&
522                     (TaskScheduler.InternalCurrent == m_scheduler || Thread.CurrentThread.IsThreadPoolThread);
523
524                 // Create the continuation task task. If we're allowed to inline, try to do so.  
525                 // The target scheduler may still deny us from executing on this thread, in which case this'll be queued.
526                 var task = CreateTask(state => {
527                     try { ((Action)state)(); }
528                     catch (Exception exc) { ThrowAsyncIfNecessary(exc); }
529                 }, m_action, m_scheduler);
530
531                 if (inlineIfPossible)
532                 {
533                     InlineIfPossibleOrElseQueue(task, needsProtection: false);
534                 }
535                 else
536                 {
537                     // We need to run asynchronously, so just schedule the task.
538                     try { task.ScheduleAndStart(needsProtection: false); }
539                     catch (TaskSchedulerException) { } // No further action is necessary, as ScheduleAndStart already transitioned task to faulted
540                 }
541             }
542         }
543     }
544
545     /// <summary>Base task continuation class used for await continuations.</summary>
546     internal class AwaitTaskContinuation : TaskContinuation, IThreadPoolWorkItem
547     {
548         /// <summary>The ExecutionContext with which to run the continuation.</summary>
549         private readonly ExecutionContext m_capturedContext;
550         /// <summary>The action to invoke.</summary>
551         protected readonly Action m_action;
552
553 #if !MONO
554         protected int m_continuationId;
555 #endif
556
557         /// <summary>Initializes the continuation.</summary>
558         /// <param name="action">The action to invoke. Must not be null.</param>
559         /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
560         /// <param name="stackMark">The captured stack mark with which to construct an ExecutionContext.</param>
561         [SecurityCritical]
562         internal AwaitTaskContinuation(Action action, bool flowExecutionContext, ref StackCrawlMark stackMark)
563         {
564             Contract.Requires(action != null);
565             m_action = action;
566             if (flowExecutionContext)
567             {
568                 m_capturedContext = ExecutionContext.Capture(
569                     ref stackMark, 
570                     ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
571             }
572         }
573
574         /// <summary>Initializes the continuation.</summary>
575         /// <param name="action">The action to invoke. Must not be null.</param>
576         /// <param name="flowExecutionContext">Whether to capture and restore ExecutionContext.</param>
577         [SecurityCritical]
578         internal AwaitTaskContinuation(Action action, bool flowExecutionContext)
579         {
580             Contract.Requires(action != null);
581             m_action = action;
582             if (flowExecutionContext)
583             {
584                 m_capturedContext = ExecutionContext.FastCapture();
585             }
586         }
587
588         /// <summary>Creates a task to run the action with the specified state on the specified scheduler.</summary>
589         /// <param name="action">The action to run. Must not be null.</param>
590         /// <param name="state">The state to pass to the action. Must not be null.</param>
591         /// <param name="scheduler">The scheduler to target.</param>
592         /// <returns>The created task.</returns>
593         protected Task CreateTask(Action<object> action, object state, TaskScheduler scheduler)
594         {
595             Contract.Requires(action != null);
596             Contract.Requires(scheduler != null);
597
598             return new Task(
599                 action, state, null, default(CancellationToken), 
600                 TaskCreationOptions.None, InternalTaskOptions.QueuedByRuntime, scheduler)  
601             { 
602                 CapturedContext = m_capturedContext 
603             };
604         }
605
606         /// <summary>Inlines or schedules the continuation onto the default scheduler.</summary>
607         /// <param name="ignored">The antecedent task, which is ignored.</param>
608         /// <param name="canInlineContinuationTask">true if inlining is permitted; otherwise, false.</param>
609         [SecuritySafeCritical]
610         internal override void Run(Task task, bool canInlineContinuationTask)
611         {
612             // For the base AwaitTaskContinuation, we allow inlining if our caller allows it
613             // and if we're in a "valid location" for it.  See the comments on 
614             // IsValidLocationForInlining for more about what's valid.  For performance
615             // reasons we would like to always inline, but we don't in some cases to avoid
616             // running arbitrary amounts of work in suspected "bad locations", like UI threads.
617             if (canInlineContinuationTask && IsValidLocationForInlining)
618             {
619                 RunCallback(GetInvokeActionCallback(), m_action, ref Task.t_currentTask); // any exceptions from m_action will be handled by s_callbackRunAction
620             }
621             else
622             {
623 #if !MONO                
624                 TplEtwProvider etwLog = TplEtwProvider.Log;
625                 if (etwLog.IsEnabled())
626                 {
627                     m_continuationId = Task.NewId();
628                     etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
629                 }
630 #endif
631                 // We couldn't inline, so now we need to schedule it
632                 ThreadPool.UnsafeQueueCustomWorkItem(this, forceGlobal: false);
633            }
634         }
635
636         /// <summary>
637         /// Gets whether the current thread is an appropriate location to inline a continuation's execution.
638         /// </summary>
639         /// <remarks>
640         /// Returns whether SynchronizationContext is null and we're in the default scheduler.
641         /// If the await had a SynchronizationContext/TaskScheduler where it began and the 
642         /// default/ConfigureAwait(true) was used, then we won't be on this path.  If, however, 
643         /// ConfigureAwait(false) was used, or the SynchronizationContext and TaskScheduler were 
644         /// naturally null/Default, then we might end up here.  If we do, we need to make sure
645         /// that we don't execute continuations in a place that isn't set up to handle them, e.g.
646         /// running arbitrary amounts of code on the UI thread.  It would be "correct", but very
647         /// expensive, to always run the continuations asynchronously, incurring lots of context
648         /// switches and allocations and locks and the like.  As such, we employ the heuristic
649         /// that if the current thread has a non-null SynchronizationContext or a non-default
650         /// scheduler, then we better not run arbitrary continuations here.
651         /// </remarks>
652         internal static bool IsValidLocationForInlining
653         {
654             get
655             {
656                 // If there's a SynchronizationContext, we'll be conservative and say 
657                 // this is a bad location to inline.
658                 var ctx = SynchronizationContext.CurrentNoFlow;
659                 if (ctx != null && ctx.GetType() != typeof(SynchronizationContext)) return false;
660
661                 // Similarly, if there's a non-default TaskScheduler, we'll be conservative
662                 // and say this is a bad location to inline.
663                 var sched = TaskScheduler.InternalCurrent;
664                 return sched == null || sched == TaskScheduler.Default;
665             }
666         }
667
668         /// <summary>IThreadPoolWorkItem override, which is the entry function for this when the ThreadPool scheduler decides to run it.</summary>
669         [SecurityCritical]
670         void ExecuteWorkItemHelper()
671         {
672 #if !MONO            
673             var etwLog = TplEtwProvider.Log;
674             Guid savedActivityId = Guid.Empty;
675             if (etwLog.TasksSetActivityIds && m_continuationId != 0)
676             {
677                 Guid activityId = TplEtwProvider.CreateGuidForTaskID(m_continuationId);
678                 System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(activityId, out savedActivityId);
679             }
680 #endif
681             try
682             {
683                 // We're not inside of a task, so t_currentTask doesn't need to be specially maintained.
684                 // We're on a thread pool thread with no higher-level callers, so exceptions can just propagate.
685
686                 // If there's no execution context, just invoke the delegate.
687                 if (m_capturedContext == null)
688                 {
689                     m_action();
690                 }
691                     // If there is an execution context, get the cached delegate and run the action under the context.
692                 else
693                 {
694                     try
695                     {
696                         ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action, true);
697                     }
698                     finally { m_capturedContext.Dispose(); }
699                 }
700             }
701             finally
702             {
703 #if !MONO                
704                 if (etwLog.TasksSetActivityIds && m_continuationId != 0)
705                 {
706                     System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(savedActivityId);
707                 }
708 #endif
709             }
710         }
711
712         [SecurityCritical]
713         void IThreadPoolWorkItem.ExecuteWorkItem()
714         {
715             // inline the fast path
716             if (m_capturedContext == null
717 #if !MONO                
718              && !TplEtwProvider.Log.IsEnabled()
719 #endif             
720             )
721             {
722                 m_action();
723             }
724             else
725             {
726                 ExecuteWorkItemHelper();
727             }
728         }
729
730         /// <summary>
731         /// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem.
732         /// </summary>
733         [SecurityCritical]
734         void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ }
735
736         /// <summary>Cached delegate that invokes an Action passed as an object parameter.</summary>
737         [SecurityCritical]
738         private static ContextCallback s_invokeActionCallback;
739
740         /// <summary>Runs an action provided as an object parameter.</summary>
741         /// <param name="state">The Action to invoke.</param>
742         [SecurityCritical]
743         private static void InvokeAction(object state) { ((Action)state)(); }
744
745         [MethodImpl(MethodImplOptions.AggressiveInlining)]
746         [SecurityCritical]
747         protected static ContextCallback GetInvokeActionCallback()
748         {
749             ContextCallback callback = s_invokeActionCallback;
750             if (callback == null) { s_invokeActionCallback = callback = InvokeAction; } // lazily initialize SecurityCritical delegate
751             return callback;
752         }
753
754         /// <summary>Runs the callback synchronously with the provided state.</summary>
755         /// <param name="callback">The callback to run.</param>
756         /// <param name="state">The state to pass to the callback.</param>
757         /// <param name="currentTask">A reference to Task.t_currentTask.</param>
758         [SecurityCritical]
759         protected void RunCallback(ContextCallback callback, object state, ref Task currentTask)
760         {
761             Contract.Requires(callback != null);
762             Contract.Assert(currentTask == Task.t_currentTask);
763
764             // Pretend there's no current task, so that no task is seen as a parent
765             // and TaskScheduler.Current does not reflect false information
766             var prevCurrentTask = currentTask;
767             try
768             {
769                 if (prevCurrentTask != null) currentTask = null;
770
771                 // If there's no captured context, just run the callback directly.
772                 if (m_capturedContext == null) callback(state);
773                 // Otherwise, use the captured context to do so.
774                 else ExecutionContext.Run(m_capturedContext, callback, state, true);
775             }
776             catch (Exception exc) // we explicitly do not request handling of dangerous exceptions like AVs
777             {
778                 ThrowAsyncIfNecessary(exc);
779             }
780             finally
781             {
782                 // Restore the current task information
783                 if (prevCurrentTask != null) currentTask = prevCurrentTask;
784
785                 // Clean up after the execution context, which is only usable once.
786                 if (m_capturedContext != null) m_capturedContext.Dispose();
787             }
788         }
789
790         /// <summary>Invokes or schedules the action to be executed.</summary>
791         /// <param name="action">The action to invoke or queue.</param>
792         /// <param name="allowInlining">
793         /// true to allow inlining, or false to force the action to run asynchronously.
794         /// </param>
795         /// <param name="currentTask">
796         /// A reference to the t_currentTask thread static value.
797         /// This is passed by-ref rather than accessed in the method in order to avoid
798         /// unnecessary thread-static writes.
799         /// </param>
800         /// <remarks>
801         /// No ExecutionContext work is performed used.  This method is only used in the
802         /// case where a raw Action continuation delegate was stored into the Task, which
803         /// only happens in Task.SetContinuationForAwait if execution context flow was disabled
804         /// via using TaskAwaiter.UnsafeOnCompleted or a similar path.
805         /// </remarks>
806         [SecurityCritical]
807         internal static void RunOrScheduleAction(Action action, bool allowInlining, ref Task currentTask)
808         {
809             Contract.Assert(currentTask == Task.t_currentTask);
810
811             // If we're not allowed to run here, schedule the action
812             if (!allowInlining || !IsValidLocationForInlining)
813             {
814                 UnsafeScheduleAction(action, currentTask);
815                 return;
816             }
817
818             // Otherwise, run it, making sure that t_currentTask is null'd out appropriately during the execution
819             Task prevCurrentTask = currentTask;
820             try
821             {
822                 if (prevCurrentTask != null) currentTask = null;
823                 action();
824             }
825             catch (Exception exception)
826             {
827                 ThrowAsyncIfNecessary(exception);
828             }
829             finally
830             {
831                 if (prevCurrentTask != null) currentTask = prevCurrentTask;
832             }
833         }
834
835         /// <summary>Schedules the action to be executed.  No ExecutionContext work is performed used.</summary>
836         /// <param name="action">The action to invoke or queue.</param>
837         [SecurityCritical]
838         internal static void UnsafeScheduleAction(Action action, Task task)
839         {
840             AwaitTaskContinuation atc = new AwaitTaskContinuation(action, flowExecutionContext: false);
841 #if !MONO
842             var etwLog = TplEtwProvider.Log;
843             if (etwLog.IsEnabled() && task != null)
844             {
845                 atc.m_continuationId = Task.NewId();
846                 etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, atc.m_continuationId);
847             }
848 #endif
849             ThreadPool.UnsafeQueueCustomWorkItem(atc, forceGlobal: false);
850         }
851
852         /// <summary>Throws the exception asynchronously on the ThreadPool.</summary>
853         /// <param name="exc">The exception to throw.</param>
854         protected static void ThrowAsyncIfNecessary(Exception exc)
855         {
856             // Awaits should never experience an exception (other than an TAE or ADUE), 
857             // unless a malicious user is explicitly passing a throwing action into the TaskAwaiter. 
858             // We don't want to allow the exception to propagate on this stack, as it'll emerge in random places, 
859             // and we can't fail fast, as that would allow for elevation of privilege.
860             //
861             // If unhandled error reporting APIs are available use those, otherwise since this 
862             // would have executed on the thread pool otherwise, let it propagate there.
863
864             if (!(exc is ThreadAbortException || exc is AppDomainUnloadedException))
865             {
866 #if FEATURE_COMINTEROP
867                 if (!WindowsRuntimeMarshal.ReportUnhandledError(exc))
868 #endif // FEATURE_COMINTEROP
869                 {
870                     var edi = ExceptionDispatchInfo.Capture(exc);
871                     ThreadPool.QueueUserWorkItem(s => ((ExceptionDispatchInfo)s).Throw(), edi);
872                 }
873             }
874         }
875
876         internal override Delegate[] GetDelegateContinuationsForDebugger()
877         {
878             Contract.Assert(m_action != null);
879             return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(m_action) };
880         }
881     }
882
883 }