Compare commits
2 commits
cd1d83714b
...
8c95fcbd85
Author | SHA1 | Date | |
---|---|---|---|
|
8c95fcbd85 | ||
|
2f690250ae |
5 changed files with 53 additions and 27 deletions
|
@ -2,6 +2,7 @@ use std::ptr::NonNull;
|
||||||
|
|
||||||
use super::gc_ref::GcRef;
|
use super::gc_ref::GcRef;
|
||||||
use super::trace;
|
use super::trace;
|
||||||
|
use crate::trace::GCTrace;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GCAllocator {
|
pub struct GCAllocator {
|
||||||
|
@ -10,6 +11,7 @@ pub struct GCAllocator {
|
||||||
|
|
||||||
impl GCAllocator {
|
impl GCAllocator {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
/// Allocate a value on the heap
|
||||||
pub fn alloc<T: trace::GCTrace>(&mut self, x: T) -> GcRef<T> {
|
pub fn alloc<T: trace::GCTrace>(&mut self, x: T) -> GcRef<T> {
|
||||||
let alloc = Allocation::new(x);
|
let alloc = Allocation::new(x);
|
||||||
let ptr = alloc.ptr as *mut T;
|
let ptr = alloc.ptr as *mut T;
|
||||||
|
@ -28,8 +30,14 @@ impl GCAllocator {
|
||||||
root.trace(&mut tracer);
|
root.trace(&mut tracer);
|
||||||
|
|
||||||
// And sweep
|
// And sweep
|
||||||
self.allocations
|
let inaccessible = self
|
||||||
|
.allocations
|
||||||
.drain_filter(|a| !tracer.is_accessible(a.ptr));
|
.drain_filter(|a| !tracer.is_accessible(a.ptr));
|
||||||
|
|
||||||
|
// And sweep
|
||||||
|
for mut to_free in inaccessible {
|
||||||
|
to_free.drop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specialization when ;-;
|
// Specialization when ;-;
|
||||||
|
@ -44,9 +52,14 @@ impl GCAllocator {
|
||||||
tracer.mark_reachable_rec(root);
|
tracer.mark_reachable_rec(root);
|
||||||
root.trace(&mut tracer);
|
root.trace(&mut tracer);
|
||||||
|
|
||||||
// And sweep
|
let inaccessible = self
|
||||||
self.allocations
|
.allocations
|
||||||
.drain_filter(|a| !tracer.is_accessible(a.ptr));
|
.drain_filter(|a| !tracer.is_accessible(a.ptr));
|
||||||
|
|
||||||
|
// And sweep
|
||||||
|
for mut to_free in inaccessible {
|
||||||
|
to_free.drop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,14 +74,12 @@ impl Allocation {
|
||||||
let ptr = Box::into_raw(alloc) as *mut ();
|
let ptr = Box::into_raw(alloc) as *mut ();
|
||||||
|
|
||||||
let drop = |ptr| unsafe {
|
let drop = |ptr| unsafe {
|
||||||
Box::from_raw(ptr as *mut T);
|
std::mem::drop(Box::from_raw(ptr as *mut T));
|
||||||
};
|
};
|
||||||
|
|
||||||
Self { ptr, drop }
|
Self { ptr, drop }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Allocation {
|
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { (self.drop)(self.ptr) };
|
unsafe { (self.drop)(self.ptr) };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{marker::PhantomData, ops::Deref, ptr::NonNull};
|
use std::{marker::PhantomData, ptr::NonNull};
|
||||||
|
|
||||||
use crate::trace::GCTrace;
|
use crate::trace::GCTrace;
|
||||||
|
|
||||||
|
@ -10,23 +10,34 @@ impl<T: GCTrace> Clone for GcRef<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: GCTrace> Deref for GcRef<T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe { self.0.as_ref() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: GCTrace> GcRef<T> {
|
impl<T: GCTrace> GcRef<T> {
|
||||||
pub(crate) unsafe fn new(ptr: NonNull<T>) -> Self {
|
pub(crate) unsafe fn new(ptr: NonNull<T>) -> Self {
|
||||||
Self(ptr, PhantomData)
|
Self(ptr, PhantomData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Ensure that this is the only instance of a pointer to the underlying value.
|
/// The caller needs to ensure that the underlying pointer hasn't been garbage collected.
|
||||||
/// You might want to instead use one of various [cell][`std::cell`] types as the allocated
|
/// Since the drop order for garbage collected structs is undefined that means it is never
|
||||||
/// type
|
/// safe to call this function in a [Drop::drop].
|
||||||
|
///
|
||||||
|
/// Do note that this doesn't mean that any particular instance of GcRef has to be marked
|
||||||
|
/// as reachable during a call to [GCAllocator::gc][`crate::allocator::GCAllocator::gc`]
|
||||||
|
/// but instead any GcRef instance referring to the same underlying pointer has to be reachable
|
||||||
|
pub unsafe fn get(&self) -> &T {
|
||||||
|
unsafe { self.0.as_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// The caller needs to ensure that the underlying pointer hasn't been garbage collected.
|
||||||
|
/// See [GcRef::get] for more details
|
||||||
|
///
|
||||||
|
/// The caller needs to ensure that this is the only instance of a pointer to the underlying value
|
||||||
|
/// (in other words that [Clone] hasn't been called, or that all other clones of the pointer have
|
||||||
|
/// already been dropped).
|
||||||
|
///
|
||||||
|
/// This function is hard(but not impossible) to use without causing UB. Unless you have a
|
||||||
|
/// really special use case you might want to instead use one of various [cell][`std::cell`]
|
||||||
|
/// types as the allocated type.
|
||||||
pub unsafe fn get_mut(this: &mut Self) -> &mut T {
|
pub unsafe fn get_mut(this: &mut Self) -> &mut T {
|
||||||
this.0.as_mut()
|
this.0.as_mut()
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,12 @@ pub mod test_utils;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
|
||||||
use super::allocator::GCAllocator;
|
use super::allocator::GCAllocator;
|
||||||
use super::test_utils::GotDropped;
|
use super::test_utils::GotDropped;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_works() {
|
fn gc_allocates_and_frees_structs() {
|
||||||
let got_dropped = GotDropped::default();
|
let got_dropped = GotDropped::default();
|
||||||
|
|
||||||
let mut gc = GCAllocator::default();
|
let mut gc = GCAllocator::default();
|
||||||
|
|
|
@ -7,6 +7,7 @@ unsafe impl GCTrace for i64 {}
|
||||||
unsafe impl GCTrace for u64 {}
|
unsafe impl GCTrace for u64 {}
|
||||||
unsafe impl GCTrace for f32 {}
|
unsafe impl GCTrace for f32 {}
|
||||||
unsafe impl GCTrace for f64 {}
|
unsafe impl GCTrace for f64 {}
|
||||||
|
unsafe impl GCTrace for bool {}
|
||||||
unsafe impl GCTrace for isize {}
|
unsafe impl GCTrace for isize {}
|
||||||
unsafe impl GCTrace for usize {}
|
unsafe impl GCTrace for usize {}
|
||||||
unsafe impl GCTrace for String {}
|
unsafe impl GCTrace for String {}
|
||||||
|
|
|
@ -20,10 +20,10 @@ impl Environment {
|
||||||
pub fn update_var(&mut self, name: &str, v: Primitive) -> Option<Primitive> {
|
pub fn update_var(&mut self, name: &str, v: Primitive) -> Option<Primitive> {
|
||||||
if let Some(cur) = self.variables.get_mut(name) {
|
if let Some(cur) = self.variables.get_mut(name) {
|
||||||
Some(std::mem::replace(cur, v))
|
Some(std::mem::replace(cur, v))
|
||||||
|
} else if let Some(ref parent) = self.parent {
|
||||||
|
unsafe { parent.get().borrow_mut().update_var(name, v) }
|
||||||
} else {
|
} else {
|
||||||
self.parent
|
None
|
||||||
.as_ref()
|
|
||||||
.and_then(|parent| parent.borrow_mut().update_var(name, v))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,9 +34,11 @@ impl Environment {
|
||||||
|
|
||||||
pub fn get_var(&self, name: &str) -> Option<Primitive> {
|
pub fn get_var(&self, name: &str) -> Option<Primitive> {
|
||||||
self.variables.get(name).cloned().or_else(|| {
|
self.variables.get(name).cloned().or_else(|| {
|
||||||
self.parent
|
if let Some(ref parent) = self.parent {
|
||||||
.as_ref()
|
unsafe { parent.get().borrow().get_var(name) }
|
||||||
.and_then(|v| (**v).borrow().get_var(name))
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,10 +50,10 @@ pub struct World {
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn set_var(&mut self, name: String, v: Primitive) -> Option<Primitive> {
|
pub fn set_var(&mut self, name: String, v: Primitive) -> Option<Primitive> {
|
||||||
self.env.borrow_mut().set_var(name, v)
|
unsafe { self.env.get().borrow_mut().set_var(name, v) }
|
||||||
}
|
}
|
||||||
pub fn get_var(&self, name: &str) -> Option<Primitive> {
|
pub fn get_var(&self, name: &str) -> Option<Primitive> {
|
||||||
self.env.borrow().get_var(name)
|
unsafe { self.env.get().borrow().get_var(name) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue