use super::*;
use event_listener::{Event, EventListener};
struct Inner {
stopped: AtomicBool,
event: Event,
}
#[must_use]
pub struct StopSource {
inner: Arc<Inner>,
}
impl StopSource {
pub fn new() -> Self {
Self {
inner: Arc::new(Inner {
stopped: AtomicBool::new(false),
event: Event::new(),
}),
}
}
pub fn token(&self) -> StopToken {
StopToken {
inner: self.inner.clone(),
listener: None,
}
}
}
impl Default for StopSource {
fn default() -> Self {
Self::new()
}
}
impl Drop for StopSource {
fn drop(&mut self) {
self.inner.stopped.store(true, Ordering::SeqCst);
self.inner.event.notify(usize::MAX);
}
}
impl fmt::Debug for StopSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "StopSource")
}
}
#[must_use]
pub struct StopToken {
inner: Arc<Inner>,
listener: Option<Pin<Box<EventListener>>>,
}
impl Clone for StopToken {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
listener: None,
}
}
}
impl fmt::Debug for StopToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "StopToken")
}
}
impl Future for StopToken {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
let this = self.get_mut();
loop {
if this.inner.stopped.load(Ordering::SeqCst) {
this.listener = None;
return task::Poll::Ready(());
}
match &mut this.listener {
Some(listener) => match listener.as_mut().poll(cx) {
task::Poll::Ready(()) => {
this.listener = None;
}
task::Poll::Pending => return task::Poll::Pending,
},
None => {
this.listener = Some(Box::pin(this.inner.event.listen()));
}
}
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct TimedOutError;
impl fmt::Display for TimedOutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "timed out")
}
}
impl std::error::Error for TimedOutError {}
pub mod future {
use super::*;
pin_project_lite::pin_project! {
#[must_use]
pub struct TimeoutAt<F, D> {
#[pin]
future: F,
#[pin]
deadline: D,
}
}
impl<F: Future, D: Future> Future for TimeoutAt<F, D> {
type Output = Result<F::Output, TimedOutError>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
let this = self.project();
if let task::Poll::Ready(out) = this.future.poll(cx) {
return task::Poll::Ready(Ok(out));
}
match this.deadline.poll(cx) {
task::Poll::Ready(_) => task::Poll::Ready(Err(TimedOutError)),
task::Poll::Pending => task::Poll::Pending,
}
}
}
pub trait FutureExt: Future + Sized {
fn timeout_at<D: Future>(self, deadline: D) -> TimeoutAt<Self, D> {
TimeoutAt {
future: self,
deadline,
}
}
}
impl<F: Future + Sized> FutureExt for F {}
}
pub mod prelude {
#[doc(no_inline)]
pub use super::future::FutureExt as _;
#[doc(no_inline)]
pub use super::{StopSource, StopToken, TimedOutError};
}