9c10c7cf6b
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
88 lines
2.4 KiB
Rust
88 lines
2.4 KiB
Rust
//! A unit that panics again when it is dropped must not cost the pool a worker.
|
|
//! Evidence: `context/test-evidence/improvise-incremental.md`.
|
|
|
|
mod support;
|
|
|
|
use improvise_incremental::sched::{Executor, Pool, Turn, Unit};
|
|
use std::sync::Arc;
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
use support::watchdog::within;
|
|
|
|
/// A unit that panics in its turn and again when it is dropped by the unwind's aftermath.
|
|
struct BoomTwice;
|
|
|
|
impl Unit for BoomTwice {
|
|
fn turn(&mut self, _: usize) -> Turn {
|
|
panic!("boom in the turn");
|
|
}
|
|
|
|
fn neighbours(&self) -> &[usize] {
|
|
&[]
|
|
}
|
|
}
|
|
|
|
impl Drop for BoomTwice {
|
|
fn drop(&mut self) {
|
|
if !std::thread::panicking() {
|
|
panic!("boom in the drop");
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Count(Arc<AtomicUsize>);
|
|
|
|
impl Unit for Count {
|
|
fn turn(&mut self, _: usize) -> Turn {
|
|
self.0.fetch_add(1, Ordering::SeqCst);
|
|
Turn {
|
|
progress: false,
|
|
more: false,
|
|
}
|
|
}
|
|
|
|
fn neighbours(&self) -> &[usize] {
|
|
&[]
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn a_unit_whose_drop_panics_does_not_hang_or_shrink_the_pool() {
|
|
let turns = Arc::new(AtomicUsize::new(0));
|
|
let t = turns.clone();
|
|
let (err, workers) = within(5, "a pool with a unit that panics in its drop", move || {
|
|
let units: Vec<Box<dyn Unit>> = vec![Box::new(BoomTwice), Box::new(Count(t))];
|
|
let exec = Arc::new(Executor::new(units, 1));
|
|
let pool = Pool::start(exec.clone(), 1);
|
|
exec.kick(0);
|
|
exec.kick(1);
|
|
let idle = exec.wait_idle();
|
|
let workers = exec.workers();
|
|
let _ = pool.finish();
|
|
(idle.err(), workers)
|
|
});
|
|
assert_eq!(
|
|
workers, 1,
|
|
"[drop] the only worker survived the dead unit's drop"
|
|
);
|
|
assert_eq!(err.map(|e| e.unit), Some(0));
|
|
assert_eq!(
|
|
turns.load(Ordering::SeqCst),
|
|
1,
|
|
"[drop] the other unit still ran"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn dropping_a_pool_while_unwinding_does_not_abort() {
|
|
within(5, "unwinding past a pool", || {
|
|
let exec = Arc::new(Executor::new(vec![Box::new(BoomTwice)], 1));
|
|
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
|
let _pool = Pool::start(exec.clone(), 1);
|
|
exec.kick(0);
|
|
let _ = exec.wait_idle();
|
|
panic!("the host fails with a pool alive");
|
|
}));
|
|
assert!(r.is_err());
|
|
});
|
|
}
|