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}