use std::pin::Pin;
use futures::{future, Future};
use super::wait;
pub fn with_timeout<
T: Send + 'static,
TFuture: Future<Output = T> + Send + 'static,
>(
original_future: TFuture,
timeout_ms: u64,
) -> Pin<Box<dyn Future<Output = Option<T>> + Send + 'static>> {
let futures: Vec<Pin<Box<dyn Future<Output = Option<T>> + Send + 'static>>> = vec![
Box::pin(async move {
wait(timeout_ms).await;
return None;
}),
Box::pin(async move {
return Some(
original_future.await,
);
}),
];
let result_future: Pin<Box<dyn Future<Output = Option<T>> + Send + 'static>> = Box::pin(async move {
let (result, _, _) = future::select_all(futures).await;
result
});
return result_future;
}
#[cfg(test)]
mod tests {
use std::time::Instant;
use cs_utils::{
random_number,
futures::{wait, with_timeout},
};
#[tokio::test]
async fn can_stop_a_future_after_a_timeout() {
let timeout: u64 = 25;
let forever_future = async move {
loop {
wait(5).await;
}
};
let with_timeout_future = with_timeout(forever_future, timeout);
let start_time = Instant::now();
let result = with_timeout_future.await;
let time_delta_ms = (Instant::now() - start_time).as_millis();
assert!(
result.is_none(),
"Timed out future must complete with `None` result.",
);
assert!(
time_delta_ms >= (timeout - 2) as u128,
"Must have waited for at least duration of the timeout (\"{delta}\" vs \"{timeout}\").",
delta = time_delta_ms,
timeout = timeout,
);
assert!(
time_delta_ms <= (timeout + 2) as u128,
"Must have waited for at most duration of the timeout (\"{delta}\" vs \"{timeout}\").",
delta = time_delta_ms,
timeout = timeout,
);
}
#[tokio::test]
async fn completed_future_returns_some_result() {
let timeout: u64 = 25;
let completion_delay: u64 = 5;
let completing_future_result = random_number(0..=i128::MAX);
let completing_future = async move {
wait(completion_delay).await;
return completing_future_result;
};
let with_timeout_future = with_timeout(completing_future, timeout);
let start_time = Instant::now();
let result = with_timeout_future.await
.expect("Completed future must complete with `Some(original result)`.");
let time_delta_ms = (Instant::now() - start_time).as_millis();
assert_eq!(
result,
completing_future_result,
"Original and received future results must match.",
);
assert!(
time_delta_ms >= (completion_delay - 2) as u128,
"Must have waited for at least duration of the completion delay (\"{delta}\" vs \"{delay}\").",
delta = time_delta_ms,
delay = completion_delay,
);
assert!(
time_delta_ms <= (completion_delay + 2) as u128,
"Must have waited for at most duration of the completion delay (\"{delta}\" vs \"{delay}\").",
delta = time_delta_ms,
delay = completion_delay,
);
}
}