use std::{
io,
task::Poll,
time::{Duration, Instant},
};
use futures::{Future, FutureExt};
use rasi_syscall::{global_timer, Handle};
struct Sleep {
deadline: Instant,
handle: Option<Handle>,
syscall: &'static dyn rasi_syscall::Timer,
}
impl Sleep {
fn new_with(deadline: Instant, syscall: &'static dyn rasi_syscall::Timer) -> Self {
Self {
deadline,
handle: None,
syscall,
}
}
}
impl Future for Sleep {
type Output = io::Result<()>;
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
if self.handle.is_none() {
let handle = self.syscall.deadline(cx.waker().clone(), self.deadline)?;
if handle.is_none() {
return Poll::Ready(Ok(()));
}
self.handle = handle;
return Poll::Pending;
}
self.syscall
.timeout_wait(cx.waker().clone(), self.handle.as_ref().unwrap())
.map(|_| Ok(()))
}
}
pub async fn sleep(duraton: Duration) {
sleep_with(duraton, global_timer()).await
}
pub async fn sleep_with(duraton: Duration, syscall: &'static dyn rasi_syscall::Timer) {
let timer = Sleep::new_with(Instant::now() + duraton, syscall);
timer.await.expect("Call register_global_timer first");
}
async fn timeout_at_with(at: Instant, syscall: &'static dyn rasi_syscall::Timer) {
let timer = Sleep::new_with(at, syscall);
timer.await.expect("Call register_global_timer first");
}
pub trait TimeoutExt: Future {
fn timeout(self, duration: Duration) -> impl Future<Output = Option<Self::Output>>
where
Self: Sized,
{
self.timeout_with(duration, global_timer())
}
fn timeout_with(
self,
duration: Duration,
syscall: &'static dyn rasi_syscall::Timer,
) -> impl Future<Output = Option<Self::Output>>
where
Self: Sized,
{
async move {
futures::select! {
_ = sleep_with(duration,syscall).fuse() => {
None
}
fut = self.fuse() => {
Some(fut)
}
}
}
}
fn timeout_at(self, at: Instant) -> impl Future<Output = Option<Self::Output>>
where
Self: Sized,
{
self.timeout_at_with(at, global_timer())
}
fn timeout_at_with(
self,
at: Instant,
syscall: &'static dyn rasi_syscall::Timer,
) -> impl Future<Output = Option<Self::Output>>
where
Self: Sized,
{
async move {
futures::select! {
fut = self.fuse() => {
Some(fut)
}
_ = timeout_at_with(at,syscall).fuse() => {
None
}
}
}
}
}
impl<T> TimeoutExt for T where T: Future {}