use how_far::{
Child, Execution, NoPulse, PhaseSpec, PlanError, ProgressExt, Pulse, Report, SharedPulse, Stop,
StopReason, Total,
};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
#[derive(Clone, Default)]
struct Flagged {
stopped: Arc<AtomicBool>,
units: Arc<AtomicU64>,
}
struct Flag(Arc<AtomicBool>);
impl Stop for Flag {
fn check(&self) -> Result<(), StopReason> {
if self.0.load(Ordering::Relaxed) {
Err(StopReason::Cancelled)
} else {
Ok(())
}
}
}
impl Stop for Flagged {
fn check(&self) -> Result<(), StopReason> {
Flag(self.stopped.clone()).check()
}
}
impl Report for Flagged {
fn advance(&self, completed: u64) {
self.units.fetch_add(completed, Ordering::Relaxed);
}
}
impl Pulse for Flagged {
fn split(
&self,
execution: Execution,
parts: &[PhaseSpec<'_>],
) -> Result<Vec<Child<'_>>, PlanError> {
NoPulse.split(execution, parts)
}
fn share(&self) -> Result<SharedPulse, PlanError> {
Ok(SharedPulse::new(self.clone()))
}
}
fn two_stages<P: Pulse>(pulse: P) -> Result<usize, PlanError> {
let parts = [
PhaseSpec::new("a", 1, Total::Exact(1)),
PhaseSpec::new("b", 1, Total::Exact(1)),
];
Ok(pulse.split(Execution::Sequence, &parts)?.len())
}
#[test]
fn generic_code_takes_any_pulse_by_value() {
assert_eq!(two_stages(&NoPulse), Ok(2));
assert_eq!(two_stages(Box::new(&NoPulse)), Ok(2));
assert_eq!(two_stages(Arc::new(&NoPulse)), Ok(2));
let mut flagged = Flagged::default();
assert_eq!(two_stages(&flagged), Ok(2));
assert_eq!(two_stages(&mut flagged), Ok(2));
assert_eq!(two_stages(Box::new(flagged)), Ok(2));
}
#[test]
fn owned_pulses_can_default_to_no_pulse() {
struct Options {
pulse: Box<dyn Pulse>,
}
impl Default for Options {
fn default() -> Self {
Self {
pulse: Box::new(&NoPulse),
}
}
}
let options = Options::default();
options.pulse.step(3).unwrap();
assert!(!options.pulse.share().unwrap().may_stop());
assert_eq!(
options.pulse.split(Execution::Sequence, &[]).err(),
Some(PlanError::EmptyOrZeroWeight)
);
let shared: Arc<dyn Pulse> = Arc::new(&NoPulse);
std::thread::scope(|scope| {
scope.spawn(|| shared.step(1).unwrap());
});
}
#[test]
fn wrappers_forward_stops_reports_splits_and_shares() {
let flagged = Arc::new(Flagged::default());
let wrapped: [Box<dyn Pulse>; 3] = [
Box::new(Arc::clone(&flagged)),
Box::new(Box::new(Arc::clone(&flagged))),
Box::new(Arc::new(Arc::clone(&flagged))),
];
for pulse in &wrapped {
pulse.step(2).unwrap();
assert_eq!(two_stages(&**pulse), Ok(2));
let shared = pulse.share().unwrap();
shared.advance(1);
assert!(shared.may_stop());
}
assert_eq!(flagged.units.load(Ordering::Relaxed), 9);
flagged.stopped.store(true, Ordering::Relaxed);
for pulse in &wrapped {
assert_eq!(pulse.check(), Err(StopReason::Cancelled));
assert_eq!(pulse.share().unwrap().check(), Err(StopReason::Cancelled));
}
}