1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! Support for statically initializing objects in memory.
/// Allocates a statically-sized global array of memory and initializes the
/// memory for a particular data structure.
///
/// This macro creates the static buffer, ensures it is initialized to the
/// proper type, and then returns a `&'static mut` reference to it.
///
/// Note: Because this instantiates a static object, you generally cannot pass
/// a type with generic paramters. github.com/tock/tock/issues/2995 for detail.
///
/// # Safety
///
/// As this macro will write directly to a global area without acquiring a lock
/// or similar, calling this macro is inherently unsafe. The caller should take
/// care to never call the code that initializes this buffer twice, as doing so
/// will overwrite the value from first allocation without running its
/// destructor.
#[macro_export]
macro_rules! static_init {
($T:ty, $e:expr $(,)?) => {{
let mut buf = $crate::static_buf!($T);
buf.write($e)
}};
}
/// An `#[inline(never)]` function that panics internally if the passed reference
/// is `true`. This function is intended for use within
/// the `static_buf!()` macro, which removes the size bloat of track_caller
/// saving the location of every single call to `static_init!()`.
/// If you hit this panic, you are either calling `static_buf!()` in
/// a loop or calling a function multiple times which internally
/// contains a call to `static_buf!()`. Typically, calls to
/// `static_buf!()` are hidden within calls to `static_init!()` or
/// component helper macros, so start your search there.
#[inline(never)]
pub fn static_buf_check_used(used: &mut bool) {
// Check if this `BUF` has already been declared and initialized. If it
// has, then this is a repeated `static_buf!()` call which is an error
// as it will alias the same `BUF`.
if *used {
// panic, this buf has already been declared and initialized.
// NOTE: To save 144 bytes of code size, use loop {} instead of this
// panic.
panic!("Error! Single static_buf!() called twice.");
} else {
// Otherwise, mark our uninitialized buffer as used.
*used = true;
}
}
/// Allocates a statically-sized global region of memory for data structures but
/// does not initialize the memory. Checks that the buffer is not aliased and is
/// only used once.
///
/// This macro creates the static buffer, and returns a
/// `StaticUninitializedBuffer` wrapper containing the buffer. The memory is
/// allocated, but it is guaranteed to be uninitialized inside of the wrapper.
///
/// Before the static buffer can be used it must be initialized. For example:
///
/// ```ignore
/// let mut static_buffer = static_buf!(T);
/// let static_reference: &'static mut T = static_buffer.initialize(T::new());
/// ```
///
/// Separating the creation of the static buffer into its own macro is not
/// strictly necessary, but it allows for more flexibility in Rust when boards
/// are initialized and the static structures are being created. Since creating
/// and initializing static buffers requires knowing the particular types (and
/// their sizes), writing shared initialization code (in components for example)
/// where the types are unknown since they vary across boards is difficult. By
/// splitting buffer creating from initialization, creating shared components is
/// possible.
#[macro_export]
macro_rules! static_buf {
($T:ty $(,)?) => {{
// Statically allocate a read-write buffer for the value without
// actually writing anything, as well as a flag to track if
// this memory has been initialized yet.
static mut BUF: (core::mem::MaybeUninit<$T>, bool) =
(core::mem::MaybeUninit::uninit(), false);
// To minimize the amount of code duplicated across every invocation
// of this macro, all of the logic for checking if the buffer has been
// used is contained within the static_buf_check_used function,
// which panics if the passed boolean has been used and sets the
// boolean to true otherwise.
$crate::utilities::static_init::static_buf_check_used(&mut BUF.1);
// If we get to this point we can wrap our buffer to be eventually
// initialized.
&mut BUF.0
}};
}