Skip to content

Commit 155d823

Browse files
committed
u
1 parent 03d0add commit 155d823

1 file changed

Lines changed: 35 additions & 29 deletions

File tree

libmimalloc-sys/build.rs

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,46 @@ use cmake::Config;
66
/// `MI_TLS_MODEL_THREAD_LOCAL` with `MI_TLS_RECURSE_GUARD` instead of the
77
/// upstream default `MI_TLS_MODEL_FIXED_SLOT`.
88
///
9-
/// Background:
10-
/// - FIXED_SLOT (default) hardcodes the per-thread `theap` pointer at
11-
/// TCB[108]/[109]. When multiple statically-linked instances of
12-
/// mimalloc-safe live in the same process (e.g. multiple Node.js
13-
/// napi addons), every instance reads and writes the same slot on
14-
/// any thread that calls into more than one of them (the main JS
15-
/// thread in particular) — instances overwrite each other's `theap`
16-
/// pointers and corrupt each other's heaps.
17-
/// - The other obvious workaround, `MI_TLS_MODEL_DYNAMIC_PTHREADS`
18-
/// (activated by passing `-DMI_HAS_TLS_SLOT=0`), turned out to have
19-
/// poor macOS testing for long-lived non-tokio threads — notably
20-
/// rayon workers pulled in via `oxc_cfg → oxc_index → rayon` — and
21-
/// caused SIGABRT crashes in rolldown's CLI tests.
9+
/// This fixes two distinct SIGABRT failure modes on macOS:
2210
///
23-
/// THREAD_LOCAL + RECURSE_GUARD avoids both failure modes:
24-
/// - `__thread` + `mi_decl_hidden` (already how upstream declares
25-
/// `__mi_theap_default` / `__mi_theap_cached` in `init.c`) gives
26-
/// per-image per-thread storage allocated by dyld's TLV system — no
27-
/// FIXED_SLOT sharing across instances.
28-
/// - RECURSE_GUARD uses a non-TLS `_mi_process_is_initialized` bool to
29-
/// short-circuit the fast path before process init completes, so a
30-
/// first `__thread` access triggering dyld's TLV malloc cannot
31-
/// recurse into mimalloc itself.
32-
/// - `MI_HAS_TLS_SLOT` stays at default 1, so the *thread-id* fast
33-
/// path (`prim.h` line ~304) still uses `mi_prim_tls_slot(0)`
34-
/// (Apple's system-defined thread-id TSD slot, shared semantically
35-
/// across all consumers — no conflict).
11+
/// Mode A — FIXED_SLOT (upstream default, 100% crash):
12+
/// TCB[108]/[109] is a hardcoded slot shared by all loaded images.
13+
/// When a second napi addon loads, its mimalloc reads the first
14+
/// addon's heap pointer from TCB[108], sees it's non-NULL, and
15+
/// skips its own initialization (`init.c:717` early-return). The
16+
/// second addon then runs with the first's heap data but its own
17+
/// uninitialized internal state — the mismatch causes an immediate
18+
/// SIGABRT on load.
19+
///
20+
/// Mode B — DYNAMIC_PTHREADS (#67's workaround, 5-15% crash):
21+
/// `-DMI_HAS_TLS_SLOT=0` routes Apple to DYNAMIC_PTHREADS, which
22+
/// stores heap pointers via `pthread_setspecific`. This fixes Mode A,
23+
/// but at process exit the destructor (`init.c:1190`) deletes the
24+
/// pthread key and zeros it. The one-time `mi_atomic_do_once` flag
25+
/// that guards key creation never resets, so the key can never be
26+
/// recreated. Background threads (rayon workers from `oxc_cfg →
27+
/// oxc_index → rayon`) are still alive during exit — their next
28+
/// alloc finds key=0, allocates a heap but can't store it, and
29+
/// returns NULL → SIGABRT.
30+
///
31+
/// THREAD_LOCAL + RECURSE_GUARD avoids both:
32+
/// - `__thread` + `mi_decl_hidden` gives per-image per-thread storage
33+
/// via dyld TLV. Different addons have different variables (fixes
34+
/// Mode A). No pthread key involved, so nothing to delete on exit
35+
/// (fixes Mode B).
36+
/// - RECURSE_GUARD short-circuits the fast path with a non-TLS bool
37+
/// (`_mi_process_is_initialized`) before process init completes,
38+
/// preventing recursion when dyld's TLV allocator calls `calloc`
39+
/// on first `__thread` access.
40+
/// - `MI_HAS_TLS_SLOT` stays at default 1, so the thread-id fast path
41+
/// still uses `mi_prim_tls_slot(0)` (Apple's system TSD slot — no
42+
/// conflict).
3643
///
3744
/// Idempotent: detects an already-patched file by a marker comment and
3845
/// returns early. Panics loudly if the upstream selector block can't be
3946
/// located — that means mimalloc changed `prim.h` and this patch needs
40-
/// to be updated. Leaves the submodule's working tree marked dirty
41-
/// after a fresh clone+build; reset with `git submodule update --force`
42-
/// if needed (next build will re-apply the patch).
47+
/// to be updated. Submodule stays pristine in git; `git submodule
48+
/// update --force` resets it, next build re-applies the patch.
4349
fn patch_apple_tls_model_for_v3() {
4450
let prim_h = "c_src/mimalloc3/include/mimalloc/prim.h";
4551
let content = std::fs::read_to_string(prim_h).unwrap_or_else(|e| {

0 commit comments

Comments
 (0)