use atap::{
Runtime, RuntimeError,
sleep::{Sleep, SleepMode},
};
use std::{
thread,
time::{Duration, Instant},
};
#[test]
fn shutdown_drains_the_backlog_then_init_starts_it_again() {
let _ = Runtime::init();
let tasks = 2_000;
let handles: Vec<_> = (0..tasks)
.map(|task| {
let mode = match task % 2 == 0 {
true => SleepMode::Precise,
false => SleepMode::Relaxed,
};
Runtime::task(Sleep::sleep(Duration::from_micros(200)).mode(mode)).spawn()
})
.collect();
let started = Instant::now();
Runtime::shutdown();
let drained = started.elapsed();
println!("shutdown drained {tasks} tasks in {drained:?}");
for (task, handle) in handles.into_iter().enumerate() {
handle
.join()
.unwrap_or_else(|error| panic!("task {task} was dropped by the shutdown: {error}"));
}
let late =
Runtime::task(Sleep::sleep(Duration::from_millis(10)).mode(SleepMode::Relaxed)).spawn();
let kept = late.clone();
assert_eq!(
late.join(),
Err(RuntimeError::TaskFailed),
"a spawn after a shutdown settles instead of waiting for something that has gone",
);
let slept = Runtime::block(Sleep::sleep(Duration::from_millis(20)).mode(SleepMode::Relaxed));
println!("a blocking call after the shutdown still slept {slept:?}");
assert!(
slept >= Duration::from_millis(20),
"the blocking call came back early, so the reactor went down with the pool",
);
let status = Runtime::status();
println!("{status}");
assert!(status.shut_down(), "the status says what happened");
assert!(!status.manager_alive(), "the manager's queue was closed");
assert!(!status.healthy());
Runtime::shutdown();
for cycle in 0..2 {
assert_eq!(
Runtime::init(),
Ok(()),
"cycle {cycle}: the runtime didn't start again"
);
assert_eq!(
Runtime::init(),
Err(RuntimeError::AlreadyInit),
"cycle {cycle}: a running runtime was started twice",
);
let status = Runtime::status();
println!("cycle {cycle}: {status}");
assert!(
status.healthy(),
"cycle {cycle}: the runtime came back degraded"
);
assert_eq!(
kept.try_join(),
Err(RuntimeError::TaskFailed),
"cycle {cycle}: a restart changed what an old handle reads",
);
let quick = Runtime::task(Sleep::sleep(Duration::from_micros(200))).spawn();
let blocking =
Runtime::task(Sleep::sleep(Duration::from_millis(5)).mode(SleepMode::Relaxed)).spawn();
let delayed =
Runtime::task(Sleep::sleep(Duration::from_millis(1)).mode(SleepMode::Relaxed))
.after(Duration::from_millis(50))
.spawn();
let counted =
Runtime::task(Sleep::sleep(Duration::from_millis(1)).mode(SleepMode::Relaxed))
.repeat()
.every(Duration::from_millis(10))
.count(3)
.spawn();
quick.join().expect("a task spawned after the restart runs");
blocking
.join()
.expect("a blocking task spawned after the restart runs");
delayed
.join()
.expect("a delayed task spawned after the restart runs");
let deadline = Instant::now() + Duration::from_secs(10);
while !counted.is_finished() && Instant::now() < deadline {
thread::sleep(Duration::from_millis(1));
}
assert!(
counted.is_finished(),
"cycle {cycle}: a timed repeat never got through its count"
);
assert!(
!counted.is_failed(),
"cycle {cycle}: a timed repeat failed after the restart"
);
Runtime::shutdown();
assert!(
Runtime::status().shut_down(),
"cycle {cycle}: the second shutdown didn't take"
);
}
}