use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use futures_util::future::{select, Either};
use futures_util::{pin_mut, Stream};
use crate::{Duration, Instant};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TimeoutError;
pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> {
let timeout_fut = Timer::after(timeout);
pin_mut!(fut);
match select(fut, timeout_fut).await {
Either::Left((r, _)) => Ok(r),
Either::Right(_) => Err(TimeoutError),
}
}
pub struct Timer {
expires_at: Instant,
yielded_once: bool,
}
impl Timer {
pub fn at(expires_at: Instant) -> Self {
Self {
expires_at,
yielded_once: false,
}
}
pub fn after(duration: Duration) -> Self {
Self {
expires_at: Instant::now() + duration,
yielded_once: false,
}
}
}
impl Unpin for Timer {}
impl Future for Timer {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.yielded_once && self.expires_at <= Instant::now() {
Poll::Ready(())
} else {
schedule_wake(self.expires_at, cx.waker());
self.yielded_once = true;
Poll::Pending
}
}
}
pub struct Ticker {
expires_at: Instant,
duration: Duration,
}
impl Ticker {
pub fn every(duration: Duration) -> Self {
let expires_at = Instant::now() + duration;
Self { expires_at, duration }
}
}
impl Unpin for Ticker {}
impl Stream for Ticker {
type Item = ();
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if self.expires_at <= Instant::now() {
let dur = self.duration;
self.expires_at += dur;
Poll::Ready(Some(()))
} else {
schedule_wake(self.expires_at, cx.waker());
Poll::Pending
}
}
}
extern "Rust" {
fn _embassy_time_schedule_wake(at: Instant, waker: &Waker);
}
fn schedule_wake(at: Instant, waker: &Waker) {
unsafe { _embassy_time_schedule_wake(at, waker) }
}