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::{LockTokenRead, LockTokenWrite};
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
19// SAFETY: `KCell` synchronizes shared access via `LockToken`, so it is `Sync` when `T: Send`.
20unsafe impl<T: Send, Class> Sync for KCell<T, Class> {}
21// SAFETY: `KCell` owns `T`, so transferring it across threads is safe when `T: Send`.
22unsafe impl<T: Send, Class> Send for KCell<T, Class> {}
23
24impl<T, Class> KCell<T, Class> {
25    /// Creates a new `KCell` containing the specified `value`.
26    #[inline]
27    pub const fn new(value: T) -> Self {
28        Self { value: core::cell::UnsafeCell::new(value), _marker: PhantomData }
29    }
30
31    /// Access the guarded value immutably using a shared lock token.
32    ///
33    /// # Safety
34    ///
35    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
36    /// instance that guards this `KCell` (rather than a different lock of the same lock class
37    /// `Class`).
38    #[inline]
39    pub unsafe fn get<'b, Token, Idx>(&self, _token: &'b Token) -> &'b T
40    where
41        Token: LockTokenRead<Class, Idx>,
42    {
43        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
44        // instance that guards this cell.
45        unsafe { &*self.value.get() }
46    }
47
48    /// Access the guarded value mutably using a mutable lock token.
49    ///
50    /// # Safety
51    ///
52    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
53    /// instance that guards this `KCell` (rather than a different lock of the same lock class
54    /// `Class`).
55    #[inline]
56    pub unsafe fn get_mut<'b, Token, Idx>(&self, _token: &'b mut Token) -> &'b mut T
57    where
58        Token: LockTokenWrite<Class, Idx>,
59    {
60        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
61        // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
62        // ensures that no other active borrows of the same cell can co-exist.
63        unsafe { &mut *self.value.get() }
64    }
65
66    /// Access the guarded value as a pinned mutable reference using a mutable lock token.
67    ///
68    /// # Safety
69    ///
70    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
71    /// instance that guards this `KCell` (rather than a different lock of the same lock class
72    /// `Class`), and that this `KCell` is pinned in memory so its inner value is never moved.
73    #[inline]
74    pub unsafe fn get_pinned_mut_locked<'b, Token, Idx>(
75        &self,
76        token: &'b mut Token,
77    ) -> core::pin::Pin<&'b mut T>
78    where
79        Token: LockTokenWrite<Class, Idx>,
80    {
81        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
82        // instance that guards this cell, and that the cell is pinned in memory.
83        unsafe { core::pin::Pin::new_unchecked(self.get_mut(token)) }
84    }
85
86    /// Returns a const raw pointer to the guarded value.
87    ///
88    /// # Safety
89    ///
90    /// The caller must guarantee that a lock instance guarding this `KCell` is held continuously
91    /// while dereferencing or accessing the returned raw pointer.
92    #[inline]
93    pub unsafe fn as_ptr<Token, Idx>(&self, _token: &Token) -> *const T
94    where
95        Token: LockTokenRead<Class, Idx>,
96    {
97        self.value.get()
98    }
99
100    /// Returns a mutable raw pointer to the guarded value.
101    ///
102    /// # Safety
103    ///
104    /// The caller must guarantee that the lock instance(s) guarding this `KCell` are held
105    /// continuously while dereferencing or accessing the returned raw pointer.
106    #[inline]
107    pub unsafe fn as_mut_ptr<Token, Idx>(&self, _token: &mut Token) -> *mut T
108    where
109        Token: LockTokenWrite<Class, Idx>,
110    {
111        self.value.get()
112    }
113
114    /// Accesses the inner value mutably by bypassing the locking requirements using unique borrow
115    /// ownership.
116    #[inline]
117    pub fn get_inner_mut(&mut self) -> &mut T {
118        self.value.get_mut()
119    }
120
121    /// Projects a pinned reference to the cell to a pinned reference to the inner value.
122    #[inline]
123    pub fn get_pinned_mut(self: core::pin::Pin<&mut Self>) -> core::pin::Pin<&mut T> {
124        // SAFETY: `KCell` is `repr(transparent)` and contains `T` at the same address.
125        // Pinning is preserved because `KCell` does not move `T`.
126        unsafe { self.map_unchecked_mut(|s| s.get_inner_mut()) }
127    }
128
129    /// Initializes a `KCell` in place with an `Init` initializer for its inner value.
130    pub fn init<I, E>(init: I) -> impl pin_init::Init<Self, E>
131    where
132        I: pin_init::Init<T, E>,
133    {
134        kcell_init(init)
135    }
136
137    /// Initializes a `KCell` with a `PinInit` initializer for its inner value.
138    pub fn pin_init<I, E>(init: I) -> impl pin_init::PinInit<Self, E>
139    where
140        I: pin_init::PinInit<T, E>,
141    {
142        kcell_init(init)
143    }
144
145    /// Unwraps the cell, returning the inner value.
146    #[inline]
147    pub fn into_inner(self) -> T {
148        self.value.into_inner()
149    }
150}
151
152impl<T: Default, Class> Default for KCell<T, Class> {
153    #[inline]
154    fn default() -> Self {
155        Self::new(T::default())
156    }
157}
158
159impl<T, Class> From<T> for KCell<T, Class> {
160    #[inline]
161    fn from(value: T) -> Self {
162        Self::new(value)
163    }
164}
165
166impl<T, Class> AsMut<T> for KCell<T, Class> {
167    #[inline]
168    fn as_mut(&mut self) -> &mut T {
169        self.get_inner_mut()
170    }
171}
172
173impl<T, Class> core::fmt::Debug for KCell<T, Class> {
174    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
175        f.debug_struct("KCell")
176            .field("value", &"<locked>")
177            .field("class", &core::any::type_name::<Class>())
178            .finish()
179    }
180}
181
182/// Creates an initializer wrapper for initializing a `KCell` with an inner `Init` or `PinInit`
183/// initializer.
184#[inline]
185pub fn kcell_init<I, Class>(init: I) -> KCellInit<I, Class> {
186    KCellInit(init, PhantomData)
187}
188
189/// Initializer for `KCell`.
190pub struct KCellInit<I, Class>(I, PhantomData<Class>);
191
192// SAFETY: KCell is repr(transparent) and contains T at the same address.
193// Pinning is preserved because KCell doesn't move T.
194unsafe impl<T, Class, I, E> pin_init::PinInit<KCell<T, Class>, E> for KCellInit<I, Class>
195where
196    I: pin_init::PinInit<T, E>,
197{
198    unsafe fn __pinned_init(self, slot: *mut KCell<T, Class>) -> Result<(), E> {
199        // SAFETY: The caller guarantees slot is valid. KCell is repr(transparent)
200        // so slot has the same address and layout as T.
201        unsafe { self.0.__pinned_init(slot as *mut T) }
202    }
203}
204
205// SAFETY: KCell is repr(transparent) and contains T at the same address and layout.
206unsafe impl<T, Class, I, E> pin_init::Init<KCell<T, Class>, E> for KCellInit<I, Class>
207where
208    I: pin_init::Init<T, E>,
209{
210    unsafe fn __init(self, slot: *mut KCell<T, Class>) -> Result<(), E> {
211        // SAFETY: The caller guarantees slot is valid. KCell is repr(transparent)
212        // so slot has the same address and layout as T.
213        unsafe { self.0.__init(slot as *mut T) }
214    }
215}
216
217#[cfg(not(feature = "kernel"))]
218#[cfg(test)]
219mod tests {
220    use super::*;
221    use crate::lock_token::LockToken;
222    use lockdep::LockClass;
223
224    struct MyClass;
225    impl LockClass for MyClass {
226        const ID: *mut core::ffi::c_void = core::ptr::null_mut();
227    }
228
229    #[test]
230    fn test_kcell_default() {
231        let cell: KCell<u32, MyClass> = KCell::default();
232        // SAFETY: Single-threaded test with exclusive access to `cell`.
233        unsafe {
234            let token = LockToken::new();
235            assert_eq!(*cell.get(&token), 0);
236        }
237    }
238
239    #[test]
240    fn test_kcell_exclusive_access() {
241        let mut cell: KCell<u32, MyClass> = KCell::new(10);
242        *cell.get_inner_mut() = 20;
243        let reference: &mut u32 = cell.as_mut();
244        *reference = 30;
245        let value = cell.into_inner();
246        assert_eq!(value, 30);
247    }
248
249    #[test]
250    fn test_kcell_as_mut_ptr() {
251        let cell: KCell<u32, MyClass> = KCell::new(100u32);
252        // SAFETY: Single-threaded test with exclusive access to `cell`.
253        unsafe {
254            let mut token = LockToken::new();
255            let ptr = cell.as_mut_ptr(&mut token);
256            assert_eq!(*ptr, 100);
257        }
258    }
259
260    #[test]
261    fn test_kcell_debug() {
262        extern crate std;
263        let cell: KCell<u32, MyClass> = KCell::new(5);
264        let debug_str = std::format!("{:?}", cell);
265        assert!(debug_str.contains("KCell"));
266        assert!(debug_str.contains("<locked>"));
267        assert!(debug_str.contains("MyClass"));
268    }
269
270    #[test]
271    fn test_kcell_init() {
272        // SAFETY: Closure initializes `slot` in place before returning `Ok(())`.
273        let init = unsafe {
274            pin_init::pin_init_from_closure(|slot: *mut u32| {
275                slot.write(42);
276                Ok::<(), core::convert::Infallible>(())
277            })
278        };
279        let cell_init = kcell_init::<_, MyClass>(init);
280
281        pin_init::stack_pin_init!(let cell = cell_init);
282        let cell: core::pin::Pin<&mut KCell<u32, MyClass>> = cell;
283
284        // SAFETY: Single-threaded test with exclusive access to `cell`.
285        unsafe {
286            let token = LockToken::<MyClass>::new();
287            let val = cell.as_ref().get_ref().get(&token);
288            assert_eq!(*val, 42);
289        }
290
291        #[pin_init::pin_data]
292        struct UnpinnedHolder {
293            cell: KCell<[usize; 4], MyClass>,
294        }
295
296        pin_init::stack_pin_init!(let holder = pin_init::pin_init!(UnpinnedHolder {
297            cell <- KCell::<[usize; 4], MyClass>::init(pin_init::init_zeroed::<[usize; 4]>()),
298        }));
299        // SAFETY: Single-threaded test with exclusive access to `holder.cell`.
300        unsafe {
301            let token = LockToken::<MyClass>::new();
302            assert_eq!(*holder.as_ref().get_ref().cell.get(&token), [0usize; 4]);
303        }
304    }
305
306    #[test]
307    fn test_kcell_get_pinned_mut_locked() {
308        let cell = core::pin::pin!(KCell::<u32, MyClass>::new(7));
309        let cell: &KCell<u32, MyClass> = cell.as_ref().get_ref();
310
311        // SAFETY: Single-threaded test with exclusive access to pinned `cell`.
312        unsafe {
313            let mut token = LockToken::<MyClass>::new();
314            let mut pinned: core::pin::Pin<&mut u32> = cell.get_pinned_mut_locked(&mut token);
315            assert_eq!(*pinned, 7);
316            pinned.set(8);
317            *pinned.as_mut().get_mut() += 1;
318            assert_eq!(*cell.get(&token), 9);
319        }
320    }
321
322    struct SecondClass;
323    impl LockClass for SecondClass {
324        const ID: *mut core::ffi::c_void = core::ptr::null_mut();
325    }
326
327    struct ThirdClass;
328    impl LockClass for ThirdClass {
329        const ID: *mut core::ffi::c_void = core::ptr::null_mut();
330    }
331
332    #[test]
333    fn test_kcell_multi_lock() {
334        let cell = core::pin::pin!(KCell::<u32, (MyClass, SecondClass)>::new(10));
335        let cell: &KCell<u32, (MyClass, SecondClass)> = cell.as_ref().get_ref();
336        // SAFETY: Single-threaded test with exclusive access to `cell`.
337        unsafe {
338            let mut token1 = LockToken::<MyClass>::new();
339            let mut token2 = LockToken::<SecondClass>::new();
340
341            // Read with either single token via `get` and `as_ptr`.
342            assert_eq!(*cell.get(&token1), 10);
343            assert_eq!(*cell.get(&token2), 10);
344            assert_eq!(*cell.as_ptr(&token1), 10);
345            assert_eq!(*cell.as_ptr(&token2), 10);
346
347            // Mutate with joined tokens in either order.
348            *cell.get_mut(token1.join(&mut token2)) = 20;
349            assert_eq!(*cell.get(&token1), 20);
350
351            *cell.get_mut(token2.join(&mut token1)) = 30;
352            assert_eq!(*cell.get(&token2), 30);
353
354            // Test `as_mut_ptr` and `get_pinned_mut_locked` with joined tokens.
355            *cell.as_mut_ptr(token1.join(&mut token2)) = 40;
356            assert_eq!(*cell.get(&token1), 40);
357
358            let mut pinned = cell.get_pinned_mut_locked(token2.join(&mut token1));
359            pinned.set(50);
360            assert_eq!(*cell.get(&token2), 50);
361        }
362
363        let cell3: KCell<u32, (MyClass, SecondClass, ThirdClass)> = KCell::new(100);
364        // SAFETY: Single-threaded test with exclusive access to `cell3`.
365        unsafe {
366            let mut t1 = LockToken::<MyClass>::new();
367            let mut t2 = LockToken::<SecondClass>::new();
368            let mut t3 = LockToken::<ThirdClass>::new();
369
370            assert_eq!(*cell3.get(&t1), 100);
371            assert_eq!(*cell3.get(&t2), 100);
372            assert_eq!(*cell3.get(&t3), 100);
373
374            *cell3.get_mut(t1.join(&mut t2).join(&mut t3)) = 200;
375            assert_eq!(*cell3.get(&t3), 200);
376        }
377    }
378}