Browse Source

Reduce `WeakGc<T>` memory usage (#3492)

* Reduce `WeakGc<T>` memory usage

Makes inner storage of `WeakGc<T>` store `()` in place of `Ephemeron` value,
insteadthe of a clone of the `Gc<T>` which is already stored in the key.

Reducing the size by the width of one pointer.

* Apply review
pull/3498/head
Haled Odat 12 months ago committed by GitHub
parent
commit
13ba869d2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 18
      boa_gc/src/internals/ephemeron_box.rs
  2. 17
      boa_gc/src/pointers/ephemeron.rs
  3. 2
      boa_gc/src/pointers/gc.rs
  4. 15
      boa_gc/src/pointers/weak.rs

18
boa_gc/src/internals/ephemeron_box.rs

@ -139,21 +139,33 @@ impl<K: Trace, V: Trace> EphemeronBox<K, V> {
data.as_ref().map(|data| &data.value) data.as_ref().map(|data| &data.value)
} }
/// Returns a reference to the ephemeron's key or None. /// Returns the pointer to the ephemeron's key or None.
/// ///
/// # Safety /// # Safety
/// ///
/// The caller must ensure there are no live mutable references to the ephemeron box's data /// The caller must ensure there are no live mutable references to the ephemeron box's data
/// before calling this method. /// before calling this method.
pub(crate) unsafe fn key(&self) -> Option<&GcBox<K>> { pub(crate) unsafe fn key_ptr(&self) -> Option<NonNull<GcBox<K>>> {
// SAFETY: the garbage collector ensures the ephemeron doesn't mutate until // SAFETY: the garbage collector ensures the ephemeron doesn't mutate until
// finalization. // finalization.
unsafe { unsafe {
let data = &*self.data.get(); let data = &*self.data.get();
data.as_ref().map(|data| data.key.as_ref()) data.as_ref().map(|data| data.key)
} }
} }
/// Returns a reference to the ephemeron's key or None.
///
/// # Safety
///
/// The caller must ensure there are no live mutable references to the ephemeron box's data
/// before calling this method.
pub(crate) unsafe fn key(&self) -> Option<&GcBox<K>> {
// SAFETY: the garbage collector ensures the ephemeron doesn't mutate until
// finalization.
unsafe { self.key_ptr().map(|data| data.as_ref()) }
}
/// Marks this `EphemeronBox` as live. /// Marks this `EphemeronBox` as live.
/// ///
/// This doesn't mark the inner value of the ephemeron. [`ErasedEphemeronBox::trace`] /// This doesn't mark the inner value of the ephemeron. [`ErasedEphemeronBox::trace`]

17
boa_gc/src/pointers/ephemeron.rs

@ -33,6 +33,23 @@ impl<K: Trace, V: Trace + Clone> Ephemeron<K, V> {
unsafe { self.inner_ptr.as_ref().value().cloned() } unsafe { self.inner_ptr.as_ref().value().cloned() }
} }
/// Gets the stored key of this `Ephemeron`, or `None` if the key was already garbage collected.
#[inline]
#[must_use]
pub fn key(&self) -> Option<Gc<K>> {
// SAFETY: this is safe because `Ephemeron` is tracked to always point to a valid pointer
// `inner_ptr`.
let key_ptr = unsafe { self.inner_ptr.as_ref().key_ptr() }?;
// SAFETY: Returned pointer is valid, so this is safe.
unsafe {
key_ptr.as_ref().inc_ref_count();
}
// SAFETY: The gc pointer's reference count has been incremented, so this is safe.
Some(unsafe { Gc::from_raw(key_ptr) })
}
/// Checks if the [`Ephemeron`] has a value. /// Checks if the [`Ephemeron`] has a value.
#[must_use] #[must_use]
pub fn has_value(&self) -> bool { pub fn has_value(&self) -> bool {

2
boa_gc/src/pointers/gc.rs

@ -59,7 +59,7 @@ impl<T: Trace> Gc<T> {
// `unsafe` block. // `unsafe` block.
// - `set_kv`: `weak` is a newly created `EphemeronBox`, meaning it isn't possible to // - `set_kv`: `weak` is a newly created `EphemeronBox`, meaning it isn't possible to
// collect it since `weak` is still live. // collect it since `weak` is still live.
unsafe { weak.inner().inner_ptr().as_mut().set(&gc, gc.clone()) } unsafe { weak.inner().inner_ptr().as_mut().set(&gc, ()) }
gc gc
} }

15
boa_gc/src/pointers/weak.rs

@ -8,33 +8,36 @@ use std::hash::{Hash, Hasher};
#[derive(Debug, Trace, Finalize)] #[derive(Debug, Trace, Finalize)]
#[repr(transparent)] #[repr(transparent)]
pub struct WeakGc<T: Trace + 'static> { pub struct WeakGc<T: Trace + 'static> {
inner: Ephemeron<T, Gc<T>>, inner: Ephemeron<T, ()>,
} }
impl<T: Trace> WeakGc<T> { impl<T: Trace> WeakGc<T> {
/// Creates a new weak pointer for a garbage collected value. /// Creates a new weak pointer for a garbage collected value.
#[inline]
#[must_use] #[must_use]
pub fn new(value: &Gc<T>) -> Self { pub fn new(value: &Gc<T>) -> Self {
Self { Self {
inner: Ephemeron::new(value, value.clone()), inner: Ephemeron::new(value, ()),
} }
} }
/// Upgrade returns a `Gc` pointer for the internal value if the pointer is still live, or `None` /// Upgrade returns a `Gc` pointer for the internal value if the pointer is still live, or `None`
/// if the value was already garbage collected. /// if the value was already garbage collected.
#[inline]
#[must_use] #[must_use]
pub fn upgrade(&self) -> Option<Gc<T>> { pub fn upgrade(&self) -> Option<Gc<T>> {
self.inner.value() self.inner.key()
} }
/// Check if the [`WeakGc`] can be upgraded. /// Check if the [`WeakGc`] can be upgraded.
#[inline]
#[must_use] #[must_use]
pub fn is_upgradable(&self) -> bool { pub fn is_upgradable(&self) -> bool {
self.inner.has_value() self.inner.has_value()
} }
#[must_use] #[must_use]
pub(crate) const fn inner(&self) -> &Ephemeron<T, Gc<T>> { pub(crate) const fn inner(&self) -> &Ephemeron<T, ()> {
&self.inner &self.inner
} }
} }
@ -47,8 +50,8 @@ impl<T: Trace> Clone for WeakGc<T> {
} }
} }
impl<T: Trace> From<Ephemeron<T, Gc<T>>> for WeakGc<T> { impl<T: Trace> From<Ephemeron<T, ()>> for WeakGc<T> {
fn from(inner: Ephemeron<T, Gc<T>>) -> Self { fn from(inner: Ephemeron<T, ()>) -> Self {
Self { inner } Self { inner }
} }
} }

Loading…
Cancel
Save