style: cargo fmt sweep under rustc 1.97.1 (toolchain reformat, no semantic change)

This commit is contained in:
smarm-agent
2026-08-13 05:56:49 +00:00
parent 1262cc30e3
commit 95306c7f60
71 changed files with 1956 additions and 763 deletions
+24 -6
View File
@@ -49,8 +49,14 @@ fn looping_actor_on_check_is_stopped() {
}
let _ = h.join();
});
assert!(saw_stopped.load(Ordering::SeqCst), "expected DownReason::Stopped");
assert!(dropped.load(Ordering::SeqCst), "Drop guard must run during the cancellation unwind");
assert!(
saw_stopped.load(Ordering::SeqCst),
"expected DownReason::Stopped"
);
assert!(
dropped.load(Ordering::SeqCst),
"Drop guard must run during the cancellation unwind"
);
}
#[test]
@@ -79,8 +85,14 @@ fn parked_on_recv_actor_is_stopped() {
}
let _ = h.join();
});
assert!(saw_stopped.load(Ordering::SeqCst), "expected DownReason::Stopped");
assert!(dropped.load(Ordering::SeqCst), "Drop guard must run on cancellation of a parked actor");
assert!(
saw_stopped.load(Ordering::SeqCst),
"expected DownReason::Stopped"
);
assert!(
dropped.load(Ordering::SeqCst),
"Drop guard must run on cancellation of a parked actor"
);
}
#[test]
@@ -185,6 +197,12 @@ fn stop_flagged_while_queued_lands_at_first_park() {
.recv_timeout(Duration::from_secs(10))
.expect("runtime deadlocked: stop against a QUEUED actor was lost at its first park");
assert!(saw_stopped.load(Ordering::SeqCst), "expected DownReason::Stopped");
assert!(dropped.load(Ordering::SeqCst), "Drop guard must run during the cancellation unwind");
assert!(
saw_stopped.load(Ordering::SeqCst),
"expected DownReason::Stopped"
);
assert!(
dropped.load(Ordering::SeqCst),
"Drop guard must run during the cancellation unwind"
);
}
+25 -26
View File
@@ -24,7 +24,11 @@ fn progress_point_counts() {
h.join().unwrap();
let after = smarm::causal::progress_snapshot();
let delta = |name: &str| {
after.iter().find(|(n, _)| n == name).map(|(_, c)| *c).unwrap()
after
.iter()
.find(|(n, _)| n == name)
.map(|(_, c)| *c)
.unwrap()
- before
.iter()
.find(|(n, _)| n == name)
@@ -45,21 +49,12 @@ fn site_guard_nesting_restores() {
assert_eq!(smarm::causal::current_site_name(), None);
{
let _outer = smarm::causal_site!("outer");
assert_eq!(
smarm::causal::current_site_name().as_deref(),
Some("outer")
);
assert_eq!(smarm::causal::current_site_name().as_deref(), Some("outer"));
{
let _inner = smarm::causal_site!("inner");
assert_eq!(
smarm::causal::current_site_name().as_deref(),
Some("inner")
);
assert_eq!(smarm::causal::current_site_name().as_deref(), Some("inner"));
}
assert_eq!(
smarm::causal::current_site_name().as_deref(),
Some("outer")
);
assert_eq!(smarm::causal::current_site_name().as_deref(), Some("outer"));
}
assert_eq!(smarm::causal::current_site_name(), None);
});
@@ -88,10 +83,7 @@ fn virtual_speedup_ledger() {
let bystander = smarm::spawn(move || {
while !stop2.load(Ordering::Relaxed) {
smarm::check!();
out2.store(
smarm::causal::my_absorbed_delay_cycles(),
Ordering::Relaxed,
);
out2.store(smarm::causal::my_absorbed_delay_cycles(), Ordering::Relaxed);
}
});
@@ -166,10 +158,7 @@ fn runnable_bystander_pays_delay() {
while !stop_b.load(Ordering::Relaxed) {
iters2.fetch_add(1, Ordering::Relaxed);
smarm::check!();
absorbed2.store(
smarm::causal::my_absorbed_delay_cycles(),
Ordering::Relaxed,
);
absorbed2.store(smarm::causal::my_absorbed_delay_cycles(), Ordering::Relaxed);
}
});
@@ -177,8 +166,7 @@ fn runnable_bystander_pays_delay() {
let i0 = iters.load(Ordering::Relaxed);
let t = std::time::Instant::now();
smarm::sleep(Duration::from_millis(150));
let rate =
(iters.load(Ordering::Relaxed) - i0) as f64 / t.elapsed().as_secs_f64();
let rate = (iters.load(Ordering::Relaxed) - i0) as f64 / t.elapsed().as_secs_f64();
out.store(rate as u64, Ordering::Relaxed);
};
@@ -448,7 +436,10 @@ fn timer_deadline_shifts_with_injected_delay() {
// Raw deadline passed, effective deadline not: nothing fires, entry kept.
assert!(t.pop_due(now + Duration::from_millis(60)).is_empty());
assert!(!t.is_empty(), "shifted entry must be re-queued, not dropped");
assert!(
!t.is_empty(),
"shifted entry must be re-queued, not dropped"
);
// Past raw + injected (with margin) it must fire. Chase in case a
// parallel test injected more debt meanwhile.
@@ -503,7 +494,11 @@ fn wall_timer_ignores_injected_delay() {
// Just past the raw deadline: the wall entry fires, the virtual one is
// re-queued at its shifted deadline.
let due = t.pop_due(now + Duration::from_millis(60));
assert_eq!(due.len(), 1, "exactly the wall entry must fire at raw deadline");
assert_eq!(
due.len(),
1,
"exactly the wall entry must fire at raw deadline"
);
assert_eq!(due[0].pid, Pid::new(0, 0));
assert!(!t.is_empty(), "virtual sibling must remain queued, shifted");
}
@@ -623,7 +618,11 @@ fn wall_send_after_ignores_injected_delay() {
// 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");
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();
+18 -10
View File
@@ -154,7 +154,10 @@ fn channel_ops_interleaved_with_monitor_churn_multi_thread() {
}
consumer.join().unwrap();
});
assert_eq!(total.load(std::sync::atomic::Ordering::Relaxed), (0..32).sum::<i64>());
assert_eq!(
total.load(std::sync::atomic::Ordering::Relaxed),
(0..32).sum::<i64>()
);
}
// ---------------------------------------------------------------------------
@@ -220,7 +223,10 @@ fn recv_timeout_reports_disconnected_on_close() {
fn recv_timeout_zero_duration_is_a_bounded_poll() {
run(|| {
let (_tx, rx) = channel::<i64>();
assert_eq!(rx.recv_timeout(Duration::ZERO), Err(RecvTimeoutError::Timeout));
assert_eq!(
rx.recv_timeout(Duration::ZERO),
Err(RecvTimeoutError::Timeout)
);
});
}
@@ -262,15 +268,17 @@ fn recv_timeout_many_waiters_multi_thread() {
let (tx, rx) = channel::<i64>();
let got = got2.clone();
let timed_out = timed_out2.clone();
handles.push(spawn(move || match rx.recv_timeout(Duration::from_millis(100)) {
Ok(v) => {
assert_eq!(v, i);
got.fetch_add(1, Ordering::Relaxed);
handles.push(spawn(move || {
match rx.recv_timeout(Duration::from_millis(100)) {
Ok(v) => {
assert_eq!(v, i);
got.fetch_add(1, Ordering::Relaxed);
}
Err(RecvTimeoutError::Timeout) => {
timed_out.fetch_add(1, Ordering::Relaxed);
}
Err(e) => panic!("unexpected: {e}"),
}
Err(RecvTimeoutError::Timeout) => {
timed_out.fetch_add(1, Ordering::Relaxed);
}
Err(e) => panic!("unexpected: {e}"),
}));
if i % 2 == 0 {
handles.push(spawn(move || {
+30 -11
View File
@@ -11,9 +11,15 @@ thread_local! {
static LOG: Cell<u64> = const { Cell::new(0) };
}
fn log(v: u64) { LOG.with(|c| c.set(c.get() | v)); }
fn get_log() -> u64 { LOG.with(|c| c.get()) }
fn reset_log() { LOG.with(|c| c.set(0)); }
fn log(v: u64) {
LOG.with(|c| c.set(c.get() | v));
}
fn get_log() -> u64 {
LOG.with(|c| c.get())
}
fn reset_log() {
LOG.with(|c| c.set(0));
}
extern "C-unwind" fn actor_simple() {
log(0x1);
@@ -56,7 +62,7 @@ fn actor_yields_and_resumes() {
use std::sync::OnceLock;
static REG_BEFORE: OnceLock<[u64; 4]> = OnceLock::new();
static REG_AFTER: OnceLock<[u64; 4]> = OnceLock::new();
static REG_AFTER: OnceLock<[u64; 4]> = OnceLock::new();
extern "C-unwind" fn actor_reg_check() {
unsafe {
@@ -73,7 +79,10 @@ extern "C-unwind" fn actor_reg_check() {
REG_BEFORE.set([s0, s1, s2, s3]).ok();
switch_to_scheduler();
let a0: u64; let a1: u64; let a2: u64; let a3: u64;
let a0: u64;
let a1: u64;
let a2: u64;
let a3: u64;
core::arch::asm!(
"mov {a0}, r12", "mov {a1}, r13", "mov {a2}, r14", "mov {a3}, r15",
a0 = out(reg) a0, a1 = out(reg) a1, a2 = out(reg) a2, a3 = out(reg) a3,
@@ -88,8 +97,14 @@ fn callee_saved_registers_survive_yield() {
let stack = Stack::new(64 * 1024, 4096).unwrap();
let sp = init_actor_stack(stack.top(), actor_reg_check);
set_actor_sp(sp);
unsafe { switch_to_actor(); switch_to_actor(); }
assert_eq!(REG_BEFORE.get().copied().unwrap(), REG_AFTER.get().copied().unwrap());
unsafe {
switch_to_actor();
switch_to_actor();
}
assert_eq!(
REG_BEFORE.get().copied().unwrap(),
REG_AFTER.get().copied().unwrap()
);
}
// Two actors, independent stacks.
@@ -123,14 +138,18 @@ fn two_actors_dont_corrupt_each_other() {
let sp_a = init_actor_stack(stack_a.top(), actor_a);
let sp_b = init_actor_stack(stack_b.top(), actor_b);
set_actor_sp(sp_a); unsafe { switch_to_actor() };
set_actor_sp(sp_a);
unsafe { switch_to_actor() };
let sp_a = get_actor_sp();
set_actor_sp(sp_b); unsafe { switch_to_actor() };
set_actor_sp(sp_b);
unsafe { switch_to_actor() };
let sp_b = get_actor_sp();
set_actor_sp(sp_a); unsafe { switch_to_actor() };
set_actor_sp(sp_b); unsafe { switch_to_actor() };
set_actor_sp(sp_a);
unsafe { switch_to_actor() };
set_actor_sp(sp_b);
unsafe { switch_to_actor() };
assert_eq!(A_VAL.with(|c| c.get()), 0xA00D);
assert_eq!(B_VAL.with(|c| c.get()), 0xB00D);
+22 -7
View File
@@ -11,8 +11,8 @@
//! OUTSIDE `run` — an in-actor assertion alone passes vacuously.
use smarm::{
channel, run, select, select_timeout, spawn, try_select, wait_readable,
wait_readable_timeout, wait_writable_timeout, yield_now, FdArm,
channel, run, select, select_timeout, spawn, try_select, wait_readable, wait_readable_timeout,
wait_writable_timeout, yield_now, FdArm,
};
use std::os::fd::RawFd;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
@@ -33,7 +33,10 @@ impl Pipe {
let mut fds: [libc::c_int; 2] = [0; 2];
let r = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC | libc::O_NONBLOCK) };
assert_eq!(r, 0, "pipe2 failed");
Pipe { read: fds[0], write: fds[1] }
Pipe {
read: fds[0],
write: fds[1],
}
}
}
@@ -253,12 +256,18 @@ fn wait_readable_timeout_times_out_then_succeeds_with_data() {
let (rfd, wfd) = (p.read, p.write);
let start = Instant::now();
assert_eq!(wait_readable_timeout(rfd, Duration::from_millis(30)).unwrap(), false);
assert_eq!(
wait_readable_timeout(rfd, Duration::from_millis(30)).unwrap(),
false
);
assert!(start.elapsed() >= Duration::from_millis(30));
// Timed-out wait must leave the fd clean; ready path returns true.
assert_eq!(raw_write(wfd, b"d"), 1);
assert_eq!(wait_readable_timeout(rfd, Duration::from_secs(5)).unwrap(), true);
assert_eq!(
wait_readable_timeout(rfd, Duration::from_secs(5)).unwrap(),
true
);
let mut buf = [0u8; 1];
assert_eq!(raw_read(rfd, &mut buf), 1);
ok2.store(true, Ordering::SeqCst);
@@ -274,7 +283,10 @@ fn wait_readable_timeout_wakes_on_late_data() {
let p = Pipe::new();
let (rfd, wfd) = (p.read, p.write);
let h = spawn(move || {
assert_eq!(wait_readable_timeout(rfd, Duration::from_secs(5)).unwrap(), true);
assert_eq!(
wait_readable_timeout(rfd, Duration::from_secs(5)).unwrap(),
true
);
let mut buf = [0u8; 1];
assert_eq!(raw_read(rfd, &mut buf), 1);
got2.store(buf[0] as u32, Ordering::SeqCst);
@@ -292,7 +304,10 @@ fn wait_writable_timeout_ready_now_on_empty_pipe() {
run(move || {
let p = Pipe::new();
// An empty pipe's write end is writable: ready-now path, no park.
assert_eq!(wait_writable_timeout(p.write, Duration::from_secs(5)).unwrap(), true);
assert_eq!(
wait_writable_timeout(p.write, Duration::from_secs(5)).unwrap(),
true
);
ok2.store(true, Ordering::SeqCst);
});
assert!(ok.load(Ordering::SeqCst));
+59 -15
View File
@@ -403,7 +403,10 @@ fn worker_pool_down_reaches_handle_down() {
let got = Arc::new(Mutex::new(Vec::new()));
let got2 = got.clone();
run(move || {
let server = start(Pool { watcher: None, log: Vec::new() });
let server = start(Pool {
watcher: None,
log: Vec::new(),
});
server.cast(PoolCast::SpawnDoomedWorker).unwrap();
let _ = server.call(()).unwrap(); // sync point: cast handled, worker live
*got2.lock().unwrap() = server.call(()).unwrap();
@@ -421,7 +424,10 @@ fn watch_dead_pid_is_noproc_down() {
let h = spawn(|| {});
let dead = h.pid();
h.join().unwrap();
let server = start(Pool { watcher: None, log: Vec::new() });
let server = start(Pool {
watcher: None,
log: Vec::new(),
});
server.cast(PoolCast::Watch(dead)).unwrap();
*got2.lock().unwrap() = server.call(()).unwrap();
});
@@ -497,7 +503,12 @@ impl GenServer for Timed {
}
fn timed(fired: Arc<Mutex<Vec<u32>>>, cancel_won: Arc<Mutex<Option<bool>>>) -> Timed {
Timed { timer: None, fired, cancel_won, last: None }
Timed {
timer: None,
fired,
cancel_won,
last: None,
}
}
// A one-shot armed from a handler fires into handle_timer with its payload.
@@ -534,7 +545,11 @@ fn cancel_before_fire_suppresses_it() {
let count = server.call(()).unwrap();
assert_eq!(count, 0, "cancelled timer must not fire");
});
assert_eq!(*cancel_won.lock().unwrap(), Some(true), "cancel beat the fire");
assert_eq!(
*cancel_won.lock().unwrap(),
Some(true),
"cancel beat the fire"
);
assert!(fired.lock().unwrap().is_empty());
}
@@ -549,11 +564,16 @@ fn tick_every_rearms_repeatedly() {
run(move || {
let cw = Arc::new(Mutex::new(None));
let server = start(timed(f2, cw));
server.cast(TkCast::Tick(Duration::from_millis(20))).unwrap();
server
.cast(TkCast::Tick(Duration::from_millis(20)))
.unwrap();
let _ = server.call(()).unwrap(); // sync: periodic armed
smarm::sleep(Duration::from_millis(130)); // ~6 periods
let count = server.call(()).unwrap();
assert!(count >= 3, "periodic should have re-armed several times, got {count}");
assert!(
count >= 3,
"periodic should have re-armed several times, got {count}"
);
});
// Every tick delivered the same payload.
assert!(fired.lock().unwrap().iter().all(|&v| v == 9));
@@ -568,7 +588,9 @@ fn cancel_stops_a_periodic() {
let c2 = cancel_won.clone();
run(move || {
let server = start(timed(f2, c2));
server.cast(TkCast::Tick(Duration::from_millis(20))).unwrap();
server
.cast(TkCast::Tick(Duration::from_millis(20)))
.unwrap();
let _ = server.call(()).unwrap();
smarm::sleep(Duration::from_millis(70)); // a few ticks
server.cast(TkCast::CancelLast).unwrap();
@@ -616,11 +638,17 @@ fn idle_fires_repeatedly_on_quiet() {
let idles = Arc::new(Mutex::new(0));
let i2 = idles.clone();
run(move || {
let server = start(Idler { window: Duration::from_millis(25), idles: i2 });
let server = start(Idler {
window: Duration::from_millis(25),
idles: i2,
});
smarm::sleep(Duration::from_millis(130)); // quiet ⇒ ~5 windows
drop(server); // keep the server alive across the quiet span
});
assert!(*idles.lock().unwrap() >= 2, "idle should re-arm and fire several times");
assert!(
*idles.lock().unwrap() >= 2,
"idle should re-arm and fire several times"
);
}
// Traffic within the window keeps idle from firing; only once the inbox goes
@@ -632,7 +660,10 @@ fn traffic_resets_the_idle_window() {
let before_quiet = Arc::new(Mutex::new(u32::MAX));
let bq = before_quiet.clone();
run(move || {
let server = start(Idler { window: Duration::from_millis(60), idles: i2 });
let server = start(Idler {
window: Duration::from_millis(60),
idles: i2,
});
// Poke every 25ms (< 60ms window) for ~100ms: each cast resets the
// window before it can elapse.
for _ in 0..4 {
@@ -643,8 +674,15 @@ fn traffic_resets_the_idle_window() {
smarm::sleep(Duration::from_millis(140)); // now genuinely quiet
drop(server);
});
assert_eq!(*before_quiet.lock().unwrap(), 0, "steady traffic must suppress idle");
assert!(*idles.lock().unwrap() >= 1, "idle fires once the inbox falls quiet");
assert_eq!(
*before_quiet.lock().unwrap(),
0,
"steady traffic must suppress idle"
);
assert!(
*idles.lock().unwrap() >= 1,
"idle fires once the inbox falls quiet"
);
}
// RFC 015 §4.7 — no armed timer survives loop exit. A server with a live
@@ -658,15 +696,21 @@ fn no_timer_survives_exit() {
let f_read = fired.clone();
run(move || {
let server = start(timed(f_server, Arc::new(Mutex::new(None))));
server.cast(TkCast::Tick(Duration::from_millis(15))).unwrap();
server
.cast(TkCast::Tick(Duration::from_millis(15)))
.unwrap();
let _ = server.call(()).unwrap(); // sync: periodic armed
smarm::sleep(Duration::from_millis(45)); // a couple of ticks
let mon = smarm::monitor(server.pid());
drop(server); // inbox closes → loop exits → guard drains timers
// Clean Down ⇒ the loop returned without the no-leak assert aborting.
// Clean Down ⇒ the loop returned without the no-leak assert aborting.
assert!(mon.rx.recv().is_ok());
let at_exit = f_read.lock().unwrap().len();
smarm::sleep(Duration::from_millis(90)); // would be several more ticks
assert_eq!(f_read.lock().unwrap().len(), at_exit, "no tick may fire after exit");
assert_eq!(
f_read.lock().unwrap().len(),
at_exit,
"no tick may fire after exit"
);
});
}
+91 -15
View File
@@ -100,7 +100,15 @@ fn state_timeout_fires() {
let got = Arc::new(Mutex::new(0u32));
let got2 = got.clone();
run(move || {
let m = TimerSm::start(T::Idle, TData { enters: 0, st_fires: 0, named_fires: 0, st_window: 5 });
let m = TimerSm::start(
T::Idle,
TData {
enters: 0,
st_fires: 0,
named_fires: 0,
st_window: 5,
},
);
m.send(Ev2::Cast(TCast::Arm)).unwrap(); // -> Armed, arms 5ms state-timeout
smarm::sleep(Duration::from_millis(40)); // let it fire
*got2.lock().unwrap() = m.call(|r| Ev2::Call(TCall::StFires(r))).unwrap();
@@ -116,13 +124,25 @@ fn state_timeout_auto_resets_on_transition() {
let got2 = got.clone();
run(move || {
// Long window so the explicit Disarm beats it comfortably.
let m = TimerSm::start(T::Idle, TData { enters: 0, st_fires: 0, named_fires: 0, st_window: 50 });
let m = TimerSm::start(
T::Idle,
TData {
enters: 0,
st_fires: 0,
named_fires: 0,
st_window: 50,
},
);
m.send(Ev2::Cast(TCast::Arm)).unwrap(); // -> Armed, arms 50ms state-timeout
m.send(Ev2::Cast(TCast::Disarm)).unwrap(); // -> Idle, auto-resets it
smarm::sleep(Duration::from_millis(80)); // past the original window
*got2.lock().unwrap() = m.call(|r| Ev2::Call(TCall::StFires(r))).unwrap();
});
assert_eq!(*got.lock().unwrap(), 0, "auto-reset cancelled the pending state-timeout");
assert_eq!(
*got.lock().unwrap(),
0,
"auto-reset cancelled the pending state-timeout"
);
}
// A named timeout survives a state change: armed in Idle, it still fires after
@@ -133,14 +153,26 @@ fn named_timeout_survives_transition() {
let got2 = got.clone();
run(move || {
// Armed's own state-timeout is long so it doesn't interfere.
let m = TimerSm::start(T::Idle, TData { enters: 0, st_fires: 0, named_fires: 0, st_window: 200 });
let m = TimerSm::start(
T::Idle,
TData {
enters: 0,
st_fires: 0,
named_fires: 0,
st_window: 200,
},
);
m.send(Ev2::Cast(TCast::Ping(20))).unwrap(); // arm "ping" for 20ms (in Idle)
m.send(Ev2::Cast(TCast::Arm)).unwrap(); // -> Armed (ping must survive this)
m.send(Ev2::Cast(TCast::Disarm)).unwrap(); // -> Idle (and this)
smarm::sleep(Duration::from_millis(60)); // let "ping" fire
*got2.lock().unwrap() = m.call(|r| Ev2::Call(TCall::NamedFires(r))).unwrap();
});
assert_eq!(*got.lock().unwrap(), 1, "named timeout fired across the transitions");
assert_eq!(
*got.lock().unwrap(),
1,
"named timeout fired across the transitions"
);
}
// Cancelling a named timeout before its window prevents the fire.
@@ -149,13 +181,25 @@ fn named_timeout_cancel() {
let got = Arc::new(Mutex::new(99u32));
let got2 = got.clone();
run(move || {
let m = TimerSm::start(T::Idle, TData { enters: 0, st_fires: 0, named_fires: 0, st_window: 200 });
let m = TimerSm::start(
T::Idle,
TData {
enters: 0,
st_fires: 0,
named_fires: 0,
st_window: 200,
},
);
m.send(Ev2::Cast(TCast::Ping(30))).unwrap(); // arm "ping" for 30ms
m.send(Ev2::Cast(TCast::CancelPing)).unwrap(); // cancel before it fires
smarm::sleep(Duration::from_millis(60)); // past the original window
*got2.lock().unwrap() = m.call(|r| Ev2::Call(TCall::NamedFires(r))).unwrap();
});
assert_eq!(*got.lock().unwrap(), 0, "cancel prevented the named-timeout fire");
assert_eq!(
*got.lock().unwrap(),
0,
"cancel prevented the named-timeout fire"
);
}
// ===========================================================================
@@ -170,15 +214,27 @@ fn cast_then_call_roundtrip() {
let got2 = got.clone();
run(move || {
// Long state-timeout window so it never fires during the test.
let m = TimerSm::start(T::Idle, TData { enters: 0, st_fires: 0, named_fires: 0, st_window: 10_000 });
m.send(Ev2::Cast(TCast::Arm)).unwrap(); // Idle -> Armed (enter)
let m = TimerSm::start(
T::Idle,
TData {
enters: 0,
st_fires: 0,
named_fires: 0,
st_window: 10_000,
},
);
m.send(Ev2::Cast(TCast::Arm)).unwrap(); // Idle -> Armed (enter)
m.send(Ev2::Cast(TCast::Disarm)).unwrap(); // Armed -> Idle (enter)
m.send(Ev2::Cast(TCast::Arm)).unwrap(); // Idle -> Armed (enter)
m.send(Ev2::Cast(TCast::Arm)).unwrap(); // Idle -> Armed (enter)
m.send(Ev2::Cast(TCast::Disarm)).unwrap(); // Armed -> Idle (enter)
// enters = 1 (start) + 4 transitions = 5.
// enters = 1 (start) + 4 transitions = 5.
*got2.lock().unwrap() = m.call(|r| Ev2::Call(TCall::Enters(r))).unwrap();
});
assert_eq!(*got.lock().unwrap(), 5, "one enter on start, one per real transition");
assert_eq!(
*got.lock().unwrap(),
5,
"one enter on start, one per real transition"
);
}
// `enter` fires once on start and once per *real* transition; a stay (a call
@@ -188,7 +244,15 @@ fn enter_on_start_and_each_transition_but_not_stay() {
let got = Arc::new(Mutex::new((0u32, 0u32, 0u32)));
let got2 = got.clone();
run(move || {
let m = TimerSm::start(T::Idle, TData { enters: 0, st_fires: 0, named_fires: 0, st_window: 10_000 }); // enter -> 1
let m = TimerSm::start(
T::Idle,
TData {
enters: 0,
st_fires: 0,
named_fires: 0,
st_window: 10_000,
},
); // enter -> 1
let after_start = m.call(|r| Ev2::Call(TCall::Enters(r))).unwrap();
// A stay (a counter read returns `prev`) must not bump enters.
let _ = m.call(|r| Ev2::Call(TCall::StFires(r))).unwrap();
@@ -208,7 +272,15 @@ fn call_to_panicking_handler_is_down() {
let got = Arc::new(Mutex::new(None::<Result<u32, CallError>>));
let got2 = got.clone();
run(move || {
let m = TimerSm::start(T::Idle, TData { enters: 0, st_fires: 0, named_fires: 0, st_window: 10_000 });
let m = TimerSm::start(
T::Idle,
TData {
enters: 0,
st_fires: 0,
named_fires: 0,
st_window: 10_000,
},
);
let r = m.call(|rep| Ev2::Call(TCall::Boom(rep)));
*got2.lock().unwrap() = Some(r);
});
@@ -299,7 +371,11 @@ fn postponed_call_answered_after_transition() {
smarm::sleep(Duration::from_millis(20)); // let the child wake with its reply
*g2.lock().unwrap() = *taken.lock().unwrap();
});
assert_eq!(*got.lock().unwrap(), Some(42), "postponed call answered by the Filled state");
assert_eq!(
*got.lock().unwrap(),
Some(42),
"postponed call answered by the Filled state"
);
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+40 -9
View File
@@ -56,7 +56,11 @@ fn snapshot_lists_actors_with_parent_edge() {
// The root itself is on-CPU (it's running this code) and rooted under
// the forest sentinel.
let root = snap.actors.iter().find(|a| a.pid == me).expect("root present");
let root = snap
.actors
.iter()
.find(|a| a.pid == me)
.expect("root present");
assert_eq!(root.state, ActorState::Running);
assert_eq!(root.supervisor, smarm::Pid::new(u32::MAX, u32::MAX));
@@ -200,7 +204,11 @@ fn tree_places_child_under_its_spawner() {
// The root is parented at the forest sentinel, so it's a genuine root,
// and the worker it spawned hangs beneath it.
let root = t.roots.iter().find(|n| n.info.pid == me).expect("root in forest");
let root = t
.roots
.iter()
.find(|n| n.info.pid == me)
.expect("root in forest");
assert!(!root.orphaned);
assert!(
root.children.iter().any(|c| c.info.pid == h.pid()),
@@ -258,14 +266,25 @@ fn tree_from_nests_children_and_reroots_orphans() {
let t = tree_from(snap);
assert_eq!(t.roots.len(), 2);
let root = t.roots.iter().find(|n| n.info.pid == root_pid).expect("root present");
let root = t
.roots
.iter()
.find(|n| n.info.pid == root_pid)
.expect("root present");
assert!(!root.orphaned);
assert_eq!(root.children.len(), 1);
assert_eq!(root.children[0].info.pid, child);
assert!(!root.children[0].orphaned);
let o = t.roots.iter().find(|n| n.info.pid == orphan).expect("orphan re-rooted");
assert!(o.orphaned, "an actor whose parent is absent must be flagged orphaned");
let o = t
.roots
.iter()
.find(|n| n.info.pid == orphan)
.expect("orphan re-rooted");
assert!(
o.orphaned,
"an actor whose parent is absent must be flagged orphaned"
);
assert!(o.children.is_empty());
}
@@ -398,7 +417,11 @@ fn stack_info_reports_defaults_and_sampled_depth() {
let info = spin_until(h.pid(), |a| a.state == ActorState::Parked);
let s = info.stack;
assert_eq!(s.reserve, 64 * 1024, "default reserve");
assert_eq!(s.guard, 1024 * 1024, "default guard (kernel stack_guard_gap convention)");
assert_eq!(
s.guard,
1024 * 1024,
"default guard (kernel stack_guard_gap convention)"
);
assert!(
s.depth_high_water >= 8 * 4096,
"hwm sampled at the deep yield: expected ≥ 32 KiB, got {}",
@@ -432,7 +455,10 @@ fn stack_info_shrink_counters_are_live() {
let spike = 768 * 4096;
assert!(spike > SHRINK_THRESHOLD);
let worker = spawn_with(
SpawnOpts { stack_reserve: Some(8 * 1024 * 1024), ..SpawnOpts::default() },
SpawnOpts {
stack_reserve: Some(8 * 1024 * 1024),
..SpawnOpts::default()
},
move || {
std::hint::black_box(burn_stack_yielding(768));
for _ in 0..(SHRINK_COOLDOWN + 8) {
@@ -457,9 +483,14 @@ fn stack_info_shrink_counters_are_live() {
spin_until(wpid, |a| a.state == ActorState::Parked);
park_tx.send(()).unwrap();
}
let info = spin_until(wpid, |a| a.state == ActorState::Parked && a.stack.shrinks >= 1);
let info = spin_until(wpid, |a| {
a.state == ActorState::Parked && a.stack.shrinks >= 1
});
let s = info.stack;
assert!(s.shrinks >= 1, "cooldown was crossed with a spike above threshold");
assert!(
s.shrinks >= 1,
"cooldown was crossed with a spike above threshold"
);
assert!(
s.parks_since_shrink < SHRINK_COOLDOWN,
"counter must reset at shrink: {}",
+13 -3
View File
@@ -56,8 +56,16 @@ fn other_actors_run_while_block_on_io_is_in_flight() {
let pos_2 = v.iter().position(|&x| x == 2).unwrap();
let pos_3 = v.iter().position(|&x| x == 3).unwrap();
let pos_4 = v.iter().position(|&x| x == 4).unwrap();
assert!(pos_2 < pos_4, "B's first step ran after A resumed: {:?}", *v);
assert!(pos_3 < pos_4, "B's second step ran after A resumed: {:?}", *v);
assert!(
pos_2 < pos_4,
"B's first step ran after A resumed: {:?}",
*v
);
assert!(
pos_3 < pos_4,
"B's second step ran after A resumed: {:?}",
*v
);
}
#[test]
@@ -76,7 +84,9 @@ fn many_concurrent_block_on_io_calls_all_complete() {
cc.fetch_add(n, Ordering::SeqCst);
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
});
assert_eq!(counter.load(Ordering::SeqCst), 10);
}
+11 -4
View File
@@ -144,8 +144,7 @@ fn write_sugar_sends_bytes_to_pipe() {
// Pipe is empty + has buffer space, so this returns immediately
// after wait_writable wakes (which happens fast because the
// kernel marks an empty pipe as immediately writable).
let n = smarm::scheduler::write(p_writer.write, b"smarm")
.expect("write failed");
let n = smarm::scheduler::write(p_writer.write, b"smarm").expect("write failed");
assert_eq!(n, 5);
c.fetch_add(1, Ordering::SeqCst);
});
@@ -209,10 +208,18 @@ fn other_actors_run_while_one_is_parked_on_wait_readable() {
let pos_lit_a = v.iter().position(|&c| c == b'a').unwrap();
let big_b_count = v.iter().filter(|&&c| c == b'B').count();
assert_eq!(big_b_count, 3, "B should have made 3 steps: {:?}", *v);
assert!(pos_big_a < pos_lit_a, "A pre-park before A post-park: {:?}", *v);
assert!(
pos_big_a < pos_lit_a,
"A pre-park before A post-park: {:?}",
*v
);
// At least the last B step should be before A resumes.
let last_big_b = v.iter().rposition(|&c| c == b'B').unwrap();
assert!(last_big_b < pos_lit_a, "B should finish before A resumes: {:?}", *v);
assert!(
last_big_b < pos_lit_a,
"B should finish before A resumes: {:?}",
*v
);
}
// ---------------------------------------------------------------------------
+10 -4
View File
@@ -57,7 +57,10 @@ fn linked_pair_one_panics_other_is_stopped() {
panic!("boom");
});
let dn = down_b.rx.recv().expect("monitor channel closed before Down");
let dn = down_b
.rx
.recv()
.expect("monitor channel closed before Down");
assert_eq!(dn.pid, b, "Down reported the wrong pid");
if matches!(dn.reason, DownReason::Stopped) {
s.store(true, Ordering::SeqCst);
@@ -117,7 +120,7 @@ fn normal_exit_does_not_propagate() {
let a = ha.pid();
link(a);
yield_now(); // let A run to completion and finalize
// A exited normally: nothing should have landed on the inbox.
// A exited normally: nothing should have landed on the inbox.
if let Ok(None) = inbox.try_recv() {
e.store(true, Ordering::SeqCst);
}
@@ -152,7 +155,10 @@ fn link_to_dead_pid_stops_a_nontrapping_caller() {
});
let b = hb.pid();
let down_b = monitor(b);
let dn = down_b.rx.recv().expect("monitor channel closed before Down");
let dn = down_b
.rx
.recv()
.expect("monitor channel closed before Down");
if matches!(dn.reason, DownReason::Stopped) {
s.store(true, Ordering::SeqCst);
}
@@ -208,7 +214,7 @@ fn unlink_prevents_propagation() {
panic!("boom"); // abnormal, but the link is gone
});
yield_now(); // let A link, unlink, and panic
// Unlinked before death → no ExitSignal should have arrived.
// Unlinked before death → no ExitSignal should have arrived.
if let Ok(None) = inbox.try_recv() {
sv.store(true, Ordering::SeqCst);
}
+27 -6
View File
@@ -67,7 +67,10 @@ fn monitor_already_dead_target_is_noproc() {
// and its generation bumped, so `pid` is now stale.
h.join().unwrap();
let down = monitor(pid);
let d = down.rx.recv().expect("NoProc Down should be delivered immediately");
let d = down
.rx
.recv()
.expect("NoProc Down should be delivered immediately");
assert_eq!(d.pid, pid);
if matches!(d.reason, DownReason::NoProc) {
o.store(true, Ordering::SeqCst);
@@ -91,7 +94,11 @@ fn multiple_monitors_all_notified() {
}
}
});
assert_eq!(count.load(Ordering::SeqCst), 3, "every monitor should see the Down");
assert_eq!(
count.load(Ordering::SeqCst),
3,
"every monitor should see the Down"
);
}
#[test]
@@ -103,8 +110,15 @@ fn demonitor_stops_delivery() {
let h = spawn(|| {});
let pid = h.pid();
let m = monitor(pid);
assert_eq!(demonitor(&m), Some(m.id), "live registration should be removed");
assert!(m.rx.recv().is_err(), "no Down should arrive after demonitor");
assert_eq!(
demonitor(&m),
Some(m.id),
"live registration should be removed"
);
assert!(
m.rx.recv().is_err(),
"no Down should arrive after demonitor"
);
let _ = h.join();
});
}
@@ -122,7 +136,10 @@ fn demonitor_one_of_many() {
let _ = h.join();
assert!(matches!(ms[0].rx.recv().unwrap().reason, DownReason::Exit));
assert!(matches!(ms[2].rx.recv().unwrap().reason, DownReason::Exit));
assert!(ms[1].rx.recv().is_err(), "demonitored channel should be closed");
assert!(
ms[1].rx.recv().is_err(),
"demonitored channel should be closed"
);
});
}
@@ -136,7 +153,11 @@ fn demonitor_after_fire_is_none() {
let m = monitor(pid);
let d = m.rx.recv().expect("Down before close");
assert!(matches!(d.reason, DownReason::Exit));
assert_eq!(demonitor(&m), None, "already-fired monitor has nothing to remove");
assert_eq!(
demonitor(&m),
None,
"already-fired monitor has nothing to remove"
);
let _ = h.join();
});
}
+16 -4
View File
@@ -3,9 +3,9 @@
//! needs to be able to park.
use smarm::{run, spawn, yield_now, LockTimeout, Mutex};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use std::sync::Mutex as StdMutex;
use std::sync::atomic::{AtomicU32, Ordering};
use std::time::{Duration, Instant};
// ---------------------------------------------------------------------------
@@ -111,8 +111,16 @@ fn contended_lock_parks_until_holder_releases() {
let pos_b_locked = v.iter().position(|s| *s == "B_locked").unwrap();
assert!(pos_a_locked < pos_b_try, "log: {:?}", *v);
assert!(pos_b_try < pos_a_dropped, "B should attempt before A drops: {:?}", *v);
assert!(pos_a_dropped < pos_b_locked, "B should lock only after A drops: {:?}", *v);
assert!(
pos_b_try < pos_a_dropped,
"B should attempt before A drops: {:?}",
*v
);
assert!(
pos_a_dropped < pos_b_locked,
"B should lock only after A drops: {:?}",
*v
);
}
// ---------------------------------------------------------------------------
@@ -209,7 +217,11 @@ fn waiters_are_granted_the_lock_in_fifo_order() {
});
let v = order.lock().unwrap().clone();
assert_eq!(v, vec![1, 2, 3, 4], "waiters should acquire in arrival order");
assert_eq!(
v,
vec![1, 2, 3, 4],
"waiters should acquire in arrival order"
);
}
// ---------------------------------------------------------------------------
+5 -3
View File
@@ -77,8 +77,7 @@ fn observer_reports_none_for_a_forged_pid() {
// An index that is not in the slab at all — the verb relays the
// primitive's `None` faithfully.
let forged = smarm::Pid::new(u32::MAX - 1, 0);
let ObserverReply::ActorInfo(none) =
obs.call(ObserverRequest::ActorInfo(forged)).unwrap()
let ObserverReply::ActorInfo(none) = obs.call(ObserverRequest::ActorInfo(forged)).unwrap()
else {
panic!("ActorInfo verb must reply ActorInfo");
};
@@ -113,7 +112,10 @@ fn observer_sees_a_parked_actor_as_parked() {
}
smarm::yield_now();
}
assert!(parked, "observer should eventually report the worker as Parked");
assert!(
parked,
"observer should eventually report the worker as Parked"
);
gate_tx.send(()).unwrap();
worker.join().unwrap();
+13 -3
View File
@@ -44,7 +44,10 @@ fn a_dead_actor_vanishes_from_every_group_it_joined() {
// Drain-on-contact: touching g1 detects the death and sweeps the pid
// out of every group (g2 included), not just g1.
assert!(members("g1").is_empty(), "evicted from the touched group");
assert!(members("g2").is_empty(), "and swept from the untouched group");
assert!(
members("g2").is_empty(),
"and swept from the untouched group"
);
assert_eq!(pick("g1"), None);
});
}
@@ -83,7 +86,11 @@ fn live_members_survive_a_peers_death() {
tx_a.send(()).unwrap();
a.join().unwrap();
assert_eq!(members("svc"), vec![b.pid()], "only the dead peer is reaped");
assert_eq!(
members("svc"),
vec![b.pid()],
"only the dead peer is reaped"
);
assert_eq!(pick("svc"), Some(b.pid()));
tx_b.send(()).unwrap();
@@ -125,7 +132,10 @@ fn joining_an_already_dead_pid_is_evicted_on_next_contact() {
// monitor() on a gone pid queues a NoProc Down immediately, so the
// membership is reaped the next time the group is touched.
join("late", pid);
assert!(members("late").is_empty(), "dead-at-join member is reaped on read");
assert!(
members("late").is_empty(),
"dead-at-join member is reaped on read"
);
assert_eq!(pick("late"), None);
});
}
+10 -2
View File
@@ -41,7 +41,11 @@ fn stop_storm_does_not_poison_runtime() {
}
c.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(completed.load(Ordering::SeqCst), 1, "root completed cleanly");
assert_eq!(
completed.load(Ordering::SeqCst),
1,
"root completed cleanly"
);
}
/// The sharper repro: a stop-flagged actor whose *next allocation* is the
@@ -85,5 +89,9 @@ fn self_stop_during_spawn_does_not_poison_shared_mutex() {
}
c.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(completed.load(Ordering::SeqCst), 1, "root completed cleanly");
assert_eq!(
completed.load(Ordering::SeqCst),
1,
"root completed cleanly"
);
}
+15 -4
View File
@@ -43,10 +43,21 @@ fn check_yields_when_timeslice_expired() {
let pos_big_b = v.iter().position(|&c| c == b'B').unwrap();
let pos_lit_a = v.iter().position(|&c| c == b'a').unwrap();
let pos_lit_b = v.iter().position(|&c| c == b'b').unwrap();
assert!(pos_big_a < pos_lit_a, "A's tail ran before B's head: {:?}", *v);
assert!(pos_big_b < pos_lit_b, "B's tail ran before A's head: {:?}", *v);
assert!(pos_big_a.max(pos_big_b) < pos_lit_a.min(pos_lit_b),
"preemption didn't interleave: {:?}", *v);
assert!(
pos_big_a < pos_lit_a,
"A's tail ran before B's head: {:?}",
*v
);
assert!(
pos_big_b < pos_lit_b,
"B's tail ran before A's head: {:?}",
*v
);
assert!(
pos_big_a.max(pos_big_b) < pos_lit_a.min(pos_lit_b),
"preemption didn't interleave: {:?}",
*v
);
}
#[test]
+13 -4
View File
@@ -65,7 +65,10 @@ fn name_held_by_live_actor_is_taken() {
ready_rx.recv().unwrap();
// Root tries to claim a live actor's name for itself -> NameTaken.
let (tx_b, _rx_b) = channel::<u64>();
assert_eq!(register(SVC, tx_b), Err(RegisterError::NameTaken { holder: a.pid() }));
assert_eq!(
register(SVC, tx_b),
Err(RegisterError::NameTaken { holder: a.pid() })
);
send(SVC, 0).unwrap(); // release a (delivers to the holder, a)
a.join().unwrap();
});
@@ -105,7 +108,10 @@ fn dead_holder_is_pruned_and_name_taken_over() {
fn send_errors_unresolved_and_no_channel() {
run(|| {
// No actor at all.
assert!(matches!(send(Name::<u64>::new("ghost"), 1u64), Err(SendError::Unresolved(_))));
assert!(matches!(
send(Name::<u64>::new("ghost"), 1u64),
Err(SendError::Unresolved(_))
));
let (ready_tx, ready_rx) = channel::<()>();
let (tx, rx) = channel::<u64>();
@@ -227,8 +233,11 @@ fn send_dyn_delivers_and_reports_wrong_type() {
ready_rx.recv().unwrap();
let p = h.pid(); // a bare Pid<Erased>, as if recovered off a Down
send_dyn::<u64>(p, 3u64).unwrap(); // right type: delivered
// Live actor, but it has no channel for &str — the genuinely-fallible case.
assert!(matches!(send_dyn::<&'static str>(p, "nope"), Err(SendError::NoChannel(_))));
// Live actor, but it has no channel for &str — the genuinely-fallible case.
assert!(matches!(
send_dyn::<&'static str>(p, "nope"),
Err(SendError::NoChannel(_))
));
done_tx.send(()).unwrap();
h.join().unwrap();
});
+77 -26
View File
@@ -14,10 +14,17 @@
//! - No slot leaks under high spawn/join churn
//! - Panic on one scheduler thread doesn't kill others
use smarm::{channel, runtime::{Config, Runtime}, spawn, yield_now, JoinHandle};
use std::sync::{atomic::{AtomicBool, AtomicU64, Ordering}, Arc};
use std::time::Duration;
use smarm::{
channel,
runtime::{Config, Runtime},
spawn, yield_now, JoinHandle,
};
use std::collections::HashSet;
use std::sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc,
};
use std::time::Duration;
// ---------------------------------------------------------------------------
// Helpers
@@ -29,7 +36,9 @@ fn rt(n: usize) -> Runtime {
}
/// Convenient single-threaded runtime (regression guard).
fn rt1() -> Runtime { rt(1) }
fn rt1() -> Runtime {
rt(1)
}
/// Multi-threaded runtime using all available parallelism.
fn rt_par() -> Runtime {
@@ -79,7 +88,9 @@ fn config_min_1_max_1_is_single_threaded() {
fn runtime_run_executes_closure() {
let flag = Arc::new(AtomicBool::new(false));
let f = flag.clone();
rt(1).run(move || { f.store(true, Ordering::SeqCst); });
rt(1).run(move || {
f.store(true, Ordering::SeqCst);
});
assert!(flag.load(Ordering::SeqCst));
}
@@ -111,8 +122,12 @@ fn runtime_can_be_used_multiple_times_sequentially() {
let b = Arc::new(AtomicU64::new(0));
let ac = a.clone();
let bc = b.clone();
r.run(move || { ac.fetch_add(1, Ordering::SeqCst); });
r.run(move || { bc.fetch_add(1, Ordering::SeqCst); });
r.run(move || {
ac.fetch_add(1, Ordering::SeqCst);
});
r.run(move || {
bc.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(a.load(Ordering::SeqCst), 1);
assert_eq!(b.load(Ordering::SeqCst), 1);
}
@@ -126,7 +141,9 @@ fn exact_1_spawn_join_works() {
let v = Arc::new(AtomicU64::new(0));
let vc = v.clone();
rt1().run(move || {
let h = spawn(move || { vc.store(42, Ordering::SeqCst); });
let h = spawn(move || {
vc.store(42, Ordering::SeqCst);
});
h.join().unwrap();
});
assert_eq!(v.load(Ordering::SeqCst), 42);
@@ -155,7 +172,9 @@ fn exact_1_panic_captured() {
let s = saw_err.clone();
rt1().run(move || {
let h = spawn(|| panic!("oops"));
if h.join().is_err() { s.store(true, Ordering::SeqCst); }
if h.join().is_err() {
s.store(true, Ordering::SeqCst);
}
});
assert!(saw_err.load(Ordering::SeqCst));
}
@@ -176,7 +195,9 @@ fn multi_thread_all_actors_complete() {
cc.fetch_add(1, Ordering::SeqCst);
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
});
assert_eq!(counter.load(Ordering::SeqCst), 100);
}
@@ -221,7 +242,9 @@ fn multi_thread_many_channels_no_lost_wakeups() {
tx.send(1).unwrap();
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
});
assert_eq!(count.load(Ordering::SeqCst), PAIRS as u64);
}
@@ -247,7 +270,9 @@ fn multi_thread_mutex_contention_no_deadlock() {
}
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
let g = m.lock_timeout(Duration::from_secs(1)).unwrap();
t.store(*g, Ordering::SeqCst);
});
@@ -262,7 +287,9 @@ fn multi_thread_join_across_threads() {
rt_par().run(move || {
let h = spawn(move || {
// Do some work to make scheduling interesting.
for _ in 0..10 { yield_now(); }
for _ in 0..10 {
yield_now();
}
vc.store(1, Ordering::SeqCst);
});
h.join().unwrap();
@@ -279,8 +306,7 @@ fn multi_thread_join_across_threads() {
#[test]
fn actors_run_on_multiple_os_threads() {
let thread_ids: Arc<smarm::Mutex<HashSet<u64>>> =
Arc::new(smarm::Mutex::new(HashSet::new()));
let thread_ids: Arc<smarm::Mutex<HashSet<u64>>> = Arc::new(smarm::Mutex::new(HashSet::new()));
rt_par().run({
let ids = thread_ids.clone();
@@ -294,11 +320,15 @@ fn actors_run_on_multiple_os_threads() {
g.insert(tid);
}));
}
for h in handles { h.join().unwrap(); }
for h in handles {
h.join().unwrap();
}
}
});
let n = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
let n = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1);
let ids = thread_ids.lock_timeout(Duration::from_secs(1)).unwrap();
// If we have >1 scheduler threads, we expect >1 OS thread IDs.
@@ -326,11 +356,17 @@ fn scheduler_stats_run_queue_len_is_observable() {
// run() completes (queue len == 0 at quiescence).
let r = rt_par();
r.run(|| {
for _ in 0..10 { spawn(|| {}); }
for _ in 0..10 {
spawn(|| {});
}
// Don't join — let them drain naturally.
});
let stats = r.stats();
assert_eq!(stats.total_run_queue_len(), 0, "queue should be empty after run()");
assert_eq!(
stats.total_run_queue_len(),
0,
"queue should be empty after run()"
);
}
#[test]
@@ -359,7 +395,9 @@ fn panic_in_actor_does_not_kill_runtime() {
}));
}
let _ = bad.join(); // expect Err
for h in good_handles { h.join().unwrap(); }
for h in good_handles {
h.join().unwrap();
}
});
assert_eq!(completed.load(Ordering::SeqCst), 10);
}
@@ -379,9 +417,11 @@ fn no_slot_leak_under_churn() {
rt_par().run(move || {
for _ in 0..500 {
let cc = c.clone();
spawn(move || { cc.fetch_add(1, Ordering::SeqCst); })
.join()
.unwrap();
spawn(move || {
cc.fetch_add(1, Ordering::SeqCst);
})
.join()
.unwrap();
}
});
assert_eq!(counter.load(Ordering::SeqCst), 500);
@@ -474,7 +514,11 @@ fn multi_thread_timer_only_no_pipe_contention() {
}
});
assert_eq!(count.load(Ordering::SeqCst), ACTORS as u64, "not all actors completed");
assert_eq!(
count.load(Ordering::SeqCst),
ACTORS as u64,
"not all actors completed"
);
let elapsed = start.elapsed();
assert!(
@@ -515,7 +559,10 @@ fn runtime_reusable_after_root_panic() {
let ran = Arc::new(AtomicBool::new(false));
let ran_t = ran.clone();
r.run(move || ran_t.store(true, Ordering::Relaxed));
assert!(ran.load(Ordering::Relaxed), "runtime unusable after root panic");
assert!(
ran.load(Ordering::Relaxed),
"runtime unusable after root panic"
);
}
// ---------------------------------------------------------------------------
@@ -528,7 +575,11 @@ fn runtime_reusable_after_root_panic() {
fn burn_stack(frames: usize) -> u64 {
let mut local = [0u8; 4096];
local[0] = frames as u8;
let below = if frames == 0 { 0 } else { burn_stack(frames - 1) };
let below = if frames == 0 {
0
} else {
burn_stack(frames - 1)
};
std::hint::black_box(&mut local);
below.wrapping_add(local[0] as u64)
}
+7 -4
View File
@@ -14,7 +14,9 @@ use std::sync::Arc;
fn root_actor_runs() {
let captured = Arc::new(AtomicI64::new(0));
let c = captured.clone();
run(move || { c.store(99, Ordering::SeqCst); });
run(move || {
c.store(99, Ordering::SeqCst);
});
assert_eq!(captured.load(Ordering::SeqCst), 99);
}
@@ -27,7 +29,9 @@ fn spawn_and_join_returns_exit() {
let captured = Arc::new(AtomicI64::new(0));
let c = captured.clone();
run(move || {
let h = spawn(move || { c.store(7, Ordering::SeqCst); });
let h = spawn(move || {
c.store(7, Ordering::SeqCst);
});
let res = h.join();
assert!(res.is_ok(), "join returned {:?}", res);
});
@@ -68,8 +72,7 @@ fn yield_now_interleaves_actors() {
#[test]
fn self_pid_is_stable_within_an_actor() {
let pid_cell: Arc<std::sync::Mutex<Option<smarm::Pid>>> =
Arc::new(std::sync::Mutex::new(None));
let pid_cell: Arc<std::sync::Mutex<Option<smarm::Pid>>> = Arc::new(std::sync::Mutex::new(None));
let p2 = pid_cell.clone();
run(move || {
let h = spawn(move || {
+14 -3
View File
@@ -19,7 +19,12 @@ fn ready_arm_returns_immediately_without_parking() {
txa.send(42).unwrap();
let i = select(&[&rxb, &rxa]);
assert_eq!(i, 1);
out2.store(rxa.try_recv().unwrap().expect("ready arm must hold a message"), Ordering::SeqCst);
out2.store(
rxa.try_recv()
.unwrap()
.expect("ready arm must hold a message"),
Ordering::SeqCst,
);
});
assert_eq!(out.load(Ordering::SeqCst), 42);
}
@@ -276,7 +281,10 @@ fn select_timeout_ready_arm_wins_without_arming_a_timer() {
let (txa, rxa) = channel::<i64>();
let (_keep_b, rxb) = channel::<i64>();
txa.send(5).unwrap();
assert_eq!(select_timeout(&[&rxb, &rxa], Duration::from_millis(500)), Some(1));
assert_eq!(
select_timeout(&[&rxb, &rxa], Duration::from_millis(500)),
Some(1)
);
assert_eq!(rxa.try_recv().unwrap(), Some(5));
});
}
@@ -342,7 +350,10 @@ fn select_timeout_closed_arm_is_ready_not_a_timeout() {
let (_keep_a, rxa) = channel::<i64>();
let (txb, rxb) = channel::<i64>();
drop(txb);
assert_eq!(select_timeout(&[&rxa, &rxb], Duration::from_millis(200)), Some(1));
assert_eq!(
select_timeout(&[&rxa, &rxb], Duration::from_millis(200)),
Some(1)
);
assert!(rxb.try_recv().is_err());
});
}
+34 -11
View File
@@ -6,9 +6,7 @@
//! big reserve behaviorally takes effect (deep recursion completes).
use smarm::runtime::{Config, DEFAULT_STACK_GUARD, DEFAULT_STACK_RESERVE};
use smarm::{
self_pid, spawn, spawn_under_with, spawn_with, GenServerBuilder, SpawnOpts,
};
use smarm::{self_pid, spawn, spawn_under_with, spawn_with, GenServerBuilder, SpawnOpts};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
@@ -45,7 +43,10 @@ fn spawn_with_overrides_reserve_and_guard() {
#[test]
fn spawn_with_partial_override_keeps_config_default_for_the_rest() {
rt1().run(|| {
let opts = SpawnOpts { stack_reserve: Some(1024 * 1024), ..SpawnOpts::default() };
let opts = SpawnOpts {
stack_reserve: Some(1024 * 1024),
..SpawnOpts::default()
};
let h = spawn_with(opts, || {
let shape = smarm::introspect::stack_shape(self_pid()).unwrap();
assert_eq!(shape, (1024 * 1024, DEFAULT_STACK_GUARD));
@@ -57,7 +58,10 @@ fn spawn_with_partial_override_keeps_config_default_for_the_rest() {
#[test]
fn spawn_with_rounds_to_pages() {
rt1().run(|| {
let opts = SpawnOpts { stack_reserve: Some(64 * 1024 + 1), guard_size: Some(4097) };
let opts = SpawnOpts {
stack_reserve: Some(64 * 1024 + 1),
guard_size: Some(4097),
};
let h = spawn_with(opts, || {
let (reserve, guard) = smarm::introspect::stack_shape(self_pid()).unwrap();
assert_eq!(reserve % 4096, 0);
@@ -73,7 +77,10 @@ fn spawn_with_rounds_to_pages() {
fn spawn_under_with_takes_opts() {
rt1().run(|| {
let me = self_pid();
let opts = SpawnOpts { stack_reserve: Some(128 * 1024), ..SpawnOpts::default() };
let opts = SpawnOpts {
stack_reserve: Some(128 * 1024),
..SpawnOpts::default()
};
let h = spawn_under_with(me, opts, || {
let (reserve, _) = smarm::introspect::stack_shape(self_pid()).unwrap();
assert_eq!(reserve, 128 * 1024);
@@ -90,7 +97,10 @@ fn spawn_under_with_takes_opts() {
fn custom_stack_never_enters_the_pool() {
rt1().run(|| {
spawn_with(
SpawnOpts { stack_reserve: Some(512 * 1024), guard_size: Some(128 * 1024) },
SpawnOpts {
stack_reserve: Some(512 * 1024),
guard_size: Some(128 * 1024),
},
|| {},
)
.join()
@@ -122,7 +132,11 @@ fn default_stack_is_recycled() {
fn burn_stack(frames: usize) -> u64 {
let mut local = [0u8; 4096];
local[0] = frames as u8;
let below = if frames == 0 { 0 } else { burn_stack(frames - 1) };
let below = if frames == 0 {
0
} else {
burn_stack(frames - 1)
};
std::hint::black_box(&mut local);
below.wrapping_add(local[0] as u64)
}
@@ -134,7 +148,10 @@ fn big_reserve_behaviorally_takes_effect() {
let done = Arc::new(AtomicBool::new(false));
let done2 = done.clone();
spawn_with(
SpawnOpts { stack_reserve: Some(8 * 1024 * 1024), ..SpawnOpts::default() },
SpawnOpts {
stack_reserve: Some(8 * 1024 * 1024),
..SpawnOpts::default()
},
move || {
std::hint::black_box(burn_stack(256));
done2.store(true, Ordering::SeqCst);
@@ -167,7 +184,10 @@ impl smarm::GenServer for Echo {
fn gen_server_builder_stack_opts() {
rt1().run(|| {
let server = GenServerBuilder::new(Echo)
.stack_opts(SpawnOpts { stack_reserve: Some(256 * 1024), ..SpawnOpts::default() })
.stack_opts(SpawnOpts {
stack_reserve: Some(256 * 1024),
..SpawnOpts::default()
})
.start();
let (reserve, guard) = server.call(()).unwrap();
assert_eq!(reserve, 256 * 1024);
@@ -200,7 +220,10 @@ impl smarm::Machine for Probe {
fn gen_statem_spawn_with_stack_opts() {
rt1().run(|| {
let m = smarm::gen_statem::spawn_with(
SpawnOpts { stack_reserve: Some(256 * 1024), ..SpawnOpts::default() },
SpawnOpts {
stack_reserve: Some(256 * 1024),
..SpawnOpts::default()
},
Probe,
);
let (tx, rx) = smarm::channel::channel();
+30 -6
View File
@@ -78,14 +78,18 @@ fn run_as_child_if_requested() {
Ok("wide_guard_top") => {
// One byte below the usable region, 64 KiB guard: must fault.
let s = Stack::new(64 * 1024, 64 * 1024).unwrap();
unsafe { s.usable_base().sub(1).write_volatile(0xAB); }
unsafe {
s.usable_base().sub(1).write_volatile(0xAB);
}
std::process::exit(0);
}
Ok("wide_guard_bottom") => {
// The very bottom page of a 64 KiB guard: an unprobed C-style
// leap over a small guard lands here — must still fault.
let s = Stack::new(64 * 1024, 64 * 1024).unwrap();
unsafe { s.usable_base().sub(64 * 1024).write_volatile(0xAB); }
unsafe {
s.usable_base().sub(64 * 1024).write_volatile(0xAB);
}
std::process::exit(0);
}
Ok("stack_overflow") => {
@@ -120,7 +124,12 @@ fn guard_page_causes_sigsegv() {
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status);
assert_eq!(
status.signal(),
Some(11),
"expected SIGSEGV, got: {:?}",
status
);
}
}
@@ -131,7 +140,12 @@ fn stack_overflow_causes_sigsegv() {
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status);
assert_eq!(
status.signal(),
Some(11),
"expected SIGSEGV, got: {:?}",
status
);
}
}
@@ -175,7 +189,12 @@ fn wide_guard_faults_at_top() {
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status);
assert_eq!(
status.signal(),
Some(11),
"expected SIGSEGV, got: {:?}",
status
);
}
}
@@ -186,6 +205,11 @@ fn wide_guard_faults_at_bottom() {
#[cfg(unix)]
{
use std::os::unix::process::ExitStatusExt;
assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status);
assert_eq!(
status.signal(),
Some(11),
"expected SIGSEGV, got: {:?}",
status
);
}
}
+16 -4
View File
@@ -42,9 +42,15 @@ fn run_as_child_if_requested() {
let opts = match mode.as_str() {
"rust_overflow" | "ffi_tier1" => SpawnOpts::default(),
// Small guard: the canary's 96 KiB displacement clears it.
"ffi_tier2" => SpawnOpts { guard_size: Some(4096), ..SpawnOpts::default() },
"ffi_tier2" => SpawnOpts {
guard_size: Some(4096),
..SpawnOpts::default()
},
// Enough reserve: the same frame simply fits.
"ffi_clean" => SpawnOpts { stack_reserve: Some(256 * 1024), ..SpawnOpts::default() },
"ffi_clean" => SpawnOpts {
stack_reserve: Some(256 * 1024),
..SpawnOpts::default()
},
other => panic!("unknown subtest {other}"),
};
let is_rust = mode == "rust_overflow";
@@ -92,8 +98,14 @@ fn rust_overflow_dies_with_tier1_message() {
err.contains("overflowed its stack") && err.contains("in the guard region"),
"missing tier-1 diagnostic; stderr:\n{err}"
);
assert!(err.contains("reserve=65536"), "wrong reserve in message:\n{err}");
assert!(err.contains("guard=1048576"), "wrong guard in message:\n{err}");
assert!(
err.contains("reserve=65536"),
"wrong reserve in message:\n{err}"
);
assert!(
err.contains("guard=1048576"),
"wrong guard in message:\n{err}"
);
}
#[test]
+16 -6
View File
@@ -24,7 +24,11 @@ const RESERVE: usize = 4 * 1024 * 1024;
fn burn_stack(frames: usize) -> u64 {
let mut local = [0u8; 4096];
local[0] = frames as u8;
let below = if frames == 0 { 0 } else { burn_stack(frames - 1) };
let below = if frames == 0 {
0
} else {
burn_stack(frames - 1)
};
std::hint::black_box(&mut local);
below.wrapping_add(local[0] as u64)
}
@@ -33,10 +37,13 @@ fn burn_stack(frames: usize) -> u64 {
fn resident_pages(lo: usize, len: usize) -> usize {
let page = 4096;
let mut vec = vec![0u8; len / page];
let ret = unsafe {
libc::mincore(lo as *mut libc::c_void, len, vec.as_mut_ptr())
};
assert_eq!(ret, 0, "mincore failed: {}", std::io::Error::last_os_error());
let ret = unsafe { libc::mincore(lo as *mut libc::c_void, len, vec.as_mut_ptr()) };
assert_eq!(
ret,
0,
"mincore failed: {}",
std::io::Error::last_os_error()
);
vec.iter().filter(|&&b| b & 1 != 0).count()
}
@@ -118,6 +125,9 @@ fn recycle_zaps_dead_stack_down_to_retain() {
[{usable_base:#x}, +{zap_len:#x})"
);
// Pooled, not munmapped: the mapping must still be there.
assert!(vma_exists(usable_base), "default-shaped stack was unmapped instead of pooled");
assert!(
vma_exists(usable_base),
"default-shaped stack was unmapped instead of pooled"
);
});
}
+12 -3
View File
@@ -66,7 +66,10 @@ fn spike_then_parks_marks_lazyfree_and_keeps_live_data() {
assert!(spike > SHRINK_THRESHOLD);
let worker = spawn_with(
SpawnOpts { stack_reserve: Some(8 * 1024 * 1024), ..SpawnOpts::default() },
SpawnOpts {
stack_reserve: Some(8 * 1024 * 1024),
..SpawnOpts::default()
},
move || {
// Live data that must survive the shrink, and an anchor
// address inside the stack for the smaps scan.
@@ -111,7 +114,11 @@ fn spike_then_parks_marks_lazyfree_and_keeps_live_data() {
"expected ≥ 2 MiB LazyFree in the stack range, got {} bytes",
lazy
);
assert_eq!(checksum, 64 * 0xA5u64, "live stack data corrupted by shrink");
assert_eq!(
checksum,
64 * 0xA5u64,
"live stack data corrupted by shrink"
);
worker.join().unwrap();
});
}
@@ -131,7 +138,9 @@ fn shallow_actor_never_shrinks() {
for _ in 0..(SHRINK_COOLDOWN + 8) {
park_rx.recv().unwrap();
}
done_tx.send(lazy_free_bytes_in(anchor - 64 * 1024, anchor + 4096)).unwrap();
done_tx
.send(lazy_free_bytes_in(anchor - 64 * 1024, anchor + 4096))
.unwrap();
});
let wpid = worker.pid();
+8 -6
View File
@@ -18,8 +18,8 @@
//! registry entry guarantees for every named server.
use smarm::{
call, channel, init, request_stop, spawn, Config, GenServer, GenServerBuilder, GenServerName,
CallError, Receiver, RecvTimeoutError,
call, channel, init, request_stop, spawn, CallError, Config, GenServer, GenServerBuilder,
GenServerName, Receiver, RecvTimeoutError,
};
use std::sync::{Arc, Mutex};
use std::time::Duration;
@@ -73,10 +73,12 @@ fn named_server_request_stop_releases_queued_caller_with_server_down() {
let (res_tx, res_rx) = channel::<Result<(), CallError>>();
// 1. Start the named server and keep its ref alive.
let server = GenServerBuilder::new(Blocker { gate: Some(gate_rx) })
.named(BLOCKER)
.start()
.expect("name should be free");
let server = GenServerBuilder::new(Blocker {
gate: Some(gate_rx),
})
.named(BLOCKER)
.start()
.expect("name should be free");
let spid = server.pid();
// 2. Send the cast and let the server dequeue it and park on the gate.
+16 -8
View File
@@ -10,7 +10,11 @@
//! out rather than produce a false pass — run with `cargo test -- --timeout`
//! or under a CI timeout.
use smarm::{channel, runtime::{Config, Runtime}, spawn, yield_now, JoinHandle};
use smarm::{
channel,
runtime::{Config, Runtime},
spawn, yield_now, JoinHandle,
};
use std::sync::{
atomic::{AtomicU64, AtomicUsize, Ordering},
Arc,
@@ -199,7 +203,9 @@ fn thundering_herd_all_wake() {
}
// Let all receivers park before we send.
for _ in 0..4 { yield_now(); }
for _ in 0..4 {
yield_now();
}
// Coordinator blasts all channels.
handles.push(spawn(move || {
@@ -240,8 +246,7 @@ fn concurrent_spawn_join_churn() {
for _ in 0..PARENTS {
let tc = t.clone();
parent_handles.push(spawn(move || {
let mut child_handles: Vec<JoinHandle> =
Vec::with_capacity(CHILDREN_PER_PARENT);
let mut child_handles: Vec<JoinHandle> = Vec::with_capacity(CHILDREN_PER_PARENT);
for _ in 0..CHILDREN_PER_PARENT {
let tcc = tc.clone();
@@ -292,7 +297,9 @@ fn join_race_child_finishes_first() {
}
// Yield enough to let children run to completion before we join.
for _ in 0..8 { yield_now(); }
for _ in 0..8 {
yield_now();
}
for h in handles {
// If child already finished, join must return immediately with Ok.
@@ -374,8 +381,7 @@ fn panic_storm_does_not_corrupt_scheduler() {
fn pid_generation_increments_on_reuse() {
use smarm::self_pid;
let pids: Arc<smarm::Mutex<Vec<smarm::Pid>>> =
Arc::new(smarm::Mutex::new(Vec::new()));
let pids: Arc<smarm::Mutex<Vec<smarm::Pid>>> = Arc::new(smarm::Mutex::new(Vec::new()));
let p = pids.clone();
rt(1).run(move || {
@@ -392,7 +398,9 @@ fn pid_generation_increments_on_reuse() {
}
});
let g = pids.lock_timeout(std::time::Duration::from_secs(1)).unwrap();
let g = pids
.lock_timeout(std::time::Duration::from_secs(1))
.unwrap();
// Any two PIDs that share an index must have different generations.
for i in 0..g.len() {
for j in (i + 1)..g.len() {
+10 -2
View File
@@ -51,7 +51,11 @@ fn transient_child_is_restarted_on_panic_then_settles() {
});
sup.join().unwrap();
});
assert_eq!(runs.load(Ordering::SeqCst), 3, "two restarts then a clean exit");
assert_eq!(
runs.load(Ordering::SeqCst),
3,
"two restarts then a clean exit"
);
}
#[test]
@@ -167,7 +171,11 @@ fn one_for_all_restarts_a_normally_exited_sibling() {
sup.join().unwrap();
});
assert_eq!(a.load(Ordering::SeqCst), 2, "A: crash then clean run");
assert_eq!(b.load(Ordering::SeqCst), 2, "B cycled with the group despite a clean exit");
assert_eq!(
b.load(Ordering::SeqCst),
2,
"B cycled with the group despite a clean exit"
);
}
#[test]
+2 -2
View File
@@ -17,8 +17,8 @@
//! caller's deliberate act, not a semantics change.
use smarm::{
init, mark_watchable, request_stop, resolve_name, terminal_reason, CallError, Config, DownReason, GenServer,
GenServerBuilder, GenServerName, NameResolution,
init, mark_watchable, request_stop, resolve_name, terminal_reason, CallError, Config,
DownReason, GenServer, GenServerBuilder, GenServerName, NameResolution,
};
use std::sync::{Arc, Mutex};
use std::time::Duration;
+7 -4
View File
@@ -35,7 +35,10 @@ impl PipePair {
let mut fds: [libc::c_int; 2] = [0; 2];
let r = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC | libc::O_NONBLOCK) };
assert_eq!(r, 0, "pipe2 failed");
PipePair { read: fds[0], write: fds[1] }
PipePair {
read: fds[0],
write: fds[1],
}
}
}
@@ -67,9 +70,9 @@ fn run_with_watchdog(limit: Duration, body: impl FnOnce() + Send + 'static) {
rt.run(body);
let _ = done_tx.send(());
});
done_rx
.recv_timeout(limit)
.expect("Runtime::run did not return: idle scheduler thread was never woken at termination");
done_rx.recv_timeout(limit).expect(
"Runtime::run did not return: idle scheduler thread was never woken at termination",
);
}
/// Permanent-hang variant: sibling blocked in `poll_wake(wake_fd, None)`
+30 -10
View File
@@ -166,14 +166,19 @@ fn timers_only_pop_entries_whose_deadline_has_passed() {
#[test]
fn timers_mix_sleep_and_wait_timeout_reasons() {
let mut t = Timers::new();
let target = Arc::new(RecordingTarget { calls: Mutex::new(Vec::new()) });
let target = Arc::new(RecordingTarget {
calls: Mutex::new(Vec::new()),
});
let now = Instant::now();
t.insert_sleep(now + Duration::from_millis(5), Pid::new(0, 0), 1);
t.insert(
now + Duration::from_millis(10),
Pid::new(1, 0),
Reason::WaitTimeout { target: target.clone(), epoch: 42 },
Reason::WaitTimeout {
target: target.clone(),
epoch: 42,
},
);
let due = t.pop_due(now + Duration::from_millis(20));
@@ -238,7 +243,10 @@ fn armed_send_timer_is_returned_and_fires() {
let mut due = t.pop_due(now + Duration::from_millis(20));
assert_eq!(due.len(), 1, "an armed send timer should pop when due");
assert!(!fired.load(Ordering::SeqCst), "pop must not fire on its own");
assert!(
!fired.load(Ordering::SeqCst),
"pop must not fire on its own"
);
run_fire(due.pop().unwrap());
assert!(fired.load(Ordering::SeqCst), "running the thunk delivers");
assert!(t.is_empty());
@@ -282,7 +290,11 @@ fn cancel_after_fire_returns_false() {
fn cancel_unknown_id_returns_false() {
let mut t = Timers::new();
let now = Instant::now();
let id = t.insert_send(now + Duration::from_millis(5), Pid::new(0, 0), Box::new(|| {}));
let id = t.insert_send(
now + Duration::from_millis(5),
Pid::new(0, 0),
Box::new(|| {}),
);
assert!(t.cancel(id));
// Second cancel of the same id: already gone.
assert!(!t.cancel(id));
@@ -293,7 +305,11 @@ fn send_timers_interleave_with_sleep_in_deadline_order() {
let mut t = Timers::new();
let now = Instant::now();
t.insert_sleep(now + Duration::from_millis(30), Pid::new(0, 0), 1);
let _id = t.insert_send(now + Duration::from_millis(10), Pid::new(1, 0), Box::new(|| {}));
let _id = t.insert_send(
now + Duration::from_millis(10),
Pid::new(1, 0),
Box::new(|| {}),
);
t.insert_sleep(now + Duration::from_millis(20), Pid::new(2, 0), 1);
let due = t.pop_due(now + Duration::from_millis(50));
@@ -308,7 +324,11 @@ fn send_timers_interleave_with_sleep_in_deadline_order() {
fn clear_drops_armed_send_timers() {
let mut t = Timers::new();
let now = Instant::now();
let id = t.insert_send(now + Duration::from_millis(10), Pid::new(0, 0), Box::new(|| {}));
let id = t.insert_send(
now + Duration::from_millis(10),
Pid::new(0, 0),
Box::new(|| {}),
);
t.clear();
assert!(t.is_empty());
// The arm record is gone too: cancelling reports nothing to cancel.
@@ -356,7 +376,7 @@ fn send_after_to_unresolved_name_is_silent() {
// Nobody registered NOPE; firing resolves to nothing and is dropped.
let _id = send_after_named(Duration::from_millis(10), NOPE, 1);
sleep(Duration::from_millis(40)); // let it fire and no-op
// Reaching here without a panic is the assertion.
// Reaching here without a panic is the assertion.
});
}
@@ -401,9 +421,9 @@ fn send_after_to_dead_typed_pid_is_silent() {
assert_eq!(report_rx.recv().unwrap(), 1); // sink has now exited
let _id = send_after(Duration::from_millis(15), sink, 2);
sleep(Duration::from_millis(45)); // let it fire against the dead pid
// No panic; the sink is gone, so its report sender dropped with it —
// closed+empty is Err (documented), which also proves nothing
// further was delivered.
// No panic; the sink is gone, so its report sender dropped with it —
// closed+empty is Err (documented), which also proves nothing
// further was delivered.
assert!(report_rx.try_recv().is_err(), "nothing further delivered");
});
}