mirror of https://github.com/boa-dev/boa.git
Browse Source
* Implement non-recursive GC tracing * Mark `Trace::trace_non_roots()` as unsafe * Apply reviewpull/3511/head
Haled Odat
12 months ago
committed by
GitHub
32 changed files with 612 additions and 336 deletions
@ -0,0 +1,104 @@
|
||||
use std::{cell::Cell, fmt}; |
||||
|
||||
const MARK_MASK: u32 = 1 << (u32::BITS - 1); |
||||
const NON_ROOTS_MASK: u32 = !MARK_MASK; |
||||
const NON_ROOTS_MAX: u32 = NON_ROOTS_MASK; |
||||
|
||||
/// The `Gcheader` contains the `GcBox`'s and `EphemeronBox`'s current state for the `Collector`'s
|
||||
/// Mark/Sweep as well as a pointer to the next node in the heap.
|
||||
///
|
||||
/// `ref_count` is the number of Gc instances, and `non_root_count` is the number of
|
||||
/// Gc instances in the heap. `non_root_count` also includes Mark Flag bit.
|
||||
///
|
||||
/// The next node is set by the `Allocator` during initialization and by the
|
||||
/// `Collector` during the sweep phase.
|
||||
pub(crate) struct GcHeader { |
||||
ref_count: Cell<u32>, |
||||
non_root_count: Cell<u32>, |
||||
} |
||||
|
||||
impl GcHeader { |
||||
/// Creates a new [`GcHeader`] with a root of 1 and next set to None.
|
||||
pub(crate) fn new() -> Self { |
||||
Self { |
||||
ref_count: Cell::new(1), |
||||
non_root_count: Cell::new(0), |
||||
} |
||||
} |
||||
|
||||
/// Returns the [`GcHeader`]'s current ref count.
|
||||
pub(crate) fn ref_count(&self) -> u32 { |
||||
self.ref_count.get() |
||||
} |
||||
|
||||
/// Returns the [`GcHeader`]'s current non-roots count
|
||||
pub(crate) fn non_root_count(&self) -> u32 { |
||||
self.non_root_count.get() & NON_ROOTS_MASK |
||||
} |
||||
|
||||
/// Increments [`GcHeader`]'s non-roots count.
|
||||
pub(crate) fn inc_non_root_count(&self) { |
||||
let non_root_count = self.non_root_count.get(); |
||||
|
||||
if (non_root_count & NON_ROOTS_MASK) < NON_ROOTS_MAX { |
||||
self.non_root_count.set(non_root_count.wrapping_add(1)); |
||||
} else { |
||||
// TODO: implement a better way to handle root overload.
|
||||
panic!("non-roots counter overflow"); |
||||
} |
||||
} |
||||
|
||||
/// Decreases [`GcHeader`]'s current non-roots count.
|
||||
pub(crate) fn reset_non_root_count(&self) { |
||||
self.non_root_count |
||||
.set(self.non_root_count.get() & !NON_ROOTS_MASK); |
||||
} |
||||
|
||||
/// Returns a bool for whether [`GcHeader`]'s mark bit is 1.
|
||||
pub(crate) fn is_marked(&self) -> bool { |
||||
self.non_root_count.get() & MARK_MASK != 0 |
||||
} |
||||
|
||||
#[inline] |
||||
pub(crate) fn inc_ref_count(&self) { |
||||
self.ref_count.set(self.ref_count.get() + 1); |
||||
} |
||||
|
||||
#[inline] |
||||
pub(crate) fn dec_ref_count(&self) { |
||||
self.ref_count.set(self.ref_count.get() - 1); |
||||
} |
||||
|
||||
/// Check if the gc object is rooted.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This only gives valid result if the we have run through the
|
||||
/// tracing non roots phase.
|
||||
#[inline] |
||||
pub(crate) fn is_rooted(&self) -> bool { |
||||
self.non_root_count() < self.ref_count() |
||||
} |
||||
|
||||
/// Sets [`GcHeader`]'s mark bit to 1.
|
||||
pub(crate) fn mark(&self) { |
||||
self.non_root_count |
||||
.set(self.non_root_count.get() | MARK_MASK); |
||||
} |
||||
|
||||
/// Sets [`GcHeader`]'s mark bit to 0.
|
||||
pub(crate) fn unmark(&self) { |
||||
self.non_root_count |
||||
.set(self.non_root_count.get() & !MARK_MASK); |
||||
} |
||||
} |
||||
|
||||
impl fmt::Debug for GcHeader { |
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||
f.debug_struct("GcHeader") |
||||
.field("marked", &self.is_marked()) |
||||
.field("ref_count", &self.ref_count.get()) |
||||
.field("non_root_count", &self.non_root_count()) |
||||
.finish_non_exhaustive() |
||||
} |
||||
} |
@ -1,8 +1,12 @@
|
||||
mod ephemeron_box; |
||||
mod gc_box; |
||||
mod gc_header; |
||||
mod vtable; |
||||
mod weak_map_box; |
||||
|
||||
pub(crate) use self::ephemeron_box::{EphemeronBox, ErasedEphemeronBox}; |
||||
pub(crate) use self::gc_header::GcHeader; |
||||
pub(crate) use self::weak_map_box::{ErasedWeakMapBox, WeakMapBox}; |
||||
pub(crate) use vtable::{vtable_of, DropFn, RunFinalizerFn, TraceFn, TraceNonRootsFn, VTable}; |
||||
|
||||
pub use self::gc_box::GcBox; |
||||
|
@ -0,0 +1,92 @@
|
||||
use crate::{GcBox, GcErasedPointer, Trace, Tracer}; |
||||
|
||||
// Workaround: https://users.rust-lang.org/t/custom-vtables-with-integers/78508
|
||||
pub(crate) const fn vtable_of<T: Trace + 'static>() -> &'static VTable { |
||||
trait HasVTable: Trace + Sized + 'static { |
||||
const VTABLE: &'static VTable; |
||||
|
||||
unsafe fn trace_fn(this: GcErasedPointer, tracer: &mut Tracer) { |
||||
// SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.
|
||||
let value = unsafe { this.cast::<GcBox<Self>>().as_ref().value() }; |
||||
|
||||
// SAFETY: The implementor must ensure that `trace` is correctly implemented.
|
||||
unsafe { |
||||
Trace::trace(value, tracer); |
||||
} |
||||
} |
||||
|
||||
unsafe fn trace_non_roots_fn(this: GcErasedPointer) { |
||||
// SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.
|
||||
let value = unsafe { this.cast::<GcBox<Self>>().as_ref().value() }; |
||||
|
||||
// SAFETY: The implementor must ensure that `trace_non_roots` is correctly implemented.
|
||||
unsafe { |
||||
Self::trace_non_roots(value); |
||||
} |
||||
} |
||||
|
||||
unsafe fn run_finalizer_fn(this: GcErasedPointer) { |
||||
// SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.
|
||||
let value = unsafe { this.cast::<GcBox<Self>>().as_ref().value() }; |
||||
|
||||
Self::run_finalizer(value); |
||||
} |
||||
|
||||
// SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.
|
||||
unsafe fn drop_fn(this: GcErasedPointer) { |
||||
// SAFETY: The caller must ensure that the passed erased pointer is `GcBox<Self>`.
|
||||
let this = this.cast::<GcBox<Self>>(); |
||||
|
||||
// SAFETY: The caller must ensure the erased pointer is not droped or deallocated.
|
||||
let _value = unsafe { Box::from_raw(this.as_ptr()) }; |
||||
} |
||||
} |
||||
|
||||
impl<T: Trace + 'static> HasVTable for T { |
||||
const VTABLE: &'static VTable = &VTable { |
||||
trace_fn: T::trace_fn, |
||||
trace_non_roots_fn: T::trace_non_roots_fn, |
||||
run_finalizer_fn: T::run_finalizer_fn, |
||||
drop_fn: T::drop_fn, |
||||
size: std::mem::size_of::<GcBox<T>>(), |
||||
}; |
||||
} |
||||
|
||||
T::VTABLE |
||||
} |
||||
|
||||
pub(crate) type TraceFn = unsafe fn(this: GcErasedPointer, tracer: &mut Tracer); |
||||
pub(crate) type TraceNonRootsFn = unsafe fn(this: GcErasedPointer); |
||||
pub(crate) type RunFinalizerFn = unsafe fn(this: GcErasedPointer); |
||||
pub(crate) type DropFn = unsafe fn(this: GcErasedPointer); |
||||
|
||||
#[derive(Debug)] |
||||
pub(crate) struct VTable { |
||||
trace_fn: TraceFn, |
||||
trace_non_roots_fn: TraceNonRootsFn, |
||||
run_finalizer_fn: RunFinalizerFn, |
||||
drop_fn: DropFn, |
||||
size: usize, |
||||
} |
||||
|
||||
impl VTable { |
||||
pub(crate) fn trace_fn(&self) -> TraceFn { |
||||
self.trace_fn |
||||
} |
||||
|
||||
pub(crate) fn trace_non_roots_fn(&self) -> TraceNonRootsFn { |
||||
self.trace_non_roots_fn |
||||
} |
||||
|
||||
pub(crate) fn run_finalizer_fn(&self) -> RunFinalizerFn { |
||||
self.run_finalizer_fn |
||||
} |
||||
|
||||
pub(crate) fn drop_fn(&self) -> DropFn { |
||||
self.drop_fn |
||||
} |
||||
|
||||
pub(crate) fn size(&self) -> usize { |
||||
self.size |
||||
} |
||||
} |
Loading…
Reference in new issue