Brings the branch up to date with main (past ade3f855) before review.
Main changed none of the files this branch changes, so the merge is
clean.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This reverts commit 85aab023. PR #166 (energy/load-robust-tests) fixes
the same test, jouleclaw-energy/tests/process_cpu.rs, the same way: it
reads the child's own counter until it reaches 0.2 J, for at least
300 ms and at most 5 s. Dropping this branch's copy of that fix keeps
the two PRs from conflicting on the file; #166 carries it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Brings the branch up to date with main before review. No file this
branch changes was touched on main, so the merge is clean.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both liveness ticks in this crate had a 500 ms wall-time cap. One is
liveness::burn_thread_cpu, used by the shared probe (the per-process,
RAPL and NVML counters). The other is apple_ioreport's own
burn_thread_cpu, used by the IOReport rail probe. Each burned its tick of
the probing thread's CPU time, but gave up after 500 ms of wall time. On
a host that runs the thread for under 5% of a core, the cap cut the tick
short. A probe that burns less between samples can see a live counter
hold, and call it frozen. The meter's verdict then depends on host load.
This is the defect fixed in proof-cascade's tick.
Production change: both are bounded by thread CPU time only. A tick
ends once the thread has burned the whole amount, however much wall time
that takes. Against a thread CPU clock that stops moving, it ends after
liveness::STUCK_CLOCK_ROUNDS (40,000) spin rounds in a row without the
clock moving, a count of the thread's own work rather than of wall time.
With no readable thread clock each still spins its amount of wall time,
as before. The wall_cap parameter and PROBE_TICK_WALL_CAP are gone; the
tests that passed a cap to the burn as a hang guard pass only the CPU
amount now.
Tests: apple_ioreport's assert_tick drops its escape clause
(`|| wall >= TEST_TICK_WALL_CAP`) and requires 25 ms of thread CPU from
every tick, however long it took.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The liveness probes' tick (liveness::burn_tick, which the per-process,
IOReport and RAPL probes all use) burned 25 ms of the probing thread's
CPU time but gave up after 500 ms of wall time. On a host that runs the
thread for under 5% of a core, the cap cut the tick short. A probe that
burns less between samples can see a live counter hold, and call it
frozen. The meter's verdict then depends on host load. This is the
defect fixed in proof-cascade's tick, in this crate's own copy.
Production change: liveness::burn is bounded by thread CPU time only.
The tick ends once the thread has burned the whole 25 ms, however much
wall time that takes. Against a thread CPU clock that stops moving it
ends after 40,000 spin rounds in a row without the clock moving, a count
of the thread's own work rather than of wall time. With no readable
thread clock it still spins 500 ms of wall time, as before; the constant
is renamed PROBE_TICK_NO_CLOCK_WALL for that remaining role.
Tests: assert_ticks_burned drops its escape clause
(`|| wall >= TICK_WALL_CAP`) and requires 25 ms of thread CPU from every
tick. The real-probe test requires a whole tick of CPU per interval
unconditionally, where it allowed half a tick whenever a tick was slow.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The liveness probe's tick burned 25 ms of the probing thread's CPU time
but gave up after 500 ms of wall time. On a loaded host that runs the
thread for under 5% of a core, the cap cut the tick short: on the macOS
runner an interval held 12.4 ms of thread CPU, under the tests' 12.5 ms
floor, and the host_status_calls_*_frozen tests failed. In production
the same cap made the probe's work per interval, and so its verdict,
depend on host load.
Production change: the tick is bounded by thread CPU time only. It ends
once the thread has burned the whole 25 ms, however much wall time that
takes. Against a thread CPU clock that stops moving it ends after 10,000
spin rounds in a row without the clock moving, a count of the thread's
own work rather than of wall time. An unreadable clock still falls back
to 25 ms of wall time.
Tests: R1 (each_tick_burns_the_probe_tick_of_thread_cpu_time) now
requires 25 ms of thread CPU from every tick. Its escape clause for a
tick that hit the wall cap is gone; that clause let a tick that only
slept for 500 ms pass. The R2 floor is unchanged, and its message no
longer cites the cap.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
the_process_counter_meters_this_process_and_nothing_else let a busy
child run for a fixed 300 ms of wall time and required its own counter
to rise 0.2 J. How much CPU a child gets in 300 ms depends on host load:
on the loaded macOS runner it spent 0.12 J and the test failed, though
nothing about the counter under test was wrong.
The window now lasts until the child's own count, read independently,
has risen 0.2 J, and at least 300 ms. A loaded host stretches the window
but always leaves 0.2 J of the child's work in it, twice the 0.1 J
ceiling on what this process may meter meanwhile, so the child-exclusion
check still has the same margin. A child that has not spent 0.2 J in
20 s fails the test: it did not work, or the host is too loaded to
tell.
Test-only change; no production code changed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
two_threads_burning_meter_more_than_one metered one thread's 200 ms of
CPU in one span and two threads' in a second span, then required the
second figure to be larger. The CPU work in each span is fixed by the
threads' own CPU clocks, but its energy is not: it follows the core type
and clock the scheduler gives each span. On the loaded macOS runner one
thread read 0.92 J and two threads 0.59 J, and the check failed though
the meter counted every thread.
Both threads now burn in one span, and a per-thread oracle (the tests'
own proc_pidinfo PROC_PIDTHREADCOUNTS read of each thread's energy
count) reads each one's rise across its burn. The span's figure must be
at least the two rises added up, and more than either one's alone. The
figures compared cover the same work at the same time, so host load
cannot move one against the other. The check is stronger than before:
it bounds the figure by both threads' whole counts, not only by another
span's figure. A meter that reads only the calling thread fails it.
Test-only change; no production code changed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>