Skip to main content

ksync/
raw_kernel_mutex.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 crate::raw_lock::RawLock;
6use core::ffi::c_void;
7use pin_init::{PinInit, pin_data, pin_init_from_closure};
8
9#[cfg(feature = "lock_name_tracing")]
10const RAW_MUTEX_SIZE: usize = 32;
11#[cfg(not(feature = "lock_name_tracing"))]
12const RAW_MUTEX_SIZE: usize = 24;
13
14unsafe extern "C" {
15    fn cpp_mutex_destroy(mutex: *mut c_void);
16    fn cpp_mutex_acquire(lock: *mut c_void, entry_storage: *mut c_void);
17    fn cpp_mutex_release(lock: *mut c_void, entry_storage: *mut c_void);
18
19    fn cpp_critical_mutex_destroy(mutex: *mut c_void);
20    fn cpp_critical_mutex_acquire(lock: *mut c_void, entry_storage: *mut c_void) -> bool;
21    fn cpp_critical_mutex_release(
22        lock: *mut c_void,
23        entry_storage: *mut c_void,
24        should_clear: bool,
25    );
26}
27
28const MUTEX_MAGIC: u32 = 0x6D757478; // 'mutx'
29const INVALID_CPU: u32 = u32::MAX;
30
31const fn make_mutex_storage() -> [u8; RAW_MUTEX_SIZE] {
32    let mut bytes = [0u8; RAW_MUTEX_SIZE];
33    #[cfg(feature = "lock_name_tracing")]
34    let offset = 8;
35    #[cfg(not(feature = "lock_name_tracing"))]
36    let offset = 0;
37
38    let magic_bytes = MUTEX_MAGIC.to_ne_bytes();
39    let cpu_bytes = INVALID_CPU.to_ne_bytes();
40
41    bytes[offset] = magic_bytes[0];
42    bytes[offset + 1] = magic_bytes[1];
43    bytes[offset + 2] = magic_bytes[2];
44    bytes[offset + 3] = magic_bytes[3];
45
46    bytes[offset + 4] = cpu_bytes[0];
47    bytes[offset + 5] = cpu_bytes[1];
48    bytes[offset + 6] = cpu_bytes[2];
49    bytes[offset + 7] = cpu_bytes[3];
50
51    bytes
52}
53
54#[repr(C, align(8))]
55struct RawMutexStorage(zr::OpaqueBytes<RAW_MUTEX_SIZE>);
56
57#[derive(Default)]
58#[repr(C, align(8))]
59pub struct LockEntryStorage(zr::OpaqueBytes<40>);
60
61/// Opaque layout block matching the Zircon C++ Mutex exactly.
62#[pin_data(PinnedDrop)]
63#[repr(C)]
64pub struct RawMutex {
65    #[cfg(feature = "lock_dep")]
66    class_id: *const c_void,
67    storage: RawMutexStorage,
68}
69
70impl RawMutex {
71    pub const INIT: Self = Self::const_init(core::ptr::null());
72
73    /// Statically initializes a RawMutex in constant context.
74    pub const fn const_init(_class_id: *const c_void) -> Self {
75        Self {
76            #[cfg(feature = "lock_dep")]
77            class_id: _class_id,
78            storage: RawMutexStorage(zr::OpaqueBytes::new(make_mutex_storage())),
79        }
80    }
81
82    /// Returns a slice over the raw mutex storage bytes.
83    #[cfg(any(test, ktest))]
84    #[inline]
85    pub fn raw_storage_slice(&self) -> &[u8] {
86        // SAFETY: The storage is valid and allocated with RAW_MUTEX_SIZE bytes.
87        unsafe { core::slice::from_raw_parts(self.storage.0.get() as *const u8, RAW_MUTEX_SIZE) }
88    }
89}
90
91impl Default for RawMutex {
92    fn default() -> Self {
93        Self::const_init(core::ptr::null())
94    }
95}
96
97// SAFETY: RawMutex is safe to share and access across threads.
98unsafe impl Sync for RawMutex {}
99unsafe impl Send for RawMutex {}
100
101zr::unsafe_pinned_drop_ffi!(RawMutex, cpp_mutex_destroy);
102
103pub struct RawMutexPolicy;
104
105impl crate::LockPolicy<RawMutex> for RawMutexPolicy {
106    type AcquireArgs = ();
107    type GuardState = ();
108
109    #[inline]
110    unsafe fn acquire(
111        lock: &RawMutex,
112        entry: *mut LockEntryStorage,
113        _args: (),
114    ) -> Self::GuardState {
115        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
116        // that `entry` points to valid storage for a lockdep entry.
117        unsafe {
118            cpp_mutex_acquire(lock.as_mut_ptr(), entry as *mut c_void);
119        }
120    }
121
122    #[inline]
123    unsafe fn reacquire(
124        lock: &RawMutex,
125        entry: *mut LockEntryStorage,
126        state: &mut Self::GuardState,
127    ) {
128        *state = unsafe { Self::acquire(lock, entry, ()) };
129    }
130
131    #[inline]
132    unsafe fn release(lock: &RawMutex, entry: *mut LockEntryStorage, _state: Self::GuardState) {
133        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
134        // that `entry` points to valid storage for a lockdep entry.
135        unsafe {
136            cpp_mutex_release(lock.as_mut_ptr(), entry as *mut c_void);
137        }
138    }
139}
140
141impl crate::RawLock for RawMutex {
142    type LockEntry = LockEntryStorage;
143    type DefaultPolicy = RawMutexPolicy;
144
145    #[inline]
146    unsafe fn init(class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
147        // SAFETY: The closure initializes the provided slot in place before returning Ok(()).
148        unsafe {
149            pin_init_from_closure(move |slot: *mut Self| {
150                slot.write(Self::const_init(class_id));
151                Ok(())
152            })
153        }
154    }
155
156    #[inline]
157    fn as_mut_ptr(&self) -> *mut c_void {
158        self as *const Self as *mut Self as *mut c_void
159    }
160}
161
162/// Opaque layout block matching the Zircon C++ CriticalMutex exactly.
163#[pin_data(PinnedDrop)]
164#[repr(C)]
165pub struct RawCriticalMutex {
166    #[cfg(feature = "lock_dep")]
167    class_id: *const c_void,
168    storage: RawMutexStorage,
169}
170
171impl RawCriticalMutex {
172    pub const INIT: Self = Self::const_init(core::ptr::null());
173
174    /// Statically initializes a RawCriticalMutex in constant context.
175    pub const fn const_init(_class_id: *const c_void) -> Self {
176        Self {
177            #[cfg(feature = "lock_dep")]
178            class_id: _class_id,
179            storage: RawMutexStorage(zr::OpaqueBytes::new(make_mutex_storage())),
180        }
181    }
182
183    /// Returns a slice over the raw critical mutex storage bytes.
184    #[cfg(any(test, ktest))]
185    #[inline]
186    pub fn raw_storage_slice(&self) -> &[u8] {
187        // SAFETY: The storage is valid and allocated with RAW_MUTEX_SIZE bytes.
188        unsafe { core::slice::from_raw_parts(self.storage.0.get() as *const u8, RAW_MUTEX_SIZE) }
189    }
190}
191
192impl Default for RawCriticalMutex {
193    fn default() -> Self {
194        Self::const_init(core::ptr::null())
195    }
196}
197
198// SAFETY: RawCriticalMutex is safe to share and access across threads.
199unsafe impl Sync for RawCriticalMutex {}
200unsafe impl Send for RawCriticalMutex {}
201
202zr::unsafe_pinned_drop_ffi!(RawCriticalMutex, cpp_critical_mutex_destroy);
203
204pub struct RawCriticalMutexPolicy;
205
206impl crate::LockPolicy<RawCriticalMutex> for RawCriticalMutexPolicy {
207    type AcquireArgs = ();
208    type GuardState = bool;
209
210    #[inline]
211    unsafe fn acquire(
212        lock: &RawCriticalMutex,
213        entry: *mut LockEntryStorage,
214        _args: (),
215    ) -> Self::GuardState {
216        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
217        // that `entry` points to valid storage for a lockdep entry.
218        unsafe { cpp_critical_mutex_acquire(lock.as_mut_ptr(), entry as *mut c_void) }
219    }
220
221    #[inline]
222    unsafe fn reacquire(
223        lock: &RawCriticalMutex,
224        entry: *mut LockEntryStorage,
225        state: &mut Self::GuardState,
226    ) {
227        *state = unsafe { Self::acquire(lock, entry, ()) };
228    }
229
230    #[inline]
231    unsafe fn release(
232        lock: &RawCriticalMutex,
233        entry: *mut LockEntryStorage,
234        should_clear: Self::GuardState,
235    ) {
236        // SAFETY: The FFI call is safe because the lock is initialized, and the caller guarantees
237        // that `entry` points to valid storage for a lockdep entry.
238        unsafe {
239            cpp_critical_mutex_release(lock.as_mut_ptr(), entry as *mut c_void, should_clear);
240        }
241    }
242}
243
244impl crate::RawLock for RawCriticalMutex {
245    type LockEntry = LockEntryStorage;
246    type DefaultPolicy = RawCriticalMutexPolicy;
247
248    #[inline]
249    unsafe fn init(class_id: *const c_void) -> impl PinInit<Self, core::convert::Infallible> {
250        // SAFETY: The closure initializes the provided slot in place before returning Ok(()).
251        unsafe {
252            pin_init_from_closure(move |slot: *mut Self| {
253                slot.write(Self::const_init(class_id));
254                Ok(())
255            })
256        }
257    }
258
259    #[inline]
260    fn as_mut_ptr(&self) -> *mut c_void {
261        self as *const Self as *mut Self as *mut c_void
262    }
263}
264
265const _: () = {
266    #[cfg(feature = "lock_dep")]
267    const EXPECTED_SIZE: usize = if cfg!(feature = "lock_name_tracing") { 40 } else { 32 };
268    #[cfg(not(feature = "lock_dep"))]
269    const EXPECTED_SIZE: usize = if cfg!(feature = "lock_name_tracing") { 32 } else { 24 };
270
271    assert!(core::mem::size_of::<RawMutex>() == EXPECTED_SIZE);
272    assert!(core::mem::align_of::<RawMutex>() == 8);
273
274    assert!(core::mem::size_of::<RawCriticalMutex>() == EXPECTED_SIZE);
275    assert!(core::mem::align_of::<RawCriticalMutex>() == 8);
276};