From c7a1dde3fdfc63bc2b46cf70381137e104f97ff1 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Tue, 16 Sep 2025 20:15:53 +0200 Subject: [PATCH] port from lazycell --- src/cargo/util/context/mod.rs | 94 +++++++++++++++++------------------ src/cargo/util/mod.rs | 2 + src/cargo/util/once.rs | 90 +++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 48 deletions(-) create mode 100644 src/cargo/util/once.rs diff --git a/src/cargo/util/context/mod.rs b/src/cargo/util/context/mod.rs index 2152be496..b0eeb3863 100644 --- a/src/cargo/util/context/mod.rs +++ b/src/cargo/util/context/mod.rs @@ -51,6 +51,7 @@ use crate::util::cache_lock::{CacheLock, CacheLockMode, CacheLocker}; use std::borrow::Cow; +use std::cell::OnceCell; use std::cell::{RefCell, RefMut}; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::{HashMap, HashSet}; @@ -74,6 +75,7 @@ use crate::core::{CliUnstable, Shell, SourceId, Workspace, WorkspaceRootConfig, use crate::ops::RegistryCredentialConfig; use crate::sources::CRATES_IO_INDEX; use crate::sources::CRATES_IO_REGISTRY; +use crate::util::OnceExt as _; use crate::util::errors::CargoResult; use crate::util::network::http::configure_http_handle; use crate::util::network::http::http_handle; @@ -85,7 +87,6 @@ use cargo_util::paths; use cargo_util_schemas::manifest::RegistryName; use curl::easy::Easy; use itertools::Itertools; -use lazycell::LazyCell; use serde::Deserialize; use serde::de::IntoDeserializer as _; use serde_untagged::UntaggedEnumVisitor; @@ -168,9 +169,9 @@ pub struct GlobalContext { /// Information about how to write messages to the shell shell: RefCell, /// A collection of configuration options - values: LazyCell>, + values: OnceCell>, /// A collection of configuration options from the credentials file - credential_values: LazyCell>, + credential_values: OnceCell>, /// CLI config values, passed in via `configure`. cli_config: Option>, /// The current working directory of cargo @@ -178,9 +179,9 @@ pub struct GlobalContext { /// Directory where config file searching should stop (inclusive). search_stop_path: Option, /// The location of the cargo executable (path to current process) - cargo_exe: LazyCell, + cargo_exe: OnceCell, /// The location of the rustdoc executable - rustdoc: LazyCell, + rustdoc: OnceCell, /// Whether we are printing extra verbose messages extra_verbose: bool, /// `frozen` is the same as `locked`, but additionally will not access the @@ -199,9 +200,9 @@ pub struct GlobalContext { /// Cli flags of the form "-Z something" unstable_flags_cli: Option>, /// A handle on curl easy mode for http calls - easy: LazyCell>, + easy: OnceCell>, /// Cache of the `SourceId` for crates.io - crates_io_source_id: LazyCell, + crates_io_source_id: OnceCell, /// If false, don't cache `rustc --version --verbose` invocations cache_rustc_info: bool, /// Creation time of this config, used to output the total build time @@ -211,23 +212,23 @@ pub struct GlobalContext { /// Environment variable snapshot. env: Env, /// Tracks which sources have been updated to avoid multiple updates. - updated_sources: LazyCell>>, + updated_sources: OnceCell>>, /// Cache of credentials from configuration or credential providers. /// Maps from url to credential value. - credential_cache: LazyCell>>, + credential_cache: OnceCell>>, /// Cache of registry config from the `[registries]` table. - registry_config: LazyCell>>>, + registry_config: OnceCell>>>, /// Locks on the package and index caches. package_cache_lock: CacheLocker, /// Cached configuration parsed by Cargo - http_config: LazyCell, - future_incompat_config: LazyCell, - net_config: LazyCell, - build_config: LazyCell, - target_cfgs: LazyCell>, - doc_extern_map: LazyCell, + http_config: OnceCell, + future_incompat_config: OnceCell, + net_config: OnceCell, + build_config: OnceCell, + target_cfgs: OnceCell>, + doc_extern_map: OnceCell, progress_config: ProgressConfig, - env_config: LazyCell>>, + env_config: OnceCell>>, /// This should be false if: /// - this is an artifact of the rustc distribution process for "stable" or for "beta" /// - this is an `#[test]` that does not opt in with `enable_nightly_features` @@ -247,10 +248,10 @@ pub struct GlobalContext { /// `WorkspaceRootConfigs` that have been found pub ws_roots: RefCell>, /// The global cache tracker is a database used to track disk cache usage. - global_cache_tracker: LazyCell>, + global_cache_tracker: OnceCell>, /// A cache of modifications to make to [`GlobalContext::global_cache_tracker`], /// saved to disk in a batch to improve performance. - deferred_global_last_use: LazyCell>, + deferred_global_last_use: OnceCell>, } impl GlobalContext { @@ -286,11 +287,11 @@ impl GlobalContext { shell: RefCell::new(shell), cwd, search_stop_path: None, - values: LazyCell::new(), - credential_values: LazyCell::new(), + values: OnceCell::new(), + credential_values: OnceCell::new(), cli_config: None, - cargo_exe: LazyCell::new(), - rustdoc: LazyCell::new(), + cargo_exe: OnceCell::new(), + rustdoc: OnceCell::new(), extra_verbose: false, frozen: false, locked: false, @@ -304,28 +305,28 @@ impl GlobalContext { }, unstable_flags: CliUnstable::default(), unstable_flags_cli: None, - easy: LazyCell::new(), - crates_io_source_id: LazyCell::new(), + easy: OnceCell::new(), + crates_io_source_id: OnceCell::new(), cache_rustc_info, creation_time: Instant::now(), target_dir: None, env, - updated_sources: LazyCell::new(), - credential_cache: LazyCell::new(), - registry_config: LazyCell::new(), + updated_sources: OnceCell::new(), + credential_cache: OnceCell::new(), + registry_config: OnceCell::new(), package_cache_lock: CacheLocker::new(), - http_config: LazyCell::new(), - future_incompat_config: LazyCell::new(), - net_config: LazyCell::new(), - build_config: LazyCell::new(), - target_cfgs: LazyCell::new(), - doc_extern_map: LazyCell::new(), + http_config: OnceCell::new(), + future_incompat_config: OnceCell::new(), + net_config: OnceCell::new(), + build_config: OnceCell::new(), + target_cfgs: OnceCell::new(), + doc_extern_map: OnceCell::new(), progress_config: ProgressConfig::default(), - env_config: LazyCell::new(), + env_config: OnceCell::new(), nightly_features_allowed: matches!(&*features::channel(), "nightly" | "dev"), ws_roots: RefCell::new(HashMap::new()), - global_cache_tracker: LazyCell::new(), - deferred_global_last_use: LazyCell::new(), + global_cache_tracker: OnceCell::new(), + deferred_global_last_use: OnceCell::new(), } } @@ -526,21 +527,21 @@ impl GlobalContext { /// Which package sources have been updated, used to ensure it is only done once. pub fn updated_sources(&self) -> RefMut<'_, HashSet> { self.updated_sources - .borrow_with(|| RefCell::new(HashSet::new())) + .get_or_init(|| RefCell::new(HashSet::new())) .borrow_mut() } /// Cached credentials from credential providers or configuration. pub fn credential_cache(&self) -> RefMut<'_, HashMap> { self.credential_cache - .borrow_with(|| RefCell::new(HashMap::new())) + .get_or_init(|| RefCell::new(HashMap::new())) .borrow_mut() } /// Cache of already parsed registries from the `[registries]` table. pub(crate) fn registry_config(&self) -> RefMut<'_, HashMap>> { self.registry_config - .borrow_with(|| RefCell::new(HashMap::new())) + .get_or_init(|| RefCell::new(HashMap::new())) .borrow_mut() } @@ -561,18 +562,15 @@ impl GlobalContext { /// using this if possible. pub fn values_mut(&mut self) -> CargoResult<&mut HashMap> { let _ = self.values()?; - Ok(self - .values - .borrow_mut() - .expect("already loaded config values")) + Ok(self.values.get_mut().expect("already loaded config values")) } // Note: this is used by RLS, not Cargo. pub fn set_values(&self, values: HashMap) -> CargoResult<()> { - if self.values.borrow().is_some() { + if self.values.get().is_some() { bail!("config values already found") } - match self.values.fill(values) { + match self.values.set(values) { Ok(()) => Ok(()), Err(_) => bail!("could not fill values"), } @@ -741,7 +739,7 @@ impl GlobalContext { /// This does NOT look at environment variables. See `get_cv_with_env` for /// a variant that supports environment variables. fn get_cv(&self, key: &ConfigKey) -> CargoResult> { - if let Some(vals) = self.credential_values.borrow() { + if let Some(vals) = self.credential_values.get() { let val = self.get_cv_helper(key, vals)?; if val.is_some() { return Ok(val); @@ -1802,7 +1800,7 @@ impl GlobalContext { } } self.credential_values - .fill(credential_values) + .set(credential_values) .expect("was not filled at beginning of the function"); Ok(()) } diff --git a/src/cargo/util/mod.rs b/src/cargo/util/mod.rs index a61fb5b96..62f181f42 100644 --- a/src/cargo/util/mod.rs +++ b/src/cargo/util/mod.rs @@ -18,6 +18,7 @@ pub use self::into_url::IntoUrl; pub use self::into_url_with_base::IntoUrlWithBase; pub(crate) use self::io::LimitErrorReader; pub use self::lockserver::{LockServer, LockServerClient, LockServerStarted}; +pub use self::once::OnceExt; pub use self::progress::{Progress, ProgressStyle}; pub use self::queue::Queue; pub use self::rustc::Rustc; @@ -56,6 +57,7 @@ pub mod lints; mod lockserver; pub mod machine_message; pub mod network; +mod once; mod progress; mod queue; pub mod restricted_names; diff --git a/src/cargo/util/once.rs b/src/cargo/util/once.rs new file mode 100644 index 000000000..63f0647e3 --- /dev/null +++ b/src/cargo/util/once.rs @@ -0,0 +1,90 @@ +//! Extension functions for [`std::sync::OnceLock`] / [`std::cell::OnceCell`] +//! +//! This adds polyfills for functionality in `lazycell` that is not stable within `std`. + +pub trait OnceExt { + type T; + + /// This might run `f` multiple times if different threads start initializing at once. + fn try_borrow_with(&self, f: F) -> Result<&Self::T, E> + where + F: FnOnce() -> Result; + + fn replace(&mut self, new_value: Self::T) -> Option; + + fn filled(&self) -> bool; +} + +impl OnceExt for std::sync::OnceLock { + type T = T; + + fn try_borrow_with(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + if let Some(value) = self.get() { + return Ok(value); + } + + // This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if + // no other `f` is executing and the value is not initialized. However, correctly implementing that is + // hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`. + let value = f()?; + // Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()` + // returns an error. We ignore it and return the value set by the other + // thread. + let _ = self.set(value); + Ok(self.get().unwrap()) + } + + fn replace(&mut self, new_value: T) -> Option { + if let Some(value) = self.get_mut() { + Some(std::mem::replace(value, new_value)) + } else { + let result = self.set(new_value); + assert!(result.is_ok()); + None + } + } + + fn filled(&self) -> bool { + self.get().is_some() + } +} + +impl OnceExt for std::cell::OnceCell { + type T = T; + + fn try_borrow_with(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + if let Some(value) = self.get() { + return Ok(value); + } + + // This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if + // no other `f` is executing and the value is not initialized. However, correctly implementing that is + // hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`. + let value = f()?; + // Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()` + // returns an error. We ignore it and return the value set by the other + // thread. + let _ = self.set(value); + Ok(self.get().unwrap()) + } + + fn replace(&mut self, new_value: T) -> Option { + if let Some(value) = self.get_mut() { + Some(std::mem::replace(value, new_value)) + } else { + let result = self.set(new_value); + assert!(result.is_ok()); + None + } + } + + fn filled(&self) -> bool { + self.get().is_some() + } +}