use core::sync::atomic::AtomicUsize;
use core::sync::atomic::Ordering::SeqCst;
use core::array::from_fn;
use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::fifo::{FifoApi, Storage, TmpArray, BlockSize};
pub struct Producer<T> {
fifo: Arc<dyn FifoApi<T>>,
num_prod: Arc<AtomicUsize>,
}
impl<T> Clone for Producer<T> {
fn clone(&self) -> Self {
self.num_prod.fetch_add(1, SeqCst);
Self {
fifo: self.fifo.clone(),
num_prod: self.num_prod.clone(),
}
}
}
impl<T> Drop for Producer<T> {
fn drop(&mut self) {
self.num_prod.fetch_sub(1, SeqCst);
}
}
impl<T> Producer<T> {
pub fn send_iter<I>(&self, into_iter: I)
where
I: IntoIterator,
I::IntoIter: ExactSizeIterator<Item = T>,
{
self.fifo.push(&mut into_iter.into_iter());
}
pub fn send(&self, item: T) {
self.send_iter(core::iter::once(item));
}
}
#[derive(Clone)]
pub struct Consumer<T> {
fifo: Arc<dyn FifoApi<T>>,
num_prod: Arc<AtomicUsize>,
}
impl<T> Consumer<T> {
pub fn no_producers(&self) -> bool {
self.num_prod.load(SeqCst) == 0
}
pub fn try_recv(&self) -> Option<T> {
self.try_recv_array().map(|[item]| item)
}
pub fn try_recv_many(&self, vec: &mut Vec<T>) -> Option<usize> {
self.try_recv_into(vec).ok()
}
pub fn try_recv_exact(&self, slice: &mut [T]) -> Option<()> {
self.try_recv_into(slice).ok()
}
pub fn try_recv_array<const N: usize>(&self) -> Option<[T; N]> {
let array = TmpArray {
inner: from_fn(|_| None),
};
self.try_recv_into(array).ok()
}
pub fn try_recv_into<S: Storage<T>>(&self, mut storage: S) -> Result<S::Output, S> {
let pushed = self.fifo.pull(&mut storage);
storage.finish(pushed)
}
pub(crate) fn fifo(&self) -> &dyn FifoApi<T> {
&*self.fifo
}
}
impl<const L: usize, const F: usize> BlockSize<L, F> {
pub fn non_blocking<T: 'static>() -> (Producer<T>, Consumer<T>) {
let fifo = Self::arc_fifo();
let num_prod = Arc::new(AtomicUsize::new(1));
let producer = Producer {
fifo: fifo.clone(),
num_prod: num_prod.clone(),
};
let consumer = Consumer {
fifo,
num_prod,
};
(producer, consumer)
}
}