messagebus 0.15.2

MessageBus allows intercommunicate with messages between modules
Documentation
use std::{
    pin::Pin,
    sync::{
        atomic::{AtomicU64, AtomicUsize, Ordering},
        Arc,
    },
};

use futures::Future;
use tokio::sync::Notify;

use crate::{
    builder::Config,
    chan::{BusSender, Receiver, Sender},
    handler::HandlerSpawner,
    message::Msg,
    Builder, BusInner, Error, Handler, Message,
};

#[derive(Default)]
pub(crate) struct TaskCounter {
    pub(crate) running: AtomicUsize,
    notify: Notify,
}

pub(crate) struct TaskCounterLease<S: Fn() -> bool> {
    need_notify: S,
    counter: Arc<TaskCounter>,
}

impl<S: Fn() -> bool> Drop for TaskCounterLease<S> {
    fn drop(&mut self) {
        let notify = (self.need_notify)();
        let prev = self.counter.running.fetch_sub(1, Ordering::Relaxed);

        if notify && prev == 1 {
            self.counter.notify.notify_waiters();
        }
    }
}

impl<S: Fn() -> bool> TaskCounterLease<S> {
    fn new(counter: Arc<TaskCounter>, need_notify: S) -> Self {
        counter.running.fetch_add(1, Ordering::Relaxed);

        Self {
            counter,
            need_notify,
        }
    }
}

impl TaskCounter {
    pub fn lease_unit<S: Fn() -> bool>(self: Arc<Self>, need_notify: S) -> TaskCounterLease<S> {
        TaskCounterLease::new(self, need_notify)
    }

    #[inline]
    pub async fn wait(&self) {
        self.notify.notified().await
    }
}

pub(crate) trait TaskSpawner<M: Message>: Send + Sync {
    fn config(&self, stream_id: u32) -> Config;
    fn is_producer(&self) -> bool;

    #[allow(clippy::too_many_arguments)]
    fn spawn_task(
        &self,
        rx: Receiver<Msg<M>>,
        stream_id: u32,
        task_id: u32,
        abort: Arc<Notify>,
        task_counter: Arc<TaskCounter>,
        spawn_counter: Arc<TaskCounter>,
        index_counter: Arc<AtomicU64>,
        bus: Arc<BusInner>,
    ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + '_>>;
}

pub(crate) struct TaskSpawnerWrapper<M: Message> {
    inner: Arc<dyn TaskSpawner<M>>,
}

impl<M: Message> Clone for TaskSpawnerWrapper<M> {
    fn clone(&self) -> Self {
        Self {
            inner: self.inner.clone(),
        }
    }
}

impl<M: Message> TaskSpawnerWrapper<M> {
    pub fn from_handler<B: Builder<M> + 'static>(builder: B) -> Self
    where
        B::Context: Handler<M>,
    {
        Self {
            inner: Arc::new(HandlerSpawner::new(builder)) as _,
        }
    }

    #[inline]
    #[allow(clippy::too_many_arguments)]
    pub async fn spawn_task(
        &self,
        (tx, rx): (Sender<Msg<M>>, Receiver<Msg<M>>),
        stream_id: u32,
        task_id: u32,
        abort: Arc<Notify>,
        task_counter: Arc<TaskCounter>,
        spawn_counter: Arc<TaskCounter>,
        index_counter: Arc<AtomicU64>,
        bus: Arc<BusInner>,
    ) -> Result<BusSender<M>, Error> {
        self.inner
            .spawn_task(
                rx,
                stream_id,
                task_id,
                abort,
                task_counter,
                spawn_counter,
                index_counter,
                bus,
            )
            .await?;

        Ok(BusSender::new(self.inner.is_producer(), tx))
    }

    #[inline]
    pub(crate) fn config(&self, stream_id: u32) -> Config {
        self.inner.config(stream_id)
    }
}