Skip to content

Commit 18eedbe

Browse files
Convert Thread FCalls to QCalls (dotnet#107495)
* Convert Thread.IsAlive property * Convert Thread.GetCurrentThread() * Convert Thread.ThreadState property * Convert Thread.Initialize()
1 parent d45ccfd commit 18eedbe

File tree

10 files changed

+95
-144
lines changed

10 files changed

+95
-144
lines changed

src/coreclr/System.Private.CoreLib/src/System/Threading/Thread.CoreCLR.cs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -151,13 +151,24 @@ public static void SpinWait(int iterations)
151151
public static bool Yield() => YieldInternal() != Interop.BOOL.FALSE;
152152

153153
[MethodImpl(MethodImplOptions.NoInlining)]
154-
private static Thread InitializeCurrentThread() => t_currentThread = GetCurrentThreadNative();
154+
private static Thread InitializeCurrentThread()
155+
{
156+
Thread? thread = null;
157+
GetCurrentThread(ObjectHandleOnStack.Create(ref thread));
158+
return t_currentThread = thread!;
159+
}
155160

156-
[MethodImpl(MethodImplOptions.InternalCall)]
157-
private static extern Thread GetCurrentThreadNative();
161+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetCurrentThread")]
162+
private static partial void GetCurrentThread(ObjectHandleOnStack thread);
158163

159-
[MethodImpl(MethodImplOptions.InternalCall)]
160-
private extern void Initialize();
164+
private void Initialize()
165+
{
166+
Thread _this = this;
167+
Initialize(ObjectHandleOnStack.Create(ref _this));
168+
}
169+
170+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_Initialize")]
171+
private static partial void Initialize(ObjectHandleOnStack thread);
161172

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

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

184191
/// <summary>
185192
/// Return whether or not this thread is a background thread. Background
@@ -247,10 +254,19 @@ public ThreadPriority Priority
247254
/// Return the thread state as a consistent set of bits. This is more
248255
/// general then IsAlive or IsBackground.
249256
/// </summary>
250-
public ThreadState ThreadState => (ThreadState)GetThreadStateNative();
257+
public ThreadState ThreadState
258+
{
259+
get
260+
{
261+
var state = (ThreadState)GetThreadState(GetNativeHandle());
262+
GC.KeepAlive(this);
263+
return state;
264+
}
265+
}
251266

252-
[MethodImpl(MethodImplOptions.InternalCall)]
253-
private extern int GetThreadStateNative();
267+
[SuppressGCTransition]
268+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetThreadState")]
269+
private static partial int GetThreadState(ThreadHandle t);
254270

255271
/// <summary>
256272
/// An unstarted thread can be marked to indicate that it will host a
@@ -327,6 +343,7 @@ public void DisableComObjectEagerCleanup()
327343
GC.KeepAlive(this);
328344
}
329345

346+
[SuppressGCTransition]
330347
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_DisableComObjectEagerCleanup")]
331348
private static partial void DisableComObjectEagerCleanup(ThreadHandle t);
332349
#else // !FEATURE_COMINTEROP

src/coreclr/debug/daccess/dacdbiimpl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5557,6 +5557,8 @@ CorDebugUserState DacDbiInterfaceImpl::GetPartialUserState(VMPTR_Thread vmThread
55575557
result |= USER_STOPPED;
55585558
}
55595559

5560+
// Don't report Thread::TS_AbortRequested
5561+
55605562
// The interruptible flag is unreliable (see issue 699245)
55615563
// The Debugger_SleepWaitJoin is always accurate when it is present, but it is still
55625564
// just a band-aid fix to cover some of the race conditions interruptible has.

src/coreclr/dlls/mscorrc/mscorrc.rc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,6 @@ BEGIN
467467

468468
IDS_EE_INVALID_CA "Invalid custom attribute provided."
469469

470-
IDS_EE_THREAD_CANNOT_GET "Unable to retrieve thread information."
471470
IDS_EE_THREAD_ABORT_WHILE_SUSPEND "Thread is suspended; attempting to abort."
472471
IDS_EE_NOVARIANTRETURN "PInvoke restriction: cannot return variants."
473472

src/coreclr/dlls/mscorrc/resource.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@
232232

233233
#define IDS_EE_INVALID_CA 0x1a10
234234

235-
#define IDS_EE_THREAD_CANNOT_GET 0x1a15
236235
#define IDS_EE_THREAD_ABORT_WHILE_SUSPEND 0x1a1c
237236

238237
#define IDS_EE_NOVARIANTRETURN 0x1a1d

src/coreclr/vm/comsynchronizable.cpp

Lines changed: 39 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -362,63 +362,17 @@ extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack th
362362
END_QCALL;
363363
}
364364

365-
FCIMPL1(FC_BOOL_RET, ThreadNative::IsAlive, ThreadBaseObject* pThisUNSAFE)
365+
extern "C" void QCALLTYPE ThreadNative_GetCurrentThread(QCall::ObjectHandleOnStack thread)
366366
{
367-
FCALL_CONTRACT;
368-
369-
if (pThisUNSAFE==NULL)
370-
FCThrowRes(kNullReferenceException, W("NullReference_This"));
371-
372-
THREADBASEREF thisRef(pThisUNSAFE);
373-
BOOL ret = false;
374-
375-
// Keep managed Thread object alive, since the native object's
376-
// lifetime is tied to the managed object's finalizer. And with
377-
// resurrection, it may be possible to get a dangling pointer here -
378-
// consider both protecting thisRef and setting the managed object's
379-
// Thread* to NULL in the GC's ScanForFinalization method.
380-
HELPER_METHOD_FRAME_BEGIN_RET_1(thisRef);
381-
382-
Thread *thread = thisRef->GetInternal();
383-
384-
if (thread == 0)
385-
COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
386-
387-
ret = ThreadIsRunning(thread);
388-
389-
HELPER_METHOD_POLL();
390-
HELPER_METHOD_FRAME_END();
391-
392-
FC_RETURN_BOOL(ret);
393-
}
394-
FCIMPLEND
395-
396-
NOINLINE static Object* GetCurrentThreadHelper()
397-
{
398-
FCALL_CONTRACT;
399-
FC_INNER_PROLOG(ThreadNative::GetCurrentThread);
400-
OBJECTREF refRetVal = NULL;
401-
402-
HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, refRetVal);
403-
refRetVal = GetThread()->GetExposedObject();
404-
HELPER_METHOD_FRAME_END();
367+
QCALL_CONTRACT;
405368

406-
FC_INNER_EPILOG();
407-
return OBJECTREFToObject(refRetVal);
408-
}
369+
BEGIN_QCALL;
409370

410-
FCIMPL0(Object*, ThreadNative::GetCurrentThread)
411-
{
412-
FCALL_CONTRACT;
413-
OBJECTHANDLE ExposedObject = GetThread()->m_ExposedObject;
414-
_ASSERTE(ExposedObject != 0); //Thread's constructor always initializes its GCHandle
415-
Object* result = *((Object**) ExposedObject);
416-
if (result != 0)
417-
return result;
371+
GCX_COOP();
372+
thread.Set(GetThread()->GetExposedObject());
418373

419-
FC_INNER_RETURN(Object*, GetCurrentThreadHelper());
374+
END_QCALL;
420375
}
421-
FCIMPLEND
422376

423377
extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId()
424378
{
@@ -442,33 +396,36 @@ extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId()
442396
return threadId;
443397
}
444398

445-
FCIMPL1(void, ThreadNative::Initialize, ThreadBaseObject* pThisUNSAFE)
399+
extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t)
446400
{
447-
FCALL_CONTRACT;
401+
QCALL_CONTRACT;
448402

449-
THREADBASEREF pThis = (THREADBASEREF) pThisUNSAFE;
403+
BEGIN_QCALL;
450404

451-
HELPER_METHOD_FRAME_BEGIN_1(pThis);
405+
GCX_COOP();
406+
407+
THREADBASEREF threadRef = NULL;
408+
GCPROTECT_BEGIN(threadRef)
409+
threadRef = (THREADBASEREF)t.Get();
452410

453-
_ASSERTE(pThis != NULL);
454-
_ASSERTE(pThis->m_InternalThread == NULL);
411+
_ASSERTE(threadRef != NULL);
412+
_ASSERTE(threadRef->GetInternal() == NULL);
455413

456414
// if we don't have an internal Thread object associated with this exposed object,
457415
// now is our first opportunity to create one.
458-
Thread *unstarted = SetupUnstartedThread();
459-
416+
Thread* unstarted = SetupUnstartedThread();
460417
PREFIX_ASSUME(unstarted != NULL);
461418

462-
pThis->SetInternal(unstarted);
463-
pThis->SetManagedThreadId(unstarted->GetThreadId());
464-
unstarted->SetExposedObject(pThis);
419+
threadRef->SetInternal(unstarted);
420+
threadRef->SetManagedThreadId(unstarted->GetThreadId());
421+
unstarted->SetExposedObject(threadRef);
465422

466423
// Initialize the thread priority to normal.
467-
pThis->SetPriority(ThreadNative::PRIORITY_NORMAL);
424+
threadRef->SetPriority(ThreadNative::PRIORITY_NORMAL);
468425

469-
HELPER_METHOD_FRAME_END();
426+
GCPROTECT_END();
427+
END_QCALL;
470428
}
471-
FCIMPLEND
472429

473430
// Return whether or not this is a background thread.
474431
FCIMPL1(FC_BOOL_RET, ThreadNative::GetIsBackground, ThreadBaseObject* pThisUNSAFE)
@@ -489,57 +446,43 @@ FCIMPL1(FC_BOOL_RET, ThreadNative::GetIsBackground, ThreadBaseObject* pThisUNSAF
489446
FCIMPLEND
490447

491448
// Deliver the state of the thread as a consistent set of bits.
492-
// This copied in VM\EEDbgInterfaceImpl.h's
493-
// CorDebugUserState GetUserState( Thread *pThread )
494-
// , so propagate changes to both functions
495-
FCIMPL1(INT32, ThreadNative::GetThreadState, ThreadBaseObject* pThisUNSAFE)
449+
// Duplicate logic in DacDbiInterfaceImpl::GetPartialUserState()
450+
extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread)
496451
{
497-
FCALL_CONTRACT;
498-
499-
INT32 res = 0;
500-
Thread::ThreadState state;
501-
502-
if (pThisUNSAFE==NULL)
503-
FCThrowRes(kNullReferenceException, W("NullReference_This"));
504-
505-
// validate the thread. Failure here implies that the thread was finalized
506-
// and then resurrected.
507-
Thread *thread = pThisUNSAFE->GetInternal();
508-
509-
if (!thread)
510-
FCThrowEx(kThreadStateException, IDS_EE_THREAD_CANNOT_GET, NULL, NULL, NULL);
452+
CONTRACTL
453+
{
454+
QCALL_CHECK_NO_GC_TRANSITION;
455+
PRECONDITION(thread != NULL);
456+
}
457+
CONTRACTL_END;
511458

512-
HELPER_METHOD_FRAME_BEGIN_RET_0();
459+
INT32 res = 0;
513460

514461
// grab a snapshot
515-
state = thread->GetSnapshotState();
462+
Thread::ThreadState state = thread->GetSnapshotState();
516463

517464
if (state & Thread::TS_Background)
518-
res |= ThreadBackground;
465+
res |= ThreadNative::ThreadBackground;
519466

520467
if (state & Thread::TS_Unstarted)
521-
res |= ThreadUnstarted;
468+
res |= ThreadNative::ThreadUnstarted;
522469

523470
// Don't report a StopRequested if the thread has actually stopped.
524471
if (state & Thread::TS_Dead)
525472
{
526-
res |= ThreadStopped;
473+
res |= ThreadNative::ThreadStopped;
527474
}
528475
else
529476
{
530477
if (state & Thread::TS_AbortRequested)
531-
res |= ThreadAbortRequested;
478+
res |= ThreadNative::ThreadAbortRequested;
532479
}
533480

534481
if (state & Thread::TS_Interruptible)
535-
res |= ThreadWaitSleepJoin;
536-
537-
HELPER_METHOD_POLL();
538-
HELPER_METHOD_FRAME_END();
482+
res |= ThreadNative::ThreadWaitSleepJoin;
539483

540484
return res;
541485
}
542-
FCIMPLEND
543486

544487
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
545488

@@ -942,16 +885,12 @@ extern "C" void QCALLTYPE ThreadNative_DisableComObjectEagerCleanup(QCall::Threa
942885
{
943886
CONTRACTL
944887
{
945-
QCALL_CHECK;
888+
QCALL_CHECK_NO_GC_TRANSITION;
946889
PRECONDITION(thread != NULL);
947890
}
948891
CONTRACTL_END;
949892

950-
BEGIN_QCALL;
951-
952893
thread->SetDisableComObjectEagerCleanup();
953-
954-
END_QCALL;
955894
}
956895
#endif //FEATURE_COMINTEROP
957896

src/coreclr/vm/comsynchronizable.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,9 @@ friend class ThreadBaseObject;
5252
ThreadAbortRequested = 128,
5353
};
5454

55-
static FCDECL1(FC_BOOL_RET, IsAlive, ThreadBaseObject* pThisUNSAFE);
56-
static FCDECL1(void, Initialize, ThreadBaseObject* pThisUNSAFE);
5755
static FCDECL1(FC_BOOL_RET, GetIsBackground, ThreadBaseObject* pThisUNSAFE);
58-
static FCDECL1(INT32, GetThreadState, ThreadBaseObject* pThisUNSAFE);
5956

6057
static FCDECL0(INT32, GetOptimalMaxSpinWaitsPerSpinIteration);
61-
static FCDECL0(Object*, GetCurrentThread);
6258
static FCDECL1(void, Finalize, ThreadBaseObject* pThis);
6359
static FCDECL1(FC_BOOL_RET,IsThreadpoolThread, ThreadBaseObject* thread);
6460
static FCDECL1(void, SetIsThreadpoolThread, ThreadBaseObject* thread);
@@ -79,10 +75,13 @@ friend class ThreadBaseObject;
7975

8076
extern "C" void QCALLTYPE ThreadNative_Start(QCall::ThreadHandle thread, int threadStackSize, int priority, PCWSTR pThreadName);
8177
extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack thread, INT32 iPriority);
78+
extern "C" void QCALLTYPE ThreadNative_GetCurrentThread(QCall::ObjectHandleOnStack thread);
8279
extern "C" void QCALLTYPE ThreadNative_SetIsBackground(QCall::ThreadHandle thread, BOOL value);
8380
extern "C" void QCALLTYPE ThreadNative_InformThreadNameChange(QCall::ThreadHandle thread, LPCWSTR name, INT32 len);
8481
extern "C" BOOL QCALLTYPE ThreadNative_YieldThread();
8582
extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId();
83+
extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t);
84+
extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread);
8685

8786
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
8887
extern "C" INT32 QCALLTYPE ThreadNative_GetApartmentState(QCall::ObjectHandleOnStack t);

src/coreclr/vm/ecalllist.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,10 @@ FCFuncStart(gMathFFuncs)
298298
FCFuncEnd()
299299

300300
FCFuncStart(gThreadFuncs)
301-
FCFuncElement("Initialize", ThreadNative::Initialize)
302-
FCFuncElement("GetCurrentThreadNative", ThreadNative::GetCurrentThread)
303301
FCFuncElement("InternalFinalize", ThreadNative::Finalize)
304-
FCFuncElement("get_IsAlive", ThreadNative::IsAlive)
305302
FCFuncElement("GetIsBackground", ThreadNative::GetIsBackground)
306303
FCFuncElement("get_IsThreadPoolThread", ThreadNative::IsThreadpoolThread)
307304
FCFuncElement("set_IsThreadPoolThread", ThreadNative::SetIsThreadpoolThread)
308-
FCFuncElement("GetThreadStateNative", ThreadNative::GetThreadState)
309305
FCFuncElement("get_OptimalMaxSpinWaitsPerSpinIteration", ThreadNative::GetOptimalMaxSpinWaitsPerSpinIteration)
310306
FCFuncEnd()
311307

src/coreclr/vm/object.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,6 @@ typedef DPTR(class ThreadBaseObject) PTR_ThreadBaseObject;
12951295
class ThreadBaseObject : public Object
12961296
{
12971297
friend class ClrDataAccess;
1298-
friend class ThreadNative;
12991298
friend class CoreLibBinder;
13001299
friend class Object;
13011300

src/coreclr/vm/qcallentrypoints.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,13 @@ static const Entry s_QCall[] =
238238
DllImportEntry(AppDomain_CreateDynamicAssembly)
239239
DllImportEntry(ThreadNative_Start)
240240
DllImportEntry(ThreadNative_SetPriority)
241+
DllImportEntry(ThreadNative_GetCurrentThread)
241242
DllImportEntry(ThreadNative_SetIsBackground)
242243
DllImportEntry(ThreadNative_InformThreadNameChange)
243244
DllImportEntry(ThreadNative_YieldThread)
244245
DllImportEntry(ThreadNative_GetCurrentOSThreadId)
246+
DllImportEntry(ThreadNative_Initialize)
247+
DllImportEntry(ThreadNative_GetThreadState)
245248
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
246249
DllImportEntry(ThreadNative_GetApartmentState)
247250
DllImportEntry(ThreadNative_SetApartmentState)

0 commit comments

Comments
 (0)