use core::{
fmt,
ops::{Deref, DerefMut},
};
use super::{Mutex, MutexGuard, RawMutex};
pub struct SpinLock<T: ?Sized> {
mutex: Mutex<T>,
}
#[must_use]
pub struct SpinLockGuard<'a, T: ?Sized> {
migration: Option<RtCriticalGuard>,
owner: MutexGuard<'a, T>,
}
impl<T> SpinLock<T> {
pub const fn new(value: T) -> Self {
Self {
mutex: Mutex::const_new(RawMutex::new_rt_lock(), value),
}
}
pub fn into_inner(self) -> T {
self.mutex.into_inner()
}
}
impl<T: ?Sized> SpinLock<T> {
#[track_caller]
pub fn lock(&self) -> SpinLockGuard<'_, T> {
crate::thread::current::validate_rt_lock_context()
.expect("RT spin lock requires a preemptible task context");
let owner = self.mutex.lock();
let migration =
RtCriticalGuard::new().expect("RT spin lock owner must acquire its migration pin");
SpinLockGuard {
migration: Some(migration),
owner,
}
}
pub fn try_lock(&self) -> Option<SpinLockGuard<'_, T>> {
crate::thread::current::validate_rt_lock_context().ok()?;
let owner = self.mutex.try_lock()?;
let migration =
RtCriticalGuard::new().expect("RT spin lock owner must acquire its migration pin");
Some(SpinLockGuard {
migration: Some(migration),
owner,
})
}
pub fn lock_irqsave(&self) -> SpinLockGuard<'_, T> {
self.lock()
}
pub fn try_lock_irqsave(&self) -> Option<SpinLockGuard<'_, T>> {
self.try_lock()
}
pub fn get_mut(&mut self) -> &mut T {
self.mutex.get_mut()
}
pub fn is_locked(&self) -> bool {
self.mutex.is_locked()
}
}
impl<T: ?Sized> Deref for SpinLockGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
&self.owner
}
}
impl<T: ?Sized> DerefMut for SpinLockGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.owner
}
}
impl<T: ?Sized> Drop for SpinLockGuard<'_, T> {
fn drop(&mut self) {
drop(self.migration.take());
}
}
impl<T: Default> Default for SpinLock<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for SpinLock<T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.mutex.fmt(formatter)
}
}
pub(super) struct RtCriticalGuard {
migration: Option<super::MigrationGuard>,
current: alloc::sync::Arc<crate::thread::ThreadCore>,
}
impl RtCriticalGuard {
pub(super) fn new() -> Result<Self, crate::thread::TaskError> {
let current = crate::thread::current::current_thread_core_arc()?;
current.enter_rt_lock_critical();
match super::MigrationGuard::new() {
Ok(migration) => Ok(Self {
migration: Some(migration),
current,
}),
Err(error) => {
current.leave_rt_lock_critical();
Err(error)
}
}
}
}
impl Drop for RtCriticalGuard {
fn drop(&mut self) {
drop(self.migration.take());
self.current.leave_rt_lock_critical();
}
}