Skip to main content

dtact_util/sync/
mpsc.rs

1//! Multi-producer, single-consumer channel — bounded ([`channel`], with
2//! backpressure) and unbounded ([`unbounded_channel`]).
3//!
4//! The message buffer itself is lock-free (no `std::sync::Mutex`), not
5//! just the waiter bookkeeping — see [`Queue`]'s doc for why bounded and
6//! unbounded need genuinely different underlying structures to achieve
7//! that.
8
9use super::wait_queue::WaitQueue;
10use crate::lockfree::{BoundedMpmcQueue, MpmcStack};
11use std::cell::RefCell;
12use std::collections::VecDeque;
13use std::sync::Arc;
14use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
15use std::task::{Context, Poll};
16
17/// The two message-buffer shapes a `Shared<T>` can hold.
18///
19/// - **Bounded**: a fixed-capacity [`BoundedMpmcQueue`] (Vyukov's
20///   lock-free ring-buffer algorithm) — genuinely lock-free push/pop, no
21///   mutex anywhere on the hot path. This replaced an earlier
22///   `Mutex<VecDeque<T>>` that measured multiple times slower than
23///   `tokio::sync::mpsc` under contention (`benches/sync_performance.rs`'s
24///   `mpsc_multi_producer_throughput`) — the mutex itself, not just the
25///   waiter bookkeeping, was the bottleneck.
26/// - **Unbounded**: a [`MpmcStack`] (the same lock-free Treiber stack
27///   used elsewhere in this crate). A stack alone is LIFO, which would
28///   silently reorder messages — wrong for a channel. `try_pop` restores
29///   FIFO order the same way [`MpmcStack::drain_all`] already does
30///   internally (reverse the LIFO pop order): the *single* receiver (this
31///   type isn't `Clone`) keeps a small local `VecDeque` refilled by
32///   draining+reversing the whole stack whenever it runs dry, so ordering
33///   is exactly as if every message had gone through one shared FIFO,
34///   amortized to O(1) per message with zero contention on the drain
35///   itself (one atomic pointer swap, not a per-message lock). A bounded
36///   ring can't be reused here because "unbounded" means capacity isn't
37///   fixed at construction time.
38#[repr(align(64))]
39enum Queue<T> {
40    Bounded(Box<BoundedMpmcQueue<T>>),
41    Unbounded(MpmcStack<T>),
42}
43
44impl<T> Queue<T> {
45    /// Push `value`. Always succeeds for the unbounded variant; for the
46    /// bounded variant, hands `value` back if the queue is currently at
47    /// capacity.
48    #[inline(always)]
49    fn try_push(&self, value: T) -> Result<(), T> {
50        match self {
51            Self::Bounded(q) => q.try_push(value),
52            Self::Unbounded(q) => {
53                q.push(value);
54                Ok(())
55            }
56        }
57    }
58}
59
60#[repr(align(64))]
61struct Shared<T> {
62    queue: Queue<T>,
63    sender_count: AtomicUsize,
64    receiver_dropped: AtomicBool,
65    /// Woken when the queue transitions empty -> nonempty (or the last
66    /// sender drops) — always exactly zero or one waiter (`Receiver`
67    /// isn't `Clone`), but reuses the same shared queue type as
68    /// everything else in this module for consistency.
69    recv_wait: WaitQueue,
70    /// Woken when the queue transitions full -> not-full (or the
71    /// receiver drops) — potentially many waiting senders. Only ever
72    /// populated for the bounded variant (the unbounded variant never
73    /// rejects a push, so nothing ever registers here), but kept
74    /// unconditionally rather than behind an `Option` for the same
75    /// "reuse the same shared type" reasoning as `recv_wait`.
76    send_wait: WaitQueue,
77}
78
79impl<T> Shared<T> {
80    #[inline(always)]
81    fn is_closed_for_send(&self) -> bool {
82        self.receiver_dropped.load(Ordering::Acquire)
83    }
84
85    #[inline(always)]
86    fn is_closed_for_recv(&self) -> bool {
87        self.sender_count.load(Ordering::Acquire) == 0
88    }
89}
90
91/// Create a bounded channel: [`Sender::send`] waits (without blocking the
92/// OS thread) once `capacity` unreceived messages are buffered.
93#[must_use]
94#[inline]
95pub fn channel<T>(capacity: usize) -> (Sender<T>, Receiver<T>) {
96    let shared = Arc::new(Shared {
97        queue: Queue::Bounded(Box::new(BoundedMpmcQueue::new(capacity.max(1)))),
98        sender_count: AtomicUsize::new(1),
99        receiver_dropped: AtomicBool::new(false),
100        recv_wait: WaitQueue::new(),
101        send_wait: WaitQueue::new(),
102    });
103    (
104        Sender {
105            shared: shared.clone(),
106        },
107        Receiver {
108            shared,
109            local: RefCell::new(VecDeque::new()),
110        },
111    )
112}
113
114/// Create an unbounded channel: [`UnboundedSender::send`] never waits,
115/// buffering as many messages as memory allows.
116#[must_use]
117#[inline]
118pub fn unbounded_channel<T>() -> (UnboundedSender<T>, UnboundedReceiver<T>) {
119    let shared = Arc::new(Shared {
120        queue: Queue::Unbounded(MpmcStack::new()),
121        sender_count: AtomicUsize::new(1),
122        receiver_dropped: AtomicBool::new(false),
123        recv_wait: WaitQueue::new(),
124        send_wait: WaitQueue::new(),
125    });
126    (
127        UnboundedSender {
128            inner: Sender {
129                shared: shared.clone(),
130            },
131        },
132        UnboundedReceiver {
133            inner: Receiver {
134                shared,
135                local: RefCell::new(VecDeque::new()),
136            },
137        },
138    )
139}
140
141/// The sending half of a bounded [`channel`]. Cheaply [`Clone`]-able —
142/// every clone counts toward the channel staying open.
143#[repr(align(64))]
144pub struct Sender<T> {
145    shared: Arc<Shared<T>>,
146}
147
148impl<T> Clone for Sender<T> {
149    #[inline(always)]
150    fn clone(&self) -> Self {
151        self.shared.sender_count.fetch_add(1, Ordering::AcqRel);
152        Self {
153            shared: self.shared.clone(),
154        }
155    }
156}
157
158impl<T> Drop for Sender<T> {
159    #[inline(always)]
160    fn drop(&mut self) {
161        if self.shared.sender_count.fetch_sub(1, Ordering::AcqRel) == 1 {
162            // Last sender gone — wake the receiver so a pending `.recv()`
163            // observes channel closure instead of hanging forever.
164            self.shared.recv_wait.wake_all();
165        }
166    }
167}
168
169impl<T> Sender<T> {
170    /// Send `value`, waiting if the channel is currently at `capacity`.
171    ///
172    /// # Errors
173    /// Returns `value` back in [`SendError`] if the receiver has been
174    /// dropped.
175    #[inline(always)]
176    pub async fn send(&self, value: T) -> Result<(), SendError<T>> {
177        let mut value = Some(value);
178        std::future::poll_fn(|cx| self.poll_send(cx, &mut value)).await
179    }
180
181    fn poll_send(&self, cx: &Context<'_>, value: &mut Option<T>) -> Poll<Result<(), SendError<T>>> {
182        if self.shared.is_closed_for_send() {
183            return Poll::Ready(Err(SendError(value.take().expect("value present"))));
184        }
185
186        if !self.shared.send_wait.has_waiters() {
187            match self
188                .shared
189                .queue
190                .try_push(value.take().expect("value present"))
191            {
192                Ok(()) => {
193                    // Only wake the receiver if it is actually parked waiting for data
194                    if self.shared.recv_wait.has_waiters() {
195                        self.shared.recv_wait.wake_one();
196                    }
197                    return Poll::Ready(Ok(()));
198                }
199                Err(v) => *value = Some(v),
200            }
201        }
202
203        let token = self.shared.send_wait.register(cx.waker());
204        if self.shared.is_closed_for_send() {
205            self.shared.send_wait.cancel(token);
206            return Poll::Ready(Err(SendError(value.take().expect("value present"))));
207        }
208
209        match self
210            .shared
211            .queue
212            .try_push(value.take().expect("value present"))
213        {
214            Ok(()) => {
215                self.shared.send_wait.cancel(token);
216                // Slow-path success: check if receiver needs a wake
217                if self.shared.recv_wait.has_waiters() {
218                    self.shared.recv_wait.wake_one();
219                }
220                Poll::Ready(Ok(()))
221            }
222            Err(v) => {
223                *value = Some(v);
224                Poll::Pending
225            }
226        }
227    }
228
229    /// `true` once the receiver has been dropped — a subsequent
230    /// [`send`](Self::send) is guaranteed to fail.
231    #[must_use]
232    #[inline(always)]
233    pub fn is_closed(&self) -> bool {
234        self.shared.is_closed_for_send()
235    }
236}
237
238/// The receiving half of a bounded [`channel`].
239#[repr(align(64))]
240pub struct Receiver<T> {
241    shared: Arc<Shared<T>>,
242    /// Local FIFO-order refill buffer for the unbounded variant's
243    /// stack-backed queue — see [`Queue`]'s doc. Always empty and unused
244    /// for the bounded variant. Exclusive to this `Receiver` (never
245    /// touched by any other thread — `Receiver` isn't `Clone`), so a
246    /// plain `RefCell` (no atomics needed) is sound even though `recv`
247    /// only takes `&mut self` at the outer async-fn level, not down
248    /// through `poll_recv`/`try_pop`.
249    local: RefCell<VecDeque<T>>,
250}
251
252// SAFETY: `local` is only ever touched from inside `try_pop`, itself only
253// ever called (transitively) from `recv(&mut self)` — the `&mut`
254// exclusive borrow that async fn holds for its whole duration is the
255// actual guarantee that no two calls into `local` overlap, even though
256// `poll_recv`'s `&self` signature (required by `std::future::poll_fn`)
257// means the auto-trait deriver can't see that exclusivity itself; `Sync`
258// just needs to be true, and it is, by construction. `RefCell` still
259// panics (rather than silently racing) if this invariant is ever somehow
260// violated, rather than relying solely on this unsafe impl.
261unsafe impl<T: Send> Sync for Receiver<T> {}
262
263impl<T> Drop for Receiver<T> {
264    #[inline(always)]
265    fn drop(&mut self) {
266        self.shared.receiver_dropped.store(true, Ordering::Release);
267        self.shared.send_wait.wake_all();
268    }
269}
270
271impl<T> Receiver<T> {
272    /// Receive the next message, waiting if the channel is currently
273    /// empty. Returns `None` once every [`Sender`] has been dropped and
274    /// the buffer is drained.
275    #[inline(always)]
276    pub async fn recv(&mut self) -> Option<T> {
277        std::future::poll_fn(|cx| self.poll_recv(cx)).await
278    }
279
280    #[inline]
281    fn poll_recv(&self, cx: &Context<'_>) -> Poll<Option<T>> {
282        if let Some(v) = self.try_pop() {
283            return Poll::Ready(Some(v));
284        }
285        if self.shared.is_closed_for_recv() {
286            // The last sender could have pushed its final item and then
287            // dropped (closing the channel) concurrently with the
288            // `try_pop` above finding it momentarily empty — one more
289            // drain attempt here catches that item before reporting the
290            // channel exhausted, rather than silently losing it.
291            return Poll::Ready(self.try_pop());
292        }
293        let token = self.shared.recv_wait.register(cx.waker());
294        if let Some(v) = self.try_pop() {
295            self.shared.recv_wait.cancel(token);
296            return Poll::Ready(Some(v));
297        }
298        if self.shared.is_closed_for_recv() {
299            let v = self.try_pop();
300            self.shared.recv_wait.cancel(token);
301            return Poll::Ready(v);
302        }
303        Poll::Pending
304    }
305
306    #[inline]
307    fn try_pop(&self) -> Option<T> {
308        let v = match &self.shared.queue {
309            Queue::Bounded(q) => q.try_pop(),
310            Queue::Unbounded(q) => {
311                let mut local = self.local.borrow_mut();
312                local.pop_front().map_or_else(
313                    || {
314                        // Zero-allocation refill optimization
315                        q.drain_into_vec_deque(&mut local);
316                        local.pop_front()
317                    },
318                    Some,
319                )
320            }
321        };
322
323        // Only wake a blocked sender if an item was actually removed
324        // AND there are senders currently parked waiting for a slot.
325        if v.is_some() && self.shared.send_wait.has_waiters() {
326            self.shared.send_wait.wake_one();
327        }
328        v
329    }
330}
331
332/// The sending half of an [`unbounded_channel`]. Cheaply [`Clone`]-able.
333#[repr(align(64))]
334pub struct UnboundedSender<T> {
335    inner: Sender<T>,
336}
337
338impl<T> Clone for UnboundedSender<T> {
339    #[inline(always)]
340    fn clone(&self) -> Self {
341        Self {
342            inner: self.inner.clone(),
343        }
344    }
345}
346
347impl<T> UnboundedSender<T> {
348    /// Send `value`. Never waits — the unbounded channel has no capacity
349    /// limit, so this is a plain (non-`async`) method, matching
350    /// `tokio::sync::mpsc::UnboundedSender::send`.
351    ///
352    /// # Errors
353    /// Returns `value` back in [`SendError`] if the receiver has been
354    /// dropped.
355    #[inline(always)]
356    pub fn send(&self, value: T) -> Result<(), SendError<T>> {
357        if self.inner.shared.is_closed_for_send() {
358            return Err(SendError(value));
359        }
360        // Infallible for the unbounded (stack-backed) queue variant.
361        self.inner
362            .shared
363            .queue
364            .try_push(value)
365            .unwrap_or_else(|_| unreachable!("unbounded queue variant never rejects a push"));
366        self.inner.shared.recv_wait.wake_one();
367        Ok(())
368    }
369
370    /// `true` once the receiver has been dropped.
371    #[must_use]
372    #[inline(always)]
373    pub fn is_closed(&self) -> bool {
374        self.inner.is_closed()
375    }
376}
377
378/// The receiving half of an [`unbounded_channel`].
379#[repr(align(64))]
380pub struct UnboundedReceiver<T> {
381    inner: Receiver<T>,
382}
383
384impl<T> UnboundedReceiver<T> {
385    /// Receive the next message, waiting if the channel is currently
386    /// empty. Returns `None` once every sender has been dropped and the
387    /// buffer is drained.
388    #[inline(always)]
389    pub async fn recv(&mut self) -> Option<T> {
390        self.inner.recv().await
391    }
392}
393
394/// Error returned when sending on a channel whose receiver has been
395/// dropped. Carries the value that couldn't be delivered back to the
396/// caller.
397#[derive(Debug, Clone, Copy, PartialEq, Eq)]
398#[repr(align(64))]
399pub struct SendError<T>(pub T);
400
401impl<T> std::fmt::Display for SendError<T> {
402    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
403        f.write_str("channel closed: receiver dropped")
404    }
405}
406
407impl<T: std::fmt::Debug> std::error::Error for SendError<T> {}