1 // Adapted from https://github.com/crossbeam-rs/crossbeam/blob/crossbeam-utils-0.8.7/crossbeam-utils/src/atomic/seq_lock.rs.
5 sync
::atomic
::{self, Ordering}
,
8 use super::utils
::Backoff
;
10 // See mod.rs for details.
11 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
12 pub(super) use core
::sync
::atomic
::AtomicU64
as AtomicStamp
;
13 #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
14 pub(super) use core
::sync
::atomic
::AtomicUsize
as AtomicStamp
;
15 #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
16 pub(super) type Stamp
= usize;
17 #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
18 pub(super) type Stamp
= u64;
20 // See mod.rs for details.
21 pub(super) type AtomicChunk
= AtomicStamp
;
22 pub(super) type Chunk
= Stamp
;
24 /// A simple stamped lock.
25 pub(super) struct SeqLock
{
26 /// The current state of the lock.
28 /// All bits except the least significant one hold the current stamp. When locked, the state
29 /// equals 1 and doesn't contain a valid stamp.
35 pub(super) const fn new() -> Self {
36 Self { state: AtomicStamp::new(0) }
39 /// If not locked, returns the current stamp.
41 /// This method should be called before optimistic reads.
43 pub(super) fn optimistic_read(&self) -> Option
<Stamp
> {
44 let state
= self.state
.load(Ordering
::Acquire
);
52 /// Returns `true` if the current stamp is equal to `stamp`.
54 /// This method should be called after optimistic reads to check whether they are valid. The
55 /// argument `stamp` should correspond to the one returned by method `optimistic_read`.
57 pub(super) fn validate_read(&self, stamp
: Stamp
) -> bool
{
58 atomic
::fence(Ordering
::Acquire
);
59 self.state
.load(Ordering
::Relaxed
) == stamp
62 /// Grabs the lock for writing.
64 pub(super) fn write(&self) -> SeqLockWriteGuard
<'_
> {
65 let mut backoff
= Backoff
::new();
67 let previous
= self.state
.swap(1, Ordering
::Acquire
);
70 atomic
::fence(Ordering
::Release
);
72 return SeqLockWriteGuard { lock: self, state: previous }
;
75 while self.state
.load(Ordering
::Relaxed
) == 1 {
82 /// An RAII guard that releases the lock and increments the stamp when dropped.
84 pub(super) struct SeqLockWriteGuard
<'a
> {
88 /// The stamp before locking.
92 impl SeqLockWriteGuard
<'_
> {
93 /// Releases the lock without incrementing the stamp.
95 pub(super) fn abort(self) {
96 // We specifically don't want to call drop(), since that's
97 // what increments the stamp.
98 let this
= ManuallyDrop
::new(self);
100 // Restore the stamp.
102 // Release ordering for synchronizing with `optimistic_read`.
103 this
.lock
.state
.store(this
.state
, Ordering
::Release
);
107 impl Drop
for SeqLockWriteGuard
<'_
> {
110 // Release the lock and increment the stamp.
112 // Release ordering for synchronizing with `optimistic_read`.
113 self.lock
.state
.store(self.state
.wrapping_add(2), Ordering
::Release
);
123 let lock
= SeqLock
::new();
124 let before
= lock
.optimistic_read().unwrap();
125 assert
!(lock
.validate_read(before
));
127 let _guard
= lock
.write();
129 assert
!(!lock
.validate_read(before
));
130 let after
= lock
.optimistic_read().unwrap();
131 assert_ne
!(before
, after
);
136 let lock
= SeqLock
::new();
137 let before
= lock
.optimistic_read().unwrap();
139 let guard
= lock
.write();
142 let after
= lock
.optimistic_read().unwrap();
143 assert_eq
!(before
, after
, "aborted write does not update the stamp");