From 37f9cdbd9be2504861bdafd59cbae9d1aebe24c4 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 7 Jun 2022 22:21:28 -0500 Subject: [PATCH 1/6] Don't build the compiler before building rust-demangler This saves a lot of time compiling. --- src/bootstrap/tool.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index b7abf6cc78d71..905fa431d29d1 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -692,6 +692,7 @@ macro_rules! tool_extended { stable = $stable:expr, $(in_tree = $in_tree:expr,)? $(submodule = $submodule:literal,)? + $(tool_std = $tool_std:literal,)? $extra_deps:block;)+) => { $( #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -740,7 +741,7 @@ macro_rules! tool_extended { compiler: $sel.compiler, target: $sel.target, tool: $tool_name, - mode: Mode::ToolRustc, + mode: if false $(|| $tool_std)? { Mode::ToolStd } else { Mode::ToolRustc }, path: $path, extra_features: $sel.extra_features, is_optional_tool: true, @@ -774,7 +775,10 @@ tool_extended!((self, builder), }); self.extra_features.push("clippy".to_owned()); }; - RustDemangler, rust_demangler, "src/tools/rust-demangler", "rust-demangler", stable=false, in_tree=true, {}; + // FIXME: tool_std is not quite right, we shouldn't allow nightly features. + // But `builder.cargo` doesn't know how to handle ToolBootstrap in stages other than 0, + // and this is close enough for now. + RustDemangler, rust_demangler, "src/tools/rust-demangler", "rust-demangler", stable=false, in_tree=true, tool_std=true, {}; Rustfmt, rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, in_tree=true, {}; RustAnalyzer, rust_analyzer, "src/tools/rust-analyzer/crates/rust-analyzer", "rust-analyzer", stable=false, submodule="rust-analyzer", {}; ); From 7c360dc117d554a11f7193505da0835c4b890c6f Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 16 Jun 2022 19:41:40 +0400 Subject: [PATCH 2/6] Move/rename `lazy::{OnceCell, Lazy}` to `cell::{OnceCell, LazyCell}` --- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- compiler/rustc_codegen_ssa/src/back/link.rs | 2 +- compiler/rustc_data_structures/src/sync.rs | 2 +- compiler/rustc_error_messages/src/lib.rs | 2 +- compiler/rustc_typeck/src/check/wfcheck.rs | 4 +- library/core/src/cell.rs | 8 + library/core/src/cell/lazy.rs | 104 +++++ library/core/src/cell/once.rs | 283 +++++++++++++ library/core/src/lazy.rs | 388 ------------------ library/core/tests/lazy.rs | 5 +- library/std/src/lazy/tests.rs | 7 +- 11 files changed, 407 insertions(+), 400 deletions(-) create mode 100644 library/core/src/cell/lazy.rs create mode 100644 library/core/src/cell/once.rs diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 0910e7c94ea12..71699b5cf3830 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -36,9 +36,9 @@ use rustc_target::abi::Size; use libc::c_uint; use smallvec::SmallVec; +use std::cell::OnceCell; use std::cell::RefCell; use std::iter; -use std::lazy::OnceCell; use tracing::debug; mod create_scope_map; diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index e70509f3ecc4b..39c7a408fb50f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -38,11 +38,11 @@ use regex::Regex; use tempfile::Builder as TempFileBuilder; use std::borrow::Borrow; +use std::cell::OnceCell; use std::collections::BTreeSet; use std::ffi::OsString; use std::fs::{File, OpenOptions}; use std::io::{BufWriter, Write}; -use std::lazy::OnceCell; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output, Stdio}; diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index f99ca53ab25df..ec3d3d49bcb86 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -173,7 +173,7 @@ cfg_if! { pub use std::cell::RefMut as LockGuard; pub use std::cell::RefMut as MappedLockGuard; - pub use std::lazy::OnceCell; + pub use std::cell::OnceCell; use std::cell::RefCell as InnerRwLock; use std::cell::RefCell as InnerLock; diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index fd4b2daae9c13..145fbd43ab114 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -18,7 +18,7 @@ use std::path::{Path, PathBuf}; use tracing::{instrument, trace}; #[cfg(not(parallel_compiler))] -use std::lazy::Lazy; +use std::cell::LazyCell as Lazy; #[cfg(parallel_compiler)] use std::lazy::SyncLazy as Lazy; diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 2db5f5d407193..ad85ac71fc886 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -29,9 +29,9 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc}; +use std::cell::LazyCell; use std::convert::TryInto; use std::iter; -use std::lazy::Lazy; use std::ops::ControlFlow; /// Helper type of a temporary returned by `.for_item(...)`. @@ -1728,7 +1728,7 @@ fn check_variances_for_type_defn<'tcx>( identify_constrained_generic_params(tcx, ty_predicates, None, &mut constrained_parameters); // Lazily calculated because it is only needed in case of an error. - let explicitly_bounded_params = Lazy::new(|| { + let explicitly_bounded_params = LazyCell::new(|| { let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id()); hir_generics .predicates diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 321f2feb921cf..63c83ddb6f7cf 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -199,6 +199,14 @@ use crate::mem; use crate::ops::{CoerceUnsized, Deref, DerefMut}; use crate::ptr::{self, NonNull}; +mod lazy; +mod once; + +#[unstable(feature = "once_cell", issue = "74465")] +pub use lazy::LazyCell; +#[unstable(feature = "once_cell", issue = "74465")] +pub use once::OnceCell; + /// A mutable memory location. /// /// # Examples diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs new file mode 100644 index 0000000000000..7844be5f783a9 --- /dev/null +++ b/library/core/src/cell/lazy.rs @@ -0,0 +1,104 @@ +use crate::cell::{Cell, OnceCell}; +use crate::fmt; +use crate::ops::Deref; + +/// A value which is initialized on the first access. +/// +/// # Examples +/// +/// ``` +/// #![feature(once_cell)] +/// +/// use std::cell::LazyCell; +/// +/// let lazy: LazyCell = LazyCell::new(|| { +/// println!("initializing"); +/// 92 +/// }); +/// println!("ready"); +/// println!("{}", *lazy); +/// println!("{}", *lazy); +/// +/// // Prints: +/// // ready +/// // initializing +/// // 92 +/// // 92 +/// ``` +#[unstable(feature = "once_cell", issue = "74465")] +pub struct LazyCell T> { + cell: OnceCell, + init: Cell>, +} + +impl LazyCell { + /// Creates a new lazy value with the given initializing function. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::cell::LazyCell; + /// + /// let hello = "Hello, World!".to_string(); + /// + /// let lazy = LazyCell::new(|| hello.to_uppercase()); + /// + /// assert_eq!(&*lazy, "HELLO, WORLD!"); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub const fn new(init: F) -> LazyCell { + LazyCell { cell: OnceCell::new(), init: Cell::new(Some(init)) } + } +} + +impl T> LazyCell { + /// 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::cell::LazyCell; + /// + /// let lazy = LazyCell::new(|| 92); + /// + /// assert_eq!(LazyCell::force(&lazy), &92); + /// assert_eq!(&*lazy, &92); + /// ``` + #[unstable(feature = "once_cell", issue = "74465")] + pub fn force(this: &LazyCell) -> &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> Deref for LazyCell { + type Target = T; + fn deref(&self) -> &T { + LazyCell::force(self) + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Default for LazyCell { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> LazyCell { + LazyCell::new(T::default) + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl fmt::Debug for LazyCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() + } +} diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs new file mode 100644 index 0000000000000..3c39394dd8c8e --- /dev/null +++ b/library/core/src/cell/once.rs @@ -0,0 +1,283 @@ +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::mem; + +/// 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::cell::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 { + // Invariant: written to at most once. + inner: UnsafeCell>, +} + +impl OnceCell { + /// Creates a new empty cell. + #[unstable(feature = "once_cell", issue = "74465")] + #[must_use] + pub const fn new() -> OnceCell { + 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> { + self.inner.get_mut().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::cell::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::cell::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(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + match self.get_or_try_init(|| Ok::(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::cell::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 { + /// 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(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + if let Some(val) = self.get() { + return Ok(val); + } + /// Avoid inlining the initialization closure into the common path that fetches + /// the already initialized value + #[cold] + fn outlined_call(f: F) -> Result + where + F: FnOnce() -> Result, + { + f() + } + let val = outlined_call(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::cell::OnceCell; + /// + /// let cell: OnceCell = 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 { + // 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`. + 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::cell::OnceCell; + /// + /// let mut cell: OnceCell = 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 { + mem::take(self).into_inner() + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Default for OnceCell { + fn default() -> Self { + Self::new() + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl fmt::Debug for OnceCell { + 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 Clone for OnceCell { + fn clone(&self) -> OnceCell { + 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 PartialEq for OnceCell { + fn eq(&self, other: &Self) -> bool { + self.get() == other.get() + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Eq for OnceCell {} + +#[unstable(feature = "once_cell", issue = "74465")] +impl const From for OnceCell { + /// Creates a new `OnceCell` which already contains the given `value`. + fn from(value: T) -> Self { + OnceCell { inner: UnsafeCell::new(Some(value)) } + } +} diff --git a/library/core/src/lazy.rs b/library/core/src/lazy.rs index 88826782a3d60..f8c06c3f9aedb 100644 --- a/library/core/src/lazy.rs +++ b/library/core/src/lazy.rs @@ -1,389 +1 @@ //! 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 { - // Invariant: written to at most once. - inner: UnsafeCell>, -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Default for OnceCell { - fn default() -> Self { - Self::new() - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl fmt::Debug for OnceCell { - 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 Clone for OnceCell { - fn clone(&self) -> OnceCell { - 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 PartialEq for OnceCell { - fn eq(&self, other: &Self) -> bool { - self.get() == other.get() - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Eq for OnceCell {} - -#[unstable(feature = "once_cell", issue = "74465")] -impl const From for OnceCell { - /// Creates a new `OnceCell` which already contains the given `value`. - fn from(value: T) -> Self { - OnceCell { inner: UnsafeCell::new(Some(value)) } - } -} - -impl OnceCell { - /// Creates a new empty cell. - #[unstable(feature = "once_cell", issue = "74465")] - #[must_use] - pub const fn new() -> OnceCell { - 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> { - self.inner.get_mut().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(&self, f: F) -> &T - where - F: FnOnce() -> T, - { - match self.get_or_try_init(|| Ok::(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 { - /// 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(&self, f: F) -> Result<&T, E> - where - F: FnOnce() -> Result, - { - if let Some(val) = self.get() { - return Ok(val); - } - /// Avoid inlining the initialization closure into the common path that fetches - /// the already initialized value - #[cold] - fn outlined_call(f: F) -> Result - where - F: FnOnce() -> Result, - { - f() - } - let val = outlined_call(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 = 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 { - // 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`. - 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 = 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 { - 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 = 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> { - cell: OnceCell, - init: Cell>, -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl fmt::Debug for Lazy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() - } -} - -impl Lazy { - /// 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 { - Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) } - } -} - -impl T> Lazy { - /// 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 { - 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> Deref for Lazy { - type Target = T; - fn deref(&self) -> &T { - Lazy::force(self) - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Default for Lazy { - /// Creates a new lazy value using `Default` as the initializing function. - fn default() -> Lazy { - Lazy::new(T::default) - } -} diff --git a/library/core/tests/lazy.rs b/library/core/tests/lazy.rs index 416d2b2dae41f..70fcc6d2d4bf7 100644 --- a/library/core/tests/lazy.rs +++ b/library/core/tests/lazy.rs @@ -1,6 +1,5 @@ use core::{ - cell::Cell, - lazy::{Lazy, OnceCell}, + cell::{Cell, LazyCell, OnceCell}, sync::atomic::{AtomicUsize, Ordering::SeqCst}, }; @@ -91,7 +90,7 @@ fn into_inner() { #[test] fn lazy_new() { let called = Cell::new(0); - let x = Lazy::new(|| { + let x = LazyCell::new(|| { called.set(called.get() + 1); 92 }); diff --git a/library/std/src/lazy/tests.rs b/library/std/src/lazy/tests.rs index 83466eb0904fb..66d6236c1117f 100644 --- a/library/std/src/lazy/tests.rs +++ b/library/std/src/lazy/tests.rs @@ -1,5 +1,6 @@ use crate::{ - lazy::{Lazy, SyncLazy, SyncOnceCell}, + cell::LazyCell, + lazy::{SyncLazy, SyncOnceCell}, panic, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, @@ -21,7 +22,7 @@ fn lazy_default() { } } - let lazy: Lazy> = <_>::default(); + let lazy: LazyCell> = <_>::default(); assert_eq!(CALLED.load(SeqCst), 0); @@ -36,7 +37,7 @@ fn lazy_default() { #[test] fn lazy_poisoning() { - let x: Lazy = Lazy::new(|| panic!("kaboom")); + let x: LazyCell = LazyCell::new(|| panic!("kaboom")); for _ in 0..2 { let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); assert!(res.is_err()); From c1a2db3372a4d6896744919284f3287650a38ab7 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 16 Jun 2022 19:39:39 +0400 Subject: [PATCH 3/6] Move/rename `lazy::Sync{OnceCell,Lazy}` to `sync::{Once,Lazy}Lock` --- .../rustc_codegen_cranelift/src/driver/jit.rs | 2 +- .../rustc_data_structures/src/jobserver.rs | 4 +- compiler/rustc_data_structures/src/sync.rs | 2 +- compiler/rustc_driver/src/lib.rs | 8 +- compiler/rustc_error_messages/src/lib.rs | 2 +- compiler/rustc_feature/src/builtin_attrs.rs | 6 +- compiler/rustc_hir/src/lang_items.rs | 4 +- compiler/rustc_hir/src/weak_lang_items.rs | 4 +- compiler/rustc_interface/src/passes.rs | 6 +- compiler/rustc_interface/src/util.rs | 6 +- .../src/framework/graphviz.rs | 4 +- .../rustc_mir_transform/src/coverage/debug.rs | 4 +- library/std/src/io/stdio.rs | 9 +- library/std/src/lazy.rs | 616 ------------------ library/std/src/sync/lazy_lock.rs | 121 ++++ library/std/src/sync/lazy_lock/tests.rs | 143 ++++ library/std/src/sync/mod.rs | 7 + library/std/src/sync/once_lock.rs | 496 ++++++++++++++ .../std/src/{lazy => sync/once_lock}/tests.rs | 166 +---- library/std/src/sys/windows/net.rs | 4 +- library/std/src/sys/windows/rand.rs | 4 +- src/librustdoc/clean/types.rs | 2 +- src/librustdoc/core.rs | 6 +- src/librustdoc/html/render/write_shared.rs | 2 +- src/librustdoc/lint.rs | 2 +- src/librustdoc/passes/bare_urls.rs | 4 +- .../run-make/libtest-thread-limit/test.rs | 8 +- src/tools/clippy/clippy_dev/src/bless.rs | 6 +- src/tools/clippy/clippy_utils/src/lib.rs | 4 +- src/tools/clippy/src/driver.rs | 6 +- src/tools/clippy/tests/compile-test.rs | 4 +- src/tools/clippy/tests/test_utils/mod.rs | 4 +- 32 files changed, 847 insertions(+), 819 deletions(-) create mode 100644 library/std/src/sync/lazy_lock.rs create mode 100644 library/std/src/sync/lazy_lock/tests.rs create mode 100644 library/std/src/sync/once_lock.rs rename library/std/src/{lazy => sync/once_lock}/tests.rs (50%) diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 1b01f4edbb3f3..a56a91000596c 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -13,7 +13,7 @@ use rustc_span::Symbol; use cranelift_jit::{JITBuilder, JITModule}; -// FIXME use std::lazy::SyncOnceCell once it stabilizes +// FIXME use std::sync::OnceLock once it stabilizes use once_cell::sync::OnceCell; use crate::{prelude::*, BackendConfig}; diff --git a/compiler/rustc_data_structures/src/jobserver.rs b/compiler/rustc_data_structures/src/jobserver.rs index 41605afb44e02..09baa3095a4d4 100644 --- a/compiler/rustc_data_structures/src/jobserver.rs +++ b/compiler/rustc_data_structures/src/jobserver.rs @@ -1,5 +1,5 @@ pub use jobserver_crate::Client; -use std::lazy::SyncLazy; +use std::sync::LazyLock; // We can only call `from_env` once per process @@ -18,7 +18,7 @@ use std::lazy::SyncLazy; // Also note that we stick this in a global because there could be // multiple rustc instances in this process, and the jobserver is // per-process. -static GLOBAL_CLIENT: SyncLazy = SyncLazy::new(|| unsafe { +static GLOBAL_CLIENT: LazyLock = LazyLock::new(|| unsafe { Client::from_env().unwrap_or_else(|| { let client = Client::new(32).expect("failed to create jobserver"); // Acquire a token for the main thread which we can release later diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index ec3d3d49bcb86..feb82cb093818 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -258,7 +258,7 @@ cfg_if! { pub use parking_lot::MutexGuard as LockGuard; pub use parking_lot::MappedMutexGuard as MappedLockGuard; - pub use std::lazy::SyncOnceCell as OnceCell; + pub use std::sync::OnceLock as OnceCell; pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32, AtomicU64}; diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 8cdbb1a6704df..caa92e74808c0 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -47,11 +47,11 @@ use std::env; use std::ffi::OsString; use std::fs; use std::io::{self, Read, Write}; -use std::lazy::SyncLazy; use std::panic::{self, catch_unwind}; use std::path::PathBuf; use std::process::{self, Command, Stdio}; use std::str; +use std::sync::LazyLock; use std::time::Instant; pub mod args; @@ -1141,8 +1141,8 @@ pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 { } } -static DEFAULT_HOOK: SyncLazy) + Sync + Send + 'static>> = - SyncLazy::new(|| { +static DEFAULT_HOOK: LazyLock) + Sync + Send + 'static>> = + LazyLock::new(|| { let hook = panic::take_hook(); panic::set_hook(Box::new(|info| { // Invoke the default handler, which prints the actual panic message and optionally a backtrace @@ -1237,7 +1237,7 @@ pub fn install_ice_hook() { if std::env::var("RUST_BACKTRACE").is_err() { std::env::set_var("RUST_BACKTRACE", "full"); } - SyncLazy::force(&DEFAULT_HOOK); + LazyLock::force(&DEFAULT_HOOK); } /// This allows tools to enable rust logging without having to magically match rustc's diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 145fbd43ab114..fefcaa898c1b9 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -20,7 +20,7 @@ use tracing::{instrument, trace}; #[cfg(not(parallel_compiler))] use std::cell::LazyCell as Lazy; #[cfg(parallel_compiler)] -use std::lazy::SyncLazy as Lazy; +use std::sync::LazyLock as Lazy; #[cfg(parallel_compiler)] use intl_memoizer::concurrent::IntlLangMemoizer; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index d4452a79dfbe1..6fcdfe44d8f64 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -9,7 +9,7 @@ use crate::{Features, Stability}; use rustc_data_structures::fx::FxHashMap; use rustc_span::symbol::{sym, Symbol}; -use std::lazy::SyncLazy; +use std::sync::LazyLock; type GateFn = fn(&Features) -> bool; @@ -809,8 +809,8 @@ pub fn is_builtin_only_local(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).map_or(false, |attr| attr.only_local) } -pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy> = - SyncLazy::new(|| { +pub static BUILTIN_ATTRIBUTE_MAP: LazyLock> = + LazyLock::new(|| { let mut map = FxHashMap::default(); for attr in BUILTIN_ATTRIBUTES.iter() { if map.insert(attr.name, attr).is_some() { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 9e5d781892487..b0bfac8e1f5ec 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -17,7 +17,7 @@ use rustc_macros::HashStable_Generic; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; -use std::lazy::SyncLazy; +use std::sync::LazyLock; pub enum LangItemGroup { Op, @@ -134,7 +134,7 @@ macro_rules! language_item_table { } /// A mapping from the name of the lang item to its order and the form it must be of. - pub static ITEM_REFS: SyncLazy> = SyncLazy::new(|| { + pub static ITEM_REFS: LazyLock> = LazyLock::new(|| { let mut item_refs = FxHashMap::default(); $( item_refs.insert($module::$name, (LangItem::$variant as usize, $target)); )* item_refs diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index 78748209d1a5b..dad22725511ed 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -7,12 +7,12 @@ use rustc_ast as ast; use rustc_data_structures::stable_map::StableMap; use rustc_span::symbol::{sym, Symbol}; -use std::lazy::SyncLazy; +use std::sync::LazyLock; macro_rules! weak_lang_items { ($($name:ident, $item:ident, $sym:ident;)*) => ( -pub static WEAK_ITEMS_REFS: SyncLazy> = SyncLazy::new(|| { +pub static WEAK_ITEMS_REFS: LazyLock> = LazyLock::new(|| { let mut map = StableMap::default(); $(map.insert(sym::$name, LangItem::$item);)* map diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 389e6483f30ff..21ccb586e8f5e 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -43,11 +43,11 @@ use std::any::Any; use std::cell::RefCell; use std::ffi::OsString; use std::io::{self, BufWriter, Write}; -use std::lazy::SyncLazy; use std::marker::PhantomPinned; use std::path::{Path, PathBuf}; use std::pin::Pin; use std::rc::Rc; +use std::sync::LazyLock; use std::{env, fs, iter}; pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> { @@ -759,7 +759,7 @@ pub fn prepare_outputs( Ok(outputs) } -pub static DEFAULT_QUERY_PROVIDERS: SyncLazy = SyncLazy::new(|| { +pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { let providers = &mut Providers::default(); providers.analysis = analysis; proc_macro_decls::provide(providers); @@ -784,7 +784,7 @@ pub static DEFAULT_QUERY_PROVIDERS: SyncLazy = SyncLazy::new(|| { *providers }); -pub static DEFAULT_EXTERN_QUERY_PROVIDERS: SyncLazy = SyncLazy::new(|| { +pub static DEFAULT_EXTERN_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { let mut extern_providers = ExternProviders::default(); rustc_metadata::provide_extern(&mut extern_providers); rustc_codegen_ssa::provide_extern(&mut extern_providers); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 3fa8017dc93c3..fb9258eb4a938 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -24,12 +24,12 @@ use rustc_span::source_map::FileLoader; use rustc_span::symbol::{sym, Symbol}; use std::env; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; -use std::lazy::SyncOnceCell; use std::mem; #[cfg(not(parallel_compiler))] use std::panic; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::OnceLock; use std::thread; use tracing::info; @@ -242,7 +242,7 @@ pub fn get_codegen_backend( maybe_sysroot: &Option, backend_name: Option<&str>, ) -> Box { - static LOAD: SyncOnceCell Box> = SyncOnceCell::new(); + static LOAD: OnceLock Box> = OnceLock::new(); let load = LOAD.get_or_init(|| { let default_codegen_backend = option_env!("CFG_DEFAULT_CODEGEN_BACKEND").unwrap_or("llvm"); @@ -265,7 +265,7 @@ pub fn get_codegen_backend( // loading, so we leave the code here. It is potentially useful for other tools // that want to invoke the rustc binary while linking to rustc as well. pub fn rustc_path<'a>() -> Option<&'a Path> { - static RUSTC_PATH: SyncOnceCell> = SyncOnceCell::new(); + static RUSTC_PATH: OnceLock> = OnceLock::new(); const BIN_PATH: &str = env!("RUSTC_INSTALL_BINDIR"); diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 59a2053ec7000..c94198c56a8d1 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -1,7 +1,7 @@ //! A helpful diagram for debugging dataflow problems. use std::borrow::Cow; -use std::lazy::SyncOnceCell; +use std::sync::OnceLock; use std::{io, ops, str}; use regex::Regex; @@ -590,7 +590,7 @@ where macro_rules! regex { ($re:literal $(,)?) => {{ - static RE: SyncOnceCell = SyncOnceCell::new(); + static RE: OnceLock = OnceLock::new(); RE.get_or_init(|| Regex::new($re).unwrap()) }}; } diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs index d31ac04274cb8..0f8679b0bd69d 100644 --- a/compiler/rustc_mir_transform/src/coverage/debug.rs +++ b/compiler/rustc_mir_transform/src/coverage/debug.rs @@ -123,15 +123,15 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Span; use std::iter; -use std::lazy::SyncOnceCell; use std::ops::Deref; +use std::sync::OnceLock; pub const NESTED_INDENT: &str = " "; const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; pub(super) fn debug_options<'a>() -> &'a DebugOptions { - static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); + static DEBUG_OPTIONS: OnceLock = OnceLock::new(); &DEBUG_OPTIONS.get_or_init(DebugOptions::from_env) } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index ae16015e35a1a..e3b62894d0f22 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -8,10 +8,9 @@ use crate::io::prelude::*; use crate::cell::{Cell, RefCell}; use crate::fmt; use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines}; -use crate::lazy::SyncOnceCell; use crate::pin::Pin; use crate::sync::atomic::{AtomicBool, Ordering}; -use crate::sync::{Arc, Mutex, MutexGuard}; +use crate::sync::{Arc, Mutex, MutexGuard, OnceLock}; use crate::sys::stdio; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; @@ -318,7 +317,7 @@ pub struct StdinLock<'a> { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn stdin() -> Stdin { - static INSTANCE: SyncOnceCell>> = SyncOnceCell::new(); + static INSTANCE: OnceLock>> = OnceLock::new(); Stdin { inner: INSTANCE.get_or_init(|| { Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin_raw())) @@ -552,7 +551,7 @@ pub struct StdoutLock<'a> { inner: ReentrantMutexGuard<'a, RefCell>>, } -static STDOUT: SyncOnceCell>>> = SyncOnceCell::new(); +static STDOUT: OnceLock>>> = OnceLock::new(); /// Constructs a new handle to the standard output of the current process. /// @@ -837,7 +836,7 @@ pub fn stderr() -> Stderr { // Note that unlike `stdout()` we don't use `at_exit` here to register a // destructor. Stderr is not buffered , so there's no need to run a // destructor for flushing the buffer - static INSTANCE: SyncOnceCell>> = SyncOnceCell::new(); + static INSTANCE: OnceLock>> = OnceLock::new(); Stderr { inner: Pin::static_ref(&INSTANCE).get_or_init_pin( diff --git a/library/std/src/lazy.rs b/library/std/src/lazy.rs index d7450962359df..f8c06c3f9aedb 100644 --- a/library/std/src/lazy.rs +++ b/library/std/src/lazy.rs @@ -1,617 +1 @@ //! Lazy values and one-time initialization of static data. - -#[cfg(test)] -mod tests; - -use crate::{ - cell::{Cell, UnsafeCell}, - fmt, - marker::PhantomData, - mem::MaybeUninit, - ops::{Deref, Drop}, - panic::{RefUnwindSafe, UnwindSafe}, - pin::Pin, - 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 = 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 { - once: Once, - // Whether or not the value is initialized is tracked by `state_and_queue`. - value: UnsafeCell>, - /// `PhantomData` to make sure dropck understands we're dropping T in our Drop impl. - /// - /// ```compile_fail,E0597 - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncOnceCell; - /// - /// struct A<'a>(&'a str); - /// - /// impl<'a> Drop for A<'a> { - /// fn drop(&mut self) {} - /// } - /// - /// let cell = SyncOnceCell::new(); - /// { - /// let s = String::new(); - /// let _ = cell.set(A(&s)); - /// } - /// ``` - _marker: PhantomData, -} - -// 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 Sync for SyncOnceCell {} -#[unstable(feature = "once_cell", issue = "74465")] -unsafe impl Send for SyncOnceCell {} - -#[unstable(feature = "once_cell", issue = "74465")] -impl RefUnwindSafe for SyncOnceCell {} -#[unstable(feature = "once_cell", issue = "74465")] -impl UnwindSafe for SyncOnceCell {} - -#[unstable(feature = "once_cell", issue = "74465")] -#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")] -impl const Default for SyncOnceCell { - /// Creates a new empty cell. - /// - /// # Example - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncOnceCell; - /// - /// fn main() { - /// assert_eq!(SyncOnceCell::<()>::new(), SyncOnceCell::default()); - /// } - /// ``` - fn default() -> SyncOnceCell { - SyncOnceCell::new() - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl fmt::Debug for SyncOnceCell { - 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 Clone for SyncOnceCell { - fn clone(&self) -> SyncOnceCell { - 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 From for SyncOnceCell { - /// Create a new cell with its contents set to `value`. - /// - /// # Example - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncOnceCell; - /// - /// # fn main() -> Result<(), i32> { - /// let a = SyncOnceCell::from(3); - /// let b = SyncOnceCell::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 PartialEq for SyncOnceCell { - fn eq(&self, other: &SyncOnceCell) -> bool { - self.get() == other.get() - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Eq for SyncOnceCell {} - -impl SyncOnceCell { - /// Creates a new empty cell. - #[unstable(feature = "once_cell", issue = "74465")] - #[must_use] - pub const fn new() -> SyncOnceCell { - SyncOnceCell { - 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::lazy::SyncOnceCell; - /// - /// static CELL: SyncOnceCell = 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(&self, f: F) -> &T - where - F: FnOnce() -> T, - { - match self.get_or_try_init(|| Ok::(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 { - /// 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(&self, f: F) -> Result<&T, E> - where - F: FnOnce() -> Result, - { - // 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() }) - } - - /// 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(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 `SyncOnceCell`, returning the wrapped value. Returns - /// `None` if the cell was empty. - /// - /// # Examples - /// - /// ``` - /// #![feature(once_cell)] - /// - /// use std::lazy::SyncOnceCell; - /// - /// let cell: SyncOnceCell = 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 { - self.take() - } - - /// 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 = 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 { - 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(&self, f: F) -> Result<(), E> - where - F: FnOnce() -> Result, - { - 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() - } -} - -unsafe impl<#[may_dangle] T> Drop for SyncOnceCell { - 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() }; - } - } -} - -/// 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> = 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> { - cell: SyncOnceCell, - init: Cell>, -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl fmt::Debug for SyncLazy { - 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 `&SyncLazy` so it is fine -// to not impl `Sync` for `F` -// we do create a `&mut Option` 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 Sync for SyncLazy where SyncOnceCell: Sync {} -// auto-derived `Send` impl is OK. - -#[unstable(feature = "once_cell", issue = "74465")] -impl RefUnwindSafe for SyncLazy where SyncOnceCell: RefUnwindSafe {} -#[unstable(feature = "once_cell", issue = "74465")] -impl UnwindSafe for SyncLazy where SyncOnceCell: UnwindSafe {} - -impl SyncLazy { - /// Creates a new lazy value with the given initializing - /// function. - #[unstable(feature = "once_cell", issue = "74465")] - pub const fn new(f: F) -> SyncLazy { - SyncLazy { cell: SyncOnceCell::new(), init: Cell::new(Some(f)) } - } -} - -impl T> SyncLazy { - /// 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 { - 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> Deref for SyncLazy { - type Target = T; - fn deref(&self) -> &T { - SyncLazy::force(self) - } -} - -#[unstable(feature = "once_cell", issue = "74465")] -impl Default for SyncLazy { - /// Creates a new lazy value using `Default` as the initializing function. - fn default() -> SyncLazy { - SyncLazy::new(T::default) - } -} diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs new file mode 100644 index 0000000000000..535cc1c42fcfd --- /dev/null +++ b/library/std/src/sync/lazy_lock.rs @@ -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> = 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> { + cell: OnceLock, + init: Cell>, +} + +impl LazyLock { + /// Creates a new lazy value with the given initializing + /// function. + #[unstable(feature = "once_cell", issue = "74465")] + pub const fn new(f: F) -> LazyLock { + LazyLock { cell: OnceLock::new(), init: Cell::new(Some(f)) } + } +} + +impl T> LazyLock { + /// 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 { + 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> Deref for LazyLock { + type Target = T; + fn deref(&self) -> &T { + LazyLock::force(self) + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Default for LazyLock { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> LazyLock { + LazyLock::new(T::default) + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl fmt::Debug for LazyLock { + 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` so it is fine +// to not impl `Sync` for `F` +// we do create a `&mut Option` 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 Sync for LazyLock where OnceLock: Sync {} +// auto-derived `Send` impl is OK. + +#[unstable(feature = "once_cell", issue = "74465")] +impl RefUnwindSafe for LazyLock where OnceLock: RefUnwindSafe {} +#[unstable(feature = "once_cell", issue = "74465")] +impl UnwindSafe for LazyLock where OnceLock: UnwindSafe {} + +#[cfg(test)] +mod tests; diff --git a/library/std/src/sync/lazy_lock/tests.rs b/library/std/src/sync/lazy_lock/tests.rs new file mode 100644 index 0000000000000..f11b66bfca56a --- /dev/null +++ b/library/std/src/sync/lazy_lock/tests.rs @@ -0,0 +1,143 @@ +use crate::{ + cell::LazyCell, + panic, + sync::{ + atomic::{AtomicUsize, Ordering::SeqCst}, + Mutex, + }, + sync::{LazyLock, OnceLock}, + thread, +}; + +fn spawn_and_wait(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> = <_>::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 = 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 = 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> = <_>::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> = 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 { + static XS: OnceLock> = 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 = 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() {} + assert_traits::>(); +} diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 87d01daeafc4c..5fc18fda6a83b 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -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; diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs new file mode 100644 index 0000000000000..813516040cdb6 --- /dev/null +++ b/library/std/src/sync/once_lock.rs @@ -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 = 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 { + once: Once, + // Whether or not the value is initialized is tracked by `state_and_queue`. + value: UnsafeCell>, + /// `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, +} + +impl OnceLock { + /// Creates a new empty cell. + #[unstable(feature = "once_cell", issue = "74465")] + #[must_use] + pub const fn new() -> OnceLock { + 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 = 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(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + match self.get_or_try_init(|| Ok::(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 { + /// 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(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + // 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(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 = 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 { + 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 = 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 { + 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(&self, f: F) -> Result<(), E> + where + F: FnOnce() -> Result, + { + 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 Sync for OnceLock {} +#[unstable(feature = "once_cell", issue = "74465")] +unsafe impl Send for OnceLock {} + +#[unstable(feature = "once_cell", issue = "74465")] +impl RefUnwindSafe for OnceLock {} +#[unstable(feature = "once_cell", issue = "74465")] +impl UnwindSafe for OnceLock {} + +#[unstable(feature = "once_cell", issue = "74465")] +#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")] +impl const Default for OnceLock { + /// Creates a new empty cell. + /// + /// # Example + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::sync::OnceLock; + /// + /// fn main() { + /// assert_eq!(OnceLock::<()>::new(), OnceLock::default()); + /// } + /// ``` + fn default() -> OnceLock { + OnceLock::new() + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl fmt::Debug for OnceLock { + 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 Clone for OnceLock { + fn clone(&self) -> OnceLock { + 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 From for OnceLock { + /// 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 PartialEq for OnceLock { + fn eq(&self, other: &OnceLock) -> bool { + self.get() == other.get() + } +} + +#[unstable(feature = "once_cell", issue = "74465")] +impl Eq for OnceLock {} + +#[unstable(feature = "once_cell", issue = "74465")] +unsafe impl<#[may_dangle] T> Drop for OnceLock { + 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; diff --git a/library/std/src/lazy/tests.rs b/library/std/src/sync/once_lock/tests.rs similarity index 50% rename from library/std/src/lazy/tests.rs rename to library/std/src/sync/once_lock/tests.rs index 66d6236c1117f..46695225b9f5a 100644 --- a/library/std/src/lazy/tests.rs +++ b/library/std/src/sync/once_lock/tests.rs @@ -1,49 +1,13 @@ use crate::{ - cell::LazyCell, - lazy::{SyncLazy, SyncOnceCell}, panic, + sync::OnceLock, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, mpsc::channel, - Mutex, }, thread, }; -#[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> = <_>::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 = LazyCell::new(|| panic!("kaboom")); - for _ in 0..2 { - let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len())); - assert!(res.is_err()); - } -} - fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> R { thread::spawn(f).join().unwrap() } @@ -51,7 +15,7 @@ fn spawn_and_wait(f: impl FnOnce() -> R + Send + 'static) -> #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn sync_once_cell() { - static ONCE_CELL: SyncOnceCell = SyncOnceCell::new(); + static ONCE_CELL: OnceLock = OnceLock::new(); assert!(ONCE_CELL.get().is_none()); @@ -66,7 +30,7 @@ fn sync_once_cell() { #[test] fn sync_once_cell_get_mut() { - let mut c = SyncOnceCell::new(); + let mut c = OnceLock::new(); assert!(c.get_mut().is_none()); c.set(90).unwrap(); *c.get_mut().unwrap() += 2; @@ -75,7 +39,7 @@ fn sync_once_cell_get_mut() { #[test] fn sync_once_cell_get_unchecked() { - let c = SyncOnceCell::new(); + let c = OnceLock::new(); c.set(92).unwrap(); unsafe { assert_eq!(c.get_unchecked(), &92); @@ -93,7 +57,7 @@ fn sync_once_cell_drop() { } } - let x = SyncOnceCell::new(); + let x = OnceLock::new(); spawn_and_wait(move || { x.get_or_init(|| Dropper); assert_eq!(DROP_CNT.load(SeqCst), 0); @@ -105,13 +69,13 @@ fn sync_once_cell_drop() { #[test] fn sync_once_cell_drop_empty() { - let x = SyncOnceCell::::new(); + let x = OnceLock::::new(); drop(x); } #[test] fn clone() { - let s = SyncOnceCell::new(); + let s = OnceLock::new(); let c = s.clone(); assert!(c.get().is_none()); @@ -122,7 +86,7 @@ fn clone() { #[test] fn get_or_try_init() { - let cell: SyncOnceCell = SyncOnceCell::new(); + let cell: OnceLock = OnceLock::new(); assert!(cell.get().is_none()); let res = panic::catch_unwind(|| cell.get_or_try_init(|| -> Result<_, ()> { panic!() })); @@ -138,122 +102,32 @@ fn get_or_try_init() { #[test] fn from_impl() { - assert_eq!(SyncOnceCell::from("value").get(), Some(&"value")); - assert_ne!(SyncOnceCell::from("foo").get(), Some(&"bar")); + assert_eq!(OnceLock::from("value").get(), Some(&"value")); + assert_ne!(OnceLock::from("foo").get(), Some(&"bar")); } #[test] fn partialeq_impl() { - assert!(SyncOnceCell::from("value") == SyncOnceCell::from("value")); - assert!(SyncOnceCell::from("foo") != SyncOnceCell::from("bar")); + assert!(OnceLock::from("value") == OnceLock::from("value")); + assert!(OnceLock::from("foo") != OnceLock::from("bar")); - assert!(SyncOnceCell::::new() == SyncOnceCell::new()); - assert!(SyncOnceCell::::new() != SyncOnceCell::from("value".to_owned())); + assert!(OnceLock::::new() == OnceLock::new()); + assert!(OnceLock::::new() != OnceLock::from("value".to_owned())); } #[test] fn into_inner() { - let cell: SyncOnceCell = SyncOnceCell::new(); + let cell: OnceLock = OnceLock::new(); assert_eq!(cell.into_inner(), None); - let cell = SyncOnceCell::new(); + let cell = OnceLock::new(); cell.set("hello".to_string()).unwrap(); assert_eq!(cell.into_inner(), Some("hello".to_string())); } -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn sync_lazy_new() { - static CALLED: AtomicUsize = AtomicUsize::new(0); - static SYNC_LAZY: SyncLazy = 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> = <_>::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: SyncLazy> = 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] -fn static_sync_lazy_via_fn() { - fn xs() -> &'static Vec { - static XS: SyncOnceCell> = 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 = 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() {} - assert_traits::>(); - assert_traits::>(); + assert_traits::>(); } #[test] @@ -262,7 +136,7 @@ fn eval_once_macro() { (|| -> $ty:ty { $($body:tt)* }) => {{ - static ONCE_CELL: SyncOnceCell<$ty> = SyncOnceCell::new(); + static ONCE_CELL: OnceLock<$ty> = OnceLock::new(); fn init() -> $ty { $($body)* } @@ -286,7 +160,7 @@ fn eval_once_macro() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn sync_once_cell_does_not_leak_partially_constructed_boxes() { - static ONCE_CELL: SyncOnceCell = SyncOnceCell::new(); + static ONCE_CELL: OnceLock = OnceLock::new(); let n_readers = 10; let n_writers = 3; @@ -321,7 +195,7 @@ fn sync_once_cell_does_not_leak_partially_constructed_boxes() { #[test] fn dropck() { - let cell = SyncOnceCell::new(); + let cell = OnceLock::new(); { let s = String::new(); cell.set(&s).unwrap(); diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 5de1231378488..d1e72cd54437c 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -2,13 +2,13 @@ use crate::cmp; use crate::io::{self, IoSlice, IoSliceMut, Read}; -use crate::lazy::SyncOnceCell; use crate::mem; use crate::net::{Shutdown, SocketAddr}; use crate::os::windows::io::{ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, }; use crate::ptr; +use crate::sync::OnceLock; use crate::sys; use crate::sys::c; use crate::sys_common::net; @@ -29,7 +29,7 @@ pub mod netc { pub struct Socket(OwnedSocket); -static WSA_CLEANUP: SyncOnceCell i32> = SyncOnceCell::new(); +static WSA_CLEANUP: OnceLock i32> = OnceLock::new(); /// Checks whether the Windows socket interface has been started already, and /// if not, starts it. diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs index 22e024d8552ec..57248e3651ba2 100644 --- a/library/std/src/sys/windows/rand.rs +++ b/library/std/src/sys/windows/rand.rs @@ -1,6 +1,6 @@ use crate::io; -use crate::lazy; use crate::mem; +use crate::sync; use crate::sys::c; /// The kinds of HashMap RNG that may be available @@ -28,7 +28,7 @@ fn get_hashmap_rng() -> HashMapRng { // Assume that if the preferred RNG is broken the first time we use it, it likely means // that: the DLL has failed to load, there is no point to calling it over-and-over again, // and we should cache the result - static VALUE: lazy::SyncOnceCell = lazy::SyncOnceCell::new(); + static VALUE: sync::OnceLock = sync::OnceLock::new(); *VALUE.get_or_init(choose_hashmap_rng) } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 4605793d0df94..6a6c78a26dcf7 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1,10 +1,10 @@ use std::cell::RefCell; use std::default::Default; use std::hash::Hash; -use std::lazy::SyncOnceCell as OnceCell; use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; +use std::sync::OnceLock as OnceCell; use std::{cmp, fmt, iter}; use arrayvec::ArrayVec; diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 53281bfde2e4f..51b245e36ba3b 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -21,9 +21,9 @@ use rustc_span::symbol::sym; use rustc_span::{source_map, Span, Symbol}; use std::cell::RefCell; -use std::lazy::SyncLazy; use std::mem; use std::rc::Rc; +use std::sync::LazyLock; use crate::clean::inline::build_external_trait; use crate::clean::{self, ItemId, TraitWithExtraInfo}; @@ -293,8 +293,8 @@ pub(crate) fn create_config( providers.typeck_item_bodies = |_, _| {}; // hack so that `used_trait_imports` won't try to call typeck providers.used_trait_imports = |_, _| { - static EMPTY_SET: SyncLazy> = - SyncLazy::new(FxHashSet::default); + static EMPTY_SET: LazyLock> = + LazyLock::new(FxHashSet::default); &EMPTY_SET }; // In case typeck does end up being called, don't ICE in case there were name resolution errors diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 8f08ff2ece3f6..9bddee199c7bf 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -3,9 +3,9 @@ use std::fmt::Write; use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufReader}; -use std::lazy::SyncLazy as Lazy; use std::path::{Component, Path, PathBuf}; use std::rc::Rc; +use std::sync::LazyLock as Lazy; use itertools::Itertools; use rustc_data_structures::flock; diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 08a1a868521f4..240aec52cff02 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -3,7 +3,7 @@ use rustc_lint::LintStore; use rustc_lint_defs::{declare_tool_lint, Lint, LintId}; use rustc_session::{lint, Session}; -use std::lazy::SyncLazy as Lazy; +use std::sync::LazyLock as Lazy; /// This function is used to setup the lint initialization. By default, in rustdoc, everything /// is "allowed". Depending if we run in test mode or not, we want some of them to be at their diff --git a/src/librustdoc/passes/bare_urls.rs b/src/librustdoc/passes/bare_urls.rs index e9e810658ef85..392e26ea6ac4c 100644 --- a/src/librustdoc/passes/bare_urls.rs +++ b/src/librustdoc/passes/bare_urls.rs @@ -9,8 +9,8 @@ use core::ops::Range; use pulldown_cmark::{Event, Parser, Tag}; use regex::Regex; use rustc_errors::Applicability; -use std::lazy::SyncLazy; use std::mem; +use std::sync::LazyLock; pub(crate) const CHECK_BARE_URLS: Pass = Pass { name: "check-bare-urls", @@ -18,7 +18,7 @@ pub(crate) const CHECK_BARE_URLS: Pass = Pass { description: "detects URLs that are not hyperlinks", }; -static URL_REGEX: SyncLazy = SyncLazy::new(|| { +static URL_REGEX: LazyLock = LazyLock::new(|| { Regex::new(concat!( r"https?://", // url scheme r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains diff --git a/src/test/run-make/libtest-thread-limit/test.rs b/src/test/run-make/libtest-thread-limit/test.rs index d899411a49ea0..26bc29216cf1a 100644 --- a/src/test/run-make/libtest-thread-limit/test.rs +++ b/src/test/run-make/libtest-thread-limit/test.rs @@ -1,8 +1,12 @@ #![feature(once_cell)] -use std::{io::ErrorKind, lazy::SyncOnceCell, thread::{self, Builder, ThreadId}}; +use std::{ + io::ErrorKind, + sync::OnceLock, + thread::{self, Builder, ThreadId}, +}; -static THREAD_ID: SyncOnceCell = SyncOnceCell::new(); +static THREAD_ID: OnceLock = OnceLock::new(); #[test] fn spawn_thread_would_block() { diff --git a/src/tools/clippy/clippy_dev/src/bless.rs b/src/tools/clippy/clippy_dev/src/bless.rs index 8e5c739afe05a..f5c51b9474fcd 100644 --- a/src/tools/clippy/clippy_dev/src/bless.rs +++ b/src/tools/clippy/clippy_dev/src/bless.rs @@ -4,12 +4,12 @@ use crate::cargo_clippy_path; use std::ffi::OsStr; use std::fs; -use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; +use std::sync::LazyLock; use walkdir::{DirEntry, WalkDir}; -static CLIPPY_BUILD_TIME: SyncLazy> = - SyncLazy::new(|| cargo_clippy_path().metadata().ok()?.modified().ok()); +static CLIPPY_BUILD_TIME: LazyLock> = + LazyLock::new(|| cargo_clippy_path().metadata().ok()?.modified().ok()); /// # Panics /// diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 0cf23ca626c75..052d9756c5153 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -62,7 +62,7 @@ pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, Spanl use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; -use std::lazy::SyncOnceCell; +use std::sync::OnceLock; use std::sync::{Mutex, MutexGuard}; use if_chain::if_chain; @@ -2078,7 +2078,7 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { false } -static TEST_ITEM_NAMES_CACHE: SyncOnceCell>>> = SyncOnceCell::new(); +static TEST_ITEM_NAMES_CACHE: OnceLock>>> = OnceLock::new(); fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 7de40fe63ac23..67467f89b475f 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -21,11 +21,11 @@ use rustc_tools_util::VersionInfo; use std::borrow::Cow; use std::env; -use std::lazy::SyncLazy; use std::ops::Deref; use std::panic; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; +use std::sync::LazyLock; /// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If /// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`. @@ -152,7 +152,7 @@ You can use tool lints to allow or deny lints from your code, eg.: const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new"; -static ICE_HOOK: SyncLazy) + Sync + Send + 'static>> = SyncLazy::new(|| { +static ICE_HOOK: LazyLock) + Sync + Send + 'static>> = LazyLock::new(|| { let hook = panic::take_hook(); panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL))); hook @@ -219,7 +219,7 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option = env::args().collect(); diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 04c2eeff08b6f..061cda7e01e5b 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -12,8 +12,8 @@ use std::env::{self, remove_var, set_var, var_os}; use std::ffi::{OsStr, OsString}; use std::fs; use std::io; -use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; +use std::sync::LazyLock; use test_utils::IS_RUSTC_TEST_SUITE; mod test_utils; @@ -69,7 +69,7 @@ extern crate tokio; /// dependencies must be added to Cargo.toml at the project root. Test /// dependencies that are not *directly* used by this test module require an /// `extern crate` declaration. -static EXTERN_FLAGS: SyncLazy = SyncLazy::new(|| { +static EXTERN_FLAGS: LazyLock = LazyLock::new(|| { let current_exe_depinfo = { let mut path = env::current_exe().unwrap(); path.set_extension("d"); diff --git a/src/tools/clippy/tests/test_utils/mod.rs b/src/tools/clippy/tests/test_utils/mod.rs index 8a4de3f6def90..ea8c54e08b338 100644 --- a/src/tools/clippy/tests/test_utils/mod.rs +++ b/src/tools/clippy/tests/test_utils/mod.rs @@ -1,9 +1,9 @@ #![allow(dead_code)] // see https://github.com/rust-lang/rust/issues/46379 -use std::lazy::SyncLazy; use std::path::PathBuf; +use std::sync::LazyLock; -pub static CARGO_CLIPPY_PATH: SyncLazy = SyncLazy::new(|| { +pub static CARGO_CLIPPY_PATH: LazyLock = LazyLock::new(|| { let mut path = std::env::current_exe().unwrap(); assert!(path.pop()); // deps path.set_file_name("cargo-clippy"); From b59abdc00ddd3b6b88451a97c241c4e4b21dc776 Mon Sep 17 00:00:00 2001 From: Arlo Siemsen Date: Fri, 17 Jun 2022 10:58:51 -0700 Subject: [PATCH 4/6] Update cargo 4 commits in 4d92f07f34ba7fb7d7f207564942508f46c225d3..8d42b0e8794ce3787c9f7d6d88b02ae80ebe8d19 2022-06-10 01:11:04 +0000 to 2022-06-17 16:46:26 +0000 - Use specific terminology for sparse HTTP-based registry (rust-lang/cargo#10764) - chore: Upgrade to clap 3.2 (rust-lang/cargo#10753) - Improve testing framework for http registries (rust-lang/cargo#10738) - doc: Improve example of using the links field (rust-lang/cargo#10728) --- Cargo.lock | 48 +++++++++++++---------- src/tools/cargo | 2 +- src/tools/rustc-workspace-hack/Cargo.toml | 2 +- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index acda64172b10a..671f85ca3b6c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -332,7 +332,7 @@ dependencies = [ "cargo-test-macro", "cargo-test-support", "cargo-util", - "clap 3.1.1", + "clap 3.2.5", "crates-io", "crossbeam-utils", "curl", @@ -470,7 +470,7 @@ dependencies = [ [[package]] name = "cargo-util" -version = "0.1.3" +version = "0.1.4" dependencies = [ "anyhow", "core-foundation", @@ -615,19 +615,19 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.1" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d76c22c9b9b215eeb8d016ad3a90417bd13cb24cf8142756e6472445876cab7" +checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7" dependencies = [ "atty", "bitflags", "clap_derive", + "clap_lex", "indexmap", - "lazy_static", - "os_str_bytes", + "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.14.2", + "textwrap 0.15.0", ] [[package]] @@ -636,14 +636,14 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25" dependencies = [ - "clap 3.1.1", + "clap 3.2.5", ] [[package]] name = "clap_derive" -version = "3.1.18" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" +checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -652,6 +652,15 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_lex" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "clippy" version = "0.1.63" @@ -684,7 +693,7 @@ name = "clippy_dev" version = "0.0.1" dependencies = [ "aho-corasick", - "clap 3.1.1", + "clap 3.2.5", "indoc", "itertools", "opener", @@ -2334,7 +2343,7 @@ dependencies = [ "ammonia", "anyhow", "chrono", - "clap 3.1.1", + "clap 3.2.5", "clap_complete", "elasticlunr-rs", "env_logger 0.7.1", @@ -2572,9 +2581,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "opaque-debug" @@ -2657,9 +2666,6 @@ name = "os_str_bytes" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] [[package]] name = "output_vt100" @@ -3515,7 +3521,7 @@ version = "1.0.0" dependencies = [ "bstr", "byteorder", - "clap 3.1.1", + "clap 3.2.5", "crossbeam-utils", "libc", "libz-sys", @@ -4696,7 +4702,7 @@ dependencies = [ "anyhow", "bytecount", "cargo_metadata", - "clap 3.1.1", + "clap 3.2.5", "derive-new", "diff", "dirs", @@ -5259,9 +5265,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" diff --git a/src/tools/cargo b/src/tools/cargo index 4d92f07f34ba7..8d42b0e8794ce 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 4d92f07f34ba7fb7d7f207564942508f46c225d3 +Subproject commit 8d42b0e8794ce3787c9f7d6d88b02ae80ebe8d19 diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml index 992a2e83f63c4..40aa0e8f715af 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -73,7 +73,7 @@ features = [ [dependencies] bstr = { version = "0.2.13", features = ["default"] } byteorder = { version = "1", features = ['default', 'std'] } -clap = { version = "3.1.1", features = ["lazy_static", "derive", "clap_derive"]} +clap = { version = "3.1.1", features = ["derive", "clap_derive"]} curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true } crossbeam-utils = { version = "0.8.0", features = ["nightly"] } libc = { version = "0.2.79", features = ["align"] } From ec78ff4cd1a5b556c8eb179110230b3c618dde11 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 18 Jun 2022 16:02:44 +0200 Subject: [PATCH 5/6] Adding new eslint checks: * no-sequences * no-throw-literal --- src/librustdoc/html/static/.eslintrc.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js index fc8b5678080c1..2817a8fe14488 100644 --- a/src/librustdoc/html/static/.eslintrc.js +++ b/src/librustdoc/html/static/.eslintrc.js @@ -89,5 +89,7 @@ module.exports = { "no-multi-assign": "error", "no-return-assign": "error", "no-script-url": "error", + "no-sequences": "error", + "no-throw-literal": "error", } }; From 5fef77d30e476524b8426f4a359132ca6a432dca Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 18 Jun 2022 16:11:12 +0200 Subject: [PATCH 6/6] Remove weird JS condition --- src/librustdoc/html/static/js/search.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index c726aeeff4729..c0b274c0a3fd3 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1333,10 +1333,7 @@ function initSearch(rawSearchIndex) { if (searchWord.indexOf(elem.pathLast) > -1 || row.normalizedName.indexOf(elem.pathLast) > -1 ) { - // filter type: ... queries - if (!results_others[fullId] !== undefined) { - index = row.normalizedName.indexOf(elem.pathLast); - } + index = row.normalizedName.indexOf(elem.pathLast); } lev = levenshtein(searchWord, elem.pathLast); if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1) {