Skip to main content

nuclei/sync/
mpsc.rs

1//! Multi-producer, single-consumer channels for sending values between tasks.
2//!
3//! The module has two flavors. The bounded one ([`channel`]) holds at most a
4//! fixed number of messages, and a send waits for room, which is backpressure.
5//! The unbounded one ([`unbounded_channel`]) never makes a send wait, so
6//! [`UnboundedSender::send`] is an ordinary function that synchronous and
7//! asynchronous code can both call. The names, signatures, error types and
8//! documented behavior are those of the established async channel API, so code
9//! written against that API compiles against these; nothing here needs a
10//! runtime, and every wait is a plain [`Waker`](core::task::Waker) handshake.
11//!
12//! ```
13//! use nuclei::sync::mpsc;
14//!
15//! let (tx, mut rx) = mpsc::channel(8);
16//! let senders: Vec<_> = (0..4)
17//!     .map(|id| {
18//!         let tx = tx.clone();
19//!         std::thread::spawn(move || {
20//!             for n in 0..100 {
21//!                 tx.blocking_send((id, n)).expect("the receiver is still there");
22//!             }
23//!         })
24//!     })
25//!     .collect();
26//! drop(tx);
27//!
28//! let mut seen = [0; 4];
29//! while let Some((id, _n)) = rx.blocking_recv() {
30//!     seen[id] += 1;
31//! }
32//! assert_eq!(seen, [100; 4]);
33//! for sender in senders {
34//!     sender.join().unwrap();
35//! }
36//! ```
37//!
38//! # Ordering
39//!
40//! What one sender sends comes out in the order it was sent, as the
41//! established API promises. The message queue is a linearizable FIFO queue
42//! of cells: a send claims the next cell, and the receiver takes cells in the
43//! order they were claimed, so a send that returned before another began
44//! comes out first. Values that different senders send at the
45//! same time come out in the order their sends claimed cells.
46//!
47//! # Disconnection
48//!
49//! When every sender is dropped, no value can be sent. That is the end of the
50//! stream: once the buffered values are received, `recv` returns `None`.
51//!
52//! If the receiver is dropped, its unread values are dropped with it, and
53//! every later send fails with an error that holds the value.
54//!
55//! # Clean shutdown
56//!
57//! Dropping the receiver loses the values still buffered. For a clean
58//! shutdown the receiver calls `close` first, which stops further sends but
59//! keeps the buffered values (and any slot a sender reserved before) usable,
60//! then receives until `recv` returns `None`.
61//!
62//! # Weak senders
63//!
64//! A [`WeakSender`] does not keep the channel open: when only weak senders
65//! remain the channel is closed. [`WeakSender::upgrade`] gives a [`Sender`]
66//! while one exists. An actor can hold a weak handle to its own mailbox
67//! without keeping it alive.
68//!
69//! # Communicating between sync and async code
70//!
71//! Every method that is not `async` can be called from any thread. For
72//! synchronous code on the receiving or sending end, the `blocking_*` methods
73//! park the calling thread until their wait is over. On a nuclei shard they
74//! run a nested `block_on` instead, which keeps the shard running its other
75//! tasks; do not call them from a task of another runtime, whose thread they
76//! block.
77//!
78//! # Differences from the established API
79//!
80//! - [`Sender::send_timeout`] waits on nuclei's own timers: on a thread that
81//!   runs no nuclei shard, the default runtime fires it.
82//! - The `blocking_*` methods called from inside a task do not panic: on a
83//!   nuclei shard they run a nested `block_on`, and inside another runtime's
84//!   task they block its thread (on `wasm32`, where nothing else can run, a
85//!   call that would wait forever panics instead).
86//! - When the receiver closes, a reservation still waiting gives back the
87//!   slots set aside for it at the close, where the established API keeps
88//!   them with the failed wait until its future is dropped. So a closed
89//!   receiver's drain never waits for a failed reservation to be dropped.
90//! - [`Receiver::try_recv`] does not park the thread for a send that is in
91//!   progress on another thread. Such a value is not in the channel yet, and
92//!   its sender wakes the receiver when it is; the established API parks the
93//!   thread until the send finishes.
94//!
95//! # Cooperative scheduling
96//!
97//! On a nuclei shard, every receive that completes (`recv`, `recv_many` and
98//! the two `poll_` forms) spends one operation of its task's cooperative
99//! budget, as IO does, and so does a send or a reservation that completes
100//! at its first attempt; one whose slots were granted after it waited in
101//! line spends nothing, as giving them back would cost it its place. When
102//! the budget is spent the call takes nothing, wakes its task and returns
103//! `Pending`, so a loop on a channel that is always ready yields to the
104//! shard's other tasks within [`BUDGET`](crate::task::coop::BUDGET)
105//! operations. [`Receiver::try_recv`], [`Sender::try_send`] and the other
106//! calls that never wait spend nothing, and off a nuclei shard there is no
107//! budget: nothing yields for it.
108//!
109//! # Progress
110//!
111//! The message queue is built for its one receiver: a bounded channel's is a
112//! ring of cells used lap after lap (a radix directory past 65,536 cells), an
113//! unbounded channel's a radix directory of leaves. A send claims the next
114//! cell with one fetch-add, finds its segment (in the ring by its position, in
115//! the directory by at most one level per ten of a 64-bit position, making a
116//! missing node with one compare-and-swap that, lost, leaves another send's),
117//! writes its value and marks the cell ready: the push is wait-free. A receive
118//! reads the next cell's mark and takes the value, with no read-modify-write
119//! and nothing another thread contends for, and never waits for a sender:
120//! when the next cell is claimed by a send that has not written it yet, the
121//! receive finds nothing for now, registers its waker and returns `Pending`,
122//! and that send wakes it once its value is in. A sender the scheduler stops
123//! between its claim and its write holds back no one else's value in the
124//! radix directory, unless the position right after it is stalled too: the
125//! receive takes the ready values past it (within 64 positions), each
126//! sender's still in its order, and its own once written. In
127//! a bounded channel's ring the values behind it wait until it runs again, as
128//! its cell holds their place in the lap; no call waits for it either way.
129//!
130//! - **Wait-free** (a bounded number of the caller's own atomic steps, whatever
131//!   other threads do): the accessors (`is_closed`, `len`, `is_empty`,
132//!   `capacity`, `max_capacity`, the sender counts, `same_channel`), `clone`,
133//!   `downgrade`, and dropping a sender or weak sender (the last sender's
134//!   drop also wakes the receiver, a two-step handshake on the waker cell).
135//!   Registering the receiver's waker is wait-free too, and so is
136//!   [`UnboundedSender::send`]: its admission is one fetch-add (taken back
137//!   with one fetch-sub on a closed channel), its push the queue's.
138//! - **Lock-free**: every bounded send, `try_send`, reservation and permit
139//!   send, whose admission is the semaphore's compare-and-swap (exact: it
140//!   never refuses a free slot);
141//!   `closed`; `WeakSender::upgrade` (a compare-and-swap loop on the sender
142//!   count); and `Receiver::close`. A bounded wait that has to queue is
143//!   lock-free for the reason every wait queue is (see the
144//!   [parent module](super#progress)): its poll pushes a node, and may serve
145//!   the queue for one pass plus one for each operation of another thread
146//!   that lands meanwhile.
147//! - **When a waiting bounded send is served** is not a bound on its own
148//!   calls. Its slot is set aside and granted by whichever thread serves the
149//!   queue once the slot is free (the receiver taking a value, a permit
150//!   dropped), so a thread the scheduler stops while it serves the queue
151//!   delays that grant until it runs again, and a `try_send` fails meanwhile
152//!   even if the last waiting send has just left. No call waits for it.
153//! - **Lock-free**: every receive (`recv`, `recv_many`, `try_recv`,
154//!   `poll_recv`, `poll_recv_many`), none of which waits for a sender inside
155//!   the call, and the receiver's drop, which drops every value left and so
156//!   takes time proportional to them.
157//! - A [`Waker`](core::task::Waker) the channel calls is the executor's, and so
158//!   is what it does; the destructor of a value runs in the call that drops
159//!   it; and the `blocking_*` methods park the thread until their wait is over,
160//!   which is what they are for.
161//!
162//! # Cancel safety
163//!
164//! `recv`, `recv_many` and `closed` are cancel safe: dropping the future loses
165//! nothing. `send` is cancel safe in the sense that a dropped future sent
166//! nothing, but it drops the value it held; `reserve` and its relatives hold no
167//! slot when dropped, and a dropped `reserve_owned` also drops its sender.
168//!
169//! # Fairness
170//!
171//! The bounded channel serves waiting sends and reservations strictly in the
172//! order they began to wait, as the established API does. Slots that come
173//! free are set aside for the oldest waiting request until it has all it
174//! asked for, so a `reserve_many(n)` is not starved by smaller requests and
175//! nothing behind it is served first; a `try_send` or `try_reserve` never
176//! takes a slot while a request waits. Dropping a waiting future gives up its
177//! place and passes on whatever was set aside or granted to it, so no slot
178//! and no wakeup is lost. The unbounded channel never waits, so it has no
179//! order to keep.
180
181mod bounded;
182mod chan;
183pub mod error;
184mod permit;
185mod queue;
186mod receiver;
187mod unbounded;
188
189pub use self::bounded::{Sender, WeakSender, channel};
190pub use self::permit::{OwnedPermit, Permit, PermitIterator};
191pub use self::receiver::Receiver;
192pub use self::unbounded::{
193    UnboundedReceiver, UnboundedSender, WeakUnboundedSender, unbounded_channel,
194};