use super::*;
use crate::{
barrier::{Barrier, NONE},
consumer::Consumer,
cursor::Cursor,
producer::ProducerBarrier,
ringbuffer::RingBuffer,
wait_strategies::{WaitStrategy, WakeupNotifier},
Sequence,
};
use crossbeam_utils::CachePadded;
use std::{
hint,
sync::atomic::{fence, Ordering},
};
pub struct SingleProducer<E, C, W>
where
C: Barrier,
W: WaitStrategy,
{
shutdown_at_sequence: Arc<CachePadded<AtomicI64>>,
ring_buffer: Arc<RingBuffer<E>>,
producer_barrier: Arc<SingleProducerBarrier>,
consumers: Vec<Consumer>,
consumer_barrier: C,
notifier: W::Notifier,
sequence: Sequence,
sequence_clear_of_consumers: Sequence,
}
impl<E, C, W> Producer<E> for SingleProducer<E, C, W>
where
C: Barrier,
W: WaitStrategy,
{
#[inline]
fn try_publish<F>(&mut self, update: F) -> Result<Sequence, RingBufferFull>
where
F: FnOnce(&mut E),
{
self.next_sequences(1).map_err(|_| RingBufferFull)?;
let sequence = self.apply_update(update);
Ok(sequence)
}
#[inline]
fn publish<F>(&mut self, update: F)
where
F: FnOnce(&mut E),
{
while self.next_sequences(1).is_err() {
hint::spin_loop();
}
self.apply_update(update);
}
#[inline]
fn try_batch_publish<F>(&mut self, n: usize, update: F) -> Result<Sequence, MissingFreeSlots>
where
F: for<'a> FnOnce(MutBatchIter<'a, E>),
{
self.next_sequences(n)?;
let sequence = self.apply_updates(n, update);
Ok(sequence)
}
#[inline]
fn batch_publish<F>(&mut self, n: usize, update: F)
where
F: for<'a> FnOnce(MutBatchIter<'a, E>),
{
debug_assert!(
n <= self.ring_buffer.capacity() as usize,
"batch_publish(n) requires n <= ring buffer capacity"
);
while self.next_sequences(n).is_err() {
hint::spin_loop();
}
self.apply_updates(n, update);
}
}
impl<E, C, W> SingleProducer<E, C, W>
where
C: Barrier,
W: WaitStrategy,
{
pub(crate) fn new(
shutdown_at_sequence: Arc<CachePadded<AtomicI64>>,
ring_buffer: Arc<RingBuffer<E>>,
producer_barrier: Arc<SingleProducerBarrier>,
consumers: Vec<Consumer>,
consumer_barrier: C,
notifier: W::Notifier,
) -> Self {
let sequence_clear_of_consumers = ring_buffer.capacity() - 1;
Self {
shutdown_at_sequence,
ring_buffer,
producer_barrier,
consumers,
consumer_barrier,
notifier,
sequence: 0,
sequence_clear_of_consumers,
}
}
#[inline]
fn next_sequences(&mut self, n: usize) -> Result<Sequence, MissingFreeSlots> {
let n = n as i64;
let n_next = self.sequence - 1 + n;
if self.sequence_clear_of_consumers < n_next {
let last_published = self.sequence - 1;
let rear_sequence_read = self.consumer_barrier.get_after(last_published);
let available_capacity = self
.ring_buffer
.available_capacity(last_published, rear_sequence_read);
if available_capacity < n {
return Err(MissingFreeSlots((n - available_capacity) as u64));
}
fence(Ordering::Acquire);
self.sequence_clear_of_consumers = last_published + available_capacity;
}
Ok(n_next)
}
#[inline]
fn apply_update<F>(&mut self, update: F) -> Sequence
where
F: FnOnce(&mut E),
{
let sequence = self.sequence;
let event_ptr = unsafe { self.ring_buffer.get(sequence).as_ptr() };
let event = unsafe { &mut *event_ptr };
update(event);
self.producer_barrier.publish(sequence);
self.notifier.wake();
self.sequence += 1;
sequence
}
#[inline]
fn apply_updates<F>(&mut self, n: usize, updates: F) -> Sequence
where
F: for<'a> FnOnce(MutBatchIter<'a, E>),
{
let n = n as i64;
let lower = self.sequence;
let upper = lower + n - 1;
let iter = MutBatchIter::new(lower, upper, &self.ring_buffer);
updates(iter);
self.producer_barrier.publish(upper);
self.notifier.wake();
self.sequence += n;
upper
}
}
impl<E, C, W> Drop for SingleProducer<E, C, W>
where
C: Barrier,
W: WaitStrategy,
{
fn drop(&mut self) {
self.shutdown_at_sequence
.store(self.sequence, Ordering::Relaxed);
self.notifier.wake();
self.consumers.iter_mut().for_each(|c| c.join());
}
}
pub struct SingleProducerBarrier {
cursor: Cursor,
}
impl SingleProducerBarrier {
pub(crate) fn new() -> Self {
Self {
cursor: Cursor::new(NONE),
}
}
}
impl Barrier for SingleProducerBarrier {
#[inline]
fn get_after(&self, _prev: Sequence) -> Sequence {
self.cursor.relaxed_value()
}
}
impl ProducerBarrier for SingleProducerBarrier {
#[inline]
fn publish(&self, sequence: Sequence) {
self.cursor.store(sequence);
}
}