authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-07 17:33:26-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-07 17:33:26-05:00
logdd49ed1c642d917af40bf4a0e03d013f40b3903b
treece62d0736ef95e5a313006c0817e56f4ae9e5aa6
parent05cf69209e44c59f838f94ab355485d2d3a0432a
parent2cc33367ebee202462d6bef7f07120dd38ff6912
signature Signed by PGP key 4AEE18F83AFDEB23

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
...@@ -256,6 +256,35 @@ pub const ChildProcess = struct {...@@ -256,6 +256,35 @@ pub const ChildProcess = struct {
256 }256 }
257 }257 }
258258
259 const WindowsAsyncReadResult = enum {
260 pending,
261 closed,
262 full,
263 };
264
265 fn windowsAsyncRead(
266 handle: windows.HANDLE,
267 overlapped: *windows.OVERLAPPED,
268 buf: *std.ArrayList(u8),
269 bump_amt: usize,
270 max_output_bytes: usize,
271 ) !WindowsAsyncReadResult {
272 while (true) {
273 const new_capacity = std.math.min(buf.items.len + bump_amt, max_output_bytes);
274 try buf.ensureTotalCapacity(new_capacity);
275 const next_buf = buf.unusedCapacitySlice();
276 if (next_buf.len == 0) return .full;
277 var read_bytes: u32 = undefined;
278 const read_result = windows.kernel32.ReadFile(handle, next_buf.ptr, math.cast(u32, next_buf.len) catch maxInt(u32), &read_bytes, overlapped);
279 if (read_result == 0) return switch (windows.kernel32.GetLastError()) {
280 .IO_PENDING => .pending,
281 .BROKEN_PIPE => .closed,
282 else => |err| windows.unexpectedError(err),
283 };
284 buf.items.len += read_bytes;
285 }
286 }
287
259 fn collectOutputWindows(child: *const ChildProcess, outs: [2]*std.ArrayList(u8), max_output_bytes: usize) !void {288 fn collectOutputWindows(child: *const ChildProcess, outs: [2]*std.ArrayList(u8), max_output_bytes: usize) !void {
260 const bump_amt = 512;289 const bump_amt = 512;
261 const handles = [_]windows.HANDLE{290 const handles = [_]windows.HANDLE{
...@@ -278,15 +307,17 @@ pub const ChildProcess = struct {...@@ -278,15 +307,17 @@ pub const ChildProcess = struct {
278307
279 // Windows Async IO requires an initial call to ReadFile before waiting on the handle308 // Windows Async IO requires an initial call to ReadFile before waiting on the handle
280 for ([_]u1{ 0, 1 }) |i| {309 for ([_]u1{ 0, 1 }) |i| {
281 const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes);310 switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) {
282 try outs[i].ensureTotalCapacity(new_capacity);311 .pending => {
283 const buf = outs[i].unusedCapacitySlice();312 wait_objects[wait_object_count] = handles[i];
284 _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]);313 wait_object_count += 1;
285 wait_objects[wait_object_count] = handles[i];314 },
286 wait_object_count += 1;315 .closed => {}, // don't add to the wait_objects list
316 .full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong,
317 }
287 }318 }
288319
289 while (true) {320 while (wait_object_count > 0) {
290 const status = windows.kernel32.WaitForMultipleObjects(wait_object_count, &wait_objects, 0, windows.INFINITE);321 const status = windows.kernel32.WaitForMultipleObjects(wait_object_count, &wait_objects, 0, windows.INFINITE);
291 if (status == windows.WAIT_FAILED) {322 if (status == windows.WAIT_FAILED) {
292 switch (windows.kernel32.GetLastError()) {323 switch (windows.kernel32.GetLastError()) {
...@@ -310,23 +341,21 @@ pub const ChildProcess = struct {...@@ -310,23 +341,21 @@ pub const ChildProcess = struct {
310 var read_bytes: u32 = undefined;341 var read_bytes: u32 = undefined;
311 if (windows.kernel32.GetOverlappedResult(handles[i], &overlapped[i], &read_bytes, 0) == 0) {342 if (windows.kernel32.GetOverlappedResult(handles[i], &overlapped[i], &read_bytes, 0) == 0) {
312 switch (windows.kernel32.GetLastError()) {343 switch (windows.kernel32.GetLastError()) {
313 .BROKEN_PIPE => {344 .BROKEN_PIPE => continue,
314 if (wait_object_count == 0)
315 break;
316 continue;
317 },
318 else => |err| return windows.unexpectedError(err),345 else => |err| return windows.unexpectedError(err),
319 }346 }
320 }347 }
321348
322 outs[i].items.len += read_bytes;349 outs[i].items.len += read_bytes;
323 const new_capacity = std.math.min(outs[i].items.len + bump_amt, max_output_bytes);350
324 try outs[i].ensureTotalCapacity(new_capacity);351 switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) {
325 const buf = outs[i].unusedCapacitySlice();352 .pending => {
326 if (buf.len == 0) return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong;353 wait_objects[wait_object_count] = handles[i];
327 _ = windows.kernel32.ReadFile(handles[i], buf.ptr, math.cast(u32, buf.len) catch maxInt(u32), null, &overlapped[i]);354 wait_object_count += 1;
328 wait_objects[wait_object_count] = handles[i];355 },
329 wait_object_count += 1;356 .closed => {}, // don't add to the wait_objects list
357 .full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong,
358 }
330 }359 }
331 }360 }
332361