#![allow(unsafe_code)]
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ops::Deref;
use core::ptr;
use core::sync::atomic::{AtomicU8, Ordering};
pub struct Static<T: Sync> {
storage: UnsafeCell<MaybeUninit<T>>,
initializer: fn() -> T,
initialized: AtomicU8,
}
const UNINITIALIZED: u8 = 0;
const INITIALIZING: u8 = 1;
const FINISHED_INITIALIZING: u8 = 2;
const INITIALIZED: u8 = 3;
const DROPPING: u8 = 4;
const POISONED: u8 = 0x10;
const DROPPED: u8 = 0xff;
unsafe impl<T: Sync> Sync for Static<T> {}
impl<T: Sync> Static<T> {
#[doc(hidden)]
pub const unsafe fn new(initializer: fn() -> T) -> Self {
Self {
storage: UnsafeCell::new(MaybeUninit::uninit()),
initializer,
initialized: AtomicU8::new(UNINITIALIZED),
}
}
#[inline(always)]
unsafe fn get_unchecked(&self) -> &T {
unsafe {
(UnsafeCell::raw_get(&self.storage) as *const T)
.as_ref()
.unwrap_unchecked()
}
}
}
impl<T: Sync + ::core::fmt::Debug> ::core::fmt::Debug for Static<T> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
(**self).fmt(f)
}
}
impl<T: Sync + ::core::fmt::Display> ::core::fmt::Display for Static<T> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
(**self).fmt(f)
}
}
impl<T: Sync> Deref for Static<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
struct PanicGuard<'a> {
initialized: &'a AtomicU8,
}
impl<'a> Drop for PanicGuard<'a> {
fn drop(&mut self) {
self.initialized.fetch_or(POISONED, Ordering::AcqRel);
}
}
unsafe {
match self.initialized.fetch_or(INITIALIZING, Ordering::AcqRel) {
UNINITIALIZED => {
let panic_guard = PanicGuard {
initialized: &self.initialized,
};
let value = (self.initializer)();
core::mem::forget(panic_guard);
ptr::write(self.storage.get() as _, value);
self.initialized
.fetch_or(FINISHED_INITIALIZING, Ordering::AcqRel);
self.get_unchecked()
}
INITIALIZING => {
panic!("Recursive or overlapping initialization of static variable");
}
INITIALIZED => self.get_unchecked(),
x if x & POISONED != 0 => panic!("Static variable has been poisoned"),
_ => panic!("Invalid state for static variable"),
}
}
}
}
impl<T: Sync> Drop for Static<T> {
fn drop(&mut self) {
unsafe {
if INITIALIZED == self.initialized.fetch_or(DROPPING, Ordering::AcqRel) {
(UnsafeCell::raw_get(&self.storage) as *mut T).drop_in_place();
self.initialized.fetch_or(DROPPED, Ordering::AcqRel);
}
}
}
}