/// <summary> /// Outputs a WaitBegin ETW event, and augments the continuation action to output a WaitEnd ETW event. /// </summary> /// <param name="task">The task being awaited.</param> /// <param name="continuation">The action to invoke when the await operation completes.</param> /// <returns>The action to use as the actual continuation.</returns> private static Action OutputWaitEtwEvents(Task task, Action continuation) { Debug.Assert(task != null, "Need a task to wait on"); Debug.Assert(continuation != null, "Need a continuation to invoke when the wait completes"); if (Task.s_asyncDebuggingEnabled) { Task.AddToActiveTasks(task); } TplEventSource log = TplEventSource.Log; if (log.IsEnabled()) { // ETW event for Task Wait Begin Task?currentTaskAtBegin = Task.InternalCurrent; // If this task's continuation is another task, get it. Task?continuationTask = AsyncMethodBuilderCore.TryGetContinuationTask(continuation); log.TaskWaitBegin( currentTaskAtBegin != null ? currentTaskAtBegin.m_taskScheduler !.Id : TaskScheduler.Default.Id, currentTaskAtBegin != null ? currentTaskAtBegin.Id : 0, task.Id, TplEventSource.TaskWaitBehavior.Asynchronous, continuationTask != null ? continuationTask.Id : 0); } // Create a continuation action that outputs the end event and then invokes the user // provided delegate. This incurs the allocations for the closure/delegate, but only if the event // is enabled, and in doing so it allows us to pass the awaited task's information into the end event // in a purely pay-for-play manner (the alternatively would be to increase the size of TaskAwaiter // just for this ETW purpose, not pay-for-play, since GetResult would need to know whether a real yield occurred). return(AsyncMethodBuilderCore.CreateContinuationWrapper(continuation, static (innerContinuation, innerTask) =>
/// <summary> /// Outputs a WaitBegin ETW event, and augments the continuation action to output a WaitEnd ETW event. /// </summary> /// <param name="task">The task being awaited.</param> /// <param name="continuation">The action to invoke when the await operation completes.</param> /// <returns>The action to use as the actual continuation.</returns> private static Action OutputWaitEtwEvents(Task task, Action continuation) { Debug.Assert(task != null, "Need a task to wait on"); Debug.Assert(continuation != null, "Need a continuation to invoke when the wait completes"); if (Task.s_asyncDebuggingEnabled) { Task.AddToActiveTasks(task); } TplEventSource log = TplEventSource.Log; if (log.IsEnabled()) { // ETW event for Task Wait Begin Task?currentTaskAtBegin = Task.InternalCurrent; // If this task's continuation is another task, get it. Task?continuationTask = AsyncMethodBuilderCore.TryGetContinuationTask(continuation); log.TaskWaitBegin( currentTaskAtBegin != null ? currentTaskAtBegin.m_taskScheduler !.Id : TaskScheduler.Default.Id, currentTaskAtBegin != null ? currentTaskAtBegin.Id : 0, task.Id, TplEventSource.TaskWaitBehavior.Asynchronous, continuationTask != null ? continuationTask.Id : 0); } // Create a continuation action that outputs the end event and then invokes the user // provided delegate. This incurs the allocations for the closure/delegate, but only if the event // is enabled, and in doing so it allows us to pass the awaited task's information into the end event // in a purely pay-for-play manner (the alternatively would be to increase the size of TaskAwaiter // just for this ETW purpose, not pay-for-play, since GetResult would need to know whether a real yield occurred). return(AsyncMethodBuilderCore.CreateContinuationWrapper(continuation, (innerContinuation, innerTask) => { if (Task.s_asyncDebuggingEnabled) { Task.RemoveFromActiveTasks(innerTask); } TplEventSource innerEtwLog = TplEventSource.Log; // ETW event for Task Wait End. Guid prevActivityId = default; bool bEtwLogEnabled = innerEtwLog.IsEnabled(); if (bEtwLogEnabled) { Task?currentTaskAtEnd = Task.InternalCurrent; innerEtwLog.TaskWaitEnd( currentTaskAtEnd != null ? currentTaskAtEnd.m_taskScheduler !.Id : TaskScheduler.Default.Id, currentTaskAtEnd != null ? currentTaskAtEnd.Id : 0, innerTask.Id); // Ensure the continuation runs under the activity ID of the task that completed for the // case the antecedent is a promise (in the other cases this is already the case). if (innerEtwLog.TasksSetActivityIds && (innerTask.Options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0) { EventSource.SetCurrentThreadActivityId(TplEventSource.CreateGuidForTaskID(innerTask.Id), out prevActivityId); } }