init_token/
init.rs

1//! Helper stuff for the [`init!`] macro.
2//!
3//! [`init!`]: macro@crate::init
4
5use std::marker::PhantomData;
6use std::mem::MaybeUninit;
7use std::sync::Once;
8
9use crate::sync_unsafe_cell::SyncUnsafeCell;
10use crate::token::TokenWrapper;
11
12/// A potentially-uninitialized `static` with all things needed to initialize it.
13///
14/// Call [`init()`] to gain an access token.
15///
16/// [`init()`]: Static::init
17pub struct Static<T, Token> {
18    // INVARIANT: This should only be written once, and in `init()`.
19    value: SyncUnsafeCell<MaybeUninit<T>>,
20    init_once: Once,
21    initializer: fn() -> T,
22    _token: PhantomData<fn(Token) -> Token>,
23}
24
25impl<T, Token: TokenWrapper> Static<T, Token> {
26    #[doc(hidden)]
27    pub const fn new(initializer: fn() -> T) -> Self {
28        Self {
29            value: SyncUnsafeCell::new(MaybeUninit::uninit()),
30            init_once: Once::new(),
31            initializer,
32            _token: PhantomData,
33        }
34    }
35
36    /// Initialize the static if not initialized yet and gain an access token.
37    ///
38    /// This is little expensive to call (has to check if initialized), so if
39    /// you already have an access token prefer to use it.
40    #[inline]
41    pub fn init(&self) -> Token {
42        self.init_once.call_once(|| {
43            // SAFETY: We're only touching `self.value` here and in `get_value()`.
44            // Another `init()` cannot coexist with us because `call_once()` prevents that (we
45            // would either return without calling the callback or block).
46            // `get_value()` cannot be called yet because a precondition of it is that it is only
47            // called after `init()` and we cannot enter this segment of code twice, guaranteed
48            // by the `call_once()`.
49            let value = unsafe { self.value.get_mut() };
50            // INVARIANT: If we already called `init()` then `call_once()` would not have called
51            // us (either skipping or panicking if the initializer panicked), and there is no
52            // other place we write to `self.value`.
53            value.write((self.initializer)());
54        });
55
56        // SAFETY: We initialized the static above, or panicked if the initializer panicked.
57        unsafe { Token::new() }
58    }
59
60    /// # Safety
61    ///
62    /// `init()` must have been already called.
63    #[doc(hidden)]
64    #[inline]
65    pub unsafe fn get_value(&self) -> &T {
66        // SAFETY: We can only write to this once in `init()` (precondition), and `init()`
67        // was already called so there will be no outstanding mutable references.
68        let value = unsafe { self.value.get() };
69        // SAFETY: `init()` was called (precondition) and initialized the value.
70        unsafe { value.assume_init_ref() }
71    }
72}