fix(causal): born current — slot reuse booked phantom park forgiveness

A fresh/reused slot started with causal_delay=0 and causal_parked=true:
the first resume then 'forgave' the entire monotone global backlog, once
per spawn, booked as park forgiveness. Under close-mode conn churn (~95k
spawns/s) that is millions of phantom forgiven ms per 700ms window, even
in 0% cells — the books could not balance under actor churn while
ka-mode stayed plausible (few spawns). Impacts were unaffected: the
first resume always precedes the first check, so nobody ever spun.

reset_counters now installs Coz's new-thread rule: causal_delay starts
at the current global ledger and causal_parked starts false — a newborn
neither owes nor is forgiven the process's history, and delay injected
while it sits spawn-queued (runnable, not blocked) is owed and paid at
its first check, the semantics audit_zero_pct_window_absorbs_leftover_
debt pins. That test (born failing, unmasked by the run() propagation
fix) and the new actor_churn_between_experiments_forgives_nothing
regression test both go green.
This commit is contained in:
Claude (sandbox)
2026-07-18 21:55:56 +00:00
parent 7eae56a296
commit d5a3ba1934
2 changed files with 72 additions and 6 deletions
+50
View File
@@ -977,3 +977,53 @@ fn audit_offcpu_gap_across_windows_not_counted() {
);
});
}
/// Slot reuse under actor churn must not book the process's accumulated
/// delay history as park forgiveness. A fresh incarnation is born current
/// (`causal_delay = GLOBAL_DELAY` at install), so churning short-lived
/// actors while no experiment is live forgives exactly nothing: the global
/// ledger is frozen (checks gate on the experiment word) and a newborn
/// starts even with it. Found live (urus close-mode load, ~95k conn
/// spawns/s): delay=0 + parked=true at install forgave the whole backlog
/// once per spawn — millions of phantom ms per 700ms window, even in 0%
/// cells, while ka-mode books stayed plausible.
#[test]
fn actor_churn_between_experiments_forgives_nothing() {
let _s = serial();
smarm::init(smarm::Config::exact(1)).run(|| {
// Seed a nonzero global backlog with a real experiment.
let stop = Arc::new(AtomicBool::new(false));
let stop_t = stop.clone();
let target = smarm::spawn(move || {
let _g = smarm::causal_site!("churn-seed-site");
while !stop_t.load(Ordering::Relaxed) {
smarm::check!();
}
});
let g0 = smarm::causal::global_delay_cycles();
smarm::causal::begin_experiment_for_test("churn-seed-site", 50);
smarm::sleep(Duration::from_millis(60));
smarm::causal::end_experiment_for_test();
let backlog = smarm::causal::global_delay_cycles() - g0;
assert!(backlog > 0, "seed experiment injected nothing");
stop.store(true, Ordering::Relaxed);
target.join().unwrap();
// Every live actor's wake (the sleep, the joins) is behind us and
// the ledger is frozen; snapshot, then churn reused slots with
// trivial actors. The first resume alone is the trigger — the actor
// body never parks.
let c0 = smarm::causal::ledger_counters();
for _ in 0..200 {
smarm::spawn(|| {}).join().unwrap();
}
let c1 = smarm::causal::ledger_counters();
let forgiven = c1.park_forgiven_cycles - c0.park_forgiven_cycles;
assert_eq!(
forgiven, 0,
"spawn churn booked {forgiven} phantom forgiveness cycles \
against a frozen {backlog}-cycle backlog"
);
});
}