From 7e023b2240046bf7bfbcc63a6a943b6aaf4a7e0a Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Tue, 10 Sep 2024 13:27:55 -0700 Subject: [PATCH 1/8] Remove some implicit Helper Method Frames (HMF) Remove FCThrow in locations where explicit HMF have been removed. Convert GC.GetGeneration() RuntimeHelpers.EnsureSufficientExecutionStack() RuntimeHelpers.AllocTailCallArgBuffer() Thread.IsBackground getter Thread.IsThreadPoolThread property --- .../src/System/GC.CoreCLR.cs | 10 ++- .../RuntimeHelpers.CoreCLR.cs | 25 ++++-- .../src/System/Threading/Thread.CoreCLR.cs | 35 ++++++-- src/coreclr/vm/comsynchronizable.cpp | 90 +++++++++++-------- src/coreclr/vm/comsynchronizable.h | 7 +- src/coreclr/vm/comutilnative.cpp | 10 +-- src/coreclr/vm/comutilnative.h | 2 +- src/coreclr/vm/ecalllist.h | 8 +- src/coreclr/vm/fcall.h | 23 ----- src/coreclr/vm/qcallentrypoints.cpp | 3 + src/coreclr/vm/reflectioninvocation.cpp | 32 ++----- src/coreclr/vm/reflectioninvocation.h | 1 - src/coreclr/vm/tailcallhelp.cpp | 18 +--- src/coreclr/vm/tailcallhelp.h | 2 +- src/coreclr/vm/threads.cpp | 4 +- src/coreclr/vm/threads.h | 2 +- 16 files changed, 131 insertions(+), 141 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index f8246642b9e61b..7154d730dded48 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -155,12 +155,16 @@ public static void RemoveMemoryPressure(long bytesAllocated) _RemoveMemoryPressure((ulong)bytesAllocated); } - // Returns the generation that obj is currently in. // - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern int GetGeneration(object obj); + public static int GetGeneration(object obj) + { + ArgumentNullException.ThrowIfNull(obj); + return GetGenerationWorker(obj); + } + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int GetGenerationWorker(object obj); // Forces a collection of all generations from 0 through Generation. // diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 77311a0242172b..1b1f550ae53e1a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -301,13 +301,18 @@ public static int OffsetToStringData // This method ensures that there is sufficient stack to execute the average Framework function. // If there is not enough stack, then it throws System.InsufficientExecutionStackException. - // Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack. - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void EnsureSufficientExecutionStack(); + // Note: this method is not to be confused with ProbeForSufficientStack. + public static void EnsureSufficientExecutionStack() + { + if (!TryEnsureSufficientExecutionStack()) + { + throw new InsufficientExecutionStackException(); + } + } // This method ensures that there is sufficient stack to execute the average Framework function. // If there is not enough stack, then it return false. - // Note: this method is not part of the CER support, and is not to be confused with ProbeForSufficientStack. + // Note: this method is not to be confused with ProbeForSufficientStack. [MethodImpl(MethodImplOptions.InternalCall)] public static extern bool TryEnsureSufficientExecutionStack(); @@ -469,7 +474,17 @@ public static IntPtr AllocateTypeAssociatedMemory(Type type, int size) private static partial IntPtr AllocateTypeAssociatedMemory(QCallTypeHandle type, uint size); [MethodImpl(MethodImplOptions.InternalCall)] - private static extern IntPtr AllocTailCallArgBuffer(int size, IntPtr gcDesc); + private static extern IntPtr AllocTailCallArgBufferWorker(int size, IntPtr gcDesc); + + private static IntPtr AllocTailCallArgBuffer(int size, IntPtr gcDesc) + { + IntPtr buffer = AllocTailCallArgBufferWorker(size, gcDesc); + if (buffer == IntPtr.Zero) + { + throw new OutOfMemoryException(); + } + return buffer; + } [MethodImpl(MethodImplOptions.InternalCall)] private static extern unsafe TailCallTls* GetTailCallInfo(IntPtr retAddrSlot, IntPtr* retAddr); diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 08b3ae8944d630..5dd5a02be3dedb 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -194,7 +194,12 @@ partial void ThreadNameChanged(string? value) /// public bool IsBackground { - get => GetIsBackground(); + get + { + Interop.BOOL res = GetIsBackground(GetNativeHandle()); + GC.KeepAlive(this); + return res != Interop.BOOL.FALSE; + } set { SetIsBackground(GetNativeHandle(), value ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); @@ -206,21 +211,35 @@ public bool IsBackground } } - [MethodImpl(MethodImplOptions.InternalCall)] - private extern bool GetIsBackground(); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetIsBackground")] + private static partial Interop.BOOL GetIsBackground(ThreadHandle t); [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetIsBackground")] private static partial void SetIsBackground(ThreadHandle t, Interop.BOOL value); /// Returns true if the thread is a threadpool thread. - public extern bool IsThreadPoolThread + public bool IsThreadPoolThread { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - [MethodImpl(MethodImplOptions.InternalCall)] - internal set; + get + { + Interop.BOOL res = GetIsThreadPoolThread(GetNativeHandle()); + GC.KeepAlive(this); + return res != Interop.BOOL.FALSE; + } + internal set + { + Debug.Assert(value); + SetIsThreadPoolThread(GetNativeHandle()); + GC.KeepAlive(this); + } } + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetIsThreadPoolThread")] + private static partial Interop.BOOL GetIsThreadPoolThread(ThreadHandle t); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetIsThreadPoolThread")] + private static partial void SetIsThreadPoolThread(ThreadHandle t); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetPriority")] [return: MarshalAs(UnmanagedType.Bool)] private static partial void SetPriority(ObjectHandleOnStack thread, int priority); diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 7f1751aaa15f58..8903d34209f40f 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -427,24 +427,6 @@ extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t) END_QCALL; } -// Return whether or not this is a background thread. -FCIMPL1(FC_BOOL_RET, ThreadNative::GetIsBackground, ThreadBaseObject* pThisUNSAFE) -{ - FCALL_CONTRACT; - - if (pThisUNSAFE==NULL) - FCThrowRes(kNullReferenceException, W("NullReference_This")); - - // validate the thread - Thread *thread = pThisUNSAFE->GetInternal(); - - if (ThreadIsDead(thread)) - FCThrowRes(kThreadStateException, W("ThreadState_Dead_State")); - - FC_RETURN_BOOL(thread->IsBackground()); -} -FCIMPLEND - // Deliver the state of the thread as a consistent set of bits. // Duplicate logic in DacDbiInterfaceImpl::GetPartialUserState() extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread) @@ -729,6 +711,30 @@ FCIMPL1(void, ThreadNative::Finalize, ThreadBaseObject* pThisUNSAFE) } FCIMPLEND +// Get whether or not this is a background thread. +extern "C" BOOL QCALLTYPE ThreadNative_GetIsBackground(QCall::ThreadHandle thread) +{ + CONTRACTL + { + QCALL_CHECK; + PRECONDITION(thread != NULL); + } + CONTRACTL_END; + + BOOL res = FALSE; + + BEGIN_QCALL; + + if (ThreadIsDead(thread)) + COMPlusThrow(kThreadStateException, W("ThreadState_Dead_State")); + + res = thread->IsBackground(); + + END_QCALL; + + return res; +} + // Set whether or not this is a background thread. extern "C" void QCALLTYPE ThreadNative_SetIsBackground(QCall::ThreadHandle thread, BOOL value) { @@ -795,41 +801,49 @@ void ThreadNative::InformThreadNameChange(Thread* pThread, LPCWSTR name, INT32 l #endif // DEBUGGING_SUPPORTED } -FCIMPL1(FC_BOOL_RET, ThreadNative::IsThreadpoolThread, ThreadBaseObject* thread) +// Get whether or not this is a threadpool thread. +extern "C" BOOL QCALLTYPE ThreadNative_GetIsThreadPoolThread(QCall::ThreadHandle thread) { - FCALL_CONTRACT; + CONTRACTL + { + QCALL_CHECK; + PRECONDITION(thread != NULL); + } + CONTRACTL_END; - if (thread==NULL) - FCThrowRes(kNullReferenceException, W("NullReference_This")); + BOOL res = FALSE; - Thread *pThread = thread->GetInternal(); + BEGIN_QCALL; - if (pThread == NULL) - FCThrowRes(kThreadStateException, W("ThreadState_Dead_State")); + if (ThreadIsDead(thread)) + COMPlusThrow(kThreadStateException, W("ThreadState_Dead_State")); - BOOL ret = pThread->IsThreadPoolThread(); + res = thread->IsThreadPoolThread(); - FC_GC_POLL_RET(); + END_QCALL; - FC_RETURN_BOOL(ret); + return res; } -FCIMPLEND -FCIMPL1(void, ThreadNative::SetIsThreadpoolThread, ThreadBaseObject* thread) +// Set thread as a threadpool thread. +extern "C" void QCALLTYPE ThreadNative_SetIsThreadPoolThread(QCall::ThreadHandle thread) { - FCALL_CONTRACT; + CONTRACTL + { + QCALL_CHECK; + PRECONDITION(thread != NULL); + } + CONTRACTL_END; - if (thread == NULL) - FCThrowResVoid(kNullReferenceException, W("NullReference_This")); + BEGIN_QCALL; - Thread *pThread = thread->GetInternal(); + if (ThreadIsDead(thread)) + COMPlusThrow(kThreadStateException, W("ThreadState_Dead_State")); - if (pThread == NULL) - FCThrowResVoid(kThreadStateException, W("ThreadState_Dead_State")); + thread->SetIsThreadPoolThread(); - pThread->SetIsThreadPoolThread(); + END_QCALL; } -FCIMPLEND FCIMPL0(INT32, ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration) { diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index b7c64c529084f8..08fce25ef92768 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -52,12 +52,8 @@ friend class ThreadBaseObject; ThreadAbortRequested = 128, }; - static FCDECL1(FC_BOOL_RET, GetIsBackground, ThreadBaseObject* pThisUNSAFE); - static FCDECL0(INT32, GetOptimalMaxSpinWaitsPerSpinIteration); static FCDECL1(void, Finalize, ThreadBaseObject* pThis); - static FCDECL1(FC_BOOL_RET,IsThreadpoolThread, ThreadBaseObject* thread); - static FCDECL1(void, SetIsThreadpoolThread, ThreadBaseObject* thread); static void Start(Thread* pNewThread, int threadStackSize, int priority, PCWSTR pThreadName); static void InformThreadNameChange(Thread* pThread, LPCWSTR name, INT32 len); @@ -76,6 +72,7 @@ friend class ThreadBaseObject; extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, PCWSTR pThreadName); extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack thread, INT32 iPriority); extern "C" void QCALLTYPE ThreadNative_GetCurrentThread(QCall::ObjectHandleOnStack thread); +extern "C" BOOL QCALLTYPE ThreadNative_GetIsBackground(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_SetIsBackground(QCall::ThreadHandle thread, BOOL value); extern "C" void QCALLTYPE ThreadNative_InformThreadNameChange(QCall::ThreadHandle thread, LPCWSTR name, INT32 len); extern "C" BOOL QCALLTYPE ThreadNative_YieldThread(); @@ -91,6 +88,8 @@ extern "C" INT32 QCALLTYPE ThreadNative_SetApartmentState(QCall::ObjectHandleOnS extern "C" BOOL QCALLTYPE ThreadNative_Join(QCall::ObjectHandleOnStack thread, INT32 Timeout); extern "C" void QCALLTYPE ThreadNative_Abort(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_ResetAbort(); +extern "C" BOOL QCALLTYPE ThreadNative_GetIsThreadPoolThread(QCall::ThreadHandle thread); +extern "C" void QCALLTYPE ThreadNative_SetIsThreadPoolThread(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_SpinWait(INT32 iterations); extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_Sleep(INT32 iTime); diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index f15b1085ebab4e..b25ccb1a2a6318 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -688,19 +688,15 @@ extern "C" int QCALLTYPE GCInterface_WaitForFullGCComplete(int millisecondsTimeo return result; } -/*================================GetGeneration================================= +/*================================GetGenerationWorker================================= **Action: Returns the generation in which args->obj is found. **Returns: The generation in which args->obj is found. **Arguments: args->obj -- The object to locate. -**Exceptions: ArgumentException if args->obj is null. ==============================================================================*/ -FCIMPL1(int, GCInterface::GetGeneration, Object* objUNSAFE) +FCIMPL1(int, GCInterface::GetGenerationWorker, Object* objUNSAFE) { FCALL_CONTRACT; - - if (objUNSAFE == NULL) - FCThrowArgumentNull(W("obj")); - + _ASSERTE(objUNSAFE != NULL); int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE); FC_GC_POLL_RET(); return result; diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index ef41239a6bb0f3..17f4cd96a51b53 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -170,7 +170,7 @@ class GCInterface { static FCDECL1(void, SetLOHCompactionMode, int newLOHCompactionyMode); static FCDECL2(FC_BOOL_RET, RegisterForFullGCNotification, UINT32 gen2Percentage, UINT32 lohPercentage); static FCDECL0(FC_BOOL_RET, CancelFullGCNotification); - static FCDECL1(int, GetGeneration, Object* objUNSAFE); + static FCDECL1(int, GetGenerationWorker, Object* objUNSAFE); static FCDECL0(UINT64, GetSegmentSize); static FCDECL0(int, GetLastGCPercentTimeInGC); static FCDECL1(UINT64, GetGenerationSize, int gen); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index b1f475b3b16406..4ceb10b666af94 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -299,9 +299,6 @@ FCFuncEnd() FCFuncStart(gThreadFuncs) FCFuncElement("InternalFinalize", ThreadNative::Finalize) - FCFuncElement("GetIsBackground", ThreadNative::GetIsBackground) - FCFuncElement("get_IsThreadPoolThread", ThreadNative::IsThreadpoolThread) - FCFuncElement("set_IsThreadPoolThread", ThreadNative::SetIsThreadpoolThread) FCFuncElement("get_OptimalMaxSpinWaitsPerSpinIteration", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration) FCFuncEnd() @@ -340,7 +337,7 @@ FCFuncStart(gGCInterfaceFuncs) FCFuncElement("GetSegmentSize", GCInterface::GetSegmentSize) FCFuncElement("GetLastGCPercentTimeInGC", GCInterface::GetLastGCPercentTimeInGC) FCFuncElement("GetGenerationSize", GCInterface::GetGenerationSize) - FCFuncElement("GetGeneration", GCInterface::GetGeneration) + FCFuncElement("GetGenerationWorker", GCInterface::GetGenerationWorker) FCFuncElement("GetMaxGeneration", GCInterface::GetMaxGeneration) FCFuncElement("_SuppressFinalize", GCInterface::SuppressFinalize) @@ -396,9 +393,8 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate) FCFuncElement("TryGetHashCode", ObjectNative::TryGetHashCode) FCFuncElement("ContentEquals", ObjectNative::ContentEquals) - FCFuncElement("EnsureSufficientExecutionStack", ReflectionInvocation::EnsureSufficientExecutionStack) FCFuncElement("TryEnsureSufficientExecutionStack", ReflectionInvocation::TryEnsureSufficientExecutionStack) - FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) + FCFuncElement("AllocTailCallArgBufferWorker", TailCallHelp::AllocTailCallArgBufferWorker) FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo) FCFuncElement("Box", JIT_Box) FCFuncElement("Unbox_Nullable", JIT_Unbox_Nullable) diff --git a/src/coreclr/vm/fcall.h b/src/coreclr/vm/fcall.h index a8b11854e795e2..5982611e882952 100644 --- a/src/coreclr/vm/fcall.h +++ b/src/coreclr/vm/fcall.h @@ -1242,29 +1242,9 @@ struct FCSigCheck { return 0; \ } -//============================================================================================== -// Like FCThrow but can be used for a VOID-returning FCall. The only -// difference is in the "return" statement. -//============================================================================================== -#define FCThrowVoid(reKind) FCThrowExVoid(reKind, 0, 0, 0, 0) - -//============================================================================================== -// This version lets you attach a message with inserts (similar to -// COMPlusThrow()). -//============================================================================================== -#define FCThrowExVoid(reKind, resID, arg1, arg2, arg3) \ - { \ - while (NULL == \ - __FCThrow(__me, reKind, resID, arg1, arg2, arg3)) {}; \ - return; \ - } - // Use FCThrowRes to throw an exception with a localized error message from the // ResourceManager in managed code. #define FCThrowRes(reKind, resourceName) FCThrowArgumentEx(reKind, NULL, resourceName) -#define FCThrowArgumentNull(argName) FCThrowArgumentEx(kArgumentNullException, argName, NULL) -#define FCThrowArgumentOutOfRange(argName, message) FCThrowArgumentEx(kArgumentOutOfRangeException, argName, message) -#define FCThrowArgument(argName, message) FCThrowArgumentEx(kArgumentException, argName, message) #define FCThrowArgumentEx(reKind, argName, resourceName) \ { \ @@ -1276,9 +1256,6 @@ struct FCSigCheck { // Use FCThrowRes to throw an exception with a localized error message from the // ResourceManager in managed code. #define FCThrowResVoid(reKind, resourceName) FCThrowArgumentVoidEx(reKind, NULL, resourceName) -#define FCThrowArgumentNullVoid(argName) FCThrowArgumentVoidEx(kArgumentNullException, argName, NULL) -#define FCThrowArgumentOutOfRangeVoid(argName, message) FCThrowArgumentVoidEx(kArgumentOutOfRangeException, argName, message) -#define FCThrowArgumentVoid(argName, message) FCThrowArgumentVoidEx(kArgumentException, argName, message) #define FCThrowArgumentVoidEx(reKind, argName, resourceName) \ { \ diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 4181a027e669c8..2f08da0154871f 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -239,6 +239,7 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_Start) DllImportEntry(ThreadNative_SetPriority) DllImportEntry(ThreadNative_GetCurrentThread) + DllImportEntry(ThreadNative_GetIsBackground) DllImportEntry(ThreadNative_SetIsBackground) DllImportEntry(ThreadNative_InformThreadNameChange) DllImportEntry(ThreadNative_YieldThread) @@ -252,6 +253,8 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_Join) DllImportEntry(ThreadNative_Abort) DllImportEntry(ThreadNative_ResetAbort) + DllImportEntry(ThreadNative_GetIsThreadPoolThread) + DllImportEntry(ThreadNative_SetIsThreadPoolThread) DllImportEntry(ThreadNative_SpinWait) DllImportEntry(ThreadNative_Interrupt) DllImportEntry(ThreadNative_Sleep) diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index d4bb3f528b86ea..2655334d779b70 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -1282,7 +1282,7 @@ extern "C" BOOL QCALLTYPE RuntimeFieldHandle_GetRVAFieldInfo(FieldDesc* pField, Module* pModule = pField->GetModule(); *address = pModule->GetRvaField(pField->GetOffset()); *size = pField->LoadSize(); - + ret = TRUE; } @@ -1438,37 +1438,17 @@ FCIMPL1(void, ReflectionInvocation::PrepareDelegate, Object* delegateUNSAFE) } FCIMPLEND -// This method checks to see if there is sufficient stack to execute the average Framework method. -// If there is not, then it throws System.InsufficientExecutionStackException. The limit for each -// thread is precomputed when the thread is created. -FCIMPL0(void, ReflectionInvocation::EnsureSufficientExecutionStack) -{ - FCALL_CONTRACT; - - Thread *pThread = GetThread(); - - // We use the address of a local variable as our "current stack pointer", which is - // plenty close enough for the purposes of this method. - UINT_PTR current = reinterpret_cast(&pThread); - UINT_PTR limit = pThread->GetCachedStackSufficientExecutionLimit(); - - if (current < limit) - { - FCThrowVoid(kInsufficientExecutionStackException); - } -} -FCIMPLEND - -// As with EnsureSufficientExecutionStack, this method checks and returns whether there is -// sufficient stack to execute the average Framework method, but rather than throwing, -// it simply returns a Boolean: true for sufficient stack space, otherwise false. +// This method checks and returns whether there is sufficient stack to execute the +// average Framework method, but rather than throwing, it simply returns a +// Boolean: true for sufficient stack space, otherwise false. FCIMPL0(FC_BOOL_RET, ReflectionInvocation::TryEnsureSufficientExecutionStack) { FCALL_CONTRACT; Thread *pThread = GetThread(); - // Same logic as EnsureSufficientExecutionStack + // We use the address of a local variable as our "current stack pointer", which is + // plenty close enough for the purposes of this method. UINT_PTR current = reinterpret_cast(&pThread); UINT_PTR limit = pThread->GetCachedStackSufficientExecutionLimit(); diff --git a/src/coreclr/vm/reflectioninvocation.h b/src/coreclr/vm/reflectioninvocation.h index ff20d72d870f9c..f2879590f2d96e 100644 --- a/src/coreclr/vm/reflectioninvocation.h +++ b/src/coreclr/vm/reflectioninvocation.h @@ -47,7 +47,6 @@ class ReflectionInvocation { public: static FCDECL1(void, PrepareDelegate, Object* delegateUNSAFE); - static FCDECL0(void, EnsureSufficientExecutionStack); static FCDECL0(FC_BOOL_RET, TryEnsureSufficientExecutionStack); // TypedReference functions, should go somewhere else diff --git a/src/coreclr/vm/tailcallhelp.cpp b/src/coreclr/vm/tailcallhelp.cpp index 4d9c60838b54f5..d59a2861a80c42 100644 --- a/src/coreclr/vm/tailcallhelp.cpp +++ b/src/coreclr/vm/tailcallhelp.cpp @@ -11,23 +11,11 @@ #include "threads.h" -FCIMPL2(void*, TailCallHelp::AllocTailCallArgBuffer, INT32 size, void* gcDesc) +FCIMPL2(void*, TailCallHelp::AllocTailCallArgBufferWorker, INT32 size, void* gcDesc) { - CONTRACTL - { - FCALL_CHECK; - INJECT_FAULT(FCThrow(kOutOfMemoryException);); - } - CONTRACTL_END - + FCALL_CONTRACT; _ASSERTE(size >= 0); - - void* result = GetThread()->GetTailCallTls()->AllocArgBuffer(size, gcDesc); - - if (result == NULL) - FCThrow(kOutOfMemoryException); - - return result; + return GetThread()->GetTailCallTls()->AllocArgBuffer(size, gcDesc); } FCIMPLEND diff --git a/src/coreclr/vm/tailcallhelp.h b/src/coreclr/vm/tailcallhelp.h index 32883076a69201..10b14bd9f6161d 100644 --- a/src/coreclr/vm/tailcallhelp.h +++ b/src/coreclr/vm/tailcallhelp.h @@ -13,7 +13,7 @@ struct ArgBufferLayout; class TailCallHelp { public: - static FCDECL2(void*, AllocTailCallArgBuffer, INT32, void*); + static FCDECL2(void*, AllocTailCallArgBufferWorker, INT32, void*); static FCDECL2(void*, GetTailCallInfo, void**, void**); static void CreateTailCallHelperStubs( diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 99a366e9a2f131..1db3062c466e77 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -6232,9 +6232,9 @@ BOOL Thread::SetStackLimits(SetStackLimitScope scope) return FALSE; } - // Compute the limit used by EnsureSufficientExecutionStack and cache it on the thread. This minimum stack size should + // Compute the limit used by TryEnsureSufficientExecutionStack and cache it on the thread. This minimum stack size should // be sufficient to allow a typical non-recursive call chain to execute, including potential exception handling and - // garbage collection. Used for probing for available stack space through RuntimeImports.EnsureSufficientExecutionStack, + // garbage collection. Used for probing for available stack space through RuntimeImports.TryEnsureSufficientExecutionStack, // among other things. #ifdef HOST_64BIT const UINT_PTR MinExecutionStackSize = 128 * 1024; diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 44a537e7fe9050..4cf384a46f94de 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -2417,7 +2417,7 @@ class Thread // These access the stack base and limit values for this thread. (They are cached during InitThread.) The // "stack base" is the "upper bound", i.e., where the stack starts growing from. (Main's call frame is at the // upper bound.) The "stack limit" is the "lower bound", i.e., how far the stack can grow down to. - // The "stack sufficient execution limit" is used by EnsureSufficientExecutionStack() to limit how much stack + // The "stack sufficient execution limit" is used by TryEnsureSufficientExecutionStack() to limit how much stack // should remain to execute the average Framework method. PTR_VOID GetCachedStackBase() {LIMITED_METHOD_DAC_CONTRACT; return m_CacheStackBase; } PTR_VOID GetCachedStackLimit() {LIMITED_METHOD_DAC_CONTRACT; return m_CacheStackLimit;} From 4345d1ae0703c3a26c520a37ffc341b4603666d5 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Mon, 23 Sep 2024 10:28:03 -0700 Subject: [PATCH 2/8] Rename GetGenerationWorker to GetGenerationInternal. --- src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs | 4 ++-- src/coreclr/vm/comutilnative.cpp | 4 ++-- src/coreclr/vm/comutilnative.h | 2 +- src/coreclr/vm/ecalllist.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs index 7154d730dded48..dd7cd4c6309443 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/GC.CoreCLR.cs @@ -160,11 +160,11 @@ public static void RemoveMemoryPressure(long bytesAllocated) public static int GetGeneration(object obj) { ArgumentNullException.ThrowIfNull(obj); - return GetGenerationWorker(obj); + return GetGenerationInternal(obj); } [MethodImpl(MethodImplOptions.InternalCall)] - private static extern int GetGenerationWorker(object obj); + private static extern int GetGenerationInternal(object obj); // Forces a collection of all generations from 0 through Generation. // diff --git a/src/coreclr/vm/comutilnative.cpp b/src/coreclr/vm/comutilnative.cpp index b25ccb1a2a6318..3788b19f0da975 100644 --- a/src/coreclr/vm/comutilnative.cpp +++ b/src/coreclr/vm/comutilnative.cpp @@ -688,12 +688,12 @@ extern "C" int QCALLTYPE GCInterface_WaitForFullGCComplete(int millisecondsTimeo return result; } -/*================================GetGenerationWorker================================= +/*================================GetGenerationInternal================================= **Action: Returns the generation in which args->obj is found. **Returns: The generation in which args->obj is found. **Arguments: args->obj -- The object to locate. ==============================================================================*/ -FCIMPL1(int, GCInterface::GetGenerationWorker, Object* objUNSAFE) +FCIMPL1(int, GCInterface::GetGenerationInternal, Object* objUNSAFE) { FCALL_CONTRACT; _ASSERTE(objUNSAFE != NULL); diff --git a/src/coreclr/vm/comutilnative.h b/src/coreclr/vm/comutilnative.h index 17f4cd96a51b53..473300b87677f9 100644 --- a/src/coreclr/vm/comutilnative.h +++ b/src/coreclr/vm/comutilnative.h @@ -170,7 +170,7 @@ class GCInterface { static FCDECL1(void, SetLOHCompactionMode, int newLOHCompactionyMode); static FCDECL2(FC_BOOL_RET, RegisterForFullGCNotification, UINT32 gen2Percentage, UINT32 lohPercentage); static FCDECL0(FC_BOOL_RET, CancelFullGCNotification); - static FCDECL1(int, GetGenerationWorker, Object* objUNSAFE); + static FCDECL1(int, GetGenerationInternal, Object* objUNSAFE); static FCDECL0(UINT64, GetSegmentSize); static FCDECL0(int, GetLastGCPercentTimeInGC); static FCDECL1(UINT64, GetGenerationSize, int gen); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 4ceb10b666af94..8996b3a16d8115 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -337,7 +337,7 @@ FCFuncStart(gGCInterfaceFuncs) FCFuncElement("GetSegmentSize", GCInterface::GetSegmentSize) FCFuncElement("GetLastGCPercentTimeInGC", GCInterface::GetLastGCPercentTimeInGC) FCFuncElement("GetGenerationSize", GCInterface::GetGenerationSize) - FCFuncElement("GetGenerationWorker", GCInterface::GetGenerationWorker) + FCFuncElement("GetGenerationInternal", GCInterface::GetGenerationInternal) FCFuncElement("GetMaxGeneration", GCInterface::GetMaxGeneration) FCFuncElement("_SuppressFinalize", GCInterface::SuppressFinalize) From fbb1077bcf8c8269546ecf93c7af18f4de78ad87 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Mon, 23 Sep 2024 13:42:33 -0700 Subject: [PATCH 3/8] Update Get/Set threadpool flag on Thread. --- .../src/System/Threading/Thread.CoreCLR.cs | 36 +++--- src/coreclr/vm/comsynchronizable.cpp | 104 ++++-------------- src/coreclr/vm/comsynchronizable.h | 30 +---- src/coreclr/vm/corelib.h | 1 + src/coreclr/vm/object.h | 2 + src/coreclr/vm/qcallentrypoints.cpp | 2 - 6 files changed, 49 insertions(+), 126 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 5dd5a02be3dedb..e514a38ee3618c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -60,6 +60,7 @@ public sealed partial class Thread // Set in unmanaged and read in managed code. private bool _isDead; + private bool _isThreadPool; private Thread() { } @@ -89,13 +90,13 @@ private unsafe void StartCore() { fixed (char* pThreadName = _name) { - StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, pThreadName); + StartInternal(GetNativeHandle(), _startHelper?._maxStackSize ?? 0, _priority, _isThreadPool ? Interop.BOOL.TRUE : Interop.BOOL.FALSE, pThreadName); } } } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Start")] - private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, char* pThreadName); + private static unsafe partial void StartInternal(ThreadHandle t, int stackSize, int priority, Interop.BOOL isThreadPool, char* pThreadName); // Called from the runtime private void StartCallback() @@ -196,12 +197,22 @@ public bool IsBackground { get { + if (_isDead) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + Interop.BOOL res = GetIsBackground(GetNativeHandle()); GC.KeepAlive(this); return res != Interop.BOOL.FALSE; } set { + if (_isDead) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + SetIsBackground(GetNativeHandle(), value ? Interop.BOOL.TRUE : Interop.BOOL.FALSE); GC.KeepAlive(this); if (!value) @@ -211,6 +222,7 @@ public bool IsBackground } } + [SuppressGCTransition] [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetIsBackground")] private static partial Interop.BOOL GetIsBackground(ThreadHandle t); @@ -222,24 +234,22 @@ public bool IsThreadPoolThread { get { - Interop.BOOL res = GetIsThreadPoolThread(GetNativeHandle()); - GC.KeepAlive(this); - return res != Interop.BOOL.FALSE; + if (_isDead) + { + throw new ThreadStateException(SR.ThreadState_Dead_State); + } + + return _isThreadPool; } internal set { Debug.Assert(value); - SetIsThreadPoolThread(GetNativeHandle()); - GC.KeepAlive(this); + Debug.Assert(!_isDead); + Debug.Assert((ThreadState & ThreadState.Unstarted) != 0); + _isThreadPool = value; } } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetIsThreadPoolThread")] - private static partial Interop.BOOL GetIsThreadPoolThread(ThreadHandle t); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetIsThreadPoolThread")] - private static partial void SetIsThreadPoolThread(ThreadHandle t); - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_SetPriority")] [return: MarshalAs(UnmanagedType.Bool)] private static partial void SetPriority(ObjectHandleOnStack thread, int priority); diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 8903d34209f40f..4062a34355ddde 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -127,20 +127,17 @@ INT32 MapFromNTPriority(INT32 NTPriority) return ours; } - -void ThreadNative::KickOffThread_Worker(LPVOID ptr) +static void KickOffThread_Worker(LPVOID ptr) { CONTRACTL { GC_TRIGGERS; THROWS; MODE_COOPERATIVE; + PRECONDITION(ptr == NULL); } CONTRACTL_END; - KickOffThread_Args *pKickOffArgs = (KickOffThread_Args *) ptr; - pKickOffArgs->retVal = 0; - PREPARE_NONVIRTUAL_CALLSITE(METHOD__THREAD__START_CALLBACK); DECLARE_ARGHOLDER_ARRAY(args, 1); args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(GetThread()->GetExposedObjectRaw()); @@ -175,7 +172,7 @@ static void PulseAllHelper(Thread* pThread) } // When an exposed thread is started by Win32, this is where it starts. -ULONG WINAPI ThreadNative::KickOffThread(void* pass) +static ULONG WINAPI KickOffThread(void* pass) { CONTRACTL @@ -212,11 +209,7 @@ ULONG WINAPI ThreadNative::KickOffThread(void* pass) _ASSERTE(GetThread() == pThread); // Now that it's started - KickOffThread_Args args; - args.share = NULL; - args.pThread = pThread; - - ManagedThreadBase::KickOff(KickOffThread_Worker, &args); + ManagedThreadBase::KickOff(KickOffThread_Worker, NULL); PulseAllHelper(pThread); @@ -230,18 +223,7 @@ ULONG WINAPI ThreadNative::KickOffThread(void* pass) return 0; } -extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, PCWSTR pThreadName) -{ - QCALL_CONTRACT; - - BEGIN_QCALL; - - ThreadNative::Start(thread, threadStackSize, priority, pThreadName); - - END_QCALL; -} - -void ThreadNative::Start(Thread* pNewThread, int threadStackSize, int priority, PCWSTR pThreadName) +static void StartThread(Thread* pNewThread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName) { STANDARD_VM_CONTRACT; @@ -291,6 +273,8 @@ void ThreadNative::Start(Thread* pNewThread, int threadStackSize, int priority, pNewThread->ChooseThreadCPUGroupAffinity(); pNewThread->SetThreadState(Thread::TS_LegalToJoin); + if (isThreadPool) + pNewThread->SetIsThreadPoolThread(); DWORD ret = pNewThread->StartThread(); @@ -321,6 +305,17 @@ void ThreadNative::Start(Thread* pNewThread, int threadStackSize, int priority, } } +extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + StartThread(thread, threadStackSize, priority, isThreadPool, pThreadName); + + END_QCALL; +} + extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack thread, INT32 iPriority) { QCALL_CONTRACT; @@ -716,7 +711,7 @@ extern "C" BOOL QCALLTYPE ThreadNative_GetIsBackground(QCall::ThreadHandle threa { CONTRACTL { - QCALL_CHECK; + QCALL_CHECK_NO_GC_TRANSITION; PRECONDITION(thread != NULL); } CONTRACTL_END; @@ -725,9 +720,6 @@ extern "C" BOOL QCALLTYPE ThreadNative_GetIsBackground(QCall::ThreadHandle threa BEGIN_QCALL; - if (ThreadIsDead(thread)) - COMPlusThrow(kThreadStateException, W("ThreadState_Dead_State")); - res = thread->IsBackground(); END_QCALL; @@ -747,9 +739,6 @@ extern "C" void QCALLTYPE ThreadNative_SetIsBackground(QCall::ThreadHandle threa BEGIN_QCALL; - if (ThreadIsDead(thread)) - COMPlusThrow(kThreadStateException, W("ThreadState_Dead_State")); - thread->SetBackground(value); END_QCALL; @@ -761,16 +750,10 @@ extern "C" void QCALLTYPE ThreadNative_InformThreadNameChange(QCall::ThreadHandl BEGIN_QCALL; - ThreadNative::InformThreadNameChange(thread, name, len); - - END_QCALL; -} + Thread* pThread = thread; -void ThreadNative::InformThreadNameChange(Thread* pThread, LPCWSTR name, INT32 len) -{ - // Set on Windows 10 Creators Update and later machines the unmanaged thread name as well. That will show up in ETW traces and debuggers which is very helpful - // if more and more threads get a meaningful name - // Will also show up in Linux in gdb and such. + // The name will show up in ETW traces and debuggers which is very helpful if more and more threads + // get a meaningful name. Will also show up in Linux in gdb and such. if (len > 0 && name != NULL && pThread->GetThreadHandle() != INVALID_HANDLE_VALUE) { SetThreadName(pThread->GetThreadHandle(), name); @@ -791,7 +774,6 @@ void ThreadNative::InformThreadNameChange(Thread* pThread, LPCWSTR name, INT32 l } #endif // PROFILING_SUPPORTED - #ifdef DEBUGGING_SUPPORTED if (CORDebuggerAttached()) { @@ -799,48 +781,6 @@ void ThreadNative::InformThreadNameChange(Thread* pThread, LPCWSTR name, INT32 l g_pDebugInterface->NameChangeEvent(NULL, pThread); } #endif // DEBUGGING_SUPPORTED -} - -// Get whether or not this is a threadpool thread. -extern "C" BOOL QCALLTYPE ThreadNative_GetIsThreadPoolThread(QCall::ThreadHandle thread) -{ - CONTRACTL - { - QCALL_CHECK; - PRECONDITION(thread != NULL); - } - CONTRACTL_END; - - BOOL res = FALSE; - - BEGIN_QCALL; - - if (ThreadIsDead(thread)) - COMPlusThrow(kThreadStateException, W("ThreadState_Dead_State")); - - res = thread->IsThreadPoolThread(); - - END_QCALL; - - return res; -} - -// Set thread as a threadpool thread. -extern "C" void QCALLTYPE ThreadNative_SetIsThreadPoolThread(QCall::ThreadHandle thread) -{ - CONTRACTL - { - QCALL_CHECK; - PRECONDITION(thread != NULL); - } - CONTRACTL_END; - - BEGIN_QCALL; - - if (ThreadIsDead(thread)) - COMPlusThrow(kThreadStateException, W("ThreadState_Dead_State")); - - thread->SetIsThreadPoolThread(); END_QCALL; } diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index 08fce25ef92768..c06af82d0f2967 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -15,22 +15,9 @@ #ifndef _COMSYNCHRONIZABLE_H #define _COMSYNCHRONIZABLE_H -#include "field.h" // For FieldDesc definition. - -// -// Each function that we call through native only gets one argument, -// which is actually a pointer to its stack of arguments. Our structs -// for accessing these are defined below. -// - -struct SharedState; - class ThreadNative { -friend class ThreadBaseObject; - public: - enum { PRIORITY_LOWEST = 0, @@ -54,22 +41,9 @@ friend class ThreadBaseObject; static FCDECL0(INT32, GetOptimalMaxSpinWaitsPerSpinIteration); static FCDECL1(void, Finalize, ThreadBaseObject* pThis); - - static void Start(Thread* pNewThread, int threadStackSize, int priority, PCWSTR pThreadName); - static void InformThreadNameChange(Thread* pThread, LPCWSTR name, INT32 len); -private: - - struct KickOffThread_Args { - Thread *pThread; - SharedState *share; - ULONG retVal; - }; - - static void KickOffThread_Worker(LPVOID /* KickOffThread_Args* */); - static ULONG WINAPI KickOffThread(void *pass); }; -extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, PCWSTR pThreadName); +extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName); extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack thread, INT32 iPriority); extern "C" void QCALLTYPE ThreadNative_GetCurrentThread(QCall::ObjectHandleOnStack thread); extern "C" BOOL QCALLTYPE ThreadNative_GetIsBackground(QCall::ThreadHandle thread); @@ -88,8 +62,6 @@ extern "C" INT32 QCALLTYPE ThreadNative_SetApartmentState(QCall::ObjectHandleOnS extern "C" BOOL QCALLTYPE ThreadNative_Join(QCall::ObjectHandleOnStack thread, INT32 Timeout); extern "C" void QCALLTYPE ThreadNative_Abort(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_ResetAbort(); -extern "C" BOOL QCALLTYPE ThreadNative_GetIsThreadPoolThread(QCall::ThreadHandle thread); -extern "C" void QCALLTYPE ThreadNative_SetIsThreadPoolThread(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_SpinWait(INT32 iterations); extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread); extern "C" void QCALLTYPE ThreadNative_Sleep(INT32 iTime); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index ec5ee0795a504f..75a1d76a8c06b9 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -836,6 +836,7 @@ DEFINE_FIELD_U(_startHelper, ThreadBaseObject, m_StartHelper) DEFINE_FIELD_U(_DONT_USE_InternalThread, ThreadBaseObject, m_InternalThread) DEFINE_FIELD_U(_priority, ThreadBaseObject, m_Priority) DEFINE_FIELD_U(_isDead, ThreadBaseObject, m_IsDead) +DEFINE_FIELD_U(_isThreadPool, ThreadBaseObject, m_IsThreadPool) DEFINE_CLASS(THREAD, Threading, Thread) DEFINE_METHOD(THREAD, START_CALLBACK, StartCallback, IM_RetVoid) #ifdef FEATURE_OBJCMARSHAL diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 6c17bf93c800e9..4e4bdf74a21839 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1328,6 +1328,8 @@ class ThreadBaseObject : public Object // Set in unmanaged code and read in managed code. bool m_IsDead; + bool m_IsThreadPool; + protected: // the ctor and dtor can do no useful work. ThreadBaseObject() {LIMITED_METHOD_CONTRACT;}; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 2f08da0154871f..f8867a0c754e36 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -253,8 +253,6 @@ static const Entry s_QCall[] = DllImportEntry(ThreadNative_Join) DllImportEntry(ThreadNative_Abort) DllImportEntry(ThreadNative_ResetAbort) - DllImportEntry(ThreadNative_GetIsThreadPoolThread) - DllImportEntry(ThreadNative_SetIsThreadPoolThread) DllImportEntry(ThreadNative_SpinWait) DllImportEntry(ThreadNative_Interrupt) DllImportEntry(ThreadNative_Sleep) From 4f84a34ebfcfea4393d43e6a261f2114468410a7 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Mon, 23 Sep 2024 14:02:54 -0700 Subject: [PATCH 4/8] Fold static function directly into QCall. --- src/coreclr/vm/comsynchronizable.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 4062a34355ddde..e4a8c87023515e 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -223,10 +223,13 @@ static ULONG WINAPI KickOffThread(void* pass) return 0; } -static void StartThread(Thread* pNewThread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName) +extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName) { - STANDARD_VM_CONTRACT; + QCALL_CONTRACT; + + BEGIN_QCALL; + Thread* pNewThread = thread; _ASSERTE(pNewThread != NULL); // Is the thread already started? You can't restart a thread. @@ -303,15 +306,6 @@ static void StartThread(Thread* pNewThread, int threadStackSize, int priority, B PulseAllHelper(pNewThread); pNewThread->HandleThreadStartupFailure(); } -} - -extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, BOOL isThreadPool, PCWSTR pThreadName) -{ - QCALL_CONTRACT; - - BEGIN_QCALL; - - StartThread(thread, threadStackSize, priority, isThreadPool, pThreadName); END_QCALL; } From 0387cf615e8d373476d6356f69dd9a52124d9925 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Mon, 23 Sep 2024 21:16:48 -0700 Subject: [PATCH 5/8] Update assert in set_IsThreadPoolThread --- .../src/System/Threading/Thread.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index e514a38ee3618c..55625bf57008bd 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -245,7 +245,7 @@ internal set { Debug.Assert(value); Debug.Assert(!_isDead); - Debug.Assert((ThreadState & ThreadState.Unstarted) != 0); + Debug.Assert(_isThreadPool || ((ThreadState & ThreadState.Unstarted) != 0)); _isThreadPool = value; } } From 1f899b139220e559171d75619925e6b3f618a719 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Tue, 24 Sep 2024 08:15:17 -0700 Subject: [PATCH 6/8] Update src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs Co-authored-by: Jan Kotas --- .../src/System/Threading/Thread.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 55625bf57008bd..82969c5edd62d5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -245,7 +245,7 @@ internal set { Debug.Assert(value); Debug.Assert(!_isDead); - Debug.Assert(_isThreadPool || ((ThreadState & ThreadState.Unstarted) != 0)); + Debug.Assert((ThreadState & ThreadState.Unstarted) != 0) || ThreadPool.UseWindowsThreadPool); _isThreadPool = value; } } From eaa03d8686a3b0f2bc09f7f4f7c29ca2eb080bc1 Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Tue, 24 Sep 2024 08:55:59 -0700 Subject: [PATCH 7/8] Fix build --- .../src/System/Threading/Thread.CoreCLR.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 82969c5edd62d5..2390ffc067a946 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -245,7 +245,11 @@ internal set { Debug.Assert(value); Debug.Assert(!_isDead); - Debug.Assert((ThreadState & ThreadState.Unstarted) != 0) || ThreadPool.UseWindowsThreadPool); + Debug.Assert(((ThreadState & ThreadState.Unstarted) != 0) +#if WINDOWS + || ThreadPool.UseWindowsThreadPool +#endif + ); _isThreadPool = value; } } From 845dddfc9a836165f3d966c4764a2e48bd379e0b Mon Sep 17 00:00:00 2001 From: Aaron R Robinson Date: Tue, 24 Sep 2024 11:50:09 -0700 Subject: [PATCH 8/8] Use the correct define. --- .../src/System/Threading/Thread.CoreCLR.cs | 2 +- .../src/System/Threading/RegisteredWaitHandle.Portable.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs index 2390ffc067a946..4f1c6d9ac10896 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs @@ -246,7 +246,7 @@ internal set Debug.Assert(value); Debug.Assert(!_isDead); Debug.Assert(((ThreadState & ThreadState.Unstarted) != 0) -#if WINDOWS +#if TARGET_WINDOWS || ThreadPool.UseWindowsThreadPool #endif ); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs index 74dbefe5869067..574dbf5ee2ed28 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/RegisteredWaitHandle.Portable.cs @@ -58,7 +58,7 @@ internal RegisteredWaitHandle(WaitHandle waitHandle, _ThreadPoolWaitOrTimerCallb #if TARGET_WASI if (OperatingSystem.IsWasi()) throw new PlatformNotSupportedException(); // TODO remove with https://github.com/dotnet/runtime/pull/107185 #endif -#if WINDOWS +#if TARGET_WINDOWS Debug.Assert(!ThreadPool.UseWindowsThreadPool); #endif GC.SuppressFinalize(this);