-
-
Save mystor/fa1141bfb30643289597 to your computer and use it in GitHub Desktop.
TraceCell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Will allocate a value. By default this value is rooted | |
unsafe fn magic_gc_allocate<T>(value: T) -> NonZero<*mut T> { | |
unimplemented!() | |
} | |
/// Mark the given pointer during a trace | |
/// Returns whether the pointer was already marked | |
unsafe fn magic_gc_mark<T>(ptr: *const T) -> bool { | |
unimplemented!() | |
} | |
/// Add the pointer as a GC root | |
unsafe fn magic_gc_add_root<T>(ptr: *const T) { | |
unimplemented!() | |
} | |
/// Remove the pointer as a GC root | |
unsafe fn magic_gc_remove_root<T>(ptr: *const T) { | |
unimplemented!() | |
} | |
/// The Trace trait which needs to be implemented on garbage collected objects | |
trait Trace { | |
/// Mark all contained Gcs | |
fn trace(&self); | |
/// Increment the root-count of all contained Gcs | |
unsafe fn root(&self); | |
/// Decrement the root-count of all contained Gcs | |
unsafe fn unroot(&self); | |
} | |
/// This is the internal data structure which holds a garbage collected | |
/// value, as well as the number of root requests which it has on it. | |
/// This object should always be allocated with it's new function | |
struct GcBox<T> { | |
_roots: Cell<usize>, | |
_value: T, | |
} | |
impl<T> GcBox { | |
/// Allocate a GcBox on the heap | |
fn new(value: T) -> NonZero<*mut GcBox<T>> { | |
magic_gc_allocate(GcBox { | |
_roots: Cell::new(1), | |
_value: value, | |
}) | |
} | |
/// Get the value form the GcBox | |
fn value(&self) -> &T { | |
&self._value | |
} | |
/// Root the | |
fn root(&self) { | |
let roots = self._roots.get() + 1; | |
self._roots.set(roots); | |
if roots == 1 { | |
magic_gc_add_root(self as *const GcBox<T>); | |
} | |
} | |
fn unroot(&self) { | |
let roots = self._roots.get() - 1; | |
self._roots.set(roots); | |
if roots == 0 { | |
magic_gc_remove_root(self as *const GcBox<T>); | |
} | |
} | |
} | |
//////// | |
// Gc // | |
//////// | |
struct Gc<T> { | |
_ptr: NonZero<*const GcBox<T>>, | |
} | |
impl<T: Trace> Gc<T> { | |
pub fn new(value: T) -> Gc<T> { | |
unsafe { | |
// Allocate the box first | |
let ptr = GcBox::new(value); | |
// The thing which we are storing internally is no longer rooted! | |
(*ptr).value().unroot(); | |
let gc = Gc { _ptr: ptr } | |
} | |
} | |
fn inner(&self) -> &GcBox<T> { | |
&*self._ptr | |
} | |
} | |
impl<T: Trace> Trace for Gc<T> { | |
fn trace(&self) { | |
// Only recurse if we haven't already marked this node | |
if !magic_gc_mark(self._ptr) { | |
self.inner().value().trace(); | |
} | |
} | |
unsafe fn root(&self) { | |
self.inner().root(); | |
} | |
unsafe fn unroot(&self) { | |
self.inner().unroot(); | |
} | |
} | |
impl<T: Trace> Clone for Gc<T> { | |
fn clone(&self) -> Gc<T> { | |
unsafe { | |
self.root(); | |
Gc { _ptr: self._ptr } | |
} | |
} | |
} | |
impl<T: Trace> Deref for Gc<T> { | |
type Target = T; | |
fn deref(&self) -> &T { | |
&self.inner().value() | |
} | |
} | |
//////////// | |
// GcCell // | |
//////////// | |
/// A mutable garbage collected pointer/cell hybrid | |
struct GcCell<T> { | |
_ptr: NonZero<*const GcBox<RefCell<T>>>, | |
} | |
impl <T: Trace> GcCell<T> { | |
pub fn new(value: T) -> GcCell<T> { | |
unsafe { | |
// Allocate the box first | |
let ptr = GcBox::new(RefCell::new(value)); | |
// The thing which we are storing internally is no longer rooted! | |
(**ptr).value().borrow().unroot(); | |
GcCell { _ptr: ptr } | |
} | |
} | |
fn inner(&self) -> &GcCellBox<T> { | |
unsafe { &(**self._ptr) } | |
} | |
pub fn borrow(&self) -> GcCellRef<T> { | |
self.inner().value().borrow() | |
} | |
pub fn borrow_mut(&self) -> GcCellRefMut<T> { | |
let inner = self.inner(); | |
let val_ref = inner.value().borrow_mut(); | |
// Ensure that the box remains alive for the lifetime of the GcCellRefMut | |
self.root(); | |
// Root everything inside the box for the lifetime of the GcCellRefMut | |
val_ref.root(); | |
GcCellRefMut { | |
_box: inner, | |
_ref: val_ref, | |
} | |
} | |
} | |
impl<T: Trace> Trace for GcCell<T> { | |
fn trace(&self) { | |
// Only recurse if we haven't already marked this node | |
if !magic_gc_mark(self._ptr) { | |
let inner = self.inner(); | |
match inner.value().borrow_state() { | |
BorrowState::Writing => { | |
// In this case, the stuff inside is already rooted, | |
// so we don't have to do anything - we can just stop here | |
} | |
_ => { | |
self.inner().value().trace(); | |
} | |
} | |
} | |
} | |
unsafe fn root(&self) { | |
self.inner().root(); | |
} | |
unsafe fn unroot(&self) { | |
self.inner().unroot(); | |
} | |
} | |
/// In the non-mutable case, nothing interesting needs to happen. We are just taking | |
/// a reference into the RefCell. | |
type GcCellRef<'a, T> = cell::Ref<'a, T>; | |
/// The GcCellRefMut struct acts as a RAII guard (like RefCell's RefMut), which | |
/// will provides unique mutable access to the value inside the GcCell while also | |
/// ensuring that the data isn't collected incorrectly | |
/// This means that it roots the internal box (to ensure that the pointer remains alive), | |
/// (although this is probably unnecessary - investigate). | |
/// as well as the data inside the box. (as it may be modified, or moved out of the object) | |
/// When this guard is present, it is not possible for the trace implementation to see inside | |
/// the object, so the object inside must be rooted to prevent it from being collected. | |
struct GcCellRefMut<'a, T> { | |
_box: &'a GcBox<RefCell<T>>, | |
_ref: ::std::cell::RefMut<'a, T>, | |
} | |
impl<'a, T: Trace> Deref for GcCellRefMut<'a, T> { | |
type Target = T; | |
#[inline] | |
fn deref(&self) -> &T { &*self._ref } | |
} | |
impl<'a, T: Trace> DerefMut for GcCellRefMut<'a, T> { | |
#[inline] | |
fn deref_mut(&mut self) -> &mut T { &mut *self._ref } | |
} | |
impl<'a, T: Trace> Drop for GcCellRefMut<'a, T> { | |
fn drop(&mut self) { | |
// The box no longer has to be kept alive | |
self._box.unroot(); | |
// The data is now within a Gc tree again | |
// we don't have to keep it alive explicitly any longer | |
self._ref.unroot(); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment