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}