authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-08 12:55:38-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-09 20:47:24-08:00
log0deaf9957c34eceecd3cb7b0033e447ef94addc5
tree19c4abf6fda15cd19107a91db05d6390fd8d0e41
parentd63172a35da5d493aef72aa074afa90ac5bb619a

std.Io: simplify operate function

- no timeout - no n_wait - infallible

5 files changed, 34 insertions(+), 54 deletions(-)

lib/std/Io.zig+4-15
...@@ -148,7 +148,7 @@ pub const VTable = struct {...@@ -148,7 +148,7 @@ pub const VTable = struct {
148 futexWaitUncancelable: *const fn (?*anyopaque, ptr: *const u32, expected: u32) void,148 futexWaitUncancelable: *const fn (?*anyopaque, ptr: *const u32, expected: u32) void,
149 futexWake: *const fn (?*anyopaque, ptr: *const u32, max_waiters: u32) void,149 futexWake: *const fn (?*anyopaque, ptr: *const u32, max_waiters: u32) void,
150150
151 operate: *const fn (?*anyopaque, []Operation, n_wait: usize, Timeout) OperateError!void,151 operate: *const fn (?*anyopaque, []Operation) void,
152152
153 dirCreateDir: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions) Dir.CreateDirError!void,153 dirCreateDir: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions) Dir.CreateDirError!void,
154 dirCreateDirPath: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions) Dir.CreateDirPathError!Dir.CreatePathStatus,154 dirCreateDirPath: *const fn (?*anyopaque, Dir, []const u8, Dir.Permissions) Dir.CreateDirPathError!Dir.CreatePathStatus,
...@@ -257,22 +257,11 @@ pub const Operation = union(enum) {...@@ -257,22 +257,11 @@ pub const Operation = union(enum) {
257 };257 };
258};258};
259259
260pub const OperateError = error{ Canceled, Timeout };
261
262/// Performs all `operations` in a non-deterministic order. Returns after all260/// Performs all `operations` in a non-deterministic order. Returns after all
263/// `operations` have been attempted. The degree to which the operations are261/// `operations` have been completed. The degree to which the operations are
264/// performed concurrently is determined by the `Io` implementation.262/// performed concurrently is determined by the `Io` implementation.
265///263pub fn operate(io: Io, operations: []Operation) void {
266/// `n_wait` is an amount of operations between `0` and `operations.len` that264 return io.vtable.operate(io.userdata, operations);
267/// determines how many attempted operations must complete before `operate`
268/// returns. Operation completion is defined by returning a value other than
269/// `error.WouldBlock`. If the operation cannot return `error.WouldBlock`, it
270/// always counts as completing.
271///
272/// In the event `error.Canceled` is returned, any number of `operations` may
273/// still have been completed successfully.
274pub fn operate(io: Io, operations: []Operation, n_wait: usize, timeout: Timeout) OperateError!void {
275 return io.vtable.operate(io.userdata, operations, n_wait, timeout);
276}265}
277266
278pub const Limit = enum(usize) {267pub const Limit = enum(usize) {
lib/std/Io/File.zig+1-1
...@@ -531,7 +531,7 @@ pub fn readStreaming(file: File, io: Io, buffer: []const []u8) Reader.Error!usiz...@@ -531,7 +531,7 @@ pub fn readStreaming(file: File, io: Io, buffer: []const []u8) Reader.Error!usiz
531 .data = buffer,531 .data = buffer,
532 .result = undefined,532 .result = undefined,
533 } };533 } };
534 io.vtable.operate(io.userdata, (&operation)[0..1], 1, .none) catch unreachable;534 io.vtable.operate(io.userdata, (&operation)[0..1]);
535 return operation.file_read_streaming.result;535 return operation.file_read_streaming.result;
536}536}
537537
lib/std/Io/Threaded.zig+28-33
...@@ -2268,20 +2268,15 @@ fn futexWake(userdata: ?*anyopaque, ptr: *const u32, max_waiters: u32) void {...@@ -2268,20 +2268,15 @@ fn futexWake(userdata: ?*anyopaque, ptr: *const u32, max_waiters: u32) void {
2268 Thread.futexWake(ptr, max_waiters);2268 Thread.futexWake(ptr, max_waiters);
2269}2269}
22702270
2271fn operate(userdata: ?*anyopaque, operations: []Io.Operation, n_wait: usize, timeout: Io.Timeout) Io.OperateError!void {2271fn operate(userdata: ?*anyopaque, operations: []Io.Operation) void {
2272 const t: *Threaded = @ptrCast(@alignCast(userdata));2272 const t: *Threaded = @ptrCast(@alignCast(userdata));
2273 const t_io = ioBasic(t);2273 _ = t;
22742274
2275 if (is_windows) @panic("TODO");2275 if (is_windows) @panic("TODO");
22762276
2277 const deadline = timeout.toDeadline(t_io) catch |err| switch (err) {
2278 error.UnsupportedClock, error.Unexpected => null,
2279 };
2280
2281 var poll_buffer: [100]posix.pollfd = undefined;2277 var poll_buffer: [100]posix.pollfd = undefined;
2282 var map_buffer: [poll_buffer.len]u8 = undefined; // poll_buffer index to operations index2278 var map_buffer: [poll_buffer.len]u8 = undefined; // poll_buffer index to operations index
2283 var poll_i: usize = 0;2279 var poll_i: usize = 0;
2284 var completed: usize = 0;
22852280
2286 // Put all the file reads with nonblocking enabled into the poll set.2281 // Put all the file reads with nonblocking enabled into the poll set.
2287 if (operations.len > poll_buffer.len) @panic("TODO");2282 if (operations.len > poll_buffer.len) @panic("TODO");
...@@ -2302,7 +2297,6 @@ fn operate(userdata: ?*anyopaque, operations: []Io.Operation, n_wait: usize, tim...@@ -2302,7 +2297,6 @@ fn operate(userdata: ?*anyopaque, operations: []Io.Operation, n_wait: usize, tim
2302 poll_i += 1;2297 poll_i += 1;
2303 } else {2298 } else {
2304 o.result = fileReadStreaming(o.file, o.data);2299 o.result = fileReadStreaming(o.file, o.data);
2305 completed += 1;
2306 }2300 }
2307 },2301 },
2308 };2302 };
...@@ -2312,41 +2306,42 @@ fn operate(userdata: ?*anyopaque, operations: []Io.Operation, n_wait: usize, tim...@@ -2312,41 +2306,42 @@ fn operate(userdata: ?*anyopaque, operations: []Io.Operation, n_wait: usize, tim
2312 return;2306 return;
2313 }2307 }
23142308
2315 const max_poll_ms = std.math.maxInt(i32);2309 while (true) {
23162310 const syscall = Syscall.start() catch |err| switch (err) {
2317 while (completed < n_wait) {2311 error.Canceled => {
2318 const timeout_ms: i32 = if (deadline) |d| t: {2312 for (map_buffer[0..poll_i]) |operation_index| {
2319 const duration = d.durationFromNow(t_io) catch @panic("TODO make this unreachable");2313 switch (operations[operation_index]) {
2320 if (duration.raw.nanoseconds <= 0) return error.Timeout;2314 .noop => unreachable,
2321 break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));2315 inline else => |*o| o.result = error.Canceled,
2322 } else -1;2316 }
2323 const syscall = try Syscall.start();2317 }
2324 const poll_rc = posix.system.poll(&poll_buffer, poll_i, timeout_ms);2318 return;
2319 },
2320 };
2321 const poll_rc = posix.system.poll(&poll_buffer, poll_i, -1);
2325 syscall.finish();2322 syscall.finish();
2326 switch (posix.errno(poll_rc)) {2323 switch (posix.errno(poll_rc)) {
2327 .SUCCESS => {2324 .SUCCESS => {
2328 if (poll_rc == 0) {2325 if (poll_rc == 0) {
2329 // Although spurious timeouts are OK, when no deadline2326 // Spurious timeout; handle same as INTR.
2330 // is passed we must not return `error.Timeout`.2327 continue;
2331 if (deadline == null) continue;
2332 return error.Timeout;
2333 }
2334 for (poll_buffer[0..poll_i], map_buffer[0..poll_i]) |*poll_fd, operation_index| {
2335 if (poll_fd.revents == 0) continue;
2336 poll_fd.fd = -1; // Disarm this operation.
2337 switch (operations[operation_index]) {
2338 .noop => unreachable,
2339 .file_read_streaming => |*o| {
2340 o.result = fileReadStreaming(o.file, o.data);
2341 completed += 1;
2342 },
2343 }
2344 }2328 }
2329 break;
2345 },2330 },
2346 .INTR => continue,2331 .INTR => continue,
2347 else => @panic("TODO handle unexpected error from poll()"),2332 else => @panic("TODO handle unexpected error from poll()"),
2348 }2333 }
2349 }2334 }
2335
2336 for (poll_buffer[0..poll_i], map_buffer[0..poll_i]) |*poll_fd, operation_index| {
2337 if (poll_fd.revents == 0) continue;
2338 switch (operations[operation_index]) {
2339 .noop => unreachable,
2340 .file_read_streaming => |*o| {
2341 o.result = fileReadStreaming(o.file, o.data);
2342 },
2343 }
2344 }
2350}2345}
23512346
2352const dirCreateDir = switch (native_os) {2347const dirCreateDir = switch (native_os) {
lib/std/process.zig-2
...@@ -502,7 +502,6 @@ pub const RunOptions = struct {...@@ -502,7 +502,6 @@ pub const RunOptions = struct {
502 create_no_window: bool = true,502 create_no_window: bool = true,
503 /// Darwin-only. Disable ASLR for the child process.503 /// Darwin-only. Disable ASLR for the child process.
504 disable_aslr: bool = false,504 disable_aslr: bool = false,
505 timeout: Io.Timeout = .none,
506};505};
507506
508pub const RunResult = struct {507pub const RunResult = struct {
...@@ -544,7 +543,6 @@ pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult {...@@ -544,7 +543,6 @@ pub fn run(gpa: Allocator, io: Io, options: RunOptions) RunError!RunResult {
544 .stderr = &stderr,543 .stderr = &stderr,
545 .stdout_limit = options.stdout_limit,544 .stdout_limit = options.stdout_limit,
546 .stderr_limit = options.stderr_limit,545 .stderr_limit = options.stderr_limit,
547 .timeout = options.timeout,
548 });546 });
549547
550 return .{548 return .{
lib/std/process/Child.zig+1-3
...@@ -138,7 +138,6 @@ pub const CollectOutputOptions = struct {...@@ -138,7 +138,6 @@ pub const CollectOutputOptions = struct {
138 allocator: ?Allocator = null,138 allocator: ?Allocator = null,
139 stdout_limit: Io.Limit = .unlimited,139 stdout_limit: Io.Limit = .unlimited,
140 stderr_limit: Io.Limit = .unlimited,140 stderr_limit: Io.Limit = .unlimited,
141 timeout: Io.Timeout = .none,
142};141};
143142
144/// Collect the output from the process's stdout and stderr. Will return once143/// Collect the output from the process's stdout and stderr. Will return once
...@@ -174,7 +173,7 @@ pub fn collectOutput(child: *const Child, io: Io, options: CollectOutputOptions)...@@ -174,7 +173,7 @@ pub fn collectOutput(child: *const Child, io: Io, options: CollectOutputOptions)
174 var all_done = true;173 var all_done = true;
175 var any_canceled = false;174 var any_canceled = false;
176 var other_err: (error{StreamTooLong} || Io.File.Reader.Error)!void = {};175 var other_err: (error{StreamTooLong} || Io.File.Reader.Error)!void = {};
177 const op_result = io.vtable.operate(io.userdata, &reads, 1, options.timeout);176 io.vtable.operate(io.userdata, &reads);
178 for (&reads, &lists, &limits, &dones) |*read, list, limit, *done| {177 for (&reads, &lists, &limits, &dones) |*read, list, limit, *done| {
179 if (done.*) continue;178 if (done.*) continue;
180 const n = read.file_read_streaming.result catch |err| switch (err) {179 const n = read.file_read_streaming.result catch |err| switch (err) {
...@@ -197,7 +196,6 @@ pub fn collectOutput(child: *const Child, io: Io, options: CollectOutputOptions)...@@ -197,7 +196,6 @@ pub fn collectOutput(child: *const Child, io: Io, options: CollectOutputOptions)
197 if (list.items.len > @intFromEnum(limit)) other_err = error.StreamTooLong;196 if (list.items.len > @intFromEnum(limit)) other_err = error.StreamTooLong;
198 }197 }
199 if (any_canceled) return error.Canceled;198 if (any_canceled) return error.Canceled;
200 try op_result; // could be error.Canceled
201 try other_err;199 try other_err;
202 if (all_done) return;200 if (all_done) return;
203 }201 }