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