Skip to main content

ax_task/sync/
rt_spin.rs

1//! Preemptible, priority-inheritance spin locks for task context.
2
3use core::{
4    fmt,
5    ops::{Deref, DerefMut},
6};
7
8use super::{Mutex, MutexGuard, RawMutex};
9
10/// Linux PREEMPT_RT spinlock semantics: contention sleeps with saved task state.
11///
12/// Holding this lock disables migration, not preemption or hardware interrupts.
13/// Interrupt handlers and scheduler transactions must use [`super::RawSpinLock`].
14pub struct SpinLock<T: ?Sized> {
15    mutex: Mutex<T>,
16}
17
18/// A task-bound guard that releases migration exclusion before lock handoff.
19#[must_use]
20pub struct SpinLockGuard<'a, T: ?Sized> {
21    migration: Option<RtCriticalGuard>,
22    owner: MutexGuard<'a, T>,
23}
24
25impl<T> SpinLock<T> {
26    /// Creates an unlocked RT spin lock.
27    pub const fn new(value: T) -> Self {
28        Self {
29            mutex: Mutex::const_new(RawMutex::new_rt_lock(), value),
30        }
31    }
32
33    /// Returns the value when the lock is exclusively owned.
34    pub fn into_inner(self) -> T {
35        self.mutex.into_inner()
36    }
37}
38
39impl<T: ?Sized> SpinLock<T> {
40    /// Acquires the lock in task context, preserving an outer wait publication.
41    #[track_caller]
42    pub fn lock(&self) -> SpinLockGuard<'_, T> {
43        crate::thread::current::validate_rt_lock_context()
44            .expect("RT spin lock requires a preemptible task context");
45        let owner = self.mutex.lock();
46        let migration =
47            RtCriticalGuard::new().expect("RT spin lock owner must acquire its migration pin");
48        SpinLockGuard {
49            migration: Some(migration),
50            owner,
51        }
52    }
53
54    /// Attempts acquisition without waiting; fails in a non-task context.
55    pub fn try_lock(&self) -> Option<SpinLockGuard<'_, T>> {
56        crate::thread::current::validate_rt_lock_context().ok()?;
57        let owner = self.mutex.try_lock()?;
58        let migration =
59            RtCriticalGuard::new().expect("RT spin lock owner must acquire its migration pin");
60        Some(SpinLockGuard {
61            migration: Some(migration),
62            owner,
63        })
64    }
65
66    /// RT IRQ-save spelling; hardware IRQ state is unchanged.
67    pub fn lock_irqsave(&self) -> SpinLockGuard<'_, T> {
68        self.lock()
69    }
70
71    /// RT IRQ-save trylock; hardware IRQ state is unchanged.
72    pub fn try_lock_irqsave(&self) -> Option<SpinLockGuard<'_, T>> {
73        self.try_lock()
74    }
75
76    /// Returns exclusive access without acquiring the lock.
77    pub fn get_mut(&mut self) -> &mut T {
78        self.mutex.get_mut()
79    }
80
81    /// Returns an advisory locked-state snapshot.
82    pub fn is_locked(&self) -> bool {
83        self.mutex.is_locked()
84    }
85}
86
87impl<T: ?Sized> Deref for SpinLockGuard<'_, T> {
88    type Target = T;
89    fn deref(&self) -> &T {
90        &self.owner
91    }
92}
93
94impl<T: ?Sized> DerefMut for SpinLockGuard<'_, T> {
95    fn deref_mut(&mut self) -> &mut T {
96        &mut self.owner
97    }
98}
99
100impl<T: ?Sized> Drop for SpinLockGuard<'_, T> {
101    fn drop(&mut self) {
102        drop(self.migration.take());
103        // The owner field drops afterwards, performing PI handoff.
104    }
105}
106
107impl<T: Default> Default for SpinLock<T> {
108    fn default() -> Self {
109        Self::new(T::default())
110    }
111}
112
113impl<T: ?Sized + fmt::Debug> fmt::Debug for SpinLock<T> {
114    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
115        self.mutex.fmt(formatter)
116    }
117}
118
119/// Task-context exclusion carried by an RT lock guard. Rust's lock borrow
120/// protects the data lifetime; this depth forbids ordinary sleeping within
121/// that borrow while permitting RT-lock contention and preemption.
122pub(super) struct RtCriticalGuard {
123    migration: Option<super::MigrationGuard>,
124    current: alloc::sync::Arc<crate::thread::ThreadCore>,
125}
126
127impl RtCriticalGuard {
128    pub(super) fn new() -> Result<Self, crate::thread::TaskError> {
129        let current = crate::thread::current::current_thread_core_arc()?;
130        current.enter_rt_lock_critical();
131        match super::MigrationGuard::new() {
132            Ok(migration) => Ok(Self {
133                migration: Some(migration),
134                current,
135            }),
136            Err(error) => {
137                current.leave_rt_lock_critical();
138                Err(error)
139            }
140        }
141    }
142}
143
144impl Drop for RtCriticalGuard {
145    fn drop(&mut self) {
146        drop(self.migration.take());
147        self.current.leave_rt_lock_critical();
148    }
149}