Skip to content

Commit d7924cc

Browse files
VSadovjkotas
andauthored
[NativeAOT] Thin locks (dotnet#79519)
* switch to managed thread ID in Lock * fattening the lock * __declspec(selectany) * few tweaks * fairness * more room for thread ids * remove CurrentNativeThreadId * couple fixes * fix win-arm64 build * win-arm64 build , another try * Apply suggestions from code review Co-authored-by: Jan Kotas <jkotas@microsoft.com> * fix after renaming * do not report successful spin if thread has waited * keep extern and undo mangling of tls_CurrentThread in asm * use SyncTable indexer in less perf-sensitive places. * GetNewHashCode just delegate to shared random * Apply suggestions from code review Co-authored-by: Jan Kotas <jkotas@microsoft.com> * unchecked const conversion * some refactoring comments and typos * min number of spins in the backoff * moved CurrentManagedThreadIdUnchecked to ManagedThreadId * Use `-1` to report success and allow using element #1 in the SyncTable * use threadstatic for managed thread ID * check before calling RhGetProcessCpuCount * use 0 as default thread ID Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent ca873ea commit d7924cc

16 files changed

Lines changed: 744 additions & 306 deletions

File tree

src/coreclr/nativeaot/Runtime/MiscHelpers.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ EXTERN_C NATIVEAOT_API void __cdecl RhSpinWait(int32_t iterations)
5151
ASSERT(iterations > 0);
5252

5353
// limit the spin count in coop mode.
54-
ASSERT_MSG(iterations <= 10000 || !ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode(),
54+
ASSERT_MSG(iterations <= 1024 || !ThreadStore::GetCurrentThread()->IsCurrentThreadInCooperativeMode(),
5555
"This is too long wait for coop mode. You must p/invoke with GC transition.");
5656

5757
YieldProcessorNormalizationInfo normalizationInfo;
58-
YieldProcessorNormalizedForPreSkylakeCount(normalizationInfo, iterations);
58+
YieldProcessorNormalized(normalizationInfo, iterations);
5959
}
6060

6161
// Yield the cpu to another thread ready to process, if one is available.

src/coreclr/nativeaot/Runtime/thread.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,10 @@ void Thread::Construct()
260260
// alloc_context ever needs different initialization, a matching change to the tls_CurrentThread
261261
// static initialization will need to be made.
262262

263-
m_uPalThreadIdForLogging = PalGetCurrentThreadIdForLogging();
263+
m_pTransitionFrame = TOP_OF_STACK_MARKER;
264+
m_pDeferredTransitionFrame = TOP_OF_STACK_MARKER;
265+
m_hPalThread = INVALID_HANDLE_VALUE;
266+
264267
m_threadId.SetToCurrentThread();
265268

266269
HANDLE curProcessPseudo = PalGetCurrentProcess();
@@ -328,7 +331,7 @@ bool Thread::CatchAtSafePoint()
328331

329332
uint64_t Thread::GetPalThreadIdForLogging()
330333
{
331-
return m_uPalThreadIdForLogging;
334+
return *(uint64_t*)&m_threadId;
332335
}
333336

334337
bool Thread::IsCurrentThread()

src/coreclr/nativeaot/Runtime/thread.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ struct ThreadBuffer
9595
PTR_VOID m_pStackLow;
9696
PTR_VOID m_pStackHigh;
9797
PTR_UInt8 m_pTEB; // Pointer to OS TEB structure for this thread
98-
uint64_t m_uPalThreadIdForLogging; // @TODO: likely debug-only
99-
EEThreadId m_threadId;
98+
EEThreadId m_threadId; // OS thread ID
10099
PTR_VOID m_pThreadStressLog; // pointer to head of thread's StressLogChunks
101100
NATIVE_CONTEXT* m_interruptedContext; // context for an asynchronously interrupted thread.
102101
#ifdef FEATURE_SUSPEND_REDIRECTION
@@ -192,7 +191,7 @@ class Thread : private ThreadBuffer
192191
gc_alloc_context * GetAllocContext(); // @TODO: I would prefer to not expose this in this way
193192

194193
#ifndef DACCESS_COMPILE
195-
uint64_t GetPalThreadIdForLogging();
194+
uint64_t GetPalThreadIdForLogging();
196195
bool IsCurrentThread();
197196

198197
void GcScanRoots(void * pfnEnumCallback, void * pvCallbackData);

src/coreclr/nativeaot/Runtime/threadstore.cpp

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -415,20 +415,9 @@ COOP_PINVOKE_HELPER(void, RhpCancelThreadAbort, (void* thread))
415415

416416
C_ASSERT(sizeof(Thread) == sizeof(ThreadBuffer));
417417

418-
EXTERN_C DECLSPEC_THREAD ThreadBuffer tls_CurrentThread;
419-
DECLSPEC_THREAD ThreadBuffer tls_CurrentThread =
420-
{
421-
{ 0 }, // m_rgbAllocContextBuffer
422-
Thread::TSF_Unknown, // m_ThreadStateFlags
423-
TOP_OF_STACK_MARKER, // m_pTransitionFrame
424-
TOP_OF_STACK_MARKER, // m_pDeferredTransitionFrame
425-
0, // m_pCachedTransitionFrame
426-
0, // m_pNext
427-
INVALID_HANDLE_VALUE, // m_hPalThread
428-
0, // m_ppvHijackedReturnAddressLocation
429-
0, // m_pvHijackedReturnAddress
430-
0, // all other fields are initialized by zeroes
431-
};
418+
#ifndef _MSC_VER
419+
__thread ThreadBuffer tls_CurrentThread;
420+
#endif
432421

433422
EXTERN_C ThreadBuffer* RhpGetThread()
434423
{

src/coreclr/nativeaot/Runtime/threadstore.inl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
EXTERN_C DECLSPEC_THREAD ThreadBuffer tls_CurrentThread;
4+
#ifdef _MSC_VER
5+
// a workaround to prevent tls_CurrentThread from becoming dynamically checked/initialized.
6+
EXTERN_C __declspec(selectany) __declspec(thread) ThreadBuffer tls_CurrentThread;
7+
#else
8+
EXTERN_C __thread ThreadBuffer tls_CurrentThread;
9+
#endif
510

611
// static
712
inline Thread * ThreadStore::RawGetCurrentThread()

src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/SynchronizedMethodHelpers.cs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,42 +14,68 @@ internal static class SynchronizedMethodHelpers
1414
private static void MonitorEnter(object obj, ref bool lockTaken)
1515
{
1616
// Inlined Monitor.Enter with a few tweaks
17-
Lock lck = Monitor.GetLock(obj);
17+
int resultOrIndex = ObjectHeader.Acquire(obj);
18+
if (resultOrIndex < 0)
19+
{
20+
lockTaken = true;
21+
return;
22+
}
23+
24+
Lock lck = resultOrIndex == 0 ?
25+
ObjectHeader.GetLockObject(obj) :
26+
SyncTable.GetLockObject(resultOrIndex);
27+
1828
if (lck.TryAcquire(0))
1929
{
2030
lockTaken = true;
2131
return;
2232
}
33+
2334
Monitor.TryAcquireContended(lck, obj, Timeout.Infinite);
2435
lockTaken = true;
2536
}
2637
private static void MonitorExit(object obj, ref bool lockTaken)
2738
{
2839
// Inlined Monitor.Exit with a few tweaks
29-
if (!lockTaken) return;
30-
Monitor.GetLock(obj).Release();
40+
if (!lockTaken)
41+
return;
42+
43+
ObjectHeader.Release(obj);
3144
lockTaken = false;
3245
}
3346

3447
private static void MonitorEnterStatic(IntPtr pEEType, ref bool lockTaken)
3548
{
3649
// Inlined Monitor.Enter with a few tweaks
3750
object obj = GetStaticLockObject(pEEType);
38-
Lock lck = Monitor.GetLock(obj);
51+
int resultOrIndex = ObjectHeader.Acquire(obj);
52+
if (resultOrIndex < 0)
53+
{
54+
lockTaken = true;
55+
return;
56+
}
57+
58+
Lock lck = resultOrIndex == 0 ?
59+
ObjectHeader.GetLockObject(obj) :
60+
SyncTable.GetLockObject(resultOrIndex);
61+
3962
if (lck.TryAcquire(0))
4063
{
4164
lockTaken = true;
4265
return;
4366
}
67+
4468
Monitor.TryAcquireContended(lck, obj, Timeout.Infinite);
4569
lockTaken = true;
4670
}
4771
private static void MonitorExitStatic(IntPtr pEEType, ref bool lockTaken)
4872
{
4973
// Inlined Monitor.Exit with a few tweaks
50-
if (!lockTaken) return;
74+
if (!lockTaken)
75+
return;
76+
5177
object obj = GetStaticLockObject(pEEType);
52-
Monitor.GetLock(obj).Release();
78+
ObjectHeader.Release(obj);
5379
lockTaken = false;
5480
}
5581

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.NativeAot.Unix.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ namespace System
99
{
1010
public static partial class Environment
1111
{
12-
internal static int CurrentNativeThreadId => ManagedThreadId.Current;
13-
1412
public static long TickCount64 => (long)RuntimeImports.RhpGetTickCount64();
1513

1614
[DoesNotReturn]

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.NativeAot.Windows.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ namespace System
77
{
88
public static partial class Environment
99
{
10-
internal static int CurrentNativeThreadId => unchecked((int)Interop.Kernel32.GetCurrentThreadId());
11-
1210
public static long TickCount64 => (long)Interop.Kernel32.GetTickCount64();
1311

1412
[DoesNotReturn]

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.NativeAot.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,17 +94,9 @@ public static object GetObjectValue(object? obj)
9494
return RuntimeImports.RhCompareObjectContentsAndPadding(o1, o2);
9595
}
9696

97-
[ThreadStatic]
98-
private static int t_hashSeed;
99-
10097
internal static int GetNewHashCode()
10198
{
102-
int multiplier = Environment.CurrentManagedThreadId * 4 + 5;
103-
// Every thread has its own generator for hash codes so that we won't get into a situation
104-
// where two threads consistently give out the same hash codes.
105-
// Choice of multiplier guarantees period of 2**32 - see Knuth Vol 2 p16 (3.2.1.2 Theorem A).
106-
t_hashSeed = t_hashSeed * multiplier + 1;
107-
return t_hashSeed;
99+
return Random.Shared.Next();
108100
}
109101

110102
public static unsafe int GetHashCode(object o)
@@ -228,6 +220,9 @@ internal static unsafe ushort GetElementSize(this Array array)
228220
internal static unsafe MethodTable* GetMethodTable(this object obj)
229221
=> obj.m_pEEType;
230222

223+
internal static unsafe ref MethodTable* GetMethodTableRef(this object obj)
224+
=> ref obj.m_pEEType;
225+
231226
internal static unsafe EETypePtr GetEETypePtr(this object obj)
232227
=> new EETypePtr(obj.m_pEEType);
233228

src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ private static void SerializeExceptionsForDump(Exception currentException, IntPt
464464
LowLevelList<Exception> exceptions = new LowLevelList<Exception>(curThreadExceptions);
465465
LowLevelList<Exception> nonThrownInnerExceptions = new LowLevelList<Exception>();
466466

467-
uint currentThreadId = (uint)Environment.CurrentNativeThreadId;
467+
uint currentThreadId = (uint)Environment.CurrentManagedThreadId;
468468

469469
// Reset nesting levels for exceptions on this thread that might not be currently in flight
470470
foreach (KeyValuePair<Exception, ExceptionData> item in s_exceptionDataTable)

0 commit comments

Comments
 (0)