Skip to content

Instantly share code, notes, and snippets.

@Mark-Simulacrum

Mark-Simulacrum/diff.diff Secret

Created Jul 24, 2020
Embed
What would you like to do?
git diff d3df8512 cfade738
diff --git a/src/libcore/lazy.rs b/src/libcore/lazy.rs
new file mode 100644
index 00000000000..5cf7217ef11
--- /dev/null
+++ b/src/libcore/lazy.rs
@@ -0,0 +1,379 @@
+//! Lazy values and one-time initialization of static data.
+
+use crate::cell::{Cell, UnsafeCell};
+use crate::fmt;
+use crate::mem;
+use crate::ops::Deref;
+
+/// A cell which can be written to only once.
+///
+/// Unlike `RefCell`, a `OnceCell` only provides shared `&T` references to its value.
+/// Unlike `Cell`, a `OnceCell` doesn't require copying or replacing the value to access it.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(once_cell)]
+///
+/// use std::lazy::OnceCell;
+///
+/// let cell = OnceCell::new();
+/// assert!(cell.get().is_none());
+///
+/// let value: &String = cell.get_or_init(|| {
+/// "Hello, World!".to_string()
+/// });
+/// assert_eq!(value, "Hello, World!");
+/// assert!(cell.get().is_some());
+/// ```
+#[unstable(feature = "once_cell", issue = "74465")]
+pub struct OnceCell<T> {
+ // Invariant: written to at most once.
+ inner: UnsafeCell<Option<T>>,
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T> Default for OnceCell<T> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: fmt::Debug> fmt::Debug for OnceCell<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.get() {
+ Some(v) => f.debug_tuple("OnceCell").field(v).finish(),
+ None => f.write_str("OnceCell(Uninit)"),
+ }
+ }
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: Clone> Clone for OnceCell<T> {
+ fn clone(&self) -> OnceCell<T> {
+ let res = OnceCell::new();
+ if let Some(value) = self.get() {
+ match res.set(value.clone()) {
+ Ok(()) => (),
+ Err(_) => unreachable!(),
+ }
+ }
+ res
+ }
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: PartialEq> PartialEq for OnceCell<T> {
+ fn eq(&self, other: &Self) -> bool {
+ self.get() == other.get()
+ }
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: Eq> Eq for OnceCell<T> {}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T> From<T> for OnceCell<T> {
+ fn from(value: T) -> Self {
+ OnceCell { inner: UnsafeCell::new(Some(value)) }
+ }
+}
+
+impl<T> OnceCell<T> {
+ /// Creates a new empty cell.
+ #[unstable(feature = "once_cell", issue = "74465")]
+ pub const fn new() -> OnceCell<T> {
+ OnceCell { inner: UnsafeCell::new(None) }
+ }
+
+ /// Gets the reference to the underlying value.
+ ///
+ /// Returns `None` if the cell is empty.
+ #[unstable(feature = "once_cell", issue = "74465")]
+ pub fn get(&self) -> Option<&T> {
+ // Safety: Safe due to `inner`'s invariant
+ unsafe { &*self.inner.get() }.as_ref()
+ }
+
+ /// Gets the mutable reference to the underlying value.
+ ///
+ /// Returns `None` if the cell is empty.
+ #[unstable(feature = "once_cell", issue = "74465")]
+ pub fn get_mut(&mut self) -> Option<&mut T> {
+ // Safety: Safe because we have unique access
+ unsafe { &mut *self.inner.get() }.as_mut()
+ }
+
+ /// Sets the contents of the cell to `value`.
+ ///
+ /// # Errors
+ ///
+ /// This method returns `Ok(())` if the cell was empty and `Err(value)` if
+ /// it was full.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::OnceCell;
+ ///
+ /// let cell = OnceCell::new();
+ /// assert!(cell.get().is_none());
+ ///
+ /// assert_eq!(cell.set(92), Ok(()));
+ /// assert_eq!(cell.set(62), Err(62));
+ ///
+ /// assert!(cell.get().is_some());
+ /// ```
+ #[unstable(feature = "once_cell", issue = "74465")]
+ pub fn set(&self, value: T) -> Result<(), T> {
+ // Safety: Safe because we cannot have overlapping mutable borrows
+ let slot = unsafe { &*self.inner.get() };
+ if slot.is_some() {
+ return Err(value);
+ }
+
+ // Safety: This is the only place where we set the slot, no races
+ // due to reentrancy/concurrency are possible, and we've
+ // checked that slot is currently `None`, so this write
+ // maintains the `inner`'s invariant.
+ let slot = unsafe { &mut *self.inner.get() };
+ *slot = Some(value);
+ Ok(())
+ }
+
+ /// Gets the contents of the cell, initializing it with `f`
+ /// if the cell was empty.
+ ///
+ /// # 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`. Doing
+ /// so results in a panic.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::OnceCell;
+ ///
+ /// let cell = OnceCell::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`. Doing
+ /// so results in a panic.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::OnceCell;
+ ///
+ /// let cell = OnceCell::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>,
+ {
+ if let Some(val) = self.get() {
+ return Ok(val);
+ }
+ let val = f()?;
+ // Note that *some* forms of reentrant initialization might lead to
+ // UB (see `reentrant_init` test). I believe that just removing this
+ // `assert`, while keeping `set/get` would be sound, but it seems
+ // better to panic, rather than to silently use an old value.
+ assert!(self.set(val).is_ok(), "reentrant init");
+ Ok(self.get().unwrap())
+ }
+
+ /// Consumes the cell, returning the wrapped value.
+ ///
+ /// Returns `None` if the cell was empty.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::OnceCell;
+ ///
+ /// let cell: OnceCell<String> = OnceCell::new();
+ /// assert_eq!(cell.into_inner(), None);
+ ///
+ /// let cell = OnceCell::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(self) -> Option<T> {
+ // Because `into_inner` takes `self` by value, the compiler statically verifies
+ // that it is not currently borrowed. So it is safe to move out `Option<T>`.
+ self.inner.into_inner()
+ }
+
+ /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state.
+ ///
+ /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized.
+ ///
+ /// Safety is guaranteed by requiring a mutable reference.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::OnceCell;
+ ///
+ /// let mut cell: OnceCell<String> = OnceCell::new();
+ /// assert_eq!(cell.take(), None);
+ ///
+ /// let mut cell = OnceCell::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> {
+ mem::take(self).into_inner()
+ }
+}
+
+/// A value which is initialized on the first access.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(once_cell)]
+///
+/// use std::lazy::Lazy;
+///
+/// let lazy: Lazy<i32> = Lazy::new(|| {
+/// println!("initializing");
+/// 92
+/// });
+/// println!("ready");
+/// println!("{}", *lazy);
+/// println!("{}", *lazy);
+///
+/// // Prints:
+/// // ready
+/// // initializing
+/// // 92
+/// // 92
+/// ```
+#[unstable(feature = "once_cell", issue = "74465")]
+pub struct Lazy<T, F = fn() -> T> {
+ cell: OnceCell<T>,
+ init: Cell<Option<F>>,
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: fmt::Debug, F> fmt::Debug for Lazy<T, F> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish()
+ }
+}
+
+impl<T, F> Lazy<T, F> {
+ /// Creates a new lazy value with the given initializing function.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// # fn main() {
+ /// use std::lazy::Lazy;
+ ///
+ /// let hello = "Hello, World!".to_string();
+ ///
+ /// let lazy = Lazy::new(|| hello.to_uppercase());
+ ///
+ /// assert_eq!(&*lazy, "HELLO, WORLD!");
+ /// # }
+ /// ```
+ #[unstable(feature = "once_cell", issue = "74465")]
+ pub const fn new(init: F) -> Lazy<T, F> {
+ Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) }
+ }
+}
+
+impl<T, F: FnOnce() -> T> Lazy<T, F> {
+ /// Forces the evaluation of this lazy value and returns a reference to
+ /// the result.
+ ///
+ /// This is equivalent to the `Deref` impl, but is explicit.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::Lazy;
+ ///
+ /// let lazy = Lazy::new(|| 92);
+ ///
+ /// assert_eq!(Lazy::force(&lazy), &92);
+ /// assert_eq!(&*lazy, &92);
+ /// ```
+ #[unstable(feature = "once_cell", issue = "74465")]
+ pub fn force(this: &Lazy<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 Lazy<T, F> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ Lazy::force(self)
+ }
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: Default> Default for Lazy<T> {
+ /// Creates a new lazy value using `Default` as the initializing function.
+ fn default() -> Lazy<T> {
+ Lazy::new(T::default)
+ }
+}
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 2f2206a117c..1621cf79751 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -239,6 +239,8 @@ pub mod char;
pub mod ffi;
#[cfg(not(test))] // See #65860
pub mod iter;
+#[unstable(feature = "once_cell", issue = "74465")]
+pub mod lazy;
pub mod option;
pub mod panic;
pub mod panicking;
diff --git a/src/libcore/tests/lazy.rs b/src/libcore/tests/lazy.rs
new file mode 100644
index 00000000000..1c0bddb9aef
--- /dev/null
+++ b/src/libcore/tests/lazy.rs
@@ -0,0 +1,124 @@
+use core::{
+ cell::Cell,
+ lazy::{Lazy, OnceCell},
+ sync::atomic::{AtomicUsize, Ordering::SeqCst},
+};
+
+#[test]
+fn once_cell() {
+ let c = OnceCell::new();
+ assert!(c.get().is_none());
+ c.get_or_init(|| 92);
+ assert_eq!(c.get(), Some(&92));
+
+ c.get_or_init(|| panic!("Kabom!"));
+ assert_eq!(c.get(), Some(&92));
+}
+
+#[test]
+fn once_cell_get_mut() {
+ let mut c = OnceCell::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 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 = OnceCell::new();
+ x.get_or_init(|| Dropper);
+ assert_eq!(DROP_CNT.load(SeqCst), 0);
+ drop(x);
+ assert_eq!(DROP_CNT.load(SeqCst), 1);
+}
+
+#[test]
+fn unsync_once_cell_drop_empty() {
+ let x = OnceCell::<&'static str>::new();
+ drop(x);
+}
+
+#[test]
+fn clone() {
+ let s = OnceCell::new();
+ let c = s.clone();
+ assert!(c.get().is_none());
+
+ s.set("hello").unwrap();
+ let c = s.clone();
+ assert_eq!(c.get().map(|c| *c), Some("hello"));
+}
+
+#[test]
+fn from_impl() {
+ assert_eq!(OnceCell::from("value").get(), Some(&"value"));
+ assert_ne!(OnceCell::from("foo").get(), Some(&"bar"));
+}
+
+#[test]
+fn partialeq_impl() {
+ assert!(OnceCell::from("value") == OnceCell::from("value"));
+ assert!(OnceCell::from("foo") != OnceCell::from("bar"));
+
+ assert!(OnceCell::<&'static str>::new() == OnceCell::new());
+ assert!(OnceCell::<&'static str>::new() != OnceCell::from("value"));
+}
+
+#[test]
+fn into_inner() {
+ let cell: OnceCell<&'static str> = OnceCell::new();
+ assert_eq!(cell.into_inner(), None);
+ let cell = OnceCell::new();
+ cell.set("hello").unwrap();
+ assert_eq!(cell.into_inner(), Some("hello"));
+}
+
+#[test]
+fn lazy_new() {
+ let called = Cell::new(0);
+ let x = Lazy::new(|| {
+ called.set(called.get() + 1);
+ 92
+ });
+
+ assert_eq!(called.get(), 0);
+
+ let y = *x - 30;
+ assert_eq!(y, 62);
+ assert_eq!(called.get(), 1);
+
+ let y = *x - 30;
+ assert_eq!(y, 62);
+ assert_eq!(called.get(), 1);
+}
+
+#[test]
+fn aliasing_in_get() {
+ let x = OnceCell::new();
+ x.set(42).unwrap();
+ let at_x = x.get().unwrap(); // --- (shared) borrow of inner `Option<T>` --+
+ let _ = x.set(27); // <-- temporary (unique) borrow of inner `Option<T>` |
+ println!("{}", at_x); // <------- up until here ---------------------------+
+}
+
+#[test]
+#[should_panic(expected = "reentrant init")]
+fn reentrant_init() {
+ let x: OnceCell<Box<i32>> = OnceCell::new();
+ let dangling_ref: Cell<Option<&i32>> = Cell::new(None);
+ x.get_or_init(|| {
+ let r = x.get_or_init(|| Box::new(92));
+ dangling_ref.set(Some(r));
+ Box::new(62)
+ });
+ eprintln!("use after free: {:?}", dangling_ref.get().unwrap());
+}
diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs
index 090ce471745..47ed6db6c67 100644
--- a/src/libcore/tests/lib.rs
+++ b/src/libcore/tests/lib.rs
@@ -43,6 +43,7 @@
#![feature(option_unwrap_none)]
#![feature(peekable_next_if)]
#![feature(partition_point)]
+#![feature(once_cell)]
#![feature(unsafe_block_in_unsafe_fn)]
#![deny(unsafe_op_in_unsafe_fn)]
@@ -62,6 +63,7 @@ mod fmt;
mod hash;
mod intrinsics;
mod iter;
+mod lazy;
mod manually_drop;
mod mem;
mod nonzero;
diff --git a/src/librustc_middle/lib.rs b/src/librustc_middle/lib.rs
index b7dccb8d8ce..a68301385b7 100644
--- a/src/librustc_middle/lib.rs
+++ b/src/librustc_middle/lib.rs
@@ -27,6 +27,7 @@
#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(box_syntax)]
+#![feature(cmp_min_max_by)]
#![feature(const_fn)]
#![feature(const_panic)]
#![feature(const_fn_transmute)]
diff --git a/src/librustc_middle/ty/layout.rs b/src/librustc_middle/ty/layout.rs
index 82daae7d921..8ae9269a6bf 100644
--- a/src/librustc_middle/ty/layout.rs
+++ b/src/librustc_middle/ty/layout.rs
@@ -876,6 +876,8 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
.iter_enumerated()
.all(|(i, v)| v.discr == ty::VariantDiscr::Relative(i.as_u32()));
+ let mut niche_filling_layout = None;
+
// Niche-filling enum optimization.
if !def.repr.inhibit_enum_layout_opt() && no_explicit_discriminants {
let mut dataful_variant = None;
@@ -972,7 +974,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
let largest_niche =
Niche::from_scalar(dl, offset, niche_scalar.clone());
- return Ok(tcx.intern_layout(Layout {
+ niche_filling_layout = Some(Layout {
variants: Variants::Multiple {
tag: niche_scalar,
tag_encoding: TagEncoding::Niche {
@@ -991,7 +993,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
largest_niche,
size,
align,
- }));
+ });
}
}
}
@@ -1214,7 +1216,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
let largest_niche = Niche::from_scalar(dl, Size::ZERO, tag.clone());
- tcx.intern_layout(Layout {
+ let tagged_layout = Layout {
variants: Variants::Multiple {
tag,
tag_encoding: TagEncoding::Direct,
@@ -1229,7 +1231,23 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
abi,
align,
size,
- })
+ };
+
+ let best_layout = match (tagged_layout, niche_filling_layout) {
+ (tagged_layout, Some(niche_filling_layout)) => {
+ // Pick the smaller layout; otherwise,
+ // pick the layout with the larger niche; otherwise,
+ // pick tagged as it has simpler codegen.
+ cmp::min_by_key(tagged_layout, niche_filling_layout, |layout| {
+ let niche_size =
+ layout.largest_niche.as_ref().map_or(0, |n| n.available(dl));
+ (layout.size, cmp::Reverse(niche_size))
+ })
+ }
+ (tagged_layout, None) => tagged_layout,
+ };
+
+ tcx.intern_layout(best_layout)
}
// Types with no meaningful known layout.
diff --git a/src/libstd/lazy.rs b/src/libstd/lazy.rs
new file mode 100644
index 00000000000..86e1cfae582
--- /dev/null
+++ b/src/libstd/lazy.rs
@@ -0,0 +1,844 @@
+//! Lazy values and one-time initialization of static data.
+
+use crate::{
+ cell::{Cell, UnsafeCell},
+ fmt,
+ mem::{self, MaybeUninit},
+ ops::{Deref, Drop},
+ panic::{RefUnwindSafe, UnwindSafe},
+ sync::Once,
+};
+
+#[doc(inline)]
+#[unstable(feature = "once_cell", issue = "74465")]
+pub use core::lazy::*;
+
+/// A synchronization primitive which can be written to only once.
+///
+/// This type is a thread-safe `OnceCell`.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(once_cell)]
+///
+/// use std::lazy::SyncOnceCell;
+///
+/// static CELL: SyncOnceCell<String> = SyncOnceCell::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 SyncOnceCell<T> {
+ once: Once,
+ // Whether or not the value is initialized is tracked by `state_and_queue`.
+ value: UnsafeCell<MaybeUninit<T>>,
+}
+
+// Why do we need `T: Send`?
+// Thread A creates a `SyncOnceCell` 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 SyncOnceCell<T> {}
+#[unstable(feature = "once_cell", issue = "74465")]
+unsafe impl<T: Send> Send for SyncOnceCell<T> {}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for SyncOnceCell<T> {}
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: UnwindSafe> UnwindSafe for SyncOnceCell<T> {}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T> Default for SyncOnceCell<T> {
+ fn default() -> SyncOnceCell<T> {
+ SyncOnceCell::new()
+ }
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: fmt::Debug> fmt::Debug for SyncOnceCell<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 SyncOnceCell<T> {
+ fn clone(&self) -> SyncOnceCell<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 SyncOnceCell<T> {
+ 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 SyncOnceCell<T> {
+ fn eq(&self, other: &SyncOnceCell<T>) -> bool {
+ self.get() == other.get()
+ }
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: Eq> Eq for SyncOnceCell<T> {}
+
+impl<T> SyncOnceCell<T> {
+ /// Creates a new empty cell.
+ #[unstable(feature = "once_cell", issue = "74465")]
+ pub const fn new() -> SyncOnceCell<T> {
+ SyncOnceCell { once: Once::new(), value: UnsafeCell::new(MaybeUninit::uninit()) }
+ }
+
+ /// 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`.
+ ///
+ /// Returns `Ok(())` if the cell's value was updated.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::SyncOnceCell;
+ ///
+ /// static CELL: SyncOnceCell<i32> = SyncOnceCell::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::lazy::SyncOnceCell;
+ ///
+ /// let cell = SyncOnceCell::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::lazy::SyncOnceCell;
+ ///
+ /// let cell = SyncOnceCell::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 `SyncLazy::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() })
+ }
+
+ /// Consumes the `SyncOnceCell`, returning the wrapped value. Returns
+ /// `None` if the cell was empty.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::SyncOnceCell;
+ ///
+ /// let cell: SyncOnceCell<String> = SyncOnceCell::new();
+ /// assert_eq!(cell.into_inner(), None);
+ ///
+ /// let cell = SyncOnceCell::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> {
+ // Safety: Safe because we immediately free `self` without dropping
+ let inner = unsafe { self.take_inner() };
+
+ // Don't drop this `SyncOnceCell`. We just moved out one of the fields, but didn't set
+ // the state to uninitialized.
+ mem::ManuallyDrop::new(self);
+ inner
+ }
+
+ /// Takes the value out of this `SyncOnceCell`, moving it back to an uninitialized state.
+ ///
+ /// Has no effect and returns `None` if the `SyncOnceCell` hasn't been initialized.
+ ///
+ /// Safety is guaranteed by requiring a mutable reference.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(once_cell)]
+ ///
+ /// use std::lazy::SyncOnceCell;
+ ///
+ /// let mut cell: SyncOnceCell<String> = SyncOnceCell::new();
+ /// assert_eq!(cell.take(), None);
+ ///
+ /// let mut cell = SyncOnceCell::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> {
+ mem::take(self).into_inner()
+ }
+
+ /// Takes the wrapped value out of a `SyncOnceCell`.
+ /// Afterwards the cell is no longer initialized.
+ ///
+ /// Safety: The cell must now be free'd WITHOUT dropping. No other usages of the cell
+ /// are valid. Only used by `into_inner` and `drop`.
+ unsafe fn take_inner(&mut self) -> Option<T> {
+ // The mutable reference guarantees there are no other threads that can observe us
+ // taking out the wrapped value.
+ // Right after this function `self` is supposed to be freed, so it makes little sense
+ // to atomically set the state to uninitialized.
+ if self.is_initialized() {
+ let value = mem::replace(&mut self.value, UnsafeCell::new(MaybeUninit::uninit()));
+ Some(value.into_inner().assume_init())
+ } 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()).get_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()).get_mut()
+ }
+}
+
+impl<T> Drop for SyncOnceCell<T> {
+ fn drop(&mut self) {
+ // Safety: The cell is being dropped, so it can't be accessed again
+ unsafe { self.take_inner() };
+ }
+}
+
+/// 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::lazy::SyncLazy;
+///
+/// static HASHMAP: SyncLazy<HashMap<i32, String>> = SyncLazy::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 SyncLazy<T, F = fn() -> T> {
+ cell: SyncOnceCell<T>,
+ init: Cell<Option<F>>,
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: fmt::Debug, F> fmt::Debug for SyncLazy<T, F> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish()
+ }
+}
+
+// We never create a `&F` from a `&SyncLazy<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 SyncLazy<T, F> where SyncOnceCell<T>: Sync {}
+// auto-derived `Send` impl is OK.
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T, F: RefUnwindSafe> RefUnwindSafe for SyncLazy<T, F> where SyncOnceCell<T>: RefUnwindSafe {}
+
+impl<T, F> SyncLazy<T, F> {
+ /// Creates a new lazy value with the given initializing
+ /// function.
+ #[unstable(feature = "once_cell", issue = "74465")]
+ pub const fn new(f: F) -> SyncLazy<T, F> {
+ SyncLazy { cell: SyncOnceCell::new(), init: Cell::new(Some(f)) }
+ }
+}
+
+impl<T, F: FnOnce() -> T> SyncLazy<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::lazy::SyncLazy;
+ ///
+ /// let lazy = SyncLazy::new(|| 92);
+ ///
+ /// assert_eq!(SyncLazy::force(&lazy), &92);
+ /// assert_eq!(&*lazy, &92);
+ /// ```
+ #[unstable(feature = "once_cell", issue = "74465")]
+ pub fn force(this: &SyncLazy<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 SyncLazy<T, F> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ SyncLazy::force(self)
+ }
+}
+
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T: Default> Default for SyncLazy<T> {
+ /// Creates a new lazy value using `Default` as the initializing function.
+ fn default() -> SyncLazy<T> {
+ SyncLazy::new(T::default)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{
+ lazy::{Lazy, SyncLazy, SyncOnceCell},
+ panic,
+ sync::{
+ atomic::{AtomicUsize, Ordering::SeqCst},
+ mpsc::channel,
+ Mutex,
+ },
+ };
+
+ #[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: Lazy<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: Lazy<String> = Lazy::new(|| panic!("kaboom"));
+ for _ in 0..2 {
+ let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
+ assert!(res.is_err());
+ }
+ }
+
+ // miri doesn't support threads
+ #[cfg(not(miri))]
+ fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R {
+ crate::thread::spawn(f).join().unwrap()
+ }
+
+ #[cfg(not(miri))]
+ fn spawn(f: impl FnOnce() + Send + 'static) {
+ let _ = crate::thread::spawn(f);
+ }
+
+ // "stub threads" for Miri
+ #[cfg(miri)]
+ fn spawn_and_wait<R: Send + 'static>(f: impl FnOnce() -> R + Send + 'static) -> R {
+ f(())
+ }
+
+ #[cfg(miri)]
+ fn spawn(f: impl FnOnce() + Send + 'static) {
+ f(())
+ }
+
+ #[test]
+ fn sync_once_cell() {
+ static ONCE_CELL: SyncOnceCell<i32> = SyncOnceCell::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 = SyncOnceCell::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 = SyncOnceCell::new();
+ c.set(92).unwrap();
+ unsafe {
+ assert_eq!(c.get_unchecked(), &92);
+ }
+ }
+
+ #[test]
+ 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 = SyncOnceCell::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 = SyncOnceCell::<String>::new();
+ drop(x);
+ }
+
+ #[test]
+ fn clone() {
+ let s = SyncOnceCell::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: SyncOnceCell<String> = SyncOnceCell::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!(SyncOnceCell::from("value").get(), Some(&"value"));
+ assert_ne!(SyncOnceCell::from("foo").get(), Some(&"bar"));
+ }
+
+ #[test]
+ fn partialeq_impl() {
+ assert!(SyncOnceCell::from("value") == SyncOnceCell::from("value"));
+ assert!(SyncOnceCell::from("foo") != SyncOnceCell::from("bar"));
+
+ assert!(SyncOnceCell::<String>::new() == SyncOnceCell::new());
+ assert!(SyncOnceCell::<String>::new() != SyncOnceCell::from("value".to_owned()));
+ }
+
+ #[test]
+ fn into_inner() {
+ let cell: SyncOnceCell<String> = SyncOnceCell::new();
+ assert_eq!(cell.into_inner(), None);
+ let cell = SyncOnceCell::new();
+ cell.set("hello".to_string()).unwrap();
+ assert_eq!(cell.into_inner(), Some("hello".to_string()));
+ }
+
+ #[test]
+ fn sync_lazy_new() {
+ static CALLED: AtomicUsize = AtomicUsize::new(0);
+ static SYNC_LAZY: SyncLazy<i32> = SyncLazy::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: SyncLazy<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(miri, ignore)] // leaks memory
+ fn static_sync_lazy() {
+ static XS: SyncLazy<Vec<i32>> = SyncLazy::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]
+ #[cfg_attr(miri, ignore)] // leaks memory
+ fn static_sync_lazy_via_fn() {
+ fn xs() -> &'static Vec<i32> {
+ static XS: SyncOnceCell<Vec<i32>> = SyncOnceCell::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: SyncLazy<String> = SyncLazy::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::<SyncOnceCell<String>>();
+ assert_traits::<SyncLazy<String>>();
+ }
+
+ #[test]
+ fn eval_once_macro() {
+ macro_rules! eval_once {
+ (|| -> $ty:ty {
+ $($body:tt)*
+ }) => {{
+ static ONCE_CELL: SyncOnceCell<$ty> = SyncOnceCell::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(miri, ignore)] // deadlocks without real threads
+ fn sync_once_cell_does_not_leak_partially_constructed_boxes() {
+ static ONCE_CELL: SyncOnceCell<String> = SyncOnceCell::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();
+ spawn(move || {
+ loop {
+ if let Some(msg) = ONCE_CELL.get() {
+ tx.send(msg).unwrap();
+ break;
+ }
+ }
+ });
+ }
+ for _ in 0..n_writers {
+ spawn(move || {
+ let _ = ONCE_CELL.set(MSG.to_owned());
+ });
+ }
+
+ for _ in 0..n_readers {
+ let msg = rx.recv().unwrap();
+ assert_eq!(msg, MSG);
+ }
+ }
+}
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 54fc35b20f7..be8d9f7a7c9 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -287,6 +287,7 @@
#![feature(linkage)]
#![feature(llvm_asm)]
#![feature(log_syntax)]
+#![feature(maybe_uninit_extra)]
#![feature(maybe_uninit_ref)]
#![feature(maybe_uninit_slice)]
#![feature(min_specialization)]
@@ -294,6 +295,7 @@
#![feature(negative_impls)]
#![feature(never_type)]
#![feature(nll)]
+#![feature(once_cell)]
#![feature(optin_builtin_traits)]
#![feature(or_patterns)]
#![feature(panic_info_message)]
@@ -477,6 +479,9 @@ pub mod process;
pub mod sync;
pub mod time;
+#[unstable(feature = "once_cell", issue = "74465")]
+pub mod lazy;
+
#[stable(feature = "futures_api", since = "1.36.0")]
pub mod task {
//! Types and Traits for working with asynchronous tasks.
diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs
index 7dc822db3d0..64260990824 100644
--- a/src/libstd/sync/once.rs
+++ b/src/libstd/sync/once.rs
@@ -132,6 +132,7 @@ unsafe impl Send for Once {}
#[derive(Debug)]
pub struct OnceState {
poisoned: bool,
+ set_state_on_drop_to: Cell<usize>,
}
/// Initialization value for static [`Once`] values.
@@ -321,7 +322,7 @@ impl Once {
}
let mut f = Some(f);
- self.call_inner(true, &mut |p| f.take().unwrap()(&OnceState { poisoned: p }));
+ self.call_inner(true, &mut |p| f.take().unwrap()(p));
}
/// Returns `true` if some `call_once` call has completed
@@ -385,7 +386,7 @@ impl Once {
// currently no way to take an `FnOnce` and call it via virtual dispatch
// without some allocation overhead.
#[cold]
- fn call_inner(&self, ignore_poisoning: bool, init: &mut dyn FnMut(bool)) {
+ fn call_inner(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&OnceState)) {
let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire);
loop {
match state_and_queue {
@@ -413,8 +414,12 @@ impl Once {
};
// Run the initialization function, letting it know if we're
// poisoned or not.
- init(state_and_queue == POISONED);
- waiter_queue.set_state_on_drop_to = COMPLETE;
+ let init_state = OnceState {
+ poisoned: state_and_queue == POISONED,
+ set_state_on_drop_to: Cell::new(COMPLETE),
+ };
+ init(&init_state);
+ waiter_queue.set_state_on_drop_to = init_state.set_state_on_drop_to.get();
break;
}
_ => {
@@ -554,6 +559,14 @@ impl OnceState {
pub fn poisoned(&self) -> bool {
self.poisoned
}
+
+ /// Poison the associated [`Once`] without explicitly panicking.
+ ///
+ /// [`Once`]: struct.Once.html
+ // NOTE: This is currently only exposed for the `lazy` module
+ pub(crate) fn poison(&self) {
+ self.set_state_on_drop_to.set(POISONED);
+ }
}
#[cfg(all(test, not(target_os = "emscripten")))]
diff --git a/src/test/ui/print_type_sizes/niche-filling.stdout b/src/test/ui/print_type_sizes/niche-filling.stdout
index 301edc0d086..1894cd218ee 100644
--- a/src/test/ui/print_type_sizes/niche-filling.stdout
+++ b/src/test/ui/print_type_sizes/niche-filling.stdout
@@ -8,12 +8,12 @@ print-type-size variant `Some`: 12 bytes
print-type-size field `.0`: 12 bytes
print-type-size variant `None`: 0 bytes
print-type-size type: `EmbeddedDiscr`: 8 bytes, alignment: 4 bytes
+print-type-size discriminant: 1 bytes
print-type-size variant `Record`: 7 bytes
-print-type-size field `.val`: 4 bytes
-print-type-size field `.post`: 2 bytes
print-type-size field `.pre`: 1 bytes
+print-type-size field `.post`: 2 bytes
+print-type-size field `.val`: 4 bytes
print-type-size variant `None`: 0 bytes
-print-type-size end padding: 1 bytes
print-type-size type: `MyOption<Union1<std::num::NonZeroU32>>`: 8 bytes, alignment: 4 bytes
print-type-size discriminant: 4 bytes
print-type-size variant `Some`: 4 bytes
diff --git a/src/test/ui/type-sizes.rs b/src/test/ui/type-sizes.rs
index 6a3f3c98f12..73a11a5e743 100644
--- a/src/test/ui/type-sizes.rs
+++ b/src/test/ui/type-sizes.rs
@@ -5,6 +5,7 @@
#![feature(never_type)]
use std::mem::size_of;
+use std::num::NonZeroU8;
struct t {a: u8, b: i8}
struct u {a: u8, b: i8, c: u8}
@@ -102,6 +103,23 @@ enum Option2<A, B> {
None
}
+// Two layouts are considered for `CanBeNicheFilledButShouldnt`:
+// Niche-filling:
+// { u32 (4 bytes), NonZeroU8 + tag in niche (1 byte), padding (3 bytes) }
+// Tagged:
+// { tag (1 byte), NonZeroU8 (1 byte), padding (2 bytes), u32 (4 bytes) }
+// Both are the same size (due to padding),
+// but the tagged layout is better as the tag creates a niche with 254 invalid values,
+// allowing types like `Option<Option<CanBeNicheFilledButShouldnt>>` to fit into 8 bytes.
+pub enum CanBeNicheFilledButShouldnt {
+ A(NonZeroU8, u32),
+ B
+}
+pub enum AlwaysTaggedBecauseItHasNoNiche {
+ A(u8, u32),
+ B
+}
+
pub fn main() {
assert_eq!(size_of::<u8>(), 1 as usize);
assert_eq!(size_of::<u32>(), 4 as usize);
@@ -145,4 +163,11 @@ pub fn main() {
assert_eq!(size_of::<Option<Option<(&(), bool)>>>(), size_of::<(bool, &())>());
assert_eq!(size_of::<Option<Option2<bool, &()>>>(), size_of::<(bool, &())>());
assert_eq!(size_of::<Option<Option2<&(), bool>>>(), size_of::<(bool, &())>());
+
+ assert_eq!(size_of::<CanBeNicheFilledButShouldnt>(), 8);
+ assert_eq!(size_of::<Option<CanBeNicheFilledButShouldnt>>(), 8);
+ assert_eq!(size_of::<Option<Option<CanBeNicheFilledButShouldnt>>>(), 8);
+ assert_eq!(size_of::<AlwaysTaggedBecauseItHasNoNiche>(), 8);
+ assert_eq!(size_of::<Option<AlwaysTaggedBecauseItHasNoNiche>>(), 8);
+ assert_eq!(size_of::<Option<Option<AlwaysTaggedBecauseItHasNoNiche>>>(), 8);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment