use super::trace::Trace;
use crate::barrier::WriteBarrier;
use crate::header::GcHeader;
use crate::mutator::Mutator;
use crate::pointee::{GcPointee, Thin};
use alloc::alloc::Layout;
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr::{null_mut, NonNull};
use core::sync::atomic::{AtomicPtr, Ordering};
pub struct Gc<'gc, T: Trace + ?Sized> {
ptr: AtomicPtr<Thin<T>>,
scope: PhantomData<&'gc *mut T>,
}
impl<'gc, T: Trace> Gc<'gc, T> {
pub fn new(m: &'gc Mutator<'gc>, obj: T) -> Self {
m.alloc(obj)
}
}
impl<'gc, T: Trace + ?Sized> Gc<'gc, T> {
pub(crate) fn get_layout(&self) -> Layout {
<T as GcPointee>::get_header(self.as_thin()).get_alloc_layout()
}
}
impl<'gc, T: Trace + ?Sized + 'gc> Deref for Gc<'gc, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
let thin_ptr = self.ptr.load(Ordering::Relaxed);
<T as GcPointee>::deref(NonNull::new(thin_ptr).unwrap())
}
}
impl<'gc, T: Trace + ?Sized> Clone for Gc<'gc, T> {
fn clone(&self) -> Self {
let ptr = self.ptr.load(Ordering::Relaxed);
Self {
ptr: AtomicPtr::new(ptr),
scope: PhantomData::<&'gc *mut T>,
}
}
}
impl<'gc, T: Trace + ?Sized> Gc<'gc, T> {
pub(crate) unsafe fn set(&self, value: Gc<'gc, T>) {
let thin_ptr = value.ptr.load(Ordering::Relaxed);
self.ptr.store(thin_ptr, Ordering::Relaxed);
}
pub(crate) unsafe fn from_ptr(ptr: *const T) -> Self {
Self {
ptr: AtomicPtr::new(ptr as *mut T as *mut Thin<T>),
scope: PhantomData::<&'gc *mut T>,
}
}
pub(crate) fn get_header(&self) -> &<T as GcPointee>::GcHeader {
<T as GcPointee>::get_header(self.as_thin())
}
pub(crate) fn get_header_ptr(&self) -> *const <T as GcPointee>::GcHeader {
<T as GcPointee>::get_header_ptr(self.as_thin())
}
pub(crate) fn as_thin(&self) -> NonNull<Thin<T>> {
unsafe { NonNull::new_unchecked(self.ptr.load(Ordering::Relaxed)) }
}
pub fn scoped_deref(&self) -> &'gc T {
let thin_ptr = self.ptr.load(Ordering::Relaxed);
<T as GcPointee>::deref(NonNull::new(thin_ptr).unwrap())
}
pub fn write_barrier<F>(&self, mu: &'gc Mutator, f: F)
where
F: FnOnce(&WriteBarrier<T>),
{
let barrier = unsafe { WriteBarrier::new(self.scoped_deref()) };
f(&barrier);
if mu.has_marked(&self) {
mu.retrace(self.scoped_deref());
}
}
}
pub struct GcOpt<'gc, T: Trace + ?Sized> {
ptr: AtomicPtr<Thin<T>>,
scope: PhantomData<&'gc *mut T>,
}
impl<'gc, T: Trace + ?Sized> From<Gc<'gc, T>> for GcOpt<'gc, T> {
fn from(gc: Gc<'gc, T>) -> Self {
Self {
ptr: AtomicPtr::new(gc.ptr.load(Ordering::Relaxed)),
scope: PhantomData::<&'gc *mut T>,
}
}
}
impl<'gc, T: Trace + ?Sized> Clone for GcOpt<'gc, T> {
fn clone(&self) -> Self {
Self {
ptr: AtomicPtr::new(self.ptr.load(Ordering::Relaxed)),
scope: PhantomData::<&'gc *mut T>,
}
}
}
impl<'gc, T: Trace + ?Sized> GcOpt<'gc, T> {
pub fn new_none() -> Self {
Self {
ptr: AtomicPtr::new(null_mut()),
scope: PhantomData::<&'gc *mut T>,
}
}
pub fn is_none(&self) -> bool {
self.ptr.load(Ordering::Relaxed).is_null()
}
pub fn is_some(&self) -> bool {
!self.is_none()
}
pub fn set_none(&self) {
self.ptr.store(null_mut(), Ordering::Relaxed)
}
pub fn as_option(&self) -> Option<Gc<'gc, T>> {
if self.is_some() {
Some(Gc {
ptr: AtomicPtr::new(self.ptr.load(Ordering::Relaxed)),
scope: PhantomData::<&'gc *mut T>,
})
} else {
None
}
}
pub fn unwrap(&self) -> Gc<'gc, T> {
self.as_option().unwrap()
}
pub(crate) unsafe fn set(&self, new: GcOpt<'gc, T>) {
let thin_ptr = new.ptr.load(Ordering::Relaxed);
self.ptr.store(thin_ptr, Ordering::SeqCst);
}
}
impl<'gc, T: Trace> GcOpt<'gc, T> {
pub fn new(m: &'gc Mutator<'gc>, obj: T) -> Self {
m.alloc(obj).into()
}
}
impl<'gc, T: Trace + ?Sized> GcOpt<'gc, T> {
pub unsafe fn from_ptr(ptr: *mut T) -> Self {
Self {
ptr: AtomicPtr::new(ptr as *mut Thin<T>),
scope: PhantomData::<&'gc *mut T>,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::header::GcMark;
use crate::{Arena, Root};
#[test]
fn set_header_marker() {
let _: Arena<Root![_]> = Arena::new(|mu| {
let gc = Gc::new(mu, 69);
let header = gc.get_header();
assert!(*gc == 69);
assert_eq!(header.get_mark(), GcMark::Blue);
header.set_mark(GcMark::Green);
assert_eq!(header.get_mark(), GcMark::Green);
assert!(*gc == 69);
});
}
#[test]
fn gc_from_gcopt() {
let _: Arena<Root![_]> = Arena::new(|mu| {
let gc = Gc::new(mu, 69);
let gc_opt = GcOpt::from(gc);
let gc = Gc::from(gc_opt.unwrap());
let header = gc.get_header();
assert!(*gc == 69);
assert_eq!(header.get_mark(), GcMark::Blue);
});
}
#[test]
fn test_gc_sizes() {
let _: Arena<Root![_]> = Arena::new(|_| {
assert!(size_of::<Gc<()>>() == size_of::<*const ()>());
assert!(size_of::<GcOpt<()>>() == size_of::<*const ()>());
});
}
}