use crate::{ finalizer_safe, internals::GcBox, trace::{Finalize, Trace}, Allocator, }; use std::{ cell::Cell, cmp::Ordering, fmt::{self, Debug, Display}, hash::{Hash, Hasher}, marker::PhantomData, ops::Deref, ptr::NonNull, rc::Rc, }; use super::rootable::Rootable; /// A garbage-collected pointer type over an immutable value. pub struct Gc { pub(crate) inner_ptr: Cell>>, pub(crate) marker: PhantomData>, } impl Gc { /// Constructs a new `Gc` with the given value. pub fn new(value: T) -> Self { // Create GcBox and allocate it to heap. // // Note: Allocator can cause Collector to run let inner_ptr = Allocator::alloc_gc(GcBox::new(value)); // SAFETY: inner_ptr was just allocated, so it must be a valid value that implements [`Trace`] unsafe { (*inner_ptr.as_ptr()).value().unroot() } // SAFETY: inner_ptr is 2-byte aligned. let inner_ptr = unsafe { Rootable::new_unchecked(inner_ptr) }; Self { inner_ptr: Cell::new(inner_ptr.rooted()), marker: PhantomData, } } /// Consumes the `Gc`, returning a wrapped raw pointer. /// /// To avoid a memory leak, the pointer must be converted back to a `Gc` using [`Gc::from_raw`]. #[allow(clippy::use_self)] pub fn into_raw(this: Gc) -> NonNull> { let ptr = this.inner_ptr(); std::mem::forget(this); ptr } } impl Gc { /// Returns `true` if the two `Gc`s point to the same allocation. pub fn ptr_eq(this: &Self, other: &Self) -> bool { GcBox::ptr_eq(this.inner(), other.inner()) } /// Constructs a `Gc` from a raw pointer. /// /// The raw pointer must have been returned by a previous call to [`Gc::into_raw`][Gc::into_raw] /// where `U` must have the same size and alignment as `T`. /// /// # Safety /// /// This function is unsafe because improper use may lead to memory corruption, double-free, /// or misbehaviour of the garbage collector. #[must_use] pub unsafe fn from_raw(ptr: NonNull>) -> Self { // SAFETY: it is the caller's job to ensure the safety of this operation. unsafe { Self { inner_ptr: Cell::new(Rootable::new_unchecked(ptr).rooted()), marker: PhantomData, } } } } impl Gc { fn is_rooted(&self) -> bool { self.inner_ptr.get().is_rooted() } fn root_ptr(&self) { self.inner_ptr.set(self.inner_ptr.get().rooted()); } fn unroot_ptr(&self) { self.inner_ptr.set(self.inner_ptr.get().unrooted()); } pub(crate) fn inner_ptr(&self) -> NonNull> { assert!(finalizer_safe()); self.inner_ptr.get().as_ptr() } fn inner(&self) -> &GcBox { // SAFETY: Please see Gc::inner_ptr() unsafe { self.inner_ptr().as_ref() } } } impl Finalize for Gc {} // SAFETY: `Gc` maintains it's own rootedness and implements all methods of // Trace. It is not possible to root an already rooted `Gc` and vice versa. unsafe impl Trace for Gc { unsafe fn trace(&self) { // SAFETY: Inner must be live and allocated GcBox. unsafe { self.inner().mark_and_trace(); } } unsafe fn root(&self) { assert!(!self.is_rooted(), "Can't double-root a Gc"); // Try to get inner before modifying our state. Inner may be // inaccessible due to this method being invoked during the sweeping // phase, and we don't want to modify our state before panicking. self.inner().root(); self.root_ptr(); } unsafe fn unroot(&self) { assert!(self.is_rooted(), "Can't double-unroot a Gc"); // Try to get inner before modifying our state. Inner may be // inaccessible due to this method being invoked during the sweeping // phase, and we don't want to modify our state before panicking. self.inner().unroot(); self.unroot_ptr(); } fn run_finalizer(&self) { Finalize::finalize(self); } } impl Clone for Gc { fn clone(&self) -> Self { let ptr = self.inner_ptr(); // SAFETY: since a `Gc` is always valid, its `inner_ptr` must also be always a valid pointer. unsafe { ptr.as_ref().root(); } // SAFETY: though `ptr` doesn't come from a `into_raw` call, it essentially does the same, // but it skips the call to `std::mem::forget` since we have a reference instead of an owned // value. unsafe { Self::from_raw(ptr) } } } impl Deref for Gc { type Target = T; fn deref(&self) -> &T { self.inner().value() } } impl Drop for Gc { fn drop(&mut self) { // If this pointer was a root, we should unroot it. if self.is_rooted() { self.inner().unroot(); } } } impl Default for Gc { fn default() -> Self { Self::new(Default::default()) } } #[allow(clippy::inline_always)] impl PartialEq for Gc { #[inline(always)] fn eq(&self, other: &Self) -> bool { **self == **other } } impl Eq for Gc {} #[allow(clippy::inline_always)] impl PartialOrd for Gc { #[inline(always)] fn partial_cmp(&self, other: &Self) -> Option { (**self).partial_cmp(&**other) } #[inline(always)] fn lt(&self, other: &Self) -> bool { **self < **other } #[inline(always)] fn le(&self, other: &Self) -> bool { **self <= **other } #[inline(always)] fn gt(&self, other: &Self) -> bool { **self > **other } #[inline(always)] fn ge(&self, other: &Self) -> bool { **self >= **other } } impl Ord for Gc { fn cmp(&self, other: &Self) -> Ordering { (**self).cmp(&**other) } } impl Hash for Gc { fn hash(&self, state: &mut H) { (**self).hash(state); } } impl Display for Gc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(&**self, f) } } impl Debug for Gc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&**self, f) } } impl fmt::Pointer for Gc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Pointer::fmt(&self.inner(), f) } } impl std::borrow::Borrow for Gc { fn borrow(&self) -> &T { self } } impl AsRef for Gc { fn as_ref(&self) -> &T { self } }