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::lock_token::{LockTokenRead, LockTokenWrite};
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
25// SAFETY: `KOnceCell` synchronizes shared access via `LockToken<Class>`, so it is `Sync` when
26// `T: Send`.
27unsafe impl<T: Send, Class> Sync for KOnceCell<T, Class> {}
28// SAFETY: `KOnceCell` owns `T`, so transferring it across threads is safe when `T: Send`.
29unsafe impl<T: Send, Class> Send for KOnceCell<T, Class> {}
30
31impl<T, Class> KOnceCell<T, Class> {
32 /// Creates a new uninitialized `KOnceCell`.
33 #[inline]
34 pub const fn new() -> Self {
35 Self {
36 value: UnsafeCell::new(MaybeUninit::uninit()),
37 initialized: UnsafeCell::new(false),
38 _marker: PhantomData,
39 }
40 }
41
42 /// Returns `true` if the cell has been initialized.
43 ///
44 /// # Safety
45 ///
46 /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
47 /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
48 /// `Class`).
49 #[inline]
50 pub unsafe fn is_initialized<Token, Idx>(&self, _token: &Token) -> bool
51 where
52 Token: LockTokenRead<Class, Idx>,
53 {
54 // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
55 // instance that guards this cell.
56 unsafe { *self.initialized.get() }
57 }
58
59 /// Accesses the initialized value immutably using a shared lock token.
60 /// Returns `None` if not yet initialized.
61 ///
62 /// # Safety
63 ///
64 /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
65 /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
66 /// `Class`).
67 #[inline]
68 pub unsafe fn get<'b, Token, Idx>(&self, _token: &'b Token) -> Option<&'b T>
69 where
70 Token: LockTokenRead<Class, Idx>,
71 {
72 // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
73 // instance that guards this cell.
74 unsafe {
75 if *self.initialized.get() { Some((*self.value.get()).assume_init_ref()) } else { None }
76 }
77 }
78
79 /// Accesses the initialized value mutably using a mutable lock token.
80 /// Returns `None` if not yet initialized.
81 ///
82 /// # Safety
83 ///
84 /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
85 /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
86 /// `Class`).
87 #[inline]
88 pub unsafe fn get_mut<'b, Token, Idx>(&self, _token: &'b mut Token) -> Option<&'b mut T>
89 where
90 Token: LockTokenWrite<Class, Idx>,
91 {
92 // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
93 // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
94 // ensures that no other active borrows of the same cell can co-exist.
95 unsafe {
96 if *self.initialized.get() { Some((*self.value.get()).assume_init_mut()) } else { None }
97 }
98 }
99
100 /// Sets the value of the cell if uninitialized, returning `Err(value)` if already initialized.
101 ///
102 /// # Safety
103 ///
104 /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
105 /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
106 /// `Class`).
107 #[inline]
108 pub unsafe fn set<Token, Idx>(&self, value: T, _token: &mut Token) -> Result<(), T>
109 where
110 Token: LockTokenWrite<Class, Idx>,
111 {
112 // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
113 // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
114 // ensures no concurrent access.
115 unsafe {
116 if *self.initialized.get() {
117 Err(value)
118 } else {
119 let _ = (*self.value.get()).write(value);
120 *self.initialized.get() = true;
121 Ok(())
122 }
123 }
124 }
125
126 /// Initializes the cell with the given closure if uninitialized, returning a mutable reference
127 /// to the contained value.
128 ///
129 /// # Safety
130 ///
131 /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
132 /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
133 /// `Class`).
134 #[inline]
135 pub unsafe fn get_or_init<'b, Token, Idx>(
136 &self,
137 f: impl FnOnce() -> T,
138 _token: &'b mut Token,
139 ) -> &'b mut T
140 where
141 Token: LockTokenWrite<Class, Idx>,
142 {
143 // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
144 // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
145 // ensures no concurrent access.
146 unsafe {
147 if !*self.initialized.get() {
148 let _ = (*self.value.get()).write(f());
149 *self.initialized.get() = true;
150 }
151 (*self.value.get()).assume_init_mut()
152 }
153 }
154
155 /// Initializes the cell with the given fallible closure if uninitialized, returning a mutable
156 /// reference to the contained value or the error.
157 ///
158 /// # Safety
159 ///
160 /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
161 /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
162 /// `Class`).
163 #[inline]
164 pub unsafe fn get_or_try_init<'b, E, Token, Idx>(
165 &self,
166 f: impl FnOnce() -> Result<T, E>,
167 _token: &'b mut Token,
168 ) -> Result<&'b mut T, E>
169 where
170 Token: LockTokenWrite<Class, Idx>,
171 {
172 // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
173 // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
174 // ensures no concurrent access.
175 unsafe {
176 if !*self.initialized.get() {
177 let val = f()?;
178 let _ = (*self.value.get()).write(val);
179 *self.initialized.get() = true;
180 }
181 Ok((*self.value.get()).assume_init_mut())
182 }
183 }
184
185 /// Initializes the cell in-place with `PinInit` if uninitialized, returning a mutable reference
186 /// to the contained value or the initialization error.
187 ///
188 /// # Safety
189 ///
190 /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
191 /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
192 /// `Class`).
193 #[inline]
194 pub unsafe fn get_or_pin_init<'b, E, Token, Idx>(
195 &self,
196 init: impl pin_init::PinInit<T, E>,
197 _token: &'b mut Token,
198 ) -> Result<&'b mut T, E>
199 where
200 Token: LockTokenWrite<Class, Idx>,
201 {
202 // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
203 // instance that guards this cell, and the exclusive mutable borrow of the `LockToken`
204 // ensures no concurrent access.
205 unsafe {
206 if !*self.initialized.get() {
207 init.__pinned_init((*self.value.get()).as_mut_ptr())?;
208 *self.initialized.get() = true;
209 }
210 Ok((*self.value.get()).assume_init_mut())
211 }
212 }
213
214 /// Accesses the initialized value immutably without checking for a `LockToken`.
215 /// Returns `None` if not yet initialized.
216 ///
217 /// # Safety
218 ///
219 /// The caller must guarantee that the specific lock instance guarding this `KOnceCell` is held.
220 #[inline]
221 pub unsafe fn get_unchecked(&self) -> Option<&T> {
222 // SAFETY: The caller guarantees that the specific lock instance protecting this cell
223 // is held.
224 unsafe {
225 if *self.initialized.get() { Some((*self.value.get()).assume_init_ref()) } else { None }
226 }
227 }
228
229 /// Accesses the initialized value mutably without checking for a `LockToken`.
230 /// Returns `None` if not yet initialized.
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_mut_unchecked(&self) -> Option<&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() { Some((*self.value.get()).assume_init_mut()) } else { None }
243 }
244 }
245
246 /// Initializes the cell with the given closure without requiring a `LockToken`.
247 ///
248 /// # Safety
249 ///
250 /// The caller must guarantee that the specific lock instance guarding this `KOnceCell` is held
251 /// exclusively without concurrent access.
252 #[allow(clippy::mut_from_ref)]
253 #[inline]
254 pub unsafe fn get_or_init_unchecked(&self, f: impl FnOnce() -> T) -> &mut T {
255 // SAFETY: The caller guarantees that the specific lock instance protecting this cell is
256 // held exclusively without concurrent access.
257 unsafe {
258 if !*self.initialized.get() {
259 let _ = (*self.value.get()).write(f());
260 *self.initialized.get() = true;
261 }
262 (*self.value.get()).assume_init_mut()
263 }
264 }
265
266 /// Initializes the cell with the given fallible closure without requiring a `LockToken`.
267 ///
268 /// # Safety
269 ///
270 /// The caller must guarantee that the specific lock instance guarding this `KOnceCell` is held
271 /// exclusively without concurrent access.
272 #[allow(clippy::mut_from_ref)]
273 #[inline]
274 pub unsafe fn get_or_try_init_unchecked<E>(
275 &self,
276 f: impl FnOnce() -> Result<T, E>,
277 ) -> Result<&mut T, E> {
278 // SAFETY: The caller guarantees that the specific lock instance protecting this cell is
279 // held exclusively without concurrent access.
280 unsafe {
281 if !*self.initialized.get() {
282 let val = f()?;
283 let _ = (*self.value.get()).write(val);
284 *self.initialized.get() = true;
285 }
286 Ok((*self.value.get()).assume_init_mut())
287 }
288 }
289
290 /// Initializes the cell in-place with `PinInit` without requiring a `LockToken`.
291 ///
292 /// # Safety
293 ///
294 /// The caller must guarantee that the specific lock instance guarding this `KOnceCell` is held
295 /// exclusively without concurrent access.
296 #[allow(clippy::mut_from_ref)]
297 #[inline]
298 pub unsafe fn get_or_pin_init_unchecked<E>(
299 &self,
300 init: impl pin_init::PinInit<T, E>,
301 ) -> Result<&mut T, E> {
302 // SAFETY: The caller guarantees that the specific lock instance protecting this cell is
303 // held exclusively without concurrent access.
304 unsafe {
305 if !*self.initialized.get() {
306 init.__pinned_init((*self.value.get()).as_mut_ptr())?;
307 *self.initialized.get() = true;
308 }
309 Ok((*self.value.get()).assume_init_mut())
310 }
311 }
312
313 /// Accesses the inner value mutably by bypassing locking requirements using unique borrow
314 /// ownership.
315 #[inline]
316 pub fn get_inner_mut(&mut self) -> Option<&mut T> {
317 if *self.initialized.get_mut() {
318 // SAFETY: `initialized` is true, so `value` has been initialized.
319 Some(unsafe { self.value.get_mut().assume_init_mut() })
320 } else {
321 None
322 }
323 }
324
325 /// Unwraps the cell, returning the inner value if initialized.
326 #[inline]
327 pub fn into_inner(mut self) -> Option<T> {
328 if *self.initialized.get_mut() {
329 // SAFETY: `initialized` is true, and resetting `initialized` to false below prevents
330 // `Drop` from dropping the value a second time.
331 let val = unsafe { core::ptr::read(self.value.get_mut().as_ptr()) };
332 *self.initialized.get_mut() = false;
333 Some(val)
334 } else {
335 None
336 }
337 }
338
339 /// Returns a safe guard proxy for this cell using an exclusive lock token.
340 ///
341 /// # Safety
342 ///
343 /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
344 /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
345 /// `Class`).
346 #[inline]
347 pub unsafe fn guard<'a, Token, Idx>(
348 &'a self,
349 token: &'a mut Token,
350 ) -> KOnceCellGuard<'a, T, Class>
351 where
352 Token: LockTokenWrite<Class, Idx>,
353 {
354 // SAFETY: The caller guarantees that the provided LockToken belongs to the specific lock
355 // instance that guards this cell.
356 unsafe { KOnceCellGuard::new(self, token) }
357 }
358}
359
360impl<T, Class> Default for KOnceCell<T, Class> {
361 #[inline]
362 fn default() -> Self {
363 Self::new()
364 }
365}
366
367impl<T, Class> core::fmt::Debug for KOnceCell<T, Class> {
368 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
369 f.debug_struct("KOnceCell")
370 .field("value", &"<locked>")
371 .field("class", &core::any::type_name::<Class>())
372 .finish()
373 }
374}
375
376impl<T, Class> Drop for KOnceCell<T, Class> {
377 fn drop(&mut self) {
378 if *self.initialized.get_mut() {
379 // SAFETY: `initialized` is true, so `value` contains an initialized `T`.
380 unsafe {
381 self.value.get_mut().assume_init_drop();
382 }
383 }
384 }
385}
386
387/// A safe RAII / proxy guard providing synchronized access to a [`KOnceCell`] protected by a lock.
388///
389/// Created by calling `guard.field_cell()` on a lock guard generated by `#[ksync::guarded]`,
390/// or via [`KOnceCell::guard`].
391pub struct KOnceCellGuard<'a, T, Class> {
392 cell: &'a KOnceCell<T, Class>,
393 _marker: core::marker::PhantomData<(&'a mut (), &'a Class)>,
394}
395
396impl<'a, T, Class> KOnceCellGuard<'a, T, Class> {
397 /// Creates a new `KOnceCellGuard` from a cell reference and an exclusive lock token.
398 ///
399 /// # Safety
400 ///
401 /// The caller must guarantee that the provided `LockToken` belongs to the specific lock
402 /// instance that guards this `KOnceCell` (rather than a different lock of the same lock class
403 /// `Class`).
404 #[inline]
405 pub unsafe fn new<Token, Idx>(cell: &'a KOnceCell<T, Class>, _token: &'a mut Token) -> Self
406 where
407 Token: LockTokenWrite<Class, Idx>,
408 {
409 Self { cell, _marker: core::marker::PhantomData }
410 }
411
412 /// Returns `true` if the cell has been initialized.
413 #[inline]
414 pub fn is_initialized(&self) -> bool {
415 // SAFETY: This guard holds exclusive lock-proven access to `self.cell`.
416 unsafe { self.cell.get_unchecked().is_some() }
417 }
418
419 /// Returns a reference to the initialized value, or `None` if uninitialized.
420 #[inline]
421 pub fn get(&self) -> Option<&T> {
422 // SAFETY: This guard holds exclusive lock-proven access to `self.cell`.
423 unsafe { self.cell.get_unchecked() }
424 }
425
426 /// Returns a mutable reference to the initialized value, or `None` if uninitialized.
427 #[inline]
428 pub fn get_mut(&mut self) -> Option<&mut T> {
429 // SAFETY: The guard guarantees exclusive access while held.
430 unsafe { self.cell.get_mut_unchecked() }
431 }
432
433 /// Sets the value of the cell if uninitialized, returning `Err(value)` if already initialized.
434 #[inline]
435 pub fn set(&mut self, value: T) -> Result<(), T> {
436 // SAFETY: The guard guarantees exclusive access while held.
437 unsafe {
438 if self.cell.get_unchecked().is_some() {
439 Err(value)
440 } else {
441 let _ = (*self.cell.value.get()).write(value);
442 *self.cell.initialized.get() = true;
443 Ok(())
444 }
445 }
446 }
447
448 /// Initializes the cell with the given closure if uninitialized, returning a mutable reference
449 /// to the contained value.
450 #[inline]
451 pub fn get_or_init(&mut self, f: impl FnOnce() -> T) -> &mut T {
452 // SAFETY: The guard guarantees exclusive access while held.
453 unsafe { self.cell.get_or_init_unchecked(f) }
454 }
455
456 /// Initializes the cell with the given fallible closure if uninitialized, returning a mutable
457 /// reference to the contained value or the error.
458 #[inline]
459 pub fn get_or_try_init<E>(&mut self, f: impl FnOnce() -> Result<T, E>) -> Result<&mut T, E> {
460 // SAFETY: The guard guarantees exclusive access while held.
461 unsafe { self.cell.get_or_try_init_unchecked(f) }
462 }
463
464 /// Initializes the cell in-place with `PinInit` if uninitialized, returning a mutable reference
465 /// to the contained value or the initialization error.
466 #[inline]
467 pub fn get_or_pin_init<E>(&mut self, init: impl pin_init::PinInit<T, E>) -> Result<&mut T, E> {
468 // SAFETY: The guard guarantees exclusive access while held.
469 unsafe { self.cell.get_or_pin_init_unchecked(init) }
470 }
471}
472
473impl<T: core::fmt::Debug, Class> core::fmt::Debug for KOnceCellGuard<'_, T, Class> {
474 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
475 f.debug_struct("KOnceCellGuard")
476 .field("value", &self.get())
477 .field("class", &core::any::type_name::<Class>())
478 .finish()
479 }
480}
481
482#[cfg(not(feature = "kernel"))]
483#[cfg(test)]
484mod tests {
485 use super::*;
486 use crate::LockToken;
487 use lockdep::LockClass;
488
489 struct MyClass;
490 impl LockClass for MyClass {
491 const ID: *mut core::ffi::c_void = core::ptr::null_mut();
492 }
493
494 #[test]
495 fn test_konce_cell_init_and_get() {
496 let cell: KOnceCell<u32, MyClass> = KOnceCell::new();
497 // SAFETY: Single-threaded test with exclusive access to `cell`.
498 unsafe {
499 let mut token = LockToken::new();
500 assert!(!cell.is_initialized(&token));
501 assert_eq!(cell.get(&token), None);
502
503 let val = cell.get_or_init(|| 42, &mut token);
504 assert_eq!(*val, 42);
505
506 assert!(cell.is_initialized(&token));
507 assert_eq!(cell.get(&token), Some(&42));
508 assert_eq!(cell.get_mut(&mut token), Some(&mut 42));
509
510 // Subsequent get_or_init does not overwrite
511 let val2 = cell.get_or_init(|| 99, &mut token);
512 assert_eq!(*val2, 42);
513 }
514 }
515
516 #[test]
517 fn test_konce_cell_set() {
518 let cell: KOnceCell<u32, MyClass> = KOnceCell::default();
519 // SAFETY: Single-threaded test with exclusive access to `cell`.
520 unsafe {
521 let mut token = LockToken::new();
522 assert_eq!(cell.set(100, &mut token), Ok(()));
523 assert_eq!(cell.set(200, &mut token), Err(200));
524 assert_eq!(cell.get(&token), Some(&100));
525 }
526 }
527
528 #[test]
529 fn test_konce_cell_try_init() {
530 let cell: KOnceCell<u32, MyClass> = KOnceCell::new();
531 // SAFETY: Single-threaded test with exclusive access to `cell`.
532 unsafe {
533 let mut token = LockToken::new();
534 let err_res = cell.get_or_try_init(|| Err::<u32, _>("failed"), &mut token);
535 assert_eq!(err_res, Err("failed"));
536 assert!(!cell.is_initialized(&token));
537
538 let ok_res = cell.get_or_try_init(|| Ok::<u32, &'static str>(77), &mut token);
539 assert_eq!(ok_res, Ok(&mut 77));
540 assert!(cell.is_initialized(&token));
541 }
542 }
543
544 #[test]
545 fn test_konce_cell_pin_init() {
546 let cell: KOnceCell<u32, MyClass> = KOnceCell::new();
547 // SAFETY: Single-threaded test with exclusive access to `cell`.
548 unsafe {
549 let mut token = LockToken::new();
550 let init = pin_init::pin_init_from_closure(|slot: *mut u32| {
551 slot.write(123);
552 Ok::<(), core::convert::Infallible>(())
553 });
554 let res = cell.get_or_pin_init(init, &mut token);
555 assert_eq!(res, Ok(&mut 123));
556 assert_eq!(cell.get(&token), Some(&123));
557 }
558 }
559
560 #[test]
561 fn test_konce_cell_drop() {
562 use core::sync::atomic::{AtomicUsize, Ordering};
563 static DROP_COUNT: AtomicUsize = AtomicUsize::new(0);
564
565 struct DropDetector;
566 impl Drop for DropDetector {
567 fn drop(&mut self) {
568 let _ = DROP_COUNT.fetch_add(1, Ordering::Relaxed);
569 }
570 }
571
572 {
573 let cell: KOnceCell<DropDetector, MyClass> = KOnceCell::new();
574 // SAFETY: Single-threaded test with exclusive access to `cell`.
575 unsafe {
576 let mut token = LockToken::new();
577 let _ = cell.get_or_init(|| DropDetector, &mut token);
578 }
579 assert_eq!(DROP_COUNT.load(Ordering::Relaxed), 0);
580 }
581 assert_eq!(DROP_COUNT.load(Ordering::Relaxed), 1);
582 }
583
584 #[test]
585 fn test_konce_cell_guard() {
586 let cell: KOnceCell<u32, MyClass> = KOnceCell::new();
587 // SAFETY: Single-threaded test with exclusive access to `cell`.
588 unsafe {
589 let mut token = LockToken::new();
590 let mut guard = cell.guard(&mut token);
591 assert!(!guard.is_initialized());
592 assert_eq!(guard.get(), None);
593 assert_eq!(guard.get_mut(), None);
594
595 let val = guard.get_or_init(|| 123);
596 assert_eq!(*val, 123);
597 assert!(guard.is_initialized());
598 assert_eq!(guard.get(), Some(&123));
599 assert_eq!(guard.get_mut(), Some(&mut 123));
600 assert_eq!(guard.set(456), Err(456));
601 }
602 }
603
604 struct SecondClass;
605 impl LockClass for SecondClass {
606 const ID: *mut core::ffi::c_void = core::ptr::null_mut();
607 }
608
609 #[test]
610 fn test_konce_cell_multi_lock() {
611 // 1. Test `is_initialized`, `get`, `set`, `get_mut`, and `guard` with 2 locks.
612 let cell: KOnceCell<u32, (MyClass, SecondClass)> = KOnceCell::new();
613 // SAFETY: Single-threaded test with exclusive access to `cell`.
614 unsafe {
615 let mut t1 = LockToken::<MyClass>::new();
616 let mut t2 = LockToken::<SecondClass>::new();
617
618 assert!(!cell.is_initialized(&t1));
619 assert!(!cell.is_initialized(&t2));
620 assert_eq!(cell.get(&t1), None);
621 assert_eq!(cell.get(&t2), None);
622
623 assert_eq!(cell.set(10, t2.join(&mut t1)), Ok(()));
624 assert!(cell.is_initialized(&t1));
625 assert!(cell.is_initialized(&t2));
626 assert_eq!(cell.get(&t1), Some(&10));
627 assert_eq!(cell.get(&t2), Some(&10));
628
629 *cell.get_mut(t1.join(&mut t2)).unwrap() = 20;
630 assert_eq!(cell.get(&t1), Some(&20));
631
632 *cell.get_mut(t2.join(&mut t1)).unwrap() = 30;
633 assert_eq!(cell.get(&t2), Some(&30));
634
635 let mut g = cell.guard(t2.join(&mut t1));
636 assert_eq!(g.get(), Some(&30));
637 *g.get_mut().unwrap() = 40;
638 assert_eq!(cell.get(&t1), Some(&40));
639 }
640
641 // 2. Test `get_or_init`, `get_or_try_init`, and `get_or_pin_init` with joined tokens.
642 let cell_init: KOnceCell<u32, (MyClass, SecondClass)> = KOnceCell::new();
643 let cell_try: KOnceCell<u32, (MyClass, SecondClass)> = KOnceCell::new();
644 let cell_pin: KOnceCell<u32, (MyClass, SecondClass)> = KOnceCell::new();
645 // SAFETY: Single-threaded test with exclusive access to the cells.
646 unsafe {
647 let mut t1 = LockToken::<MyClass>::new();
648 let mut t2 = LockToken::<SecondClass>::new();
649
650 let val = cell_init.get_or_init(|| 111, t1.join(&mut t2));
651 assert_eq!(*val, 111);
652 assert_eq!(cell_init.get(&t2), Some(&111));
653
654 let err = cell_try.get_or_try_init(|| Err::<u32, _>("err"), t2.join(&mut t1));
655 assert_eq!(err, Err("err"));
656 let ok = cell_try.get_or_try_init(|| Ok::<u32, &'static str>(222), t1.join(&mut t2));
657 assert_eq!(ok, Ok(&mut 222));
658 assert_eq!(cell_try.get(&t1), Some(&222));
659
660 let init = pin_init::pin_init_from_closure(|slot: *mut u32| {
661 slot.write(333);
662 Ok::<(), core::convert::Infallible>(())
663 });
664 let pinned = cell_pin.get_or_pin_init(init, t2.join(&mut t1));
665 assert_eq!(pinned, Ok(&mut 333));
666 assert_eq!(cell_pin.get(&t1), Some(&333));
667 }
668 }
669}