copy-channels 1.0.0

A collection of cross-thread channels for copyable types
Documentation
//! A collection of cross-thread channels for copyable types.
//!
//! This crate provides channels for publishing data cross-thread to multiple receivers. There is *no signalling*,
//! neither any form of async nor OS-based (futex etc.). Typical usage would be in an environment with a few
//! hot-polling threads, where low latency is absolutely crucial, or with external signalling and avoiding
//! the type of contention inherent in mutexes / rwlocks.
//!
//! [Watch](channels::watch) channels publish a single value, where the receiver only cares about the latest value (seqlock).
//!
//! [Broadcast](channels::broadcast) channels publish a stream of values, and the receiver sees all values in order.
//!
//! All channels are safe to use with multiple senders, but do note that this may create spinlock-type contention
//! when used at high throughput.
//!
//! Both channels come in multiple variants. [Fast](fast) channels work on any `Copy` type, however they contain
//! undefined behaviour. There is a data race. And although the data race is "protected", in that we check
//! afterwards whether or not there was contention, strict tools like miri still object, and we can't
//! guarantee that things will always work with newer compiler technology.
//!
//! [Atomic](atomic) channels are free from data races. These, however, come with restrictions on the type of
//! values transferred, and may be slightly slower. Pick your poison.
//!
//! Finally, [native] channels work only on types that are already atomic -- usefulness here is uncertain compared
//! to a simple `Arc<AtomicFoo>`, although channels do provide things like versioning and checking for close.

mod loom;

/// Low-level generic implementations.
pub mod channels {
    pub mod broadcast;
    pub mod watch;
}

pub mod fast;
pub mod native;

#[cfg(feature = "atomic")]
pub mod atomic;

use loom::sync::atomic::Ordering;

/// Trait for common implementation.
///
/// Provides common functions to enable single channel logic across multiple low-level implementations.
pub trait Slotable<T>: 'static {
    /// Type for slot that contains a single `T`
    type Slot: ?Sized;
    /// Type for array/slice element where multiple `T`s are kept
    type SlotArrayItem;

    /// Create a single slot with no value.
    fn boxed_uninit_single() -> Box<Self::Slot>;

    /// Read the slot atomically. Whether a correct `T` is read must be determined elsewhere.
    fn read(slot: &Self::Slot, ordering: Ordering) -> std::mem::MaybeUninit<T>;

    /// Write a single `T` into the slot, overwriting (without drop) what was there.
    fn write(slot: &Self::Slot, value: T, ordering: Ordering);

    /// Create a single slot with known value.
    fn create_boxed(value: T) -> Box<Self::Slot> {
        let slot = Self::boxed_uninit_single();
        Self::write(&slot, value, Ordering::Relaxed);
        slot
    }

    /// Create a slice of slot items capable of holding `n` `T`s.
    fn boxed_uninit_multiple(n: usize) -> Box<[Self::SlotArrayItem]>;

    /// Given a slice as returned from [`Self::boxed_uninit_multiple`], access one of the elements.
    fn index_in_array(items: &[Self::SlotArrayItem], index: usize) -> &Self::Slot;
}

macro_rules! impl_channels {
    ($s:ty, $b:path) => {
        crate::channels::watch::watch_impl!($s, $b);
        crate::channels::broadcast::broadcast_impl!($s, $b);
    };
}
pub(crate) use impl_channels;