Skip to main content

ax_task/thread/
pi.rs

1//! Task-local PI metadata and scheduler-owned per-lock waiter handles.
2
3use core::sync::atomic::{AtomicU64, Ordering};
4
5use crate::{
6    runtime::{
7        lock::{RawTicketGuard, RawTicketLock},
8        sync::{
9            PI_MUTEX_WAIT_STORAGE_WORDS, PiMutexCoreView, PiMutexRaw, PiMutexRef,
10            PiMutexStateError, PiTaskId, PiWaitStateError,
11        },
12    },
13    thread::{PiWaitTree, TaskError, ThreadId},
14};
15
16impl From<ThreadId> for PiTaskId {
17    fn from(thread: ThreadId) -> Self {
18        Self::new(thread.as_u64()).expect("scheduler thread identity must fit the PI owner word")
19    }
20}
21
22impl From<PiTaskId> for ThreadId {
23    fn from(thread: PiTaskId) -> Self {
24        let raw = thread.get();
25        Self::from_parts(raw as u32, (raw >> 32) as u32)
26    }
27}
28
29impl From<PiMutexStateError> for TaskError {
30    fn from(error: PiMutexStateError) -> Self {
31        match error {
32            PiMutexStateError::WaiterOwnsLock => {
33                Self::InvalidPiWaitState(PiWaitStateError::WaiterOwnsLock)
34            }
35            PiMutexStateError::InvalidState => Self::InvalidPiState,
36        }
37    }
38}
39
40/// One generation-checked edge from a blocked task to a physical PI mutex.
41///
42/// The edge is protected by the blocked task's scheduler lock, equivalent to
43/// Linux `task_struct::pi_lock`. The referenced lock waiter remains protected
44/// by the scheduler-owned wait handle installed in the physical mutex.
45#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub(crate) struct PiWaitRegistration {
47    pub(crate) lock: PiMutexRaw,
48    pub(crate) key: crate::thread::PiWaitKey,
49    pub(crate) generation: u64,
50}
51
52#[derive(Debug)]
53pub(crate) struct PiMutexWaiters {
54    pub(crate) waiters: PiWaitTree,
55}
56
57impl PiMutexWaiters {
58    const fn new() -> Self {
59        Self {
60            waiters: PiWaitTree::new(),
61        }
62    }
63}
64
65/// Scheduler-owned waiter state installed lazily into one physical PI mutex.
66pub(crate) struct PiMutexWaitHandle {
67    state: RawTicketLock<PiMutexWaiters>,
68}
69
70impl PiMutexWaitHandle {
71    fn new() -> Self {
72        Self {
73            state: RawTicketLock::new(PiMutexWaiters::new()),
74        }
75    }
76}
77
78impl Drop for PiMutexWaitHandle {
79    fn drop(&mut self) {
80        assert!(
81            self.state.lock().waiters.is_empty(),
82            "a PI mutex cannot be destroyed with live scheduler waiters"
83        );
84    }
85}
86
87pub(crate) fn lock_pi_mutex_waiters(lock: PiMutexRef<'_>) -> RawTicketGuard<'_, PiMutexWaiters> {
88    ensure_pi_mutex_wait_handle(lock.core()).state.lock()
89}
90
91pub(crate) unsafe fn lock_raw_pi_mutex_waiters(
92    lock: PiMutexRaw,
93) -> RawTicketGuard<'static, PiMutexWaiters> {
94    installed_pi_mutex_wait_handle(unsafe {
95        // SAFETY: the caller retains the registration represented by `lock`.
96        lock.core()
97    })
98    .state
99    .lock()
100}
101
102pub(crate) unsafe fn try_lock_raw_pi_mutex_waiters(
103    lock: PiMutexRaw,
104) -> Option<RawTicketGuard<'static, PiMutexWaiters>> {
105    installed_pi_mutex_wait_handle(unsafe {
106        // SAFETY: the caller retains the registration represented by `lock`.
107        lock.core()
108    })
109    .state
110    .try_lock()
111}
112
113pub(crate) unsafe fn drop_pi_mutex_wait_handle(wait_handle: *mut ()) {
114    let wait_handle = wait_handle.cast::<PiMutexWaitHandle>();
115    // SAFETY: PiMutexCore transferred the unique initialized inline object
116    // after its final safe reference and waiter registration became unreachable.
117    unsafe { wait_handle.drop_in_place() };
118}
119
120fn ensure_pi_mutex_wait_handle<'lock>(core: PiMutexCoreView<'lock>) -> &'lock PiMutexWaitHandle {
121    const _: () = assert!(
122        core::mem::size_of::<PiMutexWaitHandle>()
123            <= PI_MUTEX_WAIT_STORAGE_WORDS * core::mem::size_of::<usize>()
124    );
125    const _: () =
126        assert!(core::mem::align_of::<PiMutexWaitHandle>() <= core::mem::align_of::<usize>());
127
128    unsafe {
129        // SAFETY: every ArceOS access to this storage uses the same concrete
130        // handle type, whose size and alignment are checked above.
131        core.wait_storage().get_or_init(PiMutexWaitHandle::new)
132    }
133}
134
135fn installed_pi_mutex_wait_handle<'lock>(core: PiMutexCoreView<'lock>) -> &'lock PiMutexWaitHandle {
136    unsafe {
137        // SAFETY: a raw waiter registration is created only after the slow path
138        // initialized this exact concrete handle type.
139        core.wait_storage()
140            .get::<PiMutexWaitHandle>()
141            .expect("registered PI mutex has no scheduler wait handle")
142    }
143}
144
145/// Task-local handshake generation for one preallocated PI waiter.
146#[derive(Debug)]
147pub(crate) struct PiWaitState {
148    generation: AtomicU64,
149    top_generation: AtomicU64,
150    granted_generation: AtomicU64,
151}
152
153impl PiWaitState {
154    pub(crate) const fn new() -> Self {
155        Self {
156            generation: AtomicU64::new(0),
157            top_generation: AtomicU64::new(0),
158            granted_generation: AtomicU64::new(0),
159        }
160    }
161
162    pub(crate) fn begin(&self) -> Result<u64, TaskError> {
163        self.top_generation.store(0, Ordering::Relaxed);
164        self.granted_generation.store(0, Ordering::Relaxed);
165        self.generation
166            .try_update(Ordering::AcqRel, Ordering::Acquire, |generation| {
167                generation.checked_add(1)
168            })
169            .map(|generation| generation + 1)
170            .map_err(|_| TaskError::InvalidPiState)
171    }
172
173    pub(crate) fn mark_top(&self, generation: u64) -> Result<(), TaskError> {
174        if self.generation.load(Ordering::Acquire) != generation
175            || self.granted_generation.load(Ordering::Acquire) == generation
176        {
177            return Err(TaskError::InvalidPiState);
178        }
179        self.top_generation.store(generation, Ordering::Release);
180        Ok(())
181    }
182
183    pub(crate) fn clear_top(&self, generation: u64) {
184        let _ = self.top_generation.compare_exchange(
185            generation,
186            0,
187            Ordering::AcqRel,
188            Ordering::Acquire,
189        );
190    }
191
192    pub(crate) fn grant(&self, generation: u64) -> Result<(), TaskError> {
193        if self.generation.load(Ordering::Acquire) != generation {
194            return Err(TaskError::InvalidPiState);
195        }
196        self.clear_top(generation);
197        self.granted_generation.store(generation, Ordering::Release);
198        Ok(())
199    }
200
201    pub(crate) fn can_grant(&self, generation: u64) -> bool {
202        self.generation.load(Ordering::Acquire) == generation
203            && self.granted_generation.load(Ordering::Acquire) != generation
204    }
205
206    pub(crate) fn is_granted(&self, generation: u64) -> bool {
207        self.granted_generation.load(Ordering::Acquire) == generation
208    }
209
210    pub(crate) fn is_top(&self, generation: u64) -> bool {
211        self.top_generation.load(Ordering::Acquire) == generation
212    }
213}