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