Repository navigation
A parent's end takes its children down: every end walks its subtree and is published after it, a spawn past its commit lands, and a refused spawn spends no pid - #659
Conversation
Stage 4 of issues/kernel/a-childs-end-is-an-event-and-a-parent-takes-its-children-down.md, as ruled: every process has one parent, and an end -- exit, kill, CPU fault, handle fault -- takes its whole subtree. The decisions are toyos-proclife's new `tree` module. Each process carries a `Node`: its parent, its depth below init, the children placed under it, and a count of what holds its publication -- its own teardown and every child admitted under it and not yet published or refused. - A spawn is admitted under its place at the top of the loader, before anything is built: a place being torn down answers `Gone`, and a child more than MAX_DEPTH (64) below init `ResourceExhausted`, which the kernel logs with the depth. Admission raises the place's count. The insert asks again under the lock that inserts, and a place claimed since refuses it (`Gone`) and lowers the count, as every failed build does through the `Admission` guard's drop. - An end claims its top, then walks: one claim per hold of the table lock, each claim reading the children it owes in the same hold, so a child either landed before the claim and is walked or is refused at its insert. Between two claims a reschedule owed is served by `yield_now` with nothing held. The walk runs in `process::exit` (exit, CPU fault, handle fault) and in `kill_process`, on the ending or killing thread. A process another end claimed first is that end's to walk. - A teardown no longer publishes at once. It keeps its `Exit` on the entry and lowers its own share of the count; whoever lowers a count to zero publishes, then lowers the parent's: child before parent, at most MAX_DEPTH + 1 publications, with preemption off and the table lock given up for each publication. The ABI: `SpawnArgs` gains `place` (112 to 120 bytes): a handle carrying `WRITE` to the process the child goes under, or HANDLE_INVALID for the caller. Every process starts holding a handle to itself under `self` (`WRITE`, `DUP`, `TRANSFER`), installed by the commit that moves the endowments, so a table and a label blob hold one entry and four bytes more than a spawn may carry. The place and SYS_NAMESPACE_BUILD's connector are both handles a peer sent, and resolve through one lookup, `HandleTable::get_sent`, which answers a wrong type `InvalidArgument`; the connector's own match in ipc.rs goes. The launch wire names the parent: its header's ninth word is a place, which travels as the batch's last handle and counts against MAX_LAUNCH_EXTRAS, or init. A launch naming neither is refused. init spawns under the place (`CommandExt::under`) and answers a place being torn down with the new MSG_GONE. std launches with a copy of the caller's `self`; `under_init` asks for init and is a launch or nothing -- no launcher, a program no row declares, an endowment or an extra slot answers `PermissionDenied` and starts nothing. std's direct spawn maps the kernel's word through `to_io_error`, so init can tell `Gone` (BrokenPipe) from a refusal, and makes its endowment table after the routing: a launched spawn had leaked the namespace copy made for a direct one. The shell gains `detach`, which starts a program under init and does not wait for it. `compositor_client_death`'s relay outlived its creator; it is now placed under the test's root, through the root's `self`, so the creator's end does not take it down. Host: four new interleavings and a depth test, each with a feature control in src/ci.rs's CONTROLS. Guest: `process_tree` on tests/proctreecase, and two new arms in `launcher_refusals`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
The first sshd is killed as soon as the roster shows it, which can be before it binds its port, so a count of its listening lines is not a fact the guest arranged. What makes the arm about a kill rather than an sshd that ended by itself is the kernel's line for its end: one, and code 137. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
`every_shipped_boot_config_is_covered` found the new case config and no row for it in ALL_CONFIGS, so none of the gates over configs read it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
|
Review of CI: Growth:
The production growth is the ruled stage. The deletions are named below. BLOCKER
NOTE
REMOVE
The ladder, arm by arm (host =
|
…d the tree's tests reach what they claim - A spawn that answers a refusal leaves its caller's table as it was. Admission is the last refusal: past it the loader moves the caller's handles, and `tree::land_child` lands the child whatever happened since. A child whose place was claimed since its admission is claimed in the hold that inserts it, its retires posted by its spawner, and the spawn answers it. `tree::insert_child`, its `Refused` and the kernel's wrapper go. The model moves the caller's handles before the landing, as the kernel does, and gains L12: a refused spawn moved none of them. At 81ff550, with only that law added, the race test reds on it. - The race test runs a failed build beside a landing, which is what `mutate-refused-spawn-keeps-the-count` (renamed from the refused insert's) now reds through. No test is gated on a control feature. - `tree::tests` gains a published child leaving its parent's children, and a child landed under a place claimed since its admission. - std (fork 40470840283): a launch that cannot carry its place is refused, never spawned directly. `toyos::endow::this_process` answers the handle and panics when it is gone. - The kernel refuses a spawn endowing `self`; `MAX_SPAWN_ENDOWMENTS` and `MAX_SPAWN_LABELS_LEN` are what a spawn may carry, read at both sites. - The climb runs without `preempt_off`. - The toyos SDK host-tests `Request::parent`. - process_tree keeps the killed and CPU-fault arms, the MANAGE-only place, two `under_init` refusals, the chain, and a detached `cat`; the harness requires the loader's refusal of each B's first spawn. sshd, netd and the staged key leave proctreecase. launcher_refusals loses the no-parent arm. - Prose the review flagged is deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
`ptr::write_volatile` checks alignment only, so a null write reaches the CPU under the guest profile's debug assertions; the unmapped constant and its claim otherwise go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
|
Review of CI: no check at 6cb8071. The PR is CONFLICTING with main, so Growth:
Round 1
BLOCKER
NOTE
REMOVE
Asked
SEND BACK |
#639 deleted three guest tests this branch had changed, each with the issue that records the commit restoring it. Every hunk of this branch's side is accounted for: - `device_claim_lifetime.rs` (51cc87f) and `handle_kill_policy.rs` (6b7da44): this branch's one hunk in each set the new `SpawnArgs::place` to `HANDLE_INVALID`. It goes with the file. Reverting either deletion onto this tree fails to compile until that field is set, which is loud. - `launcher_refusals.rs` (4c19146): this branch's hunks were `parent: Parent::Init` in its three `Launch` literals, the paragraph it deleted from `the_kernel_answers_rather_than_faults`, and the arm `a_place_that_is_a_pipe`. The first two go with the file. The arm moves to `process_tree` as `a_pipe_is_no_place`: `tests/proctreecase` has the launcher it needs, and the arms after it are init answering the next launches, which is what the old arm's position asserted. - `src/build.rs`: main took `tests/quiescecase` and `tests/quiescelastcase` out of `ALL_CONFIGS`; this branch's `tests/proctreecase` row stays. - `tests/toyos.rs`: main took `esp_files` out of `RUST_SKIP` and `launcher_refusals` out of `MACHINE_TESTS` and `CARRIES`; this branch's `process_tree` rows stay. `git grep` finds no `SpawnArgs` literal without `place` and no reference to the three deleted binaries this branch added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
…arms alone The previous pin, f066099a351, merged the fork's moved main and with it #659's std commit 336c8a8e141, which builds `SpawnArgs { place, .. }` and calls `toyos::endow::this_process()`: neither exists in this tree, so the toolchain did not build here. The pin is now 61adcea7362 on the fork's `wt-toyos-m2`: main's pin aca5f527fcb, #650's 9151571cae9 (std's `aligned_alloc`, which this branch's libc relies on), the `configure_cmake` arm that names CMake's system `ToyOS` (25da73337eb), `src/llvm-project` at ceaf0fbb8 (`bit.h`'s `<endian.h>` and `is_local_impl`'s arms), and two reverts. `clang-tblgen` from an external host LLVM (61f2a91fffa, df7bd9c942e) and LLD only beside the target's own `llvm-config` (d622a37189e) were cross-platform changes to bootstrap, made to suit the store's host LLVM: a bootstrap build that names no `llvm-config` for the build triple needs neither, because it builds that triple's LLVM itself, with `clang-tblgen` in its build directory and no `lld` beside its `llvm-config`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
… landing answers what its spawner retires Answers BLOCKERs 1 and 3 of the second review of #659. **Pids (BLOCKER 3).** `toyos_proclife::pids::Pids` issues every pid the kernel hands out, init's, every spawn's and the kernel thread's, and the process table holds it beside its entries: `ProcessTable` becomes a struct, and `IdMap::reserve` and `IdMap::fill` go back out. - A spawn takes its pid at admission (`tree::admit_child`), with the other refusals and before anything is built. A spawn refused after that gives it back (`tree::refuse_child`), and the next admission takes it. A refused spawn spends none, and the pids given back at once are at most the spawns in flight at once. - `Pid::MAX` is never issued: it is the per-CPU word for no process (`percpu::current_pid`). Once every pid below it is issued, admission answers `Admit::NoPid`, the spawn answers `ResourceExhausted`, and the log says `spawn: refused, every pid below 4294967295 is issued`. Before this, the counter issued `Pid(u32::MAX)` and the next `Pid + Pid` panicked the kernel under `overflow-checks`. - Why give back, rather than take the pid at the point of no return as the review proposed: `PendingHandles::commit` itself refuses (a `self` label, a missing `TRANSFER`, no room), so a pid taken before the commit is still spent by every refused commit, without bound. Taking it after the commit leaves exhaustion to refuse after the caller's handles moved, unless admission also counts the spawns owed a pid. A 64-bit pid was not taken: std's `process::id()` and `Child::id()` answer `u32`. - Giving back is sound because no entry ever held a refused spawn's pid. The `ProcessObject` the loader makes before the commit dies with the refused spawn, so no handle, no node's children and no walk names that pid. A pid an entry held is never issued again, so a pid a walk carries across a lock release still names its process or nothing. **The landing answers the retires (BLOCKER 1).** `tree::land_child` now answers the threads its spawner retires: every thread of a child claimed as it lands, and none of any other. `tree::Landed` goes. The model's spawner posts what the landing answers instead of computing its own, so the decision is the host's to check. `Admission::land` has no branch left: it maps the answer through `scheds`, which every claim's retires also go through, and the loader posts the result after the table lock is given up. - New control `mutate-landed-child-retires-nothing`: the landing claims the child and answers nothing to retire. `cargo test -p toyos-proclife --features mutate-landed-child-retires-nothing` exits 101, with three FAILED: `a_spawn_racing_its_places_kill_leaves_nothing_under_it_and_publishes_it` and `a_spawn_racing_the_kill_of_its_own_spawner` ("pid 1 was claimed for teardown and never published an exit", and pid 3), and `a_child_landed_under_a_place_claimed_since_its_admission_is_claimed_with_it`. `src/ci.rs`'s `CONTROLS` row demands the first. - The review's kernel patch has no site left. Its nearest forms are measured as checked patches against `cd kernel && cargo check`, which builds with `-Dwarnings`: `(inserted, Vec::new())` in `Admission::land` exits 101 (unused `retire` and `pid`), and the loader's post loop deleted exits 101 (unused `retire`). The tree was restored clean after each. **Tests.** `pids::tests`: pids from 0 in order; the last is `u32::MAX - 1` and none follows it; a pid given back is the next taken. `tree::tests`' `a_spawn_past_the_last_pid_is_refused_and_a_refused_spawn_spends_none`: with two pids left, a refusal after the last gives it back three times, the landing takes it, `NoPid` raises no hold, and init publishes once its child does. As checked patches, each restored: - `refuse_child` without its `give_back`: `cargo test -p toyos-proclife` exits 101, that test FAILED. - `Pids::take` without its `Pid::MAX` stop: exit 101, both `pids` tests and that test FAILED, each on the overflow panic. **REMOVE.** The review's three lines go: `tree.rs`'s "It is the last refusal" sentence and "which runs with preemption off", and the loader's clause on `commit`'s `?`. `tree::Admitted::place` (NOTE, no caller) goes; `Admitted` gains `pid`. `issues/kernel/a-process-that-starts-four-billion-threads-panics-the-kernel.md` records the same overflow for a thread id, as the review asked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
…and std refuses a launch whose stdio it cannot duplicate Answers BLOCKER 2 and the NOTEs of the second review of #659. **`abuse_handle_table` (BLOCKER 2).** Two arms beside the repeated-endowment refusal, each spawning a copy of the test binary that exits at once: - An entry labelled `self` naming a fresh pipe's write end answers `InvalidArgument`, and that end is still this process's: a byte written to it is read from the other end. Under the review's patch (`start.rs`'s `self` check deleted) the spawn starts and `expect_err` panics. - `MAX_ENDOWMENTS` entries naming 32 distinct pipe ends, which carry `TRANSFER`, answer `InvalidArgument`; the first `MAX_ENDOWMENTS - 1` of them start a child that exits 0. Under the review's patch (`MAX_SPAWN_ENDOWMENTS = MAX_ENDOWMENTS`) the 32 start and `expect_err` panics. The arm counts with `MAX_ENDOWMENTS`, the table's size, so that patch cannot move the test with it. **`process_tree`.** `a_place_without_dup_is_refused` (NOTE on fork `toyos.rs:515`): a launch under this process's `self` narrowed to `WRITE` answers `PermissionDenied` and starts nothing. With `direct(None)` back for a refused place duplicate, the child would be spawned directly under the place, which the kernel accepts, and the arm panics. **std** (fork `wt-toyos-proclife4`, d1b9f2eae3c on 40470840283): - A stdio slot whose duplicate the kernel refuses answers that refusal instead of turning the launch into a direct spawn (NOTE on `toyos.rs:528-535`). No arm tells the two apart: for a stdio handle without `DUP` the direct spawn's own slot duplicate is refused `PermissionDenied` too, so the difference shows only to a caller whose table is full, where the direct spawn started a child holding the caller's namespace. - The `slot_map.len() > MAX_LAUNCH_SLOTS` fallback goes (NOTE on `toyos.rs:503-505`): `launch` is reached only with no extra slot. - `CommandExt::under` says a launched child's place needs `DUP` too. `issues/kernel/a-spawn-refused-for-its-callers-full-table-has-already-moved-its-endowments.md` records the NOTE on `proc.rs:64-70`: a pre-existing refusal after the commit, off this stage's path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
The doc says its questions are the whole of the crate; `pids` is one now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
…nel-issued pids `ProcessTable` holds its entries in a `HashMap<Pid, ProcessEntry>` of its own now, where the `IdMap` it replaced was declared in `id_map.rs`. `every_hashed_kernel_container_is_declared_with_a_kernel_minted_key` was red on it in `cargo run -- --ci host` (exit 1, "kernel/src/process.rs holds [\"HashMap<Pid, ProcessEntry>\"] and src/kernelkeys.rs declares no hashed container there"). The trace the row owes: `ProcessTable::insert` keys by the entry's pid, which is its `ProcessObject`'s. The two `ProcessObject::new` calls take that pid from `toyos_proclife::Pids::take`: `loader::spawn` from its admission (`tree::admit_child`), and `sched::kthread::spawn` directly. No caller chooses one; a pid given back is one `take` issued. `cargo test --lib kernelkeys`: exit 0, 4 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016t9wjdQkB8SH7bmfUoiy6L
|
Orchestrator guest runs at
Whole:
|
The dup2 arm filled every slot from 3 up and then closed `RawHandle(slot)`, on the premise that each of those slots was still at generation 0. The two endowment arms added before it (a `self` label refused, MAX_ENDOWMENTS entries refused and one fewer started) open and close pipe ends in exactly those slots, so the slots dup2 fills are past generation 0: dup2 answers the slot's own generation (`HandleTable::install_at`), the bare index named an earlier one, and the first close was a handle fault, exit 139. The arm now keeps each handle dup2 answers and closes those. Measured under QEMU, `cargo test --test toyos-build -- abuse_handle_table`: exit 1 at d69b80a (the guest's exit 139, "handle fault: ... syscall=10 a handle closed at an earlier generation"), exit 0 with this commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
…lands path The round-2 review's first BLOCKER: nothing booted reaches the path where a spawn's place is claimed between the spawn's commit and its landing, so the kernel's half of it (`Admission::land` mapping the answered threads to their scheduler records, and the loader posting those retires) could be deleted with every test green. The decision is `toyos_proclife::tree::land_child`'s and is host-tested; the kernel performing it was not. That window is the loader's own and no caller can order a kill inside it, so a test actuator does: `debug_action::KILL_PLACE_AS_SPAWN_LANDS` (22, never assigned) marks the caller's next spawn, and on the `test-actuators` kernel the loader kills that spawn's place after the commit and before the landing. The kill is `kill_process`'s own body, now `process::kill(pid)`, and the landing is the shipped one. `spawn_lands_claimed` (an `ACTUATOR_TESTS` member, so it rides the shared boot on the kernel that carries `SYS_DEBUG`): the test starts a place that hands it its `self`, marks its next spawn, and spawns a child that parks under that place. The spawn answers the child; a second spawn under the place answers `Gone`, which is the kill having happened; the wait on the child and the wait on the place both answer 137. A child claimed and never retired parks on, and holds its place unpublished, so neither wait answers. Measured under QEMU: `cargo test --test toyos-build -- spawn_lands_claimed` exit 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
…o is the tooling track's Answers the review of c280ece and takes two answers the owner gave on 2026-10-02, after that commit was written. The owner's answers: - The development host builds cargo from the Rust fork and uses no other. The notes this branch was written from recorded the opposite as a consequence of accepting cargo's C dependencies, and the owner has overruled it. The bullet "The host builds with the cargo rustup ships. #629 ... is reworked to keep rustup's" goes; the tooling track's cargo sentences on main stand and are not repeated here. - OpenSSL's build on the host runs under the host's own make and shell, and openssl-src is not forked for it. The track says so in one sentence, beside the question it bounds: what is open until M4 is how cargo's OpenSSL is built for ToyOS. The host tools themselves are declared in the-build-runs-host-tools-outside-rust-and-qemu.md, which is #629's change. The review's findings: - "cargo keeps its eight C libraries ... and nothing replaces them with Rust" said more than was decided and contradicted the open rustls-backend question. The bullet is now "cargo's C dependencies are accepted", and the eight are named by the stage that cross-builds them. - flock leaves the libc stage. No library cargo builds for ToyOS calls it: over the eight crates' sources, `rg '\bflock\s*\('` finds a call in two places. SQLite's is inside `#if SQLITE_ENABLE_LOCKING_STYLE`, which sqlite3.c defines 1 only under __APPLE__ and libsqlite3-sys 0.38.1's build.rs never sets; nghttp2's is in third-party/mruby, which libnghttp2-sys 0.1.13's build.rs does not name. - setvbuf gets the caller the stage did not name: curl's TLS key log (lib/vtls/keylog.c). - The landing queue (#650, #659, #661 "open", "in this order") and "which main does not have" turn false at a landing that need not touch this file, and go. The stage keeps the libc its gaps were measured against, pull request #650's at 15625e0, and says of that libc what it said of main's: `git grep -w` for setvbuf, socketpair and select over userland/libc at 15625e0 matches nothing. - "The plan's stages", "the plan sets no order" and "the plan does not say" pointed at a document the tree does not hold. Each is written as open. - Waiting on stage 3 of the child-process track and on /dev/null is said of the tools written in C or C++, as it was measured, and no longer of brush and uutils, which are Rust. - "C and C++ are accepted" becomes the owner's word, C. - The child-process track's sentence no longer repeats why select passes FD_SETSIZE; this track says it beside the open question. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
`pkg_install_gbae`'s client launched gbae and exited at once, on the premise that a launched program outlives the program that started it. Under this branch a launch's child is its launcher's, so gbae ended (137) with the client and the compositor never counted its window. The client now launches with `CommandExt::under_init`, the one way to outlive a starter. The launch still goes through init's launcher and the `/apps` row, so the test's subject and its refusal arms are as they were. Measured under QEMU, `cargo test --test toyos-build -- pkg_install_gbae`: exit 1 at d62803b ("gbae started and the compositor never counted a window", `exit: gbae pid=21 code=137`), exit 0 with the launch under init. Whether the shipped launchers (the compositor, the terminal, sshd) should do the same until the track's stage 5 gives a login its session is the owner's question, and nothing of theirs changes here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
#660 cut the guest suite to what no cheaper tier reaches and moved the shared boot to the T14; #678 rewrote the role prompts and the worktree commands. Conflicts, hunk by hunk: - `tests/toyos.rs`: main's harness, with this branch's registrations re-made in its shape. `process_tree` was a QEMU machine test on `tests/proctreecase`; it is now a `METAL` row on that config (`PROCTREECASE`), its binary on `RUST_SKIP`, and its judge (`process_tree`) reads off the stick what the QEMU arm read off the console: the job's exit, the loader's refusal of `/system/bin/no_such_program` once per B, and one depth refusal naming 65. `spawn_lands_claimed` stays an `ACTUATOR_TESTS` member, so it rides the shared boot on the kernel that carries `SYS_DEBUG`. The `Sched` and `CARRIES` rows have no table left to sit in. - `src/build.rs`: main's list of boot configs, plus `tests/proctreecase`. - The child-process track: main's text, with stage 4 deleted as this branch deletes it. Main's stage 6 gained "A quit reaches the process's subtree by stage 4's walk"; the citation of the deleted stage goes and the sentence stays. - `blockd_io.rs`, `spawn_cwd.rs` (modify/delete): this branch's hunk in each was the `place` field `SpawnArgs` gained. Main deleted both tests; the hunk has nothing left to adapt. - `compositor_client_death.rs` (modify/delete): this branch's hunk placed the test's relay under the test's root, because the relay outlived the creator that started it. Main deleted the test; nothing is left to adapt. - `pkg_launch_gbae.rs` (modify/delete): this branch's hunk has the client start gbae under init. Main deleted the client with `pkg_install_gbae`, which the guest-suite track brings back as a metal row from main before the cut, where the client still exits over a child that now ends with it. That track's `pkg_install_gbae` item now says the client starts gbae under init. Not a conflict, and broken by the merge: `src/ci.rs`'s `CONTROLS` rows take a `Verdict` on main, so this branch's five `toyos-proclife` rows name their tests as `Fails(...)` with the module path, and the landed-child control names its `tree` test as well. Filed: `issues/build/a-metal-judge-takes-a-names-lowest-pid-for-the-job.md`. `Readback::exit_code` takes a name's lowest pid for the job, and this branch's kernel gives a refused spawn's pid to the next admission. Before this commit, on the merged tree: `cargo run -- --build-only` exit 0, `cargo test --test toyos-build -- --list` exit 0, and `--metal --list` for `process_tree`, `abuse_handle_table` and `spawn_lands_claimed` exit 0 each (one boot each: `proctreecase`, `shared`, `shared-debug`), which builds every guest binary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
…ain consumes Main moved under this round three times: #669, #650 and #653. The merge before this one, 54c375a, took main at 74a2e70 (#669), not at de5f63c as its title says: the shared `origin/main` ref had been fetched forward between this round's fetch and its merge. This one names its commit. The one conflict is the `rust` gitlink. Main pins 3f6050fc829 (#650); this branch pinned d1b9f2eae3c, its three std commits over the pin before, on a fork branch of its own. Neither contained the other. The gitlink is now 6c7f996a4fe, the merge of d1b9f2eae3c onto 3f6050fc829: main's pin and this branch's three commits, and nothing else. The two sides share no file. It is on the fork's `main` through the merge 012fdce3c79, which also brings that branch the two of this branch's commits it lacked. `kernel/src/loader/mod.rs`, `tests/toyos.rs` and `toyos-abi/src/syscall.rs` merged without a conflict. Before this commit, on the merged tree: `cargo run -- --build-only` exit 0, with the sysroot built from the fork at 6c7f996a4fe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
The first merge of this round wrote what the restored `pkg_install_gbae` row needs into the guest-suite track's item for it. That track is the orchestrator's, and `issues/README.md` files a new fact as a new file: the track is as main has it again, and `issues/build/pkg-install-gbaes-launch-client-exits-over-a-child-that-ends-with-it.md` carries the fact, its two QEMU exits and the commit that holds the fixed client. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
|
Round 3's QEMU arms, taken before the merge of #660 moved the shared boot to the T14. Each is
Under each of the three
--- a/kernel/src/loader/start.rs
+++ b/kernel/src/loader/start.rs
@@ -77,10 +77,6 @@
if end > labels.len() {
return Err(SyscallError::InvalidArgument.into());
}
- // The kernel's own, which a caller's of that name would shadow in the child's lookup.
- if &labels[label_off as usize..end] == SELF_LABEL.as_bytes() {
- return Err(SyscallError::InvalidArgument.into());
- }
// Checked before any removal, so a missing `TRANSFER` refuses the spawn instead of leaving a hole.
let rights = data.handles.rights_of(handle)?;
if !rights.contains(Rights::TRANSFER) {
--- a/toyos-abi/src/syscall.rs
+++ b/toyos-abi/src/syscall.rs
@@ -468,7 +468,7 @@
/// truncated — the widest manifest row plus stdio.
pub const MAX_ENDOWMENTS: usize = 32;
/// `(label, handle)` pairs one spawn may carry: the kernel adds [`SELF_LABEL`].
-pub const MAX_SPAWN_ENDOWMENTS: usize = MAX_ENDOWMENTS - 1;
+pub const MAX_SPAWN_ENDOWMENTS: usize = MAX_ENDOWMENTS;
/// `(child slot, parent handle)` pairs one spawn may carry.
///
/// **Derived rather than chosen.** A slot map installs into the child's table,
--- a/kernel/src/process.rs
+++ b/kernel/src/process.rs
@@ -1748,7 +1748,7 @@
use core::sync::atomic::Ordering::Relaxed;
let Parent::Under(place) = parent else { return };
if MARKED_SPAWNER.compare_exchange(current_process().0, Pid::MAX.0, Relaxed, Relaxed).is_ok() {
- kill(place);
+ let _ = place;
}
}
--- a/kernel/src/process.rs
+++ b/kernel/src/process.rs
@@ -908,7 +908,8 @@
let admitted = self.0.take().expect("Admission: landed once");
let pid = admitted.pid();
let (inserted, retire) = tree::land_child(table, admitted, KILLED_EXIT_CODE, insert);
- (inserted, scheds(table, pid, retire))
+ let _ = (pid, retire);
+ (inserted, Vec::new())
}
}
--- a/kernel/src/loader/mod.rs
+++ b/kernel/src/loader/mod.rs
@@ -675,9 +675,7 @@
drop(guard);
// Its parent was claimed while it was built, and its walk has passed: the
// child is ended as that walk would have ended it, and the spawn answers it.
- for sched in &retire {
- scheduler::post_retire(sched);
- }
+ let _ = retire;
let t3 = crate::clock::nanos_since_boot();
log!("spawn: {} pid={} tid={} dst={} base={:#x} entry={:#x} root={:#x} symbols={}KiB (layout={}ms relocs={}ms deps={}ms tls={}ms total={}ms)", |
|
T14 evidence at
|
|
Review of CI: Growth:
Round 2
BLOCKERNone. NOTE
REMOVE
What this verdict rests on
LAND AFTER NAMED CHANGES |
No IdMap is keyed by a pid since the process table took its pids from toyos_proclife::Pids, and IdMap's `next + K::ONE` was the only place two pids were added. `impl IdKey for Pid` (kernel/src/id_map.rs) and `impl Add for Pid` (toyos-abi/src/lib.rs) have no user left and go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
…reecase boot is recorded The merge 54c375a put process_tree's judge between process_reopen and its doc, so the doc headed process_tree and process_reopen had none. process_tree, with its own doc, now stands above it. abuse_handle_table's comment on the closing loop loses the sentence about generations: the loop closes what dup2 answered and says so itself. The record gains the proctreecase boot's three numbers. The harness wrote them judging process_tree over the T14's readback of 1e9cb8c (`cargo test --test toyos-build -- --metal --metal-readback <dir> process_tree`, exit 0): complete_ms 1153, panel_max_us 3773, panel_us 21279. This branch is what adds that boot, so its record lands here. The judging of abuse_handle_table over the same head's `shared` readback also wrote three `boot.shared` rows, off a boot carrying that one member; they are not this branch's and are left out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
… says "not found" for every spawn error Both are main's behaviour, found by the third review of #659 and not changed here. std answers `LaunchError::NotSent` with the direct spawn, and the one `encode` refusal std can reach is a request past MAX_FRAME_LEN: that child holds its caller's namespace and not its row. The shell discards the error of every spawn and prints `not found`. The T14's proctreecase boot at 1e9cb8c printed it for the depth refusal the kernel recorded beside it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
Main gained #643, blockd setting its ring cursors before it answers an open, with its issues. It shares no file with this branch and the merge has no conflict. Main's `rust` gitlink is unchanged at 3f6050fc829, so the branch's pin 6c7f996a4fe stands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
|
T14 evidence at
|
Cargo.lock alone conflicted: both sides kept, main's pcap-file and byteorder_slice beside this branch's ureq, flate2 and tar trees, and cargo left the result as it resolves the merged manifests. Against origin/main the lockfile differs by what it did before the merge, +801 -6. #659 moves the rust gitlink and the kernel, so the freestanding and sysroot keys move with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
Main's nightly 36985427800, at 74a2e70, ended its three-hour toolchain step red: "crates.io holds no toyos-abi of this tree, so no sdk alias can name it". #650 and #659 had landed meanwhile and their pushes had put newer toyos-abi versions up, so the tree the nightly checked out was no longer the one crates.io's newest named. This branch deleted that step and kept the red: `release_as` ran behind `ci::at_tip`, which refuses with "HEAD ... is not main's tip" whenever a landing precedes the `release` job, and `alias` kept the crates.io refusal for a landing whose crates went up after that check. `sdk_at_tip` is now the release's one decision, taken before anything is laid out, packed or put up: it reads crates.io, then main's tip. A tree main has moved past puts nothing up and the job is green, saying so; the tip's nightly publishes. At the tip the plan it read is the one the alias is written from, so nothing read later can disagree with it. crates.io before the tip is the order that matters: a landing whose crates the first read shows has moved the tip the second read sees, and the other order leaves a landing between the two reads refused. What stays refused is the tip's own crates not being up, which publish.yml owes, and a remote that names no main. A run of an older tree still moves no alias back, which is what `at_tip` was put there for; `at_tip` is `publish`'s alone again, and private as on main. `a_landing_during_the_nightly_puts_nothing_up_and_is_no_failure` lands once before the release's first read, between its two reads and not at all, with and without newer SDK crates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
Six conflicts. - rust: the gitlink is 01b8626673f, the fork's main (012fdce3c79, which holds main's pin 6c7f996a4fe) with this branch's 61adcea7362 merged into it. Over 6c7f996a4fe it carries `configure_cmake`'s `toyos` arm and `src/llvm-project` at ceaf0fbb844, and nothing else: 012fdce3c79 still held the three cross-platform bootstrap commits 61adcea7362 reverts. - src/sysroot.rs: main's two keys. `clang::CMAKE` joins the sysroot's, beside the C++ runtime's options, and `RECIPE` is main's text with CMake's description of ToyOS named in it, at a number neither side had. - src/libc.rs: `build_c` writes the CMake files and then links main's probe. - src/libcxx.rs: main moved n2 out of the file; the configure through the sysroot's toolchain file is this branch's. - issues/build/toyos-builds-itself.md: main's Decided and To build sections whole. Main dropped the signal-set calls from the Compile bullet this branch had already replaced, with Configure and Link, by its pointer to the bootstrap issue; the pointer stays. - issues/build/bootstrap-cannot-build-llvm-clang-and-lld-for-a-toyos-host.md: this branch's body. Main's one hunk took `alarm` and the signal-set calls out of a paragraph this branch had deleted; what the build stops on at this tree is measured and written in the commits that follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
…among them) into wt/toyos-winitstall Three content conflicts, each a deletion on main's side of a block this branch had edited: - kernel/src/inbox/mod.rs: main deleted `Staged`, `handler-post`'s ring, with the actuator (#660). This branch's hunks inside it — the `owed` field, `Poll::new`, the `WatchFlags` direction and "fires" for "completes" in its doc — adapted it to the ring's new fields and go with it. `Inbox::complete` keeps this branch's wording and loses main's `raise_if_staged` call. - kernel/src/watch.rs: main deleted the `handler_post` module, `holding`, `note_post` and the `raise_if_staged` call in `IrqLock::with`. This branch's one hunk inside it was "fires" for "completes" in the module's doc, which goes with it. - userland/fsd/src/main.rs: main deleted the four test actuators (`--end-on`, `--end-at-read`, `--end-at-mount`, `--let-go-at-read`) and kept the acceptor probe; this branch deleted the probe and kept the actuators. Both deletions stand: no `caps_len`, no `probe` field, no actuator field, and `accept` is this branch's. Two resolutions no marker asked for: - src/ci.rs: #668 made a control's verdicts `Fails(..)` values, so `post-is-an-answer`'s three verdict strings become three `Fails`. - issues/build: main filed the C++ runtime's scratch removal as an issue of its own (`the-cxx-runtimes-scratch-removal-dies-on-a-finder-file.md`) beside the sweep's, which this branch had merged into one file. Main's two files stand and this branch's file goes. The `rust` gitlink is main's, `95960d6c2`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
Six conflicts, each resolved by taking main's side and applying this branch's deletion to it again: - kernel/src/actuator.rs: main deleted the rows this branch kept around `process-reopen-selftest` and kept that row; the row goes. - kernel/src/syscall/dispatch.rs: main's import list gained `spawn_place`; `sys_process_open` leaves it. - src/metal.rs: `FLASHABLE` is a list of names on main; the `process-reopen-selftest` name goes. - tests/toyos.rs: main moved the QEMU machine test out, so what is left to delete is the metal row, the `SELFTESTS` arm, the `process_reopen` judge and the two counts of that image's actuators. - tests/test-durations: main deleted the file; this branch's one hunk removed a row of it, and goes with it. - the track file: main reworded stage 0 and stage 1; stage 0 is deleted and stage 1 is main's. kernel/src/object/handle.rs and kernel/src/object/mod.rs merge clean, so the `Process` row is sealed again over #659's spawn, whose two installs on a child's object are not ordered for that. The commits after this one make that red and then remove it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
…s for it `debug_action::HOLD_SPAWN_UNTIL_CHILD_ENDS` (23) marks the caller's next spawn whose child lands: its thread parks in `loader::spawn`, after the landing and its retires, until the child's exit is published. `spawn_child_ends_first` spawns a child that exits at once under that hold and reads the child's code off the handle the spawn answers. This is the window the merge before this commit left open: #659 installs the child's own `self` at the commit, schedules the child, and installs its spawner's handle only once `loader::spawn` has returned. A child whose table closes in between takes its object's handle count to zero and back, which `HandleEntry::new` asserts against on every row now that none is `reopenable`. The binary is the control for the commit that removes the window. `ProcessEntry::object` goes: `process::process_object` was its last caller on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
The branch was at 649ea51 (#641). Three landings since sit under it: #642 (dc8212c), #659 (c1c5048) and #655 (5daab30). Two content conflicts, each main deleting what this branch's hunk stood beside: - kernel/src/object/ops.rs, close_ends_polls: #655 deleted the log's and the keyboard's close actuators, whose two arms this branch's `Process(_) => false` sat between. Main's two `false` arms stand and the process's is a third. - tests/toyos-rust-tests/src/bin/process_lifecycle.rs, the imports: #642 deleted `toyos::AsHandle` with the pid arm, its one user; this branch's `toyos::poller` import stands alone. Everything else merged by itself: #642's deletions in kernel/src/object/process.rs beside this branch's `Arc<Watch>`, #659's init changes beside the one doc sentence this branch deletes, and the `rust` gitlink at main's 95960d6c214. This commit is the resolution and nothing else. What #655's contract changes in this branch's own lines is the next commit's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm
Stage 4 of
issues/kernel/a-childs-end-is-an-event-and-a-parent-takes-its-children-down.md, as ruled: every process has one parent, and an end (exit, kill, CPU fault, handle fault) takes its whole subtree. The stage is deleted from the track.What a user sees today
A program started by another is that program's child, and ends when it ends. On
maina process has no parent, and nothing ends a program because the one that started it ended.detach <program>at the shell starts a program under init, which outlives the shell and the terminal.detach /system/bin/sshdand outlives the shell that started it.pkg_install_gbaeshowed: its client launched gbae and exited, and gbae ended as killed.The track's stage 5 gives each login a session under init, which is what separates a login's programs from the compositor and sshd. Until that session lands, a launched program dies with its launcher. The owner has ruled that the track's own interim, which stage 5's negative control states: "the stage reverted whole, where the compositor's programs die with it". The owner was not asked to accept anything further. Nothing here changes how the compositor, the terminal and sshd launch.
What changed, per decision
The decisions are
toyos-proclife'streeandpidsmodules. Each process carries aNode: its parent, its depth below init, the children placed under it, and a count of what holds its publication. The count is its own teardown, plus every child admitted under it and not yet published or refused. The kernel only performs the answers (kernel/src/process.rs,kernel/src/loader/mod.rs).tree::admit_childruns at the top ofloader::spawn, before anything is built, and takes the child's pid.Gone.MAX_DEPTH(64) below init answersResourceExhausted, and the kernel logsspawn: refused under pid N at depth D, more than 64 below init.ResourceExhausted, and the kernel logsspawn: refused, every pid below 4294967295 is issued.Admissionguard's drop (tree::refuse_child).toyos_proclife::pids::Pids, held by the process table beside its entries;ProcessTableis a struct now).Pid::MAXis never issued, because it is the per-CPU word for no process. On main,IdMapissued it, and the nextPid + Pidpanicked the kernel.Pidis noIdKeyand has noAdd: noIdMapis keyed by a pid, andIdMap's step was the only place two pids were added.ProcessObjectmade before the commit dies with the refused spawn, so no handle, no node's children and no walk names that pid. A pid an entry held is never issued again.commititself refuses after that point (aselflabel, a missingTRANSFER, no room), so each refused commit would still spend a pid, without bound. Taking it after the commit leaves exhaustion to refuse after the caller's handles moved. A 64-bit pid was not taken, because std'sprocess::id()andChild::id()answeru32.tree::land_childinserts the child in one hold of the table lock, and the caller fills the entry and schedules the thread inside that hold, through a closure.Admission::landmaps them throughscheds, which every claim's retires also go through. The loader posts them after the table lock is given up.yield_nowwith nothing held. The walk runs inprocess::exit, which serves exit, CPU fault and handle fault, and inprocess::kill, whichkill_processcalls, on the ending or killing thread. A process another end claimed first is that end's to walk.Exiton the entry and lowers its own share of the count. Whoever lowers a count to zero publishes, then lowers the parent's count. Publication goes child before parent, at mostMAX_DEPTH+ 1 times, each with the table lock given up.self. Every process starts holding a handle to itself underSELF_LABEL("self"), withWRITE,DUPandTRANSFER. The commit that moves the endowments installs it.selfof its own is refusedInvalidArgument, because the child's lookup would find it first.MAX_SPAWN_ENDOWMENTSandMAX_SPAWN_LABELS_LEN(toyos-abi) are what a spawn may carry: one entry and four label bytes under what the table holds.SYS_SPAWN's decode andbuild_child_handlesboth read them.toyos::endow::this_processanswers the handle, and panics when it is gone, since the kernel puts it in every table.SpawnArgs::place(112 bytes to 120). It is a handle carryingWRITE, orHANDLE_INVALIDfor the caller.SYS_NAMESPACE_BUILD's connector are both handles a peer sent. They resolve through one lookup,HandleTable::get_sent, which answers a wrong typeInvalidArgument.HandleError::WrongTypedocuments the exception once, and the connector's own match inipc.rsis gone.WRITE, such as aMANAGE-only one, isPermissionDenied.MAX_LAUNCH_EXTRAS.CommandExt::under).MSG_GONE, which it reads from its spawn'sBrokenPipe, std's word for the kernel'sGone. Nothing else in that path answersBrokenPipe: the command is prepared, so the spawn calls no file server, and every refusalstartmakes before the spawn isOther.rustgitlink is 6c7f996a4fe: main's pin 3f6050fc829 (libc: the POSIX surface LLVM compiles against, and the headers held to the definitions #650) with this branch's three commits (336c8a8e141, 40470840283, d1b9f2eae3c) merged over it, and nothing else. The two sides share no file. It is on the fork'smain, through the merge 012fdce3c79, and the fork branch this pull request had of its own is deleted. No lockfile names the fork.self.CommandExt::under_initasks for init, and is a launch or nothing. No launcher, a program the launcher answers as undeclared, an endowment and an extra slot each answerPermissionDeniedand start nothing.provided than fit beside the place answerInvalidInput.Launch::encoderefuses it, std answersLaunchError::NotSentwith the direct spawn, and that child holds its caller's namespace and not its row (issues/isolation/a-launch-too-large-for-one-frame-becomes-a-direct-spawn.md).to_io_error.detach <program>, which starts a program under init and does not wait for it.system.toml's note on starting sshd by hand now names it.debug_action::KILL_PLACE_AS_SPAWN_LANDS(22, never assigned) marks the caller's next spawn. On thetest-actuatorskernel the loader kills that spawn's place after the commit and before the landing, a window no caller can order a kill inside. The static, the mark and the hook are each#[cfg(feature = "test-actuators")], whichsrc/clippy.rs'sboot-actuators,test-actuatorsshapes lint on both architectures.Gates, at 3fd1419
3fd1419 merges main at 80bc97b (#643), which shares no file with this branch.
rustis at 6c7f996a4fe and the tree is clean after each. Logs andgates.exitsare under/Users/jan/.claude/jobs/2280e09e/tmp/scratchpad/orch/659-named/; the same gates at 517d8c1, before that merge, exited the same and are inat-517d8c16c/beside them.cargo run -- --ci host: EXIT=0, "Host: 64 step(s), all green". Its clippy step runs every kernel shape. Its controls step reached everytoyos-proclifecontrol's verdict, the five this branch adds among them.cargo run -- --build-only: EXIT=0. The sysroot is built from the fork at 6c7f996a4fe.impl IdKey for Pidandimpl Add for Piddead. With both deleted, the kernel builds on both architectures and in every clippy shape, and so do the std fork, the SDK, libc and every userland crate. The oracle is rustc's name resolution, which fails the build of any caller left. The guest suite and the metal staging below built every test binary.cargo test --test toyos-build, the whole guest suite: EXIT=0, "21 passed, 21 total". This branch adds no test to it.The T14
cargo run --bin toyos-metal -- --image … --readback … --fat32-check:proctreecaseEXIT=0,sharedcarryingabuse_handle_tablealone EXIT=0,shared-debugcarryingspawn_lands_claimedalone EXIT=0. Eachverdict.txtreads "passed".Ris/Users/jan/.claude/jobs/2280e09e/tmp/scratchpad/orch/659-round. Each was run by the harness at 1e9cb8c and again by the harness at 3fd1419, with the same exit and lines:cargo test --test toyos-build -- --metal --metal-readback $R/metal-process_tree process_tree: EXIT=0, "PASS process_tree", "1 passed, 0 failed, 1 boot(s)".cargo test --test toyos-build -- --metal --metal-readback $R/metal-abuse_handle_table abuse_handle_table: EXIT=0, "shared: 1 member(s)", "1 passed, 0 failed, 1 boot(s)".cargo test --test toyos-build -- --metal --metal-readback $R/metal-spawn_lands_claimed spawn_lands_claimed: EXIT=0, "shared-debug: 1 member(s)", "1 passed, 0 failed, 1 boot(s)".abuse_handle_table.rs, which moves that binary's panic line numbers;process_tree's judge withintests/toyos.rs, its text unchanged;proctreecaseboot intests/metal/lenovo-20w0003amz.toml, and files two issues.process_treewrote theproctreecaseboot's three numbers (complete 1153 ms, panel_max 3773 us, panel 21279 us), and they are committed: this branch adds that boot. Judgingabuse_handle_tablewrote threeboot.sharedrows off a boot carrying one member. They are left out, becauseshared's whole boot is not this branch's.cargo test --test toyos-build -- --metal --metal-readback /Users/jan/.claude/jobs/2280e09e/tmp/scratchpad/orch/659-named/metal-whole, EXIT=2, "staged 28 image(s)". No filter selects the shared boots alone, so the whole profile was staged and the images nobody asked for were removed. Six remain:shared(38 members),shared-2(38),shared-3(2),shared-debug(4),ccorpusandproctreecase.Tests
Host (
toyos-proclife; the model carries the count, the climb, the pids and laws L9 to L12):explore_any: some ordering has it).tree::tests: a chain is refused atMAX_DEPTH+ 1 below init; a place being torn down admits nothing; a child landed under a place claimed since its admission is claimed with it, its thread is answered for its spawner to retire, and it holds the place's publication; a published child leaves its parent's children; with two pids left, a refused spawn gives the last back three times over, the landing takes it, the next admission answersNoPidand raises no hold, and init publishes once its child does.pids::tests: pids issue in order from 0; the last isu32::MAX - 1and none follows it; a pid given back is the next taken.process_tree, aMETALrow bootingtests/proctreecase(a launcher,catand the shell). For B killed and B taking a CPU fault:selfanswerGone.Then:
MANAGE-only handle as place answersPermissionDenied.selfnarrowed toWRITEanswersPermissionDeniedand starts nothing.under_initis refused for an undeclared program and for an extra slot.: not foundfor every spawn error (issues/diagnostics/the-shell-says-not-found-for-every-spawn-error.md), so the depth is the judge's to show.cata shelldetaches outlives the shell (roster, at the instant the shell's wait answers).The row's judge also reads two kernel records off the stick:
spawn: /system/bin/no_such_program:, once per B, showing B's first act reached the loader rather than failing in std; and the one depth refusal, naming 65.abuse_handle_table, a shared-boot member, gains two arms beside its repeated-endowment refusal:selfnaming a pipe's write end answersInvalidArgument, and a byte written to that end still arrives;MAX_ENDOWMENTSentries naming distinct pipe ends answerInvalidArgument, and the firstMAX_ENDOWMENTS - 1start a child that exits 0.spawn_lands_claimed, a shared-boot member on the kernel that carriesSYS_DEBUG. The test starts a place that hands it itsself, marks its next spawn, and spawns a child that parks under that place.Gone, which is the kill having happened.Where they run, and why no cheaper tier. All three run on the T14: one row and two shared-boot members. None is a QEMU guest test.
toyos-proclife. No host harness reaches what performs them:kill's walk, the loader'sAdmissionand its retire posts,spawn_placeandget_sent, init spawning under a place, and std's ToyOS code run only on a booted machine.process_treehas a boot of its own becausetests/testcaseshas no launcher and declares no shell.High-risk checks
process_treecut to its first arm in main's API, exits 1 on "C was not ended, as killed, once B's end was published" (A parent's end takes its children down: every end walks its subtree and is published after it, a spawn past its commit lands, and a refused spawn spends no pid #659 (comment)). It has not been run on the T14.cargo test --test toyos-buildEXIT=0, "471 passed, 471 total". It is the one QEMU verdict over the shared boot's members on this kernel.Admission::landanswering no thread (mut-landing-land) and the loader posting no retire (mut-landing-post):spawn_lands_claimedexits 1 under each, at the suite's 300 s ceiling, because the wait on the child never answers. The actuator killing nothing (mut-landing-nokill): exit 1, the guest's 101 on "the kernel did not kill the place of the marked spawn".start.rs'sselfcheck deleted:abuse_handle_tableexits 1, the guest's 101 on "an endowment labelled self must be refused". The one-entry margin deleted,MAX_SPAWN_ENDOWMENTS = MAX_ENDOWMENTS: exit 1, the guest's 101 on "a spawn carrying MAX_ENDOWMENTS entries must be refused".mutate-landed-child-retires-nothing, the host control for the landing's decision:--ci hostreached its two verdicts, the interleaving and thetreelanding test both FAILED.refuse_childwithoutgive_back: exit 101, the pid exhaustion test FAILED.Pids::takewithout itsPid::MAXstop: exit 101, bothpidstests and that test FAILED. Both were measured at cc49e13; since thentoyos-proclifehas changed by one line of its crate doc.toyos-proclife's interleavings cover the protocol this stage changes, and this round changes none of it:kill_process's body moved toprocess::kill(pid)unchanged. They ran in--ci host.Documentation/admin-guide/cgroup-v2.rst):cgroup.killkills every process in the cgroup and all its descendants, which is what the walk does from an end.cgroup.events'populatedturns 0 only when the cgroup and every descendant holds no live process, which is the publication order the host model checks.cgroup.max.depthrefuses a new descendant at or past the limit, which is the depth refusal the chain checks.pid_max:alloc_pidanswersEAGAINonce no pid is free, a refusal and never a panic, andforkfrees the pid of a child it fails to finish (kernel/pid.c,free_pidoncopy_process's error path).Unsure of
shared(38 tests ended, 38 exit 0),shared-2(38, 38),shared-3(2, 2),shared-debug(4, 4),ccorpus(90, 90, tinycc 206 to 208 among them) andproctreecase(1, 1); eachtoyos-metalEXIT=0 with verdictpassed, in A parent's end takes its children down: every end walks its subtree and is published after it, a spawn past its commit lands, and a refused spawn spends no pid #659 (comment). They are green arms only, and the harness has not judged these readbacks: the judging exits above are over the boots at 1e9cb8c.toyos-metal's exit and the kernel'sexit:record of each member.dupfail. The place's half is measured, byprocess_tree's launch under aselfnarrowed toWRITE.mainholds more than this pin. Its tip, 012fdce3c79, also carries M2: CMake's ToyOS in every C sysroot, LLVM's two ToyOS arms, and LLVM, clang and LLD built for a ToyOS host by unchanged bootstrap as far as libc lets it #661's five commits (25da73337eb to 7cd37350b51), which nothing on ToyOS'smainconsumes. This pull request pins 6c7f996a4fe, which has none of them.spawn_lands_claimedreds a broken landing by a hang, at the runner's deadline, not by a named assertion: a child that is never retired answers no wait.sys_sysinfo's roster, which then lists such a child among older ones and, cut short by a small buffer, keeps the lowest pids;reap::finished_pids, which sorts for determinism; and the metal judge'sReadback::exit_code, which takes a name's lowest pid for the job (issues/build/a-metal-judge-takes-a-names-lowest-pid-for-the-job.md).provides five connectors can no longer launch: the place takes one of the five.yield_nowbetween two claims of a walk is unmeasured: no test reaches it deterministically. It runs on the exit, kill and CPU-fault paths; the CPU-fault entry runs atBASELINE_TRAP, whichyield_nowasserts.origin/mainref had moved. ad21ebf sayspkg_install_gbaewas red at d62803b; it was measured at d69b80a, whose tree differs only inabuse_handle_table.rs.What remains
issues/isolation/the-supervisor-is-host-tested-and-owns-the-stop.mdstage 2 moves launch resolution intotoyos-supervisor, init's refusal of a launch naming no parent moves with it.issues/kernel/a-process-that-starts-four-billion-threads-panics-the-kernel.md: a thread id still overflows into a kernel panic, as a pid did.issues/kernel/a-spawn-refused-for-its-callers-full-table-has-already-moved-its-endowments.md:sys_spawnstill installs the child's handle after the commit, so a full caller table refuses a spawn whose endowments have moved. Every other refusal leaves the caller's table as it was.issues/isolation/a-launch-too-large-for-one-frame-becomes-a-direct-spawn.mdandissues/diagnostics/the-shell-says-not-found-for-every-spawn-error.md: main's behaviour, filed here and not changed.kernel/src/sleeplock.rs's doc onFREEsaysid_mapnever issuesu32::MAXin the pid half, andissues/kernel/a-double-fault-on-cpu-1-under-a-wide-suite.mdsays pids come fromIdMap. Pids come fromtoyos_proclife::Pids.🤖 Generated with Claude Code
https://claude.ai/code/session_013UDZQ6fSKw14e4w2TKTRfm