Skip to content

fix(thread): detach pool worker threads — 8MB stack leaked per exited worker (true-async/server#93)#174

Merged
EdmondDantes merged 4 commits into
mainfrom
fix-pool-worker-thread-leak
Jul 3, 2026
Merged

fix(thread): detach pool worker threads — 8MB stack leaked per exited worker (true-async/server#93)#174
EdmondDantes merged 4 commits into
mainfrom
fix-pool-worker-thread-leak

Conversation

@EdmondDantes

Copy link
Copy Markdown
Contributor

libuv_start_thread discards the uv_thread_t handle and nothing ever joins these threads: every exited joinable worker kept its 8MB stack + TCB forever. Invisible to valgrind (not a malloc), one-shot for normal pool teardown — but ThreadPool::reload() retires a whole cohort per rotation, so a hot-reload soak grew +16MB VmSize / ~60KB RSS per rotation, linearly (measured over 539 rotations while stress-testing HttpServer::reload()).

Fix: uv_thread_detach right after a successful create (same pattern as the process-watcher threads in this file). Nothing can join these threads anyway — the handle was never stored.

After: VmSize flat across rotations; residual RSS drift is the by-design channel-registry retention (~1.4KB/rotation, freed at thread shutdown).

Suites: thread_pool + thread + fs_watcher 163/163 green.

…its 8MB stack (true-async/server#93)

libuv_start_thread discards the uv_thread_t handle and nothing ever joins
these threads, so each exited joinable worker kept its stack and TCB for
the life of the process. One-shot for a normal pool teardown, but
ThreadPool::reload() retires a full cohort per rotation: +16MB VmSize and
~60KB RSS per rotation, growing linearly (measured across a 539-rotation
soak). uv_thread_detach right after create returns the stacks to the OS:
VmSize is now flat across rotations and the residual RSS drift drops to
the by-design channel-registry retention (~1.4KB/rotation, freed at
thread shutdown). thread_pool/thread/fs_watcher suites 163/163.
…rue-async/server#93)

uv_thread_detach landed in libuv 1.50.0, not 1.45 — the CI pins (1.45/1.48)
predate it and the Linux matrix failed with an implicit declaration. Align
everything with the release builds, which already run libuv 1.52.1:

- config.m4: minimum bumped 1.45.0 -> 1.50.0 (error message names
  uv_thread_detach as the reason)
- build-linux / coverage / build / debug-* workflows: source-built libuv
  pinned 1.52.1, cache keys updated
- nightly-io-chaos: ubuntu-24.04 apt libuv (1.48) is no longer enough —
  build from source like the rest
- libuv_reactor.c: plain uv_thread_detach, no platform ifdef
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@EdmondDantes EdmondDantes merged commit fccb207 into main Jul 3, 2026
9 checks passed
@EdmondDantes EdmondDantes deleted the fix-pool-worker-thread-leak branch July 3, 2026 15:27
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