-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Managed fixes for async task tracking #123727
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
There was a problem hiding this 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 |
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs
Show resolved
Hide resolved
| { | ||
| if (s_asyncDebuggingEnabled) | ||
| { | ||
| s_runtimeAsyncContinuationTicks ??= new Collections.Concurrent.ConcurrentDictionary<Continuation, long>(ContinuationEqualityComparer.Instance); |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
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.
| { | ||
| if (s_asyncDebuggingEnabled) | ||
| { | ||
| s_runtimeAsyncContinuationTicks ??= new Collections.Concurrent.ConcurrentDictionary<Continuation, long>(ContinuationEqualityComparer.Instance); |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
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.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs
Show resolved
Hide resolved
| } | ||
| } | ||
|
|
||
| internal class ContinuationEqualityComparer : IEqualityComparer<Continuation> |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs
Outdated
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs
Outdated
Show resolved
Hide resolved
|
|
||
| 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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this 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.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs
Outdated
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs
Outdated
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs
Outdated
Show resolved
Hide resolved
Added comments to clarify the purpose of the Task field.
No description provided.