Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When using ChildProcess.collectOutput() on multiple processes, it waits for longest living process in Windows #22709

Open
capthehacker99 opened this issue Feb 1, 2025 · 0 comments
Labels
bug Observed behavior contradicts documented or intended behavior

Comments

@capthehacker99
Copy link

Zig Version

0.14.0-dev.3012+3348478fc

Steps to Reproduce and Observed Behavior

How to reproduce

In a Windows 11 machine run

const std = @import("std");


pub fn runProcess(allocator: std.mem.Allocator, is_long: bool, collect: bool) void {
    var child = std.process.Child.init(if(is_long) @as([]const []const u8, &.{ "cmd", "/c", "timeout /NOBREAK /T 8 > nul" }) else @as([]const []const u8, &.{ "cmd", "/c", "timeout /NOBREAK /T 2 > nul" }), allocator);
    child.stdout_behavior = .Pipe;
    child.stderr_behavior = .Pipe;
    child.spawn() catch return;
    const start = std.time.timestamp();
    const hid = @intFromPtr(child.id);
    std.debug.print("Process Spawned {X}\n", .{ hid });
    if(collect) {
        var child_stdout = std.ArrayList(u8).init(allocator);
        var child_stderr = std.ArrayList(u8).init(allocator);
        defer child_stdout.deinit();
        defer child_stderr.deinit();
        child.collectOutput(&child_stdout, &child_stderr, std.math.maxInt(usize)) catch return;
        std.debug.print("Process Collected {X} at {d}\n", .{ hid, std.time.timestamp()-start });
    }
    _ = child.wait() catch return;
    std.debug.print("Process Died {X} at {d}\n", .{ hid, std.time.timestamp()-start });
}

pub fn main() !void {
    var aa = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    const allocator = aa.allocator();
    defer aa.deinit();
    var tsa: std.heap.ThreadSafeAllocator = .{
        .child_allocator = allocator
    };
    std.debug.print("With collect output:\n", .{});
    {
        var wg: std.Thread.WaitGroup = .{};
        for(0..5) |_|
            wg.spawnManager(runProcess, .{ tsa.allocator(), false, true });
        wg.spawnManager(runProcess, .{ tsa.allocator(), true, true });
        wg.wait();
    }
    std.debug.print("Without collect output:\n", .{});
    {
        var wg: std.Thread.WaitGroup = .{};
        for(0..5) |_|
            wg.spawnManager(runProcess, .{ tsa.allocator(), false, false });
        wg.spawnManager(runProcess, .{ tsa.allocator(), true, false });
        wg.wait();
    }
}

Observed behavior

When using collect output, collectOutput will block until the long process exit for every process.
Without collectOutput, the short process will die first at 2 seconds then the long process will die at 8 seconds

Output

With collect output:
Process Spawned 11C
Process Spawned 150
Process Spawned 17C
Process Spawned 164
Process Spawned 138
Process Spawned 198
Process Collected 150 at 8
Process Collected 164 at 8
Process Collected 138 at 8
Process Collected 198 at 8
Process Collected 11C at 8
Process Collected 17C at 8
Process Died 150 at 8
Process Died 164 at 8
Process Died 138 at 8
Process Died 198 at 8
Process Died 11C at 8
Process Died 17C at 8
Without collect output:
Process Spawned D0
Process Spawned 180
Process Spawned 1D4
Process Spawned 1C0
Process Spawned 150
Process Spawned 1B0
Process Died D0 at 2
Process Died 180 at 2
Process Died 150 at 2
Process Died 1D4 at 2
Process Died 1C0 at 2
Process Died 1B0 at 8

Expected Behavior

Expected behavior

When using collect output, collectOutput should block until the process that it is called on die not the longest living process.

@capthehacker99 capthehacker99 added the bug Observed behavior contradicts documented or intended behavior label Feb 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior
Projects
None yet
Development

No branches or pull requests

1 participant