Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Common/src/Internal/Runtime/CompilerHelpers/StartupCodeHelpers.cs26
-rw-r--r--src/Common/src/TypeSystem/Ecma/EcmaField.cs3
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/GCStaticEETypeNode.cs4
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/NodeFactory.cs6
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/ReadyToRunHelperNode.cs6
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64Emitter.cs8
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/Target_X64/X64ReadyToRunHelperNode.cs36
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DependencyAnalysis/TypeManagerIndirectionNode.cs1
-rw-r--r--src/Native/Runtime/thread.cpp97
-rw-r--r--src/Native/Runtime/thread.h10
-rw-r--r--src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs88
-rw-r--r--src/System.Private.CoreLib/src/System.Private.CoreLib.csproj1
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/CompilerServices/ClassConstructorRunner.cs10
-rw-r--r--src/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs15
-rw-r--r--src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs5
-rw-r--r--tests/src/Simple/BasicThreading/BasicThreading.cmd12
-rw-r--r--tests/src/Simple/BasicThreading/BasicThreading.cs139
-rw-r--r--tests/src/Simple/BasicThreading/BasicThreading.csproj7
-rw-r--r--tests/src/Simple/BasicThreading/BasicThreading.sh9
-rw-r--r--tests/src/Simple/BasicThreading/no_cpp1
-rw-r--r--tests/src/Simple/BasicThreading/no_unix1
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