use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::atomic::{Ordering, fence};
use crate::spmc::queue::Queue;
#[repr(C, align(128))]
pub(super) struct ConsumerState {
read_cursor: usize,
cached_write_cursor: usize,
}
#[must_use = "the returned ReadResult indicates whether a value was read, the queue was empty, or the consumer was lapped"]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReadResult<T> {
Value(T),
Empty,
Lapped {
skipped: usize,
},
}
pub struct Consumer<T: bytemuck::AnyBitPattern, const CAPACITY: usize> {
state: ConsumerState,
inner: Arc<Queue<T, CAPACITY>>,
_not_sync: PhantomData<*const ()>,
}
impl<T: bytemuck::AnyBitPattern, const CAPACITY: usize> std::fmt::Debug for Consumer<T, CAPACITY> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Consumer")
.field("read_cursor", &self.state.read_cursor)
.field("cached_write_cursor", &self.state.cached_write_cursor)
.finish_non_exhaustive()
}
}
unsafe impl<T: bytemuck::AnyBitPattern, const CAPACITY: usize> Send for Consumer<T, CAPACITY> {}
static_assertions::assert_impl_all!(Consumer<u32, 2>: Send);
static_assertions::assert_not_impl_any!(Consumer<u32, 2>: Sync, Clone, Copy);
impl<T, const CAPACITY: usize> Consumer<T, CAPACITY>
where
T: bytemuck::AnyBitPattern,
{
pub(super) fn new(queue: Arc<Queue<T, CAPACITY>>) -> Self {
Self {
state: ConsumerState {
read_cursor: 0,
cached_write_cursor: 0,
},
inner: queue,
_not_sync: PhantomData,
}
}
#[inline]
pub fn try_read(&mut self) -> ReadResult<T> {
if self.state.read_cursor == self.state.cached_write_cursor {
if self.is_still_empty() {
return ReadResult::Empty;
}
}
let slot = unsafe {
self.inner
.slots
.get_unchecked(self.state.read_cursor & (CAPACITY - 1))
};
let expected_seq = self.state.read_cursor.wrapping_mul(2).wrapping_add(2);
loop {
let seq1 = slot.seq.load(Ordering::Acquire);
if seq1 & 1 == 1 {
std::hint::spin_loop();
continue;
}
if seq1 != expected_seq {
self.state.cached_write_cursor = self
.inner
.producer_state
.write_cursor
.load(Ordering::Acquire);
let new_r_cursor = self.state.cached_write_cursor.wrapping_sub(1);
let skipped = new_r_cursor.wrapping_sub(self.state.read_cursor);
self.state.read_cursor = new_r_cursor;
return ReadResult::Lapped { skipped };
}
let attempted_read = unsafe { slot.data.get().read_volatile() };
fence(Ordering::Acquire);
let seq2 = slot.seq.load(Ordering::Relaxed);
if seq1 == seq2 {
self.state.read_cursor = self.state.read_cursor.wrapping_add(1);
return ReadResult::Value(attempted_read);
}
}
}
#[cold]
fn is_still_empty(&mut self) -> bool {
self.state.cached_write_cursor = self
.inner
.producer_state
.write_cursor
.load(Ordering::Acquire);
self.state.read_cursor == self.state.cached_write_cursor
}
}