Skip to content

fix(sys): mimalloc3 v3.5.1 + fix for the process-init thread's thread-done hook - #97

Merged
Brooooooklyn merged 1 commit into
mainfrom
fix/mimalloc3-first-thread-done
Sep 4, 2026
Merged

Brooooooklyn merged 1 commit into
mainfrom
fix/mimalloc3-first-thread-done

Conversation

@Brooooooklyn

@Brooooooklyn Brooooooklyn commented Sep 4, 2026 •

Copy link
Copy Markdown
Member

Background

mimalloc v3.5.0 (shipped in libmimalloc-sys2 0.1.61 / mimalloc-safe 0.1.65 by #92) never associates the process-init thread's theap with the pthread thread-done key. If the first thread that touches the allocator exits — a Node.js worker_threads worker that loads the addon first, then exits — _mi_thread_done never runs for it, its theap stays registered with its thread id, and the next thread that reuses that pthread TCB aborts in debug builds:

mimalloc: assertion failed: at "src/init.c":327, _mi_thread_init_with_heap
  assertion: "theap==NULL"

Release builds skip the assert but leak the dead thread's theap and pages. Linux only in practice (macOS enables MI_TLS_RECURSE_GUARD, which makes the new thread adopt the stale theap). Upstream culprit: microsoft/mimalloc@5d9cb381 ("remove static main theap and tld"); v3.4.5 was the last good tag. Reported upstream with the fix: microsoft/mimalloc#1393. Found via rolldown/rolldown#10268 CI (watch debounce timers survive a worker-first registrant that exited), which pinned mimalloc-safe = "=0.1.64" as a stopgap.

Changes

Verification (Docker linux/arm64, node 22, debug + release builds of example/ with --features v3)

tree Node worker-first flow (1 / 64 / 64+4 concurrent workers) Rust std threads reusing the dead TCB release cargo test --features v3 sys-test
v3.5.0 / v3.5.1 unfixed assertion + SIGABRT assertion + SIGABRT OK (leaks) — —
1e5d14ca OK OK OK 6/6 251/251

Bisect over v3.4.5..v3.5.0 (7 steps) isolates 5d9cb381. The v2 line (c_src/mimalloc v2.5.0) does not have the gap and passes the same flow.

Once released, rolldown can drop its =0.1.64 pin.

🤖 Generated with Claude Code

https://claude.ai/code/session_01T34fMrYCf2V13Mg8BKmnda


Note

High Risk
Touches process-wide allocator and per-thread teardown in native code; wrong behavior can cause crashes or leaks in multi-threaded hosts (e.g. Node worker_threads), though scope is limited to the v3 submodule.

Overview
Repoints the mimalloc3 git submodule from microsoft/mimalloc (dev3) to napi-rs/mimalloc on branch fix/first-thread-done, matching the prior fork-based workflow used for targeted allocator fixes.

That branch carries mimalloc v3.5.1 plus a small src/init.c change so the process-init thread’s default heap is registered with the pthread thread-done hook. Without it, a worker thread that is the first allocator user and then exits can leave a stale heap on reused pthread TCBs (debug abort in _mi_thread_init_with_heap, leaks in release on Linux).

No Rust build.rs or crate source changes in this diff; consumers get the fix via the updated vendored mimalloc3 tree.

Reviewed by Cursor Bugbot for commit dd53da2. Bugbot is set up for automated code reviews on this repo. Configure here.

…-done hook

mimalloc v3.5.0 (shipped by #92) never associates the process-init
thread's theap with the pthread thread-done key. When the first thread
that touches the allocator exits (a Node.js worker that loads the addon
first), `_mi_thread_done` never runs for it, its theap stays registered
with its thread id, and the next thread that reuses that pthread TCB
aborts in debug builds:

  mimalloc: assertion failed: at "src/init.c":327, _mi_thread_init_with_heap
    assertion: "theap==NULL"

Release builds skip the assert but leak the dead thread's theap and
pages. Upstream culprit: microsoft/mimalloc@5d9cb381; reported with the
fix as microsoft/mimalloc#1393.

Point the `mimalloc3` submodule at the napi-rs/mimalloc fork branch
`fix/first-thread-done` = upstream v3.5.1 + the 11-line src/init.c fix
(same approach as #71), until upstream ships it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T34fMrYCf2V13Mg8BKmnda
@cursor

cursor Bot commented Sep 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_fceb089e-55a8-4416-8113-a18f533d39a9)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-04T15:15:48.078684Z dd53da2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Brooooooklyn
Brooooooklyn merged commit 6aba2ab into main Sep 4, 2026
21 checks passed
@Brooooooklyn
Brooooooklyn deleted the fix/mimalloc3-first-thread-done branch September 4, 2026 15:21
@github-actions github-actions Bot mentioned this pull request Aug 24, 2026
Brooooooklyn pushed a commit that referenced this pull request Sep 4, 2026
## 🤖 New release

* `libmimalloc-sys2`: 0.1.61 -> 0.1.62 (✓ API compatible changes)
* `mimalloc-safe`: 0.1.65 -> 0.1.66 (✓ API compatible changes)

<details><summary><i><b>Changelog</b></i></summary><p>

## `libmimalloc-sys2`

<blockquote>

## [0.1.62] - 2026-09-04

### Bug Fixes

- *(sys)* Mimalloc3 v3.5.1 + fix for the process-init thread's
thread-done hook
([#97](#97))

### Chore

- *(deps)* Update mimalloc (v2) submodule to v2.5.1
([#98](#98))
</blockquote>

## `mimalloc-safe`

<blockquote>

## [0.1.66] - 2026-09-04

### Bug Fixes

- *(sys)* Mimalloc3 v3.5.1 + fix for the process-init thread's
thread-done hook
([#97](#97))

### Chore

- *(deps)* Update dependency @napi-rs/cli to v3.9.0
([#94](#94))
- *(deps)* Update dependency @emnapi/core to v2.0.0-alpha.5
([#95](#95))
- *(deps)* Update dependency @emnapi/runtime to v2.0.0-alpha.5
([#96](#96))
- *(deps)* Update mimalloc (v2) submodule to v2.5.1
([#98](#98))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@github-actions github-actions Bot mentioned this pull request Sep 4, 2026
Brooooooklyn pushed a commit that referenced this pull request Sep 4, 2026
## 🤖 New release

* `libmimalloc-sys2`: 0.1.61 -> 0.1.62
* `mimalloc-safe`: 0.1.65 -> 0.1.66

<details><summary><i><b>Changelog</b></i></summary><p>

## `libmimalloc-sys2`

<blockquote>

## [0.1.62] - 2026-09-04

### Bug Fixes

- *(sys)* Mimalloc3 v3.5.1 + fix for the process-init thread's
thread-done hook
([#97](#97))

### Chore

- *(deps)* Update mimalloc (v2) submodule to v2.5.1
([#98](#98))
</blockquote>

## `mimalloc-safe`

<blockquote>

## [0.1.66] - 2026-09-04

### Bug Fixes

- *(sys)* Mimalloc3 v3.5.1 + fix for the process-init thread's
thread-done hook
([#97](#97))

### Chore

- *(deps)* Update dependency @napi-rs/cli to v3.9.0
([#94](#94))
- *(deps)* Update dependency @emnapi/core to v2.0.0-alpha.5
([#95](#95))
- *(deps)* Update dependency @emnapi/runtime to v2.0.0-alpha.5
([#96](#96))
- *(deps)* Update mimalloc (v2) submodule to v2.5.1
([#98](#98))
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Brooooooklyn added a commit to rolldown/rolldown that referenced this pull request Sep 4, 2026
0.1.66 ships mimalloc v3.5.1 plus the fix for the process-init thread's
thread-done hook (see microsoft/mimalloc#1393, napi-rs/mimalloc-safe#97),
so the `=0.1.64` stopgap from 4855394 is no longer needed. Cargo.lock:
mimalloc-safe 0.1.64 -> 0.1.66, libmimalloc-sys2 0.1.60 -> 0.1.62.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T34fMrYCf2V13Mg8BKmnda
Brooooooklyn added a commit that referenced this pull request Sep 14, 2026
… fix (#104)

### Background

Upstream closed microsoft/mimalloc#1393 and took the fix in a different
form: microsoft/mimalloc@439dc27b, shipped in **v3.5.2**. That commit
works on the default TLS model (verified with the #1393 repro on Linux,
debug shared build: `e35400bd` aborts, `v3.5.2` prints `OK`).

But it moved `mi_process_setup_auto_thread_done()` into
`mi_process_init_once()` **before** `_mi_process_is_initialized = true`.
With `MI_TLS_RECURSE_GUARD` (auto-on for `MI_TLS_MODEL_LOCAL` on Apple,
which is what `build.rs` forces for v3 on macOS) `_mi_theap_default()`
returns `_mi_theap_empty` at that point:

```
mi_process_init_once()                          [v3.5.2 src/init.c:558-564]
  ├─ mi_thread_init()                           __mi_theap_default = &mi_process_theap_main
  ├─ _mi_tls_slots_init()
  ├─ _mi_thread_locals_init()
  ├─ mi_process_setup_auto_thread_done()
  │    └─ _mi_theap_default()  ──► RECURSE_GUARD: _mi_process_is_initialized == false
  │                                ──► returns &_mi_theap_empty
  │                                ──► debug: assert fires / release: no association
  └─ _mi_process_is_initialized = true          ← too late
```

So on plain `v3.5.2`, `cargo test --features v3` on macOS aborts at
process init in every debug binary:

```
mimalloc: assertion failed: at "src/init.c":450, mi_process_setup_auto_thread_done
  assertion: "mi_theap_is_initialized(theap)"
```

and release builds silently skip the association, i.e. the #1393 leak is
back on macOS. Reproducible with upstream alone: `cmake
-DCMAKE_BUILD_TYPE=Debug -DMI_TLS_MODEL=LOCAL` on macOS,
`./mimalloc-test-api` passes on v3.5.1 and aborts on v3.5.2. Same on
Linux with `-DMI_TLS_RECURSE_GUARD=ON`.

### Changes

- **`mimalloc`** (v2) — `v2.5.1` → `v2.5.2` (official tag, no patches).
- **`mimalloc3`** — napi-rs fork `1e5d14ca` (v3.5.1 + #1393) → napi-rs
fork `afa6c90d` = upstream **v3.5.2** + one-line fix: set
`_mi_process_is_initialized = true` before
`mi_process_setup_auto_thread_done()` (napi-rs/mimalloc@afa6c90d, branch
`fix/process-init-flag-before-thread-done`). Same approach as #71 / #97.
- **`.gitmodules`** — `mimalloc3` branch name updated.
- No change to `build.rs` or the Rust sources.

### Verification

Upstream, macOS arm64, Debug, `-DMI_TLS_MODEL=LOCAL`:

| tree | `mimalloc-test-api` |
|---|---|
| v3.5.1 | 50/50 |
| v3.5.2 | abort at process init (134) |
| v3.5.2 + `afa6c90d` | 50/50 |

Upstream, Linux x64 (Docker gcc:14), Debug shared, #1393 repro:

| tree | `RECURSE_GUARD=OFF` | `RECURSE_GUARD=ON` |
|---|---|---|
| v3.5.2 | OK | abort (134) |
| v3.5.2 + `afa6c90d` | OK | OK |

This repo, macOS arm64, every CI command: `cargo test` / `--features
secure` / `extended` / `v3` / `extended,v3` all pass;
`libmimalloc-sys-test` 258/258 (v2, secure, extended) and 251/251 (v3).

Pre-existing and untouched: upstream `mimalloc-test-stress-heaps` fails
the `refcount == 1` assert with `MI_TLS_MODEL=LOCAL` on macOS on v3.5.1
too; `extended::tests::runtime_stable_option` fails under `cargo test
--workspace` on v2.5.1 too (not in CI).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_016srh5svTkqfW21zGcU6ht9

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes which mimalloc v3 C sources are built, affecting
process/thread allocator initialization on macOS and other TLS
recurse-guard configurations.
> 
> **Overview**
> Points the **`mimalloc3`** git submodule at the napi-rs fork branch
**`fix/process-init-flag-before-thread-done`** instead of
**`fix/first-thread-done`**, so v3 tracks upstream **v3.5.2** plus a
one-line init ordering fix: set `_mi_process_is_initialized` before
`mi_process_setup_auto_thread_done()`.
> 
> That avoids macOS **`MI_TLS_MODEL_LOCAL`** / recurse-guard failures
where v3.5.2 alone aborts in debug or reintroduces the #1393 leak in
release. No Rust or `build.rs` changes in this diff—only the submodule
branch pin in **`.gitmodules`**.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
09fc0c6. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant