From 1e5d14ca11b27fb352820405d2ed422c7b56b468 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 4 Sep 2026 23:05:44 +0800 Subject: [PATCH] associate the process-init thread's theap with the thread-done key The thread that runs `mi_process_init` initializes its default theap before `mi_process_setup_auto_thread_done` creates the pthread thread-done key, so `_mi_prim_thread_associate_default_theap` is a no-op for it and `_mi_thread_done` never runs when that thread terminates. Up to v3.4.5, `mi_process_setup_auto_thread_done` re-associated it right after creating the key (`_mi_theap_default_set(&mi_process_theap_main)`); 5d9cb381 ("remove static main theap and tld") dropped that call, so since v3.5.0 nothing associates the first thread. When the first thread to touch mimalloc is short-lived (for example a Node.js worker thread that dlopen's an addon statically linked with mimalloc, then exits), its theap stays registered in the heap with its thread id. A later thread that reuses the same pthread TCB finds it in `mi_heap_check_for_existing_theap` and trips `mi_assert_internal(theap==NULL)` in `_mi_thread_init_with_heap` in debug builds; release builds skip the check but the dead thread's theap and pages are never abandoned or freed. Associate the current default theap with the key right after creating it, when it is already initialized. The v2 line (dev) still does the equivalent via `_mi_heap_set_default_direct(&_mi_heap_main)`. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01T34fMrYCf2V13Mg8BKmnda --- src/init.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/init.c b/src/init.c index 68e94aaa2..06fb9ca34 100644 --- a/src/init.c +++ b/src/init.c @@ -441,6 +441,17 @@ static void mi_thread_theaps_done(mi_tld_t* tld) static void mi_process_setup_auto_thread_done(void) { mi_atomic_do_once { _mi_prim_thread_init_auto_done(); + // The thread that ran `mi_process_init` initialized its default theap before the + // thread-done key existed, so `_mi_theap_default_set` could not associate it + // (see `_mi_prim_thread_associate_default_theap`). Associate it now so that + // `_mi_thread_done` also runs when that thread terminates (for example when the + // first thread to use mimalloc is a short-lived worker thread that loaded us). + // Otherwise its theap stays registered in the heap with a thread id that the OS + // may reuse for a later thread. + mi_theap_t* theap = _mi_theap_default(); + if (mi_theap_is_initialized(theap)) { + _mi_prim_thread_associate_default_theap(theap); + } } }