Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempt to support garbage collected arrays/vectors #29

Merged
merged 10 commits into from
Jul 6, 2021
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ edition = "2018"
readme = "README.md"

[dependencies]
scopeguard = "1.1"
# Manually included tracing support for third party libraries
# Providing support for these important libraries,
# gives zerogc 'batteries included' support.
Expand All @@ -19,9 +20,6 @@ zerogc-derive = { path = "libs/derive", version = "0.2.0-alpha.3" }
[workspace]
members = ["libs/simple", "libs/derive", "libs/context"]

[profile.dev]
opt-level = 1

[features]
default = ["std"]
# Depend on the standard library (optional)
Expand Down
70 changes: 52 additions & 18 deletions libs/context/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use zerogc::{Gc, GcSafe, GcSystem, Trace, GcSimpleAlloc, NullTrace, TraceImmutab

use crate::{CollectorContext};
use crate::state::{CollectionManager, RawContext};
use zerogc::vec::GcVec;
use zerogc::vec::repr::GcVecRepr;

/// A specific implementation of a collector
pub unsafe trait RawCollectorImpl: 'static + Sized {
Expand All @@ -20,6 +22,8 @@ pub unsafe trait RawCollectorImpl: 'static + Sized {
/// The simple collector implements this as
/// a trait object pointer.
type DynTracePtr: Copy + Debug + 'static;
/// The configuration
type Config: Sized + Default;

/// A pointer to this collector
///
Expand All @@ -31,6 +35,8 @@ pub unsafe trait RawCollectorImpl: 'static + Sized {

/// The context
type RawContext: RawContext<Self>;
/// The raw representation of a vec
type RawVecRepr: GcVecRepr;

/// True if this collector is a singleton
///
Expand All @@ -50,18 +56,18 @@ pub unsafe trait RawCollectorImpl: 'static + Sized {
/// Initialize an instance of the collector
///
/// Must panic if the collector is not a singleton
fn init(logger: Logger) -> NonNull<Self>;
fn init(config: Self::Config, logger: Logger) -> NonNull<Self>;

/// The id of this collector
#[inline]
fn id(&self) -> CollectorId<Self> {
CollectorId { ptr: unsafe { Self::Ptr::from_raw(self as *const _ as *mut _) } }
}
unsafe fn gc_write_barrier<'gc, T, V>(
owner: &Gc<'gc, T, CollectorId<Self>>,
unsafe fn gc_write_barrier<'gc, O, V>(
owner: &Gc<'gc, O, CollectorId<Self>>,
value: &Gc<'gc, V, CollectorId<Self>>,
field_offset: usize
) where T: GcSafe + ?Sized + 'gc, V: GcSafe + ?Sized + 'gc;
) where O: GcSafe + ?Sized + 'gc, V: GcSafe + ?Sized + 'gc;
/// The logger associated with this collector
fn logger(&self) -> &Logger;

Expand Down Expand Up @@ -90,7 +96,7 @@ pub unsafe trait SingletonCollector: RawCollectorImpl<Ptr=PhantomData<&'static S
/// Initialize the global singleton
///
/// Panics if already initialized
fn init_global(logger: Logger);
fn init_global(config: Self::Config, logger: Logger);
}

impl<C: RawCollectorImpl> PartialEq for CollectorId<C> {
Expand Down Expand Up @@ -278,6 +284,7 @@ impl<C: RawCollectorImpl> CollectorId<C> {
}
unsafe impl<C: RawCollectorImpl> ::zerogc::CollectorId for CollectorId<C> {
type System = CollectorRef<C>;
type RawVecRepr = C::RawVecRepr;

#[inline]
fn from_gc_ptr<'a, 'gc, T>(gc: &'a Gc<'gc, T, Self>) -> &'a Self where T: GcSafe + ?Sized + 'gc, 'gc: 'a {
Expand All @@ -286,11 +293,11 @@ unsafe impl<C: RawCollectorImpl> ::zerogc::CollectorId for CollectorId<C> {


#[inline(always)]
unsafe fn gc_write_barrier<'gc, T, V>(
owner: &Gc<'gc, T, Self>,
unsafe fn gc_write_barrier<'gc, O, V>(
owner: &Gc<'gc, O, Self>,
value: &Gc<'gc, V, Self>,
field_offset: usize
) where T: GcSafe + ?Sized + 'gc, V: GcSafe + ?Sized + 'gc {
) where O: GcSafe + ?Sized + 'gc, V: GcSafe + ?Sized + 'gc {
C::gc_write_barrier(owner, value, field_offset)
}

Expand Down Expand Up @@ -341,13 +348,36 @@ impl<C: RawCollectorImpl> WeakCollectorRef<C> {

pub unsafe trait RawSimpleAlloc: RawCollectorImpl {
fn alloc<'gc, T: GcSafe + 'gc>(context: &'gc CollectorContext<Self>, value: T) -> Gc<'gc, T, CollectorId<Self>>;
unsafe fn alloc_uninit_slice<'gc, T>(context: &'gc CollectorContext<Self>, len: usize) -> (CollectorId<Self>, *mut T)
where T: GcSafe + 'gc;
fn alloc_vec<'gc, T>(context: &'gc CollectorContext<Self>) -> GcVec<'gc, T, CollectorContext<Self>>
where T: GcSafe + 'gc;
fn alloc_vec_with_capacity<'gc, T>(context: &'gc CollectorContext<Self>, capacity: usize) -> GcVec<'gc, T, CollectorContext<Self>>
where T: GcSafe + 'gc;
}
unsafe impl<'gc, T, C> GcSimpleAlloc<'gc, T> for CollectorContext<C>
where T: GcSafe + 'gc, C: RawSimpleAlloc {
unsafe impl<C> GcSimpleAlloc for CollectorContext<C>
where C: RawSimpleAlloc {
#[inline]
fn alloc(&'gc self, value: T) -> Gc<'gc, T, Self::Id> {
fn alloc<'gc, T>(&'gc self, value: T) -> Gc<'gc, T, Self::Id>
where T: GcSafe + 'gc {
C::alloc(self, value)
}

#[inline]
unsafe fn alloc_uninit_slice<'gc, T>(&'gc self, len: usize) -> (Self::Id, *mut T)
where T: GcSafe + 'gc {
C::alloc_uninit_slice(self, len)
}

#[inline]
fn alloc_vec<'gc, T>(&'gc self) -> GcVec<'gc, T, Self> where T: GcSafe + 'gc {
C::alloc_vec(self)
}

#[inline]
fn alloc_vec_with_capacity<'gc, T>(&'gc self, capacity: usize) -> GcVec<'gc, T, Self> where T: GcSafe + 'gc {
C::alloc_vec_with_capacity(self, capacity)
}
}

/// A reference to the collector.
Expand All @@ -371,26 +401,26 @@ unsafe impl<C: SyncCollector> Sync for CollectorRef<C> {}
#[doc(hidden)]
pub trait CollectorInit<C: RawCollectorImpl<Ptr=Self>>: CollectorPtr<C> {
fn create() -> CollectorRef<C> {
Self::with_logger(Logger::root(
Self::with_logger(C::Config::default(), Logger::root(
slog::Discard,
o!()
))
}
fn with_logger(logger: Logger) -> CollectorRef<C>;
fn with_logger(config: C::Config, logger: Logger) -> CollectorRef<C>;
}

impl<C: RawCollectorImpl<Ptr=NonNull<C>>> CollectorInit<C> for NonNull<C> {
fn with_logger(logger: Logger) -> CollectorRef<C> {
fn with_logger(config: C::Config, logger: Logger) -> CollectorRef<C> {
assert!(!C::SINGLETON);
let raw_ptr = C::init(logger);
let raw_ptr = C::init(config, logger);
CollectorRef { ptr: raw_ptr }
}
}
impl<C> CollectorInit<C> for PhantomData<&'static C>
where C: SingletonCollector {
fn with_logger(logger: Logger) -> CollectorRef<C> {
fn with_logger(config: C::Config, logger: Logger) -> CollectorRef<C> {
assert!(C::SINGLETON);
C::init_global(logger); // TODO: Is this safe?
C::init_global(config, logger); // TODO: Is this safe?
// NOTE: The raw pointer is implicit (now that we're leaked)
CollectorRef { ptr: PhantomData }
}
Expand All @@ -405,7 +435,11 @@ impl<C: RawCollectorImpl> CollectorRef<C> {

#[inline]
pub fn with_logger(logger: Logger) -> Self where C::Ptr: CollectorInit<C> {
<C::Ptr as CollectorInit<C>>::with_logger(logger)
Self::with_config(C::Config::default(), logger)
}

pub fn with_config(config: C::Config, logger: Logger) -> Self where C::Ptr: CollectorInit<C> {
<C::Ptr as CollectorInit<C>>::with_logger(config, logger)
}

#[inline]
Expand Down
4 changes: 2 additions & 2 deletions libs/context/src/state/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,13 @@ pub(crate) trait SyncCollectorImpl: RawCollectorImpl<Manager=CollectionManager<S
unreachable!("cant free while collection is in progress")
},
}
// Now drop the Box
drop(Box::from_raw(raw));
/*
* Notify all threads waiting for contexts to be valid.
* TODO: I think this is really only useful if we're waiting....
*/
self.manager().valid_contexts_wait.notify_all();
// Now drop the Box
drop(Box::from_raw(raw));
}
/// Wait until the specified collection is finished
///
Expand Down
11 changes: 8 additions & 3 deletions libs/context/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ use core::cell::Cell;
/// Get the offset of the specified field within a structure
#[macro_export]
macro_rules! field_offset {
($target:ty, $($field:ident).+) => {
unsafe { (core::ptr::addr_of!((*(std::ptr::null_mut::<$target>()))$(.$field)*) as usize) }
};
($target:ty, $($field:ident).+) => {{
const OFFSET: usize = {
let uninit = core::mem::MaybeUninit::<$target>::uninit();
unsafe { ((core::ptr::addr_of!((*uninit.as_ptr())$(.$field)*)) as *const u8)
.offset_from(uninit.as_ptr() as *const u8) as usize }
};
OFFSET
}};
}

#[cfg(feature = "sync")]
Expand Down
56 changes: 44 additions & 12 deletions libs/derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,12 @@ fn impl_erase(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
};
for param in &mut generics.params {
let rewritten_param: GenericArgument;
fn unsupported_lifetime_param(lt: &Lifetime) -> Error {
Error::new(
lt.span(),
"Unless Self: NullTrace, derive(GcErase) is currently unable to handle lifetimes"
)
}
match param {
GenericParam::Type(ref mut type_param) => {
let param_name = &type_param.ident;
Expand All @@ -740,7 +746,20 @@ fn impl_erase(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
rewritten_params.push(parse_quote!(#param_name));
continue
}
let original_bounds = type_param.bounds.iter().cloned().collect::<Vec<_>>();
fn rewrite_bound(bound: &TypeParamBound, info: &GcTypeInfo) -> Result<TypeParamBound, Error> {
match bound {
TypeParamBound::Trait(ref t) => Ok(TypeParamBound::Trait(t.clone())),
TypeParamBound::Lifetime(ref lt) if *lt == info.config.gc_lifetime() => {
Ok(parse_quote!('new_gc))
},
TypeParamBound::Lifetime(ref lt) => {
return Err(unsupported_lifetime_param(lt))
}
}
}
let original_bounds = type_param.bounds.iter()
.map(|bound| rewrite_bound(bound, info))
.collect::<Result<Vec<_>, _>>()?;
type_param.bounds.push(parse_quote!(#zerogc_crate::GcErase<'min, #collector_id>));
type_param.bounds.push(parse_quote!('min));
let rewritten_type: Type = parse_quote!(<#param_name as #zerogc_crate::GcErase<'min, #collector_id>>::Erased);
Expand All @@ -754,13 +773,10 @@ fn impl_erase(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
},
GenericParam::Lifetime(ref l) => {
if l.lifetime == info.config.gc_lifetime() {
rewritten_param = parse_quote!('static);
rewritten_param = parse_quote!('min);
assert!(!info.config.ignored_lifetimes.contains(&l.lifetime));
} else {
return Err(Error::new(
l.span(),
"Unless Self: NullTrace, derive(GcErase) is currently unable to handle lifetimes"
))
return Err(unsupported_lifetime_param(&l.lifetime))
}
},
GenericParam::Const(ref param) => {
Expand Down Expand Up @@ -881,14 +897,33 @@ fn impl_rebrand(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
};
for param in &mut generics.params {
let rewritten_param: GenericArgument;
fn unsupported_lifetime_param(lt: &Lifetime) -> Error {
Error::new(
lt.span(),
"Unless Self: NullTrace, derive(GcRebrand) is currently unable to handle lifetimes"
)
}
match param {
GenericParam::Type(ref mut type_param) => {
let param_name = &type_param.ident;
if info.is_ignored_param(&*type_param) {
rewritten_params.push(parse_quote!(#param_name));
continue
}
let original_bounds = type_param.bounds.iter().cloned().collect::<Vec<_>>();
let original_bounds = type_param.bounds.iter()
.map(|bound| rewrite_bound(bound, info))
.collect::<Result<Vec<_>, Error>>()?;
fn rewrite_bound(bound: &TypeParamBound, info: &GcTypeInfo) -> Result<TypeParamBound, Error> {
match bound {
TypeParamBound::Trait(ref t) => Ok(TypeParamBound::Trait(t.clone())),
TypeParamBound::Lifetime(ref lt) if *lt == info.config.gc_lifetime() => {
Ok(parse_quote!('new_gc))
},
TypeParamBound::Lifetime(ref lt) => {
return Err(unsupported_lifetime_param(lt))
}
}
}
type_param.bounds.push(parse_quote!(#zerogc_crate::GcRebrand<'new_gc, #collector_id>));
let rewritten_type: Type = parse_quote!(<#param_name as #zerogc_crate::GcRebrand<'new_gc, #collector_id>>::Branded);
rewritten_restrictions.push(WherePredicate::Type(PredicateType {
Expand All @@ -904,10 +939,7 @@ fn impl_rebrand(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
rewritten_param = parse_quote!('new_gc);
assert!(!info.config.ignored_lifetimes.contains(&l.lifetime));
} else {
return Err(Error::new(
l.span(),
"Unless Self: NullTrace, derive(GcRebrand) is currently unable to handle lifetimes"
))
return Err(unsupported_lifetime_param(&l.lifetime));
}
},
GenericParam::Const(ref param) => {
Expand Down Expand Up @@ -962,7 +994,7 @@ fn impl_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
let zerogc_crate = zerogc_crate();
let name = &target.ident;
let generics = add_trait_bounds_except(
&target.generics, parse_quote!(zerogc::Trace),
&target.generics, parse_quote!(#zerogc_crate::Trace),
&info.config.ignore_params, None
)?;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
Expand Down
Loading