Skip to content

Commit

Permalink
Convert Thread FCalls to QCalls (#107495)
Browse files Browse the repository at this point in the history
* Convert Thread.IsAlive property

* Convert Thread.GetCurrentThread()

* Convert Thread.ThreadState property

* Convert Thread.Initialize()
  • Loading branch information
AaronRobinsonMSFT committed Sep 9, 2024
1 parent d45ccfd commit 18eedbe
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 144 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/// <summary>Clean up the thread when it goes away.</summary>
~Thread() => InternalFinalize(); // Delegate to the unmanaged portion.
Expand All @@ -175,11 +186,7 @@ partial void ThreadNameChanged(string? value)
private static partial void InformThreadNameChange(ThreadHandle t, string? name, int len);

/// <summary>Returns true if the thread has been started and is not dead.</summary>
public extern bool IsAlive
{
[MethodImpl(MethodImplOptions.InternalCall)]
get;
}
public bool IsAlive => (ThreadState & (ThreadState.Unstarted | ThreadState.Stopped | ThreadState.Aborted)) == 0;

/// <summary>
/// Return whether or not this thread is a background thread. Background
Expand Down Expand Up @@ -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.
/// </summary>
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);

/// <summary>
/// An unstarted thread can be marked to indicate that it will host a
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/dlls/mscorrc/mscorrc.rc
Original file line number Diff line number Diff line change
Expand Up @@ -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."

Expand Down
1 change: 0 additions & 1 deletion src/coreclr/dlls/mscorrc/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
139 changes: 39 additions & 100 deletions src/coreclr/vm/comsynchronizable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand All @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down
7 changes: 3 additions & 4 deletions src/coreclr/vm/comsynchronizable.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
1 change: 0 additions & 1 deletion src/coreclr/vm/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 18eedbe

Please sign in to comment.