Skip to content

Commit 446e00c

Browse files
authored
Merge pull request #220 from solidiquis/race-to-print-bug
Fixed race to print
2 parents 94e65de + 9fd3eff commit 446e00c

File tree

5 files changed

+70
-68
lines changed

5 files changed

+70
-68
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ $ rustup toolchain install nightly-2023-06-11
285285
Thereafter:
286286

287287
```
288-
$ cargo +nightly-2023-03-05 install erdtree
288+
$ cargo +nightly-2023-06-11 install erdtree
289289
```
290290

291291
### Homebrew-core

src/context/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,10 @@ impl Context {
257257
/// Initializes [Context], optionally reading in the configuration file to override defaults.
258258
/// Arguments provided will take precedence over config.
259259
pub fn try_init() -> Result<Self, Error> {
260-
let args = Self::compute_args()?;
261-
Self::from_arg_matches(&args).map_err(Error::Config)
260+
Self::compute_args().and_then(|args| {
261+
color::no_color_env();
262+
Self::from_arg_matches(&args).map_err(Error::Config)
263+
})
262264
}
263265

264266
/// Determines whether or not it's appropriate to display color in output based on

src/main.rs

Lines changed: 18 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,13 @@
1010
clippy::style,
1111
clippy::suspicious
1212
)]
13-
#![allow(
14-
clippy::cast_possible_truncation,
15-
clippy::cast_precision_loss,
16-
clippy::cast_sign_loss,
17-
clippy::let_underscore_untyped,
18-
clippy::struct_excessive_bools,
19-
clippy::too_many_arguments
20-
)]
13+
#![allow(clippy::cast_precision_loss, clippy::struct_excessive_bools)]
2114

2215
use clap::CommandFactory;
2316
use context::{layout, Context};
24-
use progress::Message;
17+
use progress::{Indicator, IndicatorHandle, Message};
2518
use render::{Engine, Flat, FlatInverted, Inverted, Regular};
26-
use std::{error::Error, io::stdout, process::ExitCode, sync::Arc};
19+
use std::{error::Error, io::stdout, process::ExitCode};
2720
use tree::Tree;
2821

2922
/// Operations to wrangle ANSI escaped strings.
@@ -44,10 +37,7 @@ mod icons;
4437
/// Concerned with displaying a progress indicator when stdout is a tty.
4538
mod progress;
4639

47-
/// Concerned with taking an initialized [`Tree`] and its [`Node`]s and rendering the output.
48-
///
49-
/// [`Tree`]: tree::Tree
50-
/// [`Node`]: tree::node::Node
40+
/// Concerned with taking an initialized [`tree::Tree`] and its [`tree::node::Node`]s and rendering the output.
5141
mod render;
5242

5343
/// Global used throughout the program to paint the output.
@@ -80,29 +70,18 @@ fn run() -> Result<(), Box<dyn Error>> {
8070
return Ok(());
8171
}
8272

83-
context::color::no_color_env();
84-
8573
styles::init(ctx.no_color());
8674

87-
let indicator = (ctx.stdout_is_tty && !ctx.no_progress)
88-
.then(progress::Indicator::measure)
89-
.map(Arc::new);
75+
let indicator = Indicator::maybe_init(&ctx);
9076

91-
if indicator.is_some() {
92-
let indicator = indicator.clone();
93-
94-
ctrlc::set_handler(move || {
95-
let _ = progress::IndicatorHandle::terminate(indicator.clone());
96-
tty::restore_tty();
97-
})?;
98-
}
99-
100-
let (tree, ctx) = match Tree::try_init(ctx, indicator.clone()) {
101-
Ok(res) => res,
102-
Err(err) => {
103-
let _ = progress::IndicatorHandle::terminate(indicator);
104-
return Err(Box::new(err));
105-
},
77+
let (tree, ctx) = {
78+
match Tree::try_init(ctx, indicator.as_ref()) {
79+
Ok(res) => res,
80+
Err(err) => {
81+
IndicatorHandle::terminate(indicator);
82+
return Err(Box::new(err));
83+
},
84+
}
10685
};
10786

10887
macro_rules! compute_output {
@@ -122,12 +101,11 @@ fn run() -> Result<(), Box<dyn Error>> {
122101
if let Some(mut progress) = indicator {
123102
progress.mailbox().send(Message::RenderReady)?;
124103

125-
if let Some(hand) = Arc::get_mut(&mut progress) {
126-
hand.join_handle
127-
.take()
128-
.map(|h| h.join().unwrap())
129-
.transpose()?;
130-
}
104+
progress
105+
.join_handle
106+
.take()
107+
.map(|h| h.join().unwrap())
108+
.transpose()?;
131109
}
132110

133111
#[cfg(debug_assertions)]

src/progress.rs

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1+
use crate::{context::Context, tty};
12
use crossterm::{
23
cursor,
34
terminal::{self, ClearType},
45
ExecutableCommand,
56
};
67
use std::{
78
io::{self, Write},
8-
sync::{
9-
mpsc::{self, SendError, SyncSender},
10-
Arc,
11-
},
9+
sync::mpsc::{self, SendError, SyncSender},
1210
thread::{self, JoinHandle},
1311
};
1412

@@ -99,24 +97,54 @@ impl IndicatorHandle {
9997
self.mailbox.clone()
10098
}
10199

102-
/// Send a message through to the `priority_mailbox` tear down the [`Indicator`].
103-
pub fn terminate(this: Option<Arc<Self>>) -> Result<(), Error> {
104-
if let Some(mut handle) = this {
105-
handle.mailbox().send(Message::Finish)?;
100+
/// Analogous to [`Self::try_terminate`] but panics if failure.
101+
pub fn terminate(this: Option<Self>) {
102+
Self::try_terminate(this).expect("Failed to properly terminate the progress indicator");
103+
}
106104

107-
if let Some(hand) = Arc::get_mut(&mut handle) {
108-
hand.join_handle
109-
.take()
110-
.map(|h| h.join().unwrap())
111-
.transpose()?;
112-
}
105+
/// Attempts to terminate the [`Indicator`] with cleanup.
106+
pub fn try_terminate(this: Option<Self>) -> Result<(), Error> {
107+
if let Some(mut handle) = this {
108+
// This is allowed to fail silently. If user administers interrupt then the `Indicator`
109+
// will be dropped along with the receiving end of the `mailbox`.
110+
//
111+
// If user does not administer interrupt but file-system traversal fails for whatever
112+
// reason then this will proceed as normal.
113+
let _ = handle.mailbox().send(Message::Finish);
114+
115+
handle
116+
.join_handle
117+
.take()
118+
.map(|h| h.join().unwrap())
119+
.transpose()?;
113120
}
114121

115122
Ok(())
116123
}
117124
}
118125

119126
impl<'a> Indicator<'a> {
127+
/// Initializes an [`Indicator`] returning an atomic reference counter of an [`IndicatorHandle`] if
128+
/// a progress indicator is enabled via [`Context`]. Upon initialization an interrupt handler is
129+
/// also registered. Sources of panic can come from [`IndicatorHandle::terminate`] or
130+
/// [`ctrlc::set_handler`].
131+
pub fn maybe_init(ctx: &Context) -> Option<IndicatorHandle> {
132+
(ctx.stdout_is_tty && !ctx.no_progress)
133+
.then(Indicator::measure)
134+
.map(|indicator| {
135+
let mailbox = indicator.mailbox();
136+
137+
let int_handler = move || {
138+
let _ = mailbox.try_send(Message::Finish);
139+
tty::restore_tty();
140+
};
141+
142+
ctrlc::set_handler(int_handler).expect("Failed to set interrupt handler");
143+
144+
indicator
145+
})
146+
}
147+
120148
/// Initializes a worker thread that owns [`Indicator`] that awaits on [`Message`]s to traverse
121149
/// through its internal states. An [`IndicatorHandle`] is returned as a mechanism to allow the
122150
/// outside world to send messages to the worker thread and ultimately to the [`Indicator`].

src/tree/mod.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ use std::{
1616
fs,
1717
path::PathBuf,
1818
result::Result as StdResult,
19-
sync::{
20-
mpsc::{self, Sender},
21-
Arc,
22-
},
19+
sync::mpsc::{self, Sender},
2320
thread,
2421
};
2522
use visitor::{BranchVisitorBuilder, TraversalState};
@@ -30,10 +27,7 @@ pub mod count;
3027
/// Errors related to traversal, [Tree] construction, and the like.
3128
pub mod error;
3229

33-
/// Contains components of the [`Tree`] data structure that derive from [`DirEntry`].
34-
///
35-
/// [`Tree`]: Tree
36-
/// [`DirEntry`]: ignore::DirEntry
30+
/// Contains components of the [`Tree`] data structure that derive from [`ignore::DirEntry`].
3731
pub mod node;
3832

3933
/// Custom visitor that operates on each thread during filesystem traversal.
@@ -57,7 +51,7 @@ impl Tree {
5751
/// various properties necessary to render output.
5852
pub fn try_init(
5953
mut ctx: Context,
60-
indicator: Option<Arc<IndicatorHandle>>,
54+
indicator: Option<&IndicatorHandle>,
6155
) -> Result<(Self, Context)> {
6256
let mut column_properties = column::Properties::from(&ctx);
6357

@@ -105,12 +99,12 @@ impl Tree {
10599
fn traverse(
106100
ctx: &Context,
107101
column_properties: &mut column::Properties,
108-
indicator: Option<Arc<IndicatorHandle>>,
102+
indicator: Option<&IndicatorHandle>,
109103
) -> Result<(Arena<Node>, NodeId)> {
110104
let walker = WalkParallel::try_from(ctx)?;
111105
let (tx, rx) = mpsc::channel();
112106

113-
let progress_indicator_mailbox = indicator.map(|arc| arc.mailbox());
107+
let progress_indicator_mailbox = indicator.map(IndicatorHandle::mailbox);
114108

115109
thread::scope(|s| {
116110
let res = s.spawn(move || {

0 commit comments

Comments
 (0)