Reading from the stdout of a spawned process on Windows with the new std.Io.Reader seems to cause problem. I used to be able to read from the stdout of a child process (see read_child_stdout_150()). I migrated it to use the new std.Io.Reader (read_child_stdout_151()) and started getting unreachable panic on handling the IO_PENDING error at line 640 in std.os.windows.zig.
The stdout of a child process is set up as Named Pipe via windowsMakeAsyncPipe() in std.process.Child.zig as a windows.FILE_FLAG_OVERLAPPED file handle (line 1336). IO_PENDING error can occur with an OVERLAPPED file handle. I don’t understand why it was working pre-0.15.1 and now it doesn’t.
Is it something wrong in the way std.Io.Reader is being used in read_child_stdout_151()?
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const alloc = gpa.allocator();
const argv: [2][]const u8 = .{ "/usr/bin/echo", "foo bar" };
var child = std.process.Child.init(&argv, alloc);
child.stdin_behavior = .Pipe;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Ignore;
try child.spawn();
// try read_child_stdout_150(child.stdout.?);
try read_child_stdout_151(child.stdout.?);
_ = try child.wait();
}
fn read_child_stdout_150(child_stdout_f: std.fs.File) !void {
const reader = child_stdout_f.reader();
var chunk: [1024]u8 = undefined;
while (true) {
const len = try reader.read(&chunk);
if (len == 0) break;
std.debug.print("Chunk: {s}\n", .{chunk[0..len]});
}
}
fn read_child_stdout_151(child_stdout_f: std.fs.File) !void {
var reader_buf: [1024]u8 = undefined;
var f_reader = child_stdout_f.reader(&reader_buf);
var reader = &f_reader.interface;
var chunk: [1024]u8 = undefined;
while (true) {
const len = try reader.readSliceShort(&chunk);
if (len == 0) break;
std.debug.print("Chunk: {s}\n", .{chunk[0..len]});
}
}
I have checked the Windows API ReadFile(). For a FILE_FLAG_OVERLAPPED file handle, ReadFile() can return ERROR_IO_PENDING. It means “the read has been queued, but not finished yet”, not an error condition.
The typical case to handle ERROR_IO_PENDING as a blocking read is to wait on the OVERLAPPED.hEvent.
OVERLAPPED ol = {0};
ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
BOOL ok = ReadFile(hFile, buffer, size, NULL, &ol);
if (!ok) {
if (GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(ol.hEvent, INFINITE);
DWORD bytesRead;
GetOverlappedResult(hFile, &ol, &bytesRead, TRUE);
}
}
Good to know it’s being addressed. BTW I had tried both readerStreaming() and reader() and had the same issue.
I’m just not sure whether it’s a real bug or it’s something transient. If it’s a real bug, I’ll file a bug report. I just don’t want to clog up the bug pipeline with non-issues.
The FILE_FLAG_OVERLAPPED flag on a file handle is used for async IO on Windows. Not sure if the upcoming async work will resolve it.