//! Thread-safe, non-blocking, "first one wins" flavor of `OnceCell`. //! //! If two threads race to initialize a type from the `race` module, they //! don't block, execute initialization function together, but only one of //! them stores the result. //! //! This module does not require `std` feature. //! //! # Atomic orderings //! //! All types in this module use `Acquire` and `Release` //! [atomic orderings](Ordering) for all their operations. While this is not //! strictly necessary for types other than `OnceBox`, it is useful for users as //! it allows them to be certain that after `get` or `get_or_init` returns on //! one thread, any side-effects caused by the setter thread prior to them //! calling `set` or `get_or_init` will be made visible to that thread; without //! it, it's possible for it to appear as if they haven't happened yet from the //! getter thread's perspective. This is an acceptable tradeoff to make since //! `Acquire` and `Release` have very little performance overhead on most //! architectures versus `Relaxed`.
#[cfg(not(feature = "portable-atomic"))] use core::sync::atomic; #[cfg(feature = "portable-atomic")] use portable_atomic as atomic;
use atomic::{AtomicPtr, AtomicUsize, Ordering}; use core::cell::UnsafeCell; use core::marker::PhantomData; use core::num::NonZeroUsize; use core::ptr;
/// A thread-safe cell which can be written to only once. #[derive(Default, Debug)] pubstruct OnceNonZeroUsize {
inner: AtomicUsize,
}
/// Gets the underlying value. #[inline] pubfn get(&self) -> Option<NonZeroUsize> { let val = self.inner.load(Ordering::Acquire);
NonZeroUsize::new(val)
}
/// Sets the contents of this cell to `value`. /// /// Returns `Ok(())` if the cell was empty and `Err(())` if it was /// full. #[inline] pubfn set(&self, value: NonZeroUsize) -> Result<(), ()> { let exchange = self.inner.compare_exchange(0, value.get(), Ordering::AcqRel, Ordering::Acquire); match exchange {
Ok(_) => Ok(()),
Err(_) => Err(()),
}
}
/// Gets the contents of the cell, initializing it with `f` if the cell was /// empty. /// /// If several threads concurrently run `get_or_init`, more than one `f` can /// be called. However, all threads will return the same value, produced by /// some `f`. pubfn get_or_init<F>(&self, f: F) -> NonZeroUsize where
F: FnOnce() -> NonZeroUsize,
{ enum Void {} matchself.get_or_try_init(|| Ok::<NonZeroUsize, Void>(f())) {
Ok(val) => val,
Err(void) => match void {},
}
}
/// Gets the contents of the cell, initializing it with `f` if /// the cell was empty. If the cell was empty and `f` failed, an /// error is returned. /// /// If several threads concurrently run `get_or_init`, more than one `f` can /// be called. However, all threads will return the same value, produced by /// some `f`. pubfn get_or_try_init<F, E>(&self, f: F) -> Result<NonZeroUsize, E> where
F: FnOnce() -> Result<NonZeroUsize, E>,
{ let val = self.inner.load(Ordering::Acquire); let res = match NonZeroUsize::new(val) {
Some(it) => it,
None => { letmut val = f()?.get(); let exchange = self.inner.compare_exchange(0, val, Ordering::AcqRel, Ordering::Acquire); iflet Err(old) = exchange {
val = old;
} unsafe { NonZeroUsize::new_unchecked(val) }
}
};
Ok(res)
}
}
/// A thread-safe cell which can be written to only once. #[derive(Default, Debug)] pubstruct OnceBool {
inner: OnceNonZeroUsize,
}
/// Sets the contents of this cell to `value`. /// /// Returns `Ok(())` if the cell was empty and `Err(())` if it was /// full. #[inline] pubfn set(&self, value: bool) -> Result<(), ()> { self.inner.set(OnceBool::to_usize(value))
}
/// Gets the contents of the cell, initializing it with `f` if the cell was /// empty. /// /// If several threads concurrently run `get_or_init`, more than one `f` can /// be called. However, all threads will return the same value, produced by /// some `f`. pubfn get_or_init<F>(&self, f: F) -> bool where
F: FnOnce() -> bool,
{
OnceBool::from_usize(self.inner.get_or_init(|| OnceBool::to_usize(f())))
}
/// Gets the contents of the cell, initializing it with `f` if /// the cell was empty. If the cell was empty and `f` failed, an /// error is returned. /// /// If several threads concurrently run `get_or_init`, more than one `f` can /// be called. However, all threads will return the same value, produced by /// some `f`. pubfn get_or_try_init<F, E>(&self, f: F) -> Result<bool, E> where
F: FnOnce() -> Result<bool, E>,
{ self.inner.get_or_try_init(|| f().map(OnceBool::to_usize)).map(OnceBool::from_usize)
}
/// Gets a reference to the underlying value. pubfn get(&self) -> Option<&'a T> { let ptr = self.inner.load(Ordering::Acquire); unsafe { ptr.as_ref() }
}
/// Sets the contents of this cell to `value`. /// /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was /// full. pubfn set(&self, value: &'a T) -> Result<(), ()> { let ptr = value as *const T as *mut T; let exchange = self.inner.compare_exchange(ptr::null_mut(), ptr, Ordering::AcqRel, Ordering::Acquire); match exchange {
Ok(_) => Ok(()),
Err(_) => Err(()),
}
}
/// Gets the contents of the cell, initializing it with `f` if the cell was /// empty. /// /// If several threads concurrently run `get_or_init`, more than one `f` can /// be called. However, all threads will return the same value, produced by /// some `f`. pubfn get_or_init<F>(&self, f: F) -> &'a T where
F: FnOnce() -> &'a T,
{ enum Void {} matchself.get_or_try_init(|| Ok::<&'a T, Void>(f())) {
Ok(val) => val,
Err(void) => match void {},
}
}
/// Gets the contents of the cell, initializing it with `f` if /// the cell was empty. If the cell was empty and `f` failed, an /// error is returned. /// /// If several threads concurrently run `get_or_init`, more than one `f` can /// be called. However, all threads will return the same value, produced by /// some `f`. pubfn get_or_try_init<F, E>(&self, f: F) -> Result<&'a T, E> where
F: FnOnce() -> Result<&'a T, E>,
{ letmut ptr = self.inner.load(Ordering::Acquire);
if ptr.is_null() { // TODO replace with `cast_mut` when MSRV reaches 1.65.0 (also in `set`)
ptr = f()? as *const T as *mut T; let exchange = self.inner.compare_exchange(
ptr::null_mut(),
ptr,
Ordering::AcqRel,
Ordering::Acquire,
); iflet Err(old) = exchange {
ptr = old;
}
}
Ok(unsafe { &*ptr })
}
/// ```compile_fail /// use once_cell::race::OnceRef; /// /// let mut l = OnceRef::new(); /// /// { /// let y = 2; /// let mut r = OnceRef::new(); /// r.set(&y).unwrap(); /// core::mem::swap(&mut l, &mut r); /// } /// /// // l now contains a dangling reference to y /// eprintln!("uaf: {}", l.get().unwrap()); /// ``` fn _dummy() {}
}
/// Gets a reference to the underlying value. pubfn get(&self) -> Option<&T> { let ptr = self.inner.load(Ordering::Acquire); if ptr.is_null() { return None;
}
Some(unsafe { &*ptr })
}
/// Sets the contents of this cell to `value`. /// /// Returns `Ok(())` if the cell was empty and `Err(value)` if it was /// full. pubfn set(&self, value: Box<T>) -> Result<(), Box<T>> { let ptr = Box::into_raw(value); let exchange = self.inner.compare_exchange(
ptr::null_mut(),
ptr,
Ordering::AcqRel,
Ordering::Acquire,
); if exchange.is_err() { let value = unsafe { Box::from_raw(ptr) }; return Err(value);
}
Ok(())
}
/// Gets the contents of the cell, initializing it with `f` if the cell was /// empty. /// /// If several threads concurrently run `get_or_init`, more than one `f` can /// be called. However, all threads will return the same value, produced by /// some `f`. pubfn get_or_init<F>(&self, f: F) -> &T where
F: FnOnce() -> Box<T>,
{ enum Void {} matchself.get_or_try_init(|| Ok::<Box<T>, Void>(f())) {
Ok(val) => val,
Err(void) => match void {},
}
}
/// Gets the contents of the cell, initializing it with `f` if /// the cell was empty. If the cell was empty and `f` failed, an /// error is returned. /// /// If several threads concurrently run `get_or_init`, more than one `f` can /// be called. However, all threads will return the same value, produced by /// some `f`. pubfn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E> where
F: FnOnce() -> Result<Box<T>, E>,
{ letmut ptr = self.inner.load(Ordering::Acquire);
if ptr.is_null() { let val = f()?;
ptr = Box::into_raw(val); let exchange = self.inner.compare_exchange(
ptr::null_mut(),
ptr,
Ordering::AcqRel,
Ordering::Acquire,
); iflet Err(old) = exchange {
drop(unsafe { Box::from_raw(ptr) });
ptr = old;
}
};
Ok(unsafe { &*ptr })
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.