use std::cell::Cell; use std::cmp::Ordering; use std::fmt::{self, Debug, Display}; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::ops::Deref; use std::ptr::{self, addr_of_mut, NonNull}; use std::rc::Rc; use crate::internals::GcBox; use crate::trace::{Finalize, Trace}; use crate::{finalizer_safe, Allocator}; // Technically, this function is safe, since we're just modifying the address of a pointer without // dereferencing it. pub(crate) fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { // SAFETY: this should be safe as ptr must be a valid nonnull unsafe { ptr::write(addr_of_mut!(ptr).cast::<*mut u8>(), data.cast::()); } ptr } /// 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::allocate(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() } let gc = Self { inner_ptr: Cell::new(inner_ptr), marker: PhantomData, }; gc.set_root(); gc } } 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()) } /// Will return a new rooted `Gc` from a `GcBox` pointer pub(crate) fn from_ptr(ptr: NonNull>) -> Self { // SAFETY: the value provided as a pointer MUST be a valid GcBox. unsafe { ptr.as_ref().root_inner(); let gc = Self { inner_ptr: Cell::new(ptr), marker: PhantomData, }; gc.set_root(); gc } } } /// Returns the given pointer with its root bit cleared. pub(crate) unsafe fn clear_root_bit( ptr: NonNull>, ) -> NonNull> { let ptr = ptr.as_ptr(); let data = ptr.cast::(); let addr = data as isize; let ptr = set_data_ptr(ptr, data.wrapping_offset((addr & !1) - addr)); // SAFETY: ptr must be a non null value unsafe { NonNull::new_unchecked(ptr) } } impl Gc { fn rooted(&self) -> bool { self.inner_ptr.get().as_ptr().cast::() as usize & 1 != 0 } pub(crate) fn set_root(&self) { let ptr = self.inner_ptr.get().as_ptr(); let data = ptr.cast::(); let addr = data as isize; let ptr = set_data_ptr(ptr, data.wrapping_offset((addr | 1) - addr)); // SAFETY: ptr must be a non null value. unsafe { self.inner_ptr.set(NonNull::new_unchecked(ptr)); } } fn clear_root(&self) { // SAFETY: inner_ptr must be a valid non-null pointer to a live GcBox. unsafe { self.inner_ptr.set(clear_root_bit(self.inner_ptr.get())); } } #[inline] pub(crate) fn inner_ptr(&self) -> NonNull> { assert!(finalizer_safe()); // SAFETY: inner_ptr must be a live GcBox. Calling this on a dropped GcBox // can result in Undefined Behavior. unsafe { clear_root_bit(self.inner_ptr.get()) } } #[inline] 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 { #[inline] unsafe fn trace(&self) { // SAFETY: Inner must be live and allocated GcBox. unsafe { self.inner().trace_inner(); } } #[inline] unsafe fn weak_trace(&self) { self.inner().weak_trace_inner(); } #[inline] unsafe fn root(&self) { assert!(!self.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_inner(); self.set_root(); } #[inline] unsafe fn unroot(&self) { assert!(self.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_inner(); self.clear_root(); } #[inline] fn run_finalizer(&self) { Finalize::finalize(self); } } impl Clone for Gc { #[inline] fn clone(&self) -> Self { Self::from_ptr(self.inner_ptr()) } } impl Deref for Gc { type Target = T; #[inline] fn deref(&self) -> &T { self.inner().value() } } impl Drop for Gc { #[inline] fn drop(&mut self) { // If this pointer was a root, we should unroot it. if self.rooted() { self.inner().unroot_inner(); } } } impl Default for Gc { #[inline] 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 { #[inline] 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 } }