| author | |
| committer | |
| log | cc4552733351390aecab9ae900beb822237d6041 |
| tree | 575c7ad933f2ea04c4125704a55dc823839af57e |
| parent | 5d4a02c350a18a70cf1f92f6638b5d26689c16b4 |
only works on linux so far10 files changed, 559 insertions(+), 97 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -460,6 +460,7 @@ set(ZIG_STD_FILES |
| 460 | 460 | "empty.zig" |
| 461 | 461 | "event.zig" |
| 462 | 462 | "event/channel.zig" |
| 463 | "event/fs.zig" | |
| 463 | 464 | "event/future.zig" |
| 464 | 465 | "event/group.zig" |
| 465 | 466 | "event/lock.zig" |
std/build.zig+2-2| ... | ... | @@ -603,10 +603,10 @@ pub const Builder = struct { |
| 603 | 603 | } |
| 604 | 604 | |
| 605 | 605 | fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { |
| 606 | return self.copyFileMode(source_path, dest_path, os.default_file_mode); | |
| 606 | return self.copyFileMode(source_path, dest_path, os.File.default_mode); | |
| 607 | 607 | } |
| 608 | 608 | |
| 609 | fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void { | |
| 609 | fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.File.Mode) !void { | |
| 610 | 610 | if (self.verbose) { |
| 611 | 611 | warn("cp {} {}\n", source_path, dest_path); |
| 612 | 612 | } |
std/debug/index.zig+1-5| ... | ... | @@ -672,14 +672,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type |
| 672 | 672 | |
| 673 | 673 | const ParseFormValueError = error{ |
| 674 | 674 | EndOfStream, |
| 675 | Io, | |
| 676 | BadFd, | |
| 677 | Unexpected, | |
| 678 | 675 | InvalidDebugInfo, |
| 679 | 676 | EndOfFile, |
| 680 | IsDir, | |
| 681 | 677 | OutOfMemory, |
| 682 | }; | |
| 678 | } || std.os.File.ReadError; | |
| 683 | 679 | |
| 684 | 680 | fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { |
| 685 | 681 | return switch (form_id) { |
std/event.zig+10-8| ... | ... | @@ -1,17 +1,19 @@ |
| 1 | pub const Channel = @import("event/channel.zig").Channel; | |
| 2 | pub const Future = @import("event/future.zig").Future; | |
| 3 | pub const Group = @import("event/group.zig").Group; | |
| 4 | pub const Lock = @import("event/lock.zig").Lock; | |
| 1 | 5 | pub const Locked = @import("event/locked.zig").Locked; |
| 2 | 6 | pub const Loop = @import("event/loop.zig").Loop; |
| 3 | pub const Lock = @import("event/lock.zig").Lock; | |
| 7 | pub const fs = @import("event/fs.zig"); | |
| 4 | 8 | pub const tcp = @import("event/tcp.zig"); |
| 5 | pub const Channel = @import("event/channel.zig").Channel; | |
| 6 | pub const Group = @import("event/group.zig").Group; | |
| 7 | pub const Future = @import("event/future.zig").Future; | |
| 8 | 9 | |
| 9 | 10 | test "import event tests" { |
| 11 | _ = @import("event/channel.zig"); | |
| 12 | _ = @import("event/fs.zig"); | |
| 13 | _ = @import("event/future.zig"); | |
| 14 | _ = @import("event/group.zig"); | |
| 15 | _ = @import("event/lock.zig"); | |
| 10 | 16 | _ = @import("event/locked.zig"); |
| 11 | 17 | _ = @import("event/loop.zig"); |
| 12 | _ = @import("event/lock.zig"); | |
| 13 | 18 | _ = @import("event/tcp.zig"); |
| 14 | _ = @import("event/channel.zig"); | |
| 15 | _ = @import("event/group.zig"); | |
| 16 | _ = @import("event/future.zig"); | |
| 17 | 19 | } |
std/event/fs.zig created+343| ... | ... | @@ -0,0 +1,343 @@ |
| 1 | const std = @import("../index.zig"); | |
| 2 | const event = std.event; | |
| 3 | const assert = std.debug.assert; | |
| 4 | const os = std.os; | |
| 5 | const mem = std.mem; | |
| 6 | ||
| 7 | pub const RequestNode = std.atomic.Queue(Request).Node; | |
| 8 | ||
| 9 | pub const Request = struct { | |
| 10 | msg: Msg, | |
| 11 | finish: Finish, | |
| 12 | ||
| 13 | pub const Finish = union(enum) { | |
| 14 | TickNode: event.Loop.NextTickNode, | |
| 15 | DeallocCloseOperation: *CloseOperation, | |
| 16 | NoAction, | |
| 17 | }; | |
| 18 | ||
| 19 | pub const Msg = union(enum) { | |
| 20 | PWriteV: PWriteV, | |
| 21 | PReadV: PReadV, | |
| 22 | OpenRead: OpenRead, | |
| 23 | Close: Close, | |
| 24 | WriteFile: WriteFile, | |
| 25 | End, // special - means the fs thread should exit | |
| 26 | ||
| 27 | pub const PWriteV = struct { | |
| 28 | fd: os.FileHandle, | |
| 29 | data: []const []const u8, | |
| 30 | offset: usize, | |
| 31 | result: Error!void, | |
| 32 | ||
| 33 | pub const Error = error{}; | |
| 34 | }; | |
| 35 | ||
| 36 | pub const PReadV = struct { | |
| 37 | fd: os.FileHandle, | |
| 38 | iov: []os.linux.iovec, | |
| 39 | offset: usize, | |
| 40 | result: Error!usize, | |
| 41 | ||
| 42 | pub const Error = os.File.ReadError; | |
| 43 | }; | |
| 44 | ||
| 45 | pub const OpenRead = struct { | |
| 46 | /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265 | |
| 47 | path: []const u8, | |
| 48 | result: Error!os.FileHandle, | |
| 49 | ||
| 50 | pub const Error = os.File.OpenError; | |
| 51 | }; | |
| 52 | ||
| 53 | pub const WriteFile = struct { | |
| 54 | /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265 | |
| 55 | path: []const u8, | |
| 56 | contents: []const u8, | |
| 57 | mode: os.File.Mode, | |
| 58 | result: Error!void, | |
| 59 | ||
| 60 | pub const Error = os.File.OpenError || os.File.WriteError; | |
| 61 | }; | |
| 62 | ||
| 63 | pub const Close = struct { | |
| 64 | fd: os.FileHandle, | |
| 65 | }; | |
| 66 | }; | |
| 67 | }; | |
| 68 | ||
| 69 | /// data - both the outer and inner references - must live until pwritev promise completes. | |
| 70 | pub async fn pwritev(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: []const []const u8) !void { | |
| 71 | //const data_dupe = try mem.dupe(loop.allocator, []const u8, data); | |
| 72 | //defer loop.allocator.free(data_dupe); | |
| 73 | ||
| 74 | // workaround for https://github.com/ziglang/zig/issues/1194 | |
| 75 | var my_handle: promise = undefined; | |
| 76 | suspend |p| { | |
| 77 | my_handle = p; | |
| 78 | resume p; | |
| 79 | } | |
| 80 | ||
| 81 | var req_node = RequestNode{ | |
| 82 | .next = undefined, | |
| 83 | .data = Request{ | |
| 84 | .msg = Request.Msg{ | |
| 85 | .PWriteV = Request.Msg.PWriteV{ | |
| 86 | .fd = fd, | |
| 87 | .data = data, | |
| 88 | .offset = offset, | |
| 89 | .result = undefined, | |
| 90 | }, | |
| 91 | }, | |
| 92 | .finish = Request.Finish{ | |
| 93 | .TickNode = event.Loop.NextTickNode{ | |
| 94 | .next = undefined, | |
| 95 | .data = my_handle, | |
| 96 | }, | |
| 97 | }, | |
| 98 | }, | |
| 99 | }; | |
| 100 | ||
| 101 | suspend |_| { | |
| 102 | loop.linuxFsRequest(&req_node); | |
| 103 | } | |
| 104 | ||
| 105 | return req_node.data.msg.PWriteV.result; | |
| 106 | } | |
| 107 | ||
| 108 | /// data - just the inner references - must live until pwritev promise completes. | |
| 109 | pub async fn preadv(loop: *event.Loop, fd: os.FileHandle, offset: usize, data: []const []u8) !usize { | |
| 110 | //const data_dupe = try mem.dupe(loop.allocator, []const u8, data); | |
| 111 | //defer loop.allocator.free(data_dupe); | |
| 112 | ||
| 113 | // workaround for https://github.com/ziglang/zig/issues/1194 | |
| 114 | var my_handle: promise = undefined; | |
| 115 | suspend |p| { | |
| 116 | my_handle = p; | |
| 117 | resume p; | |
| 118 | } | |
| 119 | ||
| 120 | const iovecs = try loop.allocator.alloc(os.linux.iovec, data.len); | |
| 121 | defer loop.allocator.free(iovecs); | |
| 122 | ||
| 123 | for (data) |buf, i| { | |
| 124 | iovecs[i] = os.linux.iovec{ | |
| 125 | .iov_base = buf.ptr, | |
| 126 | .iov_len = buf.len, | |
| 127 | }; | |
| 128 | } | |
| 129 | ||
| 130 | var req_node = RequestNode{ | |
| 131 | .next = undefined, | |
| 132 | .data = Request{ | |
| 133 | .msg = Request.Msg{ | |
| 134 | .PReadV = Request.Msg.PReadV{ | |
| 135 | .fd = fd, | |
| 136 | .iov = iovecs, | |
| 137 | .offset = offset, | |
| 138 | .result = undefined, | |
| 139 | }, | |
| 140 | }, | |
| 141 | .finish = Request.Finish{ | |
| 142 | .TickNode = event.Loop.NextTickNode{ | |
| 143 | .next = undefined, | |
| 144 | .data = my_handle, | |
| 145 | }, | |
| 146 | }, | |
| 147 | }, | |
| 148 | }; | |
| 149 | ||
| 150 | suspend |_| { | |
| 151 | loop.linuxFsRequest(&req_node); | |
| 152 | } | |
| 153 | ||
| 154 | return req_node.data.msg.PReadV.result; | |
| 155 | } | |
| 156 | ||
| 157 | pub async fn openRead(loop: *event.Loop, path: []const u8) os.File.OpenError!os.FileHandle { | |
| 158 | // workaround for https://github.com/ziglang/zig/issues/1194 | |
| 159 | var my_handle: promise = undefined; | |
| 160 | suspend |p| { | |
| 161 | my_handle = p; | |
| 162 | resume p; | |
| 163 | } | |
| 164 | ||
| 165 | var req_node = RequestNode{ | |
| 166 | .next = undefined, | |
| 167 | .data = Request{ | |
| 168 | .msg = Request.Msg{ | |
| 169 | .OpenRead = Request.Msg.OpenRead{ | |
| 170 | .path = path, | |
| 171 | .result = undefined, | |
| 172 | }, | |
| 173 | }, | |
| 174 | .finish = Request.Finish{ | |
| 175 | .TickNode = event.Loop.NextTickNode{ | |
| 176 | .next = undefined, | |
| 177 | .data = my_handle, | |
| 178 | }, | |
| 179 | }, | |
| 180 | }, | |
| 181 | }; | |
| 182 | ||
| 183 | suspend |_| { | |
| 184 | loop.linuxFsRequest(&req_node); | |
| 185 | } | |
| 186 | ||
| 187 | return req_node.data.msg.OpenRead.result; | |
| 188 | } | |
| 189 | ||
| 190 | /// This abstraction helps to close file handles in defer expressions | |
| 191 | /// without suspending. Start a CloseOperation before opening a file. | |
| 192 | pub const CloseOperation = struct { | |
| 193 | loop: *event.Loop, | |
| 194 | have_fd: bool, | |
| 195 | close_req_node: RequestNode, | |
| 196 | ||
| 197 | pub fn create(loop: *event.Loop) (error{OutOfMemory}!*CloseOperation) { | |
| 198 | const self = try loop.allocator.createOne(CloseOperation); | |
| 199 | self.* = CloseOperation{ | |
| 200 | .loop = loop, | |
| 201 | .have_fd = false, | |
| 202 | .close_req_node = RequestNode{ | |
| 203 | .next = undefined, | |
| 204 | .data = Request{ | |
| 205 | .msg = Request.Msg{ | |
| 206 | .Close = Request.Msg.Close{ .fd = undefined }, | |
| 207 | }, | |
| 208 | .finish = Request.Finish{ .DeallocCloseOperation = self }, | |
| 209 | }, | |
| 210 | }, | |
| 211 | }; | |
| 212 | return self; | |
| 213 | } | |
| 214 | ||
| 215 | /// Defer this after creating. | |
| 216 | pub fn deinit(self: *CloseOperation) void { | |
| 217 | if (self.have_fd) { | |
| 218 | self.loop.linuxFsRequest(&self.close_req_node); | |
| 219 | } else { | |
| 220 | self.loop.allocator.destroy(self); | |
| 221 | } | |
| 222 | } | |
| 223 | ||
| 224 | pub fn setHandle(self: *CloseOperation, handle: os.FileHandle) void { | |
| 225 | self.close_req_node.data.msg.Close.fd = handle; | |
| 226 | self.have_fd = true; | |
| 227 | } | |
| 228 | }; | |
| 229 | ||
| 230 | /// contents must remain alive until writeFile completes. | |
| 231 | pub async fn writeFile(loop: *event.Loop, path: []const u8, contents: []const u8) !void { | |
| 232 | return await (async writeFileMode(loop, path, contents, os.File.default_mode) catch unreachable); | |
| 233 | } | |
| 234 | ||
| 235 | /// contents must remain alive until writeFile completes. | |
| 236 | pub async fn writeFileMode(loop: *event.Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void { | |
| 237 | // workaround for https://github.com/ziglang/zig/issues/1194 | |
| 238 | var my_handle: promise = undefined; | |
| 239 | suspend |p| { | |
| 240 | my_handle = p; | |
| 241 | resume p; | |
| 242 | } | |
| 243 | ||
| 244 | const path_with_null = try std.cstr.addNullByte(loop.allocator, path); | |
| 245 | defer loop.allocator.free(path_with_null); | |
| 246 | ||
| 247 | var req_node = RequestNode{ | |
| 248 | .next = undefined, | |
| 249 | .data = Request{ | |
| 250 | .msg = Request.Msg{ | |
| 251 | .WriteFile = Request.Msg.WriteFile{ | |
| 252 | .path = path_with_null[0..path.len], | |
| 253 | .contents = contents, | |
| 254 | .mode = mode, | |
| 255 | .result = undefined, | |
| 256 | }, | |
| 257 | }, | |
| 258 | .finish = Request.Finish{ | |
| 259 | .TickNode = event.Loop.NextTickNode{ | |
| 260 | .next = undefined, | |
| 261 | .data = my_handle, | |
| 262 | }, | |
| 263 | }, | |
| 264 | }, | |
| 265 | }; | |
| 266 | ||
| 267 | suspend |_| { | |
| 268 | loop.linuxFsRequest(&req_node); | |
| 269 | } | |
| 270 | ||
| 271 | return req_node.data.msg.WriteFile.result; | |
| 272 | } | |
| 273 | ||
| 274 | /// The promise resumes when the last data has been confirmed written, but before the file handle | |
| 275 | /// is closed. | |
| 276 | pub async fn readFile(loop: *event.Loop, file_path: []const u8, max_size: usize) ![]u8 { | |
| 277 | var close_op = try CloseOperation.create(loop); | |
| 278 | defer close_op.deinit(); | |
| 279 | ||
| 280 | const path_with_null = try std.cstr.addNullByte(loop.allocator, file_path); | |
| 281 | defer loop.allocator.free(path_with_null); | |
| 282 | ||
| 283 | const fd = try await (async openRead(loop, path_with_null[0..file_path.len]) catch unreachable); | |
| 284 | close_op.setHandle(fd); | |
| 285 | ||
| 286 | var list = std.ArrayList(u8).init(loop.allocator); | |
| 287 | defer list.deinit(); | |
| 288 | ||
| 289 | while (true) { | |
| 290 | try list.ensureCapacity(list.len + os.page_size); | |
| 291 | const buf = list.items[list.len..]; | |
| 292 | const buf_array = [][]u8{buf}; | |
| 293 | const amt = try await (async preadv(loop, fd, list.len, buf_array) catch unreachable); | |
| 294 | list.len += amt; | |
| 295 | if (amt < buf.len) { | |
| 296 | return list.toOwnedSlice(); | |
| 297 | } | |
| 298 | } | |
| 299 | } | |
| 300 | ||
| 301 | const test_tmp_dir = "std_event_fs_test"; | |
| 302 | ||
| 303 | test "write a file, watch it, write it again" { | |
| 304 | var da = std.heap.DirectAllocator.init(); | |
| 305 | defer da.deinit(); | |
| 306 | ||
| 307 | const allocator = &da.allocator; | |
| 308 | ||
| 309 | // TODO move this into event loop too | |
| 310 | try os.makePath(allocator, test_tmp_dir); | |
| 311 | defer os.deleteTree(allocator, test_tmp_dir) catch {}; | |
| 312 | ||
| 313 | var loop: event.Loop = undefined; | |
| 314 | try loop.initMultiThreaded(allocator); | |
| 315 | defer loop.deinit(); | |
| 316 | ||
| 317 | var result: error!void = undefined; | |
| 318 | const handle = try async<allocator> testFsWatchCantFail(&loop, &result); | |
| 319 | defer cancel handle; | |
| 320 | ||
| 321 | loop.run(); | |
| 322 | return result; | |
| 323 | } | |
| 324 | ||
| 325 | async fn testFsWatchCantFail(loop: *event.Loop, result: *(error!void)) void { | |
| 326 | result.* = await async testFsWatch(loop) catch unreachable; | |
| 327 | } | |
| 328 | ||
| 329 | async fn testFsWatch(loop: *event.Loop) !void { | |
| 330 | const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt"); | |
| 331 | defer loop.allocator.free(file_path); | |
| 332 | ||
| 333 | const contents = | |
| 334 | \\line 1 | |
| 335 | \\line 2 | |
| 336 | ; | |
| 337 | ||
| 338 | // first just write then read the file | |
| 339 | try await try async writeFile(loop, file_path, contents); | |
| 340 | ||
| 341 | const read_contents = try await try async readFile(loop, file_path, 1024 * 1024); | |
| 342 | assert(mem.eql(u8, read_contents, contents)); | |
| 343 | } |
std/event/loop.zig+136-51| ... | ... | @@ -2,10 +2,12 @@ const std = @import("../index.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const mem = std.mem; |
| 5 | const posix = std.os.posix; | |
| 6 | const windows = std.os.windows; | |
| 7 | 5 | const AtomicRmwOp = builtin.AtomicRmwOp; |
| 8 | 6 | const AtomicOrder = builtin.AtomicOrder; |
| 7 | const fs = std.event.fs; | |
| 8 | const os = std.os; | |
| 9 | const posix = os.posix; | |
| 10 | const windows = os.windows; | |
| 9 | 11 | |
| 10 | 12 | pub const Loop = struct { |
| 11 | 13 | allocator: *mem.Allocator, |
| ... | ... | @@ -13,7 +15,7 @@ pub const Loop = struct { |
| 13 | 15 | os_data: OsData, |
| 14 | 16 | final_resume_node: ResumeNode, |
| 15 | 17 | pending_event_count: usize, |
| 16 | extra_threads: []*std.os.Thread, | |
| 18 | extra_threads: []*os.Thread, | |
| 17 | 19 | |
| 18 | 20 | // pre-allocated eventfds. all permanently active. |
| 19 | 21 | // this is how we send promises to be resumed on other threads. |
| ... | ... | @@ -65,7 +67,7 @@ pub const Loop = struct { |
| 65 | 67 | /// TODO copy elision / named return values so that the threads referencing *Loop |
| 66 | 68 | /// have the correct pointer value. |
| 67 | 69 | pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { |
| 68 | const core_count = try std.os.cpuCount(allocator); | |
| 70 | const core_count = try os.cpuCount(allocator); | |
| 69 | 71 | return self.initInternal(allocator, core_count); |
| 70 | 72 | } |
| 71 | 73 | |
| ... | ... | @@ -92,7 +94,7 @@ pub const Loop = struct { |
| 92 | 94 | ); |
| 93 | 95 | errdefer self.allocator.free(self.eventfd_resume_nodes); |
| 94 | 96 | |
| 95 | self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count); | |
| 97 | self.extra_threads = try self.allocator.alloc(*os.Thread, extra_thread_count); | |
| 96 | 98 | errdefer self.allocator.free(self.extra_threads); |
| 97 | 99 | |
| 98 | 100 | try self.initOsData(extra_thread_count); |
| ... | ... | @@ -104,17 +106,34 @@ pub const Loop = struct { |
| 104 | 106 | self.allocator.free(self.extra_threads); |
| 105 | 107 | } |
| 106 | 108 | |
| 107 | const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || | |
| 108 | std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError || | |
| 109 | std.os.WindowsCreateIoCompletionPortError; | |
| 109 | const InitOsDataError = os.LinuxEpollCreateError || mem.Allocator.Error || os.LinuxEventFdError || | |
| 110 | os.SpawnThreadError || os.LinuxEpollCtlError || os.BsdKEventError || | |
| 111 | os.WindowsCreateIoCompletionPortError; | |
| 110 | 112 | |
| 111 | 113 | const wakeup_bytes = []u8{0x1} ** 8; |
| 112 | 114 | |
| 113 | 115 | fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { |
| 114 | 116 | switch (builtin.os) { |
| 115 | 117 | builtin.Os.linux => { |
| 118 | self.os_data.fs_queue = std.atomic.Queue(fs.Request).init(); | |
| 119 | self.os_data.fs_queue_len = 0; | |
| 120 | // we need another thread for the file system because Linux does not have an async | |
| 121 | // file system I/O API. | |
| 122 | self.os_data.fs_end_request = fs.RequestNode{ | |
| 123 | .next = undefined, | |
| 124 | .data = fs.Request{ | |
| 125 | .msg = fs.Request.Msg.End, | |
| 126 | .finish = fs.Request.Finish.NoAction, | |
| 127 | }, | |
| 128 | }; | |
| 129 | self.os_data.fs_thread = try os.spawnThread(self, linuxFsRun); | |
| 116 | 130 | errdefer { |
| 117 | while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); | |
| 131 | self.linuxFsRequest(&self.os_data.fs_end_request); | |
| 132 | self.os_data.fs_thread.wait(); | |
| 133 | } | |
| 134 | ||
| 135 | errdefer { | |
| 136 | while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd); | |
| 118 | 137 | } |
| 119 | 138 | for (self.eventfd_resume_nodes) |*eventfd_node| { |
| 120 | 139 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| ... | ... | @@ -123,7 +142,7 @@ pub const Loop = struct { |
| 123 | 142 | .id = ResumeNode.Id.EventFd, |
| 124 | 143 | .handle = undefined, |
| 125 | 144 | }, |
| 126 | .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), | |
| 145 | .eventfd = try os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK), | |
| 127 | 146 | .epoll_op = posix.EPOLL_CTL_ADD, |
| 128 | 147 | }, |
| 129 | 148 | .next = undefined, |
| ... | ... | @@ -131,17 +150,17 @@ pub const Loop = struct { |
| 131 | 150 | self.available_eventfd_resume_nodes.push(eventfd_node); |
| 132 | 151 | } |
| 133 | 152 | |
| 134 | self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC); | |
| 135 | errdefer std.os.close(self.os_data.epollfd); | |
| 153 | self.os_data.epollfd = try os.linuxEpollCreate(posix.EPOLL_CLOEXEC); | |
| 154 | errdefer os.close(self.os_data.epollfd); | |
| 136 | 155 | |
| 137 | self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); | |
| 138 | errdefer std.os.close(self.os_data.final_eventfd); | |
| 156 | self.os_data.final_eventfd = try os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); | |
| 157 | errdefer os.close(self.os_data.final_eventfd); | |
| 139 | 158 | |
| 140 | 159 | self.os_data.final_eventfd_event = posix.epoll_event{ |
| 141 | 160 | .events = posix.EPOLLIN, |
| 142 | 161 | .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, |
| 143 | 162 | }; |
| 144 | try std.os.linuxEpollCtl( | |
| 163 | try os.linuxEpollCtl( | |
| 145 | 164 | self.os_data.epollfd, |
| 146 | 165 | posix.EPOLL_CTL_ADD, |
| 147 | 166 | self.os_data.final_eventfd, |
| ... | ... | @@ -151,19 +170,19 @@ pub const Loop = struct { |
| 151 | 170 | var extra_thread_index: usize = 0; |
| 152 | 171 | errdefer { |
| 153 | 172 | // writing 8 bytes to an eventfd cannot fail |
| 154 | std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; | |
| 173 | os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; | |
| 155 | 174 | while (extra_thread_index != 0) { |
| 156 | 175 | extra_thread_index -= 1; |
| 157 | 176 | self.extra_threads[extra_thread_index].wait(); |
| 158 | 177 | } |
| 159 | 178 | } |
| 160 | 179 | while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { |
| 161 | self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); | |
| 180 | self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun); | |
| 162 | 181 | } |
| 163 | 182 | }, |
| 164 | 183 | builtin.Os.macosx => { |
| 165 | self.os_data.kqfd = try std.os.bsdKQueue(); | |
| 166 | errdefer std.os.close(self.os_data.kqfd); | |
| 184 | self.os_data.kqfd = try os.bsdKQueue(); | |
| 185 | errdefer os.close(self.os_data.kqfd); | |
| 167 | 186 | |
| 168 | 187 | self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count); |
| 169 | 188 | errdefer self.allocator.free(self.os_data.kevents); |
| ... | ... | @@ -191,7 +210,7 @@ pub const Loop = struct { |
| 191 | 210 | }; |
| 192 | 211 | self.available_eventfd_resume_nodes.push(eventfd_node); |
| 193 | 212 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent); |
| 194 | _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); | |
| 213 | _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); | |
| 195 | 214 | eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE; |
| 196 | 215 | eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER; |
| 197 | 216 | // this one is for waiting for events |
| ... | ... | @@ -216,30 +235,30 @@ pub const Loop = struct { |
| 216 | 235 | .udata = @ptrToInt(&self.final_resume_node), |
| 217 | 236 | }; |
| 218 | 237 | const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent); |
| 219 | _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); | |
| 238 | _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null); | |
| 220 | 239 | self.os_data.final_kevent.flags = posix.EV_ENABLE; |
| 221 | 240 | self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER; |
| 222 | 241 | |
| 223 | 242 | var extra_thread_index: usize = 0; |
| 224 | 243 | errdefer { |
| 225 | _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable; | |
| 244 | _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable; | |
| 226 | 245 | while (extra_thread_index != 0) { |
| 227 | 246 | extra_thread_index -= 1; |
| 228 | 247 | self.extra_threads[extra_thread_index].wait(); |
| 229 | 248 | } |
| 230 | 249 | } |
| 231 | 250 | while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { |
| 232 | self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); | |
| 251 | self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun); | |
| 233 | 252 | } |
| 234 | 253 | }, |
| 235 | 254 | builtin.Os.windows => { |
| 236 | self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( | |
| 255 | self.os_data.io_port = try os.windowsCreateIoCompletionPort( | |
| 237 | 256 | windows.INVALID_HANDLE_VALUE, |
| 238 | 257 | null, |
| 239 | 258 | undefined, |
| 240 | 259 | undefined, |
| 241 | 260 | ); |
| 242 | errdefer std.os.close(self.os_data.io_port); | |
| 261 | errdefer os.close(self.os_data.io_port); | |
| 243 | 262 | |
| 244 | 263 | for (self.eventfd_resume_nodes) |*eventfd_node, i| { |
| 245 | 264 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| ... | ... | @@ -262,7 +281,7 @@ pub const Loop = struct { |
| 262 | 281 | while (i < extra_thread_index) : (i += 1) { |
| 263 | 282 | while (true) { |
| 264 | 283 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| 265 | std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; | |
| 284 | os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; | |
| 266 | 285 | break; |
| 267 | 286 | } |
| 268 | 287 | } |
| ... | ... | @@ -272,7 +291,7 @@ pub const Loop = struct { |
| 272 | 291 | } |
| 273 | 292 | } |
| 274 | 293 | while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { |
| 275 | self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun); | |
| 294 | self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun); | |
| 276 | 295 | } |
| 277 | 296 | }, |
| 278 | 297 | else => {}, |
| ... | ... | @@ -282,17 +301,17 @@ pub const Loop = struct { |
| 282 | 301 | fn deinitOsData(self: *Loop) void { |
| 283 | 302 | switch (builtin.os) { |
| 284 | 303 | builtin.Os.linux => { |
| 285 | std.os.close(self.os_data.final_eventfd); | |
| 286 | while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); | |
| 287 | std.os.close(self.os_data.epollfd); | |
| 304 | os.close(self.os_data.final_eventfd); | |
| 305 | while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd); | |
| 306 | os.close(self.os_data.epollfd); | |
| 288 | 307 | self.allocator.free(self.eventfd_resume_nodes); |
| 289 | 308 | }, |
| 290 | 309 | builtin.Os.macosx => { |
| 291 | 310 | self.allocator.free(self.os_data.kevents); |
| 292 | std.os.close(self.os_data.kqfd); | |
| 311 | os.close(self.os_data.kqfd); | |
| 293 | 312 | }, |
| 294 | 313 | builtin.Os.windows => { |
| 295 | std.os.close(self.os_data.io_port); | |
| 314 | os.close(self.os_data.io_port); | |
| 296 | 315 | }, |
| 297 | 316 | else => {}, |
| 298 | 317 | } |
| ... | ... | @@ -307,17 +326,17 @@ pub const Loop = struct { |
| 307 | 326 | try self.modFd( |
| 308 | 327 | fd, |
| 309 | 328 | posix.EPOLL_CTL_ADD, |
| 310 | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET, | |
| 329 | os.linux.EPOLLIN | os.linux.EPOLLOUT | os.linux.EPOLLET, | |
| 311 | 330 | resume_node, |
| 312 | 331 | ); |
| 313 | 332 | } |
| 314 | 333 | |
| 315 | 334 | pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void { |
| 316 | var ev = std.os.linux.epoll_event{ | |
| 335 | var ev = os.linux.epoll_event{ | |
| 317 | 336 | .events = events, |
| 318 | .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, | |
| 337 | .data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) }, | |
| 319 | 338 | }; |
| 320 | try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev); | |
| 339 | try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev); | |
| 321 | 340 | } |
| 322 | 341 | |
| 323 | 342 | pub fn removeFd(self: *Loop, fd: i32) void { |
| ... | ... | @@ -326,7 +345,7 @@ pub const Loop = struct { |
| 326 | 345 | } |
| 327 | 346 | |
| 328 | 347 | fn removeFdNoCounter(self: *Loop, fd: i32) void { |
| 329 | std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; | |
| 348 | os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {}; | |
| 330 | 349 | } |
| 331 | 350 | |
| 332 | 351 | pub async fn waitFd(self: *Loop, fd: i32) !void { |
| ... | ... | @@ -353,7 +372,7 @@ pub const Loop = struct { |
| 353 | 372 | builtin.Os.macosx => { |
| 354 | 373 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); |
| 355 | 374 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; |
| 356 | _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { | |
| 375 | _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch { | |
| 357 | 376 | self.next_tick_queue.unget(next_tick_node); |
| 358 | 377 | self.available_eventfd_resume_nodes.push(resume_stack_node); |
| 359 | 378 | return; |
| ... | ... | @@ -361,8 +380,8 @@ pub const Loop = struct { |
| 361 | 380 | }, |
| 362 | 381 | builtin.Os.linux => { |
| 363 | 382 | // the pending count is already accounted for |
| 364 | const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | | |
| 365 | std.os.linux.EPOLLET; | |
| 383 | const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT | | |
| 384 | os.linux.EPOLLET; | |
| 366 | 385 | self.modFd( |
| 367 | 386 | eventfd_node.eventfd, |
| 368 | 387 | eventfd_node.epoll_op, |
| ... | ... | @@ -379,7 +398,7 @@ pub const Loop = struct { |
| 379 | 398 | // the consumer code can decide whether to read the completion key. |
| 380 | 399 | // it has to do this for normal I/O, so we match that behavior here. |
| 381 | 400 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| 382 | std.os.windowsPostQueuedCompletionStatus( | |
| 401 | os.windowsPostQueuedCompletionStatus( | |
| 383 | 402 | self.os_data.io_port, |
| 384 | 403 | undefined, |
| 385 | 404 | eventfd_node.completion_key, |
| ... | ... | @@ -406,6 +425,9 @@ pub const Loop = struct { |
| 406 | 425 | self.finishOneEvent(); // the reference we start with |
| 407 | 426 | |
| 408 | 427 | self.workerRun(); |
| 428 | ||
| 429 | self.os_data.fs_thread.wait(); | |
| 430 | ||
| 409 | 431 | for (self.extra_threads) |extra_thread| { |
| 410 | 432 | extra_thread.wait(); |
| 411 | 433 | } |
| ... | ... | @@ -453,15 +475,16 @@ pub const Loop = struct { |
| 453 | 475 | // cause all the threads to stop |
| 454 | 476 | switch (builtin.os) { |
| 455 | 477 | builtin.Os.linux => { |
| 478 | self.linuxFsRequest(&self.os_data.fs_end_request); | |
| 456 | 479 | // writing 8 bytes to an eventfd cannot fail |
| 457 | std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; | |
| 480 | os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; | |
| 458 | 481 | return; |
| 459 | 482 | }, |
| 460 | 483 | builtin.Os.macosx => { |
| 461 | 484 | const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); |
| 462 | 485 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; |
| 463 | 486 | // cannot fail because we already added it and this just enables it |
| 464 | _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; | |
| 487 | _ = os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable; | |
| 465 | 488 | return; |
| 466 | 489 | }, |
| 467 | 490 | builtin.Os.windows => { |
| ... | ... | @@ -469,7 +492,7 @@ pub const Loop = struct { |
| 469 | 492 | while (i < self.extra_threads.len + 1) : (i += 1) { |
| 470 | 493 | while (true) { |
| 471 | 494 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| 472 | std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; | |
| 495 | os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue; | |
| 473 | 496 | break; |
| 474 | 497 | } |
| 475 | 498 | } |
| ... | ... | @@ -492,8 +515,8 @@ pub const Loop = struct { |
| 492 | 515 | switch (builtin.os) { |
| 493 | 516 | builtin.Os.linux => { |
| 494 | 517 | // only process 1 event so we don't steal from other threads |
| 495 | var events: [1]std.os.linux.epoll_event = undefined; | |
| 496 | const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); | |
| 518 | var events: [1]os.linux.epoll_event = undefined; | |
| 519 | const count = os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); | |
| 497 | 520 | for (events[0..count]) |ev| { |
| 498 | 521 | const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); |
| 499 | 522 | const handle = resume_node.handle; |
| ... | ... | @@ -516,7 +539,7 @@ pub const Loop = struct { |
| 516 | 539 | }, |
| 517 | 540 | builtin.Os.macosx => { |
| 518 | 541 | var eventlist: [1]posix.Kevent = undefined; |
| 519 | const count = std.os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable; | |
| 542 | const count = os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable; | |
| 520 | 543 | for (eventlist[0..count]) |ev| { |
| 521 | 544 | const resume_node = @intToPtr(*ResumeNode, ev.udata); |
| 522 | 545 | const handle = resume_node.handle; |
| ... | ... | @@ -541,9 +564,9 @@ pub const Loop = struct { |
| 541 | 564 | while (true) { |
| 542 | 565 | var nbytes: windows.DWORD = undefined; |
| 543 | 566 | var overlapped: ?*windows.OVERLAPPED = undefined; |
| 544 | switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { | |
| 545 | std.os.WindowsWaitResult.Aborted => return, | |
| 546 | std.os.WindowsWaitResult.Normal => {}, | |
| 567 | switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { | |
| 568 | os.WindowsWaitResult.Aborted => return, | |
| 569 | os.WindowsWaitResult.Normal => {}, | |
| 547 | 570 | } |
| 548 | 571 | if (overlapped != null) break; |
| 549 | 572 | } |
| ... | ... | @@ -569,11 +592,73 @@ pub const Loop = struct { |
| 569 | 592 | } |
| 570 | 593 | } |
| 571 | 594 | |
| 595 | fn linuxFsRequest(self: *Loop, request_node: *fs.RequestNode) void { | |
| 596 | _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | |
| 597 | self.os_data.fs_queue.put(request_node); | |
| 598 | _ = @atomicRmw(i32, &self.os_data.fs_queue_len, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); // let this wrap | |
| 599 | const rc = os.linux.futex_wake(@ptrToInt(&self.os_data.fs_queue_len), os.linux.FUTEX_WAKE, 1); | |
| 600 | switch (os.linux.getErrno(rc)) { | |
| 601 | 0 => {}, | |
| 602 | posix.EINVAL => unreachable, | |
| 603 | else => unreachable, | |
| 604 | } | |
| 605 | } | |
| 606 | ||
| 607 | fn linuxFsRun(self: *Loop) void { | |
| 608 | var processed_count: i32 = 0; // we let this wrap | |
| 609 | while (true) { | |
| 610 | while (self.os_data.fs_queue.get()) |node| { | |
| 611 | processed_count +%= 1; | |
| 612 | switch (node.data.msg) { | |
| 613 | @TagType(fs.Request.Msg).PWriteV => @panic("TODO"), | |
| 614 | @TagType(fs.Request.Msg).PReadV => |*msg| { | |
| 615 | msg.result = os.posix_preadv(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset); | |
| 616 | }, | |
| 617 | @TagType(fs.Request.Msg).OpenRead => |*msg| { | |
| 618 | const flags = posix.O_LARGEFILE | posix.O_RDONLY; | |
| 619 | msg.result = os.posixOpenC(msg.path.ptr, flags, 0); | |
| 620 | }, | |
| 621 | @TagType(fs.Request.Msg).Close => |*msg| os.close(msg.fd), | |
| 622 | @TagType(fs.Request.Msg).WriteFile => |*msg| blk: { | |
| 623 | const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | | |
| 624 | posix.O_CLOEXEC | posix.O_TRUNC; | |
| 625 | const fd = os.posixOpenC(msg.path.ptr, flags, msg.mode) catch |err| { | |
| 626 | msg.result = err; | |
| 627 | break :blk; | |
| 628 | }; | |
| 629 | defer os.close(fd); | |
| 630 | msg.result = os.posixWrite(fd, msg.contents); | |
| 631 | }, | |
| 632 | @TagType(fs.Request.Msg).End => return, | |
| 633 | } | |
| 634 | switch (node.data.finish) { | |
| 635 | @TagType(fs.Request.Finish).TickNode => |*tick_node| self.onNextTick(tick_node), | |
| 636 | @TagType(fs.Request.Finish).DeallocCloseOperation => |close_op| { | |
| 637 | self.allocator.destroy(close_op); | |
| 638 | }, | |
| 639 | @TagType(fs.Request.Finish).NoAction => {}, | |
| 640 | } | |
| 641 | self.finishOneEvent(); | |
| 642 | } | |
| 643 | const rc = os.linux.futex_wait(@ptrToInt(&self.os_data.fs_queue_len), os.linux.FUTEX_WAIT, processed_count, null); | |
| 644 | switch (os.linux.getErrno(rc)) { | |
| 645 | 0 => continue, | |
| 646 | posix.EINTR => continue, | |
| 647 | posix.EAGAIN => continue, | |
| 648 | else => unreachable, | |
| 649 | } | |
| 650 | } | |
| 651 | } | |
| 652 | ||
| 572 | 653 | const OsData = switch (builtin.os) { |
| 573 | 654 | builtin.Os.linux => struct { |
| 574 | 655 | epollfd: i32, |
| 575 | 656 | final_eventfd: i32, |
| 576 | final_eventfd_event: std.os.linux.epoll_event, | |
| 657 | final_eventfd_event: os.linux.epoll_event, | |
| 658 | fs_thread: *os.Thread, | |
| 659 | fs_queue_len: i32, // we let this wrap | |
| 660 | fs_queue: std.atomic.Queue(fs.Request), | |
| 661 | fs_end_request: fs.RequestNode, | |
| 577 | 662 | }, |
| 578 | 663 | builtin.Os.macosx => MacOsData, |
| 579 | 664 | builtin.Os.windows => struct { |
std/io.zig+7-9| ... | ... | @@ -415,13 +415,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ |
| 415 | 415 | self.at_end = (read < left); |
| 416 | 416 | return pos + read; |
| 417 | 417 | } |
| 418 | ||
| 419 | 418 | }; |
| 420 | 419 | } |
| 421 | 420 | |
| 422 | 421 | pub const SliceInStream = struct { |
| 423 | 422 | const Self = this; |
| 424 | pub const Error = error { }; | |
| 423 | pub const Error = error{}; | |
| 425 | 424 | pub const Stream = InStream(Error); |
| 426 | 425 | |
| 427 | 426 | pub stream: Stream, |
| ... | ... | @@ -481,13 +480,12 @@ pub const SliceOutStream = struct { |
| 481 | 480 | |
| 482 | 481 | assert(self.pos <= self.slice.len); |
| 483 | 482 | |
| 484 | const n = | |
| 485 | if (self.pos + bytes.len <= self.slice.len) | |
| 486 | bytes.len | |
| 487 | else | |
| 488 | self.slice.len - self.pos; | |
| 483 | const n = if (self.pos + bytes.len <= self.slice.len) | |
| 484 | bytes.len | |
| 485 | else | |
| 486 | self.slice.len - self.pos; | |
| 489 | 487 | |
| 490 | std.mem.copy(u8, self.slice[self.pos..self.pos + n], bytes[0..n]); | |
| 488 | std.mem.copy(u8, self.slice[self.pos .. self.pos + n], bytes[0..n]); | |
| 491 | 489 | self.pos += n; |
| 492 | 490 | |
| 493 | 491 | if (n < bytes.len) { |
| ... | ... | @@ -586,7 +584,7 @@ pub const BufferedAtomicFile = struct { |
| 586 | 584 | }); |
| 587 | 585 | errdefer allocator.destroy(self); |
| 588 | 586 | |
| 589 | self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.default_file_mode); | |
| 587 | self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode); | |
| 590 | 588 | errdefer self.atomic_file.deinit(); |
| 591 | 589 | |
| 592 | 590 | self.file_stream = FileOutStream.init(&self.atomic_file.file); |
std/os/file.zig+29-10| ... | ... | @@ -15,10 +15,21 @@ pub const File = struct { |
| 15 | 15 | /// The OS-specific file descriptor or file handle. |
| 16 | 16 | handle: os.FileHandle, |
| 17 | 17 | |
| 18 | pub const Mode = switch (builtin.os) { | |
| 19 | Os.windows => void, | |
| 20 | else => u32, | |
| 21 | }; | |
| 22 | ||
| 23 | pub const default_mode = switch (builtin.os) { | |
| 24 | Os.windows => {}, | |
| 25 | else => 0o666, | |
| 26 | }; | |
| 27 | ||
| 18 | 28 | pub const OpenError = os.WindowsOpenError || os.PosixOpenError; |
| 19 | 29 | |
| 20 | 30 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. |
| 21 | 31 | /// Call close to clean up. |
| 32 | /// TODO deprecated, just use open | |
| 22 | 33 | pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File { |
| 23 | 34 | if (is_posix) { |
| 24 | 35 | const flags = posix.O_LARGEFILE | posix.O_RDONLY; |
| ... | ... | @@ -39,16 +50,18 @@ pub const File = struct { |
| 39 | 50 | } |
| 40 | 51 | } |
| 41 | 52 | |
| 42 | /// Calls `openWriteMode` with os.default_file_mode for the mode. | |
| 53 | /// Calls `openWriteMode` with os.File.default_mode for the mode. | |
| 54 | /// TODO deprecated, just use open | |
| 43 | 55 | pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File { |
| 44 | return openWriteMode(allocator, path, os.default_file_mode); | |
| 56 | return openWriteMode(allocator, path, os.File.default_mode); | |
| 45 | 57 | } |
| 46 | 58 | |
| 47 | 59 | /// If the path does not exist it will be created. |
| 48 | 60 | /// If a file already exists in the destination it will be truncated. |
| 49 | 61 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. |
| 50 | 62 | /// Call close to clean up. |
| 51 | pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { | |
| 63 | /// TODO deprecated, just use open | |
| 64 | pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File { | |
| 52 | 65 | if (is_posix) { |
| 53 | 66 | const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; |
| 54 | 67 | const fd = try os.posixOpen(allocator, path, flags, file_mode); |
| ... | ... | @@ -72,7 +85,8 @@ pub const File = struct { |
| 72 | 85 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists |
| 73 | 86 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. |
| 74 | 87 | /// Call close to clean up. |
| 75 | pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File { | |
| 88 | /// TODO deprecated, just use open | |
| 89 | pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: Mode) OpenError!File { | |
| 76 | 90 | if (is_posix) { |
| 77 | 91 | const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL; |
| 78 | 92 | const fd = try os.posixOpen(allocator, path, flags, file_mode); |
| ... | ... | @@ -282,7 +296,7 @@ pub const File = struct { |
| 282 | 296 | Unexpected, |
| 283 | 297 | }; |
| 284 | 298 | |
| 285 | pub fn mode(self: *File) ModeError!os.FileMode { | |
| 299 | pub fn mode(self: *File) ModeError!Mode { | |
| 286 | 300 | if (is_posix) { |
| 287 | 301 | var stat: posix.Stat = undefined; |
| 288 | 302 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); |
| ... | ... | @@ -296,7 +310,7 @@ pub const File = struct { |
| 296 | 310 | |
| 297 | 311 | // TODO: we should be able to cast u16 to ModeError!u32, making this |
| 298 | 312 | // explicit cast not necessary |
| 299 | return os.FileMode(stat.mode); | |
| 313 | return Mode(stat.mode); | |
| 300 | 314 | } else if (is_windows) { |
| 301 | 315 | return {}; |
| 302 | 316 | } else { |
| ... | ... | @@ -305,9 +319,11 @@ pub const File = struct { |
| 305 | 319 | } |
| 306 | 320 | |
| 307 | 321 | pub const ReadError = error{ |
| 308 | BadFd, | |
| 309 | Io, | |
| 322 | FileClosed, | |
| 323 | InputOutput, | |
| 310 | 324 | IsDir, |
| 325 | WouldBlock, | |
| 326 | SystemResources, | |
| 311 | 327 | |
| 312 | 328 | Unexpected, |
| 313 | 329 | }; |
| ... | ... | @@ -323,9 +339,12 @@ pub const File = struct { |
| 323 | 339 | posix.EINTR => continue, |
| 324 | 340 | posix.EINVAL => unreachable, |
| 325 | 341 | posix.EFAULT => unreachable, |
| 326 | posix.EBADF => return error.BadFd, | |
| 327 | posix.EIO => return error.Io, | |
| 342 | posix.EAGAIN => return error.WouldBlock, | |
| 343 | posix.EBADF => return error.FileClosed, | |
| 344 | posix.EIO => return error.InputOutput, | |
| 328 | 345 | posix.EISDIR => return error.IsDir, |
| 346 | posix.ENOBUFS => return error.SystemResources, | |
| 347 | posix.ENOMEM => return error.SystemResources, | |
| 329 | 348 | else => return os.unexpectedErrorPosix(read_err), |
| 330 | 349 | } |
| 331 | 350 | } |
std/os/index.zig+22-12| ... | ... | @@ -38,16 +38,6 @@ pub const path = @import("path.zig"); |
| 38 | 38 | pub const File = @import("file.zig").File; |
| 39 | 39 | pub const time = @import("time.zig"); |
| 40 | 40 | |
| 41 | pub const FileMode = switch (builtin.os) { | |
| 42 | Os.windows => void, | |
| 43 | else => u32, | |
| 44 | }; | |
| 45 | ||
| 46 | pub const default_file_mode = switch (builtin.os) { | |
| 47 | Os.windows => {}, | |
| 48 | else => 0o666, | |
| 49 | }; | |
| 50 | ||
| 51 | 41 | pub const page_size = 4 * 1024; |
| 52 | 42 | |
| 53 | 43 | pub const UserInfo = @import("get_user_id.zig").UserInfo; |
| ... | ... | @@ -256,6 +246,26 @@ pub fn posixRead(fd: i32, buf: []u8) !void { |
| 256 | 246 | } |
| 257 | 247 | } |
| 258 | 248 | |
| 249 | pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) !usize { | |
| 250 | while (true) { | |
| 251 | const rc = posix.preadv(fd, iov, count, offset); | |
| 252 | const err = posix.getErrno(rc); | |
| 253 | switch (err) { | |
| 254 | 0 => return rc, | |
| 255 | posix.EINTR => continue, | |
| 256 | posix.EINVAL => unreachable, | |
| 257 | posix.EFAULT => unreachable, | |
| 258 | posix.EAGAIN => return error.WouldBlock, | |
| 259 | posix.EBADF => return error.FileClosed, | |
| 260 | posix.EIO => return error.InputOutput, | |
| 261 | posix.EISDIR => return error.IsDir, | |
| 262 | posix.ENOBUFS => return error.SystemResources, | |
| 263 | posix.ENOMEM => return error.SystemResources, | |
| 264 | else => return unexpectedErrorPosix(err), | |
| 265 | } | |
| 266 | } | |
| 267 | } | |
| 268 | ||
| 259 | 269 | pub const PosixWriteError = error{ |
| 260 | 270 | WouldBlock, |
| 261 | 271 | FileClosed, |
| ... | ... | @@ -853,7 +863,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con |
| 853 | 863 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is |
| 854 | 864 | /// merged and readily available, |
| 855 | 865 | /// there is a possibility of power loss or application termination leaving temporary files present |
| 856 | pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void { | |
| 866 | pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { | |
| 857 | 867 | var in_file = try os.File.openRead(allocator, source_path); |
| 858 | 868 | defer in_file.close(); |
| 859 | 869 | |
| ... | ... | @@ -879,7 +889,7 @@ pub const AtomicFile = struct { |
| 879 | 889 | |
| 880 | 890 | /// dest_path must remain valid for the lifetime of AtomicFile |
| 881 | 891 | /// call finish to atomically replace dest_path with contents |
| 882 | pub fn init(allocator: *Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile { | |
| 892 | pub fn init(allocator: *Allocator, dest_path: []const u8, mode: File.Mode) !AtomicFile { | |
| 883 | 893 | const dirname = os.path.dirname(dest_path); |
| 884 | 894 | |
| 885 | 895 | var rand_buf: [12]u8 = undefined; |
std/os/linux/index.zig+8| ... | ... | @@ -692,6 +692,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us |
| 692 | 692 | return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout)); |
| 693 | 693 | } |
| 694 | 694 | |
| 695 | pub fn futex_wake(uaddr: usize, futex_op: u32, val: i32) usize { | |
| 696 | return syscall3(SYS_futex, uaddr, futex_op, @bitCast(u32, val)); | |
| 697 | } | |
| 698 | ||
| 695 | 699 | pub fn getcwd(buf: [*]u8, size: usize) usize { |
| 696 | 700 | return syscall2(SYS_getcwd, @ptrToInt(buf), size); |
| 697 | 701 | } |
| ... | ... | @@ -742,6 +746,10 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize { |
| 742 | 746 | return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count); |
| 743 | 747 | } |
| 744 | 748 | |
| 749 | pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { | |
| 750 | return syscall4(SYS_preadv, @intCast(usize, fd), @ptrToInt(iov), count, offset); | |
| 751 | } | |
| 752 | ||
| 745 | 753 | // TODO https://github.com/ziglang/zig/issues/265 |
| 746 | 754 | pub fn rmdir(path: [*]const u8) usize { |
| 747 | 755 | return syscall1(SYS_rmdir, @ptrToInt(path)); |