authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2021-06-05 20:26:59+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-05 19:54:26-07:00
log072c3202c36ab05e4b2e61a23d6b870cb949feaf
tree8a61c419aaae6c2a90594a5df419644af4459afd
parent07852a4bc8b5785d4b68c217099f7aef13620a97

std: Better handing of POLLHUP in ChildProcess (#8988)

* std: Better handing of POLLHUP in ChildProcess Upon hitting the EOF condition there are two main differences between how Linux and the *BSD-derived systems behave: the former sets POLLHUP and POLLIN and, after reading any residual data, only POLLHUP remains set. The latter signal the EOF condition by setting both flags thus requiring some extra checks to determine if the stream is "done". DragonFly workaround/hack for POLLHUP is no longer required. Closes #8969

1 files changed, 20 insertions(+), 9 deletions(-)

lib/std/child_process.zig+20-9
...@@ -203,16 +203,18 @@ pub const ChildProcess = struct {...@@ -203,16 +203,18 @@ pub const ChildProcess = struct {
203 // of space an ArrayList will allocate grows exponentially.203 // of space an ArrayList will allocate grows exponentially.
204 const bump_amt = 512;204 const bump_amt = 512;
205205
206 // TODO https://github.com/ziglang/zig/issues/8724206 const err_mask = os.POLLERR | os.POLLNVAL | os.POLLHUP;
207 // parent process does not receive POLLHUP events
208 const dragonfly_workaround = builtin.os.tag == .dragonfly;
209207
210 while (dead_fds < poll_fds.len) {208 while (dead_fds < poll_fds.len) {
211 const events = try os.poll(&poll_fds, std.math.maxInt(i32));209 const events = try os.poll(&poll_fds, std.math.maxInt(i32));
212 if (events == 0) continue;210 if (events == 0) continue;
213211
212 var remove_stdout = false;
213 var remove_stderr = false;
214 // Try reading whatever is available before checking the error214 // Try reading whatever is available before checking the error
215 // conditions.215 // conditions.
216 // It's still possible to read after a POLLHUP is received, always
217 // check if there's some data waiting to be read first.
216 if (poll_fds[0].revents & os.POLLIN != 0) {218 if (poll_fds[0].revents & os.POLLIN != 0) {
217 // stdout is ready.219 // stdout is ready.
218 const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes);220 const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes);
...@@ -222,9 +224,12 @@ pub const ChildProcess = struct {...@@ -222,9 +224,12 @@ pub const ChildProcess = struct {
222 const nread = try os.read(poll_fds[0].fd, buf);224 const nread = try os.read(poll_fds[0].fd, buf);
223 stdout.items.len += nread;225 stdout.items.len += nread;
224226
225 // insert POLLHUP event because dragonfly fails to do so227 // Remove the fd when the EOF condition is met.
226 if (dragonfly_workaround and nread == 0) poll_fds[0].revents |= os.POLLHUP;228 remove_stdout = nread == 0;
229 } else {
230 remove_stdout = poll_fds[0].revents & err_mask != 0;
227 }231 }
232
228 if (poll_fds[1].revents & os.POLLIN != 0) {233 if (poll_fds[1].revents & os.POLLIN != 0) {
229 // stderr is ready.234 // stderr is ready.
230 const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes);235 const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes);
...@@ -234,16 +239,18 @@ pub const ChildProcess = struct {...@@ -234,16 +239,18 @@ pub const ChildProcess = struct {
234 const nread = try os.read(poll_fds[1].fd, buf);239 const nread = try os.read(poll_fds[1].fd, buf);
235 stderr.items.len += nread;240 stderr.items.len += nread;
236241
237 // insert POLLHUP event because dragonfly fails to do so242 // Remove the fd when the EOF condition is met.
238 if (dragonfly_workaround and nread == 0) poll_fds[1].revents |= os.POLLHUP;243 remove_stderr = nread == 0;
244 } else {
245 remove_stderr = poll_fds[1].revents & err_mask != 0;
239 }246 }
240247
241 // Exclude the fds that signaled an error.248 // Exclude the fds that signaled an error.
242 if (poll_fds[0].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) {249 if (remove_stdout) {
243 poll_fds[0].fd = -1;250 poll_fds[0].fd = -1;
244 dead_fds += 1;251 dead_fds += 1;
245 }252 }
246 if (poll_fds[1].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) {253 if (remove_stderr) {
247 poll_fds[1].fd = -1;254 poll_fds[1].fd = -1;
248 dead_fds += 1;255 dead_fds += 1;
249 }256 }
...@@ -294,6 +301,10 @@ pub const ChildProcess = struct {...@@ -294,6 +301,10 @@ pub const ChildProcess = struct {
294301
295 var stdout = std.ArrayList(u8).init(args.allocator);302 var stdout = std.ArrayList(u8).init(args.allocator);
296 var stderr = std.ArrayList(u8).init(args.allocator);303 var stderr = std.ArrayList(u8).init(args.allocator);
304 errdefer {
305 stdout.deinit();
306 stderr.deinit();
307 }
297308
298 try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes);309 try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes);
299310