Skip to main content

low_latency_data_structures/spmc/
producer.rs

1use std::marker::PhantomData;
2use std::sync::Arc;
3use std::sync::atomic::AtomicUsize;
4
5use crate::mem::Allocation;
6use crate::spmc::queue::{Queue, Slot};
7
8#[repr(C, align(128))]
9pub(super) struct ProducerState {
10    pub(super) write_cursor: AtomicUsize,
11}
12
13/// The publishing handle of an SPMC broadcast queue.
14///
15/// Created together with its [`Consumer`](crate::spmc::Consumer)s by
16/// [`new`](crate::spmc::new). A `Producer` is [`Send`] but not [`Sync`]:
17/// at most one thread may publish at a time. To enforce that, the type is
18/// neither [`Clone`] nor [`Copy`].
19///
20/// Publishing never blocks and never allocates: when the queue is full the
21/// oldest slot is overwritten, and any consumer that hasn't read past that
22/// slot will observe a [`ReadResult::Lapped`](crate::spmc::ReadResult::Lapped)
23/// on its next read.
24pub struct Producer<T: bytemuck::AnyBitPattern, const CAPACITY: usize, AllocT: Allocation<Slot<T>>>
25{
26    inner: Arc<Queue<T, CAPACITY, AllocT>>,
27    _not_sync: PhantomData<*const ()>,
28}
29
30impl<T: bytemuck::AnyBitPattern, const CAPACITY: usize, AllocT: Allocation<Slot<T>>> std::fmt::Debug
31    for Producer<T, CAPACITY, AllocT>
32{
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.debug_struct("Producer")
35            .field("capacity", &CAPACITY)
36            .finish_non_exhaustive()
37    }
38}
39
40// SAFETY: it is Send on its own but we need to restrict only Sync.
41unsafe impl<T: bytemuck::AnyBitPattern, const CAPACITY: usize, AllocT: Allocation<Slot<T>>> Send
42    for Producer<T, CAPACITY, AllocT>
43{
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::mem::test_util::NeverAlloc;
50
51    static_assertions::assert_impl_all!(Producer<u32, 1, NeverAlloc>: Send);
52    static_assertions::assert_not_impl_any!(Producer<u32, 1, NeverAlloc>: Sync, Clone, Copy);
53}
54
55impl<T, const CAPACITY: usize, AllocT> Producer<T, CAPACITY, AllocT>
56where
57    T: bytemuck::AnyBitPattern,
58    AllocT: Allocation<Slot<T>>,
59{
60    pub(super) fn new(queue: Arc<Queue<T, CAPACITY, AllocT>>) -> Self {
61        Self {
62            inner: queue,
63            _not_sync: PhantomData,
64        }
65    }
66
67    /// Publishes `value` into the next slot.
68    ///
69    /// Wait-free. Never blocks, never allocates. If the queue is full, the
70    /// oldest slot is silently overwritten; consumers that haven't read past
71    /// that slot will observe a [`ReadResult::Lapped`](crate::spmc::ReadResult::Lapped)
72    /// on their next [`try_read`](crate::spmc::Consumer::try_read).
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// use low_latency_data_structures::spmc::{self, ReadResult, new};
78    /// use low_latency_data_structures::mem::global::GlobalAllocator;
79    ///
80    /// let (producer, [mut consumer]) = new::<u64, 16, 1, GlobalAllocator>(
81    ///     spmc::Options::global_mlocked(),
82    /// );
83    /// producer.publish(42);
84    /// assert_eq!(consumer.try_read(), ReadResult::Value(42));
85    /// ```
86    #[inline]
87    pub fn publish(&self, value: T) {
88        self.inner.publish(value)
89    }
90}