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}