Skip to content

Conversation

github-actions[bot]
Copy link
Contributor

@github-actions github-actions bot commented Aug 26, 2025

Backport of #118928 to release/10.0

/cc @MichalStrehovsky

Customer Impact

  • Customer reported
  • Found internally

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

  • Yes
  • No

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:

  • The PR target branch is release/X.0-staging, not release/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.

MichalStrehovsky and others added 11 commits August 26, 2025 05:32
… 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.
Copy link
Member

@jeffschwMSFT jeffschwMSFT left a 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

@jeffschwMSFT jeffschwMSFT added the Servicing-approved Approved for servicing release label Aug 26, 2025
@jeffschwMSFT jeffschwMSFT added this to the 10.0.0 milestone Aug 26, 2025
@jeffschwMSFT jeffschwMSFT merged commit adaec22 into release/10.0 Aug 26, 2025
103 checks passed
@MichalStrehovsky MichalStrehovsky deleted the backport/pr-118928-to-release/10.0 branch August 26, 2025 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Servicing-approved Approved for servicing release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants