use std::time::Duration;
use crate::{Promise, PromiseRejection};
impl<E> Promise<(), E>
where
E: PromiseRejection,
{
pub fn sleep(duration: Duration) -> Self {
Self::lazy(async move {
#[cfg(feature = "tokio")]
if tokio::runtime::Handle::try_current().is_ok() {
let timer = Promise::<(), ()>::lazy(async move {
tokio::time::sleep(duration).await;
Ok(())
});
if timer.await.is_ok() {
return Ok(());
}
}
#[cfg(feature = "smol")]
return {
smol::Timer::after(duration).await;
Ok(())
};
#[cfg(not(feature = "smol"))]
return {
blocking::unblock(move || std::thread::sleep(duration)).await;
Ok(())
};
})
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use std::time::{Duration, Instant};
use crate::{Promise, PromiseRejection, TaskFailure};
#[derive(Debug, PartialEq)]
enum E {
AlreadyConsumed,
TaskFailed,
}
impl PromiseRejection for E {
fn already_consumed() -> Self {
Self::AlreadyConsumed
}
fn task_failed(_: TaskFailure) -> Self {
Self::TaskFailed
}
}
const NAP: Duration = Duration::from_millis(50);
#[cfg(feature = "tokio")]
#[test]
fn sleeps_via_tokio() {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.expect("build current-thread tokio runtime");
let start = Instant::now();
let result = rt.block_on(async { Promise::<(), E>::sleep(NAP).await });
assert_eq!(result, Ok(()));
assert!(start.elapsed() >= NAP);
}
#[cfg(feature = "tokio")]
#[test]
fn selects_the_timer_at_first_poll() {
use std::task::{Context, Waker};
let rt = tokio::runtime::Builder::new_current_thread()
.enable_time()
.build()
.expect("build current-thread tokio runtime");
let mut promise = {
let _guard = rt.enter();
Promise::<(), E>::sleep(NAP)
};
let mut cx = Context::from_waker(Waker::noop());
let start = Instant::now();
while !promise.poll_settled(&mut cx) {}
assert_eq!(promise.consume(), Some(Ok(())));
assert!(start.elapsed() >= NAP);
}
#[cfg(feature = "tokio")]
#[test]
fn falls_back_without_a_time_driver() {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.expect("build current-thread tokio runtime");
let start = Instant::now();
let result = rt.block_on(async { Promise::<(), E>::sleep(NAP).await });
assert_eq!(result, Ok(()));
assert!(start.elapsed() >= NAP);
}
#[cfg(all(feature = "smol", not(feature = "tokio")))]
#[test]
fn sleeps_via_smol() {
let start = Instant::now();
let result = smol::block_on(Promise::<(), E>::sleep(NAP));
assert_eq!(result, Ok(()));
assert!(start.elapsed() >= NAP);
}
#[cfg(not(any(feature = "tokio", feature = "smol")))]
#[test]
fn sleeps_via_blocking_pool() {
use std::task::{Context, Waker};
let mut promise = Promise::<(), E>::sleep(NAP);
let mut cx = Context::from_waker(Waker::noop());
let start = Instant::now();
while !promise.poll_settled(&mut cx) {}
assert_eq!(promise.consume(), Some(Ok(())));
assert!(start.elapsed() >= NAP);
}
}