1use core::marker::PhantomPinned;
6use core::sync::atomic::{AtomicU32, Ordering, fence};
7
8use crate::common::{SYNC_OPT_ACQ_REL_OPS, SYNC_OPT_FENCE, SyncOpt};
9
10#[cfg(feature = "kernel")]
11unsafe extern "C" {
12 fn cpp_arch_yield();
13}
14
15#[inline(always)]
17fn arch_yield() {
18 #[cfg(feature = "kernel")]
19 unsafe {
22 cpp_arch_yield()
23 }
24}
25
26pub type SequenceNumber = u32;
27
28const SEQ_NUM_WRITE_IN_FLIGHT: SequenceNumber = 0x1;
29
30const fn write_in_flight(seq_num: SequenceNumber) -> bool {
31 (seq_num & SEQ_NUM_WRITE_IN_FLIGHT) != 0
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub struct ReadTransactionToken(SequenceNumber);
36
37impl ReadTransactionToken {
38 pub const fn new() -> Self {
39 Self(1)
40 }
41
42 pub const fn seq_num(&self) -> SequenceNumber {
43 self.0
44 }
45}
46
47impl Default for ReadTransactionToken {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53#[must_use = "the write cycle ends as soon as the guard is dropped"]
56#[derive(Debug)]
57pub struct WriteGuard<'a, const SYNC_OPT: u8 = SYNC_OPT_FENCE> {
58 lock: &'a SeqLock<SYNC_OPT>,
59}
60
61impl<const SYNC_OPT: u8> Drop for WriteGuard<'_, SYNC_OPT> {
62 #[inline]
63 fn drop(&mut self) {
64 self.lock.release();
65 }
66}
67
68#[repr(transparent)]
69pub struct SeqLock<const SYNC_OPT: u8 = SYNC_OPT_FENCE> {
70 seq_num: AtomicU32,
71 _pin: PhantomPinned,
73}
74
75impl<const SYNC_OPT: u8> SeqLock<SYNC_OPT> {
76 pub const SYNC_OPT: SyncOpt = SyncOpt::from_u8(SYNC_OPT);
78
79 pub const COPY_WRAPPER_SYNC_OPT: SyncOpt =
81 if SYNC_OPT == SYNC_OPT_ACQ_REL_OPS { SyncOpt::AcqRelOps } else { SyncOpt::None };
82
83 const VALID_SYNC_OPT: () = assert!(
84 (SYNC_OPT == SYNC_OPT_ACQ_REL_OPS) || (SYNC_OPT == SYNC_OPT_FENCE),
85 "The synchronization options chosen for a SeqLock must be either Acquire/Release, or Fence"
86 );
87
88 pub const fn new() -> Self {
89 let () = Self::VALID_SYNC_OPT;
90 Self { seq_num: AtomicU32::new(0), _pin: PhantomPinned }
91 }
92
93 #[cfg(test)]
95 #[inline]
96 #[must_use]
97 pub fn seq_num(&self) -> SequenceNumber {
98 self.seq_num_with_order(Ordering::Relaxed)
99 }
100
101 #[cfg(test)]
103 #[inline]
104 #[must_use]
105 pub fn seq_num_with_order(&self, order: Ordering) -> SequenceNumber {
106 self.seq_num.load(order)
107 }
108
109 #[inline]
112 #[must_use]
113 pub fn begin_read_transaction(&self) -> ReadTransactionToken {
114 loop {
115 let seq_num = self.seq_num.load(Ordering::Acquire);
116 if !write_in_flight(seq_num) {
117 return ReadTransactionToken(seq_num);
118 }
119 arch_yield();
120 }
121 }
122
123 #[inline]
125 #[must_use]
126 pub fn try_begin_read_transaction(&self) -> Option<ReadTransactionToken> {
127 let seq_num = self.seq_num.load(Ordering::Acquire);
128 if write_in_flight(seq_num) {
129 return None;
130 }
131 Some(ReadTransactionToken(seq_num))
132 }
133
134 #[inline]
135 #[must_use = "a read transaction which was not validated may have observed a torn payload"]
136 pub fn end_read_transaction(&self, token: ReadTransactionToken) -> bool {
137 if write_in_flight(token.0) {
138 return false;
139 }
140
141 if Self::SYNC_OPT == SyncOpt::Fence {
144 fence(Ordering::Acquire);
145 }
146
147 self.seq_num.load(Ordering::Relaxed) == token.0
148 }
149
150 #[inline]
152 pub fn read_transaction<T>(&self, mut read_payload: impl FnMut() -> T) -> T {
153 loop {
154 let token = self.begin_read_transaction();
155 let payload = read_payload();
156 if self.end_read_transaction(token) {
157 return payload;
158 }
159 }
160 }
161
162 #[inline]
165 pub fn acquire(&self) -> WriteGuard<'_, SYNC_OPT> {
166 loop {
167 let mut expected = self.seq_num.load(Ordering::Relaxed);
169 while write_in_flight(expected) {
170 arch_yield();
171 expected = self.seq_num.load(Ordering::Relaxed);
172 }
173
174 if self
177 .seq_num
178 .compare_exchange(
179 expected,
180 expected.wrapping_add(1),
181 Ordering::Acquire,
182 Ordering::Relaxed,
183 )
184 .is_ok()
185 {
186 if Self::SYNC_OPT == SyncOpt::Fence {
189 fence(Ordering::Release);
190 }
191 return WriteGuard { lock: self };
192 }
193 }
194 }
195
196 #[inline]
198 #[must_use]
199 pub fn try_acquire(&self) -> Option<WriteGuard<'_, SYNC_OPT>> {
200 let expected = self.seq_num.load(Ordering::Relaxed);
202 if write_in_flight(expected) {
203 return None;
204 }
205
206 if self
209 .seq_num
210 .compare_exchange(
211 expected,
212 expected.wrapping_add(1),
213 Ordering::Acquire,
214 Ordering::Relaxed,
215 )
216 .is_ok()
217 {
218 if Self::SYNC_OPT == SyncOpt::Fence {
221 fence(Ordering::Release);
222 }
223 Some(WriteGuard { lock: self })
224 } else {
225 None
226 }
227 }
228
229 #[inline]
230 pub(crate) fn release(&self) {
231 let before = self.seq_num.fetch_add(1, Ordering::Release);
232 debug_assert!(
233 write_in_flight(before),
234 "SeqLock was not held when the write guard was dropped"
235 );
236 }
237}
238
239impl<const SYNC_OPT: u8> Default for SeqLock<SYNC_OPT> {
240 fn default() -> Self {
241 Self::new()
242 }
243}
244
245impl<const SYNC_OPT: u8> core::fmt::Debug for SeqLock<SYNC_OPT> {
246 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
247 f.debug_struct("SeqLock")
248 .field("sync_opt", &Self::SYNC_OPT)
249 .field("seq_num", &self.seq_num.load(Ordering::Relaxed))
250 .finish()
251 }
252}
253
254zr::static_assert!(core::mem::size_of::<SeqLock>() == 4);
256zr::static_assert!(core::mem::align_of::<SeqLock>() == 4);
257zr::static_assert!(core::mem::size_of::<SeqLock<SYNC_OPT_ACQ_REL_OPS>>() == 4);
258zr::static_assert!(core::mem::align_of::<SeqLock<SYNC_OPT_ACQ_REL_OPS>>() == 4);
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 type AcqRelSeqLock = SeqLock<SYNC_OPT_ACQ_REL_OPS>;
265
266 #[test]
267 fn test_initial_state() {
268 let lock: SeqLock = SeqLock::new();
269 assert_eq!(lock.seq_num(), 0);
270 assert_eq!(SeqLock::<SYNC_OPT_FENCE>::default().seq_num(), 0);
271 assert_eq!(AcqRelSeqLock::new().seq_num(), 0);
272 }
273
274 #[test]
275 fn test_sync_opt() {
276 assert_eq!(SeqLock::<SYNC_OPT_FENCE>::SYNC_OPT, SyncOpt::Fence);
277 assert_eq!(SeqLock::<SYNC_OPT_FENCE>::COPY_WRAPPER_SYNC_OPT, SyncOpt::None);
278 assert_eq!(AcqRelSeqLock::SYNC_OPT, SyncOpt::AcqRelOps);
279 assert_eq!(AcqRelSeqLock::COPY_WRAPPER_SYNC_OPT, SyncOpt::AcqRelOps);
280 }
281
282 #[test]
283 fn test_uncontested_read() {
284 let lock: SeqLock = SeqLock::new();
285
286 let token1 = lock.begin_read_transaction();
288 assert!(lock.end_read_transaction(token1));
289
290 let token2 = lock.begin_read_transaction();
293 assert!(lock.end_read_transaction(token2));
294 assert_eq!(token1.seq_num(), token2.seq_num());
295
296 drop(lock.acquire());
299 let token3 = lock.begin_read_transaction();
300 assert!(lock.end_read_transaction(token3));
301 assert_ne!(token1.seq_num(), token3.seq_num());
302 }
303
304 #[test]
305 fn test_contested_read() {
306 let lock: SeqLock = SeqLock::new();
307
308 let token = lock.begin_read_transaction();
311
312 let guard = lock.acquire();
315 assert_eq!(lock.seq_num(), 1);
316 assert!(!lock.end_read_transaction(token));
317
318 drop(guard);
319 assert_eq!(lock.seq_num(), 2);
320 assert!(!lock.end_read_transaction(token));
321 }
322
323 #[test]
324 fn test_read_non_blocking() {
325 let lock: SeqLock = SeqLock::new();
326
327 let token = lock.try_begin_read_transaction().expect("no write cycle is in flight");
330 assert!(lock.end_read_transaction(token));
331
332 let guard = lock.acquire();
335 assert!(lock.try_begin_read_transaction().is_none());
336
337 drop(guard);
338 let token = lock.try_begin_read_transaction().expect("no write cycle is in flight");
339 assert!(lock.end_read_transaction(token));
340 }
341
342 #[test]
343 fn test_invalid_token() {
344 let lock: SeqLock = SeqLock::new();
345 let token = ReadTransactionToken::new();
346 assert!(write_in_flight(token.seq_num()));
347 assert!(!lock.end_read_transaction(token));
348 }
349
350 #[test]
351 fn test_read_transaction_helper() {
352 use core::sync::atomic::AtomicU64;
353
354 let lock: SeqLock = SeqLock::new();
355 let payload = AtomicU64::new(0);
356
357 {
358 let _guard = lock.acquire();
359 payload.store(42, Ordering::Relaxed);
360 }
361
362 assert_eq!(lock.read_transaction(|| payload.load(Ordering::Relaxed)), 42);
363 }
364
365 #[test]
366 fn test_uncontested_write() {
367 let lock: SeqLock = SeqLock::new();
368
369 const TRIALS: u32 = 1000;
373 for i in 0..TRIALS {
374 assert_eq!(lock.seq_num(), i * 4);
375
376 drop(lock.acquire());
377 assert_eq!(lock.seq_num(), (i * 4) + 2);
378
379 let guard = lock.try_acquire().expect("uncontested try_acquire must succeed");
380 assert_eq!(lock.seq_num(), (i * 4) + 3);
381 drop(guard);
382 assert_eq!(lock.seq_num(), (i * 4) + 4);
383 }
384 }
385
386 #[test]
387 fn test_try_acquire_is_contested() {
388 let lock: SeqLock = SeqLock::new();
389
390 let guard = lock.try_acquire().expect("uncontested try_acquire must succeed");
391 assert_eq!(lock.seq_num(), 1);
392
393 assert!(lock.try_acquire().is_none());
394 assert_eq!(lock.seq_num(), 1);
395
396 drop(guard);
397 assert_eq!(lock.seq_num(), 2);
398 }
399
400 #[test]
401 fn test_contested_write() {
402 use core::sync::atomic::AtomicU32;
403 use std::sync::Arc;
404 use std::thread;
405 use std::time::Duration;
406
407 const NOT_STARTED: u32 = 0;
419 const ATTEMPTING_ACQUIRE: u32 = 1;
420 const ACQUIRE_SUCCEEDED: u32 = 2;
421
422 let lock = Arc::new(SeqLock::<SYNC_OPT_FENCE>::new());
423 let state = Arc::new(AtomicU32::new(NOT_STARTED));
424
425 let guard = lock.acquire();
426 assert!(lock.try_acquire().is_none());
427
428 let acquire_thread = {
429 let lock = lock.clone();
430 let state = state.clone();
431 thread::spawn(move || {
432 state.store(ATTEMPTING_ACQUIRE, Ordering::SeqCst);
433 let guard = lock.acquire();
434 state.store(ACQUIRE_SUCCEEDED, Ordering::SeqCst);
435 drop(guard);
436 })
437 };
438
439 while state.load(Ordering::SeqCst) != ATTEMPTING_ACQUIRE {
441 arch_yield();
443 }
444
445 thread::sleep(Duration::from_millis(500));
448 assert_eq!(state.load(Ordering::SeqCst), ATTEMPTING_ACQUIRE);
449
450 drop(guard);
453 while state.load(Ordering::SeqCst) != ACQUIRE_SUCCEEDED {
454 arch_yield();
456 }
457
458 drop(lock.acquire());
462
463 acquire_thread.join().unwrap();
466 }
467
468 #[test]
469 fn test_concurrent_readers_writers() {
470 use core::sync::atomic::AtomicU64;
471 use std::sync::Arc;
472 use std::thread;
473
474 struct Payload {
476 lock: SeqLock,
477 a: AtomicU64,
478 b: AtomicU64,
479 }
480
481 const ITERATIONS: u64 = 5000;
482
483 let payload =
484 Arc::new(Payload { lock: SeqLock::new(), a: AtomicU64::new(0), b: AtomicU64::new(!0) });
485
486 let writer = {
487 let payload = payload.clone();
488 thread::spawn(move || {
489 for i in 1..=ITERATIONS {
490 let _guard = payload.lock.acquire();
491 payload.a.store(i, Ordering::Relaxed);
492 payload.b.store(!i, Ordering::Relaxed);
493 }
494 })
495 };
496
497 let mut successful_transactions: u64 = 0;
498 let mut observed: u64 = 0;
499 while observed < ITERATIONS {
500 let token = payload.lock.begin_read_transaction();
501 let a = payload.a.load(Ordering::Relaxed);
502 let b = payload.b.load(Ordering::Relaxed);
503 if payload.lock.end_read_transaction(token) {
504 assert_eq!(a, !b, "observed a torn payload in a successful transaction");
505 successful_transactions += 1;
506 observed = observed.max(a);
507 }
508 }
509
510 let (a, b) = payload.lock.read_transaction(|| {
511 (payload.a.load(Ordering::Relaxed), payload.b.load(Ordering::Relaxed))
512 });
513 assert_eq!(a, !b);
514
515 writer.join().unwrap();
516 assert!(successful_transactions > 0, "no read transaction ever succeeded");
517 }
518}