|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace System.Runtime.CompilerServices
{
[StackTraceHidden]
[DebuggerStepThrough]
internal static unsafe partial class StaticsHelpers
{
[LibraryImport(RuntimeHelpers.QCall)]
private static partial void GetThreadStaticsByIndex(ByteRefOnStack result, int index, [MarshalAs(UnmanagedType.Bool)] bool gcStatics);
[LibraryImport(RuntimeHelpers.QCall)]
private static partial void GetThreadStaticsByMethodTable(ByteRefOnStack result, MethodTable* pMT, [MarshalAs(UnmanagedType.Bool)] bool gcStatics);
[Intrinsic]
private static ref byte VolatileReadAsByref(ref IntPtr address) => ref VolatileReadAsByref(ref address);
[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static ref byte GetNonGCStaticBaseSlow(MethodTable* mt)
{
InitHelpers.InitClassSlow(mt);
return ref DynamicStaticsInfo.MaskStaticsPointer(ref VolatileReadAsByref(ref mt->AuxiliaryData->GetDynamicStaticsInfo()._pNonGCStatics));
}
[DebuggerHidden]
private static ref byte GetNonGCStaticBase(MethodTable* mt)
{
ref byte nonGCStaticBase = ref VolatileReadAsByref(ref mt->AuxiliaryData->GetDynamicStaticsInfo()._pNonGCStatics);
if ((((nuint)Unsafe.AsPointer(ref nonGCStaticBase)) & DynamicStaticsInfo.ISCLASSNOTINITED) != 0)
return ref GetNonGCStaticBaseSlow(mt);
else
return ref nonGCStaticBase;
}
[DebuggerHidden]
private static ref byte GetDynamicNonGCStaticBase(DynamicStaticsInfo *dynamicStaticsInfo)
{
ref byte nonGCStaticBase = ref VolatileReadAsByref(ref dynamicStaticsInfo->_pNonGCStatics);
if ((((nuint)Unsafe.AsPointer(ref nonGCStaticBase)) & DynamicStaticsInfo.ISCLASSNOTINITED) != 0)
return ref GetNonGCStaticBaseSlow(dynamicStaticsInfo->_methodTable);
else
return ref nonGCStaticBase;
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static ref byte GetGCStaticBaseSlow(MethodTable* mt)
{
InitHelpers.InitClassSlow(mt);
return ref DynamicStaticsInfo.MaskStaticsPointer(ref VolatileReadAsByref(ref mt->AuxiliaryData->GetDynamicStaticsInfo()._pGCStatics));
}
[DebuggerHidden]
private static ref byte GetGCStaticBase(MethodTable* mt)
{
ref byte gcStaticBase = ref VolatileReadAsByref(ref mt->AuxiliaryData->GetDynamicStaticsInfo()._pGCStatics);
if ((((nuint)Unsafe.AsPointer(ref gcStaticBase)) & DynamicStaticsInfo.ISCLASSNOTINITED) != 0)
return ref GetGCStaticBaseSlow(mt);
else
return ref gcStaticBase;
}
[DebuggerHidden]
private static ref byte GetDynamicGCStaticBase(DynamicStaticsInfo *dynamicStaticsInfo)
{
ref byte gcStaticBase = ref VolatileReadAsByref(ref dynamicStaticsInfo->_pGCStatics);
if ((((nuint)Unsafe.AsPointer(ref gcStaticBase)) & DynamicStaticsInfo.ISCLASSNOTINITED) != 0)
return ref GetGCStaticBaseSlow(dynamicStaticsInfo->_methodTable);
else
return ref gcStaticBase;
}
// Thread static helpers
/// <summary>
/// Return beginning of the object as a reference to byte
/// </summary>
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ref byte GetObjectAsRefByte(object obj)
{
return ref Unsafe.Subtract(ref RuntimeHelpers.GetRawData(obj), sizeof(MethodTable*));
}
[StructLayout(LayoutKind.Sequential)]
internal struct ThreadLocalData
{
internal const int NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY = 2;
internal int _cNonCollectibleTlsData; // Size of offset into the non-collectible TLS array which is valid, NOTE: this is relative to the start of the nonCollectibleTlsArrayData object, not the start of the data in the array
internal int _cCollectibleTlsData; // Size of offset into the TLS array which is valid
private IntPtr _nonCollectibleTlsArrayData_private; // This is object[], but using object[] directly causes the structure to be laid out via auto-layout, which is not what we want.
internal IntPtr* _collectibleTlsArrayData; // Points at the Thread local array data.
internal object[] NonCollectibleTlsArrayData
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return Unsafe.As<IntPtr, object[]>(ref _nonCollectibleTlsArrayData_private);
}
}
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetIndexOffset(int index)
{
return index & 0xFFFFFF;
}
private const int NonCollectibleTLSIndexType = 0;
private const int DirectOnThreadLocalDataTLSIndexType = 2;
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetIndexType(int index)
{
return index >> 24;
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsIndexAllocated(int index)
{
return index != -1;
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static ref byte GetNonGCThreadStaticsByIndexSlow(int index)
{
ByteRef result = default;
GetThreadStaticsByIndex(ByteRefOnStack.Create(ref result), index, false);
return ref result.Get();
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static ref byte GetGCThreadStaticsByIndexSlow(int index)
{
ByteRef result = default;
GetThreadStaticsByIndex(ByteRefOnStack.Create(ref result), index, true);
return ref result.Get();
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static ref byte GetNonGCThreadStaticBaseSlow(MethodTable* mt)
{
ByteRef result = default;
GetThreadStaticsByMethodTable(ByteRefOnStack.Create(ref result), mt, false);
return ref result.Get();
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static ref byte GetGCThreadStaticBaseSlow(MethodTable* mt)
{
ByteRef result = default;
GetThreadStaticsByMethodTable(ByteRefOnStack.Create(ref result), mt, true);
return ref result.Get();
}
[DebuggerHidden]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ref byte GetThreadLocalStaticBaseByIndex(int index, bool gcStatics)
{
ThreadLocalData *t_ThreadStatics = System.Threading.Thread.GetThreadStaticsBase();
int indexOffset = GetIndexOffset(index);
if (GetIndexType(index) == NonCollectibleTLSIndexType)
{
if (t_ThreadStatics->_cNonCollectibleTlsData > GetIndexOffset(index))
{
object? threadStaticObjectNonCollectible = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(t_ThreadStatics->NonCollectibleTlsArrayData), indexOffset - ThreadLocalData.NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY);
if (threadStaticObjectNonCollectible != null)
{
return ref GetObjectAsRefByte(threadStaticObjectNonCollectible);
}
}
}
else if (GetIndexType(index) == DirectOnThreadLocalDataTLSIndexType)
{
return ref Unsafe.Add(ref Unsafe.AsRef<byte>(t_ThreadStatics), indexOffset);
}
else
{
int cCollectibleTlsData = t_ThreadStatics->_cCollectibleTlsData;
if (cCollectibleTlsData > indexOffset)
{
IntPtr* pCollectibleTlsArrayData = t_ThreadStatics->_collectibleTlsArrayData;
pCollectibleTlsArrayData += indexOffset;
IntPtr objHandle = *pCollectibleTlsArrayData;
if (objHandle != IntPtr.Zero)
{
object? threadStaticObject = GCHandle.InternalGet(objHandle);
if (threadStaticObject != null)
{
return ref GetObjectAsRefByte(threadStaticObject);
}
}
}
}
if (gcStatics)
return ref GetGCThreadStaticsByIndexSlow(index);
else
return ref GetNonGCThreadStaticsByIndexSlow(index);
}
[DebuggerHidden]
private static ref byte GetNonGCThreadStaticBase(MethodTable* mt)
{
int index = mt->AuxiliaryData->GetThreadStaticsInfo()._nonGCTlsIndex;
if (IsIndexAllocated(index))
return ref GetThreadLocalStaticBaseByIndex(index, false);
else
return ref GetNonGCThreadStaticBaseSlow(mt);
}
[DebuggerHidden]
private static ref byte GetGCThreadStaticBase(MethodTable* mt)
{
int index = mt->AuxiliaryData->GetThreadStaticsInfo()._gcTlsIndex;
if (IsIndexAllocated(index))
return ref GetThreadLocalStaticBaseByIndex(index, true);
else
return ref GetGCThreadStaticBaseSlow(mt);
}
[DebuggerHidden]
private static ref byte GetDynamicNonGCThreadStaticBase(ThreadStaticsInfo *threadStaticsInfo)
{
int index = threadStaticsInfo->_nonGCTlsIndex;
if (IsIndexAllocated(index))
return ref GetThreadLocalStaticBaseByIndex(index, false);
else
return ref GetNonGCThreadStaticBaseSlow(threadStaticsInfo->_genericStatics._dynamicStatics._methodTable);
}
[DebuggerHidden]
private static ref byte GetDynamicGCThreadStaticBase(ThreadStaticsInfo *threadStaticsInfo)
{
int index = threadStaticsInfo->_gcTlsIndex;
if (IsIndexAllocated(index))
return ref GetThreadLocalStaticBaseByIndex(index, true);
else
return ref GetGCThreadStaticBaseSlow(threadStaticsInfo->_genericStatics._dynamicStatics._methodTable);
}
[DebuggerHidden]
private static ref byte GetOptimizedNonGCThreadStaticBase(int index)
{
return ref GetThreadLocalStaticBaseByIndex(index, false);
}
[DebuggerHidden]
private static ref byte GetOptimizedGCThreadStaticBase(int index)
{
return ref GetThreadLocalStaticBaseByIndex(index, true);
}
}
}
|