public static unsafe void EnsureClassConstructorRun(IntPtr staticClassConstructionContext) { StaticClassConstructionContext *context = (StaticClassConstructionContext *)staticClassConstructionContext; ClassConstructorRunner.EnsureClassConstructorRun(context); }
private static unsafe object CheckStaticClassConstructionReturnThreadStaticBase(TypeManagerSlot *pModuleData, int typeTlsIndex, StaticClassConstructionContext *context) { object threadStaticBase = ThreadStatics.GetThreadStaticBaseForType(pModuleData, typeTlsIndex); EnsureClassConstructorRun(context); return(threadStaticBase); }
public static unsafe void EnsureClassConstructorRun(StaticClassConstructionContext *pContext) { IntPtr pfnCctor = pContext->cctorMethodAddress; NoisyLog("EnsureClassConstructorRun, cctor={0}, thread={1}", pfnCctor, CurrentManagedThreadId); // If we were called from MRT, this check is redundant but harmless. This is in case someone within classlib // (cough, Reflection) needs to call this explicitly. if (pContext->initialized == 1) { NoisyLog("Cctor already run, cctor={0}, thread={1}", pfnCctor, CurrentManagedThreadId); return; } CctorHandle cctor = Cctor.GetCctor(pContext); Cctor[] cctors = cctor.Array; int cctorIndex = cctor.Index; try { Lock cctorLock = cctors[cctorIndex].Lock; if (DeadlockAwareAcquire(cctor, pfnCctor)) { int currentManagedThreadId = CurrentManagedThreadId; try { NoisyLog("Acquired cctor lock, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); cctors[cctorIndex].HoldingThread = currentManagedThreadId; if (pContext->initialized == 0) // Check again in case some thread raced us while we were acquiring the lock. { TypeInitializationException priorException = cctors[cctorIndex].Exception; if (priorException != null) { throw priorException; } try { NoisyLog("Calling cctor, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); ((delegate * < void >)pfnCctor)(); // Insert a memory barrier here to order any writes executed as part of static class // construction above with respect to the initialized flag update we're about to make // below. This is important since the fast path for checking the cctor uses a normal read // and doesn't come here so without the barrier it could observe initialized == 1 but // still see uninitialized static fields on the class. Interlocked.MemoryBarrier(); NoisyLog("Set type inited, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); pContext->initialized = 1; } catch (Exception e) { TypeInitializationException wrappedException = new TypeInitializationException(null, SR.TypeInitialization_Type_NoTypeAvailable, e); cctors[cctorIndex].Exception = wrappedException; throw wrappedException; } } } finally { cctors[cctorIndex].HoldingThread = ManagedThreadIdNone; NoisyLog("Releasing cctor lock, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId); cctorLock.Release(); } } else { // Cctor cycle resulted in a deadlock. We will break the guarantee and return without running the // .cctor. } } finally { Cctor.Release(cctor); } NoisyLog("EnsureClassConstructorRun complete, cctor={0}, thread={1}", pfnCctor, CurrentManagedThreadId); }
private static unsafe IntPtr CheckStaticClassConstructionReturnNonGCStaticBase(StaticClassConstructionContext *context, IntPtr nonGcStaticBase) { EnsureClassConstructorRun(context); return(nonGcStaticBase); }
//============================================================================================================== // Ensures the class constructor for the given type has run. // // Called by the runtime when it finds a class whose static class constructor has probably not run // (probably because it checks in the initialized flag without thread synchronization). // // The context structure passed by reference lives in the image of one of the application's modules. // The contents are thus fixed (do not require pinning) and the address can be used as a unique // identifier for the context. // // This guarantee is violated in one specific case: where a class constructor cycle would cause a deadlock. If // so, per ECMA specs, this method returns without guaranteeing that the .cctor has run. // // No attempt is made to detect or break deadlocks due to other synchronization mechanisms. //============================================================================================================== private static unsafe object CheckStaticClassConstructionReturnGCStaticBase(StaticClassConstructionContext *context, object gcStaticBase) { EnsureClassConstructorRun(context); return(gcStaticBase); }
//========================================================================================================== // Gets the Cctor entry associated with a specific class constructor context (creating it if necessary.) //========================================================================================================== public static CctorHandle GetCctor(StaticClassConstructionContext *pContext) { #if DEBUG const int Grow = 2; #else const int Grow = 10; #endif // WASMTODO: Remove this when the Initialize method gets called by the runtime startup #if TARGET_WASM if (s_cctorGlobalLock == null) { Interlocked.CompareExchange(ref s_cctorGlobalLock, new Lock(), null); } if (s_cctorArrays == null) { Interlocked.CompareExchange(ref s_cctorArrays, new Cctor[10][], null); } #endif // TARGET_WASM using (LockHolder.Hold(s_cctorGlobalLock)) { Cctor[]? resultArray = null; int resultIndex = -1; if (s_count != 0) { // Search for the cctor context in our existing arrays for (int cctorIndex = 0; cctorIndex < s_cctorArraysCount; ++cctorIndex) { Cctor[] segment = s_cctorArrays[cctorIndex]; for (int i = 0; i < segment.Length; i++) { if (segment[i]._pContext == pContext) { resultArray = segment; resultIndex = i; break; } } if (resultArray != null) { break; } } } if (resultArray == null) { // look for an empty entry in an existing array for (int cctorIndex = 0; cctorIndex < s_cctorArraysCount; ++cctorIndex) { Cctor[] segment = s_cctorArrays[cctorIndex]; for (int i = 0; i < segment.Length; i++) { if (segment[i]._pContext == default(StaticClassConstructionContext *)) { resultArray = segment; resultIndex = i; break; } } if (resultArray != null) { break; } } if (resultArray == null) { // allocate a new array resultArray = new Cctor[Grow]; if (s_cctorArraysCount == s_cctorArrays.Length) { // grow the container Array.Resize(ref s_cctorArrays, (s_cctorArrays.Length * 2) + 1); } // store the array in the container, this cctor gets index 0 s_cctorArrays[s_cctorArraysCount] = resultArray; s_cctorArraysCount++; resultIndex = 0; } Debug.Assert(resultArray[resultIndex]._pContext == default(StaticClassConstructionContext *)); resultArray[resultIndex]._pContext = pContext; resultArray[resultIndex].Lock = new Lock(); s_count++; } Interlocked.Increment(ref resultArray[resultIndex]._refCount); return(new CctorHandle(resultArray, resultIndex)); } }
public static unsafe void *CheckStaticClassConstruction(void *returnValue, StaticClassConstructionContext *pContext) { EnsureClassConstructorRun(pContext); return(returnValue); }
//========================================================================================================== // Gets the Cctor entry associated with a specific class constructor context (creating it if necessary.) //========================================================================================================== public static CctorHandle GetCctor(StaticClassConstructionContext *pContext) { #if DEBUG const int Grow = 2; #else const int Grow = 10; #endif s_cctorGlobalLock.Acquire(); try { Cctor[] resultArray = null; int resultIndex = -1; if (s_count != 0) { // Search for the cctor context in our existing arrays for (int cctorIndex = 0; cctorIndex < s_cctorArraysCount; ++cctorIndex) { Cctor[] segment = s_cctorArrays[cctorIndex]; for (int i = 0; i < segment.Length; i++) { if (segment[i]._pContext == pContext) { resultArray = segment; resultIndex = i; break; } } if (resultArray != null) { break; } } } if (resultArray == null) { // look for an empty entry in an existing array for (int cctorIndex = 0; cctorIndex < s_cctorArraysCount; ++cctorIndex) { Cctor[] segment = s_cctorArrays[cctorIndex]; for (int i = 0; i < segment.Length; i++) { if (segment[i]._pContext == default(StaticClassConstructionContext *)) { resultArray = segment; resultIndex = i; break; } } if (resultArray != null) { break; } } if (resultArray == null) { // allocate a new array resultArray = new Cctor[Grow]; if (s_cctorArraysCount == s_cctorArrays.Length) { // grow the container Array.Resize(ref s_cctorArrays, (s_cctorArrays.Length * 2) + 1); } // store the array in the container, this cctor gets index 0 s_cctorArrays[s_cctorArraysCount] = resultArray; s_cctorArraysCount++; resultIndex = 0; } Debug.Assert(resultArray[resultIndex]._pContext == default(StaticClassConstructionContext *)); resultArray[resultIndex]._pContext = pContext; resultArray[resultIndex].Lock = new Lock(); s_count++; } Interlocked.Increment(ref resultArray[resultIndex]._refCount); return(new CctorHandle(resultArray, resultIndex)); } finally { s_cctorGlobalLock.Release(); } }