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}