Skip to main content

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;