Skip to content

Commit 383d767

Browse files
committed
make util::Progress threadsafe
This is a pre-requisite for obtaining progress information from `gitoxde` which is integrated [via this PR](#11448). Obtaining progress information works by having a thread poll `gitoxide`'s progress in regular intervals.
1 parent af8ec14 commit 383d767

File tree

8 files changed

+59
-44
lines changed

8 files changed

+59
-44
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ libgit2-sys = "=0.14.1"
5050
memchr = "2.1.3"
5151
opener = "0.5"
5252
os_info = "3.5.0"
53+
parking_lot = "0.12.1"
5354
pasetors = { version = "0.6.4", features = ["v3", "paserk", "std", "serde"] }
5455
pathdiff = "0.2"
5556
percent-encoding = "2.0"

src/cargo/core/compiler/job_queue.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ struct DrainState<'cfg> {
137137
documented: HashSet<PackageId>,
138138
scraped: HashSet<PackageId>,
139139
counts: HashMap<PackageId, usize>,
140-
progress: Progress<'cfg>,
140+
progress: Progress,
141141
next_id: u32,
142142
timings: Timings<'cfg>,
143143

src/cargo/core/package.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ pub struct Downloads<'a, 'cfg> {
328328
/// The next ID to use for creating a token (see `Download::token`).
329329
next: usize,
330330
/// Progress bar.
331-
progress: RefCell<Option<Progress<'cfg>>>,
331+
progress: RefCell<Option<Progress>>,
332332
/// Number of downloads that have successfully finished.
333333
downloads_finished: usize,
334334
/// Total bytes for all successfully downloaded packages.

src/cargo/core/shell.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ impl fmt::Debug for Shell {
7575
/// A `Write`able object, either with or without color support
7676
enum ShellOut {
7777
/// A plain write object without color support
78-
Write(Box<dyn Write>),
78+
Write(Box<dyn Write + Send>),
7979
/// Color-enabled stdio, with information on whether color should be used
8080
Stream {
8181
stdout: StandardStream,
@@ -114,7 +114,7 @@ impl Shell {
114114
}
115115

116116
/// Creates a shell from a plain writable object, with no color, and max verbosity.
117-
pub fn from_write(out: Box<dyn Write>) -> Shell {
117+
pub fn from_write(out: Box<dyn Write + Send>) -> Shell {
118118
Shell {
119119
output: ShellOut::Write(out),
120120
verbosity: Verbosity::Verbose,

src/cargo/ops/cargo_clean.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -315,14 +315,14 @@ trait CleaningProgressBar {
315315
fn on_clean(&mut self) -> CargoResult<()>;
316316
}
317317

318-
struct CleaningFolderBar<'cfg> {
319-
bar: Progress<'cfg>,
318+
struct CleaningFolderBar {
319+
bar: Progress,
320320
max: usize,
321321
cur: usize,
322322
}
323323

324-
impl<'cfg> CleaningFolderBar<'cfg> {
325-
fn new(cfg: &'cfg Config, max: usize) -> Self {
324+
impl CleaningFolderBar {
325+
fn new(cfg: &Config, max: usize) -> Self {
326326
Self {
327327
bar: Progress::with_style("Cleaning", ProgressStyle::Percentage, cfg),
328328
max,
@@ -335,7 +335,7 @@ impl<'cfg> CleaningFolderBar<'cfg> {
335335
}
336336
}
337337

338-
impl<'cfg> CleaningProgressBar for CleaningFolderBar<'cfg> {
338+
impl CleaningProgressBar for CleaningFolderBar {
339339
fn display_now(&mut self) -> CargoResult<()> {
340340
self.bar.tick_now(self.cur_progress(), self.max, "")
341341
}
@@ -346,16 +346,16 @@ impl<'cfg> CleaningProgressBar for CleaningFolderBar<'cfg> {
346346
}
347347
}
348348

349-
struct CleaningPackagesBar<'cfg> {
350-
bar: Progress<'cfg>,
349+
struct CleaningPackagesBar {
350+
bar: Progress,
351351
max: usize,
352352
cur: usize,
353353
num_files_folders_cleaned: usize,
354354
package_being_cleaned: String,
355355
}
356356

357-
impl<'cfg> CleaningPackagesBar<'cfg> {
358-
fn new(cfg: &'cfg Config, max: usize) -> Self {
357+
impl CleaningPackagesBar {
358+
fn new(cfg: &Config, max: usize) -> Self {
359359
Self {
360360
bar: Progress::with_style("Cleaning", ProgressStyle::Ratio, cfg),
361361
max,
@@ -384,7 +384,7 @@ impl<'cfg> CleaningPackagesBar<'cfg> {
384384
}
385385
}
386386

387-
impl<'cfg> CleaningProgressBar for CleaningPackagesBar<'cfg> {
387+
impl CleaningProgressBar for CleaningPackagesBar {
388388
fn display_now(&mut self) -> CargoResult<()> {
389389
self.bar
390390
.tick_now(self.cur_progress(), self.max, &self.format_message())

src/cargo/sources/registry/http_remote.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ pub struct Downloads<'cfg> {
105105
/// The next ID to use for creating a token (see `Download::token`).
106106
next: usize,
107107
/// Progress bar.
108-
progress: RefCell<Option<Progress<'cfg>>>,
108+
progress: RefCell<Option<Progress>>,
109109
/// Number of downloads that have successfully finished.
110110
downloads_finished: usize,
111111
/// Number of times the caller has requested blocking. This is used for

src/cargo/util/config/mod.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
//! translate from `ConfigValue` and environment variables to the caller's
5050
//! desired type.
5151
52+
use parking_lot::{Mutex, MutexGuard};
5253
use std::borrow::Cow;
5354
use std::cell::{RefCell, RefMut};
5455
use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -62,7 +63,7 @@ use std::io::{self, SeekFrom};
6263
use std::mem;
6364
use std::path::{Path, PathBuf};
6465
use std::str::FromStr;
65-
use std::sync::Once;
66+
use std::sync::{Arc, Once};
6667
use std::time::Instant;
6768

6869
use self::ConfigValue as CV;
@@ -156,7 +157,7 @@ pub struct Config {
156157
/// The location of the user's Cargo home directory. OS-dependent.
157158
home_path: Filesystem,
158159
/// Information about how to write messages to the shell
159-
shell: RefCell<Shell>,
160+
shell: Arc<Mutex<Shell>>,
160161
/// A collection of configuration options
161162
values: LazyCell<HashMap<String, ConfigValue>>,
162163
/// A collection of configuration options from the credentials file
@@ -282,7 +283,7 @@ impl Config {
282283

283284
Config {
284285
home_path: Filesystem::new(homedir),
285-
shell: RefCell::new(shell),
286+
shell: Arc::new(Mutex::new(shell)),
286287
cwd,
287288
search_stop_path: None,
288289
values: LazyCell::new(),
@@ -393,8 +394,17 @@ impl Config {
393394
}
394395

395396
/// Gets a reference to the shell, e.g., for writing error messages.
396-
pub fn shell(&self) -> RefMut<'_, Shell> {
397-
self.shell.borrow_mut()
397+
///
398+
/// # Deadlock Warning
399+
///
400+
/// A deadlock will occour if a thread calls this method while still holding the guard returned in the previous call.
401+
pub fn shell(&self) -> MutexGuard<'_, Shell> {
402+
self.shell.lock()
403+
}
404+
405+
/// Gets a shared reference to the shell, e.g., for writing error messages, for use when writing from threads.
406+
pub fn shell_detached(&self) -> Arc<Mutex<Shell>> {
407+
Arc::clone(&self.shell)
398408
}
399409

400410
/// Gets the path to the `rustdoc` executable.
@@ -1286,9 +1296,7 @@ impl Config {
12861296
// --config path_to_file
12871297
let str_path = arg_as_path
12881298
.to_str()
1289-
.ok_or_else(|| {
1290-
anyhow::format_err!("config path {:?} is not utf-8", arg_as_path)
1291-
})?
1299+
.ok_or_else(|| format_err!("config path {:?} is not utf-8", arg_as_path))?
12921300
.to_string();
12931301
self._load_file(&self.cwd().join(&str_path), &mut seen, true, WhyLoad::Cli)
12941302
.with_context(|| format!("failed to load config from `{}`", str_path))?

src/cargo/util/progress.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
use parking_lot::Mutex;
12
use std::cmp;
23
use std::env;
4+
use std::sync::Arc;
35
use std::time::{Duration, Instant};
46

57
use crate::core::shell::Verbosity;
8+
use crate::core::Shell;
69
use crate::util::config::ProgressWhen;
710
use crate::util::{CargoResult, Config};
811
use cargo_util::is_ci;
912
use unicode_width::UnicodeWidthChar;
1013

11-
pub struct Progress<'cfg> {
12-
state: Option<State<'cfg>>,
14+
pub struct Progress {
15+
state: Option<State>,
1316
}
1417

1518
pub enum ProgressStyle {
@@ -23,8 +26,8 @@ struct Throttle {
2326
last_update: Instant,
2427
}
2528

26-
struct State<'cfg> {
27-
config: &'cfg Config,
29+
struct State {
30+
shell: Arc<Mutex<Shell>>,
2831
format: Format,
2932
name: String,
3033
done: bool,
@@ -39,8 +42,8 @@ struct Format {
3942
max_print: usize,
4043
}
4144

42-
impl<'cfg> Progress<'cfg> {
43-
pub fn with_style(name: &str, style: ProgressStyle, cfg: &'cfg Config) -> Progress<'cfg> {
45+
impl Progress {
46+
pub fn with_style(name: &str, style: ProgressStyle, cfg: &Config) -> Progress {
4447
// report no progress when -q (for quiet) or TERM=dumb are set
4548
// or if running on Continuous Integration service like Travis where the
4649
// output logs get mangled.
@@ -60,15 +63,15 @@ impl<'cfg> Progress<'cfg> {
6063
Progress::new_priv(name, style, cfg)
6164
}
6265

63-
fn new_priv(name: &str, style: ProgressStyle, cfg: &'cfg Config) -> Progress<'cfg> {
66+
fn new_priv(name: &str, style: ProgressStyle, cfg: &Config) -> Progress {
6467
let progress_config = cfg.progress_config();
6568
let width = progress_config
6669
.width
6770
.or_else(|| cfg.shell().err_width().progress_max_width());
6871

6972
Progress {
7073
state: width.map(|n| State {
71-
config: cfg,
74+
shell: cfg.shell_detached(),
7275
format: Format {
7376
style,
7477
max_width: n,
@@ -93,7 +96,7 @@ impl<'cfg> Progress<'cfg> {
9396
self.state.is_some()
9497
}
9598

96-
pub fn new(name: &str, cfg: &'cfg Config) -> Progress<'cfg> {
99+
pub fn new(name: &str, cfg: &Config) -> Progress {
97100
Self::with_style(name, ProgressStyle::Percentage, cfg)
98101
}
99102

@@ -180,7 +183,7 @@ impl Throttle {
180183
}
181184
}
182185

183-
impl<'cfg> State<'cfg> {
186+
impl State {
184187
fn tick(&mut self, cur: usize, max: usize, msg: &str) -> CargoResult<()> {
185188
if self.done {
186189
return Ok(());
@@ -215,29 +218,32 @@ impl<'cfg> State<'cfg> {
215218
}
216219

217220
// Only update if the line has changed.
218-
if self.config.shell().is_cleared() || self.last_line.as_ref() != Some(&line) {
219-
let mut shell = self.config.shell();
220-
shell.set_needs_clear(false);
221-
shell.status_header(&self.name)?;
222-
write!(shell.err(), "{}\r", line)?;
223-
self.last_line = Some(line);
224-
shell.set_needs_clear(true);
221+
{
222+
let mut shell = self.shell.lock();
223+
if shell.is_cleared() || self.last_line.as_ref() != Some(&line) {
224+
shell.set_needs_clear(false);
225+
shell.status_header(&self.name)?;
226+
write!(shell.err(), "{}\r", line)?;
227+
self.last_line = Some(line);
228+
shell.set_needs_clear(true);
229+
}
225230
}
226231

227232
Ok(())
228233
}
229234

230235
fn clear(&mut self) {
231236
// No need to clear if the progress is not currently being displayed.
232-
if self.last_line.is_some() && !self.config.shell().is_cleared() {
233-
self.config.shell().err_erase_line();
237+
let mut shell = self.shell.lock();
238+
if self.last_line.is_some() && !shell.is_cleared() {
239+
shell.err_erase_line();
234240
self.last_line = None;
235241
}
236242
}
237243

238244
fn try_update_max_width(&mut self) {
239245
if self.fixed_width.is_none() {
240-
if let Some(n) = self.config.shell().err_width().progress_max_width() {
246+
if let Some(n) = self.shell.lock().err_width().progress_max_width() {
241247
self.format.max_width = n;
242248
}
243249
}
@@ -323,7 +329,7 @@ impl Format {
323329
}
324330
}
325331

326-
impl<'cfg> Drop for State<'cfg> {
332+
impl Drop for State {
327333
fn drop(&mut self) {
328334
self.clear();
329335
}

0 commit comments

Comments
 (0)