| ... | @@ -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; |
| 205 | | 205 | |
| 206 | // TODO https://github.com/ziglang/zig/issues/8724 | 206 | 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; | | |
| 209 | | 207 | |
| 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; |
| 213 | | 211 | |
| | 212 | var remove_stdout = false; |
| | 213 | var remove_stderr = false; |
| 214 | // Try reading whatever is available before checking the error | 214 | // 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; |
| 224 | | 226 | |
| 225 | // insert POLLHUP event because dragonfly fails to do so | 227 | // 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; |
| 236 | | 241 | |
| 237 | // insert POLLHUP event because dragonfly fails to do so | 242 | // 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 | } |
| 240 | | 247 | |
| 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 { |
| 294 | | 301 | |
| 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 | } |
| 297 | | 308 | |
| 298 | try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes); | 309 | try collectOutputPosix(child, &stdout, &stderr, args.max_output_bytes); |
| 299 | | 310 | |