use core::{ cell::UnsafeCell, ops::Deref, sync::atomic::{AtomicBool, Ordering}, mem::MaybeUninit, }; /// A wrapper around a type that is safe to read but unsafe to write. /// /// Always implements [`Send`] and [`Sync`], because data races cannot happen without writes. /// /// This type [`Deref`]s to its contents, so you write using [`UnsafeWriteCell::write()`] instead of `cell.write()`. #[repr(transparent)] pub struct UnsafeWriteCell { val: UnsafeCell, } impl UnsafeWriteCell { /// Creates a new `UnsafeWrite` containing the passed value. pub const fn new(val: T) -> Self { Self { val: UnsafeCell::new(val), } } /// Overwrites the value in the `UnsafeWrite` with the provided value. /// /// Takes an `&Self` so that one can write to statics. pub unsafe fn write(this: &Self, val: T) { *this.val.get() = val; } } impl Deref for UnsafeWriteCell { type Target = T; fn deref(&self) -> &Self::Target { unsafe { &*self.val.get() } } } // safe because data races cannot happen without a write unsafe impl Send for UnsafeWriteCell {} unsafe impl Sync for UnsafeWriteCell {} /// A struct which is only initialized once and can be shared between threads. pub struct SyncLazy T = fn() -> T> { val: UnsafeCell>, used: AtomicBool, closure: F, } impl T> SyncLazy { /// Creates a new `SyncLazy` containing the specified value. pub const fn new(closure: F) -> Self { Self { val: UnsafeCell::new(MaybeUninit::uninit()), used: AtomicBool::new(false), closure, } } /// Gets a reference to the value in the `SyncLazy`. /// /// Cannot be called as `lazy.get()` to prevent interference with a method on `T` through [`Deref`]. pub fn get(this: &Self) -> &T { let val = unsafe { &mut *this.val.get() }; if this .used .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire) .is_ok() { val.write((this.closure)()); // i think this is the right way to ensure that the write above never causes a data race? this.used.store(true, Ordering::Release); } unsafe { val.assume_init_ref() } } } impl T> Deref for SyncLazy { type Target = T; fn deref(&self) -> &Self::Target { Self::get(self) } } // safe cause we use AtomicBool to make sure that we only write once unsafe impl T> Send for SyncLazy {} unsafe impl T> Sync for SyncLazy {} #[macro_export] macro_rules! lazy_static { ($($vis:vis static ref $ident:ident : $ty:ty = $expr:expr;)+) => { $( $vis static $ident: $crate::sync::SyncLazy<$ty> = $crate::sync::SyncLazy::new(|| $expr); )+ }; }