-
Notifications
You must be signed in to change notification settings - Fork 5.1k
[release/10.0] Fix deadlock when creating threads from ModuleInitializer in a shared library #119082
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jeffschwMSFT
merged 12 commits into
release/10.0
from
backport/pr-118928-to-release/10.0
Aug 26, 2025
Merged
[release/10.0] Fix deadlock when creating threads from ModuleInitializer in a shared library #119082
jeffschwMSFT
merged 12 commits into
release/10.0
from
backport/pr-118928-to-release/10.0
Aug 26, 2025
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Contributor
… library The problem is that the first time we reverse p/invoke into an API in the shared library, we need to do two things: initialize runtime and run module initializers. Running module initializers is done as part of runtime initialization in the shared library case because it was convenient to do it there (in the EXE case, we run them after runtime initialization, right before Main). The problem with running it as part of the runtime initialization is that the arbitrary user code can deadlock us (see the issues for stack). This separates runtime initialization and running module initializers. Doing just that doesn't fix the problem though because the deadlock we saw is part of thread creation. So we'd just deadlock on the module initializer runner. This introduces a new kind of reverse p/invoke enter routine that doesn't wait for module initializers. Instead we wait for module initializers before we are ready to run user code on the thread (and the thread is initialized just enough that ThreadStart can make progress). This also fixes a potential issue in the EXE case where threads created from module initializers could start running before we're finished running module initializers. Instead this introduces a new opportunity for deadlocking :(. I don't actually like this fix. But we need to do something about it because EventSource does exactly this (#118773). Fixes #118773. Fixes #107699.
Co-authored-by: Jan Kotas <[email protected]>
jkotas
approved these changes
Aug 26, 2025
jeffschwMSFT
approved these changes
Aug 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
approved. we can merge when ready
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Backport of #118928 to release/10.0
/cc @MichalStrehovsky
Customer Impact
When building a shared library with native AOT and a
ModuleInitializer
somewhere tries to create a thread, we would deadlock the runtime initialization.This was found internally last year and looked like a corner case we don't need to worry about much (because who creates threads in a ModuleInitializer). Then we got a customer report because EventPipe/EventSource infra may create threads as part of module initializer :/
Regression
Testing
Targeted test was added that creates a thread as part of a module initializer in a shared library. Since we're changing how new threads are born, this is also tested with non-shared-library tests.
Risk
Should be low, we're not doing anything unusual, just starting new threads in a native method instead of directly starting as managed.
IMPORTANT: If this backport is for a servicing release, please verify that:
release/X.0-staging
, notrelease/X.0
.Package authoring no longer needed in .NET 9
IMPORTANT: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version.
Keep in mind that we still need package authoring in .NET 8 and older versions.