<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/110954>110954</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [libc++] Avoid allocations when the process is shutting down
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            libc++
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          m417z
      </td>
    </tr>
</table>

<pre>
    This issue is motivated by a pretty nasty problem I had to debug on Windows. I'm not sure libc++ does something that's strictly wrong in this case, but I believe that it can improve and reduce the likelihood of such problems.

After several attempts, I reduced my problem to the following. The issue can be observed in Windows 10 by compiling the snippet with the [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain with the `msvcrt` runtime. I couldn't reproduce it on Windows 11, but a different variant might work.

```cpp
// Compile: g++.exe test.cpp -target x86_64-w64-mingw32 -Wl,-subsystem,windows -o test.exe

#include <windows.h>

class TestDestruction {
   public:
    ~TestDestruction() {
        // Expected to be run and determine the process exit code.
        TerminateProcess(GetCurrentProcess(), 5678);
    }
};
TestDestruction x;

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
        SHELLEXECUTEINFOW sh{
            .cbSize = sizeof(sh),
            .lpVerb = L"open",
            .lpFile = L"shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
            .nShow = SW_SHOWMINNOACTIVE,
        };
        ShellExecuteExW(&sh);

        ExitProcess(1234);
        return 0;
}
```
The code should return `5678`, but sometimes it returns `1234`, in which case `~TestDestruction` never runs:

```
start /wait test.exe & echo %errorlevel%
1234

start /wait test.exe & echo %errorlevel%
5678
```

A more convenient test case which does several attempts:
<details><summary>Test case source code (click to expand)</summary>
<p>

```cpp
#include <windows.h>

#include <locale.h>
#include <stdio.h>
#include <time.h>
#include <wchar.h>

class TestDestruction {
   public:
    ~TestDestruction() {
        // Expected to run and determine the process exit code.
        TerminateProcess(GetCurrentProcess(), 5678);
    }
};
TestDestruction x;

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
    if (lpCmdLine && lpCmdLine[0] == '1' && lpCmdLine[1] == '\0') {
        SHELLEXECUTEINFOW sh{
            .cbSize = sizeof(sh),
            .lpVerb = L"open",
            .lpFile = L"shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}",
            .nShow = SW_SHOWMINNOACTIVE,
        };
        ShellExecuteExW(&sh);

        ExitProcess(1234);
        return 0;
    }

    WCHAR path[MAX_PATH];
    GetModuleFileNameW(nullptr, path, MAX_PATH);
    WCHAR cmdline[MAX_PATH + 16];
    wsprintfW(cmdline, L"\"%s\" 1", path);
    int i;
    for (i = 0; i < 20; i++) {
        STARTUPINFOW si = {sizeof(si)};
        PROCESS_INFORMATION pi;
        if (!CreateProcessW(path, cmdline, nullptr, nullptr, FALSE, 0, nullptr,
                            nullptr, &si, &pi)) {
            MessageBoxA(nullptr, "CreateProcessW", "Error", MB_ICONERROR);
            break;
        }

        WaitForSingleObject(pi.hProcess, INFINITE);

        DWORD exitCode;
        GetExitCodeProcess(pi.hProcess, &exitCode);
        if (exitCode != 5678) {
            char msg[256];
            wsprintfA(msg, "Unexpected exit code: %u", exitCode);
            MessageBoxA(nullptr, msg, "Error", MB_ICONERROR);
            break;
        }

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);

        Sleep(1000);
    }

    if (i == 20) {
        MessageBoxA(nullptr, "Failed to trigger bug in 20 attempts", "Hmm",
                    MB_ICONINFORMATION);
    } else {
        char msg[256];
        wsprintfA(msg, "Triggered bug in %u attempts", i + 1);
        MessageBoxA(nullptr, msg, "Success?", MB_ICONINFORMATION);
    }
}
```

</p>
</details> 

`TerminateProcess` is only for a quick demonstration, it could also be something like database flushing which is problematic to be skipped.

Even worse, running this snippet may show an error message box to the user. It's always called in the buggy scenario, but usually the process terminates quickly enough and it's not visible. The stack trace in this case is the following:
```
0d user32!MessageBoxA+0x4e
0e msvcrt!_crtMessageBoxA+0x1fc
0f msvcrt!NMSG_WRITE+0x16a
10 msvcrt!amsg_exit+0x45
11 msvcrt!getptd+0x1b
12 msvcrt!setlocale+0x67
13 libc__!ZNSt3__18ios_base4InitD1Ev+0x1aa
14 libc__!ZTv0_n24_NSt3__19strstreamD0Ev+0x442
15 libc__!ZNKSt3__17codecvtIwciE10do_unshiftERiPcS3_RS3_+0x45
16 libc__!ZNSt3__18ios_base4InitD1Ev+0xe10
17 libc__!ZNSt3__113basic_ostreamIwNS_11char_traitsIwEEE5flushEv+0x59
18 libc__!ZNSt3__18ios_base4InitC1Ev+0x88
19 libc__!_execute_onexit_table+0x4e [../misc/onexit_table.c @ 67] 
1a libc__!_CRT_INIT+0x8f [../crt/crtdll.c @ 131] 
1b libc__!__DllMainCRTStartup+0x157 [../crt/crtdll.c @ 196] 
1c ntdll!LdrpCallInitRoutine+0x61
1d ntdll!LdrShutdownProcess+0x22a
1e ntdll!RtlExitUserProcess+0xad
1f kernel32!ExitProcessImplementation+0xb
20 explorer!wWinMain+0xe6c
21 explorer!__scrt_common_main_seh+0x106
22 kernel32!BaseThreadInitThunk+0x14
23 ntdll!RtlUserThreadStart+0x21
```

After trying various things to try and explain the problem, here is what happens to the best of my understanding:
* `ShellExecuteExW` spawns extra threads, and these threads are still active in the background after it returns.
* `ExitProcess` is called when one of the background threads is using the CRT heap. This makes the heap unusable during process shutdown:
> all the other threads in the process are forcible terminated
> [...]
> it can’t [allocate] because the heap lock was owned by another thread

https://devblogs.microsoft.com/oldnewthing/20120106-00/?p=8663
* During libc++ cleanup, `setlocale` is being called (likely via `__libcpp_locale_guard`).
* `msvcrt`'s implementation of `setlocale` calls `_getptd_noexit`, which tries to get the per-thread data structure, but it might be already freed, so [it tries to allocate it again](https://github.com/adityaramesh/concurrency_runtime/blob/c92c41b8ff41657a164c7760fd67b77d84ba766f/VS2013_SP2/crt/src/tidtable.c#L283).
* Allocation fails, in which case [a message box is shown](https://github.com/adityaramesh/concurrency_runtime/blob/c92c41b8ff41657a164c7760fd67b77d84ba766f/VS2013_SP2/crt/src/crt0msg.c#L297).

This is a pretty bad problem that can cause issues such as data corruption and bad user experience due to the flashing message box.

There are several factors that contribute to the problem, but the worst offender is probably `msvcrt` - not only does it treat memory allocation failure as fatal instead of returning `NULL`, it also shows a message box to the user.

Still, on libc++'s side, are the `setlocale` calls really necessary during process shutdown? If not, it can check whether the process is shutting down (via `RtlDllShutdownInProgress` or `DllMain`'s `lpvReserved` value during `DLL_PROCESS_DETACH`), and do only the minimal necessary cleanup in this case.

And if the calls are necessary, is the code designed to handle failures gracefully? If `setlocale` returned `NULL` as it should in this case, I see that the code throws `bad_alloc`, I assume that it will cause the process shutdown to be aborted without proceeding with the other destructors.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzsektv47iW8K9hbYgYEm3LziILx48u40ulCrG76-LbCJR0bPGGIjUkZce9mN8-OKRky05S3RhgZu7iGoW02zrn8LxfFLdW7BXAAxk_kvHiC29cqc1DNYonf37JdHF62JbCUmFtA1RYWmknDtxBQbMT5bQ24NyJKm7didZGZxIquqYlL6jTtICs2VOt6E-hCn20A7ombFJRpR21jQEqRZYT9kjYIy00WGp1Ba4Uak9dyR1hE0utMyJ38kSPRqs9FYo6ZCjnFgib06xxdE0zkAIO4LGocDTnioqqNvoAlKuCGiiaHB_jka8gRal1QfWO2iYvO77tgEQLEs3C39nOgaEWDmC4pNw5qGpn8ch1S66g1UVmpz31nZZSH4XaD-i2hFZtyE0GVGcWzAEKlKFVCI0j1GOuq1rIIDZQq0Rdg6NH4Ur_Axk_Snmo7iqh9kcyXhA2LZ2rLRnOCFsRttoLVzbZINcVYavKOm3sPzVhqx4Wu6dOa5mXXKge5SSq7CE3jiQRNY1yooIBXdNcN7JQhE0cNVAb7bUnXM-UNI479XNaiN0ODChHD9wIrhytxL509KjN65VSSRKFf3ldt794AejcawDIcEb3wSEG8AbUgXWDvK7pneNmD46-TZM0Gd0dk1GQa8jo3U9J2PzONpk9WQcVYfNjy-OdDhTgDa64YEOhctkUQMmwAx6UZLjsQ-WSW0u3YN0CrDNN7oRWlEwew2NKad1kUuRohe4X-p838IRNUfV9LP9pxV6-1ZBjMDmNHmIa5d21AAemEip4bG10DtZSeEPP1gUMrmltPSx38CMAEjb9Ddy8MWiRy2-E3aPFxsnEfx32OCKTRSv5ZHF-cCv52_lJ-CuUoz_Xz7Mfa_SJb1ygsF_Xz5vt7Hm-pOVaWcdVjlF6zW_v0wP_YeDwd1Cefmy2L1TW86p4EuqXoMihmlfFptTHD62w-bp8elr-Yzn_fbtcP6--_6S2fAeEn0GebcSf6C4LasWfoHeETW0ZVPoBuKz_AJN58CfCmK5BEcY-g10JCWdYW4KU6FPh3-SRRYtotFpGd8PZcnYXR8n93YwtpnfRNIrY4zAaRvcLtNtn9BWK78lvfqabr99_fls_P3-fzbfrP5bvMPoOcNYScrR8g7xxsHz76V0pCcJfe0SHsHwTPb-L2XB063D4MeAao2h0IXL2wi5LtI5Ygnd7aktMSx0iSSLvy0nUJSJfPUQFFlNVgLII5jkIYJj8SpGXvn7gs3cBm0RUYdLHYLTn2P6QMeu4cRjKRy7cOdNQwhIKeakpYWMwRhsJB5CEjQOW56ZH9L9HxYv-EVNt9aKVNqg2dQAlMDMj4SB1UECot7fl7SzvcF6A40JazIrDuW2qipsTGS63ZzpWNyZvTUPYNJcif8VEBm81V4U3-Zyw1QX1TLq-SbUflYW_k6KvoaTOuYQ-0NVj6wqhP33qK99nD495yc3_eYH4d3X436sOYocufaaD0YgBeSE8fozIeIFpFTMrYZOYsMlHYPE1GBnPI_zPv8vRv3o5unb9808_519nL7TmriTjx2-zf6Q_Ztuv2JT38X4D900XjQTU5TOvANlUjZS1MxhmHp3N6Rn_hqFwSF4VMjhRB0dxUoqT2-OOtjZCuR2e0iGxubcfGc-9KcY2fKNxsEzLwvWxGBLi6pedNhgIwhsMdUPx65yy8D206h8783b2sv39R-vHgQCZPF7cVeDpH9j3x8v3-XKzSRHz5dtsu_7-TGvxDixEKGHx3MAlu6EGOuX2NNFTfe_rava0QZ-j0fWDT1PG-dMjgs4n2i-1l-kjbeDnG1jL9_Co32bX3kAYuxWCtb8vsfK3__vtMV3Pvz8vX16-v3zkwfjJDPDXd08-cGPvZVy4lTYbofYSvmf_hNyh-sSgPIfLnK6fV-vn9Xb5eXwtfn5_Wfj6M9cFvDv8N3DL9tklCm8OISw5438gWbB1B0EJi9Gb2lr1sbKxYtPK7sn4kY3fBUz36QIHDYLAQem_K-jK7rmq4mRK2LhpbfErbn9p68sp_6OmnUtt4StXhYRbbb-n_h54Wxrgxecm30iAGpNpFEWftws31VR0ZZBFH5rtV_Gx4kKGLsgZsd-DoVnjl0Es6u1muqj5WlWf1Z_zYUHjvSTzgRwUJE4Jt4z-tW997FfbwDoUHfPoULfsi5DjP7LT33CqTZN7Kw9X1571azl_PX-d23bCVnW_jSdsdRkS6E1D_67vTCIqLNVKnnxV4fQ_GhwYCqi0ss7w0A3P_frOz3lcWr8VuSwFpXgFWnDHM5w_drKx_ucw0AjbLeO4E3m7UbGvoq6huFpCLQ-g6FGbsEA0jVJh8ybsefVW8RNOm0fKFfXDF62C7mmm37pdX2PBDOg6rCm5PPKTpTmXMmz4ECJr9vsTtTkoboTuxtTGNlzK01X37jpt2aAWeaKgdLMvfccvwhlKO3oQVmQSwnrROo4jl-E5XC1GURdX28jLXHdt2qjwUgwZYfGVdz1Gb6N2ZxYBbXeELE5z427h4l3eAu4ugM_fNr-lP1983UCYhLfDb3SB4ZXdp5hIw3HdeBxfIPbgalcEClk3PV8eW3Bh5vMQyaSFGPq1cpoSFv__540bpmk8Fdqm6DSjtRJuES8PgSjv2Br1cLaHKFVslLa499YZ6wzwahG1eKMRa_HG_bP-X0CYYL3ID259zMUyjgqdNsqWYueWL-JHvhmmL5thei1z8rdZhrg1XTx5jxMPM25FnurA7_r4vEnjGNNV6gwXzq6Py-Vy7AOnpTe-b8lN_4qFecfCtF08xPcXlBRCQ55qhRZNHc9as4z8_nowIGxVCZsTtuqDDHJKRhFNJn5KCmR5j-z8ZZti-xEO3p1JeQfAv4WULY14GPeIZD0i6UJKnEDnL9uN48Y1dbD-ePJLevdJj15OFT4jLH4qTD3nUqJOXnTjfIuJ_he3oEUfdFM2rtBHdS6_j9EbY53bwRn0xUnskn63YPqgvGghd_QVjALpQ7U30ayrWkIFyrX58zF6ayOFRRTeaqkNGMLi43kKRydK2pBlcR8mTW1uXJrrqtIqrbhQqYUy6CpKWgzWZ-SRWwi9AmpjWzbqNYC3Gy427AuIwgVob4agi_hXWyx_B-PMCRP0AXNog4lNqL0NrcDJp0cUgbcpty0BmGtLMD4THkvuaMnrGpTtcncG1lG9o9WJNqoAYx1XxVWiZDNKkuh22Ewiamt-VJbCmzOcOi-O72GRE1eChe5Hyg2maCEl5bkTBziXBZ6_7o1uVEG5l_Cyrhxcnd6fXEP5bOvLsQRFtQIU4YZid7iwtLHdldL8ZUtL4DWWDWFpxV8hVAj8kTaqsRiMtGgMYnRFyba-29sKLimX0mNqV6JtutPUVTlDyXfa5FiqLrWtuFDxcTfA1un8U7i2I0tGphG5v3cIwyUmeAcYiBnkvLFwYVvq_JUeuaX6qNr7SNXnqu9J1_dlBRwyqfd2UIncaKt3rr0707JQcPQeRtiKRTGL4ii5wyZ3RYbY_SymSTK8GGkRNNa7x8wlcIUZZo4WvJSoYL8MELy1ImFTfxt5ogfBETpNkVBdpwEn3TfcFH5zfX_tGOdLO98XiKscgD5xezIe6FfhaSipqdK-8IaleOifnBHg42MPLhgTzF3QpO-5aFj2NeZ87yq6e74MKJcIeKI7A1AggNVoQOEudDtjIh7fYy76q6tMXgh34oZXYEtMz1rlfm-Zn9L2spKwVSZ1hg_vWT6Ks-luN4qT8YTHySifTJJoVySTbDIppqOMT5JkR9jqjw2L4mG6-cHOmd8arExOFG1VImz4xKbDa8XPggCo453vet9fKIwf-VWriD1liSH0ryZqblxU2X0r6f2kJ2nY74Zb_8sVf8aLy2U3ZtScKxoi0t9y23CXzm1wllwb09ReV5gYERs7TczVYASoHLMNnK_NJQ-tfE93N-xgMvcJtb2y2PHcaWNbXrRyRmSNO1Ps1QF0VfwJW35M-jvAjN_NCzyTp-t78DvfaPtBxV-SeB8G7mgFlcaSc-0GDTJm6Y47LqlQ1mHA6F2b0VEokkTPvz89dVdQLkw26Beo388mi774G6wiiKxVL9eE9yNE4QMSddNe6X8Q-gb8yKEA0zM3p89T_Yqud6iAbhRDK5eAmbaENrle8rwImA5JITqmtDaXvTi5kLJrf9bYAO1NW8i0QYi2K-uyGEkiWR9eILwkgWAHLptzUUKEp6e02w4ultvZ_GvIjV31LXSwGnJYCSUqLnsSt4n5alC6fuMDJ61QT4PSUKVnfK-PUDT9jVcB_sUZv5Mo_e6k8wZL9ziR7RopT60-b40SfANLwNk10IeE6-44b19zWVML7estZw5cadCDSBJlvEi9V7Yutqbc2qa6vA9zxCbkUj5vrd4OyzzTxmF3IVypGxfAoPAjdvfGSCiwRXvzo40dfCkehsX98J5_gYd4wib37D5i0y_lQzJmuyQaDXdDyPIhsHGSJFF0P5xGnBV8nHwRDyxioziKhtEonkTTQVIkBYvi6SQBPp7EGRlFUHEhB1IeqoE2-y8-1TzEcXQ_Hn2RPANp_ZtLjPXDgpHx4ot58K-_ZM3eklEkhXX2QscJJ_07Tz208YLODloUvQC3odP6lct_aYx8-EVixyO7V3Fqo8OSdRVSJmGrVpTDA_uvAAAA__-sRz8q">