concurrent/chainlock_transaction.rs
1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::cell::Cell;
6use core::fmt;
7use core::marker::{PhantomData, PhantomPinned};
8use core::mem::offset_of;
9use core::ptr::NonNull;
10use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
11
12//
13// # General Chain Lock Transaction Facility
14//
15// Chain lock transactions are a layer on top of chain locks that track the state necessary for
16// entities (i.e. CPUs) to acquire a series of one or more chain locks and correctly handle
17// arbitration with other entities contending for the same locks. The base transaction facility
18// provides the main hooks required by the general chain lock implementation, and has compile-time
19// enabled state for debugging and performance tracing to help diagnose issues with chain lock
20// usage.
21//
22
23const UNLOCKED_TOKEN_VALUE: u64 = 0;
24const RESERVED_TOKEN_VALUES: u64 = 10000;
25const INITIAL_TOKEN_VALUE: u64 = RESERVED_TOKEN_VALUES + 1;
26const MAX_TOKEN_VALUE: u64 = u64::MAX;
27
28// SAFETY: `concurrent_chainlock_transaction_token_generator` is defined in
29// `chainlock_transaction_common.cc` with C linkage (`extern "C"`) as a lock-free
30// `std::atomic<uint64_t>` with static storage duration, verified by `static_assert` in C++ to have
31// the exact size and alignment of `u64` / `AtomicU64`.
32unsafe extern "C" {
33 // Shared global atomic token generator defined in `chainlock_transaction_common.cc` so that
34 // C++ and Rust transactions allocate tokens from the same monotonic generator and can
35 // interoperate on the same `ChainLock` instances.
36 static concurrent_chainlock_transaction_token_generator: AtomicU64;
37}
38
39/// Zero-sized capability token proving that a transaction of type `T` is currently active on the
40/// calling execution context.
41pub struct ChainLockTransactionToken<T> {
42 // Safety invariant: A `ChainLockTransactionToken<T>` value may only exist on an execution
43 // context while a transaction of type `T` is registered as the active transaction on that
44 // context and remains alive and pinned at its memory address. `*mut T` makes the token
45 // `!Send`, `!Sync`, and invariant in `T`.
46 _private: PhantomData<*mut T>,
47}
48
49impl<T> fmt::Debug for ChainLockTransactionToken<T> {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 f.debug_struct("ChainLockTransactionToken").finish()
52 }
53}
54
55impl<T> ChainLockTransactionToken<T> {
56 #[inline]
57 pub(crate) const fn new() -> Self {
58 Self { _private: PhantomData }
59 }
60}
61
62/// A transaction generation token used to identify the owning transaction of a `ChainLock` and
63/// arbitrate priority when contention occurs.
64#[repr(transparent)]
65#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
66pub struct Token(u64);
67
68impl Token {
69 /// Token value representing an unlocked `ChainLock`.
70 pub const UNLOCKED: Self = Self(UNLOCKED_TOKEN_VALUE);
71
72 /// Maximum possible token value, representing the lowest possible transaction priority.
73 pub const MAX: Self = Self(MAX_TOKEN_VALUE);
74
75 /// Creates a new unlocked token.
76 #[inline]
77 pub const fn new() -> Self {
78 Self::UNLOCKED
79 }
80
81 #[inline]
82 pub(crate) const fn from_raw(value: u64) -> Self {
83 Self(value)
84 }
85
86 /// Returns `true` if this token is valid (i.e., not the unlocked token value).
87 #[inline]
88 pub const fn is_valid(&self) -> bool {
89 self.value() != UNLOCKED_TOKEN_VALUE
90 }
91
92 /// Returns `true` if this token is a reserved token (e.g., per-CPU token used during
93 /// context switch state save/restore).
94 #[inline]
95 pub const fn is_reserved(&self) -> bool {
96 self.is_valid() && self.value() <= RESERVED_TOKEN_VALUES
97 }
98
99 /// Returns the raw 64-bit integer value of this token.
100 #[inline]
101 pub const fn value(&self) -> u64 {
102 self.0
103 }
104}
105
106/// Atomic storage for a [`Token`].
107#[repr(transparent)]
108#[derive(Debug, Default)]
109pub struct AtomicToken(AtomicU64);
110
111impl AtomicToken {
112 /// Creates a new [`AtomicToken`] initialized with `token`.
113 #[inline]
114 pub const fn new(token: Token) -> Self {
115 Self(AtomicU64::new(token.value()))
116 }
117
118 /// Loads a [`Token`] from the atomic variable.
119 #[inline]
120 pub fn load(&self, order: Ordering) -> Token {
121 Token::from_raw(self.0.load(order))
122 }
123
124 /// Stores a [`Token`] into the atomic variable.
125 #[inline]
126 pub fn store(&self, token: Token, order: Ordering) {
127 self.0.store(token.value(), order);
128 }
129
130 /// Stores `new` into the atomic variable if the current value is `current`.
131 #[inline]
132 pub fn compare_exchange(
133 &self,
134 current: Token,
135 new: Token,
136 success: Ordering,
137 failure: Ordering,
138 ) -> Result<Token, Token> {
139 self.0
140 .compare_exchange(current.value(), new.value(), success, failure)
141 .map(Token::from_raw)
142 .map_err(Token::from_raw)
143 }
144}
145
146/// Saved transaction state returned by
147/// [`ChainLockTransaction::save_state_and_use_reserved_token`] and
148/// [`ChainLockTransaction::save_state_initial`], used to restore an active transaction across
149/// context switches.
150#[repr(C)]
151pub struct SavedState<E: TransactionHooks> {
152 // Safety invariant: Points to the `ChainLockTransaction<E>` from which this `SavedState` was
153 // created via `with_replacement`. Callers of `restore_state` must ensure that transaction is
154 // still alive and pinned at this address.
155 transaction: NonNull<ChainLockTransaction<E>>,
156 token: Token,
157}
158
159impl<E: TransactionHooks> Clone for SavedState<E> {
160 #[inline]
161 fn clone(&self) -> Self {
162 *self
163 }
164}
165
166impl<E: TransactionHooks> Copy for SavedState<E> {}
167
168impl<E: TransactionHooks> fmt::Debug for SavedState<E> {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 f.debug_struct("SavedState")
171 .field("transaction", &self.transaction)
172 .field("token", &self.token)
173 .finish()
174 }
175}
176
177impl<E: TransactionHooks> SavedState<E> {
178 /// Returns the saved transaction token.
179 #[inline]
180 pub const fn token(&self) -> Token {
181 self.token
182 }
183
184 /// Returns the non-null pointer to the saved transaction.
185 #[inline]
186 pub const fn transaction(&self) -> NonNull<ChainLockTransaction<E>> {
187 self.transaction
188 }
189
190 #[inline]
191 pub(crate) fn with_replacement(
192 transaction: &ChainLockTransaction<E>,
193 replacement: Token,
194 ) -> Self {
195 let token = transaction.token();
196 transaction.replace_token(replacement);
197 Self { transaction: NonNull::from(transaction), token }
198 }
199}
200
201/// Non-terminal control flow action returned by a transaction attempt closure.
202#[derive(Clone, Copy, Debug, PartialEq, Eq)]
203pub enum Action {
204 /// Unwind held locks, execute the backoff protocol, and re-enter the transaction attempt.
205 Backoff,
206 /// Reset finalization state and re-enter the transaction attempt with the same token.
207 Restart,
208}
209
210/// Marker type indicating completion of a transaction with no return value.
211#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
212pub struct DoneType;
213
214/// Constant value of [`DoneType`] indicating transaction completion.
215pub const DONE: DoneType = DoneType;
216
217/// Result of a single execution of a transaction attempt closure passed to `until_done`.
218#[derive(Debug, PartialEq, Eq)]
219pub enum TransactionResult<T = ()> {
220 /// The transaction must back off or restart and re-execute the closure.
221 Action(Action),
222 /// The transaction completed with the terminal value `T`.
223 Value(T),
224}
225
226impl TransactionResult<()> {
227 /// Convenience constant for completing a `()`-returning transaction.
228 pub const DONE: Self = Self::Value(());
229}
230
231impl<T> TransactionResult<T> {
232 /// Convenience constant for requesting a transaction backoff.
233 pub const BACKOFF: Self = Self::Action(Action::Backoff);
234
235 /// Convenience constant for requesting a transaction restart.
236 pub const RESTART: Self = Self::Action(Action::Restart);
237
238 /// Returns `true` if this result is an [`Action`].
239 #[inline]
240 pub const fn is_action(&self) -> bool {
241 matches!(self, Self::Action(_))
242 }
243
244 /// Returns the [`Action`] contained in this result.
245 ///
246 /// # Panics
247 ///
248 /// Panics if `!self.is_action()`.
249 #[inline]
250 pub fn action(&self) -> Action {
251 match self {
252 Self::Action(action) => *action,
253 Self::Value(_) => unreachable!(),
254 }
255 }
256
257 /// Consumes this result and returns the terminal value `T`.
258 ///
259 /// # Panics
260 ///
261 /// Panics if `self.is_action()`.
262 #[inline]
263 pub fn take_value(self) -> T {
264 match self {
265 Self::Value(value) => value,
266 Self::Action(_) => unreachable!(),
267 }
268 }
269}
270
271impl<T> From<Action> for TransactionResult<T> {
272 #[inline]
273 fn from(action: Action) -> Self {
274 Self::Action(action)
275 }
276}
277
278impl From<DoneType> for TransactionResult<()> {
279 #[inline]
280 fn from(_: DoneType) -> Self {
281 Self::Value(())
282 }
283}
284
285/// Trait implemented by return types of closures passed to `ChainLockTransaction::until_done`.
286///
287/// The callable passed to [`ChainLockTransaction::until_done`] may return:
288/// 1. [`TransactionResult<()>`] (a `()`-returning transaction),
289/// 2. [`TransactionResult<T>`] (a transaction returning user-defined type `T`), or
290/// 3. [`DoneType`] (an infallible transaction with implied return type `()`).
291pub trait IntoTransactionResult {
292 /// The terminal value type returned by `until_done` when the transaction completes.
293 type Output;
294
295 /// Converts the closure return value into a [`TransactionResult`].
296 fn into_transaction_result(self) -> TransactionResult<Self::Output>;
297}
298
299impl IntoTransactionResult for DoneType {
300 type Output = ();
301
302 #[inline]
303 fn into_transaction_result(self) -> TransactionResult<()> {
304 TransactionResult::Value(())
305 }
306}
307
308impl<T> IntoTransactionResult for TransactionResult<T> {
309 type Output = T;
310
311 #[inline]
312 fn into_transaction_result(self) -> TransactionResult<T> {
313 self
314 }
315}
316
317/// Lifecycle state of a [`ChainLockTransaction`].
318#[repr(i32)]
319#[derive(Clone, Copy, Debug, PartialEq, Eq)]
320pub enum State {
321 /// Transaction has started and may acquire locks or back off.
322 Started = 0,
323 /// Transaction has acquired all needed locks and been finalized.
324 Finalized = 1,
325 /// Transaction is complete.
326 Complete = 2,
327}
328
329/// Trait for contention state stored inside a [`ChainLock`](crate::ChainLock).
330pub trait ContentionState: Default {
331 /// Constant initial value for `const fn` construction of `ChainLock`.
332 const INIT: Self;
333}
334
335impl ContentionState for AtomicU32 {
336 const INIT: Self = AtomicU32::new(0);
337}
338
339impl ContentionState for AtomicU64 {
340 const INIT: Self = AtomicU64::new(0);
341}
342
343impl ContentionState for () {
344 const INIT: Self = ();
345}
346
347/// Trait representing a chain lock transaction type used by [`ChainLock`](crate::ChainLock) and
348/// [`ChainLockGuard`](crate::ChainLockGuard).
349///
350/// This is automatically implemented by [`ChainLockTransaction<E>`] for any [`TransactionHooks`]
351/// implementation `E`.
352pub trait Transaction: Sized {
353 /// State used by `ChainLock` to store contended lock state for backoff mitigation.
354 type ContentionState: ContentionState;
355
356 /// Returns a pointer to the active transaction for the current execution context, or `None` if
357 /// no transaction is active.
358 fn active() -> core::option::Option<NonNull<Self>>;
359
360 /// Returns a shared reference to the active transaction, asserting in debug builds that a
361 /// transaction is currently active.
362 fn active_ref(token: &ChainLockTransactionToken<Self>) -> &Self;
363
364 /// Performs an architecture-specific CPU relax/yield operation.
365 fn yield_cpu();
366
367 /// Returns the current [`Token`] of this transaction.
368 fn token(&self) -> Token;
369
370 /// Asserts that this transaction has not been finalized (when debug accounting is enabled).
371 fn assert_not_finalized(&self);
372
373 /// Asserts that this transaction currently holds `expected` locks (when debug accounting is
374 /// enabled).
375 fn assert_num_locks_held(&self, expected: u32);
376
377 /// Accounting hook invoked when a `ChainLock` is acquired by this transaction.
378 fn on_acquire(&self, token: &ChainLockTransactionToken<Self>);
379
380 /// Accounting hook invoked when a `ChainLock` is released by this transaction.
381 fn on_release(&self, token: &ChainLockTransactionToken<Self>);
382
383 /// Accounting/tracing hook invoked when contention is encountered while acquiring a
384 /// `ChainLock`.
385 fn on_contention(&self, token: &ChainLockTransactionToken<Self>);
386
387 /// Contention hook invoked when a `ChainLock` encounters a higher-priority owner.
388 ///
389 /// Returns `true` if backoff was recorded and the caller should unwind, or `false` if the
390 /// lock's token changed and acquisition should be retried.
391 fn record_backoff_or_retry(
392 &self,
393 observed_token: Token,
394 lock_state: &AtomicToken,
395 contention_state: &Self::ContentionState,
396 token: &ChainLockTransactionToken<Self>,
397 ) -> bool;
398
399 /// Contention hook invoked when releasing a `ChainLock` to sample contention state, invoke
400 /// `do_release`, and notify backed-off contenders.
401 fn release_and_notify<F: FnOnce()>(
402 &self,
403 contention_state: &Self::ContentionState,
404 do_release: F,
405 token: &ChainLockTransactionToken<Self>,
406 );
407}
408
409/// Storage trait for debug lock accounting in [`ChainLockTransaction`].
410pub trait AccountingStorage: Default {
411 /// Whether debug lock accounting is enabled.
412 const ENABLED: bool;
413
414 /// Creates a new accounting storage instance with the given initial `locks_held` and
415 /// `finalized` state.
416 fn new(locks_held: u32, finalized: bool) -> Self;
417
418 /// Returns the number of locks currently held by the transaction.
419 fn locks_held(&self) -> u32;
420
421 /// Returns whether the transaction is finalized.
422 fn finalized(&self) -> bool;
423
424 /// Increments the count of locks held by the transaction.
425 fn on_acquire(&self);
426
427 /// Decrements the count of locks held by the transaction.
428 fn on_release(&self);
429
430 /// Updates whether the transaction is finalized.
431 fn set_finalized(&self, finalized: bool);
432}
433
434/// Storage for debug accounting when `DebugAccountingEnabled` is `true`.
435#[repr(C)]
436#[derive(Default)]
437pub struct AccountingStorageEnabled {
438 locks_held: Cell<u32>,
439 finalized: Cell<bool>,
440}
441
442impl AccountingStorage for AccountingStorageEnabled {
443 const ENABLED: bool = true;
444
445 #[inline]
446 fn new(locks_held: u32, finalized: bool) -> Self {
447 Self { locks_held: Cell::new(locks_held), finalized: Cell::new(finalized) }
448 }
449
450 #[inline]
451 fn locks_held(&self) -> u32 {
452 self.locks_held.get()
453 }
454
455 #[inline]
456 fn finalized(&self) -> bool {
457 self.finalized.get()
458 }
459
460 #[inline]
461 fn on_acquire(&self) {
462 self.locks_held.set(self.locks_held.get() + 1);
463 }
464
465 #[inline]
466 fn on_release(&self) {
467 self.locks_held.set(self.locks_held.get() - 1);
468 }
469
470 #[inline]
471 fn set_finalized(&self, finalized: bool) {
472 self.finalized.set(finalized);
473 }
474}
475
476/// Zero-sized storage for debug accounting when `DebugAccountingEnabled` is `false`.
477#[repr(C)]
478#[derive(Default)]
479pub struct AccountingStorageDisabled;
480
481impl AccountingStorage for AccountingStorageDisabled {
482 const ENABLED: bool = false;
483
484 #[inline]
485 fn new(_locks_held: u32, _finalized: bool) -> Self {
486 Self
487 }
488
489 #[inline]
490 fn locks_held(&self) -> u32 {
491 0
492 }
493
494 #[inline]
495 fn finalized(&self) -> bool {
496 false
497 }
498
499 #[inline]
500 fn on_acquire(&self) {}
501
502 #[inline]
503 fn on_release(&self) {}
504
505 #[inline]
506 fn set_finalized(&self, _finalized: bool) {}
507}
508
509/// Storage trait for contention spin tracing in `ChainLockTransaction`.
510pub trait TraceStorage<C: Copy> {
511 /// Whether contention spin tracing is enabled.
512 const ENABLED: bool;
513
514 /// Creates a new trace storage instance with the given `callsite_info`.
515 fn new(callsite_info: C) -> Self;
516
517 /// Records the start of contention if not already recorded.
518 fn on_contention<E: TransactionHooks<CallsiteInfo = C>>(&self);
519
520 /// Emits the contention trace event if contention occurred during this transaction.
521 fn on_finalized<E: TransactionHooks<CallsiteInfo = C>>(&self);
522
523 /// Updates the callsite info when restarting a transaction.
524 fn set_callsite_info(&self, callsite_info: C);
525}
526
527/// Extra state when contention tracing is enabled.
528#[repr(C)]
529pub struct TraceStorageEnabled<C: Copy> {
530 contention_start_ticks: Cell<i64>,
531 callsite_info: Cell<C>,
532}
533
534impl<C: Copy> TraceStorageEnabled<C> {
535 const INVALID_TICKS: i64 = 0;
536}
537
538impl<C: Copy + Default> Default for TraceStorageEnabled<C> {
539 fn default() -> Self {
540 Self {
541 contention_start_ticks: Cell::new(Self::INVALID_TICKS),
542 callsite_info: Cell::new(C::default()),
543 }
544 }
545}
546
547impl<C: Copy> TraceStorage<C> for TraceStorageEnabled<C> {
548 const ENABLED: bool = true;
549
550 #[inline]
551 fn new(callsite_info: C) -> Self {
552 Self {
553 contention_start_ticks: Cell::new(Self::INVALID_TICKS),
554 callsite_info: Cell::new(callsite_info),
555 }
556 }
557
558 #[inline]
559 fn on_contention<E: TransactionHooks<CallsiteInfo = C>>(&self) {
560 if self.contention_start_ticks.get() == Self::INVALID_TICKS {
561 self.contention_start_ticks.set(E::get_current_ticks());
562 }
563 }
564
565 #[inline]
566 fn on_finalized<E: TransactionHooks<CallsiteInfo = C>>(&self) {
567 let start_ticks = self.contention_start_ticks.get();
568 if start_ticks != Self::INVALID_TICKS {
569 E::on_finalized(start_ticks, self.callsite_info.get());
570 self.contention_start_ticks.set(Self::INVALID_TICKS);
571 }
572 }
573
574 #[inline]
575 fn set_callsite_info(&self, callsite_info: C) {
576 self.callsite_info.set(callsite_info);
577 }
578}
579
580/// Zero-sized storage when contention tracing is disabled.
581#[repr(C)]
582#[derive(Default)]
583pub struct TraceStorageDisabled;
584
585impl<C: Copy> TraceStorage<C> for TraceStorageDisabled {
586 const ENABLED: bool = false;
587
588 #[inline]
589 fn new(_callsite_info: C) -> Self {
590 Self
591 }
592
593 #[inline]
594 fn on_contention<E: TransactionHooks<CallsiteInfo = C>>(&self) {}
595
596 #[inline]
597 fn on_finalized<E: TransactionHooks<CallsiteInfo = C>>(&self) {}
598
599 #[inline]
600 fn set_callsite_info(&self, _callsite_info: C) {}
601}
602
603/// Helper type for selecting storage types from compile-time `bool` constants.
604pub struct Bool<const B: bool>;
605
606/// Maps a compile-time `bool` to [`AccountingStorageEnabled`] or [`AccountingStorageDisabled`].
607pub trait AccountingSelector {
608 /// Selected accounting storage type.
609 type Storage: AccountingStorage;
610}
611
612impl AccountingSelector for Bool<true> {
613 type Storage = AccountingStorageEnabled;
614}
615
616impl AccountingSelector for Bool<false> {
617 type Storage = AccountingStorageDisabled;
618}
619
620/// Type alias selecting [`AccountingStorageEnabled`] when `ENABLED` is `true`, or
621/// [`AccountingStorageDisabled`] when `ENABLED` is `false`.
622pub type SelectAccounting<const ENABLED: bool> = <Bool<ENABLED> as AccountingSelector>::Storage;
623
624/// Maps a compile-time `bool` to [`TraceStorageEnabled<C>`] or [`TraceStorageDisabled`].
625pub trait TracingSelector<C: Copy> {
626 /// Selected trace storage type.
627 type Storage: TraceStorage<C>;
628}
629
630impl<C: Copy> TracingSelector<C> for Bool<true> {
631 type Storage = TraceStorageEnabled<C>;
632}
633
634impl<C: Copy> TracingSelector<C> for Bool<false> {
635 type Storage = TraceStorageDisabled;
636}
637
638/// Type alias selecting [`TraceStorageEnabled<C>`] when `ENABLED` is `true`, or
639/// [`TraceStorageDisabled`] when `ENABLED` is `false`.
640pub type SelectTracing<const ENABLED: bool, C> = <Bool<ENABLED> as TracingSelector<C>>::Storage;
641
642/// Trait implemented by environment-specific state savers (such as IRQ-save and preempt-disable
643/// guards) managed by [`ChainLockTransaction::until_done`].
644pub trait StateSaver<T> {
645 /// Relaxes the saved state (e.g., unregistering the transaction, re-enabling interrupts /
646 /// preemption, waiting for conflict resolution, re-disabling interrupts / preemption, and
647 /// re-registering the transaction) during a backoff cycle.
648 fn relax(&mut self, transaction: &T);
649}
650
651/// Trait implemented by compile-time state option tags passed to
652/// [`ChainLockTransaction::until_done`].
653pub trait StateOption<T> {
654 /// The RAII [`StateSaver`] type instantiated for this option.
655 type Saver: StateSaver<T>;
656
657 /// Saves the environment state for the duration of the transaction and returns the RAII
658 /// [`StateSaver`].
659 fn save_state(&self) -> Self::Saver;
660}
661
662/// Wrapper for a transaction state option tag.
663#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
664pub struct Option<O>(pub O);
665
666impl<O> Option<O> {
667 /// Creates a new `Option` wrapping `option`.
668 #[inline]
669 pub const fn new(option: O) -> Self {
670 Self(option)
671 }
672}
673
674impl<T, O: StateOption<T>> StateOption<T> for Option<O> {
675 type Saver = O::Saver;
676
677 #[inline]
678 fn save_state(&self) -> Self::Saver {
679 self.0.save_state()
680 }
681}
682
683/// Environment-specific hooks required by [`ChainLockTransaction`].
684///
685/// # Safety
686///
687/// Implementors must ensure that:
688/// - [`Self::active`] returns the exact pointer most recently passed to [`Self::set_active`] on
689/// the current execution context (or `None` if [`Self::set_active`] has not been called or was
690/// most recently called with `None` on the current execution context).
691/// - The storage backing [`Self::active`] and [`Self::set_active`] is strictly local to the
692/// current execution context (e.g., per-CPU or thread-local storage) and is never shared across
693/// concurrently executing contexts.
694pub unsafe trait TransactionHooks: Sized + 'static {
695 /// State used by `ChainLock` to store contended lock state for backoff mitigation
696 /// (e.g., `AtomicU32` for `cpu_mask_t` in the Zircon kernel).
697 type ContentionState: ContentionState;
698
699 /// Debugging/tracing information that identifies the code location of the transaction.
700 type CallsiteInfo: Copy;
701
702 /// Storage for debug lock accounting (`SelectAccounting<ENABLED>`).
703 type Accounting: AccountingStorage;
704
705 /// Storage for contention spin tracing (`SelectTracing<ENABLED, Self::CallsiteInfo>`).
706 type Tracing: TraceStorage<Self::CallsiteInfo>;
707
708 /// Additional environment-specific state stored in each transaction instance
709 /// (e.g., `Cell<u64>` for `conflict_id_` in the Zircon kernel, or `()` when none is needed).
710 type ExtraState: Default;
711
712 /// Returns a pointer to the active chain lock transaction for the current execution context
713 /// (e.g., current CPU), or `None` if none is active.
714 fn active() -> core::option::Option<NonNull<ChainLockTransaction<Self>>>;
715
716 /// Sets the active chain lock transaction for the current execution context.
717 ///
718 /// # Safety
719 ///
720 /// The caller must ensure that `transaction` is either `None` or points to a valid
721 /// `ChainLockTransaction<Self>` that remains alive and pinned at its memory address for as
722 /// long as it remains registered as the active transaction on the current execution context.
723 unsafe fn set_active(transaction: core::option::Option<NonNull<ChainLockTransaction<Self>>>);
724
725 /// Performs an architecture-specific CPU relax/yield operation.
726 fn yield_cpu();
727
728 /// Allocates a reserved token for use with
729 /// [`ChainLockTransaction::save_state_and_use_reserved_token`] and
730 /// [`ChainLockTransaction::save_state_initial`].
731 fn allocate_reserved_token() -> Token;
732
733 /// Gets the current monotonic ticks for contention spin tracing.
734 #[inline]
735 fn get_current_ticks() -> i64 {
736 0
737 }
738
739 /// Emits a trace event or updates counters when a contended transaction is finalized.
740 #[inline]
741 fn on_finalized(_contention_start_ticks: i64, _callsite_info: Self::CallsiteInfo) {}
742
743 /// Updates counters or state when a transaction backs off (invoked after incrementing
744 /// `backoff_count`).
745 #[inline]
746 fn on_backoff(_transaction: &ChainLockTransaction<Self>) {}
747
748 /// Handles backoff mitigation when contention with a higher-priority transaction is observed.
749 ///
750 /// Returns `true` if the backoff was recorded and the caller should unwind held locks, or
751 /// `false` if the lock's token changed and the caller should retry acquisition.
752 fn record_backoff_or_retry(
753 transaction: &ChainLockTransaction<Self>,
754 observed_token: Token,
755 lock_state: &AtomicToken,
756 contention_state: &Self::ContentionState,
757 ) -> bool;
758
759 /// Carefully sequences the operations required to release a `ChainLock` and notify backed-off
760 /// contenders.
761 fn release_and_notify<F: FnOnce()>(
762 transaction: &ChainLockTransaction<Self>,
763 contention_state: &Self::ContentionState,
764 do_release: F,
765 );
766}
767
768/// Environment-independent framework for chain lock transactions.
769///
770/// Chain lock transactions are a layer on top of chain locks that track the state necessary for
771/// entities (i.e. CPUs) to acquire a series of one or more chain locks and correctly handle
772/// arbitration with other entities contending for the same locks.
773#[repr(C)]
774pub struct ChainLockTransaction<E: TransactionHooks> {
775 token: Cell<Token>,
776 state: Cell<State>,
777 accounting: E::Accounting,
778 tracing: E::Tracing,
779 backoff_count: Cell<u64>,
780 extra: E::ExtraState,
781 _pin: PhantomData<(*mut (), PhantomPinned)>,
782}
783
784impl<E: TransactionHooks> ChainLockTransaction<E> {
785 /// Token value representing an unlocked `ChainLock`.
786 pub const UNLOCKED_TOKEN_VALUE: u64 = UNLOCKED_TOKEN_VALUE;
787
788 /// Number of reserved token values (`1..=RESERVED_TOKEN_VALUES`).
789 pub const RESERVED_TOKEN_VALUES: u64 = RESERVED_TOKEN_VALUES;
790
791 /// Initial value for the global dynamic transaction token generator.
792 pub const INITIAL_TOKEN_VALUE: u64 = INITIAL_TOKEN_VALUE;
793
794 /// Maximum possible token value, used transiently during lock release.
795 pub const MAX_TOKEN_VALUE: u64 = MAX_TOKEN_VALUE;
796
797 /// Whether debug lock accounting is enabled for this transaction type.
798 pub const DEBUG_ACCOUNTING_ENABLED: bool = E::Accounting::ENABLED;
799
800 /// Whether contention spin tracing is enabled for this transaction type.
801 pub const CONTENTION_TRACING_ENABLED: bool = E::Tracing::ENABLED;
802
803 /// Constant value of [`DoneType`] for completing a `()`-returning transaction.
804 pub const DONE: DoneType = DONE;
805
806 /// Returns the current [`Token`] of this transaction.
807 #[inline]
808 pub fn token(&self) -> Token {
809 self.token.get()
810 }
811
812 /// Allocates the next monotonically increasing dynamic transaction [`Token`] from the global
813 /// token generator.
814 #[inline]
815 pub fn next_token() -> Token {
816 // SAFETY: Accessing the extern static `concurrent_chainlock_transaction_token_generator`
817 // and calling `fetch_add` on it is sound because it is defined in C++ with static storage
818 // duration as a lock-free 64-bit atomic integer.
819 let raw = unsafe {
820 concurrent_chainlock_transaction_token_generator.fetch_add(1, Ordering::Relaxed)
821 };
822 Token::from_raw(raw)
823 }
824
825 /// Returns the reserved [`Token`] for `reserved_index` (e.g., a CPU number).
826 ///
827 /// Asserts in debug builds that `reserved_index < Self::RESERVED_TOKEN_VALUES`.
828 #[inline]
829 pub fn reserved_token(reserved_index: usize) -> Token {
830 debug_assert!((reserved_index as u64) < Self::RESERVED_TOKEN_VALUES);
831 Token::from_raw((reserved_index as u64) + 1)
832 }
833
834 /// Returns the current [`State`] of this transaction.
835 #[inline]
836 pub fn state(&self) -> State {
837 self.state.get()
838 }
839
840 /// Updates the [`State`] of this transaction.
841 #[inline]
842 pub(crate) fn set_state(&self, state: State) {
843 self.state.set(state);
844 }
845
846 // Replace a token value when saving or restoring transaction state. Both tokens must be valid
847 // and one or the other must be reserved.
848 #[inline]
849 pub(crate) fn replace_token(&self, replacement: Token) {
850 let current = self.token.get();
851 debug_assert!(current.is_valid());
852 debug_assert!(replacement.is_valid());
853 debug_assert_ne!(current.is_reserved(), replacement.is_reserved());
854 self.token.set(replacement);
855 }
856
857 /// Provides a common sequence for executing a chain lock transaction and the required
858 /// backoff arbitration logic.
859 ///
860 /// The caller is responsible for providing three arguments:
861 /// 1. An implementation of [`StateOption<Self>`] (such as [`Option<O>`]): Used to instantiate
862 /// the environment-provided [`StateSaver`] type, indicating the relevant states to save,
863 /// such as IRQ enable flags, preemption flags, etc., and restore after each transaction
864 /// attempt.
865 /// 2. An instance of [`E::CallsiteInfo`](TransactionHooks::CallsiteInfo): Used for debugging
866 /// and tracing to identify the code location of the transaction.
867 /// 3. A callable implementing a single transaction attempt: This can be a function or closure
868 /// accepting a [`&ChainLockTransactionToken<Self>`](ChainLockTransactionToken) and
869 /// returning a type implementing [`IntoTransactionResult`].
870 ///
871 /// The callable should match one of the following signatures:
872 /// a. `FnMut(&ChainLockTransactionToken<Self>) -> TransactionResult<()>`: A transaction that
873 /// has a return type `()`.
874 /// b. `FnMut(&ChainLockTransactionToken<Self>) -> TransactionResult<T>`: A transaction that
875 /// has a user-defined return type `T`.
876 /// c. `FnMut(&ChainLockTransactionToken<Self>) -> DoneType`: A transaction that has an implied
877 /// return type `()`.
878 ///
879 /// The overall return type of `until_done` is either `T` or `()`, depending on the signature
880 /// of `callable`, as described above.
881 ///
882 /// A basic transaction is structured as follows:
883 ///
884 /// 1. Define a callable that will be executed until the transaction is complete. In this
885 /// example, the callable will return `zx_status_t` when the transaction is complete, which
886 /// will become the return value of `until_done`.
887 ///
888 /// ```ignore
889 /// let do_transaction =
890 /// |token: &ChainLockTransactionToken| -> TransactionResult<zx_status_t> {
891 /// if !object.get_lock().acquire_or_backoff(token) {
892 /// return Action::Backoff.into(); // Execute the backoff protocol and re-enter.
893 /// }
894 ///
895 /// if restart_transaction_condition {
896 /// object.get_lock().release(token);
897 /// return Action::Restart.into(); // Restart the transaction and re-enter.
898 /// }
899 ///
900 /// if !locked_operation_on_object(object, token) {
901 /// object.get_lock().release(token);
902 /// // Complete the transaction, returning the error to the caller.
903 /// return TransactionResult::Value(ZX_ERR_BAD_STATE);
904 /// }
905 ///
906 /// object.get_lock().release(token);
907 /// // Complete the transaction, returning success to the caller.
908 /// TransactionResult::Value(ZX_OK)
909 /// };
910 /// ```
911 ///
912 /// In cases where the user return type is `()`, the callable can return
913 /// [`ChainLockTransaction::DONE`] (or `DONE.into()` / [`TransactionResult::DONE`]) to complete
914 /// the transaction.
915 ///
916 /// 2. Execute the transaction using the given options and callable. In this example, the
917 /// constant `STATE_OPTION_A` is a value of type [`Option<O>`] with an environment-specific
918 /// option value to pass to the state saver. The callable is invoked cyclically, with
919 /// intervening backoffs, until a terminal value (of a user-defined return type in this
920 /// case) is returned.
921 ///
922 /// ```ignore
923 /// return ChainLockTransaction::until_done(
924 /// STATE_OPTION_A,
925 /// clt_callsite!("foo"),
926 /// do_transaction,
927 /// );
928 /// ```
929 #[inline]
930 pub fn until_done<O, F, R>(
931 option: O,
932 callsite_info: E::CallsiteInfo,
933 mut callable: F,
934 ) -> R::Output
935 where
936 O: StateOption<Self>,
937 F: FnMut(&ChainLockTransactionToken<Self>) -> R,
938 R: IntoTransactionResult,
939 {
940 // Instantiate the state saver type with the supplied option to take care of state save,
941 // restore, and relax operations.
942 //
943 // Declared before `transaction` so that in Rust's reverse-declaration drop order,
944 // `transaction` is dropped (and unregistered) BEFORE `state_saver` restores interrupts /
945 // preemption state.
946 let mut state_saver = option.save_state();
947
948 // Instantiate the transaction to store the transaction state and register it as the
949 // active transaction once placed in its stack slot.
950 let transaction = Self::new_unregistered(callsite_info, 0, false);
951 // SAFETY: `transaction` remains alive and unmoved in its stack slot for the duration of
952 // `until_done`, and `token` does not escape `until_done`. `register` asserts that no other
953 // transaction is currently active on the current execution context.
954 let token = unsafe { transaction.register() };
955
956 // Execute the callable until it returns a terminal value indicating the transaction is
957 // complete.
958 loop {
959 // Execute the transaction and get the returned result.
960 let result = callable(&token).into_transaction_result();
961
962 // If the result is not an action it is a terminal value. Complete the transaction and
963 // return the user defined value, if any.
964 match result {
965 TransactionResult::Value(value) => {
966 if transaction.state() == State::Started {
967 Self::finalize(&token);
968 }
969 transaction.set_state(State::Complete);
970 return value;
971 }
972 TransactionResult::Action(Action::Backoff) => {
973 debug_assert_eq!(transaction.state(), State::Started);
974 transaction.assert_num_locks_held(0);
975 transaction.assert_not_finalized();
976 transaction.backoff_count.set(transaction.backoff_count.get() + 1);
977 E::on_backoff(&transaction); // Notify after updating backoff_count.
978 state_saver.relax(&transaction);
979 debug_assert_eq!(E::active(), Some(NonNull::from(&transaction)));
980 }
981 TransactionResult::Action(Action::Restart) => {
982 debug_assert_eq!(transaction.state(), State::Finalized);
983 transaction.restart(callsite_info, &token);
984 }
985 }
986 }
987 }
988
989 /// Returns the number of times this transaction has backed off.
990 #[inline]
991 pub fn backoff_count(&self) -> u64 {
992 self.backoff_count.get()
993 }
994
995 /// Returns a reference to the environment-specific extra state (e.g., `conflict_id_` in the
996 /// kernel).
997 #[inline]
998 pub const fn extra(&self) -> &E::ExtraState {
999 &self.extra
1000 }
1001
1002 /// Asserts that at least one lock is currently held by this transaction (when debug accounting
1003 /// is enabled).
1004 #[inline]
1005 pub fn assert_at_least_one_lock_held(&self) {
1006 if Self::DEBUG_ACCOUNTING_ENABLED {
1007 assert!(self.accounting.locks_held() > 0);
1008 }
1009 }
1010
1011 /// Asserts that at most one lock is currently held by this transaction (when debug accounting
1012 /// is enabled).
1013 #[inline]
1014 pub fn assert_at_most_one_lock_held(&self) {
1015 if Self::DEBUG_ACCOUNTING_ENABLED {
1016 assert!(self.accounting.locks_held() <= 1);
1017 }
1018 }
1019
1020 /// Asserts that exactly `expected` locks are currently held by this transaction (when debug
1021 /// accounting is enabled).
1022 #[inline]
1023 pub fn assert_num_locks_held(&self, expected: u32) {
1024 if Self::DEBUG_ACCOUNTING_ENABLED {
1025 let actual = self.accounting.locks_held();
1026 assert_eq!(expected, actual, "expected={expected}, actual={actual}");
1027 }
1028 }
1029
1030 /// Asserts that this transaction has been finalized (when debug accounting is enabled).
1031 #[inline]
1032 pub fn assert_finalized(&self) {
1033 if Self::DEBUG_ACCOUNTING_ENABLED {
1034 assert!(self.accounting.finalized());
1035 }
1036 }
1037
1038 /// Asserts that this transaction has not been finalized (when debug accounting is enabled).
1039 #[inline]
1040 pub fn assert_not_finalized(&self) {
1041 if Self::DEBUG_ACCOUNTING_ENABLED {
1042 assert!(!self.accounting.finalized());
1043 }
1044 }
1045
1046 /// Asserts in debug builds that a transaction is currently active and mints a
1047 /// [`ChainLockTransactionToken<Self>`].
1048 ///
1049 /// # Safety
1050 ///
1051 /// The caller must ensure that a `ChainLockTransaction<E>` is currently registered as active
1052 /// on the current execution context, and that the returned token is not used while that
1053 /// transaction is unregistered or after it is dropped.
1054 #[inline]
1055 pub unsafe fn assert_active() -> ChainLockTransactionToken<Self> {
1056 debug_assert!(E::active().is_some());
1057 ChainLockTransactionToken::new()
1058 }
1059
1060 /// Mints a [`ChainLockTransactionToken<Self>`] without checking at runtime for efficiency in
1061 /// trivial cases.
1062 ///
1063 /// # Safety
1064 ///
1065 /// The caller must ensure that a `ChainLockTransaction<E>` is currently registered as active
1066 /// on the current execution context, and that the returned token is not used while that
1067 /// transaction is unregistered or after it is dropped.
1068 #[inline]
1069 pub unsafe fn mark_active() -> ChainLockTransactionToken<Self> {
1070 ChainLockTransactionToken::new()
1071 }
1072
1073 /// Returns a pointer to the active transaction, or `None` if none is active.
1074 #[inline]
1075 pub fn active() -> core::option::Option<NonNull<Self>> {
1076 E::active()
1077 }
1078
1079 /// Returns a reference to the active transaction. Asserts in debug builds that a transaction
1080 /// is active.
1081 #[inline]
1082 pub fn active_ref(_token: &ChainLockTransactionToken<Self>) -> &Self {
1083 let ptr = E::active();
1084 debug_assert!(ptr.is_some());
1085 // SAFETY: `_token` is a valid `ChainLockTransactionToken<Self>`, which by its safety
1086 // invariant guarantees that `E::active()` is `Some` and points to a valid, initialized
1087 // `ChainLockTransaction<E>` that remains alive and pinned at its memory address on the
1088 // current execution context, with all interior mutations mediated through `Cell`.
1089 unsafe { ptr.unwrap_unchecked().as_ref() }
1090 }
1091
1092 /// Saves the pointer to the current transaction and allocates a reserved token to sync leaf
1093 /// chain locks to during critical sequences.
1094 #[inline]
1095 pub fn save_state_and_use_reserved_token(
1096 token: &ChainLockTransactionToken<Self>,
1097 ) -> SavedState<E> {
1098 let transaction = Self::active_ref(token);
1099 debug_assert!(!transaction.token().is_reserved());
1100 SavedState::with_replacement(transaction, E::allocate_reserved_token())
1101 }
1102
1103 /// Creates a saved state for a bare transaction used to kick off a new thread.
1104 #[inline]
1105 pub fn save_state_initial(&self, _token: &ChainLockTransactionToken<Self>) -> SavedState<E> {
1106 debug_assert_ne!(E::active(), Some(NonNull::from(self)));
1107 SavedState::with_replacement(self, E::allocate_reserved_token())
1108 }
1109
1110 /// Restores the active transaction using the given saved state.
1111 ///
1112 /// # Safety
1113 ///
1114 /// The caller must ensure that `saved_state` was produced by
1115 /// [`Self::save_state_and_use_reserved_token`] or [`Self::save_state_initial`] for a
1116 /// `ChainLockTransaction<E>` instance that remains alive and pinned at its memory address for
1117 /// as long as it remains registered as the active transaction on the current execution
1118 /// context.
1119 #[inline]
1120 pub unsafe fn restore_state(saved_state: SavedState<E>) {
1121 let transaction_ptr = saved_state.transaction();
1122 // SAFETY: The caller guarantees `saved_state` points to a valid, live `Self` instance on
1123 // the current execution context.
1124 let transaction = unsafe { transaction_ptr.as_ref() };
1125 debug_assert!(transaction.token().is_reserved());
1126 transaction.replace_token(saved_state.token());
1127 // SAFETY: The caller guarantees `transaction_ptr` points to a valid `Self` instance that
1128 // remains alive and pinned while registered as active.
1129 unsafe { E::set_active(Some(transaction_ptr)) };
1130 }
1131
1132 /// Directly finalizes the active transaction, ending an active contention tracing duration,
1133 /// if any.
1134 #[inline]
1135 pub fn finalize(token: &ChainLockTransactionToken<Self>) {
1136 let transaction = Self::active_ref(token);
1137 transaction.assert_not_finalized();
1138 transaction.set_finalized(true);
1139 }
1140
1141 /// Restarts this transaction, resetting key state including the callsite info.
1142 #[inline]
1143 pub fn restart(
1144 &self,
1145 callsite_info: E::CallsiteInfo,
1146 _token: &ChainLockTransactionToken<Self>,
1147 ) {
1148 self.assert_finalized();
1149 self.assert_at_most_one_lock_held();
1150 debug_assert!(self.token().is_valid());
1151 self.set_finalized(false);
1152 if Self::CONTENTION_TRACING_ENABLED {
1153 self.tracing.set_callsite_info(callsite_info);
1154 }
1155 }
1156
1157 /// Returns the number of locks currently held by this transaction.
1158 ///
1159 /// Requires `Self::DEBUG_ACCOUNTING_ENABLED` to be `true` at compile time.
1160 #[inline]
1161 pub fn locks_held(&self) -> u32 {
1162 const {
1163 assert!(Self::DEBUG_ACCOUNTING_ENABLED);
1164 }
1165 self.accounting.locks_held()
1166 }
1167
1168 /// Returns whether this transaction is currently finalized.
1169 ///
1170 /// Requires `Self::DEBUG_ACCOUNTING_ENABLED` to be `true` at compile time.
1171 #[inline]
1172 pub fn is_finalized(&self) -> bool {
1173 const {
1174 assert!(Self::DEBUG_ACCOUNTING_ENABLED);
1175 }
1176 self.accounting.finalized()
1177 }
1178
1179 /// Creates an unregistered bare transaction with the given initial state.
1180 ///
1181 /// Because Rust does not guarantee copy elision (RVO) when returning by value, callers
1182 /// creating a bare transaction outside [`Self::until_done`] must place the returned
1183 /// transaction in its stack variable first, and then call [`Self::register`] (or
1184 /// [`Self::restore_state`] with [`Self::save_state_initial`]). Note that `Drop` for
1185 /// [`ChainLockTransaction`] unconditionally calls [`Self::unregister`], so the transaction
1186 /// must be registered as active before it is dropped.
1187 #[inline]
1188 pub fn new_unregistered(
1189 callsite_info: E::CallsiteInfo,
1190 locks_held: u32,
1191 finalized: bool,
1192 ) -> Self {
1193 let state = if finalized { State::Finalized } else { State::Started };
1194 Self {
1195 token: Cell::new(Self::next_token()),
1196 state: Cell::new(state),
1197 accounting: E::Accounting::new(locks_held, finalized),
1198 tracing: E::Tracing::new(callsite_info),
1199 backoff_count: Cell::new(0),
1200 extra: E::ExtraState::default(),
1201 _pin: PhantomData,
1202 }
1203 }
1204
1205 /// Registers `self` as the active transaction for the current execution context and mints a
1206 /// [`ChainLockTransactionToken<Self>`].
1207 ///
1208 /// Asserts that no transaction is currently active.
1209 ///
1210 /// # Safety
1211 ///
1212 /// The caller must ensure that:
1213 /// - `self` remains alive and pinned at its current memory address (i.e. not moved or
1214 /// forgotten without unregistering) for as long as it remains registered as the active
1215 /// transaction on the current execution context.
1216 /// - The returned [`ChainLockTransactionToken<Self>`] is not used while `self` is unregistered
1217 /// or after `self` is dropped.
1218 #[inline]
1219 pub unsafe fn register(&self) -> ChainLockTransactionToken<Self> {
1220 assert!(E::active().is_none());
1221 // SAFETY: The caller guarantees `self` remains alive and pinned at its memory address
1222 // while registered as the active transaction on the current execution context.
1223 unsafe { E::set_active(Some(NonNull::from(self))) };
1224 ChainLockTransactionToken::new()
1225 }
1226
1227 /// Unregisters `self` as the active transaction for the current execution context.
1228 ///
1229 /// Asserts in debug builds that `self` is the currently active transaction.
1230 ///
1231 /// # Safety
1232 ///
1233 /// The caller must ensure that:
1234 /// - `self` is currently the active transaction on the current execution context.
1235 /// - No [`ChainLockTransactionToken<Self>`] associated with `self` is used to access the
1236 /// active transaction while `self` is unregistered.
1237 #[inline]
1238 pub unsafe fn unregister(&self) {
1239 debug_assert_eq!(E::active(), Some(NonNull::from(self)));
1240 // SAFETY: Passing `None` to `E::set_active` clears the active transaction pointer on the
1241 // current execution context.
1242 unsafe { E::set_active(None) };
1243 }
1244
1245 #[inline]
1246 pub(crate) fn set_finalized(&self, finalized: bool) {
1247 self.set_state(if finalized { State::Finalized } else { State::Started });
1248 if Self::DEBUG_ACCOUNTING_ENABLED {
1249 self.accounting.set_finalized(finalized);
1250 }
1251 if Self::CONTENTION_TRACING_ENABLED && finalized {
1252 self.tracing.on_finalized::<E>();
1253 }
1254 }
1255}
1256
1257impl<E: TransactionHooks> Drop for ChainLockTransaction<E> {
1258 #[inline]
1259 fn drop(&mut self) {
1260 // SAFETY: Unconditionally unregister `self` on drop (matching C++
1261 // `~ChainLockTransaction`), asserting in debug builds that `self` is the active
1262 // transaction. Because `self` is being dropped, no valid
1263 // `ChainLockTransactionToken<Self>` may access it after this point.
1264 unsafe {
1265 self.unregister();
1266 }
1267 }
1268}
1269
1270impl<E: TransactionHooks> Transaction for ChainLockTransaction<E> {
1271 type ContentionState = E::ContentionState;
1272
1273 #[inline]
1274 fn active() -> core::option::Option<NonNull<Self>> {
1275 E::active()
1276 }
1277
1278 #[inline]
1279 fn active_ref(token: &ChainLockTransactionToken<Self>) -> &Self {
1280 Self::active_ref(token)
1281 }
1282
1283 #[inline]
1284 fn yield_cpu() {
1285 E::yield_cpu();
1286 }
1287
1288 #[inline]
1289 fn token(&self) -> Token {
1290 Self::token(self)
1291 }
1292
1293 #[inline]
1294 fn assert_not_finalized(&self) {
1295 Self::assert_not_finalized(self);
1296 }
1297
1298 #[inline]
1299 fn assert_num_locks_held(&self, expected: u32) {
1300 Self::assert_num_locks_held(self, expected);
1301 }
1302
1303 #[inline]
1304 fn on_acquire(&self, _token: &ChainLockTransactionToken<Self>) {
1305 if Self::DEBUG_ACCOUNTING_ENABLED {
1306 self.accounting.on_acquire();
1307 }
1308 }
1309
1310 #[inline]
1311 fn on_release(&self, _token: &ChainLockTransactionToken<Self>) {
1312 if Self::DEBUG_ACCOUNTING_ENABLED {
1313 self.accounting.on_release();
1314 }
1315 }
1316
1317 #[inline]
1318 fn on_contention(&self, _token: &ChainLockTransactionToken<Self>) {
1319 if Self::CONTENTION_TRACING_ENABLED {
1320 self.tracing.on_contention::<E>();
1321 }
1322 }
1323
1324 #[inline]
1325 fn record_backoff_or_retry(
1326 &self,
1327 observed_token: Token,
1328 lock_state: &AtomicToken,
1329 contention_state: &Self::ContentionState,
1330 _token: &ChainLockTransactionToken<Self>,
1331 ) -> bool {
1332 E::record_backoff_or_retry(self, observed_token, lock_state, contention_state)
1333 }
1334
1335 #[inline]
1336 fn release_and_notify<F: FnOnce()>(
1337 &self,
1338 contention_state: &Self::ContentionState,
1339 do_release: F,
1340 _token: &ChainLockTransactionToken<Self>,
1341 ) {
1342 E::release_and_notify(self, contention_state, do_release);
1343 }
1344}
1345
1346// Verify layout parity across all 4 combinations of `(DEBUG_ACCOUNTING_ENABLED,
1347// CONTENTION_TRACING_ENABLED)` for both empty `ExtraState` (`concurrent::ChainLockTransaction`)
1348// and 8-byte `ExtraState` (`::ChainLockTransaction` in `kconcurrent` with `uint64_t
1349// conflict_id_`).
1350#[repr(C)]
1351#[derive(Clone, Copy, Default)]
1352struct DummyKernelCallsiteInfo {
1353 label: *const u8,
1354 line_number: u32,
1355}
1356
1357macro_rules! impl_dummy_hooks {
1358 ($name:ident, $debug:expr, $trace:expr, $extra:ty) => {
1359 struct $name;
1360 // SAFETY: `$name` is an uninstantiated type used solely for compile-time layout
1361 // assertions; `active()` always returns `None` and `set_active()` is never called at
1362 // runtime.
1363 unsafe impl TransactionHooks for $name {
1364 type ContentionState = AtomicU32;
1365 type CallsiteInfo = DummyKernelCallsiteInfo;
1366 type Accounting = SelectAccounting<$debug>;
1367 type Tracing = SelectTracing<$trace, Self::CallsiteInfo>;
1368 type ExtraState = $extra;
1369
1370 fn active() -> core::option::Option<NonNull<ChainLockTransaction<Self>>> {
1371 None
1372 }
1373 unsafe fn set_active(_: core::option::Option<NonNull<ChainLockTransaction<Self>>>) {}
1374 fn yield_cpu() {}
1375 fn allocate_reserved_token() -> Token {
1376 Token::UNLOCKED
1377 }
1378 fn record_backoff_or_retry(
1379 _: &ChainLockTransaction<Self>,
1380 _: Token,
1381 _: &AtomicToken,
1382 _: &Self::ContentionState,
1383 ) -> bool {
1384 true
1385 }
1386 fn release_and_notify<F: FnOnce()>(
1387 _: &ChainLockTransaction<Self>,
1388 _: &Self::ContentionState,
1389 do_release: F,
1390 ) {
1391 do_release();
1392 }
1393 }
1394 };
1395}
1396
1397impl_dummy_hooks!(BaseFalseFalse, false, false, ());
1398impl_dummy_hooks!(BaseTrueFalse, true, false, ());
1399impl_dummy_hooks!(BaseFalseTrue, false, true, ());
1400impl_dummy_hooks!(BaseTrueTrue, true, true, ());
1401
1402impl_dummy_hooks!(KernelFalseFalse, false, false, Cell<u64>);
1403impl_dummy_hooks!(KernelTrueFalse, true, false, Cell<u64>);
1404impl_dummy_hooks!(KernelFalseTrue, false, true, Cell<u64>);
1405impl_dummy_hooks!(KernelTrueTrue, true, true, Cell<u64>);
1406
1407zr::static_assert!(
1408 size_of::<ChainLockTransactionToken<ChainLockTransaction<BaseFalseFalse>>>() == 0
1409);
1410zr::static_assert!(
1411 align_of::<ChainLockTransactionToken<ChainLockTransaction<BaseFalseFalse>>>() == 1
1412);
1413zr::static_assert!(size_of::<Token>() == 8);
1414zr::static_assert!(align_of::<Token>() == 8);
1415zr::static_assert!(size_of::<AtomicToken>() == 8);
1416zr::static_assert!(align_of::<AtomicToken>() == 8);
1417zr::static_assert!(size_of::<SavedState<BaseFalseFalse>>() == 16);
1418zr::static_assert!(align_of::<SavedState<BaseFalseFalse>>() == 8);
1419zr::static_assert!(offset_of!(SavedState<BaseFalseFalse>, transaction) == 0);
1420zr::static_assert!(offset_of!(SavedState<BaseFalseFalse>, token) == 8);
1421zr::static_assert!(size_of::<State>() == 4);
1422zr::static_assert!(align_of::<State>() == 4);
1423
1424zr::static_assert!(size_of::<DummyKernelCallsiteInfo>() == 16);
1425zr::static_assert!(align_of::<DummyKernelCallsiteInfo>() == 8);
1426
1427zr::static_assert!(size_of::<AccountingStorageEnabled>() == 8);
1428zr::static_assert!(align_of::<AccountingStorageEnabled>() == 4);
1429zr::static_assert!(offset_of!(AccountingStorageEnabled, locks_held) == 0);
1430zr::static_assert!(offset_of!(AccountingStorageEnabled, finalized) == 4);
1431
1432// Base `concurrent::ChainLockTransaction` layout assertions:
1433zr::static_assert!(size_of::<ChainLockTransaction<BaseFalseFalse>>() == 24);
1434zr::static_assert!(align_of::<ChainLockTransaction<BaseFalseFalse>>() == 8);
1435zr::static_assert!(offset_of!(ChainLockTransaction<BaseFalseFalse>, token) == 0);
1436zr::static_assert!(offset_of!(ChainLockTransaction<BaseFalseFalse>, state) == 8);
1437zr::static_assert!(offset_of!(ChainLockTransaction<BaseFalseFalse>, backoff_count) == 16);
1438
1439zr::static_assert!(size_of::<ChainLockTransaction<BaseTrueFalse>>() == 32);
1440zr::static_assert!(align_of::<ChainLockTransaction<BaseTrueFalse>>() == 8);
1441zr::static_assert!(offset_of!(ChainLockTransaction<BaseTrueFalse>, token) == 0);
1442zr::static_assert!(offset_of!(ChainLockTransaction<BaseTrueFalse>, state) == 8);
1443zr::static_assert!(offset_of!(ChainLockTransaction<BaseTrueFalse>, accounting) == 12);
1444zr::static_assert!(offset_of!(ChainLockTransaction<BaseTrueFalse>, backoff_count) == 24);
1445
1446zr::static_assert!(size_of::<ChainLockTransaction<BaseFalseTrue>>() == 48);
1447zr::static_assert!(align_of::<ChainLockTransaction<BaseFalseTrue>>() == 8);
1448zr::static_assert!(offset_of!(ChainLockTransaction<BaseFalseTrue>, token) == 0);
1449zr::static_assert!(offset_of!(ChainLockTransaction<BaseFalseTrue>, state) == 8);
1450zr::static_assert!(offset_of!(ChainLockTransaction<BaseFalseTrue>, tracing) == 16);
1451zr::static_assert!(offset_of!(ChainLockTransaction<BaseFalseTrue>, backoff_count) == 40);
1452
1453zr::static_assert!(size_of::<ChainLockTransaction<BaseTrueTrue>>() == 56);
1454zr::static_assert!(align_of::<ChainLockTransaction<BaseTrueTrue>>() == 8);
1455zr::static_assert!(offset_of!(ChainLockTransaction<BaseTrueTrue>, token) == 0);
1456zr::static_assert!(offset_of!(ChainLockTransaction<BaseTrueTrue>, state) == 8);
1457zr::static_assert!(offset_of!(ChainLockTransaction<BaseTrueTrue>, accounting) == 12);
1458zr::static_assert!(offset_of!(ChainLockTransaction<BaseTrueTrue>, tracing) == 24);
1459zr::static_assert!(offset_of!(ChainLockTransaction<BaseTrueTrue>, backoff_count) == 48);
1460
1461// Kernel `::ChainLockTransaction` (with `uint64_t conflict_id_`) layout assertions:
1462zr::static_assert!(size_of::<ChainLockTransaction<KernelFalseFalse>>() == 32);
1463zr::static_assert!(offset_of!(ChainLockTransaction<KernelFalseFalse>, extra) == 24);
1464
1465zr::static_assert!(size_of::<ChainLockTransaction<KernelTrueFalse>>() == 40);
1466zr::static_assert!(offset_of!(ChainLockTransaction<KernelTrueFalse>, extra) == 32);
1467
1468zr::static_assert!(size_of::<ChainLockTransaction<KernelFalseTrue>>() == 56);
1469zr::static_assert!(offset_of!(ChainLockTransaction<KernelFalseTrue>, extra) == 48);
1470
1471zr::static_assert!(size_of::<ChainLockTransaction<KernelTrueTrue>>() == 64);
1472zr::static_assert!(offset_of!(ChainLockTransaction<KernelTrueTrue>, extra) == 56);