Repository navigation
A process's tids stop below Tid::MAX, and its next thread start is refused - #1184
Conversation
…fused `process::spawn_thread` took a thread's id from `IdMap<Tid, ThreadEntry>`, whose `insert` stepped its counter with `Tid + Tid`. The kernel builds with `overflow-checks = true`, so a process's 2^32nd thread start panicked under `PROCESS_TABLE` and hung the machine; the start before it was issued `Tid(u32::MAX)`, the per-CPU word for no thread, whose every page fault `handle_page_fault` refuses. Which tid a thread gets is a lifecycle decision, so it moves to `toyos_proclife::tids`, beside `pids`: `Tids` issues each tid once, never `Tid::MAX`, and says when it is spent. `Lifecycle::tids` exposes it, and `spawn::admit` answers `Admit::TidsSpent` at both of a start's moments, which `spawn_thread` answers as its other refusals (`SYS_THREAD_SPAWN`: `ResourceExhausted`). The insert takes its tid under the lock that admitted it, so the take cannot find them spent. `kernel/src/id_map.rs` is deleted: its one user was the thread map, now a `BTreeMap` beside the process's `Tids`. `impl Add for Tid` in `toyos-abi` had no other user and goes with it. Host tests: `tids::the_last_tid_is_one_below_max_and_none_follows_it` and `spawn::a_process_whose_tids_are_spent_refuses_at_both_moments`, each shown red by a checked mutation (the spent check removed from `Tids::take`, and from `admit`). Closes issues/a-process-that-starts-four-billion-threads-panics-the-kernel.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review r1 of #1184 at 4eebd97ArchitectureThe tid choice moves out of a kernel-private generic Correctness
RecordThe issue's exit has two parts:
The durable rule ("never EvidenceChecked against the evidence repository at 5fa237b98.
No guest run was done. I accept that: the kernel edit is a type-checked change of map type with identical iteration order, and the decision itself is held by host tests. FindingsNo blockers. SUGGESTION
Note for the orchestrator: LAND |
Problem
process::spawn_threadtook a thread's id fromIdMap<Tid, ThreadEntry>(
kernel/src/id_map.rs), whoseinsertstepped its counter withTid + Tid.The kernel builds with
overflow-checks = true, so one process starting andjoining threads in a loop panicked the kernel on its 2^32nd start, under
PROCESS_TABLE, which hangs the machine rather than reporting. The startbefore that was issued
Tid(u32::MAX), the per-CPU word for no thread, sohandle_page_faultrefused every fault that thread took.Change
Which tid a thread gets is a lifecycle decision, so it now lives in
toyos_proclife::tids, besidepids:Tidsissues each tid of a process once, neverTid::MAX, and says when itis spent.
Lifecycle::tidsexposes it, andspawn::admitanswersAdmit::TidsSpentat both of a thread start's moments.
spawn_threadanswers that like itsother refusals, so
SYS_THREAD_SPAWNreturnsResourceExhausted.never find the tids spent. If it did, that would be a broken invariant and
expectpanics.kernel/src/id_map.rsis deleted. Its only user was the thread map, whichis now a
BTreeMapbeside the process'sTids.impl Add for Tidintoyos-abihad no other user and is deleted too. That is a token change insdk/abi, so CI builds a new sysroot.Closes
issues/a-process-that-starts-four-billion-threads-panics-the-kernel.md.Checks
Revision 4eebd97. Evidence: 5fa237b98/pr-1184/r1.
cargo testinkernel/proclifepasses. The new tests aretids::tests::the_last_tid_is_one_below_max_and_none_follows_itandspawn::tests::a_process_whose_tids_are_spent_refuses_at_both_moments.at 4eebd97. Removing the spent check from
Tids::takemakes the firsttest fail, and removing it from
admitmakes the second fail; both exit101 (controls).
cargo run -- --ci hoston the tree just before the commit: exit 1, withclippy the only red step (log). Under
mutate-spawn-skips-the-insert-recheck, the new test was cfg'd out, whichleft an import and a model helper unused. The fix keeps the test in both
arms and gates only its insert assertions. The parts that change can affect
were rerun at 4eebd97:
--ci host lint, exit 0(log), and
--ci host models, exit 0(log).
cargo run -- --build-only: exit 0 (log), on thesame tree. The fix changed only test code.
No guest or metal run. What changed is the decision and the type that holds
it, and the host tests establish both. The kernel side is a mechanical swap of
the map type, and the exhaustive
matchonAdmitchecks it at compile time.Risks
kernel/src/process.rs, infault_inaround line 1104.This branch changes lines 150–245 and
spawn_thread, so they do not overlap.kernel/src/loader/mod.rsgets one mechanical line (get_mut(&tid)).That file belongs to the loader move another agent is making, so this is a
trivial overlap.
issues/a-double-fault-on-cpu-1-under-a-wide-suite.mdsays "Pids come fromIdMap". That was already stale before this change, since pids come fromtoyos_proclife::pids. It is investigation history, so I left it unchanged.🤖 Generated with Claude Code