Skip to main content

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}