3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
10 // <OWNER>[....]</OWNER>
12 // Implementation of task continuations, TaskContinuation, and its descendants.
14 // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
16 using System.Security;
17 using System.Diagnostics.Contracts;
18 using System.Runtime.ExceptionServices;
19 using System.Runtime.CompilerServices;
20 using System.Threading;
22 #if FEATURE_COMINTEROP
23 using System.Runtime.InteropServices.WindowsRuntime;
24 #endif // FEATURE_COMINTEROP
26 namespace System.Threading.Tasks
28 // Task type used to implement: Task ContinueWith(Action<Task,...>)
29 internal sealed class ContinuationTaskFromTask : Task
31 private Task m_antecedent;
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)
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);
44 /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
46 internal override void InnerInvoke()
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.");
55 // Notify the debugger we're completing an asynchronous wait on a task
56 antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
58 // Invoke the delegate
59 Contract.Assert(m_action != null);
60 var action = m_action as Action<Task>;
66 var actionWithState = m_action as Action<Task, object>;
67 if (actionWithState != null)
69 actionWithState(antecedent, m_stateObject);
72 Contract.Assert(false, "Invalid m_action in ContinuationTaskFromTask");
76 // Task type used to implement: Task<TResult> ContinueWith(Func<Task,...>)
77 internal sealed class ContinuationResultTaskFromTask<TResult> : Task<TResult>
79 private Task m_antecedent;
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)
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);
92 /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
94 internal override void InnerInvoke()
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.");
103 // Notify the debugger we're completing an asynchronous wait on a task
104 antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
106 // Invoke the delegate
107 Contract.Assert(m_action != null);
108 var func = m_action as Func<Task, TResult>;
111 m_result = func(antecedent);
114 var funcWithState = m_action as Func<Task, object, TResult>;
115 if (funcWithState != null)
117 m_result = funcWithState(antecedent, m_stateObject);
120 Contract.Assert(false, "Invalid m_action in ContinuationResultTaskFromTask");
124 // Task type used to implement: Task ContinueWith(Action<Task<TAntecedentResult>,...>)
125 internal sealed class ContinuationTaskFromResultTask<TAntecedentResult> : Task
127 private Task<TAntecedentResult> m_antecedent;
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)
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);
140 /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
142 internal override void InnerInvoke()
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.");
151 // Notify the debugger we're completing an asynchronous wait on a task
152 antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
154 // Invoke the delegate
155 Contract.Assert(m_action != null);
156 var action = m_action as Action<Task<TAntecedentResult>>;
162 var actionWithState = m_action as Action<Task<TAntecedentResult>, object>;
163 if (actionWithState != null)
165 actionWithState(antecedent, m_stateObject);
168 Contract.Assert(false, "Invalid m_action in ContinuationTaskFromResultTask");
172 // Task type used to implement: Task<TResult> ContinueWith(Func<Task<TAntecedentResult>,...>)
173 internal sealed class ContinuationResultTaskFromResultTask<TAntecedentResult, TResult> : Task<TResult>
175 private Task<TAntecedentResult> m_antecedent;
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)
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);
188 /// Evaluates the value selector of the Task which is passed in as an object and stores the result.
190 internal override void InnerInvoke()
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.");
199 // Notify the debugger we're completing an asynchronous wait on a task
200 antecedent.NotifyDebuggerOfWaitCompletionIfNecessary();
202 // Invoke the delegate
203 Contract.Assert(m_action != null);
204 var func = m_action as Func<Task<TAntecedentResult>, TResult>;
207 m_result = func(antecedent);
210 var funcWithState = m_action as Func<Task<TAntecedentResult>, object, TResult>;
211 if (funcWithState != null)
213 m_result = funcWithState(antecedent, m_stateObject);
216 Contract.Assert(false, "Invalid m_action in ContinuationResultTaskFromResultTask");
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
228 /// <summary>Represents a continuation.</summary>
229 internal abstract class TaskContinuation
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);
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.
241 [SecuritySafeCritical]
242 protected static void InlineIfPossibleOrElseQueue(Task task, bool needsProtection)
244 Contract.Requires(task != null);
245 Contract.Assert(task.m_taskScheduler != null);
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.
252 if (!task.MarkStarted())
253 return; // task has been previously started or canceled. Stop processing.
257 task.m_stateFlags |= Task.TASK_STATE_STARTED;
260 // Try to inline it but queue if we can't
263 if (!task.m_taskScheduler.TryRunInline(task, taskWasPreviouslyQueued: false))
265 task.m_taskScheduler.InternalQueueTask(task);
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
276 TaskSchedulerException tse = new TaskSchedulerException(e);
277 task.AddException(tse);
285 internal abstract Delegate[] GetDelegateContinuationsForDebugger();
289 /// <summary>Provides the standard implementation of a task continuation.</summary>
290 internal class StandardTaskContinuation : TaskContinuation
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;
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)
305 Contract.Requires(task != null, "TaskContinuation ctor: task is null");
306 Contract.Requires(scheduler != null, "TaskContinuation ctor: scheduler is null");
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);
313 if (Task.s_asyncDebuggingEnabled)
315 Task.AddToActiveTasks(m_task);
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)
324 Contract.Assert(completedTask != null);
325 Contract.Assert(completedTask.IsCompleted, "ContinuationTask.Run(): completedTask not completed");
327 // Check if the completion status of the task works with the desired
328 // activation criteria of the TaskContinuationOptions.
329 TaskContinuationOptions options = m_options;
331 completedTask.IsRanToCompletion ?
332 (options & TaskContinuationOptions.NotOnRanToCompletion) == 0 :
333 (completedTask.IsCanceled ?
334 (options & TaskContinuationOptions.NotOnCanceled) == 0 :
335 (options & TaskContinuationOptions.NotOnFaulted) == 0);
337 // If the completion status is allowed, run the continuation.
338 Task continuationTask = m_task;
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)
346 // Log now that we are sure that this continuation is being ran
347 AsyncCausalityTracer.TraceOperationRelation(CausalityTraceLevel.Important, continuationTask.Id, CausalityRelation.AssignDelegate);
349 continuationTask.m_taskScheduler = m_taskScheduler;
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
356 InlineIfPossibleOrElseQueue(continuationTask, needsProtection: true);
360 try { continuationTask.ScheduleAndStart(needsProtection: true); }
361 catch (TaskSchedulerException)
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.
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);
373 internal override Delegate[] GetDelegateContinuationsForDebugger()
375 if (m_task.m_action == null)
377 return m_task.GetDelegateContinuationsForDebugger();
380 return new Delegate[] { m_task.m_action as Delegate };
384 /// <summary>Task continuation for awaiting with a current synchronization context.</summary>
385 internal sealed class SynchronizationContextAwaitTaskContinuation : AwaitTaskContinuation
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>
391 private static ContextCallback s_postActionCallback;
392 /// <summary>The context with which to run the action.</summary>
393 private readonly SynchronizationContext m_syncContext;
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>
401 internal SynchronizationContextAwaitTaskContinuation(
402 SynchronizationContext context, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) :
403 base(action, flowExecutionContext, ref stackMark)
405 Contract.Assert(context != null);
406 m_syncContext = context;
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)
415 // If we're allowed to inline, run the action on this thread.
416 if (canInlineContinuationTask &&
417 m_syncContext == SynchronizationContext.CurrentNoFlow)
419 RunCallback(GetInvokeActionCallback(), m_action, ref Task.t_currentTask);
421 // Otherwise, Post the action back to the SynchronizationContext.
424 TplEtwProvider etwLog = TplEtwProvider.Log;
425 if (etwLog.IsEnabled())
427 m_continuationId = Task.NewId();
428 etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
430 RunCallback(GetPostActionCallback(), this, ref Task.t_currentTask);
432 // Any exceptions will be handled by RunCallback.
435 /// <summary>Calls InvokeOrPostAction(false) on the supplied SynchronizationContextAwaitTaskContinuation.</summary>
436 /// <param name="state">The SynchronizationContextAwaitTaskContinuation.</param>
438 private static void PostAction(object state)
440 var c = (SynchronizationContextAwaitTaskContinuation)state;
442 TplEtwProvider etwLog = TplEtwProvider.Log;
443 if (etwLog.TasksSetActivityIds && c.m_continuationId != 0)
445 c.m_syncContext.Post(s_postCallback, GetActionLogDelegate(c.m_continuationId, c.m_action));
449 c.m_syncContext.Post(s_postCallback, c.m_action); // s_postCallback is manually cached, as the compiler won't in a SecurityCritical method
453 private static Action GetActionLogDelegate(int continuationId, Action action)
457 Guid savedActivityId;
458 Guid activityId = TplEtwProvider.CreateGuidForTaskID(continuationId);
459 System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(activityId, out savedActivityId);
461 finally { System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(savedActivityId); }
465 /// <summary>Gets a cached delegate for the PostAction method.</summary>
467 /// A delegate for PostAction, which expects a SynchronizationContextAwaitTaskContinuation
468 /// to be passed as state.
470 [MethodImpl(MethodImplOptions.AggressiveInlining)]
472 private static ContextCallback GetPostActionCallback()
474 ContextCallback callback = s_postActionCallback;
475 if (callback == null) { s_postActionCallback = callback = PostAction; } // lazily initialize SecurityCritical delegate
480 /// <summary>Task continuation for awaiting with a task scheduler.</summary>
481 internal sealed class TaskSchedulerAwaitTaskContinuation : AwaitTaskContinuation
483 /// <summary>The scheduler on which to run the action.</summary>
484 private readonly TaskScheduler m_scheduler;
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>
492 internal TaskSchedulerAwaitTaskContinuation(
493 TaskScheduler scheduler, Action action, bool flowExecutionContext, ref StackCrawlMark stackMark) :
494 base(action, flowExecutionContext, ref stackMark)
496 Contract.Assert(scheduler != null);
497 m_scheduler = scheduler;
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)
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)
508 base.Run(ignored, canInlineContinuationTask);
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);
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);
528 if (inlineIfPossible)
530 InlineIfPossibleOrElseQueue(task, needsProtection: false);
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
542 /// <summary>Base task continuation class used for await continuations.</summary>
543 internal class AwaitTaskContinuation : TaskContinuation, IThreadPoolWorkItem
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;
550 protected int m_continuationId;
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>
557 internal AwaitTaskContinuation(Action action, bool flowExecutionContext, ref StackCrawlMark stackMark)
559 Contract.Requires(action != null);
561 if (flowExecutionContext)
563 m_capturedContext = ExecutionContext.Capture(
565 ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
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>
573 internal AwaitTaskContinuation(Action action, bool flowExecutionContext)
575 Contract.Requires(action != null);
577 if (flowExecutionContext)
579 m_capturedContext = ExecutionContext.FastCapture();
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)
590 Contract.Requires(action != null);
591 Contract.Requires(scheduler != null);
594 action, state, null, default(CancellationToken),
595 TaskCreationOptions.None, InternalTaskOptions.QueuedByRuntime, scheduler)
597 CapturedContext = m_capturedContext
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)
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)
614 RunCallback(GetInvokeActionCallback(), m_action, ref Task.t_currentTask); // any exceptions from m_action will be handled by s_callbackRunAction
618 TplEtwProvider etwLog = TplEtwProvider.Log;
619 if (etwLog.IsEnabled())
621 m_continuationId = Task.NewId();
622 etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, m_continuationId);
625 // We couldn't inline, so now we need to schedule it
626 ThreadPool.UnsafeQueueCustomWorkItem(this, forceGlobal: false);
631 /// Gets whether the current thread is an appropriate location to inline a continuation's execution.
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.
646 internal static bool IsValidLocationForInlining
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;
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;
662 /// <summary>IThreadPoolWorkItem override, which is the entry function for this when the ThreadPool scheduler decides to run it.</summary>
664 void ExecuteWorkItemHelper()
666 var etwLog = TplEtwProvider.Log;
667 Guid savedActivityId = Guid.Empty;
668 if (etwLog.TasksSetActivityIds && m_continuationId != 0)
670 Guid activityId = TplEtwProvider.CreateGuidForTaskID(m_continuationId);
671 System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(activityId, out savedActivityId);
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.
678 // If there's no execution context, just invoke the delegate.
679 if (m_capturedContext == null)
683 // If there is an execution context, get the cached delegate and run the action under the context.
688 ExecutionContext.Run(m_capturedContext, GetInvokeActionCallback(), m_action, true);
690 finally { m_capturedContext.Dispose(); }
695 if (etwLog.TasksSetActivityIds && m_continuationId != 0)
697 System.Diagnostics.Tracing.EventSource.SetCurrentThreadActivityId(savedActivityId);
703 void IThreadPoolWorkItem.ExecuteWorkItem()
705 // inline the fast path
706 if (m_capturedContext == null && !TplEtwProvider.Log.IsEnabled()
713 ExecuteWorkItemHelper();
718 /// The ThreadPool calls this if a ThreadAbortException is thrown while trying to execute this workitem.
721 void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae) { /* nop */ }
723 /// <summary>Cached delegate that invokes an Action passed as an object parameter.</summary>
725 private static ContextCallback s_invokeActionCallback;
727 /// <summary>Runs an action provided as an object parameter.</summary>
728 /// <param name="state">The Action to invoke.</param>
730 private static void InvokeAction(object state) { ((Action)state)(); }
732 [MethodImpl(MethodImplOptions.AggressiveInlining)]
734 protected static ContextCallback GetInvokeActionCallback()
736 ContextCallback callback = s_invokeActionCallback;
737 if (callback == null) { s_invokeActionCallback = callback = InvokeAction; } // lazily initialize SecurityCritical delegate
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>
746 protected void RunCallback(ContextCallback callback, object state, ref Task currentTask)
748 Contract.Requires(callback != null);
749 Contract.Assert(currentTask == Task.t_currentTask);
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;
756 if (prevCurrentTask != null) currentTask = null;
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);
763 catch (Exception exc) // we explicitly do not request handling of dangerous exceptions like AVs
765 ThrowAsyncIfNecessary(exc);
769 // Restore the current task information
770 if (prevCurrentTask != null) currentTask = prevCurrentTask;
772 // Clean up after the execution context, which is only usable once.
773 if (m_capturedContext != null) m_capturedContext.Dispose();
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.
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.
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.
794 internal static void RunOrScheduleAction(Action action, bool allowInlining, ref Task currentTask)
796 Contract.Assert(currentTask == Task.t_currentTask);
798 // If we're not allowed to run here, schedule the action
799 if (!allowInlining || !IsValidLocationForInlining)
801 UnsafeScheduleAction(action, currentTask);
805 // Otherwise, run it, making sure that t_currentTask is null'd out appropriately during the execution
806 Task prevCurrentTask = currentTask;
809 if (prevCurrentTask != null) currentTask = null;
812 catch (Exception exception)
814 ThrowAsyncIfNecessary(exception);
818 if (prevCurrentTask != null) currentTask = prevCurrentTask;
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>
825 internal static void UnsafeScheduleAction(Action action, Task task)
827 AwaitTaskContinuation atc = new AwaitTaskContinuation(action, flowExecutionContext: false);
829 var etwLog = TplEtwProvider.Log;
830 if (etwLog.IsEnabled() && task != null)
832 atc.m_continuationId = Task.NewId();
833 etwLog.AwaitTaskContinuationScheduled((task.ExecutingTaskScheduler ?? TaskScheduler.Default).Id, task.Id, atc.m_continuationId);
836 ThreadPool.UnsafeQueueCustomWorkItem(atc, forceGlobal: false);
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)
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.
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.
851 if (!(exc is ThreadAbortException || exc is AppDomainUnloadedException))
853 #if FEATURE_COMINTEROP
854 if (!WindowsRuntimeMarshal.ReportUnhandledError(exc))
855 #endif // FEATURE_COMINTEROP
857 var edi = ExceptionDispatchInfo.Capture(exc);
858 ThreadPool.QueueUserWorkItem(s => ((ExceptionDispatchInfo)s).Throw(), edi);
863 internal override Delegate[] GetDelegateContinuationsForDebugger()
865 Contract.Assert(m_action != null);
866 return new Delegate[] { AsyncMethodBuilderCore.TryGetStateMachineForDebugger(m_action) };