diff --git a/oscars/src/collectors/mark_sweep/tests.rs b/oscars/src/collectors/mark_sweep/tests.rs index 454e5e6..dff3901 100644 --- a/oscars/src/collectors/mark_sweep/tests.rs +++ b/oscars/src/collectors/mark_sweep/tests.rs @@ -6,6 +6,35 @@ use super::WeakGc; use super::WeakMap; use super::cell::GcRefCell; +#[test] +fn unsafe_empty_trace_runs_finalize() { + use core::sync::atomic::{AtomicUsize, Ordering}; + + static FINALIZED: AtomicUsize = AtomicUsize::new(0); + + struct Probe; + + impl Finalize for Probe { + fn finalize(&self) { + FINALIZED.fetch_add(1, Ordering::SeqCst); + } + } + + // SAFETY: `Probe` has no GC references to trace. + unsafe impl Trace for Probe { + crate::unsafe_empty_trace!(); + } + + FINALIZED.store(0, Ordering::SeqCst); + let probe = Probe; + ::run_finalizer(&probe); + assert_eq!( + FINALIZED.load(Ordering::SeqCst), + 1, + "unsafe_empty_trace! must delegate to Finalize::finalize" + ); +} + #[test] fn basic_gc() { let collector = &mut MarkSweepGarbageCollector::default() diff --git a/oscars/src/collectors/mark_sweep/trace.rs b/oscars/src/collectors/mark_sweep/trace.rs index 580120d..9e39e0b 100644 --- a/oscars/src/collectors/mark_sweep/trace.rs +++ b/oscars/src/collectors/mark_sweep/trace.rs @@ -75,6 +75,23 @@ macro_rules! empty_trace { }; } +/// Utility macro to define an empty implementation of [`Trace`] inside an +/// `unsafe impl Trace` block. +/// +/// This mirrors `empty_trace!` semantics while making the unsafety explicit at +/// the call site. +#[macro_export] +macro_rules! unsafe_empty_trace { + () => { + #[inline] + unsafe fn trace(&self, _color: $crate::collectors::mark_sweep::TraceColor) {} + #[inline] + fn run_finalizer(&self) { + $crate::collectors::mark_sweep::Finalize::finalize(self); + } + }; +} + /// Utility macro to manually implement [`Trace`] on a type. /// /// You define a `this` parameter name and pass in a body, which should call `mark` on every diff --git a/oscars/src/collectors/mark_sweep_arena2/tests.rs b/oscars/src/collectors/mark_sweep_arena2/tests.rs index e556a56..bcf7fcc 100644 --- a/oscars/src/collectors/mark_sweep_arena2/tests.rs +++ b/oscars/src/collectors/mark_sweep_arena2/tests.rs @@ -6,6 +6,35 @@ use super::WeakGc; use super::WeakMap; use super::cell::GcRefCell; +#[test] +fn unsafe_empty_trace_runs_finalize() { + use core::sync::atomic::{AtomicUsize, Ordering}; + + static FINALIZED: AtomicUsize = AtomicUsize::new(0); + + struct Probe; + + impl Finalize for Probe { + fn finalize(&self) { + FINALIZED.fetch_add(1, Ordering::SeqCst); + } + } + + // SAFETY: `Probe` has no GC references to trace. + unsafe impl Trace for Probe { + crate::unsafe_empty_trace!(); + } + + FINALIZED.store(0, Ordering::SeqCst); + let probe = Probe; + ::run_finalizer(&probe); + assert_eq!( + FINALIZED.load(Ordering::SeqCst), + 1, + "unsafe_empty_trace! must delegate to Finalize::finalize" + ); +} + #[test] fn basic_gc() { let collector = &mut MarkSweepGarbageCollector::default() diff --git a/oscars/src/collectors/mark_sweep_arena2/trace.rs b/oscars/src/collectors/mark_sweep_arena2/trace.rs index 716d347..6740308 100644 --- a/oscars/src/collectors/mark_sweep_arena2/trace.rs +++ b/oscars/src/collectors/mark_sweep_arena2/trace.rs @@ -1,4 +1,4 @@ -// Both collectors use the exact same `Trace` types -// NOTE: `empty_trace!` and `custom_trace!` hardcode `mark_sweep` paths -// This works now but will silently break if the types ever diverge. +// Both collectors use the exact same `Trace` types. +// NOTE: `empty_trace!` and `custom_trace!` resolve through mark_sweep paths. +// This works today because mark_sweep2 depends on mark_sweep. pub use crate::collectors::mark_sweep::trace::{Finalize, Trace, TraceColor};