Skip to main content

nuclei/sync/
broadcast.rs

1//! A multi-producer, multi-consumer broadcast channel: each value sent
2//! reaches every receiver that exists when it is sent.
3//!
4//! [`channel`] returns a [`Sender`] and a [`Receiver`] around a capacity.
5//! [`Sender::send`] puts a value in the channel for every receiver; each
6//! [`Receiver`] takes the values in order with [`Receiver::recv`], cloning
7//! them (`T: Clone`). The channel holds the last `capacity` values (rounded
8//! up to a power of two): a receiver that falls further behind loses the
9//! oldest ones, and its next receive reports how many with
10//! [`RecvError::Lagged`] before it goes on from the oldest value still
11//! held. The channel closes once every sender is gone: receivers take what
12//! is left, then get [`RecvError::Closed`]. The API is the established
13//! async broadcast-channel API; nothing here needs a runtime.
14//!
15//! ```
16//! use nuclei::sync::broadcast;
17//!
18//! let (tx, mut rx1) = broadcast::channel(16);
19//! let mut rx2 = tx.subscribe();
20//! tx.send(10).unwrap();
21//! tx.send(20).unwrap();
22//! assert_eq!(rx1.try_recv(), Ok(10));
23//! assert_eq!(rx2.try_recv(), Ok(10));
24//! assert_eq!(rx1.try_recv(), Ok(20));
25//! ```
26//!
27//! # Values live in nodes published lock-free
28//!
29//! The channel is a ring of `capacity` slots, each an atomic pointer
30//! holding the node of the latest value sent to it with that value's
31//! position. A sender takes the next position with one `fetch_add`,
32//! publishes its node into the position's slot with a compare-and-swap that
33//! never replaces a later position, and retires the node it replaced. A
34//! receive is a protected load of its slot and a clone of the value: it
35//! writes nothing another thread reads, so receivers never contend with
36//! one another or with the senders. Two consequences, both differing from
37//! the lock-based API:
38//!
39//! - The channel needs `T: Send + Sync + 'static`: values are shared by
40//!   reference between threads and reclaimed later.
41//! - A value is dropped when a later send takes its slot (or the channel
42//!   goes), not as soon as every receiver has taken it: the channel holds
43//!   up to `capacity` values whatever the receivers have read. [`Sender::len`]
44//!   still counts what the slowest receiver has yet to take.
45//!
46//! Under the `recycle` feature its nodes come from a per-thread cache instead
47//! of the global allocator.
48//!
49//! # Progress
50//!
51//! - Wait-free: [`Receiver::try_recv`], [`Receiver::len`],
52//!   [`Receiver::is_empty`], [`Sender::receiver_count`],
53//!   [`Sender::same_channel`], [`Sender::subscribe`],
54//!   [`Receiver::resubscribe`], [`Sender::downgrade`], the sender counts,
55//!   and the clones and drops but the last sender's and the last
56//!   receiver's.
57//! - Lock-free: [`Sender::send`] (its compare-and-swap is retried only if a
58//!   send a whole ring later published into the same slot meanwhile, and it
59//!   ends by waking the waiting receivers through
60//!   [`Notify::notify_waiters`](super::Notify), which is lock-free);
61//!   [`Sender::len`] and [`Sender::is_empty`] (they look at every
62//!   receiver); polling [`Receiver::recv`] and [`Sender::closed`] (a poll
63//!   may queue a waiter); [`WeakSender::upgrade`] (a compare-and-swap on
64//!   the sender count, retried when a sender was cloned or dropped
65//!   meanwhile); and the last sender's or receiver's drop, for its wake.
66
67#![allow(unsafe_code)]
68
69use core::fmt;
70
71use kovan::{
72    Atomic, CachePadded, RetiredNode, Shared as Ptr, alloc_node, drop_node, pin, retire_node,
73};
74
75use super::Notify;
76use super::internal::{Arc, AtomicBool, AtomicU64, AtomicUsize, Ordering};
77
78pub use self::error::{RecvError, SendError, TryRecvError};
79
80pub mod error {
81    //! Error types of the broadcast channel.
82
83    use core::fmt;
84    use std::error::Error;
85
86    /// Error produced when sending a value fails: there is no receiver.
87    ///
88    /// The value is handed back in the error.
89    #[derive(PartialEq, Eq, Clone, Copy)]
90    pub struct SendError<T>(pub T);
91
92    impl<T> fmt::Debug for SendError<T> {
93        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94            f.debug_struct("SendError").finish_non_exhaustive()
95        }
96    }
97
98    impl<T> fmt::Display for SendError<T> {
99        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
100            write!(fmt, "channel closed")
101        }
102    }
103
104    impl<T> Error for SendError<T> {}
105
106    /// Error produced when a receive fails.
107    #[derive(Debug, PartialEq, Eq, Clone)]
108    pub enum RecvError {
109        /// Every sender is gone and every value was taken.
110        Closed,
111        /// The receiver fell behind by this many values, which are lost;
112        /// the next receive takes the oldest value the channel still holds.
113        Lagged(u64),
114    }
115
116    impl fmt::Display for RecvError {
117        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
118            match self {
119                RecvError::Closed => write!(fmt, "channel closed"),
120                RecvError::Lagged(missed) => write!(fmt, "channel lagged by {missed}"),
121            }
122        }
123    }
124
125    impl Error for RecvError {}
126
127    /// Error produced when a receive that does not wait fails.
128    #[derive(Debug, PartialEq, Eq, Clone)]
129    pub enum TryRecvError {
130        /// No value has come since the last one this receiver took.
131        Empty,
132        /// Every sender is gone and every value was taken.
133        Closed,
134        /// The receiver fell behind by this many values, which are lost;
135        /// the next receive takes the oldest value the channel still holds.
136        Lagged(u64),
137    }
138
139    impl fmt::Display for TryRecvError {
140        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
141            match self {
142                TryRecvError::Empty => write!(fmt, "channel empty"),
143                TryRecvError::Closed => write!(fmt, "channel closed"),
144                TryRecvError::Lagged(missed) => write!(fmt, "channel lagged by {missed}"),
145            }
146        }
147    }
148
149    impl Error for TryRecvError {}
150}
151
152/// A value in the ring, with its position.
153#[repr(C)]
154struct Node<T> {
155    retired: RetiredNode,
156    pos: u64,
157    value: T,
158}
159
160/// Where a receiver is: the position of the next value it takes. Only its
161/// receiver writes it; [`Sender::len`] reads every one.
162struct Cursor {
163    next: AtomicU64,
164}
165
166struct Shared<T: Send + Sync + 'static> {
167    /// The slots, each the node of the latest position sent to it.
168    slots: Box<[Atomic<Node<T>>]>,
169    /// `slots.len() - 1`: a position's slot is `pos & mask`.
170    mask: u64,
171    /// The next position a send takes. Padded: every send writes it.
172    tail: CachePadded<AtomicU64>,
173    /// Every sender is gone.
174    closed: AtomicBool,
175    /// How many [`Sender`]s exist. Once zero it stays zero: a sender is
176    /// made only from another, or by an upgrade that finds one.
177    senders: AtomicUsize,
178    /// How many [`WeakSender`]s exist.
179    weak_senders: AtomicUsize,
180    /// The receivers' cursors, by id; their count is the receiver count.
181    cursors: kovan_map::HashMap<u64, Arc<Cursor>>,
182    receivers: AtomicUsize,
183    /// Wakes receivers waiting for a value or the close.
184    notify_rx: Notify,
185    /// Wakes senders waiting for the receivers to go.
186    notify_tx: Notify,
187}
188
189// SAFETY: the slots' nodes hold `T`s that are moved between threads and
190// read by reference from several at once: `T: Send + Sync` covers both.
191unsafe impl<T: Send + Sync + 'static> Send for Shared<T> {}
192// SAFETY: as above.
193unsafe impl<T: Send + Sync + 'static> Sync for Shared<T> {}
194
195impl<T: Send + Sync + 'static> Shared<T> {
196    fn capacity(&self) -> u64 {
197        self.mask + 1
198    }
199
200    /// A new receiver, which takes the values sent from now on.
201    fn subscribe(shared: &Arc<Self>) -> Receiver<T> {
202        // The ids only tell receivers apart, so the counter is std's even
203        // under loom, which has no const constructor for a static.
204        static IDS: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(0);
205        let id = IDS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
206        // SeqCst: against a send's claim of its position (also SeqCst), so
207        // the value of a position at or past this one reaches the receiver.
208        let cursor = Arc::new(Cursor {
209            next: AtomicU64::new(shared.tail.load(Ordering::SeqCst)),
210        });
211        shared.receivers.fetch_add(1, Ordering::SeqCst);
212        shared.cursors.insert(id, Arc::clone(&cursor));
213        Receiver {
214            shared: Arc::clone(shared),
215            id,
216            cursor,
217        }
218    }
219}
220
221impl<T: Send + Sync + 'static> Drop for Shared<T> {
222    fn drop(&mut self) {
223        let guard = pin();
224        for slot in self.slots.iter() {
225            let node = slot.load(Ordering::Acquire, &guard);
226            if !node.is_null() {
227                // SAFETY: no handle is left, so no thread can reach the slot,
228                // and the node came from `alloc_node`; let go once, here.
229                unsafe { drop_node(node.as_raw()) };
230            }
231        }
232    }
233}
234
235/// Creates a broadcast channel holding the last `capacity` values (rounded
236/// up to a power of two), returning a sender and a first receiver.
237///
238/// # Panics
239///
240/// When `capacity` is zero, or above `usize::MAX / 2`.
241///
242/// ```
243/// use nuclei::sync::broadcast;
244///
245/// let (tx, mut rx) = broadcast::channel::<u32>(4);
246/// tx.send(1).unwrap();
247/// assert_eq!(rx.try_recv(), Ok(1));
248/// ```
249pub fn channel<T: Clone + Send + Sync + 'static>(capacity: usize) -> (Sender<T>, Receiver<T>) {
250    let tx = Sender::new(capacity);
251    let rx = tx.subscribe();
252    (tx, rx)
253}
254
255/// Sends values to every [`Receiver`] of the channel.
256///
257/// Created by [`channel`]. Clone it for more producers. The channel closes
258/// for the receivers when the last sender is dropped.
259pub struct Sender<T: Send + Sync + 'static> {
260    shared: Arc<Shared<T>>,
261}
262
263/// Receives the values of the channel, in order.
264///
265/// Created by [`channel`], [`Sender::subscribe`] or
266/// [`Receiver::resubscribe`]; each takes every value sent after it was made.
267pub struct Receiver<T: Send + Sync + 'static> {
268    shared: Arc<Shared<T>>,
269    id: u64,
270    cursor: Arc<Cursor>,
271}
272
273/// A sender that does not keep the channel open.
274///
275/// Made by [`Sender::downgrade`]. Once every [`Sender`] is gone the channel
276/// is closed, whatever `WeakSender`s are left. A `WeakSender` sends nothing
277/// itself: [`upgrade`](Self::upgrade) makes a [`Sender`] of it while one
278/// exists.
279///
280/// ```
281/// use nuclei::sync::broadcast;
282///
283/// let (tx, _rx) = broadcast::channel::<u8>(4);
284/// let weak = tx.downgrade();
285/// assert!(weak.upgrade().is_some());
286/// drop(tx);
287/// assert!(weak.upgrade().is_none());
288/// ```
289pub struct WeakSender<T: Send + Sync + 'static> {
290    shared: Arc<Shared<T>>,
291}
292
293impl<T: Send + Sync + 'static> Sender<T> {
294    /// A sender of a new channel holding the last `capacity` values (rounded
295    /// up to a power of two), with no receiver yet: each comes from
296    /// [`subscribe`](Self::subscribe).
297    ///
298    /// # Panics
299    ///
300    /// When `capacity` is zero, or above `usize::MAX / 2`.
301    ///
302    /// ```
303    /// use nuclei::sync::broadcast::Sender;
304    ///
305    /// let tx = Sender::new(4);
306    /// assert!(tx.send(1).is_err(), "no receiver yet");
307    /// let mut rx = tx.subscribe();
308    /// tx.send(2).unwrap();
309    /// assert_eq!(rx.try_recv(), Ok(2));
310    /// ```
311    pub fn new(capacity: usize) -> Self {
312        assert!(
313            capacity > 0,
314            "a broadcast channel's capacity cannot be zero"
315        );
316        assert!(
317            capacity <= usize::MAX >> 1,
318            "a broadcast channel's capacity cannot exceed usize::MAX / 2"
319        );
320        let capacity = capacity.next_power_of_two();
321        Self {
322            shared: Arc::new(Shared {
323                slots: (0..capacity).map(|_| Atomic::null()).collect(),
324                mask: capacity as u64 - 1,
325                tail: CachePadded::new(AtomicU64::new(0)),
326                closed: AtomicBool::new(false),
327                senders: AtomicUsize::new(1),
328                weak_senders: AtomicUsize::new(0),
329                // 64 is the smallest table the map accepts.
330                cursors: kovan_map::HashMap::with_capacity(64),
331                receivers: AtomicUsize::new(0),
332                notify_rx: Notify::new(),
333                notify_tx: Notify::new(),
334            }),
335        }
336    }
337
338    /// Sends `value` to every receiver there is now, returning how many.
339    ///
340    /// A send never waits: the channel holds the last `capacity` values, and
341    /// a receiver too far behind loses the oldest.
342    ///
343    /// # Errors
344    ///
345    /// When there is no receiver; the value is handed back.
346    ///
347    /// ```
348    /// use nuclei::sync::broadcast;
349    ///
350    /// let (tx, rx) = broadcast::channel(2);
351    /// assert_eq!(tx.send("a"), Ok(1));
352    /// drop(rx);
353    /// assert_eq!(tx.send("b").unwrap_err().0, "b");
354    /// ```
355    pub fn send(&self, value: T) -> Result<usize, SendError<T>> {
356        let shared = &*self.shared;
357        let receivers = shared.receivers.load(Ordering::SeqCst);
358        if receivers == 0 {
359            return Err(SendError(value));
360        }
361        let pos = shared.tail.fetch_add(1, Ordering::SeqCst);
362        let slot = &shared.slots[(pos & shared.mask) as usize];
363        let node = alloc_node(Node {
364            retired: RetiredNode::new(),
365            pos,
366            value,
367        });
368        let guard = pin();
369        let mut current = slot.load(Ordering::Acquire, &guard);
370        loop {
371            // SAFETY: the guard keeps the slot's node alive while it is read.
372            if unsafe { current.as_ref() }.is_some_and(|old| old.pos > pos) {
373                // A send a whole ring later took the slot first: every
374                // receiver is past this value already, which goes now.
375                // SAFETY: never published; ours alone.
376                unsafe { drop_node(node) };
377                break;
378            }
379            // SAFETY: a fresh node, published here once.
380            let new = unsafe { Ptr::from_raw(node) };
381            match slot.compare_exchange(current, new, Ordering::AcqRel, Ordering::Acquire, &guard) {
382                Ok(old) => {
383                    if !old.is_null() {
384                        // SAFETY: the swap unlinked `old`; retired once, here.
385                        unsafe { retire_node(old.as_raw()) };
386                    }
387                    break;
388                }
389                Err(now) => current = now,
390            }
391        }
392        drop(guard);
393        shared.notify_rx.notify_waiters();
394        Ok(receivers)
395    }
396
397    /// A new receiver, which takes the values sent from now on.
398    ///
399    /// ```
400    /// use nuclei::sync::broadcast;
401    ///
402    /// let (tx, _rx) = broadcast::channel(2);
403    /// tx.send(1).unwrap();
404    /// let mut late = tx.subscribe();
405    /// tx.send(2).unwrap();
406    /// assert_eq!(late.try_recv(), Ok(2));
407    /// ```
408    pub fn subscribe(&self) -> Receiver<T> {
409        Shared::subscribe(&self.shared)
410    }
411
412    /// How many receivers there are.
413    pub fn receiver_count(&self) -> usize {
414        self.shared.receivers.load(Ordering::SeqCst)
415    }
416
417    /// A [`WeakSender`] of the channel, which does not keep it open. The
418    /// sender itself stays.
419    #[must_use = "a WeakSender that is not kept does nothing"]
420    pub fn downgrade(&self) -> WeakSender<T> {
421        // Relaxed: a plain counter.
422        self.shared.weak_senders.fetch_add(1, Ordering::Relaxed);
423        WeakSender {
424            shared: Arc::clone(&self.shared),
425        }
426    }
427
428    /// How many [`Sender`]s exist.
429    pub fn strong_count(&self) -> usize {
430        self.shared.senders.load(Ordering::Acquire)
431    }
432
433    /// How many [`WeakSender`]s exist.
434    pub fn weak_count(&self) -> usize {
435        self.shared.weak_senders.load(Ordering::Relaxed)
436    }
437
438    /// How many values the slowest receiver has yet to take, at most the
439    /// capacity (older ones are lost to it); zero with no receiver.
440    ///
441    /// ```
442    /// use nuclei::sync::broadcast;
443    ///
444    /// let (tx, mut rx) = broadcast::channel(4);
445    /// tx.send(1).unwrap();
446    /// tx.send(2).unwrap();
447    /// assert_eq!(tx.len(), 2);
448    /// rx.try_recv().unwrap();
449    /// assert_eq!(tx.len(), 1);
450    /// ```
451    pub fn len(&self) -> usize {
452        let shared = &*self.shared;
453        let tail = shared.tail.load(Ordering::SeqCst);
454        let behind = shared
455            .cursors
456            .iter()
457            .map(|(_, cursor)| tail.saturating_sub(cursor.next.load(Ordering::Acquire)))
458            .max()
459            .unwrap_or(0);
460        behind.min(shared.capacity()) as usize
461    }
462
463    /// Whether every receiver took every value it can still take.
464    pub fn is_empty(&self) -> bool {
465        self.len() == 0
466    }
467
468    /// Whether `other` sends on the same channel.
469    pub fn same_channel(&self, other: &Self) -> bool {
470        Arc::ptr_eq(&self.shared, &other.shared)
471    }
472
473    /// Completes once there is no receiver.
474    ///
475    /// ```
476    /// use core::future::Future;
477    /// use core::pin::pin;
478    /// use core::task::{Context, Poll, Waker};
479    /// use nuclei::sync::broadcast;
480    ///
481    /// let (tx, rx) = broadcast::channel::<u8>(1);
482    /// let mut cx = Context::from_waker(Waker::noop());
483    /// let mut closed = pin!(tx.closed());
484    /// assert_eq!(closed.as_mut().poll(&mut cx), Poll::Pending);
485    /// drop(rx);
486    /// assert_eq!(closed.as_mut().poll(&mut cx), Poll::Ready(()));
487    /// ```
488    pub async fn closed(&self) {
489        while self.receiver_count() > 0 {
490            // Made before the count is read again: the last receiver's drop
491            // wakes it after its decrement.
492            let notified = self.shared.notify_tx.notified();
493            if self.receiver_count() == 0 {
494                return;
495            }
496            notified.await;
497        }
498    }
499}
500
501impl<T: Send + Sync + 'static> Clone for Sender<T> {
502    fn clone(&self) -> Self {
503        // Relaxed: a plain counter, and the clone proves a sender exists.
504        self.shared.senders.fetch_add(1, Ordering::Relaxed);
505        Self {
506            shared: Arc::clone(&self.shared),
507        }
508    }
509}
510
511impl<T: Send + Sync + 'static> Drop for Sender<T> {
512    fn drop(&mut self) {
513        // AcqRel: the thread that drops the last sender sees every earlier
514        // sender's sends before it closes the channel.
515        if self.shared.senders.fetch_sub(1, Ordering::AcqRel) == 1 {
516            self.shared.closed.store(true, Ordering::SeqCst);
517            self.shared.notify_rx.notify_waiters();
518        }
519    }
520}
521
522impl<T: Send + Sync + 'static> fmt::Debug for Sender<T> {
523    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524        f.debug_struct("Sender")
525            .field("receivers", &self.receiver_count())
526            .finish_non_exhaustive()
527    }
528}
529
530impl<T: Send + Sync + 'static> WeakSender<T> {
531    /// A [`Sender`] of the channel, while one exists; `None` once the last
532    /// is gone (the channel is closed then, for good).
533    pub fn upgrade(&self) -> Option<Sender<T>> {
534        let senders = &self.shared.senders;
535        // Acquire on a success, as a clone's: the new sender's sends come
536        // after the close only if no sender was left, which this rules out.
537        let mut count = senders.load(Ordering::Acquire);
538        while count > 0 {
539            match senders.compare_exchange_weak(
540                count,
541                count + 1,
542                Ordering::AcqRel,
543                Ordering::Acquire,
544            ) {
545                Ok(_) => {
546                    return Some(Sender {
547                        shared: Arc::clone(&self.shared),
548                    });
549                }
550                Err(now) => count = now,
551            }
552        }
553        None
554    }
555
556    /// How many [`Sender`]s exist.
557    pub fn strong_count(&self) -> usize {
558        self.shared.senders.load(Ordering::Acquire)
559    }
560
561    /// How many [`WeakSender`]s exist.
562    pub fn weak_count(&self) -> usize {
563        self.shared.weak_senders.load(Ordering::Relaxed)
564    }
565}
566
567impl<T: Send + Sync + 'static> Clone for WeakSender<T> {
568    fn clone(&self) -> Self {
569        self.shared.weak_senders.fetch_add(1, Ordering::Relaxed);
570        Self {
571            shared: Arc::clone(&self.shared),
572        }
573    }
574}
575
576impl<T: Send + Sync + 'static> Drop for WeakSender<T> {
577    fn drop(&mut self) {
578        self.shared.weak_senders.fetch_sub(1, Ordering::Relaxed);
579    }
580}
581
582impl<T: Send + Sync + 'static> fmt::Debug for WeakSender<T> {
583    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
584        f.debug_struct("WeakSender").finish_non_exhaustive()
585    }
586}
587
588impl<T: Send + Sync + 'static> Receiver<T> {
589    fn next(&self) -> u64 {
590        // Relaxed: only this receiver writes it.
591        self.cursor.next.load(Ordering::Relaxed)
592    }
593
594    fn advance(&self, next: u64) {
595        // Release: `Sender::len` reads where the receiver is.
596        self.cursor.next.store(next, Ordering::Release);
597    }
598
599    /// How many values were sent that this receiver has yet to take; past
600    /// the capacity, its next receive reports a lag.
601    pub fn len(&self) -> usize {
602        let tail = self.shared.tail.load(Ordering::SeqCst);
603        tail.saturating_sub(self.next()) as usize
604    }
605
606    /// Whether this receiver took every value sent.
607    pub fn is_empty(&self) -> bool {
608        self.len() == 0
609    }
610
611    /// A new receiver, which takes the values sent from now on.
612    pub fn resubscribe(&self) -> Self {
613        Shared::subscribe(&self.shared)
614    }
615}
616
617impl<T: Clone + Send + Sync + 'static> Receiver<T> {
618    /// Takes the next value, if one came, without waiting.
619    ///
620    /// # Errors
621    ///
622    /// [`TryRecvError::Empty`] when none came since the last one taken;
623    /// [`TryRecvError::Lagged`] once, when the receiver lost values, before
624    /// it goes on from the oldest one still held; [`TryRecvError::Closed`]
625    /// once every sender is gone and every value was taken.
626    ///
627    /// ```
628    /// use nuclei::sync::broadcast::{self, error::TryRecvError};
629    ///
630    /// let (tx, mut rx) = broadcast::channel(2);
631    /// for i in 0..3 {
632    ///     tx.send(i).unwrap();
633    /// }
634    /// assert_eq!(rx.try_recv(), Err(TryRecvError::Lagged(1)));
635    /// assert_eq!(rx.try_recv(), Ok(1));
636    /// assert_eq!(rx.try_recv(), Ok(2));
637    /// assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
638    /// drop(tx);
639    /// assert_eq!(rx.try_recv(), Err(TryRecvError::Closed));
640    /// ```
641    pub fn try_recv(&mut self) -> Result<T, TryRecvError> {
642        self.try_recv_if(|| true).map_err(|look| match look {
643            Look::Lagged(missed) => TryRecvError::Lagged(missed),
644            Look::Closed => TryRecvError::Closed,
645            // An admit that always says yes never refuses.
646            Look::Empty | Look::Refused => TryRecvError::Empty,
647        })
648    }
649
650    /// [`try_recv`](Self::try_recv) that asks `admit` once the receive would
651    /// complete (a value, a lag, the end) and before it moves on: a `false`
652    /// leaves the receiver where it was.
653    fn try_recv_if(&mut self, admit: impl FnOnce() -> bool) -> Result<T, Look> {
654        let shared = &*self.shared;
655        let next = self.next();
656        let guard = pin();
657        let slot = &shared.slots[(next & shared.mask) as usize];
658        // SAFETY: the guard keeps the node alive while it is read.
659        match unsafe { slot.load(Ordering::Acquire, &guard).as_ref() } {
660            Some(node) if node.pos == next => {
661                if !admit() {
662                    return Err(Look::Refused);
663                }
664                let value = node.value.clone();
665                drop(guard);
666                self.advance(next + 1);
667                return Ok(value);
668            }
669            Some(node) if node.pos > next => {
670                drop(guard);
671                if !admit() {
672                    return Err(Look::Refused);
673                }
674                // A later position took the slot: positions before the oldest
675                // the ring can hold are lost. That one is past `next`, since
676                // the slot's position is at least a ring past it and below
677                // the tail.
678                let oldest = shared
679                    .tail
680                    .load(Ordering::SeqCst)
681                    .saturating_sub(shared.capacity());
682                self.advance(oldest);
683                return Err(Look::Lagged(oldest - next));
684            }
685            // Not sent yet, or sent and not published yet.
686            _ => {}
687        }
688        drop(guard);
689        // SeqCst, the flag before the tail: the last sender's close comes
690        // after every send, so with the flag seen every position below the
691        // tail is published, and one at `next` would have been found.
692        if shared.closed.load(Ordering::SeqCst) && shared.tail.load(Ordering::SeqCst) <= next {
693            return Err(if admit() { Look::Closed } else { Look::Refused });
694        }
695        Err(Look::Empty)
696    }
697
698    /// Waits for the next value and takes it.
699    ///
700    /// # Errors
701    ///
702    /// [`RecvError::Lagged`] once, when the receiver lost values, before it
703    /// goes on from the oldest one still held; [`RecvError::Closed`] once
704    /// every sender is gone and every value was taken.
705    ///
706    /// # Cancel safety
707    ///
708    /// Dropping the future takes nothing: the next receive gets the value.
709    ///
710    /// # Cooperative scheduling
711    ///
712    /// On a nuclei shard a receive that completes spends one operation of
713    /// its task's budget; with the budget spent it takes nothing and yields
714    /// to the shard's other tasks first (see `nuclei::task::coop`).
715    pub async fn recv(&mut self) -> Result<T, RecvError> {
716        let shared = Arc::clone(&self.shared);
717        loop {
718            // Made before the look: a send after the look wakes it (see
719            // `Notify`: a `Notified` hears every `notify_waiters` made after
720            // its creation).
721            let notified = shared.notify_rx.notified();
722            match self.try_recv_if(crate::coop::try_spend) {
723                Ok(value) => return Ok(value),
724                Err(Look::Lagged(missed)) => return Err(RecvError::Lagged(missed)),
725                Err(Look::Closed) => return Err(RecvError::Closed),
726                Err(Look::Empty) => notified.await,
727                Err(Look::Refused) => crate::coop::yield_spent().await,
728            }
729        }
730    }
731
732    /// Receives the next value from a thread that may block.
733    ///
734    /// Like [`recv`](Self::recv), but it parks the calling thread while no
735    /// value came and the channel is open. On a nuclei shard (inside one of
736    /// its tasks) it does not park the thread: it runs a nested `block_on`,
737    /// so the shard keeps running its other tasks meanwhile. Inside another
738    /// runtime's task it blocks that task's thread.
739    ///
740    /// # Errors
741    ///
742    /// As for [`recv`](Self::recv).
743    ///
744    /// # Panics
745    ///
746    /// On `wasm32`, where no other thread can send, when no value came and
747    /// the channel is open: the call could never end.
748    ///
749    /// ```
750    /// use nuclei::sync::broadcast::{self, RecvError};
751    ///
752    /// let (tx, mut rx) = broadcast::channel::<u8>(4);
753    /// let receiver = std::thread::spawn(move || {
754    ///     assert_eq!(rx.blocking_recv(), Ok(10));
755    ///     assert_eq!(rx.blocking_recv(), Err(RecvError::Closed));
756    /// });
757    /// tx.send(10).unwrap();
758    /// drop(tx);
759    /// receiver.join().unwrap();
760    /// ```
761    pub fn blocking_recv(&mut self) -> Result<T, RecvError> {
762        super::internal::blocking(self.recv())
763    }
764}
765
766/// What a receiver's look came to, short of a value.
767enum Look {
768    Empty,
769    Lagged(u64),
770    Closed,
771    /// The look's `admit` turned the receive down: nothing moved.
772    Refused,
773}
774
775impl<T: Send + Sync + 'static> Drop for Receiver<T> {
776    fn drop(&mut self) {
777        self.shared.cursors.remove(&self.id);
778        if self.shared.receivers.fetch_sub(1, Ordering::SeqCst) == 1 {
779            self.shared.notify_tx.notify_waiters();
780        }
781    }
782}
783
784impl<T: Send + Sync + 'static> fmt::Debug for Receiver<T> {
785    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
786        f.debug_struct("Receiver")
787            .field("next", &self.next())
788            .finish_non_exhaustive()
789    }
790}