crossbeam-utils/src/atomic/seq_lock.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use core::mem; |
2 | | use core::sync::atomic::{self, AtomicUsize, Ordering}; |
3 | | |
4 | | use crate::Backoff; |
5 | | |
6 | | /// A simple stamped lock. |
7 | | pub(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. |
12 | | state: AtomicUsize, |
13 | | } |
14 | | |
15 | | impl SeqLock { |
16 | | pub(crate) const fn new() -> Self { |
17 | | Self { |
18 | | state: AtomicUsize::new(0), |
19 | | } |
20 | | } |
21 | | |
22 | | /// If not locked, returns the current stamp. |
23 | | /// |
24 | | /// This method should be called before optimistic reads. |
25 | | #[inline] |
26 | 3 | pub(crate) fn optimistic_read(&self) -> Option<usize> { |
27 | 3 | let state = self.state.load(Ordering::Acquire); |
28 | 3 | if state == 1 { |
29 | 0 | None |
30 | | } else { |
31 | 3 | Some(state) |
32 | | } |
33 | 3 | } Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Line | Count | Source | 26 | 2 | pub(crate) fn optimistic_read(&self) -> Option<usize> { | 27 | 2 | let state = self.state.load(Ordering::Acquire); | 28 | 2 | if state == 1 { | 29 | 0 | None | 30 | | } else { | 31 | 2 | Some(state) | 32 | | } | 33 | 2 | } |
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Line | Count | Source | 26 | 1 | pub(crate) fn optimistic_read(&self) -> Option<usize> { | 27 | 1 | let state = self.state.load(Ordering::Acquire); | 28 | 1 | if state == 1 { | 29 | 0 | None | 30 | | } else { | 31 | 1 | Some(state) | 32 | | } | 33 | 1 | } |
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::optimistic_read |
34 | | |
35 | | /// 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] |
40 | 1 | pub(crate) fn validate_read(&self, stamp: usize) -> bool { |
41 | 1 | atomic::fence(Ordering::Acquire); |
42 | 1 | self.state.load(Ordering::Relaxed) == stamp |
43 | 1 | } |
44 | | |
45 | | /// Grabs the lock for writing. |
46 | | #[inline] |
47 | 2 | pub(crate) fn write(&'static self) -> SeqLockWriteGuard { |
48 | 2 | let backoff = Backoff::new(); |
49 | | loop { |
50 | 2 | let previous = self.state.swap(1, Ordering::Acquire); |
51 | 2 | |
52 | 2 | if previous != 1 { |
53 | 2 | atomic::fence(Ordering::Release); |
54 | 2 | |
55 | 2 | return SeqLockWriteGuard { |
56 | 2 | lock: self, |
57 | 2 | state: previous, |
58 | 2 | }; |
59 | 0 | } |
60 | 0 |
|
61 | 0 | backoff.snooze(); |
62 | | } |
63 | 2 | } Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write <crossbeam_utils::atomic::seq_lock::SeqLock>::write Line | Count | Source | 47 | 1 | pub(crate) fn write(&'static self) -> SeqLockWriteGuard { | 48 | 1 | let backoff = Backoff::new(); | 49 | | loop { | 50 | 1 | let previous = self.state.swap(1, Ordering::Acquire); | 51 | 1 | | 52 | 1 | if previous != 1 { | 53 | 1 | atomic::fence(Ordering::Release); | 54 | 1 | | 55 | 1 | return SeqLockWriteGuard { | 56 | 1 | lock: self, | 57 | 1 | state: previous, | 58 | 1 | }; | 59 | 0 | } | 60 | 0 |
| 61 | 0 | backoff.snooze(); | 62 | | } | 63 | 1 | } |
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write <crossbeam_utils::atomic::seq_lock::SeqLock>::write Line | Count | Source | 47 | 1 | pub(crate) fn write(&'static self) -> SeqLockWriteGuard { | 48 | 1 | let backoff = Backoff::new(); | 49 | | loop { | 50 | 1 | let previous = self.state.swap(1, Ordering::Acquire); | 51 | 1 | | 52 | 1 | if previous != 1 { | 53 | 1 | atomic::fence(Ordering::Release); | 54 | 1 | | 55 | 1 | return SeqLockWriteGuard { | 56 | 1 | lock: self, | 57 | 1 | state: previous, | 58 | 1 | }; | 59 | 0 | } | 60 | 0 |
| 61 | 0 | backoff.snooze(); | 62 | | } | 63 | 1 | } |
Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write Unexecuted instantiation: <crossbeam_utils::atomic::seq_lock::SeqLock>::write |
64 | | } |
65 | | |
66 | | /// An RAII guard that releases the lock and increments the stamp when dropped. |
67 | | pub(crate) struct SeqLockWriteGuard { |
68 | | /// The parent lock. |
69 | | lock: &'static SeqLock, |
70 | | |
71 | | /// The stamp before locking. |
72 | | state: usize, |
73 | | } |
74 | | |
75 | | impl SeqLockWriteGuard { |
76 | | /// Releases the lock without incrementing the stamp. |
77 | | #[inline] |
78 | 1 | pub(crate) fn abort(self) { |
79 | 1 | self.lock.state.store(self.state, Ordering::Release); |
80 | 1 | |
81 | 1 | // We specifically don't want to call drop(), since that's |
82 | 1 | // what increments the stamp. |
83 | 1 | mem::forget(self); |
84 | 1 | } |
85 | | } |
86 | | |
87 | | impl Drop for SeqLockWriteGuard { |
88 | | #[inline] |
89 | 1 | fn drop(&mut self) { |
90 | 1 | // Release the lock and increment the stamp. |
91 | 1 | self.lock |
92 | 1 | .state |
93 | 1 | .store(self.state.wrapping_add(2), Ordering::Release); |
94 | 1 | } |
95 | | } |
96 | | |
97 | | #[cfg(test)] |
98 | | mod tests { |
99 | | use super::SeqLock; |
100 | | |
101 | | #[test] |
102 | 1 | fn test_abort() { crossbeam_utils::atomic::seq_lock::tests::test_abort::{closure#0} Line | Count | Source | 102 | 1 | fn test_abort() { |
|
103 | | static LK: SeqLock = SeqLock::new(); |
104 | 1 | let before = LK.optimistic_read().unwrap(); |
105 | 1 | { |
106 | 1 | let guard = LK.write(); |
107 | 1 | guard.abort(); |
108 | 1 | } |
109 | 1 | let after = LK.optimistic_read().unwrap(); |
110 | 0 | assert_eq!(before, after, "aborted write does not update the stamp"); |
111 | 1 | } crossbeam_utils::atomic::seq_lock::tests::test_abort Line | Count | Source | 102 | 1 | fn test_abort() { | 103 | | static LK: SeqLock = SeqLock::new(); | 104 | 1 | let before = LK.optimistic_read().unwrap(); | 105 | 1 | { | 106 | 1 | let guard = LK.write(); | 107 | 1 | guard.abort(); | 108 | 1 | } | 109 | 1 | let after = LK.optimistic_read().unwrap(); | 110 | 0 | assert_eq!(before, after, "aborted write does not update the stamp"); | 111 | 1 | } |
|
112 | | } |