Repository navigation
tests: give the exec lock's forced stop and retry race a loaded host's time (exec-lock-flake) - #1564
Merged
Pedro Henrique Penna (ppenna) merged 1 commit intoNov 13, 2026
Conversation
Contributor
This pull request changes existing testsIt modifies or removes 2 existing test asset(s), listed below. Review each with care: a change to a test can hide a regression that the test caught. See doc/contribute.md#changes-to-existing-tests. Test code
Relocated unchanged: 0; added: 0. Compared the merge base |
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues were identified for the test-only exec-lock timing adjustments.
No open issues 🎉
What changed in this PR
This tests-only PR reduces timing sensitivity in the CLI exec-lock tests on loaded hosts, supporting the NVX refactoring workstream without changing product behavior.
Changes:
- Increases the contention test’s stop timeout from 0.1 to 5 seconds to allow forced-termination confirmation.
- Extends the retry-race admission wait from 4 to 8 seconds and removes yielding from its polling loop.
- Documents the timing rationale while preserving all existing assertions.
| File | Description |
|---|---|
nvx-rs/tests/cli/exec_lock.rs |
Adjusts timing and polling in two exec-lock tests for loaded hosts. |
Review effort: Balanced
…s time exec_lock::exec_processes_contend_but_other_sandboxes_and_stop_proceed stopped its sandbox with --timeout 0.1 while an exec ran. The fake OpenVMM serves one control session at a time, so the exec's session kept the stop's request from the guest, and the stop forced the VM after its grace. It then waits up to --timeout again, from just before it terminates OpenVMM, for the process to exit, and a stop whose terminated OpenVMM does not exit in time fails with still-running and keeps the runtime record, as doc/run.md says. Windows ends a terminated process asynchronously, and under load its exit took up to 1.4 s to confirm, so the 0.1-second window failed the test on Windows CI and on a loaded workstation. The stop now has --timeout 5. Its grace stays far below the minute that the workload sleeps, so the stop still forces the VM, and still ends well within the test's 30-second bound. exec_lock::a_retry_that_waits_for_the_lifecycle_lock_past_the_deadline_is_busy polled the lifecycle lock with a yield between its tries, which on a loaded host lets every other ready thread run first while the exec's brief tries of the lock come and go, and it needed one within the first half of a 4-second --wait, which a loaded host can spend starting the exec. Under load, all five rounds could miss and fail the test with "no round delayed a retry of the exec past its deadline". It now polls without yielding, with an 8-second --wait. Part of #1076. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna)
force-pushed
the
agents/slim-exec-lock-flake
branch
from
November 13, 2026 03:51
8e0ecc5 to
0e8dae6
Compare
Pedro Henrique Penna (ppenna)
marked this pull request as ready for review
November 13, 2026 03:52
Pedro Henrique Penna (ppenna)
merged commit Nov 13, 2026
367f7fe
into
feature/nvx-refactor
83 of 84 checks passed
Pedro Henrique Penna (ppenna)
deleted the
agents/slim-exec-lock-flake
branch
November 13, 2026 04:10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #1076: exec-lock-flake. Fixes two flaky tests of the CLI's exec lock, which #1469 (SL3.9) added. Only the tests change.
What and why
Symptom.
exec_lock::exec_processes_contend_but_other_sandboxes_and_stop_proceedfailed in the jobnvx-rs / windows-latestof run 43345750757 (#1555) and of run 43234396201, attempt 1 (#1533), and once on a loaded Windows workstation:Its sibling
exec_lock::a_retry_that_waits_for_the_lifecycle_lock_past_the_deadline_is_busyfailed once on a loaded workstation too. Under load it fails withno round delayed a retry of the exec past its deadline.Root cause of the stop's failure. With
--timeout T,sandbox stop(host/cli/sandbox/lifecycle/stop.rs) works in two windows:T: it attaches with a deadline ofT, sends STOP with what is left of the grace, and waits for OpenVMM to exit until the grace ends.stop_verifiedthen takes a freshDeadline::after(T).terminate_withincallsvm::stopwith no grace and a 25 ms bound, which terminates the verified process (TerminateProcesson Windows) and waits 25 ms, and thenwait_for_exitwaits until that deadline. A process that still runs then isTimedOut: the stop fails withstill-running(with no "forced termination failed", so the termination itself succeeded) and keeps the runtime record.So
Tbounds the grace and, separately, the confirmation of the termination: withT = 0.1, about 100 ms afterTerminateProcessreturns. In this test the stop always forces the VM: the fake OpenVMM serves one control session at a time (one named-pipe instance on Windows, accepts one by one on Unix), the exec's session, whose workload sleeps for a minute, holds it, and the stop's attach fails afterT(managed guest shutdown failed: ... control endpoint is busy). NVXC v2's cancellation of an exec by a shutdown (SL4.4b) applies to a STOP in the exec's own session, so a longer grace can't make this stop graceful.Windows ends a terminated process asynchronously, and a loaded host takes a while to show it. With an instrumented copy of the test (not committed), which terminates the fake OpenVMM during the exec as the stop does and times
Process::wait, with the test and its load pinned to the same CPUs (the load being two loops of the rest of the CLI suite with 16 test threads each):A test problem, not a product one. The stop behaves as documented, which lets a termination that is not confirmed in time fail:
vm::stop's rustdoc: "If exit is still unconfirmed, attempts forced termination and waits untildeadline. [...] An unconfirmed exit fails withErrorKind::StillRunning".terminate_within's: "waits untildeadlinefor it to exit [...] One that still runs at the deadline isStopOutcome::TimedOut[...] keep the record."doc/run.md: "Astopthat cannot determine the OpenVMM process, whose terminated OpenVMM does not exit in time, or whose wait Ctrl-C ends, keeps the sandbox's runtime record, and the nextstopretries", and, for a VM of another runtime identity, "stopterminates its OpenVMM without a guest shutdown, waits up to--timeoutseconds for it to exit".doc/usage.md,still-running: "A stop could not confirm the VM process's exit, so the caller keeps its verified process."StillRunningis "for an exit that it could not confirm".The 0.1-second budget was the test's own: it asked the stop to confirm a termination within 100 ms.
Root cause of the retry race's failure. The sibling needs an exec whose retry waits for the lifecycle lock until after its
--waitdeadline. The exec holds that lock only while it tries the exec lock, briefly, every 25 ms or so, so the test polls it withtry_lockuntil it finds a try, and takes it; it accepts a try found within the first half of--wait(4 s) after the spawn. A round that misses gives way to another, and the test fails after 5. Two things made rounds miss under load:thread::yield_now(),SwitchToThreadon Windows), which on a busy processor lets every other ready thread run first. On one loaded CPU, most rounds polled only tens of times in their 4 to 5 seconds, and the exec's tries passed unseen.Rounds that reached the precondition, with an instrumented copy of the test under the load above (not committed). Each run interleaved its two variants under the same load:
--waitFix.
exec_processes_contend_but_other_sandboxes_and_stop_proceedtakes--timeout 5, with a comment that says why. Its grace stays far below the minute that the workload sleeps, so it still forces the VM, andforcibly terminatedstill holds, now with 5 s to confirm the termination. It takes about 5 s, the time that its attach waits for the busy endpoint, and still ends well within the test's 30-second bound, which still shows that it waited neither for the exec lock nor for the workload.a_retry_that_waits_for_the_lifecycle_lock_past_the_deadline_is_busypolls without yielding, and its exec's--waitis 8 s, with comments: a round finds a try within 4 s, and the precondition keeps half of--wait, now 4 s, before the exec's deadline. A reached round holds the lifecycle lock for 8.5 s instead of 4.5 s.Every assertion of both tests stays. No product code, default, or retry changes.
Not changed here.
doc/usage.md's--timeoutrow lists the stop's control requests, its wait for the VM to exit once the guest shuts it down, and its wait for the lifecycle lock, but not its wait after a forced termination, whichdoc/run.mddescribes.stop_forces_the_verified_vm_after_its_grace_period_even_after_an_acknowledgementintests/cli.rsgives its forced stops 1 s to confirm the termination, which the one-CPU maximum above exceeds; it has not been seen failing.Size
0 core lines in 0 counted files: the change is in
nvx-rs/tests/cli/exec_lock.rsalone, with 17 lines added and 11 removed.Changes to existing tests
python scripts\check_test_changes.py --base origin/feature/nvx-refactorlists 2 changes to existing tests, hencetests-modified:exec_processes_contend_but_other_sandboxes_and_stop_proceed: its stop's--timeoutgoes from 0.1 to 5, with a comment that explains the stop's two windows. Every assertion stays.a_retry_that_waits_for_the_lifecycle_lock_past_the_deadline_is_busy:WAITgoes from 4 s to 8 s, and its poll loop no longer callsthread::yield_now(), with comments. Every assertion stays.doc/acceptance-case-matrix.mdlists no test ofnvx-rs/tests/cli/, so no row changes.Validation
Stress, before and after, on a Windows workstation (20 logical CPUs): the base's and this change's test binaries, both built on
ab2673e7751, ran the test one after the other, in turns, under the same load, pinned to the same CPUs withstart /affinity. Load: two loops of the rest of the CLI suite (cli.exe --skip TEST --test-threads 16).exec_processes_contend_but_other_sandboxes_and_stop_proceedstill-runningat line 132; 2 of 15 in an earlier runa_retry_that_waits_for_the_lifecycle_lock_past_the_deadline_is_busya_retry_that_waits_for_the_lifecycle_lock_past_the_deadline_is_busyno round delayed a retry); 1 of 10 in an earlier runno round delayed a retry)CPU-bound load alone (busy loops) failed neither test before the change.
cargo fmt --all --checkcargo clippy --locked --all-targets --all-features -- -D warnings, the only Clippy run that compilestests/clicargo test --locked --all-features --test cli exec_lock::cargo test --locked --all-features --test clicargo +1.89 check --locked --all-targets --all-features(MSRV)python scripts\check_test_layout.pypython scripts\check_test_changes.py --base origin/feature/nvx-refactorEvery check but the Windows
exec_lock::run ran on this commit before its rebase ontoecd6e02d85f, which changes rustdoc, docs, and scripts only (git range-diffshows no change); the Windowsexec_lock::run is on the rebased head.The stop's stderr on Windows with
--timeout 5, which took 5.02 s:warning: managed guest shutdown failed: Error { code: TimedOut, message: "the control connection timed out: control endpoint is busy" },warning: OpenVMM was forcibly terminated after managed stop,warning: the forced stop produced no outcome report.Left to CI: the rest of
cargo testand the other gates, which this change doesn't touch. No microVM scenario runs these tests.