@@ -2583,100 +2583,114 @@ void Thread::CleanupDetachedThreads()
2583
2583
2584
2584
_ASSERTE (!ThreadStore::HoldingThreadStore ());
2585
2585
2586
- ThreadStoreLockHolder threadStoreLockHolder ;
2586
+ ArrayList threadsWithManagedCleanup ;
2587
2587
2588
- Thread *thread = ThreadStore::GetAllThreadList (NULL , 0 , 0 );
2588
+ {
2589
+ ThreadStoreLockHolder threadStoreLockHolder;
2589
2590
2590
- STRESS_LOG0 (LF_SYNC, LL_INFO1000, " T::CDT called \n " );
2591
+ Thread *thread = ThreadStore::GetAllThreadList ( NULL , 0 , 0 );
2591
2592
2592
- while (thread != NULL )
2593
- {
2594
- Thread *next = ThreadStore::GetAllThreadList (thread, 0 , 0 );
2593
+ STRESS_LOG0 (LF_SYNC, LL_INFO1000, " T::CDT called\n " );
2595
2594
2596
- if (thread-> IsDetached () )
2595
+ while (thread != NULL )
2597
2596
{
2598
- STRESS_LOG1 (LF_SYNC, LL_INFO1000, " T::CDT - detaching thread 0x%p \n " , thread );
2597
+ Thread *next = ThreadStore::GetAllThreadList ( thread, 0 , 0 );
2599
2598
2600
- if (! thread->IsGCSpecial ())
2599
+ if (thread->IsDetached ())
2601
2600
{
2602
- GCX_COOP ();
2603
- // During the actual thread shutdown,
2604
- // it may not be practical for us to run enough managed code to clean up
2605
- // any managed code that needs to know when a thread exits.
2606
- // Instead, run that clean up here when the thread object is finalized
2607
- // (which is definitely after the thread has exited)
2608
- PREPARE_NONVIRTUAL_CALLSITE (METHOD__THREAD__ON_THREAD_EXITING);
2609
- DECLARE_ARGHOLDER_ARRAY (args, 1 );
2610
- args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER (thread->GetExposedObject ());
2611
- CALL_MANAGED_METHOD_NORET (args);
2612
- }
2601
+ STRESS_LOG1 (LF_SYNC, LL_INFO1000, " T::CDT - detaching thread 0x%p\n " , thread);
2602
+
2603
+ if (!thread->IsGCSpecial ())
2604
+ {
2605
+ // Record threads that we'll need to do some managed cleanup of
2606
+ // after we exit the thread store lock.
2607
+ threadsWithManagedCleanup.Append (thread);
2608
+ }
2613
2609
2614
- // Unmark that the thread is detached while we have the
2615
- // thread store lock. This will ensure that no other
2616
- // thread will race in here and try to delete it, too.
2617
- thread->ResetThreadState (TS_Detached);
2618
- InterlockedDecrement (&m_DetachCount);
2619
- if (!thread->IsBackground ())
2620
- InterlockedDecrement (&m_ActiveDetachCount);
2621
-
2622
- // If the debugger is attached, then we need to unlock the
2623
- // thread store before calling OnThreadTerminate. That
2624
- // way, we won't be holding the thread store lock if we
2625
- // need to block sending a detach thread event.
2626
- BOOL debuggerAttached =
2610
+ // Unmark that the thread is detached while we have the
2611
+ // thread store lock. This will ensure that no other
2612
+ // thread will race in here and try to delete it, too.
2613
+ thread->ResetThreadState (TS_Detached);
2614
+ InterlockedDecrement (&m_DetachCount);
2615
+ if (!thread->IsBackground ())
2616
+ InterlockedDecrement (&m_ActiveDetachCount);
2617
+
2618
+ // If the debugger is attached, then we need to unlock the
2619
+ // thread store before calling OnThreadTerminate. That
2620
+ // way, we won't be holding the thread store lock if we
2621
+ // need to block sending a detach thread event.
2622
+ BOOL debuggerAttached =
2627
2623
#ifdef DEBUGGING_SUPPORTED
2628
- CORDebuggerAttached ();
2624
+ CORDebuggerAttached ();
2629
2625
#else // !DEBUGGING_SUPPORTED
2630
- FALSE ;
2626
+ FALSE ;
2631
2627
#endif // !DEBUGGING_SUPPORTED
2632
2628
2633
- if (debuggerAttached)
2634
- ThreadStore::UnlockThreadStore ();
2629
+ if (debuggerAttached)
2630
+ ThreadStore::UnlockThreadStore ();
2635
2631
2636
- thread->OnThreadTerminate (debuggerAttached ? FALSE : TRUE );
2632
+ thread->OnThreadTerminate (debuggerAttached ? FALSE : TRUE );
2637
2633
2638
2634
#ifdef DEBUGGING_SUPPORTED
2639
- if (debuggerAttached)
2635
+ if (debuggerAttached)
2636
+ {
2637
+ ThreadSuspend::LockThreadStore (ThreadSuspend::SUSPEND_OTHER);
2638
+
2639
+ // We remember the next Thread in the thread store
2640
+ // list before deleting the current one. But we can't
2641
+ // use that Thread pointer now that we release the
2642
+ // thread store lock in the middle of the loop. We
2643
+ // have to start from the beginning of the list every
2644
+ // time. If two threads T1 and T2 race into
2645
+ // CleanupDetachedThreads, then T1 will grab the first
2646
+ // Thread on the list marked for deletion and release
2647
+ // the lock. T2 will grab the second one on the
2648
+ // list. T2 may complete destruction of its Thread,
2649
+ // then T1 might re-acquire the thread store lock and
2650
+ // try to use the next Thread in the thread store. But
2651
+ // T2 just deleted that next Thread.
2652
+ thread = ThreadStore::GetAllThreadList (NULL , 0 , 0 );
2653
+ }
2654
+ else
2655
+ #endif // DEBUGGING_SUPPORTED
2656
+ {
2657
+ thread = next;
2658
+ }
2659
+ }
2660
+ else if (thread->HasThreadState (TS_Finalized))
2640
2661
{
2641
- ThreadSuspend::LockThreadStore (ThreadSuspend::SUSPEND_OTHER);
2642
-
2643
- // We remember the next Thread in the thread store
2644
- // list before deleting the current one. But we can't
2645
- // use that Thread pointer now that we release the
2646
- // thread store lock in the middle of the loop. We
2647
- // have to start from the beginning of the list every
2648
- // time. If two threads T1 and T2 race into
2649
- // CleanupDetachedThreads, then T1 will grab the first
2650
- // Thread on the list marked for deletion and release
2651
- // the lock. T2 will grab the second one on the
2652
- // list. T2 may complete destruction of its Thread,
2653
- // then T1 might re-acquire the thread store lock and
2654
- // try to use the next Thread in the thread store. But
2655
- // T2 just deleted that next Thread.
2656
- thread = ThreadStore::GetAllThreadList (NULL , 0 , 0 );
2662
+ STRESS_LOG1 (LF_SYNC, LL_INFO1000, " T::CDT - finalized thread 0x%p\n " , thread);
2663
+
2664
+ thread->ResetThreadState (TS_Finalized);
2665
+ // We have finalized the managed Thread object. Now it is time to clean up the unmanaged part
2666
+ thread->DecExternalCount (TRUE );
2667
+ thread = next;
2657
2668
}
2658
2669
else
2659
- #endif // DEBUGGING_SUPPORTED
2660
2670
{
2661
2671
thread = next;
2662
2672
}
2663
2673
}
2664
- else if (thread->HasThreadState (TS_Finalized))
2665
- {
2666
- STRESS_LOG1 (LF_SYNC, LL_INFO1000, " T::CDT - finalized thread 0x%p\n " , thread);
2667
2674
2668
- thread->ResetThreadState (TS_Finalized);
2669
- // We have finalized the managed Thread object. Now it is time to clean up the unmanaged part
2670
- thread->DecExternalCount (TRUE );
2671
- thread = next;
2672
- }
2673
- else
2674
- {
2675
- thread = next;
2676
- }
2675
+ s_fCleanFinalizedThread = FALSE ;
2677
2676
}
2678
2677
2679
- s_fCleanFinalizedThread = FALSE ;
2678
+ ArrayList::Iterator iter = threadsWithManagedCleanup.Iterate ();
2679
+ while (iter.Next ())
2680
+ {
2681
+ // During the actual thread shutdown,
2682
+ // it may not be practical for us to run enough managed code to clean up
2683
+ // any managed code that needs to know when a thread exits.
2684
+ // Instead, run that clean up here when the thread object is finalized
2685
+ // (which is definitely after the thread has exited)
2686
+ //
2687
+ // We know its safe to do this work here as we can't have finalized the managed thread object yet
2688
+ // as we're running on the finalizer thread right now.
2689
+ PREPARE_NONVIRTUAL_CALLSITE (METHOD__THREAD__ON_THREAD_EXITING);
2690
+ DECLARE_ARGHOLDER_ARRAY (args, 1 );
2691
+ args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER (((Thread*)iter.GetElement ())->GetExposedObject ());
2692
+ CALL_MANAGED_METHOD_NORET (args);
2693
+ }
2680
2694
}
2681
2695
2682
2696
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT
0 commit comments