1 // SPDX-License-Identifier: Apache-2.0 OR MIT
3 // Adapted from https://github.com/crossbeam-rs/crossbeam/blob/crossbeam-utils-0.8.7/crossbeam-utils/src/atomic/seq_lock.rs.
7 sync
::atomic
::{self, Ordering}
,
10 use super::utils
::Backoff
;
12 // See mod.rs for details.
13 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
14 pub(super) use core
::sync
::atomic
::AtomicU64
as AtomicStamp
;
15 #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
16 pub(super) use core
::sync
::atomic
::AtomicUsize
as AtomicStamp
;
17 #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
18 pub(super) type Stamp
= usize;
19 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
20 pub(super) type Stamp
= u64;
22 // See mod.rs for details.
23 pub(super) type AtomicChunk
= AtomicStamp
;
24 pub(super) type Chunk
= Stamp
;
26 /// A simple stamped lock.
27 pub(super) struct SeqLock
{
28 /// The current state of the lock.
30 /// All bits except the least significant one hold the current stamp. When locked, the state
31 /// equals 1 and doesn't contain a valid stamp.
37 pub(super) const fn new() -> Self {
38 Self { state: AtomicStamp::new(0) }
41 /// If not locked, returns the current stamp.
43 /// This method should be called before optimistic reads.
45 pub(super) fn optimistic_read(&self) -> Option
<Stamp
> {
46 let state
= self.state
.load(Ordering
::Acquire
);
54 /// Returns `true` if the current stamp is equal to `stamp`.
56 /// This method should be called after optimistic reads to check whether they are valid. The
57 /// argument `stamp` should correspond to the one returned by method `optimistic_read`.
59 pub(super) fn validate_read(&self, stamp
: Stamp
) -> bool
{
60 atomic
::fence(Ordering
::Acquire
);
61 self.state
.load(Ordering
::Relaxed
) == stamp
64 /// Grabs the lock for writing.
66 pub(super) fn write(&self) -> SeqLockWriteGuard
<'_
> {
67 let mut backoff
= Backoff
::new();
69 let previous
= self.state
.swap(1, Ordering
::Acquire
);
72 atomic
::fence(Ordering
::Release
);
74 return SeqLockWriteGuard { lock: self, state: previous }
;
77 while self.state
.load(Ordering
::Relaxed
) == 1 {
84 /// An RAII guard that releases the lock and increments the stamp when dropped.
86 pub(super) struct SeqLockWriteGuard
<'a
> {
90 /// The stamp before locking.
94 impl SeqLockWriteGuard
<'_
> {
95 /// Releases the lock without incrementing the stamp.
97 pub(super) fn abort(self) {
98 // We specifically don't want to call drop(), since that's
99 // what increments the stamp.
100 let this
= ManuallyDrop
::new(self);
102 // Restore the stamp.
104 // Release ordering for synchronizing with `optimistic_read`.
105 this
.lock
.state
.store(this
.state
, Ordering
::Release
);
109 impl Drop
for SeqLockWriteGuard
<'_
> {
112 // Release the lock and increment the stamp.
114 // Release ordering for synchronizing with `optimistic_read`.
115 self.lock
.state
.store(self.state
.wrapping_add(2), Ordering
::Release
);
125 let lock
= SeqLock
::new();
126 let before
= lock
.optimistic_read().unwrap();
127 assert
!(lock
.validate_read(before
));
129 let _guard
= lock
.write();
131 assert
!(!lock
.validate_read(before
));
132 let after
= lock
.optimistic_read().unwrap();
133 assert_ne
!(before
, after
);
138 let lock
= SeqLock
::new();
139 let before
= lock
.optimistic_read().unwrap();
141 let guard
= lock
.write();
144 let after
= lock
.optimistic_read().unwrap();
145 assert_eq
!(before
, after
, "aborted write does not update the stamp");