S-P6 - the quantum is spent, and a thread is not a zombie
Two gaps were named when S-P5 closed, and this is both of them.
The time slice is spent, not merely declared
The scheduler computed a slice per class and then re-picked on every timer
tick, so the slice was a number nothing read. hw_schedule now decrements
slice_left and returns without re-picking, unless a higher class became
runnable.
Gated rather than inspected. The boot gate compares accounted ticks against
scheduler runs: with the quantum enforced a boot measures ticks=491 runs=82,
and with the check removed it measures 335/335 - one run per tick, which is
what "the slice is a number nothing read" looks like from outside. The
assertion was confirmed to go red by disabling the quantum.
A thread has no reaper, so it must not become a zombie
tests/linux/musl_texec.c was written to cover the one combination nothing
exercised: threads created and joined while children exec. It found a defect on
its first run, and the defect was not in exec.
An exiting task became a ZOMBIE and waited to be reaped. waitpid matches on
ppid, and a thread inherits its creator's parent rather than becoming its
child - so the thing that joins it cannot wait for it, and nothing else is
looking. Every thread that ever exited held its slot for the rest of the boot.
The failure surfaced nowhere near the threads. A program creating and joining
eight threads leaked eight slots; several commands later the shell's fork was
refused on a machine with plenty of memory. Six too-many-children refusals in
one boot, then a wedge. The fork guard added in S-P5 is what made it visible -
before that the slots were simply gone, and the table quietly ran out.
Joining is already synchronised without a zombie: exit clears the word the joiner sleeps on and wakes the futex. The zombie was carrying an exit status nobody was able to ask for.
Threads are now reaped where they die, and the refusals go from six to zero.
Two things this cost, both worth writing down
A slot released outside the transition table stays RUNNING. The first
version called hw_task_release directly, which is running -> free. The
table refuses that, correctly, and logged one [TASKS] ILLEGAL line per
thread - so instead of leaking a zombie the kernel leaked a task stuck in
RUNNING, which is worse and quieter. The table was right and the fix was wrong.
It goes through ZOMBIE now. Without that table the change would have looked
like it worked.
One boot argued the opposite of the truth. Raising the task table from 24 to 32 slots measured a broken boot, and 24 measured a working one, so 32 was recorded as the cause. It was not: the leak was, and with the leak fixed 32 boots clean eight times out of eight. Two single boots on a machine with a known intermittent failure decided a question they cannot decide - which is this project's own rule about counts, applied to a sample of one.
What is verified
- 8/8 clean boots with
repeat-boot.sh. TEXEC_OK rounds=8 execs=8when the program is run.- Zero
too-many-childrenrefusals in a boot that previously had six. - Zero
[TASKS] ILLEGALtransitions.
What is not
musl_texec is not in the boot script. It passes its own rounds, but
running it in the boot wedges the guest in a later phase, and that is an open
defect rather than a passing test. The gate asserts on it whenever it is
started - keyed on the program having been started, not on the binary being
present on the media, because those are different questions and the difference
already cost a run.