kernel/lib/sync/unchecked_cell.rs
1// SPDX-License-Identifier: GPL-3.0-only
2//! UncheckedCell datatype; a convenience wrapper around `UnsafeCell<MaybeUninit<T>>`
3//!
4//! Authors: MarioS271
5
6use core::cell::UnsafeCell;
7use core::mem::MaybeUninit;
8
9#[cfg(feature = "debug-checks")] use crate::lib::panic::kernel_panic;
10#[cfg(feature = "debug-checks")] use crate::lib::panic_codes::PanicCode;
11#[cfg(feature = "debug-checks")] use core::sync::atomic::AtomicBool;
12#[cfg(feature = "debug-checks")] use core::sync::atomic::Ordering;
13
14
15/// A datatype with interior mutability, which can be uninitialized via [`MaybeUninit`]
16/// When the cargo feature `debug-checks` is enabled, this type also checks for double initialization
17/// and uninitialized access.
18pub struct UncheckedCell<T> {
19 value: UnsafeCell<MaybeUninit<T>>,
20 #[cfg(feature = "debug-checks")] is_init: AtomicBool
21}
22
23/// Safety: this type can be shared across CPUs as the callers have the responsibility to
24/// ensure proper data safety because of the `unsafe` contracts on [`init`](Self::init) and [`get`](Self::get)
25unsafe impl<T: Send + Sync> Sync for UncheckedCell<T> {}
26
27impl<T> UncheckedCell<T> {
28 /// Constructor; creates a new [`UncheckedCell`]
29 #[allow(clippy::new_without_default)]
30 pub const fn new() -> Self {
31 Self {
32 value: UnsafeCell::new(MaybeUninit::uninit()),
33 #[cfg(feature = "debug-checks")] is_init: AtomicBool::new(false)
34 }
35 }
36
37 /// Initialize the inner value
38 ///
39 /// # Safety
40 /// The caller must guarantee the following:
41 /// - That this method has never been called before and will never be called again
42 /// - That at the time of calling this method, no references or pointers to this data exist
43 /// - While this method is being called, no other CPU is working with the given data
44 pub unsafe fn init(&self, value: T) {
45 #[cfg(feature = "debug-checks")]
46 {
47 if self.is_init.load(Ordering::Relaxed) {
48 kernel_panic(
49 PanicCode::DoubleInitialization,
50 "Attempted to double-initialize a UncheckedCell"
51 );
52 }
53 }
54
55 unsafe { (*self.value.get()).write(value) };
56
57 #[cfg(feature = "debug-checks")]
58 self.is_init.store(true, Ordering::Relaxed);
59 }
60
61 /// Get a reference to the inner value
62 ///
63 /// # Safety
64 /// The caller must guarantee the following:
65 /// - That this value's `init` method has already been called before
66 /// - That at this method's entire execution time, no mutable references or pointers to this data
67 /// exist or will exist
68 pub unsafe fn get(&self) -> &T {
69 #[cfg(feature = "debug-checks")]
70 {
71 if !self.is_init.load(Ordering::Relaxed) {
72 kernel_panic(
73 PanicCode::UninitializedAccess,
74 "Attempted to access the contents of an uninitialized UncheckedCell"
75 );
76 }
77 }
78
79 unsafe { (*self.value.get()).assume_init_ref() }
80 }
81}