Skip to content

Commit

Permalink
Implement ephemeron-based weak map
Browse files Browse the repository at this point in the history
  • Loading branch information
jedel1043 committed Jun 20, 2023
1 parent 610cf2c commit b8adfa7
Show file tree
Hide file tree
Showing 8 changed files with 377 additions and 20 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion boa_gc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ thinvec = ["thin-vec"]
boa_profiler.workspace = true
boa_macros.workspace = true

thin-vec = { version = "0.2.12", optional = true }
thin-vec = { version = "0.2.12", optional = true }
hashbrown = { version = "0.14.0", features = ["raw"] }
18 changes: 17 additions & 1 deletion boa_gc/src/internals/ephemeron_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,27 @@ impl<K: Trace + ?Sized, V: Trace> EphemeronBox<K, V> {
}

/// Returns a reference to the ephemeron's value or None.
pub(crate) fn value(&self) -> Option<&V> {
///
/// # Safety
///
/// The garbage collector must not run between the call to this function and the eventual
/// drop of the returned reference, since that could free the inner value.
pub(crate) unsafe fn value(&self) -> Option<&V> {
// SAFETY: the garbage collector ensures `ptr` is valid as long as `data` is `Some`.
unsafe { self.data.get().map(|ptr| &ptr.as_ref().value) }
}

/// Returns a reference to the ephemeron's key or None.
///
/// # Safety
///
/// The garbage collector must not run between the call to this function and the eventual
/// drop of the returned reference, since that could free the inner value.
pub(crate) unsafe fn key(&self) -> Option<&GcBox<K>> {
// SAFETY: the garbage collector ensures `ptr` is valid as long as `data` is `Some`.
unsafe { self.data.get().map(|ptr| ptr.as_ref().key.as_ref()) }
}

/// Marks this `EphemeronBox` as live.
///
/// This doesn't mark the inner value of the ephemeron. [`ErasedEphemeronBox::trace`]
Expand Down
15 changes: 8 additions & 7 deletions boa_gc/src/internals/weak_map_box.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{GcRefCell, Trace, WeakGc};
use std::{cell::Cell, collections::HashMap, ptr::NonNull};
use crate::{pointers::RawWeakMap, GcRefCell, Trace, WeakGc};
use std::{cell::Cell, ptr::NonNull};

/// A box that is used to track [`WeakMap`][`crate::WeakMap`]s.
pub(crate) struct WeakMapBox<K: Trace + Sized + 'static, V: Trace + Sized + 'static> {
pub(crate) map: WeakGc<GcRefCell<HashMap<WeakGc<K>, V>>>,
pub(crate) map: WeakGc<GcRefCell<RawWeakMap<K, V>>>,
pub(crate) next: Cell<Option<NonNull<dyn ErasedWeakMapBox>>>,
}

Expand All @@ -18,15 +18,16 @@ pub(crate) trait ErasedWeakMapBox {
/// Returns `true` if the [`WeakMapBox`] is live.
fn is_live(&self) -> bool;

/// Traces the weak reference inside of the [`WeakMapBox`] it the weak map is live.
/// Traces the weak reference inside of the [`WeakMapBox`] if the weak map is live.
unsafe fn trace(&self);
}

impl<K: Trace, V: Trace> ErasedWeakMapBox for WeakMapBox<K, V> {
impl<K: Trace, V: Trace + Clone> ErasedWeakMapBox for WeakMapBox<K, V> {
fn clear_dead_entries(&self) {
if let Some(map) = self.map.upgrade() {
let mut map = map.borrow_mut();
map.retain(|k, _| k.upgrade().is_some());
if let Ok(mut map) = map.try_borrow_mut() {
map.clear_expired()
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions boa_gc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ pub(crate) mod internals;

use boa_profiler::Profiler;
use internals::{EphemeronBox, ErasedEphemeronBox, ErasedWeakMapBox, WeakMapBox};
use pointers::RawWeakMap;
use std::{
cell::{Cell, RefCell},
collections::HashMap,
mem,
ptr::NonNull,
};
Expand Down Expand Up @@ -219,11 +219,11 @@ impl Allocator {
})
}

fn alloc_weak_map<K: Trace, V: Trace>() -> WeakMap<K, V> {
fn alloc_weak_map<K: Trace, V: Trace + Clone>() -> WeakMap<K, V> {
let _timer = Profiler::global().start_event("New WeakMap", "BoaAlloc");

let weak_map = WeakMap {
inner: Gc::new(GcRefCell::new(HashMap::new())),
inner: Gc::new(GcRefCell::new(RawWeakMap::new())),
};
let weak = WeakGc::new(&weak_map.inner);

Expand Down
2 changes: 1 addition & 1 deletion boa_gc/src/pointers/ephemeron.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl<K: Trace + ?Sized, V: Trace> Ephemeron<K, V> {
self.inner_ptr.get().as_ptr()
}

fn inner(&self) -> &EphemeronBox<K, V> {
pub(crate) fn inner(&self) -> &EphemeronBox<K, V> {
// SAFETY: Please see Gc::inner_ptr()
unsafe { self.inner_ptr().as_ref() }
}
Expand Down
2 changes: 2 additions & 0 deletions boa_gc/src/pointers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ pub use ephemeron::Ephemeron;
pub use gc::Gc;
pub use weak::WeakGc;
pub use weak_map::WeakMap;

pub(crate) use weak_map::RawWeakMap;
Loading

0 comments on commit b8adfa7

Please sign in to comment.