sheredom / subprocess.h

🐜 single header process launching solution for C and C++
The Unlicense
1.1k stars 97 forks source link

Deadlock if synchronous subprocess fills pipe #82

Open rongcuid opened 6 months ago

rongcuid commented 6 months ago

If a subprocess outputs large amount of data, it deadlocks both parent and subprocess due to pipe blocking:

#include <stdlib.h>
#include <stdio.h>

#include "subprocess.h"

int main() {
    const char *command_line[] = {"dd", "if=/dev/zero", "bs=1k", "count=65", NULL};
    struct subprocess_s process;
    int result = subprocess_create(command_line, subprocess_option_search_user_path, &process);
    if (result) {
        fprintf(stderr, "Failed to create subprocess: %d\n", result);
        return 1;
    }
    int proc_return;
    result = subprocess_join(&process, &proc_return);
    if (result) {
        fprintf(stderr, "Failed to join subprocess\n");
        return 1;
    }
    printf("Subprocess returned %d\n", proc_return);
    result = subprocess_destroy(&process);
    if (result) {
        fprintf(stderr, "Failed to join subprocess\n");
        return 1;
    }
    return 0;
}

Notice that subprocess dd writes 65k data to stdout, which is greater than Linux's default pipe buffer size, thus blocking the child. However, since subprocess_read_stdout must be used after joining, the parent process cannot progress either, as it can neither drain the pipe nor wait for child to finish.

sheredom commented 6 months ago

I can't think of a better solution than to advise you use async. I don't really want to start spawning threads behind your back to handle this kind of thing (and don't know of another way to generally fix this!).

rongcuid commented 6 months ago

For my case, I am actually not using the outputs. So it might be good to allow ignoring stdout/stderr.

Actually, did I misunderstand something? It seems like I can read from stdout before I join. At least it works on Linux.

sheredom commented 6 months ago

I might be able to add an option to ignore stdout/stderr aye.

You should be able to read before join iirc, its just that if you don't have enough data to read it could block forever.

rongcuid commented 6 months ago

I thought it should return EOF?