Skip to main content

ksync/
kcell.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::lock_token::LockToken;
6use core::marker::PhantomData;
7
8/// A container cell that safe-guards data of type `T` using type-level lock class tracking.
9///
10/// `KCell` holds data that can only be safely accessed (read or written) by proving that the
11/// corresponding mutual exclusion lock of class `Class` is currently held by the current thread
12/// via a `LockToken`.
13#[repr(transparent)]
14pub struct KCell<T, Class> {
15    value: core::cell::UnsafeCell<T>,
16    _marker: PhantomData<Class>,
17}
18
19unsafe impl<T: Send, Class> Sync for KCell<T, Class> {}
20unsafe impl<T: Send, Class> Send for KCell<T, Class> {}
21
22impl<T, Class> KCell<T, Class> {
23    /// Creates a new `KCell` containing the specified `value`.
24    #[inline]
25    pub const fn new(value: T) -> Self {
26        Self { value: core::cell::UnsafeCell::new(value), _marker: PhantomData }
27    }
28
29    /// Access the guarded value immutably using a shared lock token.
30    ///
31    /// # Safety
32    ///
33    /// The caller must guarantee that:
34    /// 1. The provided `LockToken` belongs to the specific lock instance that guards this `KCell`
35    ///    (rather than a different lock of the same lock class `Class`).
36    /// 2. The lock is held continuously for the lifetime of the returned reference `'b`.
37    #[inline]
38    pub unsafe fn get<'b>(&self, _token: &'b LockToken<'_, Class>) -> &'b T {
39        // SAFETY: The caller guarantees that the correct lock instance is held continuously
40        // for the duration of the reference lifetime `'b`, ensuring safe immutable access to
41        // the inner value without data races.
42        unsafe { &*self.value.get() }
43    }
44
45    /// Access the guarded value mutably using a mutable lock token.
46    ///
47    /// # Safety
48    ///
49    /// The caller must guarantee that:
50    /// 1. The provided `LockToken` belongs to the specific lock instance that guards this `KCell`
51    ///    (rather than a different lock of the same lock class `Class`).
52    /// 2. The lock is held continuously for the lifetime of the returned reference `'b`.
53    #[inline]
54    pub unsafe fn get_mut<'b>(&self, _token: &'b mut LockToken<'_, Class>) -> &'b mut T {
55        // SAFETY: The caller guarantees that the correct lock instance is held continuously for the
56        // duration of the reference lifetime `'b`, and the exclusive mutable borrow of the
57        // `LockToken` ensures that no other active borrows of the same cell can co-exist,
58        // permitting safe mutable projection from the inner `UnsafeCell` without aliasing or data
59        // races.
60        unsafe { &mut *self.value.get() }
61    }
62
63    /// Returns a mutable raw pointer to the guarded value.
64    ///
65    /// # Safety
66    ///
67    /// The caller must guarantee that the lock instance guarding this `KCell` is held continuously
68    /// while dereferencing or accessing the returned raw pointer.
69    #[inline]
70    pub unsafe fn as_mut_ptr(&self, _token: &mut LockToken<'_, Class>) -> *mut T {
71        self.value.get()
72    }
73
74    /// Accesses the inner value mutably by bypassing the locking requirements using unique borrow
75    /// ownership.
76    #[inline]
77    pub fn get_inner_mut(&mut self) -> &mut T {
78        self.value.get_mut()
79    }
80
81    /// Projects a pinned reference to the cell to a pinned reference to the inner value.
82    #[inline]
83    pub fn get_pinned_mut(self: core::pin::Pin<&mut Self>) -> core::pin::Pin<&mut T> {
84        // SAFETY: `KCell` is `repr(transparent)` and contains `T` at the same address.
85        // Pinning is preserved because `KCell` does not move `T`.
86        unsafe { self.map_unchecked_mut(|s| s.get_inner_mut()) }
87    }
88
89    /// Unwraps the cell, returning the inner value.
90    #[inline]
91    pub fn into_inner(self) -> T {
92        self.value.into_inner()
93    }
94}
95
96impl<T: Default, Class> Default for KCell<T, Class> {
97    #[inline]
98    fn default() -> Self {
99        Self::new(T::default())
100    }
101}
102
103impl<T, Class> From<T> for KCell<T, Class> {
104    #[inline]
105    fn from(value: T) -> Self {
106        Self::new(value)
107    }
108}
109
110impl<T, Class> AsMut<T> for KCell<T, Class> {
111    #[inline]
112    fn as_mut(&mut self) -> &mut T {
113        self.get_inner_mut()
114    }
115}
116
117impl<T, Class> core::fmt::Debug for KCell<T, Class> {
118    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119        f.debug_struct("KCell")
120            .field("value", &"<locked>")
121            .field("class", &core::any::type_name::<Class>())
122            .finish()
123    }
124}
125
126/// Creates a `PinInit` wrapper for initializing a `KCell` with an inner initializer.
127#[inline]
128pub fn kcell_init<I, Class>(init: I) -> KCellInit<I, Class> {
129    KCellInit(init, PhantomData)
130}
131
132/// Initializer for `KCell`.
133pub struct KCellInit<I, Class>(I, PhantomData<Class>);
134
135// SAFETY: KCell is repr(transparent) and contains T at the same address.
136// Pinning is preserved because KCell doesn't move T.
137unsafe impl<T, Class, I, E> pin_init::PinInit<KCell<T, Class>, E> for KCellInit<I, Class>
138where
139    I: pin_init::PinInit<T, E>,
140{
141    unsafe fn __pinned_init(self, slot: *mut KCell<T, Class>) -> Result<(), E> {
142        // SAFETY: The caller guarantees slot is valid. KCell is repr(transparent)
143        // so slot has the same address and layout as T.
144        unsafe { self.0.__pinned_init(slot as *mut T) }
145    }
146}
147
148#[cfg(not(feature = "kernel"))]
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use lockdep::LockClass;
153
154    struct MyClass;
155    impl LockClass for MyClass {
156        const ID: *mut core::ffi::c_void = core::ptr::null_mut();
157    }
158
159    #[test]
160    fn test_kcell_default() {
161        let cell: KCell<u32, MyClass> = KCell::default();
162        unsafe {
163            let token = LockToken::new();
164            assert_eq!(*cell.get(&token), 0);
165        }
166    }
167
168    #[test]
169    fn test_kcell_exclusive_access() {
170        let mut cell: KCell<u32, MyClass> = KCell::new(10);
171        *cell.get_inner_mut() = 20;
172        let reference: &mut u32 = cell.as_mut();
173        *reference = 30;
174        let value = cell.into_inner();
175        assert_eq!(value, 30);
176    }
177
178    #[test]
179    fn test_kcell_as_mut_ptr() {
180        let cell: KCell<u32, MyClass> = KCell::new(100u32);
181        unsafe {
182            let mut token = LockToken::new();
183            let ptr = cell.as_mut_ptr(&mut token);
184            assert_eq!(*ptr, 100);
185        }
186    }
187
188    #[test]
189    fn test_kcell_debug() {
190        extern crate std;
191        let cell: KCell<u32, MyClass> = KCell::new(5);
192        let debug_str = std::format!("{:?}", cell);
193        assert!(debug_str.contains("KCell"));
194        assert!(debug_str.contains("<locked>"));
195        assert!(debug_str.contains("MyClass"));
196    }
197
198    #[test]
199    fn test_kcell_init() {
200        let init = unsafe {
201            pin_init::pin_init_from_closure(|slot: *mut u32| {
202                slot.write(42);
203                Ok::<(), core::convert::Infallible>(())
204            })
205        };
206        let cell_init = kcell_init::<_, MyClass>(init);
207
208        pin_init::stack_pin_init!(let cell = cell_init);
209        let cell: core::pin::Pin<&mut KCell<u32, MyClass>> = cell;
210
211        unsafe {
212            let token = LockToken::<MyClass>::new();
213            let val = cell.as_ref().get_ref().get(&token);
214            assert_eq!(*val, 42);
215        }
216    }
217}