Skip to content

Commit c7a1dde

Browse files
susitsmepage
authored andcommitted
port from lazycell
1 parent 742a111 commit c7a1dde

File tree

3 files changed

+138
-48
lines changed

3 files changed

+138
-48
lines changed

src/cargo/util/context/mod.rs

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
5252
use crate::util::cache_lock::{CacheLock, CacheLockMode, CacheLocker};
5353
use std::borrow::Cow;
54+
use std::cell::OnceCell;
5455
use std::cell::{RefCell, RefMut};
5556
use std::collections::hash_map::Entry::{Occupied, Vacant};
5657
use std::collections::{HashMap, HashSet};
@@ -74,6 +75,7 @@ use crate::core::{CliUnstable, Shell, SourceId, Workspace, WorkspaceRootConfig,
7475
use crate::ops::RegistryCredentialConfig;
7576
use crate::sources::CRATES_IO_INDEX;
7677
use crate::sources::CRATES_IO_REGISTRY;
78+
use crate::util::OnceExt as _;
7779
use crate::util::errors::CargoResult;
7880
use crate::util::network::http::configure_http_handle;
7981
use crate::util::network::http::http_handle;
@@ -85,7 +87,6 @@ use cargo_util::paths;
8587
use cargo_util_schemas::manifest::RegistryName;
8688
use curl::easy::Easy;
8789
use itertools::Itertools;
88-
use lazycell::LazyCell;
8990
use serde::Deserialize;
9091
use serde::de::IntoDeserializer as _;
9192
use serde_untagged::UntaggedEnumVisitor;
@@ -168,19 +169,19 @@ pub struct GlobalContext {
168169
/// Information about how to write messages to the shell
169170
shell: RefCell<Shell>,
170171
/// A collection of configuration options
171-
values: LazyCell<HashMap<String, ConfigValue>>,
172+
values: OnceCell<HashMap<String, ConfigValue>>,
172173
/// A collection of configuration options from the credentials file
173-
credential_values: LazyCell<HashMap<String, ConfigValue>>,
174+
credential_values: OnceCell<HashMap<String, ConfigValue>>,
174175
/// CLI config values, passed in via `configure`.
175176
cli_config: Option<Vec<String>>,
176177
/// The current working directory of cargo
177178
cwd: PathBuf,
178179
/// Directory where config file searching should stop (inclusive).
179180
search_stop_path: Option<PathBuf>,
180181
/// The location of the cargo executable (path to current process)
181-
cargo_exe: LazyCell<PathBuf>,
182+
cargo_exe: OnceCell<PathBuf>,
182183
/// The location of the rustdoc executable
183-
rustdoc: LazyCell<PathBuf>,
184+
rustdoc: OnceCell<PathBuf>,
184185
/// Whether we are printing extra verbose messages
185186
extra_verbose: bool,
186187
/// `frozen` is the same as `locked`, but additionally will not access the
@@ -199,9 +200,9 @@ pub struct GlobalContext {
199200
/// Cli flags of the form "-Z something"
200201
unstable_flags_cli: Option<Vec<String>>,
201202
/// A handle on curl easy mode for http calls
202-
easy: LazyCell<RefCell<Easy>>,
203+
easy: OnceCell<RefCell<Easy>>,
203204
/// Cache of the `SourceId` for crates.io
204-
crates_io_source_id: LazyCell<SourceId>,
205+
crates_io_source_id: OnceCell<SourceId>,
205206
/// If false, don't cache `rustc --version --verbose` invocations
206207
cache_rustc_info: bool,
207208
/// Creation time of this config, used to output the total build time
@@ -211,23 +212,23 @@ pub struct GlobalContext {
211212
/// Environment variable snapshot.
212213
env: Env,
213214
/// Tracks which sources have been updated to avoid multiple updates.
214-
updated_sources: LazyCell<RefCell<HashSet<SourceId>>>,
215+
updated_sources: OnceCell<RefCell<HashSet<SourceId>>>,
215216
/// Cache of credentials from configuration or credential providers.
216217
/// Maps from url to credential value.
217-
credential_cache: LazyCell<RefCell<HashMap<CanonicalUrl, CredentialCacheValue>>>,
218+
credential_cache: OnceCell<RefCell<HashMap<CanonicalUrl, CredentialCacheValue>>>,
218219
/// Cache of registry config from the `[registries]` table.
219-
registry_config: LazyCell<RefCell<HashMap<SourceId, Option<RegistryConfig>>>>,
220+
registry_config: OnceCell<RefCell<HashMap<SourceId, Option<RegistryConfig>>>>,
220221
/// Locks on the package and index caches.
221222
package_cache_lock: CacheLocker,
222223
/// Cached configuration parsed by Cargo
223-
http_config: LazyCell<CargoHttpConfig>,
224-
future_incompat_config: LazyCell<CargoFutureIncompatConfig>,
225-
net_config: LazyCell<CargoNetConfig>,
226-
build_config: LazyCell<CargoBuildConfig>,
227-
target_cfgs: LazyCell<Vec<(String, TargetCfgConfig)>>,
228-
doc_extern_map: LazyCell<RustdocExternMap>,
224+
http_config: OnceCell<CargoHttpConfig>,
225+
future_incompat_config: OnceCell<CargoFutureIncompatConfig>,
226+
net_config: OnceCell<CargoNetConfig>,
227+
build_config: OnceCell<CargoBuildConfig>,
228+
target_cfgs: OnceCell<Vec<(String, TargetCfgConfig)>>,
229+
doc_extern_map: OnceCell<RustdocExternMap>,
229230
progress_config: ProgressConfig,
230-
env_config: LazyCell<Arc<HashMap<String, OsString>>>,
231+
env_config: OnceCell<Arc<HashMap<String, OsString>>>,
231232
/// This should be false if:
232233
/// - this is an artifact of the rustc distribution process for "stable" or for "beta"
233234
/// - this is an `#[test]` that does not opt in with `enable_nightly_features`
@@ -247,10 +248,10 @@ pub struct GlobalContext {
247248
/// `WorkspaceRootConfigs` that have been found
248249
pub ws_roots: RefCell<HashMap<PathBuf, WorkspaceRootConfig>>,
249250
/// The global cache tracker is a database used to track disk cache usage.
250-
global_cache_tracker: LazyCell<RefCell<GlobalCacheTracker>>,
251+
global_cache_tracker: OnceCell<RefCell<GlobalCacheTracker>>,
251252
/// A cache of modifications to make to [`GlobalContext::global_cache_tracker`],
252253
/// saved to disk in a batch to improve performance.
253-
deferred_global_last_use: LazyCell<RefCell<DeferredGlobalLastUse>>,
254+
deferred_global_last_use: OnceCell<RefCell<DeferredGlobalLastUse>>,
254255
}
255256

256257
impl GlobalContext {
@@ -286,11 +287,11 @@ impl GlobalContext {
286287
shell: RefCell::new(shell),
287288
cwd,
288289
search_stop_path: None,
289-
values: LazyCell::new(),
290-
credential_values: LazyCell::new(),
290+
values: OnceCell::new(),
291+
credential_values: OnceCell::new(),
291292
cli_config: None,
292-
cargo_exe: LazyCell::new(),
293-
rustdoc: LazyCell::new(),
293+
cargo_exe: OnceCell::new(),
294+
rustdoc: OnceCell::new(),
294295
extra_verbose: false,
295296
frozen: false,
296297
locked: false,
@@ -304,28 +305,28 @@ impl GlobalContext {
304305
},
305306
unstable_flags: CliUnstable::default(),
306307
unstable_flags_cli: None,
307-
easy: LazyCell::new(),
308-
crates_io_source_id: LazyCell::new(),
308+
easy: OnceCell::new(),
309+
crates_io_source_id: OnceCell::new(),
309310
cache_rustc_info,
310311
creation_time: Instant::now(),
311312
target_dir: None,
312313
env,
313-
updated_sources: LazyCell::new(),
314-
credential_cache: LazyCell::new(),
315-
registry_config: LazyCell::new(),
314+
updated_sources: OnceCell::new(),
315+
credential_cache: OnceCell::new(),
316+
registry_config: OnceCell::new(),
316317
package_cache_lock: CacheLocker::new(),
317-
http_config: LazyCell::new(),
318-
future_incompat_config: LazyCell::new(),
319-
net_config: LazyCell::new(),
320-
build_config: LazyCell::new(),
321-
target_cfgs: LazyCell::new(),
322-
doc_extern_map: LazyCell::new(),
318+
http_config: OnceCell::new(),
319+
future_incompat_config: OnceCell::new(),
320+
net_config: OnceCell::new(),
321+
build_config: OnceCell::new(),
322+
target_cfgs: OnceCell::new(),
323+
doc_extern_map: OnceCell::new(),
323324
progress_config: ProgressConfig::default(),
324-
env_config: LazyCell::new(),
325+
env_config: OnceCell::new(),
325326
nightly_features_allowed: matches!(&*features::channel(), "nightly" | "dev"),
326327
ws_roots: RefCell::new(HashMap::new()),
327-
global_cache_tracker: LazyCell::new(),
328-
deferred_global_last_use: LazyCell::new(),
328+
global_cache_tracker: OnceCell::new(),
329+
deferred_global_last_use: OnceCell::new(),
329330
}
330331
}
331332

@@ -526,21 +527,21 @@ impl GlobalContext {
526527
/// Which package sources have been updated, used to ensure it is only done once.
527528
pub fn updated_sources(&self) -> RefMut<'_, HashSet<SourceId>> {
528529
self.updated_sources
529-
.borrow_with(|| RefCell::new(HashSet::new()))
530+
.get_or_init(|| RefCell::new(HashSet::new()))
530531
.borrow_mut()
531532
}
532533

533534
/// Cached credentials from credential providers or configuration.
534535
pub fn credential_cache(&self) -> RefMut<'_, HashMap<CanonicalUrl, CredentialCacheValue>> {
535536
self.credential_cache
536-
.borrow_with(|| RefCell::new(HashMap::new()))
537+
.get_or_init(|| RefCell::new(HashMap::new()))
537538
.borrow_mut()
538539
}
539540

540541
/// Cache of already parsed registries from the `[registries]` table.
541542
pub(crate) fn registry_config(&self) -> RefMut<'_, HashMap<SourceId, Option<RegistryConfig>>> {
542543
self.registry_config
543-
.borrow_with(|| RefCell::new(HashMap::new()))
544+
.get_or_init(|| RefCell::new(HashMap::new()))
544545
.borrow_mut()
545546
}
546547

@@ -561,18 +562,15 @@ impl GlobalContext {
561562
/// using this if possible.
562563
pub fn values_mut(&mut self) -> CargoResult<&mut HashMap<String, ConfigValue>> {
563564
let _ = self.values()?;
564-
Ok(self
565-
.values
566-
.borrow_mut()
567-
.expect("already loaded config values"))
565+
Ok(self.values.get_mut().expect("already loaded config values"))
568566
}
569567

570568
// Note: this is used by RLS, not Cargo.
571569
pub fn set_values(&self, values: HashMap<String, ConfigValue>) -> CargoResult<()> {
572-
if self.values.borrow().is_some() {
570+
if self.values.get().is_some() {
573571
bail!("config values already found")
574572
}
575-
match self.values.fill(values) {
573+
match self.values.set(values) {
576574
Ok(()) => Ok(()),
577575
Err(_) => bail!("could not fill values"),
578576
}
@@ -741,7 +739,7 @@ impl GlobalContext {
741739
/// This does NOT look at environment variables. See `get_cv_with_env` for
742740
/// a variant that supports environment variables.
743741
fn get_cv(&self, key: &ConfigKey) -> CargoResult<Option<ConfigValue>> {
744-
if let Some(vals) = self.credential_values.borrow() {
742+
if let Some(vals) = self.credential_values.get() {
745743
let val = self.get_cv_helper(key, vals)?;
746744
if val.is_some() {
747745
return Ok(val);
@@ -1802,7 +1800,7 @@ impl GlobalContext {
18021800
}
18031801
}
18041802
self.credential_values
1805-
.fill(credential_values)
1803+
.set(credential_values)
18061804
.expect("was not filled at beginning of the function");
18071805
Ok(())
18081806
}

src/cargo/util/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub use self::into_url::IntoUrl;
1818
pub use self::into_url_with_base::IntoUrlWithBase;
1919
pub(crate) use self::io::LimitErrorReader;
2020
pub use self::lockserver::{LockServer, LockServerClient, LockServerStarted};
21+
pub use self::once::OnceExt;
2122
pub use self::progress::{Progress, ProgressStyle};
2223
pub use self::queue::Queue;
2324
pub use self::rustc::Rustc;
@@ -56,6 +57,7 @@ pub mod lints;
5657
mod lockserver;
5758
pub mod machine_message;
5859
pub mod network;
60+
mod once;
5961
mod progress;
6062
mod queue;
6163
pub mod restricted_names;

src/cargo/util/once.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! Extension functions for [`std::sync::OnceLock`] / [`std::cell::OnceCell`]
2+
//!
3+
//! This adds polyfills for functionality in `lazycell` that is not stable within `std`.
4+
5+
pub trait OnceExt {
6+
type T;
7+
8+
/// This might run `f` multiple times if different threads start initializing at once.
9+
fn try_borrow_with<F, E>(&self, f: F) -> Result<&Self::T, E>
10+
where
11+
F: FnOnce() -> Result<Self::T, E>;
12+
13+
fn replace(&mut self, new_value: Self::T) -> Option<Self::T>;
14+
15+
fn filled(&self) -> bool;
16+
}
17+
18+
impl<T> OnceExt for std::sync::OnceLock<T> {
19+
type T = T;
20+
21+
fn try_borrow_with<F, E>(&self, f: F) -> Result<&T, E>
22+
where
23+
F: FnOnce() -> Result<T, E>,
24+
{
25+
if let Some(value) = self.get() {
26+
return Ok(value);
27+
}
28+
29+
// This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if
30+
// no other `f` is executing and the value is not initialized. However, correctly implementing that is
31+
// hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`.
32+
let value = f()?;
33+
// Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()`
34+
// returns an error. We ignore it and return the value set by the other
35+
// thread.
36+
let _ = self.set(value);
37+
Ok(self.get().unwrap())
38+
}
39+
40+
fn replace(&mut self, new_value: T) -> Option<T> {
41+
if let Some(value) = self.get_mut() {
42+
Some(std::mem::replace(value, new_value))
43+
} else {
44+
let result = self.set(new_value);
45+
assert!(result.is_ok());
46+
None
47+
}
48+
}
49+
50+
fn filled(&self) -> bool {
51+
self.get().is_some()
52+
}
53+
}
54+
55+
impl<T> OnceExt for std::cell::OnceCell<T> {
56+
type T = T;
57+
58+
fn try_borrow_with<F, E>(&self, f: F) -> Result<&T, E>
59+
where
60+
F: FnOnce() -> Result<T, E>,
61+
{
62+
if let Some(value) = self.get() {
63+
return Ok(value);
64+
}
65+
66+
// This is not how the unstable `OnceLock::get_or_try_init` works. That only starts `f` if
67+
// no other `f` is executing and the value is not initialized. However, correctly implementing that is
68+
// hard (one has properly handle panics in `f`) and not doable with the stable API of `OnceLock`.
69+
let value = f()?;
70+
// Another thread might have initialized `self` since we checked that `self.get()` returns `None`. If this is the case, `self.set()`
71+
// returns an error. We ignore it and return the value set by the other
72+
// thread.
73+
let _ = self.set(value);
74+
Ok(self.get().unwrap())
75+
}
76+
77+
fn replace(&mut self, new_value: T) -> Option<T> {
78+
if let Some(value) = self.get_mut() {
79+
Some(std::mem::replace(value, new_value))
80+
} else {
81+
let result = self.set(new_value);
82+
assert!(result.is_ok());
83+
None
84+
}
85+
}
86+
87+
fn filled(&self) -> bool {
88+
self.get().is_some()
89+
}
90+
}

0 commit comments

Comments
 (0)