@@ -29,6 +29,9 @@ pub const Id = switch (native_os) {
29
29
id : Id ,
30
30
thread_handle : if (native_os == .windows ) windows .HANDLE else void ,
31
31
32
+ /// Linux only. May be unavailable on older kernel versions.
33
+ pid_fd : ? posix.fd_t ,
34
+
32
35
allocator : mem.Allocator ,
33
36
34
37
/// The writing end of the child process's standard input pipe.
@@ -213,6 +216,7 @@ pub fn init(argv: []const []const u8, allocator: mem.Allocator) ChildProcess {
213
216
.argv = argv ,
214
217
.id = undefined ,
215
218
.thread_handle = undefined ,
219
+ .pid_fd = null ,
216
220
.err_pipe = null ,
217
221
.term = null ,
218
222
.env_map = null ,
@@ -291,10 +295,22 @@ pub fn killPosix(self: *ChildProcess) !Term {
291
295
self .cleanupStreams ();
292
296
return term ;
293
297
}
294
- posix .kill (self .id , posix .SIG .TERM ) catch | err | switch (err ) {
295
- error .ProcessNotFound = > return error .AlreadyTerminated ,
296
- else = > return err ,
297
- };
298
+ if (self .pid_fd ) | pid_fd | {
299
+ if (native_os == .linux ) {
300
+ switch (linux .E .init (linux .pidfd_send_signal (pid_fd , posix .SIG .TERM , null , 0 ))) {
301
+ .SUCCESS = > {},
302
+ .SRCH = > return error .AlreadyTerminated ,
303
+ else = > | err | return posix .unexpectedErrno (err ),
304
+ }
305
+ } else {
306
+ unreachable ;
307
+ }
308
+ } else {
309
+ posix .kill (self .id , posix .SIG .TERM ) catch | err | switch (err ) {
310
+ error .ProcessNotFound = > return error .AlreadyTerminated ,
311
+ else = > return err ,
312
+ };
313
+ }
298
314
self .waitUnwrapped ();
299
315
return self .term .? ;
300
316
}
@@ -305,6 +321,7 @@ pub const WaitError = SpawnError || std.os.windows.GetProcessMemoryInfoError;
305
321
pub fn wait (self : * ChildProcess ) WaitError ! Term {
306
322
const term = if (native_os == .windows ) try self .waitWindows () else self .waitPosix ();
307
323
self .id = undefined ;
324
+ self .pid_fd = null ;
308
325
return term ;
309
326
}
310
327
@@ -451,6 +468,34 @@ fn waitUnwrappedWindows(self: *ChildProcess) WaitError!void {
451
468
452
469
fn waitUnwrapped (self : * ChildProcess ) void {
453
470
const res : posix.WaitPidResult = res : {
471
+ if (self .pid_fd ) | pid_fd | {
472
+ if (native_os == .linux ) {
473
+ var info : linux.siginfo_t = undefined ;
474
+ var ru : linux.rusage = undefined ;
475
+ while (true ) {
476
+ switch (linux .E .init (linux .syscall5 (.waitid , @intFromEnum (linux .P .PIDFD ), @intCast (pid_fd ), @intFromPtr (& info ), linux .W .EXITED , @intFromPtr (& ru )))) {
477
+ .SUCCESS = > break ,
478
+ .INTR = > continue ,
479
+ else = > unreachable ,
480
+ }
481
+ }
482
+ if (self .request_resource_usage_statistics ) {
483
+ self .resource_usage_statistics .rusage = ru ;
484
+ }
485
+ const status : u32 = @bitCast (info .fields .common .second .sigchld .status );
486
+ break :res posix.WaitPidResult {
487
+ .pid = info .fields .common .first .piduid .pid ,
488
+ .status = switch (info .code ) {
489
+ 1 = > (status & 0xff ) << 8 , // CLD_EXITED
490
+ 2 , 3 = > status & 0x7f , // CLD_KILLED, CLD_DUMPED
491
+ else = > unreachable ,
492
+ },
493
+ };
494
+ } else {
495
+ unreachable ;
496
+ }
497
+ }
498
+
454
499
if (self .request_resource_usage_statistics ) {
455
500
switch (native_os ) {
456
501
.linux , .macos , .ios = > {
@@ -727,13 +772,17 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void {
727
772
defer self .allocator .free (stack );
728
773
729
774
var clone_args = mem .zeroes (linux .clone_args );
730
- clone_args .flags = linux .CLONE .VM | linux .CLONE .VFORK | linux .CLONE .CLEAR_SIGHAND ;
775
+ var pid_fd : posix.fd_t = undefined ;
776
+ clone_args .flags = linux .CLONE .VM | linux .CLONE .VFORK | linux .CLONE .CLEAR_SIGHAND | linux .CLONE .PIDFD ;
731
777
clone_args .exit_signal = linux .SIG .CHLD ;
732
778
clone_args .stack = @intFromPtr (stack .ptr );
733
779
clone_args .stack_size = stack_size ;
780
+ clone_args .pidfd = @intFromPtr (& pid_fd );
734
781
var rc = linux .clone3 (& clone_args , @sizeOf (linux .clone_args ), spawnPosixChildHelper , @intFromPtr (& child_arg ));
735
782
switch (linux .E .init (rc )) {
736
- .SUCCESS = > {},
783
+ .SUCCESS = > {
784
+ self .pid_fd = pid_fd ;
785
+ },
737
786
.AGAIN , .NOMEM = > return error .SystemResources ,
738
787
.INVAL , .NOSYS = > {
739
788
// Fallback to use clone().
0 commit comments