quinine/
lib.rs

1//! Quinine implements atomic, lock-free, but write-once versions of
2//! containers like [`Option<Box<T>>`] ([`MonoBox`]) and
3//! [`Option<Arc<T>>`] ([`MonoArc`]).  Write-once means that the value
4//! transitions from [`None`] to [`Some`] at most once during the
5//! atomic object's lifetime, and the object is then frozen as is.
6//! This monotonicity makes it easy to access monotonic containers
7//! from multiple threads without any special coordination between
8//! readers and writers.
9//!
10//! Crates like [ArcSwap](https://crates.io/crates/arc-swap) offer
11//! optimised versions of
12//! [`RwLock<Arc<T>>`](https://doc.rust-lang.org/std/sync/struct.RwLock.html),
13//! for read-mostly workloads.  Quinine's containers are even more
14//! heavily biased away from writes ([`MonoBox`] and [`MonoArc`] can
15//! only be mutated once), and offer even lower overhead in return:
16//! stores require only a
17//! [`AtomicPtr::compare_exchange`](core::sync::atomic::AtomicPtr::compare_exchange),
18//! and reads are plain
19//! [`Ordering::Acquire`](core::sync::atomic::Ordering) loads.  Of
20//! course, obtaining a full-blown [`Arc`](alloc::sync::Arc) incurs
21//! reference counting overhead, just like a regular
22//! [`Arc::clone`](alloc::sync::Arc::clone).
23//!
24//! When containers are updated without locking, but only so long as
25//! the set of resources (e.g., memory allocations) owned by that
26//! container grows monotonically, we can implement simple update
27//! algorithms based on compare-and-swap, without having to worry
28//! about object lifetimes and concurrent readers. All references and
29//! other shared capabilities readers might have obtained via a
30//! monotonic container will remain valid as long as the container
31//! itself is valid.
32//!
33//! For example, once we've observed a [`MonoBox`] with [`Some`]
34//! value, we can safely use its pointee for however long we have a
35//! reference to that [`MonoBox`] (something that Rust's ownership
36//! system enforces for us): the [`MonoBox`]'s value is now
37//! frozen, so the pointee's lifetime exactly matches the
38//! [`MonoBox`]'s lifetime.
39//!
40//! Monotonic containers may only release resources or otherwise
41//! change non-monotonically when a mutable reference (`&mut`) serves
42//! as a witness of single ownership.  For example, that's how
43//! containers can implement [`Drop::drop`].
44#![cfg_attr(not(any(feature = "std", test)), no_std)]
45
46#[cfg(doc)]
47extern crate alloc;
48
49mod arc;
50mod r#box;
51
52pub use arc::MonoArc;
53pub use r#box::MonoBox;