diff options
21 files changed, 468 insertions, 17 deletions
diff --git a/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs b/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs index ba8c0c2b1..b20ae0b01 100644 --- a/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs +++ b/src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs @@ -26,7 +26,7 @@ namespace Internal.Runtime.CompilerHelpers for (int i = 0; i < modules.Length; i++) { - InitializeGlobalTablesForModule(modules[i]); + InitializeGlobalTablesForModule(modules[i], i); } // We are now at a stage where we can use GC statics - publish the list of modules @@ -71,16 +71,17 @@ namespace Internal.Runtime.CompilerHelpers /// statics, etc that need initializing. InitializeGlobalTables walks through the modules /// and offers each a chance to initialize its global tables. /// </summary> - private static unsafe void InitializeGlobalTablesForModule(IntPtr typeManager) + private static unsafe void InitializeGlobalTablesForModule(IntPtr typeManager, int moduleIndex) { // Configure the module indirection cell with the newly created TypeManager. This allows EETypes to find // their interface dispatch map tables. int length; - IntPtr* section = (IntPtr*)GetModuleSection(typeManager, ReadyToRunSectionType.TypeManagerIndirection, out length); - *section = typeManager; + TypeManagerSlot* section = (TypeManagerSlot*)RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.TypeManagerIndirection, out length); + section->TypeManager = typeManager; + section->ModuleIndex = moduleIndex; // Initialize statics if any are present - IntPtr staticsSection = GetModuleSection(typeManager, ReadyToRunSectionType.GCStaticRegion, out length); + IntPtr staticsSection = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.GCStaticRegion, out length); if (staticsSection != IntPtr.Zero) { Debug.Assert(length % IntPtr.Size == 0); @@ -88,7 +89,7 @@ namespace Internal.Runtime.CompilerHelpers } // Initialize frozen object segment with GC present - IntPtr frozenObjectSection = GetModuleSection(typeManager, ReadyToRunSectionType.FrozenObjectRegion, out length); + IntPtr frozenObjectSection = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.FrozenObjectRegion, out length); if (frozenObjectSection != IntPtr.Zero) { Debug.Assert(length % IntPtr.Size == 0); @@ -110,7 +111,7 @@ namespace Internal.Runtime.CompilerHelpers int length; // Run eager class constructors if any are present - IntPtr eagerClassConstructorSection = GetModuleSection(typeManager, ReadyToRunSectionType.EagerCctor, out length); + IntPtr eagerClassConstructorSection = RuntimeImports.RhGetModuleSection(typeManager, ReadyToRunSectionType.EagerCctor, out length); if (eagerClassConstructorSection != IntPtr.Zero) { Debug.Assert(length % IntPtr.Size == 0); @@ -158,12 +159,15 @@ namespace Internal.Runtime.CompilerHelpers return len; } - [RuntimeImport(".", "RhpGetModuleSection")] - [MethodImplAttribute(MethodImplOptions.InternalCall)] - private static extern IntPtr GetModuleSection(IntPtr module, ReadyToRunSectionType section, out int length); - [RuntimeImport(".", "RhpCreateTypeManager")] [MethodImplAttribute(MethodImplOptions.InternalCall)] private static unsafe extern IntPtr CreateTypeManager(IntPtr moduleHeader); } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct TypeManagerSlot + { + public IntPtr TypeManager; + public Int32 ModuleIndex; + } } diff --git a/src/Common/src/TypeSystem/Ecma/EcmaField.cs b/src/Common/src/TypeSystem/Ecma/EcmaField.cs index 7055c66e3..430d9b328 100644 --- a/src/Common/src/TypeSystem/Ecma/EcmaField.cs +++ b/src/Common/src/TypeSystem/Ecma/EcmaField.cs @@ -155,8 +155,7 @@ namespace Internal.TypeSystem.Ecma { if (metadataReader.StringComparer.Equals(nameHandle, "ThreadStaticAttribute")) { - // TODO: Thread statics - //flags |= FieldFlags.ThreadStatic; + flags |= FieldFlags.ThreadStatic; } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs index 88d287e88..1028e3d75 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs @@ -91,8 +91,8 @@ namespace ILCompiler.DependencyAnalysis totalSize = Math.Max(totalSize, _target.PointerSize * 3); // minimum GC eetype size is 3 pointers dataBuilder.EmitInt(totalSize); - // This is just so that EEType::Validate doesn't blow up at runtime - dataBuilder.EmitPointerReloc(this); // Related type: itself + // Related type: System.Object. This allows storing an instance of this type in an array of objects. + dataBuilder.EmitPointerReloc(factory.NecessaryTypeSymbol(factory.TypeSystemContext.GetWellKnownType(WellKnownType.Object))); return dataBuilder.ToObjectData(); } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs index 97be12a47..4906ef034 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs @@ -504,7 +504,9 @@ namespace ILCompiler.DependencyAnalysis private static readonly string[][] s_helperEntrypointNames = new string[][] { new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnGCStaticBase" }, - new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnNonGCStaticBase" } + new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnNonGCStaticBase" }, + new string[] { "System.Runtime.CompilerServices", "ClassConstructorRunner", "CheckStaticClassConstructionReturnThreadStaticBase" }, + new string[] { "Internal.Runtime", "ThreadStatics", "GetThreadStaticBaseForType" } }; private ISymbolNode[] _helperEntrypointSymbols; @@ -716,5 +718,7 @@ namespace ILCompiler.DependencyAnalysis { EnsureClassConstructorRunAndReturnGCStaticBase, EnsureClassConstructorRunAndReturnNonGCStaticBase, + EnsureClassConstructorRunAndReturnThreadStaticBase, + GetThreadStaticBaseForType, } } diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs index 477eef6dd..249fc4114 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs @@ -156,6 +156,12 @@ namespace ILCompiler.DependencyAnalysis dependencyList.Add(factory.VirtualMethodUse((MethodDesc)_target), "ReadyToRun Virtual Method Address Load"); return dependencyList; } + else if (_id == ReadyToRunHelperId.GetThreadStaticBase) + { + DependencyList dependencyList = new DependencyList(); + dependencyList.Add(factory.TypeThreadStaticsSymbol((MetadataType)_target), "ReadyToRun Thread Static Storage"); + return dependencyList; + } else { return null; diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs index 7c8714876..82637edde 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs @@ -32,6 +32,14 @@ namespace ILCompiler.DependencyAnalysis.X64 Builder.EmitByte((byte)(0xC0 | (((int)regSrc & 0x07) << 3) | (((int)regDst & 0x07)))); } + public void EmitMOV(Register regDst, int imm32) + { + AddrMode rexAddrMode = new AddrMode(regDst, null, 0, 0, AddrModeSize.Int32); + EmitRexPrefix(regDst, ref rexAddrMode); + Builder.EmitByte((byte)(0xB8 | ((int)regDst & 0x07))); + Builder.EmitInt(imm32); + } + public void EmitLEAQ(Register reg, ISymbolNode symbol, int delta = 0) { AddrMode rexAddrMode = new AddrMode(Register.RAX, null, 0, 0, AddrModeSize.Int64); diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs index b782bd7b0..8a7bea4a1 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs @@ -108,7 +108,41 @@ namespace ILCompiler.DependencyAnalysis break; case ReadyToRunHelperId.GetThreadStaticBase: - encoder.EmitINT3(); + { + MetadataType target = (MetadataType)Target; + ThreadStaticsNode targetNode = factory.TypeThreadStaticsSymbol(target) as ThreadStaticsNode; + int typeTlsIndex = 0; + + // The GetThreadStaticBase helper should be generated only in the compilation module group + // that contains the thread static field because the helper needs the index of the type + // in Thread Static section of the containing module. + // TODO: This needs to be fixed this for the multi-module compilation + Debug.Assert(targetNode != null); + + if (!relocsOnly) + { + // Get index of the targetNode in the Thread Static region + typeTlsIndex = factory.ThreadStaticsRegion.IndexOfEmbeddedObject(targetNode); + } + + // First arg: address of the TypeManager slot that provides the helper with + // information about module index and the type manager instance (which is used + // for initialization on first access). + encoder.EmitLEAQ(encoder.TargetRegister.Arg0, factory.TypeManagerIndirection); + // Second arg: index of the type in the ThreadStatic section of the modules + encoder.EmitMOV(encoder.TargetRegister.Arg1, typeTlsIndex); + + if (!factory.TypeSystemContext.HasLazyStaticConstructor(target)) + { + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.GetThreadStaticBaseForType)); + } + else + { + encoder.EmitLEAQ(encoder.TargetRegister.Arg2, factory.TypeNonGCStaticsSymbol(target)); + // TODO: performance optimization - inline the check verifying whether we need to trigger the cctor + encoder.EmitJMP(factory.HelperEntrypoint(HelperEntrypoint.EnsureClassConstructorRunAndReturnThreadStaticBase)); + } + } break; case ReadyToRunHelperId.GetGCStaticBase: diff --git a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs index 7bd01fb1a..b71d4c103 100644 --- a/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs +++ b/src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs @@ -27,6 +27,7 @@ namespace ILCompiler.DependencyAnalysis objData.DefinedSymbols.Add(this); objData.RequirePointerAlignment(); objData.EmitZeroPointer(); + objData.EmitZeroPointer(); return objData.ToObjectData(); } } diff --git a/src/Native/Runtime/thread.cpp b/src/Native/Runtime/thread.cpp index 34e523f9e..b149b2553 100644 --- a/src/Native/Runtime/thread.cpp +++ b/src/Native/Runtime/thread.cpp @@ -30,6 +30,9 @@ #ifndef DACCESS_COMPILE +EXTERN_C REDHAWK_API void* REDHAWK_CALLCONV RhpHandleAlloc(void* pObject, int type); +EXTERN_C REDHAWK_API void REDHAWK_CALLCONV RhHandleFree(void*); + #ifdef _MSC_VER extern "C" void _ReadWriteBarrier(void); #pragma intrinsic(_ReadWriteBarrier) @@ -286,6 +289,11 @@ void Thread::Construct() m_numDynamicTypesTlsCells = 0; m_pDynamicTypesTlsCells = NULL; +#if CORERT + m_pThreadLocalModuleStatics = NULL; + m_numThreadLocalModuleStatics = 0; +#endif // CORERT + // NOTE: We do not explicitly defer to the GC implementation to initialize the alloc_context. The // alloc_context will be initialized to 0 via the static initialization of tls_CurrentThread. If the // alloc_context ever needs different initialization, a matching change to the tls_CurrentThread @@ -368,6 +376,20 @@ void Thread::Destroy() delete[] m_pDynamicTypesTlsCells; } +#if CORERT + if (m_pThreadLocalModuleStatics != NULL) + { + for (UInt32 i = 0; i < m_numThreadLocalModuleStatics; i++) + { + if (m_pThreadLocalModuleStatics[i] != NULL) + { + RhHandleFree(m_pThreadLocalModuleStatics[i]); + } + } + delete[] m_pThreadLocalModuleStatics; + } +#endif // CORERT + RedhawkGCInterface::ReleaseAllocContext(GetAllocContext()); // Thread::Destroy is called when the thread's "home" fiber dies. We mark the thread as "detached" here @@ -1130,6 +1152,81 @@ FORCEINLINE void Thread::InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame } } +#if CORERT +Object* Thread::GetThreadStaticStorageForModule(UInt32 moduleIndex) +{ + // Return a pointer to the TLS storage if it has already been + // allocated for the specified module. + if (moduleIndex < m_numThreadLocalModuleStatics) + { + Object** threadStaticsStorageHandle = (Object**)m_pThreadLocalModuleStatics[moduleIndex]; + if (threadStaticsStorageHandle != NULL) + { + return *threadStaticsStorageHandle; + } + } + + return NULL; +} + +Boolean Thread::SetThreadStaticStorageForModule(Object * pStorage, UInt32 moduleIndex) +{ + // Grow thread local storage if needed. + if (m_numThreadLocalModuleStatics <= moduleIndex) + { + UInt32 newSize = moduleIndex + 1; + if (newSize < moduleIndex) + { + return FALSE; + } + + PTR_PTR_VOID pThreadLocalModuleStatics = new (nothrow) PTR_VOID[newSize]; + if (pThreadLocalModuleStatics == NULL) + { + return FALSE; + } + + memset(&pThreadLocalModuleStatics[m_numThreadLocalModuleStatics], 0, sizeof(PTR_VOID) * (newSize - m_numThreadLocalModuleStatics)); + + if (m_pThreadLocalModuleStatics != NULL) + { + memcpy(pThreadLocalModuleStatics, m_pThreadLocalModuleStatics, sizeof(PTR_VOID) * m_numThreadLocalModuleStatics); + delete[] m_pThreadLocalModuleStatics; + } + + m_pThreadLocalModuleStatics = pThreadLocalModuleStatics; + m_numThreadLocalModuleStatics = newSize; + } + + void* threadStaticsStorageHandle = RhpHandleAlloc(pStorage, 2 /* Normal */); + if (threadStaticsStorageHandle == NULL) + { + return FALSE; + } + + // Free the existing storage before assigning a new one + if (m_pThreadLocalModuleStatics[moduleIndex] != NULL) + { + RhHandleFree(m_pThreadLocalModuleStatics[moduleIndex]); + } + + m_pThreadLocalModuleStatics[moduleIndex] = threadStaticsStorageHandle; + return TRUE; +} + +COOP_PINVOKE_HELPER(Array*, RhGetThreadStaticStorageForModule, (UInt32 moduleIndex)) +{ + Thread * pCurrentThread = ThreadStore::RawGetCurrentThread(); + return (Array*)pCurrentThread->GetThreadStaticStorageForModule(moduleIndex); +} + +COOP_PINVOKE_HELPER(Boolean, RhSetThreadStaticStorageForModule, (Array * pStorage, UInt32 moduleIndex)) +{ + Thread * pCurrentThread = ThreadStore::RawGetCurrentThread(); + return pCurrentThread->SetThreadStaticStorageForModule((Object*)pStorage, moduleIndex); +} +#endif // CORERT + // Standard calling convention variant and actual implementation for RhpReversePInvokeAttachOrTrapThread EXTERN_C NOINLINE void FASTCALL RhpReversePInvokeAttachOrTrapThread2(ReversePInvokeFrame * pFrame) { diff --git a/src/Native/Runtime/thread.h b/src/Native/Runtime/thread.h index 5d4d39fcf..5eada8318 100644 --- a/src/Native/Runtime/thread.h +++ b/src/Native/Runtime/thread.h @@ -91,6 +91,11 @@ struct ThreadBuffer // Thread Statics Storage for dynamic types UInt32 m_numDynamicTypesTlsCells; PTR_PTR_UInt8 m_pDynamicTypesTlsCells; + +#if CORERT + PTR_PTR_VOID m_pThreadLocalModuleStatics; + UInt32 m_numThreadLocalModuleStatics; +#endif // CORERT }; struct ReversePInvokeFrame @@ -248,6 +253,11 @@ public: bool InlineTryFastReversePInvoke(ReversePInvokeFrame * pFrame); void InlineReversePInvokeReturn(ReversePInvokeFrame * pFrame); + +#if CORERT + Object* GetThreadStaticStorageForModule(UInt32 moduleIndex); + Boolean SetThreadStaticStorageForModule(Object * pStorage, UInt32 moduleIndex); +#endif // CORERT }; #ifndef GCENV_INCLUDED diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs b/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs new file mode 100644 index 000000000..ceff3a00b --- /dev/null +++ b/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs @@ -0,0 +1,88 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime; +using Internal.Runtime.CompilerHelpers; + +namespace Internal.Runtime +{ + /// <summary> + /// This class is used by ReadyToRun helpers to get access to thread static fields of a type + /// and to allocate required TLS memory. + /// </summary> + internal static class ThreadStatics + { + /// <summary> + /// This method is called from a ReadyToRun helper to get base address of thread + /// static storage for the given type. + /// </summary> + internal static unsafe object GetThreadStaticBaseForType(TypeManagerSlot* pModuleData, Int32 typeTlsIndex) + { + // Get the array that holds thread static memory blocks for each type in the given module + Int32 moduleIndex = pModuleData->ModuleIndex; + object[] storage = (object[])RuntimeImports.RhGetThreadStaticStorageForModule(moduleIndex); + + // Check whether thread static storage has already been allocated for this module and type. + if ((storage != null) && (typeTlsIndex < storage.Length) && (storage[typeTlsIndex] != null)) + { + return storage[typeTlsIndex]; + } + + // This the first access to the thread statics of the type corresponding to typeTlsIndex. + // Make sure there is enough storage allocated to hold it. + storage = EnsureThreadStaticStorage(moduleIndex, storage, requiredSize: typeTlsIndex + 1); + + // Allocate an object that will represent a memory block for all thread static fields of the type + object threadStaticBase = AllocateThreadStaticStorageForType(pModuleData->TypeManager, typeTlsIndex); + storage[typeTlsIndex] = threadStaticBase; + + return threadStaticBase; + + } + + /// <summary> + /// if it is required, this method extends thread static storage of the given module + /// to the specified size and then registers the memory with the runtime. + /// </summary> + private static object[] EnsureThreadStaticStorage(Int32 moduleIndex, object[] existingStorage, Int32 requiredSize) + { + if ((existingStorage != null) && (requiredSize < existingStorage.Length)) + { + return existingStorage; + } + + object[] newStorage = new object[requiredSize]; + if (existingStorage != null) + { + Array.Copy(existingStorage, newStorage, existingStorage.Length); + } + + // Install the newly created array as thread static storage for the given module + // on the current thread. This call can fail due to a failure to allocate/extend required + // internal thread specific resources. + if (!RuntimeImports.RhSetThreadStaticStorageForModule(newStorage, moduleIndex)) + { + throw new OutOfMemoryException(); + } + + return newStorage; + } + + /// <summary> + /// This method allocates an object that represents a memory block for all thread static fields of the type + /// that corresponds to the specified TLS index. + /// </summary> + private static unsafe object AllocateThreadStaticStorageForType(IntPtr typeManager, Int32 typeTlsIndex) + { + Int32 length; + IntPtr* threadStaticRegion; + + // Get a pointer to the beginning of the module's Thread Static section. Then get a pointer + // to the EEType 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])); + } + } +} diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 472d017ee..f13af57fe 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -76,6 +76,7 @@ <Compile Include="..\..\Common\src\Internal\Runtime\CompilerHelpers\StartupDebug.cs"> <Link>Internal\Runtime\CompilerHelpers\StartupCode\StartupDebug.cs</Link> </Compile> + <Compile Include="Internal\Runtime\ThreadStatics.cs" /> <Compile Include="Internal\Runtime\CompilerHelpers\StartupCode\StartupCodeHelpers.Extensions.cs" /> <Compile Include="Internal\Runtime\CompilerHelpers\ArrayHelpers.cs" /> <Compile Include="Internal\Runtime\CompilerHelpers\InteropHelpers.cs" /> diff --git a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs index eb37ac750..9abd5023e 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs @@ -8,6 +8,9 @@ using System.Diagnostics; using System.Collections.Generic; using System.Runtime.InteropServices; +using Internal.Runtime; +using Internal.Runtime.CompilerHelpers; + namespace System.Runtime.CompilerServices { // Marked [EagerStaticClassConstruction] because Cctor.GetCctor @@ -49,6 +52,13 @@ namespace System.Runtime.CompilerServices EnsureClassConstructorRun(context); return nonGcStaticBase; } + + private unsafe static object CheckStaticClassConstructionReturnThreadStaticBase(TypeManagerSlot* pModuleData, Int32 typeTlsIndex, StaticClassConstructionContext* context) + { + object threadStaticBase = ThreadStatics.GetThreadStaticBaseForType(pModuleData, typeTlsIndex); + EnsureClassConstructorRun(context); + return threadStaticBase; + } #endif public static unsafe void EnsureClassConstructorRun(StaticClassConstructionContext* pContext) diff --git a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 7beebd523..36cd75b18 100644 --- a/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using Internal.Runtime; namespace System.Runtime { @@ -418,6 +419,10 @@ namespace System.Runtime internal static unsafe extern bool RhFindBlob(IntPtr hOsModule, uint blobId, byte** ppbBlob, uint* pcbBlob); #if CORERT + [RuntimeImport(RuntimeLibrary, "RhpGetModuleSection")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern IntPtr RhGetModuleSection(IntPtr module, ReadyToRunSectionType section, out int length); + internal static uint RhGetLoadedModules(IntPtr[] resultArray) { IntPtr[] loadedModules = Internal.Runtime.CompilerHelpers.StartupCodeHelpers.Modules; @@ -445,6 +450,16 @@ namespace System.Runtime [RuntimeImport(RuntimeLibrary, "RhGetThreadStaticFieldAddress")] internal static unsafe extern byte* RhGetThreadStaticFieldAddress(EETypePtr pEEType, IntPtr fieldCookie); +#if CORERT + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhGetThreadStaticStorageForModule")] + internal static unsafe extern Array RhGetThreadStaticStorageForModule(Int32 moduleIndex); + + [MethodImplAttribute(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhSetThreadStaticStorageForModule")] + internal static unsafe extern bool RhSetThreadStaticStorageForModule(Array storage, Int32 moduleIndex); +#endif + [MethodImplAttribute(MethodImplOptions.InternalCall)] [RuntimeImport(RuntimeLibrary, "RhGetCodeTarget")] internal static extern IntPtr RhGetCodeTarget(IntPtr pCode); diff --git a/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs index e240db0a2..f32429fcc 100644 --- a/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +using Internal.Runtime; namespace System.Runtime { @@ -44,6 +45,10 @@ namespace System.Runtime [RuntimeImport(RuntimeLibrary, "RhpRegisterFrozenSegment")] internal static extern bool RhpRegisterFrozenSegment(IntPtr pSegmentStart, int length); + [RuntimeImport(RuntimeLibrary, "RhpGetModuleSection")] + [MethodImplAttribute(MethodImplOptions.InternalCall)] + internal static extern IntPtr RhGetModuleSection(IntPtr module, ReadyToRunSectionType section, out int length); + // // calls to runtime for allocation // These calls are needed in types which cannot use "new" to allocate and need to do it manually diff --git a/tests/src/Simple/BasicThreading/BasicThreading.cmd b/tests/src/Simple/BasicThreading/BasicThreading.cmd new file mode 100644 index 000000000..aa72a68bb --- /dev/null +++ b/tests/src/Simple/BasicThreading/BasicThreading.cmd @@ -0,0 +1,12 @@ +@echo off +setlocal +"%1\%2" +set ErrorCode=%ERRORLEVEL% +IF "%ErrorCode%"=="100" ( + echo %~n0: pass + EXIT /b 0 +) ELSE ( + echo %~n0: fail + EXIT /b 1 +) +endlocal diff --git a/tests/src/Simple/BasicThreading/BasicThreading.cs b/tests/src/Simple/BasicThreading/BasicThreading.cs new file mode 100644 index 000000000..6d2f9c9d4 --- /dev/null +++ b/tests/src/Simple/BasicThreading/BasicThreading.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading.Tasks; + +class Program +{ + static int Main() + { + SimpleReadWriteThreadStaticTest.Run(42, "SimpleReadWriteThreadStatic"); + ThreadStaticsTestWithTasks.Run(); + return 100; + } +} + +class SimpleReadWriteThreadStaticTest +{ + public static void Run(int intValue, string stringValue) + { + NonGenericReadWriteThreadStaticsTest(intValue, "NonGeneric" + stringValue); + GenericReadWriteThreadStaticsTest(intValue + 1, "Generic" + stringValue); + } + + class NonGenericType + { + [ThreadStatic] + public static int IntValue; + + [ThreadStatic] + public static string StringValue; + } + + class GenericType<T, V> + { + [ThreadStatic] + public static T ValueT; + + [ThreadStatic] + public static V ValueV; + } + + static void NonGenericReadWriteThreadStaticsTest(int intValue, string stringValue) + { + NonGenericType.IntValue = intValue; + NonGenericType.StringValue = stringValue; + + if (NonGenericType.IntValue != intValue) + { + throw new Exception("SimpleReadWriteThreadStaticsTest: wrong integer value: " + NonGenericType.IntValue.ToString()); + } + + if (NonGenericType.StringValue != stringValue) + { + throw new Exception("SimpleReadWriteThreadStaticsTest: wrong string value: " + NonGenericType.StringValue); + } + } + + static void GenericReadWriteThreadStaticsTest(int intValue, string stringValue) + { + GenericType<int, string>.ValueT = intValue; + GenericType<int, string>.ValueV = stringValue; + + if (GenericType<int, string>.ValueT != intValue) + { + throw new Exception("GenericReadWriteThreadStaticsTest1a: wrong integer value: " + GenericType<int, string>.ValueT.ToString()); + } + + if (GenericType<int, string>.ValueV != stringValue) + { + throw new Exception("GenericReadWriteThreadStaticsTest1b: wrong string value: " + GenericType<int, string>.ValueV); + } + + intValue++; + GenericType<int, int>.ValueT = intValue; + GenericType<int, int>.ValueV = intValue + 1; + + if (GenericType<int, int>.ValueT != intValue) + { + throw new Exception("GenericReadWriteThreadStaticsTest2a: wrong integer value: " + GenericType<int, string>.ValueT.ToString()); + } + + if (GenericType<int, int>.ValueV != (intValue + 1)) + { + throw new Exception("GenericReadWriteThreadStaticsTest2b: wrong integer value: " + GenericType<int, string>.ValueV.ToString()); + } + + GenericType<string, string>.ValueT = stringValue + "a"; + GenericType<string, string>.ValueV = stringValue + "b"; + + if (GenericType<string, string>.ValueT != (stringValue + "a")) + { + throw new Exception("GenericReadWriteThreadStaticsTest3a: wrong string value: " + GenericType<string, string>.ValueT); + } + + if (GenericType<string, string>.ValueV != (stringValue + "b")) + { + throw new Exception("GenericReadWriteThreadStaticsTest3b: wrong string value: " + GenericType<string, string>.ValueV); + } + } +} + +class ThreadStaticsTestWithTasks +{ + static object lockObject = new object(); + const int TotalTaskCount = 32; + + public static void Run() + { + Task[] tasks = new Task[TotalTaskCount]; + for (int i = 0; i < tasks.Length; ++i) + { + tasks[i] = Task.Factory.StartNew((param) => + { + int index = (int)param; + int intTestValue = index * 10; + string stringTestValue = "ThreadStaticsTestWithTasks" + index; + + // Try to run the on every other task + if ((index % 2) == 0) + { + lock (lockObject) + { + SimpleReadWriteThreadStaticTest.Run(intTestValue, stringTestValue); + } + } + else + { + SimpleReadWriteThreadStaticTest.Run(intTestValue, stringTestValue); + } + }, i); + } + for (int i = 0; i < tasks.Length; ++i) + { + tasks[i].Wait(); + } + } +} diff --git a/tests/src/Simple/BasicThreading/BasicThreading.csproj b/tests/src/Simple/BasicThreading/BasicThreading.csproj new file mode 100644 index 000000000..0948ddf81 --- /dev/null +++ b/tests/src/Simple/BasicThreading/BasicThreading.csproj @@ -0,0 +1,7 @@ +<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Compile Include="*.cs" /> + </ItemGroup> + + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), SimpleTest.targets))\SimpleTest.targets" /> +</Project> diff --git a/tests/src/Simple/BasicThreading/BasicThreading.sh b/tests/src/Simple/BasicThreading/BasicThreading.sh new file mode 100644 index 000000000..6640f629f --- /dev/null +++ b/tests/src/Simple/BasicThreading/BasicThreading.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +$1/$2 +if [ $? == 100 ]; then + echo pass + exit 0 +else + echo fail + exit 1 +fi diff --git a/tests/src/Simple/BasicThreading/no_cpp b/tests/src/Simple/BasicThreading/no_cpp new file mode 100644 index 000000000..639bcaf8b --- /dev/null +++ b/tests/src/Simple/BasicThreading/no_cpp @@ -0,0 +1 @@ +Skip this test for cpp codegen mode diff --git a/tests/src/Simple/BasicThreading/no_unix b/tests/src/Simple/BasicThreading/no_unix new file mode 100644 index 000000000..e6c22996c --- /dev/null +++ b/tests/src/Simple/BasicThreading/no_unix @@ -0,0 +1 @@ +Doesn't work on OSX.
\ No newline at end of file |