Skip to content

Terminating zig build run does not terminate child processes #20853

@MadLittleMods

Description

@MadLittleMods

Zig Version

0.13.0

Steps to Reproduce and Observed Behavior

  1. Using the standard zig init boilerplate
  2. Write a little program that keeps running and doesn't exit:
    src/main.zig
    const std = @import("std");
    
    pub fn main() void {
        while (true) {
            std.log.info("Hello, World!", .{});
            std.time.sleep(1 * std.time.ns_per_s);
        }
    }
  3. zig build run
  4. Notice the three processes that have spawned (the original zig build run, the expanded zig build, and running the final binary)
    $ ps aux | grep zig
    USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    eric     3972336  0.0  0.0 434540 35328 pts/21   Sl+  01:20   0:00 zig build run
    eric     3972354  0.0  0.0 282024  2816 pts/21   Sl+  01:20   0:00 /home/eric/Documents/code/zig/random/kill-build-hello-world-test/.zig-cache/o/cdb0a375ac43b8fc9e3e09e439f80b53/build /home/eric/zig/0.13.0/files/zig /home/eric/Documents/code/zig/random/kill-build-hello-world-test /home/eric/Documents/code/zig/random/kill-build-hello-world-test/.zig-cache /home/eric/.cache/zig --seed 0x4ec1d056 -Z148e1dbdd164c915 run
    eric     3972408  0.0  0.0   1064   512 pts/21   S+   01:20   0:00 /home/eric/Documents/code/zig/random/kill-build-hello-world-test/zig-out/bin/kill-build-hello-world-test
  5. Kill the zig build run parent process (kill 3972336)
  6. Notice that the program keeps running and outputting text
  7. If you instead kill the main binary (kill 3972408), all of the processes are cleaned up

Also tested with the latest master (0.14.0-dev.655+d30d37e35)

Expected Behavior

My expected/desired behavior is that when killing the zig build run process, all of the child processes are cleaned up (no orphaned child processes).

Note: There is no automatic propagation of signals (SIGTERM or otherwise) to children in the process tree so the behavior is as expected in POSIX land. We would need to add our own signal handlers to make this work.

Related issue: #18340


My personal use case is wanting to use zig build run in a child process runner. The only thing I can access there is child.kill() or child.id (process ID) so I'm unable to clean up all of the processes unless I split the build from running the binary.

test "run and kill main process" {
   const allocator = std.testing.allocator;

   const main_argv = [_][]const u8{ "zig", "build", "run" };
   var main_process = std.process.Child.init(&main_argv, allocator);
   // Prevent writing to `stdout` so the test runner doesn't hang,
   // see https://github.com/ziglang/zig/issues/15091
   main_process.stdin_behavior = .Ignore;
   main_process.stdout_behavior = .Ignore;
   main_process.stderr_behavior = .Ignore;

   try main_process.spawn();

   std.time.sleep(3 * std.time.ns_per_s);

   const main_term = try main_process.kill();
   // std.debug.print("main_process.id {}", .{main_process.id});

   try std.testing.expectEqual(std.process.Child.Term{ .Signal = std.posix.SIG.TERM }, main_term);
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behavior

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions