Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions src/gc-arena/src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl ArenaParameters {
/// `Rootable<'a>` for *any* possible `'a`. This is necessary so that the `Root` types can be
/// branded by the unique, invariant lifetimes that makes an `Arena` sound.
pub trait Rootable<'a> {
type Root: Collect + 'a;
type Root: ?Sized + Collect + 'a;
}

/// A convenience macro for quickly creating type that implements of `Rootable`.
Expand Down Expand Up @@ -139,13 +139,19 @@ pub type Root<'a, R> = <R as Rootable<'a>>::Root;
/// this way, incremental garbage collection can be achieved (assuming "sufficiently small" calls
/// to `mutate`) that is both extremely safe and zero overhead vs what you would write in C with raw
/// pointers and manually ensuring that invariants are held.
pub struct Arena<R: for<'a> Rootable<'a> + ?Sized> {
pub struct Arena<R: for<'a> Rootable<'a> + ?Sized>
where
Root<'static, R>: Sized,
{
// We rely on the implicit drop order here, `root` *must* be dropped before `context`!
root: Root<'static, R>,
context: Context,
}

impl<R: for<'a> Rootable<'a> + ?Sized> Arena<R> {
impl<R: for<'a> Rootable<'a> + ?Sized> Arena<R>
where
Root<'static, R>: Sized,
{
/// Create a new arena with the given garbage collector tuning parameters. You must provide a
/// closure that accepts a `MutationContext` and returns the appropriate root.
pub fn new<F>(arena_parameters: ArenaParameters, f: F) -> Arena<R>
Expand Down Expand Up @@ -216,7 +222,10 @@ impl<R: for<'a> Rootable<'a> + ?Sized> Arena<R> {
pub fn map_root<R2: for<'a> Rootable<'a> + ?Sized>(
self,
f: impl for<'gc> FnOnce(MutationContext<'gc, '_>, Root<'gc, R>) -> Root<'gc, R2>,
) -> Arena<R2> {
) -> Arena<R2>
where
Root<'static, R2>: Sized,
{
self.context.root_barrier();
let new_root: Root<'static, R2> = unsafe { f(self.context.mutation_context(), self.root) };
Arena {
Expand All @@ -229,7 +238,10 @@ impl<R: for<'a> Rootable<'a> + ?Sized> Arena<R> {
pub fn try_map_root<R2: for<'a> Rootable<'a> + ?Sized, E>(
self,
f: impl for<'gc> FnOnce(MutationContext<'gc, '_>, Root<'gc, R>) -> Result<Root<'gc, R2>, E>,
) -> Result<Arena<R2>, E> {
) -> Result<Arena<R2>, E>
where
Root<'static, R2>: Sized,
{
self.context.root_barrier();
let new_root: Root<'static, R2> = unsafe { f(self.context.mutation_context(), self.root)? };
Ok(Arena {
Expand Down
8 changes: 8 additions & 0 deletions src/gc-arena/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ fn test_dynamic_roots() {
)
});

let root3 = arena.mutate(|mc, root_set| {
root_set.stash::<Rootable![[u8]]>(mc, unsize!(Gc::allocate(mc, [1, 2, 3]) => [u8]))
});

arena.collect_all();
arena.collect_all();

Expand All @@ -427,10 +431,14 @@ fn test_dynamic_roots() {
let root2 = root_set.fetch(&root2);
assert_eq!(*root2.0, 27);
assert_eq!(*root2.1, true);

let root3 = root_set.fetch::<Rootable![[u8]]>(&root3);
assert_eq!(*root3, [1, 2, 3]);
});

drop(root1);
drop(root2);
drop(root3);

arena.collect_all();
arena.collect_all();
Expand Down