Skip to main content

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}