use std::time::{Duration, Instant};
#[test]
fn sleep_zero_returns_immediately() {
let mut rt = dope_runtime::runtime::Runtime::<
dope_runtime::executor::LocalExecutor<
dope_runtime::DefaultBackend,
dope_core::profile::Throughput,
>,
>::new_local()
.unwrap();
let ctx = rt.ctx();
rt.run_until(async move {
let start = Instant::now();
dope_runtime::time::sleep_with(ctx, Duration::ZERO)
.await
.unwrap();
assert!(start.elapsed() < Duration::from_millis(10));
});
}
#[test]
fn sleep_short_duration() {
let mut rt = dope_runtime::runtime::Runtime::<
dope_runtime::executor::LocalExecutor<
dope_runtime::DefaultBackend,
dope_core::profile::Throughput,
>,
>::new_local()
.unwrap();
let ctx = rt.ctx();
rt.run_until(async move {
let start = Instant::now();
dope_runtime::time::sleep_with(ctx, Duration::from_millis(50))
.await
.unwrap();
let elapsed = start.elapsed();
assert!(
elapsed >= Duration::from_millis(40),
"too fast: {elapsed:?}"
);
assert!(
elapsed < Duration::from_millis(200),
"too slow: {elapsed:?}"
);
});
}
#[test]
fn sleep_until_in_past() {
let mut rt = dope_runtime::runtime::Runtime::<
dope_runtime::executor::LocalExecutor<
dope_runtime::DefaultBackend,
dope_core::profile::Throughput,
>,
>::new_local()
.unwrap();
let ctx = rt.ctx();
rt.run_until(async move {
let past = Instant::now() - Duration::from_secs(1);
let start = Instant::now();
dope_runtime::time::sleep_with(ctx, past.saturating_duration_since(Instant::now()))
.await
.unwrap();
assert!(start.elapsed() < Duration::from_millis(10));
});
}
#[test]
fn multiple_concurrent_sleeps() {
let mut rt = dope_runtime::runtime::Runtime::<
dope_runtime::executor::LocalExecutor<
dope_runtime::DefaultBackend,
dope_core::profile::Throughput,
>,
>::new_local()
.unwrap();
let ctx = rt.ctx();
rt.run_until(async move {
let start = Instant::now();
let (a, b, c) = futures_join3(
dope_runtime::time::sleep_with(ctx, Duration::from_millis(30)),
dope_runtime::time::sleep_with(ctx, Duration::from_millis(60)),
dope_runtime::time::sleep_with(ctx, Duration::from_millis(90)),
)
.await;
a.unwrap();
b.unwrap();
c.unwrap();
let elapsed = start.elapsed();
assert!(
elapsed >= Duration::from_millis(80),
"too fast: {elapsed:?}"
);
assert!(
elapsed < Duration::from_millis(300),
"too slow: {elapsed:?}"
);
});
}
#[test]
fn timeout_fires_before_slow_future() {
let mut rt = dope_runtime::runtime::Runtime::<
dope_runtime::executor::LocalExecutor<
dope_runtime::DefaultBackend,
dope_core::profile::Throughput,
>,
>::new_local()
.unwrap();
let ctx = rt.ctx();
rt.run_until(async move {
let result = dope_runtime::time::timeout_with(ctx, Duration::from_millis(30), async {
dope_runtime::time::sleep_with(ctx, Duration::from_secs(10))
.await
.unwrap();
42
})
.await;
assert!(result.is_err());
});
}
#[test]
fn timeout_does_not_fire_for_fast_future() {
let mut rt = dope_runtime::runtime::Runtime::<
dope_runtime::executor::LocalExecutor<
dope_runtime::DefaultBackend,
dope_core::profile::Throughput,
>,
>::new_local()
.unwrap();
let ctx = rt.ctx();
rt.run_until(async move {
let result = dope_runtime::time::timeout_with(ctx, Duration::from_secs(10), async {
dope_runtime::time::sleep_with(ctx, Duration::from_millis(10))
.await
.unwrap();
42
})
.await;
assert_eq!(result.unwrap(), 42);
});
}
#[test]
fn cancelled_sleep_does_not_leak() {
let mut rt = dope_runtime::runtime::Runtime::<
dope_runtime::executor::LocalExecutor<
dope_runtime::DefaultBackend,
dope_core::profile::Throughput,
>,
>::new_local()
.unwrap();
let ctx = rt.ctx();
rt.run_until(async move {
for _ in 0..1000 {
let fut = dope_runtime::time::sleep_with(ctx, Duration::from_secs(999));
drop(fut);
}
dope_runtime::time::sleep_with(ctx, Duration::from_millis(10))
.await
.unwrap();
});
}
async fn futures_join3<A, B, C>(a: A, b: B, c: C) -> (A::Output, B::Output, C::Output)
where
A: std::future::Future,
B: std::future::Future,
C: std::future::Future,
{
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
struct Join3<A: Future, B: Future, C: Future> {
a: Pin<Box<A>>,
b: Pin<Box<B>>,
c: Pin<Box<C>>,
out_a: Option<A::Output>,
out_b: Option<B::Output>,
out_c: Option<C::Output>,
}
impl<A: Future, B: Future, C: Future> Future for Join3<A, B, C> {
type Output = (A::Output, B::Output, C::Output);
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let me = unsafe { self.get_unchecked_mut() };
if me.out_a.is_none()
&& let Poll::Ready(v) = me.a.as_mut().poll(cx)
{
me.out_a = Some(v);
}
if me.out_b.is_none()
&& let Poll::Ready(v) = me.b.as_mut().poll(cx)
{
me.out_b = Some(v);
}
if me.out_c.is_none()
&& let Poll::Ready(v) = me.c.as_mut().poll(cx)
{
me.out_c = Some(v);
}
if me.out_a.is_some() && me.out_b.is_some() && me.out_c.is_some() {
Poll::Ready((
me.out_a.take().unwrap(),
me.out_b.take().unwrap(),
me.out_c.take().unwrap(),
))
} else {
Poll::Pending
}
}
}
Join3 {
a: Box::pin(a),
b: Box::pin(b),
c: Box::pin(c),
out_a: None,
out_b: None,
out_c: None,
}
.await
}