use crate::runtime::execution::ExecutionState;
use crate::runtime::storage::StorageKey;
use crate::sync::Once;
use std::marker::PhantomData;
pub struct Lazy<T: Sync> {
#[doc(hidden)]
pub cell: Once,
#[doc(hidden)]
pub init: fn() -> T,
#[doc(hidden)]
pub _p: PhantomData<T>,
}
impl<T: Sync> std::fmt::Debug for Lazy<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Lazy").finish_non_exhaustive()
}
}
impl<T: Sync> Lazy<T> {
pub fn get(&'static self) -> &T {
unsafe fn extend_lt<'a, T>(t: &'a T) -> &'static T {
std::mem::transmute(t)
}
let initialize = ExecutionState::with(|state| state.get_storage::<_, DropGuard<T>>(self).is_none());
if initialize {
self.cell.call_once(|| {
let value = (self.init)();
ExecutionState::with(|state| state.init_storage(self, DropGuard(value)));
});
}
ExecutionState::with(|state| {
let value: &DropGuard<T> = state.get_storage(self).expect("should be initialized");
unsafe { extend_lt(&value.0) }
})
}
}
impl<T: Sync> From<&Lazy<T>> for StorageKey {
fn from(lazy: &Lazy<T>) -> Self {
StorageKey(lazy as *const _ as usize, 0x3)
}
}
pub trait LazyStatic {
#[doc(hidden)]
fn initialize(lazy: &Self);
}
pub fn initialize<T: LazyStatic>(lazy: &T) {
LazyStatic::initialize(lazy);
}
static PRINTED_DROP_WARNING: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
fn maybe_warn_about_drop() {
use owo_colors::OwoColorize;
use std::sync::atomic::Ordering;
if PRINTED_DROP_WARNING
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
if std::env::var("SHUTTLE_SILENCE_WARNINGS").is_ok() {
return;
}
if ExecutionState::with(|state| state.config.silence_warnings) {
return;
}
eprintln!(
"{}: Shuttle runs the `Drop` method of `lazy_static` values at the end of an execution, \
unlike the actual `lazy_static` implementation. This difference may cause false positives. \
See https://docs.rs/shuttle/*/shuttle/lazy_static/index.html#warning-about-drop-behavior \
for details or to disable this warning.",
"WARNING".yellow(),
);
}
}
#[derive(Debug)]
struct DropGuard<T>(T);
impl<T> Drop for DropGuard<T> {
fn drop(&mut self) {
maybe_warn_about_drop();
}
}