| ... | ... | @@ -168,6 +168,139 @@ test "null_writer" { |
| 168 | 168 | null_writer.writeAll("yay" ** 10) catch |err| switch (err) {}; |
| 169 | 169 | } |
| 170 | 170 | |
| 171 | pub 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 | |
| 197 | pub 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. |
| 277 | pub 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 | |
| 297 | pub const PollFile = struct { |
| 298 | file: File, |
| 299 | direction: Direction, |
| 300 | |
| 301 | pub const Direction = enum { in, out }; |
| 302 | }; |
| 303 | |
| 171 | 304 | test { |
| 172 | 305 | _ = @import("io/bit_reader.zig"); |
| 173 | 306 | _ = @import("io/bit_writer.zig"); |