lifetime_guard/lib.rs
1#![doc = include_str!("../README.md")]
2
3use std::{cell::Cell, marker::PhantomPinned, pin::Pin, ptr::NonNull};
4
5/// Strong guard for granting read access to a single interior mutable value to
6/// `RefGuard`.
7///
8/// A `ValueGuard`:`RefGuard` relationship is exclusive, and behaves similarly
9/// to a single `Rc` and `Weak` pair, but notably does not require heap
10/// allocation. `ValueGuard::registration` creates a `GuardRegistration`, which
11/// provides a movable wrapper for safety creating the circular references
12/// between two pinned self referential structs.
13///
14/// # Safety
15///
16/// This struct *must* not be leaked to the stack using `mem::forget` or any
17/// other mechanism that causes the contents of `Self` to be overwritten
18/// without `Drop::drop()` running.
19/// Doing so creates unsoundness that likely will lead to dereferencing a null
20/// pointer.
21///
22/// Note that it is sound to leak `Self` to the heap using methods including
23/// `Box::leak()` because heap allocated data will never be overwritten if it
24/// is never freed.
25pub struct ValueGuard<T> {
26 /// Contains the value being immutably accessed by `RefGuard` and
27 /// mutably accessed by `Self`
28 ///
29 /// This needs to be a cell so that the original immutable alias
30 /// to `Self` (given to `RefGuard`) can continue to be referenced after
31 /// invalidated by the creation of a mutable alias for `Self::set`.
32 data: Cell<T>,
33 /// A pointer to a `RefGuard` with read access to `data` to invalidate that
34 /// `RefGuard` when `Self` is dropped.
35 ref_guard: Cell<Option<NonNull<RefGuard<T>>>>,
36 _marker: PhantomPinned,
37}
38
39impl<T> ValueGuard<T> {
40 /// Creates a new `ValueGuard` containing `data`.
41 #[inline]
42 pub fn new(data: T) -> Self {
43 Self {
44 data: Cell::new(data),
45 ref_guard: Cell::new(None),
46 _marker: PhantomPinned,
47 }
48 }
49
50 /// Returns a `GuardRegistration`, which can be used to safety link `Self`
51 /// to a `RefGuard`.
52 #[inline]
53 pub fn registration<'a>(self: Pin<&'a Self>) -> GuardRegistration<'a, T> {
54 GuardRegistration { value_guard: self }
55 }
56
57 /// Sets the internal value stored by `Self`.
58 #[inline]
59 pub fn set(&self, value: T) {
60 self.data.set(value);
61 }
62}
63
64/// Helper function to invalidate a `ValueGuard`'s `RefGuard` reference
65#[inline]
66fn invalidate_value_guard<T>(guard: NonNull<ValueGuard<T>>) {
67 unsafe { (*guard.as_ptr()).ref_guard.set(None) };
68}
69
70impl<T: Copy> ValueGuard<T> {
71 /// Gets a copy of the value stored inside this `ValueGuard`.
72 #[inline]
73 pub fn get(&self) -> T {
74 self.data.get()
75 }
76}
77
78impl<T> Drop for ValueGuard<T> {
79 #[inline]
80 fn drop(&mut self) {
81 if let Some(guard) = self.ref_guard.get() {
82 invalidate_ref_guard(guard);
83 }
84 }
85}
86
87/// Weak guard for acquiring read only access to a `ValueGuard`'s value.
88///
89/// # Safety
90///
91/// This struct *must* not be leaked to the stack using `mem::forget` or any
92/// other mechanism that causes the contents of `Self` to be overwritten
93/// without `Drop::drop()` running.
94/// Doing so creates unsoundness that likely will lead to dereferencing a null
95/// pointer.
96///
97/// Note that it is sound to leak `Self` to the heap using methods including
98/// `Box::leak()` because heap allocated data will never be overwritten if it
99/// is never freed.
100pub struct RefGuard<T> {
101 value_guard: Cell<Option<NonNull<ValueGuard<T>>>>,
102 _marker: PhantomPinned,
103}
104
105impl<T> RefGuard<T> {
106 /// Creates a new `RefGuard` with no reference to a `ValueGuard`.
107 #[inline]
108 pub fn new() -> Self {
109 Self {
110 value_guard: Cell::new(None),
111 _marker: PhantomPinned,
112 }
113 }
114}
115
116/// Helper function to invalidate a `RefGuard`'s `ValueGuard` reference
117#[inline]
118fn invalidate_ref_guard<T>(guard: NonNull<RefGuard<T>>) {
119 unsafe { (*guard.as_ptr()).value_guard.set(None) };
120}
121
122impl<T: Copy> RefGuard<T> {
123 /// Gets a copy of the value stored inside the `ValueGuard` this `RefGuard`
124 /// references.
125 #[inline]
126 pub fn get(&self) -> Option<T> {
127 self.value_guard
128 .get()
129 .map(|guard| unsafe { (*guard.as_ptr()).get() })
130 }
131}
132
133impl<T> Drop for RefGuard<T> {
134 #[inline]
135 fn drop(&mut self) {
136 if let Some(guard) = self.value_guard.get() {
137 invalidate_value_guard(guard);
138 }
139 }
140}
141
142impl<T> Default for RefGuard<T> {
143 #[inline]
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149/// Safe api for creating self reference between a pinned `ValueGuard` and
150/// `RefGuard` pair.
151///
152/// This can be acquired with
153/// [`ValueGuard::registration()`](ValueGuard::registration).
154pub struct GuardRegistration<'a, T> {
155 value_guard: Pin<&'a ValueGuard<T>>,
156}
157
158impl<'a, T> GuardRegistration<'a, T> {
159 /// Binds a provided `slot` to the `self.value_guard`.
160 ///
161 /// This means they will reference each other, and will invalidate their
162 /// references to each other when dropped.
163 ///
164 /// This method also invalidates the existing references held by the
165 /// now-replaced referencees of `slot` and `self.value_guard` to avoid
166 /// dangling pointers.
167 pub fn register(self, slot: Pin<&'a RefGuard<T>>) {
168 // replace slot's value guard with reference to self.value_guard
169 // and invalidate slot's old value guard if it exists
170 if let Some(old_guard) = slot
171 .value_guard
172 .replace(Some(self.value_guard.get_ref().into()))
173 {
174 invalidate_value_guard(old_guard);
175 }
176
177 // replace self.value_guard's ref guard with reference to slot
178 // and invalidate self.value_guard's old ref guard if it exists
179 if let Some(old_guard) = self
180 .value_guard
181 .ref_guard
182 .replace(Some(slot.get_ref().into()))
183 {
184 invalidate_ref_guard(old_guard);
185 }
186 }
187}
188
189#[cfg(test)]
190mod test {
191 use std::{mem, pin};
192
193 use super::*;
194
195 #[test]
196 fn basic() {
197 let weak = pin::pin!(RefGuard::new());
198 {
199 let strong = pin::pin!(ValueGuard::new(2));
200 strong.as_ref().registration().register(weak.as_ref());
201
202 assert_eq!(strong.get(), 2);
203 assert_eq!(weak.get(), Some(2));
204
205 strong.as_ref().set(3);
206 assert_eq!(strong.get(), 3);
207 assert_eq!(weak.get(), Some(3));
208 }
209
210 assert_eq!(weak.get(), None);
211 }
212
213 #[test]
214 fn multiple_registrations() {
215 let weak1 = pin::pin!(RefGuard::new());
216 let weak2 = pin::pin!(RefGuard::new());
217 {
218 let strong = pin::pin!(ValueGuard::new(2));
219 strong.as_ref().registration().register(weak1.as_ref());
220
221 assert_eq!(strong.get(), 2);
222 assert_eq!(weak1.get(), Some(2));
223
224 strong.as_ref().set(3);
225 assert_eq!(strong.get(), 3);
226 assert_eq!(weak1.get(), Some(3));
227
228 // register next ptr, should invalidate previous weak ref (weak1)
229 strong.as_ref().registration().register(weak2.as_ref());
230 assert_eq!(weak1.get(), None);
231 assert_eq!(weak1.value_guard.get(), None);
232
233 assert_eq!(strong.get(), 3);
234 assert_eq!(weak2.get(), Some(3));
235
236 strong.as_ref().set(4);
237 assert_eq!(strong.get(), 4);
238 assert_eq!(weak2.get(), Some(4));
239 }
240
241 assert_eq!(weak1.get(), None);
242 assert_eq!(weak2.get(), None);
243 }
244
245 #[test]
246 #[cfg_attr(miri, ignore)]
247 fn safe_leak() {
248 let strong = Box::pin(ValueGuard::new(10));
249 let weak = pin::pin!(RefGuard::new());
250 strong.as_ref().registration().register(weak.as_ref());
251
252 // strong is now a ValueGuard on the heap that will never be freed
253 // this is sound because it will never be overwritten
254 mem::forget(strong);
255
256 assert_eq!(weak.get(), Some(10));
257 }
258}