Repository: dotnet/runtime
File: src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs (C#)

Code before:
                AsyncDispatcherInfo asyncDispatcherInfo;
                asyncDispatcherInfo.Next = AsyncDispatcherInfo.t_current;
                asyncDispatcherInfo.NextContinuation = MoveContinuationState();
                AsyncDispatcherInfo.t_current = &asyncDispatcherInfo;
                asyncDispatcherInfo.Task = this;

                if (TplEventSource.Log.IsEnabled())
                {
                    TplEventSource.Log.TraceSynchronousWorkBegin(this.Id, CausalitySynchronousWork.Execution);
                }

                while (true)
                {
                    Debug.Assert(asyncDispatcherInfo.NextContinuation != null);
                    try
                    {
                        Continuation curContinuation = asyncDispatcherInfo.NextContinuation;
                        Continuation? nextContinuation = curContinuation.Next;
                        asyncDispatcherInfo.NextContinuation = nextContinuation;

                        ref byte resultLoc = ref nextContinuation != null ? ref nextContinuation.GetResultStorageOrNull() : ref GetResultStorage();
                        long tickCount = Task.GetRuntimeAsyncContinuationTicks(curContinuation, out long tickCountVal) ? tickCountVal : Environment.TickCount64;
                        Task.UpdateRuntimeAsyncTaskTicks(this, tickCount);
                        Continuation? newContinuation = curContinuation.ResumeInfo->Resume(curContinuation, ref resultLoc);

                        Task.RemoveRuntimeAsyncContinuationTicks(curContinuation);

                        if (newContinuation != null)
                        {
                            Task.UpdateRuntimeAsyncContinuationTicks(newContinuation, tickCount);
                            newContinuation.Next = nextContinuation;
                            HandleSuspended();
                            contexts.Pop();
                            AsyncDispatcherInfo.t_current = asyncDispatcherInfo.Next;
                            break;
                        }
                    }
                    catch (Exception ex)
                    {
                        Continuation? handlerContinuation = UnwindToPossibleHandler(asyncDispatcherInfo.NextContinuation);
                        if (handlerContinuation == null)
                        {
                            // Tail of AsyncTaskMethodBuilderT.SetException
                            bool successfullySet = ex is OperationCanceledException oce ?
                                TrySetCanceled(oce.CancellationToken, oce) :
                                TrySetException(ex);

                            contexts.Pop();

                            AsyncDispatcherInfo.t_current = asyncDispatcherInfo.Next;


Review comment (bug):
If the continuation executes quickly is there a risk its already deleted before the debugger ever gets an opportunity to observe it? Would that matter or the debugger only cares about observing the tasks which are in-progress at the moment a debug event occurs?

Code after:
                            Task.RemoveRuntimeAsyncContinuationTimestamp(nextCont);
                            nextCont = nextCont.Next;
                        }
                    }

                    Task.ThrowAsync(ex, targetContext: null);
                }
            }

#pragma warning disable CA1822 // Mark members as static
            [MethodImpl(MethodImplOptions.NoOptimization)]
            public void NotifyDebuggerOfRuntimeAsyncState()
            {
            }
#pragma warning restore CA1822

            [StackTraceHidden]
            private unsafe void DispatchContinuations()
            {
                ExecutionAndSyncBlockStore contexts = default;
                contexts.Push();

                AsyncDispatcherInfo asyncDispatcherInfo;
                asyncDispatcherInfo.Next = AsyncDispatcherInfo.t_current;
                asyncDispatcherInfo.NextContinuation = MoveContinuationState();
                asyncDispatcherInfo.CurrentTask = this;
                AsyncDispatcherInfo.t_current = &asyncDispatcherInfo;
                bool isTplEnabled = TplEventSource.Log.IsEnabled();

                if (isTplEnabled)
                {
                    TplEventSource.Log.TraceSynchronousWorkBegin(this.Id, CausalitySynchronousWork.Execution);
                }

                while (true)
                {
                    Debug.Assert(asyncDispatcherInfo.NextContinuation != null);
                    Continuation curContinuation = asyncDispatcherInfo.NextContinuation;
                    try
                    {
                        Continuation? nextContinuation = curContinuation.Next;
                        asyncDispatcherInfo.NextContinuation = nextContinuation;

                        ref byte resultLoc = ref nextContinuation != null ? ref nextContinuation.GetResultStorageOrNull() : ref GetResultStorage();
                        long timestamp = 0;
                        if (Task.s_asyncDebuggingEnabled)
                        {
                            timestamp = Task.GetRuntimeAsyncContinuationTimestamp(curContinuation, out long timestampVal) ? timestampVal : Stopwatch.GetTimestamp();
                            // we have dequeued curContinuation; update task tick info so that we can track its start time from a debugger
                            Task.UpdateRuntimeAsyncTaskTimestamp(this, timestamp);
                        }