Skip to content

Commit

Permalink
Implement type loader support for thread statics (#71524)
Browse files Browse the repository at this point in the history
We do threadstatics differently from .NET Native and this wasn't implemented.

Fixes #70878 that was reaching a failfast because we couldn't build the target of GVM dispatch at runtime due to unhandled dictionary cell.

I've homed the threadstatic bases under the type manager of the template type. We could potentially make a new type manager, but they currently use this one.

Deleted some more .NET Native leftovers.

Had to update the DynamicGeneric test RD.XML because the test was assuming .NET Native behavior where the ctors are always kept on types that escape the dataflow analysis.
  • Loading branch information
MichalStrehovsky authored Jul 4, 2022
1 parent 5961604 commit 7496163
Show file tree
Hide file tree
Showing 19 changed files with 156 additions and 182 deletions.
34 changes: 30 additions & 4 deletions src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,30 @@ internal IntPtr DynamicNonGcStaticsData
#endif
}

internal IntPtr DynamicThreadStaticsIndex
{
get
{
Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0);
uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicThreadStaticOffset);
fixed (MethodTable* pThis = &this)
{
return *(IntPtr*)((byte*)pThis + cbOffset);
}
}
#if TYPE_LOADER_IMPLEMENTATION
set
{
Debug.Assert((RareFlags & EETypeRareFlags.IsDynamicTypeWithThreadStatics) != 0);
uint cbOffset = GetFieldOffset(EETypeField.ETF_DynamicThreadStaticOffset);
fixed (MethodTable* pThis = &this)
{
*(IntPtr*)((byte*)pThis + cbOffset) = value;
}
}
#endif
}

internal DynamicModule* DynamicModule
{
get
Expand Down Expand Up @@ -1243,12 +1267,14 @@ internal IntPtr PointerToTypeManager
{
get
{
uint cbOffset = GetFieldOffset(EETypeField.ETF_TypeManagerIndirection);
// This is always a pointer to a pointer to a type manager
return (IntPtr)(*(TypeManagerHandle**)((byte*)Unsafe.AsPointer(ref this) + cbOffset));
if (IsDynamicType || !SupportsRelativePointers)
return GetField<Pointer>(EETypeField.ETF_TypeManagerIndirection).Value;

return GetField<RelativePointer>(EETypeField.ETF_TypeManagerIndirection).Value;
}
set
{
Debug.Assert(IsDynamicType);
uint cbOffset = GetFieldOffset(EETypeField.ETF_TypeManagerIndirection);
// This is always a pointer to a pointer to a type manager
*(TypeManagerHandle**)((byte*)Unsafe.AsPointer(ref this) + cbOffset) = (TypeManagerHandle*)value;
Expand Down Expand Up @@ -1497,7 +1523,7 @@ internal static uint GetSizeofEEType(
(fHasGenericInfo ? sizeof(IntPtr)*2 : 0) + // pointers to GenericDefinition and GenericComposition
(fHasNonGcStatics ? sizeof(IntPtr) : 0) + // pointer to data
(fHasGcStatics ? sizeof(IntPtr) : 0) + // pointer to data
(fHasThreadStatics ? sizeof(uint) : 0)); // tls offset
(fHasThreadStatics ? sizeof(IntPtr) : 0)); // threadstatic index cell
}
#endif
}
Expand Down
12 changes: 0 additions & 12 deletions src/coreclr/nativeaot/Runtime/RuntimeInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,16 +367,4 @@ COOP_PINVOKE_HELPER(void *, RhNewInterfaceDispatchCell, (MethodTable * pInterfac
}
#endif // FEATURE_CACHED_INTERFACE_DISPATCH

COOP_PINVOKE_HELPER(PTR_UInt8, RhGetThreadLocalStorageForDynamicType, (uint32_t uOffset, uint32_t tlsStorageSize, uint32_t numTlsCells))
{
Thread * pCurrentThread = ThreadStore::GetCurrentThread();

PTR_UInt8 pResult = pCurrentThread->GetThreadLocalStorageForDynamicType(uOffset);
if (pResult != NULL || tlsStorageSize == 0 || numTlsCells == 0)
return pResult;

ASSERT(tlsStorageSize > 0 && numTlsCells > 0);
return pCurrentThread->AllocateThreadLocalStorageForDynamicType(uOffset, tlsStorageSize, numTlsCells);
}

#endif // DACCESS_COMPILE
64 changes: 0 additions & 64 deletions src/coreclr/nativeaot/Runtime/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,6 @@ void Thread::Construct()
(offsetof(Thread, m_pTransitionFrame)));
#endif // USE_PORTABLE_HELPERS

m_numDynamicTypesTlsCells = 0;
m_pDynamicTypesTlsCells = NULL;

m_pThreadLocalModuleStatics = NULL;
m_numThreadLocalModuleStatics = 0;

Expand Down Expand Up @@ -353,16 +350,6 @@ void Thread::Destroy()
if (m_hPalThread != INVALID_HANDLE_VALUE)
PalCloseHandle(m_hPalThread);

if (m_pDynamicTypesTlsCells != NULL)
{
for (uint32_t i = 0; i < m_numDynamicTypesTlsCells; i++)
{
if (m_pDynamicTypesTlsCells[i] != NULL)
delete[] m_pDynamicTypesTlsCells[i];
}
delete[] m_pDynamicTypesTlsCells;
}

if (m_pThreadLocalModuleStatics != NULL)
{
for (uint32_t i = 0; i < m_numThreadLocalModuleStatics; i++)
Expand Down Expand Up @@ -1104,58 +1091,7 @@ PTR_UInt8 Thread::GetThreadLocalStorage(uint32_t uTlsIndex, uint32_t uTlsStartOf
#endif
}

PTR_UInt8 Thread::GetThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset)
{
// Note: When called from GC root enumeration, no changes can be made by the AllocateThreadLocalStorageForDynamicType to
// the 2 variables accessed here because AllocateThreadLocalStorageForDynamicType is called in cooperative mode.

uTlsTypeOffset &= ~DYNAMIC_TYPE_TLS_OFFSET_FLAG;
return dac_cast<PTR_UInt8>(uTlsTypeOffset < m_numDynamicTypesTlsCells ? m_pDynamicTypesTlsCells[uTlsTypeOffset] : NULL);
}

#ifndef DACCESS_COMPILE
PTR_UInt8 Thread::AllocateThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset, uint32_t tlsStorageSize, uint32_t numTlsCells)
{
uTlsTypeOffset &= ~DYNAMIC_TYPE_TLS_OFFSET_FLAG;

if (m_pDynamicTypesTlsCells == NULL || m_numDynamicTypesTlsCells <= uTlsTypeOffset)
{
// Keep at least a 2x grow so that we don't have to reallocate everytime a new type with TLS statics is created
if (numTlsCells < 2 * m_numDynamicTypesTlsCells)
numTlsCells = 2 * m_numDynamicTypesTlsCells;

PTR_UInt8* pTlsCells = new (nothrow) PTR_UInt8[numTlsCells];
if (pTlsCells == NULL)
return NULL;

memset(&pTlsCells[m_numDynamicTypesTlsCells], 0, sizeof(PTR_UInt8) * (numTlsCells - m_numDynamicTypesTlsCells));

if (m_pDynamicTypesTlsCells != NULL)
{
memcpy(pTlsCells, m_pDynamicTypesTlsCells, sizeof(PTR_UInt8) * m_numDynamicTypesTlsCells);
delete[] m_pDynamicTypesTlsCells;
}

m_pDynamicTypesTlsCells = pTlsCells;
m_numDynamicTypesTlsCells = numTlsCells;
}

ASSERT(uTlsTypeOffset < m_numDynamicTypesTlsCells);

if (m_pDynamicTypesTlsCells[uTlsTypeOffset] == NULL)
{
uint8_t* pTlsStorage = new (nothrow) uint8_t[tlsStorageSize];
if (pTlsStorage == NULL)
return NULL;

// Initialize storage to 0's before returning it
memset(pTlsStorage, 0, tlsStorageSize);

m_pDynamicTypesTlsCells[uTlsTypeOffset] = pTlsStorage;
}

return m_pDynamicTypesTlsCells[uTlsTypeOffset];
}

#ifndef TARGET_UNIX
EXTERN_C NATIVEAOT_API uint32_t __cdecl RhCompatibleReentrantWaitAny(UInt32_BOOL alertable, uint32_t timeout, uint32_t count, HANDLE* pHandles)
Expand Down
10 changes: 0 additions & 10 deletions src/coreclr/nativeaot/Runtime/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ class Thread;
#define TOP_OF_STACK_MARKER ((PInvokeTransitionFrame*)(ptrdiff_t)-1)
#define REDIRECTED_THREAD_MARKER ((PInvokeTransitionFrame*)(ptrdiff_t)-2)

#define DYNAMIC_TYPE_TLS_OFFSET_FLAG 0x80000000


enum SyncRequestResult
{
TryAgain,
Expand Down Expand Up @@ -99,10 +96,6 @@ struct ThreadBuffer
uint32_t m_uRand; // current per-thread random number
#endif // FEATURE_GC_STRESS

// Thread Statics Storage for dynamic types
uint32_t m_numDynamicTypesTlsCells;
PTR_PTR_UInt8 m_pDynamicTypesTlsCells;

};

struct ReversePInvokeFrame
Expand Down Expand Up @@ -200,9 +193,6 @@ class Thread : private ThreadBuffer

void GetStackBounds(PTR_VOID * ppStackLow, PTR_VOID * ppStackHigh);

PTR_UInt8 AllocateThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset, uint32_t tlsStorageSize, uint32_t numTlsCells);

PTR_UInt8 GetThreadLocalStorageForDynamicType(uint32_t uTlsTypeOffset);
PTR_UInt8 GetThreadLocalStorage(uint32_t uTlsIndex, uint32_t uTlsStartOffset);

void PushExInfo(ExInfo * pExInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@ public static unsafe object GetThreadStaticBase(IntPtr cookie)
return ThreadStatics.GetThreadStaticBaseForType(*(TypeManagerSlot**)cookie, (int)*((IntPtr*)(cookie) + 1));
}

public static int GetHighestStaticThreadStaticIndex(TypeManagerHandle typeManager)
{
RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.ThreadStaticRegion, out int length);
return length / IntPtr.Size;
}

public static unsafe int ObjectHeaderSize => sizeof(EETypePtr);

[DebuggerGuidedStepThroughAttribute]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public abstract class TypeLoaderCallbacks
{
public abstract TypeManagerHandle GetModuleForMetadataReader(MetadataReader reader);
public abstract bool TryGetConstructedGenericTypeForComponents(RuntimeTypeHandle genericTypeDefinitionHandle, RuntimeTypeHandle[] genericTypeArgumentHandles, out RuntimeTypeHandle runtimeTypeHandle);
public abstract int GetThreadStaticsSizeForDynamicType(int index, out int numTlsCells);
public abstract IntPtr GetThreadStaticGCDescForDynamicType(TypeManagerHandle handle, int index);
public abstract IntPtr GenericLookupFromContextAndSignature(IntPtr context, IntPtr signature, out IntPtr auxResult);
public abstract bool GetRuntimeMethodHandleComponents(RuntimeMethodHandle runtimeMethodHandle, out RuntimeTypeHandle declaringTypeHandle, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgs);
public abstract RuntimeMethodHandle GetRuntimeMethodHandleForComponents(RuntimeTypeHandle declaringTypeHandle, string methodName, RuntimeSignature methodSignature, RuntimeTypeHandle[] genericMethodArgs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,18 @@ private static unsafe object AllocateThreadStaticStorageForType(TypeManagerHandl
// Get a pointer to the beginning of the module's Thread Static section. Then get a pointer
// to the MethodTable that represents a memory map for thread statics storage.
threadStaticRegion = (IntPtr*)RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.ThreadStaticRegion, out length);
return RuntimeImports.RhNewObject(new EETypePtr(threadStaticRegion[typeTlsIndex]));

IntPtr gcDesc;
if (typeTlsIndex < (length / IntPtr.Size))
{
gcDesc = threadStaticRegion[typeTlsIndex];
}
else
{
gcDesc = Internal.Runtime.Augments.RuntimeAugments.TypeLoaderCallbacks.GetThreadStaticGCDescForDynamicType(typeManager, typeTlsIndex);
}

return RuntimeImports.RhNewObject(new EETypePtr(gcDesc));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -459,10 +459,6 @@ internal static unsafe int RhCompatibleReentrantWaitAny(bool alertable, int time
[RuntimeImport(RuntimeLibrary, "RhGetThunkSize")]
internal static extern int RhGetThunkSize();

[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhGetThreadLocalStorageForDynamicType")]
internal static extern IntPtr RhGetThreadLocalStorageForDynamicType(int index, int tlsStorageSize, int numTlsCells);

[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhResolveDispatchOnType")]
// For my life cannot figure out the ordering of modifiers this is expecting.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,6 @@ namespace System.Runtime
[ReflectionBlocked]
public static class TypeLoaderExports
{
public static IntPtr GetThreadStaticsForDynamicType(int index)
{
IntPtr result = RuntimeImports.RhGetThreadLocalStorageForDynamicType(index, 0, 0);
if (result != IntPtr.Zero)
return result;

int numTlsCells;
int tlsStorageSize = RuntimeAugments.TypeLoaderCallbacks.GetThreadStaticsSizeForDynamicType(index, out numTlsCells);
result = RuntimeImports.RhGetThreadLocalStorageForDynamicType(index, tlsStorageSize, numTlsCells);

if (result == IntPtr.Zero)
throw new OutOfMemoryException();

return result;
}

public static unsafe void ActivatorCreateInstanceAny(ref object ptrToData, IntPtr pEETypePtr)
{
EETypePtr pEEType = new EETypePtr(pEETypePtr);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public static unsafe int GetNumVtableSlots(this RuntimeTypeHandle rtth)
return rtth.ToEETypePtr()->NumVtableSlots;
}

public static unsafe TypeManagerHandle GetTypeManager(this RuntimeTypeHandle rtth)
{
return rtth.ToEETypePtr()->TypeManager;
}

public static unsafe IntPtr GetDictionary(this RuntimeTypeHandle rtth)
{
return EETypeCreator.GetDictionary(rtth.ToEETypePtr());
Expand Down Expand Up @@ -148,6 +153,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
IntPtr gcStaticData = IntPtr.Zero;
IntPtr nonGcStaticData = IntPtr.Zero;
IntPtr genericComposition = IntPtr.Zero;
IntPtr threadStaticIndex = IntPtr.Zero;

try
{
Expand Down Expand Up @@ -221,6 +227,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
isByRefLike = false;
componentSize = checked((ushort)state.TypeBeingBuilt.Instantiation.Length);
baseSize = 0;
typeManager = PermanentAllocatedMemoryBlobs.GetPointerToIntPtr(moduleInfo.Handle.GetIntPtrUNSAFE());
}
else
{
Expand Down Expand Up @@ -251,6 +258,8 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
}
Debug.Assert(i == state.GenericVarianceFlags.Length);
}

typeManager = PermanentAllocatedMemoryBlobs.GetPointerToIntPtr(moduleInfo.Handle.GetIntPtrUNSAFE());
}

flags |= (ushort)EETypeFlags.IsDynamicTypeFlag;
Expand Down Expand Up @@ -453,7 +462,6 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
pEEType->OptionalFieldsPtr = (byte*)pEEType + cbEEType;
optionalFields.WriteToEEType(pEEType, cbOptionalFieldsSize);

pEEType->PointerToTypeManager = PermanentAllocatedMemoryBlobs.GetPointerToIntPtr(moduleInfo.Handle.GetIntPtrUNSAFE());
pEEType->DynamicModule = dynamicModulePtr;

// Copy VTable entries from template type
Expand Down Expand Up @@ -639,22 +647,11 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
// create GC desc
if (state.GcDataSize != 0 && state.GcStaticDesc == IntPtr.Zero)
{
if (state.GcStaticEEType != IntPtr.Zero)
{
// Statics are allocated on GC heap
object obj = RuntimeAugments.NewObject(((MethodTable*)state.GcStaticEEType)->ToRuntimeTypeHandle());
gcStaticData = RuntimeAugments.RhHandleAlloc(obj, GCHandleType.Normal);

pEEType->DynamicGcStaticsData = gcStaticData;
}
else
{
int cbStaticGCDesc;
state.GcStaticDesc = CreateStaticGCDesc(state.StaticGCLayout, out state.AllocatedStaticGCDesc, out cbStaticGCDesc);
int cbStaticGCDesc;
state.GcStaticDesc = CreateStaticGCDesc(state.StaticGCLayout, out state.AllocatedStaticGCDesc, out cbStaticGCDesc);
#if GENERICS_FORCE_USG
TestGCDescsForEquality(state.GcStaticDesc, state.NonUniversalStaticGCDesc, cbStaticGCDesc, false);
TestGCDescsForEquality(state.GcStaticDesc, state.NonUniversalStaticGCDesc, cbStaticGCDesc, false);
#endif
}
}

if (state.ThreadDataSize != 0 && state.ThreadStaticDesc == IntPtr.Zero)
Expand Down Expand Up @@ -686,8 +683,21 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo

if (!isGenericEETypeDef && state.ThreadDataSize != 0)
{
// TODO: thread statics
throw new NotSupportedException();
state.ThreadStaticOffset = TypeLoaderEnvironment.Instance.GetNextThreadStaticsOffsetValue(pEEType->TypeManager);

threadStaticIndex = MemoryHelpers.AllocateMemory(IntPtr.Size * 2);
*(IntPtr*)threadStaticIndex = pEEType->PointerToTypeManager;
*(((IntPtr*)threadStaticIndex) + 1) = (IntPtr)state.ThreadStaticOffset;
pEEType->DynamicThreadStaticsIndex = threadStaticIndex;
}

if (!isGenericEETypeDef && state.GcDataSize != 0)
{
// Statics are allocated on GC heap
object obj = RuntimeAugments.NewObject(((MethodTable*)state.GcStaticDesc)->ToRuntimeTypeHandle());
gcStaticData = RuntimeAugments.RhHandleAlloc(obj, GCHandleType.Normal);

pEEType->DynamicGcStaticsData = gcStaticData;
}

if (state.Dictionary != null)
Expand Down Expand Up @@ -723,6 +733,8 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
MemoryHelpers.FreeMemory(nonGcStaticData);
if (writableDataPtr != IntPtr.Zero)
MemoryHelpers.FreeMemory(writableDataPtr);
if (threadStaticIndex != IntPtr.Zero)
MemoryHelpers.FreeMemory(threadStaticIndex);
}
}
}
Expand Down
Loading

0 comments on commit 7496163

Please sign in to comment.