Skip to main content

ksync/
konce_cell.rs

1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7use core::cell::UnsafeCell;
8use core::marker::PhantomData;
9use core::mem::MaybeUninit;
10
11use crate::LockToken;
12
13/// A cell that provides lazy/one-time initialization synchronized by a lock class `Class`.
14///
15/// `KOnceCell` holds data of type `T` that is uninitialized when created, and can be initialized
16/// at most once by presenting proof (`LockToken`) of holding a lock of class `Class`.
17///
18/// Because access is token-gated by `Class`, initialization is completely free of atomic overhead.
19pub struct KOnceCell<T, Class> {
20    value: UnsafeCell<MaybeUninit<T>>,
21    initialized: UnsafeCell<bool>,
22    _marker: PhantomData<Class>,
23}
24
25unsafe impl<T: Send, Class> Sync for KOnceCell<T, Class> {}
26unsafe impl<T: Send, Class> Send for KOnceCell<T, Class> {}
27
28impl<T, Class> KOnceCell<T, Class> {
29    /// Creates a new uninitialized `KOnceCell`.
30    #[inline]
31    pub const fn new() -> Self {
32        Self {
33            value: UnsafeCell::new(MaybeUninit::uninit()),
34            initialized: UnsafeCell::new(false),
35            _marker: PhantomData,
36        }
37    }
38
39    /// Returns `true` if the cell has been initialized.
40    ///
41    /// # Safety
42    ///
43    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
44    /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
45    /// `Class`).
46    #[inline]
47    pub unsafe fn is_initialized(&self, _token: &LockToken<'_, Class>) -> bool {
48        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
49        // instance that guards this cell.
50        unsafe { *self.initialized.get() }
51    }
52
53    /// Accesses the initialized value immutably using a shared lock token.
54    /// Returns `None` if not yet initialized.
55    ///
56    /// # Safety
57    ///
58    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
59    /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
60    /// `Class`).
61    #[inline]
62    pub unsafe fn get<'b>(&self, _token: &'b LockToken<'_, Class>) -> Option<&'b T> {
63        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
64        // instance that guards this cell.
65        if unsafe { *self.initialized.get() } {
66            Some(unsafe { (*self.value.get()).assume_init_ref() })
67        } else {
68            None
69        }
70    }
71
72    /// Accesses the initialized value mutably using a mutable lock token.
73    /// Returns `None` if not yet initialized.
74    ///
75    /// # Safety
76    ///
77    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
78    /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
79    /// `Class`).
80    #[inline]
81    pub unsafe fn get_mut<'b>(&self, _token: &'b mut LockToken<'_, Class>) -> Option<&'b mut T> {
82        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
83        // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
84        // ensures that no other active borrows of the same cell can co-exist.
85        if unsafe { *self.initialized.get() } {
86            Some(unsafe { (*self.value.get()).assume_init_mut() })
87        } else {
88            None
89        }
90    }
91
92    /// Sets the value of the cell if uninitialized, returning `Err(value)` if already initialized.
93    ///
94    /// # Safety
95    ///
96    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
97    /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
98    /// `Class`).
99    #[inline]
100    pub unsafe fn set(&self, value: T, _token: &mut LockToken<'_, Class>) -> Result<(), T> {
101        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
102        // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
103        // ensures no concurrent access.
104        unsafe {
105            if *self.initialized.get() {
106                Err(value)
107            } else {
108                (*self.value.get()).write(value);
109                *self.initialized.get() = true;
110                Ok(())
111            }
112        }
113    }
114
115    /// Initializes the cell with the given closure if uninitialized, returning a mutable reference
116    /// to the contained value.
117    ///
118    /// # Safety
119    ///
120    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
121    /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
122    /// `Class`).
123    #[inline]
124    pub unsafe fn get_or_init<'b>(
125        &self,
126        f: impl FnOnce() -> T,
127        _token: &'b mut LockToken<'_, Class>,
128    ) -> &'b mut T {
129        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
130        // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
131        // ensures no concurrent access.
132        unsafe {
133            if !*self.initialized.get() {
134                (*self.value.get()).write(f());
135                *self.initialized.get() = true;
136            }
137            (*self.value.get()).assume_init_mut()
138        }
139    }
140
141    /// Initializes the cell with the given fallible closure if uninitialized, returning a mutable
142    /// reference to the contained value or the error.
143    ///
144    /// # Safety
145    ///
146    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
147    /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
148    /// `Class`).
149    #[inline]
150    pub unsafe fn get_or_try_init<'b, E>(
151        &self,
152        f: impl FnOnce() -> Result<T, E>,
153        _token: &'b mut LockToken<'_, Class>,
154    ) -> Result<&'b mut T, E> {
155        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
156        // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
157        // ensures no concurrent access.
158        unsafe {
159            if !*self.initialized.get() {
160                let val = f()?;
161                (*self.value.get()).write(val);
162                *self.initialized.get() = true;
163            }
164            Ok((*self.value.get()).assume_init_mut())
165        }
166    }
167
168    /// Initializes the cell in-place with `PinInit` if uninitialized, returning a mutable reference
169    /// to the contained value or the initialization error.
170    ///
171    /// # Safety
172    ///
173    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
174    /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
175    /// `Class`).
176    #[inline]
177    pub unsafe fn get_or_pin_init<'b, E>(
178        &self,
179        init: impl pin_init::PinInit<T, E>,
180        _token: &'b mut LockToken<'_, Class>,
181    ) -> Result<&'b mut T, E> {
182        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
183        // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
184        // ensures no concurrent access.
185        unsafe {
186            if !*self.initialized.get() {
187                init.__pinned_init((*self.value.get()).as_mut_ptr())?;
188                *self.initialized.get() = true;
189            }
190            Ok((*self.value.get()).assume_init_mut())
191        }
192    }
193
194    /// Accesses the initialized value immutably without checking for a `LockToken`.
195    /// Returns `None` if not yet initialized.
196    ///
197    /// # Safety
198    ///
199    /// The caller must guarantee that the specific lock instance guarding this `KOnceCell` is held.
200    #[inline]
201    pub unsafe fn get_unchecked(&self) -> Option<&T> {
202        // SAFETY: The caller guarantees that the specific lock instance protecting this cell
203        // is held.
204        if unsafe { *self.initialized.get() } {
205            Some(unsafe { (*self.value.get()).assume_init_ref() })
206        } else {
207            None
208        }
209    }
210
211    /// Accesses the initialized value mutably without checking for a `LockToken`.
212    /// Returns `None` if not yet initialized.
213    ///
214    /// # Safety
215    ///
216    /// The caller must guarantee that the specific lock instance guarding this `KOnceCell` is held
217    /// exclusively without concurrent access.
218    #[allow(clippy::mut_from_ref)]
219    #[inline]
220    pub unsafe fn get_mut_unchecked(&self) -> Option<&mut T> {
221        // SAFETY: The caller guarantees that the specific lock instance protecting this cell is
222        // held exclusively without concurrent access.
223        if unsafe { *self.initialized.get() } {
224            Some(unsafe { (*self.value.get()).assume_init_mut() })
225        } else {
226            None
227        }
228    }
229
230    /// Initializes the cell with the given closure without requiring a `LockToken`.
231    ///
232    /// # Safety
233    ///
234    /// The caller must guarantee that the specific lock instance guarding this `KOnceCell` is held
235    /// exclusively without concurrent access.
236    #[allow(clippy::mut_from_ref)]
237    #[inline]
238    pub unsafe fn get_or_init_unchecked(&self, f: impl FnOnce() -> T) -> &mut T {
239        // SAFETY: The caller guarantees that the specific lock instance protecting this cell is
240        // held exclusively without concurrent access.
241        unsafe {
242            if !*self.initialized.get() {
243                (*self.value.get()).write(f());
244                *self.initialized.get() = true;
245            }
246            (*self.value.get()).assume_init_mut()
247        }
248    }
249
250    /// Initializes the cell with the given fallible closure without requiring a `LockToken`.
251    ///
252    /// # Safety
253    ///
254    /// The caller must guarantee that the specific lock instance guarding this `KOnceCell` is held
255    /// exclusively without concurrent access.
256    #[allow(clippy::mut_from_ref)]
257    #[inline]
258    pub unsafe fn get_or_try_init_unchecked<E>(
259        &self,
260        f: impl FnOnce() -> Result<T, E>,
261    ) -> Result<&mut T, E> {
262        // SAFETY: The caller guarantees that the specific lock instance protecting this cell is
263        // held exclusively without concurrent access.
264        unsafe {
265            if !*self.initialized.get() {
266                let val = f()?;
267                (*self.value.get()).write(val);
268                *self.initialized.get() = true;
269            }
270            Ok((*self.value.get()).assume_init_mut())
271        }
272    }
273
274    /// Initializes the cell in-place with `PinInit` without requiring a `LockToken`.
275    ///
276    /// # Safety
277    ///
278    /// The caller must guarantee that the specific lock instance guarding this `KOnceCell` is held
279    /// exclusively without concurrent access.
280    #[allow(clippy::mut_from_ref)]
281    #[inline]
282    pub unsafe fn get_or_pin_init_unchecked<E>(
283        &self,
284        init: impl pin_init::PinInit<T, E>,
285    ) -> Result<&mut T, E> {
286        // SAFETY: The caller guarantees that the specific lock instance protecting this cell is
287        // held exclusively without concurrent access.
288        unsafe {
289            if !*self.initialized.get() {
290                init.__pinned_init((*self.value.get()).as_mut_ptr())?;
291                *self.initialized.get() = true;
292            }
293            Ok((*self.value.get()).assume_init_mut())
294        }
295    }
296
297    /// Accesses the inner value mutably by bypassing locking requirements using unique borrow
298    /// ownership.
299    #[inline]
300    pub fn get_inner_mut(&mut self) -> Option<&mut T> {
301        if *self.initialized.get_mut() {
302            Some(unsafe { self.value.get_mut().assume_init_mut() })
303        } else {
304            None
305        }
306    }
307
308    /// Unwraps the cell, returning the inner value if initialized.
309    #[inline]
310    pub fn into_inner(mut self) -> Option<T> {
311        if *self.initialized.get_mut() {
312            // Read the initialized value and reset `initialized` so `Drop` does not drop it again.
313            let val = unsafe { core::ptr::read(self.value.get_mut().as_ptr()) };
314            *self.initialized.get_mut() = false;
315            Some(val)
316        } else {
317            None
318        }
319    }
320
321    /// Returns a safe guard proxy for this cell using an exclusive lock token.
322    ///
323    /// # Safety
324    ///
325    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
326    /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
327    /// `Class`).
328    #[inline]
329    pub unsafe fn guard<'a>(
330        &'a self,
331        token: &'a mut LockToken<'_, Class>,
332    ) -> KOnceCellGuard<'a, T, Class> {
333        // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
334        // instance that guards this cell.
335        unsafe { KOnceCellGuard::new(self, token) }
336    }
337}
338
339impl<T, Class> Default for KOnceCell<T, Class> {
340    #[inline]
341    fn default() -> Self {
342        Self::new()
343    }
344}
345
346impl<T, Class> core::fmt::Debug for KOnceCell<T, Class> {
347    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
348        f.debug_struct("KOnceCell")
349            .field("value", &"<locked>")
350            .field("class", &core::any::type_name::<Class>())
351            .finish()
352    }
353}
354
355impl<T, Class> Drop for KOnceCell<T, Class> {
356    fn drop(&mut self) {
357        if *self.initialized.get_mut() {
358            unsafe {
359                self.value.get_mut().assume_init_drop();
360            }
361        }
362    }
363}
364
365/// A safe RAII / proxy guard providing synchronized access to a [`KOnceCell`] protected by a lock.
366///
367/// Created by calling `guard.field_cell()` on a lock guard generated by `#[ksync::guarded]`,
368/// or via [`KOnceCell::guard`].
369pub struct KOnceCellGuard<'a, T, Class> {
370    cell: &'a KOnceCell<T, Class>,
371    _marker: core::marker::PhantomData<(&'a mut (), &'a Class)>,
372}
373
374impl<'a, T, Class> KOnceCellGuard<'a, T, Class> {
375    /// Creates a new `KOnceCellGuard` from a cell reference and an exclusive lock token.
376    ///
377    /// # Safety
378    ///
379    /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
380    /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
381    /// `Class`).
382    #[inline]
383    pub unsafe fn new(cell: &'a KOnceCell<T, Class>, _token: &'a mut LockToken<'_, Class>) -> Self {
384        Self { cell, _marker: core::marker::PhantomData }
385    }
386
387    /// Returns `true` if the cell has been initialized.
388    #[inline]
389    pub fn is_initialized(&self) -> bool {
390        unsafe { self.cell.get_unchecked().is_some() }
391    }
392
393    /// Returns a reference to the initialized value, or `None` if uninitialized.
394    #[inline]
395    pub fn get(&self) -> Option<&T> {
396        unsafe { self.cell.get_unchecked() }
397    }
398
399    /// Returns a mutable reference to the initialized value, or `None` if uninitialized.
400    #[inline]
401    pub fn get_mut(&mut self) -> Option<&mut T> {
402        // SAFETY: The guard guarantees exclusive access while held.
403        unsafe { self.cell.get_mut_unchecked() }
404    }
405
406    /// Sets the value of the cell if uninitialized, returning `Err(value)` if already initialized.
407    #[inline]
408    pub fn set(&mut self, value: T) -> Result<(), T> {
409        // SAFETY: The guard guarantees exclusive access while held.
410        unsafe {
411            if self.cell.get_unchecked().is_some() {
412                Err(value)
413            } else {
414                (*self.cell.value.get()).write(value);
415                *self.cell.initialized.get() = true;
416                Ok(())
417            }
418        }
419    }
420
421    /// Initializes the cell with the given closure if uninitialized, returning a mutable reference
422    /// to the contained value.
423    #[inline]
424    pub fn get_or_init(&mut self, f: impl FnOnce() -> T) -> &mut T {
425        // SAFETY: The guard guarantees exclusive access while held.
426        unsafe { self.cell.get_or_init_unchecked(f) }
427    }
428
429    /// Initializes the cell with the given fallible closure if uninitialized, returning a mutable
430    /// reference to the contained value or the error.
431    #[inline]
432    pub fn get_or_try_init<E>(&mut self, f: impl FnOnce() -> Result<T, E>) -> Result<&mut T, E> {
433        // SAFETY: The guard guarantees exclusive access while held.
434        unsafe { self.cell.get_or_try_init_unchecked(f) }
435    }
436
437    /// Initializes the cell in-place with `PinInit` if uninitialized, returning a mutable reference
438    /// to the contained value or the initialization error.
439    #[inline]
440    pub fn get_or_pin_init<E>(&mut self, init: impl pin_init::PinInit<T, E>) -> Result<&mut T, E> {
441        // SAFETY: The guard guarantees exclusive access while held.
442        unsafe { self.cell.get_or_pin_init_unchecked(init) }
443    }
444}
445
446impl<T: core::fmt::Debug, Class> core::fmt::Debug for KOnceCellGuard<'_, T, Class> {
447    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
448        f.debug_struct("KOnceCellGuard")
449            .field("value", &self.get())
450            .field("class", &core::any::type_name::<Class>())
451            .finish()
452    }
453}
454
455#[cfg(not(feature = "kernel"))]
456#[cfg(test)]
457mod tests {
458    use super::*;
459    use lockdep::LockClass;
460
461    struct MyClass;
462    impl LockClass for MyClass {
463        const ID: *mut core::ffi::c_void = core::ptr::null_mut();
464    }
465
466    #[test]
467    fn test_konce_cell_init_and_get() {
468        let cell: KOnceCell<u32, MyClass> = KOnceCell::new();
469        unsafe {
470            let mut token = LockToken::new();
471            assert!(!cell.is_initialized(&token));
472            assert_eq!(cell.get(&token), None);
473
474            let val = cell.get_or_init(|| 42, &mut token);
475            assert_eq!(*val, 42);
476
477            assert!(cell.is_initialized(&token));
478            assert_eq!(cell.get(&token), Some(&42));
479            assert_eq!(cell.get_mut(&mut token), Some(&mut 42));
480
481            // Subsequent get_or_init does not overwrite
482            let val2 = cell.get_or_init(|| 99, &mut token);
483            assert_eq!(*val2, 42);
484        }
485    }
486
487    #[test]
488    fn test_konce_cell_set() {
489        let cell: KOnceCell<u32, MyClass> = KOnceCell::default();
490        unsafe {
491            let mut token = LockToken::new();
492            assert_eq!(cell.set(100, &mut token), Ok(()));
493            assert_eq!(cell.set(200, &mut token), Err(200));
494            assert_eq!(cell.get(&token), Some(&100));
495        }
496    }
497
498    #[test]
499    fn test_konce_cell_try_init() {
500        let cell: KOnceCell<u32, MyClass> = KOnceCell::new();
501        unsafe {
502            let mut token = LockToken::new();
503            let err_res = cell.get_or_try_init(|| Err::<u32, _>("failed"), &mut token);
504            assert_eq!(err_res, Err("failed"));
505            assert!(!cell.is_initialized(&token));
506
507            let ok_res = cell.get_or_try_init(|| Ok::<u32, &'static str>(77), &mut token);
508            assert_eq!(ok_res, Ok(&mut 77));
509            assert!(cell.is_initialized(&token));
510        }
511    }
512
513    #[test]
514    fn test_konce_cell_pin_init() {
515        let cell: KOnceCell<u32, MyClass> = KOnceCell::new();
516        unsafe {
517            let mut token = LockToken::new();
518            let init = pin_init::pin_init_from_closure(|slot: *mut u32| {
519                slot.write(123);
520                Ok::<(), core::convert::Infallible>(())
521            });
522            let res = cell.get_or_pin_init(init, &mut token);
523            assert_eq!(res, Ok(&mut 123));
524            assert_eq!(cell.get(&token), Some(&123));
525        }
526    }
527
528    #[test]
529    fn test_konce_cell_drop() {
530        use core::sync::atomic::{AtomicUsize, Ordering};
531        static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
532
533        struct DropDetector;
534        impl Drop for DropDetector {
535            fn drop(&mut self) {
536                DROP_COUNT.fetch_add(1, Ordering::Relaxed);
537            }
538        }
539
540        {
541            let cell: KOnceCell<DropDetector, MyClass> = KOnceCell::new();
542            unsafe {
543                let mut token = LockToken::new();
544                let _ = cell.get_or_init(|| DropDetector, &mut token);
545            }
546            assert_eq!(DROP_COUNT.load(Ordering::Relaxed), 0);
547        }
548        assert_eq!(DROP_COUNT.load(Ordering::Relaxed), 1);
549    }
550
551    #[test]
552    fn test_konce_cell_guard() {
553        let cell: KOnceCell<u32, MyClass> = KOnceCell::new();
554        unsafe {
555            let mut token = LockToken::new();
556            let mut guard = cell.guard(&mut token);
557            assert!(!guard.is_initialized());
558            assert_eq!(guard.get(), None);
559            assert_eq!(guard.get_mut(), None);
560
561            let val = guard.get_or_init(|| 123);
562            assert_eq!(*val, 123);
563            assert!(guard.is_initialized());
564            assert_eq!(guard.get(), Some(&123));
565            assert_eq!(guard.get_mut(), Some(&mut 123));
566            assert_eq!(guard.set(456), Err(456));
567        }
568    }
569}