Repository navigation
fix(runtime-core): bound the stop-path disk-handle scan at 30 s with an injectable probe (HACK-1328) - #553
Conversation
…ake it injectable (HACK-1328) `down`, `recover` and host-filesystem inspection bounded the lsof disk-handle scan at 5 s, below scans measured on loaded hosts (macOS 5.4-6.5 s; Linux up to 5.3 s with concurrent scans at load ~120). A slow scan made an ordinary `down` end provider_timeout_uncertain without the clean-stop marker, so the next restart fell back to the fresh disk budget. - Move the scan into lifecycle/disk_handles.rs behind a HandleProbe and a Clock; the result is interpreted the same way for every probe (only exit 1 with no output proves the disks closed). - Raise STOP_HANDLE_WAIT to 30 s, matching RELEASE_HANDLE_WAIT; document both. - Split process::capture into spawn + Pending::poll/expire + wait_within so the bounded wait runs on an injected clock; production behavior is unchanged. - Fake-clock tests cover both bounds (slow scan accepted, hung or late scan expired as uncertain, finite), killed by mutation; real-lsof tests cover a held disk and closed disks. - Serialize real lsof scans across test processes with a test-only flock so concurrent whole-host scans no longer time out the lsof-dependent tests. - Add scripts/measure-lsof-scan.ts to measure scan times on a host. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve lifecycle.rs: keep the 30 s production stop bound in disk_handles and the injectable probe; keep next's test-only LOADED_HOST_SCAN_WAIT for lock_absent_disks fixtures (HACK-1457) as STOP_SCAN_WAIT at the call site. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…every lsof benchmark child (HACK-1328) Review fixes for #553. - process::wait_within read the clock only when the child was still running, so a waiter that overslept past the bound and found the child finished accepted it. The clock is now read after every poll: a result observed at or after the bound is refused as provider_timeout_uncertain (the child has already been reaped, so nothing is signalled). A running child or held streams at the bound still expire as timeout or output-retention. Fake-clock tests oversleep across the bound and the completion (refused) and within the bound (accepted); restoring the old order fails the first. - scripts/measure-lsof-scan.ts registers every child (burners and scans) as it is spawned, inside the cleanup scope, and kills and awaits all of them on success, a failed spawn part-way through, a hung scan or SIGINT/SIGTERM. Each scan is bounded (--scan-timeout, default 120 s) across exit and both streams; expiry cancels the reads so a descendant holding a pipe cannot keep the run alive. tests/measure-lsof-scan.test.ts covers a clean run, a hung scan, a held pipe, a partial launch failure and an abort, each checking no child is left running; mutations removing the bound, the read cancel, the cleanup scope or stopAll each fail it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…re (HACK-1328) Review fix for #553: abort raced the run against a scan without stopping it, so a scan whose pipe a descendant held kept its readers and its timer alive, and the CLI could linger up to the scan bound after SIGINT/SIGTERM. A spawn failure in the concurrent round could likewise abandon sibling scans. Each scan now takes the run's AbortSignal and ends at completion, its bound, or abort; on every path it clears its timer and listener, kills and awaits its child and cancels and awaits both reads. Teardown aborts the run, awaits all active scans, then stops every owned child. An already-aborted signal starts nothing. Adds --lsof for timing another lsof and for the CLI test. Tests: an already-aborted signal; a failed spawn in the concurrent round stops its hung sibling; SIGINT to the CLI during a scan whose pipe a descendant holds exits 130 within 4 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Independent review (GPT-6-Astra, codex-dio): VERDICT: CLEAN at fe29abe. Reviewer thread mcp:1c082903-bbd9-4cbe-a2c3-42090cee6afe. Report-only review over three rounds. Round 1 at fee8ad8 raised two P2s: a late scan result accepted after the deadline, and benchmark children escaping cleanup. Both were fixed in 11b4215. Round 2 raised one P2 (abort not cancelling in-flight scans), fixed in fe29abe. Round 3: no remaining findings. Exact-head CI and fresh macOS lsof timings remain separate gates. No PR merge performed. |
|
Re the Codex connector comments above (summary and usage-limit notice at fee8ad8): they hold no findings; the bot review didn't run because of its usage limit. The independent GPT-6-Astra (codex-dio) review stood in for it and is CLEAN at fe29abe (comment above). A |
|
You have reached your Codex usage limits. You can see your limits in the Codex usage dashboard. |
Why
down,recoverand host-filesystem inspection prove the provider released its disks by runninglsofoverstorage.rawandoverlay.raw. That scan was bounded at 5 s (STOP_HANDLE_WAIT).lsofwalks every process's descriptors, and on a loaded host it takes longer than that.downthen ended asprovider_timeout_uncertain, wrote no clean-stop marker, and the next restart fell back to the fresh disk budget. The start path's release check already used 30 s. Neither bound had a test, becauselsofis a fixed host path.What changed
lifecycle/disk_handles.rs. The scan now goes through an injectableHandleProbeand aClock. Production uses the hostlsofand the monotonic clock. The answer is interpreted the same way for every probe: only exit 1 with no stdout and no stderr proves the disks closed. The ownership checks are unchanged (VM lock,lsof, sockets, disk identity).RELEASE_HANDLE_WAIT. Both constants now live together with their measurements documented. 30 s is more than four times the slowest scan measured. Withdown's other bounded waits (15 s guest shutdown acknowledgement, 10 s provider exit), a stop still ends within about a minute.process::capturesplit intospawn,Pending::poll/expireandwait_within(pending, timeout, clock). The bounded wait can now run on a fake clock. Production behavior is the same: the bound covers exit plus stream close; on timeout only the direct child is killed and reaped; a helper that keeps output open is reported asprovider_output_uncertain.lsoftests. In test builds only, reallsofscans take a per-userflock, so every test process (isolated children included) runs one whole-host scan at a time. Each scan's bound starts once it holds the lock. Concurrent scans under load are what timed these tests out. The lock is released explicitly, so a forked copy of the descriptor cannot keep it held.scripts/measure-lsof-scan.tstimes the exact scan (sequential and concurrent rounds, optional CPU burners) and prints JSON with no paths or hostnames.Measurements
These are from a Linux host with 20 CPUs (
bun scripts/measure-lsof-scan.ts). Every scan returned the clean no-holder answer.The earlier macOS figure, 5.4–6.5 s at about 1,850 processes and load 30–80, came from the previous
RELEASE_HANDLE_WAITcomment. No new macOS timings have been taken yet. To take them, runbun scripts/measure-lsof-scan.ts --runs 32 --concurrency 8(add--burn Nfor CPU contention) on a loaded Mac.Tests
disk_handles::testsare not platform-gated, so they run on Linux and macOS:both_bounds_outlast_a_slow_loaded_host_scan: a scan four times the slowest measured one (26 s on the fake clock) passes under both bounds.both_bounds_expire_a_hung_scan_as_uncertain_at_the_bound: a scan that never finishes is expired at the bound asprovider_timeout_uncertain. The test also checks that each bound is at most 45 s.a_scan_finishing_after_the_bound_is_uncertain: a "no holder" answer that arrives late is not accepted.only_a_silent_no_holder_answer_proves_the_disks_closed: a listed PID, a diagnostic on stderr, exit 0 and exit 2 all refuse asstop_uncertain.host_lsof_finds_a_held_diskandhost_lsof_proves_closed_disks_closed(isolated child): the same checks against reallsof. They skip on Linux hosts withoutlsof; macOS must have it.process::testsadd exit status and both streams, kill and reap on timeout, and a helper that retains output reported asprovider_output_uncertain.Mutation testing. All 11 mutations were killed:
Where it ran
cargo fmt --check,cargo clippy --all-targets -D warnings, the same with--all-features, and the focused tests (provider::lifecycle::disk_handles,provider::process) all pass. The fullcargo testrun was still queued behind other work on this machine's shared job limiter when I pushed; hosted CI is the full-suite signal.cargo clippy --target aarch64-apple-darwin --all-targets(default and all features, clean), cross-compiled from Linux. The macOS-only lifecycle tests (admission_pool,short_home,host_filesystem,interrupted) were not run locally. The CICandidate core (macos-latest)job runs them.Remaining gaps
lock_absent_disks→ stop bound,released→ release bound) is covered by the macOS lifecycle tests only. Both bounds are 30 s, so swapping them would change nothing.Linear: HACK-1328
Review fixes (after fee8ad8)
process::wait_withinreads the clock after every poll. A result first seen at or after the bound is refused asprovider_timeout_uncertain, so a waiter that oversleeps past the deadline can no longer accept a late scan. New fake-clock tests oversleep across the bound (refused) and within it (accepted). Restoring the old order makes the first test fail.scripts/measure-lsof-scan.tsowns every child it starts:--scan-timeout, default 120 s).--lsoflets you time a differentlsof.tests/measure-lsof-scan.test.tshas 8 tests: a clean run, a hung scan, a held pipe, a partial launch failure, abort, an already-aborted signal, a concurrent spawn failure, and SIGINT to the CLI with a held pipe.nextkeeps test(runtime-core): size release-check scans for load and pin the drop, kill-anchor and retired-format guards #501's test-only 60 sLOADED_HOST_SCAN_WAITfor thelock_absent_disksfixtures, asSTOP_SCAN_WAIT. Production stays at the 30 sdisk_handles::STOP_HANDLE_WAIT.fe29abef.🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.