Child process stdin and stdout interaction

Hello everyone

I have a problem with reading from stdout of std.process.Child, I am not really sure what I am doing wrong here:

    var pkl_process = std.process.Child.init(&.{ "pkl", "server" }, allocator);
    pkl_process.stdin_behavior = .Pipe;
    pkl_process.stdout_behavior = .Pipe;
    pkl_process.stderr_behavior = .Pipe;
    try pkl_process.spawn();

    // Write to stdin, then close it
    if (pkl_process.stdin) |*stdin| {
        var stdin_buffer: [1024]u8 = undefined;
        var stdin_writer = stdin.writer(&stdin_buffer);
        var writer = &stdin_writer.interface;

        try writer.writeAll(hex_escaped.data.bytes);

        try writer.flush();
        stdin.close();

        pkl_process.stdin = null;
    }

    // Read from stdout
    if (pkl_process.stdout) |*stdout| {
        var stdout_buffer: [1024]u8 = undefined;
        var stdout_reader = stdout.reader(&stdout_buffer);
        var reader = &stdout_reader.interface;

        var writer_buffer: [1024]u8 = undefined;
        while (true) {
            var writer: std.Io.Writer = .fixed(&writer_buffer);
            const data_len = try reader.stream(&writer, .limited(1024));
            if (data_len == 0) break;

            std.debug.print("{s}", .{writer_buffer[0..data_len]});
        }
    }

    // Read from stderr
    if (pkl_process.stderr) |*stderr| {
        var stderr_buffer: [4096]u8 = undefined;
        while (true) {
            const bytes_read = try stderr.read(&stderr_buffer);
            if (bytes_read == 0) break;
            std.debug.print("stderr: {s}", .{stderr_buffer[0..bytes_read]});
        }
    }

    // Wait for the process to complete
    _ = try pkl_process.wait();

Basically I can send the data through but can’t get back the response using: const data_len = try reader.stream(&writer, .limited(1024)); If I use reader.streamRemaining(&writer); it works but that depends on the size of the buffer. So with outputs size bigger than 1024 bytes it won’t read correctly.

Also I would like to be able to preserver stdin and stdout handles for later use as well. What would be the proper way to set that up? I’ll probably have to create a dedicated struct for communicating with my process but not really sure what to do at the moment.

Not sure about your specific setup on the child process. In the past when I dealt with reading from and writing to stdout and stdin of child process, I had to use threads.

Most child programs are single thread process, reading from its stdin and writing to its stdout in turns. If the parent process doesn’t write enough to the child’s stdin to its expectation, the child won’t finish reading and proceed to write to its stdout. This is the case if the child is looking for some EOL or EOF while reading. The parent waiting on reading the child’s stdout would stuck.

Here’re some examples. The first one is a simple case for reporting a bug on 0.15.1 but it illustrates reading from the child stdout in a thread. The second one is a more complicate case illustrating reading and writing to child stdin/stdout using two threads.

1 Like

Thank you for the examples. It really helps.