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

Appearance settings

[clr-interp] Ensure initialization of MethodDesc->m_interpreterCode - #130026

#130026
Merged
janvorli merged 1 commit into
dotnet:maindotnet/runtime:mainfrom
BrzVlad:fix-clrinterp-code-race-halfBrzVlad/runtime:fix-clrinterp-code-race-halfCopy head branch name to clipboard
Jul 9, 2026
Merged

[clr-interp] Ensure initialization of MethodDesc->m_interpreterCode#130026
janvorli merged 1 commit into
dotnet:maindotnet/runtime:mainfrom
BrzVlad:fix-clrinterp-code-race-halfBrzVlad/runtime:fix-clrinterp-code-race-halfCopy head branch name to clipboard

Conversation

@BrzVlad

@BrzVlad BrzVlad commented Jun 30, 2026

Copy link
Copy Markdown
Member

When calling a method, we need to determine whether the method is interpreted or not. If m_interpreterCode is uninitialized, we will call ThePrestub to trigger method compilation (PrepareInterpreterCode). The expectation is that, once ThePrestub finishes, the interpreter code will always be initialized. This assumption didn't hold with the current code in the following scenario:

Two threads can race to compile a method in JitCompileCodeLocked (since method compilation is not always done under a lock in order to prevent deadlocks from recursive dependencies). Following method compilation the code publishing flow is to set the native code first via a CAS in SetNativeCode followed by a simple publish of the interpreter code by the thread which wins the native code publish race. The problem with this approach is that the thread losing the race can return early, without having the interpreter code set.

We fix this by making sure the interpreter code is always published by all threads compiling the method. It shouldn't matter which thread wins the race because both compilation results are identical and they are both preserved.

Should fix sporadic assertions from #129465

When calling a method, we need to determine whether the method is interpreted or not. If `m_interpreterCode` is uninitialized, we will call `ThePrestub` to trigger method compilation (PrepareInterpreterCode). The expectation is that, once `ThePrestub` finishes, the interpreter code will always be initialized. This assumption didn't hold with the current code in the following scenario:

Two threads can race to compile a method in `JitCompileCodeLocked` (since method compilation is not always done under a lock in order to prevent deadlocks from recursive dependencies). Following method compilation the code publishing flow is to set the native code first via a CAS in `SetNativeCode` followed by a simple publish of the interpreter code by the thread which wins the native code publish race. The problem with this approach is that the thread losing the race can return early, without having the interpreter code set.

We fix this by making sure the interpreter code is always published by all threads compiling the method. It shouldn't matter which thread wins the race because both compilation results are identical and they are both preserved.
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @BrzVlad, @janvorli, @kg
See info in area-owners.md if you want to be subscribed.

Copilot AI 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 change adjusts the CoreCLR method compilation publishing sequence so that, for interpreted methods, MethodDesc::m_interpreterCode is always initialized even when multiple threads race in JitCompileCodeLocked and one thread loses the native code publish CAS.

Changes:

  • Move the SetNativeCode publish/early-return block to occur after publishing interpreter bytecode (SetInterpreterCode) so the losing thread still initializes m_interpreterCode before returning the winner’s pOtherCode.

@BrzVlad

BrzVlad commented Jun 30, 2026

Copy link
Copy Markdown
Member Author

I believe there is still a theoretical issue. One thread is publishing the interpreter code followed by the native code. Then another thread might not bother compiling because it exits early via

if ((pCode = pConfig->IsJitCancellationRequested()))

In that case I believe it is possible for the thread to obtain a stale value of the interpreter code. Meaning we would need a store barrier between setting of interpreter and native codes and then a load barrier between checking that the native code is set and the future loading of the interpreter code.

Anyhow, I'm not able to reliably reproduce this issue for now, so I'll wait to see if we are still getting this assertion in the future.

Comment thread src/coreclr/vm/prestub.cpp

@janvorli janvorli left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, thank you!

@janvorli
janvorli merged commit 25997e1 into dotnet:main Jul 9, 2026
122 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview7 milestone Jul 10, 2026
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
…130026)

When calling a method, we need to determine whether the method is
interpreted or not. If `m_interpreterCode` is uninitialized, we will
call `ThePrestub` to trigger method compilation
(PrepareInterpreterCode). The expectation is that, once `ThePrestub`
finishes, the interpreter code will always be initialized. This
assumption didn't hold with the current code in the following scenario:

Two threads can race to compile a method in `JitCompileCodeLocked`
(since method compilation is not always done under a lock in order to
prevent deadlocks from recursive dependencies). Following method
compilation the code publishing flow is to set the native code first via
a CAS in `SetNativeCode` followed by a simple publish of the interpreter
code by the thread which wins the native code publish race. The problem
with this approach is that the thread losing the race can return early,
without having the interpreter code set.

We fix this by making sure the interpreter code is always published by
all threads compiling the method. It shouldn't matter which thread wins
the race because both compilation results are identical and they are
both preserved.

Should fix sporadic assertions from
#129465
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.