Expand description
A multi-producer, multi-consumer channel that retains only the last sent value.
channel returns a Sender and a Receiver around an initial
value. Each Receiver tracks the last value seen by its own caller.
The API is the established async watch-channel API; nothing here needs a
runtime.
Receiver::borrow_and_updatereturns the current value and marks it seen.Receiver::borrowreturns it without marking it seen.Receiver::changedcompletes when a value the receiver has not seen is in the channel, and marks it seen. At creation the initial value is seen, and a receiver made bySender::subscribesees the value that was current at that moment as seen.Receiver::has_changedis the synchronous check; it errors if and only if the channel is closed, whilechangederrors only when the channel is closed and the current value is seen.Sender::is_closedandSender::closedtell the producer that everyReceiveris gone. The value is dropped when every sender and every receiver is gone.
use nuclei::sync::watch;
let (tx, mut rx) = watch::channel("hello");
assert_eq!(*rx.borrow_and_update(), "hello");
assert!(!rx.has_changed().unwrap());
tx.send("world").unwrap();
assert!(rx.has_changed().unwrap());
assert_eq!(*rx.borrow_and_update(), "world");§The value lives in an Atom
Where a lock-based channel guards the value with a read-write lock, this
one keeps it in an Atom. A send publishes a new allocation with
one swap; a borrow is one protected load and never blocks a sender, and a
sender never blocks a borrow. Two consequences follow, and both differ
from the lock-based API:
- The channel needs
T: Send + Sync + 'static: values are shared by reference between threads and reclaimed later. - A
Refdoes not hold a lock but a reclamation guard. A value that was replaced stays allocated until everyRefthat can see it is dropped, so keep aRefshort. It is!Send: it must be dropped on the thread that took it, and the compiler therefore refuses to hold one across an await in a task that may move threads. - The old value cannot be handed back by value while readers may still
look at it, so
Sender::send_replacereturns a clone (T: Clone), and the two in-place modifiers,Sender::send_modifyandSender::send_if_modified, work on a clone too: they copy the value, let the closure change the copy and publish it with a compare-and-swap, retrying on a racing sender. Their closures areFnMutand may run more than once. A closure that panics, or that returnsfalsefromsend_if_modified, publishes nothing, and the channel keeps the value it had.
§Progress
- Wait-free (a bounded number of the caller’s own steps whatever other
threads do):
Receiver::borrow,Receiver::borrow_and_update,Receiver::has_changed,Receiver::is_closed,Receiver::mark_changed,Receiver::mark_unchanged,Sender::borrow,Sender::is_closed,Sender::subscribe,Sender::receiver_count,Sender::sender_count, bothsame_channelchecks, the clones, and dropping aRef. A borrow is anAtomload, which is wait-free. - The publication of
Sender::sendandSender::send_replace(the swap and the version bump) is wait-free, but each ends by waking the waiting receivers throughNotify::notify_waiters, which is lock-free: it takes the queue’s drain role with a compare-and-swap, or leaves a mark for the thread that holds it, and a pass may serve waiters other threads queue meanwhile. Sosendandsend_replaceare lock-free, not wait-free. So is dropping the last sender or the last receiver, for the same wake. Sender::send_modifyandSender::send_if_modifiedare lock-free: they retry the compare-and-swap whenever another sender published first, so a call can be delayed without bound by other senders’ progress, and each retry runs the closure again.- Polling
Receiver::changed,Receiver::wait_forandSender::closedis lock-free: a poll may queue a waiter, which is a lock-free queue operation.
Modules§
- error
- Error types of the watch channel.
Structs§
- Receiver
- Receives values from the associated
Senders. - Ref
- A reference to the value inside the channel.
- Sender
- Sends values to the associated
Receivers.
Functions§
- channel
- Creates a new watch channel around
init, returning the send and receive handles.