@@ -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 ( ) ;
0 commit comments