mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-25 00:00:05 +03:00
Use a channel to update the check_all progress
The previous code was checking the threads in the order they were created. So the progress update would be blocked on an earlier thread even if later thread were already done. Add to that that multiple instances of `cargo build` cannot run in parallel, they will be serialized instead. So if the exercises needs to be recompiled, depending on the order those `cargo build` are run, the first update can be a long time coming. So instead of relying on the thread terminating, use a channel to get notified when an exercise check is done, regardless of the order they finish in.
This commit is contained in:
parent
c52867eb8b
commit
5c17abd1bf
|
@ -5,6 +5,7 @@ use std::{
|
||||||
io::{self, Read, Seek, StdoutLock, Write},
|
io::{self, Read, Seek, StdoutLock, Write},
|
||||||
path::{Path, MAIN_SEPARATOR_STR},
|
path::{Path, MAIN_SEPARATOR_STR},
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
|
sync::mpsc,
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -409,35 +410,43 @@ impl AppState {
|
||||||
let n_exercises = self.exercises.len();
|
let n_exercises = self.exercises.len();
|
||||||
|
|
||||||
let (mut checked_count, mut results) = thread::scope(|s| {
|
let (mut checked_count, mut results) = thread::scope(|s| {
|
||||||
let handles = self
|
let (tx, rx) = mpsc::channel();
|
||||||
.exercises
|
|
||||||
|
self.exercises
|
||||||
.iter()
|
.iter()
|
||||||
.map(|exercise| {
|
.enumerate()
|
||||||
thread::Builder::new()
|
.for_each(|(index, exercise)| {
|
||||||
.spawn_scoped(s, || exercise.run_exercise(None, &self.cmd_runner))
|
let tx = tx.clone();
|
||||||
})
|
let cmd_runner = &self.cmd_runner;
|
||||||
.collect::<Vec<_>>();
|
let _ = thread::Builder::new().spawn_scoped(s, move || {
|
||||||
|
tx.send((index, exercise.run_exercise(None, cmd_runner)))
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Drop this `tx`, since the `rx` loop will not stop while there is
|
||||||
|
// at least one tx alive (i.e. we want the loop to block only while
|
||||||
|
// there are `tx` clones, i.e. threads)
|
||||||
|
drop(tx);
|
||||||
|
|
||||||
let mut results = vec![AllExercisesResult::Pending; n_exercises];
|
let mut results = vec![AllExercisesResult::Pending; n_exercises];
|
||||||
let mut checked_count = 0;
|
let mut checked_count = 0;
|
||||||
for (exercise_ind, spawn_res) in handles.into_iter().enumerate() {
|
write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?;
|
||||||
|
stdout.flush()?;
|
||||||
|
while let Ok((exercise_ind, result)) = rx.recv() {
|
||||||
|
results[exercise_ind] = result.map_or_else(
|
||||||
|
|_| AllExercisesResult::Error,
|
||||||
|
|success| {
|
||||||
|
checked_count += 1;
|
||||||
|
if success {
|
||||||
|
AllExercisesResult::Success
|
||||||
|
} else {
|
||||||
|
AllExercisesResult::Failed
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?;
|
write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?;
|
||||||
stdout.flush()?;
|
stdout.flush()?;
|
||||||
|
|
||||||
results[exercise_ind] = spawn_res
|
|
||||||
.context("Spawn error")
|
|
||||||
.and_then(|handle| handle.join().unwrap())
|
|
||||||
.map_or_else(
|
|
||||||
|_| AllExercisesResult::Error,
|
|
||||||
|success| {
|
|
||||||
checked_count += 1;
|
|
||||||
if success {
|
|
||||||
AllExercisesResult::Success
|
|
||||||
} else {
|
|
||||||
AllExercisesResult::Failed
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok::<_, io::Error>((checked_count, results))
|
Ok::<_, io::Error>((checked_count, results))
|
||||||
|
|
Loading…
Reference in a new issue