diff --git a/libs/context/src/collector.rs b/libs/context/src/collector.rs index ad4e301..4dd163c 100644 --- a/libs/context/src/collector.rs +++ b/libs/context/src/collector.rs @@ -342,10 +342,10 @@ impl WeakCollectorRef { pub unsafe trait RawSimpleAlloc: RawCollectorImpl { fn alloc<'gc, T: GcSafe + 'gc>(context: &'gc CollectorContext, value: T) -> Gc<'gc, T, CollectorId>; } -unsafe impl<'gc, T, C> GcSimpleAlloc<'gc, T> for CollectorContext - where T: GcSafe + 'gc, C: RawSimpleAlloc { +unsafe impl GcSimpleAlloc for CollectorContext + 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) } } diff --git a/libs/context/src/handle.rs b/libs/context/src/handle.rs index 77001eb..39e499a 100644 --- a/libs/context/src/handle.rs +++ b/libs/context/src/handle.rs @@ -8,7 +8,7 @@ use core::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; use alloc::boxed::Box; use alloc::vec::Vec; -use zerogc::{Trace, GcSafe, GcErase, GcRebrand, GcVisitor, NullTrace, TraceImmutable, GcHandleSystem, GcBindHandle}; +use zerogc::{Trace, GcSafe, GcErase, GcRebrand, GcVisitor, NullTrace, TraceImmutable, GcHandleSystem}; use crate::{Gc, WeakCollectorRef, CollectorId, CollectorContext, CollectorRef, CollectionManager}; use crate::collector::RawCollectorImpl; @@ -417,30 +417,10 @@ unsafe impl ::zerogc::GcHandle for GcHandle; type Id = CollectorId; - fn use_critical(&self, func: impl FnOnce(&T) -> R) -> R { - self.collector.ensure_valid(|collector| unsafe { - /* - * This should be sufficient to ensure - * the value won't be collected or relocated. - * - * Note that this is implemented using a read lock, - * so recursive calls will deadlock. - * This is preferable to using `recursive_read`, - * since that could starve writers (collectors). - */ - C::Manager::prevent_collection(collector.as_ref(), || { - let value = self.inner.as_ref().value - .load(Ordering::Acquire) as *mut T; - func(&*value) - }) - }) - } -} -unsafe impl<'new_gc, T, C> GcBindHandle<'new_gc, T> for GcHandle - where T: GcSafe, T: GcRebrand<'new_gc, CollectorId>, - T::Branded: GcSafe, C: RawHandleImpl { #[inline] - fn bind_to(&self, context: &'new_gc CollectorContext) -> Gc<'new_gc, T::Branded, CollectorId> { + fn bind_to<'new_gc>(&self, context: &'new_gc CollectorContext) -> Gc<'new_gc, T::Branded, CollectorId> + where T: GcRebrand<'new_gc, Self::Id>, + >::Branded: GcSafe { /* * We can safely assume the object will * be as valid as long as the context. @@ -472,6 +452,24 @@ unsafe impl<'new_gc, T, C> GcBindHandle<'new_gc, T> for GcHandle } } + fn use_critical(&self, func: impl FnOnce(&T) -> R) -> R { + self.collector.ensure_valid(|collector| unsafe { + /* + * This should be sufficient to ensure + * the value won't be collected or relocated. + * + * Note that this is implemented using a read lock, + * so recursive calls will deadlock. + * This is preferable to using `recursive_read`, + * since that could starve writers (collectors). + */ + C::Manager::prevent_collection(collector.as_ref(), || { + let value = self.inner.as_ref().value + .load(Ordering::Acquire) as *mut T; + func(&*value) + }) + }) + } } unsafe impl Trace for GcHandle { /// See docs on reachability @@ -595,16 +593,23 @@ unsafe impl Send for GcHandle { /// Requires that the collector is thread-safe. unsafe impl Sync for GcHandle {} +#[doc(hidden)] +pub trait GcSafeErase<'a, Id: ::zerogc::CollectorId>: GcErase<'a, Id> + GcSafe + where >::Erased: GcSafe { +} /// We support handles -unsafe impl<'gc, 'a, T, C> GcHandleSystem<'gc, 'a, T> for CollectorRef - where C: RawHandleImpl, - T: GcSafe + 'gc, - T: GcErase<'a, CollectorId>, - T::Erased: GcSafe { - type Handle = GcHandle; +unsafe impl GcHandleSystem for CollectorRef + where C: RawHandleImpl, { + type Handle<'a, T> + where T: GcSafe + ?Sized + GcErase<'a, CollectorId>, + >>::Erased: GcSafe + = GcHandle<>::Erased, C>; #[inline] - fn create_handle(gc: Gc<'gc, T, CollectorId>) -> Self::Handle { + fn create_handle<'gc, 'a, T>(gc: Gc<'gc, T, CollectorId>) -> Self::Handle<'a, T> + where T: ?Sized + GcSafe + 'gc, + T: GcErase<'a, CollectorId>, + T::Erased: GcSafe { unsafe { let collector = gc.collector_id(); let value = gc.as_raw_ptr(); diff --git a/libs/context/src/lib.rs b/libs/context/src/lib.rs index 2819d15..46be53b 100644 --- a/libs/context/src/lib.rs +++ b/libs/context/src/lib.rs @@ -2,6 +2,7 @@ negative_impls, // !Send is much cleaner than `PhantomData` untagged_unions, // I want to avoid ManuallyDrop in unions const_fn_trait_bound, // So generics + const fn are unstable, huh? + generic_associated_types, // GcHandle )] #![cfg_attr(not(feature = "std"), no_std)] //! The implementation of (GcContext)[`::zerogc::GcContext`] that is diff --git a/src/lib.rs b/src/lib.rs index 5a3792f..0c04be0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature( const_panic, // RFC 2345 - Const asserts + generic_associated_types, // Needed for GcHandleSystem )] #![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] @@ -208,12 +209,11 @@ pub unsafe trait GcSystem { /// /// This type-system hackery is needed because /// we need to place bounds on `T as GcBrand` -// TODO: Remove when we get more powerful types -pub unsafe trait GcHandleSystem<'gc, 'a, T: GcSafe + ?Sized + 'gc>: GcSystem - where T: GcErase<'a, Self::Id>, - >::Erased: GcSafe { +pub unsafe trait GcHandleSystem: GcSystem { /// The type of handles to this object. - type Handle: GcHandle<>::Erased, System=Self>; + type Handle<'a, T>: GcHandle<>::Erased, System=Self> + where T: ?Sized + GcSafe + GcErase<'a, Self::Id>, + >::Erased: GcSafe; /// Create a handle to the specified GC pointer, /// which can be used without a context @@ -222,7 +222,9 @@ pub unsafe trait GcHandleSystem<'gc, 'a, T: GcSafe + ?Sized + 'gc>: GcSystem /// /// The system is implicit in the [Gc] #[doc(hidden)] - fn create_handle(gc: Gc<'gc, T, Self::Id>) -> Self::Handle; + fn create_handle<'gc, 'a, T>(gc: Gc<'gc, T, Self::Id>) -> Self::Handle<'a, T> + where T: ?Sized + GcSafe + GcErase<'a, Self::Id>, + >::Erased: GcSafe; } /// The context of garbage collection, @@ -321,7 +323,7 @@ pub unsafe trait GcContext: Sized { /// /// Some garbage collectors implement more complex interfaces, /// so implementing this is optional -pub unsafe trait GcSimpleAlloc<'gc, T: GcSafe + 'gc>: GcContext + 'gc { +pub unsafe trait GcSimpleAlloc: GcContext { /// Allocate the specified object in this garbage collector, /// binding it to the lifetime of this collector. /// @@ -334,7 +336,7 @@ pub unsafe trait GcSimpleAlloc<'gc, T: GcSafe + 'gc>: GcContext + 'gc { /// /// This gives a immutable reference to the resulting object. /// Once allocated, the object can only be correctly modified with a `GcCell` - fn alloc(&'gc self, value: T) -> Gc<'gc, T, Self::Id>; + fn alloc<'gc, T: GcSafe + 'gc>(&'gc self, value: T) -> Gc<'gc, T, Self::Id>; } /// The internal representation of a frozen context /// @@ -468,11 +470,11 @@ impl<'gc, T: GcSafe + ?Sized + 'gc, Id: CollectorId> Gc<'gc, T, Id> { /// Create a handle to this object, which can be used without a context #[inline] - pub fn create_handle<'a>(&self) -> >::Handle - where Id::System: GcHandleSystem<'gc, 'a, T>, + pub fn create_handle<'a>(&self) -> ::Handle<'a, T> + where Id::System: GcHandleSystem, T: GcErase<'a, Id> + 'a, >::Erased: GcSafe + 'a { - >::create_handle(*self) + ::create_handle(*self) } /// Get a reference to the system @@ -628,6 +630,21 @@ pub unsafe trait GcHandle: Clone + NullTrace { /// The type of [CollectorId] used with this sytem type Id: CollectorId; + /// Associate this handle with the specified context, + /// allowing its underlying object to be accessed + /// as long as the context is valid. + /// + /// The underlying object can be accessed just like any + /// other object that would be allocated from the context. + /// It'll be properly collected and can even be used as a root + /// at the next safepoint. + fn bind_to<'new_gc>(&self, context: &'new_gc ::Context) -> Gc< + 'new_gc, + >::Branded, + Self::Id + > where T: GcRebrand<'new_gc, Self::Id>, + >::Branded: GcSafe; + /// Access this handle inside the closure, /// possibly associating it with the specified /// @@ -646,30 +663,6 @@ pub unsafe trait GcHandle: Clone + NullTrace { */ fn use_critical(&self, func: impl FnOnce(&T) -> R) -> R; } -/// Trait for binding [GcHandle]s to contexts -/// using [GcBindHandle::bind_to] -/// -/// This is separate from the [GcHandle] trait -/// because Rust doesn't have Generic Associated Types -/// -/// TODO: Remove when we get more powerful types -pub unsafe trait GcBindHandle<'new_gc, T: GcSafe + ?Sized>: GcHandle - where T: GcRebrand<'new_gc, Self::Id>, - >::Branded: GcSafe { - /// Associate this handle with the specified context, - /// allowing its underlying object to be accessed - /// as long as the context is valid. - /// - /// The underlying object can be accessed just like any - /// other object that would be allocated from the context. - /// It'll be properly collected and can even be used as a root - /// at the next safepoint. - fn bind_to(&self, context: &'new_gc ::Context) -> Gc< - 'new_gc, - >::Branded, - Self::Id - >; -} /// Safely trigger a write barrier before /// writing to a garbage collected value. diff --git a/src/prelude.rs b/src/prelude.rs index ce8c861..c730c9d 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -21,8 +21,6 @@ pub use crate::{ pub use crate::{ GcSafe, GcErase, GcRebrand, Trace, TraceImmutable, NullTrace }; -// Hack traits -pub use crate::{GcBindHandle}; // TODO: Should this trait be auto-imported??? pub use crate::CollectorId; // Utils