//! 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`.
// The "atomic orderings" section of the documentation above promises // "happens-before" semantics. This drives the choice of orderings in the uses // of `compare_exchange` below. On success, the value was zero/null, so there // was nothing to acquire (there is never any `Ordering::Release` store of 0). // On failure, the value was nonzero, so it was initialized previously (perhaps // on another thread) using `Ordering::Release`, so we must use // `Ordering::Acquire` to ensure that store "happens-before" this load.
#[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)
}
/// Get the reference to the underlying value, without checking if the cell /// is initialized. /// /// # Safety /// /// Caller must ensure that the cell is in initialized state, and that /// the contents are acquired by (synchronized to) this thread. pubunsafefn get_unchecked(&self) -> NonZeroUsize { #[inline(always)] fn as_const_ptr(r: &AtomicUsize) -> *const usize { use core::mem::align_of;
let p: *const AtomicUsize = r; // SAFETY: "This type has the same size and bit validity as // the underlying integer type, usize. However, the alignment of // this type is always equal to its size, even on targets where // usize has a lesser alignment." const _ALIGNMENT_COMPATIBLE: () =
assert!(align_of::<AtomicUsize>() % align_of::<usize>() == 0);
p.cast::<usize>()
}
// SAFETY: The caller is responsible for ensuring that the value // was initialized and that the contents have been acquired by // this thread. Assuming that, we can assume there will be no // conflicting writes to the value since the value will never // change once initialized. This relies on the statement in // https://doc.rust-lang.org/1.83.0/core/sync/atomic/ that "(A // `compare_exchange` or `compare_exchange_weak` that does not // succeed is not considered a write." let val = unsafe { p.read() };
// SAFETY: The caller is responsible for ensuring the value is // initialized and thus not zero. unsafe { NonZeroUsize::new_unchecked(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<(), ()> { matchself.compare_exchange(value) {
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>,
{ matchself.get() {
Some(it) => Ok(it),
None => self.init(f),
}
}
/// 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(Self::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,
{ Self::from_usize(self.inner.get_or_init(|| Self::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(Self::to_usize)).map(Self::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<(), ()> { matchself.compare_exchange(value) {
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>,
{ matchself.get() {
Some(val) => Ok(val),
None => self.init(f),
}
}
/// ```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() {}
}
/// Creates a new cell with the given value. pubfn with_value(value: Box<T>) -> Self { Self { inner: AtomicPtr::new(Box::into_raw(value)), ghost: PhantomData }
}
/// 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::Release,
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>,
{ matchself.get() {
Some(val) => Ok(val),
None => self.init(f)
}
}
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.