Skip to main content

ksync/
kmutex.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::marker::PhantomData;
6use core::pin::Pin;
7use pin_init::{PinInit, pin_data, pin_init, pin_init_from_closure, pinned_drop};
8
9use crate::{LockToken, RawLock, RawMutex};
10use lockdep::LockClass;
11
12/// A safe, Zircon-compatible mutual exclusion lock supporting compile-time order validation.
13///
14/// `KMutex` wraps a platform-specific `RawLock` abstraction. It is pinned in memory to support FFI
15/// loop-detector active list registrations safely under the lock class `Class`.
16#[repr(transparent)] // Ensure KMutex has the same layout as the underlying RawLock M.
17#[pin_data]
18pub struct KMutex<Class: LockClass, M: RawLock = RawMutex> {
19    #[pin]
20    mutex: M,
21    _marker: PhantomData<Class>,
22}
23
24impl<Class: LockClass, M: RawLock> KMutex<Class, M> {
25    /// Create a new KMutex with a pre-initialized raw lock.
26    pub const fn new(mutex: M) -> Self {
27        Self { mutex, _marker: PhantomData }
28    }
29
30    /// Safe dynamic initialization of the validation lock inside pin context.
31    pub fn init() -> impl PinInit<Self, core::convert::Infallible> {
32        pin_init!(Self {
33            mutex <- unsafe { M::init(Self::class_id()) },
34            _marker: PhantomData,
35        })
36    }
37
38    /// Acquires the lock and registers the active loop node.
39    #[inline]
40    pub fn lock(&self) -> impl PinInit<KMutexGuard<'_, Class, M>, core::convert::Infallible> {
41        KMutexGuard::new(self)
42    }
43
44    const fn class_id() -> *const core::ffi::c_void {
45        if cfg!(feature = "lock_dep") { Class::ID } else { core::ptr::null() }
46    }
47}
48
49impl<Class: LockClass, M: RawLock> core::fmt::Debug for KMutex<Class, M> {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        f.debug_struct("KMutex").field("class", &core::any::type_name::<Class>()).finish()
52    }
53}
54
55/// A validation guard representing exclusive lock ownership and active list participation.
56///
57/// The guard is pinned in memory to ensure that its `lock_entry` pointer remains safe and valid
58/// inside the C++ loop detector active thread list.
59#[repr(C)]
60#[pin_data(PinnedDrop)]
61pub struct KMutexGuard<'a, Class: LockClass, M: RawLock = RawMutex> {
62    mutex: &'a KMutex<Class, M>,
63
64    #[pin]
65    lock_entry: M::LockEntry,
66
67    state: M::GuardState,
68
69    token: LockToken<'a, Class>,
70}
71
72impl<'a, Class: LockClass, M: RawLock> KMutexGuard<'a, Class, M> {
73    /// Creates a new stack-pinned validation guard initialization block.
74    pub fn new(mutex: &'a KMutex<Class, M>) -> impl PinInit<Self, core::convert::Infallible> {
75        // SAFETY: The closure correctly initializes all fields of the allocated `KMutexGuard`
76        // and satisfies all safety requirements of `pin_init_from_closure`.
77        unsafe {
78            pin_init_from_closure(move |this: *mut Self| -> Result<(), core::convert::Infallible> {
79                // SAFETY: `this` is a valid pointer to uninitialized memory allocated for
80                // `KMutexGuard`.
81
82                let mutex_addr = core::ptr::addr_of_mut!((*this).mutex);
83                core::ptr::write(mutex_addr, mutex);
84
85                let entry_addr = core::ptr::addr_of_mut!((*this).lock_entry);
86                core::ptr::write(entry_addr, M::LockEntry::default());
87
88                let state = mutex.mutex.acquire(entry_addr);
89
90                let state_addr = core::ptr::addr_of_mut!((*this).state);
91                core::ptr::write(state_addr, state);
92
93                let token_addr = core::ptr::addr_of_mut!((*this).token);
94                core::ptr::write(token_addr, LockToken::new());
95
96                Ok(())
97            })
98        }
99    }
100
101    /// Returns a shared reference to the lock proof `LockToken`.
102    #[inline]
103    pub fn token(&self) -> &LockToken<'a, Class> {
104        &self.token
105    }
106
107    /// Returns a mutable reference to the lock proof `LockToken` inside this pinned projection.
108    #[inline]
109    pub fn token_mut(self: Pin<&mut Self>) -> &mut LockToken<'a, Class> {
110        // SAFETY: Modifying the non-pinned raw `token` field does not violate pinning invariants
111        // since the token has no drop logic or pointer-location sensitivity.
112        let me = unsafe { self.get_unchecked_mut() };
113        &mut me.token
114    }
115}
116
117#[pinned_drop]
118impl<'a, Class: LockClass, M: RawLock> PinnedDrop for KMutexGuard<'a, Class, M> {
119    // SAFETY: The stack slot `lock_entry` remains valid and pinned on the stack until this drop
120    // block completes. Accessing the fields directly to release the raw lock and remove the
121    // active list node is safe and correct under the current thread context.
122    fn drop(self: Pin<&mut Self>) {
123        unsafe {
124            let me = self.get_unchecked_mut();
125            let entry_addr = &mut me.lock_entry as *mut _;
126            me.mutex.mutex.release(entry_addr, me.state);
127        }
128    }
129}
130
131#[cfg(not(feature = "kernel"))]
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use crate::{KCell, RawMutex, guarded};
136    use lockdep::LockClass;
137    use pin_init::{pin_init, stack_pin_init};
138
139    struct MyClass;
140    impl LockClass for MyClass {
141        const ID: *mut core::ffi::c_void = core::ptr::null_mut();
142    }
143
144    #[pin_init::pin_data]
145    struct MyStruct {
146        #[pin]
147        mu: KMutex<MyClass>,
148        data1: KCell<u32, MyClass>,
149        data2: KCell<i32, MyClass>,
150    }
151
152    #[test]
153    fn test_basic_token_access() {
154        stack_pin_init!(let s = pin_init!(MyStruct {
155            mu <- KMutex::init(),
156            data1: KCell::new(10),
157            data2: KCell::new(-5),
158        }));
159
160        lock!(let mut guard = s.mu.lock());
161
162        unsafe {
163            assert_eq!(*s.data1.get(guard.token()), 10);
164            assert_eq!(*s.data2.get(guard.token()), -5);
165        }
166        unsafe {
167            let token_mut = guard.as_mut().token_mut();
168            *s.data1.get_mut(token_mut) = 20;
169            assert_eq!(*s.data1.get(guard.token()), 20);
170        }
171    }
172
173    #[guarded]
174    struct MyGuardedStruct {
175        #[mutex]
176        mu: KMutex,
177        #[guarded_by(mu)]
178        data1: u32,
179        #[guarded_by(mu)]
180        data2: i32,
181    }
182
183    #[test]
184    fn test_macro_guarded() {
185        stack_pin_init!(let s = pin_init!(MyGuardedStruct {
186            mu <- KMutex::init(),
187            data1: 100.into(),
188            data2: (-50).into(),
189        }));
190
191        {
192            lock!(let mut guard = s.lock_mu());
193
194            // Safe individual field access
195            assert_eq!(*guard.data1(), 100);
196            assert_eq!(*guard.data2(), -50);
197
198            *guard.as_mut().data1_mut() = 200;
199            assert_eq!(*guard.data1(), 200);
200
201            // Safe disjoint/split access
202            let fields = guard.as_mut().fields_mut();
203            *fields.data1 += 50;
204            *fields.data2 += 50;
205        }
206
207        // Verify fields
208        lock!(let guard = s.lock_mu());
209        assert_eq!(*guard.data1(), 250);
210        assert_eq!(*guard.data2(), 0);
211    }
212
213    #[test]
214    fn test_kmutex_init() {
215        stack_pin_init!(let mu = KMutex::<MyClass>::init());
216        lock!(mu.lock());
217    }
218
219    #[test]
220    fn test_kmutex_debug() {
221        extern crate std;
222        stack_pin_init!(let mu = KMutex::<MyClass>::init());
223        let debug_str = std::format!("{:?}", mu);
224        assert!(debug_str.contains("KMutex"));
225    }
226
227    #[guarded]
228    struct MyMultiGuardedStruct {
229        #[mutex]
230        mu1: KMutex,
231        #[mutex]
232        mu2: KMutex,
233        #[guarded_by(mu1)]
234        data1: u32,
235        #[guarded_by(mu2)]
236        data2: i32,
237    }
238
239    #[test]
240    fn test_macro_multi_guarded() {
241        stack_pin_init!(let s = pin_init!(MyMultiGuardedStruct {
242            mu1 <- KMutex::init(),
243            mu2 <- KMutex::init(),
244            data1: 10.into(),
245            data2: 20.into(),
246        }));
247
248        lock!(let mut guard1 = s.lock_mu1());
249        lock!(let mut guard2 = s.lock_mu2());
250
251        assert_eq!(*guard1.data1(), 10);
252        assert_eq!(*guard2.data2(), 20);
253        *guard1.as_mut().data1_mut() = 15;
254        *guard2.as_mut().data2_mut() = 25;
255        assert_eq!(*guard1.data1(), 15);
256        assert_eq!(*guard2.data2(), 25);
257    }
258
259    #[guarded]
260    struct MyDefaultGuardedStruct {
261        #[mutex]
262        mu: KMutex,
263        #[guarded_by(mu)]
264        data: u32,
265    }
266
267    #[test]
268    fn test_derive_default_guarded() {
269        stack_pin_init!(let s = pin_init!(MyDefaultGuardedStruct {
270            mu <- KMutex::init(),
271            data: 0.into(),
272        }));
273        lock!(let guard = s.lock_mu());
274        assert_eq!(*guard.data(), 0);
275    }
276
277    #[guarded]
278    struct MyGenericLockGuardedStruct<L: RawLock> {
279        #[mutex]
280        mu: KMutex<L>,
281        #[guarded_by(mu)]
282        data: u32,
283    }
284
285    #[test]
286    fn test_macro_generic_lock_guarded() {
287        stack_pin_init!(let s = pin_init!(MyGenericLockGuardedStruct::<RawMutex> {
288            mu <- KMutex::init(),
289            data: 100.into(),
290        }));
291
292        lock!(let guard = s.lock_mu());
293        assert_eq!(*guard.data(), 100);
294    }
295
296    #[guarded]
297    struct MyGenericGuardedStruct<T> {
298        #[mutex]
299        mu: KMutex,
300        #[guarded_by(mu)]
301        data: T,
302    }
303
304    #[test]
305    fn test_macro_generic_guarded() {
306        stack_pin_init!(let s = pin_init!(MyGenericGuardedStruct::<u32> {
307            mu <- KMutex::init(),
308            data: 0.into(),
309        }));
310        lock!(let mut guard = s.lock_mu());
311        assert_eq!(*guard.data(), 0);
312
313        *guard.as_mut().data_mut() = 42;
314        assert_eq!(*guard.data(), 42);
315
316        let fields = guard.as_mut().fields_mut();
317        *fields.data = 100;
318
319        let fields_shared = guard.fields();
320        assert_eq!(*fields_shared.data, 100);
321    }
322
323    #[guarded]
324    struct MyExplicitParentGuardedStruct {
325        #[mutex]
326        mu: KMutex,
327        #[guarded_by(mu)]
328        data: u32,
329        pub label: &'static str,
330    }
331
332    impl MyExplicitParentGuardedStruct {
333        pub fn has_label(&self) -> bool {
334            !self.label.is_empty()
335        }
336    }
337
338    impl<'a> MyExplicitParentGuardedStructMuGuard<'a> {
339        pub fn process_with_context(self: Pin<&mut Self>) {
340            let me = unsafe { self.get_unchecked_mut() };
341            let has_label = me.parent.has_label();
342            let label = me.parent.label;
343            if has_label && label == "apply_update" {
344                unsafe {
345                    let mut_self = Pin::new_unchecked(me);
346                    let fields = mut_self.fields_mut();
347                    *fields.data = 100;
348                }
349            }
350        }
351    }
352
353    #[test]
354    fn test_macro_guard_explicit_parent_access() {
355        stack_pin_init!(let s = pin_init!(MyExplicitParentGuardedStruct {
356            mu <- KMutex::init(),
357            data: 0.into(),
358            label: "apply_update",
359        }));
360
361        {
362            lock!(let mut guard = s.lock_mu());
363            guard.as_mut().process_with_context();
364        }
365
366        lock!(let guard = s.lock_mu());
367        assert_eq!(*guard.data(), 100);
368    }
369}