threads(M9): thread_join syscall — retire the per-thread endpoint
join no longer needs a per-thread IPC endpoint. New thread_join(tid) syscall blocks the caller until the task with id tid exits; the exit paths call wakeJoinersLocked. join only reclaims the joined thread's USER stack, which the thread vacates the instant it enters the kernel to exit, so waking at exit time (not reap time) is safe — no reaper/aspace juggling or user-memory write, and equally std-shaped (like pthread_join). thread_spawn drops the exit-endpoint arg (runtime passes no_cap). thread-test's join mode runs 40 spawn+join cycles that would exhaust the 16-slot handle table under the old endpoint scheme. Also harden the M8 reaper: its single per-core reap slot could be overwritten by a second death on that core before draining (a fresh-task/SMP timing window), an intermittent one-stack leak that made task-reap ~20% flaky. Replace it with a per-core reap LIST plus a .reaping task state so a pending slot can't be reused before its stack is freed. task-reap now 11/11 isolated. Deferred: detached-thread user-stack reclaim (still at process exit, as in M3). Gate thread-join PASS (3x); full guardrail 26/26; build + host tests clean.
This commit is contained in:
+25
-9
@@ -370,16 +370,32 @@ full guardrail); `zig build`/`zig build test` clean.
|
||||
With the reaper (M8) able to act *after* a thread is fully off its stack, migrate `join`
|
||||
to the std shape and drop M3's per-thread exit endpoint:
|
||||
|
||||
- [ ] `thread_spawn` takes a user **completion word** (in the `Thread` handle's memory)
|
||||
and a joinable/detached flag. On reap the kernel writes 0 to that word and
|
||||
`futex_wake`s it (a `CLONE_CHILD_CLEARTID` equivalent — safe now the thread is off
|
||||
its stack). `join` = `futex_wait` on the word, then `munmap` the stack; a
|
||||
**detached** thread's stack is `munmap`ped by the reaper instead. No IPC endpoint
|
||||
per thread.
|
||||
- [ ] `thread-join` passes on the new path; a check confirms joining N threads creates no
|
||||
per-thread endpoints (handle count stable).
|
||||
- [x] A **`thread_join(tid)` syscall** (not a user futex word): it blocks the caller until
|
||||
the task with id `tid` exits, and the exit paths call `wakeJoinersLocked`. `join`
|
||||
only reclaims the joined thread's **user** stack, which the thread vacates the moment
|
||||
it enters the kernel to exit — so waking at *exit* time (not reap time) is safe, and
|
||||
no reaper/address-space juggling or user-memory write is needed. This is equally
|
||||
std-shaped (like `pthread_join`) and much simpler/safer than the planned
|
||||
reaper-written completion word. `thread_spawn` no longer takes an exit endpoint (the
|
||||
runtime passes `no_cap`); the per-thread IPC endpoint is gone.
|
||||
- [x] `thread-join` passes on the new path, and its join mode now runs **40 spawn+join
|
||||
cycles** — under the old per-thread-endpoint scheme those leaked handles would
|
||||
exhaust the 16-slot handle table; here they all succeed, proving join is endpoint-free.
|
||||
|
||||
**Gate:** `thread-join`/`thread-mutex` green on futex-completion join; guardrail green.
|
||||
**Gate (met):** `thread-join` passes (3× isolated) on the `thread_join` path; full
|
||||
guardrail 26/26 (incl. `process-kill`, `supervision`, `fault-recovery`, `task-reap`);
|
||||
`zig build`/`zig build test` clean.
|
||||
|
||||
> **Reaper hardened here (fixes an M8 flake).** M8's single per-core reap slot could be
|
||||
> *overwritten* by a second death on that core before the first drained (a fresh-task/SMP
|
||||
> timing window) — an intermittent one-stack leak (`task-reap` flaked ~20%). Replaced it
|
||||
> with a per-core reap **list** plus a `.reaping` task state so a pending slot can't be
|
||||
> reused before its stack is freed. `task-reap` now 11/11 isolated + 2× in the batch.
|
||||
|
||||
> **Deferred:** detached-thread **user-stack** reclaim (still freed at process exit, as in
|
||||
> M3). Doing it in the reaper needs the saved address space + stack range and a
|
||||
> translate/unmap in a not-currently-loaded aspace — real complexity for a bounded leak.
|
||||
> A follow-up when a consumer needs it.
|
||||
|
||||
### M10 — Per-thread TLS (`threadlocal`)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user