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 486e5a505abeb..08b3ae8944d63 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 @@ -151,13 +151,24 @@ public static void SpinWait(int iterations) public static bool Yield() => YieldInternal() != Interop.BOOL.FALSE; [MethodImpl(MethodImplOptions.NoInlining)] - private static Thread InitializeCurrentThread() => t_currentThread = GetCurrentThreadNative(); + private static Thread InitializeCurrentThread() + { + Thread? thread = null; + GetCurrentThread(ObjectHandleOnStack.Create(ref thread)); + return t_currentThread = thread!; + } - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern Thread GetCurrentThreadNative(); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetCurrentThread")] + private static partial void GetCurrentThread(ObjectHandleOnStack thread); - [MethodImpl(MethodImplOptions.InternalCall)] - private extern void Initialize(); + private void Initialize() + { + Thread _this = this; + Initialize(ObjectHandleOnStack.Create(ref _this)); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Initialize")] + private static partial void Initialize(ObjectHandleOnStack thread); /// Clean up the thread when it goes away. ~Thread() => InternalFinalize(); // Delegate to the unmanaged portion. @@ -175,11 +186,7 @@ partial void ThreadNameChanged(string? value) private static partial void InformThreadNameChange(ThreadHandle t, string? name, int len); /// Returns true if the thread has been started and is not dead. - public extern bool IsAlive - { - [MethodImpl(MethodImplOptions.InternalCall)] - get; - } + public bool IsAlive => (ThreadState & (ThreadState.Unstarted | ThreadState.Stopped | ThreadState.Aborted)) == 0; /// /// Return whether or not this thread is a background thread. Background @@ -247,10 +254,19 @@ public ThreadPriority Priority /// Return the thread state as a consistent set of bits. This is more /// general then IsAlive or IsBackground. /// - public ThreadState ThreadState => (ThreadState)GetThreadStateNative(); + public ThreadState ThreadState + { + get + { + var state = (ThreadState)GetThreadState(GetNativeHandle()); + GC.KeepAlive(this); + return state; + } + } - [MethodImpl(MethodImplOptions.InternalCall)] - private extern int GetThreadStateNative(); + [SuppressGCTransition] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetThreadState")] + private static partial int GetThreadState(ThreadHandle t); /// /// An unstarted thread can be marked to indicate that it will host a @@ -327,6 +343,7 @@ public void DisableComObjectEagerCleanup() GC.KeepAlive(this); } + [SuppressGCTransition] [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_DisableComObjectEagerCleanup")] private static partial void DisableComObjectEagerCleanup(ThreadHandle t); #else // !FEATURE_COMINTEROP diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 6309533648bb2..223d675df254a 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -5557,6 +5557,8 @@ CorDebugUserState DacDbiInterfaceImpl::GetPartialUserState(VMPTR_Thread vmThread result |= USER_STOPPED; } + // Don't report Thread::TS_AbortRequested + // The interruptible flag is unreliable (see issue 699245) // The Debugger_SleepWaitJoin is always accurate when it is present, but it is still // just a band-aid fix to cover some of the race conditions interruptible has. diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index 927b287844df7..1d58a0681b584 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -467,7 +467,6 @@ BEGIN IDS_EE_INVALID_CA "Invalid custom attribute provided." - IDS_EE_THREAD_CANNOT_GET "Unable to retrieve thread information." IDS_EE_THREAD_ABORT_WHILE_SUSPEND "Thread is suspended; attempting to abort." IDS_EE_NOVARIANTRETURN "PInvoke restriction: cannot return variants." diff --git a/src/coreclr/dlls/mscorrc/resource.h b/src/coreclr/dlls/mscorrc/resource.h index 09bf78dfb93c4..964cb7128325d 100644 --- a/src/coreclr/dlls/mscorrc/resource.h +++ b/src/coreclr/dlls/mscorrc/resource.h @@ -232,7 +232,6 @@ #define IDS_EE_INVALID_CA 0x1a10 -#define IDS_EE_THREAD_CANNOT_GET 0x1a15 #define IDS_EE_THREAD_ABORT_WHILE_SUSPEND 0x1a1c #define IDS_EE_NOVARIANTRETURN 0x1a1d diff --git a/src/coreclr/vm/comsynchronizable.cpp b/src/coreclr/vm/comsynchronizable.cpp index 73cc949ec66d5..7f1751aaa15f5 100644 --- a/src/coreclr/vm/comsynchronizable.cpp +++ b/src/coreclr/vm/comsynchronizable.cpp @@ -362,63 +362,17 @@ extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack th END_QCALL; } -FCIMPL1(FC_BOOL_RET, ThreadNative::IsAlive, ThreadBaseObject* pThisUNSAFE) +extern "C" void QCALLTYPE ThreadNative_GetCurrentThread(QCall::ObjectHandleOnStack thread) { - FCALL_CONTRACT; - - if (pThisUNSAFE==NULL) - FCThrowRes(kNullReferenceException, W("NullReference_This")); - - THREADBASEREF thisRef(pThisUNSAFE); - BOOL ret = false; - - // Keep managed Thread object alive, since the native object's - // lifetime is tied to the managed object's finalizer. And with - // resurrection, it may be possible to get a dangling pointer here - - // consider both protecting thisRef and setting the managed object's - // Thread* to NULL in the GC's ScanForFinalization method. - HELPER_METHOD_FRAME_BEGIN_RET_1(thisRef); - - Thread *thread = thisRef->GetInternal(); - - if (thread == 0) - COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET); - - ret = ThreadIsRunning(thread); - - HELPER_METHOD_POLL(); - HELPER_METHOD_FRAME_END(); - - FC_RETURN_BOOL(ret); -} -FCIMPLEND - -NOINLINE static Object* GetCurrentThreadHelper() -{ - FCALL_CONTRACT; - FC_INNER_PROLOG(ThreadNative::GetCurrentThread); - OBJECTREF refRetVal = NULL; - - HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, refRetVal); - refRetVal = GetThread()->GetExposedObject(); - HELPER_METHOD_FRAME_END(); + QCALL_CONTRACT; - FC_INNER_EPILOG(); - return OBJECTREFToObject(refRetVal); -} + BEGIN_QCALL; -FCIMPL0(Object*, ThreadNative::GetCurrentThread) -{ - FCALL_CONTRACT; - OBJECTHANDLE ExposedObject = GetThread()->m_ExposedObject; - _ASSERTE(ExposedObject != 0); //Thread's constructor always initializes its GCHandle - Object* result = *((Object**) ExposedObject); - if (result != 0) - return result; + GCX_COOP(); + thread.Set(GetThread()->GetExposedObject()); - FC_INNER_RETURN(Object*, GetCurrentThreadHelper()); + END_QCALL; } -FCIMPLEND extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId() { @@ -442,33 +396,36 @@ extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId() return threadId; } -FCIMPL1(void, ThreadNative::Initialize, ThreadBaseObject* pThisUNSAFE) +extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t) { - FCALL_CONTRACT; + QCALL_CONTRACT; - THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE; + BEGIN_QCALL; - HELPER_METHOD_FRAME_BEGIN_1(pThis); + GCX_COOP(); + + THREADBASEREF threadRef = NULL; + GCPROTECT_BEGIN(threadRef) + threadRef = (THREADBASEREF)t.Get(); - _ASSERTE(pThis != NULL); - _ASSERTE(pThis->m_InternalThread == NULL); + _ASSERTE(threadRef != NULL); + _ASSERTE(threadRef->GetInternal() == NULL); // if we don't have an internal Thread object associated with this exposed object, // now is our first opportunity to create one. - Thread *unstarted = SetupUnstartedThread(); - + Thread* unstarted = SetupUnstartedThread(); PREFIX_ASSUME(unstarted != NULL); - pThis->SetInternal(unstarted); - pThis->SetManagedThreadId(unstarted->GetThreadId()); - unstarted->SetExposedObject(pThis); + threadRef->SetInternal(unstarted); + threadRef->SetManagedThreadId(unstarted->GetThreadId()); + unstarted->SetExposedObject(threadRef); // Initialize the thread priority to normal. - pThis->SetPriority(ThreadNative::PRIORITY_NORMAL); + threadRef->SetPriority(ThreadNative::PRIORITY_NORMAL); - HELPER_METHOD_FRAME_END(); + GCPROTECT_END(); + END_QCALL; } -FCIMPLEND // Return whether or not this is a background thread. FCIMPL1(FC_BOOL_RET, ThreadNative::GetIsBackground, ThreadBaseObject* pThisUNSAFE) @@ -489,57 +446,43 @@ FCIMPL1(FC_BOOL_RET, ThreadNative::GetIsBackground, ThreadBaseObject* pThisUNSAF FCIMPLEND // Deliver the state of the thread as a consistent set of bits. -// This copied in VM\EEDbgInterfaceImpl.h's -// CorDebugUserState GetUserState( Thread *pThread ) -// , so propagate changes to both functions -FCIMPL1(INT32, ThreadNative::GetThreadState, ThreadBaseObject* pThisUNSAFE) +// Duplicate logic in DacDbiInterfaceImpl::GetPartialUserState() +extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread) { - FCALL_CONTRACT; - - INT32 res = 0; - Thread::ThreadState state; - - if (pThisUNSAFE==NULL) - FCThrowRes(kNullReferenceException, W("NullReference_This")); - - // validate the thread. Failure here implies that the thread was finalized - // and then resurrected. - Thread *thread = pThisUNSAFE->GetInternal(); - - if (!thread) - FCThrowEx(kThreadStateException, IDS_EE_THREAD_CANNOT_GET, NULL, NULL, NULL); + CONTRACTL + { + QCALL_CHECK_NO_GC_TRANSITION; + PRECONDITION(thread != NULL); + } + CONTRACTL_END; - HELPER_METHOD_FRAME_BEGIN_RET_0(); + INT32 res = 0; // grab a snapshot - state = thread->GetSnapshotState(); + Thread::ThreadState state = thread->GetSnapshotState(); if (state & Thread::TS_Background) - res |= ThreadBackground; + res |= ThreadNative::ThreadBackground; if (state & Thread::TS_Unstarted) - res |= ThreadUnstarted; + res |= ThreadNative::ThreadUnstarted; // Don't report a StopRequested if the thread has actually stopped. if (state & Thread::TS_Dead) { - res |= ThreadStopped; + res |= ThreadNative::ThreadStopped; } else { if (state & Thread::TS_AbortRequested) - res |= ThreadAbortRequested; + res |= ThreadNative::ThreadAbortRequested; } if (state & Thread::TS_Interruptible) - res |= ThreadWaitSleepJoin; - - HELPER_METHOD_POLL(); - HELPER_METHOD_FRAME_END(); + res |= ThreadNative::ThreadWaitSleepJoin; return res; } -FCIMPLEND #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT @@ -942,16 +885,12 @@ extern "C" void QCALLTYPE ThreadNative_DisableComObjectEagerCleanup(QCall::Threa { CONTRACTL { - QCALL_CHECK; + QCALL_CHECK_NO_GC_TRANSITION; PRECONDITION(thread != NULL); } CONTRACTL_END; - BEGIN_QCALL; - thread->SetDisableComObjectEagerCleanup(); - - END_QCALL; } #endif //FEATURE_COMINTEROP diff --git a/src/coreclr/vm/comsynchronizable.h b/src/coreclr/vm/comsynchronizable.h index 9180216deeea9..b7c64c529084f 100644 --- a/src/coreclr/vm/comsynchronizable.h +++ b/src/coreclr/vm/comsynchronizable.h @@ -52,13 +52,9 @@ friend class ThreadBaseObject; ThreadAbortRequested = 128, }; - static FCDECL1(FC_BOOL_RET, IsAlive, ThreadBaseObject* pThisUNSAFE); - static FCDECL1(void, Initialize, ThreadBaseObject* pThisUNSAFE); static FCDECL1(FC_BOOL_RET, GetIsBackground, ThreadBaseObject* pThisUNSAFE); - static FCDECL1(INT32, GetThreadState, ThreadBaseObject* pThisUNSAFE); static FCDECL0(INT32, GetOptimalMaxSpinWaitsPerSpinIteration); - static FCDECL0(Object*, GetCurrentThread); static FCDECL1(void, Finalize, ThreadBaseObject* pThis); static FCDECL1(FC_BOOL_RET,IsThreadpoolThread, ThreadBaseObject* thread); static FCDECL1(void, SetIsThreadpoolThread, ThreadBaseObject* thread); @@ -79,10 +75,13 @@ 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" 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(); extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId(); +extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t); +extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread); #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT extern "C" INT32 QCALLTYPE ThreadNative_GetApartmentState(QCall::ObjectHandleOnStack t); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index 48edcc4d04672..75d3d4cd1652a 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -298,14 +298,10 @@ FCFuncStart(gMathFFuncs) FCFuncEnd() FCFuncStart(gThreadFuncs) - FCFuncElement("Initialize", ThreadNative::Initialize) - FCFuncElement("GetCurrentThreadNative", ThreadNative::GetCurrentThread) FCFuncElement("InternalFinalize", ThreadNative::Finalize) - FCFuncElement("get_IsAlive", ThreadNative::IsAlive) FCFuncElement("GetIsBackground", ThreadNative::GetIsBackground) FCFuncElement("get_IsThreadPoolThread", ThreadNative::IsThreadpoolThread) FCFuncElement("set_IsThreadPoolThread", ThreadNative::SetIsThreadpoolThread) - FCFuncElement("GetThreadStateNative", ThreadNative::GetThreadState) FCFuncElement("get_OptimalMaxSpinWaitsPerSpinIteration", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration) FCFuncEnd() diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index c6a644a03edde..6c17bf93c800e 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1295,7 +1295,6 @@ typedef DPTR(class ThreadBaseObject) PTR_ThreadBaseObject; class ThreadBaseObject : public Object { friend class ClrDataAccess; - friend class ThreadNative; friend class CoreLibBinder; friend class Object; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 02ad38c982128..4181a027e669c 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -238,10 +238,13 @@ static const Entry s_QCall[] = DllImportEntry(AppDomain_CreateDynamicAssembly) DllImportEntry(ThreadNative_Start) DllImportEntry(ThreadNative_SetPriority) + DllImportEntry(ThreadNative_GetCurrentThread) DllImportEntry(ThreadNative_SetIsBackground) DllImportEntry(ThreadNative_InformThreadNameChange) DllImportEntry(ThreadNative_YieldThread) DllImportEntry(ThreadNative_GetCurrentOSThreadId) + DllImportEntry(ThreadNative_Initialize) + DllImportEntry(ThreadNative_GetThreadState) #ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT DllImportEntry(ThreadNative_GetApartmentState) DllImportEntry(ThreadNative_SetApartmentState) diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index f3b7894c3dea6..44a537e7fe905 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -468,7 +468,6 @@ class Thread friend class ThreadSuspend; friend class SyncBlock; friend struct PendingSync; - friend class ThreadNative; #ifdef _DEBUG friend class EEContract; #endif @@ -2706,6 +2705,24 @@ class Thread #endif // TRACK_SYNC + // Access to thread handle and ThreadId. + HANDLE GetThreadHandle() + { + LIMITED_METHOD_CONTRACT; +#if defined(_DEBUG) && !defined(DACCESS_COMPILE) + { + CounterHolder handleHolder(&m_dwThreadHandleBeingUsed); + HANDLE handle = m_ThreadHandle; + _ASSERTE ( handle == INVALID_HANDLE_VALUE + || m_OSThreadId == 0 + || m_OSThreadId == 0xbaadf00d + || ::MatchThreadHandleToOsId(handle, (DWORD)m_OSThreadId) ); + } +#endif + DACCOP_IGNORE(FieldAccess, "Treated as raw address, no marshaling is necessary"); + return m_ThreadHandle; + } + private: // For suspends: CLREvent m_DebugSuspendEvent; @@ -2729,25 +2746,6 @@ class Thread return walk; } - // Access to thread handle and ThreadId. - HANDLE GetThreadHandle() - { - LIMITED_METHOD_CONTRACT; -#if defined(_DEBUG) && !defined(DACCESS_COMPILE) - { - CounterHolder handleHolder(&m_dwThreadHandleBeingUsed); - HANDLE handle = m_ThreadHandle; - _ASSERTE ( handle == INVALID_HANDLE_VALUE - || m_OSThreadId == 0 - || m_OSThreadId == 0xbaadf00d - || ::MatchThreadHandleToOsId(handle, (DWORD)m_OSThreadId) ); - } -#endif - - DACCOP_IGNORE(FieldAccess, "Treated as raw address, no marshaling is necessary"); - return m_ThreadHandle; - } - void SetThreadHandle(HANDLE h) { LIMITED_METHOD_CONTRACT;