Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,34 @@ 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()
{
t_currentThread = GetCurrentThread() ?? GetCurrentThreadWorker();
return t_currentThread;

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern Thread GetCurrentThreadNative();
[MethodImpl(MethodImplOptions.NoInlining)]
static Thread GetCurrentThreadWorker()
{
Thread? thread = null;
GetCurrentThreadSlow(ObjectHandleOnStack.Create(ref thread));
return thread!;
}
}

[MethodImpl(MethodImplOptions.InternalCall)]
private extern void Initialize();
private static extern Thread GetCurrentThread();

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetCurrentThreadSlow")]
private static partial void GetCurrentThreadSlow(ObjectHandleOnStack thread);

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,12 +196,19 @@ 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
public bool IsAlive
{
[MethodImpl(MethodImplOptions.InternalCall)]
get;
get
{
Thread _this = this;
return IsAliveWorker(ObjectHandleOnStack.Create(ref _this));
}
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_IsAlive")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool IsAliveWorker(ObjectHandleOnStack t);

/// <summary>
/// Return whether or not this thread is a background thread. Background
/// threads do not affect when the Execution Engine shuts down.
Expand Down Expand Up @@ -247,10 +275,17 @@ 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
{
Thread _this = this;
return (ThreadState)GetThreadState(ObjectHandleOnStack.Create(ref _this));
}
}

[MethodImpl(MethodImplOptions.InternalCall)]
private extern int GetThreadStateNative();
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_GetThreadState")]
private static partial int GetThreadState(ObjectHandleOnStack t);

/// <summary>
/// An unstarted thread can be marked to indicate that it will host a
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
139 changes: 62 additions & 77 deletions src/coreclr/vm/comsynchronizable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,63 +362,47 @@ extern "C" void QCALLTYPE ThreadNative_SetPriority(QCall::ObjectHandleOnStack th
END_QCALL;
}

FCIMPL1(FC_BOOL_RET, ThreadNative::IsAlive, ThreadBaseObject* pThisUNSAFE)
extern "C" BOOL QCALLTYPE ThreadNative_IsAlive(QCall::ObjectHandleOnStack t)
{
FCALL_CONTRACT;
QCALL_CONTRACT;

if (pThisUNSAFE==NULL)
FCThrowRes(kNullReferenceException, W("NullReference_This"));
BOOL ret = FALSE;

THREADBASEREF thisRef(pThisUNSAFE);
BOOL ret = false;
BEGIN_QCALL;

// 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);
GCX_COOP();

Thread *thread = thisRef->GetInternal();
if (t.Get() == NULL)
COMPlusThrow(kNullReferenceException, W("NullReference_This"));

if (thread == 0)
Thread* thread = ((THREADBASEREF)t.Get())->GetInternal();
if (thread == NULL)
COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);

ret = ThreadIsRunning(thread);

HELPER_METHOD_POLL();
HELPER_METHOD_FRAME_END();

FC_RETURN_BOOL(ret);
END_QCALL;
return ret;
}
FCIMPLEND

NOINLINE static Object* GetCurrentThreadHelper()
FCIMPL0(Object*, ThreadNative::GetCurrentThread)
{
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();

FC_INNER_EPILOG();
return OBJECTREFToObject(refRetVal);
return OBJECTREFToObject(GetThread()->GetExposedObjectRaw());
}
FCIMPLEND

FCIMPL0(Object*, ThreadNative::GetCurrentThread)
extern "C" void QCALLTYPE ThreadNative_GetCurrentThreadSlow(QCall::ObjectHandleOnStack thread)
{
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;
QCALL_CONTRACT;

BEGIN_QCALL;

FC_INNER_RETURN(Object*, GetCurrentThreadHelper());
GCX_COOP();
thread.Set(GetThread()->GetExposedObject());

END_QCALL;
}
FCIMPLEND

extern "C" UINT64 QCALLTYPE ThreadNative_GetCurrentOSThreadId()
{
Expand All @@ -442,33 +426,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();

_ASSERTE(pThis != NULL);
_ASSERTE(pThis->m_InternalThread == NULL);
THREADBASEREF threadRef = NULL;
GCPROTECT_BEGIN(threadRef)
threadRef = (THREADBASEREF)t.Get();

_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 +476,55 @@ 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::ObjectHandleOnStack t)
{
FCALL_CONTRACT;

INT32 res = 0;
Thread::ThreadState state;
QCALL_CONTRACT;

if (pThisUNSAFE==NULL)
FCThrowRes(kNullReferenceException, W("NullReference_This"));
INT32 res = 0;

// validate the thread. Failure here implies that the thread was finalized
// and then resurrected.
Thread *thread = pThisUNSAFE->GetInternal();
BEGIN_QCALL;

if (!thread)
FCThrowEx(kThreadStateException, IDS_EE_THREAD_CANNOT_GET, NULL, NULL, NULL);
Thread* thread = NULL;
{
GCX_COOP();
if (t.Get() == NULL)
COMPlusThrow(kNullReferenceException, W("NullReference_This"));

HELPER_METHOD_FRAME_BEGIN_RET_0();
// validate the thread. Failure here implies that the thread was finalized
// and then resurrected.
thread = ((THREADBASEREF)t.Get())->GetInternal();
if (thread == NULL)
COMPlusThrow(kThreadStateException, IDS_EE_THREAD_CANNOT_GET);
}

// 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;
res |= ThreadNative::ThreadWaitSleepJoin;

HELPER_METHOD_POLL();
HELPER_METHOD_FRAME_END();
END_QCALL;

return res;
}
FCIMPLEND

#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT

Expand Down
7 changes: 4 additions & 3 deletions src/coreclr/vm/comsynchronizable.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,7 @@ 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);
Expand All @@ -79,10 +76,14 @@ 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" BOOL QCALLTYPE ThreadNative_IsAlive(QCall::ObjectHandleOnStack t);
extern "C" void QCALLTYPE ThreadNative_GetCurrentThreadSlow(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::ObjectHandleOnStack t);

#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
extern "C" INT32 QCALLTYPE ThreadNative_GetApartmentState(QCall::ObjectHandleOnStack t);
Expand Down
5 changes: 1 addition & 4 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,11 @@ FCFuncStart(gMathFFuncs)
FCFuncEnd()

FCFuncStart(gThreadFuncs)
FCFuncElement("Initialize", ThreadNative::Initialize)
FCFuncElement("GetCurrentThreadNative", ThreadNative::GetCurrentThread)
FCFuncElement("GetCurrentThread", 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 @@ -1302,7 +1302,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
4 changes: 4 additions & 0 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,14 @@ static const Entry s_QCall[] =
DllImportEntry(AppDomain_CreateDynamicAssembly)
DllImportEntry(ThreadNative_Start)
DllImportEntry(ThreadNative_SetPriority)
DllImportEntry(ThreadNative_IsAlive)
DllImportEntry(ThreadNative_GetCurrentThreadSlow)
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