selectables/lib.rs
1//! # selectables
2//!
3//! Lock-free channels with a unified recv-arm selection model.
4//!
5//! The crate provides multiple channel flavors that can be selected with the same
6//! `recv`-based protocol, including non-blocking and timed fallback behavior.
7//!
8//! ## Channel modules
9//! - [unbounded_mpmc] lock-free unbounded multi-producer, multi-consumer channel.
10//! - [bounded_mpmc] lock-free bounded multi-producer, multi-consumer channel.
11//! - [bounded_mpsc] lock-free bounded multi-producer, single-consumer channel.
12//! - [unbounded_mpsc] lock-free unbounded multi-producer, single-consumer channel.
13//! - [bounded_broadcast] bounded multi-producer, multi-receiver broadcast channel with per-receiver
14//! lag detection and independent cursors.
15//! - [oneshot] single-send/single-delivery channel compatible with recv select arms.
16//! - [watch] latest-value broadcast channel with versioned change notifications.
17//! - [rendezvous] zero-buffer synchronous handoff channel.
18//!
19//! ## Selection model
20//! - [select!] supports `recv(rx) -> msg => { ... }` and `send(tx, val) -> res => { ... }` arms.
21//! - Non-blocking fallback: `default => { ... }`.
22//! - Timed fallback: `default(duration) => { ... }`.
23//! - Low-level builder API is provided by [Select] and [SelectedOperation].
24//!
25//! ## Timers and disabled arms
26//! - [bounded_mpmc::after] and [bounded_mpsc::after] create one-shot timer receivers.
27//! - [bounded_mpmc::never] and [bounded_mpsc::never] create permanently non-firing receivers.
28//! - [interval::interval] and [interval::interval_at] create repeating timer receivers.
29//!
30//! ## Error model
31//! - [RecvError] for blocking receive/completion failures. Includes `Lagged { skipped }` for
32//! bounded broadcast channels when a receiver falls behind senders.
33//! - [error::TryRecvError] for non-blocking receive attempts.
34//! - [SendError] for failed sends that return ownership of the message.
35//!
36//! ## Lag handling (broadcast channels only)
37//! When using [bounded_broadcast], receivers may encounter `Lagged { skipped }` if they fall
38//! behind the senders. This is normal and indicates the ring buffer wrapped around before the
39//! receiver caught up. Recommended handling:
40//!
41//! ```text
42//! match rx.recv() {
43//! Ok(msg) => process(msg),
44//! Err(RecvError::Lagged { skipped }) => {
45//! log::warn!("Receiver lagged by {} messages; recovered", skipped);
46//! // Receiver cursor was automatically advanced; next recv will get oldest available
47//! }
48//! Err(RecvError::Disconnected) => {
49//! log::info!("All senders disconnected");
50//! break;
51//! }
52//! }
53//! ```
54//!
55//! See [bounded_broadcast] module docs for detailed lag recovery patterns.
56//!
57//! ## Quick example
58//! ```
59//! use std::time::Duration;
60//! use selectables::{Select, bounded_mpmc, select};
61//!
62//! let (tx, rx) = bounded_mpmc::channel::<i32>(1);
63//! tx.send(7).unwrap();
64//!
65//! select! {
66//! recv(rx) -> msg => assert_eq!(msg, Ok(7)),
67//! default(Duration::from_millis(10)) => panic!("unexpected timeout"),
68//! }
69//!
70//! let mut sel = Select::new();
71//! let i = sel.recv(bounded_mpmc::never::<i32>());
72//! let _ = i;
73//! ```
74
75use std::sync::Arc;
76use std::sync::atomic::AtomicUsize;
77
78#[macro_use]
79mod internals;
80mod error;
81mod select;
82mod waiter;
83
84pub mod bounded_broadcast;
85pub mod bounded_mpmc;
86pub mod bounded_mpsc;
87pub mod interval;
88pub mod oneshot;
89pub mod rendezvous;
90pub mod unbounded_mpmc;
91pub mod unbounded_mpsc;
92pub mod watch;
93
94pub use error::{RecvError, SendError};
95pub use select::{Select, SelectedOperation};
96
97/// Trait for receivers that can participate in `recv` arms of [`select!`].
98///
99/// The select protocol is a four-phase algorithm: **try → register → park → complete**.
100/// Each phase is represented by one or more methods on this trait.
101pub trait SelectableReceiver {
102 /// The value type produced by a successful receive.
103 type Output;
104 /// `true` when a value is immediately available (try phase).
105 ///
106 /// Returns `true` if the channel is disconnected as well, so that
107 /// [`complete`](Self::complete) can return the appropriate error right away.
108 fn is_ready(&self) -> bool;
109 /// Register a waiter for the select park phase.
110 ///
111 /// `case_id` identifies this arm among all arms in the `select!` call.
112 /// `selected` is the shared atomic that the first arm to fire sets to its `case_id`.
113 fn register_select(&self, case_id: usize, selected: Arc<AtomicUsize>);
114 /// Remove the previously registered waiter (losing-arm cleanup).
115 ///
116 /// Called on every arm that did *not* win the selection so that no dangling
117 /// waker references remain in the channel's waiter list.
118 fn abort_select(&self, selected: &Arc<AtomicUsize>);
119 /// Consume one value after winning the selection (complete phase).
120 ///
121 /// Returns `Err(RecvError::Disconnected)` if the channel is empty and all
122 /// senders have been dropped.
123 fn complete(&self) -> Result<Self::Output, RecvError>;
124}
125
126/// Trait for senders that can participate in `send` arms of `select!`.
127///
128/// `is_ready` returns `true` when a send can complete without blocking (buffer
129/// has space or the channel is disconnected). The select protocol uses the same
130/// four-phase algorithm as for receivers: try → register → park → complete.
131pub trait SelectableSender {
132 /// The value type this sender accepts.
133 type Input: Send;
134 /// `true` when a send would complete immediately (buffer not full, or
135 /// all receivers dropped — in which case `complete_send` returns `Err`).
136 fn is_ready(&self) -> bool;
137 /// Register a send-side waiter for the select park phase.
138 fn register_select(&self, case_id: usize, selected: Arc<AtomicUsize>);
139 /// Remove the previously registered waiter (losing arm cleanup).
140 fn abort_select(&self, selected: &Arc<AtomicUsize>);
141 /// Execute the send after winning the selection.
142 /// Returns `Err(SendError(val))` if all receivers have been dropped.
143 fn complete_send(&self, value: Self::Input) -> Result<(), SendError<Self::Input>>;
144}