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;