state/lib.rs
1#![doc(html_root_url = "https://docs.rs/state/0.6.0")]
2#![warn(missing_docs)]
3
4//! # Safe, Effortless `state` Management
5//!
6//! This crate allows you to safely and effortlessly manage global and/or
7//! thread-local state. Three primitives are provided for state management:
8//!
9//! * **[`struct@TypeMap`]:** Type-based storage for many values.
10//! * **[`InitCell`]:** Thread-safe init-once storage for a single value.
11//! * **[`LocalInitCell`]:** Thread-local init-once-per-thread cell.
12//!
13//! ## Usage
14//!
15//! Include `state` in your `Cargo.toml` `[dependencies]`:
16//!
17//! ```toml
18//! [dependencies]
19//! state = "0.6.0"
20//! ```
21//!
22//! Thread-local state management is not enabled by default. You can enable it
23//! via the `tls` feature:
24//!
25//! ```toml
26//! [dependencies]
27//! state = { version = "0.6.0", features = ["tls"] }
28//! ```
29//!
30//! ## Use Cases
31//!
32//! ### Memoizing Expensive Operations
33//!
34//! The [`InitCell`] type can be used to conveniently memoize expensive
35//! read-based operations without needing to mutably borrow. Consider a `struct`
36//! with a field `value` and method `compute()` that performs an expensive
37//! operation on `value` to produce a derived value. We can use `InitCell` to
38//! memoize `compute()`:
39//!
40//! ```rust
41//! use state::InitCell;
42//!
43//! struct Value;
44//! struct DerivedValue;
45//!
46//! struct Foo {
47//! value: Value,
48//! cached: InitCell<DerivedValue>
49//! }
50//!
51//! impl Foo {
52//! fn set_value(&mut self, v: Value) {
53//! self.value = v;
54//! self.cached.reset();
55//! }
56//!
57//! fn compute(&self) -> &DerivedValue {
58//! self.cached.get_or_init(|| {
59//! let _value = &self.value;
60//! unimplemented!("expensive computation with `self.value`")
61//! })
62//! }
63//! }
64//! ```
65//!
66//! ### Read-Only Singleton
67//!
68//! Suppose you have the following structure which is initialized in `main`
69//! after receiving input from the user:
70//!
71//! ```rust
72//! struct Configuration {
73//! name: String,
74//! number: isize,
75//! verbose: bool
76//! }
77//!
78//! fn main() {
79//! let config = Configuration {
80//! /* fill in structure at run-time from user input */
81//! # name: "Sergio".to_string(),
82//! # number: 1,
83//! # verbose: true
84//! };
85//! }
86//! ```
87//!
88//! You'd like to access this structure later, at any point in the program,
89//! without any synchronization overhead. Prior to `state`, assuming you needed
90//! to setup the structure after program start, your options were:
91//!
92//! 1. Use `static mut` and `unsafe` to set an `Option<Configuration>` to
93//! `Some`. Retrieve by checking for `Some`.
94//! 2. Use `lazy_static` with a `RwLock` to set an
95//! `RwLock<Option<Configuration>>` to `Some`. Retrieve by `lock`ing and
96//! checking for `Some`, paying the cost of synchronization.
97//!
98//! With `state`, you can use [`LocalInitCell`] as follows:
99//!
100//! ```rust
101//! # extern crate state;
102//! # #[cfg(feature = "tls")]
103//! # fn main() {
104//! # use state::LocalInitCell;
105//! # struct Configuration { name: String, number: isize, verbose: bool }
106//! static CONFIG: LocalInitCell<Configuration> = LocalInitCell::new();
107//!
108//! fn main() {
109//! CONFIG.set(|| Configuration {
110//! /* fill in structure at run-time from user input */
111//! # name: "Sergio".to_string(),
112//! # number: 1,
113//! # verbose: true
114//! });
115//!
116//! /* at any point later in the program, in any thread */
117//! let config = CONFIG.get();
118//! }
119//! # }
120//! # #[cfg(not(feature = "tls"))]
121//! # fn main() { }
122//! ```
123//!
124//! Note that you can _also_ use [`InitCell`] to the same effect.
125//!
126//! ### Read/Write Singleton
127//!
128//! Following from the previous example, let's now say that we want to be able
129//! to modify our singleton `Configuration` structure as the program evolves. We
130//! have two options:
131//!
132//! 1. If we want to maintain the _same_ state in any thread, we can use a
133//! `InitCell` structure and wrap our `Configuration` structure in a
134//! synchronization primitive.
135//! 2. If we want to maintain _different_ state in any thread, we can continue
136//! to use a `LocalInitCell` structure and wrap our `LocalInitCell` type in a
137//! `Cell` structure for internal mutability.
138//!
139//! In this example, we'll choose **1**. The next example illustrates an
140//! instance of **2**.
141//!
142//! The following implements **1** by using a `InitCell` structure and wrapping
143//! the `Configuration` type with a `RwLock`:
144//!
145//! ```rust
146//! # struct Configuration { name: String, number: isize, verbose: bool }
147//! # use state::InitCell;
148//! # use std::sync::RwLock;
149//! static CONFIG: InitCell<RwLock<Configuration>> = InitCell::new();
150//!
151//! fn main() {
152//! let config = Configuration {
153//! /* fill in structure at run-time from user input */
154//! # name: "Sergio".to_string(),
155//! # number: 1,
156//! # verbose: true
157//! };
158//!
159//! // Make the config avaiable globally.
160//! CONFIG.set(RwLock::new(config));
161//!
162//! /* at any point later in the program, in any thread */
163//! let mut_config = CONFIG.get().write();
164//! }
165//! ```
166//!
167//! ### Mutable, thread-local data
168//!
169//! Imagine you want to count the number of invocations to a function per
170//! thread. You'd like to store the count in a `Cell<usize>` and use
171//! `count.set(count.get() + 1)` to increment the count. Prior to `state`, your
172//! only option was to use the `thread_local!` macro. `state` provides a more
173//! flexible, and arguably simpler solution via `LocalInitCell`. This scanario
174//! is implemented in the folloiwng:
175//!
176//! ```rust
177//! # extern crate state;
178//! # use std::cell::Cell;
179//! # use std::thread;
180//! # #[cfg(feature = "tls")]
181//! # use state::LocalInitCell;
182//! # #[cfg(feature = "tls")]
183//! static COUNT: LocalInitCell<Cell<usize>> = LocalInitCell::new();
184//!
185//! # #[cfg(not(feature = "tls"))] fn function_to_measure() { }
186//! # #[cfg(feature = "tls")]
187//! fn function_to_measure() {
188//! let count = COUNT.get();
189//! count.set(count.get() + 1);
190//! }
191//!
192//! # #[cfg(not(feature = "tls"))] fn main() { }
193//! # #[cfg(feature = "tls")]
194//! fn main() {
195//! // setup the initializer for thread-local state
196//! COUNT.set(|| Cell::new(0));
197//!
198//! // spin up many threads that call `function_to_measure`.
199//! let mut threads = vec![];
200//! for i in 0..10 {
201//! threads.push(thread::spawn(|| {
202//! // Thread IDs may be reusued, so we reset the state.
203//! COUNT.get().set(0);
204//! function_to_measure();
205//! COUNT.get().get()
206//! }));
207//! }
208//!
209//! // retrieve the total
210//! let total: usize = threads.into_iter()
211//! .map(|t| t.join().unwrap())
212//! .sum();
213//!
214//! assert_eq!(total, 10);
215//! }
216//! ```
217//! ## Correctness
218//!
219//! `state` has been extensively vetted, manually and automatically, for soundness
220//! and correctness. _All_ unsafe code, including in internal concurrency
221//! primitives, `TypeMap`, and `InitCell` are exhaustively verified for pairwise
222//! concurrency correctness and internal aliasing exclusion with `loom`.
223//! Multithreading invariants, aliasing invariants, and other soundness properties
224//! are verified with `miri`. Verification is run by the CI on every commit.
225//!
226//! ## Performance
227//!
228//! `state` is heavily tuned to perform optimally. `get{_local}` and
229//! `set{_local}` calls to a `TypeMap` incur overhead due to type lookup.
230//! `InitCell`, on the other hand, is optimal for global storage retrieval; it is
231//! _slightly faster_ than accessing global state initialized through
232//! `lazy_static!`, more so across many threads. `LocalInitCell` incurs slight
233//! overhead due to thread lookup. However, `LocalInitCell` has no
234//! synchronization overhead, so retrieval from `LocalInitCell` is faster than
235//! through `InitCell` across many threads.
236//!
237//! Bear in mind that `state` allows global initialization at _any_ point in the
238//! program. Other solutions, such as `lazy_static!` and `thread_local!` allow
239//! initialization _only_ a priori. In other words, `state`'s abilities are a
240//! superset of those provided by `lazy_static!` and `thread_local!` while being
241//! more performant.
242//!
243//! ## When To Use
244//!
245//! You should avoid using global `state` as much as possible. Instead, thread
246//! state manually throughout your program when feasible.
247
248mod ident_hash;
249mod cell;
250mod init;
251mod shim;
252
253#[doc(hidden)]
254pub mod type_map;
255
256pub use type_map::TypeMap;
257pub use cell::InitCell;
258
259#[cfg(feature = "tls")] mod tls;
260#[cfg(feature = "tls")] mod thread_local;
261#[cfg(feature = "tls")] pub use tls::LocalInitCell;
262
263/// Exports for use by loom tests but otherwise private.
264#[cfg(loom)]
265#[path = "."]
266pub mod private {
267 /// The `Init` type.
268 pub mod init;
269}