# Why is my Rust code blocking???

Rust is a low-level programming language known for its performance and safety. We'll look at why my first attempt to use `std::process` resulted in blocking.

### Problem

The code snippet below is an example of a Rust program that can potentially block. It uses the `Command` struct from the `std::process` module to spawn a new process that runs the `ffmpeg` command. It reads an audio file in `mp3` format from standard input (`stdin`), passes it to `ffmpeg` as input, convert the audio file to `ogg` format, and then reads the output from `ffmpeg` and writes it to standard output (`stdout`).

```rust
fn main() {
    let mut cmd = Command::new("ffmpeg")
        .arg("-i")
        .arg("-")
        .arg("-c:a")
        .arg("libopus")
        .arg("-f")
        .arg("ogg")
        .arg("-")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    let mut input_data = Vec::new();
    stdin().read_to_end(&mut input_data).unwrap();
    let mut output_data = Vec::new();

    let mut stdin = cmd.stdin.take().unwrap();
    let mut stdout = cmd.stdout.take().unwrap();

    stdin.write_all(&input_data).unwrap();
    stdout.read_to_end(&mut output_data).unwrap();

    let exit_status = cmd.wait().unwrap();

    if !exit_status.success() {
        panic!("ffmpeg failed");
    }
}
```

In this scenario, the Rust program is using the `write_all` method to write data to the `stdin` pipe, which is connected to the `ffmpeg` child process. The `ffmpeg` process is writing data to its `stdout` pipe, but nobody is consuming that data.

When the `stdout` pipe buffer fills up, the `ffmpeg` process will block while trying to write further data. This means it will also stop reading data from `stdin`, causing the `stdin` pipe buffer to fill up. When the `stdin` pipe buffer is full, the parent Rust process will also block while trying to write further data.

### Solution

To avoid this issue, the program can use another thread to write the data to `stdin`, allowing the `ffmpeg` process to read from `stdin` and the parent process to write to it concurrently.

```rust
fn main() {
    let mut cmd = Command::new("ffmpeg")
        .arg("-i")
        .arg("-")
        .arg("-c:a")
        .arg("libopus")
        .arg("-f")
        .arg("ogg")
        .arg("-")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    let mut input_data = Vec::new();
    stdin().read_to_end(&mut input_data).unwrap();
    let mut output_data = Vec::new();

    let mut stdin = cmd.stdin.take().unwrap();
    let mut stdout = cmd.stdout.take().unwrap();

    // Use a thread to write to stdin
    let writer = thread::spawn(move || {
        stdin.write_all(&input_data).unwrap();
    });

    stdout.read_to_end(&mut output_data).unwrap();

    writer.join().unwrap();

    let exit_status = cmd.wait().unwrap();

    if !exit_status.success() {
        panic!("ffmpeg failed");
    }
}
```

### Why am I doing this?

I'm working on a Telegram bot that can send voice messages.

Since the [sendVoice](https://core.telegram.org/bots/api#sendvoice) API in Telegram only accepts .OGG file encoded with OPUS, I created this simple Rust program wrapped in an [Actix Web](https://actix.rs/) server.

Source code: [https://github.com/lzm0/mpeg2opus](https://github.com/lzm0/mpeg2opus)
