Rollup merge of #98165 - WaffleLapkin:once_things_renamings, r=m-ou-se
once cell renamings This PR does the renamings proposed in https://github.com/rust-lang/rust/issues/74465#issuecomment-1153703128 - Move/rename `lazy::{OnceCell, Lazy}` to `cell::{OnceCell, LazyCell}` - Move/rename `lazy::{SyncOnceCell, SyncLazy}` to `sync::{OnceLock, LazyLock}` (I used `Lazy...` instead of `...Lazy` as it seems to be more consistent, easier to pronounce, etc) ```@rustbot``` label +T-libs-api -T-libs
This commit is contained in:
121
library/std/src/sync/lazy_lock.rs
Normal file
121
library/std/src/sync/lazy_lock.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
use crate::cell::Cell;
|
||||
use crate::fmt;
|
||||
use crate::ops::Deref;
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::OnceLock;
|
||||
|
||||
/// A value which is initialized on the first access.
|
||||
///
|
||||
/// This type is a thread-safe `Lazy`, and can be used in statics.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// static HASHMAP: LazyLock<HashMap<i32, String>> = LazyLock::new(|| {
|
||||
/// println!("initializing");
|
||||
/// let mut m = HashMap::new();
|
||||
/// m.insert(13, "Spica".to_string());
|
||||
/// m.insert(74, "Hoyten".to_string());
|
||||
/// m
|
||||
/// });
|
||||
///
|
||||
/// fn main() {
|
||||
/// println!("ready");
|
||||
/// std::thread::spawn(|| {
|
||||
/// println!("{:?}", HASHMAP.get(&13));
|
||||
/// }).join().unwrap();
|
||||
/// println!("{:?}", HASHMAP.get(&74));
|
||||
///
|
||||
/// // Prints:
|
||||
/// // ready
|
||||
/// // initializing
|
||||
/// // Some("Spica")
|
||||
/// // Some("Hoyten")
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub struct LazyLock<T, F = fn() -> T> {
|
||||
cell: OnceLock<T>,
|
||||
init: Cell<Option<F>>,
|
||||
}
|
||||
|
||||
impl<T, F> LazyLock<T, F> {
|
||||
/// Creates a new lazy value with the given initializing
|
||||
/// function.
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub const fn new(f: F) -> LazyLock<T, F> {
|
||||
LazyLock { cell: OnceLock::new(), init: Cell::new(Some(f)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F: FnOnce() -> T> LazyLock<T, F> {
|
||||
/// Forces the evaluation of this lazy value and
|
||||
/// returns a reference to result. This is equivalent
|
||||
/// to the `Deref` impl, but is explicit.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::LazyLock;
|
||||
///
|
||||
/// let lazy = LazyLock::new(|| 92);
|
||||
///
|
||||
/// assert_eq!(LazyLock::force(&lazy), &92);
|
||||
/// assert_eq!(&*lazy, &92);
|
||||
/// ```
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub fn force(this: &LazyLock<T, F>) -> &T {
|
||||
this.cell.get_or_init(|| match this.init.take() {
|
||||
Some(f) => f(),
|
||||
None => panic!("Lazy instance has previously been poisoned"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T, F: FnOnce() -> T> Deref for LazyLock<T, F> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
LazyLock::force(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T: Default> Default for LazyLock<T> {
|
||||
/// Creates a new lazy value using `Default` as the initializing function.
|
||||
fn default() -> LazyLock<T> {
|
||||
LazyLock::new(T::default)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Lazy").field("cell", &self.cell).finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
// We never create a `&F` from a `&LazyLock<T, F>` so it is fine
|
||||
// to not impl `Sync` for `F`
|
||||
// we do create a `&mut Option<F>` in `force`, but this is
|
||||
// properly synchronized, so it only happens once
|
||||
// so it also does not contribute to this impl.
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
unsafe impl<T, F: Send> Sync for LazyLock<T, F> where OnceLock<T>: Sync {}
|
||||
// auto-derived `Send` impl is OK.
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T, F: UnwindSafe> RefUnwindSafe for LazyLock<T, F> where OnceLock<T>: RefUnwindSafe {}
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T, F: UnwindSafe> UnwindSafe for LazyLock<T, F> where OnceLock<T>: UnwindSafe {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
143
library/std/src/sync/lazy_lock/tests.rs
Normal file
143
library/std/src/sync/lazy_lock/tests.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use crate::{
|
||||
cell::LazyCell,
|
||||
panic,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||
Mutex,
|
||||
},
|
||||
sync::{LazyLock, OnceLock},
|
||||
thread,
|
||||
};
|
||||
|
||||
fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R {
|
||||
thread::spawn(f).join().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lazy_default() {
|
||||
static CALLED: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
struct Foo(u8);
|
||||
impl Default for Foo {
|
||||
fn default() -> Self {
|
||||
CALLED.fetch_add(1, SeqCst);
|
||||
Foo(42)
|
||||
}
|
||||
}
|
||||
|
||||
let lazy: LazyCell<Mutex<Foo>> = <_>::default();
|
||||
|
||||
assert_eq!(CALLED.load(SeqCst), 0);
|
||||
|
||||
assert_eq!(lazy.lock().unwrap().0, 42);
|
||||
assert_eq!(CALLED.load(SeqCst), 1);
|
||||
|
||||
lazy.lock().unwrap().0 = 21;
|
||||
|
||||
assert_eq!(lazy.lock().unwrap().0, 21);
|
||||
assert_eq!(CALLED.load(SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lazy_poisoning() {
|
||||
let x: LazyCell<String> = LazyCell::new(|| panic!("kaboom"));
|
||||
for _ in 0..2 {
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||
fn sync_lazy_new() {
|
||||
static CALLED: AtomicUsize = AtomicUsize::new(0);
|
||||
static SYNC_LAZY: LazyLock<i32> = LazyLock::new(|| {
|
||||
CALLED.fetch_add(1, SeqCst);
|
||||
92
|
||||
});
|
||||
|
||||
assert_eq!(CALLED.load(SeqCst), 0);
|
||||
|
||||
spawn_and_wait(|| {
|
||||
let y = *SYNC_LAZY - 30;
|
||||
assert_eq!(y, 62);
|
||||
assert_eq!(CALLED.load(SeqCst), 1);
|
||||
});
|
||||
|
||||
let y = *SYNC_LAZY - 30;
|
||||
assert_eq!(y, 62);
|
||||
assert_eq!(CALLED.load(SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_lazy_default() {
|
||||
static CALLED: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
struct Foo(u8);
|
||||
impl Default for Foo {
|
||||
fn default() -> Self {
|
||||
CALLED.fetch_add(1, SeqCst);
|
||||
Foo(42)
|
||||
}
|
||||
}
|
||||
|
||||
let lazy: LazyLock<Mutex<Foo>> = <_>::default();
|
||||
|
||||
assert_eq!(CALLED.load(SeqCst), 0);
|
||||
|
||||
assert_eq!(lazy.lock().unwrap().0, 42);
|
||||
assert_eq!(CALLED.load(SeqCst), 1);
|
||||
|
||||
lazy.lock().unwrap().0 = 21;
|
||||
|
||||
assert_eq!(lazy.lock().unwrap().0, 21);
|
||||
assert_eq!(CALLED.load(SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||
fn static_sync_lazy() {
|
||||
static XS: LazyLock<Vec<i32>> = LazyLock::new(|| {
|
||||
let mut xs = Vec::new();
|
||||
xs.push(1);
|
||||
xs.push(2);
|
||||
xs.push(3);
|
||||
xs
|
||||
});
|
||||
|
||||
spawn_and_wait(|| {
|
||||
assert_eq!(&*XS, &vec![1, 2, 3]);
|
||||
});
|
||||
|
||||
assert_eq!(&*XS, &vec![1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_sync_lazy_via_fn() {
|
||||
fn xs() -> &'static Vec<i32> {
|
||||
static XS: OnceLock<Vec<i32>> = OnceLock::new();
|
||||
XS.get_or_init(|| {
|
||||
let mut xs = Vec::new();
|
||||
xs.push(1);
|
||||
xs.push(2);
|
||||
xs.push(3);
|
||||
xs
|
||||
})
|
||||
}
|
||||
assert_eq!(xs(), &vec![1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_lazy_poisoning() {
|
||||
let x: LazyLock<String> = LazyLock::new(|| panic!("kaboom"));
|
||||
for _ in 0..2 {
|
||||
let res = panic::catch_unwind(|| x.len());
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_sync_send() {
|
||||
fn assert_traits<T: Send + Sync>() {}
|
||||
assert_traits::<LazyLock<String>>();
|
||||
}
|
||||
@@ -170,11 +170,18 @@ pub use self::poison::{LockResult, PoisonError, TryLockError, TryLockResult};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub use self::lazy_lock::LazyLock;
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub use self::once_lock::OnceLock;
|
||||
|
||||
pub mod mpsc;
|
||||
|
||||
mod barrier;
|
||||
mod condvar;
|
||||
mod lazy_lock;
|
||||
mod mutex;
|
||||
mod once;
|
||||
mod once_lock;
|
||||
mod poison;
|
||||
mod rwlock;
|
||||
|
||||
496
library/std/src/sync/once_lock.rs
Normal file
496
library/std/src/sync/once_lock.rs
Normal file
@@ -0,0 +1,496 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::pin::Pin;
|
||||
use crate::sync::Once;
|
||||
|
||||
/// A synchronization primitive which can be written to only once.
|
||||
///
|
||||
/// This type is a thread-safe `OnceCell`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// static CELL: OnceLock<String> = OnceLock::new();
|
||||
/// assert!(CELL.get().is_none());
|
||||
///
|
||||
/// std::thread::spawn(|| {
|
||||
/// let value: &String = CELL.get_or_init(|| {
|
||||
/// "Hello, World!".to_string()
|
||||
/// });
|
||||
/// assert_eq!(value, "Hello, World!");
|
||||
/// }).join().unwrap();
|
||||
///
|
||||
/// let value: Option<&String> = CELL.get();
|
||||
/// assert!(value.is_some());
|
||||
/// assert_eq!(value.unwrap().as_str(), "Hello, World!");
|
||||
/// ```
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub struct OnceLock<T> {
|
||||
once: Once,
|
||||
// Whether or not the value is initialized is tracked by `state_and_queue`.
|
||||
value: UnsafeCell<MaybeUninit<T>>,
|
||||
/// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl.
|
||||
///
|
||||
/// ```compile_fail,E0597
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// struct A<'a>(&'a str);
|
||||
///
|
||||
/// impl<'a> Drop for A<'a> {
|
||||
/// fn drop(&mut self) {}
|
||||
/// }
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// {
|
||||
/// let s = String::new();
|
||||
/// let _ = cell.set(A(&s));
|
||||
/// }
|
||||
/// ```
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> OnceLock<T> {
|
||||
/// Creates a new empty cell.
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
#[must_use]
|
||||
pub const fn new() -> OnceLock<T> {
|
||||
OnceLock {
|
||||
once: Once::new(),
|
||||
value: UnsafeCell::new(MaybeUninit::uninit()),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the reference to the underlying value.
|
||||
///
|
||||
/// Returns `None` if the cell is empty, or being initialized. This
|
||||
/// method never blocks.
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub fn get(&self) -> Option<&T> {
|
||||
if self.is_initialized() {
|
||||
// Safe b/c checked is_initialized
|
||||
Some(unsafe { self.get_unchecked() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the mutable reference to the underlying value.
|
||||
///
|
||||
/// Returns `None` if the cell is empty. This method never blocks.
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub fn get_mut(&mut self) -> Option<&mut T> {
|
||||
if self.is_initialized() {
|
||||
// Safe b/c checked is_initialized and we have a unique access
|
||||
Some(unsafe { self.get_unchecked_mut() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the contents of this cell to `value`.
|
||||
///
|
||||
/// May block if another thread is currently attempting to initialize the cell. The cell is
|
||||
/// guaranteed to contain a value when set returns, though not necessarily the one provided.
|
||||
///
|
||||
/// Returns `Ok(())` if the cell's value was set by this call.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// static CELL: OnceLock<i32> = OnceLock::new();
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert!(CELL.get().is_none());
|
||||
///
|
||||
/// std::thread::spawn(|| {
|
||||
/// assert_eq!(CELL.set(92), Ok(()));
|
||||
/// }).join().unwrap();
|
||||
///
|
||||
/// assert_eq!(CELL.set(62), Err(62));
|
||||
/// assert_eq!(CELL.get(), Some(&92));
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub fn set(&self, value: T) -> Result<(), T> {
|
||||
let mut value = Some(value);
|
||||
self.get_or_init(|| value.take().unwrap());
|
||||
match value {
|
||||
None => Ok(()),
|
||||
Some(value) => Err(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the contents of the cell, initializing it with `f` if the cell
|
||||
/// was empty.
|
||||
///
|
||||
/// Many threads may call `get_or_init` concurrently with different
|
||||
/// initializing functions, but it is guaranteed that only one function
|
||||
/// will be executed.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f` panics, the panic is propagated to the caller, and the cell
|
||||
/// remains uninitialized.
|
||||
///
|
||||
/// It is an error to reentrantly initialize the cell from `f`. The
|
||||
/// exact outcome is unspecified. Current implementation deadlocks, but
|
||||
/// this may be changed to a panic in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// let value = cell.get_or_init(|| 92);
|
||||
/// assert_eq!(value, &92);
|
||||
/// let value = cell.get_or_init(|| unreachable!());
|
||||
/// assert_eq!(value, &92);
|
||||
/// ```
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub fn get_or_init<F>(&self, f: F) -> &T
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
match self.get_or_try_init(|| Ok::<T, !>(f())) {
|
||||
Ok(val) => val,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f` panics, the panic is propagated to the caller, and
|
||||
/// the cell remains uninitialized.
|
||||
///
|
||||
/// It is an error to reentrantly initialize the cell from `f`.
|
||||
/// The exact outcome is unspecified. Current implementation
|
||||
/// deadlocks, but this may be changed to a panic in the future.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
|
||||
/// assert!(cell.get().is_none());
|
||||
/// let value = cell.get_or_try_init(|| -> Result<i32, ()> {
|
||||
/// Ok(92)
|
||||
/// });
|
||||
/// assert_eq!(value, Ok(&92));
|
||||
/// assert_eq!(cell.get(), Some(&92))
|
||||
/// ```
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
|
||||
where
|
||||
F: FnOnce() -> Result<T, E>,
|
||||
{
|
||||
// Fast path check
|
||||
// NOTE: We need to perform an acquire on the state in this method
|
||||
// in order to correctly synchronize `LazyLock::force`. This is
|
||||
// currently done by calling `self.get()`, which in turn calls
|
||||
// `self.is_initialized()`, which in turn performs the acquire.
|
||||
if let Some(value) = self.get() {
|
||||
return Ok(value);
|
||||
}
|
||||
self.initialize(f)?;
|
||||
|
||||
debug_assert!(self.is_initialized());
|
||||
|
||||
// SAFETY: The inner value has been initialized
|
||||
Ok(unsafe { self.get_unchecked() })
|
||||
}
|
||||
|
||||
/// Internal-only API that gets the contents of the cell, initializing it
|
||||
/// in two steps with `f` and `g` if the cell was empty.
|
||||
///
|
||||
/// `f` is called to construct the value, which is then moved into the cell
|
||||
/// and given as a (pinned) mutable reference to `g` to finish
|
||||
/// initialization.
|
||||
///
|
||||
/// This allows `g` to inspect an manipulate the value after it has been
|
||||
/// moved into its final place in the cell, but before the cell is
|
||||
/// considered initialized.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If `f` or `g` panics, the panic is propagated to the caller, and the
|
||||
/// cell remains uninitialized.
|
||||
///
|
||||
/// With the current implementation, if `g` panics, the value from `f` will
|
||||
/// not be dropped. This should probably be fixed if this is ever used for
|
||||
/// a type where this matters.
|
||||
///
|
||||
/// It is an error to reentrantly initialize the cell from `f`. The exact
|
||||
/// outcome is unspecified. Current implementation deadlocks, but this may
|
||||
/// be changed to a panic in the future.
|
||||
pub(crate) fn get_or_init_pin<F, G>(self: Pin<&Self>, f: F, g: G) -> Pin<&T>
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
G: FnOnce(Pin<&mut T>),
|
||||
{
|
||||
if let Some(value) = self.get_ref().get() {
|
||||
// SAFETY: The inner value was already initialized, and will not be
|
||||
// moved anymore.
|
||||
return unsafe { Pin::new_unchecked(value) };
|
||||
}
|
||||
|
||||
let slot = &self.value;
|
||||
|
||||
// Ignore poisoning from other threads
|
||||
// If another thread panics, then we'll be able to run our closure
|
||||
self.once.call_once_force(|_| {
|
||||
let value = f();
|
||||
// SAFETY: We use the Once (self.once) to guarantee unique access
|
||||
// to the UnsafeCell (slot).
|
||||
let value: &mut T = unsafe { (&mut *slot.get()).write(value) };
|
||||
// SAFETY: The value has been written to its final place in
|
||||
// self.value. We do not to move it anymore, which we promise here
|
||||
// with a Pin<&mut T>.
|
||||
g(unsafe { Pin::new_unchecked(value) });
|
||||
});
|
||||
|
||||
// SAFETY: The inner value has been initialized, and will not be moved
|
||||
// anymore.
|
||||
unsafe { Pin::new_unchecked(self.get_ref().get_unchecked()) }
|
||||
}
|
||||
|
||||
/// Consumes the `OnceLock`, returning the wrapped value. Returns
|
||||
/// `None` if the cell was empty.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let cell: OnceLock<String> = OnceLock::new();
|
||||
/// assert_eq!(cell.into_inner(), None);
|
||||
///
|
||||
/// let cell = OnceLock::new();
|
||||
/// cell.set("hello".to_string()).unwrap();
|
||||
/// assert_eq!(cell.into_inner(), Some("hello".to_string()));
|
||||
/// ```
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub fn into_inner(mut self) -> Option<T> {
|
||||
self.take()
|
||||
}
|
||||
|
||||
/// Takes the value out of this `OnceLock`, moving it back to an uninitialized state.
|
||||
///
|
||||
/// Has no effect and returns `None` if the `OnceLock` hasn't been initialized.
|
||||
///
|
||||
/// Safety is guaranteed by requiring a mutable reference.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// let mut cell: OnceLock<String> = OnceLock::new();
|
||||
/// assert_eq!(cell.take(), None);
|
||||
///
|
||||
/// let mut cell = OnceLock::new();
|
||||
/// cell.set("hello".to_string()).unwrap();
|
||||
/// assert_eq!(cell.take(), Some("hello".to_string()));
|
||||
/// assert_eq!(cell.get(), None);
|
||||
/// ```
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
pub fn take(&mut self) -> Option<T> {
|
||||
if self.is_initialized() {
|
||||
self.once = Once::new();
|
||||
// SAFETY: `self.value` is initialized and contains a valid `T`.
|
||||
// `self.once` is reset, so `is_initialized()` will be false again
|
||||
// which prevents the value from being read twice.
|
||||
unsafe { Some((&mut *self.value.get()).assume_init_read()) }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_initialized(&self) -> bool {
|
||||
self.once.is_completed()
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn initialize<F, E>(&self, f: F) -> Result<(), E>
|
||||
where
|
||||
F: FnOnce() -> Result<T, E>,
|
||||
{
|
||||
let mut res: Result<(), E> = Ok(());
|
||||
let slot = &self.value;
|
||||
|
||||
// Ignore poisoning from other threads
|
||||
// If another thread panics, then we'll be able to run our closure
|
||||
self.once.call_once_force(|p| {
|
||||
match f() {
|
||||
Ok(value) => {
|
||||
unsafe { (&mut *slot.get()).write(value) };
|
||||
}
|
||||
Err(e) => {
|
||||
res = Err(e);
|
||||
|
||||
// Treat the underlying `Once` as poisoned since we
|
||||
// failed to initialize our value. Calls
|
||||
p.poison();
|
||||
}
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The value must be initialized
|
||||
unsafe fn get_unchecked(&self) -> &T {
|
||||
debug_assert!(self.is_initialized());
|
||||
(&*self.value.get()).assume_init_ref()
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The value must be initialized
|
||||
unsafe fn get_unchecked_mut(&mut self) -> &mut T {
|
||||
debug_assert!(self.is_initialized());
|
||||
(&mut *self.value.get()).assume_init_mut()
|
||||
}
|
||||
}
|
||||
|
||||
// Why do we need `T: Send`?
|
||||
// Thread A creates a `OnceLock` and shares it with
|
||||
// scoped thread B, which fills the cell, which is
|
||||
// then destroyed by A. That is, destructor observes
|
||||
// a sent value.
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
unsafe impl<T: Send> Send for OnceLock<T> {}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceLock<T> {}
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")]
|
||||
impl<T> const Default for OnceLock<T> {
|
||||
/// Creates a new empty cell.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// fn main() {
|
||||
/// assert_eq!(OnceLock::<()>::new(), OnceLock::default());
|
||||
/// }
|
||||
/// ```
|
||||
fn default() -> OnceLock<T> {
|
||||
OnceLock::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T: fmt::Debug> fmt::Debug for OnceLock<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.get() {
|
||||
Some(v) => f.debug_tuple("Once").field(v).finish(),
|
||||
None => f.write_str("Once(Uninit)"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T: Clone> Clone for OnceLock<T> {
|
||||
fn clone(&self) -> OnceLock<T> {
|
||||
let cell = Self::new();
|
||||
if let Some(value) = self.get() {
|
||||
match cell.set(value.clone()) {
|
||||
Ok(()) => (),
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
cell
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T> From<T> for OnceLock<T> {
|
||||
/// Create a new cell with its contents set to `value`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(once_cell)]
|
||||
///
|
||||
/// use std::sync::OnceLock;
|
||||
///
|
||||
/// # fn main() -> Result<(), i32> {
|
||||
/// let a = OnceLock::from(3);
|
||||
/// let b = OnceLock::new();
|
||||
/// b.set(3)?;
|
||||
/// assert_eq!(a, b);
|
||||
/// Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(value: T) -> Self {
|
||||
let cell = Self::new();
|
||||
match cell.set(value) {
|
||||
Ok(()) => cell,
|
||||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T: PartialEq> PartialEq for OnceLock<T> {
|
||||
fn eq(&self, other: &OnceLock<T>) -> bool {
|
||||
self.get() == other.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
impl<T: Eq> Eq for OnceLock<T> {}
|
||||
|
||||
#[unstable(feature = "once_cell", issue = "74465")]
|
||||
unsafe impl<#[may_dangle] T> Drop for OnceLock<T> {
|
||||
fn drop(&mut self) {
|
||||
if self.is_initialized() {
|
||||
// SAFETY: The cell is initialized and being dropped, so it can't
|
||||
// be accessed again. We also don't touch the `T` other than
|
||||
// dropping it, which validates our usage of #[may_dangle].
|
||||
unsafe { (&mut *self.value.get()).assume_init_drop() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
203
library/std/src/sync/once_lock/tests.rs
Normal file
203
library/std/src/sync/once_lock/tests.rs
Normal file
@@ -0,0 +1,203 @@
|
||||
use crate::{
|
||||
panic,
|
||||
sync::OnceLock,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||
mpsc::channel,
|
||||
},
|
||||
thread,
|
||||
};
|
||||
|
||||
fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R {
|
||||
thread::spawn(f).join().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||
fn sync_once_cell() {
|
||||
static ONCE_CELL: OnceLock<i32> = OnceLock::new();
|
||||
|
||||
assert!(ONCE_CELL.get().is_none());
|
||||
|
||||
spawn_and_wait(|| {
|
||||
ONCE_CELL.get_or_init(|| 92);
|
||||
assert_eq!(ONCE_CELL.get(), Some(&92));
|
||||
});
|
||||
|
||||
ONCE_CELL.get_or_init(|| panic!("Kabom!"));
|
||||
assert_eq!(ONCE_CELL.get(), Some(&92));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_once_cell_get_mut() {
|
||||
let mut c = OnceLock::new();
|
||||
assert!(c.get_mut().is_none());
|
||||
c.set(90).unwrap();
|
||||
*c.get_mut().unwrap() += 2;
|
||||
assert_eq!(c.get_mut(), Some(&mut 92));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_once_cell_get_unchecked() {
|
||||
let c = OnceLock::new();
|
||||
c.set(92).unwrap();
|
||||
unsafe {
|
||||
assert_eq!(c.get_unchecked(), &92);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||
fn sync_once_cell_drop() {
|
||||
static DROP_CNT: AtomicUsize = AtomicUsize::new(0);
|
||||
struct Dropper;
|
||||
impl Drop for Dropper {
|
||||
fn drop(&mut self) {
|
||||
DROP_CNT.fetch_add(1, SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let x = OnceLock::new();
|
||||
spawn_and_wait(move || {
|
||||
x.get_or_init(|| Dropper);
|
||||
assert_eq!(DROP_CNT.load(SeqCst), 0);
|
||||
drop(x);
|
||||
});
|
||||
|
||||
assert_eq!(DROP_CNT.load(SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_once_cell_drop_empty() {
|
||||
let x = OnceLock::<String>::new();
|
||||
drop(x);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clone() {
|
||||
let s = OnceLock::new();
|
||||
let c = s.clone();
|
||||
assert!(c.get().is_none());
|
||||
|
||||
s.set("hello".to_string()).unwrap();
|
||||
let c = s.clone();
|
||||
assert_eq!(c.get().map(String::as_str), Some("hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_or_try_init() {
|
||||
let cell: OnceLock<String> = OnceLock::new();
|
||||
assert!(cell.get().is_none());
|
||||
|
||||
let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() }));
|
||||
assert!(res.is_err());
|
||||
assert!(!cell.is_initialized());
|
||||
assert!(cell.get().is_none());
|
||||
|
||||
assert_eq!(cell.get_or_try_init(|| Err(())), Err(()));
|
||||
|
||||
assert_eq!(cell.get_or_try_init(|| Ok::<_, ()>("hello".to_string())), Ok(&"hello".to_string()));
|
||||
assert_eq!(cell.get(), Some(&"hello".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_impl() {
|
||||
assert_eq!(OnceLock::from("value").get(), Some(&"value"));
|
||||
assert_ne!(OnceLock::from("foo").get(), Some(&"bar"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partialeq_impl() {
|
||||
assert!(OnceLock::from("value") == OnceLock::from("value"));
|
||||
assert!(OnceLock::from("foo") != OnceLock::from("bar"));
|
||||
|
||||
assert!(OnceLock::<String>::new() == OnceLock::new());
|
||||
assert!(OnceLock::<String>::new() != OnceLock::from("value".to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_inner() {
|
||||
let cell: OnceLock<String> = OnceLock::new();
|
||||
assert_eq!(cell.into_inner(), None);
|
||||
let cell = OnceLock::new();
|
||||
cell.set("hello".to_string()).unwrap();
|
||||
assert_eq!(cell.into_inner(), Some("hello".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_sync_send() {
|
||||
fn assert_traits<T: Send + Sync>() {}
|
||||
assert_traits::<OnceLock<String>>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_once_macro() {
|
||||
macro_rules! eval_once {
|
||||
(|| -> $ty:ty {
|
||||
$($body:tt)*
|
||||
}) => {{
|
||||
static ONCE_CELL: OnceLock<$ty> = OnceLock::new();
|
||||
fn init() -> $ty {
|
||||
$($body)*
|
||||
}
|
||||
ONCE_CELL.get_or_init(init)
|
||||
}};
|
||||
}
|
||||
|
||||
let fib: &'static Vec<i32> = eval_once! {
|
||||
|| -> Vec<i32> {
|
||||
let mut res = vec![1, 1];
|
||||
for i in 0..10 {
|
||||
let next = res[i] + res[i + 1];
|
||||
res.push(next);
|
||||
}
|
||||
res
|
||||
}
|
||||
};
|
||||
assert_eq!(fib[5], 8)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_os = "emscripten", ignore)]
|
||||
fn sync_once_cell_does_not_leak_partially_constructed_boxes() {
|
||||
static ONCE_CELL: OnceLock<String> = OnceLock::new();
|
||||
|
||||
let n_readers = 10;
|
||||
let n_writers = 3;
|
||||
const MSG: &str = "Hello, World";
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
for _ in 0..n_readers {
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
if let Some(msg) = ONCE_CELL.get() {
|
||||
tx.send(msg).unwrap();
|
||||
break;
|
||||
}
|
||||
#[cfg(target_env = "sgx")]
|
||||
crate::thread::yield_now();
|
||||
}
|
||||
});
|
||||
}
|
||||
for _ in 0..n_writers {
|
||||
thread::spawn(move || {
|
||||
let _ = ONCE_CELL.set(MSG.to_owned());
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..n_readers {
|
||||
let msg = rx.recv().unwrap();
|
||||
assert_eq!(msg, MSG);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dropck() {
|
||||
let cell = OnceLock::new();
|
||||
{
|
||||
let s = String::new();
|
||||
cell.set(&s).unwrap();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user