Skip to content

Commit

Permalink
CloneUnrooted
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Aug 3, 2019
1 parent a195f1d commit 08767e3
Show file tree
Hide file tree
Showing 5 changed files with 459 additions and 214 deletions.
41 changes: 22 additions & 19 deletions vm/src/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
primitive, Function, FunctionRef, Generic, Getable, OpaqueRef, OpaqueValue, OwnedFunction,
Pushable, Pushed, RuntimeResult, Unrooted, VmType, WithVM, IO,
},
gc::{GcPtr, Trace},
gc::{self, GcPtr, Trace},
stack::{ClosureState, ExternState, State},
thread::{ActiveThread, ThreadInternal},
types::VmInt,
Expand Down Expand Up @@ -183,17 +183,22 @@ fn spawn_<'vm>(value: WithVM<'vm, Function<&'vm Thread, fn(())>>) -> VmResult<Ro
let thread = value.vm.new_thread()?;
{
let mut context = thread.current_context();
let callable = match value.value.get_variant().0 {
ValueRepr::Closure(closure) => State::Closure(ClosureState {
closure,
instruction_index: 0,
}),
ValueRepr::Function(function) => State::Extern(ExternState::new(function)),
_ => State::Unknown,
let value_variant = value.value.get_variant();
let callable = match value_variant.get_repr() {
ValueRepr::Closure(closure) => {
construct_gc!(State::Closure(@ construct_gc!(ClosureState {
@ closure,
instruction_index: 0,
})))
}
ValueRepr::Function(function) => {
construct_gc!(State::Extern(@ ExternState::new(function)))
}
_ => gc::Borrow::from_static(State::Unknown),
};
value.value.push(&mut context)?;
value_variant.clone().push(&mut context)?;
context.push(ValueRepr::Int(0));
context.context().stack.enter_scope(1, callable);
context.context().stack.enter_scope(1, &*callable);
}
Ok(thread)
}
Expand Down Expand Up @@ -286,18 +291,16 @@ fn spawn_on<'vm>(

push_future_wrapper(&mut context, &future);

// SAFETY The value we call clone_unrooted on lives on the stack for the duration of the block
let callable = unsafe {
match context.stack()[..].last().unwrap().get_repr() {
ValueRepr::Function(ext) => Callable::Extern(ext.clone_unrooted()),
_ => unreachable!(),
}
};

SpawnFuture(future.shared()).push(&mut context).unwrap();

let mut context = context.context();
let callable = match context.stack[context.stack.len() - 2].get_repr() {
ValueRepr::Function(ext) => construct_gc!(Callable::Extern(@ ext)),
_ => unreachable!(),
};

let fields = slice::from_ref(context.stack.last().unwrap());
let def = PartialApplicationDataDef(callable, fields);
let def = construct_gc!(PartialApplicationDataDef(@callable, fields));
let value = Variants::from(context.gc.alloc(def).unwrap());

context.stack.pop_many(2);
Expand Down
197 changes: 175 additions & 22 deletions vm/src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,18 @@ impl<T: ?Sized> From<OwnedPtr<T>> for GcPtr<T> {
}
}

pub trait CloneUnrooted {
type Value;
unsafe fn clone_unrooted(&self) -> Self::Value;
}

impl<T: ?Sized + CloneUnrooted> CloneUnrooted for &'_ T {
type Value = T::Value;
unsafe fn clone_unrooted(&self) -> Self::Value {
(**self).clone_unrooted()
}
}

#[derive(Debug, Eq, PartialEq)]
pub struct Borrow<'a, T>(T, PhantomData<&'a T>);

Expand All @@ -404,9 +416,61 @@ impl<T> DerefMut for Borrow<'_, T> {
}
}

impl<T> CloneUnrooted for Borrow<'_, T>
where
T: CloneUnrooted,
{
type Value = T::Value;
unsafe fn clone_unrooted(&self) -> Self::Value {
self.0.clone_unrooted()
}
}

unsafe impl<'a, T> DataDef for Borrow<'a, T>
where
T: DataDef,
T::Value: Sized,
{
type Value = T::Value;
fn size(&self) -> usize {
(**self).size()
}
fn initialize(self, result: WriteOnly<Self::Value>) -> &mut Self::Value {
self.0.initialize(result)
}
}

impl<'gc, T> Borrow<'gc, T> {
pub fn map<U>(&self, f: impl FnOnce(&T) -> U) -> Borrow<'gc, U> {
Borrow(f(&self.0), PhantomData)
#[inline]
pub fn new(value: &'gc T) -> Borrow<'gc, T::Value>
where
T: CloneUnrooted,
{
// SAFETY The returned value is tied to the lifetime of the `value` root meaning the
// GcRef is also rooted
unsafe { Borrow::with_root(value.clone_unrooted(), value) }
}

#[inline]
pub fn from_static(value: T) -> Self
where
T: 'static, // TODO Necessary?
{
Borrow(value, PhantomData)
}

#[inline]
pub(crate) unsafe fn with_root<U: ?Sized>(value: T, _root: &'gc U) -> Self {
Borrow(value, PhantomData)
}

pub fn map<U>(&self, f: impl FnOnce(&T) -> &U) -> Borrow<'gc, U::Value>
where
U: CloneUnrooted,
{
let v = f(&self.0);
// SAFETY We can create a new root since the `'gc` lifetime is preserved
unsafe { Borrow(v.clone_unrooted(), PhantomData) }
}

pub unsafe fn map_unrooted<U>(self, f: impl FnOnce(T) -> U) -> Borrow<'gc, U> {
Expand All @@ -416,6 +480,10 @@ impl<'gc, T> Borrow<'gc, T> {
pub unsafe fn unrooted(self) -> T {
self.0
}

pub fn as_lifetime(&self) -> &'gc () {
&()
}
}

impl<'gc, T: ?Sized> From<OwnedGcRef<'gc, T>> for GcRef<'gc, T> {
Expand All @@ -434,32 +502,13 @@ impl<'a, T: ?Sized> Clone for GcRef<'a, T> {
}

impl<'a, T: ?Sized> GcRef<'a, T> {
#[inline]
pub fn new(value: &GcPtr<T>) -> GcRef<T> {
// SAFETY The returned value is tied to the lifetime of the `value` root meaning the
// GcRef is also rooted
unsafe { GcRef::with_root(value, value) }
}

#[inline]
pub(crate) unsafe fn with_root<U: ?Sized>(value: &GcPtr<T>, _root: &'a U) -> GcRef<'a, T> {
Borrow(value.clone_unrooted(), PhantomData)
}

pub fn as_ref(&self) -> &'a T {
// SAFETY 'a is the lifetime that the value actually lives. Since `T` is behind a pointer
// we can make that pointer have that lifetime
unsafe { forget_lifetime(&*self.0) }
}
}

impl<'a, T: ?Sized> OwnedGcRef<'a, T> {
#[inline]
pub(crate) unsafe fn with_root<U: ?Sized>(value: OwnedPtr<T>, _root: &'a U) -> Self {
Borrow(value, PhantomData)
}
}

/// A pointer to a garbage collected value.
///
/// It is only safe to access data through a `GcPtr` if the value is rooted (stored in a place
Expand Down Expand Up @@ -528,6 +577,13 @@ impl<T: ?Sized + fmt::Display> fmt::Display for GcPtr<T> {
}
}

impl<T: ?Sized> CloneUnrooted for GcPtr<T> {
type Value = Self;
unsafe fn clone_unrooted(&self) -> Self {
GcPtr(self.0)
}
}

impl<T: ?Sized> GcPtr<T> {
/// Unsafe as it is up to the caller to ensure that this pointer is not referenced somewhere
/// else
Expand Down Expand Up @@ -564,10 +620,14 @@ impl<T: ?Sized> GcPtr<T> {
ptr::eq::<T>(&**self, &**other)
}

pub unsafe fn clone_unrooted(&self) -> Self {
pub unsafe fn unrooted(&self) -> Self {
GcPtr(self.0)
}

pub fn as_lifetime(&self) -> &() {
&()
}

fn header(&self) -> &GcHeader {
unsafe {
let p = self.0.as_ptr() as *mut u8;
Expand Down Expand Up @@ -651,6 +711,99 @@ macro_rules! impl_trace {
}
}

#[macro_export]
macro_rules! construct_enum_gc {
(impl $typ: ident $(:: $variant: ident)? [$($acc: tt)*] [$($ptr: ident)*] @ $expr: expr, $($rest: tt)*) => { {
let ptr = $expr;
$crate::construct_enum_gc!(impl $typ $(:: $variant)?
[$($acc)* unsafe { $crate::gc::CloneUnrooted::clone_unrooted(&ptr) },]
[$($ptr)* ptr]
$($rest)*
)
} };

(impl $typ: ident $(:: $variant: ident)? [$($acc: tt)*] [$($ptr: ident)*] $expr: expr, $($rest: tt)*) => {
$crate::construct_enum_gc!(impl $typ $(:: $variant)?
[$($acc)* $expr,]
[$($ptr)*]
$($rest)*
)
};

(impl $typ: ident $(:: $variant: ident)? [$($acc: tt)*] [$($ptr: ident)*] @ $expr: expr) => { {
let ptr = $expr;
$crate::construct_enum_gc!(impl $typ $(:: $variant)?
[$($acc)* unsafe { $crate::gc::CloneUnrooted::clone_unrooted(&ptr) },]
[$($ptr)* ptr]
)
} };

(impl $typ: ident $(:: $variant: ident)? [$($acc: tt)*] [$($ptr: ident)*] $expr: expr) => {
$crate::construct_enum_gc!(impl $typ $(:: $variant)?
[$($acc)* $expr,]
[$($ptr)*]
)
};

(impl $typ: ident $(:: $variant: ident)? [$($acc: tt)*] [$($ptr: ident)*] ) => { {
let root = [$( $ptr.as_lifetime() )*].first().map_or(&(), |v| *v);
#[allow(unused_unsafe)]
let v = $typ $(:: $variant)? ( $($acc)* );
// Make sure that we constructed something and didn't call a function which could leak the
// pointers
match &v {
$typ $(:: $variant)? (..) if true => (),
_ => unreachable!(),
}
#[allow(unused_unsafe)]
unsafe { $crate::gc::Borrow::with_root(v, root) }
} };
}

#[macro_export]
macro_rules! construct_gc {
(impl $typ: ident [$($acc: tt)*] [$($ptr: ident)*] @ $field: ident : $expr: expr, $($rest: tt)*) => { {
let $field = $expr;
$crate::construct_gc!(impl $typ
[$($acc)* $field: unsafe { $crate::gc::CloneUnrooted::clone_unrooted(&$field) },]
[$($ptr)* $field]
$($rest)*
)
} };

(impl $typ: ident [$($acc: tt)*] [$($ptr: ident)*] @ $field: ident, $($rest: tt)*) => {
$crate::construct_gc!(impl $typ
[$($acc)* $field: unsafe { $crate::gc::CloneUnrooted::clone_unrooted(&$field) },]
[$($ptr)* $field]
$($rest)*
)
};

(impl $typ: ident [$($acc: tt)*] [$($ptr: ident)*] $field: ident $(: $expr: expr)?, $($rest: tt)*) => {
$crate::construct_gc!(impl $typ
[$($acc)* $field $(: $expr)?,]
[$($ptr)*]
$($rest)*
)
};

(impl $typ: ident [$($acc: tt)*] [$($ptr: ident)*] ) => { {
let root = [$( $ptr.as_lifetime() )*].first().map_or(&(), |v| *v);
#[allow(unused_unsafe)]
let v = $typ { $($acc)* };
#[allow(unused_unsafe)]
unsafe { $crate::gc::Borrow::with_root(v, root) }
} };

($typ: ident { $( $tt: tt )* } ) => {
$crate::construct_gc!{impl $typ [] [] $( $tt )* }
};

($typ: ident $(:: $variant: ident)? ( $( $tt: tt )* ) ) => {
$crate::construct_enum_gc!{impl $typ $(:: $variant)? [] [] $( $tt )* }
};
}

macro_rules! deref_trace {
([$($params: tt)*] $ty: ty) => {
unsafe impl<$($params)*> Trace for $ty {
Expand Down
Loading

0 comments on commit 08767e3

Please sign in to comment.