Repository: dotnet/runtime
File: src/coreclr/vm/threadsuspend.cpp (C++)

Code before:

    //
    // Remember that we're the one suspending the EE
    //
    g_pSuspensionThread = pCurThread;

    //
    // First, we reset the event that we're about to tell other threads to wait for.
    //
    GCHeapUtilities::GetGCHeap()->ResetWaitForGCEvent();

    //
    // Tell all threads, globally, to wait for WaitForGCEvent.
    //
    ThreadStore::SetThreadTrapForSuspension();

    // Flush the store buffers on all CPUs, to ensure two things:
    // - we get a reliable reading of the threads' m_fPreemptiveGCDisabled state
    // - other threads see that g_TrapReturningThreads is set
    // See VSW 475315 and 488918 for details.
    minipal_memory_barrier_process_wide();

#ifdef FEATURE_PERFTRACING
    // Save the GC mode for each thread. This must be done after the memory barrier
    // to get a reliable reading, but before suspension changes thread states.
    // Used by the sample profiler to determine if the thread was executing managed code.
    {
        Thread* pTargetThread = NULL;
        while ((pTargetThread = ThreadStore::GetThreadList(pTargetThread)) != NULL)
        {
            pTargetThread->SaveGCModeOnSuspension();
        }
    }
#endif // FEATURE_PERFTRACING

    int prevRemaining = INT32_MAX;
    bool observeOnly = true;
    uint32_t rehijackDelay = 8;
    uint32_t usecsSinceYield = 0;

    while(true)
    {
        int remaining = 0;
        Thread* pTargetThread = NULL;
        while ((pTargetThread = ThreadStore::GetThreadList(pTargetThread)) != NULL)
        {
            if (pTargetThread == pCurThread)
                continue;

            if (pTargetThread->m_fPreemptiveGCDisabled.LoadWithoutBarrier())
            {

Review comment (suggestion):
Going through all threads and reading/writing to their thread local state scales poorly on many core machines with a lot of threads.

Can we make this pay for play and do this only when we are suspending for the event pipe sampler?

Alternatively, we can set/reset a thread local flag around `ThreadStore::IsTrappingThreadsForSuspension()` path in threadsuspend.cpp. We should hit that path only if the thread has been executing managed code.

Code after:

    // Caller is expected to be holding the ThreadStore lock.  Also, caller must
    // have set GcInProgress before coming here, or things will break;
    _ASSERTE(ThreadStore::HoldingThreadStore() || IsAtProcessExit());

    // This thread
    Thread  *pCurThread = GetThreadNULLOk();

    //
    // Remember that we're the one suspending the EE
    //
    g_pSuspensionThread = pCurThread;

    //
    // First, we reset the event that we're about to tell other threads to wait for.
    //
    GCHeapUtilities::GetGCHeap()->ResetWaitForGCEvent();

    //
    // Tell all threads, globally, to wait for WaitForGCEvent.
    //
    ThreadStore::SetThreadTrapForSuspension();

    // Flush the store buffers on all CPUs, to ensure two things:
    // - we get a reliable reading of the threads' m_fPreemptiveGCDisabled state
    // - other threads see that g_TrapReturningThreads is set
    // See VSW 475315 and 488918 for details.
    minipal_memory_barrier_process_wide();

    int prevRemaining = INT32_MAX;
    bool observeOnly = true;
    uint32_t rehijackDelay = 8;
    uint32_t usecsSinceYield = 0;

    while(true)
    {
        int remaining = 0;
        Thread* pTargetThread = NULL;
        while ((pTargetThread = ThreadStore::GetThreadList(pTargetThread)) != NULL)
        {
            if (pTargetThread == pCurThread)
                continue;

            if (pTargetThread->m_fPreemptiveGCDisabled.LoadWithoutBarrier())
            {
                remaining++;
                if (!observeOnly)
                {
                    pTargetThread->Hijack();
                }
            }