use core::{mem::ManuallyDrop, sync::atomic::Ordering};
use super::utils::Backoff;
#[cfg(portable_atomic_unsafe_assume_privileged)]
use crate::imp::interrupt::arch as interrupt;
const LOCKED: State = 1;
impl SeqLock {
#[inline]
pub(super) fn write(&self) -> SeqLockWriteGuard<'_> {
#[cfg(portable_atomic_unsafe_assume_privileged)]
let interrupt_state = interrupt::disable();
let mut backoff = Backoff::new();
loop {
let previous = self.state.swap(LOCKED, Ordering::Acquire);
if previous != LOCKED {
crate::fence(Ordering::Release);
return SeqLockWriteGuard {
lock: self,
state: previous,
#[cfg(portable_atomic_unsafe_assume_privileged)]
interrupt_state,
};
}
while self.state.load(Ordering::Relaxed) == LOCKED {
backoff.snooze();
}
}
}
}
#[must_use]
pub(crate) struct SeqLockWriteGuard<'a> {
lock: &'a SeqLock,
state: State,
#[cfg(portable_atomic_unsafe_assume_privileged)]
interrupt_state: interrupt::State,
}
impl SeqLockWriteGuard<'_> {
#[inline]
pub(crate) fn abort(self) {
let this = ManuallyDrop::new(self);
this.lock.state.store(this.state, Ordering::Release);
#[cfg(portable_atomic_unsafe_assume_privileged)]
unsafe {
interrupt::restore(this.interrupt_state);
}
}
}
impl Drop for SeqLockWriteGuard<'_> {
#[inline]
fn drop(&mut self) {
let state = self.next_stamp();
self.lock.state.store(state, Ordering::Release);
#[cfg(portable_atomic_unsafe_assume_privileged)]
unsafe {
interrupt::restore(self.interrupt_state);
}
}
}
#[cfg(test)]
#[allow(clippy::items_after_test_module)]
mod tests {
use super::SeqLock;
#[test]
fn smoke() {
let lock = SeqLock::new();
let before = lock.optimistic_read().unwrap();
assert!(lock.validate_read(before));
{
let _guard = lock.write();
assert!(lock.optimistic_read().is_none());
}
assert!(!lock.validate_read(before));
let after = lock.optimistic_read().unwrap();
assert_ne!(before, after);
}
#[test]
fn test_wrap() {
let mut lock = SeqLock::new();
let zero = lock.optimistic_read().unwrap();
*lock.state.get_mut() = 0xFFFF_FFFE; let before = lock.optimistic_read().unwrap();
assert!(lock.validate_read(before));
{
let _guard = lock.write();
assert!(lock.optimistic_read().is_none());
}
assert!(!lock.validate_read(before));
let after = lock.optimistic_read().unwrap();
assert_ne!(before, after);
assert_ne!(after, zero);
#[allow(clippy::identity_op)]
{
assert_eq!(*lock.state.get_mut() & 0xFFFF_FFFF, 0);
}
}
#[test]
fn test_abort() {
let lock = SeqLock::new();
let before = lock.optimistic_read().unwrap();
{
let guard = lock.write();
assert!(lock.optimistic_read().is_none());
guard.abort();
}
let after = lock.optimistic_read().unwrap();
assert_eq!(before, after, "aborted write does not update the stamp");
}
}