Skip to content

Commit dd94a01

Browse files
committed
docs(plan): §17.6 —— mcpp run 的 ninja 空转满一个核
Observed three times in the sandbox verification, always on the `mcpp run` after a `mcpp build`: ninja at 99.9% CPU with zero children and zero system time, on a complete eight-edge graph with nothing to do. Not the graph — the host runs the same project, with the same two `stage_file` edges, in about two seconds. Not reproducible on demand afterwards in the same sandbox. One difference recorded without a claim: the sandbox's ninja and the host's are different binaries at the same version. The deterministic corollary is worth fixing regardless: killing `mcpp run` does not kill its ninja, so every timed-out step leaks a spinning core — one orphan outlived the removal of its entire sandbox.
1 parent 2a1a9b9 commit dd94a01

1 file changed

Lines changed: 40 additions & 0 deletions

File tree

.agents/docs/2026-09-04-named-runners-and-the-universal-command-surface.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,3 +765,43 @@ surface is the claim that running on a device is like running hosted, and a
765765
hosted `run` that cannot report a program's status is not that. Changing it
766766
means choosing what mcpp's own failures return once the child's status is
767767
passed through.
768+
769+
### 17.6 Open: `mcpp run`'s ninja spins at 100% of one core with nothing to do
770+
771+
Observed three times during the sandbox verification — twice on
772+
`riscv64-none-elf`, once on `aarch64-none-elf` — always on the `mcpp run` that
773+
follows a `mcpp build` in the same project.
774+
775+
What was measured while it was happening:
776+
777+
| observation | reading |
778+
|---|---|
779+
| ninja processes | 3, each at 99.9% CPU |
780+
| child processes of each | **zero** — no compiler was running |
781+
| system time (`stime`) | **0** — a pure user-space loop, not an I/O wait |
782+
| the graph | 8 edges, every output present, no missing input, no future mtime |
783+
| host load | 3.65 on 32 cores, i.e. one core each and nothing else |
784+
785+
So ninja had no work and burned 27 minutes of CPU deciding that.
786+
787+
IT IS NOT THE GRAPH. The same project built and run on the host completes in
788+
about two seconds, and its `build.ninja` carries the same two `stage_file` edges
789+
— the build-cache staging that first looked like the correlation. Running the
790+
same ninja binary over the same directory by hand exits 0.
791+
792+
IT DOES NOT REPRODUCE ON DEMAND. Re-running both sequences afterwards inside the
793+
same sandbox — `mcpp run` alone, and `mcpp build` followed by `mcpp run`
794+
succeeded, printing the program's output and exiting 0. Whatever the trigger is,
795+
it is not the command sequence by itself.
796+
797+
One difference is recorded without a claim attached: the sandbox's ninja and the
798+
host's are DIFFERENT BINARIES AT THE SAME VERSION — 273768 bytes dynamically
799+
linked against 2202320 bytes static, different SHA-256, both answering `1.12.1`.
800+
That is worth resolving on its own terms, since it means "ninja 1.12.1" does not
801+
name one artefact, but it has not been shown to cause this.
802+
803+
A DETERMINISTIC COROLLARY, WORTH FIXING WHATEVER THE CAUSE IS: killing `mcpp
804+
run` does not kill the ninja it spawned. Each timed-out step left an orphan
805+
spinning at 100% of a core with its working directory already deleted, and one
806+
survived the removal of the whole sandbox it belonged to. Any CI that wraps
807+
`mcpp` in `timeout` leaks a busy core per timeout.

0 commit comments

Comments
 (0)