os/src/sync.rs

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);
)+
};
}