Repository: microsoft/terminal
File: src/host/consoleInformation.cpp (C++)

Code before:
    ZeroMemory((void*)&CPInfo, sizeof(CPInfo));
    ZeroMemory((void*)&OutputCPInfo, sizeof(OutputCPInfo));
}

static thread_local ULONG recursionCount = 0;
static til::ticket_lock lock;

bool CONSOLE_INFORMATION::IsConsoleLocked()
{
    return recursionCount != 0;
}

#pragma prefast(suppress : 26135, "Adding lock annotation spills into entire project. Future work.")
void CONSOLE_INFORMATION::LockConsole()
{
    const auto rc = ++recursionCount;
    FAIL_FAST_IF(rc == 0);
    if (rc == 1)
    {
        lock.lock();
    }
}

#pragma prefast(suppress : 26135, "Adding lock annotation spills into entire project. Future work.")
void CONSOLE_INFORMATION::UnlockConsole()
{
    const auto rc = --recursionCount;
    FAIL_FAST_IF(rc == ULONG_MAX);
    if (rc == 0)
    {
        lock.unlock();
    }
}

ULONG CONSOLE_INFORMATION::GetCSRecursionCount()
{
    return recursionCount;
}

// Routine Description:
// - This routine allocates and initialized a console and its associated
//   data - input buffer and screen buffer.
// - NOTE: Will read global ServiceLocator::LocateGlobals().getConsoleInformation expecting Settings to already be filled.
// Arguments:
// - title - Window Title to display
// Return Value:
// - STATUS_SUCCESS if successful.
[[nodiscard]] NTSTATUS CONSOLE_INFORMATION::AllocateConsole(const std::wstring_view title)
{
    CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
    // Synchronize flags

Review comment (suggestion):
I got to here and I was like "uhhhh shouldn't this be atomic" and then I looked back up where you made it `thread_local` and went "ooh duh."

Code after:
    ZeroMemory((void*)&CPInfo, sizeof(CPInfo));
    ZeroMemory((void*)&OutputCPInfo, sizeof(OutputCPInfo));
}

// Access to thread-local variables is thread-safe, because each thread
// gets its own copy of this variable with a default value of 0.
//
// Whenever we want to acquire the lock we increment recursionCount and on
// each release we decrement it again. We can then make the lock safe for
// reentrancy by only acquiring/releasing the lock if the recursionCount is 0.
// In a sense, recursionCount is counting the actual function
// call recursion depth of the caller. This works as long as
// the caller makes sure to properly call Unlock() once for each Lock().
static thread_local ULONG recursionCount = 0;
static til::ticket_lock lock;

bool CONSOLE_INFORMATION::IsConsoleLocked()
{
    return recursionCount != 0;
}

#pragma prefast(suppress : 26135, "Adding lock annotation spills into entire project. Future work.")
void CONSOLE_INFORMATION::LockConsole()
{
    // See description of recursionCount a few lines above.
    const auto rc = ++recursionCount;
    FAIL_FAST_IF(rc == 0);
    if (rc == 1)
    {
        lock.lock();
    }
}

#pragma prefast(suppress : 26135, "Adding lock annotation spills into entire project. Future work.")
void CONSOLE_INFORMATION::UnlockConsole()
{
    // See description of recursionCount a few lines above.
    const auto rc = --recursionCount;
    FAIL_FAST_IF(rc == ULONG_MAX);
    if (rc == 0)
    {
        lock.unlock();
    }
}

ULONG CONSOLE_INFORMATION::GetCSRecursionCount()
{
    return recursionCount;
}

// Routine Description: