feat(causal): wall-anchored send_after — user-facing timer opt-out (RFC 007)
send_after_wall / send_after_named_wall (+ Timers::insert_send_wall) arm a
message-delivery timer that opts out of the RFC 007 virtual-time shift and
fires at its raw deadline regardless of injected delay — the Send-reason
sibling of sleep_wall, closing the jar item whose substrate efbc254 landed.
For deadlines that reflect the outside world (protocol timeouts, wall-clock
schedules) rather than workload pacing. cancel_timer is anchor-agnostic and
unchanged; without the feature the API exists and is identical to send_after.
The gen_server timer layer (send_after_to, RFC 015 §5) deliberately stays
virtual-only — an opt-out there means new options on the gen_server/statem
timeout API, out of scope for now.
Tests: wall send fires at raw deadline while a virtual sibling shifts;
cancel on a wall send with debt outstanding; featureless delivery/cancel
smokes through the public named API. 15/15 causal, 34/34 binaries both
feature configs, lib clippy clean both.
This commit is contained in:
@@ -570,3 +570,81 @@ fn sleeping_actor_pays_injected_delay() {
|
||||
"sleeper paid nothing: slept {ms}ms, expected >= 150ms (raw 50 + injected 150)"
|
||||
);
|
||||
}
|
||||
|
||||
/// RFC 007 user-facing opt-out: a wall-anchored `send_after` entry fires at
|
||||
/// its raw deadline while a virtual sibling armed at the same instant is
|
||||
/// shifted by injected delay — the `Send`-reason mirror of
|
||||
/// `wall_timer_ignores_injected_delay`.
|
||||
#[test]
|
||||
fn wall_send_after_ignores_injected_delay() {
|
||||
let _s = serial();
|
||||
use smarm::pid::Pid;
|
||||
use smarm::timer::Timers;
|
||||
use std::time::Instant;
|
||||
|
||||
let hz = smarm::causal::tsc_hz();
|
||||
let mut t = Timers::new();
|
||||
let now = Instant::now();
|
||||
let wall_fired = Arc::new(AtomicBool::new(false));
|
||||
let virt_fired = Arc::new(AtomicBool::new(false));
|
||||
let (w2, v2) = (wall_fired.clone(), virt_fired.clone());
|
||||
let _wid = t.insert_send_wall(
|
||||
now + Duration::from_millis(50),
|
||||
Pid::new(0, 0),
|
||||
Box::new(move || w2.store(true, Ordering::Relaxed)),
|
||||
);
|
||||
let _vid = t.insert_send(
|
||||
now + Duration::from_millis(50),
|
||||
Pid::new(1, 0),
|
||||
Box::new(move || v2.store(true, Ordering::Relaxed)),
|
||||
);
|
||||
|
||||
// 100ms of debt lands while both are pending.
|
||||
smarm::causal::inject_delay_cycles_for_test((hz * 0.100) as u64);
|
||||
|
||||
// Just past the raw deadline: only the wall send pops; run its thunk.
|
||||
let due = t.pop_due(now + Duration::from_millis(60));
|
||||
assert_eq!(due.len(), 1, "exactly the wall send must fire at raw deadline");
|
||||
for e in due {
|
||||
if let smarm::timer::Reason::Send { fire } = e.reason {
|
||||
fire();
|
||||
}
|
||||
}
|
||||
assert!(wall_fired.load(Ordering::Relaxed), "wall send must deliver");
|
||||
assert!(!virt_fired.load(Ordering::Relaxed), "virtual send must not");
|
||||
assert!(!t.is_empty(), "virtual sibling must remain queued, shifted");
|
||||
}
|
||||
|
||||
/// `cancel` is anchor-agnostic: a wall-anchored `send_after` cancels exactly
|
||||
/// like a virtual one and never delivers, even with delay debt outstanding.
|
||||
#[test]
|
||||
fn wall_send_after_cancels() {
|
||||
let _s = serial();
|
||||
use smarm::pid::Pid;
|
||||
use smarm::timer::Timers;
|
||||
use std::time::Instant;
|
||||
|
||||
let hz = smarm::causal::tsc_hz();
|
||||
let mut t = Timers::new();
|
||||
let now = Instant::now();
|
||||
let delivered = Arc::new(AtomicBool::new(false));
|
||||
let d2 = delivered.clone();
|
||||
let id = t.insert_send_wall(
|
||||
now + Duration::from_millis(20),
|
||||
Pid::new(0, 0),
|
||||
Box::new(move || d2.store(true, Ordering::Relaxed)),
|
||||
);
|
||||
|
||||
smarm::causal::inject_delay_cycles_for_test((hz * 0.100) as u64);
|
||||
|
||||
assert!(t.cancel(id), "cancel must find the wall-anchored send");
|
||||
for e in t.pop_due(now + Duration::from_secs(3600)) {
|
||||
if let smarm::timer::Reason::Send { fire } = e.reason {
|
||||
fire();
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
!delivered.load(Ordering::Relaxed),
|
||||
"cancelled wall send_after delivered"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user