use std::{
io::{Error, ErrorKind},
sync::{
Arc,
atomic::{self, AtomicUsize},
},
thread,
time::{Duration, Instant},
};
use blackhole::{ActiveLimit, BlackHole, Job, OneTime, Result};
const ATOMIC_ORDERING: atomic::Ordering = atomic::Ordering::Relaxed;
const DELAY_AS_MILLIS: u64 = 500;
struct Task {
index: usize,
data: Arc<AtomicUsize>,
}
impl Job for Task {
fn run(&mut self) -> Option<Box<dyn Job>> {
if self.index == 0 {
panic!("Test panicking...");
}
match self.index % 2 {
0 => {
println!("#{} -> {}", self.index, self.data.load(ATOMIC_ORDERING));
None
},
_ => {
let last = self.data.fetch_add(self.index * 2 - 1, ATOMIC_ORDERING);
println!("{} + {} = {}", last, self.index, last + self.index);
thread::sleep(Duration::from_millis(DELAY_AS_MILLIS));
Some(Box::new(Task { index: self.index + 1, data: self.data.clone() }))
}
}
}
}
#[test]
fn tests() -> Result<()> {
const ACTIVE_LIMIT: usize = 4;
const QUEUE_LIMIT: usize = 100;
const COUNT: usize = ACTIVE_LIMIT * 2 + ACTIVE_LIMIT - 1;
let black_hole = BlackHole::make_with_active_limit(ActiveLimit::try_from(ACTIVE_LIMIT).unwrap(), QUEUE_LIMIT)?;
let data = Arc::new(AtomicUsize::new(0));
let start_time = Instant::now();
for i in 0..=COUNT {
let data = data.clone();
black_hole.throw(Task { index: i, data })?;
}
black_hole.escape_on_idle()?;
let expected_max_runtime = Duration::from_millis({
let active_limit = u64::try_from(ACTIVE_LIMIT).unwrap();
let left = u64::try_from(COUNT - ACTIVE_LIMIT).unwrap();
DELAY_AS_MILLIS + left / active_limit * DELAY_AS_MILLIS + match left % active_limit {
0 => DELAY_AS_MILLIS,
_ => DELAY_AS_MILLIS * 2
}
});
println!("Expected max runtime: {:?}", &expected_max_runtime);
let runtime = Instant::now() - start_time;
println!("Runtime: {:?}", &runtime);
assert_eq!(data.load(ATOMIC_ORDERING), COUNT * (COUNT + 1) / 2);
if runtime < expected_max_runtime {
Ok(())
} else {
Err(Error::new(ErrorKind::Other, "::escape_on_idle() took too much time"))
}
}
#[test]
#[ignore]
fn stressing_black_hole() -> Result<()> {
const QUEUE_LIMIT: usize = 100_000;
static mut BYTES: [u8; QUEUE_LIMIT] = [0; QUEUE_LIMIT];
let black_hole = BlackHole::make_with_active_limit(blackhole::available_parallelism()?, QUEUE_LIMIT)?;
for i in 0..QUEUE_LIMIT {
assert!(black_hole.throw(OneTime::new(move || unsafe { BYTES[i] = 1; }))?.is_none());
}
black_hole.escape_on_idle()?;
unsafe {
for i in 0..BYTES.len() {
assert_eq!(&BYTES[i], &1);
}
}
Ok(())
}