Skip to content

Conversation

@AaronRobinsonMSFT
Copy link
Member

The code path in question is executed during a GC. This code path also allocates managed memory, which means it is incompatible to run during a GC. Ensuring the cctor run prior to any GC is the goal of this change.

Fixes #121538

The code path in question is executed during a GC.
This code path also allocates managed memory, which
means it is incompatible to run during a GC. Ensuring
the cctor run prior to any GC is the goal of this change.
Copy link
Contributor

Copilot AI left a comment

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 fixes a critical race condition where the FindReferenceTargetsCallback static constructor could be invoked during garbage collection, causing managed memory allocations in a GC-incompatible code path. The fix ensures the static constructor runs before any GC callbacks are registered.

  • Added an EnsureInitialized() method to trigger the static constructor
  • Invoked EnsureInitialized() in RegisterGCCallbacks() before GC callbacks are registered

@jkotas
Copy link
Member

jkotas commented Nov 12, 2025

We should do some validation for this:

  • Check that the WinRT tracker test is able to hit this bug when compiled with optimizations off
  • Check that the WinRT tracker test is passing with optimizations off with this fix

@AaronRobinsonMSFT
Copy link
Member Author

We should do some validation for this:

  • Check that the WinRT tracker test is able to hit this bug when compiled with optimizations off
  • Check that the WinRT tracker test is passing with optimizations off with this fix

Done. Found an additional issue when running src/tests/Interop/COM/ComWrappers/API/ComWrappersTests.csproj in Debug under native AOT.

@AaronRobinsonMSFT
Copy link
Member Author

@EgorBo FYI we're adding back two Unsafe.As() uses due to type load during the GC.

@EgorBo
Copy link
Member

EgorBo commented Nov 12, 2025

Is it fine to keep it as is if we have a guarantee that And/Or are always imported as intrinsic?

@jkotas
Copy link
Member

jkotas commented Nov 12, 2025

we're adding back two Unsafe.As() uses due to type load during the GC.

The problem are allocations caused by

if ((!typeof(T).IsPrimitive && !typeof(T).IsEnum) ||
typeof(T) == typeof(float) || typeof(T) == typeof(double) ||
(typeof(T).IsEnum && (typeof(T).GetEnumUnderlyingType() == typeof(float) || typeof(T).GetEnumUnderlyingType() == typeof(double))))
in non-optimized builds (when these reflection APIs are not expanded as intrinsics by the JIT). It is not really a type load.

@jkotas
Copy link
Member

jkotas commented Nov 12, 2025

Is it fine to keep it as is if we have a guarantee that And/Or are always imported as intrinsic?

Yes, it would be ok to keep it as is if the JIT can be changed to guarantee that the generic Interlocked.And/Or is imported as an intrinsics that does not call anything that can GC allocate.

@AaronRobinsonMSFT
Copy link
Member Author

/ba-g I've no clue what is going on, but none of it are these changes.

@AaronRobinsonMSFT AaronRobinsonMSFT merged commit 3dde055 into dotnet:main Nov 13, 2025
129 of 144 checks passed
@AaronRobinsonMSFT AaronRobinsonMSFT deleted the runtime_121538 branch November 13, 2025 15:25
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.

.NET 10 Native AOT causes WinUI 3 desktop applications to not respond

3 participants