authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-07 17:33:26-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-11 12:12:49-07:00
logc4458e1b455d493efd39fa4cf8a5b2b7a2565618
tree1cb065ad2b5083831371824791e3bdf1fb248f97
parentfbde43524f0534b911bb749ad302ff3ed063ca97

Merge pull request #10813 from marler8997/windowsChildHang

child_process: collectOutputWindows handle broken_pipe from ReadFile

1 files changed, 48 insertions(+), 19 deletions(-)

lib/std/child_process.zig+48-19
...@@ -252,6 +252,35 @@ pub const ChildProcess = struct {...@@ -252,6 +252,35 @@ pub const ChildProcess = struct {
252 }252 }
253 }253 }
254254
255 const WindowsAsyncReadResult = enum {
256 pending,
257 closed,
258 full,
259 };
260
261 fn windowsAsyncRead(
262 handle: windows.HANDLE,
263 overlapped: *windows.OVERLAPPED,
264 buf: *std.ArrayList(u8),
265 bump_amt: usize,
266 max_output_bytes: usize,
267 ) !WindowsAsyncReadResult {
268 while (true) {
269 const new_capacity = std.math.min(buf.items.len + bump_amt, max_output_bytes);
270 try buf.ensureTotalCapacity(new_capacity);
271 const next_buf = buf.unusedCapacitySlice();
272 if (next_buf.len == 0) return .full;
273 var read_bytes: u32 = undefined;
274 const read_result = windows.kernel32.ReadFile(handle, next_buf.ptr, math.cast(u32, next_buf.len) catch maxInt(u32), &read_bytes, overlapped);
275 if (read_result == 0) return switch (windows.kernel32.GetLastError()) {
276 .IO_PENDING => .pending,
277 .BROKEN_PIPE => .closed,
278 else => |err| windows.unexpectedError(err),
279 };
280 buf.items.len += read_bytes;
281 }
282 }
283
255 fn collectOutputWindows(child: *const ChildProcess, outs: [2]*std.ArrayList(u8), max_output_bytes: usize) !void {284 fn collectOutputWindows(child: *const ChildProcess, outs: [2]*std.ArrayList(u8), max_output_bytes: usize) !void {
256 const bump_amt = 512;285 const bump_amt = 512;
257 const handles = [_]windows.HANDLE{286 const handles = [_]windows.HANDLE{
...@@ -274,15 +303,17 @@ pub const ChildProcess = struct {...@@ -274,15 +303,17 @@ pub const ChildProcess = struct {
274303
275 // Windows Async IO requires an initial call to ReadFile before waiting on the handle304 // Windows Async IO requires an initial call to ReadFile before waiting on the handle
276 for ([_]u1{ 0, 1 }) |i| {305 for ([_]u1{ 0, 1 }) |i| {
277 const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes);306 switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) {
278 try outs[i].ensureTotalCapacity(new_capacity);307 .pending => {
279 const buf = outs[i].unusedCapacitySlice();308 wait_objects[wait_object_count] = handles[i];
280 _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]);309 wait_object_count += 1;
281 wait_objects[wait_object_count] = handles[i];310 },
282 wait_object_count += 1;311 .closed => {}, // don't add to the wait_objects list
312 .full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong,
313 }
283 }314 }
284315
285 while (true) {316 while (wait_object_count > 0) {
286 const status = windows.kernel32.WaitForMultipleObjects(wait_object_count, &wait_objects, 0, windows.INFINITE);317 const status = windows.kernel32.WaitForMultipleObjects(wait_object_count, &wait_objects, 0, windows.INFINITE);
287 if (status == windows.WAIT_FAILED) {318 if (status == windows.WAIT_FAILED) {
288 switch (windows.kernel32.GetLastError()) {319 switch (windows.kernel32.GetLastError()) {
...@@ -306,23 +337,21 @@ pub const ChildProcess = struct {...@@ -306,23 +337,21 @@ pub const ChildProcess = struct {
306 var read_bytes: u32 = undefined;337 var read_bytes: u32 = undefined;
307 if (windows.kernel32.GetOverlappedResult(handles[i], &overlapped[i], &read_bytes, 0) == 0) {338 if (windows.kernel32.GetOverlappedResult(handles[i], &overlapped[i], &read_bytes, 0) == 0) {
308 switch (windows.kernel32.GetLastError()) {339 switch (windows.kernel32.GetLastError()) {
309 .BROKEN_PIPE => {340 .BROKEN_PIPE => continue,
310 if (wait_object_count == 0)
311 break;
312 continue;
313 },
314 else => |err| return windows.unexpectedError(err),341 else => |err| return windows.unexpectedError(err),
315 }342 }
316 }343 }
317344
318 outs[i].items.len += read_bytes;345 outs[i].items.len += read_bytes;
319 const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes);346
320 try outs[i].ensureTotalCapacity(new_capacity);347 switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) {
321 const buf = outs[i].unusedCapacitySlice();348 .pending => {
322 if (buf.len == 0) return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong;349 wait_objects[wait_object_count] = handles[i];
323 _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]);350 wait_object_count += 1;
324 wait_objects[wait_object_count] = handles[i];351 },
325 wait_object_count += 1;352 .closed => {}, // don't add to the wait_objects list
353 .full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong,
354 }
326 }355 }
327 }356 }
328357