Skip to content

Commit afc320b

Browse files
committed
Fix error about too many open files during the final check
1 parent cba4a6f commit afc320b

File tree

1 file changed

+64
-37
lines changed

1 file changed

+64
-37
lines changed

src/app_state.rs

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use anyhow::{bail, Context, Error, Result};
1+
use anyhow::{bail, Context, Result};
22
use std::{
33
env,
44
fs::{self, File},
5-
io::{Read, StdoutLock, Write},
5+
io::{self, Read, StdoutLock, Write},
66
path::Path,
77
process::{Command, Stdio},
88
thread,
@@ -35,6 +35,12 @@ pub enum StateFileStatus {
3535
NotRead,
3636
}
3737

38+
enum AllExercisesCheck {
39+
Pending(usize),
40+
AllDone,
41+
CheckedUntil(usize),
42+
}
43+
3844
pub struct AppState {
3945
current_exercise_ind: usize,
4046
exercises: Vec<Exercise>,
@@ -340,59 +346,80 @@ impl AppState {
340346
}
341347
}
342348

343-
/// Mark the current exercise as done and move on to the next pending exercise if one exists.
344-
/// If all exercises are marked as done, run all of them to make sure that they are actually
345-
/// done. If an exercise which is marked as done fails, mark it as pending and continue on it.
346-
pub fn done_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> {
347-
let exercise = &mut self.exercises[self.current_exercise_ind];
348-
if !exercise.done {
349-
exercise.done = true;
350-
self.n_done += 1;
351-
}
352-
353-
if let Some(ind) = self.next_pending_exercise_ind() {
354-
self.set_current_exercise_ind(ind)?;
355-
return Ok(ExercisesProgress::NewPending);
356-
}
357-
349+
// Return the exercise index of the first pending exercise found.
350+
fn check_all_exercises(&self, stdout: &mut StdoutLock) -> Result<Option<usize>> {
358351
stdout.write_all(RERUNNING_ALL_EXERCISES_MSG)?;
359-
360352
let n_exercises = self.exercises.len();
361353

362-
let pending_exercise_ind = thread::scope(|s| {
354+
let status = thread::scope(|s| {
363355
let handles = self
364356
.exercises
365-
.iter_mut()
366-
.map(|exercise| {
367-
s.spawn(|| {
368-
let success = exercise.run_exercise(None, &self.cmd_runner)?;
369-
exercise.done = success;
370-
Ok::<_, Error>(success)
371-
})
372-
})
357+
.iter()
358+
.map(|exercise| s.spawn(|| exercise.run_exercise(None, &self.cmd_runner)))
373359
.collect::<Vec<_>>();
374360

375361
for (exercise_ind, handle) in handles.into_iter().enumerate() {
376362
write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?;
377363
stdout.flush()?;
378364

379-
let success = handle.join().unwrap()?;
365+
let Ok(success) = handle.join().unwrap() else {
366+
return Ok(AllExercisesCheck::CheckedUntil(exercise_ind));
367+
};
368+
380369
if !success {
381-
stdout.write_all(b"\n\n")?;
382-
return Ok(Some(exercise_ind));
370+
return Ok(AllExercisesCheck::Pending(exercise_ind));
383371
}
384372
}
385373

386-
Ok::<_, Error>(None)
374+
Ok::<_, io::Error>(AllExercisesCheck::AllDone)
387375
})?;
388376

389-
if let Some(pending_exercise_ind) = pending_exercise_ind {
377+
let mut exercise_ind = match status {
378+
AllExercisesCheck::Pending(exercise_ind) => return Ok(Some(exercise_ind)),
379+
AllExercisesCheck::AllDone => return Ok(None),
380+
AllExercisesCheck::CheckedUntil(ind) => ind,
381+
};
382+
383+
// We got an error while checking all exercises in parallel.
384+
// This could be because we exceeded the limit of open file descriptors.
385+
// Therefore, try to continue the check sequentially.
386+
for exercise in &self.exercises[exercise_ind..] {
387+
write!(stdout, "\rProgress: {exercise_ind}/{n_exercises}")?;
388+
stdout.flush()?;
389+
390+
let success = exercise.run_exercise(None, &self.cmd_runner)?;
391+
if !success {
392+
return Ok(Some(exercise_ind));
393+
}
394+
395+
exercise_ind += 1;
396+
}
397+
398+
Ok(None)
399+
}
400+
401+
/// Mark the current exercise as done and move on to the next pending exercise if one exists.
402+
/// If all exercises are marked as done, run all of them to make sure that they are actually
403+
/// done. If an exercise which is marked as done fails, mark it as pending and continue on it.
404+
pub fn done_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> {
405+
let exercise = &mut self.exercises[self.current_exercise_ind];
406+
if !exercise.done {
407+
exercise.done = true;
408+
self.n_done += 1;
409+
}
410+
411+
if let Some(ind) = self.next_pending_exercise_ind() {
412+
self.set_current_exercise_ind(ind)?;
413+
return Ok(ExercisesProgress::NewPending);
414+
}
415+
416+
if let Some(pending_exercise_ind) = self.check_all_exercises(stdout)? {
417+
stdout.write_all(b"\n\n")?;
418+
390419
self.current_exercise_ind = pending_exercise_ind;
391-
self.n_done = self
392-
.exercises
393-
.iter()
394-
.filter(|exercise| exercise.done)
395-
.count() as u16;
420+
self.exercises[pending_exercise_ind].done = false;
421+
// All exercises were marked as done.
422+
self.n_done -= 1;
396423
self.write()?;
397424
return Ok(ExercisesProgress::NewPending);
398425
}

0 commit comments

Comments
 (0)