mod base;
#[cfg(feature = "lockdep")]
pub(crate) mod lockdep;
#[cfg(feature = "lock-api")]
mod raw;
pub(crate) mod rwlock;
use core::{fmt, ptr};
#[cfg(feature = "lock-api")]
pub use self::raw::*;
use self::{
base::{BaseSpinLock, BaseSpinLockGuard},
rwlock::{BaseSpinRwLock, BaseSpinRwLockReadGuard, BaseSpinRwLockWriteGuard},
};
use crate::sync::context::{PreemptIrqSaveState, PreemptState, RawState};
#[repr(transparent)]
pub struct SpinLock<T: ?Sized>(BaseSpinLock<RawState, T>);
pub type SpinLockGuard<'a, T> = BaseSpinLockGuard<'a, PreemptState, T>;
pub type SpinLockIrqSaveGuard<'a, T> = BaseSpinLockGuard<'a, PreemptIrqSaveState, T>;
pub type RawSpinLockGuard<'a, T> = BaseSpinLockGuard<'a, RawState, T>;
impl<T> SpinLock<T> {
#[inline(always)]
#[track_caller]
pub const fn new(data: T) -> Self {
Self(BaseSpinLock::new(data))
}
#[inline(always)]
pub fn into_inner(self) -> T {
self.0.into_inner()
}
}
impl<T: ?Sized> SpinLock<T> {
#[inline(always)]
fn with_state<G: crate::sync::context::GuardState>(&self) -> &BaseSpinLock<G, T> {
unsafe { &*(ptr::from_ref(&self.0) as *const BaseSpinLock<G, T>) }
}
#[inline(always)]
fn with_state_mut<G: crate::sync::context::GuardState>(&mut self) -> &mut BaseSpinLock<G, T> {
unsafe { &mut *(ptr::from_mut(&mut self.0) as *mut BaseSpinLock<G, T>) }
}
#[inline(always)]
#[track_caller]
pub fn lock(&self) -> SpinLockGuard<'_, T> {
self.with_state::<PreemptState>().lock()
}
#[inline(always)]
#[track_caller]
pub fn lock_nested(&self, subclass: u32) -> SpinLockGuard<'_, T> {
self.with_state::<PreemptState>().lock_nested(subclass)
}
#[inline(always)]
#[track_caller]
pub fn try_lock(&self) -> Option<SpinLockGuard<'_, T>> {
self.with_state::<PreemptState>().try_lock()
}
#[inline(always)]
#[track_caller]
pub fn lock_irqsave(&self) -> SpinLockIrqSaveGuard<'_, T> {
self.with_state::<PreemptIrqSaveState>().lock()
}
#[inline(always)]
#[track_caller]
pub fn lock_irqsave_nested(&self, subclass: u32) -> SpinLockIrqSaveGuard<'_, T> {
self.with_state::<PreemptIrqSaveState>()
.lock_nested(subclass)
}
#[inline(always)]
#[track_caller]
pub fn try_lock_irqsave(&self) -> Option<SpinLockIrqSaveGuard<'_, T>> {
self.with_state::<PreemptIrqSaveState>().try_lock()
}
#[inline(always)]
#[track_caller]
pub unsafe fn lock_raw(&self) -> RawSpinLockGuard<'_, T> {
self.with_state::<RawState>().lock()
}
#[inline(always)]
#[track_caller]
pub unsafe fn try_lock_raw(&self) -> Option<RawSpinLockGuard<'_, T>> {
self.with_state::<RawState>().try_lock()
}
#[inline(always)]
pub fn is_locked(&self) -> bool {
self.0.is_locked()
}
#[inline(always)]
pub fn get_mut(&mut self) -> &mut T {
self.with_state_mut::<RawState>().get_mut()
}
#[doc(hidden)]
#[inline(always)]
pub unsafe fn force_unlock(&self) {
unsafe { self.with_state::<PreemptState>().force_unlock() };
}
}
impl<T: Default> Default for SpinLock<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: fmt::Debug> fmt::Debug for SpinLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_lock() {
Some(guard) => f.debug_struct("SpinLock").field("data", &&*guard).finish(),
None => f
.debug_struct("SpinLock")
.field("data", &"<locked>")
.finish(),
}
}
}
#[repr(transparent)]
pub struct SpinRwLock<T: ?Sized>(BaseSpinRwLock<RawState, T>);
pub type SpinRwLockReadGuard<'a, T> = BaseSpinRwLockReadGuard<'a, PreemptState, T>;
pub type SpinRwLockWriteGuard<'a, T> = BaseSpinRwLockWriteGuard<'a, PreemptState, T>;
pub type SpinRwLockIrqSaveReadGuard<'a, T> = BaseSpinRwLockReadGuard<'a, PreemptIrqSaveState, T>;
pub type SpinRwLockIrqSaveWriteGuard<'a, T> = BaseSpinRwLockWriteGuard<'a, PreemptIrqSaveState, T>;
pub type RawSpinRwLockReadGuard<'a, T> = BaseSpinRwLockReadGuard<'a, RawState, T>;
pub type RawSpinRwLockWriteGuard<'a, T> = BaseSpinRwLockWriteGuard<'a, RawState, T>;
impl<T> SpinRwLock<T> {
#[inline(always)]
#[track_caller]
pub const fn new(data: T) -> Self {
Self(BaseSpinRwLock::new(data))
}
#[inline(always)]
pub fn into_inner(self) -> T {
self.0.into_inner()
}
}
impl<T: ?Sized> SpinRwLock<T> {
#[inline(always)]
fn with_state<G: crate::sync::context::GuardState>(&self) -> &BaseSpinRwLock<G, T> {
unsafe { &*(ptr::from_ref(&self.0) as *const BaseSpinRwLock<G, T>) }
}
#[inline(always)]
fn with_state_mut<G: crate::sync::context::GuardState>(&mut self) -> &mut BaseSpinRwLock<G, T> {
unsafe { &mut *(ptr::from_mut(&mut self.0) as *mut BaseSpinRwLock<G, T>) }
}
#[inline(always)]
#[track_caller]
pub fn read(&self) -> SpinRwLockReadGuard<'_, T> {
self.with_state::<PreemptState>().read()
}
#[inline(always)]
#[track_caller]
pub fn try_read(&self) -> Option<SpinRwLockReadGuard<'_, T>> {
self.with_state::<PreemptState>().try_read()
}
#[inline(always)]
#[track_caller]
pub fn write(&self) -> SpinRwLockWriteGuard<'_, T> {
self.with_state::<PreemptState>().write()
}
#[inline(always)]
#[track_caller]
pub fn try_write(&self) -> Option<SpinRwLockWriteGuard<'_, T>> {
self.with_state::<PreemptState>().try_write()
}
#[inline(always)]
#[track_caller]
pub fn read_irqsave(&self) -> SpinRwLockIrqSaveReadGuard<'_, T> {
self.with_state::<PreemptIrqSaveState>().read()
}
#[inline(always)]
#[track_caller]
pub fn try_read_irqsave(&self) -> Option<SpinRwLockIrqSaveReadGuard<'_, T>> {
self.with_state::<PreemptIrqSaveState>().try_read()
}
#[inline(always)]
#[track_caller]
pub fn write_irqsave(&self) -> SpinRwLockIrqSaveWriteGuard<'_, T> {
self.with_state::<PreemptIrqSaveState>().write()
}
#[inline(always)]
#[track_caller]
pub fn try_write_irqsave(&self) -> Option<SpinRwLockIrqSaveWriteGuard<'_, T>> {
self.with_state::<PreemptIrqSaveState>().try_write()
}
#[inline(always)]
#[track_caller]
pub unsafe fn read_raw(&self) -> RawSpinRwLockReadGuard<'_, T> {
self.with_state::<RawState>().read()
}
#[inline(always)]
#[track_caller]
pub unsafe fn try_read_raw(&self) -> Option<RawSpinRwLockReadGuard<'_, T>> {
self.with_state::<RawState>().try_read()
}
#[inline(always)]
#[track_caller]
pub unsafe fn write_raw(&self) -> RawSpinRwLockWriteGuard<'_, T> {
self.with_state::<RawState>().write()
}
#[inline(always)]
#[track_caller]
pub unsafe fn try_write_raw(&self) -> Option<RawSpinRwLockWriteGuard<'_, T>> {
self.with_state::<RawState>().try_write()
}
#[inline(always)]
pub fn get_mut(&mut self) -> &mut T {
self.with_state_mut::<RawState>().get_mut()
}
#[doc(hidden)]
#[inline(always)]
pub unsafe fn force_read_decrement_raw(&self) {
unsafe {
self.with_state::<RawState>().force_read_decrement();
}
}
}
impl<T: Default> Default for SpinRwLock<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> From<T> for SpinRwLock<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: fmt::Debug> fmt::Debug for SpinRwLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_read() {
Some(guard) => f
.debug_struct("SpinRwLock")
.field("data", &&*guard)
.finish(),
None => f
.debug_struct("SpinRwLock")
.field("data", &"<write locked>")
.finish(),
}
}
}
#[cfg(all(test, feature = "host-test", not(target_os = "none")))]
mod tests {
use std::{
sync::{Arc, mpsc},
thread,
};
use super::{SpinLock, SpinRwLock};
use crate::sync::context::host_context_snapshot;
#[test]
fn spin_lock_acquisition_method_selects_context_policy() {
let lock = SpinLock::new(());
assert_eq!(host_context_snapshot(), (0, true));
let guard = lock.lock();
assert_eq!(host_context_snapshot(), (1, true));
drop(guard);
assert_eq!(host_context_snapshot(), (0, true));
let guard = lock.lock_irqsave();
assert_eq!(host_context_snapshot(), (1, false));
drop(guard);
assert_eq!(host_context_snapshot(), (0, true));
let guard = lock.lock_irqsave_nested(1);
assert_eq!(host_context_snapshot(), (1, false));
drop(guard);
assert_eq!(host_context_snapshot(), (0, true));
}
#[test]
fn spin_rwlock_acquisition_method_selects_context_policy() {
let lock = SpinRwLock::new(());
let reader = lock.read();
assert_eq!(host_context_snapshot(), (1, true));
drop(reader);
assert_eq!(host_context_snapshot(), (0, true));
let writer = lock.write_irqsave();
assert_eq!(host_context_snapshot(), (1, false));
drop(writer);
assert_eq!(host_context_snapshot(), (0, true));
}
#[test]
fn failed_spin_rwlock_try_modes_restore_context() {
let lock = Arc::new(SpinRwLock::new(()));
let holder_lock = Arc::clone(&lock);
let (held_sender, held_receiver) = mpsc::channel();
let (release_sender, release_receiver) = mpsc::channel();
let holder = thread::spawn(move || {
let held = unsafe { holder_lock.write_raw() };
held_sender.send(()).unwrap();
release_receiver.recv().unwrap();
drop(held);
});
held_receiver.recv().unwrap();
assert!(lock.try_read().is_none());
assert_eq!(host_context_snapshot(), (0, true));
assert!(lock.try_write().is_none());
assert_eq!(host_context_snapshot(), (0, true));
assert!(lock.try_read_irqsave().is_none());
assert_eq!(host_context_snapshot(), (0, true));
assert!(lock.try_write_irqsave().is_none());
assert_eq!(host_context_snapshot(), (0, true));
assert!(unsafe { lock.try_read_raw() }.is_none());
assert!(unsafe { lock.try_write_raw() }.is_none());
assert_eq!(host_context_snapshot(), (0, true));
release_sender.send(()).unwrap();
holder.join().unwrap();
}
}