TraceCell
May 16, 2015 ยท View on GitHub
/// Will allocate a value. By default this value is rooted
unsafe fn magic_gc_allocate
/// Mark the given pointer during a trace
/// Returns whether the pointer was already marked
unsafe fn magic_gc_mark
/// Add the pointer as a GC root
unsafe fn magic_gc_add_root
/// Remove the pointer as a GC root
unsafe fn magic_gc_remove_root
/// 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
impl
/// 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
impl<T: Trace> Gc
// 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
unsafe fn root(&self) {
self.inner().root();
}
unsafe fn unroot(&self) {
self.inner().unroot();
}
}
impl<T: Trace> Clone for Gc
impl<T: Trace> Deref for Gc
fn deref(&self) -> &T {
&self.inner().value()
}
}
//////////// // GcCell // ////////////
/// A mutable garbage collected pointer/cell hybrid
struct GcCell
impl <T: Trace> GcCell
// 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
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
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(); } }