init_token/
init_big.rs

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