Coverage Report

Created: 2026-03-23 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/seize-0.5.1/src/guard.rs
Line
Count
Source
1
use std::fmt;
2
use std::marker::PhantomData;
3
use std::sync::atomic::{AtomicPtr, Ordering};
4
5
use crate::raw::{self, Reservation, Thread};
6
use 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.
13
pub 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
171k
    fn protect<T>(&self, ptr: &AtomicPtr<T>, order: Ordering) -> *mut T {
65
171k
        ptr.load(raw::Collector::protect(order))
66
171k
    }
Unexecuted instantiation: <papaya::raw::utils::MapGuard<seize::guard::LocalGuard> as seize::guard::Guard>::protect::<papaya::raw::Entry<bytes::bytes::Bytes, ()>>
<papaya::raw::utils::MapGuard<seize::guard::LocalGuard> as seize::guard::Guard>::protect::<papaya::raw::alloc::RawTable<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>
Line
Count
Source
64
171k
    fn protect<T>(&self, ptr: &AtomicPtr<T>, order: Ordering) -> *mut T {
65
171k
        ptr.load(raw::Collector::protect(order))
66
171k
    }
Unexecuted instantiation: <_ as seize::guard::Guard>::protect::<_>
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
0
    fn swap<T>(&self, ptr: &AtomicPtr<T>, value: *mut T, order: Ordering) -> *mut T {
74
0
        ptr.swap(value, raw::Collector::protect(order))
75
0
    }
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
0
    fn compare_exchange<T>(
84
0
        &self,
85
0
        ptr: &AtomicPtr<T>,
86
0
        current: *mut T,
87
0
        new: *mut T,
88
0
        success: Ordering,
89
0
        failure: Ordering,
90
0
    ) -> Result<*mut T, *mut T> {
91
0
        ptr.compare_exchange(
92
0
            current,
93
0
            new,
94
0
            raw::Collector::protect(success),
95
0
            raw::Collector::protect(failure),
96
        )
97
0
    }
Unexecuted instantiation: <papaya::raw::utils::MapGuard<seize::guard::LocalGuard> as seize::guard::Guard>::compare_exchange::<papaya::raw::Entry<bytes::bytes::Bytes, ()>>
Unexecuted instantiation: <_ as seize::guard::Guard>::compare_exchange::<_>
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
0
    fn compare_exchange_weak<T>(
106
0
        &self,
107
0
        ptr: &AtomicPtr<T>,
108
0
        current: *mut T,
109
0
        new: *mut T,
110
0
        success: Ordering,
111
0
        failure: Ordering,
112
0
    ) -> Result<*mut T, *mut T> {
113
0
        ptr.compare_exchange_weak(
114
0
            current,
115
0
            new,
116
0
            raw::Collector::protect(success),
117
0
            raw::Collector::protect(failure),
118
        )
119
0
    }
Unexecuted instantiation: <papaya::raw::utils::MapGuard<seize::guard::LocalGuard> as seize::guard::Guard>::compare_exchange_weak::<papaya::raw::Entry<bytes::bytes::Bytes, ()>>
Unexecuted instantiation: <_ as seize::guard::Guard>::compare_exchange_weak::<_>
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.
145
pub 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
160
impl LocalGuard<'_> {
161
    #[inline]
162
171k
    pub(crate) fn enter(collector: &Collector) -> LocalGuard<'_> {
163
171k
        let thread = Thread::current();
164
165
        // Safety: `thread` is the current thread.
166
171k
        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
171k
        let guards = reservation.guards.get();
171
171k
        reservation.guards.set(guards + 1);
172
173
171k
        if guards == 0 {
174
171k
            // Safety: Only called on the current thread, which is currently inactive.
175
171k
            unsafe { collector.raw.enter(reservation) };
176
171k
        }
177
178
171k
        LocalGuard {
179
171k
            thread,
180
171k
            reservation,
181
171k
            collector,
182
171k
            _unsend: PhantomData,
183
171k
        }
184
171k
    }
<seize::guard::LocalGuard>::enter
Line
Count
Source
162
171k
    pub(crate) fn enter(collector: &Collector) -> LocalGuard<'_> {
163
171k
        let thread = Thread::current();
164
165
        // Safety: `thread` is the current thread.
166
171k
        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
171k
        let guards = reservation.guards.get();
171
171k
        reservation.guards.set(guards + 1);
172
173
171k
        if guards == 0 {
174
171k
            // Safety: Only called on the current thread, which is currently inactive.
175
171k
            unsafe { collector.raw.enter(reservation) };
176
171k
        }
177
178
171k
        LocalGuard {
179
171k
            thread,
180
171k
            reservation,
181
171k
            collector,
182
171k
            _unsend: PhantomData,
183
171k
        }
184
171k
    }
Unexecuted instantiation: <seize::guard::LocalGuard>::enter
185
}
186
187
impl Guard for LocalGuard<'_> {
188
    /// Refreshes the guard.
189
    #[inline]
190
0
    fn refresh(&mut self) {
191
        // Safety: `self.reservation` is owned by the current thread.
192
0
        let reservation = unsafe { &*self.reservation };
193
0
        let guards = reservation.guards.get();
194
195
0
        if guards == 1 {
196
            // Safety: We have a unique reference to the last active guard.
197
0
            unsafe { self.collector.raw.refresh(reservation) }
198
0
        }
199
0
    }
200
201
    /// Flush any retired values in the local batch.
202
    #[inline]
203
0
    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
0
        unsafe { self.collector.raw.try_retire_batch(self.thread) }
209
0
    }
210
211
    /// Returns the collector this guard was created from.
212
    #[inline]
213
0
    fn collector(&self) -> &Collector {
214
0
        self.collector
215
0
    }
216
217
    /// Returns a numeric identifier for the current thread.
218
    #[inline]
219
0
    fn thread_id(&self) -> usize {
220
0
        self.thread.id
221
0
    }
Unexecuted instantiation: <seize::guard::LocalGuard as seize::guard::Guard>::thread_id
Unexecuted instantiation: <seize::guard::LocalGuard as seize::guard::Guard>::thread_id
222
223
    /// Retires a value, running `reclaim` when no threads hold a reference to
224
    /// it.
225
    #[inline]
226
0
    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
0
        unsafe { self.collector.raw.add(ptr, reclaim, self.thread) }
231
0
    }
Unexecuted instantiation: <seize::guard::LocalGuard as seize::guard::Guard>::defer_retire::<papaya::raw::Entry<bytes::bytes::Bytes, ()>>
Unexecuted instantiation: <seize::guard::LocalGuard as seize::guard::Guard>::defer_retire::<papaya::raw::alloc::RawTable<papaya::raw::Entry<bytes::bytes::Bytes, ()>>>
Unexecuted instantiation: <seize::guard::LocalGuard as seize::guard::Guard>::defer_retire::<_>
232
}
233
234
impl Drop for LocalGuard<'_> {
235
    #[inline]
236
171k
    fn drop(&mut self) {
237
        // Safety: `self.reservation` is owned by the current thread.
238
171k
        let reservation = unsafe { &*self.reservation };
239
240
        // Decrement the active guard count.
241
171k
        let guards = reservation.guards.get();
242
171k
        reservation.guards.set(guards - 1);
243
244
171k
        if guards == 1 {
245
171k
            // Safety: We have a unique reference to the last active guard.
246
171k
            unsafe { self.collector.raw.leave(reservation) };
247
171k
        }
248
171k
    }
<seize::guard::LocalGuard as core::ops::drop::Drop>::drop
Line
Count
Source
236
171k
    fn drop(&mut self) {
237
        // Safety: `self.reservation` is owned by the current thread.
238
171k
        let reservation = unsafe { &*self.reservation };
239
240
        // Decrement the active guard count.
241
171k
        let guards = reservation.guards.get();
242
171k
        reservation.guards.set(guards - 1);
243
244
171k
        if guards == 1 {
245
171k
            // Safety: We have a unique reference to the last active guard.
246
171k
            unsafe { self.collector.raw.leave(reservation) };
247
171k
        }
248
171k
    }
Unexecuted instantiation: <seize::guard::LocalGuard as core::ops::drop::Drop>::drop
249
}
250
251
impl fmt::Debug for LocalGuard<'_> {
252
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253
0
        f.debug_tuple("LocalGuard").finish()
254
0
    }
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.
269
pub 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.
282
unsafe impl Sync for OwnedGuard<'_> {}
283
284
// Safety: `OwnedGuard` owns its thread slot and is not tied to any
285
// thread-locals.
286
unsafe impl Send for OwnedGuard<'_> {}
287
288
impl OwnedGuard<'_> {
289
    #[inline]
290
0
    pub(crate) fn enter(collector: &Collector) -> OwnedGuard<'_> {
291
        // Create a thread slot that will last for the lifetime of this guard.
292
0
        let thread = Thread::create();
293
294
        // Safety: We have ownership of `thread` and have not shared it.
295
0
        let reservation = unsafe { collector.raw.reservation(thread) };
296
297
        // Safety: We have ownership of `reservation`.
298
0
        unsafe { collector.raw.enter(reservation) };
299
300
0
        OwnedGuard {
301
0
            collector,
302
0
            thread,
303
0
            reservation,
304
0
        }
305
0
    }
306
}
307
308
impl Guard for OwnedGuard<'_> {
309
    /// Refreshes the guard.
310
    #[inline]
311
0
    fn refresh(&mut self) {
312
        // Safety: `self.reservation` is owned by the current thread.
313
0
        let reservation = unsafe { &*self.reservation };
314
0
        unsafe { self.collector.raw.refresh(reservation) }
315
0
    }
316
317
    /// Flush any retired values in the local batch.
318
    #[inline]
319
0
    fn flush(&self) {
320
        // Safety: `self.reservation` is owned by the current thread.
321
0
        let reservation = unsafe { &*self.reservation };
322
0
        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
0
        unsafe { self.collector.raw.try_retire_batch(self.thread) }
328
0
    }
329
330
    /// Returns the collector this guard was created from.
331
    #[inline]
332
0
    fn collector(&self) -> &Collector {
333
0
        self.collector
334
0
    }
335
336
    /// Returns a numeric identifier for the current thread.
337
    #[inline]
338
0
    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
0
        Thread::current().id
344
0
    }
345
346
    /// Retires a value, running `reclaim` when no threads hold a reference to
347
    /// it.
348
    #[inline]
349
0
    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
0
        let reservation = unsafe { &*self.reservation };
352
0
        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
0
        unsafe { self.collector.raw.add(ptr, reclaim, self.thread) }
358
0
    }
359
}
360
361
impl Drop for OwnedGuard<'_> {
362
    #[inline]
363
0
    fn drop(&mut self) {
364
        // Safety: `self.reservation` is owned by the current thread.
365
0
        let reservation = unsafe { &*self.reservation };
366
367
        // Safety: `self.thread` is an owned thread.
368
0
        unsafe { self.collector.raw.leave(reservation) };
369
370
        // Safety: We are in `drop` and never share `self.thread`.
371
0
        unsafe { Thread::free(self.thread.id) };
372
0
    }
373
}