Skip to content

Conversation

@rcj1
Copy link
Contributor

@rcj1 rcj1 commented Jan 28, 2026

No description provided.

@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jan 28, 2026
@jkotas jkotas added runtime-async area-Diagnostics-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jan 28, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Copilot AI review requested due to automatic review settings January 29, 2026 18:37
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds managed fixes for async task tracking by introducing tick count tracking for runtime async tasks and continuations to support debugging scenarios.

Changes:

  • Adds two new ConcurrentDictionary fields to track task and continuation tick counts for debugging
  • Implements methods to set, get, update, and remove tick count tracking for both tasks and continuations
  • Integrates tick count tracking into the continuation allocation and dispatch flow
  • Adds TPL event source tracing for synchronous work begin/end and operation begin/end events

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs Adds static dictionaries and helper methods for tracking task and continuation tick counts, removes unnecessary blank line
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs Integrates tick count tracking into continuation lifecycle, adds ContinuationEqualityComparer, extends AsyncDispatcherInfo with Task field, adds TPL event source tracing

{
if (s_asyncDebuggingEnabled)
{
s_runtimeAsyncContinuationTicks ??= new Collections.Concurrent.ConcurrentDictionary<Continuation, long>(ContinuationEqualityComparer.Instance);
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent namespace qualification: The ConcurrentDictionary type should be fully qualified as System.Collections.Concurrent.ConcurrentDictionary to match the field declarations on lines 184 and 187. The file does not have a using System.Collections.Concurrent; directive.

Copilot uses AI. Check for mistakes.
{
if (s_asyncDebuggingEnabled)
{
s_runtimeAsyncContinuationTicks ??= new Collections.Concurrent.ConcurrentDictionary<Continuation, long>(ContinuationEqualityComparer.Instance);
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent namespace qualification: The ConcurrentDictionary type should be fully qualified as System.Collections.Concurrent.ConcurrentDictionary to match the field declarations on lines 184 and 187. The file does not have a using System.Collections.Concurrent; directive.

Copilot uses AI. Check for mistakes.
}
}

internal class ContinuationEqualityComparer : IEqualityComparer<Continuation>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What difference does it make specifying this comparer? Is this a workaround for missing functionality in the Continuation class where most objects have this default behavior but Continuations are special?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the virtual functions do not work on Continuations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can use a comment

#else
[FieldOffset(8)]
#endif
public Task Task;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment describing which Task gets stored here and when would be helpful.


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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have guessed the tick count represented the first time a Task starts doing work but this seems to be updating the tick count every time a new continuation starts. Is that the desired behavior? It would be helpful to have a nice comment somewhere that describes what behavior we are trying to achieve with these side data structures.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added some comments on the methods and around them

Task.UpdateRuntimeAsyncTaskTicks(this, tickCount);
Continuation? newContinuation = curContinuation.ResumeInfo->Resume(curContinuation, ref resultLoc);

Task.RemoveRuntimeAsyncContinuationTicks(curContinuation);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should add comments to all of this code. The UpdateRuntimeAsyncTaskTicks Is just to keep track of the start time of the invocation currently inflight, as it is not on the chain. Once its work is done we remove it.

Continuation newContinuation = (Continuation)RuntimeTypeHandle.InternalAllocNoChecks(contMT);
#endif
prevContinuation.Next = newContinuation;
Task.SetRuntimeAsyncContinuationTicks(newContinuation, Environment.TickCount64);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we also mentioned swapping from TickCount to StopWatch if the debugger cares about getting higher precision timings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good because the etw events are also QPC timed

Copilot AI review requested due to automatic review settings January 31, 2026 20:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

Added comments to clarify the purpose of the Task field.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants