Skip to content

Commit 14a10b6

Browse files
Ensure consistency between ThreadState.Stopped APIs that check if a thread is dead (#122568)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent f669bcb commit 14a10b6

File tree

4 files changed

+16
-39
lines changed

4 files changed

+16
-39
lines changed

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

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ public sealed partial class Thread
6363
// but those types of changes may race with the reset anyway, so this field doesn't need to be synchronized.
6464
private bool _mayNeedResetForThreadPool;
6565

66-
// Set in unmanaged and read in managed code.
66+
// This is set in two places:
67+
// For threads started with Thread.Start: Set in managed code as the thread is exiting.
68+
// For external threads that attach to the runtime: Set in unmanaged code as part of thread detach.
69+
// This is only read in managed code.
6770
private bool _isDead;
6871
private bool _isThreadPool;
6972

@@ -290,6 +293,11 @@ public ThreadState ThreadState
290293
{
291294
get
292295
{
296+
if (_isDead)
297+
{
298+
return ThreadState.Stopped;
299+
}
300+
293301
var state = (ThreadState)GetThreadState(GetNativeHandle());
294302
GC.KeepAlive(this);
295303
return state;
@@ -324,16 +332,6 @@ internal void ClearWaitSleepJoinState()
324332
[SuppressGCTransition]
325333
private static partial void ClearWaitSleepJoinState(ThreadHandle t);
326334

327-
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "ThreadNative_ReportDead")]
328-
[SuppressGCTransition]
329-
private static partial void ReportDead(ThreadHandle t);
330-
331-
internal void NotifyThreadDeath()
332-
{
333-
ReportDead(GetNativeHandle());
334-
GC.KeepAlive(this);
335-
}
336-
337335
/// <summary>
338336
/// An unstarted thread can be marked to indicate that it will host a
339337
/// single-threaded or multi-threaded apartment.
@@ -568,16 +566,15 @@ WaitSubsystem.ThreadWaitInfo AllocateWaitInfo()
568566
}
569567
#endif
570568

571-
#pragma warning disable CA1822 // Member 'OnThreadExiting' does not access instance data and can be marked as static
572569
private void OnThreadExiting()
573-
#pragma warning restore CA1822 // Member 'OnThreadExiting' does not access instance data and can be marked as static
574570
{
575-
#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI
576-
// Update the native side to report that the thread is dead.
577-
// We do this before OnThreadExiting and SetJoinHandle so that any threads
578-
// waiting on this thread to end will correctly see that it is stopped
571+
// Consider this managed thread as dead.
572+
// The unmanaged thread is still alive, but will die soon, after cleaning up some state.
573+
// We set _isDead = true before calling _waitInfo?.OnThreadExiting() and SetJoinHandle()
574+
// so that any threads waiting on this thread to end will correctly see that it is stopped
579575
// when we set the join handle.
580-
NotifyThreadDeath();
576+
_isDead = true;
577+
#if TARGET_UNIX || TARGET_BROWSER || TARGET_WASI
581578
// Inform the wait subsystem that the thread is exiting. For instance, this would abandon any mutexes locked by
582579
// the thread.
583580
_waitInfo?.OnThreadExiting();

src/coreclr/vm/comsynchronizable.cpp

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -394,14 +394,8 @@ extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle threa
394394
// grab a snapshot
395395
Thread::ThreadState state = thread->GetSnapshotState();
396396

397-
// If the thread is dead, just report that its dead.
398-
// It's possible that the thread may still be in the final bits of shutting down,
399-
// but for the purposes of the managed API surface, it should be reported
400-
// as stopped.
401397
if (state & Thread::TS_Dead)
402-
{
403-
return ThreadNative::ThreadStopped;
404-
}
398+
res |= ThreadNative::ThreadStopped;
405399

406400
if (state & Thread::TS_Background)
407401
res |= ThreadNative::ThreadBackground;
@@ -446,18 +440,6 @@ extern "C" void QCALLTYPE ThreadNative_ClearWaitSleepJoinState(QCall::ThreadHand
446440
thread->ResetThreadStateNC(Thread::TSNC_DebuggerSleepWaitJoin);
447441
}
448442

449-
extern "C" void QCALLTYPE ThreadNative_ReportDead(QCall::ThreadHandle thread)
450-
{
451-
CONTRACTL
452-
{
453-
QCALL_CHECK_NO_GC_TRANSITION;
454-
PRECONDITION(thread != NULL);
455-
}
456-
CONTRACTL_END;
457-
458-
thread->SetThreadState(Thread::TS_ReportDead);
459-
}
460-
461443
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
462444

463445
// Return whether the thread hosts an STA, is a member of the MTA or is not

src/coreclr/vm/comsynchronizable.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ extern "C" void QCALLTYPE ThreadNative_Initialize(QCall::ObjectHandleOnStack t);
5858
extern "C" INT32 QCALLTYPE ThreadNative_GetThreadState(QCall::ThreadHandle thread);
5959
extern "C" void QCALLTYPE ThreadNative_SetWaitSleepJoinState(QCall::ThreadHandle thread);
6060
extern "C" void QCALLTYPE ThreadNative_ClearWaitSleepJoinState(QCall::ThreadHandle thread);
61-
extern "C" void QCALLTYPE ThreadNative_ReportDead(QCall::ThreadHandle thread);
6261
extern "C" INT32 QCALLTYPE ThreadNative_ReentrantWaitAny(BOOL alertable, INT32 timeout, INT32 count, HANDLE *handles);
6362
#ifdef TARGET_WINDOWS
6463
extern "C" void QCALLTYPE ThreadNative_Interrupt(QCall::ThreadHandle thread);

src/coreclr/vm/qcallentrypoints.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ static const Entry s_QCall[] =
296296
DllImportEntry(ThreadNative_GetThreadState)
297297
DllImportEntry(ThreadNative_SetWaitSleepJoinState)
298298
DllImportEntry(ThreadNative_ClearWaitSleepJoinState)
299-
DllImportEntry(ThreadNative_ReportDead)
300299
DllImportEntry(ThreadNative_ReentrantWaitAny)
301300
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
302301
DllImportEntry(ThreadNative_GetApartmentState)

0 commit comments

Comments
 (0)