Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[RuntimeAsync] Enable runtime async in Libraries partition#119432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Draft
VSadov wants to merge3 commits intodotnet:main
base:main
Choose a base branch
Loading
fromVSadov:asyncLibs

Conversation

@VSadov
Copy link
Member

@VSadovVSadov commentedSep 6, 2025
edited
Loading

Mostly to see what happens and how far we can get with this.

am11 reacted with hooray emoji
@dotnet-policy-servicedotnet-policy-servicebot added the linkable-frameworkIssues associated with delivering a linker friendly framework labelSep 6, 2025
@dotnet-policy-service
Copy link
Contributor

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

@VSadov
Copy link
MemberAuthor

CC:@jakobbotsch@agocke

@VSadovVSadov added the NO-MERGEThe PR is not ready for merge yet (see discussion for detailed reasons) labelSep 6, 2025
@VSadov
Copy link
MemberAuthor

VSadov commentedSep 6, 2025
edited
Loading

A lot of tests actually run and pass when I run this locally. More than I expected. :-)

agocke, En3Tho, and GerardSmit reacted with hooray emoji

@VSadov
Copy link
MemberAuthor

VSadov commentedSep 6, 2025
edited
Loading

Some JIT asserts:
(could be Roslyn NYIs, could be JIT issues)

  Assert failure(PID 49480 [0x0000c148], Thread: 48504 [0xbd78]): Assertion failed 'lastBlockILEndOffset < beginOffs' in 'System.Net.Tests.HttpListenerWebSocketTests:ReceiveAsync_ReadBuffer_WithWindowsAuthScheme_Success():this' during 'Generate code' (IL size 302; hash 0xef07e726; Tier0)      File: E:\dotnet004\runtime\src\coreclr\jit\scopeinfo.cpp:1566      Image: E:\dotnet004\runtime\artifacts\bin\testhost\net10.0-windows-Release-x64\dotnet.exe
  Assert failure(PID 62300 [0x0000f35c], Thread: 12816 [0x3210]): Assertion failed '!varTypeIsStruct(node) && !varTypeIsStruct(type)' in 'System.IO.Stream:ReadAsync(System.Memory`1[byte],System.Threading.CancellationToken):int:this' during 'Importation' (IL size 40; hash 0xaffe5a9c; FullOpts)      File: E:\dotnet004\runtime\src\coreclr\jit\gentree.h:4710      Image: E:\dotnet004\runtime\artifacts\bin\testhost\net10.0-windows-Release-x64\dotnet.exe

@VSadov
Copy link
MemberAuthor

VSadov commentedSep 6, 2025
edited
Loading

Something odd is going on with sync context in a few cases:

System.Net.Http.Functional.Tests.PlatformHandler_HttpClientHandler_Asynchrony_Test.ResponseHeadersRead_SynchronizationContextNotUsedByHandler(responseHeadersRead: False, contentMode: BytePerChunk) [FAIL]        Sync Ctx used:    at System.Environment.get_StackTrace() in E:\dotnet004\runtime\src\libraries\System.Private.CoreLib\src\System\Environment.cs:line 239           at System.Threading.Tests.TrackingSynchronizationContext.Post(SendOrPostCallback d, Object state)           at System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask) in E:\dotnet004\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\Tasks\TaskContinuation.cs:line 697           at System.Threading.Tasks.Task.RunContinuations(Object continuationObject) in E:\dotnet004\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\Tasks\Task.cs:line 3486           at System.Threading.Tasks.Task`1.TrySetResult(TResult result) in E:\dotnet004\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\Tasks\Future.cs:line 385           at System.Threading.Tasks.TaskCompletionSource`1.TrySetResult(TResult result) in E:\dotnet004\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\Tasks\TaskCompletionSource_T.cs:line 218           at System.Net.Http.WinHttpHandler.StartRequestAsync(WinHttpRequestState state) in E:\dotnet004\runtime\src\libraries\System.Net.Http.WinHttpHandler\src\System\Net\Http\WinHttpHandler.cs:line 1007           at System.Runtime.CompilerServices.AsyncHelpers.ThunkTaskCore.MoveNext[T,TOps](T task) in E:\dotnet004\runtime\src\coreclr\System.Private.CoreLib\src\System\Runtime\CompilerServices\AsyncHelpers.CoreCLR.cs:line 354           at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state) in E:\dotnet004\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\ExecutionContext.cs:line 264           at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) in E:\dotnet004\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\Tasks\Task.cs:line 2346           at System.Threading.ThreadPoolWorkQueue.Dispatch() in E:\dotnet004\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\ThreadPoolWorkQueue.cs:line 1154           at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart() in E:\dotnet004\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\PortableThreadPool.WorkerThread.cs:line 118           at System.Threading.Thread.StartCallback() in E:\dotnet004\runtime\src\coreclr\System.Private.CoreLib\src\System\Threading\Thread.CoreCLR.cs:line 104

@VSadov
Copy link
MemberAuthor

VSadov commentedSep 9, 2025
edited
Loading

I have a fix for the following assert:

  Assert failure(PID 7324 [0x00001c9c], Thread: 29684 [0x73f4]): Assertion failed 'callInfo.SaveAndRestoreSynchronizationContextField && (callInfo.SynchronizationContextLclNum != BAD_VAR_NUM)' in 'System.IO.Compression.Tests.ZipFile_Extract_Stream:ExtractToDirectoryRoundTrip(bool):this' during 'Transform async' (IL size 90; hash 0x550642b2; Tier0)      File: E:\dotnet004\runtime\src\coreclr\jit\async.cpp:1510      Image: E:\dotnet004\runtime\artifacts\bin\testhost\net10.0-windows-Release-x64\dotnet.exe

looking at others.

Comment on lines 962 to 968
// TODO: (async) we do not need to save ByRef-containing locals
// as by the spec an await turns them into zero-inited state.
// For now just store/restore as if there are no gc refs.
// This is mostly to handle the "fake" live-across-await byrefs
// in min-opts, since C#-compiled code by itself does not let
// byrefs be live across awaits.
unsigned objCount = layout->HasGCByRef() ?0 : layout->GetGCPtrCount();
Copy link
Member

Choose a reason for hiding this comment

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

We will need to figure out what is creating these LIR edges that are live across the await and stop doing that. This fix will replace assert with bad codegen instead.

Copy link
MemberAuthor

@VSadovVSadovSep 11, 2025
edited
Loading

Choose a reason for hiding this comment

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

I typically see the assert when compilingDebug code for Task-returning methods with ref-like parameters.
Ex:

       publicstaticTaskWhenAll(paramsReadOnlySpan<Task>tasks){

async
This method is not async, but it's thunk will be async and will also have a byref-like parameter.
The part that the edge lives across the await is likely a result ofDebug not tracking liveness precisely.

Once we have zeroing of byrefs that live across await, we may not need this.
This is not a fix. It is a workaround. - my goal is to get all Libraries tests pass or find something truly blocking.

Copy link
MemberAuthor

@VSadovVSadovSep 11, 2025
edited
Loading

Choose a reason for hiding this comment

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

Assuming that it is a result ofDebug emit, perhaps forcing thunks to always compile optimized might be an alternative workaround, if there is an easy way to force.

I just thought of this workaround first and it seems working well enough.

Copy link
Member

Choose a reason for hiding this comment

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

The part that the edge lives across the await is likely a result of Debug not tracking liveness precisely.

There are two sources of live state across async calls in JIT IR:

  1. Locals. These behave like IL locals and can be multiply defined and multiply used. They are what liveness analysis treat. They are added byAsyncLiveness::GetLiveLocals.
  2. LIR edges. These are single-def single-use (SDSU) values that are defined in one place in a basic block and consumed later in the same basic block. When they overlap an async call (defined before, used after) they must also be preserved on heap. Liveness analysis doesnot treat these; they are known to be live when they overlap the call. They are added byAsyncTransformation::LiftLIREdges.

The liveness imprecision only comes into play for (1). However, we already ignore byref locals for these. It happens here:

if ((dsc->TypeIs(TYP_BYREF) && !dsc->IsImplicitByRef()) ||
(dsc->TypeIs(TYP_STRUCT) && dsc->GetLayout()->HasGCByRef()))
{
// Even if these are address exposed we expect them to be dead at
// suspension points. TODO: It would be good to somehow verify these
// aren't obviously live, if the JIT creates live ranges that span a
// suspension point then this makes it quite hard to diagnose that.
returnfalse;
}

That just leaves (2). But since these are known to be live, it is (almost?) always going to be a bug that resulted in these.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Good point! I will log an issue on this - to follow up with real fix.

@VSadov
Copy link
MemberAuthor

I see only 26 failed tests now in Chk/Ret configuration on win-x64.

There are 23 cases ofAssertion failed 'lastBlockILEndOffset < beginOffs'
I will look at this next.

There are also 8 cases ofSync Ctx used: . . .
That could be harder to figure. I am not sure it is always deterministic.

Also fixing one thing sometimes exposes another, but so far with every fix/workaround I see more tests passing.

This was referencedSep 12, 2025
@VSadov
Copy link
MemberAuthor

VSadov commentedSep 17, 2025
edited
Loading

The AVs inFinalizeTaskReturningThunk appear to be cased by a rare case where GS cookie check uses the same register as continuation.

Logged an issue on that -#119796 and pushed a possible fix here.

Now my local run is down to:

  • 11 failures in-rc checked -lc debug config
  • 15 failures in-rc checked -lc release config

@jakobbotsch
Copy link
Member

Assert failure(PID 34 [0x00000022], Thread: 2715 [0x0a9b]): Assertion failed 'varDsc->CanBeReplacedWithItsField(comp) || varDsc->lvDoNotEnregister || !varDsc->lvPromoted' in 'System.IO.Compression.ZipArchive:ReadCentralDirectoryAsync(System.Threading.CancellationToken):this' during 'Lowering nodeinfo' (IL size 251; hash 0xd6ce9939; Tier1)

File: /__w/1/s/src/coreclr/jit/lower.cpp:5464Image: /root/helix/work/correlation/dotnet

seems like a new one. Could be Linux-arm64 specific.

https://dev.azure.com/dnceng-public/public/_build/results?buildId=1205773&view=logs&jobId=5d0c0cf7-4d03-5f34-03a4-db2320737fb6&j=5d0c0cf7-4d03-5f34-03a4-db2320737fb6&t=72a619ee-3adc-500c-4c86-d8374816747b

Fix for this is in#121749.

@VSadov
Copy link
MemberAuthor

The problem is that we now restore contexts as we unwind during return

Ah, I missed that part, I thought we restore only on logical returns.

How did it work before? Was it not the same try/finally save/restore, but around call sites?

@jakobbotsch
Copy link
Member

How did it work before? Was it not the same try/finally save/restore, but around call sites?

Previously we only restored in the finally on a synchronous return. If we suspended we did not change the Thread contexts, but we saved the contexts from before the call in the continuation, and on resumption we then restored those.

(That gives the same behavior for async to async calls, but using the contexts from before the call is not right if the callee modifies them and is not itself async, in which case the caller should see and save the new contexts.)

@VSadov
Copy link
MemberAuthor

Previously we only restored in the finally on a synchronous return. If we suspended we did not change the Thread contexts, but we saved the contexts from before the call in the continuation, and on resumption we then restored those.

If I read this correctly, now the callee restores when it returns continuation, whereas before the caller would not restore if it gets continuation from callee.

In such case an alternative "fix" could be to not restore in the callee when continuation is returned. Is that correct?
(The finally would need to check some flag and that would penalize synchronous scenario, thus it is not a good fix. I just want to make sure I understand the issue.)

@jakobbotsch
Copy link
Member

jakobbotsch commentedNov 18, 2025
edited
Loading

In such case an alternative "fix" could be to not restore in the callee when continuation is returned. Is that correct?
(The finally would need to check some flag and that would penalize synchronous scenario, thus it is not a good fix. I just want to make sure I understand the issue.)

Not exactly. It would be easy for us to skip the restore in the callee -- the JIT has no issue returning from suspension without invoking the finally. But that wouldn't be correct, since the caller will read the contexts fromThread.CurrentThread as part of its suspension, and save them in the continuation.

If the callee is async it needs to save and restore the contexts, even on suspension, because the caller will read the them from theThread object.
Similarly, if the callee is sync but changes the contexts and still returns a suspended task then the caller should see the updated contexts. That's the behavior we didn't get right before.

@VSadov
Copy link
MemberAuthor

If the callee is async it needs to save and restore the contexts, even on suspension, because the caller will read the them from the Thread object.
Similarly, if the callee is sync but changes the contexts and still returns a suspended task then the caller should see the updated contexts. That's the behavior we didn't get right before.

I see. The caller must read from the Thread, because the caller does not know if callee is async or something Task-returning (in which case context changes prior to suspending must be observed), but that requires that an async callee must restore even when suspending. I missed this detail of the change.

jakobbotsch reacted with thumbs up emoji

@jakobbotsch
Copy link
Member

The correspondence with async1 code is:

  • The try-finally inserted in async methods corresponds toAsyncTaskMethodBuilder.Start that Roslyn puts into every async1 method
  • Saving ofExecutionContext during suspension corresponds to what happens inAsyncTaskMethodBuilder.GetStateMachineBox
  • Saving ofSynchronizationContext/TaskScheduler corresponds to what happens in the call toTaskAwaiter.UnsafeOnCompletedInternal fromAsyncTaskMethodBuilder.AwaitUnsafeOnCompleted

The two last items above happen after the callee was already called. Hence if the callee changed the contexts then the caller observes those changed contexts.

@VSadov
Copy link
MemberAuthor

I've pushed the currently pending fixes to this PR.
(except the OSR one, which seems more complex and also not sure if Libraries tests are sensitive to that issue)

@am11
Copy link
Member

I think it wasn’t the intention of#121749 to close this PR. Reopening.

jakobbotsch reacted with thumbs up emoji

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@jakobbotschjakobbotschjakobbotsch left review comments

Copilot code reviewCopilotAwaiting requested review from CopilotCopilot will automatically review once the pull request is marked ready for review

+1 more reviewer

@am11am11am11 left review comments

Reviewers whose approvals may not affect merge requirements

At least 1 approving review is required to merge this pull request.

Assignees

@VSadovVSadov

Labels

area-VM-coreclrlinkable-frameworkIssues associated with delivering a linker friendly frameworkruntime-async

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

3 participants

@VSadov@jakobbotsch@am11

[8]ページ先頭

©2009-2025 Movatter.jp