lifetime_guard/
lib.rs

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