seize/guard.rs
1use std::fmt;
2use std::marker::PhantomData;
3use std::sync::atomic::{AtomicPtr, Ordering};
4
5use crate::raw::{self, Reservation, Thread};
6use crate::Collector;
7
8/// A guard that enables protected loads of concurrent objects.
9///
10/// This trait provides common functionality implemented by [`LocalGuard`] and
11/// [`OwnedGuard`]. See [the guide](crate::guide#starting-operations) for an
12/// introduction to using guards.
13pub trait Guard {
14 /// Refreshes the guard.
15 ///
16 /// Calling this method is similar to dropping and immediately creating a
17 /// new guard. The current thread remains active, but any pointers that
18 /// were previously protected may be reclaimed.
19 ///
20 /// # Safety
21 ///
22 /// This method is not marked as `unsafe`, but will affect the validity of
23 /// pointers loaded using [`Guard::protect`], similar to dropping a guard.
24 /// It is intended to be used safely by users of concurrent data structures,
25 /// as references will be tied to the guard and this method takes `&mut
26 /// self`.
27 fn refresh(&mut self);
28
29 /// Flush any retired values in the local batch.
30 ///
31 /// This method flushes any values from the current thread's local batch,
32 /// starting the reclamation process. Note that no memory can be
33 /// reclaimed while this guard is active, but calling `flush` may allow
34 /// memory to be reclaimed more quickly after the guard is dropped.
35 ///
36 /// Note that the batch must contain at least as many objects as the number
37 /// of currently active threads for a flush to be performed. See
38 /// [`Collector::batch_size`] for details about batch sizes.
39 fn flush(&self);
40
41 /// Returns the collector this guard was created from.
42 fn collector(&self) -> &Collector;
43
44 /// Returns a numeric identifier for the current thread.
45 ///
46 /// Guards rely on thread-local state, including thread IDs. This method is
47 /// a cheap way to get an identifier for the current thread without TLS
48 /// overhead. Note that thread IDs may be reused, so the value returned
49 /// is only unique for the lifetime of this thread.
50 fn thread_id(&self) -> usize;
51
52 /// Protects the load of an atomic pointer.
53 ///
54 /// Any valid pointer loaded through a guard using the `protect` method is
55 /// guaranteed to stay valid until the guard is dropped, or the object
56 /// is retired by the current thread. Importantly, if another thread
57 /// retires this object, it will not be reclaimed for the lifetime of
58 /// this guard.
59 ///
60 /// Note that the lifetime of a guarded pointer is logically tied to that of
61 /// the guard — when the guard is dropped the pointer is invalidated. Data
62 /// structures that return shared references to values should ensure that
63 /// the lifetime of the reference is tied to the lifetime of a guard.
64 fn protect<T>(&self, ptr: &AtomicPtr<T>, order: Ordering) -> *mut T {
65 ptr.load(raw::Collector::protect(order))
66 }
67
68 /// Stores a value into the pointer, returning the protected previous value.
69 ///
70 /// This method is equivalent to [`AtomicPtr::swap`], except the returned
71 /// value is guaranteed to be protected with the same guarantees as
72 /// [`Guard::protect`].
73 fn swap<T>(&self, ptr: &AtomicPtr<T>, value: *mut T, order: Ordering) -> *mut T {
74 ptr.swap(value, raw::Collector::protect(order))
75 }
76
77 /// Stores a value into the pointer if the current value is the same as the
78 /// `current` value, returning the protected previous value.
79 ///
80 /// This method is equivalent to [`AtomicPtr::compare_exchange`], except the
81 /// returned value is guaranteed to be protected with the same
82 /// guarantees as [`Guard::protect`].
83 fn compare_exchange<T>(
84 &self,
85 ptr: &AtomicPtr<T>,
86 current: *mut T,
87 new: *mut T,
88 success: Ordering,
89 failure: Ordering,
90 ) -> Result<*mut T, *mut T> {
91 ptr.compare_exchange(
92 current,
93 new,
94 raw::Collector::protect(success),
95 raw::Collector::protect(failure),
96 )
97 }
98
99 /// Stores a value into the pointer if the current value is the same as the
100 /// `current` value, returning the protected previous value.
101 ///
102 /// This method is equivalent to [`AtomicPtr::compare_exchange_weak`],
103 /// except the returned value is guaranteed to be protected with the
104 /// same guarantees as [`Guard::protect`].
105 fn compare_exchange_weak<T>(
106 &self,
107 ptr: &AtomicPtr<T>,
108 current: *mut T,
109 new: *mut T,
110 success: Ordering,
111 failure: Ordering,
112 ) -> Result<*mut T, *mut T> {
113 ptr.compare_exchange_weak(
114 current,
115 new,
116 raw::Collector::protect(success),
117 raw::Collector::protect(failure),
118 )
119 }
120
121 /// Retires a value, running `reclaim` when no threads hold a reference to
122 /// it.
123 ///
124 /// This method delays reclamation until the guard is dropped, as opposed to
125 /// [`Collector::retire`], which may reclaim objects immediately.
126 ///
127 ///
128 /// # Safety
129 ///
130 /// The retired pointer must no longer be accessible to any thread that
131 /// enters after it is removed. Additionally, the pointer must be valid
132 /// to pass to the provided reclaimer, once it is safe to reclaim.
133 unsafe fn defer_retire<T>(&self, ptr: *mut T, reclaim: unsafe fn(*mut T, &Collector));
134}
135
136/// A guard that keeps the current thread marked as active.
137///
138/// Local guards are created by calling [`Collector::enter`]. Unlike
139/// [`OwnedGuard`], a local guard is tied to the current thread and does not
140/// implement `Send`. This makes local guards relatively cheap to create and
141/// destroy.
142///
143/// Most of the functionality provided by this type is through the [`Guard`]
144/// trait.
145pub struct LocalGuard<'a> {
146 /// The collector that this guard is associated with.
147 collector: &'a Collector,
148
149 // The current thread.
150 thread: Thread,
151
152 // The reservation for the current thread.
153 reservation: *const Reservation,
154
155 // `LocalGuard` not be `Send or Sync` as we are tied to the state of the
156 // current thread in the collector.
157 _unsend: PhantomData<*mut ()>,
158}
159
160impl LocalGuard<'_> {
161 #[inline]
162 pub(crate) fn enter(collector: &Collector) -> LocalGuard<'_> {
163 let thread = Thread::current();
164
165 // Safety: `thread` is the current thread.
166 let reservation = unsafe { collector.raw.reservation(thread) };
167
168 // Calls to `enter` may be reentrant, so we need to keep track of the number of
169 // active guards for the current thread.
170 let guards = reservation.guards.get();
171 reservation.guards.set(guards + 1);
172
173 if guards == 0 {
174 // Safety: Only called on the current thread, which is currently inactive.
175 unsafe { collector.raw.enter(reservation) };
176 }
177
178 LocalGuard {
179 thread,
180 reservation,
181 collector,
182 _unsend: PhantomData,
183 }
184 }
185}
186
187impl Guard for LocalGuard<'_> {
188 /// Refreshes the guard.
189 #[inline]
190 fn refresh(&mut self) {
191 // Safety: `self.reservation` is owned by the current thread.
192 let reservation = unsafe { &*self.reservation };
193 let guards = reservation.guards.get();
194
195 if guards == 1 {
196 // Safety: We have a unique reference to the last active guard.
197 unsafe { self.collector.raw.refresh(reservation) }
198 }
199 }
200
201 /// Flush any retired values in the local batch.
202 #[inline]
203 fn flush(&self) {
204 // Note that this does not actually retire any values, it just attempts to add
205 // the batch to any active reservations lists, including ours.
206 //
207 // Safety: `self.thread` is the current thread.
208 unsafe { self.collector.raw.try_retire_batch(self.thread) }
209 }
210
211 /// Returns the collector this guard was created from.
212 #[inline]
213 fn collector(&self) -> &Collector {
214 self.collector
215 }
216
217 /// Returns a numeric identifier for the current thread.
218 #[inline]
219 fn thread_id(&self) -> usize {
220 self.thread.id
221 }
222
223 /// Retires a value, running `reclaim` when no threads hold a reference to
224 /// it.
225 #[inline]
226 unsafe fn defer_retire<T>(&self, ptr: *mut T, reclaim: unsafe fn(*mut T, &Collector)) {
227 // Safety:
228 // - `self.thread` is the current thread.
229 // - The validity of the pointer is guaranteed by the caller.
230 unsafe { self.collector.raw.add(ptr, reclaim, self.thread) }
231 }
232}
233
234impl Drop for LocalGuard<'_> {
235 #[inline]
236 fn drop(&mut self) {
237 // Safety: `self.reservation` is owned by the current thread.
238 let reservation = unsafe { &*self.reservation };
239
240 // Decrement the active guard count.
241 let guards = reservation.guards.get();
242 reservation.guards.set(guards - 1);
243
244 if guards == 1 {
245 // Safety: We have a unique reference to the last active guard.
246 unsafe { self.collector.raw.leave(reservation) };
247 }
248 }
249}
250
251impl fmt::Debug for LocalGuard<'_> {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 f.debug_tuple("LocalGuard").finish()
254 }
255}
256
257/// A guard that protects objects for it's lifetime, independent of the current
258/// thread.
259///
260/// Unlike [`LocalGuard`], an owned guard is independent of the current thread,
261/// allowing it to implement `Send` and `Sync`. This is useful for holding
262/// guards across `.await` points in work-stealing schedulers, where execution
263/// may be resumed on a different thread than started on. However, owned guards
264/// are more expensive to create and destroy, so should be avoided if
265/// cross-thread usage is not required.
266///
267/// Most of the functionality provided by this type is through the [`Guard`]
268/// trait.
269pub struct OwnedGuard<'a> {
270 /// The collector that this guard is associated with.
271 collector: &'a Collector,
272
273 // An owned thread, unique to this guard.
274 thread: Thread,
275
276 // The reservation for this guard.
277 reservation: *const Reservation,
278}
279
280// Safety: All shared methods on `OwnedGuard` that access shared memory are
281// synchronized with locks.
282unsafe impl Sync for OwnedGuard<'_> {}
283
284// Safety: `OwnedGuard` owns its thread slot and is not tied to any
285// thread-locals.
286unsafe impl Send for OwnedGuard<'_> {}
287
288impl OwnedGuard<'_> {
289 #[inline]
290 pub(crate) fn enter(collector: &Collector) -> OwnedGuard<'_> {
291 // Create a thread slot that will last for the lifetime of this guard.
292 let thread = Thread::create();
293
294 // Safety: We have ownership of `thread` and have not shared it.
295 let reservation = unsafe { collector.raw.reservation(thread) };
296
297 // Safety: We have ownership of `reservation`.
298 unsafe { collector.raw.enter(reservation) };
299
300 OwnedGuard {
301 collector,
302 thread,
303 reservation,
304 }
305 }
306}
307
308impl Guard for OwnedGuard<'_> {
309 /// Refreshes the guard.
310 #[inline]
311 fn refresh(&mut self) {
312 // Safety: `self.reservation` is owned by the current thread.
313 let reservation = unsafe { &*self.reservation };
314 unsafe { self.collector.raw.refresh(reservation) }
315 }
316
317 /// Flush any retired values in the local batch.
318 #[inline]
319 fn flush(&self) {
320 // Safety: `self.reservation` is owned by the current thread.
321 let reservation = unsafe { &*self.reservation };
322 let _lock = reservation.lock.lock().unwrap();
323 // Note that this does not actually retire any values, it just attempts to add
324 // the batch to any active reservations lists, including ours.
325 //
326 // Safety: We hold the lock and so have unique access to the batch.
327 unsafe { self.collector.raw.try_retire_batch(self.thread) }
328 }
329
330 /// Returns the collector this guard was created from.
331 #[inline]
332 fn collector(&self) -> &Collector {
333 self.collector
334 }
335
336 /// Returns a numeric identifier for the current thread.
337 #[inline]
338 fn thread_id(&self) -> usize {
339 // We can't return the ID of our thread slot because `OwnedGuard` is `Send` so
340 // the ID is not uniquely tied to the current thread. We also can't
341 // return the OS thread ID because it might conflict with our thread
342 // IDs, so we have to get/create the current thread.
343 Thread::current().id
344 }
345
346 /// Retires a value, running `reclaim` when no threads hold a reference to
347 /// it.
348 #[inline]
349 unsafe fn defer_retire<T>(&self, ptr: *mut T, reclaim: unsafe fn(*mut T, &Collector)) {
350 // Safety: `self.reservation` is owned by the current thread.
351 let reservation = unsafe { &*self.reservation };
352 let _lock = reservation.lock.lock().unwrap();
353
354 // Safety:
355 // - We hold the lock and so have unique access to the batch.
356 // - The validity of the pointer is guaranteed by the caller.
357 unsafe { self.collector.raw.add(ptr, reclaim, self.thread) }
358 }
359}
360
361impl Drop for OwnedGuard<'_> {
362 #[inline]
363 fn drop(&mut self) {
364 // Safety: `self.reservation` is owned by the current thread.
365 let reservation = unsafe { &*self.reservation };
366
367 // Safety: `self.thread` is an owned thread.
368 unsafe { self.collector.raw.leave(reservation) };
369
370 // Safety: We are in `drop` and never share `self.thread`.
371 unsafe { Thread::free(self.thread.id) };
372 }
373}