102 lines
2.9 KiB
Rust
102 lines
2.9 KiB
Rust
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<T> {
|
|
val: UnsafeCell<T>,
|
|
}
|
|
|
|
impl<T> UnsafeWriteCell<T> {
|
|
/// 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<T> Deref for UnsafeWriteCell<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
unsafe { &*self.val.get() }
|
|
}
|
|
}
|
|
|
|
// safe because data races cannot happen without a write
|
|
unsafe impl<T> Send for UnsafeWriteCell<T> {}
|
|
unsafe impl<T> Sync for UnsafeWriteCell<T> {}
|
|
|
|
/// A struct which is only initialized once and can be shared between threads.
|
|
pub struct SyncLazy<T, F: Fn() -> T = fn() -> T> {
|
|
val: UnsafeCell<MaybeUninit<T>>,
|
|
used: AtomicBool,
|
|
closure: F,
|
|
}
|
|
|
|
impl<T, F: Fn() -> T> SyncLazy<T, F> {
|
|
/// 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, F: Fn() -> T> Deref for SyncLazy<T, F> {
|
|
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, F: Send + Fn() -> T> Send for SyncLazy<T, F> {}
|
|
unsafe impl<T: Sync, F: Sync + Fn() -> T> Sync for SyncLazy<T, F> {}
|
|
|
|
#[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);
|
|
)+
|
|
};
|
|
}
|