1use core::mem;
2use core::sync::atomic::{self, AtomicUsize, Ordering};
34use crate::Backoff;
56/// A simple stamped lock.
7pub(crate) struct SeqLock {
8/// The current state of the lock.
9 ///
10 /// All bits except the least significant one hold the current stamp. When locked, the state
11 /// equals 1 and doesn't contain a valid stamp.
12state: AtomicUsize,
13}
1415impl SeqLock {
16pub(crate) const fn new() -> Self {
17Self {
18 state: AtomicUsize::new(0),
19 }
20 }
2122/// If not locked, returns the current stamp.
23 ///
24 /// This method should be called before optimistic reads.
25#[inline]
26pub(crate) fn optimistic_read(&self) -> Option<usize> {
27let state = self.state.load(Ordering::Acquire);
28if state == 1 {
29None
30} else {
31Some(state)
32 }
33 }
3435/// Returns `true` if the current stamp is equal to `stamp`.
36 ///
37 /// This method should be called after optimistic reads to check whether they are valid. The
38 /// argument `stamp` should correspond to the one returned by method `optimistic_read`.
39#[inline]
40pub(crate) fn validate_read(&self, stamp: usize) -> bool {
41 atomic::fence(Ordering::Acquire);
42self.state.load(Ordering::Relaxed) == stamp
43 }
4445/// Grabs the lock for writing.
46#[inline]
47pub(crate) fn write(&'static self) -> SeqLockWriteGuard {
48let backoff = Backoff::new();
49loop {
50let previous = self.state.swap(1, Ordering::Acquire);
5152if previous != 1 {
53 atomic::fence(Ordering::Release);
5455return SeqLockWriteGuard {
56 lock: self,
57 state: previous,
58 };
59 }
6061 backoff.snooze();
62 }
63 }
64}
6566/// An RAII guard that releases the lock and increments the stamp when dropped.
67pub(crate) struct SeqLockWriteGuard {
68/// The parent lock.
69lock: &'static SeqLock,
7071/// The stamp before locking.
72state: usize,
73}
7475impl SeqLockWriteGuard {
76/// Releases the lock without incrementing the stamp.
77#[inline]
78pub(crate) fn abort(self) {
79self.lock.state.store(self.state, Ordering::Release);
8081// We specifically don't want to call drop(), since that's
82 // what increments the stamp.
83mem::forget(self);
84 }
85}
8687impl Drop for SeqLockWriteGuard {
88#[inline]
89fn drop(&mut self) {
90// Release the lock and increment the stamp.
91self.lock
92 .state
93 .store(self.state.wrapping_add(2), Ordering::Release);
94 }
95}
9697#[cfg(test)]
98mod tests {
99use super::SeqLock;
100101#[test]
102fn test_abort() {
103static LK: SeqLock = SeqLock::new();
104let before = LK.optimistic_read().unwrap();
105 {
106let guard = LK.write();
107 guard.abort();
108 }
109let after = LK.optimistic_read().unwrap();
110assert_eq!(before, after, "aborted write does not update the stamp");
111 }
112}