boa_gc/pointers/ephemeron.rs
1#![allow(clippy::doc_link_with_quotes)]
2
3use crate::{
4 Allocator, Gc, Tracer, finalizer_safe,
5 internals::EphemeronBox,
6 trace::{Finalize, Trace},
7};
8use std::ptr::NonNull;
9
10/// A key-value pair where the value becomes unaccesible when the key is garbage collected.
11///
12/// You can read more about ephemerons on:
13/// - Racket's page about [**ephemerons**][eph], which gives a brief overview.
14/// - Barry Hayes' paper ["_Ephemerons_: a new finalization mechanism"][acm] which explains the topic
15/// in full detail.
16///
17///
18/// [eph]: https://docs.racket-lang.org/reference/ephemerons.html
19/// [acm]: https://dl.acm.org/doi/10.1145/263700.263733
20#[derive(Debug)]
21pub struct Ephemeron<K: Trace + ?Sized + 'static, V: Trace + 'static> {
22 inner_ptr: NonNull<EphemeronBox<K, V>>,
23}
24
25impl<K: Trace + ?Sized, V: Trace + Clone> Ephemeron<K, V> {
26 /// Gets the stored value of this `Ephemeron`, or `None` if the key was already garbage collected.
27 ///
28 /// This needs to return a clone of the value because holding a reference to it between
29 /// garbage collection passes could drop the underlying allocation, causing an Use After Free.
30 #[must_use]
31 pub fn value(&self) -> Option<V> {
32 // SAFETY: this is safe because `Ephemeron` is tracked to always point to a valid pointer
33 // `inner_ptr`.
34 unsafe { self.inner_ptr.as_ref().value().cloned() }
35 }
36
37 /// Gets the stored key of this `Ephemeron`, or `None` if the key was already garbage collected.
38 #[inline]
39 #[must_use]
40 pub fn key(&self) -> Option<Gc<K>> {
41 // SAFETY: this is safe because `Ephemeron` is tracked to always point to a valid pointer
42 // `inner_ptr`.
43 let key_ptr = unsafe { self.inner_ptr.as_ref().key_ptr() }?;
44
45 // SAFETY: Returned pointer is valid, so this is safe.
46 unsafe {
47 key_ptr.as_ref().inc_ref_count();
48 }
49
50 // SAFETY: The gc pointer's reference count has been incremented, so this is safe.
51 Some(unsafe { Gc::from_raw(key_ptr) })
52 }
53
54 /// Checks if the [`Ephemeron`] has a value.
55 #[must_use]
56 pub fn has_value(&self) -> bool {
57 // SAFETY: this is safe because `Ephemeron` is tracked to always point to a valid pointer
58 // `inner_ptr`.
59 unsafe { self.inner_ptr.as_ref().value().is_some() }
60 }
61}
62
63impl<K: Trace + ?Sized, V: Trace> Ephemeron<K, V> {
64 /// Creates a new `Ephemeron`.
65 #[must_use]
66 pub fn new(key: &Gc<K>, value: V) -> Self {
67 let inner_ptr = Allocator::alloc_ephemeron(EphemeronBox::new(key, value));
68 Self { inner_ptr }
69 }
70
71 /// Returns `true` if the two `Ephemeron`s point to the same allocation.
72 #[must_use]
73 pub fn ptr_eq(this: &Self, other: &Self) -> bool {
74 std::ptr::addr_eq(this.inner(), other.inner())
75 }
76
77 pub(crate) fn inner_ptr(&self) -> NonNull<EphemeronBox<K, V>> {
78 assert!(finalizer_safe());
79 self.inner_ptr
80 }
81
82 pub(crate) fn inner(&self) -> &EphemeronBox<K, V> {
83 // SAFETY: Please see Gc::inner_ptr()
84 unsafe { self.inner_ptr().as_ref() }
85 }
86
87 /// Constructs an `Ephemeron<K, V>` from a raw pointer.
88 ///
89 /// # Safety
90 ///
91 /// This function is unsafe because improper use may lead to memory corruption, double-free,
92 /// or misbehaviour of the garbage collector.
93 #[must_use]
94 pub(crate) const unsafe fn from_raw(inner_ptr: NonNull<EphemeronBox<K, V>>) -> Self {
95 Self { inner_ptr }
96 }
97}
98
99impl<K: Trace + ?Sized, V: Trace> Finalize for Ephemeron<K, V> {
100 fn finalize(&self) {
101 // SAFETY: inner_ptr should be alive when calling finalize.
102 // We don't call inner_ptr() to avoid overhead of calling finalizer_safe().
103 unsafe {
104 self.inner_ptr.as_ref().dec_ref_count();
105 }
106 }
107}
108
109// SAFETY: `Ephemeron`s trace implementation only marks its inner box because we want to stop
110// tracing through weakly held pointers.
111unsafe impl<K: Trace + ?Sized, V: Trace> Trace for Ephemeron<K, V> {
112 unsafe fn trace(&self, _tracer: &mut Tracer) {
113 // SAFETY: We need to mark the inner box of the `Ephemeron` since it is reachable
114 // from a root and this means it cannot be dropped.
115 unsafe {
116 self.inner().mark();
117 }
118 }
119
120 unsafe fn trace_non_roots(&self) {
121 self.inner().inc_non_root_count();
122 }
123
124 fn run_finalizer(&self) {
125 Finalize::finalize(self);
126 }
127}
128
129impl<K: Trace + ?Sized, V: Trace> Clone for Ephemeron<K, V> {
130 fn clone(&self) -> Self {
131 let ptr = self.inner_ptr();
132 self.inner().inc_ref_count();
133 // SAFETY: `&self` is a valid Ephemeron pointer.
134 unsafe { Self::from_raw(ptr) }
135 }
136}
137
138impl<K: Trace + ?Sized, V: Trace> Drop for Ephemeron<K, V> {
139 fn drop(&mut self) {
140 if finalizer_safe() {
141 Finalize::finalize(self);
142 }
143 }
144}