-
Notifications
You must be signed in to change notification settings - Fork 566
Labels
copilot`copilot-cli` or other AIs were used to author this`copilot-cli` or other AIs were used to author thistrimmable-type-map
Milestone
Description
Part of #10788
Problem
TrimmableTypeMap.Initialize() has a race condition. The Interlocked.CompareExchange correctly ensures only one instance wins the singleton slot, but the losing thread still proceeds to call RegisterBootstrapNativeMethod():
// TrimmableTypeMap.cs:41-48
internal static void Initialize()
{
var instance = new TrimmableTypeMap();
var previous = Interlocked.CompareExchange(ref s_instance, instance, null);
Debug.Assert(previous is null, "TrimmableTypeMap must only be created once.");
instance.RegisterBootstrapNativeMethod(); // runs even if CompareExchange lost
}If two threads race on Initialize():
- Both create a
TrimmableTypeMapinstance (expensive — scans type map attributes) - Only one wins the
CompareExchange - Both call
RegisterBootstrapNativeMethod()— the losing thread registers on its discarded instance, which is a no-op, but it still creates JNI types and callsRegisterNativesunnecessarily
Fix
Add an early return guard after the CompareExchange:
internal static void Initialize()
{
var instance = new TrimmableTypeMap();
var previous = Interlocked.CompareExchange(ref s_instance, instance, null);
if (previous is not null)
return;
instance.RegisterBootstrapNativeMethod();
}Impact
Low — Initialize() is typically called once during startup. But the fix is trivial and prevents unnecessary work + potential double JNI registration.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
copilot`copilot-cli` or other AIs were used to author this`copilot-cli` or other AIs were used to author thistrimmable-type-map