Skip to content

Commit

Permalink
feat: Automatically unroot values stored in Gc allocated values
Browse files Browse the repository at this point in the history
`RootedThread` and `RootedValue` are reference counted pointers so
storing them in a value allocated on the GC heap will create a cycle
which will never be dropped.

To avoid this we detect when such a value is allocated on the GC heap
and remove the value from the root list.

Fixes #746
  • Loading branch information
Marwes committed Jun 25, 2019
1 parent 844418d commit 6ebc398
Show file tree
Hide file tree
Showing 33 changed files with 674 additions and 405 deletions.
96 changes: 55 additions & 41 deletions Cargo.lock

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions codegen/src/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use syn::{
};

fn get_gluon_meta_items(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta>> {
if attr.path.segments.len() == 1 && attr.path.segments[0].ident == "gluon" {
if attr.path.segments.len() == 1
&& (attr.path.segments[0].ident == "gluon" || attr.path.segments[0].ident == "gluon_trace")
{
match attr.interpret_meta() {
Some(List(ref meta)) => Some(meta.nested.iter().cloned().collect()),
_ => None,
Expand All @@ -28,6 +30,7 @@ pub struct Container {
pub crate_name: CrateName,
pub vm_type: Option<String>,
pub newtype: bool,
pub skip: bool,
}

impl Container {
Expand All @@ -37,6 +40,7 @@ impl Container {
let mut crate_name = CrateName::None;
let mut vm_type = None;
let mut newtype = false;
let mut skip = false;

for meta_items in item.attrs.iter().filter_map(get_gluon_meta_items) {
for meta_item in meta_items {
Expand All @@ -61,7 +65,11 @@ impl Container {
vm_type = Some(get_lit_str(&m.ident, &m.ident, &m.lit).unwrap().value())
}

_ => panic!("unexpected gluon container attribute"),
Meta(Word(ref w)) if w == "skip" => {
skip = true;
}

_ => panic!("unexpected gluon container attribute: {:?}", meta_item),
}
}
}
Expand All @@ -70,6 +78,7 @@ impl Container {
crate_name,
vm_type,
newtype,
skip,
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@
//!
//! use std::sync::Arc;
//!
//! // Userdata requires Debug + Send + Sync
//! #[derive(Userdata, Debug)]
//! // Userdata requires Traverseable + Debug + Send + Sync
//! #[derive(Userdata, Traverseable, Debug)]
//! struct Ident {
//! group: Arc<str>,
//! name: Arc<str>,
Expand Down Expand Up @@ -192,7 +192,7 @@ pub fn vm_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
}

#[doc(hidden)]
#[proc_macro_derive(Traverseable, attributes(gluon))]
#[proc_macro_derive(Traverseable, attributes(gluon, gluon_trace))]
pub fn traverseable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
traverseable::derive(input.into()).into()
}
Expand Down
31 changes: 24 additions & 7 deletions codegen/src/traverseable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ pub fn derive(input: TokenStream) -> TokenStream {
..
} = derive_input;

let tokens = match data {
Data::Struct(ast) => derive_struct(&container, ast, ident, generics),
Data::Enum(ast) => derive_enum(&container, ast, ident, generics),
Data::Union(_) => panic!("Unions are not supported"),
let tokens = if container.skip {
gen_impl(&container, ident, generics, quote!())
} else {
match data {
Data::Struct(ast) => derive_struct(&container, ast, ident, generics),
Data::Enum(ast) => derive_enum(&container, ast, ident, generics),
Data::Union(_) => panic!("Unions are not supported"),
}
};

tokens.into()
Expand Down Expand Up @@ -97,7 +101,8 @@ fn derive_enum(
};
}

gen_impl(container, ident, generics, cons)
let tokens = gen_impl(container, ident, generics, cons);
tokens
}

fn gen_impl(
Expand Down Expand Up @@ -138,6 +143,18 @@ fn gen_impl(
impl #impl_generics _gluon_gc::Traverseable for #ident #ty_generics
#where_clause #(#traverseable_bounds,)*
{
fn root(&self, gc: &mut _gluon_gc::Gc) {
fn mark<T: ?Sized + _gluon_gc::Traverseable>(this: &T, gc: &mut _gluon_gc::Gc) {
_gluon_gc::Traverseable::root(this, gc)
}
#push_impl
}
fn unroot(&self, gc: &mut _gluon_gc::Gc) {
fn mark<T: ?Sized + _gluon_gc::Traverseable>(this: &T, gc: &mut _gluon_gc::Gc) {
_gluon_gc::Traverseable::unroot(this, gc)
}
#push_impl
}
fn traverse(&self, gc: &mut _gluon_gc:: Gc) {
fn mark<T: ?Sized + _gluon_gc::Traverseable>(this: &T, gc: &mut _gluon_gc::Gc) {
_gluon_gc::Traverseable::traverse(this, gc)
Expand All @@ -162,15 +179,15 @@ fn gen_variant_match(ident: &Ident, variant: &Variant) -> TokenStream {

quote! {
#pattern => {
#(mark(#field_idents2);)*
#(mark(#field_idents2, gc);)*
}
}
}

fn create_traverseable_bounds(generics: &Generics) -> Vec<TokenStream> {
map_type_params(generics, |ty| {
quote! {
#ty: _gluon_api::Traverseable
#ty: _gluon_gc::Traverseable
}
})
}
Expand Down
4 changes: 0 additions & 4 deletions codegen/src/userdata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStr
#where_clause #(#trait_bounds,)* #(#lifetime_bounds),*
{
}

#[automatically_derived]
#[allow(unused_attributes, unused_variables)]
impl #impl_generics _gluon_gc::Traverseable for #ident #ty_generics {}
};
}
}
2 changes: 1 addition & 1 deletion codegen/tests/derive_userdata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use gluon::{import, Compiler, Thread};
use init::new_vm;
use std::sync::Arc;

#[derive(Userdata, Debug, VmType)]
#[derive(Userdata, Traverseable, Debug, VmType)]
#[gluon(vm_type = "WindowHandle")]
struct WindowHandle {
id: Arc<u64>,
Expand Down
2 changes: 1 addition & 1 deletion examples/marshalling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ fn marshal_wrapper() -> Result<()> {
Ok(())
}

#[derive(Userdata, Debug, Clone, VmType)]
#[derive(Userdata, Traverseable, Debug, Clone, VmType)]
#[gluon(vm_type = "WindowHandle")]
struct WindowHandle {
id: Arc<u64>,
Expand Down
6 changes: 4 additions & 2 deletions repl/src/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,16 +190,18 @@ macro_rules! impl_userdata {
};
}

#[derive(Userdata, VmType)]
#[derive(Userdata, Traverseable, VmType)]
#[gluon(vm_type = "Editor")]
#[gluon_trace(skip)]
struct Editor {
editor: Mutex<rustyline::Editor<Completer>>,
}

impl_userdata! { Editor }

#[derive(Userdata, VmType)]
#[derive(Userdata, Traverseable, VmType)]
#[gluon(vm_type = "CpuPool")]
#[gluon_trace(skip)]
struct CpuPool(self::futures_cpupool::CpuPool);

impl_userdata! { CpuPool }
Expand Down
3 changes: 1 addition & 2 deletions src/compiler_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use std::borrow::{Borrow, BorrowMut};
use std::mem;
use std::ops::Deref;
use std::result::Result as StdResult;
use std::sync::Arc;

Expand Down Expand Up @@ -655,7 +654,7 @@ where
/// Result of successful execution
pub struct ExecuteValue<T, E>
where
T: Deref<Target = Thread>,
T: for<'a> VmRoot<'a>,
{
pub id: Symbol,
pub expr: E,
Expand Down
18 changes: 12 additions & 6 deletions src/std_lib/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,14 @@ impl<'vm, 'value> Getable<'vm, 'value> for Headers {

// By implementing `Userdata` on `Body` it can be automatically pushed and retrieved from gluon
// threads
#[derive(Userdata, VmType)]
#[derive(Userdata, Traverseable, VmType)]
#[gluon(vm_type = "std.http.types.Body")]
#[gluon(crate_name = "::vm")]
#[gluon_trace(skip)]
// Representation of a http body that is in the prograss of being read
pub struct Body(Arc<Mutex<Box<dyn Stream<Item = PushAsRef<Chunk, [u8]>, Error = vm::Error> + Send>>>);
pub struct Body(
Arc<Mutex<Box<dyn Stream<Item = PushAsRef<Chunk, [u8]>, Error = vm::Error> + Send>>>,
);

// Types implementing `Userdata` requires a `std::fmt::Debug` implementation so it can be displayed
impl fmt::Debug for Body {
Expand All @@ -128,9 +131,10 @@ fn read_chunk(
}

// A http body that is being written
#[derive(Userdata, VmType)]
#[derive(Userdata, Traverseable, VmType)]
#[gluon(vm_type = "std.http.types.ResponseBody")]
#[gluon(crate_name = "::vm")]
#[gluon_trace(skip)]
pub struct ResponseBody(Arc<Mutex<Option<hyper::body::Sender>>>);

impl fmt::Debug for ResponseBody {
Expand Down Expand Up @@ -178,9 +182,10 @@ fn write_response(
})
}

#[derive(Debug, Userdata, VmType)]
#[derive(Debug, Userdata, Traverseable, VmType)]
#[gluon(vm_type = "std.http.types.Uri")]
#[gluon(crate_name = "::vm")]
#[gluon_trace(skip)]
struct Uri(http::Uri);

// Next we define some record types which are marshalled to and from gluon. These have equivalent
Expand Down Expand Up @@ -240,8 +245,9 @@ fn listen(
type ReqBody = hyper::Body;
type ResBody = hyper::Body;
type Error = vm::Error;
type Future =
Box<dyn Future<Item = http::Response<hyper::Body>, Error = Self::Error> + Send + 'static>;
type Future = Box<
dyn Future<Item = http::Response<hyper::Body>, Error = Self::Error> + Send + 'static,
>;

fn call(&mut self, request: http::Request<hyper::Body>) -> Self::Future {
let (parts, body) = request.into_parts();
Expand Down
3 changes: 2 additions & 1 deletion src/std_lib/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ fn eprintln(s: &str) -> IO<()> {
IO::Value(())
}

#[derive(Userdata, VmType)]
#[derive(Userdata, Traverseable, VmType)]
#[gluon(vm_type = "std.io.File")]
#[gluon(crate_name = "::vm")]
#[gluon_trace(skip)]
struct GluonFile(Mutex<Option<File>>);

macro_rules! unwrap_file {
Expand Down
3 changes: 2 additions & 1 deletion src/std_lib/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ use crate::vm::{
ExternModule,
};

#[derive(Clone, Debug, Userdata, VmType)]
#[derive(Clone, Debug, Userdata, Traverseable, VmType)]
#[gluon(vm_type = "std.random.XorShiftRng")]
#[gluon(crate_name = "::vm")]
#[gluon_trace(skip)]
struct XorShiftRng(self::rand_xorshift::XorShiftRng);

field_decl! { value, gen }
Expand Down
6 changes: 4 additions & 2 deletions src/std_lib/regex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ use crate::real_std::error::Error as StdError;

use crate::vm::{self, api::Collect, thread::Thread, ExternModule};

#[derive(Debug, Userdata, VmType)]
#[derive(Debug, Userdata, Traverseable, VmType)]
#[gluon(vm_type = "std.regex.Regex")]
#[gluon(crate_name = "vm")]
#[gluon_trace(skip)]
struct Regex(regex::Regex);

#[derive(Debug, Userdata, VmType)]
#[derive(Debug, Userdata, Traverseable, VmType)]
#[gluon(vm_type = "std.regex.Error")]
#[gluon(crate_name = "vm")]
#[gluon_trace(skip)]
struct Error(regex::Error);

fn new(re: &str) -> Result<Regex, Error> {
Expand Down
Loading

0 comments on commit 6ebc398

Please sign in to comment.