Foundf a way to freeze debugger with rayon. How to avoid it?

I develop in VsCode on Linux, and found a way to freeze debugger with a simple call to rayon. The full source is here, but let me copy the relevant parts here.

There is a vec of structs of type WaveFileData. The call to rayon applies fn detect_sample_rate() to paths from each struct. The fn calls file program on each path and parses the output.

// All about Wav file
#[derive(Debug, Default)]
pub struct WaveFileData {
    pub path: PathBuf,
    pub sample_rate: WaveSampleRate,
}

// Detected sample rate of Wav file
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
pub enum WaveSampleRate {
    F48000,
    F44100,
    #[default]
    Unknown,
}

...

// Detect sample rates
        rayon::ThreadPoolBuilder::new().num_threads(2).build_global().unwrap();
        self.wave_data.par_iter_mut().for_each(|wave| {
            wave.sample_rate = Self::detect_sample_rate(&wave.path);
        });

...

 fn detect_sample_rate(path: &Path) -> WaveSampleRate {
        let output = match std::process::Command::new("file")
            .arg(path)
            .output()
        {
            Ok(output) if output.status.success() => output,
            _ => return WaveSampleRate::Unknown,
        };

The code as it is now freezes the debugger at the first call to process.

  • Without debugger, it works great.
  • Switching num_threads to 1 makes it work.
  • Replacing process call with a dummy value makes it work.
  • Removing ThreadPoolBuilder call has no effect, I added it later.

Do you know why? Did I miss something?

rustc 1.92.0 (ded5c06cf 2025-12-08)
lldb version 21.1.6

It's likely an issue with how you are calling subprocesses. Each call to detect_sample_rate generates a new subprocess using std::process::Command in rayon threads and adding lldb on top of that likely causes a deadlock. If you serialize the subprocess creation then it should work (which is what happens when you run with one thread). Maybe using something like the hound crate to avoid calling subprocesses entirely?

Please explain what you mean by "serialize"? Separate Command::new().arg() from output() and use mutex to make sure only one command::new happens at a time?

I have already found a workaround in this specific case, I am mostly curious how to avoid the problem in the future.

Thanks!

By "serialize" I mean ensuring only one subprocess runs at a time. A mutex around the entire Command call should work.

use std::sync::Mutex;
use std::sync::LazyLock;

static SUBPROCESS_LOCK: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));

fn detect_sample_rate(path: &Path) -> WaveSampleRate {
    let _guard = SUBPROCESS_LOCK.lock().unwrap();

    let output = std::process::Command::new("file")
        .arg(path)
        .output();
    //....
}

Rayon still paralelizes the rest of your work, but subprocess spawning should become sequential.

Ahh, thanks. I did not think in this way because that is where I came from: the rest of the work is just minuscule compared to process call, and that was the reason I added rayon to begin with.

1 Like

Then using a crate like hound or something else here may be more helpful and allow you to parallelize the reads.

Wav metadata is easy to read manually, the spec is on Wikipedia.

That may be better, just use File::Open instead of calling subprocesses and you should be good to go.