orbit-client-pool 0.5.5

Protocol-neutral local and fleet-wide outbound client pools for Orbit process fleets.
Documentation
use std::future::{Future, pending};
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::task::{Context, Poll, Waker};

use async_trait::async_trait;

use crate::{ClientManager, PoolStats};

#[derive(Default)]
pub(crate) struct Control {
    pub(crate) create_pending: AtomicBool,
    pub(crate) recycle_pending: AtomicBool,
    pub(crate) create_error: AtomicBool,
    pub(crate) recycle_error: AtomicBool,
    pub(crate) live: AtomicUsize,
    pub(crate) created: AtomicUsize,
    pub(crate) dropped: AtomicUsize,
}

pub(crate) struct Manager(pub(crate) Arc<Control>);
pub(crate) struct Client(Arc<Control>);

impl Drop for Client {
    fn drop(&mut self) {
        self.0.live.fetch_sub(1, Ordering::SeqCst);
        self.0.dropped.fetch_add(1, Ordering::SeqCst);
    }
}

#[async_trait]
impl ClientManager for Manager {
    type Client = Client;
    type Error = std::io::Error;

    async fn create(&self) -> Result<Client, Self::Error> {
        if self.0.create_pending.load(Ordering::SeqCst) {
            pending::<()>().await;
        }
        if self.0.create_error.load(Ordering::SeqCst) {
            return Err(std::io::Error::other("test creation rejected"));
        }
        self.0.live.fetch_add(1, Ordering::SeqCst);
        self.0.created.fetch_add(1, Ordering::SeqCst);
        Ok(Client(self.0.clone()))
    }

    async fn recycle(
        &self,
        _: &mut Client,
    ) -> Result<(), Self::Error> {
        if self.0.recycle_pending.load(Ordering::SeqCst) {
            pending::<()>().await;
        }
        if self.0.recycle_error.load(Ordering::SeqCst) {
            return Err(std::io::Error::other("test recycle rejected"));
        }
        Ok(())
    }
}

pub(crate) fn poll_once<F: Future>(future: Pin<&mut F>) -> Poll<F::Output> {
    future.poll(&mut Context::from_waker(Waker::noop()))
}

pub(crate) fn ready<F: Future>(mut future: Pin<&mut F>) -> F::Output {
    match poll_once(future.as_mut()) {
        Poll::Ready(value) => value,
        Poll::Pending => panic!("operation unexpectedly needs another wake"),
    }
}

pub(crate) fn empty(stats: PoolStats) {
    assert_eq!(
        (
            stats.live,
            stats.idle,
            stats.leased,
            stats.creating,
            stats.in_flight,
            stats.waiting
        ),
        (0, 0, 0, 0, 0, 0)
    );
}