safe_gc/lib.rs
1#![doc = include_str!("../README.md")]
2#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4
5mod free_list;
6mod mark_bits;
7
8use free_list::FreeList;
9use mark_bits::MarkBits;
10use std::{
11 any::{Any, TypeId},
12 cell::RefCell,
13 collections::HashMap,
14 fmt::Debug,
15 hash::Hash,
16 rc::Rc,
17 sync::atomic,
18};
19
20/// Report references to other GC-managed objects to the collector.
21///
22/// This trait must be implemented by all types that are allocated within a
23/// [`Heap`].
24///
25/// Failure to enumerate all edges in an instance will result in wacky -- but
26/// still safe! -- behavior: panics when attempting to access a collected
27/// object, accesses to a "wrong" object that's been allocated in place of the
28/// old object, etc...
29///
30/// # Example
31///
32/// ```
33/// use safe_gc::{Collector, Gc, Trace};
34///
35/// struct List<T: Trace> {
36/// value: Gc<T>,
37/// prev: Option<Gc<List<T>>>,
38/// next: Option<Gc<List<T>>>,
39/// }
40///
41/// impl<T: Trace> Trace for List<T> {
42/// fn trace(&self, collector: &mut Collector) {
43/// collector.edge(self.value);
44/// if let Some(prev) = self.prev {
45/// collector.edge(prev);
46/// }
47/// if let Some(next) = self.next {
48/// collector.edge(next);
49/// }
50/// }
51/// }
52/// ```
53pub trait Trace: Any {
54 /// Call `collector.edge(gc)` for each `Gc<T>` reference within `self`.
55 fn trace(&self, collector: &mut Collector);
56}
57
58/// A reference to a garbage-collected `T`.
59///
60/// `Gc<T>` should be used when:
61///
62/// * Referencing other GC-managed objects from within a GC-managed object's
63/// type definition.
64///
65/// * Traversing or mutating GC-managed objects when you know a garbage
66/// collection cannot happen.
67///
68/// A `Gc<T>` does *not* root the referenced `T` or keep it alive across garbage
69/// collections. (The [`Root<T>`][crate::Root] type does that.) Therefore,
70/// `Gc<T>` should *not* be used to hold onto GC references across any operation
71/// that could trigger a garbage collection.
72///
73/// # Example: Referencing Other GC-Managed Objects Within a GC-Managed Object
74///
75/// ```
76/// use safe_gc::{Collector, Gc, Trace};
77///
78/// struct Tree<T: Trace> {
79/// // A non-nullable reference to a GC-managed `T`.
80/// value: Gc<T>,
81///
82/// // Nullable references to parent, left, and right tree nodes.
83/// parent: Option<Gc<Tree<T>>>,
84/// left: Option<Gc<Tree<T>>>,
85/// right: Option<Gc<Tree<T>>>,
86/// }
87///
88/// impl<T: Trace> Trace for Tree<T> {
89/// fn trace(&self, collector: &mut Collector) {
90/// // Report each of the `Gc<T>`s referenced from within `self` to the
91/// // collector. See the `Trace` docs for more details.
92/// collector.edge(self.value);
93/// if let Some(parent) = self.parent {
94/// collector.edge(parent);
95/// }
96/// if let Some(left) = self.left {
97/// collector.edge(left);
98/// }
99/// if let Some(right) = self.right {
100/// collector.edge(right);
101/// }
102/// }
103/// }
104/// ```
105///
106/// # Example: Accessing a `Gc<T>`'s referenced `T`
107///
108/// ```
109/// use safe_gc::{Gc, Heap, Trace};
110///
111/// struct Node {
112/// value: u32,
113/// tail: Option<Gc<Node>>,
114/// }
115///
116/// impl Trace for Node {
117/// // ...
118/// # fn trace(&self, _: &mut safe_gc::Collector) {}
119/// }
120///
121/// let mut heap = Heap::new();
122///
123/// let a = heap.alloc(Node { value: 36, tail: None });
124/// let b = heap.alloc(Node { value: 42, tail: Some(a.into()) });
125/// let c = heap.alloc(Node { value: 99, tail: Some(b.clone().into()) });
126///
127/// // Read `(*c).tail`.
128/// let c_tail = heap[&c].tail;
129/// assert_eq!(c_tail, Some(b.into()));
130///
131/// // Write `(*c).tail = None`.
132/// heap[&c].tail = None;
133/// ```
134///
135/// # Example: Downgrading a `Root<T>` into a `Gc<T>`
136///
137/// The [`Heap::alloc`] method returns rooted references, but to store those
138/// references into the field of a GC-managed object, you'll need to unroot the
139/// reference with [`Root<T>::unrooted`][crate::Root::unrooted]. (You can also
140/// use `root.into()` or `Gc::from(root)`.)
141///
142/// ```
143/// use safe_gc::{Gc, Heap, Root, Trace};
144///
145/// struct Cat {
146/// siblings: Vec<Gc<Cat>>,
147/// }
148///
149/// impl Trace for Cat {
150/// // ...
151/// # fn trace(&self, _: &mut safe_gc::Collector) {}
152/// }
153///
154/// let mut heap = Heap::new();
155///
156/// let momo: Root<Cat> = heap.alloc(Cat { siblings: vec![] });
157/// let juno: Root<Cat> = heap.alloc(Cat { siblings: vec![] });
158///
159/// // Add `momo` and `juno` to each other's siblings vectors. This requires
160/// // downgrading the `Root<Cat>`s to `Gc<Cat>`s via the `unrooted` method.
161/// heap[&momo].siblings.push(juno.unrooted());
162/// heap[&juno].siblings.push(momo.unrooted());
163/// ```
164///
165/// # Example: Upgrading a `Gc<T>` into a `Root<T>`
166///
167/// You can upgrade a `Gc<T>` into a [`Root<T>`][crate::Root] via the
168/// [`Heap::root`] method, so that you can hold references to GC-objects across
169/// operations that can potentially trigger garbage collections.
170///
171/// See the docs for [`Heap::root`] for more details and an example.
172pub struct Gc<T> {
173 heap_id: u32,
174 index: u32,
175 _phantom: std::marker::PhantomData<*mut T>,
176}
177
178impl<T> Gc<T> {
179 /// Create a new `Gc<T>` from a raw heap ID and index.
180 pub fn from_raw_parts(heap_id: u32, index: u32) -> Self {
181 Self {
182 heap_id,
183 index,
184 _phantom: std::marker::PhantomData,
185 }
186 }
187
188 /// Get the raw heap ID and index from a `Gc<T>`.
189 pub fn into_raw_parts(self) -> (u32, u32) {
190 (self.heap_id, self.index)
191 }
192}
193
194impl<T> Clone for Gc<T> {
195 fn clone(&self) -> Self {
196 *self
197 }
198}
199
200impl<T> Copy for Gc<T> {}
201
202impl<T> Debug for Gc<T> {
203 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204 f.debug_struct(&format!("Gc<{}>", std::any::type_name::<T>()))
205 .field("heap_id", &self.heap_id)
206 .field("index", &self.index)
207 .finish()
208 }
209}
210
211impl<T> Hash for Gc<T> {
212 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
213 self.heap_id.hash(state);
214 self.index.hash(state);
215 }
216}
217
218impl<T> PartialEq<Self> for Gc<T> {
219 fn eq(&self, other: &Self) -> bool {
220 self.heap_id == other.heap_id && self.index == other.index
221 }
222}
223
224impl<T> PartialEq<Root<T>> for Gc<T>
225where
226 T: Trace,
227{
228 fn eq(&self, other: &Root<T>) -> bool {
229 *self == other.unrooted()
230 }
231}
232
233impl<T> Eq for Gc<T> {}
234
235impl<T> From<Root<T>> for Gc<T>
236where
237 T: Trace,
238{
239 fn from(root: Root<T>) -> Self {
240 root.unrooted()
241 }
242}
243
244impl<'a, T> From<&'a Root<T>> for Gc<T>
245where
246 T: Trace,
247{
248 fn from(root: &'a Root<T>) -> Self {
249 root.unrooted()
250 }
251}
252
253struct RootSet<T> {
254 inner: Rc<RefCell<FreeList<Gc<T>>>>,
255}
256
257impl<T> Clone for RootSet<T> {
258 fn clone(&self) -> Self {
259 Self {
260 inner: Rc::clone(&self.inner),
261 }
262 }
263}
264
265impl<T> Default for RootSet<T> {
266 fn default() -> Self {
267 Self {
268 inner: Rc::new(RefCell::new(FreeList::<Gc<T>>::default())),
269 }
270 }
271}
272
273impl<T> RootSet<T>
274where
275 T: Trace,
276{
277 fn insert(&self, gc: Gc<T>) -> Root<T> {
278 let mut inner = self.inner.borrow_mut();
279 let index = inner.alloc(gc);
280 Root {
281 roots: self.clone(),
282 index,
283 }
284 }
285
286 fn remove(&self, index: u32) {
287 let mut inner = self.inner.borrow_mut();
288 inner.dealloc(index);
289 }
290
291 fn trace(&self, collector: &mut Collector) {
292 let inner = self.inner.borrow();
293 for (_, gc) in inner.iter() {
294 collector.edge(*gc);
295 }
296 }
297}
298
299/// A rooted reference to a GC-managed `T`.
300///
301/// `Root<T>`s prevent their referenced `T` from being reclaimed during garbage
302/// collections. This makes them suitable for holding references to GC-managed
303/// objects across operations that can trigger GCs.
304///
305/// `Root<T>`s are *not* suitable for referencing other GC-manged objects within
306/// the definition of a GC-managed object. Doing this will effectively leak
307/// everything transitively referenced from the `Root<T>`. Instead, use
308/// [`Gc<T>`][crate::Gc] for references to other GC-managed objects from within
309/// a GC-managed object.
310///
311/// See also the docs for [`Gc<T>`][crate::Gc] for more examples of converting
312/// between `Root<T>` and `Gc<T>` and when you want to use which type.
313///
314/// # Example: Creating a `Root<T>` via Allocation
315///
316/// ```
317/// use safe_gc::{Gc, Heap, Root, Trace};
318///
319/// struct Node {
320/// value: u32,
321/// tail: Option<Gc<Node>>,
322/// }
323///
324/// impl Trace for Node {
325/// // ...
326/// # fn trace(&self, _: &mut safe_gc::Collector) {}
327/// }
328///
329/// let mut heap = Heap::new();
330///
331/// // Allocating a new GC object in a heap returns a `Root<T>` reference.
332/// let node: Root<Node> = heap.alloc(Node { value: 1234, tail: None });
333/// ```
334pub struct Root<T>
335where
336 T: Trace,
337{
338 roots: RootSet<T>,
339 index: u32,
340}
341
342impl<T> Clone for Root<T>
343where
344 T: Trace,
345{
346 fn clone(&self) -> Self {
347 self.roots.insert(self.unrooted())
348 }
349}
350
351impl<T> PartialEq<Root<T>> for Root<T>
352where
353 T: Trace,
354{
355 fn eq(&self, other: &Root<T>) -> bool {
356 self.unrooted() == other.unrooted()
357 }
358}
359
360impl<T> PartialEq<Gc<T>> for Root<T>
361where
362 T: Trace,
363{
364 fn eq(&self, other: &Gc<T>) -> bool {
365 self.unrooted() == *other
366 }
367}
368
369impl<T> Drop for Root<T>
370where
371 T: Trace,
372{
373 fn drop(&mut self) {
374 self.roots.remove(self.index);
375 }
376}
377
378impl<T> Root<T>
379where
380 T: Trace,
381{
382 /// Get an unrooted [`Gc<T>`][crate::Gc] reference pointing to the same `T`
383 /// that this `Root<T>` points to.
384 ///
385 /// See also the docs for [`Gc<T>`][crate::Gc] for more examples of
386 /// converting between `Root<T>` and `Gc<T>` and when you want to use which
387 /// type.
388 pub fn unrooted(&self) -> Gc<T> {
389 let inner = (*self.roots.inner).borrow();
390 *inner.get(self.index)
391 }
392}
393
394struct Arena<T> {
395 roots: RootSet<T>,
396 elements: FreeList<T>,
397}
398
399// We don't default to 0-capacity arenas because the arenas themselves are
400// lazily constructed, and so by the time we are constructing an arena, we will
401// always immediately push onto it.
402const DEFAULT_ARENA_CAPACITY: usize = 32;
403
404impl<T> Default for Arena<T> {
405 fn default() -> Self {
406 Arena {
407 roots: RootSet::<T>::default(),
408 elements: FreeList::with_capacity(DEFAULT_ARENA_CAPACITY),
409 }
410 }
411}
412
413impl<T> Arena<T>
414where
415 T: Trace,
416{
417 #[inline]
418 fn try_alloc(&mut self, heap_id: u32, value: T) -> Result<Root<T>, T> {
419 let index = self.elements.try_alloc(value)?;
420 Ok(self.root(Gc {
421 heap_id,
422 index,
423 _phantom: std::marker::PhantomData,
424 }))
425 }
426
427 fn alloc_slow(&mut self, heap_id: u32, value: T) -> Root<T> {
428 if self.elements.len() == self.elements.capacity() {
429 self.elements.double_capacity();
430 }
431 let index = self.elements.try_alloc(value).ok().unwrap();
432 self.root(Gc {
433 heap_id,
434 index,
435 _phantom: std::marker::PhantomData,
436 })
437 }
438
439 #[inline]
440 fn root(&self, gc: Gc<T>) -> Root<T> {
441 self.roots.insert(gc)
442 }
443}
444
445trait ArenaObject: Any {
446 fn as_any(&self) -> &dyn Any;
447
448 fn as_any_mut(&mut self) -> &mut dyn Any;
449
450 fn trace_roots(&self, collector: &mut Collector);
451
452 fn trace_one(&mut self, index: u32, collector: &mut Collector);
453
454 fn capacity(&self) -> usize;
455
456 fn sweep(&mut self, mark_bits: &MarkBits);
457}
458
459impl<T> ArenaObject for Arena<T>
460where
461 T: Trace,
462{
463 fn as_any(&self) -> &dyn Any {
464 self
465 }
466
467 fn as_any_mut(&mut self) -> &mut dyn Any {
468 self
469 }
470
471 fn trace_roots(&self, collector: &mut Collector) {
472 self.roots.trace(collector);
473 }
474
475 fn trace_one(&mut self, index: u32, collector: &mut Collector) {
476 self.elements.get(index).trace(collector);
477 }
478
479 fn capacity(&self) -> usize {
480 self.elements.capacity()
481 }
482
483 fn sweep(&mut self, mark_bits: &MarkBits) {
484 let capacity = self.elements.capacity();
485 for index in 0..u32::try_from(capacity).unwrap() {
486 if !mark_bits.get(index) {
487 self.elements.dealloc(index);
488 }
489 }
490
491 // If we are close to running out of capacity, reserve additional
492 // space. This amortizes the cost of collections and avoids the failure
493 // mode where we do a full GC on every object allocation:
494 //
495 // * The arena has zero available capacity.
496 // * The mutator tries to allocate, triggering a GC.
497 // * The GC is able to free up only one (or only a small handfull) of
498 // slots.
499 // * The mutator's pending allocation fills the newly reclaimed slot.
500 // * Now we are out of capacity again, and the process repeats from the
501 // top.
502 let len = self.elements.len();
503 let available = capacity - len;
504 if available < capacity / 4 {
505 self.elements.double_capacity();
506 }
507 }
508}
509
510/// The garbage collector for a heap.
511///
512/// GC-managed objects should report all of their references to other GC-managed
513/// objects (aka "edges") to the collector in their [`Trace`] implementations.
514///
515/// See the docs for [`Trace`] for more information.
516//
517// This type is only exposed to users so they can report edges, but internally
518// this does a bit more than that:
519//
520// * It maintains the mark stack work lists that contain all the GC objects
521// we've seen but have not yet finished processing.
522//
523// * It maintains the mark bits for all GC objects in the heap, which keep track
524// of which GC objects we have and have not seen while tracing the live set.
525pub struct Collector {
526 heap_id: u32,
527 mark_stacks: HashMap<TypeId, Vec<u32>>,
528 mark_bits: HashMap<TypeId, MarkBits>,
529}
530
531impl Collector {
532 fn new(heap_id: u32) -> Self {
533 Self {
534 heap_id,
535 mark_stacks: HashMap::default(),
536 mark_bits: HashMap::default(),
537 }
538 }
539
540 /// Report a reference to another GC-managed object (aka an "edge" in the
541 /// heap graph).
542 ///
543 /// See the docs for [`Trace`] for more information.
544 ///
545 /// Panics when given cross-heap edges. See the "Cross-`Heap` GC References"
546 /// section of [`Heap`]'s documentation for details on cross-heap edges.
547 pub fn edge<T>(&mut self, to: Gc<T>)
548 where
549 T: Trace,
550 {
551 assert_eq!(to.heap_id, self.heap_id);
552 let ty = TypeId::of::<T>();
553 let mark_bits = self.mark_bits.get_mut(&ty).unwrap();
554 if mark_bits.set(to.index) {
555 return;
556 }
557 let mark_stack = self.mark_stacks.entry(ty).or_default();
558 mark_stack.push(to.index);
559 }
560
561 fn next_non_empty_mark_stack(&self) -> Option<TypeId> {
562 self.mark_stacks.iter().find_map(
563 |(ty, stack)| {
564 if stack.is_empty() {
565 None
566 } else {
567 Some(*ty)
568 }
569 },
570 )
571 }
572
573 fn pop_mark_stack(&mut self, type_id: TypeId) -> Option<u32> {
574 self.mark_stacks.get_mut(&type_id).unwrap().pop()
575 }
576}
577
578/// A collection of GC-managed objects.
579///
580/// A `Heap` is a collection of GC-managed objects that can all reference each
581/// other, and garbage collection is performed at the `Heap` granularity. The
582/// smaller the `Heap`, the less overhead imposed by garbage collecting it. The
583/// larger the `Heap`, the more overhead is imposed.
584///
585/// There are no restrictions on the shape of references between GC-managed
586/// objects within a `Heap`: references may form arbitrary cycles and there is
587/// no imposed ownership hierarchy.
588///
589/// # Allocating Objects
590///
591/// You can allocate objects with the [`Heap::alloc`] method.
592///
593/// You can allocate instances of any number of heterogeneous types of
594/// GC-managed objects within a `Heap`: they may, for example, contain both
595/// `Cons` objects and `Tree` objects. `Heap`s are *not* constrained to only
596/// instances of a single, uniform `T` type of GC objects.
597///
598/// All types allocated within a heap must, however, implement the [`Trace`]
599/// trait. See the [`Trace`] trait's docs for more details.
600///
601/// ```
602/// use safe_gc::{Gc, Heap, Trace};
603///
604/// struct Tree<T: Trace> {
605/// value: Gc<T>,
606/// parent: Option<Gc<Tree<T>>>,
607/// left: Option<Gc<Tree<T>>>,
608/// right: Option<Gc<Tree<T>>>,
609/// }
610///
611/// impl<T: Trace> Trace for Tree<T> {
612/// // See the docs for `Trace` for details...
613/// # fn trace(&self, _: &mut safe_gc::Collector) {}
614/// }
615///
616/// struct Cat {
617/// cuteness: u32,
618/// cat_tree: Option<Gc<Tree<Cat>>>,
619/// }
620///
621/// impl Trace for Cat {
622/// // See the docs for `Trace` for details...
623/// # fn trace(&self, _: &mut safe_gc::Collector) {}
624/// }
625///
626/// let mut heap = Heap::new();
627///
628/// // Allocate a cat within the heap!
629/// let goomba = heap.alloc(Cat {
630/// cuteness: u32::MAX,
631/// cat_tree: None,
632/// });
633///
634/// // Also allocate a tree within the heap!
635/// let tree = heap.alloc(Tree {
636/// value: goomba.unrooted(),
637/// parent: None,
638/// left: None,
639/// right: None,
640/// });
641///
642/// // Create a cycle: the `tree` already references `goomba`, but now
643/// // `goomba` references the `tree` as well.
644/// heap[goomba].cat_tree = Some(tree.into());
645/// ```
646///
647/// # Accessing Allocating Objects
648///
649/// Rather than dereferencing pointers to allocated GC objects directly, you
650/// must use one of two types ([`Gc<T>`][crate::Gc] or [`Root<T>`][crate::Root])
651/// to index into the `Heap` to access the referenced `T` object. This enables
652/// `safe-gc`'s lack of `unsafe` code and allows the implementation to follow
653/// Rust's ownership and borrowing discipline.
654///
655/// Given a [`Gc<T>`][crate::Gc] or [`Root<T>`][crate::Root], you can use the
656/// [`Heap::get`] method to get an `&T`. Similarly, you can use the
657/// [`Heap::get_mut`] method to get an `&mut T`. As convenient syntactic sugar,
658/// `Heap` also implements [`std::ops::Index`] and [`std::ops::IndexMut`] as
659/// aliases for `get` and `get_mut` respectively.
660///
661/// The [`Gc<T>`][crate::Gc] index type is an unrooted reference, suitable for
662/// defining references to other GC-managed types within a GC-managed type's
663/// definition.
664///
665/// The [`Root<T>`][crate::Root] index type is a rooted reference, prevents its
666/// referent from being reclaimed during garbage collections, and is suitable
667/// for holding GC-managed objects alive from outside of the `Heap`.
668///
669/// See the docs for [`Gc<T>`][crate::Gc] and [`Root<T>`][crate::Root] for more
670/// details, how to convert between them, and other examples.
671///
672/// ```
673/// use safe_gc::{Heap, Trace};
674///
675/// struct Point(u32, u32);
676///
677/// impl Trace for Point {
678/// // ...
679/// # fn trace(&self, _: &mut safe_gc::Collector) {}
680/// }
681///
682/// let mut heap = Heap::new();
683///
684/// let p = heap.alloc(Point(42, 36));
685///
686/// // Read data from an object in the heap.
687/// let p0 = heap[&p].0;
688/// assert_eq!(p0, 42);
689///
690/// // Write data to an object in the heap.
691/// heap[&p].1 = 5;
692/// ```
693///
694/// # When Can Garbage Collections Happen?
695///
696/// There are two ways to trigger garbage collection:
697///
698/// 1. When the [`Heap::gc`] method is explicitly invoked.
699///
700/// 2. When allocating an object in the heap with [`Heap::alloc`].
701///
702/// Note that both of those methods require an `&mut self`, so they cannot be
703/// invoked when there are shared `&Heap` borrows. Therefore, when there are
704/// shared `&Heap` borrows, you may freely use [`Gc<T>`][crate::Gc] instead of
705/// [`Root<T>`][crate::Root] to hold references to objects in the heap without
706/// fear of the GC collecting the objects out from under your feet. Traversing
707/// deeply nested structures will have more overhead when using
708/// [`Root<T>`][crate::Root] than when using [`Gc<T>`][crate::Gc] because
709/// [`Root<T>`][crate::Root] must maintain its associated entry in the heap's
710/// root set.
711///
712/// # Finalization and `Drop`
713///
714/// Unlike many GC libraries for Rust, it is perfectly safe and footgun-free to
715/// put `Drop` types in the GC heap, even if they have references to other GC
716/// objects. Finalization of and `Drop` implementations for GC objects is
717/// usually tricky because of the risks of accessing objects that have already
718/// been reclaimed by the collector or accidentally entrenching objects and
719/// making them live again.
720///
721/// The reason this is fine with `safe-gc` is because the `Drop` implementation
722/// doesn't have access to a `Heap`, so it statically *cannot* suffer from those
723/// kinds of bugs.
724///
725/// # Cross-`Heap` GC References
726///
727/// Typically, GC objects will only reference other GC objects that are within
728/// the same `Heap`. In fact, edges reported to the [`Collector`] *must* point
729/// to objects within the same `Heap` as the object being traced or else
730/// [`Collector::edge`] will raise a panic.
731///
732/// However, you can create cross-heap edges with [`Root<T>`][crate::Root], but
733/// it requires some care. While a [`Root<T>`][crate::Root] pointing to an
734/// object in the same `Heap` will make all transitively referenced objects
735/// unreclaimable, a [`Root<T>`][crate::Root] pointing to an object in another
736/// heap will not indefinitely leak memory, provided you do not create *cycles*
737/// across `Heap`s. To fully collect all garbage across all `Heap`s, you will
738/// need to run GC on each `Heap` either
739///
740/// * in topological order of cross-`Heap` edges, if you statically know that
741/// order in your application, or
742///
743/// * in a fixed point loop, if you do not statically know that order.
744///
745/// However, if you don't statically know that topological order, it is
746/// recommended that you don't create cross-`Heap` edges; you will likely
747/// accidentally create cycles and leak memory. Instead, simply put everything
748/// in the same `Heap`.
749///
750/// Collecting cycles across `Heap`s would require a global, cross-`Heap`
751/// collector, and is not a goal of this crate. If you do choose to create
752/// cross-`Heap` references, the responsibility of avoiding cross-`Heap` cycles
753/// is yours.
754pub struct Heap {
755 // The unique ID for this heap. Used to ensure that `Gc<T>`s are only used
756 // with their associated arena. Could use branded lifetimes to avoid these
757 // IDs and checks statically, but the API is gross and pushes lifetimes into
758 // everything.
759 id: u32,
760
761 // A map from `type_id(T)` to `Arena<T>`.
762 arenas: HashMap<TypeId, Box<dyn ArenaObject>>,
763
764 collector: Collector,
765}
766
767impl Default for Heap {
768 fn default() -> Self {
769 Heap::new()
770 }
771}
772
773impl<T> std::ops::Index<Root<T>> for Heap
774where
775 T: Trace,
776{
777 type Output = T;
778 fn index(&self, root: Root<T>) -> &Self::Output {
779 &self[root.unrooted()]
780 }
781}
782
783impl<T> std::ops::IndexMut<Root<T>> for Heap
784where
785 T: Trace,
786{
787 fn index_mut(&mut self, root: Root<T>) -> &mut Self::Output {
788 &mut self[root.unrooted()]
789 }
790}
791
792impl<'a, T> std::ops::Index<&'a Root<T>> for Heap
793where
794 T: Trace,
795{
796 type Output = T;
797 fn index(&self, root: &'a Root<T>) -> &Self::Output {
798 &self[root.unrooted()]
799 }
800}
801
802impl<'a, T> std::ops::IndexMut<&'a Root<T>> for Heap
803where
804 T: Trace,
805{
806 fn index_mut(&mut self, root: &'a Root<T>) -> &mut Self::Output {
807 &mut self[root.unrooted()]
808 }
809}
810
811impl<T> std::ops::Index<Gc<T>> for Heap
812where
813 T: Trace,
814{
815 type Output = T;
816 fn index(&self, index: Gc<T>) -> &Self::Output {
817 self.get(index)
818 }
819}
820
821impl<T> std::ops::IndexMut<Gc<T>> for Heap
822where
823 T: Trace,
824{
825 fn index_mut(&mut self, gc: Gc<T>) -> &mut Self::Output {
826 self.get_mut(gc)
827 }
828}
829
830impl Heap {
831 /// Construct a new `Heap`.
832 ///
833 /// # Example
834 ///
835 /// ```
836 /// use safe_gc::Heap;
837 ///
838 /// let heap = Heap::new();
839 /// ```
840 #[inline]
841 pub fn new() -> Self {
842 let id = Self::next_id();
843 Self {
844 id,
845 arenas: HashMap::default(),
846 collector: Collector::new(id),
847 }
848 }
849
850 #[inline]
851 fn next_id() -> u32 {
852 static ID_COUNTER: atomic::AtomicU32 = atomic::AtomicU32::new(0);
853 ID_COUNTER.fetch_add(1, atomic::Ordering::AcqRel)
854 }
855
856 #[inline]
857 fn arena<T>(&self) -> Option<&Arena<T>>
858 where
859 T: Trace,
860 {
861 let arena = self.arenas.get(&TypeId::of::<T>())?;
862 Some(arena.as_any().downcast_ref().unwrap())
863 }
864
865 #[inline]
866 fn arena_mut<T>(&mut self) -> Option<&mut Arena<T>>
867 where
868 T: Trace,
869 {
870 let arena = self.arenas.get_mut(&TypeId::of::<T>())?;
871 Some(arena.as_any_mut().downcast_mut().unwrap())
872 }
873
874 #[inline]
875 fn ensure_arena<T>(&mut self) -> &mut Arena<T>
876 where
877 T: Trace,
878 {
879 self.arenas
880 .entry(TypeId::of::<T>())
881 .or_insert_with(|| Box::new(Arena::<T>::default()) as _)
882 .as_any_mut()
883 .downcast_mut()
884 .unwrap()
885 }
886
887 /// Allocate an object in the heap.
888 ///
889 /// # Example
890 ///
891 /// ```
892 /// use safe_gc::{Gc, Heap, Trace};
893 ///
894 /// struct List {
895 /// value: u32,
896 /// prev: Option<Gc<List>>,
897 /// next: Option<Gc<List>>,
898 /// }
899 ///
900 /// impl Trace for List {
901 /// // See the docs for `Trace` for details...
902 /// # fn trace(&self, _: &mut safe_gc::Collector) {}
903 /// }
904 ///
905 /// let mut heap = Heap::new();
906 ///
907 /// // Allocate an object in the heap.
908 /// let list = heap.alloc(List {
909 /// value: 10,
910 /// prev: None,
911 /// next: None,
912 /// });
913 /// ```
914 #[inline]
915 pub fn alloc<T>(&mut self, value: T) -> Root<T>
916 where
917 T: Trace,
918 {
919 let heap_id = self.id;
920 let arena = self.ensure_arena::<T>();
921 match arena.try_alloc(heap_id, value) {
922 Ok(root) => root,
923 Err(value) => self.alloc_slow(value),
924 }
925 }
926
927 #[inline(never)]
928 fn alloc_slow<T>(&mut self, value: T) -> Root<T>
929 where
930 T: Trace,
931 {
932 // TODO: need to temporarily root `value` across this GC so that its
933 // edges don't get collected.
934 self.gc();
935 let heap_id = self.id;
936 let arena = self.ensure_arena::<T>();
937 arena.alloc_slow(heap_id, value)
938 }
939
940 /// Get a shared reference to an allocated object in the heap.
941 ///
942 /// You can also use [`std::ops::Index`] to access objects in the heap.
943 ///
944 /// # Example
945 ///
946 /// ```
947 /// use safe_gc::{Gc, Heap, Trace};
948 ///
949 /// struct List {
950 /// value: u32,
951 /// prev: Option<Gc<List>>,
952 /// next: Option<Gc<List>>,
953 /// }
954 ///
955 /// impl Trace for List {
956 /// // See the docs for `Trace` for details...
957 /// # fn trace(&self, _: &mut safe_gc::Collector) {}
958 /// }
959 ///
960 /// let mut heap = Heap::new();
961 ///
962 /// // Allocate an object in the heap.
963 /// let list = heap.alloc(List {
964 /// value: 10,
965 /// prev: None,
966 /// next: None,
967 /// });
968 ///
969 /// // Access an allocated object in the heap.
970 /// let value = heap.get(list).value;
971 /// assert_eq!(value, 10);
972 /// ```
973 #[inline]
974 pub fn get<T>(&self, gc: impl Into<Gc<T>>) -> &T
975 where
976 T: Trace,
977 {
978 let gc = gc.into();
979 assert_eq!(self.id, gc.heap_id);
980 let arena = self.arena::<T>().unwrap();
981 arena.elements.get(gc.index)
982 }
983
984 /// Get a shared reference to an allocated object in the heap.
985 ///
986 /// You can also use [`std::ops::Index`] to access objects in the heap.
987 ///
988 /// # Example
989 ///
990 /// ```
991 /// use safe_gc::{Gc, Heap, Trace};
992 ///
993 /// struct List {
994 /// value: u32,
995 /// prev: Option<Gc<List>>,
996 /// next: Option<Gc<List>>,
997 /// }
998 ///
999 /// impl Trace for List {
1000 /// // See the docs for `Trace` for details...
1001 /// # fn trace(&self, _: &mut safe_gc::Collector) {}
1002 /// }
1003 ///
1004 /// let mut heap = Heap::new();
1005 ///
1006 /// // Allocate an object in the heap.
1007 /// let list = heap.alloc(List {
1008 /// value: 10,
1009 /// prev: None,
1010 /// next: None,
1011 /// });
1012 ///
1013 /// // Mutate an allocated object in the heap.
1014 /// heap.get_mut(&list).value += 1;
1015 /// assert_eq!(heap[list].value, 11);
1016 /// ```
1017 #[inline]
1018 pub fn get_mut<T>(&mut self, gc: impl Into<Gc<T>>) -> &mut T
1019 where
1020 T: Trace,
1021 {
1022 let gc = gc.into();
1023 assert_eq!(self.id, gc.heap_id);
1024 let arena = self.arena_mut::<T>().unwrap();
1025 arena.elements.get_mut(gc.index)
1026 }
1027
1028 /// Root a reference to a GC object.
1029 ///
1030 /// This method upgrades a [`Gc<T>`][crate::Gc] into a
1031 /// [`Root<T>`][crate::Root]. This is useful for holding references to
1032 /// GC-managed objects across operations that can potentially trigger
1033 /// garbage collections, making sure that the collector doesn't reclaim them
1034 /// from under your feed.
1035 ///
1036 /// # Example
1037 ///
1038 /// ```
1039 /// use safe_gc::{Gc, Heap, Root, Trace};
1040 ///
1041 /// struct Node {
1042 /// value: u32,
1043 /// tail: Option<Gc<Node>>,
1044 /// }
1045 ///
1046 /// impl Trace for Node {
1047 /// // See the docs for `Trace` for details...
1048 /// # fn trace(&self, _: &mut safe_gc::Collector) {}
1049 /// }
1050 ///
1051 /// let mut heap = Heap::new();
1052 ///
1053 /// let a = heap.alloc(Node { value: 0, tail: None });
1054 /// let b = heap.alloc(Node { value: 1, tail: Some(a.into()) });
1055 ///
1056 /// // Get a reference to a `Gc<T>` from the heap.
1057 /// let b_tail: Gc<Node> = heap[b].tail.unwrap();
1058 ///
1059 /// // Upgrade the `Gc<T>` to a `Root<T>` via the `Heap::root` method so it is
1060 /// // suitable for holding across garbage collections.
1061 /// let b_tail: Root<Node> = heap.root(b_tail);
1062 ///
1063 /// // Now we can perform operations, like heap allocation, that might GC
1064 /// // without worrying about `b_tail`'s referenced GC object from being
1065 /// // collected out from under us.
1066 /// heap.alloc(Node { value: 123, tail: None });
1067 ///
1068 /// // And we can access `b_tail`'s referenced GC object again, after GC may
1069 /// // have happened.
1070 /// let b_tail_value = heap[&b_tail].value;
1071 /// assert_eq!(b_tail_value, 0);
1072 /// ```
1073 #[inline]
1074 pub fn root<T>(&self, gc: Gc<T>) -> Root<T>
1075 where
1076 T: Trace,
1077 {
1078 assert_eq!(self.id, gc.heap_id);
1079 let arena = self.arena::<T>().unwrap();
1080 arena.root(gc)
1081 }
1082
1083 /// Collect garbage.
1084 ///
1085 /// Any object in the heap that is not transitively referenced by a
1086 /// [`Root<T>`][crate::Root] will be reclaimed.
1087 ///
1088 /// # Example
1089 ///
1090 /// ```
1091 /// use safe_gc::Heap;
1092 ///
1093 /// let mut heap = Heap::new();
1094 ///
1095 /// # let allocate_a_bunch_of_stuff = |_| {};
1096 /// allocate_a_bunch_of_stuff(&mut heap);
1097 ///
1098 /// // Collect garbage!
1099 /// heap.gc();
1100 /// ```
1101 #[inline(never)]
1102 pub fn gc(&mut self) {
1103 debug_assert!(self.collector.mark_stacks.values().all(|s| s.is_empty()));
1104
1105 // Reset/pre-allocate the mark bits.
1106 for (ty, arena) in &self.arenas {
1107 self.collector
1108 .mark_bits
1109 .entry(*ty)
1110 .or_default()
1111 .reset(arena.capacity());
1112 }
1113
1114 // Mark all roots.
1115 for arena in self.arenas.values() {
1116 arena.trace_roots(&mut self.collector);
1117 }
1118
1119 // Mark everything transitively reachable from the roots.
1120 //
1121 // NB: We have a two-level fixed-point loop to avoid checking if every
1122 // mark stack is non-empty on every iteration of the hottest, inner-most
1123 // loop.
1124 while let Some(type_id) = self.collector.next_non_empty_mark_stack() {
1125 while let Some(index) = self.collector.pop_mark_stack(type_id) {
1126 self.arenas
1127 .get_mut(&type_id)
1128 .unwrap()
1129 .trace_one(index, &mut self.collector);
1130 }
1131 }
1132
1133 // Sweep.
1134 for (ty, arena) in &mut self.arenas {
1135 let mark_bits = &self.collector.mark_bits[ty];
1136 arena.sweep(mark_bits);
1137 }
1138 }
1139}
1140
1141#[cfg(test)]
1142mod tests {
1143 use super::*;
1144
1145 #[test]
1146 fn object_safety() {
1147 fn _trace(_: &dyn Trace) {}
1148 fn _arena_object(_: &dyn ArenaObject) {}
1149 }
1150}