Repository: microsoft/terminal
File: src/interactivity/base/ServiceLocator.cpp (C++)

Code before:
}

void ServiceLocator::RundownAndExit(const HRESULT hr)
{
    static thread_local bool preventRecursion = false;
    static std::atomic<bool> locked;

    // BODGY:
    // pRender->TriggerTeardown() might cause another VtEngine pass, which then might fail to write to the IO pipe.
    // If that happens it calls VtIo::CloseOutput(), which in turn calls ServiceLocator::RundownAndExit().
    // This prevents the unintended recursion and resulting deadlock.
    if (std::exchange(preventRecursion, true))
    {
        return;
    }

    // MSFT:40146639
    //   The premise of this function is that 1 thread enters and 0 threads leave alive.
    //   We need to prevent anyone from calling us until we actually ExitProcess(),
    //   so that we don't TriggerTeardown() twice. LockConsole() can't be used here,
    //   because doing so would prevent the render thread from progressing.
    if (locked.exchange(true, std::memory_order_relaxed))
    {
        Sleep(INFINITE);
    }

    // MSFT:15506250
    // In VT I/O Mode, a client application might die before we've rendered
    //      the last bit of text they've emitted. So give the VtRenderer one
    //      last chance to paint before it is killed.
    if (s_globals.pRender)
    {
        s_globals.pRender->TriggerTeardown();
    }

    // MSFT:40226902 - HOTFIX shutdown on OneCore, by leaking the renderer, thereby
    // reducing the change for existing race conditions to turn into deadlocks.
#ifndef NDEBUG
    // By locking the console, we ensure no background tasks are accessing the
    // classes we're going to destruct down below (for instance: CursorBlinker).
    s_globals.getConsoleInformation().LockConsole();
#endif

    // A History Lesson from MSFT: 13576341:
    // We introduced RundownAndExit to give services that hold onto important handles
    // an opportunity to let those go when we decide to exit from the console for various reasons.
    // This was because Console IO Services (ConIoSvcComm) on OneCore editions was holding onto
    // pipe and ALPC handles to talk to CSRSS ConIoSrv.dll to broker which console got display/keyboard control.
    // If we simply run straight into TerminateProcess, those handles aren't necessarily released right away.
    // The terminate operation can have a rundown period of time where APCs are serviced (such as from
    // a DirectX kernel callback/flush/cleanup) that can take substantially longer than we expect (several whole seconds).

Review comment (suggestion):
Please put a big ol' comment here why it's ok to put `Sleep(INFINITE);`. I think it'd be nice instead of having to find this PR and read through the discussion.

Code after:
}

void ServiceLocator::RundownAndExit(const HRESULT hr)
{
    static thread_local bool preventRecursion = false;
    static std::atomic<bool> locked;

    // BODGY:
    // pRender->TriggerTeardown() might cause another VtEngine pass, which then might fail to write to the IO pipe.
    // If that happens it calls VtIo::CloseOutput(), which in turn calls ServiceLocator::RundownAndExit().
    // This prevents the unintended recursion and resulting deadlock.
    if (std::exchange(preventRecursion, true))
    {
        return;
    }

    // MSFT:40146639
    //   The premise of this function is that 1 thread enters and 0 threads leave alive.
    //   We need to prevent anyone from calling us until we actually ExitProcess(),
    //   so that we don't TriggerTeardown() twice. LockConsole() can't be used here,
    //   because doing so would prevent the render thread from progressing.
    if (locked.exchange(true, std::memory_order_relaxed))
    {
        // If we reach this point, another thread is already in the process of exiting.
        // There's a lot of ways to suspend ourselves until we exit, one of which is "sleep forever".
        Sleep(INFINITE);
    }

    // MSFT:15506250
    // In VT I/O Mode, a client application might die before we've rendered
    //      the last bit of text they've emitted. So give the VtRenderer one
    //      last chance to paint before it is killed.
    if (s_globals.pRender)
    {
        s_globals.pRender->TriggerTeardown();
    }

    // MSFT:40226902 - HOTFIX shutdown on OneCore, by leaking the renderer, thereby
    // reducing the change for existing race conditions to turn into deadlocks.
#ifndef NDEBUG
    // By locking the console, we ensure no background tasks are accessing the
    // classes we're going to destruct down below (for instance: CursorBlinker).
    s_globals.getConsoleInformation().LockConsole();
#endif

    // A History Lesson from MSFT: 13576341:
    // We introduced RundownAndExit to give services that hold onto important handles
    // an opportunity to let those go when we decide to exit from the console for various reasons.
    // This was because Console IO Services (ConIoSvcComm) on OneCore editions was holding onto
    // pipe and ALPC handles to talk to CSRSS ConIoSrv.dll to broker which console got display/keyboard control.
    // If we simply run straight into TerminateProcess, those handles aren't necessarily released right away.