use std::{cell::RefCell, future::Future, rc::Rc, time::Duration};
use futures::{
channel::oneshot,
future,
future::{AbortHandle, FutureExt as _},
};
use crate::platform;
type FutureResolver = Rc<RefCell<Option<oneshot::Sender<()>>>>;
pub fn resettable_delay_for(
delay: Duration,
is_stopped: bool,
) -> (impl Future<Output = ()>, ResettableDelayHandle) {
ResettableDelayHandle::new(delay, is_stopped)
}
#[derive(Debug)]
pub struct ResettableDelayHandle {
future_resolver: FutureResolver,
timeout: Duration,
abort_handle: RefCell<AbortHandle>,
}
impl ResettableDelayHandle {
pub fn stop(&self) {
self.abort_handle.borrow().abort();
}
pub fn reset(&self) {
self.abort_handle.borrow().abort();
self.spawn_timer();
}
fn new(
timeout: Duration,
is_stopped: bool,
) -> (impl Future<Output = ()>, Self) {
let (tx, rx) = oneshot::channel();
let tx = Rc::new(RefCell::new(Some(tx)));
let (abort, _) = AbortHandle::new_pair();
let this = Self {
future_resolver: tx,
abort_handle: RefCell::new(abort),
timeout,
};
if !is_stopped {
this.spawn_timer();
}
let delay_fut = async move {
if rx.await.is_err() {
future::pending::<()>().await;
};
};
(delay_fut, this)
}
fn spawn_timer(&self) {
let future_resolver = Rc::clone(&self.future_resolver);
let timeout = self.timeout;
let (fut, abort) = future::abortable(async move {
platform::delay_for(timeout).await;
if let Some(rsvr) = future_resolver.borrow_mut().take() {
_ = rsvr.send(());
}
});
platform::spawn(fut.map(drop));
drop(self.abort_handle.replace(abort));
}
}