Skip to content

Commit 85b9952

Browse files
committed
allocate RootNode from dedicated pool
1 parent 2fbadae commit 85b9952

5 files changed

Lines changed: 97 additions & 30 deletions

File tree

oscars/src/collectors/mark_sweep_branded/gc.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Core pointer types.
22
33
use crate::{
4-
alloc::mempool3::PoolItem,
4+
alloc::mempool3::{PoolAllocator, PoolItem},
55
collectors::mark_sweep_branded::{
66
gc_box::GcBox,
77
mutation_ctx::MutationContext,
@@ -13,7 +13,8 @@ use core::fmt;
1313
use core::marker::PhantomData;
1414
use core::ops::Deref;
1515
use core::ptr::NonNull;
16-
use rust_alloc::boxed::Box;
16+
17+
pub(crate) type RootDropFn = unsafe fn(&mut PoolAllocator<'static>, NonNull<u8>);
1718

1819
/// A transient pointer to a GC-managed value.
1920
#[derive(Debug)]
@@ -61,6 +62,10 @@ pub(crate) struct RootNode<'id, T: Trace> {
6162
pub(crate) link: RootLink,
6263
/// Pointer to the allocation
6364
pub(crate) gc_ptr: NonNull<PoolItem<GcBox<T>>>,
65+
/// Type-erased drop function for freeing this RootNode
66+
pub(crate) drop_fn: RootDropFn,
67+
/// Raw pointer to the Collector for freeing this node
68+
pub(crate) collector_ptr: *const crate::collectors::mark_sweep_branded::Collector,
6469
pub(crate) _marker: PhantomData<*mut &'id ()>,
6570
}
6671

@@ -83,14 +88,14 @@ impl<'id, T: Trace> Root<'id, T> {
8388

8489
impl<'id, T: Trace> Drop for Root<'id, T> {
8590
fn drop(&mut self) {
86-
// SAFETY:
87-
// * `self.raw` was created by `Box::into_raw`
88-
// * The address is stable.
8991
unsafe {
90-
let node = Box::from_raw(self.raw.as_ptr());
91-
if node.link.is_linked() {
92-
RootLink::unlink(NonNull::from(&node.link));
92+
let node_ref = self.raw.as_ref();
93+
if node_ref.link.is_linked() {
94+
RootLink::unlink(NonNull::from(&node_ref.link));
9395
}
96+
// SAFETY: collector_ptr is valid for the lifetime of the GcContext
97+
let collector = &*node_ref.collector_ptr;
98+
collector.free_root_node(self.raw.cast::<u8>(), node_ref.drop_fn);
9499
}
95100
}
96101
}

oscars/src/collectors/mark_sweep_branded/mod.rs

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub(crate) struct Collector {
4141
// and we ensure that `Gc` objects and pool allocations do not outlive
4242
// the `Collector` instance
4343
pub(crate) pool: RefCell<PoolAllocator<'static>>,
44+
/// Dedicated pool for RootNode allocations
45+
pub(crate) root_pool: RefCell<PoolAllocator<'static>>,
4446
pub(crate) sentinel: RootSentinel,
4547
pub(crate) generic_alloc_id: Cell<usize>,
4648
pub(crate) ephemerons: RefCell<Vec<EphemeronEntry>>,
@@ -50,6 +52,7 @@ impl Collector {
5052
fn new() -> Self {
5153
Self {
5254
pool: RefCell::new(PoolAllocator::default()),
55+
root_pool: RefCell::new(PoolAllocator::default()),
5356
sentinel: RootSentinel::new(),
5457
generic_alloc_id: Cell::new(0),
5558
ephemerons: RefCell::new(Vec::new()),
@@ -70,6 +73,54 @@ impl Collector {
7073
});
7174
}
7275

76+
/// Allocates a RootNode from the dedicated root pool.
77+
pub(crate) fn alloc_root_node<'id, T: trace::Trace>(
78+
&self,
79+
gc_ptr: NonNull<crate::alloc::mempool3::PoolItem<GcBox<T>>>,
80+
) -> NonNull<gc::RootNode<'id, T>> {
81+
use crate::alloc::mempool3::PoolItem;
82+
83+
unsafe fn drop_and_free<T: trace::Trace>(
84+
pool: &mut PoolAllocator<'static>,
85+
ptr: NonNull<u8>,
86+
) {
87+
unsafe {
88+
let typed_ptr = ptr.cast::<PoolItem<gc::RootNode<'_, T>>>();
89+
core::ptr::drop_in_place(typed_ptr.as_ptr());
90+
pool.free_slot(ptr);
91+
}
92+
}
93+
94+
let size = mem::size_of::<PoolItem<gc::RootNode<'id, T>>>();
95+
let mut pool = self.root_pool.borrow_mut();
96+
let slot_ptr = pool
97+
.alloc_slot_raw(size)
98+
.expect("root pool allocation failed");
99+
100+
unsafe {
101+
let typed_ptr = slot_ptr.cast::<PoolItem<gc::RootNode<'id, T>>>();
102+
core::ptr::write(
103+
typed_ptr.as_ptr(),
104+
PoolItem(gc::RootNode {
105+
link: root_link::RootLink::new(),
106+
gc_ptr,
107+
drop_fn: drop_and_free::<T>,
108+
collector_ptr: self as *const Collector,
109+
_marker: PhantomData,
110+
}),
111+
);
112+
NonNull::new_unchecked(&mut (*typed_ptr.as_ptr()).0)
113+
}
114+
}
115+
116+
/// Frees a RootNode back to the root pool.
117+
pub(crate) fn free_root_node(&self, ptr: NonNull<u8>, drop_fn: gc::RootDropFn) {
118+
let mut pool = self.root_pool.borrow_mut();
119+
unsafe {
120+
(drop_fn)(&mut pool, ptr);
121+
}
122+
}
123+
73124
/// Allocates a value from the pool.
74125
///
75126
/// # Panics
@@ -161,8 +212,6 @@ impl Collector {
161212

162213
/// Runs a collection cycle
163214
pub(crate) fn collect(&self) {
164-
let sentinel_ptr = self.sentinel.as_ptr();
165-
166215
let color = TraceColor::new();
167216

168217
let gc_ptr_offset = core::mem::offset_of!(
@@ -178,7 +227,7 @@ impl Collector {
178227
"gc_ptr offset must be stable across all T: Sized"
179228
);
180229

181-
for link_ptr in RootLink::iter_from_sentinel(sentinel_ptr) {
230+
for link_ptr in self.sentinel.iter() {
182231
unsafe {
183232
// Read the `gc_ptr` field using the stable offset
184233
let gc_ptr_ptr = link_ptr
@@ -260,6 +309,28 @@ impl Drop for Collector {
260309
/// Frees all remaining allocations
261310
fn drop(&mut self) {
262311
use crate::alloc::mempool3::PoolItem;
312+
313+
// Free all root nodes first
314+
let all_roots: Vec<(NonNull<u8>, gc::RootDropFn)> = self
315+
.root_pool
316+
.borrow()
317+
.iter_live_slots()
318+
.map(|ptr| unsafe {
319+
let drop_fn = (*ptr.cast::<PoolItem<gc::RootNode<'_, ()>>>().as_ptr())
320+
.0
321+
.drop_fn;
322+
(ptr, drop_fn)
323+
})
324+
.collect();
325+
let mut root_pool = self.root_pool.borrow_mut();
326+
for (ptr, drop_fn) in all_roots {
327+
unsafe {
328+
(drop_fn)(&mut root_pool, ptr);
329+
}
330+
}
331+
drop(root_pool);
332+
333+
// Then free all GC allocations
263334
let all: Vec<(NonNull<u8>, DropFn)> = self
264335
.pool
265336
.borrow()

oscars/src/collectors/mark_sweep_branded/mutation_ctx.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
use crate::collectors::mark_sweep_branded::{
44
Collector,
55
ephemeron::Ephemeron,
6-
gc::{Gc, Root, RootNode},
6+
gc::{Gc, Root},
77
root_link::RootLink,
88
trace::{Finalize, Trace},
99
weak::WeakGc,
1010
};
1111
use core::marker::PhantomData;
12-
use core::ptr::NonNull;
13-
use rust_alloc::boxed::Box;
1412

1513
/// Handle for GC allocations
1614
pub struct MutationContext<'id, 'gc> {
@@ -40,15 +38,7 @@ impl<'id, 'gc> MutationContext<'id, 'gc> {
4038

4139
/// Promotes a `Gc` pointer to a `Root`
4240
pub fn root<T: Trace + Finalize + 'gc>(&self, gc: Gc<'gc, T>) -> Root<'id, T> {
43-
let gc_ptr = gc.ptr;
44-
45-
let node = Box::new(RootNode {
46-
link: RootLink::new(),
47-
gc_ptr,
48-
_marker: PhantomData,
49-
});
50-
51-
let raw = unsafe { NonNull::new_unchecked(Box::into_raw(node)) };
41+
let raw = self.collector.alloc_root_node(gc.ptr);
5242

5343
// SAFETY: `raw` points to a stable `RootNode`.
5444
unsafe {

oscars/src/collectors/mark_sweep_branded/root_link.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,6 @@ impl RootLink {
6767
node_ref.next.set(None);
6868
}
6969
}
70-
71-
/// Returns an iterator over all nodes after `sentinel`.
72-
pub(crate) fn iter_from_sentinel(sentinel: NonNull<Self>) -> RootLinkIter {
73-
// SAFETY: sentinel is pinned in Collector and outlives the iteration.
74-
let first = unsafe { sentinel.as_ref().next.get() };
75-
RootLinkIter { current: first }
76-
}
7770
}
7871

7972
/// Iterator over root links in the intrusive list.
@@ -109,4 +102,10 @@ impl RootSentinel {
109102
NonNull::new_unchecked(self.0.as_ref().get_ref() as *const RootLink as *mut RootLink)
110103
}
111104
}
105+
106+
/// Returns an iterator over all root nodes after this sentinel.
107+
pub(crate) fn iter(&self) -> RootLinkIter {
108+
let first = self.0.as_ref().next.get();
109+
RootLinkIter { current: first }
110+
}
112111
}

oscars/src/collectors/mark_sweep_branded/tests/api_compliance.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ mod tests {
2626
let make_dummy = || RootNode::<i32> {
2727
link: RootLink::new(),
2828
gc_ptr: core::ptr::NonNull::dangling(),
29+
drop_fn: |_, _| {},
30+
collector_ptr: core::ptr::null(),
2931
_marker: core::marker::PhantomData,
3032
};
3133

0 commit comments

Comments
 (0)