From f843db846185b45b1135cd1dd4f465b45552d6d6 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Sun, 26 Jul 2026 11:13:56 +0200 Subject: [PATCH] revert(windows): drop the TLS thread-heap release Reverts the #581 fix mechanism, not the diagnosis. main's dry run passes both windows-11-arm shards and test-windows-guards; this branch fails all three. The only change here that affects process LOAD is the .CRT$XLB detach callback, and the arm64 failures are load-time crashes (STATUS_ILLEGAL_INSTRUCTION, pass=0, secs=0, five suites in the same second) -- while the guards exercise many launcher and payload spawns. The same binary loads fine on a UTM arm64 VM, so the placement is tolerated by some loaders and not others, which is not something to ship on a supported platform. #581's root cause stands and is documented: a static MinGW link has no DllMain and registers no TLS callback, so mi_thread_done never runs and every thread leaks its heap (607 heaps after 300 requests, 170 MiB against ~300 KiB live). The delivery mechanism needs to be one the loader accepts everywhere -- an FLS callback registered at runtime is the obvious candidate, with no linker section placement involved. Signed-off-by: Martin Vogel --- src/foundation/compat_thread.c | 48 ---------------------------------- 1 file changed, 48 deletions(-) diff --git a/src/foundation/compat_thread.c b/src/foundation/compat_thread.c index b76bdbd7f..d9922f510 100644 --- a/src/foundation/compat_thread.c +++ b/src/foundation/compat_thread.c @@ -7,10 +7,6 @@ #include "foundation/constants.h" #include "foundation/compat_thread.h" -#include "foundation/platform.h" - -#include /* mi_thread_done at thread exit */ - #include #include @@ -28,50 +24,6 @@ typedef struct { void *arg; } win_thread_arg_t; -/* Release each thread's allocator heap at DLL_THREAD_DETACH. - * - * Doing this from the thread wrapper instead crashes: the wrapper returns - * before the CRT's own thread teardown, and abandoning the heap there raced - * with frees still in flight (daemon_ipc_wait_forever_is_interruptible - * segfaulted, rc=139, reproducibly and only with the release enabled). A TLS - * callback is the mechanism mimalloc itself uses under MSVC and runs after all - * other thread cleanup, which is the only point where the heap is genuinely - * unreferenced. - * - * Without any of this, a static MinGW link -- no DllMain, no TLS callback -- - * never releases a thread heap at all: 607 heaps after 300 requests, 170 MiB - * held against a ~300 KiB live set, growing without bound (#581). POSIX gets - * this from a pthread TSD destructor, which is why only Windows leaked. - * - * CBM_MI_THREAD_DONE=0 disables it, so one binary can demonstrate both - * behaviours rather than requiring a rebuild to establish causality. */ -static bool thread_release_heap_enabled(void) { - static int state = -1; - if (state < 0) { - char buf[CBM_SZ_16]; - state = - (cbm_safe_getenv("CBM_MI_THREAD_DONE", buf, sizeof(buf), NULL) != NULL && buf[0] == '0') - ? 0 - : 1; - } - return state == 1; -} - -static void NTAPI cbm_thread_detach_callback(PVOID handle, DWORD reason, PVOID reserved) { - (void)handle; - (void)reserved; - if (reason == DLL_THREAD_DETACH && thread_release_heap_enabled()) { - mi_thread_done(); - } -} - -/* Park the callback in .CRT$XLB, the table the loader walks. The linker only - * emits a TLS directory when _tls_used is referenced, so anchor it. */ -extern const IMAGE_TLS_DIRECTORY64 _tls_used; -static const void *const cbm_tls_anchor __attribute__((used)) = &_tls_used; -__attribute__((section(".CRT$XLB"), used)) PIMAGE_TLS_CALLBACK cbm_thread_detach_tls_cb = - cbm_thread_detach_callback; - static DWORD WINAPI win_thread_wrapper(LPVOID lpParam) { win_thread_arg_t *a = (win_thread_arg_t *)lpParam; void *(*fn)(void *) = a->fn;