starnix_sync/
rw_seq_lock.rs1use crate::{LockDepGuard, LockDepMutex, LockLevel, ThreadAffinity, ThreadAffinityGuard};
6use std::fmt;
7use std::ops::{Deref, DerefMut};
8use std::sync::atomic::{AtomicUsize, Ordering};
9
10pub struct RwSeqLock<L> {
18 seq: AtomicUsize,
21 lock: L,
23 affinity: ThreadAffinity,
26}
27
28pub struct RwSeqLockGuard<'a, G> {
33 seq: &'a AtomicUsize,
34 _affinity: ThreadAffinityGuard<'a>,
35 guard: G,
36}
37
38impl<L> RwSeqLock<L> {
39 pub const fn new(lock: L) -> Self {
41 Self { seq: AtomicUsize::new(0), lock, affinity: ThreadAffinity::new() }
42 }
43
44 pub fn read_seq<R, F: Fn() -> R>(&self, f: F) -> R {
50 self.affinity.assert_not_attached();
51 loop {
52 let seq1 = self.seq.load(Ordering::Acquire);
53 if seq1 % 2 != 0 {
54 std::hint::spin_loop();
56 continue;
57 }
58
59 let result = f();
60
61 std::sync::atomic::fence(Ordering::Acquire);
66
67 let seq2 = self.seq.load(Ordering::Acquire);
68 if seq1 == seq2 {
69 return result;
71 }
72 }
73 }
74}
75
76impl<T, L: LockLevel> RwSeqLock<LockDepMutex<T, L>> {
77 pub fn lock(&self) -> RwSeqLockGuard<'_, LockDepGuard<'_, T>> {
83 let guard = self.lock.lock();
84 let prev = self.seq.fetch_add(1, Ordering::Release);
87 debug_assert!(prev % 2 == 0, "RwSeqLock sequence should be even before locking");
88 RwSeqLockGuard { seq: &self.seq, _affinity: self.affinity.attach(), guard }
89 }
90}
91
92impl<'a, G> Drop for RwSeqLockGuard<'a, G> {
93 fn drop(&mut self) {
94 let prev = self.seq.fetch_add(1, Ordering::Release);
97 debug_assert!(prev % 2 != 0, "RwSeqLock sequence should be odd before unlocking");
98 }
99}
100
101impl<'a, G: Deref> Deref for RwSeqLockGuard<'a, G> {
102 type Target = G::Target;
103 fn deref(&self) -> &Self::Target {
104 &self.guard
105 }
106}
107
108impl<'a, G: DerefMut> DerefMut for RwSeqLockGuard<'a, G> {
109 fn deref_mut(&mut self) -> &mut Self::Target {
110 &mut self.guard
111 }
112}
113
114impl<L: Default> Default for RwSeqLock<L> {
115 fn default() -> Self {
116 Self::new(L::default())
117 }
118}
119
120impl<L: fmt::Debug> fmt::Debug for RwSeqLock<L> {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.debug_struct("RwSeqLock").field("lock", &self.lock).finish()
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use crate::{Unlocked, lock_ordering};
130 use std::sync::atomic::{AtomicU32, Ordering};
131
132 lock_ordering! {
133 Unlocked => TestLevel,
134 }
135
136 #[test]
137 fn test_rw_seq_lock() {
138 let lock: RwSeqLock<LockDepMutex<u32, TestLevel>> = RwSeqLock::new(0.into());
139 let data = AtomicU32::new(0);
140
141 let read_val = lock.read_seq(|| data.load(Ordering::Relaxed));
142 assert_eq!(read_val, 0);
143
144 {
145 let mut guard = lock.lock();
146 *guard = 1;
147 data.store(1, Ordering::Relaxed);
148 }
149
150 let read_val2 = lock.read_seq(|| data.load(Ordering::Relaxed));
151 assert_eq!(read_val2, 1);
152 }
153
154 #[test]
155 fn test_rw_seq_lock_concurrent() {
156 use std::sync::Arc;
157 use std::thread;
158
159 struct TestData {
160 lock: RwSeqLock<LockDepMutex<(), TestLevel>>,
161 val1: AtomicU32,
162 val2: AtomicU32,
163 }
164
165 let data = Arc::new(TestData {
166 lock: RwSeqLock::new(Default::default()),
167 val1: AtomicU32::new(0),
168 val2: AtomicU32::new(0),
169 });
170
171 let mut handles = vec![];
172
173 for i in 0..4 {
175 let data = data.clone();
176 handles.push(thread::spawn(move || {
177 for j in 0..1000 {
178 let val = i * 1000 + j;
179 let _guard = data.lock.lock();
180 data.val1.store(val, Ordering::Relaxed);
181 thread::yield_now();
182 data.val2.store(val, Ordering::Relaxed);
183 }
184 }));
185 }
186
187 for _ in 0..4 {
189 let data = data.clone();
190 handles.push(thread::spawn(move || {
191 for _ in 0..1000 {
192 let (v1, v2) = data.lock.read_seq(|| {
193 let v1 = data.val1.load(Ordering::Relaxed);
194 thread::yield_now();
195 let v2 = data.val2.load(Ordering::Relaxed);
196 (v1, v2)
197 });
198 assert_eq!(v1, v2);
199 }
200 }));
201 }
202
203 for handle in handles {
204 handle.join().unwrap();
205 }
206 }
207}