light_qsbr/
lib.rs

1//! # light-qsbr
2//!
3//! `light-qsbr` provides a **lightweight Quiescent-State-Based Reclamation (QSBR)**
4//! mechanism for asynchronous runtimes and lock-free data structures.
5//!
6//! It is designed as a minimal alternative to heavy garbage collectors and allows
7//! safe memory reclamation in concurrent environments, where objects can only be freed
8//! once all participating threads have advanced past a known **epoch**.
9//!
10//! ## Core Concepts
11//!
12//! - [`SharedManager`] is the **global manager**.
13//!   It tracks the current global epoch and the number of executors participating in each epoch.
14//!
15//! - [`LocalManager`] is the **thread-local manager**.
16//!   Each executor (thread or runtime worker) has its own local manager,
17//!   which schedules memory to be deallocated or dropped when it is safe.
18//!
19//! - **Epochs** are global counters.
20//!   Memory is only reclaimed once all executors have passed the epoch
21//!   in which the memory was retired.
22//!
23//! ## Workflow
24//!
25//! 1. Create a [`SharedManager`].
26//! 2. For each executor (thread/worker), call [`SharedManager::register_new_executor`].
27//!    This installs a thread-local [`LocalManager`].
28//! 3. Executors schedule memory for deallocation or dropping via
29//!    [`LocalManager::schedule_deallocate`], [`LocalManager::schedule_deallocate_slice`],
30//!    or [`LocalManager::schedule_drop`].
31//! 4. Periodically, executors call [`LocalManager::maybe_pass_epoch`] to attempt to advance
32//!    the epoch and trigger safe reclamation.
33//! 5. When shutting down, executors must call
34//!    `unsafe { LocalManager::deregister() }` to deregister themselves.
35//!
36//! ## Example
37//!
38//! ```rust
39//! use std::cell::Cell;
40//! use light_qsbr::{SharedManager, local_manager, LocalManager};
41//! use light_qsbr::orengine_utils::OrengineInstant;
42//!
43//! # fn main() {
44//! let shared = SharedManager::new();
45//!
46//! // Register an executor for this thread
47//! shared.register_new_executor();
48//!
49//! // Schedule deallocation or dropping through the local manager
50//! let value = Box::new(42);
51//! let ptr = Box::into_raw(value);
52//! unsafe {
53//!     local_manager().schedule_deallocate(ptr);
54//! }
55//!
56//! // Periodically try to advance the epoch (e.g., in an event loop)
57//! local_manager().maybe_pass_epoch(OrengineInstant::now());
58//!
59//! // Deregister before thread exit
60//! unsafe { LocalManager::deregister() };
61//! # }
62//! ```
63//!
64//! ## Safety Guarantees
65//!
66//! - Objects scheduled for reclamation will only be freed once **all executors**
67//!   have passed the epoch in which they were retired.
68//! - The API is intentionally minimal: the runtime must periodically call
69//!   [`LocalManager::maybe_pass_epoch`] to make progress.
70//!
71//! ## When to Use
72//!
73//! - Implementing lock-free collections that need safe memory reclamation.
74//! - Async runtimes that want QSBR without the overhead of hazard pointers or full garbage collection.
75//! - Situations where *you control executor lifecycle* and can ensure correct registration/deregistration.
76//!
77//! ## When *Not* to Use
78//!
79//! - If you need automatic reclamation without explicit epoch-passing.
80//! - If your workload is highly dynamic with frequent thread churn—QSBR works best with stable executor sets.
81//!
82//! ## See Also
83//!
84//! - [`SharedManager`] for the global epoch tracker.
85//! - [`LocalManager`] for per-thread scheduling of reclamation.
86//! - [`local_manager`] to access the thread-local [`LocalManager`].
87//!
88//! ---
89
90#![deny(clippy::all)]
91#![deny(clippy::assertions_on_result_states)]
92#![deny(clippy::match_wild_err_arm)]
93#![deny(clippy::allow_attributes_without_reason)]
94#![warn(clippy::pedantic)]
95#![warn(clippy::nursery)]
96#![warn(clippy::cargo)]
97#![allow(
98    clippy::multiple_crate_versions,
99    reason = "They were set by dev-dependencies"
100)]
101#![allow(async_fn_in_trait, reason = "It improves readability.")]
102#![allow(
103    clippy::missing_const_for_fn,
104    reason = "Since we cannot make a constant function non-constant after its release,
105    we need to look for a reason to make it constant, and not vice versa."
106)]
107#![allow(clippy::inline_always, reason = "We write highly optimized code.")]
108#![allow(
109    clippy::must_use_candidate,
110    reason = "It is better to developer think about it."
111)]
112#![allow(
113    clippy::module_name_repetitions,
114    reason = "This is acceptable most of the time."
115)]
116#![allow(
117    clippy::missing_errors_doc,
118    reason = "Unless the error is something special,
119    the developer should document it."
120)]
121#![allow(clippy::redundant_pub_crate, reason = "It improves readability.")]
122#![allow(clippy::struct_field_names, reason = "It improves readability.")]
123#![allow(
124    clippy::module_inception,
125    reason = "It is fine if a file in has the same mane as a module."
126)]
127#![allow(clippy::if_not_else, reason = "It improves readability.")]
128#![allow(
129    rustdoc::private_intra_doc_links,
130    reason = "It allows to create more readable docs."
131)]
132#![allow(
133    clippy::result_unit_err,
134    reason = "The function's doc should explain what it returns."
135)]
136
137mod local_manager;
138mod number_of_executors;
139mod deffered;
140mod shared_manager;
141#[cfg(test)]
142mod test;
143
144pub use orengine_utils;
145pub use local_manager::*;
146pub use shared_manager::*;