copy_channels/
lib.rs

1//! A collection of cross-thread channels for copyable types.
2//!
3//! This crate provides channels for publishing data cross-thread to multiple receivers. There is *no signalling*,
4//! neither any form of async nor OS-based (futex etc.). Typical usage would be in an environment with a few
5//! hot-polling threads, where low latency is absolutely crucial, or with external signalling and avoiding
6//! the type of contention inherent in mutexes / rwlocks.
7//!
8//! [Watch](channels::watch) channels publish a single value, where the receiver only cares about the latest value (seqlock).
9//!
10//! [Broadcast](channels::broadcast) channels publish a stream of values, and the receiver sees all values in order.
11//!
12//! All channels are safe to use with multiple senders, but do note that this may create spinlock-type contention
13//! when used at high throughput.
14//!
15//! Both channels come in multiple variants. [Fast](fast) channels work on any `Copy` type, however they contain
16//! undefined behaviour. There is a data race. And although the data race is "protected", in that we check
17//! afterwards whether or not there was contention, strict tools like miri still object, and we can't
18//! guarantee that things will always work with newer compiler technology.
19//!
20//! [Atomic](atomic) channels are free from data races. These, however, come with restrictions on the type of
21//! values transferred, and may be slightly slower. Pick your poison.
22//!
23//! Finally, [native] channels work only on types that are already atomic -- usefulness here is uncertain compared
24//! to a simple `Arc<AtomicFoo>`, although channels do provide things like versioning and checking for close.
25
26mod loom;
27
28/// Low-level generic implementations.
29pub mod channels {
30    pub mod broadcast;
31    pub mod watch;
32}
33
34pub mod fast;
35pub mod native;
36
37#[cfg(feature = "atomic")]
38pub mod atomic;
39
40use loom::sync::atomic::Ordering;
41
42/// Trait for common implementation.
43///
44/// Provides common functions to enable single channel logic across multiple low-level implementations.
45pub trait Slotable<T>: 'static {
46    /// Type for slot that contains a single `T`
47    type Slot: ?Sized;
48    /// Type for array/slice element where multiple `T`s are kept
49    type SlotArrayItem;
50
51    /// Create a single slot with no value.
52    fn boxed_uninit_single() -> Box<Self::Slot>;
53
54    /// Read the slot atomically. Whether a correct `T` is read must be determined elsewhere.
55    fn read(slot: &Self::Slot, ordering: Ordering) -> std::mem::MaybeUninit<T>;
56
57    /// Write a single `T` into the slot, overwriting (without drop) what was there.
58    fn write(slot: &Self::Slot, value: T, ordering: Ordering);
59
60    /// Create a single slot with known value.
61    fn create_boxed(value: T) -> Box<Self::Slot> {
62        let slot = Self::boxed_uninit_single();
63        Self::write(&slot, value, Ordering::Relaxed);
64        slot
65    }
66
67    /// Create a slice of slot items capable of holding `n` `T`s.
68    fn boxed_uninit_multiple(n: usize) -> Box<[Self::SlotArrayItem]>;
69
70    /// Given a slice as returned from [`Self::boxed_uninit_multiple`], access one of the elements.
71    fn index_in_array(items: &[Self::SlotArrayItem], index: usize) -> &Self::Slot;
72}
73
74macro_rules! impl_channels {
75    ($s:ty, $b:path) => {
76        crate::channels::watch::watch_impl!($s, $b);
77        crate::channels::broadcast::broadcast_impl!($s, $b);
78    };
79}
80pub(crate) use impl_channels;