use std::{
io::Result,
sync::OnceLock,
task::{Context, Poll},
time::{Duration, Instant},
};
use futures::{Future, FutureExt};
pub mod syscall {
use super::*;
pub trait Driver: Sync + Send {
fn deadline(&self, deadline: Instant) -> Result<Option<DeadLine>>;
}
pub trait DriverDeadLine: Sync + Send {
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<()>;
}
}
pub struct DeadLine(Box<dyn syscall::DriverDeadLine>);
impl<D: syscall::DriverDeadLine + 'static> From<D> for DeadLine {
fn from(value: D) -> Self {
Self(Box::new(value))
}
}
impl DeadLine {
pub fn as_raw_ptr(&self) -> &dyn syscall::DriverDeadLine {
&*self.0
}
}
impl Future for DeadLine {
type Output = ();
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.as_raw_ptr().poll_ready(cx)
}
}
pub async fn sleep(duration: Duration) {
sleep_with(duration, get_timer_driver()).await
}
pub async fn sleep_until(at: Instant) {
sleep_until_with(at, get_timer_driver()).await
}
pub async fn sleep_until_with(at: Instant, driver: &dyn syscall::Driver) {
if let Some(dead_line) = driver
.deadline(at)
.expect("Call register_global_timer first")
{
dead_line.await
}
}
pub async fn sleep_with(duration: Duration, driver: &dyn syscall::Driver) {
if let Some(dead_line) = driver
.deadline(Instant::now() + duration)
.expect("Call register_global_timer first")
{
dead_line.await
}
}
pub trait TimeoutExt: Future {
fn timeout(self, duration: Duration) -> impl Future<Output = Option<Self::Output>>
where
Self: Sized,
{
self.timeout_with(duration, get_timer_driver())
}
fn timeout_with(
self,
duration: Duration,
driver: &dyn syscall::Driver,
) -> impl Future<Output = Option<Self::Output>>
where
Self: Sized,
{
async move {
futures::select! {
_ = sleep_with(duration,driver).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, get_timer_driver())
}
fn timeout_at_with(
self,
at: Instant,
driver: &dyn syscall::Driver,
) -> impl Future<Output = Option<Self::Output>>
where
Self: Sized,
{
async move {
futures::select! {
_ = sleep_until_with(at,driver).fuse() => {
None
}
fut = self.fuse() => {
Some(fut)
}
}
}
}
}
impl<T> TimeoutExt for T where T: Future {}
static GLOBAL_TIMER: OnceLock<Box<dyn syscall::Driver>> = OnceLock::new();
pub fn register_timer_driver<T: syscall::Driver + 'static>(timer: T) {
if GLOBAL_TIMER.set(Box::new(timer)).is_err() {
panic!("Multiple calls to register_timer_driver are not permitted!!!");
}
}
pub fn get_timer_driver() -> &'static dyn syscall::Driver {
GLOBAL_TIMER
.get()
.expect("Call register_global_timer first")
.as_ref()
}