1use core::sync::atomic::{AtomicU64, Ordering};
4
5mod entry;
6#[cfg(feature = "lockdep")]
7pub(in crate::sync) mod lockdep;
8mod pi_core;
9
10use self::entry::{
11 FastLockAttempt, LockEntry, capture_current_and_prepare_slow, owner_spin_eligible,
12 owner_spin_progress_gates,
13};
14pub use self::pi_core::*;
15
16pub struct RawMutex {
23 core: PiMutexCore,
24 next_waiter_sequence: AtomicU64,
25 #[cfg(feature = "lockdep")]
26 pub(crate) lockdep: super::lockdep::LockdepMap,
27}
28
29pub(in crate::sync) struct PiMutexAlgorithm<'lock> {
31 core: PiMutexCoreView<'lock>,
32 next_waiter_sequence: &'lock AtomicU64,
33}
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub struct PiMutexLockInterrupted;
38
39impl core::fmt::Display for PiMutexLockInterrupted {
40 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41 formatter.write_str("PI mutex wait interrupted")
42 }
43}
44
45impl core::error::Error for PiMutexLockInterrupted {}
46
47pub trait InterruptibleMutexExt<T: ?Sized> {
49 fn lock_interruptible<F>(
60 &self,
61 should_interrupt: F,
62 ) -> Result<MutexGuard<'_, T>, PiMutexLockInterrupted>
63 where
64 F: FnMut() -> bool;
65}
66
67#[cfg(not(feature = "lockdep"))]
68pub type LockSubclass = u32;
70#[cfg(feature = "lockdep")]
71pub type LockSubclass = super::lockdep::LockSubclass;
72
73pub trait LockdepMutexExt<T: ?Sized> {
75 fn lock_nested(&self, subclass: LockSubclass) -> MutexGuard<'_, T>;
77}
78
79impl<T: ?Sized> LockdepMutexExt<T> for Mutex<T> {
80 #[inline(always)]
81 #[track_caller]
82 fn lock_nested(&self, subclass: LockSubclass) -> MutexGuard<'_, T> {
83 #[cfg(not(feature = "lockdep"))]
84 {
85 let _ = subclass;
86 self.lock()
87 }
88
89 #[cfg(feature = "lockdep")]
90 {
91 let raw = unsafe { self.raw() };
93 raw.lock_nested(subclass);
94 unsafe { self.make_guard_unchecked() }
96 }
97 }
98}
99
100impl RawMutex {
101 pub const fn new() -> Self {
103 Self {
104 core: PiMutexCore::new(),
105 next_waiter_sequence: AtomicU64::new(0),
106 #[cfg(feature = "lockdep")]
107 lockdep: super::lockdep::LockdepMap::new(),
108 }
109 }
110
111 const fn algorithm(&self) -> PiMutexAlgorithm<'_> {
112 PiMutexAlgorithm::new(self.core.view(), &self.next_waiter_sequence)
113 }
114
115 pub fn is_owned_by_current(&self) -> bool {
117 self.algorithm().is_owned_by_current()
118 }
119}
120
121impl<'lock> PiMutexAlgorithm<'lock> {
122 pub(in crate::sync) const fn new(
123 core: PiMutexCoreView<'lock>,
124 next_waiter_sequence: &'lock AtomicU64,
125 ) -> Self {
126 Self {
127 core,
128 next_waiter_sequence,
129 }
130 }
131
132 pub(in crate::sync) fn is_owned_by_current(&self) -> bool {
133 Self::core_is_owned_by_current(self.core)
134 }
135
136 pub(in crate::sync) fn core_is_owned_by_current(core: PiMutexCoreView<'_>) -> bool {
137 core.is_owned_by(Self::current_task_id())
138 }
139
140 #[inline(always)]
141 fn current_task_id() -> PiTaskId {
142 task_result(
143 crate::thread::current::current_thread_id(),
144 "capture current PI mutex task",
145 )
146 .into()
147 }
148
149 pub(in crate::sync) fn lock_pi(&self) {
150 #[cfg(feature = "qperf-metrics")]
151 crate::diagnostics::counters::record_pi_mutex_lock_attempt();
152 match capture_current_and_prepare_slow(
153 || {
154 task_result(
155 crate::thread::current::current_thread_token(),
156 "capture current PI mutex task",
157 )
158 },
159 |current| self.try_or_observe_current_token(current.id().into()),
160 || {
161 task_result(
165 crate::thread::current::validate_blocking_context(),
166 "validate PI mutex blocking context",
167 );
168 },
169 ) {
170 LockEntry::Acquired => {
171 #[cfg(feature = "qperf-metrics")]
172 crate::diagnostics::counters::record_pi_mutex_fast_acquisition();
173 }
174 LockEntry::Contended(current) => {
175 #[cfg(feature = "qperf-metrics")]
176 crate::diagnostics::counters::record_pi_mutex_slow_entry();
177 self.lock_contended(current);
178 }
179 }
180 }
181
182 fn lock_pi_interruptible(
183 &self,
184 mut should_interrupt: impl FnMut() -> bool,
185 ) -> Result<(), PiMutexLockInterrupted> {
186 match capture_current_and_prepare_slow(
187 || {
188 task_result(
189 crate::thread::current::current_thread_token(),
190 "capture current PI mutex task",
191 )
192 },
193 |current| self.try_or_observe_current_token(current.id().into()),
194 || {
195 task_result(
196 crate::thread::current::validate_blocking_context(),
197 "validate PI mutex blocking context",
198 );
199 },
200 ) {
201 LockEntry::Acquired => Ok(()),
202 LockEntry::Contended(current) => {
203 self.lock_contended_interruptible(current, &mut should_interrupt)
204 }
205 }
206 }
207
208 #[cold]
209 #[inline(never)]
210 fn lock_contended(&self, current: crate::thread::CurrentThreadToken) {
211 let current_id = current.id().into();
212 let sequence = self.next_waiter_sequence.fetch_add(1, Ordering::Relaxed);
213 let lock = core_result(self.core.mutex_ref(), "borrow PI mutex identity");
214 let token = match task_result(
215 crate::runtime::sync::pi_mutex_lock_slow(lock, ¤t, sequence),
216 "register PI mutex waiter",
217 ) {
218 PiMutexLockResult::Acquired => {
219 #[cfg(feature = "qperf-metrics")]
220 crate::diagnostics::counters::record_pi_mutex_slow_race_acquisition();
221 return;
222 }
223 PiMutexLockResult::Waiting(token) => {
224 #[cfg(feature = "qperf-metrics")]
225 crate::diagnostics::counters::record_pi_mutex_waiter_registration();
226 token
227 }
228 };
229 debug_assert_eq!(token.thread_id(), current_id);
230 if self.try_claim_waiter(&token, ¤t) {
231 return;
232 }
233 self.wait_for_handoff(token, ¤t);
234 }
235
236 #[cold]
237 #[inline(never)]
238 fn lock_contended_interruptible(
239 &self,
240 current: crate::thread::CurrentThreadToken,
241 should_interrupt: &mut impl FnMut() -> bool,
242 ) -> Result<(), PiMutexLockInterrupted> {
243 let current_id = current.id().into();
244 let sequence = self.next_waiter_sequence.fetch_add(1, Ordering::Relaxed);
245 let lock = core_result(self.core.mutex_ref(), "borrow PI mutex identity");
246 let token = match task_result(
247 crate::runtime::sync::pi_mutex_lock_slow(lock, ¤t, sequence),
248 "register interruptible PI mutex waiter",
249 ) {
250 PiMutexLockResult::Acquired => return Ok(()),
251 PiMutexLockResult::Waiting(token) => token,
252 };
253 debug_assert_eq!(token.thread_id(), current_id);
254
255 loop {
256 if self.try_claim_waiter(&token, ¤t) {
257 return Ok(());
258 }
259 if should_interrupt() {
260 match task_result(
261 crate::runtime::sync::pi_wait_try_cancel(&token),
262 "cancel interruptible PI mutex waiter",
263 ) {
264 PiWaitCancelOutcome::Cancelled => {
265 task_result(
266 crate::runtime::sync::pi::cancel_prepared_pi_park(&token),
267 "cancel prepared interruptible PI mutex park",
268 );
269 return Err(PiMutexLockInterrupted);
270 }
271 PiWaitCancelOutcome::HandoffPending => continue,
272 }
273 }
274 if !token.can_claim() && !self.spin_on_owner(&token) {
275 task_result(
276 crate::runtime::sync::pi_park_current_once(&token),
277 "park interruptible PI mutex waiter",
278 );
279 }
280 }
281 }
282
283 fn wait_for_handoff(&self, token: PiWaitToken, current: &crate::thread::CurrentThreadToken) {
284 loop {
285 if self.try_claim_waiter(&token, current) {
286 break;
287 }
288 if !token.can_claim() && !self.spin_on_owner(&token) {
289 #[cfg(feature = "qperf-metrics")]
290 crate::diagnostics::counters::record_pi_mutex_waiter_park();
291 task_result(
292 crate::runtime::sync::pi_park_current_once(&token),
293 "park PI mutex waiter",
294 );
295 }
296 }
297 assert!(
298 self.core.is_owned_by(token.thread_id()),
299 "PI core owner must name the granted waiter"
300 );
301 }
302
303 fn spin_on_owner(&self, token: &PiWaitToken) -> bool {
309 let Some(owner) = token.initial_owner() else {
310 return token.can_claim() || token.is_granted();
311 };
312 let cpu_count = task_result(
313 crate::sched::cpu_topology_len(),
314 "capture PI mutex CPU topology",
315 );
316
317 loop {
318 if token.can_claim() || token.is_granted() {
319 return true;
320 }
321
322 let may_spin = owner_spin_eligible(cpu_count, || {
323 owner_spin_progress_gates(
324 self.core.is_owned_by(owner),
325 token.initial_owner_is_on_cpu(),
326 token.is_top_waiter(),
327 crate::runtime::task_runtime::current_preemption_pending(),
328 )
329 });
330 if !may_spin {
331 return token.can_claim() || token.is_granted();
332 }
333
334 core::hint::spin_loop();
335 }
336 }
337
338 fn try_or_observe_current_token(&self, current: PiTaskId) -> FastLockAttempt {
339 match core_result(self.core.try_acquire(current), "try PI mutex acquisition") {
340 PiMutexAcquire::Acquired => FastLockAttempt::Acquired,
341 PiMutexAcquire::Contended => FastLockAttempt::Contended,
342 }
343 }
344
345 pub(in crate::sync) fn try_lock_pi(&self) -> bool {
346 let current = Self::current_task_id();
347 match self.core.try_acquire(current) {
348 Ok(PiMutexAcquire::Acquired) => true,
349 Ok(PiMutexAcquire::Contended) | Err(PiMutexStateError::WaiterOwnsLock) => false,
350 Err(error) => panic!("try PI mutex failed: {error}"),
351 }
352 }
353
354 fn try_claim_waiter(
355 &self,
356 token: &PiWaitToken,
357 current: &crate::thread::CurrentThreadToken,
358 ) -> bool {
359 if token.is_granted() {
360 task_result(
361 crate::runtime::sync::pi::cancel_prepared_pi_park(token),
362 "cancel prepared PI mutex park after handoff",
363 );
364 return true;
365 }
366 if !token.can_claim() {
367 return false;
368 }
369 let claimed = match task_result(
370 crate::runtime::sync::pi_mutex_claim(token, current),
371 "claim ownerless PI mutex handoff",
372 ) {
373 PiMutexClaimOutcome::Claimed => true,
374 PiMutexClaimOutcome::Retry => false,
375 };
376 if claimed {
377 task_result(
378 crate::runtime::sync::pi::cancel_prepared_pi_park(token),
379 "cancel prepared PI mutex park after claim",
380 );
381 }
382 claimed
383 }
384
385 pub(in crate::sync) unsafe fn unlock_pi(&self) {
386 unsafe { Self::unlock_core(self.core) };
388 }
389
390 pub(in crate::sync) unsafe fn unlock_core(core: PiMutexCoreView<'_>) {
391 let current = Self::current_task_id();
392 match core_result(
397 unsafe { core.try_release_owned(current) },
398 "try PI mutex release",
399 ) {
400 PiMutexOwnedRelease::Released => {}
401 PiMutexOwnedRelease::Contended(owner) => {
402 #[cfg(feature = "qperf-metrics")]
403 crate::diagnostics::counters::record_pi_mutex_contended_release();
404 unsafe { Self::unlock_contended(core, owner) };
407 }
408 }
409 }
410
411 unsafe fn unlock_contended(core: PiMutexCoreView<'_>, owner: PiTaskId) {
412 let lock = core_result(core.mutex_ref(), "borrow PI mutex release identity");
413 task_result(
414 unsafe {
415 crate::runtime::sync::pi_mutex_release_owned(lock, owner.into())
418 },
419 "release contended PI mutex",
420 );
421 }
422
423 pub(in crate::sync) fn is_locked(&self) -> bool {
424 Self::core_is_locked(self.core)
425 }
426
427 pub(in crate::sync) fn core_is_locked(core: PiMutexCoreView<'_>) -> bool {
428 core.is_locked()
429 }
430}
431
432impl RawMutex {
433 fn lock_pi(&self) {
434 self.algorithm().lock_pi();
435 }
436
437 fn lock_pi_interruptible(
438 &self,
439 should_interrupt: impl FnMut() -> bool,
440 ) -> Result<(), PiMutexLockInterrupted> {
441 self.algorithm().lock_pi_interruptible(should_interrupt)
442 }
443
444 fn try_lock_pi(&self) -> bool {
445 self.algorithm().try_lock_pi()
446 }
447
448 unsafe fn unlock_pi(&self) {
449 unsafe { self.algorithm().unlock_pi() };
451 }
452
453 #[cfg(feature = "lockdep")]
454 #[track_caller]
455 fn lock_nested(&self, subclass: LockSubclass) {
456 let lockdep = lockdep::LockdepAcquire::prepare_nested(self, false, subclass);
457 self.lock_pi();
458 lockdep.finish(true);
459 }
460
461 #[cfg(feature = "lockdep")]
462 #[track_caller]
463 fn lock_interruptible_nested(
464 &self,
465 subclass: LockSubclass,
466 should_interrupt: impl FnMut() -> bool,
467 ) -> Result<(), PiMutexLockInterrupted> {
468 let lockdep = lockdep::LockdepAcquire::prepare_nested(self, false, subclass);
469 let result = self.lock_pi_interruptible(should_interrupt);
470 lockdep.finish(result.is_ok());
471 result
472 }
473
474 #[cfg(feature = "lockdep")]
475 #[track_caller]
476 fn try_lock_nested(&self, subclass: LockSubclass) -> bool {
477 let lockdep = lockdep::LockdepAcquire::prepare_nested(self, true, subclass);
478 let acquired = self.try_lock_pi();
479 lockdep.finish(acquired);
480 acquired
481 }
482}
483
484impl Default for RawMutex {
485 fn default() -> Self {
486 Self::new()
487 }
488}
489
490unsafe impl lock_api::RawMutex for RawMutex {
495 type GuardMarker = lock_api::GuardNoSend;
496
497 const INIT: Self = Self::new();
498
499 #[inline(always)]
500 #[track_caller]
501 fn lock(&self) {
502 #[cfg(feature = "lockdep")]
503 self.lock_nested(super::lockdep::DEFAULT_LOCK_SUBCLASS);
504
505 #[cfg(not(feature = "lockdep"))]
506 self.lock_pi();
507 }
508
509 #[inline(always)]
510 #[track_caller]
511 fn try_lock(&self) -> bool {
512 #[cfg(feature = "lockdep")]
513 {
514 self.try_lock_nested(super::lockdep::DEFAULT_LOCK_SUBCLASS)
515 }
516
517 #[cfg(not(feature = "lockdep"))]
518 {
519 self.try_lock_pi()
520 }
521 }
522
523 #[inline(always)]
524 unsafe fn unlock(&self) {
525 #[cfg(feature = "lockdep")]
526 lockdep::release(self);
527 unsafe { self.unlock_pi() };
530 }
531
532 #[inline(always)]
533 fn is_locked(&self) -> bool {
534 self.algorithm().is_locked()
535 }
536}
537
538#[track_caller]
539fn core_result<T>(result: Result<T, PiMutexStateError>, operation: &'static str) -> T {
540 result.unwrap_or_else(|error| panic!("{operation} failed: {error}"))
541}
542
543#[track_caller]
544pub(super) fn task_result<T, E>(result: Result<T, E>, operation: &'static str) -> T
545where
546 E: core::fmt::Display,
547{
548 result.unwrap_or_else(|error| panic!("{operation} failed: {error}"))
549}
550
551pub type Mutex<T> = lock_api::Mutex<RawMutex, T>;
553pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>;
555
556impl<T: ?Sized> InterruptibleMutexExt<T> for Mutex<T> {
557 #[track_caller]
558 fn lock_interruptible<F>(
559 &self,
560 should_interrupt: F,
561 ) -> Result<MutexGuard<'_, T>, PiMutexLockInterrupted>
562 where
563 F: FnMut() -> bool,
564 {
565 let raw = unsafe { self.raw() };
568 #[cfg(feature = "lockdep")]
569 raw.lock_interruptible_nested(super::lockdep::DEFAULT_LOCK_SUBCLASS, should_interrupt)?;
570 #[cfg(not(feature = "lockdep"))]
571 raw.lock_pi_interruptible(should_interrupt)?;
572
573 Ok(unsafe { self.make_guard_unchecked() })
575 }
576}