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}