Cache continuation used for runtime async callable value task thunks - #127973

Merged
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation
May 18, 2026
Merged

Cache continuation used for runtime async callable value task thunks#127973
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

ValueTask backed by IValueTaskSource can suspend without allocating. However, runtime async did not support this when calling a ValueTask returning method through a runtime async callable thunk. In that case we would always allocate in the case where the ValueTask suspended.

This adds a custom ValueTaskContinuation that replaces the existing ValueTaskSourceNotifier mechanism. We now cache a ValueTaskContinuation instance and reuse it if possible whenever suspending for a ValueTask.

The same approach could be used to avoid allocations for runtime async callable thunks for task-returning methods.

cc @VSadov

`ValueTask` backed by `IValueTaskSource` can suspend without allocating.
However, runtime async did not support this when calling a `ValueTask`
returning method through a runtime async callable thunk. In that case we
would always allocate in the case where the `ValueTask` suspended.
This adds a custom `ValueTaskContinuation` that replaces the existing
`ValueTaskSourceNotifier` mechanism. We now cache a
`ValueTaskContinuation` instance and reuse it if possible whenever
suspending for a `ValueTask`.
The same approach could be used to avoid allocations for runtime async
callable thunks for task-returning methods.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 updates CoreCLR’s runtime-async callable thunk path for ValueTask/ValueTask<T> so that suspending on ValueTask backed by IValueTaskSource can reuse a cached continuation object rather than allocating per-suspension, and removes the older ValueTaskSourceNotifier / AsTaskOrNotifier mechanism.

Changes:

  • Introduces AsyncHelpers.TransparentAwaitValueTask* and a ValueTaskContinuation type that integrates with runtime-async suspension/resumption and is cached in TLS for reuse.
  • Removes ValueTask.AsTaskOrNotifier and the ValueTaskSourceNotifier helper type from ValueTask.
  • Adjusts VM continuation type handling to distinguish continuation subtypes that do have metadata (managed subclasses) vs the dynamically-created metadata-less ones, including DAC support.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.csRemoves AsTaskOrNotifier and deletes ValueTaskSourceNotifier.
src/coreclr/vm/vars.hppAdds global g_singletonContinuationEEClass declaration for continuation metadata detection.
src/coreclr/vm/vars.cppAdds global g_singletonContinuationEEClass definition.
src/coreclr/vm/typehandle.inlAvoids “upcasting” continuations that have real metadata.
src/coreclr/vm/typehandle.hAdds TypeHandle::IsContinuationWithMetadata().
src/coreclr/vm/typehandle.cppImplements TypeHandle::IsContinuationWithMetadata() and relaxes class-object allocation restriction for metadata continuations.
src/coreclr/vm/methodtable.hAdds MethodTable::IsContinuationWithMetadata() and relaxes asserts/preconditions for metadata continuations.
src/coreclr/vm/methodtable.cppImplements MethodTable::IsContinuationWithMetadata() and updates continuation-related special-casing.
src/coreclr/vm/corelib.hRemoves binder entries for ValueTask.AsTaskOrNotifier; adds binder entries for TransparentAwaitValueTask*.
src/coreclr/vm/asyncthunks.cppEmits thunk IL that tail-awaits via TransparentAwaitValueTask* instead of AsTaskOrNotifier + TransparentAwait.
src/coreclr/vm/asynccontinuations.cppMoves singleton continuation EEClass storage to global g_singletonContinuationEEClass.
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.csUpdates managed type-system thunk emission to call TransparentAwaitValueTask* and TailAwait.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdds ValueTaskContinuation, TLS caching, and TransparentAwaitValueTask*; updates suspension handling to use it.
src/coreclr/inc/dacvars.hExposes g_singletonContinuationEEClass to DAC.
src/coreclr/debug/daccess/dacdbiimpl.cppUpdates continuation canonical-MT validity logic to account for metadata continuations.

Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/asyncthunks.cpp
@VSadov

Copy link
Copy Markdown
Member

Nice! Instead of caching and reusing ValueTaskSourceNotifier, we now cache/reuse the entire head continuation.

A scenario that awaits in a loop some ValueTaskSource wrapper (like reading chunks from a pipe) can run allocation-free.

@jakobbotsch

jakobbotsch commented May 10, 2026

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

Also, we probably need to introduce a runtime async mechanism similar to PoolingAsyncValueTaskMethodBuilder -- e.g. apply [PoolContinuations] on a runtime async method and it would cache continuations for that method following that scheme too.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 21 out of 21 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs:1

  • The XML doc block still describes awaiting ValueTask, but TransparentAwait now accepts Task only, while ValueTask awaiting is handled by TransparentAwaitValueTask* above. Please update/move this comment so TransparentAwait’s docs describe only the Task scenario, and place the ValueTask-specific explanation on TransparentAwaitValueTask / TransparentAwaitValueTaskOfT.
// Licensed to the .NET Foundation under one or more agreements.

Comment threadsrc/coreclr/vm/methodtable.cpp
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

I think I will do that separately. Right now the caching is essentially free since we can store it in the runtime async TLS that we already needed to access. If we introduce the per-core cache we will be paying more when the TLS cache doesn't hit, so I want to make sure I have some benchmarks for this.

@VSadov

Copy link
Copy Markdown
Member

I think I will do that separately

Per thread caching of 1 head continuation is probably the most effective as we reduce necessary allocations for that scenario to none.

Additional caching may cost more while having less impact. It makes sense to look at that separately.

@jakobbotsch
jakobbotsch marked this pull request as ready for review May 12, 2026 22:05
CopilotAI review requested due to automatic review settings May 12, 2026 22:05

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Also some decent size improvements for NativeAOT since the thunks no longer need a suspension/resumption point:

BaseSizeDiffSizeDeltaPercentImprovedName
16,852,99216,716,288-136,704-0.811%System.Linq.AsyncEnumerable.Tests.exe
115,198,464115,079,168-119,296-0.104%System.Text.Json.SourceGeneration.Roslyn4.4.Tests.exe
10,291,20010,282,496-8,704-0.085%System.Threading.Channels.Tests.exe
12,003,32811,993,088-10,240-0.085%System.IO.Tests.exe
19,561,47219,545,088-16,384-0.084%System.IO.Compression.Tests.exe
14,579,71214,568,960-10,752-0.074%System.Net.WebClient.Tests.exe
20,044,28820,029,952-14,336-0.072%System.Formats.Tar.Tests.exe
45,556,22445,524,992-31,232-0.069%System.Security.Cryptography.Tests.exe
21,715,45621,700,608-14,848-0.068%System.IO.FileSystem.Tests.exe
18,455,55218,443,264-12,288-0.067%System.Threading.Tasks.Parallel.Tests.exe
18,526,20818,513,920-12,288-0.066%System.IO.MemoryMappedFiles.Tests.exe
10,067,45610,060,800-6,656-0.066%System.Net.ServerSentEvents.Tests.exe
17,734,14417,722,368-11,776-0.066%System.Threading.Tasks.Extensions.Tests.exe
15,771,64815,761,408-10,240-0.065%System.Windows.Extensions.Tests.exe
19,296,76819,284,480-12,288-0.064%System.IO.Pipes.Tests.exe
11,286,01611,278,848-7,168-0.064%System.IO.Compression.Brotli.Tests.exe
19,570,17619,557,888-12,288-0.063%System.Diagnostics.Process.Tests.exe
20,273,66420,260,864-12,800-0.063%System.Threading.Tasks.Dataflow.Tests.exe
25,897,98425,881,600-16,384-0.063%System.Text.Json.SourceGeneration.Roslyn3.11.Tests.exe
11,343,36011,336,192-7,168-0.063%System.Net.WebSockets.Tests.exe
10,675,20010,668,544-6,656-0.062%System.IO.Pipelines.Tests.exe
13,250,56013,242,368-8,192-0.062%System.IO.Packaging.Tests.exe
20,009,98419,997,696-12,288-0.061%System.Net.WebSockets.Client.Tests.exe
31,281,66431,263,232-18,432-0.059%System.Net.Http.Functional.Tests.exe
20,942,84820,930,560-12,288-0.059%System.Net.Quic.Functional.Tests.exe
17,798,65617,788,416-10,240-0.058%System.Formats.Tar.Manual.Tests.exe
23,142,91223,129,600-13,312-0.058%System.Threading.Tasks.Tests.exe
18,947,58418,936,832-10,752-0.057%System.Runtime.Caching.Tests.exe
17,168,89617,159,168-9,728-0.057%System.Runtime.Serialization.Formatters.Disabled.Tests.exe
17,439,23217,429,504-9,728-0.056%System.Diagnostics.TraceSource.Tests.exe
17,223,16817,213,952-9,216-0.054%System.Net.ServicePoint.Tests.exe
18,070,52818,060,800-9,728-0.054%System.Console.Tests.exe
17,975,29617,965,568-9,728-0.054%System.Threading.ThreadPool.Tests.exe
18,137,60018,127,872-9,728-0.054%System.Diagnostics.TextWriterTraceListener.Tests.exe
18,380,80018,371,072-9,728-0.053%System.Threading.Tests.exe
17,296,89617,287,680-9,216-0.053%System.Runtime.InteropServices.RuntimeInformation.Tests.exe
17,438,72017,429,504-9,216-0.053%System.Net.Ping.Functional.Tests.exe
17,244,16017,234,944-9,216-0.053%System.Runtime.InvariantTimezone.Tests.exe
17,290,24017,281,024-9,216-0.053%System.Threading.Overlapped.Tests.exe
17,363,96817,354,752-9,216-0.053%Microsoft.Win32.SystemEvents.Tests.exe
17,306,11217,296,896-9,216-0.053%System.Security.Claims.Tests.exe
18,594,81618,585,088-9,728-0.052%ComInterfaceGenerator.Tests.exe
18,692,60818,682,880-9,728-0.052%Microsoft.Extensions.Logging.Console.Tests.exe
17,578,49617,569,280-9,216-0.052%MetricOuterLoop1.Tests.exe
17,573,88817,564,672-9,216-0.052%MetricOuterLoop.Tests.exe
19,688,96019,678,720-10,240-0.052%System.Net.Http.Unit.Tests.exe
17,975,29617,966,080-9,216-0.051%System.Threading.ThreadPool.WindowsThreadPool.Tests.exe
28,039,16828,024,832-14,336-0.051%System.Net.Security.Tests.exe
22,881,28022,869,504-11,776-0.051%System.Net.Sockets.Tests.exe
19,059,20019,049,472-9,728-0.051%System.Net.HttpListener.Tests.exe
18,040,83218,031,616-9,216-0.051%Microsoft.Extensions.Diagnostics.Tests.exe
17,238,52817,229,824-8,704-0.050%System.ComponentModel.EventBasedAsync.Tests.exe
17,369,08817,360,384-8,704-0.050%System.Diagnostics.StackTrace.Tests.exe
18,496,51218,487,296-9,216-0.050%Microsoft.Extensions.Hosting.Systemd.Tests.exe
17,320,44817,311,744-8,704-0.050%System.Security.Principal.Windows.Tests.exe
17,337,34417,328,640-8,704-0.050%System.Threading.Timer.Tests.exe
17,668,09617,659,392-8,704-0.049%System.IO.FileSystem.Watcher.Tests.exe
17,624,57617,615,872-8,704-0.049%System.Threading.Thread.Tests.exe
19,876,86419,867,136-9,728-0.049%System.Text.Encoding.Tests.exe
17,670,65617,661,952-8,704-0.049%System.Buffers.Tests.exe
19,401,21619,392,000-9,216-0.048%System.Net.Primitives.Functional.Tests.exe
17,161,72817,153,536-8,192-0.048%IcuAppLocal.Tests.exe
18,184,70418,176,000-8,704-0.048%System.Text.Encoding.CodePages.Tests.exe
10,061,82410,057,216-4,608-0.046%System.IO.Compression.ZipFile.Tests.exe
19,087,87219,079,168-8,704-0.046%Microsoft.Extensions.Hosting.WindowsServices.Tests.exe
21,631,48821,621,760-9,728-0.045%System.Data.OleDb.Tests.exe
20,581,37620,572,160-9,216-0.045%System.Diagnostics.DiagnosticSource.Tests.exe
13,877,76013,871,616-6,144-0.044%Microsoft.Extensions.Configuration.Functional.Tests.exe
19,752,96019,744,256-8,704-0.044%System.Resources.ResourceManager.Tests.exe
20,748,80020,739,584-9,216-0.044%System.Collections.Concurrent.Tests.exe
20,463,10420,454,400-8,704-0.043%System.Reflection.Tests.exe
20,241,40820,232,704-8,704-0.043%System.Runtime.InteropServices.Tests.exe
20,940,80020,932,096-8,704-0.042%System.Runtime.Extensions.Tests.exe
12,425,72812,420,608-5,120-0.041%System.Xml.Linq.SDMSample.Tests.exe
21,210,11221,201,408-8,704-0.041%System.Net.Requests.Tests.exe
29,510,65629,498,880-11,776-0.040%System.Net.Http.WinHttpHandler.Functional.Tests.exe
12,858,88012,853,760-5,120-0.040%System.Xml.Schema.Extensions.Tests.exe
24,283,13624,273,408-9,728-0.040%Microsoft.Extensions.Http.Tests.exe
14,142,46414,136,832-5,632-0.040%System.DirectoryServices.Protocols.Tests.exe
10,599,42410,595,328-4,096-0.039%Common.Tests.exe
9,264,1289,260,544-3,584-0.039%Microsoft.Bcl.AsyncInterfaces.Tests.exe
14,903,80814,898,176-5,632-0.038%System.Xml.Linq.Streaming.Tests.exe
13,953,53613,948,416-5,120-0.037%Microsoft.Extensions.Configuration.Xml.Tests.exe
15,094,78415,089,152-5,632-0.037%System.Security.Cryptography.Cng.Tests.exe
23,411,71223,403,008-8,704-0.037%System.Memory.Tests.exe
27,287,04027,277,312-9,728-0.036%Microsoft.Extensions.Hosting.Unit.Tests.exe
16,117,24816,111,616-5,632-0.035%System.Data.Odbc.Tests.exe
26,646,52826,637,312-9,216-0.035%Microsoft.Extensions.Logging.EventSource.Tests.exe
26,441,21626,432,000-9,216-0.035%System.ComponentModel.TypeConverter.Tests.exe
16,525,31216,519,680-5,632-0.034%System.Xml.Linq.Events.Tests.exe
16,778,75216,773,120-5,632-0.034%System.Xml.Linq.Misc.Tests.exe
26,103,80826,095,104-8,704-0.033%System.Globalization.Tests.exe
26,014,72026,006,016-8,704-0.033%System.Globalization.Nls.Tests.exe
17,009,66417,004,032-5,632-0.033%System.Xml.Linq.xNodeReader.Tests.exe
9,675,2649,672,192-3,072-0.032%System.IO.UnmanagedMemoryStream.Tests.exe
15,048,19215,043,584-4,608-0.031%System.Data.DataSetExtensions.Tests.exe
16,673,28016,668,160-5,120-0.031%System.Xml.Linq.Properties.Tests.exe
11,599,36011,595,776-3,584-0.031%System.Net.Http.Json.Unit.Tests.exe
17,065,98417,060,864-5,120-0.030%System.Xml.Linq.xNodeBuilder.Tests.exe
20,819,45620,813,312-6,144-0.030%System.Security.Cryptography.Csp.Tests.exe
17,123,84017,118,720-5,120-0.030%System.Xml.Linq.TreeManipulation.Tests.exe
33,400,83233,391,104-9,728-0.029%Microsoft.Bcl.Cryptography.Tests.exe
18,455,04018,449,920-5,120-0.028%Microsoft.Extensions.Configuration.FileExtensions.Tests.exe
32,762,36832,753,152-9,216-0.028%System.Linq.Expressions.Tests.exe
15,110,65615,106,560-4,096-0.027%System.Drawing.Primitives.Tests.exe
18,381,31218,376,704-4,608-0.025%Microsoft.Extensions.Primitives.Tests.exe
20,099,58420,094,464-5,120-0.025%Microsoft.Extensions.Configuration.Json.Tests.exe
10,521,60010,519,040-2,560-0.024%Microsoft.Extensions.Options.Tests.exe
16,973,82416,977,920+4,096+0.024%System.Private.CoreLib.dll
20,038,65620,034,048-4,608-0.023%Microsoft.Extensions.Configuration.UserSecrets.Tests.exe
19,972,09619,967,488-4,608-0.023%Microsoft.Extensions.Logging.Tests.exe
9,351,1689,349,120-2,048-0.022%System.IO.IsolatedStorage.Tests.exe
9,376,2569,374,208-2,048-0.022%System.Net.Mail.Unit.Tests.exe
18,356,22418,352,128-4,096-0.022%Microsoft.Extensions.FileProviders.Composite.Tests.exe
44,814,33644,804,608-9,728-0.022%System.Runtime.Tests.exe
18,774,01618,769,920-4,096-0.022%Microsoft.Extensions.FileProviders.Physical.Tests.exe
18,491,90418,487,808-4,096-0.022%Microsoft.Extensions.Configuration.Tests.exe
9,346,5609,344,512-2,048-0.022%Microsoft.Extensions.Diagnostics.Abstractions.Tests.exe
10,118,65610,116,608-2,048-0.020%Microsoft.Extensions.Caching.Memory.Tests.exe
30,107,13630,101,504-5,632-0.019%Microsoft.Extensions.DependencyModel.Tests.exe
10,508,28810,506,240-2,048-0.019%System.Net.Http.WinHttpHandler.Unit.Tests.exe
8,563,2008,561,664-1,536-0.018%System.Diagnostics.Contracts.Tests.exe
8,642,0488,640,512-1,536-0.018%System.Net.WebHeaderCollection.Tests.exe
8,770,0488,768,512-1,536-0.018%System.IO.FileSystem.DriveInfo.Tests.exe
12,285,95212,283,904-2,048-0.017%System.Reflection.Metadata.Tests.exe
8,989,6968,988,160-1,536-0.017%System.Net.Primitives.Pal.Tests.exe
33,161,21633,155,584-5,632-0.017%System.Private.Xml.Tests.exe
9,454,0809,452,544-1,536-0.016%Microsoft.Extensions.Logging.Testing.Tests.exe
9,630,7209,629,184-1,536-0.016%System.Composition.Convention.Tests.exe
9,314,8169,313,280-1,536-0.016%System.Net.Primitives.UnitTests.Tests.exe
11,484,16011,482,624-1,536-0.013%System.IO.Ports.Tests.exe
8,601,6008,600,576-1,024-0.012%Microsoft.Extensions.Hosting.Abstractions.Tests.exe
8,720,3848,719,360-1,024-0.012%System.Private.Uri.Unit.Tests.exe
8,546,8168,545,792-1,024-0.012%System.Runtime.CompilerServices.VisualC.Tests.exe
8,653,3128,652,288-1,024-0.012%System.Globalization.CalendarsWithConfigSwitch.Tests.exe
8,836,6088,835,584-1,024-0.012%System.Console.Manual.Tests.exe
9,364,4809,363,456-1,024-0.011%System.Reflection.Context.Tests.exe
9,041,4089,040,384-1,024-0.011%System.Net.NameResolution.Pal.Tests.exe
9,646,5929,645,568-1,024-0.011%System.Private.Uri.Functional.Tests.exe
9,704,4489,703,424-1,024-0.011%System.Text.RegularExpressions.Unit.Tests.exe
9,206,2729,205,248-1,024-0.011%Microsoft.Extensions.Configuration.EnvironmentVariables.Tests.exe
9,004,0329,003,008-1,024-0.011%System.Runtime.Serialization.Primitives.Tests.exe
9,056,2569,055,232-1,024-0.011%Microsoft.Extensions.Configuration.CommandLine.Tests.exe
9,216,5129,215,488-1,024-0.011%System.ServiceProcess.ServiceController.Tests.exe
9,539,5849,538,560-1,024-0.011%System.IO.Hashing.Tests.exe
9,671,1689,670,144-1,024-0.011%Microsoft.Extensions.Hosting.Functional.Tests.exe
4,940,2884,939,776-512-0.010%coreclr.dll
10,935,80810,934,784-1,024-0.009%System.Runtime.Intrinsics.Tests.exe
11,610,11211,609,088-1,024-0.009%System.Resources.Extensions.Tests.exe
11,193,85611,192,832-1,024-0.009%System.Text.Encodings.Web.Tests.exe
12,287,48812,286,464-1,024-0.008%System.Net.NetworkInformation.Functional.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Nls.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Tests.exe
9,039,3609,038,848-512-0.006%Microsoft.Extensions.FileSystemGlobbing.Tests.exe
8,558,0808,557,568-512-0.006%System.Text.Encoding.Extensions.Tests.exe
8,704,5128,704,000-512-0.006%System.Security.SecureString.Tests.exe
8,701,4408,700,928-512-0.006%System.Security.Cryptography.ProtectedData.Tests.exe
8,688,1288,687,616-512-0.006%System.Runtime.InteropServices.ComDisabled.Tests.exe
9,143,8089,143,296-512-0.006%System.Security.AccessControl.Tests.exe
9,025,5369,025,024-512-0.006%System.Reflection.TypeExtensions.Tests.exe
8,812,0328,811,520-512-0.006%System.Resources.Reader.Tests.exe
9,083,3929,082,880-512-0.006%System.ValueTuple.Tests.exe
8,606,2088,605,696-512-0.006%System.Resources.Writer.Tests.exe
8,817,6648,817,152-512-0.006%System.Web.HttpUtility.Tests.exe
8,863,2328,862,720-512-0.006%System.Reflection.Extensions.Tests.exe
8,670,2088,669,696-512-0.006%System.Runtime.Handles.Tests.exe
9,165,8249,165,312-512-0.006%System.Globalization.Calendars.Tests.exe
8,543,7448,543,232-512-0.006%System.IO.FileSystem.Primitives.Tests.exe
8,592,3848,591,872-512-0.006%Microsoft.Bcl.Numerics.Tests.exe
8,952,8328,952,320-512-0.006%Microsoft.Bcl.Memory.Tests.exe
8,717,3128,716,800-512-0.006%System.Diagnostics.FileVersionInfo.Tests.exe
8,700,4168,699,904-512-0.006%System.IO.FileSystem.Manual.Tests.exe
8,929,7928,929,280-512-0.006%System.IO.Pipes.AccessControl.Tests.exe
8,624,1288,623,616-512-0.006%System.Composition.Runtime.Tests.exe
9,108,9929,108,480-512-0.006%System.IO.FileSystem.AccessControl.Tests.exe
8,559,1048,558,592-512-0.006%System.Composition.AttributeModel.Tests.exe
8,759,2968,758,784-512-0.006%Invariant.Tests.exe
8,547,8408,547,328-512-0.006%System.ComponentModel.Tests.exe
9,161,7289,161,216-512-0.006%System.ComponentModel.Primitives.Tests.exe
8,959,4888,958,976-512-0.006%System.Threading.AccessControl.Tests.exe
8,991,2328,990,720-512-0.006%Microsoft.Win32.Registry.Tests.exe
8,647,6808,647,168-512-0.006%Microsoft.Win32.Registry.AccessControl.Tests.exe
8,549,3768,548,864-512-0.006%System.Diagnostics.Tools.Tests.exe
8,664,5768,664,064-512-0.006%Microsoft.Win32.Primitives.Tests.exe
9,750,0169,749,504-512-0.005%System.Collections.Specialized.Tests.exe
22,340,60822,339,584-1,024-0.005%System.Security.Cryptography.Pkcs.Tests.exe
9,852,9289,852,416-512-0.005%System.Formats.Nrbf.Tests.exe
9,763,3289,762,816-512-0.005%System.Formats.Asn1.Tests.exe
9,361,4089,360,896-512-0.005%System.Security.Permissions.Tests.exe
11,048,44811,047,936-512-0.005%System.Runtime.Numerics.Tests.exe
9,627,1369,626,624-512-0.005%System.Diagnostics.EventLog.Tests.exe
18,779,13618,778,112-1,024-0.005%System.Numerics.Vectors.Tests.exe
9,746,4329,745,920-512-0.005%System.Threading.RateLimiting.Tests.exe
9,961,4729,960,960-512-0.005%LibraryImportGenerator.Tests.exe
9,949,6969,949,184-512-0.005%System.Collections.NonGeneric.Tests.exe
9,486,8489,486,336-512-0.005%Microsoft.Extensions.Configuration.Ini.Tests.exe
13,553,66413,554,176+512+0.004%crossgen2.exe
11,796,48011,795,968-512-0.004%System.Security.Cryptography.Cose.Tests.exe
11,381,24811,380,736-512-0.004%System.Net.Security.Unit.Tests.exe
15,545,85615,545,344-512-0.003%System.Linq.Tests.exe
17,838,59217,838,080-512-0.003%System.Collections.Tests.exe

CopilotAI review requested due to automatic review settings May 13, 2026 09:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch
jakobbotsch merged commit d9f57ab into dotnet:mainMay 18, 2026
155 of 157 checks passed
@jakobbotsch
jakobbotsch deleted the cache-value-task-source-continuation branch May 18, 2026 07:31
VSadov added a commit that referenced this pull request May 21, 2026
…ce (#128399)
Same idea as in #127973, but applied when the actual Await is used (not
a thunk).
With the original repro that I was looking at, merging of #127973 helped
to reduce Gen0 GCs from ~ 15/sec to ~ 12/sec.
Alocations went down but not completely, because of code like the
following piece in the `DoReceive()` loop and possibly in other places.
This pattern still churns continuations:
https://github.com/dotnet/aspnetcore/blob/c0c2230799a3f876aa673a66bc008bf9b803acac/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs#L173-L182
```cs
var flushTask = Input.FlushAsync();
var paused = !flushTask.IsCompleted;
if (paused)
{
SocketsLog.ConnectionPause(_logger, this);
}
var result = await flushTask;
```
Because `flushTask` is not a method, the `await` does not go through a
thunk which could reuse the continuation. We call the actual `Await` in
this case and it suspends/allocates.
The change in this PR uses the same approach as in the thunk, but
applied to the Await, when awaiting a ValueTaskSource.
With this change we get to ~ 2 Gen0/sec in the repro - on par with
net10.
tommcdon added a commit that referenced this pull request May 23, 2026
…128496)
CordbAsyncStackWalk::PopulateFrame() crashes when encountering a
continuation whose ResumeInfo.DiagnosticIP is NULL (e.g. the new
ValueTaskContinuation introduced in #127973). GetNativeCodeInfoForAddr
is called with a null address which fails.
Fix: In PopulateFrame(), skip continuations with diagnosticIP == NULL
the same way DiagnosticHidden frames are skipped (advance to Next).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jakobbotsch added a commit that referenced this pull request May 26, 2026
Apply a similar optimization as #127973 to task returning thunks.
This is a very minor throughput optimization, but it also removes the
suspension point from all these thunks, which makes each of these thunks
smaller.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 17, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jakobbotsch@VSadov@lateralusX
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Cache continuation used for runtime async callable value task thunks - #127973

Merged
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation
May 18, 2026
Merged

Cache continuation used for runtime async callable value task thunks#127973
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

ValueTask backed by IValueTaskSource can suspend without allocating. However, runtime async did not support this when calling a ValueTask returning method through a runtime async callable thunk. In that case we would always allocate in the case where the ValueTask suspended.

This adds a custom ValueTaskContinuation that replaces the existing ValueTaskSourceNotifier mechanism. We now cache a ValueTaskContinuation instance and reuse it if possible whenever suspending for a ValueTask.

The same approach could be used to avoid allocations for runtime async callable thunks for task-returning methods.

cc @VSadov

`ValueTask` backed by `IValueTaskSource` can suspend without allocating.
However, runtime async did not support this when calling a `ValueTask`
returning method through a runtime async callable thunk. In that case we
would always allocate in the case where the `ValueTask` suspended.
This adds a custom `ValueTaskContinuation` that replaces the existing
`ValueTaskSourceNotifier` mechanism. We now cache a
`ValueTaskContinuation` instance and reuse it if possible whenever
suspending for a `ValueTask`.
The same approach could be used to avoid allocations for runtime async
callable thunks for task-returning methods.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 updates CoreCLR’s runtime-async callable thunk path for ValueTask/ValueTask<T> so that suspending on ValueTask backed by IValueTaskSource can reuse a cached continuation object rather than allocating per-suspension, and removes the older ValueTaskSourceNotifier / AsTaskOrNotifier mechanism.

Changes:

  • Introduces AsyncHelpers.TransparentAwaitValueTask* and a ValueTaskContinuation type that integrates with runtime-async suspension/resumption and is cached in TLS for reuse.
  • Removes ValueTask.AsTaskOrNotifier and the ValueTaskSourceNotifier helper type from ValueTask.
  • Adjusts VM continuation type handling to distinguish continuation subtypes that do have metadata (managed subclasses) vs the dynamically-created metadata-less ones, including DAC support.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.csRemoves AsTaskOrNotifier and deletes ValueTaskSourceNotifier.
src/coreclr/vm/vars.hppAdds global g_singletonContinuationEEClass declaration for continuation metadata detection.
src/coreclr/vm/vars.cppAdds global g_singletonContinuationEEClass definition.
src/coreclr/vm/typehandle.inlAvoids “upcasting” continuations that have real metadata.
src/coreclr/vm/typehandle.hAdds TypeHandle::IsContinuationWithMetadata().
src/coreclr/vm/typehandle.cppImplements TypeHandle::IsContinuationWithMetadata() and relaxes class-object allocation restriction for metadata continuations.
src/coreclr/vm/methodtable.hAdds MethodTable::IsContinuationWithMetadata() and relaxes asserts/preconditions for metadata continuations.
src/coreclr/vm/methodtable.cppImplements MethodTable::IsContinuationWithMetadata() and updates continuation-related special-casing.
src/coreclr/vm/corelib.hRemoves binder entries for ValueTask.AsTaskOrNotifier; adds binder entries for TransparentAwaitValueTask*.
src/coreclr/vm/asyncthunks.cppEmits thunk IL that tail-awaits via TransparentAwaitValueTask* instead of AsTaskOrNotifier + TransparentAwait.
src/coreclr/vm/asynccontinuations.cppMoves singleton continuation EEClass storage to global g_singletonContinuationEEClass.
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.csUpdates managed type-system thunk emission to call TransparentAwaitValueTask* and TailAwait.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdds ValueTaskContinuation, TLS caching, and TransparentAwaitValueTask*; updates suspension handling to use it.
src/coreclr/inc/dacvars.hExposes g_singletonContinuationEEClass to DAC.
src/coreclr/debug/daccess/dacdbiimpl.cppUpdates continuation canonical-MT validity logic to account for metadata continuations.

Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/asyncthunks.cpp
@VSadov

Copy link
Copy Markdown
Member

Nice! Instead of caching and reusing ValueTaskSourceNotifier, we now cache/reuse the entire head continuation.

A scenario that awaits in a loop some ValueTaskSource wrapper (like reading chunks from a pipe) can run allocation-free.

@jakobbotsch

jakobbotsch commented May 10, 2026

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

Also, we probably need to introduce a runtime async mechanism similar to PoolingAsyncValueTaskMethodBuilder -- e.g. apply [PoolContinuations] on a runtime async method and it would cache continuations for that method following that scheme too.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 21 out of 21 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs:1

  • The XML doc block still describes awaiting ValueTask, but TransparentAwait now accepts Task only, while ValueTask awaiting is handled by TransparentAwaitValueTask* above. Please update/move this comment so TransparentAwait’s docs describe only the Task scenario, and place the ValueTask-specific explanation on TransparentAwaitValueTask / TransparentAwaitValueTaskOfT.
// Licensed to the .NET Foundation under one or more agreements.

Comment threadsrc/coreclr/vm/methodtable.cpp
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

I think I will do that separately. Right now the caching is essentially free since we can store it in the runtime async TLS that we already needed to access. If we introduce the per-core cache we will be paying more when the TLS cache doesn't hit, so I want to make sure I have some benchmarks for this.

@VSadov

Copy link
Copy Markdown
Member

I think I will do that separately

Per thread caching of 1 head continuation is probably the most effective as we reduce necessary allocations for that scenario to none.

Additional caching may cost more while having less impact. It makes sense to look at that separately.

@jakobbotsch
jakobbotsch marked this pull request as ready for review May 12, 2026 22:05
CopilotAI review requested due to automatic review settings May 12, 2026 22:05

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Also some decent size improvements for NativeAOT since the thunks no longer need a suspension/resumption point:

BaseSizeDiffSizeDeltaPercentImprovedName
16,852,99216,716,288-136,704-0.811%System.Linq.AsyncEnumerable.Tests.exe
115,198,464115,079,168-119,296-0.104%System.Text.Json.SourceGeneration.Roslyn4.4.Tests.exe
10,291,20010,282,496-8,704-0.085%System.Threading.Channels.Tests.exe
12,003,32811,993,088-10,240-0.085%System.IO.Tests.exe
19,561,47219,545,088-16,384-0.084%System.IO.Compression.Tests.exe
14,579,71214,568,960-10,752-0.074%System.Net.WebClient.Tests.exe
20,044,28820,029,952-14,336-0.072%System.Formats.Tar.Tests.exe
45,556,22445,524,992-31,232-0.069%System.Security.Cryptography.Tests.exe
21,715,45621,700,608-14,848-0.068%System.IO.FileSystem.Tests.exe
18,455,55218,443,264-12,288-0.067%System.Threading.Tasks.Parallel.Tests.exe
18,526,20818,513,920-12,288-0.066%System.IO.MemoryMappedFiles.Tests.exe
10,067,45610,060,800-6,656-0.066%System.Net.ServerSentEvents.Tests.exe
17,734,14417,722,368-11,776-0.066%System.Threading.Tasks.Extensions.Tests.exe
15,771,64815,761,408-10,240-0.065%System.Windows.Extensions.Tests.exe
19,296,76819,284,480-12,288-0.064%System.IO.Pipes.Tests.exe
11,286,01611,278,848-7,168-0.064%System.IO.Compression.Brotli.Tests.exe
19,570,17619,557,888-12,288-0.063%System.Diagnostics.Process.Tests.exe
20,273,66420,260,864-12,800-0.063%System.Threading.Tasks.Dataflow.Tests.exe
25,897,98425,881,600-16,384-0.063%System.Text.Json.SourceGeneration.Roslyn3.11.Tests.exe
11,343,36011,336,192-7,168-0.063%System.Net.WebSockets.Tests.exe
10,675,20010,668,544-6,656-0.062%System.IO.Pipelines.Tests.exe
13,250,56013,242,368-8,192-0.062%System.IO.Packaging.Tests.exe
20,009,98419,997,696-12,288-0.061%System.Net.WebSockets.Client.Tests.exe
31,281,66431,263,232-18,432-0.059%System.Net.Http.Functional.Tests.exe
20,942,84820,930,560-12,288-0.059%System.Net.Quic.Functional.Tests.exe
17,798,65617,788,416-10,240-0.058%System.Formats.Tar.Manual.Tests.exe
23,142,91223,129,600-13,312-0.058%System.Threading.Tasks.Tests.exe
18,947,58418,936,832-10,752-0.057%System.Runtime.Caching.Tests.exe
17,168,89617,159,168-9,728-0.057%System.Runtime.Serialization.Formatters.Disabled.Tests.exe
17,439,23217,429,504-9,728-0.056%System.Diagnostics.TraceSource.Tests.exe
17,223,16817,213,952-9,216-0.054%System.Net.ServicePoint.Tests.exe
18,070,52818,060,800-9,728-0.054%System.Console.Tests.exe
17,975,29617,965,568-9,728-0.054%System.Threading.ThreadPool.Tests.exe
18,137,60018,127,872-9,728-0.054%System.Diagnostics.TextWriterTraceListener.Tests.exe
18,380,80018,371,072-9,728-0.053%System.Threading.Tests.exe
17,296,89617,287,680-9,216-0.053%System.Runtime.InteropServices.RuntimeInformation.Tests.exe
17,438,72017,429,504-9,216-0.053%System.Net.Ping.Functional.Tests.exe
17,244,16017,234,944-9,216-0.053%System.Runtime.InvariantTimezone.Tests.exe
17,290,24017,281,024-9,216-0.053%System.Threading.Overlapped.Tests.exe
17,363,96817,354,752-9,216-0.053%Microsoft.Win32.SystemEvents.Tests.exe
17,306,11217,296,896-9,216-0.053%System.Security.Claims.Tests.exe
18,594,81618,585,088-9,728-0.052%ComInterfaceGenerator.Tests.exe
18,692,60818,682,880-9,728-0.052%Microsoft.Extensions.Logging.Console.Tests.exe
17,578,49617,569,280-9,216-0.052%MetricOuterLoop1.Tests.exe
17,573,88817,564,672-9,216-0.052%MetricOuterLoop.Tests.exe
19,688,96019,678,720-10,240-0.052%System.Net.Http.Unit.Tests.exe
17,975,29617,966,080-9,216-0.051%System.Threading.ThreadPool.WindowsThreadPool.Tests.exe
28,039,16828,024,832-14,336-0.051%System.Net.Security.Tests.exe
22,881,28022,869,504-11,776-0.051%System.Net.Sockets.Tests.exe
19,059,20019,049,472-9,728-0.051%System.Net.HttpListener.Tests.exe
18,040,83218,031,616-9,216-0.051%Microsoft.Extensions.Diagnostics.Tests.exe
17,238,52817,229,824-8,704-0.050%System.ComponentModel.EventBasedAsync.Tests.exe
17,369,08817,360,384-8,704-0.050%System.Diagnostics.StackTrace.Tests.exe
18,496,51218,487,296-9,216-0.050%Microsoft.Extensions.Hosting.Systemd.Tests.exe
17,320,44817,311,744-8,704-0.050%System.Security.Principal.Windows.Tests.exe
17,337,34417,328,640-8,704-0.050%System.Threading.Timer.Tests.exe
17,668,09617,659,392-8,704-0.049%System.IO.FileSystem.Watcher.Tests.exe
17,624,57617,615,872-8,704-0.049%System.Threading.Thread.Tests.exe
19,876,86419,867,136-9,728-0.049%System.Text.Encoding.Tests.exe
17,670,65617,661,952-8,704-0.049%System.Buffers.Tests.exe
19,401,21619,392,000-9,216-0.048%System.Net.Primitives.Functional.Tests.exe
17,161,72817,153,536-8,192-0.048%IcuAppLocal.Tests.exe
18,184,70418,176,000-8,704-0.048%System.Text.Encoding.CodePages.Tests.exe
10,061,82410,057,216-4,608-0.046%System.IO.Compression.ZipFile.Tests.exe
19,087,87219,079,168-8,704-0.046%Microsoft.Extensions.Hosting.WindowsServices.Tests.exe
21,631,48821,621,760-9,728-0.045%System.Data.OleDb.Tests.exe
20,581,37620,572,160-9,216-0.045%System.Diagnostics.DiagnosticSource.Tests.exe
13,877,76013,871,616-6,144-0.044%Microsoft.Extensions.Configuration.Functional.Tests.exe
19,752,96019,744,256-8,704-0.044%System.Resources.ResourceManager.Tests.exe
20,748,80020,739,584-9,216-0.044%System.Collections.Concurrent.Tests.exe
20,463,10420,454,400-8,704-0.043%System.Reflection.Tests.exe
20,241,40820,232,704-8,704-0.043%System.Runtime.InteropServices.Tests.exe
20,940,80020,932,096-8,704-0.042%System.Runtime.Extensions.Tests.exe
12,425,72812,420,608-5,120-0.041%System.Xml.Linq.SDMSample.Tests.exe
21,210,11221,201,408-8,704-0.041%System.Net.Requests.Tests.exe
29,510,65629,498,880-11,776-0.040%System.Net.Http.WinHttpHandler.Functional.Tests.exe
12,858,88012,853,760-5,120-0.040%System.Xml.Schema.Extensions.Tests.exe
24,283,13624,273,408-9,728-0.040%Microsoft.Extensions.Http.Tests.exe
14,142,46414,136,832-5,632-0.040%System.DirectoryServices.Protocols.Tests.exe
10,599,42410,595,328-4,096-0.039%Common.Tests.exe
9,264,1289,260,544-3,584-0.039%Microsoft.Bcl.AsyncInterfaces.Tests.exe
14,903,80814,898,176-5,632-0.038%System.Xml.Linq.Streaming.Tests.exe
13,953,53613,948,416-5,120-0.037%Microsoft.Extensions.Configuration.Xml.Tests.exe
15,094,78415,089,152-5,632-0.037%System.Security.Cryptography.Cng.Tests.exe
23,411,71223,403,008-8,704-0.037%System.Memory.Tests.exe
27,287,04027,277,312-9,728-0.036%Microsoft.Extensions.Hosting.Unit.Tests.exe
16,117,24816,111,616-5,632-0.035%System.Data.Odbc.Tests.exe
26,646,52826,637,312-9,216-0.035%Microsoft.Extensions.Logging.EventSource.Tests.exe
26,441,21626,432,000-9,216-0.035%System.ComponentModel.TypeConverter.Tests.exe
16,525,31216,519,680-5,632-0.034%System.Xml.Linq.Events.Tests.exe
16,778,75216,773,120-5,632-0.034%System.Xml.Linq.Misc.Tests.exe
26,103,80826,095,104-8,704-0.033%System.Globalization.Tests.exe
26,014,72026,006,016-8,704-0.033%System.Globalization.Nls.Tests.exe
17,009,66417,004,032-5,632-0.033%System.Xml.Linq.xNodeReader.Tests.exe
9,675,2649,672,192-3,072-0.032%System.IO.UnmanagedMemoryStream.Tests.exe
15,048,19215,043,584-4,608-0.031%System.Data.DataSetExtensions.Tests.exe
16,673,28016,668,160-5,120-0.031%System.Xml.Linq.Properties.Tests.exe
11,599,36011,595,776-3,584-0.031%System.Net.Http.Json.Unit.Tests.exe
17,065,98417,060,864-5,120-0.030%System.Xml.Linq.xNodeBuilder.Tests.exe
20,819,45620,813,312-6,144-0.030%System.Security.Cryptography.Csp.Tests.exe
17,123,84017,118,720-5,120-0.030%System.Xml.Linq.TreeManipulation.Tests.exe
33,400,83233,391,104-9,728-0.029%Microsoft.Bcl.Cryptography.Tests.exe
18,455,04018,449,920-5,120-0.028%Microsoft.Extensions.Configuration.FileExtensions.Tests.exe
32,762,36832,753,152-9,216-0.028%System.Linq.Expressions.Tests.exe
15,110,65615,106,560-4,096-0.027%System.Drawing.Primitives.Tests.exe
18,381,31218,376,704-4,608-0.025%Microsoft.Extensions.Primitives.Tests.exe
20,099,58420,094,464-5,120-0.025%Microsoft.Extensions.Configuration.Json.Tests.exe
10,521,60010,519,040-2,560-0.024%Microsoft.Extensions.Options.Tests.exe
16,973,82416,977,920+4,096+0.024%System.Private.CoreLib.dll
20,038,65620,034,048-4,608-0.023%Microsoft.Extensions.Configuration.UserSecrets.Tests.exe
19,972,09619,967,488-4,608-0.023%Microsoft.Extensions.Logging.Tests.exe
9,351,1689,349,120-2,048-0.022%System.IO.IsolatedStorage.Tests.exe
9,376,2569,374,208-2,048-0.022%System.Net.Mail.Unit.Tests.exe
18,356,22418,352,128-4,096-0.022%Microsoft.Extensions.FileProviders.Composite.Tests.exe
44,814,33644,804,608-9,728-0.022%System.Runtime.Tests.exe
18,774,01618,769,920-4,096-0.022%Microsoft.Extensions.FileProviders.Physical.Tests.exe
18,491,90418,487,808-4,096-0.022%Microsoft.Extensions.Configuration.Tests.exe
9,346,5609,344,512-2,048-0.022%Microsoft.Extensions.Diagnostics.Abstractions.Tests.exe
10,118,65610,116,608-2,048-0.020%Microsoft.Extensions.Caching.Memory.Tests.exe
30,107,13630,101,504-5,632-0.019%Microsoft.Extensions.DependencyModel.Tests.exe
10,508,28810,506,240-2,048-0.019%System.Net.Http.WinHttpHandler.Unit.Tests.exe
8,563,2008,561,664-1,536-0.018%System.Diagnostics.Contracts.Tests.exe
8,642,0488,640,512-1,536-0.018%System.Net.WebHeaderCollection.Tests.exe
8,770,0488,768,512-1,536-0.018%System.IO.FileSystem.DriveInfo.Tests.exe
12,285,95212,283,904-2,048-0.017%System.Reflection.Metadata.Tests.exe
8,989,6968,988,160-1,536-0.017%System.Net.Primitives.Pal.Tests.exe
33,161,21633,155,584-5,632-0.017%System.Private.Xml.Tests.exe
9,454,0809,452,544-1,536-0.016%Microsoft.Extensions.Logging.Testing.Tests.exe
9,630,7209,629,184-1,536-0.016%System.Composition.Convention.Tests.exe
9,314,8169,313,280-1,536-0.016%System.Net.Primitives.UnitTests.Tests.exe
11,484,16011,482,624-1,536-0.013%System.IO.Ports.Tests.exe
8,601,6008,600,576-1,024-0.012%Microsoft.Extensions.Hosting.Abstractions.Tests.exe
8,720,3848,719,360-1,024-0.012%System.Private.Uri.Unit.Tests.exe
8,546,8168,545,792-1,024-0.012%System.Runtime.CompilerServices.VisualC.Tests.exe
8,653,3128,652,288-1,024-0.012%System.Globalization.CalendarsWithConfigSwitch.Tests.exe
8,836,6088,835,584-1,024-0.012%System.Console.Manual.Tests.exe
9,364,4809,363,456-1,024-0.011%System.Reflection.Context.Tests.exe
9,041,4089,040,384-1,024-0.011%System.Net.NameResolution.Pal.Tests.exe
9,646,5929,645,568-1,024-0.011%System.Private.Uri.Functional.Tests.exe
9,704,4489,703,424-1,024-0.011%System.Text.RegularExpressions.Unit.Tests.exe
9,206,2729,205,248-1,024-0.011%Microsoft.Extensions.Configuration.EnvironmentVariables.Tests.exe
9,004,0329,003,008-1,024-0.011%System.Runtime.Serialization.Primitives.Tests.exe
9,056,2569,055,232-1,024-0.011%Microsoft.Extensions.Configuration.CommandLine.Tests.exe
9,216,5129,215,488-1,024-0.011%System.ServiceProcess.ServiceController.Tests.exe
9,539,5849,538,560-1,024-0.011%System.IO.Hashing.Tests.exe
9,671,1689,670,144-1,024-0.011%Microsoft.Extensions.Hosting.Functional.Tests.exe
4,940,2884,939,776-512-0.010%coreclr.dll
10,935,80810,934,784-1,024-0.009%System.Runtime.Intrinsics.Tests.exe
11,610,11211,609,088-1,024-0.009%System.Resources.Extensions.Tests.exe
11,193,85611,192,832-1,024-0.009%System.Text.Encodings.Web.Tests.exe
12,287,48812,286,464-1,024-0.008%System.Net.NetworkInformation.Functional.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Nls.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Tests.exe
9,039,3609,038,848-512-0.006%Microsoft.Extensions.FileSystemGlobbing.Tests.exe
8,558,0808,557,568-512-0.006%System.Text.Encoding.Extensions.Tests.exe
8,704,5128,704,000-512-0.006%System.Security.SecureString.Tests.exe
8,701,4408,700,928-512-0.006%System.Security.Cryptography.ProtectedData.Tests.exe
8,688,1288,687,616-512-0.006%System.Runtime.InteropServices.ComDisabled.Tests.exe
9,143,8089,143,296-512-0.006%System.Security.AccessControl.Tests.exe
9,025,5369,025,024-512-0.006%System.Reflection.TypeExtensions.Tests.exe
8,812,0328,811,520-512-0.006%System.Resources.Reader.Tests.exe
9,083,3929,082,880-512-0.006%System.ValueTuple.Tests.exe
8,606,2088,605,696-512-0.006%System.Resources.Writer.Tests.exe
8,817,6648,817,152-512-0.006%System.Web.HttpUtility.Tests.exe
8,863,2328,862,720-512-0.006%System.Reflection.Extensions.Tests.exe
8,670,2088,669,696-512-0.006%System.Runtime.Handles.Tests.exe
9,165,8249,165,312-512-0.006%System.Globalization.Calendars.Tests.exe
8,543,7448,543,232-512-0.006%System.IO.FileSystem.Primitives.Tests.exe
8,592,3848,591,872-512-0.006%Microsoft.Bcl.Numerics.Tests.exe
8,952,8328,952,320-512-0.006%Microsoft.Bcl.Memory.Tests.exe
8,717,3128,716,800-512-0.006%System.Diagnostics.FileVersionInfo.Tests.exe
8,700,4168,699,904-512-0.006%System.IO.FileSystem.Manual.Tests.exe
8,929,7928,929,280-512-0.006%System.IO.Pipes.AccessControl.Tests.exe
8,624,1288,623,616-512-0.006%System.Composition.Runtime.Tests.exe
9,108,9929,108,480-512-0.006%System.IO.FileSystem.AccessControl.Tests.exe
8,559,1048,558,592-512-0.006%System.Composition.AttributeModel.Tests.exe
8,759,2968,758,784-512-0.006%Invariant.Tests.exe
8,547,8408,547,328-512-0.006%System.ComponentModel.Tests.exe
9,161,7289,161,216-512-0.006%System.ComponentModel.Primitives.Tests.exe
8,959,4888,958,976-512-0.006%System.Threading.AccessControl.Tests.exe
8,991,2328,990,720-512-0.006%Microsoft.Win32.Registry.Tests.exe
8,647,6808,647,168-512-0.006%Microsoft.Win32.Registry.AccessControl.Tests.exe
8,549,3768,548,864-512-0.006%System.Diagnostics.Tools.Tests.exe
8,664,5768,664,064-512-0.006%Microsoft.Win32.Primitives.Tests.exe
9,750,0169,749,504-512-0.005%System.Collections.Specialized.Tests.exe
22,340,60822,339,584-1,024-0.005%System.Security.Cryptography.Pkcs.Tests.exe
9,852,9289,852,416-512-0.005%System.Formats.Nrbf.Tests.exe
9,763,3289,762,816-512-0.005%System.Formats.Asn1.Tests.exe
9,361,4089,360,896-512-0.005%System.Security.Permissions.Tests.exe
11,048,44811,047,936-512-0.005%System.Runtime.Numerics.Tests.exe
9,627,1369,626,624-512-0.005%System.Diagnostics.EventLog.Tests.exe
18,779,13618,778,112-1,024-0.005%System.Numerics.Vectors.Tests.exe
9,746,4329,745,920-512-0.005%System.Threading.RateLimiting.Tests.exe
9,961,4729,960,960-512-0.005%LibraryImportGenerator.Tests.exe
9,949,6969,949,184-512-0.005%System.Collections.NonGeneric.Tests.exe
9,486,8489,486,336-512-0.005%Microsoft.Extensions.Configuration.Ini.Tests.exe
13,553,66413,554,176+512+0.004%crossgen2.exe
11,796,48011,795,968-512-0.004%System.Security.Cryptography.Cose.Tests.exe
11,381,24811,380,736-512-0.004%System.Net.Security.Unit.Tests.exe
15,545,85615,545,344-512-0.003%System.Linq.Tests.exe
17,838,59217,838,080-512-0.003%System.Collections.Tests.exe

CopilotAI review requested due to automatic review settings May 13, 2026 09:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch
jakobbotsch merged commit d9f57ab into dotnet:mainMay 18, 2026
155 of 157 checks passed
@jakobbotsch
jakobbotsch deleted the cache-value-task-source-continuation branch May 18, 2026 07:31
VSadov added a commit that referenced this pull request May 21, 2026
…ce (#128399)
Same idea as in #127973, but applied when the actual Await is used (not
a thunk).
With the original repro that I was looking at, merging of #127973 helped
to reduce Gen0 GCs from ~ 15/sec to ~ 12/sec.
Alocations went down but not completely, because of code like the
following piece in the `DoReceive()` loop and possibly in other places.
This pattern still churns continuations:
https://github.com/dotnet/aspnetcore/blob/c0c2230799a3f876aa673a66bc008bf9b803acac/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs#L173-L182
```cs
var flushTask = Input.FlushAsync();
var paused = !flushTask.IsCompleted;
if (paused)
{
SocketsLog.ConnectionPause(_logger, this);
}
var result = await flushTask;
```
Because `flushTask` is not a method, the `await` does not go through a
thunk which could reuse the continuation. We call the actual `Await` in
this case and it suspends/allocates.
The change in this PR uses the same approach as in the thunk, but
applied to the Await, when awaiting a ValueTaskSource.
With this change we get to ~ 2 Gen0/sec in the repro - on par with
net10.
tommcdon added a commit that referenced this pull request May 23, 2026
…128496)
CordbAsyncStackWalk::PopulateFrame() crashes when encountering a
continuation whose ResumeInfo.DiagnosticIP is NULL (e.g. the new
ValueTaskContinuation introduced in #127973). GetNativeCodeInfoForAddr
is called with a null address which fails.
Fix: In PopulateFrame(), skip continuations with diagnosticIP == NULL
the same way DiagnosticHidden frames are skipped (advance to Next).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jakobbotsch added a commit that referenced this pull request May 26, 2026
Apply a similar optimization as #127973 to task returning thunks.
This is a very minor throughput optimization, but it also removes the
suspension point from all these thunks, which makes each of these thunks
smaller.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 17, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jakobbotsch@VSadov@lateralusX
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Cache continuation used for runtime async callable value task thunks - #127973

Merged
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation
May 18, 2026
Merged

Cache continuation used for runtime async callable value task thunks#127973
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

ValueTask backed by IValueTaskSource can suspend without allocating. However, runtime async did not support this when calling a ValueTask returning method through a runtime async callable thunk. In that case we would always allocate in the case where the ValueTask suspended.

This adds a custom ValueTaskContinuation that replaces the existing ValueTaskSourceNotifier mechanism. We now cache a ValueTaskContinuation instance and reuse it if possible whenever suspending for a ValueTask.

The same approach could be used to avoid allocations for runtime async callable thunks for task-returning methods.

cc @VSadov

`ValueTask` backed by `IValueTaskSource` can suspend without allocating.
However, runtime async did not support this when calling a `ValueTask`
returning method through a runtime async callable thunk. In that case we
would always allocate in the case where the `ValueTask` suspended.
This adds a custom `ValueTaskContinuation` that replaces the existing
`ValueTaskSourceNotifier` mechanism. We now cache a
`ValueTaskContinuation` instance and reuse it if possible whenever
suspending for a `ValueTask`.
The same approach could be used to avoid allocations for runtime async
callable thunks for task-returning methods.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 updates CoreCLR’s runtime-async callable thunk path for ValueTask/ValueTask<T> so that suspending on ValueTask backed by IValueTaskSource can reuse a cached continuation object rather than allocating per-suspension, and removes the older ValueTaskSourceNotifier / AsTaskOrNotifier mechanism.

Changes:

  • Introduces AsyncHelpers.TransparentAwaitValueTask* and a ValueTaskContinuation type that integrates with runtime-async suspension/resumption and is cached in TLS for reuse.
  • Removes ValueTask.AsTaskOrNotifier and the ValueTaskSourceNotifier helper type from ValueTask.
  • Adjusts VM continuation type handling to distinguish continuation subtypes that do have metadata (managed subclasses) vs the dynamically-created metadata-less ones, including DAC support.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.csRemoves AsTaskOrNotifier and deletes ValueTaskSourceNotifier.
src/coreclr/vm/vars.hppAdds global g_singletonContinuationEEClass declaration for continuation metadata detection.
src/coreclr/vm/vars.cppAdds global g_singletonContinuationEEClass definition.
src/coreclr/vm/typehandle.inlAvoids “upcasting” continuations that have real metadata.
src/coreclr/vm/typehandle.hAdds TypeHandle::IsContinuationWithMetadata().
src/coreclr/vm/typehandle.cppImplements TypeHandle::IsContinuationWithMetadata() and relaxes class-object allocation restriction for metadata continuations.
src/coreclr/vm/methodtable.hAdds MethodTable::IsContinuationWithMetadata() and relaxes asserts/preconditions for metadata continuations.
src/coreclr/vm/methodtable.cppImplements MethodTable::IsContinuationWithMetadata() and updates continuation-related special-casing.
src/coreclr/vm/corelib.hRemoves binder entries for ValueTask.AsTaskOrNotifier; adds binder entries for TransparentAwaitValueTask*.
src/coreclr/vm/asyncthunks.cppEmits thunk IL that tail-awaits via TransparentAwaitValueTask* instead of AsTaskOrNotifier + TransparentAwait.
src/coreclr/vm/asynccontinuations.cppMoves singleton continuation EEClass storage to global g_singletonContinuationEEClass.
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.csUpdates managed type-system thunk emission to call TransparentAwaitValueTask* and TailAwait.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdds ValueTaskContinuation, TLS caching, and TransparentAwaitValueTask*; updates suspension handling to use it.
src/coreclr/inc/dacvars.hExposes g_singletonContinuationEEClass to DAC.
src/coreclr/debug/daccess/dacdbiimpl.cppUpdates continuation canonical-MT validity logic to account for metadata continuations.

Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/asyncthunks.cpp
@VSadov

Copy link
Copy Markdown
Member

Nice! Instead of caching and reusing ValueTaskSourceNotifier, we now cache/reuse the entire head continuation.

A scenario that awaits in a loop some ValueTaskSource wrapper (like reading chunks from a pipe) can run allocation-free.

@jakobbotsch

jakobbotsch commented May 10, 2026

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

Also, we probably need to introduce a runtime async mechanism similar to PoolingAsyncValueTaskMethodBuilder -- e.g. apply [PoolContinuations] on a runtime async method and it would cache continuations for that method following that scheme too.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 21 out of 21 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs:1

  • The XML doc block still describes awaiting ValueTask, but TransparentAwait now accepts Task only, while ValueTask awaiting is handled by TransparentAwaitValueTask* above. Please update/move this comment so TransparentAwait’s docs describe only the Task scenario, and place the ValueTask-specific explanation on TransparentAwaitValueTask / TransparentAwaitValueTaskOfT.
// Licensed to the .NET Foundation under one or more agreements.

Comment threadsrc/coreclr/vm/methodtable.cpp
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

I think I will do that separately. Right now the caching is essentially free since we can store it in the runtime async TLS that we already needed to access. If we introduce the per-core cache we will be paying more when the TLS cache doesn't hit, so I want to make sure I have some benchmarks for this.

@VSadov

Copy link
Copy Markdown
Member

I think I will do that separately

Per thread caching of 1 head continuation is probably the most effective as we reduce necessary allocations for that scenario to none.

Additional caching may cost more while having less impact. It makes sense to look at that separately.

@jakobbotsch
jakobbotsch marked this pull request as ready for review May 12, 2026 22:05
CopilotAI review requested due to automatic review settings May 12, 2026 22:05

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Also some decent size improvements for NativeAOT since the thunks no longer need a suspension/resumption point:

BaseSizeDiffSizeDeltaPercentImprovedName
16,852,99216,716,288-136,704-0.811%System.Linq.AsyncEnumerable.Tests.exe
115,198,464115,079,168-119,296-0.104%System.Text.Json.SourceGeneration.Roslyn4.4.Tests.exe
10,291,20010,282,496-8,704-0.085%System.Threading.Channels.Tests.exe
12,003,32811,993,088-10,240-0.085%System.IO.Tests.exe
19,561,47219,545,088-16,384-0.084%System.IO.Compression.Tests.exe
14,579,71214,568,960-10,752-0.074%System.Net.WebClient.Tests.exe
20,044,28820,029,952-14,336-0.072%System.Formats.Tar.Tests.exe
45,556,22445,524,992-31,232-0.069%System.Security.Cryptography.Tests.exe
21,715,45621,700,608-14,848-0.068%System.IO.FileSystem.Tests.exe
18,455,55218,443,264-12,288-0.067%System.Threading.Tasks.Parallel.Tests.exe
18,526,20818,513,920-12,288-0.066%System.IO.MemoryMappedFiles.Tests.exe
10,067,45610,060,800-6,656-0.066%System.Net.ServerSentEvents.Tests.exe
17,734,14417,722,368-11,776-0.066%System.Threading.Tasks.Extensions.Tests.exe
15,771,64815,761,408-10,240-0.065%System.Windows.Extensions.Tests.exe
19,296,76819,284,480-12,288-0.064%System.IO.Pipes.Tests.exe
11,286,01611,278,848-7,168-0.064%System.IO.Compression.Brotli.Tests.exe
19,570,17619,557,888-12,288-0.063%System.Diagnostics.Process.Tests.exe
20,273,66420,260,864-12,800-0.063%System.Threading.Tasks.Dataflow.Tests.exe
25,897,98425,881,600-16,384-0.063%System.Text.Json.SourceGeneration.Roslyn3.11.Tests.exe
11,343,36011,336,192-7,168-0.063%System.Net.WebSockets.Tests.exe
10,675,20010,668,544-6,656-0.062%System.IO.Pipelines.Tests.exe
13,250,56013,242,368-8,192-0.062%System.IO.Packaging.Tests.exe
20,009,98419,997,696-12,288-0.061%System.Net.WebSockets.Client.Tests.exe
31,281,66431,263,232-18,432-0.059%System.Net.Http.Functional.Tests.exe
20,942,84820,930,560-12,288-0.059%System.Net.Quic.Functional.Tests.exe
17,798,65617,788,416-10,240-0.058%System.Formats.Tar.Manual.Tests.exe
23,142,91223,129,600-13,312-0.058%System.Threading.Tasks.Tests.exe
18,947,58418,936,832-10,752-0.057%System.Runtime.Caching.Tests.exe
17,168,89617,159,168-9,728-0.057%System.Runtime.Serialization.Formatters.Disabled.Tests.exe
17,439,23217,429,504-9,728-0.056%System.Diagnostics.TraceSource.Tests.exe
17,223,16817,213,952-9,216-0.054%System.Net.ServicePoint.Tests.exe
18,070,52818,060,800-9,728-0.054%System.Console.Tests.exe
17,975,29617,965,568-9,728-0.054%System.Threading.ThreadPool.Tests.exe
18,137,60018,127,872-9,728-0.054%System.Diagnostics.TextWriterTraceListener.Tests.exe
18,380,80018,371,072-9,728-0.053%System.Threading.Tests.exe
17,296,89617,287,680-9,216-0.053%System.Runtime.InteropServices.RuntimeInformation.Tests.exe
17,438,72017,429,504-9,216-0.053%System.Net.Ping.Functional.Tests.exe
17,244,16017,234,944-9,216-0.053%System.Runtime.InvariantTimezone.Tests.exe
17,290,24017,281,024-9,216-0.053%System.Threading.Overlapped.Tests.exe
17,363,96817,354,752-9,216-0.053%Microsoft.Win32.SystemEvents.Tests.exe
17,306,11217,296,896-9,216-0.053%System.Security.Claims.Tests.exe
18,594,81618,585,088-9,728-0.052%ComInterfaceGenerator.Tests.exe
18,692,60818,682,880-9,728-0.052%Microsoft.Extensions.Logging.Console.Tests.exe
17,578,49617,569,280-9,216-0.052%MetricOuterLoop1.Tests.exe
17,573,88817,564,672-9,216-0.052%MetricOuterLoop.Tests.exe
19,688,96019,678,720-10,240-0.052%System.Net.Http.Unit.Tests.exe
17,975,29617,966,080-9,216-0.051%System.Threading.ThreadPool.WindowsThreadPool.Tests.exe
28,039,16828,024,832-14,336-0.051%System.Net.Security.Tests.exe
22,881,28022,869,504-11,776-0.051%System.Net.Sockets.Tests.exe
19,059,20019,049,472-9,728-0.051%System.Net.HttpListener.Tests.exe
18,040,83218,031,616-9,216-0.051%Microsoft.Extensions.Diagnostics.Tests.exe
17,238,52817,229,824-8,704-0.050%System.ComponentModel.EventBasedAsync.Tests.exe
17,369,08817,360,384-8,704-0.050%System.Diagnostics.StackTrace.Tests.exe
18,496,51218,487,296-9,216-0.050%Microsoft.Extensions.Hosting.Systemd.Tests.exe
17,320,44817,311,744-8,704-0.050%System.Security.Principal.Windows.Tests.exe
17,337,34417,328,640-8,704-0.050%System.Threading.Timer.Tests.exe
17,668,09617,659,392-8,704-0.049%System.IO.FileSystem.Watcher.Tests.exe
17,624,57617,615,872-8,704-0.049%System.Threading.Thread.Tests.exe
19,876,86419,867,136-9,728-0.049%System.Text.Encoding.Tests.exe
17,670,65617,661,952-8,704-0.049%System.Buffers.Tests.exe
19,401,21619,392,000-9,216-0.048%System.Net.Primitives.Functional.Tests.exe
17,161,72817,153,536-8,192-0.048%IcuAppLocal.Tests.exe
18,184,70418,176,000-8,704-0.048%System.Text.Encoding.CodePages.Tests.exe
10,061,82410,057,216-4,608-0.046%System.IO.Compression.ZipFile.Tests.exe
19,087,87219,079,168-8,704-0.046%Microsoft.Extensions.Hosting.WindowsServices.Tests.exe
21,631,48821,621,760-9,728-0.045%System.Data.OleDb.Tests.exe
20,581,37620,572,160-9,216-0.045%System.Diagnostics.DiagnosticSource.Tests.exe
13,877,76013,871,616-6,144-0.044%Microsoft.Extensions.Configuration.Functional.Tests.exe
19,752,96019,744,256-8,704-0.044%System.Resources.ResourceManager.Tests.exe
20,748,80020,739,584-9,216-0.044%System.Collections.Concurrent.Tests.exe
20,463,10420,454,400-8,704-0.043%System.Reflection.Tests.exe
20,241,40820,232,704-8,704-0.043%System.Runtime.InteropServices.Tests.exe
20,940,80020,932,096-8,704-0.042%System.Runtime.Extensions.Tests.exe
12,425,72812,420,608-5,120-0.041%System.Xml.Linq.SDMSample.Tests.exe
21,210,11221,201,408-8,704-0.041%System.Net.Requests.Tests.exe
29,510,65629,498,880-11,776-0.040%System.Net.Http.WinHttpHandler.Functional.Tests.exe
12,858,88012,853,760-5,120-0.040%System.Xml.Schema.Extensions.Tests.exe
24,283,13624,273,408-9,728-0.040%Microsoft.Extensions.Http.Tests.exe
14,142,46414,136,832-5,632-0.040%System.DirectoryServices.Protocols.Tests.exe
10,599,42410,595,328-4,096-0.039%Common.Tests.exe
9,264,1289,260,544-3,584-0.039%Microsoft.Bcl.AsyncInterfaces.Tests.exe
14,903,80814,898,176-5,632-0.038%System.Xml.Linq.Streaming.Tests.exe
13,953,53613,948,416-5,120-0.037%Microsoft.Extensions.Configuration.Xml.Tests.exe
15,094,78415,089,152-5,632-0.037%System.Security.Cryptography.Cng.Tests.exe
23,411,71223,403,008-8,704-0.037%System.Memory.Tests.exe
27,287,04027,277,312-9,728-0.036%Microsoft.Extensions.Hosting.Unit.Tests.exe
16,117,24816,111,616-5,632-0.035%System.Data.Odbc.Tests.exe
26,646,52826,637,312-9,216-0.035%Microsoft.Extensions.Logging.EventSource.Tests.exe
26,441,21626,432,000-9,216-0.035%System.ComponentModel.TypeConverter.Tests.exe
16,525,31216,519,680-5,632-0.034%System.Xml.Linq.Events.Tests.exe
16,778,75216,773,120-5,632-0.034%System.Xml.Linq.Misc.Tests.exe
26,103,80826,095,104-8,704-0.033%System.Globalization.Tests.exe
26,014,72026,006,016-8,704-0.033%System.Globalization.Nls.Tests.exe
17,009,66417,004,032-5,632-0.033%System.Xml.Linq.xNodeReader.Tests.exe
9,675,2649,672,192-3,072-0.032%System.IO.UnmanagedMemoryStream.Tests.exe
15,048,19215,043,584-4,608-0.031%System.Data.DataSetExtensions.Tests.exe
16,673,28016,668,160-5,120-0.031%System.Xml.Linq.Properties.Tests.exe
11,599,36011,595,776-3,584-0.031%System.Net.Http.Json.Unit.Tests.exe
17,065,98417,060,864-5,120-0.030%System.Xml.Linq.xNodeBuilder.Tests.exe
20,819,45620,813,312-6,144-0.030%System.Security.Cryptography.Csp.Tests.exe
17,123,84017,118,720-5,120-0.030%System.Xml.Linq.TreeManipulation.Tests.exe
33,400,83233,391,104-9,728-0.029%Microsoft.Bcl.Cryptography.Tests.exe
18,455,04018,449,920-5,120-0.028%Microsoft.Extensions.Configuration.FileExtensions.Tests.exe
32,762,36832,753,152-9,216-0.028%System.Linq.Expressions.Tests.exe
15,110,65615,106,560-4,096-0.027%System.Drawing.Primitives.Tests.exe
18,381,31218,376,704-4,608-0.025%Microsoft.Extensions.Primitives.Tests.exe
20,099,58420,094,464-5,120-0.025%Microsoft.Extensions.Configuration.Json.Tests.exe
10,521,60010,519,040-2,560-0.024%Microsoft.Extensions.Options.Tests.exe
16,973,82416,977,920+4,096+0.024%System.Private.CoreLib.dll
20,038,65620,034,048-4,608-0.023%Microsoft.Extensions.Configuration.UserSecrets.Tests.exe
19,972,09619,967,488-4,608-0.023%Microsoft.Extensions.Logging.Tests.exe
9,351,1689,349,120-2,048-0.022%System.IO.IsolatedStorage.Tests.exe
9,376,2569,374,208-2,048-0.022%System.Net.Mail.Unit.Tests.exe
18,356,22418,352,128-4,096-0.022%Microsoft.Extensions.FileProviders.Composite.Tests.exe
44,814,33644,804,608-9,728-0.022%System.Runtime.Tests.exe
18,774,01618,769,920-4,096-0.022%Microsoft.Extensions.FileProviders.Physical.Tests.exe
18,491,90418,487,808-4,096-0.022%Microsoft.Extensions.Configuration.Tests.exe
9,346,5609,344,512-2,048-0.022%Microsoft.Extensions.Diagnostics.Abstractions.Tests.exe
10,118,65610,116,608-2,048-0.020%Microsoft.Extensions.Caching.Memory.Tests.exe
30,107,13630,101,504-5,632-0.019%Microsoft.Extensions.DependencyModel.Tests.exe
10,508,28810,506,240-2,048-0.019%System.Net.Http.WinHttpHandler.Unit.Tests.exe
8,563,2008,561,664-1,536-0.018%System.Diagnostics.Contracts.Tests.exe
8,642,0488,640,512-1,536-0.018%System.Net.WebHeaderCollection.Tests.exe
8,770,0488,768,512-1,536-0.018%System.IO.FileSystem.DriveInfo.Tests.exe
12,285,95212,283,904-2,048-0.017%System.Reflection.Metadata.Tests.exe
8,989,6968,988,160-1,536-0.017%System.Net.Primitives.Pal.Tests.exe
33,161,21633,155,584-5,632-0.017%System.Private.Xml.Tests.exe
9,454,0809,452,544-1,536-0.016%Microsoft.Extensions.Logging.Testing.Tests.exe
9,630,7209,629,184-1,536-0.016%System.Composition.Convention.Tests.exe
9,314,8169,313,280-1,536-0.016%System.Net.Primitives.UnitTests.Tests.exe
11,484,16011,482,624-1,536-0.013%System.IO.Ports.Tests.exe
8,601,6008,600,576-1,024-0.012%Microsoft.Extensions.Hosting.Abstractions.Tests.exe
8,720,3848,719,360-1,024-0.012%System.Private.Uri.Unit.Tests.exe
8,546,8168,545,792-1,024-0.012%System.Runtime.CompilerServices.VisualC.Tests.exe
8,653,3128,652,288-1,024-0.012%System.Globalization.CalendarsWithConfigSwitch.Tests.exe
8,836,6088,835,584-1,024-0.012%System.Console.Manual.Tests.exe
9,364,4809,363,456-1,024-0.011%System.Reflection.Context.Tests.exe
9,041,4089,040,384-1,024-0.011%System.Net.NameResolution.Pal.Tests.exe
9,646,5929,645,568-1,024-0.011%System.Private.Uri.Functional.Tests.exe
9,704,4489,703,424-1,024-0.011%System.Text.RegularExpressions.Unit.Tests.exe
9,206,2729,205,248-1,024-0.011%Microsoft.Extensions.Configuration.EnvironmentVariables.Tests.exe
9,004,0329,003,008-1,024-0.011%System.Runtime.Serialization.Primitives.Tests.exe
9,056,2569,055,232-1,024-0.011%Microsoft.Extensions.Configuration.CommandLine.Tests.exe
9,216,5129,215,488-1,024-0.011%System.ServiceProcess.ServiceController.Tests.exe
9,539,5849,538,560-1,024-0.011%System.IO.Hashing.Tests.exe
9,671,1689,670,144-1,024-0.011%Microsoft.Extensions.Hosting.Functional.Tests.exe
4,940,2884,939,776-512-0.010%coreclr.dll
10,935,80810,934,784-1,024-0.009%System.Runtime.Intrinsics.Tests.exe
11,610,11211,609,088-1,024-0.009%System.Resources.Extensions.Tests.exe
11,193,85611,192,832-1,024-0.009%System.Text.Encodings.Web.Tests.exe
12,287,48812,286,464-1,024-0.008%System.Net.NetworkInformation.Functional.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Nls.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Tests.exe
9,039,3609,038,848-512-0.006%Microsoft.Extensions.FileSystemGlobbing.Tests.exe
8,558,0808,557,568-512-0.006%System.Text.Encoding.Extensions.Tests.exe
8,704,5128,704,000-512-0.006%System.Security.SecureString.Tests.exe
8,701,4408,700,928-512-0.006%System.Security.Cryptography.ProtectedData.Tests.exe
8,688,1288,687,616-512-0.006%System.Runtime.InteropServices.ComDisabled.Tests.exe
9,143,8089,143,296-512-0.006%System.Security.AccessControl.Tests.exe
9,025,5369,025,024-512-0.006%System.Reflection.TypeExtensions.Tests.exe
8,812,0328,811,520-512-0.006%System.Resources.Reader.Tests.exe
9,083,3929,082,880-512-0.006%System.ValueTuple.Tests.exe
8,606,2088,605,696-512-0.006%System.Resources.Writer.Tests.exe
8,817,6648,817,152-512-0.006%System.Web.HttpUtility.Tests.exe
8,863,2328,862,720-512-0.006%System.Reflection.Extensions.Tests.exe
8,670,2088,669,696-512-0.006%System.Runtime.Handles.Tests.exe
9,165,8249,165,312-512-0.006%System.Globalization.Calendars.Tests.exe
8,543,7448,543,232-512-0.006%System.IO.FileSystem.Primitives.Tests.exe
8,592,3848,591,872-512-0.006%Microsoft.Bcl.Numerics.Tests.exe
8,952,8328,952,320-512-0.006%Microsoft.Bcl.Memory.Tests.exe
8,717,3128,716,800-512-0.006%System.Diagnostics.FileVersionInfo.Tests.exe
8,700,4168,699,904-512-0.006%System.IO.FileSystem.Manual.Tests.exe
8,929,7928,929,280-512-0.006%System.IO.Pipes.AccessControl.Tests.exe
8,624,1288,623,616-512-0.006%System.Composition.Runtime.Tests.exe
9,108,9929,108,480-512-0.006%System.IO.FileSystem.AccessControl.Tests.exe
8,559,1048,558,592-512-0.006%System.Composition.AttributeModel.Tests.exe
8,759,2968,758,784-512-0.006%Invariant.Tests.exe
8,547,8408,547,328-512-0.006%System.ComponentModel.Tests.exe
9,161,7289,161,216-512-0.006%System.ComponentModel.Primitives.Tests.exe
8,959,4888,958,976-512-0.006%System.Threading.AccessControl.Tests.exe
8,991,2328,990,720-512-0.006%Microsoft.Win32.Registry.Tests.exe
8,647,6808,647,168-512-0.006%Microsoft.Win32.Registry.AccessControl.Tests.exe
8,549,3768,548,864-512-0.006%System.Diagnostics.Tools.Tests.exe
8,664,5768,664,064-512-0.006%Microsoft.Win32.Primitives.Tests.exe
9,750,0169,749,504-512-0.005%System.Collections.Specialized.Tests.exe
22,340,60822,339,584-1,024-0.005%System.Security.Cryptography.Pkcs.Tests.exe
9,852,9289,852,416-512-0.005%System.Formats.Nrbf.Tests.exe
9,763,3289,762,816-512-0.005%System.Formats.Asn1.Tests.exe
9,361,4089,360,896-512-0.005%System.Security.Permissions.Tests.exe
11,048,44811,047,936-512-0.005%System.Runtime.Numerics.Tests.exe
9,627,1369,626,624-512-0.005%System.Diagnostics.EventLog.Tests.exe
18,779,13618,778,112-1,024-0.005%System.Numerics.Vectors.Tests.exe
9,746,4329,745,920-512-0.005%System.Threading.RateLimiting.Tests.exe
9,961,4729,960,960-512-0.005%LibraryImportGenerator.Tests.exe
9,949,6969,949,184-512-0.005%System.Collections.NonGeneric.Tests.exe
9,486,8489,486,336-512-0.005%Microsoft.Extensions.Configuration.Ini.Tests.exe
13,553,66413,554,176+512+0.004%crossgen2.exe
11,796,48011,795,968-512-0.004%System.Security.Cryptography.Cose.Tests.exe
11,381,24811,380,736-512-0.004%System.Net.Security.Unit.Tests.exe
15,545,85615,545,344-512-0.003%System.Linq.Tests.exe
17,838,59217,838,080-512-0.003%System.Collections.Tests.exe

CopilotAI review requested due to automatic review settings May 13, 2026 09:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch
jakobbotsch merged commit d9f57ab into dotnet:mainMay 18, 2026
155 of 157 checks passed
@jakobbotsch
jakobbotsch deleted the cache-value-task-source-continuation branch May 18, 2026 07:31
VSadov added a commit that referenced this pull request May 21, 2026
…ce (#128399)
Same idea as in #127973, but applied when the actual Await is used (not
a thunk).
With the original repro that I was looking at, merging of #127973 helped
to reduce Gen0 GCs from ~ 15/sec to ~ 12/sec.
Alocations went down but not completely, because of code like the
following piece in the `DoReceive()` loop and possibly in other places.
This pattern still churns continuations:
https://github.com/dotnet/aspnetcore/blob/c0c2230799a3f876aa673a66bc008bf9b803acac/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs#L173-L182
```cs
var flushTask = Input.FlushAsync();
var paused = !flushTask.IsCompleted;
if (paused)
{
SocketsLog.ConnectionPause(_logger, this);
}
var result = await flushTask;
```
Because `flushTask` is not a method, the `await` does not go through a
thunk which could reuse the continuation. We call the actual `Await` in
this case and it suspends/allocates.
The change in this PR uses the same approach as in the thunk, but
applied to the Await, when awaiting a ValueTaskSource.
With this change we get to ~ 2 Gen0/sec in the repro - on par with
net10.
tommcdon added a commit that referenced this pull request May 23, 2026
…128496)
CordbAsyncStackWalk::PopulateFrame() crashes when encountering a
continuation whose ResumeInfo.DiagnosticIP is NULL (e.g. the new
ValueTaskContinuation introduced in #127973). GetNativeCodeInfoForAddr
is called with a null address which fails.
Fix: In PopulateFrame(), skip continuations with diagnosticIP == NULL
the same way DiagnosticHidden frames are skipped (advance to Next).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jakobbotsch added a commit that referenced this pull request May 26, 2026
Apply a similar optimization as #127973 to task returning thunks.
This is a very minor throughput optimization, but it also removes the
suspension point from all these thunks, which makes each of these thunks
smaller.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 17, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jakobbotsch@VSadov@lateralusX
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Cache continuation used for runtime async callable value task thunks - #127973

Merged
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation
May 18, 2026
Merged

Cache continuation used for runtime async callable value task thunks#127973
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

ValueTask backed by IValueTaskSource can suspend without allocating. However, runtime async did not support this when calling a ValueTask returning method through a runtime async callable thunk. In that case we would always allocate in the case where the ValueTask suspended.

This adds a custom ValueTaskContinuation that replaces the existing ValueTaskSourceNotifier mechanism. We now cache a ValueTaskContinuation instance and reuse it if possible whenever suspending for a ValueTask.

The same approach could be used to avoid allocations for runtime async callable thunks for task-returning methods.

cc @VSadov

`ValueTask` backed by `IValueTaskSource` can suspend without allocating.
However, runtime async did not support this when calling a `ValueTask`
returning method through a runtime async callable thunk. In that case we
would always allocate in the case where the `ValueTask` suspended.
This adds a custom `ValueTaskContinuation` that replaces the existing
`ValueTaskSourceNotifier` mechanism. We now cache a
`ValueTaskContinuation` instance and reuse it if possible whenever
suspending for a `ValueTask`.
The same approach could be used to avoid allocations for runtime async
callable thunks for task-returning methods.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 updates CoreCLR’s runtime-async callable thunk path for ValueTask/ValueTask<T> so that suspending on ValueTask backed by IValueTaskSource can reuse a cached continuation object rather than allocating per-suspension, and removes the older ValueTaskSourceNotifier / AsTaskOrNotifier mechanism.

Changes:

  • Introduces AsyncHelpers.TransparentAwaitValueTask* and a ValueTaskContinuation type that integrates with runtime-async suspension/resumption and is cached in TLS for reuse.
  • Removes ValueTask.AsTaskOrNotifier and the ValueTaskSourceNotifier helper type from ValueTask.
  • Adjusts VM continuation type handling to distinguish continuation subtypes that do have metadata (managed subclasses) vs the dynamically-created metadata-less ones, including DAC support.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.csRemoves AsTaskOrNotifier and deletes ValueTaskSourceNotifier.
src/coreclr/vm/vars.hppAdds global g_singletonContinuationEEClass declaration for continuation metadata detection.
src/coreclr/vm/vars.cppAdds global g_singletonContinuationEEClass definition.
src/coreclr/vm/typehandle.inlAvoids “upcasting” continuations that have real metadata.
src/coreclr/vm/typehandle.hAdds TypeHandle::IsContinuationWithMetadata().
src/coreclr/vm/typehandle.cppImplements TypeHandle::IsContinuationWithMetadata() and relaxes class-object allocation restriction for metadata continuations.
src/coreclr/vm/methodtable.hAdds MethodTable::IsContinuationWithMetadata() and relaxes asserts/preconditions for metadata continuations.
src/coreclr/vm/methodtable.cppImplements MethodTable::IsContinuationWithMetadata() and updates continuation-related special-casing.
src/coreclr/vm/corelib.hRemoves binder entries for ValueTask.AsTaskOrNotifier; adds binder entries for TransparentAwaitValueTask*.
src/coreclr/vm/asyncthunks.cppEmits thunk IL that tail-awaits via TransparentAwaitValueTask* instead of AsTaskOrNotifier + TransparentAwait.
src/coreclr/vm/asynccontinuations.cppMoves singleton continuation EEClass storage to global g_singletonContinuationEEClass.
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.csUpdates managed type-system thunk emission to call TransparentAwaitValueTask* and TailAwait.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdds ValueTaskContinuation, TLS caching, and TransparentAwaitValueTask*; updates suspension handling to use it.
src/coreclr/inc/dacvars.hExposes g_singletonContinuationEEClass to DAC.
src/coreclr/debug/daccess/dacdbiimpl.cppUpdates continuation canonical-MT validity logic to account for metadata continuations.

Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/asyncthunks.cpp
@VSadov

Copy link
Copy Markdown
Member

Nice! Instead of caching and reusing ValueTaskSourceNotifier, we now cache/reuse the entire head continuation.

A scenario that awaits in a loop some ValueTaskSource wrapper (like reading chunks from a pipe) can run allocation-free.

@jakobbotsch

jakobbotsch commented May 10, 2026

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

Also, we probably need to introduce a runtime async mechanism similar to PoolingAsyncValueTaskMethodBuilder -- e.g. apply [PoolContinuations] on a runtime async method and it would cache continuations for that method following that scheme too.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 21 out of 21 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs:1

  • The XML doc block still describes awaiting ValueTask, but TransparentAwait now accepts Task only, while ValueTask awaiting is handled by TransparentAwaitValueTask* above. Please update/move this comment so TransparentAwait’s docs describe only the Task scenario, and place the ValueTask-specific explanation on TransparentAwaitValueTask / TransparentAwaitValueTaskOfT.
// Licensed to the .NET Foundation under one or more agreements.

Comment threadsrc/coreclr/vm/methodtable.cpp
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

I think I will do that separately. Right now the caching is essentially free since we can store it in the runtime async TLS that we already needed to access. If we introduce the per-core cache we will be paying more when the TLS cache doesn't hit, so I want to make sure I have some benchmarks for this.

@VSadov

Copy link
Copy Markdown
Member

I think I will do that separately

Per thread caching of 1 head continuation is probably the most effective as we reduce necessary allocations for that scenario to none.

Additional caching may cost more while having less impact. It makes sense to look at that separately.

@jakobbotsch
jakobbotsch marked this pull request as ready for review May 12, 2026 22:05
CopilotAI review requested due to automatic review settings May 12, 2026 22:05

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Also some decent size improvements for NativeAOT since the thunks no longer need a suspension/resumption point:

BaseSizeDiffSizeDeltaPercentImprovedName
16,852,99216,716,288-136,704-0.811%System.Linq.AsyncEnumerable.Tests.exe
115,198,464115,079,168-119,296-0.104%System.Text.Json.SourceGeneration.Roslyn4.4.Tests.exe
10,291,20010,282,496-8,704-0.085%System.Threading.Channels.Tests.exe
12,003,32811,993,088-10,240-0.085%System.IO.Tests.exe
19,561,47219,545,088-16,384-0.084%System.IO.Compression.Tests.exe
14,579,71214,568,960-10,752-0.074%System.Net.WebClient.Tests.exe
20,044,28820,029,952-14,336-0.072%System.Formats.Tar.Tests.exe
45,556,22445,524,992-31,232-0.069%System.Security.Cryptography.Tests.exe
21,715,45621,700,608-14,848-0.068%System.IO.FileSystem.Tests.exe
18,455,55218,443,264-12,288-0.067%System.Threading.Tasks.Parallel.Tests.exe
18,526,20818,513,920-12,288-0.066%System.IO.MemoryMappedFiles.Tests.exe
10,067,45610,060,800-6,656-0.066%System.Net.ServerSentEvents.Tests.exe
17,734,14417,722,368-11,776-0.066%System.Threading.Tasks.Extensions.Tests.exe
15,771,64815,761,408-10,240-0.065%System.Windows.Extensions.Tests.exe
19,296,76819,284,480-12,288-0.064%System.IO.Pipes.Tests.exe
11,286,01611,278,848-7,168-0.064%System.IO.Compression.Brotli.Tests.exe
19,570,17619,557,888-12,288-0.063%System.Diagnostics.Process.Tests.exe
20,273,66420,260,864-12,800-0.063%System.Threading.Tasks.Dataflow.Tests.exe
25,897,98425,881,600-16,384-0.063%System.Text.Json.SourceGeneration.Roslyn3.11.Tests.exe
11,343,36011,336,192-7,168-0.063%System.Net.WebSockets.Tests.exe
10,675,20010,668,544-6,656-0.062%System.IO.Pipelines.Tests.exe
13,250,56013,242,368-8,192-0.062%System.IO.Packaging.Tests.exe
20,009,98419,997,696-12,288-0.061%System.Net.WebSockets.Client.Tests.exe
31,281,66431,263,232-18,432-0.059%System.Net.Http.Functional.Tests.exe
20,942,84820,930,560-12,288-0.059%System.Net.Quic.Functional.Tests.exe
17,798,65617,788,416-10,240-0.058%System.Formats.Tar.Manual.Tests.exe
23,142,91223,129,600-13,312-0.058%System.Threading.Tasks.Tests.exe
18,947,58418,936,832-10,752-0.057%System.Runtime.Caching.Tests.exe
17,168,89617,159,168-9,728-0.057%System.Runtime.Serialization.Formatters.Disabled.Tests.exe
17,439,23217,429,504-9,728-0.056%System.Diagnostics.TraceSource.Tests.exe
17,223,16817,213,952-9,216-0.054%System.Net.ServicePoint.Tests.exe
18,070,52818,060,800-9,728-0.054%System.Console.Tests.exe
17,975,29617,965,568-9,728-0.054%System.Threading.ThreadPool.Tests.exe
18,137,60018,127,872-9,728-0.054%System.Diagnostics.TextWriterTraceListener.Tests.exe
18,380,80018,371,072-9,728-0.053%System.Threading.Tests.exe
17,296,89617,287,680-9,216-0.053%System.Runtime.InteropServices.RuntimeInformation.Tests.exe
17,438,72017,429,504-9,216-0.053%System.Net.Ping.Functional.Tests.exe
17,244,16017,234,944-9,216-0.053%System.Runtime.InvariantTimezone.Tests.exe
17,290,24017,281,024-9,216-0.053%System.Threading.Overlapped.Tests.exe
17,363,96817,354,752-9,216-0.053%Microsoft.Win32.SystemEvents.Tests.exe
17,306,11217,296,896-9,216-0.053%System.Security.Claims.Tests.exe
18,594,81618,585,088-9,728-0.052%ComInterfaceGenerator.Tests.exe
18,692,60818,682,880-9,728-0.052%Microsoft.Extensions.Logging.Console.Tests.exe
17,578,49617,569,280-9,216-0.052%MetricOuterLoop1.Tests.exe
17,573,88817,564,672-9,216-0.052%MetricOuterLoop.Tests.exe
19,688,96019,678,720-10,240-0.052%System.Net.Http.Unit.Tests.exe
17,975,29617,966,080-9,216-0.051%System.Threading.ThreadPool.WindowsThreadPool.Tests.exe
28,039,16828,024,832-14,336-0.051%System.Net.Security.Tests.exe
22,881,28022,869,504-11,776-0.051%System.Net.Sockets.Tests.exe
19,059,20019,049,472-9,728-0.051%System.Net.HttpListener.Tests.exe
18,040,83218,031,616-9,216-0.051%Microsoft.Extensions.Diagnostics.Tests.exe
17,238,52817,229,824-8,704-0.050%System.ComponentModel.EventBasedAsync.Tests.exe
17,369,08817,360,384-8,704-0.050%System.Diagnostics.StackTrace.Tests.exe
18,496,51218,487,296-9,216-0.050%Microsoft.Extensions.Hosting.Systemd.Tests.exe
17,320,44817,311,744-8,704-0.050%System.Security.Principal.Windows.Tests.exe
17,337,34417,328,640-8,704-0.050%System.Threading.Timer.Tests.exe
17,668,09617,659,392-8,704-0.049%System.IO.FileSystem.Watcher.Tests.exe
17,624,57617,615,872-8,704-0.049%System.Threading.Thread.Tests.exe
19,876,86419,867,136-9,728-0.049%System.Text.Encoding.Tests.exe
17,670,65617,661,952-8,704-0.049%System.Buffers.Tests.exe
19,401,21619,392,000-9,216-0.048%System.Net.Primitives.Functional.Tests.exe
17,161,72817,153,536-8,192-0.048%IcuAppLocal.Tests.exe
18,184,70418,176,000-8,704-0.048%System.Text.Encoding.CodePages.Tests.exe
10,061,82410,057,216-4,608-0.046%System.IO.Compression.ZipFile.Tests.exe
19,087,87219,079,168-8,704-0.046%Microsoft.Extensions.Hosting.WindowsServices.Tests.exe
21,631,48821,621,760-9,728-0.045%System.Data.OleDb.Tests.exe
20,581,37620,572,160-9,216-0.045%System.Diagnostics.DiagnosticSource.Tests.exe
13,877,76013,871,616-6,144-0.044%Microsoft.Extensions.Configuration.Functional.Tests.exe
19,752,96019,744,256-8,704-0.044%System.Resources.ResourceManager.Tests.exe
20,748,80020,739,584-9,216-0.044%System.Collections.Concurrent.Tests.exe
20,463,10420,454,400-8,704-0.043%System.Reflection.Tests.exe
20,241,40820,232,704-8,704-0.043%System.Runtime.InteropServices.Tests.exe
20,940,80020,932,096-8,704-0.042%System.Runtime.Extensions.Tests.exe
12,425,72812,420,608-5,120-0.041%System.Xml.Linq.SDMSample.Tests.exe
21,210,11221,201,408-8,704-0.041%System.Net.Requests.Tests.exe
29,510,65629,498,880-11,776-0.040%System.Net.Http.WinHttpHandler.Functional.Tests.exe
12,858,88012,853,760-5,120-0.040%System.Xml.Schema.Extensions.Tests.exe
24,283,13624,273,408-9,728-0.040%Microsoft.Extensions.Http.Tests.exe
14,142,46414,136,832-5,632-0.040%System.DirectoryServices.Protocols.Tests.exe
10,599,42410,595,328-4,096-0.039%Common.Tests.exe
9,264,1289,260,544-3,584-0.039%Microsoft.Bcl.AsyncInterfaces.Tests.exe
14,903,80814,898,176-5,632-0.038%System.Xml.Linq.Streaming.Tests.exe
13,953,53613,948,416-5,120-0.037%Microsoft.Extensions.Configuration.Xml.Tests.exe
15,094,78415,089,152-5,632-0.037%System.Security.Cryptography.Cng.Tests.exe
23,411,71223,403,008-8,704-0.037%System.Memory.Tests.exe
27,287,04027,277,312-9,728-0.036%Microsoft.Extensions.Hosting.Unit.Tests.exe
16,117,24816,111,616-5,632-0.035%System.Data.Odbc.Tests.exe
26,646,52826,637,312-9,216-0.035%Microsoft.Extensions.Logging.EventSource.Tests.exe
26,441,21626,432,000-9,216-0.035%System.ComponentModel.TypeConverter.Tests.exe
16,525,31216,519,680-5,632-0.034%System.Xml.Linq.Events.Tests.exe
16,778,75216,773,120-5,632-0.034%System.Xml.Linq.Misc.Tests.exe
26,103,80826,095,104-8,704-0.033%System.Globalization.Tests.exe
26,014,72026,006,016-8,704-0.033%System.Globalization.Nls.Tests.exe
17,009,66417,004,032-5,632-0.033%System.Xml.Linq.xNodeReader.Tests.exe
9,675,2649,672,192-3,072-0.032%System.IO.UnmanagedMemoryStream.Tests.exe
15,048,19215,043,584-4,608-0.031%System.Data.DataSetExtensions.Tests.exe
16,673,28016,668,160-5,120-0.031%System.Xml.Linq.Properties.Tests.exe
11,599,36011,595,776-3,584-0.031%System.Net.Http.Json.Unit.Tests.exe
17,065,98417,060,864-5,120-0.030%System.Xml.Linq.xNodeBuilder.Tests.exe
20,819,45620,813,312-6,144-0.030%System.Security.Cryptography.Csp.Tests.exe
17,123,84017,118,720-5,120-0.030%System.Xml.Linq.TreeManipulation.Tests.exe
33,400,83233,391,104-9,728-0.029%Microsoft.Bcl.Cryptography.Tests.exe
18,455,04018,449,920-5,120-0.028%Microsoft.Extensions.Configuration.FileExtensions.Tests.exe
32,762,36832,753,152-9,216-0.028%System.Linq.Expressions.Tests.exe
15,110,65615,106,560-4,096-0.027%System.Drawing.Primitives.Tests.exe
18,381,31218,376,704-4,608-0.025%Microsoft.Extensions.Primitives.Tests.exe
20,099,58420,094,464-5,120-0.025%Microsoft.Extensions.Configuration.Json.Tests.exe
10,521,60010,519,040-2,560-0.024%Microsoft.Extensions.Options.Tests.exe
16,973,82416,977,920+4,096+0.024%System.Private.CoreLib.dll
20,038,65620,034,048-4,608-0.023%Microsoft.Extensions.Configuration.UserSecrets.Tests.exe
19,972,09619,967,488-4,608-0.023%Microsoft.Extensions.Logging.Tests.exe
9,351,1689,349,120-2,048-0.022%System.IO.IsolatedStorage.Tests.exe
9,376,2569,374,208-2,048-0.022%System.Net.Mail.Unit.Tests.exe
18,356,22418,352,128-4,096-0.022%Microsoft.Extensions.FileProviders.Composite.Tests.exe
44,814,33644,804,608-9,728-0.022%System.Runtime.Tests.exe
18,774,01618,769,920-4,096-0.022%Microsoft.Extensions.FileProviders.Physical.Tests.exe
18,491,90418,487,808-4,096-0.022%Microsoft.Extensions.Configuration.Tests.exe
9,346,5609,344,512-2,048-0.022%Microsoft.Extensions.Diagnostics.Abstractions.Tests.exe
10,118,65610,116,608-2,048-0.020%Microsoft.Extensions.Caching.Memory.Tests.exe
30,107,13630,101,504-5,632-0.019%Microsoft.Extensions.DependencyModel.Tests.exe
10,508,28810,506,240-2,048-0.019%System.Net.Http.WinHttpHandler.Unit.Tests.exe
8,563,2008,561,664-1,536-0.018%System.Diagnostics.Contracts.Tests.exe
8,642,0488,640,512-1,536-0.018%System.Net.WebHeaderCollection.Tests.exe
8,770,0488,768,512-1,536-0.018%System.IO.FileSystem.DriveInfo.Tests.exe
12,285,95212,283,904-2,048-0.017%System.Reflection.Metadata.Tests.exe
8,989,6968,988,160-1,536-0.017%System.Net.Primitives.Pal.Tests.exe
33,161,21633,155,584-5,632-0.017%System.Private.Xml.Tests.exe
9,454,0809,452,544-1,536-0.016%Microsoft.Extensions.Logging.Testing.Tests.exe
9,630,7209,629,184-1,536-0.016%System.Composition.Convention.Tests.exe
9,314,8169,313,280-1,536-0.016%System.Net.Primitives.UnitTests.Tests.exe
11,484,16011,482,624-1,536-0.013%System.IO.Ports.Tests.exe
8,601,6008,600,576-1,024-0.012%Microsoft.Extensions.Hosting.Abstractions.Tests.exe
8,720,3848,719,360-1,024-0.012%System.Private.Uri.Unit.Tests.exe
8,546,8168,545,792-1,024-0.012%System.Runtime.CompilerServices.VisualC.Tests.exe
8,653,3128,652,288-1,024-0.012%System.Globalization.CalendarsWithConfigSwitch.Tests.exe
8,836,6088,835,584-1,024-0.012%System.Console.Manual.Tests.exe
9,364,4809,363,456-1,024-0.011%System.Reflection.Context.Tests.exe
9,041,4089,040,384-1,024-0.011%System.Net.NameResolution.Pal.Tests.exe
9,646,5929,645,568-1,024-0.011%System.Private.Uri.Functional.Tests.exe
9,704,4489,703,424-1,024-0.011%System.Text.RegularExpressions.Unit.Tests.exe
9,206,2729,205,248-1,024-0.011%Microsoft.Extensions.Configuration.EnvironmentVariables.Tests.exe
9,004,0329,003,008-1,024-0.011%System.Runtime.Serialization.Primitives.Tests.exe
9,056,2569,055,232-1,024-0.011%Microsoft.Extensions.Configuration.CommandLine.Tests.exe
9,216,5129,215,488-1,024-0.011%System.ServiceProcess.ServiceController.Tests.exe
9,539,5849,538,560-1,024-0.011%System.IO.Hashing.Tests.exe
9,671,1689,670,144-1,024-0.011%Microsoft.Extensions.Hosting.Functional.Tests.exe
4,940,2884,939,776-512-0.010%coreclr.dll
10,935,80810,934,784-1,024-0.009%System.Runtime.Intrinsics.Tests.exe
11,610,11211,609,088-1,024-0.009%System.Resources.Extensions.Tests.exe
11,193,85611,192,832-1,024-0.009%System.Text.Encodings.Web.Tests.exe
12,287,48812,286,464-1,024-0.008%System.Net.NetworkInformation.Functional.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Nls.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Tests.exe
9,039,3609,038,848-512-0.006%Microsoft.Extensions.FileSystemGlobbing.Tests.exe
8,558,0808,557,568-512-0.006%System.Text.Encoding.Extensions.Tests.exe
8,704,5128,704,000-512-0.006%System.Security.SecureString.Tests.exe
8,701,4408,700,928-512-0.006%System.Security.Cryptography.ProtectedData.Tests.exe
8,688,1288,687,616-512-0.006%System.Runtime.InteropServices.ComDisabled.Tests.exe
9,143,8089,143,296-512-0.006%System.Security.AccessControl.Tests.exe
9,025,5369,025,024-512-0.006%System.Reflection.TypeExtensions.Tests.exe
8,812,0328,811,520-512-0.006%System.Resources.Reader.Tests.exe
9,083,3929,082,880-512-0.006%System.ValueTuple.Tests.exe
8,606,2088,605,696-512-0.006%System.Resources.Writer.Tests.exe
8,817,6648,817,152-512-0.006%System.Web.HttpUtility.Tests.exe
8,863,2328,862,720-512-0.006%System.Reflection.Extensions.Tests.exe
8,670,2088,669,696-512-0.006%System.Runtime.Handles.Tests.exe
9,165,8249,165,312-512-0.006%System.Globalization.Calendars.Tests.exe
8,543,7448,543,232-512-0.006%System.IO.FileSystem.Primitives.Tests.exe
8,592,3848,591,872-512-0.006%Microsoft.Bcl.Numerics.Tests.exe
8,952,8328,952,320-512-0.006%Microsoft.Bcl.Memory.Tests.exe
8,717,3128,716,800-512-0.006%System.Diagnostics.FileVersionInfo.Tests.exe
8,700,4168,699,904-512-0.006%System.IO.FileSystem.Manual.Tests.exe
8,929,7928,929,280-512-0.006%System.IO.Pipes.AccessControl.Tests.exe
8,624,1288,623,616-512-0.006%System.Composition.Runtime.Tests.exe
9,108,9929,108,480-512-0.006%System.IO.FileSystem.AccessControl.Tests.exe
8,559,1048,558,592-512-0.006%System.Composition.AttributeModel.Tests.exe
8,759,2968,758,784-512-0.006%Invariant.Tests.exe
8,547,8408,547,328-512-0.006%System.ComponentModel.Tests.exe
9,161,7289,161,216-512-0.006%System.ComponentModel.Primitives.Tests.exe
8,959,4888,958,976-512-0.006%System.Threading.AccessControl.Tests.exe
8,991,2328,990,720-512-0.006%Microsoft.Win32.Registry.Tests.exe
8,647,6808,647,168-512-0.006%Microsoft.Win32.Registry.AccessControl.Tests.exe
8,549,3768,548,864-512-0.006%System.Diagnostics.Tools.Tests.exe
8,664,5768,664,064-512-0.006%Microsoft.Win32.Primitives.Tests.exe
9,750,0169,749,504-512-0.005%System.Collections.Specialized.Tests.exe
22,340,60822,339,584-1,024-0.005%System.Security.Cryptography.Pkcs.Tests.exe
9,852,9289,852,416-512-0.005%System.Formats.Nrbf.Tests.exe
9,763,3289,762,816-512-0.005%System.Formats.Asn1.Tests.exe
9,361,4089,360,896-512-0.005%System.Security.Permissions.Tests.exe
11,048,44811,047,936-512-0.005%System.Runtime.Numerics.Tests.exe
9,627,1369,626,624-512-0.005%System.Diagnostics.EventLog.Tests.exe
18,779,13618,778,112-1,024-0.005%System.Numerics.Vectors.Tests.exe
9,746,4329,745,920-512-0.005%System.Threading.RateLimiting.Tests.exe
9,961,4729,960,960-512-0.005%LibraryImportGenerator.Tests.exe
9,949,6969,949,184-512-0.005%System.Collections.NonGeneric.Tests.exe
9,486,8489,486,336-512-0.005%Microsoft.Extensions.Configuration.Ini.Tests.exe
13,553,66413,554,176+512+0.004%crossgen2.exe
11,796,48011,795,968-512-0.004%System.Security.Cryptography.Cose.Tests.exe
11,381,24811,380,736-512-0.004%System.Net.Security.Unit.Tests.exe
15,545,85615,545,344-512-0.003%System.Linq.Tests.exe
17,838,59217,838,080-512-0.003%System.Collections.Tests.exe

CopilotAI review requested due to automatic review settings May 13, 2026 09:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch
jakobbotsch merged commit d9f57ab into dotnet:mainMay 18, 2026
155 of 157 checks passed
@jakobbotsch
jakobbotsch deleted the cache-value-task-source-continuation branch May 18, 2026 07:31
VSadov added a commit that referenced this pull request May 21, 2026
…ce (#128399)
Same idea as in #127973, but applied when the actual Await is used (not
a thunk).
With the original repro that I was looking at, merging of #127973 helped
to reduce Gen0 GCs from ~ 15/sec to ~ 12/sec.
Alocations went down but not completely, because of code like the
following piece in the `DoReceive()` loop and possibly in other places.
This pattern still churns continuations:
https://github.com/dotnet/aspnetcore/blob/c0c2230799a3f876aa673a66bc008bf9b803acac/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs#L173-L182
```cs
var flushTask = Input.FlushAsync();
var paused = !flushTask.IsCompleted;
if (paused)
{
SocketsLog.ConnectionPause(_logger, this);
}
var result = await flushTask;
```
Because `flushTask` is not a method, the `await` does not go through a
thunk which could reuse the continuation. We call the actual `Await` in
this case and it suspends/allocates.
The change in this PR uses the same approach as in the thunk, but
applied to the Await, when awaiting a ValueTaskSource.
With this change we get to ~ 2 Gen0/sec in the repro - on par with
net10.
tommcdon added a commit that referenced this pull request May 23, 2026
…128496)
CordbAsyncStackWalk::PopulateFrame() crashes when encountering a
continuation whose ResumeInfo.DiagnosticIP is NULL (e.g. the new
ValueTaskContinuation introduced in #127973). GetNativeCodeInfoForAddr
is called with a null address which fails.
Fix: In PopulateFrame(), skip continuations with diagnosticIP == NULL
the same way DiagnosticHidden frames are skipped (advance to Next).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jakobbotsch added a commit that referenced this pull request May 26, 2026
Apply a similar optimization as #127973 to task returning thunks.
This is a very minor throughput optimization, but it also removes the
suspension point from all these thunks, which makes each of these thunks
smaller.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 17, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jakobbotsch@VSadov@lateralusX
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Cache continuation used for runtime async callable value task thunks - #127973

Merged
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation
May 18, 2026
Merged

Cache continuation used for runtime async callable value task thunks#127973
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

ValueTask backed by IValueTaskSource can suspend without allocating. However, runtime async did not support this when calling a ValueTask returning method through a runtime async callable thunk. In that case we would always allocate in the case where the ValueTask suspended.

This adds a custom ValueTaskContinuation that replaces the existing ValueTaskSourceNotifier mechanism. We now cache a ValueTaskContinuation instance and reuse it if possible whenever suspending for a ValueTask.

The same approach could be used to avoid allocations for runtime async callable thunks for task-returning methods.

cc @VSadov

`ValueTask` backed by `IValueTaskSource` can suspend without allocating.
However, runtime async did not support this when calling a `ValueTask`
returning method through a runtime async callable thunk. In that case we
would always allocate in the case where the `ValueTask` suspended.
This adds a custom `ValueTaskContinuation` that replaces the existing
`ValueTaskSourceNotifier` mechanism. We now cache a
`ValueTaskContinuation` instance and reuse it if possible whenever
suspending for a `ValueTask`.
The same approach could be used to avoid allocations for runtime async
callable thunks for task-returning methods.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 updates CoreCLR’s runtime-async callable thunk path for ValueTask/ValueTask<T> so that suspending on ValueTask backed by IValueTaskSource can reuse a cached continuation object rather than allocating per-suspension, and removes the older ValueTaskSourceNotifier / AsTaskOrNotifier mechanism.

Changes:

  • Introduces AsyncHelpers.TransparentAwaitValueTask* and a ValueTaskContinuation type that integrates with runtime-async suspension/resumption and is cached in TLS for reuse.
  • Removes ValueTask.AsTaskOrNotifier and the ValueTaskSourceNotifier helper type from ValueTask.
  • Adjusts VM continuation type handling to distinguish continuation subtypes that do have metadata (managed subclasses) vs the dynamically-created metadata-less ones, including DAC support.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.csRemoves AsTaskOrNotifier and deletes ValueTaskSourceNotifier.
src/coreclr/vm/vars.hppAdds global g_singletonContinuationEEClass declaration for continuation metadata detection.
src/coreclr/vm/vars.cppAdds global g_singletonContinuationEEClass definition.
src/coreclr/vm/typehandle.inlAvoids “upcasting” continuations that have real metadata.
src/coreclr/vm/typehandle.hAdds TypeHandle::IsContinuationWithMetadata().
src/coreclr/vm/typehandle.cppImplements TypeHandle::IsContinuationWithMetadata() and relaxes class-object allocation restriction for metadata continuations.
src/coreclr/vm/methodtable.hAdds MethodTable::IsContinuationWithMetadata() and relaxes asserts/preconditions for metadata continuations.
src/coreclr/vm/methodtable.cppImplements MethodTable::IsContinuationWithMetadata() and updates continuation-related special-casing.
src/coreclr/vm/corelib.hRemoves binder entries for ValueTask.AsTaskOrNotifier; adds binder entries for TransparentAwaitValueTask*.
src/coreclr/vm/asyncthunks.cppEmits thunk IL that tail-awaits via TransparentAwaitValueTask* instead of AsTaskOrNotifier + TransparentAwait.
src/coreclr/vm/asynccontinuations.cppMoves singleton continuation EEClass storage to global g_singletonContinuationEEClass.
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.csUpdates managed type-system thunk emission to call TransparentAwaitValueTask* and TailAwait.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdds ValueTaskContinuation, TLS caching, and TransparentAwaitValueTask*; updates suspension handling to use it.
src/coreclr/inc/dacvars.hExposes g_singletonContinuationEEClass to DAC.
src/coreclr/debug/daccess/dacdbiimpl.cppUpdates continuation canonical-MT validity logic to account for metadata continuations.

Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/asyncthunks.cpp
@VSadov

Copy link
Copy Markdown
Member

Nice! Instead of caching and reusing ValueTaskSourceNotifier, we now cache/reuse the entire head continuation.

A scenario that awaits in a loop some ValueTaskSource wrapper (like reading chunks from a pipe) can run allocation-free.

@jakobbotsch

jakobbotsch commented May 10, 2026

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

Also, we probably need to introduce a runtime async mechanism similar to PoolingAsyncValueTaskMethodBuilder -- e.g. apply [PoolContinuations] on a runtime async method and it would cache continuations for that method following that scheme too.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 21 out of 21 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs:1

  • The XML doc block still describes awaiting ValueTask, but TransparentAwait now accepts Task only, while ValueTask awaiting is handled by TransparentAwaitValueTask* above. Please update/move this comment so TransparentAwait’s docs describe only the Task scenario, and place the ValueTask-specific explanation on TransparentAwaitValueTask / TransparentAwaitValueTaskOfT.
// Licensed to the .NET Foundation under one or more agreements.

Comment threadsrc/coreclr/vm/methodtable.cpp
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

I think I will do that separately. Right now the caching is essentially free since we can store it in the runtime async TLS that we already needed to access. If we introduce the per-core cache we will be paying more when the TLS cache doesn't hit, so I want to make sure I have some benchmarks for this.

@VSadov

Copy link
Copy Markdown
Member

I think I will do that separately

Per thread caching of 1 head continuation is probably the most effective as we reduce necessary allocations for that scenario to none.

Additional caching may cost more while having less impact. It makes sense to look at that separately.

@jakobbotsch
jakobbotsch marked this pull request as ready for review May 12, 2026 22:05
CopilotAI review requested due to automatic review settings May 12, 2026 22:05

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Also some decent size improvements for NativeAOT since the thunks no longer need a suspension/resumption point:

BaseSizeDiffSizeDeltaPercentImprovedName
16,852,99216,716,288-136,704-0.811%System.Linq.AsyncEnumerable.Tests.exe
115,198,464115,079,168-119,296-0.104%System.Text.Json.SourceGeneration.Roslyn4.4.Tests.exe
10,291,20010,282,496-8,704-0.085%System.Threading.Channels.Tests.exe
12,003,32811,993,088-10,240-0.085%System.IO.Tests.exe
19,561,47219,545,088-16,384-0.084%System.IO.Compression.Tests.exe
14,579,71214,568,960-10,752-0.074%System.Net.WebClient.Tests.exe
20,044,28820,029,952-14,336-0.072%System.Formats.Tar.Tests.exe
45,556,22445,524,992-31,232-0.069%System.Security.Cryptography.Tests.exe
21,715,45621,700,608-14,848-0.068%System.IO.FileSystem.Tests.exe
18,455,55218,443,264-12,288-0.067%System.Threading.Tasks.Parallel.Tests.exe
18,526,20818,513,920-12,288-0.066%System.IO.MemoryMappedFiles.Tests.exe
10,067,45610,060,800-6,656-0.066%System.Net.ServerSentEvents.Tests.exe
17,734,14417,722,368-11,776-0.066%System.Threading.Tasks.Extensions.Tests.exe
15,771,64815,761,408-10,240-0.065%System.Windows.Extensions.Tests.exe
19,296,76819,284,480-12,288-0.064%System.IO.Pipes.Tests.exe
11,286,01611,278,848-7,168-0.064%System.IO.Compression.Brotli.Tests.exe
19,570,17619,557,888-12,288-0.063%System.Diagnostics.Process.Tests.exe
20,273,66420,260,864-12,800-0.063%System.Threading.Tasks.Dataflow.Tests.exe
25,897,98425,881,600-16,384-0.063%System.Text.Json.SourceGeneration.Roslyn3.11.Tests.exe
11,343,36011,336,192-7,168-0.063%System.Net.WebSockets.Tests.exe
10,675,20010,668,544-6,656-0.062%System.IO.Pipelines.Tests.exe
13,250,56013,242,368-8,192-0.062%System.IO.Packaging.Tests.exe
20,009,98419,997,696-12,288-0.061%System.Net.WebSockets.Client.Tests.exe
31,281,66431,263,232-18,432-0.059%System.Net.Http.Functional.Tests.exe
20,942,84820,930,560-12,288-0.059%System.Net.Quic.Functional.Tests.exe
17,798,65617,788,416-10,240-0.058%System.Formats.Tar.Manual.Tests.exe
23,142,91223,129,600-13,312-0.058%System.Threading.Tasks.Tests.exe
18,947,58418,936,832-10,752-0.057%System.Runtime.Caching.Tests.exe
17,168,89617,159,168-9,728-0.057%System.Runtime.Serialization.Formatters.Disabled.Tests.exe
17,439,23217,429,504-9,728-0.056%System.Diagnostics.TraceSource.Tests.exe
17,223,16817,213,952-9,216-0.054%System.Net.ServicePoint.Tests.exe
18,070,52818,060,800-9,728-0.054%System.Console.Tests.exe
17,975,29617,965,568-9,728-0.054%System.Threading.ThreadPool.Tests.exe
18,137,60018,127,872-9,728-0.054%System.Diagnostics.TextWriterTraceListener.Tests.exe
18,380,80018,371,072-9,728-0.053%System.Threading.Tests.exe
17,296,89617,287,680-9,216-0.053%System.Runtime.InteropServices.RuntimeInformation.Tests.exe
17,438,72017,429,504-9,216-0.053%System.Net.Ping.Functional.Tests.exe
17,244,16017,234,944-9,216-0.053%System.Runtime.InvariantTimezone.Tests.exe
17,290,24017,281,024-9,216-0.053%System.Threading.Overlapped.Tests.exe
17,363,96817,354,752-9,216-0.053%Microsoft.Win32.SystemEvents.Tests.exe
17,306,11217,296,896-9,216-0.053%System.Security.Claims.Tests.exe
18,594,81618,585,088-9,728-0.052%ComInterfaceGenerator.Tests.exe
18,692,60818,682,880-9,728-0.052%Microsoft.Extensions.Logging.Console.Tests.exe
17,578,49617,569,280-9,216-0.052%MetricOuterLoop1.Tests.exe
17,573,88817,564,672-9,216-0.052%MetricOuterLoop.Tests.exe
19,688,96019,678,720-10,240-0.052%System.Net.Http.Unit.Tests.exe
17,975,29617,966,080-9,216-0.051%System.Threading.ThreadPool.WindowsThreadPool.Tests.exe
28,039,16828,024,832-14,336-0.051%System.Net.Security.Tests.exe
22,881,28022,869,504-11,776-0.051%System.Net.Sockets.Tests.exe
19,059,20019,049,472-9,728-0.051%System.Net.HttpListener.Tests.exe
18,040,83218,031,616-9,216-0.051%Microsoft.Extensions.Diagnostics.Tests.exe
17,238,52817,229,824-8,704-0.050%System.ComponentModel.EventBasedAsync.Tests.exe
17,369,08817,360,384-8,704-0.050%System.Diagnostics.StackTrace.Tests.exe
18,496,51218,487,296-9,216-0.050%Microsoft.Extensions.Hosting.Systemd.Tests.exe
17,320,44817,311,744-8,704-0.050%System.Security.Principal.Windows.Tests.exe
17,337,34417,328,640-8,704-0.050%System.Threading.Timer.Tests.exe
17,668,09617,659,392-8,704-0.049%System.IO.FileSystem.Watcher.Tests.exe
17,624,57617,615,872-8,704-0.049%System.Threading.Thread.Tests.exe
19,876,86419,867,136-9,728-0.049%System.Text.Encoding.Tests.exe
17,670,65617,661,952-8,704-0.049%System.Buffers.Tests.exe
19,401,21619,392,000-9,216-0.048%System.Net.Primitives.Functional.Tests.exe
17,161,72817,153,536-8,192-0.048%IcuAppLocal.Tests.exe
18,184,70418,176,000-8,704-0.048%System.Text.Encoding.CodePages.Tests.exe
10,061,82410,057,216-4,608-0.046%System.IO.Compression.ZipFile.Tests.exe
19,087,87219,079,168-8,704-0.046%Microsoft.Extensions.Hosting.WindowsServices.Tests.exe
21,631,48821,621,760-9,728-0.045%System.Data.OleDb.Tests.exe
20,581,37620,572,160-9,216-0.045%System.Diagnostics.DiagnosticSource.Tests.exe
13,877,76013,871,616-6,144-0.044%Microsoft.Extensions.Configuration.Functional.Tests.exe
19,752,96019,744,256-8,704-0.044%System.Resources.ResourceManager.Tests.exe
20,748,80020,739,584-9,216-0.044%System.Collections.Concurrent.Tests.exe
20,463,10420,454,400-8,704-0.043%System.Reflection.Tests.exe
20,241,40820,232,704-8,704-0.043%System.Runtime.InteropServices.Tests.exe
20,940,80020,932,096-8,704-0.042%System.Runtime.Extensions.Tests.exe
12,425,72812,420,608-5,120-0.041%System.Xml.Linq.SDMSample.Tests.exe
21,210,11221,201,408-8,704-0.041%System.Net.Requests.Tests.exe
29,510,65629,498,880-11,776-0.040%System.Net.Http.WinHttpHandler.Functional.Tests.exe
12,858,88012,853,760-5,120-0.040%System.Xml.Schema.Extensions.Tests.exe
24,283,13624,273,408-9,728-0.040%Microsoft.Extensions.Http.Tests.exe
14,142,46414,136,832-5,632-0.040%System.DirectoryServices.Protocols.Tests.exe
10,599,42410,595,328-4,096-0.039%Common.Tests.exe
9,264,1289,260,544-3,584-0.039%Microsoft.Bcl.AsyncInterfaces.Tests.exe
14,903,80814,898,176-5,632-0.038%System.Xml.Linq.Streaming.Tests.exe
13,953,53613,948,416-5,120-0.037%Microsoft.Extensions.Configuration.Xml.Tests.exe
15,094,78415,089,152-5,632-0.037%System.Security.Cryptography.Cng.Tests.exe
23,411,71223,403,008-8,704-0.037%System.Memory.Tests.exe
27,287,04027,277,312-9,728-0.036%Microsoft.Extensions.Hosting.Unit.Tests.exe
16,117,24816,111,616-5,632-0.035%System.Data.Odbc.Tests.exe
26,646,52826,637,312-9,216-0.035%Microsoft.Extensions.Logging.EventSource.Tests.exe
26,441,21626,432,000-9,216-0.035%System.ComponentModel.TypeConverter.Tests.exe
16,525,31216,519,680-5,632-0.034%System.Xml.Linq.Events.Tests.exe
16,778,75216,773,120-5,632-0.034%System.Xml.Linq.Misc.Tests.exe
26,103,80826,095,104-8,704-0.033%System.Globalization.Tests.exe
26,014,72026,006,016-8,704-0.033%System.Globalization.Nls.Tests.exe
17,009,66417,004,032-5,632-0.033%System.Xml.Linq.xNodeReader.Tests.exe
9,675,2649,672,192-3,072-0.032%System.IO.UnmanagedMemoryStream.Tests.exe
15,048,19215,043,584-4,608-0.031%System.Data.DataSetExtensions.Tests.exe
16,673,28016,668,160-5,120-0.031%System.Xml.Linq.Properties.Tests.exe
11,599,36011,595,776-3,584-0.031%System.Net.Http.Json.Unit.Tests.exe
17,065,98417,060,864-5,120-0.030%System.Xml.Linq.xNodeBuilder.Tests.exe
20,819,45620,813,312-6,144-0.030%System.Security.Cryptography.Csp.Tests.exe
17,123,84017,118,720-5,120-0.030%System.Xml.Linq.TreeManipulation.Tests.exe
33,400,83233,391,104-9,728-0.029%Microsoft.Bcl.Cryptography.Tests.exe
18,455,04018,449,920-5,120-0.028%Microsoft.Extensions.Configuration.FileExtensions.Tests.exe
32,762,36832,753,152-9,216-0.028%System.Linq.Expressions.Tests.exe
15,110,65615,106,560-4,096-0.027%System.Drawing.Primitives.Tests.exe
18,381,31218,376,704-4,608-0.025%Microsoft.Extensions.Primitives.Tests.exe
20,099,58420,094,464-5,120-0.025%Microsoft.Extensions.Configuration.Json.Tests.exe
10,521,60010,519,040-2,560-0.024%Microsoft.Extensions.Options.Tests.exe
16,973,82416,977,920+4,096+0.024%System.Private.CoreLib.dll
20,038,65620,034,048-4,608-0.023%Microsoft.Extensions.Configuration.UserSecrets.Tests.exe
19,972,09619,967,488-4,608-0.023%Microsoft.Extensions.Logging.Tests.exe
9,351,1689,349,120-2,048-0.022%System.IO.IsolatedStorage.Tests.exe
9,376,2569,374,208-2,048-0.022%System.Net.Mail.Unit.Tests.exe
18,356,22418,352,128-4,096-0.022%Microsoft.Extensions.FileProviders.Composite.Tests.exe
44,814,33644,804,608-9,728-0.022%System.Runtime.Tests.exe
18,774,01618,769,920-4,096-0.022%Microsoft.Extensions.FileProviders.Physical.Tests.exe
18,491,90418,487,808-4,096-0.022%Microsoft.Extensions.Configuration.Tests.exe
9,346,5609,344,512-2,048-0.022%Microsoft.Extensions.Diagnostics.Abstractions.Tests.exe
10,118,65610,116,608-2,048-0.020%Microsoft.Extensions.Caching.Memory.Tests.exe
30,107,13630,101,504-5,632-0.019%Microsoft.Extensions.DependencyModel.Tests.exe
10,508,28810,506,240-2,048-0.019%System.Net.Http.WinHttpHandler.Unit.Tests.exe
8,563,2008,561,664-1,536-0.018%System.Diagnostics.Contracts.Tests.exe
8,642,0488,640,512-1,536-0.018%System.Net.WebHeaderCollection.Tests.exe
8,770,0488,768,512-1,536-0.018%System.IO.FileSystem.DriveInfo.Tests.exe
12,285,95212,283,904-2,048-0.017%System.Reflection.Metadata.Tests.exe
8,989,6968,988,160-1,536-0.017%System.Net.Primitives.Pal.Tests.exe
33,161,21633,155,584-5,632-0.017%System.Private.Xml.Tests.exe
9,454,0809,452,544-1,536-0.016%Microsoft.Extensions.Logging.Testing.Tests.exe
9,630,7209,629,184-1,536-0.016%System.Composition.Convention.Tests.exe
9,314,8169,313,280-1,536-0.016%System.Net.Primitives.UnitTests.Tests.exe
11,484,16011,482,624-1,536-0.013%System.IO.Ports.Tests.exe
8,601,6008,600,576-1,024-0.012%Microsoft.Extensions.Hosting.Abstractions.Tests.exe
8,720,3848,719,360-1,024-0.012%System.Private.Uri.Unit.Tests.exe
8,546,8168,545,792-1,024-0.012%System.Runtime.CompilerServices.VisualC.Tests.exe
8,653,3128,652,288-1,024-0.012%System.Globalization.CalendarsWithConfigSwitch.Tests.exe
8,836,6088,835,584-1,024-0.012%System.Console.Manual.Tests.exe
9,364,4809,363,456-1,024-0.011%System.Reflection.Context.Tests.exe
9,041,4089,040,384-1,024-0.011%System.Net.NameResolution.Pal.Tests.exe
9,646,5929,645,568-1,024-0.011%System.Private.Uri.Functional.Tests.exe
9,704,4489,703,424-1,024-0.011%System.Text.RegularExpressions.Unit.Tests.exe
9,206,2729,205,248-1,024-0.011%Microsoft.Extensions.Configuration.EnvironmentVariables.Tests.exe
9,004,0329,003,008-1,024-0.011%System.Runtime.Serialization.Primitives.Tests.exe
9,056,2569,055,232-1,024-0.011%Microsoft.Extensions.Configuration.CommandLine.Tests.exe
9,216,5129,215,488-1,024-0.011%System.ServiceProcess.ServiceController.Tests.exe
9,539,5849,538,560-1,024-0.011%System.IO.Hashing.Tests.exe
9,671,1689,670,144-1,024-0.011%Microsoft.Extensions.Hosting.Functional.Tests.exe
4,940,2884,939,776-512-0.010%coreclr.dll
10,935,80810,934,784-1,024-0.009%System.Runtime.Intrinsics.Tests.exe
11,610,11211,609,088-1,024-0.009%System.Resources.Extensions.Tests.exe
11,193,85611,192,832-1,024-0.009%System.Text.Encodings.Web.Tests.exe
12,287,48812,286,464-1,024-0.008%System.Net.NetworkInformation.Functional.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Nls.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Tests.exe
9,039,3609,038,848-512-0.006%Microsoft.Extensions.FileSystemGlobbing.Tests.exe
8,558,0808,557,568-512-0.006%System.Text.Encoding.Extensions.Tests.exe
8,704,5128,704,000-512-0.006%System.Security.SecureString.Tests.exe
8,701,4408,700,928-512-0.006%System.Security.Cryptography.ProtectedData.Tests.exe
8,688,1288,687,616-512-0.006%System.Runtime.InteropServices.ComDisabled.Tests.exe
9,143,8089,143,296-512-0.006%System.Security.AccessControl.Tests.exe
9,025,5369,025,024-512-0.006%System.Reflection.TypeExtensions.Tests.exe
8,812,0328,811,520-512-0.006%System.Resources.Reader.Tests.exe
9,083,3929,082,880-512-0.006%System.ValueTuple.Tests.exe
8,606,2088,605,696-512-0.006%System.Resources.Writer.Tests.exe
8,817,6648,817,152-512-0.006%System.Web.HttpUtility.Tests.exe
8,863,2328,862,720-512-0.006%System.Reflection.Extensions.Tests.exe
8,670,2088,669,696-512-0.006%System.Runtime.Handles.Tests.exe
9,165,8249,165,312-512-0.006%System.Globalization.Calendars.Tests.exe
8,543,7448,543,232-512-0.006%System.IO.FileSystem.Primitives.Tests.exe
8,592,3848,591,872-512-0.006%Microsoft.Bcl.Numerics.Tests.exe
8,952,8328,952,320-512-0.006%Microsoft.Bcl.Memory.Tests.exe
8,717,3128,716,800-512-0.006%System.Diagnostics.FileVersionInfo.Tests.exe
8,700,4168,699,904-512-0.006%System.IO.FileSystem.Manual.Tests.exe
8,929,7928,929,280-512-0.006%System.IO.Pipes.AccessControl.Tests.exe
8,624,1288,623,616-512-0.006%System.Composition.Runtime.Tests.exe
9,108,9929,108,480-512-0.006%System.IO.FileSystem.AccessControl.Tests.exe
8,559,1048,558,592-512-0.006%System.Composition.AttributeModel.Tests.exe
8,759,2968,758,784-512-0.006%Invariant.Tests.exe
8,547,8408,547,328-512-0.006%System.ComponentModel.Tests.exe
9,161,7289,161,216-512-0.006%System.ComponentModel.Primitives.Tests.exe
8,959,4888,958,976-512-0.006%System.Threading.AccessControl.Tests.exe
8,991,2328,990,720-512-0.006%Microsoft.Win32.Registry.Tests.exe
8,647,6808,647,168-512-0.006%Microsoft.Win32.Registry.AccessControl.Tests.exe
8,549,3768,548,864-512-0.006%System.Diagnostics.Tools.Tests.exe
8,664,5768,664,064-512-0.006%Microsoft.Win32.Primitives.Tests.exe
9,750,0169,749,504-512-0.005%System.Collections.Specialized.Tests.exe
22,340,60822,339,584-1,024-0.005%System.Security.Cryptography.Pkcs.Tests.exe
9,852,9289,852,416-512-0.005%System.Formats.Nrbf.Tests.exe
9,763,3289,762,816-512-0.005%System.Formats.Asn1.Tests.exe
9,361,4089,360,896-512-0.005%System.Security.Permissions.Tests.exe
11,048,44811,047,936-512-0.005%System.Runtime.Numerics.Tests.exe
9,627,1369,626,624-512-0.005%System.Diagnostics.EventLog.Tests.exe
18,779,13618,778,112-1,024-0.005%System.Numerics.Vectors.Tests.exe
9,746,4329,745,920-512-0.005%System.Threading.RateLimiting.Tests.exe
9,961,4729,960,960-512-0.005%LibraryImportGenerator.Tests.exe
9,949,6969,949,184-512-0.005%System.Collections.NonGeneric.Tests.exe
9,486,8489,486,336-512-0.005%Microsoft.Extensions.Configuration.Ini.Tests.exe
13,553,66413,554,176+512+0.004%crossgen2.exe
11,796,48011,795,968-512-0.004%System.Security.Cryptography.Cose.Tests.exe
11,381,24811,380,736-512-0.004%System.Net.Security.Unit.Tests.exe
15,545,85615,545,344-512-0.003%System.Linq.Tests.exe
17,838,59217,838,080-512-0.003%System.Collections.Tests.exe

CopilotAI review requested due to automatic review settings May 13, 2026 09:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch
jakobbotsch merged commit d9f57ab into dotnet:mainMay 18, 2026
155 of 157 checks passed
@jakobbotsch
jakobbotsch deleted the cache-value-task-source-continuation branch May 18, 2026 07:31
VSadov added a commit that referenced this pull request May 21, 2026
…ce (#128399)
Same idea as in #127973, but applied when the actual Await is used (not
a thunk).
With the original repro that I was looking at, merging of #127973 helped
to reduce Gen0 GCs from ~ 15/sec to ~ 12/sec.
Alocations went down but not completely, because of code like the
following piece in the `DoReceive()` loop and possibly in other places.
This pattern still churns continuations:
https://github.com/dotnet/aspnetcore/blob/c0c2230799a3f876aa673a66bc008bf9b803acac/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs#L173-L182
```cs
var flushTask = Input.FlushAsync();
var paused = !flushTask.IsCompleted;
if (paused)
{
SocketsLog.ConnectionPause(_logger, this);
}
var result = await flushTask;
```
Because `flushTask` is not a method, the `await` does not go through a
thunk which could reuse the continuation. We call the actual `Await` in
this case and it suspends/allocates.
The change in this PR uses the same approach as in the thunk, but
applied to the Await, when awaiting a ValueTaskSource.
With this change we get to ~ 2 Gen0/sec in the repro - on par with
net10.
tommcdon added a commit that referenced this pull request May 23, 2026
…128496)
CordbAsyncStackWalk::PopulateFrame() crashes when encountering a
continuation whose ResumeInfo.DiagnosticIP is NULL (e.g. the new
ValueTaskContinuation introduced in #127973). GetNativeCodeInfoForAddr
is called with a null address which fails.
Fix: In PopulateFrame(), skip continuations with diagnosticIP == NULL
the same way DiagnosticHidden frames are skipped (advance to Next).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jakobbotsch added a commit that referenced this pull request May 26, 2026
Apply a similar optimization as #127973 to task returning thunks.
This is a very minor throughput optimization, but it also removes the
suspension point from all these thunks, which makes each of these thunks
smaller.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 17, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jakobbotsch@VSadov@lateralusX
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Cache continuation used for runtime async callable value task thunks - #127973

Merged
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation
May 18, 2026
Merged

Cache continuation used for runtime async callable value task thunks#127973
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

ValueTask backed by IValueTaskSource can suspend without allocating. However, runtime async did not support this when calling a ValueTask returning method through a runtime async callable thunk. In that case we would always allocate in the case where the ValueTask suspended.

This adds a custom ValueTaskContinuation that replaces the existing ValueTaskSourceNotifier mechanism. We now cache a ValueTaskContinuation instance and reuse it if possible whenever suspending for a ValueTask.

The same approach could be used to avoid allocations for runtime async callable thunks for task-returning methods.

cc @VSadov

`ValueTask` backed by `IValueTaskSource` can suspend without allocating.
However, runtime async did not support this when calling a `ValueTask`
returning method through a runtime async callable thunk. In that case we
would always allocate in the case where the `ValueTask` suspended.
This adds a custom `ValueTaskContinuation` that replaces the existing
`ValueTaskSourceNotifier` mechanism. We now cache a
`ValueTaskContinuation` instance and reuse it if possible whenever
suspending for a `ValueTask`.
The same approach could be used to avoid allocations for runtime async
callable thunks for task-returning methods.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 updates CoreCLR’s runtime-async callable thunk path for ValueTask/ValueTask<T> so that suspending on ValueTask backed by IValueTaskSource can reuse a cached continuation object rather than allocating per-suspension, and removes the older ValueTaskSourceNotifier / AsTaskOrNotifier mechanism.

Changes:

  • Introduces AsyncHelpers.TransparentAwaitValueTask* and a ValueTaskContinuation type that integrates with runtime-async suspension/resumption and is cached in TLS for reuse.
  • Removes ValueTask.AsTaskOrNotifier and the ValueTaskSourceNotifier helper type from ValueTask.
  • Adjusts VM continuation type handling to distinguish continuation subtypes that do have metadata (managed subclasses) vs the dynamically-created metadata-less ones, including DAC support.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.csRemoves AsTaskOrNotifier and deletes ValueTaskSourceNotifier.
src/coreclr/vm/vars.hppAdds global g_singletonContinuationEEClass declaration for continuation metadata detection.
src/coreclr/vm/vars.cppAdds global g_singletonContinuationEEClass definition.
src/coreclr/vm/typehandle.inlAvoids “upcasting” continuations that have real metadata.
src/coreclr/vm/typehandle.hAdds TypeHandle::IsContinuationWithMetadata().
src/coreclr/vm/typehandle.cppImplements TypeHandle::IsContinuationWithMetadata() and relaxes class-object allocation restriction for metadata continuations.
src/coreclr/vm/methodtable.hAdds MethodTable::IsContinuationWithMetadata() and relaxes asserts/preconditions for metadata continuations.
src/coreclr/vm/methodtable.cppImplements MethodTable::IsContinuationWithMetadata() and updates continuation-related special-casing.
src/coreclr/vm/corelib.hRemoves binder entries for ValueTask.AsTaskOrNotifier; adds binder entries for TransparentAwaitValueTask*.
src/coreclr/vm/asyncthunks.cppEmits thunk IL that tail-awaits via TransparentAwaitValueTask* instead of AsTaskOrNotifier + TransparentAwait.
src/coreclr/vm/asynccontinuations.cppMoves singleton continuation EEClass storage to global g_singletonContinuationEEClass.
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.csUpdates managed type-system thunk emission to call TransparentAwaitValueTask* and TailAwait.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdds ValueTaskContinuation, TLS caching, and TransparentAwaitValueTask*; updates suspension handling to use it.
src/coreclr/inc/dacvars.hExposes g_singletonContinuationEEClass to DAC.
src/coreclr/debug/daccess/dacdbiimpl.cppUpdates continuation canonical-MT validity logic to account for metadata continuations.

Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/asyncthunks.cpp
@VSadov

Copy link
Copy Markdown
Member

Nice! Instead of caching and reusing ValueTaskSourceNotifier, we now cache/reuse the entire head continuation.

A scenario that awaits in a loop some ValueTaskSource wrapper (like reading chunks from a pipe) can run allocation-free.

@jakobbotsch

jakobbotsch commented May 10, 2026

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

Also, we probably need to introduce a runtime async mechanism similar to PoolingAsyncValueTaskMethodBuilder -- e.g. apply [PoolContinuations] on a runtime async method and it would cache continuations for that method following that scheme too.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 21 out of 21 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs:1

  • The XML doc block still describes awaiting ValueTask, but TransparentAwait now accepts Task only, while ValueTask awaiting is handled by TransparentAwaitValueTask* above. Please update/move this comment so TransparentAwait’s docs describe only the Task scenario, and place the ValueTask-specific explanation on TransparentAwaitValueTask / TransparentAwaitValueTaskOfT.
// Licensed to the .NET Foundation under one or more agreements.

Comment threadsrc/coreclr/vm/methodtable.cpp
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

I think I will do that separately. Right now the caching is essentially free since we can store it in the runtime async TLS that we already needed to access. If we introduce the per-core cache we will be paying more when the TLS cache doesn't hit, so I want to make sure I have some benchmarks for this.

@VSadov

Copy link
Copy Markdown
Member

I think I will do that separately

Per thread caching of 1 head continuation is probably the most effective as we reduce necessary allocations for that scenario to none.

Additional caching may cost more while having less impact. It makes sense to look at that separately.

@jakobbotsch
jakobbotsch marked this pull request as ready for review May 12, 2026 22:05
CopilotAI review requested due to automatic review settings May 12, 2026 22:05

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Also some decent size improvements for NativeAOT since the thunks no longer need a suspension/resumption point:

BaseSizeDiffSizeDeltaPercentImprovedName
16,852,99216,716,288-136,704-0.811%System.Linq.AsyncEnumerable.Tests.exe
115,198,464115,079,168-119,296-0.104%System.Text.Json.SourceGeneration.Roslyn4.4.Tests.exe
10,291,20010,282,496-8,704-0.085%System.Threading.Channels.Tests.exe
12,003,32811,993,088-10,240-0.085%System.IO.Tests.exe
19,561,47219,545,088-16,384-0.084%System.IO.Compression.Tests.exe
14,579,71214,568,960-10,752-0.074%System.Net.WebClient.Tests.exe
20,044,28820,029,952-14,336-0.072%System.Formats.Tar.Tests.exe
45,556,22445,524,992-31,232-0.069%System.Security.Cryptography.Tests.exe
21,715,45621,700,608-14,848-0.068%System.IO.FileSystem.Tests.exe
18,455,55218,443,264-12,288-0.067%System.Threading.Tasks.Parallel.Tests.exe
18,526,20818,513,920-12,288-0.066%System.IO.MemoryMappedFiles.Tests.exe
10,067,45610,060,800-6,656-0.066%System.Net.ServerSentEvents.Tests.exe
17,734,14417,722,368-11,776-0.066%System.Threading.Tasks.Extensions.Tests.exe
15,771,64815,761,408-10,240-0.065%System.Windows.Extensions.Tests.exe
19,296,76819,284,480-12,288-0.064%System.IO.Pipes.Tests.exe
11,286,01611,278,848-7,168-0.064%System.IO.Compression.Brotli.Tests.exe
19,570,17619,557,888-12,288-0.063%System.Diagnostics.Process.Tests.exe
20,273,66420,260,864-12,800-0.063%System.Threading.Tasks.Dataflow.Tests.exe
25,897,98425,881,600-16,384-0.063%System.Text.Json.SourceGeneration.Roslyn3.11.Tests.exe
11,343,36011,336,192-7,168-0.063%System.Net.WebSockets.Tests.exe
10,675,20010,668,544-6,656-0.062%System.IO.Pipelines.Tests.exe
13,250,56013,242,368-8,192-0.062%System.IO.Packaging.Tests.exe
20,009,98419,997,696-12,288-0.061%System.Net.WebSockets.Client.Tests.exe
31,281,66431,263,232-18,432-0.059%System.Net.Http.Functional.Tests.exe
20,942,84820,930,560-12,288-0.059%System.Net.Quic.Functional.Tests.exe
17,798,65617,788,416-10,240-0.058%System.Formats.Tar.Manual.Tests.exe
23,142,91223,129,600-13,312-0.058%System.Threading.Tasks.Tests.exe
18,947,58418,936,832-10,752-0.057%System.Runtime.Caching.Tests.exe
17,168,89617,159,168-9,728-0.057%System.Runtime.Serialization.Formatters.Disabled.Tests.exe
17,439,23217,429,504-9,728-0.056%System.Diagnostics.TraceSource.Tests.exe
17,223,16817,213,952-9,216-0.054%System.Net.ServicePoint.Tests.exe
18,070,52818,060,800-9,728-0.054%System.Console.Tests.exe
17,975,29617,965,568-9,728-0.054%System.Threading.ThreadPool.Tests.exe
18,137,60018,127,872-9,728-0.054%System.Diagnostics.TextWriterTraceListener.Tests.exe
18,380,80018,371,072-9,728-0.053%System.Threading.Tests.exe
17,296,89617,287,680-9,216-0.053%System.Runtime.InteropServices.RuntimeInformation.Tests.exe
17,438,72017,429,504-9,216-0.053%System.Net.Ping.Functional.Tests.exe
17,244,16017,234,944-9,216-0.053%System.Runtime.InvariantTimezone.Tests.exe
17,290,24017,281,024-9,216-0.053%System.Threading.Overlapped.Tests.exe
17,363,96817,354,752-9,216-0.053%Microsoft.Win32.SystemEvents.Tests.exe
17,306,11217,296,896-9,216-0.053%System.Security.Claims.Tests.exe
18,594,81618,585,088-9,728-0.052%ComInterfaceGenerator.Tests.exe
18,692,60818,682,880-9,728-0.052%Microsoft.Extensions.Logging.Console.Tests.exe
17,578,49617,569,280-9,216-0.052%MetricOuterLoop1.Tests.exe
17,573,88817,564,672-9,216-0.052%MetricOuterLoop.Tests.exe
19,688,96019,678,720-10,240-0.052%System.Net.Http.Unit.Tests.exe
17,975,29617,966,080-9,216-0.051%System.Threading.ThreadPool.WindowsThreadPool.Tests.exe
28,039,16828,024,832-14,336-0.051%System.Net.Security.Tests.exe
22,881,28022,869,504-11,776-0.051%System.Net.Sockets.Tests.exe
19,059,20019,049,472-9,728-0.051%System.Net.HttpListener.Tests.exe
18,040,83218,031,616-9,216-0.051%Microsoft.Extensions.Diagnostics.Tests.exe
17,238,52817,229,824-8,704-0.050%System.ComponentModel.EventBasedAsync.Tests.exe
17,369,08817,360,384-8,704-0.050%System.Diagnostics.StackTrace.Tests.exe
18,496,51218,487,296-9,216-0.050%Microsoft.Extensions.Hosting.Systemd.Tests.exe
17,320,44817,311,744-8,704-0.050%System.Security.Principal.Windows.Tests.exe
17,337,34417,328,640-8,704-0.050%System.Threading.Timer.Tests.exe
17,668,09617,659,392-8,704-0.049%System.IO.FileSystem.Watcher.Tests.exe
17,624,57617,615,872-8,704-0.049%System.Threading.Thread.Tests.exe
19,876,86419,867,136-9,728-0.049%System.Text.Encoding.Tests.exe
17,670,65617,661,952-8,704-0.049%System.Buffers.Tests.exe
19,401,21619,392,000-9,216-0.048%System.Net.Primitives.Functional.Tests.exe
17,161,72817,153,536-8,192-0.048%IcuAppLocal.Tests.exe
18,184,70418,176,000-8,704-0.048%System.Text.Encoding.CodePages.Tests.exe
10,061,82410,057,216-4,608-0.046%System.IO.Compression.ZipFile.Tests.exe
19,087,87219,079,168-8,704-0.046%Microsoft.Extensions.Hosting.WindowsServices.Tests.exe
21,631,48821,621,760-9,728-0.045%System.Data.OleDb.Tests.exe
20,581,37620,572,160-9,216-0.045%System.Diagnostics.DiagnosticSource.Tests.exe
13,877,76013,871,616-6,144-0.044%Microsoft.Extensions.Configuration.Functional.Tests.exe
19,752,96019,744,256-8,704-0.044%System.Resources.ResourceManager.Tests.exe
20,748,80020,739,584-9,216-0.044%System.Collections.Concurrent.Tests.exe
20,463,10420,454,400-8,704-0.043%System.Reflection.Tests.exe
20,241,40820,232,704-8,704-0.043%System.Runtime.InteropServices.Tests.exe
20,940,80020,932,096-8,704-0.042%System.Runtime.Extensions.Tests.exe
12,425,72812,420,608-5,120-0.041%System.Xml.Linq.SDMSample.Tests.exe
21,210,11221,201,408-8,704-0.041%System.Net.Requests.Tests.exe
29,510,65629,498,880-11,776-0.040%System.Net.Http.WinHttpHandler.Functional.Tests.exe
12,858,88012,853,760-5,120-0.040%System.Xml.Schema.Extensions.Tests.exe
24,283,13624,273,408-9,728-0.040%Microsoft.Extensions.Http.Tests.exe
14,142,46414,136,832-5,632-0.040%System.DirectoryServices.Protocols.Tests.exe
10,599,42410,595,328-4,096-0.039%Common.Tests.exe
9,264,1289,260,544-3,584-0.039%Microsoft.Bcl.AsyncInterfaces.Tests.exe
14,903,80814,898,176-5,632-0.038%System.Xml.Linq.Streaming.Tests.exe
13,953,53613,948,416-5,120-0.037%Microsoft.Extensions.Configuration.Xml.Tests.exe
15,094,78415,089,152-5,632-0.037%System.Security.Cryptography.Cng.Tests.exe
23,411,71223,403,008-8,704-0.037%System.Memory.Tests.exe
27,287,04027,277,312-9,728-0.036%Microsoft.Extensions.Hosting.Unit.Tests.exe
16,117,24816,111,616-5,632-0.035%System.Data.Odbc.Tests.exe
26,646,52826,637,312-9,216-0.035%Microsoft.Extensions.Logging.EventSource.Tests.exe
26,441,21626,432,000-9,216-0.035%System.ComponentModel.TypeConverter.Tests.exe
16,525,31216,519,680-5,632-0.034%System.Xml.Linq.Events.Tests.exe
16,778,75216,773,120-5,632-0.034%System.Xml.Linq.Misc.Tests.exe
26,103,80826,095,104-8,704-0.033%System.Globalization.Tests.exe
26,014,72026,006,016-8,704-0.033%System.Globalization.Nls.Tests.exe
17,009,66417,004,032-5,632-0.033%System.Xml.Linq.xNodeReader.Tests.exe
9,675,2649,672,192-3,072-0.032%System.IO.UnmanagedMemoryStream.Tests.exe
15,048,19215,043,584-4,608-0.031%System.Data.DataSetExtensions.Tests.exe
16,673,28016,668,160-5,120-0.031%System.Xml.Linq.Properties.Tests.exe
11,599,36011,595,776-3,584-0.031%System.Net.Http.Json.Unit.Tests.exe
17,065,98417,060,864-5,120-0.030%System.Xml.Linq.xNodeBuilder.Tests.exe
20,819,45620,813,312-6,144-0.030%System.Security.Cryptography.Csp.Tests.exe
17,123,84017,118,720-5,120-0.030%System.Xml.Linq.TreeManipulation.Tests.exe
33,400,83233,391,104-9,728-0.029%Microsoft.Bcl.Cryptography.Tests.exe
18,455,04018,449,920-5,120-0.028%Microsoft.Extensions.Configuration.FileExtensions.Tests.exe
32,762,36832,753,152-9,216-0.028%System.Linq.Expressions.Tests.exe
15,110,65615,106,560-4,096-0.027%System.Drawing.Primitives.Tests.exe
18,381,31218,376,704-4,608-0.025%Microsoft.Extensions.Primitives.Tests.exe
20,099,58420,094,464-5,120-0.025%Microsoft.Extensions.Configuration.Json.Tests.exe
10,521,60010,519,040-2,560-0.024%Microsoft.Extensions.Options.Tests.exe
16,973,82416,977,920+4,096+0.024%System.Private.CoreLib.dll
20,038,65620,034,048-4,608-0.023%Microsoft.Extensions.Configuration.UserSecrets.Tests.exe
19,972,09619,967,488-4,608-0.023%Microsoft.Extensions.Logging.Tests.exe
9,351,1689,349,120-2,048-0.022%System.IO.IsolatedStorage.Tests.exe
9,376,2569,374,208-2,048-0.022%System.Net.Mail.Unit.Tests.exe
18,356,22418,352,128-4,096-0.022%Microsoft.Extensions.FileProviders.Composite.Tests.exe
44,814,33644,804,608-9,728-0.022%System.Runtime.Tests.exe
18,774,01618,769,920-4,096-0.022%Microsoft.Extensions.FileProviders.Physical.Tests.exe
18,491,90418,487,808-4,096-0.022%Microsoft.Extensions.Configuration.Tests.exe
9,346,5609,344,512-2,048-0.022%Microsoft.Extensions.Diagnostics.Abstractions.Tests.exe
10,118,65610,116,608-2,048-0.020%Microsoft.Extensions.Caching.Memory.Tests.exe
30,107,13630,101,504-5,632-0.019%Microsoft.Extensions.DependencyModel.Tests.exe
10,508,28810,506,240-2,048-0.019%System.Net.Http.WinHttpHandler.Unit.Tests.exe
8,563,2008,561,664-1,536-0.018%System.Diagnostics.Contracts.Tests.exe
8,642,0488,640,512-1,536-0.018%System.Net.WebHeaderCollection.Tests.exe
8,770,0488,768,512-1,536-0.018%System.IO.FileSystem.DriveInfo.Tests.exe
12,285,95212,283,904-2,048-0.017%System.Reflection.Metadata.Tests.exe
8,989,6968,988,160-1,536-0.017%System.Net.Primitives.Pal.Tests.exe
33,161,21633,155,584-5,632-0.017%System.Private.Xml.Tests.exe
9,454,0809,452,544-1,536-0.016%Microsoft.Extensions.Logging.Testing.Tests.exe
9,630,7209,629,184-1,536-0.016%System.Composition.Convention.Tests.exe
9,314,8169,313,280-1,536-0.016%System.Net.Primitives.UnitTests.Tests.exe
11,484,16011,482,624-1,536-0.013%System.IO.Ports.Tests.exe
8,601,6008,600,576-1,024-0.012%Microsoft.Extensions.Hosting.Abstractions.Tests.exe
8,720,3848,719,360-1,024-0.012%System.Private.Uri.Unit.Tests.exe
8,546,8168,545,792-1,024-0.012%System.Runtime.CompilerServices.VisualC.Tests.exe
8,653,3128,652,288-1,024-0.012%System.Globalization.CalendarsWithConfigSwitch.Tests.exe
8,836,6088,835,584-1,024-0.012%System.Console.Manual.Tests.exe
9,364,4809,363,456-1,024-0.011%System.Reflection.Context.Tests.exe
9,041,4089,040,384-1,024-0.011%System.Net.NameResolution.Pal.Tests.exe
9,646,5929,645,568-1,024-0.011%System.Private.Uri.Functional.Tests.exe
9,704,4489,703,424-1,024-0.011%System.Text.RegularExpressions.Unit.Tests.exe
9,206,2729,205,248-1,024-0.011%Microsoft.Extensions.Configuration.EnvironmentVariables.Tests.exe
9,004,0329,003,008-1,024-0.011%System.Runtime.Serialization.Primitives.Tests.exe
9,056,2569,055,232-1,024-0.011%Microsoft.Extensions.Configuration.CommandLine.Tests.exe
9,216,5129,215,488-1,024-0.011%System.ServiceProcess.ServiceController.Tests.exe
9,539,5849,538,560-1,024-0.011%System.IO.Hashing.Tests.exe
9,671,1689,670,144-1,024-0.011%Microsoft.Extensions.Hosting.Functional.Tests.exe
4,940,2884,939,776-512-0.010%coreclr.dll
10,935,80810,934,784-1,024-0.009%System.Runtime.Intrinsics.Tests.exe
11,610,11211,609,088-1,024-0.009%System.Resources.Extensions.Tests.exe
11,193,85611,192,832-1,024-0.009%System.Text.Encodings.Web.Tests.exe
12,287,48812,286,464-1,024-0.008%System.Net.NetworkInformation.Functional.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Nls.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Tests.exe
9,039,3609,038,848-512-0.006%Microsoft.Extensions.FileSystemGlobbing.Tests.exe
8,558,0808,557,568-512-0.006%System.Text.Encoding.Extensions.Tests.exe
8,704,5128,704,000-512-0.006%System.Security.SecureString.Tests.exe
8,701,4408,700,928-512-0.006%System.Security.Cryptography.ProtectedData.Tests.exe
8,688,1288,687,616-512-0.006%System.Runtime.InteropServices.ComDisabled.Tests.exe
9,143,8089,143,296-512-0.006%System.Security.AccessControl.Tests.exe
9,025,5369,025,024-512-0.006%System.Reflection.TypeExtensions.Tests.exe
8,812,0328,811,520-512-0.006%System.Resources.Reader.Tests.exe
9,083,3929,082,880-512-0.006%System.ValueTuple.Tests.exe
8,606,2088,605,696-512-0.006%System.Resources.Writer.Tests.exe
8,817,6648,817,152-512-0.006%System.Web.HttpUtility.Tests.exe
8,863,2328,862,720-512-0.006%System.Reflection.Extensions.Tests.exe
8,670,2088,669,696-512-0.006%System.Runtime.Handles.Tests.exe
9,165,8249,165,312-512-0.006%System.Globalization.Calendars.Tests.exe
8,543,7448,543,232-512-0.006%System.IO.FileSystem.Primitives.Tests.exe
8,592,3848,591,872-512-0.006%Microsoft.Bcl.Numerics.Tests.exe
8,952,8328,952,320-512-0.006%Microsoft.Bcl.Memory.Tests.exe
8,717,3128,716,800-512-0.006%System.Diagnostics.FileVersionInfo.Tests.exe
8,700,4168,699,904-512-0.006%System.IO.FileSystem.Manual.Tests.exe
8,929,7928,929,280-512-0.006%System.IO.Pipes.AccessControl.Tests.exe
8,624,1288,623,616-512-0.006%System.Composition.Runtime.Tests.exe
9,108,9929,108,480-512-0.006%System.IO.FileSystem.AccessControl.Tests.exe
8,559,1048,558,592-512-0.006%System.Composition.AttributeModel.Tests.exe
8,759,2968,758,784-512-0.006%Invariant.Tests.exe
8,547,8408,547,328-512-0.006%System.ComponentModel.Tests.exe
9,161,7289,161,216-512-0.006%System.ComponentModel.Primitives.Tests.exe
8,959,4888,958,976-512-0.006%System.Threading.AccessControl.Tests.exe
8,991,2328,990,720-512-0.006%Microsoft.Win32.Registry.Tests.exe
8,647,6808,647,168-512-0.006%Microsoft.Win32.Registry.AccessControl.Tests.exe
8,549,3768,548,864-512-0.006%System.Diagnostics.Tools.Tests.exe
8,664,5768,664,064-512-0.006%Microsoft.Win32.Primitives.Tests.exe
9,750,0169,749,504-512-0.005%System.Collections.Specialized.Tests.exe
22,340,60822,339,584-1,024-0.005%System.Security.Cryptography.Pkcs.Tests.exe
9,852,9289,852,416-512-0.005%System.Formats.Nrbf.Tests.exe
9,763,3289,762,816-512-0.005%System.Formats.Asn1.Tests.exe
9,361,4089,360,896-512-0.005%System.Security.Permissions.Tests.exe
11,048,44811,047,936-512-0.005%System.Runtime.Numerics.Tests.exe
9,627,1369,626,624-512-0.005%System.Diagnostics.EventLog.Tests.exe
18,779,13618,778,112-1,024-0.005%System.Numerics.Vectors.Tests.exe
9,746,4329,745,920-512-0.005%System.Threading.RateLimiting.Tests.exe
9,961,4729,960,960-512-0.005%LibraryImportGenerator.Tests.exe
9,949,6969,949,184-512-0.005%System.Collections.NonGeneric.Tests.exe
9,486,8489,486,336-512-0.005%Microsoft.Extensions.Configuration.Ini.Tests.exe
13,553,66413,554,176+512+0.004%crossgen2.exe
11,796,48011,795,968-512-0.004%System.Security.Cryptography.Cose.Tests.exe
11,381,24811,380,736-512-0.004%System.Net.Security.Unit.Tests.exe
15,545,85615,545,344-512-0.003%System.Linq.Tests.exe
17,838,59217,838,080-512-0.003%System.Collections.Tests.exe

CopilotAI review requested due to automatic review settings May 13, 2026 09:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch
jakobbotsch merged commit d9f57ab into dotnet:mainMay 18, 2026
155 of 157 checks passed
@jakobbotsch
jakobbotsch deleted the cache-value-task-source-continuation branch May 18, 2026 07:31
VSadov added a commit that referenced this pull request May 21, 2026
…ce (#128399)
Same idea as in #127973, but applied when the actual Await is used (not
a thunk).
With the original repro that I was looking at, merging of #127973 helped
to reduce Gen0 GCs from ~ 15/sec to ~ 12/sec.
Alocations went down but not completely, because of code like the
following piece in the `DoReceive()` loop and possibly in other places.
This pattern still churns continuations:
https://github.com/dotnet/aspnetcore/blob/c0c2230799a3f876aa673a66bc008bf9b803acac/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs#L173-L182
```cs
var flushTask = Input.FlushAsync();
var paused = !flushTask.IsCompleted;
if (paused)
{
SocketsLog.ConnectionPause(_logger, this);
}
var result = await flushTask;
```
Because `flushTask` is not a method, the `await` does not go through a
thunk which could reuse the continuation. We call the actual `Await` in
this case and it suspends/allocates.
The change in this PR uses the same approach as in the thunk, but
applied to the Await, when awaiting a ValueTaskSource.
With this change we get to ~ 2 Gen0/sec in the repro - on par with
net10.
tommcdon added a commit that referenced this pull request May 23, 2026
…128496)
CordbAsyncStackWalk::PopulateFrame() crashes when encountering a
continuation whose ResumeInfo.DiagnosticIP is NULL (e.g. the new
ValueTaskContinuation introduced in #127973). GetNativeCodeInfoForAddr
is called with a null address which fails.
Fix: In PopulateFrame(), skip continuations with diagnosticIP == NULL
the same way DiagnosticHidden frames are skipped (advance to Next).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jakobbotsch added a commit that referenced this pull request May 26, 2026
Apply a similar optimization as #127973 to task returning thunks.
This is a very minor throughput optimization, but it also removes the
suspension point from all these thunks, which makes each of these thunks
smaller.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 17, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jakobbotsch@VSadov@lateralusX
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Cache continuation used for runtime async callable value task thunks - #127973

Merged
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation
May 18, 2026
Merged

Cache continuation used for runtime async callable value task thunks#127973
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

ValueTask backed by IValueTaskSource can suspend without allocating. However, runtime async did not support this when calling a ValueTask returning method through a runtime async callable thunk. In that case we would always allocate in the case where the ValueTask suspended.

This adds a custom ValueTaskContinuation that replaces the existing ValueTaskSourceNotifier mechanism. We now cache a ValueTaskContinuation instance and reuse it if possible whenever suspending for a ValueTask.

The same approach could be used to avoid allocations for runtime async callable thunks for task-returning methods.

cc @VSadov

`ValueTask` backed by `IValueTaskSource` can suspend without allocating.
However, runtime async did not support this when calling a `ValueTask`
returning method through a runtime async callable thunk. In that case we
would always allocate in the case where the `ValueTask` suspended.
This adds a custom `ValueTaskContinuation` that replaces the existing
`ValueTaskSourceNotifier` mechanism. We now cache a
`ValueTaskContinuation` instance and reuse it if possible whenever
suspending for a `ValueTask`.
The same approach could be used to avoid allocations for runtime async
callable thunks for task-returning methods.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 updates CoreCLR’s runtime-async callable thunk path for ValueTask/ValueTask<T> so that suspending on ValueTask backed by IValueTaskSource can reuse a cached continuation object rather than allocating per-suspension, and removes the older ValueTaskSourceNotifier / AsTaskOrNotifier mechanism.

Changes:

  • Introduces AsyncHelpers.TransparentAwaitValueTask* and a ValueTaskContinuation type that integrates with runtime-async suspension/resumption and is cached in TLS for reuse.
  • Removes ValueTask.AsTaskOrNotifier and the ValueTaskSourceNotifier helper type from ValueTask.
  • Adjusts VM continuation type handling to distinguish continuation subtypes that do have metadata (managed subclasses) vs the dynamically-created metadata-less ones, including DAC support.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.csRemoves AsTaskOrNotifier and deletes ValueTaskSourceNotifier.
src/coreclr/vm/vars.hppAdds global g_singletonContinuationEEClass declaration for continuation metadata detection.
src/coreclr/vm/vars.cppAdds global g_singletonContinuationEEClass definition.
src/coreclr/vm/typehandle.inlAvoids “upcasting” continuations that have real metadata.
src/coreclr/vm/typehandle.hAdds TypeHandle::IsContinuationWithMetadata().
src/coreclr/vm/typehandle.cppImplements TypeHandle::IsContinuationWithMetadata() and relaxes class-object allocation restriction for metadata continuations.
src/coreclr/vm/methodtable.hAdds MethodTable::IsContinuationWithMetadata() and relaxes asserts/preconditions for metadata continuations.
src/coreclr/vm/methodtable.cppImplements MethodTable::IsContinuationWithMetadata() and updates continuation-related special-casing.
src/coreclr/vm/corelib.hRemoves binder entries for ValueTask.AsTaskOrNotifier; adds binder entries for TransparentAwaitValueTask*.
src/coreclr/vm/asyncthunks.cppEmits thunk IL that tail-awaits via TransparentAwaitValueTask* instead of AsTaskOrNotifier + TransparentAwait.
src/coreclr/vm/asynccontinuations.cppMoves singleton continuation EEClass storage to global g_singletonContinuationEEClass.
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.csUpdates managed type-system thunk emission to call TransparentAwaitValueTask* and TailAwait.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdds ValueTaskContinuation, TLS caching, and TransparentAwaitValueTask*; updates suspension handling to use it.
src/coreclr/inc/dacvars.hExposes g_singletonContinuationEEClass to DAC.
src/coreclr/debug/daccess/dacdbiimpl.cppUpdates continuation canonical-MT validity logic to account for metadata continuations.

Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/asyncthunks.cpp
@VSadov

Copy link
Copy Markdown
Member

Nice! Instead of caching and reusing ValueTaskSourceNotifier, we now cache/reuse the entire head continuation.

A scenario that awaits in a loop some ValueTaskSource wrapper (like reading chunks from a pipe) can run allocation-free.

@jakobbotsch

jakobbotsch commented May 10, 2026

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

Also, we probably need to introduce a runtime async mechanism similar to PoolingAsyncValueTaskMethodBuilder -- e.g. apply [PoolContinuations] on a runtime async method and it would cache continuations for that method following that scheme too.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 21 out of 21 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs:1

  • The XML doc block still describes awaiting ValueTask, but TransparentAwait now accepts Task only, while ValueTask awaiting is handled by TransparentAwaitValueTask* above. Please update/move this comment so TransparentAwait’s docs describe only the Task scenario, and place the ValueTask-specific explanation on TransparentAwaitValueTask / TransparentAwaitValueTaskOfT.
// Licensed to the .NET Foundation under one or more agreements.

Comment threadsrc/coreclr/vm/methodtable.cpp
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

I think I will do that separately. Right now the caching is essentially free since we can store it in the runtime async TLS that we already needed to access. If we introduce the per-core cache we will be paying more when the TLS cache doesn't hit, so I want to make sure I have some benchmarks for this.

@VSadov

Copy link
Copy Markdown
Member

I think I will do that separately

Per thread caching of 1 head continuation is probably the most effective as we reduce necessary allocations for that scenario to none.

Additional caching may cost more while having less impact. It makes sense to look at that separately.

@jakobbotsch
jakobbotsch marked this pull request as ready for review May 12, 2026 22:05
CopilotAI review requested due to automatic review settings May 12, 2026 22:05

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Also some decent size improvements for NativeAOT since the thunks no longer need a suspension/resumption point:

BaseSizeDiffSizeDeltaPercentImprovedName
16,852,99216,716,288-136,704-0.811%System.Linq.AsyncEnumerable.Tests.exe
115,198,464115,079,168-119,296-0.104%System.Text.Json.SourceGeneration.Roslyn4.4.Tests.exe
10,291,20010,282,496-8,704-0.085%System.Threading.Channels.Tests.exe
12,003,32811,993,088-10,240-0.085%System.IO.Tests.exe
19,561,47219,545,088-16,384-0.084%System.IO.Compression.Tests.exe
14,579,71214,568,960-10,752-0.074%System.Net.WebClient.Tests.exe
20,044,28820,029,952-14,336-0.072%System.Formats.Tar.Tests.exe
45,556,22445,524,992-31,232-0.069%System.Security.Cryptography.Tests.exe
21,715,45621,700,608-14,848-0.068%System.IO.FileSystem.Tests.exe
18,455,55218,443,264-12,288-0.067%System.Threading.Tasks.Parallel.Tests.exe
18,526,20818,513,920-12,288-0.066%System.IO.MemoryMappedFiles.Tests.exe
10,067,45610,060,800-6,656-0.066%System.Net.ServerSentEvents.Tests.exe
17,734,14417,722,368-11,776-0.066%System.Threading.Tasks.Extensions.Tests.exe
15,771,64815,761,408-10,240-0.065%System.Windows.Extensions.Tests.exe
19,296,76819,284,480-12,288-0.064%System.IO.Pipes.Tests.exe
11,286,01611,278,848-7,168-0.064%System.IO.Compression.Brotli.Tests.exe
19,570,17619,557,888-12,288-0.063%System.Diagnostics.Process.Tests.exe
20,273,66420,260,864-12,800-0.063%System.Threading.Tasks.Dataflow.Tests.exe
25,897,98425,881,600-16,384-0.063%System.Text.Json.SourceGeneration.Roslyn3.11.Tests.exe
11,343,36011,336,192-7,168-0.063%System.Net.WebSockets.Tests.exe
10,675,20010,668,544-6,656-0.062%System.IO.Pipelines.Tests.exe
13,250,56013,242,368-8,192-0.062%System.IO.Packaging.Tests.exe
20,009,98419,997,696-12,288-0.061%System.Net.WebSockets.Client.Tests.exe
31,281,66431,263,232-18,432-0.059%System.Net.Http.Functional.Tests.exe
20,942,84820,930,560-12,288-0.059%System.Net.Quic.Functional.Tests.exe
17,798,65617,788,416-10,240-0.058%System.Formats.Tar.Manual.Tests.exe
23,142,91223,129,600-13,312-0.058%System.Threading.Tasks.Tests.exe
18,947,58418,936,832-10,752-0.057%System.Runtime.Caching.Tests.exe
17,168,89617,159,168-9,728-0.057%System.Runtime.Serialization.Formatters.Disabled.Tests.exe
17,439,23217,429,504-9,728-0.056%System.Diagnostics.TraceSource.Tests.exe
17,223,16817,213,952-9,216-0.054%System.Net.ServicePoint.Tests.exe
18,070,52818,060,800-9,728-0.054%System.Console.Tests.exe
17,975,29617,965,568-9,728-0.054%System.Threading.ThreadPool.Tests.exe
18,137,60018,127,872-9,728-0.054%System.Diagnostics.TextWriterTraceListener.Tests.exe
18,380,80018,371,072-9,728-0.053%System.Threading.Tests.exe
17,296,89617,287,680-9,216-0.053%System.Runtime.InteropServices.RuntimeInformation.Tests.exe
17,438,72017,429,504-9,216-0.053%System.Net.Ping.Functional.Tests.exe
17,244,16017,234,944-9,216-0.053%System.Runtime.InvariantTimezone.Tests.exe
17,290,24017,281,024-9,216-0.053%System.Threading.Overlapped.Tests.exe
17,363,96817,354,752-9,216-0.053%Microsoft.Win32.SystemEvents.Tests.exe
17,306,11217,296,896-9,216-0.053%System.Security.Claims.Tests.exe
18,594,81618,585,088-9,728-0.052%ComInterfaceGenerator.Tests.exe
18,692,60818,682,880-9,728-0.052%Microsoft.Extensions.Logging.Console.Tests.exe
17,578,49617,569,280-9,216-0.052%MetricOuterLoop1.Tests.exe
17,573,88817,564,672-9,216-0.052%MetricOuterLoop.Tests.exe
19,688,96019,678,720-10,240-0.052%System.Net.Http.Unit.Tests.exe
17,975,29617,966,080-9,216-0.051%System.Threading.ThreadPool.WindowsThreadPool.Tests.exe
28,039,16828,024,832-14,336-0.051%System.Net.Security.Tests.exe
22,881,28022,869,504-11,776-0.051%System.Net.Sockets.Tests.exe
19,059,20019,049,472-9,728-0.051%System.Net.HttpListener.Tests.exe
18,040,83218,031,616-9,216-0.051%Microsoft.Extensions.Diagnostics.Tests.exe
17,238,52817,229,824-8,704-0.050%System.ComponentModel.EventBasedAsync.Tests.exe
17,369,08817,360,384-8,704-0.050%System.Diagnostics.StackTrace.Tests.exe
18,496,51218,487,296-9,216-0.050%Microsoft.Extensions.Hosting.Systemd.Tests.exe
17,320,44817,311,744-8,704-0.050%System.Security.Principal.Windows.Tests.exe
17,337,34417,328,640-8,704-0.050%System.Threading.Timer.Tests.exe
17,668,09617,659,392-8,704-0.049%System.IO.FileSystem.Watcher.Tests.exe
17,624,57617,615,872-8,704-0.049%System.Threading.Thread.Tests.exe
19,876,86419,867,136-9,728-0.049%System.Text.Encoding.Tests.exe
17,670,65617,661,952-8,704-0.049%System.Buffers.Tests.exe
19,401,21619,392,000-9,216-0.048%System.Net.Primitives.Functional.Tests.exe
17,161,72817,153,536-8,192-0.048%IcuAppLocal.Tests.exe
18,184,70418,176,000-8,704-0.048%System.Text.Encoding.CodePages.Tests.exe
10,061,82410,057,216-4,608-0.046%System.IO.Compression.ZipFile.Tests.exe
19,087,87219,079,168-8,704-0.046%Microsoft.Extensions.Hosting.WindowsServices.Tests.exe
21,631,48821,621,760-9,728-0.045%System.Data.OleDb.Tests.exe
20,581,37620,572,160-9,216-0.045%System.Diagnostics.DiagnosticSource.Tests.exe
13,877,76013,871,616-6,144-0.044%Microsoft.Extensions.Configuration.Functional.Tests.exe
19,752,96019,744,256-8,704-0.044%System.Resources.ResourceManager.Tests.exe
20,748,80020,739,584-9,216-0.044%System.Collections.Concurrent.Tests.exe
20,463,10420,454,400-8,704-0.043%System.Reflection.Tests.exe
20,241,40820,232,704-8,704-0.043%System.Runtime.InteropServices.Tests.exe
20,940,80020,932,096-8,704-0.042%System.Runtime.Extensions.Tests.exe
12,425,72812,420,608-5,120-0.041%System.Xml.Linq.SDMSample.Tests.exe
21,210,11221,201,408-8,704-0.041%System.Net.Requests.Tests.exe
29,510,65629,498,880-11,776-0.040%System.Net.Http.WinHttpHandler.Functional.Tests.exe
12,858,88012,853,760-5,120-0.040%System.Xml.Schema.Extensions.Tests.exe
24,283,13624,273,408-9,728-0.040%Microsoft.Extensions.Http.Tests.exe
14,142,46414,136,832-5,632-0.040%System.DirectoryServices.Protocols.Tests.exe
10,599,42410,595,328-4,096-0.039%Common.Tests.exe
9,264,1289,260,544-3,584-0.039%Microsoft.Bcl.AsyncInterfaces.Tests.exe
14,903,80814,898,176-5,632-0.038%System.Xml.Linq.Streaming.Tests.exe
13,953,53613,948,416-5,120-0.037%Microsoft.Extensions.Configuration.Xml.Tests.exe
15,094,78415,089,152-5,632-0.037%System.Security.Cryptography.Cng.Tests.exe
23,411,71223,403,008-8,704-0.037%System.Memory.Tests.exe
27,287,04027,277,312-9,728-0.036%Microsoft.Extensions.Hosting.Unit.Tests.exe
16,117,24816,111,616-5,632-0.035%System.Data.Odbc.Tests.exe
26,646,52826,637,312-9,216-0.035%Microsoft.Extensions.Logging.EventSource.Tests.exe
26,441,21626,432,000-9,216-0.035%System.ComponentModel.TypeConverter.Tests.exe
16,525,31216,519,680-5,632-0.034%System.Xml.Linq.Events.Tests.exe
16,778,75216,773,120-5,632-0.034%System.Xml.Linq.Misc.Tests.exe
26,103,80826,095,104-8,704-0.033%System.Globalization.Tests.exe
26,014,72026,006,016-8,704-0.033%System.Globalization.Nls.Tests.exe
17,009,66417,004,032-5,632-0.033%System.Xml.Linq.xNodeReader.Tests.exe
9,675,2649,672,192-3,072-0.032%System.IO.UnmanagedMemoryStream.Tests.exe
15,048,19215,043,584-4,608-0.031%System.Data.DataSetExtensions.Tests.exe
16,673,28016,668,160-5,120-0.031%System.Xml.Linq.Properties.Tests.exe
11,599,36011,595,776-3,584-0.031%System.Net.Http.Json.Unit.Tests.exe
17,065,98417,060,864-5,120-0.030%System.Xml.Linq.xNodeBuilder.Tests.exe
20,819,45620,813,312-6,144-0.030%System.Security.Cryptography.Csp.Tests.exe
17,123,84017,118,720-5,120-0.030%System.Xml.Linq.TreeManipulation.Tests.exe
33,400,83233,391,104-9,728-0.029%Microsoft.Bcl.Cryptography.Tests.exe
18,455,04018,449,920-5,120-0.028%Microsoft.Extensions.Configuration.FileExtensions.Tests.exe
32,762,36832,753,152-9,216-0.028%System.Linq.Expressions.Tests.exe
15,110,65615,106,560-4,096-0.027%System.Drawing.Primitives.Tests.exe
18,381,31218,376,704-4,608-0.025%Microsoft.Extensions.Primitives.Tests.exe
20,099,58420,094,464-5,120-0.025%Microsoft.Extensions.Configuration.Json.Tests.exe
10,521,60010,519,040-2,560-0.024%Microsoft.Extensions.Options.Tests.exe
16,973,82416,977,920+4,096+0.024%System.Private.CoreLib.dll
20,038,65620,034,048-4,608-0.023%Microsoft.Extensions.Configuration.UserSecrets.Tests.exe
19,972,09619,967,488-4,608-0.023%Microsoft.Extensions.Logging.Tests.exe
9,351,1689,349,120-2,048-0.022%System.IO.IsolatedStorage.Tests.exe
9,376,2569,374,208-2,048-0.022%System.Net.Mail.Unit.Tests.exe
18,356,22418,352,128-4,096-0.022%Microsoft.Extensions.FileProviders.Composite.Tests.exe
44,814,33644,804,608-9,728-0.022%System.Runtime.Tests.exe
18,774,01618,769,920-4,096-0.022%Microsoft.Extensions.FileProviders.Physical.Tests.exe
18,491,90418,487,808-4,096-0.022%Microsoft.Extensions.Configuration.Tests.exe
9,346,5609,344,512-2,048-0.022%Microsoft.Extensions.Diagnostics.Abstractions.Tests.exe
10,118,65610,116,608-2,048-0.020%Microsoft.Extensions.Caching.Memory.Tests.exe
30,107,13630,101,504-5,632-0.019%Microsoft.Extensions.DependencyModel.Tests.exe
10,508,28810,506,240-2,048-0.019%System.Net.Http.WinHttpHandler.Unit.Tests.exe
8,563,2008,561,664-1,536-0.018%System.Diagnostics.Contracts.Tests.exe
8,642,0488,640,512-1,536-0.018%System.Net.WebHeaderCollection.Tests.exe
8,770,0488,768,512-1,536-0.018%System.IO.FileSystem.DriveInfo.Tests.exe
12,285,95212,283,904-2,048-0.017%System.Reflection.Metadata.Tests.exe
8,989,6968,988,160-1,536-0.017%System.Net.Primitives.Pal.Tests.exe
33,161,21633,155,584-5,632-0.017%System.Private.Xml.Tests.exe
9,454,0809,452,544-1,536-0.016%Microsoft.Extensions.Logging.Testing.Tests.exe
9,630,7209,629,184-1,536-0.016%System.Composition.Convention.Tests.exe
9,314,8169,313,280-1,536-0.016%System.Net.Primitives.UnitTests.Tests.exe
11,484,16011,482,624-1,536-0.013%System.IO.Ports.Tests.exe
8,601,6008,600,576-1,024-0.012%Microsoft.Extensions.Hosting.Abstractions.Tests.exe
8,720,3848,719,360-1,024-0.012%System.Private.Uri.Unit.Tests.exe
8,546,8168,545,792-1,024-0.012%System.Runtime.CompilerServices.VisualC.Tests.exe
8,653,3128,652,288-1,024-0.012%System.Globalization.CalendarsWithConfigSwitch.Tests.exe
8,836,6088,835,584-1,024-0.012%System.Console.Manual.Tests.exe
9,364,4809,363,456-1,024-0.011%System.Reflection.Context.Tests.exe
9,041,4089,040,384-1,024-0.011%System.Net.NameResolution.Pal.Tests.exe
9,646,5929,645,568-1,024-0.011%System.Private.Uri.Functional.Tests.exe
9,704,4489,703,424-1,024-0.011%System.Text.RegularExpressions.Unit.Tests.exe
9,206,2729,205,248-1,024-0.011%Microsoft.Extensions.Configuration.EnvironmentVariables.Tests.exe
9,004,0329,003,008-1,024-0.011%System.Runtime.Serialization.Primitives.Tests.exe
9,056,2569,055,232-1,024-0.011%Microsoft.Extensions.Configuration.CommandLine.Tests.exe
9,216,5129,215,488-1,024-0.011%System.ServiceProcess.ServiceController.Tests.exe
9,539,5849,538,560-1,024-0.011%System.IO.Hashing.Tests.exe
9,671,1689,670,144-1,024-0.011%Microsoft.Extensions.Hosting.Functional.Tests.exe
4,940,2884,939,776-512-0.010%coreclr.dll
10,935,80810,934,784-1,024-0.009%System.Runtime.Intrinsics.Tests.exe
11,610,11211,609,088-1,024-0.009%System.Resources.Extensions.Tests.exe
11,193,85611,192,832-1,024-0.009%System.Text.Encodings.Web.Tests.exe
12,287,48812,286,464-1,024-0.008%System.Net.NetworkInformation.Functional.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Nls.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Tests.exe
9,039,3609,038,848-512-0.006%Microsoft.Extensions.FileSystemGlobbing.Tests.exe
8,558,0808,557,568-512-0.006%System.Text.Encoding.Extensions.Tests.exe
8,704,5128,704,000-512-0.006%System.Security.SecureString.Tests.exe
8,701,4408,700,928-512-0.006%System.Security.Cryptography.ProtectedData.Tests.exe
8,688,1288,687,616-512-0.006%System.Runtime.InteropServices.ComDisabled.Tests.exe
9,143,8089,143,296-512-0.006%System.Security.AccessControl.Tests.exe
9,025,5369,025,024-512-0.006%System.Reflection.TypeExtensions.Tests.exe
8,812,0328,811,520-512-0.006%System.Resources.Reader.Tests.exe
9,083,3929,082,880-512-0.006%System.ValueTuple.Tests.exe
8,606,2088,605,696-512-0.006%System.Resources.Writer.Tests.exe
8,817,6648,817,152-512-0.006%System.Web.HttpUtility.Tests.exe
8,863,2328,862,720-512-0.006%System.Reflection.Extensions.Tests.exe
8,670,2088,669,696-512-0.006%System.Runtime.Handles.Tests.exe
9,165,8249,165,312-512-0.006%System.Globalization.Calendars.Tests.exe
8,543,7448,543,232-512-0.006%System.IO.FileSystem.Primitives.Tests.exe
8,592,3848,591,872-512-0.006%Microsoft.Bcl.Numerics.Tests.exe
8,952,8328,952,320-512-0.006%Microsoft.Bcl.Memory.Tests.exe
8,717,3128,716,800-512-0.006%System.Diagnostics.FileVersionInfo.Tests.exe
8,700,4168,699,904-512-0.006%System.IO.FileSystem.Manual.Tests.exe
8,929,7928,929,280-512-0.006%System.IO.Pipes.AccessControl.Tests.exe
8,624,1288,623,616-512-0.006%System.Composition.Runtime.Tests.exe
9,108,9929,108,480-512-0.006%System.IO.FileSystem.AccessControl.Tests.exe
8,559,1048,558,592-512-0.006%System.Composition.AttributeModel.Tests.exe
8,759,2968,758,784-512-0.006%Invariant.Tests.exe
8,547,8408,547,328-512-0.006%System.ComponentModel.Tests.exe
9,161,7289,161,216-512-0.006%System.ComponentModel.Primitives.Tests.exe
8,959,4888,958,976-512-0.006%System.Threading.AccessControl.Tests.exe
8,991,2328,990,720-512-0.006%Microsoft.Win32.Registry.Tests.exe
8,647,6808,647,168-512-0.006%Microsoft.Win32.Registry.AccessControl.Tests.exe
8,549,3768,548,864-512-0.006%System.Diagnostics.Tools.Tests.exe
8,664,5768,664,064-512-0.006%Microsoft.Win32.Primitives.Tests.exe
9,750,0169,749,504-512-0.005%System.Collections.Specialized.Tests.exe
22,340,60822,339,584-1,024-0.005%System.Security.Cryptography.Pkcs.Tests.exe
9,852,9289,852,416-512-0.005%System.Formats.Nrbf.Tests.exe
9,763,3289,762,816-512-0.005%System.Formats.Asn1.Tests.exe
9,361,4089,360,896-512-0.005%System.Security.Permissions.Tests.exe
11,048,44811,047,936-512-0.005%System.Runtime.Numerics.Tests.exe
9,627,1369,626,624-512-0.005%System.Diagnostics.EventLog.Tests.exe
18,779,13618,778,112-1,024-0.005%System.Numerics.Vectors.Tests.exe
9,746,4329,745,920-512-0.005%System.Threading.RateLimiting.Tests.exe
9,961,4729,960,960-512-0.005%LibraryImportGenerator.Tests.exe
9,949,6969,949,184-512-0.005%System.Collections.NonGeneric.Tests.exe
9,486,8489,486,336-512-0.005%Microsoft.Extensions.Configuration.Ini.Tests.exe
13,553,66413,554,176+512+0.004%crossgen2.exe
11,796,48011,795,968-512-0.004%System.Security.Cryptography.Cose.Tests.exe
11,381,24811,380,736-512-0.004%System.Net.Security.Unit.Tests.exe
15,545,85615,545,344-512-0.003%System.Linq.Tests.exe
17,838,59217,838,080-512-0.003%System.Collections.Tests.exe

CopilotAI review requested due to automatic review settings May 13, 2026 09:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch
jakobbotsch merged commit d9f57ab into dotnet:mainMay 18, 2026
155 of 157 checks passed
@jakobbotsch
jakobbotsch deleted the cache-value-task-source-continuation branch May 18, 2026 07:31
VSadov added a commit that referenced this pull request May 21, 2026
…ce (#128399)
Same idea as in #127973, but applied when the actual Await is used (not
a thunk).
With the original repro that I was looking at, merging of #127973 helped
to reduce Gen0 GCs from ~ 15/sec to ~ 12/sec.
Alocations went down but not completely, because of code like the
following piece in the `DoReceive()` loop and possibly in other places.
This pattern still churns continuations:
https://github.com/dotnet/aspnetcore/blob/c0c2230799a3f876aa673a66bc008bf9b803acac/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs#L173-L182
```cs
var flushTask = Input.FlushAsync();
var paused = !flushTask.IsCompleted;
if (paused)
{
SocketsLog.ConnectionPause(_logger, this);
}
var result = await flushTask;
```
Because `flushTask` is not a method, the `await` does not go through a
thunk which could reuse the continuation. We call the actual `Await` in
this case and it suspends/allocates.
The change in this PR uses the same approach as in the thunk, but
applied to the Await, when awaiting a ValueTaskSource.
With this change we get to ~ 2 Gen0/sec in the repro - on par with
net10.
tommcdon added a commit that referenced this pull request May 23, 2026
…128496)
CordbAsyncStackWalk::PopulateFrame() crashes when encountering a
continuation whose ResumeInfo.DiagnosticIP is NULL (e.g. the new
ValueTaskContinuation introduced in #127973). GetNativeCodeInfoForAddr
is called with a null address which fails.
Fix: In PopulateFrame(), skip continuations with diagnosticIP == NULL
the same way DiagnosticHidden frames are skipped (advance to Next).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jakobbotsch added a commit that referenced this pull request May 26, 2026
Apply a similar optimization as #127973 to task returning thunks.
This is a very minor throughput optimization, but it also removes the
suspension point from all these thunks, which makes each of these thunks
smaller.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 17, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jakobbotsch@VSadov@lateralusX
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Cache continuation used for runtime async callable value task thunks - #127973

Merged
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation
May 18, 2026
Merged

Cache continuation used for runtime async callable value task thunks#127973
jakobbotsch merged 13 commits into
dotnet:mainfrom
jakobbotsch:cache-value-task-source-continuation

Conversation

@jakobbotsch

Copy link
Copy Markdown
Member

ValueTask backed by IValueTaskSource can suspend without allocating. However, runtime async did not support this when calling a ValueTask returning method through a runtime async callable thunk. In that case we would always allocate in the case where the ValueTask suspended.

This adds a custom ValueTaskContinuation that replaces the existing ValueTaskSourceNotifier mechanism. We now cache a ValueTaskContinuation instance and reuse it if possible whenever suspending for a ValueTask.

The same approach could be used to avoid allocations for runtime async callable thunks for task-returning methods.

cc @VSadov

`ValueTask` backed by `IValueTaskSource` can suspend without allocating.
However, runtime async did not support this when calling a `ValueTask`
returning method through a runtime async callable thunk. In that case we
would always allocate in the case where the `ValueTask` suspended.
This adds a custom `ValueTaskContinuation` that replaces the existing
`ValueTaskSourceNotifier` mechanism. We now cache a
`ValueTaskContinuation` instance and reuse it if possible whenever
suspending for a `ValueTask`.
The same approach could be used to avoid allocations for runtime async
callable thunks for task-returning methods.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 updates CoreCLR’s runtime-async callable thunk path for ValueTask/ValueTask<T> so that suspending on ValueTask backed by IValueTaskSource can reuse a cached continuation object rather than allocating per-suspension, and removes the older ValueTaskSourceNotifier / AsTaskOrNotifier mechanism.

Changes:

  • Introduces AsyncHelpers.TransparentAwaitValueTask* and a ValueTaskContinuation type that integrates with runtime-async suspension/resumption and is cached in TLS for reuse.
  • Removes ValueTask.AsTaskOrNotifier and the ValueTaskSourceNotifier helper type from ValueTask.
  • Adjusts VM continuation type handling to distinguish continuation subtypes that do have metadata (managed subclasses) vs the dynamically-created metadata-less ones, including DAC support.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.csRemoves AsTaskOrNotifier and deletes ValueTaskSourceNotifier.
src/coreclr/vm/vars.hppAdds global g_singletonContinuationEEClass declaration for continuation metadata detection.
src/coreclr/vm/vars.cppAdds global g_singletonContinuationEEClass definition.
src/coreclr/vm/typehandle.inlAvoids “upcasting” continuations that have real metadata.
src/coreclr/vm/typehandle.hAdds TypeHandle::IsContinuationWithMetadata().
src/coreclr/vm/typehandle.cppImplements TypeHandle::IsContinuationWithMetadata() and relaxes class-object allocation restriction for metadata continuations.
src/coreclr/vm/methodtable.hAdds MethodTable::IsContinuationWithMetadata() and relaxes asserts/preconditions for metadata continuations.
src/coreclr/vm/methodtable.cppImplements MethodTable::IsContinuationWithMetadata() and updates continuation-related special-casing.
src/coreclr/vm/corelib.hRemoves binder entries for ValueTask.AsTaskOrNotifier; adds binder entries for TransparentAwaitValueTask*.
src/coreclr/vm/asyncthunks.cppEmits thunk IL that tail-awaits via TransparentAwaitValueTask* instead of AsTaskOrNotifier + TransparentAwait.
src/coreclr/vm/asynccontinuations.cppMoves singleton continuation EEClass storage to global g_singletonContinuationEEClass.
src/coreclr/tools/Common/TypeSystem/IL/Stubs/AsyncThunks.csUpdates managed type-system thunk emission to call TransparentAwaitValueTask* and TailAwait.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdds ValueTaskContinuation, TLS caching, and TransparentAwaitValueTask*; updates suspension handling to use it.
src/coreclr/inc/dacvars.hExposes g_singletonContinuationEEClass to DAC.
src/coreclr/debug/daccess/dacdbiimpl.cppUpdates continuation canonical-MT validity logic to account for metadata continuations.

Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/methodtable.h Outdated
Comment threadsrc/coreclr/vm/asyncthunks.cpp
@VSadov

Copy link
Copy Markdown
Member

Nice! Instead of caching and reusing ValueTaskSourceNotifier, we now cache/reuse the entire head continuation.

A scenario that awaits in a loop some ValueTaskSource wrapper (like reading chunks from a pipe) can run allocation-free.

@jakobbotsch

jakobbotsch commented May 10, 2026

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

Also, we probably need to introduce a runtime async mechanism similar to PoolingAsyncValueTaskMethodBuilder -- e.g. apply [PoolContinuations] on a runtime async method and it would cache continuations for that method following that scheme too.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 21 out of 21 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs:1

  • The XML doc block still describes awaiting ValueTask, but TransparentAwait now accepts Task only, while ValueTask awaiting is handled by TransparentAwaitValueTask* above. Please update/move this comment so TransparentAwait’s docs describe only the Task scenario, and place the ValueTask-specific explanation on TransparentAwaitValueTask / TransparentAwaitValueTaskOfT.
// Licensed to the .NET Foundation under one or more agreements.

Comment threadsrc/coreclr/vm/methodtable.cpp
@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

I think I will use the same caching scheme as in #55955, which in addition to the TLS cached object also has a per-core cache.

I think I will do that separately. Right now the caching is essentially free since we can store it in the runtime async TLS that we already needed to access. If we introduce the per-core cache we will be paying more when the TLS cache doesn't hit, so I want to make sure I have some benchmarks for this.

@VSadov

Copy link
Copy Markdown
Member

I think I will do that separately

Per thread caching of 1 head continuation is probably the most effective as we reduce necessary allocations for that scenario to none.

Additional caching may cost more while having less impact. It makes sense to look at that separately.

@jakobbotsch
jakobbotsch marked this pull request as ready for review May 12, 2026 22:05
CopilotAI review requested due to automatic review settings May 12, 2026 22:05

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch

Copy link
Copy Markdown
MemberAuthor

Also some decent size improvements for NativeAOT since the thunks no longer need a suspension/resumption point:

BaseSizeDiffSizeDeltaPercentImprovedName
16,852,99216,716,288-136,704-0.811%System.Linq.AsyncEnumerable.Tests.exe
115,198,464115,079,168-119,296-0.104%System.Text.Json.SourceGeneration.Roslyn4.4.Tests.exe
10,291,20010,282,496-8,704-0.085%System.Threading.Channels.Tests.exe
12,003,32811,993,088-10,240-0.085%System.IO.Tests.exe
19,561,47219,545,088-16,384-0.084%System.IO.Compression.Tests.exe
14,579,71214,568,960-10,752-0.074%System.Net.WebClient.Tests.exe
20,044,28820,029,952-14,336-0.072%System.Formats.Tar.Tests.exe
45,556,22445,524,992-31,232-0.069%System.Security.Cryptography.Tests.exe
21,715,45621,700,608-14,848-0.068%System.IO.FileSystem.Tests.exe
18,455,55218,443,264-12,288-0.067%System.Threading.Tasks.Parallel.Tests.exe
18,526,20818,513,920-12,288-0.066%System.IO.MemoryMappedFiles.Tests.exe
10,067,45610,060,800-6,656-0.066%System.Net.ServerSentEvents.Tests.exe
17,734,14417,722,368-11,776-0.066%System.Threading.Tasks.Extensions.Tests.exe
15,771,64815,761,408-10,240-0.065%System.Windows.Extensions.Tests.exe
19,296,76819,284,480-12,288-0.064%System.IO.Pipes.Tests.exe
11,286,01611,278,848-7,168-0.064%System.IO.Compression.Brotli.Tests.exe
19,570,17619,557,888-12,288-0.063%System.Diagnostics.Process.Tests.exe
20,273,66420,260,864-12,800-0.063%System.Threading.Tasks.Dataflow.Tests.exe
25,897,98425,881,600-16,384-0.063%System.Text.Json.SourceGeneration.Roslyn3.11.Tests.exe
11,343,36011,336,192-7,168-0.063%System.Net.WebSockets.Tests.exe
10,675,20010,668,544-6,656-0.062%System.IO.Pipelines.Tests.exe
13,250,56013,242,368-8,192-0.062%System.IO.Packaging.Tests.exe
20,009,98419,997,696-12,288-0.061%System.Net.WebSockets.Client.Tests.exe
31,281,66431,263,232-18,432-0.059%System.Net.Http.Functional.Tests.exe
20,942,84820,930,560-12,288-0.059%System.Net.Quic.Functional.Tests.exe
17,798,65617,788,416-10,240-0.058%System.Formats.Tar.Manual.Tests.exe
23,142,91223,129,600-13,312-0.058%System.Threading.Tasks.Tests.exe
18,947,58418,936,832-10,752-0.057%System.Runtime.Caching.Tests.exe
17,168,89617,159,168-9,728-0.057%System.Runtime.Serialization.Formatters.Disabled.Tests.exe
17,439,23217,429,504-9,728-0.056%System.Diagnostics.TraceSource.Tests.exe
17,223,16817,213,952-9,216-0.054%System.Net.ServicePoint.Tests.exe
18,070,52818,060,800-9,728-0.054%System.Console.Tests.exe
17,975,29617,965,568-9,728-0.054%System.Threading.ThreadPool.Tests.exe
18,137,60018,127,872-9,728-0.054%System.Diagnostics.TextWriterTraceListener.Tests.exe
18,380,80018,371,072-9,728-0.053%System.Threading.Tests.exe
17,296,89617,287,680-9,216-0.053%System.Runtime.InteropServices.RuntimeInformation.Tests.exe
17,438,72017,429,504-9,216-0.053%System.Net.Ping.Functional.Tests.exe
17,244,16017,234,944-9,216-0.053%System.Runtime.InvariantTimezone.Tests.exe
17,290,24017,281,024-9,216-0.053%System.Threading.Overlapped.Tests.exe
17,363,96817,354,752-9,216-0.053%Microsoft.Win32.SystemEvents.Tests.exe
17,306,11217,296,896-9,216-0.053%System.Security.Claims.Tests.exe
18,594,81618,585,088-9,728-0.052%ComInterfaceGenerator.Tests.exe
18,692,60818,682,880-9,728-0.052%Microsoft.Extensions.Logging.Console.Tests.exe
17,578,49617,569,280-9,216-0.052%MetricOuterLoop1.Tests.exe
17,573,88817,564,672-9,216-0.052%MetricOuterLoop.Tests.exe
19,688,96019,678,720-10,240-0.052%System.Net.Http.Unit.Tests.exe
17,975,29617,966,080-9,216-0.051%System.Threading.ThreadPool.WindowsThreadPool.Tests.exe
28,039,16828,024,832-14,336-0.051%System.Net.Security.Tests.exe
22,881,28022,869,504-11,776-0.051%System.Net.Sockets.Tests.exe
19,059,20019,049,472-9,728-0.051%System.Net.HttpListener.Tests.exe
18,040,83218,031,616-9,216-0.051%Microsoft.Extensions.Diagnostics.Tests.exe
17,238,52817,229,824-8,704-0.050%System.ComponentModel.EventBasedAsync.Tests.exe
17,369,08817,360,384-8,704-0.050%System.Diagnostics.StackTrace.Tests.exe
18,496,51218,487,296-9,216-0.050%Microsoft.Extensions.Hosting.Systemd.Tests.exe
17,320,44817,311,744-8,704-0.050%System.Security.Principal.Windows.Tests.exe
17,337,34417,328,640-8,704-0.050%System.Threading.Timer.Tests.exe
17,668,09617,659,392-8,704-0.049%System.IO.FileSystem.Watcher.Tests.exe
17,624,57617,615,872-8,704-0.049%System.Threading.Thread.Tests.exe
19,876,86419,867,136-9,728-0.049%System.Text.Encoding.Tests.exe
17,670,65617,661,952-8,704-0.049%System.Buffers.Tests.exe
19,401,21619,392,000-9,216-0.048%System.Net.Primitives.Functional.Tests.exe
17,161,72817,153,536-8,192-0.048%IcuAppLocal.Tests.exe
18,184,70418,176,000-8,704-0.048%System.Text.Encoding.CodePages.Tests.exe
10,061,82410,057,216-4,608-0.046%System.IO.Compression.ZipFile.Tests.exe
19,087,87219,079,168-8,704-0.046%Microsoft.Extensions.Hosting.WindowsServices.Tests.exe
21,631,48821,621,760-9,728-0.045%System.Data.OleDb.Tests.exe
20,581,37620,572,160-9,216-0.045%System.Diagnostics.DiagnosticSource.Tests.exe
13,877,76013,871,616-6,144-0.044%Microsoft.Extensions.Configuration.Functional.Tests.exe
19,752,96019,744,256-8,704-0.044%System.Resources.ResourceManager.Tests.exe
20,748,80020,739,584-9,216-0.044%System.Collections.Concurrent.Tests.exe
20,463,10420,454,400-8,704-0.043%System.Reflection.Tests.exe
20,241,40820,232,704-8,704-0.043%System.Runtime.InteropServices.Tests.exe
20,940,80020,932,096-8,704-0.042%System.Runtime.Extensions.Tests.exe
12,425,72812,420,608-5,120-0.041%System.Xml.Linq.SDMSample.Tests.exe
21,210,11221,201,408-8,704-0.041%System.Net.Requests.Tests.exe
29,510,65629,498,880-11,776-0.040%System.Net.Http.WinHttpHandler.Functional.Tests.exe
12,858,88012,853,760-5,120-0.040%System.Xml.Schema.Extensions.Tests.exe
24,283,13624,273,408-9,728-0.040%Microsoft.Extensions.Http.Tests.exe
14,142,46414,136,832-5,632-0.040%System.DirectoryServices.Protocols.Tests.exe
10,599,42410,595,328-4,096-0.039%Common.Tests.exe
9,264,1289,260,544-3,584-0.039%Microsoft.Bcl.AsyncInterfaces.Tests.exe
14,903,80814,898,176-5,632-0.038%System.Xml.Linq.Streaming.Tests.exe
13,953,53613,948,416-5,120-0.037%Microsoft.Extensions.Configuration.Xml.Tests.exe
15,094,78415,089,152-5,632-0.037%System.Security.Cryptography.Cng.Tests.exe
23,411,71223,403,008-8,704-0.037%System.Memory.Tests.exe
27,287,04027,277,312-9,728-0.036%Microsoft.Extensions.Hosting.Unit.Tests.exe
16,117,24816,111,616-5,632-0.035%System.Data.Odbc.Tests.exe
26,646,52826,637,312-9,216-0.035%Microsoft.Extensions.Logging.EventSource.Tests.exe
26,441,21626,432,000-9,216-0.035%System.ComponentModel.TypeConverter.Tests.exe
16,525,31216,519,680-5,632-0.034%System.Xml.Linq.Events.Tests.exe
16,778,75216,773,120-5,632-0.034%System.Xml.Linq.Misc.Tests.exe
26,103,80826,095,104-8,704-0.033%System.Globalization.Tests.exe
26,014,72026,006,016-8,704-0.033%System.Globalization.Nls.Tests.exe
17,009,66417,004,032-5,632-0.033%System.Xml.Linq.xNodeReader.Tests.exe
9,675,2649,672,192-3,072-0.032%System.IO.UnmanagedMemoryStream.Tests.exe
15,048,19215,043,584-4,608-0.031%System.Data.DataSetExtensions.Tests.exe
16,673,28016,668,160-5,120-0.031%System.Xml.Linq.Properties.Tests.exe
11,599,36011,595,776-3,584-0.031%System.Net.Http.Json.Unit.Tests.exe
17,065,98417,060,864-5,120-0.030%System.Xml.Linq.xNodeBuilder.Tests.exe
20,819,45620,813,312-6,144-0.030%System.Security.Cryptography.Csp.Tests.exe
17,123,84017,118,720-5,120-0.030%System.Xml.Linq.TreeManipulation.Tests.exe
33,400,83233,391,104-9,728-0.029%Microsoft.Bcl.Cryptography.Tests.exe
18,455,04018,449,920-5,120-0.028%Microsoft.Extensions.Configuration.FileExtensions.Tests.exe
32,762,36832,753,152-9,216-0.028%System.Linq.Expressions.Tests.exe
15,110,65615,106,560-4,096-0.027%System.Drawing.Primitives.Tests.exe
18,381,31218,376,704-4,608-0.025%Microsoft.Extensions.Primitives.Tests.exe
20,099,58420,094,464-5,120-0.025%Microsoft.Extensions.Configuration.Json.Tests.exe
10,521,60010,519,040-2,560-0.024%Microsoft.Extensions.Options.Tests.exe
16,973,82416,977,920+4,096+0.024%System.Private.CoreLib.dll
20,038,65620,034,048-4,608-0.023%Microsoft.Extensions.Configuration.UserSecrets.Tests.exe
19,972,09619,967,488-4,608-0.023%Microsoft.Extensions.Logging.Tests.exe
9,351,1689,349,120-2,048-0.022%System.IO.IsolatedStorage.Tests.exe
9,376,2569,374,208-2,048-0.022%System.Net.Mail.Unit.Tests.exe
18,356,22418,352,128-4,096-0.022%Microsoft.Extensions.FileProviders.Composite.Tests.exe
44,814,33644,804,608-9,728-0.022%System.Runtime.Tests.exe
18,774,01618,769,920-4,096-0.022%Microsoft.Extensions.FileProviders.Physical.Tests.exe
18,491,90418,487,808-4,096-0.022%Microsoft.Extensions.Configuration.Tests.exe
9,346,5609,344,512-2,048-0.022%Microsoft.Extensions.Diagnostics.Abstractions.Tests.exe
10,118,65610,116,608-2,048-0.020%Microsoft.Extensions.Caching.Memory.Tests.exe
30,107,13630,101,504-5,632-0.019%Microsoft.Extensions.DependencyModel.Tests.exe
10,508,28810,506,240-2,048-0.019%System.Net.Http.WinHttpHandler.Unit.Tests.exe
8,563,2008,561,664-1,536-0.018%System.Diagnostics.Contracts.Tests.exe
8,642,0488,640,512-1,536-0.018%System.Net.WebHeaderCollection.Tests.exe
8,770,0488,768,512-1,536-0.018%System.IO.FileSystem.DriveInfo.Tests.exe
12,285,95212,283,904-2,048-0.017%System.Reflection.Metadata.Tests.exe
8,989,6968,988,160-1,536-0.017%System.Net.Primitives.Pal.Tests.exe
33,161,21633,155,584-5,632-0.017%System.Private.Xml.Tests.exe
9,454,0809,452,544-1,536-0.016%Microsoft.Extensions.Logging.Testing.Tests.exe
9,630,7209,629,184-1,536-0.016%System.Composition.Convention.Tests.exe
9,314,8169,313,280-1,536-0.016%System.Net.Primitives.UnitTests.Tests.exe
11,484,16011,482,624-1,536-0.013%System.IO.Ports.Tests.exe
8,601,6008,600,576-1,024-0.012%Microsoft.Extensions.Hosting.Abstractions.Tests.exe
8,720,3848,719,360-1,024-0.012%System.Private.Uri.Unit.Tests.exe
8,546,8168,545,792-1,024-0.012%System.Runtime.CompilerServices.VisualC.Tests.exe
8,653,3128,652,288-1,024-0.012%System.Globalization.CalendarsWithConfigSwitch.Tests.exe
8,836,6088,835,584-1,024-0.012%System.Console.Manual.Tests.exe
9,364,4809,363,456-1,024-0.011%System.Reflection.Context.Tests.exe
9,041,4089,040,384-1,024-0.011%System.Net.NameResolution.Pal.Tests.exe
9,646,5929,645,568-1,024-0.011%System.Private.Uri.Functional.Tests.exe
9,704,4489,703,424-1,024-0.011%System.Text.RegularExpressions.Unit.Tests.exe
9,206,2729,205,248-1,024-0.011%Microsoft.Extensions.Configuration.EnvironmentVariables.Tests.exe
9,004,0329,003,008-1,024-0.011%System.Runtime.Serialization.Primitives.Tests.exe
9,056,2569,055,232-1,024-0.011%Microsoft.Extensions.Configuration.CommandLine.Tests.exe
9,216,5129,215,488-1,024-0.011%System.ServiceProcess.ServiceController.Tests.exe
9,539,5849,538,560-1,024-0.011%System.IO.Hashing.Tests.exe
9,671,1689,670,144-1,024-0.011%Microsoft.Extensions.Hosting.Functional.Tests.exe
4,940,2884,939,776-512-0.010%coreclr.dll
10,935,80810,934,784-1,024-0.009%System.Runtime.Intrinsics.Tests.exe
11,610,11211,609,088-1,024-0.009%System.Resources.Extensions.Tests.exe
11,193,85611,192,832-1,024-0.009%System.Text.Encodings.Web.Tests.exe
12,287,48812,286,464-1,024-0.008%System.Net.NetworkInformation.Functional.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Nls.Tests.exe
15,399,93615,398,912-1,024-0.007%System.Globalization.Extensions.Tests.exe
9,039,3609,038,848-512-0.006%Microsoft.Extensions.FileSystemGlobbing.Tests.exe
8,558,0808,557,568-512-0.006%System.Text.Encoding.Extensions.Tests.exe
8,704,5128,704,000-512-0.006%System.Security.SecureString.Tests.exe
8,701,4408,700,928-512-0.006%System.Security.Cryptography.ProtectedData.Tests.exe
8,688,1288,687,616-512-0.006%System.Runtime.InteropServices.ComDisabled.Tests.exe
9,143,8089,143,296-512-0.006%System.Security.AccessControl.Tests.exe
9,025,5369,025,024-512-0.006%System.Reflection.TypeExtensions.Tests.exe
8,812,0328,811,520-512-0.006%System.Resources.Reader.Tests.exe
9,083,3929,082,880-512-0.006%System.ValueTuple.Tests.exe
8,606,2088,605,696-512-0.006%System.Resources.Writer.Tests.exe
8,817,6648,817,152-512-0.006%System.Web.HttpUtility.Tests.exe
8,863,2328,862,720-512-0.006%System.Reflection.Extensions.Tests.exe
8,670,2088,669,696-512-0.006%System.Runtime.Handles.Tests.exe
9,165,8249,165,312-512-0.006%System.Globalization.Calendars.Tests.exe
8,543,7448,543,232-512-0.006%System.IO.FileSystem.Primitives.Tests.exe
8,592,3848,591,872-512-0.006%Microsoft.Bcl.Numerics.Tests.exe
8,952,8328,952,320-512-0.006%Microsoft.Bcl.Memory.Tests.exe
8,717,3128,716,800-512-0.006%System.Diagnostics.FileVersionInfo.Tests.exe
8,700,4168,699,904-512-0.006%System.IO.FileSystem.Manual.Tests.exe
8,929,7928,929,280-512-0.006%System.IO.Pipes.AccessControl.Tests.exe
8,624,1288,623,616-512-0.006%System.Composition.Runtime.Tests.exe
9,108,9929,108,480-512-0.006%System.IO.FileSystem.AccessControl.Tests.exe
8,559,1048,558,592-512-0.006%System.Composition.AttributeModel.Tests.exe
8,759,2968,758,784-512-0.006%Invariant.Tests.exe
8,547,8408,547,328-512-0.006%System.ComponentModel.Tests.exe
9,161,7289,161,216-512-0.006%System.ComponentModel.Primitives.Tests.exe
8,959,4888,958,976-512-0.006%System.Threading.AccessControl.Tests.exe
8,991,2328,990,720-512-0.006%Microsoft.Win32.Registry.Tests.exe
8,647,6808,647,168-512-0.006%Microsoft.Win32.Registry.AccessControl.Tests.exe
8,549,3768,548,864-512-0.006%System.Diagnostics.Tools.Tests.exe
8,664,5768,664,064-512-0.006%Microsoft.Win32.Primitives.Tests.exe
9,750,0169,749,504-512-0.005%System.Collections.Specialized.Tests.exe
22,340,60822,339,584-1,024-0.005%System.Security.Cryptography.Pkcs.Tests.exe
9,852,9289,852,416-512-0.005%System.Formats.Nrbf.Tests.exe
9,763,3289,762,816-512-0.005%System.Formats.Asn1.Tests.exe
9,361,4089,360,896-512-0.005%System.Security.Permissions.Tests.exe
11,048,44811,047,936-512-0.005%System.Runtime.Numerics.Tests.exe
9,627,1369,626,624-512-0.005%System.Diagnostics.EventLog.Tests.exe
18,779,13618,778,112-1,024-0.005%System.Numerics.Vectors.Tests.exe
9,746,4329,745,920-512-0.005%System.Threading.RateLimiting.Tests.exe
9,961,4729,960,960-512-0.005%LibraryImportGenerator.Tests.exe
9,949,6969,949,184-512-0.005%System.Collections.NonGeneric.Tests.exe
9,486,8489,486,336-512-0.005%Microsoft.Extensions.Configuration.Ini.Tests.exe
13,553,66413,554,176+512+0.004%crossgen2.exe
11,796,48011,795,968-512-0.004%System.Security.Cryptography.Cose.Tests.exe
11,381,24811,380,736-512-0.004%System.Net.Security.Unit.Tests.exe
15,545,85615,545,344-512-0.003%System.Linq.Tests.exe
17,838,59217,838,080-512-0.003%System.Collections.Tests.exe

CopilotAI review requested due to automatic review settings May 13, 2026 09:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 23 out of 23 changed files in this pull request and generated 2 comments.

@jakobbotsch
jakobbotsch merged commit d9f57ab into dotnet:mainMay 18, 2026
155 of 157 checks passed
@jakobbotsch
jakobbotsch deleted the cache-value-task-source-continuation branch May 18, 2026 07:31
VSadov added a commit that referenced this pull request May 21, 2026
…ce (#128399)
Same idea as in #127973, but applied when the actual Await is used (not
a thunk).
With the original repro that I was looking at, merging of #127973 helped
to reduce Gen0 GCs from ~ 15/sec to ~ 12/sec.
Alocations went down but not completely, because of code like the
following piece in the `DoReceive()` loop and possibly in other places.
This pattern still churns continuations:
https://github.com/dotnet/aspnetcore/blob/c0c2230799a3f876aa673a66bc008bf9b803acac/src/Servers/Kestrel/Transport.Sockets/src/Internal/SocketConnection.cs#L173-L182
```cs
var flushTask = Input.FlushAsync();
var paused = !flushTask.IsCompleted;
if (paused)
{
SocketsLog.ConnectionPause(_logger, this);
}
var result = await flushTask;
```
Because `flushTask` is not a method, the `await` does not go through a
thunk which could reuse the continuation. We call the actual `Await` in
this case and it suspends/allocates.
The change in this PR uses the same approach as in the thunk, but
applied to the Await, when awaiting a ValueTaskSource.
With this change we get to ~ 2 Gen0/sec in the repro - on par with
net10.
tommcdon added a commit that referenced this pull request May 23, 2026
…128496)
CordbAsyncStackWalk::PopulateFrame() crashes when encountering a
continuation whose ResumeInfo.DiagnosticIP is NULL (e.g. the new
ValueTaskContinuation introduced in #127973). GetNativeCodeInfoForAddr
is called with a null address which fails.
Fix: In PopulateFrame(), skip continuations with diagnosticIP == NULL
the same way DiagnosticHidden frames are skipped (advance to Next).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jakobbotsch added a commit that referenced this pull request May 26, 2026
Apply a similar optimization as #127973 to task returning thunks.
This is a very minor throughput optimization, but it also removes the
suspension point from all these thunks, which makes each of these thunks
smaller.
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jun 17, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jakobbotsch@VSadov@lateralusX