local_async_utils 0.3.0

Utilities for single-threaded async programming
Documentation
use super::shared_state::{SharedState, Source};
use futures::FutureExt;
use std::cell::Cell;
use std::fmt;
use std::future::{Future, poll_fn};
use std::ops::ControlFlow;
use std::rc::Rc;

struct Data {
    notified: Cell<bool>,
    has_sender: Cell<bool>,
    #[cfg(debug_assertions)]
    has_receiver: Cell<bool>,
}

impl Source for Data {
    type Item = ();

    fn try_yield_one(&self) -> ControlFlow<Option<Self::Item>> {
        if !self.has_sender.get() {
            ControlFlow::Break(None)
        } else if self.notified.replace(false) {
            ControlFlow::Break(Some(()))
        } else {
            ControlFlow::Continue(())
        }
    }
}

type StateRc = Rc<SharedState<Data>>;

#[derive(Clone)]
pub struct Sender(StateRc);

pub struct Receiver(StateRc);

pub fn condvar() -> (Sender, Receiver) {
    let state = SharedState::new(Data {
        notified: Cell::new(false),
        has_sender: Cell::new(true),
        #[cfg(debug_assertions)]
        has_receiver: Cell::new(true),
    });
    (Sender(state.clone()), Receiver(state))
}

impl Sender {
    pub fn signal_one(&self) {
        #[cfg(debug_assertions)]
        debug_assert!(self.0.has_receiver.get());
        self.0.notified.set(true);
        self.0.notify();
    }
}

impl Drop for Sender {
    fn drop(&mut self) {
        self.0.has_sender.set(false);
        self.0.notify();
    }
}

impl fmt::Debug for Sender {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Sender").field("notified", &self.0.notified.get()).finish()
    }
}

impl Receiver {
    pub fn wait_for_one(&mut self) -> impl Future<Output = bool> + '_ {
        poll_fn(|cx| self.0.poll_wait(cx)).map(|v| v.is_some())
    }
}

impl Drop for Receiver {
    fn drop(&mut self) {
        self.0.receiver_dropped();
        #[cfg(debug_assertions)]
        self.0.has_receiver.set(false);
    }
}

impl fmt::Debug for Receiver {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Receiver")
            .field("notified", &self.0.notified.get())
            .field("has_sender", &self.0.has_sender.get())
            .finish()
    }
}