Skip to content

Commit 7cabb2f

Browse files
committed
mark_sweep: run finalizers for erased/live values
1 parent 3720e75 commit 7cabb2f

6 files changed

Lines changed: 124 additions & 8 deletions

File tree

oscars/src/collectors/mark_sweep/internals/ephemeron.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ pub(crate) const fn vtable_of<K: Trace + 'static, V: Trace + 'static>() -> &'sta
142142
},
143143
finalize_fn: |this| unsafe {
144144
let ephemeron = this.cast::<PoolItem<Ephemeron<K, V>>>().as_ref().value();
145-
Finalize::finalize(ephemeron);
145+
Trace::run_finalizer(ephemeron);
146146
},
147147
_key_type_id: TypeId::of::<K>(),
148148
_key_size: size_of::<WeakGcBox<K>>(),

oscars/src/collectors/mark_sweep/internals/gc_box.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::collectors::mark_sweep::Finalize;
66
use crate::collectors::mark_sweep::internals::gc_header::{GcHeader, HeaderColor};
77
use crate::collectors::mark_sweep::{Trace, TraceColor};
88

9-
use super::{DropFn, TraceFn, VTable, vtable_of};
9+
use super::{DropFn, FinalizeFn, TraceFn, VTable, vtable_of};
1010

1111
pub struct NonTraceable(());
1212

@@ -166,6 +166,10 @@ impl<T: Trace + ?Sized> GcBox<T> {
166166
self.vtable.drop_fn()
167167
}
168168

169+
pub(crate) fn finalize_fn(&self) -> FinalizeFn {
170+
self.vtable.finalize_fn()
171+
}
172+
169173
pub(crate) fn size(&self) -> usize {
170174
self.vtable.size()
171175
}

oscars/src/collectors/mark_sweep/internals/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ mod gc_header;
44
mod vtable;
55

66
pub(crate) use ephemeron::Ephemeron;
7-
pub(crate) use vtable::{DropFn, TraceFn, VTable, vtable_of};
7+
pub(crate) use vtable::{DropFn, FinalizeFn, TraceFn, VTable, vtable_of};
88

99
pub use self::gc_box::{GcBox, NonTraceable, WeakGcBox};

oscars/src/collectors/mark_sweep/internals/vtable.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,20 @@ pub(crate) const fn vtable_of<T: Trace + 'static>() -> &'static VTable {
2727
// SAFETY: The caller must ensure the erased pointer is not dropped or deallocated.
2828
unsafe { core::ptr::drop_in_place(this.as_mut()) };
2929
}
30+
31+
// SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.
32+
unsafe fn finalize_fn(this: GcErasedPointer) {
33+
// SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.
34+
let value = unsafe { this.cast::<PoolItem<GcBox<Self>>>().as_ref().value() };
35+
Trace::run_finalizer(value);
36+
}
3037
}
3138

3239
impl<T: Trace + 'static> HasVTable for T {
3340
const VTABLE: &'static VTable = &VTable {
3441
trace_fn: T::trace_fn,
3542
drop_fn: T::drop_fn,
43+
finalize_fn: T::finalize_fn,
3644
type_id: TypeId::of::<T>(),
3745
size: size_of::<GcBox<T>>(),
3846
};
@@ -43,11 +51,13 @@ pub(crate) const fn vtable_of<T: Trace + 'static>() -> &'static VTable {
4351

4452
pub(crate) type TraceFn = unsafe fn(this: GcErasedPointer, color: TraceColor);
4553
pub(crate) type DropFn = unsafe fn(this: GcErasedPointer);
54+
pub(crate) type FinalizeFn = unsafe fn(this: GcErasedPointer);
4655

4756
#[derive(Debug)]
4857
pub(crate) struct VTable {
4958
trace_fn: TraceFn,
5059
drop_fn: DropFn,
60+
finalize_fn: FinalizeFn,
5161
type_id: TypeId,
5262
size: usize,
5363
}
@@ -61,6 +71,10 @@ impl VTable {
6171
self.drop_fn
6272
}
6373

74+
pub(crate) fn finalize_fn(&self) -> FinalizeFn {
75+
self.finalize_fn
76+
}
77+
6478
pub(crate) const fn type_id(&self) -> TypeId {
6579
self.type_id
6680
}

oscars/src/collectors/mark_sweep/mod.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,9 @@ impl MarkSweepGarbageCollector {
183183
let ephemerons = core::mem::take(&mut *self.ephemeron_queue.borrow_mut());
184184
for ephemeron in ephemerons {
185185
let ephemeron_ref = unsafe { ephemeron.as_ref() };
186-
unsafe { ephemeron_ref.value().drop_fn()(ephemeron) };
186+
let vtable = ephemeron_ref.value();
187+
unsafe { vtable.finalize_fn()(ephemeron) };
188+
unsafe { vtable.drop_fn()(ephemeron) };
187189
self.allocator
188190
.borrow_mut()
189191
.free_slot(ephemeron.cast::<u8>());
@@ -192,14 +194,18 @@ impl MarkSweepGarbageCollector {
192194
let roots = core::mem::take(&mut *self.root_queue.borrow_mut());
193195
for node in roots {
194196
let node_ref = unsafe { node.as_ref() };
195-
unsafe { node_ref.value().drop_fn()(node) };
197+
let gc_box = node_ref.value();
198+
unsafe { gc_box.finalize_fn()(node) };
199+
unsafe { gc_box.drop_fn()(node) };
196200
self.allocator.borrow_mut().free_slot(node.cast::<u8>());
197201
}
198202

199203
let pending_e = core::mem::take(&mut *self.pending_ephemeron_queue.borrow_mut());
200204
for ephemeron in pending_e {
201205
let ephemeron_ref = unsafe { ephemeron.as_ref() };
202-
unsafe { ephemeron_ref.value().drop_fn()(ephemeron) };
206+
let vtable = ephemeron_ref.value();
207+
unsafe { vtable.finalize_fn()(ephemeron) };
208+
unsafe { vtable.drop_fn()(ephemeron) };
203209
self.allocator
204210
.borrow_mut()
205211
.free_slot(ephemeron.cast::<u8>());
@@ -208,7 +214,9 @@ impl MarkSweepGarbageCollector {
208214
let pending_r = core::mem::take(&mut *self.pending_root_queue.borrow_mut());
209215
for node in pending_r {
210216
let node_ref = unsafe { node.as_ref() };
211-
unsafe { node_ref.value().drop_fn()(node) };
217+
let gc_box = node_ref.value();
218+
unsafe { gc_box.finalize_fn()(node) };
219+
unsafe { gc_box.drop_fn()(node) };
212220
self.allocator.borrow_mut().free_slot(node.cast::<u8>());
213221
}
214222
}
@@ -298,7 +306,7 @@ impl MarkSweepGarbageCollector {
298306
// Check if the value is not reachable, i.e. dead.
299307
if !gc_box.is_reachable(color) {
300308
// Finalize the dead item
301-
gc_box.finalize();
309+
unsafe { gc_box.finalize_fn()(*node) };
302310
// Recheck if the value is now rooted again after finalization.
303311
if gc_box.is_rooted() {
304312
unsafe { gc_box.trace_fn()(*node, color) };

oscars/src/collectors/mark_sweep/tests.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,3 +724,93 @@ fn collector_drop_runs_ephemeron_value_destructors_for_live_values() {
724724
"collector drop should run ephemeron value destructor"
725725
);
726726
}
727+
728+
#[test]
729+
fn collector_drop_runs_finalizers_for_live_gc_values() {
730+
use core::cell::Cell;
731+
use rust_alloc::rc::Rc;
732+
733+
struct FinalizeSpy {
734+
finalized: Rc<Cell<u32>>,
735+
}
736+
737+
impl Finalize for FinalizeSpy {
738+
fn finalize(&self) {
739+
self.finalized.set(self.finalized.get() + 1);
740+
}
741+
}
742+
743+
// SAFETY: `FinalizeSpy` has no traceable children.
744+
unsafe impl Trace for FinalizeSpy {
745+
crate::empty_trace!();
746+
}
747+
748+
let finalized = Rc::new(Cell::new(0));
749+
{
750+
let collector = &mut MarkSweepGarbageCollector::default()
751+
.with_page_size(128)
752+
.with_heap_threshold(256);
753+
754+
let _gc = Gc::new_in(
755+
FinalizeSpy {
756+
finalized: Rc::clone(&finalized),
757+
},
758+
collector,
759+
);
760+
761+
assert_eq!(finalized.get(), 0);
762+
}
763+
764+
assert_eq!(
765+
finalized.get(),
766+
1,
767+
"collector drop should run value finalizer"
768+
);
769+
}
770+
771+
#[test]
772+
fn collector_drop_runs_ephemeron_value_finalizers_for_live_values() {
773+
use core::cell::Cell;
774+
use rust_alloc::rc::Rc;
775+
776+
struct FinalizeSpy {
777+
finalized: Rc<Cell<u32>>,
778+
}
779+
780+
impl Finalize for FinalizeSpy {
781+
fn finalize(&self) {
782+
self.finalized.set(self.finalized.get() + 1);
783+
}
784+
}
785+
786+
// SAFETY: `FinalizeSpy` has no traceable children.
787+
unsafe impl Trace for FinalizeSpy {
788+
crate::empty_trace!();
789+
}
790+
791+
let finalized = Rc::new(Cell::new(0));
792+
{
793+
let collector = &mut MarkSweepGarbageCollector::default()
794+
.with_page_size(128)
795+
.with_heap_threshold(256);
796+
797+
let mut map = WeakMap::new(collector);
798+
let key = Gc::new_in(1u64, collector);
799+
800+
map.insert(
801+
&key,
802+
FinalizeSpy {
803+
finalized: Rc::clone(&finalized),
804+
},
805+
collector,
806+
);
807+
808+
assert_eq!(finalized.get(), 0);
809+
}
810+
811+
assert_eq!(
812+
finalized.get(),
813+
1,
814+
"collector drop should run ephemeron value finalizer"
815+
);
816+
}

0 commit comments

Comments
 (0)