Skip to content
Merged
5 changes: 3 additions & 2 deletions src/coreclr/nativeaot/Bootstrap/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ static char& __unbox_z = __stop___unbox;
#endif // _MSC_VER

extern "C" bool RhInitialize(bool isDll);
extern "C" void RhSetRuntimeInitializationCallback(int (*fPtr)());
extern "C" void RhSetRuntimeInitializationCallback(int (*fRuntimeInit)(), void (*fModuleInit)());

extern "C" bool RhRegisterOSModule(void * pModule,
void * pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
Expand Down Expand Up @@ -176,6 +176,7 @@ extern "C" int __managed__Main(int argc, char* argv[]);
#else
#define NATIVEAOT_ENTRYPOINT __managed__Startup
extern "C" void __managed__Startup();
extern "C" void RunModuleInitializers();
#endif // !NATIVEAOT_DLL

static int InitializeRuntime()
Expand Down Expand Up @@ -248,7 +249,7 @@ static struct InitializeRuntimePointerHelper
{
InitializeRuntimePointerHelper()
{
RhSetRuntimeInitializationCallback(&InitializeRuntime);
RhSetRuntimeInitializationCallback(&InitializeRuntime, &RunModuleInitializers);
}
} initializeRuntimePointerHelper;
#endif // NATIVEAOT_DLL
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ internal static partial class StartupCodeHelpers
/// </summary>
private static int s_moduleCount;

internal static volatile bool s_runningModuleInitializers;

/// <summary>
/// GC handle of an array with s_moduleCount elements, each representing and array of GC static bases of the types in the module.
/// </summary>
Expand Down Expand Up @@ -160,12 +162,22 @@ private static unsafe void InitializeModuleFrozenObjectSegment(IntPtr segmentSta
}
}

[UnmanagedCallersOnly(EntryPoint = "RunModuleInitializers")]
internal static void RunModuleInitializers2()
{
RunModuleInitializers();
}

internal static void RunModuleInitializers()
{
s_runningModuleInitializers = true;

for (int i = 0; i < s_moduleCount; i++)
{
RunInitializers(s_modules[i], ReadyToRunSectionType.ModuleInitializerList);
}

s_runningModuleInitializers = false;
}

private static unsafe void RunInitializers(TypeManagerHandle typeManager, ReadyToRunSectionType section)
Expand Down
50 changes: 44 additions & 6 deletions src/coreclr/nativeaot/Runtime/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
#ifndef DACCESS_COMPILE

static int (*g_RuntimeInitializationCallback)();
static void (*g_ModuleInitializerCallback)();
static Thread* g_RuntimeInitializingThread;
static Thread* g_ModuleInitializingThread;

#endif //!DACCESS_COMPILE

Expand Down Expand Up @@ -1188,12 +1190,13 @@ EXTERN_C uint32_t QCALLTYPE RhCompatibleReentrantWaitAny(UInt32_BOOL alertable,
}
#endif // TARGET_UNIX

EXTERN_C void RhSetRuntimeInitializationCallback(int (*fPtr)())
EXTERN_C void RhSetRuntimeInitializationCallback(int (*fRuntimeInit)(), void (*fModuleInit)())
{
g_RuntimeInitializationCallback = fPtr;
g_RuntimeInitializationCallback = fRuntimeInit;
g_ModuleInitializerCallback = fModuleInit;
}

void Thread::ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame)
void Thread::ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame, bool fEnsureModuleInitializersExecuted)
{
if (!IsStateSet(TSF_Attached))
{
Expand All @@ -1202,6 +1205,14 @@ void Thread::ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame)
EnsureRuntimeInitialized();
}

// Additionally check g_RuntimeInitializingThread to cover the case where we're currently running EnsureRuntimeInitialized
// on this thread but we need a reverse p/invoke transition. Module initializers should only run after the runtime
// is initialized.
if (fEnsureModuleInitializersExecuted && g_ModuleInitializerCallback != NULL && g_ModuleInitializingThread != this && g_RuntimeInitializingThread != this)
{
EnsureModuleInitializersExecuted();
}

ThreadStore::AttachCurrentThread();
}

Expand Down Expand Up @@ -1249,6 +1260,22 @@ void Thread::EnsureRuntimeInitialized()
PalInterlockedExchangePointer((void *volatile *)&g_RuntimeInitializingThread, NULL);
}

void Thread::EnsureModuleInitializersExecuted()
{
while (PalInterlockedCompareExchangePointer((void *volatile *)&g_ModuleInitializingThread, this, NULL) != NULL)
{
PalSleep(1);
}

if (g_ModuleInitializerCallback != NULL)
{
g_ModuleInitializerCallback();
g_ModuleInitializerCallback = NULL;
}

PalInterlockedExchangePointer((void *volatile *)&g_ModuleInitializingThread, NULL);
}

Object * Thread::GetThreadAbortException()
{
return m_threadAbortException;
Expand Down Expand Up @@ -1324,10 +1351,10 @@ FCIMPL0(size_t, RhGetDefaultStackSize)
FCIMPLEND

// Standard calling convention variant and actual implementation for RhpReversePInvokeAttachOrTrapThread
EXTERN_C NOINLINE void FASTCALL RhpReversePInvokeAttachOrTrapThread2(ReversePInvokeFrame* pFrame)
EXTERN_C NOINLINE void FASTCALL RhpReversePInvokeAttachOrTrapThread2(ReversePInvokeFrame* pFrame, bool fEnsureModuleInitializersExecuted)
{
ASSERT(pFrame->m_savedThread == ThreadStore::RawGetCurrentThread());
pFrame->m_savedThread->ReversePInvokeAttachOrTrapThread(pFrame);
pFrame->m_savedThread->ReversePInvokeAttachOrTrapThread(pFrame, fEnsureModuleInitializersExecuted);
}

//
Expand All @@ -1341,7 +1368,18 @@ FCIMPL1(void, RhpReversePInvoke, ReversePInvokeFrame * pFrame)
if (pCurThread->InlineTryFastReversePInvoke(pFrame))
return;

RhpReversePInvokeAttachOrTrapThread2(pFrame);
RhpReversePInvokeAttachOrTrapThread2(pFrame, true);
}
FCIMPLEND

FCIMPL1(void, RhpReversePInvokeNoModuleInitializer, ReversePInvokeFrame * pFrame)
{
Thread * pCurThread = ThreadStore::RawGetCurrentThread();
pFrame->m_savedThread = pCurThread;
if (pCurThread->InlineTryFastReversePInvoke(pFrame))
return;

RhpReversePInvokeAttachOrTrapThread2(pFrame, false);
}
FCIMPLEND

Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/nativeaot/Runtime/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ class Thread : private RuntimeThreadLocals
bool CacheTransitionFrameForSuspend();
void ResetCachedTransitionFrame();
void EnsureRuntimeInitialized();
void EnsureModuleInitializersExecuted();

//
// SyncState members
Expand Down Expand Up @@ -352,7 +353,7 @@ class Thread : private RuntimeThreadLocals
//
void WaitForGC(PInvokeTransitionFrame* pTransitionFrame);

void ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame);
void ReversePInvokeAttachOrTrapThread(ReversePInvokeFrame * pFrame, bool fEnsureModuleInitializersExecuted);

bool InlineTryFastReversePInvoke(ReversePInvokeFrame * pFrame);
void InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ private unsafe bool CreateThread(GCHandle<Thread> thisThreadHandle)
/// <summary>
/// This is an entry point for managed threads created by application
/// </summary>
/////////////////////////////////////////////////////////////////////////
// WARNING: The compiler special cases this UnmanagedCallersOnly method
// and performs a "lite" reverse p/invoke transition that will not wait
// for module initializers before starting to run this method.
// We wait for module initializers later in StartThread manually.
/////////////////////////////////////////////////////////////////////////
[UnmanagedCallersOnly]
private static IntPtr ThreadEntryPoint(IntPtr parameter)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ private unsafe bool CreateThread(GCHandle<Thread> thisThreadHandle)
/// <summary>
/// This is an entry point for managed threads created by application
/// </summary>
/////////////////////////////////////////////////////////////////////////
// WARNING: The compiler special cases this UnmanagedCallersOnly method
// and performs a "lite" reverse p/invoke transition that will not wait
// for module initializers before starting to run this method.
// We wait for module initializers later in StartThread manually.
/////////////////////////////////////////////////////////////////////////
[UnmanagedCallersOnly]
private static uint ThreadEntryPoint(IntPtr parameter)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,11 @@ private static void StartThread(IntPtr parameter)
IncrementRunningForeground();
}

while (Internal.Runtime.CompilerHelpers.StartupCodeHelpers.s_runningModuleInitializers)
{
Yield();
}

try
{
StartHelper? startHelper = thread._startHelper;
Expand Down
12 changes: 12 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3552,6 +3552,18 @@ private void getHelperFtn(CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP *pNativeE
entryPoint = GetHelperFtnUncached(ftnNum);
_helperCache.Add(ftnNum, entryPoint);
}

#if !READYTORUN
if (ftnNum == CorInfoHelpFunc.CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER
&& MethodBeingCompiled.Name == "ThreadEntryPoint"
&& MethodBeingCompiled.OwningType is MetadataType mdOwningType
&& mdOwningType.Module == _compilation.TypeSystemContext.SystemModule
&& mdOwningType is { Name: "Thread", Namespace: "System.Threading" })
{
entryPoint = _compilation.NodeFactory.ExternFunctionSymbol("RhpReversePInvokeNoModuleInitializer");
}
#endif

if (entryPoint.RepresentsIndirectionCell)
{
pNativeEntrypoint->addr = (void*)ObjectToHandle(entryPoint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,6 @@ public override MethodIL EmitIL()
}
}

MetadataType startup = Context.GetOptionalHelperType("StartupCodeHelpers");

// Run module initializers
MethodDesc runModuleInitializers = startup?.GetMethod("RunModuleInitializers", null);
if (runModuleInitializers != null)
{
codeStream.Emit(ILOpcode.call, emitter.NewToken(runModuleInitializers));
}

codeStream.Emit(ILOpcode.ret);
return emitter.Link(this);
}
Expand Down
16 changes: 15 additions & 1 deletion src/tests/nativeaot/SmokeTests/SharedLibrary/SharedLibrary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,29 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

namespace SharedLibrary
{
public class ClassLibrary
{
static Thread s_setterThread;
static int s_primitiveInt;

[ModuleInitializer]
public static void CreateThreadInModuleInitializer()
{
// Regression test for https://github.com/dotnet/runtime/issues/107699
// where creating threads in module initializer would lead to a deadlock.
s_setterThread = new Thread(() => { s_primitiveInt = 10; });
s_setterThread.Start();
}

[UnmanagedCallersOnly(EntryPoint = "ReturnsPrimitiveInt", CallConvs = new Type[] { typeof(CallConvStdcall) })]
public static int ReturnsPrimitiveInt()
{
return 10;
s_setterThread.Join();
return s_primitiveInt;
}

[UnmanagedCallersOnly(EntryPoint = "ReturnsPrimitiveBool", CallConvs = new Type[] { typeof(CallConvStdcall) })]
Expand Down
Loading