Skip to content

Commit 5c17abd

Browse files
committed
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.
1 parent c52867e commit 5c17abd

File tree

1 file changed

+32
-23
lines changed

1 file changed

+32
-23
lines changed

src/app_state.rs

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{
55
io::{self, Read, Seek, StdoutLock, Write},
66
path::{Path, MAIN_SEPARATOR_STR},
77
process::{Command, Stdio},
8+
sync::mpsc,
89
thread,
910
};
1011

@@ -409,35 +410,43 @@ impl AppState {
409410
let n_exercises = self.exercises.len();
410411

411412
let (mut checked_count, mut results) = thread::scope(|s| {
412-
let handles = self
413-
.exercises
413+
let (tx, rx) = mpsc::channel();
414+
415+
self.exercises
414416
.iter()
415-
.map(|exercise| {
416-
thread::Builder::new()
417-
.spawn_scoped(s, || exercise.run_exercise(None, &self.cmd_runner))
418-
})
419-
.collect::<Vec<_>>();
417+
.enumerate()
418+
.for_each(|(index, exercise)| {
419+
let tx = tx.clone();
420+
let cmd_runner = &self.cmd_runner;
421+
let _ = thread::Builder::new().spawn_scoped(s, move || {
422+
tx.send((index, exercise.run_exercise(None, cmd_runner)))
423+
});
424+
});
425+
426+
// Drop this `tx`, since the `rx` loop will not stop while there is
427+
// at least one tx alive (i.e. we want the loop to block only while
428+
// there are `tx` clones, i.e. threads)
429+
drop(tx);
420430

421431
let mut results = vec![AllExercisesResult::Pending; n_exercises];
422432
let mut checked_count = 0;
423-
for (exercise_ind, spawn_res) in handles.into_iter().enumerate() {
433+
write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?;
434+
stdout.flush()?;
435+
while let Ok((exercise_ind, result)) = rx.recv() {
436+
results[exercise_ind] = result.map_or_else(
437+
|_| AllExercisesResult::Error,
438+
|success| {
439+
checked_count += 1;
440+
if success {
441+
AllExercisesResult::Success
442+
} else {
443+
AllExercisesResult::Failed
444+
}
445+
},
446+
);
447+
424448
write!(stdout, "\rProgress: {checked_count}/{n_exercises}")?;
425449
stdout.flush()?;
426-
427-
results[exercise_ind] = spawn_res
428-
.context("Spawn error")
429-
.and_then(|handle| handle.join().unwrap())
430-
.map_or_else(
431-
|_| AllExercisesResult::Error,
432-
|success| {
433-
checked_count += 1;
434-
if success {
435-
AllExercisesResult::Success
436-
} else {
437-
AllExercisesResult::Failed
438-
}
439-
},
440-
);
441450
}
442451

443452
Ok::<_, io::Error>((checked_count, results))

0 commit comments

Comments
 (0)