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}