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}