mod inner {
use web_time::{Duration, Instant};
#[derive(Debug, Clone, Copy)]
pub struct Timer(Instant);
impl Timer {
pub fn now() -> Self {
Timer(Instant::now())
}
pub fn elapsed(&self) -> Duration {
self.0.elapsed()
}
pub fn elapsed_ms(&self) -> u64 {
self.0.elapsed().as_millis() as u64
}
}
}
pub use inner::Timer;
pub(crate) fn expired(start: &Timer, limit: Option<web_time::Duration>) -> bool {
limit.is_some_and(|limit| start.elapsed() > limit)
}
pub(crate) fn evaluate_within<T>(
items: &mut Vec<T>,
start: &Timer,
limit: Option<web_time::Duration>,
mut evaluate: impl FnMut(&mut [T]),
) {
#[cfg(feature = "parallel")]
let batch = rayon::current_num_threads().max(1);
#[cfg(not(feature = "parallel"))]
let batch = 1;
let mut done = 0;
while done < items.len() {
let end = (done + batch).min(items.len());
evaluate(&mut items[done..end]);
done = end;
if expired(start, limit) {
break;
}
}
items.truncate(done);
}
#[cfg(test)]
mod tests {
use super::*;
use web_time::Duration;
#[test]
fn evaluation_stops_at_the_limit_and_keeps_what_it_evaluated() {
let start = Timer::now();
let mut items: Vec<u32> = (0..1000).collect();
evaluate_within(
&mut items,
&start,
Some(Duration::from_millis(20)),
|batch| {
std::thread::sleep(Duration::from_millis(5));
batch.iter_mut().for_each(|x| *x += 1_000_000);
},
);
assert!(
!items.is_empty() && items.len() < 1000,
"evaluated {}",
items.len()
);
assert!(
items.iter().all(|&x| x >= 1_000_000),
"an unevaluated item survived"
);
}
#[test]
fn without_a_limit_everything_is_evaluated() {
let start = Timer::now();
let mut items = vec![0u8; 50];
evaluate_within(&mut items, &start, None, |batch| {
batch.iter_mut().for_each(|x| *x = 1)
});
assert_eq!(items, vec![1u8; 50]);
}
}