thread_aware/lib.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6
7//! Essential building blocks for thread-per-core libraries.
8//!
9//! This crate allows you to express migrations between NUMA nodes, threads, or specific CPU cores.
10//! It can serve as a foundation for building components and runtimes that operate across multiple
11//! memory affinities.
12//!
13//! # Theory of Operation
14//!
15//! At a high level, this crate enables thread migrations of state via the [`ThreadAware`] trait:
16//! - Runtimes (and similar) can use it to inform types that they were just moved across a thread or NUMA boundary.
17//! - The authors of said types can then act on this information to implement performance optimizations. Such optimizations
18//! might include re-allocating memory in a new NUMA region, connecting to a thread-local I/O scheduler,
19//! or detaching from shared, possibly contended memory with the previous thread.
20//!
21//! Similar to [`Clone`], there are no exact semantic prescriptions of how types should behave on relocation.
22//! They might continue to share some state (e.g., a common cache) or fully detach from it for performance reasons.
23//! The primary goal is performance, so types should aim to minimize contention on synchronization primitives
24//! and cross-NUMA memory access. Like `Clone`, the relocation itself should be mostly transparent and predictable
25//! to users.
26//!
27//! ## Implementing [`ThreadAware`], and `Arc<T, PerCore>`
28//!
29//! In most cases [`ThreadAware`] should be implemented via the provided derive macro.
30//! As thread-awareness of a type usually involves letting all contained fields know of an ongoing
31//! relocation, the derive macro does just that. A default impl is provided for many `std` types,
32//! so the macro should 'just work' on most compounds of built-ins.
33//!
34//! External crates might often not implement [`ThreadAware`]. In many of these cases using our
35//! [`thread_aware::Arc`](Arc) offers a convenient solution: It combines an upstream
36//! [`std::sync::Arc`] with a relocation [`Strategy`](storage::Strategy), and implements [`ThreadAware`] for it. For
37//! example, while an `Arc<Foo, PerProcess>` effectively acts as vanilla `Arc`, an
38//! `Arc<Foo, PerCore>` ensures a separate `Foo` is available any time the types moves a core boundary.
39//!
40//!
41//! ## Relation to [`Send`]
42//!
43//! [`ThreadAware`] requires [`Send`] as a supertrait. Types are first sent to another thread,
44//! then the [`ThreadAware`] relocation notification is invoked.
45//!
46//!
47//! ## Thread vs. Core Semantics
48//!
49//! As this library is primarily intended for use in thread-per-core runtimes,
50//! we use the terms 'thread' and 'core' interchangeably. The assumption is that items
51//! primarily relocate between different threads, where each thread is pinned to a different CPU core.
52//! Should a runtime utilize more than one thread per core (e.g., for internal I/O) user code should
53//! be able to observe this fact.
54//!
55//! ## [`ThreadAware`] vs. [`Unaware`]
56//!
57//! Sometimes you might need to move inert types as-is, essentially bypassing all
58//! thread-aware handling. These might be foreign types that carry no allocation, do
59//! no I/O, or otherwise do not require any thread-specific handling.
60//!
61//! [`Unaware`] can be used to encapsulate such types, a wrapper that itself implements [`ThreadAware`], but
62//! otherwise does not react to it. You can think of it as a `MoveAsIs<T>`. However, it was
63//! deliberately named `Unaware` to signal that only types which are genuinely unaware of their
64//! thread relocations (i.e., don't impl [`ThreadAware`]) should be wrapped in such.
65//!
66//! Wrapping types that implement the trait is discouraged, as it will prevent them from properly
67//! relocating and might have an impact on their performance, but not correctness, see below.
68//!
69//! ## Performance vs. Correctness
70//!
71//! It is important to note that [`ThreadAware`] is a cooperative performance optimization and contention avoidance
72//! primitive, not a guarantee of behavior for either the caller or callee. In other words, callers and runtimes must
73//! continue to operate correctly if the trait is invoked incorrectly.
74//!
75//! In particular, [`ThreadAware`] may not always be invoked when a type leaves the current thread.
76//! While runtimes should reduce the incidence of that through their API design, it may nonetheless
77//! happen via [`std::thread::spawn`] and other means. In these cases types should still function
78//! correctly, although they might experience degraded performance through contention of now-shared
79//! resources.
80//!
81//! ## Provided Implementations
82//!
83//! [`ThreadAware`] is implemented for many standard library types, including primitive types, Vec,
84//! String, Option, Result, tuples, etc. However, it's explicitly not implemented for [`std::sync::Arc`]
85//! as that type implies some level of cross-thread sharing and thus needs special attention when used
86//! from types that implement [`ThreadAware`].
87//!
88//! # Features
89//!
90//! * **`derive`** *(default)*: Re-exports the `#[derive(ThreadAware)]` macro from the companion
91//! `thread_aware_macros` crate. Disable to avoid pulling in proc-macro code in minimal
92//! environments: `default-features = false`.
93//! * **`threads`**: Enables features mainly used by async runtimes for OS interactions.
94//!
95//! ## 3rd-party crate impls
96//!
97//! The following opt-in features provide [`ThreadAware`] implementations for
98//! inert value types from popular 3rd-party crates. Enabling a feature pulls
99//! that crate in as a dependency. By default none are enabled and this crate
100//! brings in no extra dependencies.
101//!
102//! Feature names follow this convention so that future breaking versions of
103//! the wrapped crate can be supported additively:
104//!
105//! * Stable `1.x` (or any other stable major) → bare crate name
106//! (e.g. `bytes`, `http`, `uuid`).
107//! * `N.x` for `N >= 2` → `<crate><N>` (e.g. `bytes2` if `bytes 2.x` ever lands).
108//! * `0.x` → `<crate>0<minor>` (e.g. `jiff02` for `jiff 0.2.x`).
109//!
110//! * **`bytes`**: Impls for `bytes::Bytes`, `bytes::BytesMut`.
111//! * **`http`**: Impls for `http::StatusCode`, `http::Method`, `http::Version`,
112//! `http::HeaderName`, `http::HeaderValue`, `http::HeaderMap<HeaderValue>`,
113//! `http::Uri`, `http::uri::Authority`, `http::uri::Scheme`,
114//! `http::uri::PathAndQuery`, `http::uri::Port<T>`, `http::Error`,
115//! `http::uri::InvalidUri`, `http::Request<T>`, `http::Response<T>`.
116//! * **`jiff02`**: Impls for `jiff::Timestamp`, `jiff::civil::DateTime`, etc.
117//! * **`uuid`**: Impl for `uuid::Uuid`.
118//!
119//! # Examples
120//!
121//! ## Deriving [`ThreadAware`]
122//!
123//! When the `derive` feature (enabled by default) is active you can simply
124//! derive [`ThreadAware`] instead of writing the implementation manually.
125//!
126//! ```rust
127//! use thread_aware::ThreadAware;
128//!
129//! #[derive(Debug, Clone, ThreadAware)]
130//! struct Point {
131//! x: i32,
132//! y: i32,
133//! }
134//! ```
135//!
136//! ## Enabling [`ThreadAware`] via `Arc<T, S>`
137//!
138//! For types containing fields not [`ThreadAware`], you can use [`Arc`] to specify a
139//! strategy, and wrap them in an [`Arc`] that implements the trait.
140//!
141//!
142//! ```rust
143//! use thread_aware::{Arc, PerCore, ThreadAware};
144//! # #[derive(Debug, Default)]
145//! # struct Client;
146//!
147//! #[derive(Debug, Clone, ThreadAware)]
148//! struct Service {
149//! name: String,
150//! client: Arc<Client, PerCore>,
151//! }
152//!
153//! impl Service {
154//! fn new() -> Self {
155//! Self {
156//! name: "MyService".to_string(),
157//! client: Arc::new(|| Client::default()),
158//! }
159//! }
160//! }
161//! ```
162
163#![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/thread_aware/logo.png")]
164#![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/thread_aware/favicon.ico")]
165
166mod cell;
167mod core;
168mod impls;
169mod third_party;
170mod wrappers;
171
172pub mod closure;
173
174#[cfg(feature = "threads")]
175pub mod registry;
176
177#[doc(hidden)]
178pub mod __private;
179pub mod affinity;
180
181#[doc(inline)]
182pub use core::ThreadAware;
183
184// Re-export the derive macro (behind the `derive` feature) so users can
185// simply `use thread_aware::ThreadAware;`. Disable the feature to avoid the
186// proc-macro dependency in minimal builds.
187/// Derive macro implementing `ThreadAware` for structs and enums.
188///
189/// The generated implementation transfers each field by calling its own
190/// `ThreadAware::relocate` method. Fields annotated with `#[thread_aware(skip)]` are
191/// left as-is (moved without invoking `relocate`).
192///
193/// # Supported Items
194/// * Structs (named, tuple, or unit)
195/// * Enums (all variant field styles)
196///
197/// Unions are not supported and will produce a compile error.
198///
199/// # Attributes
200/// * `#[thread_aware(skip)]`: Prevents a field from being recursively transferred.
201///
202/// # Generic Bounds
203/// Generic type parameters appearing in non-skipped fields automatically receive a
204/// `::thread_aware::ThreadAware` bound (occurrences only inside `PhantomData<..>` are ignored).
205///
206/// # Example
207/// ```rust
208/// use thread_aware::ThreadAware;
209/// use thread_aware::affinity::Affinity;
210/// #[derive(ThreadAware)]
211/// struct Payload {
212/// id: u64,
213/// data: Vec<u8>,
214/// }
215///
216/// #[derive(ThreadAware)]
217/// struct Wrapper {
218/// // This field will be recursively transferred.
219/// inner: Payload,
220/// // This field will be moved without calling `relocate`.
221/// #[thread_aware(skip)]
222/// raw_len: usize,
223/// }
224///
225/// fn demo(a1: Option<Affinity>, a2: Affinity, mut w: Wrapper) {
226/// // Move the wrapper from a1 to a2.
227/// w.relocate(a1, a2);
228/// }
229/// ```
230#[cfg(feature = "derive")]
231pub use ::thread_aware_macros::ThreadAware;
232pub use cell::{Arc, PerCore, PerNuma, PerProcess, storage};
233pub use wrappers::{Unaware, unaware};