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 {
203203 // of space an ArrayList will allocate grows exponentially.
204204 const bump_amt = 512;
205205
206 // TODO https://github.com/ziglang/zig/issues/8724
207 // parent process does not receive POLLHUP events
208 const dragonfly_workaround = builtin.os.tag == .dragonfly;
206 const err_mask = os.POLLERR | os.POLLNVAL | os.POLLHUP;
209207
210208 while (dead_fds < poll_fds.len) {
211209 const events = try os.poll(&poll_fds, std.math.maxInt(i32));
212210 if (events == 0) continue;
213211
212 var remove_stdout = false;
213 var remove_stderr = false;
214214 // Try reading whatever is available before checking the error
215215 // 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.
216218 if (poll_fds[0].revents & os.POLLIN != 0) {
217219 // stdout is ready.
218220 const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes);
......@@ -222,9 +224,12 @@ pub const ChildProcess = struct {
222224 const nread = try os.read(poll_fds[0].fd, buf);
223225 stdout.items.len += nread;
224226
225 // insert POLLHUP event because dragonfly fails to do so
226 if (dragonfly_workaround and nread == 0) poll_fds[0].revents |= os.POLLHUP;
227 // Remove the fd when the EOF condition is met.
228 remove_stdout = nread == 0;
229 } else {
230 remove_stdout = poll_fds[0].revents & err_mask != 0;
227231 }
232
228233 if (poll_fds[1].revents & os.POLLIN != 0) {
229234 // stderr is ready.
230235 const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes);
......@@ -234,16 +239,18 @@ pub const ChildProcess = struct {
234239 const nread = try os.read(poll_fds[1].fd, buf);
235240 stderr.items.len += nread;
236241
237 // insert POLLHUP event because dragonfly fails to do so
238 if (dragonfly_workaround and nread == 0) poll_fds[1].revents |= os.POLLHUP;
242 // Remove the fd when the EOF condition is met.
243 remove_stderr = nread == 0;
244 } else {
245 remove_stderr = poll_fds[1].revents & err_mask != 0;
239246 }
240247
241248 // Exclude the fds that signaled an error.
242 if (poll_fds[0].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) {
249 if (remove_stdout) {
243250 poll_fds[0].fd = -1;
244251 dead_fds += 1;
245252 }
246 if (poll_fds[1].revents & (os.POLLERR | os.POLLNVAL | os.POLLHUP) != 0) {
253 if (remove_stderr) {
247254 poll_fds[1].fd = -1;
248255 dead_fds += 1;
249256 }
......@@ -294,6 +301,10 @@ pub const ChildProcess = struct {
294301
295302 var stdout = std.ArrayList(u8).init(args.allocator);
296303 var stderr = std.ArrayList(u8).init(args.allocator);
304 errdefer {
305 stdout.deinit();
306 stderr.deinit();
307 }
297308
298309 try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes);
299310