authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-27 22:06:18-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-27 22:06:18-07:00
log814de45bd2e6711056c634b089b2ebf0c96f50b9
tree8d58dfb29656e8fddb73a890ef233f21b58ab359
parent5236842a9d0f7b0a82c440bcc08d315db4051d48

add std.io.poll and implement it for POSIX

I think having inputs is problematic here, it should only be for outputs.

1 files changed, 133 insertions(+), 0 deletions(-)

lib/std/io.zig+133
......@@ -168,6 +168,139 @@ test "null_writer" {
168168 null_writer.writeAll("yay" ** 10) catch |err| switch (err) {};
169169}
170170
171pub fn poll(
172 allocator: std.mem.Allocator,
173 comptime StreamEnum: type,
174 files: PollFiles(StreamEnum),
175) Poller(StreamEnum) {
176 const enum_fields = @typeInfo(StreamEnum).Enum.fields;
177 var result: Poller(StreamEnum) = undefined;
178 inline for (0..enum_fields.len) |i| {
179 result.fifos[i] = .{
180 .allocator = allocator,
181 .buf = &.{},
182 .head = 0,
183 .count = 0,
184 };
185 result.poll_fds[i] = .{
186 .fd = @field(files, enum_fields[i].name).file.handle,
187 .events = switch (@field(files, enum_fields[i].name).direction) {
188 .in => os.POLL.IN,
189 .out => os.POLL.OUT,
190 },
191 .revents = undefined,
192 };
193 }
194 return result;
195}
196
197pub fn Poller(comptime StreamEnum: type) type {
198 return struct {
199 const enum_fields = @typeInfo(StreamEnum).Enum.fields;
200 const Fifo = std.fifo.LinearFifo(u8, .Dynamic);
201
202 fifos: [enum_fields.len]Fifo,
203 //directions: [enum_fields.len]PollFile.Direction,
204 //handles: [enum_fields.len]std.fs.File.Handle,
205 poll_fds: [enum_fields.len]std.os.pollfd,
206
207 const Self = @This();
208
209 pub fn poll(self: *Self) !void {
210 if (builtin.os.tag == .windows) {
211 return pollWindows(self);
212 } else {
213 return pollPosix(self);
214 }
215 }
216
217 pub inline fn fifo(self: *Self, comptime which: StreamEnum) *Fifo {
218 return &self.fifos[@enumToInt(which)];
219 }
220
221 pub fn done(self: Self) bool {
222 for (self.poll_fds) |poll_fd| {
223 if (poll_fd.fd != -1) return false;
224 } else return true;
225 }
226
227 fn pollWindows(self: *Self) !void {
228 _ = self;
229 @compileError("TODO");
230 }
231
232 fn pollPosix(self: *Self) !void {
233 // We ask for ensureUnusedCapacity with this much extra space. This
234 // has more of an effect on small reads because once the reads
235 // start to get larger the amount of space an ArrayList will
236 // allocate grows exponentially.
237 const bump_amt = 512;
238
239 const err_mask = os.POLL.ERR | os.POLL.NVAL | os.POLL.HUP;
240
241 const events_len = try os.poll(&self.poll_fds, std.math.maxInt(i32));
242 if (events_len == 0) return;
243
244 inline for (0..enum_fields.len) |i| {
245 // Try reading whatever is available before checking the error
246 // conditions.
247 // It's still possible to read after a POLL.HUP is received,
248 // always check if there's some data waiting to be read first.
249 if (self.poll_fds[i].revents & os.POLL.IN != 0) {
250 const q = &self.fifos[i];
251 const buf = try q.writableWithSize(bump_amt);
252 const amt = try os.read(self.poll_fds[i].fd, buf);
253 q.update(amt);
254 std.debug.print("read {d} bytes\n", .{amt});
255 if (amt == 0) {
256 // Remove the fd when the EOF condition is met.
257 self.poll_fds[i].fd = -1;
258 }
259 } else if (self.poll_fds[i].revents & err_mask != 0) {
260 // Exclude the fds that signaled an error.
261 self.poll_fds[i].fd = -1;
262 } else if (self.poll_fds[i].revents & os.POLL.OUT != 0) {
263 const q = &self.fifos[i];
264 const amt = try os.write(self.poll_fds[i].fd, q.readableSlice(0));
265 q.discard(amt);
266 if (amt == 0) {
267 self.poll_fds[i].fd = -1;
268 }
269 }
270 }
271 }
272 };
273}
274
275/// Given an enum, returns a struct with fields of that enum, each field
276/// representing an I/O stream for polling.
277pub fn PollFiles(comptime StreamEnum: type) type {
278 const enum_fields = @typeInfo(StreamEnum).Enum.fields;
279 var struct_fields: [enum_fields.len]std.builtin.Type.StructField = undefined;
280 for (&struct_fields, enum_fields) |*struct_field, enum_field| {
281 struct_field.* = .{
282 .name = enum_field.name,
283 .type = PollFile,
284 .default_value = null,
285 .is_comptime = false,
286 .alignment = @alignOf(PollFile),
287 };
288 }
289 return @Type(.{ .Struct = .{
290 .layout = .Auto,
291 .fields = &struct_fields,
292 .decls = &.{},
293 .is_tuple = false,
294 } });
295}
296
297pub const PollFile = struct {
298 file: File,
299 direction: Direction,
300
301 pub const Direction = enum { in, out };
302};
303
171304test {
172305 _ = @import("io/bit_reader.zig");
173306 _ = @import("io/bit_writer.zig");