Skip to content

Commit 85899f0

Browse files
committed
Call OnThreadExiting outside of the thread store lock
1 parent ac08ce6 commit 85899f0

File tree

1 file changed

+83
-69
lines changed

1 file changed

+83
-69
lines changed

src/coreclr/vm/threads.cpp

Lines changed: 83 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2583,100 +2583,114 @@ void Thread::CleanupDetachedThreads()
25832583

25842584
_ASSERTE(!ThreadStore::HoldingThreadStore());
25852585

2586-
ThreadStoreLockHolder threadStoreLockHolder;
2586+
ArrayList threadsWithManagedCleanup;
25872587

2588-
Thread *thread = ThreadStore::GetAllThreadList(NULL, 0, 0);
2588+
{
2589+
ThreadStoreLockHolder threadStoreLockHolder;
25892590

2590-
STRESS_LOG0(LF_SYNC, LL_INFO1000, "T::CDT called\n");
2591+
Thread *thread = ThreadStore::GetAllThreadList(NULL, 0, 0);
25912592

2592-
while (thread != NULL)
2593-
{
2594-
Thread *next = ThreadStore::GetAllThreadList(thread, 0, 0);
2593+
STRESS_LOG0(LF_SYNC, LL_INFO1000, "T::CDT called\n");
25952594

2596-
if (thread->IsDetached())
2595+
while (thread != NULL)
25972596
{
2598-
STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - detaching thread 0x%p\n", thread);
2597+
Thread *next = ThreadStore::GetAllThreadList(thread, 0, 0);
25992598

2600-
if (!thread->IsGCSpecial())
2599+
if (thread->IsDetached())
26012600
{
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+
}
26132609

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 =
26272623
#ifdef DEBUGGING_SUPPORTED
2628-
CORDebuggerAttached();
2624+
CORDebuggerAttached();
26292625
#else // !DEBUGGING_SUPPORTED
2630-
FALSE;
2626+
FALSE;
26312627
#endif // !DEBUGGING_SUPPORTED
26322628

2633-
if (debuggerAttached)
2634-
ThreadStore::UnlockThreadStore();
2629+
if (debuggerAttached)
2630+
ThreadStore::UnlockThreadStore();
26352631

2636-
thread->OnThreadTerminate(debuggerAttached ? FALSE : TRUE);
2632+
thread->OnThreadTerminate(debuggerAttached ? FALSE : TRUE);
26372633

26382634
#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))
26402661
{
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;
26572668
}
26582669
else
2659-
#endif // DEBUGGING_SUPPORTED
26602670
{
26612671
thread = next;
26622672
}
26632673
}
2664-
else if (thread->HasThreadState(TS_Finalized))
2665-
{
2666-
STRESS_LOG1(LF_SYNC, LL_INFO1000, "T::CDT - finalized thread 0x%p\n", thread);
26672674

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;
26772676
}
26782677

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+
}
26802694
}
26812695

26822696
#ifdef FEATURE_COMINTEROP_APARTMENT_SUPPORT

0 commit comments

Comments
 (0)