rudo_gc/lib.rs
1//! A garbage-collected smart pointer library for Rust.
2//!
3//! `rudo-gc` provides a `Gc<T>` smart pointer with automatic memory reclamation
4//! and cycle detection. It uses a **`BiBOP` (Big Bag of Pages)** memory layout for
5//! efficient O(1) allocation and a **Mark-Sweep** garbage collection algorithm.
6//!
7//! # Features
8//!
9//! - **Automatic cycle detection**: Unlike `Rc<T>`, `Gc<T>` can collect cyclic references
10//! - **`BiBOP` memory layout**: O(1) allocation with size-class based segments
11//! - **Non-moving GC**: Address stability for Rust's `&T` references
12//! - **Ergonomic API**: Similar to `Rc<T>` with `#[derive(Trace)]` for custom types
13//!
14//! # Quick Start
15//!
16//! ```ignore
17//! use rudo_gc::{Gc, Trace};
18//!
19//! // Simple allocation
20//! let x = Gc::new(42);
21//! println!("Value: {}", *x);
22//!
23//! // Custom types with derive
24//! #[derive(Trace)]
25//! struct Node {
26//! value: i32,
27//! next: Option<Gc<Node>>,
28//! }
29//!
30//! let node = Gc::new(Node { value: 1, next: None });
31//! ```
32//!
33//! # Handling Cycles
34//!
35//! ```ignore
36//! use rudo_gc::{Gc, Trace, collect};
37//! use std::cell::RefCell;
38//!
39//! #[derive(Trace)]
40//! struct Node {
41//! next: RefCell<Option<Gc<Node>>>,
42//! }
43//!
44//! let a = Gc::new(Node { next: RefCell::new(None) });
45//! let b = Gc::new(Node { next: RefCell::new(None) });
46//!
47//! // Create cycle: a -> b -> a
48//! *a.next.borrow_mut() = Some(Gc::clone(&b));
49//! *b.next.borrow_mut() = Some(Gc::clone(&a));
50//!
51//! drop(a);
52//! drop(b);
53//! collect(); // Cycle is detected and freed
54//! ```
55
56#![warn(missing_docs)]
57#![warn(clippy::pedantic)]
58#![warn(clippy::nursery)]
59#![allow(clippy::module_name_repetitions)]
60#![allow(clippy::missing_panics_doc)]
61#![allow(clippy::not_unsafe_ptr_arg_deref)]
62#![allow(clippy::clone_on_copy)]
63
64pub mod cell;
65pub mod gc;
66pub mod handles;
67mod metrics;
68mod ptr;
69mod scan;
70mod stack;
71mod trace;
72mod trace_closure;
73
74pub mod sync;
75
76#[cfg(feature = "tracing")]
77mod tracing;
78
79#[cfg(feature = "tokio")]
80pub mod tokio;
81
82/// `BiBOP` memory management internals.
83///
84/// This module is public for testing and advanced use cases.
85/// Most users should use `Gc<T>` directly.
86pub mod heap;
87
88// Re-export public API
89pub use cell::GcCell;
90pub use cell::{GcCapture, GcThreadSafeCell, GcThreadSafeRefMut};
91pub use gc::incremental::{
92 is_incremental_marking_active, is_write_barrier_active, mark_new_object_black,
93 IncrementalConfig, IncrementalMarkState, MarkPhase, MarkSliceResult, MarkStats,
94};
95
96#[cfg(feature = "debug-suspicious-sweep")]
97pub use gc::{
98 clear_history, current_gc_cycle_id, get_gc_cycle_id, is_detection_enabled, is_suspicious_sweep,
99 record_young_object, set_detection_enabled,
100};
101
102/// Enable or disable suspicious sweep detection at runtime.
103///
104/// This allows toggling the detection on/off in production for debugging.
105#[cfg(feature = "debug-suspicious-sweep")]
106pub fn set_suspicious_sweep_detection(enabled: bool) {
107 gc::set_detection_enabled(enabled);
108}
109
110/// Check if suspicious sweep detection is currently enabled.
111#[must_use]
112#[cfg(feature = "debug-suspicious-sweep")]
113pub fn is_suspicious_sweep_detection_enabled() -> bool {
114 gc::is_detection_enabled()
115}
116pub use sync::{GcMutex, GcRwLock};
117
118/// Configure incremental marking settings.
119///
120/// Use this to enable and configure incremental marking for reduced GC pause times.
121pub fn set_incremental_config(config: gc::incremental::IncrementalConfig) {
122 IncrementalMarkState::global().set_config(config);
123}
124
125/// Check if incremental GC is enabled.
126#[must_use]
127pub fn is_incremental_gc_enabled() -> bool {
128 IncrementalMarkState::global().config().enabled
129}
130
131/// Get the current incremental marking configuration.
132#[must_use]
133pub fn get_incremental_config() -> gc::incremental::IncrementalConfig {
134 *IncrementalMarkState::global().config()
135}
136
137/// Yield to the garbage collector for cooperative scheduling.
138///
139/// This function allows the GC to run during long-running computations,
140/// which is particularly useful when incremental marking is enabled.
141/// When incremental marking is not active, this is a no-op.
142///
143/// # Examples
144///
145/// ```ignore
146/// use rudo_gc::Gc;
147///
148/// fn process_large_dataset(items: &[Item]) {
149/// for (i, item) in items.iter().enumerate() {
150/// process_item(item);
151///
152/// // Yield every 1000 items to allow GC marking
153/// if i % 1000 == 0 {
154/// Gc::<()>::yield_now();
155/// }
156/// }
157/// }
158/// ```
159pub fn yield_now() {
160 if crate::gc::incremental::is_incremental_marking_active() {
161 let config = get_incremental_config();
162 let budget = config.increment_size;
163 crate::heap::with_heap(|heap| {
164 let _ = crate::gc::incremental::incremental_mark_slice(heap, budget);
165 });
166 }
167}
168pub use gc::{
169 collect, collect_full, default_collect_condition, safepoint, set_collect_condition,
170 set_gc_enabled, CollectInfo, PerThreadMarkQueue, StealQueue,
171};
172pub use handles::{
173 AsyncHandle, AsyncHandleGuard, AsyncHandleScope, EscapeableHandleScope, Handle, HandleScope,
174 MaybeHandle, SealedHandleScope,
175};
176pub use metrics::{
177 current_heap_size, current_old_size, current_young_size, gc_history, global_metrics,
178 last_gc_metrics, CollectionType, FallbackReason, GcHistory, GcMetrics, GlobalMetrics,
179};
180pub use ptr::{Ephemeron, Gc, GcBox, Weak};
181pub use scan::scan_heap_region_conservatively;
182pub use trace::{Trace, Visitor};
183pub use trace_closure::TraceClosure;
184
185#[cfg(feature = "tracing")]
186pub use tracing::GcId;
187
188// Re-export derive macros when feature is enabled
189#[cfg(feature = "derive")]
190pub use rudo_gc_derive::Trace;
191
192#[doc(hidden)]
193pub mod test_util {
194 pub use crate::gc::{clear_test_roots, register_test_root};
195
196 #[cfg(any(test, feature = "test-util"))]
197 pub use crate::gc::iter_test_roots;
198
199 /// Get the internal `GcBox` pointer.
200 pub fn internal_ptr<T: crate::Trace>(gc: &crate::Gc<T>) -> *const u8 {
201 crate::Gc::internal_ptr(gc)
202 }
203
204 /// Reconstruct a `Gc` from an internal pointer.
205 ///
206 /// # Safety
207 ///
208 /// The pointer must be a valid, currently allocated `GcBox<T>`.
209 #[must_use]
210 pub unsafe fn from_raw<T: crate::Trace + 'static>(ptr: *const u8) -> crate::Gc<T> {
211 unsafe { crate::Gc::from_raw(ptr) }
212 }
213
214 /// Clear CPU registers to prevent stale pointer values from being treated as roots.
215 ///
216 /// This is useful in tests to ensure objects are collected even when
217 /// stale pointer values remain in callee-saved registers after function returns.
218 ///
219 /// # Safety
220 ///
221 /// This function clears callee-saved registers (R12-R15 on `x86_64`).
222 /// It should only be called when those registers don't contain values
223 /// needed by the calling code.
224 pub unsafe fn clear_registers() {
225 // SAFETY: Caller guarantees that callee-saved registers don't contain
226 // values needed by the calling code.
227 unsafe { crate::stack::clear_registers() };
228 }
229
230 /// Reset all global GC state for test isolation.
231 ///
232 /// This function clears:
233 /// - Thread registry (unregisters all threads)
234 /// - Segment manager (frees all pages)
235 /// - GC requested flag
236 /// - Page size cache
237 /// - Test roots
238 ///
239 /// Call this at the start of each test to ensure clean state:
240 ///
241 /// ```ignore
242 /// use rudo_gc::test_util::reset;
243 ///
244 /// #[test]
245 /// fn my_test() {
246 /// reset();
247 /// // Test code with clean GC state
248 /// }
249 /// ```
250 pub fn reset() {
251 unsafe { crate::heap::reset_for_testing() };
252 clear_test_roots();
253 crate::gc::incremental::IncrementalMarkState::global().reset();
254 }
255}
256
257#[cfg(test)]
258mod blacklisting_test;