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> {}