| 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,6 +460,7 @@ set(ZIG_STD_FILES |
| 460 | "empty.zig" | 460 | "empty.zig" |
| 461 | "event.zig" | 461 | "event.zig" |
| 462 | "event/channel.zig" | 462 | "event/channel.zig" |
| 463 | "event/fs.zig" | ||
| 463 | "event/future.zig" | 464 | "event/future.zig" |
| 464 | "event/group.zig" | 465 | "event/group.zig" |
| 465 | "event/lock.zig" | 466 | "event/lock.zig" |
std/build.zig+2-2| ... | @@ -603,10 +603,10 @@ pub const Builder = struct { | ... | @@ -603,10 +603,10 @@ pub const Builder = struct { |
| 603 | } | 603 | } |
| 604 | 604 | ||
| 605 | fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { | 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 | if (self.verbose) { | 610 | if (self.verbose) { |
| 611 | warn("cp {} {}\n", source_path, dest_path); | 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,14 +672,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type |
| 672 | 672 | ||
| 673 | const ParseFormValueError = error{ | 673 | const ParseFormValueError = error{ |
| 674 | EndOfStream, | 674 | EndOfStream, |
| 675 | Io, | ||
| 676 | BadFd, | ||
| 677 | Unexpected, | ||
| 678 | InvalidDebugInfo, | 675 | InvalidDebugInfo, |
| 679 | EndOfFile, | 676 | EndOfFile, |
| 680 | IsDir, | ||
| 681 | OutOfMemory, | 677 | OutOfMemory, |
| 682 | }; | 678 | } || std.os.File.ReadError; |
| 683 | 679 | ||
| 684 | fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { | 680 | fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { |
| 685 | return switch (form_id) { | 681 | return switch (form_id) { |
std/event.zig+10-8| ... | @@ -1,17 +1,19 @@ | ... | @@ -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 | pub const Locked = @import("event/locked.zig").Locked; | 5 | pub const Locked = @import("event/locked.zig").Locked; |
| 2 | pub const Loop = @import("event/loop.zig").Loop; | 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 | pub const tcp = @import("event/tcp.zig"); | 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 | test "import event tests" { | 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 | _ = @import("event/locked.zig"); | 16 | _ = @import("event/locked.zig"); |
| 11 | _ = @import("event/loop.zig"); | 17 | _ = @import("event/loop.zig"); |
| 12 | _ = @import("event/lock.zig"); | ||
| 13 | _ = @import("event/tcp.zig"); | 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,10 +2,12 @@ const std = @import("../index.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const posix = std.os.posix; | ||
| 6 | const windows = std.os.windows; | ||
| 7 | const AtomicRmwOp = builtin.AtomicRmwOp; | 5 | const AtomicRmwOp = builtin.AtomicRmwOp; |
| 8 | const AtomicOrder = builtin.AtomicOrder; | 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 | pub const Loop = struct { | 12 | pub const Loop = struct { |
| 11 | allocator: *mem.Allocator, | 13 | allocator: *mem.Allocator, |
| ... | @@ -13,7 +15,7 @@ pub const Loop = struct { | ... | @@ -13,7 +15,7 @@ pub const Loop = struct { |
| 13 | os_data: OsData, | 15 | os_data: OsData, |
| 14 | final_resume_node: ResumeNode, | 16 | final_resume_node: ResumeNode, |
| 15 | pending_event_count: usize, | 17 | pending_event_count: usize, |
| 16 | extra_threads: []*std.os.Thread, | 18 | extra_threads: []*os.Thread, |
| 17 | 19 | ||
| 18 | // pre-allocated eventfds. all permanently active. | 20 | // pre-allocated eventfds. all permanently active. |
| 19 | // this is how we send promises to be resumed on other threads. | 21 | // this is how we send promises to be resumed on other threads. |
| ... | @@ -65,7 +67,7 @@ pub const Loop = struct { | ... | @@ -65,7 +67,7 @@ pub const Loop = struct { |
| 65 | /// TODO copy elision / named return values so that the threads referencing *Loop | 67 | /// TODO copy elision / named return values so that the threads referencing *Loop |
| 66 | /// have the correct pointer value. | 68 | /// have the correct pointer value. |
| 67 | pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { | 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 | return self.initInternal(allocator, core_count); | 71 | return self.initInternal(allocator, core_count); |
| 70 | } | 72 | } |
| 71 | 73 | ||
| ... | @@ -92,7 +94,7 @@ pub const Loop = struct { | ... | @@ -92,7 +94,7 @@ pub const Loop = struct { |
| 92 | ); | 94 | ); |
| 93 | errdefer self.allocator.free(self.eventfd_resume_nodes); | 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 | errdefer self.allocator.free(self.extra_threads); | 98 | errdefer self.allocator.free(self.extra_threads); |
| 97 | 99 | ||
| 98 | try self.initOsData(extra_thread_count); | 100 | try self.initOsData(extra_thread_count); |
| ... | @@ -104,17 +106,34 @@ pub const Loop = struct { | ... | @@ -104,17 +106,34 @@ pub const Loop = struct { |
| 104 | self.allocator.free(self.extra_threads); | 106 | self.allocator.free(self.extra_threads); |
| 105 | } | 107 | } |
| 106 | 108 | ||
| 107 | const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError || | 109 | const InitOsDataError = os.LinuxEpollCreateError || mem.Allocator.Error || os.LinuxEventFdError || |
| 108 | std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError || | 110 | os.SpawnThreadError || os.LinuxEpollCtlError || os.BsdKEventError || |
| 109 | std.os.WindowsCreateIoCompletionPortError; | 111 | os.WindowsCreateIoCompletionPortError; |
| 110 | 112 | ||
| 111 | const wakeup_bytes = []u8{0x1} ** 8; | 113 | const wakeup_bytes = []u8{0x1} ** 8; |
| 112 | 114 | ||
| 113 | fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { | 115 | fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void { |
| 114 | switch (builtin.os) { | 116 | switch (builtin.os) { |
| 115 | builtin.Os.linux => { | 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 | errdefer { | 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 | for (self.eventfd_resume_nodes) |*eventfd_node| { | 138 | for (self.eventfd_resume_nodes) |*eventfd_node| { |
| 120 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ | 139 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| ... | @@ -123,7 +142,7 @@ pub const Loop = struct { | ... | @@ -123,7 +142,7 @@ pub const Loop = struct { |
| 123 | .id = ResumeNode.Id.EventFd, | 142 | .id = ResumeNode.Id.EventFd, |
| 124 | .handle = undefined, | 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 | .epoll_op = posix.EPOLL_CTL_ADD, | 146 | .epoll_op = posix.EPOLL_CTL_ADD, |
| 128 | }, | 147 | }, |
| 129 | .next = undefined, | 148 | .next = undefined, |
| ... | @@ -131,17 +150,17 @@ pub const Loop = struct { | ... | @@ -131,17 +150,17 @@ pub const Loop = struct { |
| 131 | self.available_eventfd_resume_nodes.push(eventfd_node); | 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); | 153 | self.os_data.epollfd = try os.linuxEpollCreate(posix.EPOLL_CLOEXEC); |
| 135 | errdefer std.os.close(self.os_data.epollfd); | 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); | 156 | self.os_data.final_eventfd = try os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK); |
| 138 | errdefer std.os.close(self.os_data.final_eventfd); | 157 | errdefer os.close(self.os_data.final_eventfd); |
| 139 | 158 | ||
| 140 | self.os_data.final_eventfd_event = posix.epoll_event{ | 159 | self.os_data.final_eventfd_event = posix.epoll_event{ |
| 141 | .events = posix.EPOLLIN, | 160 | .events = posix.EPOLLIN, |
| 142 | .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, | 161 | .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) }, |
| 143 | }; | 162 | }; |
| 144 | try std.os.linuxEpollCtl( | 163 | try os.linuxEpollCtl( |
| 145 | self.os_data.epollfd, | 164 | self.os_data.epollfd, |
| 146 | posix.EPOLL_CTL_ADD, | 165 | posix.EPOLL_CTL_ADD, |
| 147 | self.os_data.final_eventfd, | 166 | self.os_data.final_eventfd, |
| ... | @@ -151,19 +170,19 @@ pub const Loop = struct { | ... | @@ -151,19 +170,19 @@ pub const Loop = struct { |
| 151 | var extra_thread_index: usize = 0; | 170 | var extra_thread_index: usize = 0; |
| 152 | errdefer { | 171 | errdefer { |
| 153 | // writing 8 bytes to an eventfd cannot fail | 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 | while (extra_thread_index != 0) { | 174 | while (extra_thread_index != 0) { |
| 156 | extra_thread_index -= 1; | 175 | extra_thread_index -= 1; |
| 157 | self.extra_threads[extra_thread_index].wait(); | 176 | self.extra_threads[extra_thread_index].wait(); |
| 158 | } | 177 | } |
| 159 | } | 178 | } |
| 160 | while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { | 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 | builtin.Os.macosx => { | 183 | builtin.Os.macosx => { |
| 165 | self.os_data.kqfd = try std.os.bsdKQueue(); | 184 | self.os_data.kqfd = try os.bsdKQueue(); |
| 166 | errdefer std.os.close(self.os_data.kqfd); | 185 | errdefer os.close(self.os_data.kqfd); |
| 167 | 186 | ||
| 168 | self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count); | 187 | self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count); |
| 169 | errdefer self.allocator.free(self.os_data.kevents); | 188 | errdefer self.allocator.free(self.os_data.kevents); |
| ... | @@ -191,7 +210,7 @@ pub const Loop = struct { | ... | @@ -191,7 +210,7 @@ pub const Loop = struct { |
| 191 | }; | 210 | }; |
| 192 | self.available_eventfd_resume_nodes.push(eventfd_node); | 211 | self.available_eventfd_resume_nodes.push(eventfd_node); |
| 193 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent); | 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 | eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE; | 214 | eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE; |
| 196 | eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER; | 215 | eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER; |
| 197 | // this one is for waiting for events | 216 | // this one is for waiting for events |
| ... | @@ -216,30 +235,30 @@ pub const Loop = struct { | ... | @@ -216,30 +235,30 @@ pub const Loop = struct { |
| 216 | .udata = @ptrToInt(&self.final_resume_node), | 235 | .udata = @ptrToInt(&self.final_resume_node), |
| 217 | }; | 236 | }; |
| 218 | const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent); | 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 | self.os_data.final_kevent.flags = posix.EV_ENABLE; | 239 | self.os_data.final_kevent.flags = posix.EV_ENABLE; |
| 221 | self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER; | 240 | self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER; |
| 222 | 241 | ||
| 223 | var extra_thread_index: usize = 0; | 242 | var extra_thread_index: usize = 0; |
| 224 | errdefer { | 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 | while (extra_thread_index != 0) { | 245 | while (extra_thread_index != 0) { |
| 227 | extra_thread_index -= 1; | 246 | extra_thread_index -= 1; |
| 228 | self.extra_threads[extra_thread_index].wait(); | 247 | self.extra_threads[extra_thread_index].wait(); |
| 229 | } | 248 | } |
| 230 | } | 249 | } |
| 231 | while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { | 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 | builtin.Os.windows => { | 254 | builtin.Os.windows => { |
| 236 | self.os_data.io_port = try std.os.windowsCreateIoCompletionPort( | 255 | self.os_data.io_port = try os.windowsCreateIoCompletionPort( |
| 237 | windows.INVALID_HANDLE_VALUE, | 256 | windows.INVALID_HANDLE_VALUE, |
| 238 | null, | 257 | null, |
| 239 | undefined, | 258 | undefined, |
| 240 | undefined, | 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 | for (self.eventfd_resume_nodes) |*eventfd_node, i| { | 263 | for (self.eventfd_resume_nodes) |*eventfd_node, i| { |
| 245 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ | 264 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| ... | @@ -262,7 +281,7 @@ pub const Loop = struct { | ... | @@ -262,7 +281,7 @@ pub const Loop = struct { |
| 262 | while (i < extra_thread_index) : (i += 1) { | 281 | while (i < extra_thread_index) : (i += 1) { |
| 263 | while (true) { | 282 | while (true) { |
| 264 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | 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 | break; | 285 | break; |
| 267 | } | 286 | } |
| 268 | } | 287 | } |
| ... | @@ -272,7 +291,7 @@ pub const Loop = struct { | ... | @@ -272,7 +291,7 @@ pub const Loop = struct { |
| 272 | } | 291 | } |
| 273 | } | 292 | } |
| 274 | while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) { | 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 | else => {}, | 297 | else => {}, |
| ... | @@ -282,17 +301,17 @@ pub const Loop = struct { | ... | @@ -282,17 +301,17 @@ pub const Loop = struct { |
| 282 | fn deinitOsData(self: *Loop) void { | 301 | fn deinitOsData(self: *Loop) void { |
| 283 | switch (builtin.os) { | 302 | switch (builtin.os) { |
| 284 | builtin.Os.linux => { | 303 | builtin.Os.linux => { |
| 285 | std.os.close(self.os_data.final_eventfd); | 304 | os.close(self.os_data.final_eventfd); |
| 286 | while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd); | 305 | while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd); |
| 287 | std.os.close(self.os_data.epollfd); | 306 | os.close(self.os_data.epollfd); |
| 288 | self.allocator.free(self.eventfd_resume_nodes); | 307 | self.allocator.free(self.eventfd_resume_nodes); |
| 289 | }, | 308 | }, |
| 290 | builtin.Os.macosx => { | 309 | builtin.Os.macosx => { |
| 291 | self.allocator.free(self.os_data.kevents); | 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 | builtin.Os.windows => { | 313 | builtin.Os.windows => { |
| 295 | std.os.close(self.os_data.io_port); | 314 | os.close(self.os_data.io_port); |
| 296 | }, | 315 | }, |
| 297 | else => {}, | 316 | else => {}, |
| 298 | } | 317 | } |
| ... | @@ -307,17 +326,17 @@ pub const Loop = struct { | ... | @@ -307,17 +326,17 @@ pub const Loop = struct { |
| 307 | try self.modFd( | 326 | try self.modFd( |
| 308 | fd, | 327 | fd, |
| 309 | posix.EPOLL_CTL_ADD, | 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 | resume_node, | 330 | resume_node, |
| 312 | ); | 331 | ); |
| 313 | } | 332 | } |
| 314 | 333 | ||
| 315 | pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void { | 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 | .events = events, | 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 | pub fn removeFd(self: *Loop, fd: i32) void { | 342 | pub fn removeFd(self: *Loop, fd: i32) void { |
| ... | @@ -326,7 +345,7 @@ pub const Loop = struct { | ... | @@ -326,7 +345,7 @@ pub const Loop = struct { |
| 326 | } | 345 | } |
| 327 | 346 | ||
| 328 | fn removeFdNoCounter(self: *Loop, fd: i32) void { | 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 | pub async fn waitFd(self: *Loop, fd: i32) !void { | 351 | pub async fn waitFd(self: *Loop, fd: i32) !void { |
| ... | @@ -353,7 +372,7 @@ pub const Loop = struct { | ... | @@ -353,7 +372,7 @@ pub const Loop = struct { |
| 353 | builtin.Os.macosx => { | 372 | builtin.Os.macosx => { |
| 354 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); | 373 | const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent); |
| 355 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; | 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 | self.next_tick_queue.unget(next_tick_node); | 376 | self.next_tick_queue.unget(next_tick_node); |
| 358 | self.available_eventfd_resume_nodes.push(resume_stack_node); | 377 | self.available_eventfd_resume_nodes.push(resume_stack_node); |
| 359 | return; | 378 | return; |
| ... | @@ -361,8 +380,8 @@ pub const Loop = struct { | ... | @@ -361,8 +380,8 @@ pub const Loop = struct { |
| 361 | }, | 380 | }, |
| 362 | builtin.Os.linux => { | 381 | builtin.Os.linux => { |
| 363 | // the pending count is already accounted for | 382 | // the pending count is already accounted for |
| 364 | const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | | 383 | const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT | |
| 365 | std.os.linux.EPOLLET; | 384 | os.linux.EPOLLET; |
| 366 | self.modFd( | 385 | self.modFd( |
| 367 | eventfd_node.eventfd, | 386 | eventfd_node.eventfd, |
| 368 | eventfd_node.epoll_op, | 387 | eventfd_node.epoll_op, |
| ... | @@ -379,7 +398,7 @@ pub const Loop = struct { | ... | @@ -379,7 +398,7 @@ pub const Loop = struct { |
| 379 | // the consumer code can decide whether to read the completion key. | 398 | // the consumer code can decide whether to read the completion key. |
| 380 | // it has to do this for normal I/O, so we match that behavior here. | 399 | // it has to do this for normal I/O, so we match that behavior here. |
| 381 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | 400 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); |
| 382 | std.os.windowsPostQueuedCompletionStatus( | 401 | os.windowsPostQueuedCompletionStatus( |
| 383 | self.os_data.io_port, | 402 | self.os_data.io_port, |
| 384 | undefined, | 403 | undefined, |
| 385 | eventfd_node.completion_key, | 404 | eventfd_node.completion_key, |
| ... | @@ -406,6 +425,9 @@ pub const Loop = struct { | ... | @@ -406,6 +425,9 @@ pub const Loop = struct { |
| 406 | self.finishOneEvent(); // the reference we start with | 425 | self.finishOneEvent(); // the reference we start with |
| 407 | 426 | ||
| 408 | self.workerRun(); | 427 | self.workerRun(); |
| 428 | |||
| 429 | self.os_data.fs_thread.wait(); | ||
| 430 | |||
| 409 | for (self.extra_threads) |extra_thread| { | 431 | for (self.extra_threads) |extra_thread| { |
| 410 | extra_thread.wait(); | 432 | extra_thread.wait(); |
| 411 | } | 433 | } |
| ... | @@ -453,15 +475,16 @@ pub const Loop = struct { | ... | @@ -453,15 +475,16 @@ pub const Loop = struct { |
| 453 | // cause all the threads to stop | 475 | // cause all the threads to stop |
| 454 | switch (builtin.os) { | 476 | switch (builtin.os) { |
| 455 | builtin.Os.linux => { | 477 | builtin.Os.linux => { |
| 478 | self.linuxFsRequest(&self.os_data.fs_end_request); | ||
| 456 | // writing 8 bytes to an eventfd cannot fail | 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 | return; | 481 | return; |
| 459 | }, | 482 | }, |
| 460 | builtin.Os.macosx => { | 483 | builtin.Os.macosx => { |
| 461 | const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); | 484 | const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent); |
| 462 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; | 485 | const eventlist = ([*]posix.Kevent)(undefined)[0..0]; |
| 463 | // cannot fail because we already added it and this just enables it | 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 | return; | 488 | return; |
| 466 | }, | 489 | }, |
| 467 | builtin.Os.windows => { | 490 | builtin.Os.windows => { |
| ... | @@ -469,7 +492,7 @@ pub const Loop = struct { | ... | @@ -469,7 +492,7 @@ pub const Loop = struct { |
| 469 | while (i < self.extra_threads.len + 1) : (i += 1) { | 492 | while (i < self.extra_threads.len + 1) : (i += 1) { |
| 470 | while (true) { | 493 | while (true) { |
| 471 | const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1); | 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 | break; | 496 | break; |
| 474 | } | 497 | } |
| 475 | } | 498 | } |
| ... | @@ -492,8 +515,8 @@ pub const Loop = struct { | ... | @@ -492,8 +515,8 @@ pub const Loop = struct { |
| 492 | switch (builtin.os) { | 515 | switch (builtin.os) { |
| 493 | builtin.Os.linux => { | 516 | builtin.Os.linux => { |
| 494 | // only process 1 event so we don't steal from other threads | 517 | // only process 1 event so we don't steal from other threads |
| 495 | var events: [1]std.os.linux.epoll_event = undefined; | 518 | var events: [1]os.linux.epoll_event = undefined; |
| 496 | const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); | 519 | const count = os.linuxEpollWait(self.os_data.epollfd, events[0..], -1); |
| 497 | for (events[0..count]) |ev| { | 520 | for (events[0..count]) |ev| { |
| 498 | const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); | 521 | const resume_node = @intToPtr(*ResumeNode, ev.data.ptr); |
| 499 | const handle = resume_node.handle; | 522 | const handle = resume_node.handle; |
| ... | @@ -516,7 +539,7 @@ pub const Loop = struct { | ... | @@ -516,7 +539,7 @@ pub const Loop = struct { |
| 516 | }, | 539 | }, |
| 517 | builtin.Os.macosx => { | 540 | builtin.Os.macosx => { |
| 518 | var eventlist: [1]posix.Kevent = undefined; | 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 | for (eventlist[0..count]) |ev| { | 543 | for (eventlist[0..count]) |ev| { |
| 521 | const resume_node = @intToPtr(*ResumeNode, ev.udata); | 544 | const resume_node = @intToPtr(*ResumeNode, ev.udata); |
| 522 | const handle = resume_node.handle; | 545 | const handle = resume_node.handle; |
| ... | @@ -541,9 +564,9 @@ pub const Loop = struct { | ... | @@ -541,9 +564,9 @@ pub const Loop = struct { |
| 541 | while (true) { | 564 | while (true) { |
| 542 | var nbytes: windows.DWORD = undefined; | 565 | var nbytes: windows.DWORD = undefined; |
| 543 | var overlapped: ?*windows.OVERLAPPED = undefined; | 566 | var overlapped: ?*windows.OVERLAPPED = undefined; |
| 544 | switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { | 567 | switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) { |
| 545 | std.os.WindowsWaitResult.Aborted => return, | 568 | os.WindowsWaitResult.Aborted => return, |
| 546 | std.os.WindowsWaitResult.Normal => {}, | 569 | os.WindowsWaitResult.Normal => {}, |
| 547 | } | 570 | } |
| 548 | if (overlapped != null) break; | 571 | if (overlapped != null) break; |
| 549 | } | 572 | } |
| ... | @@ -569,11 +592,73 @@ pub const Loop = struct { | ... | @@ -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 | const OsData = switch (builtin.os) { | 653 | const OsData = switch (builtin.os) { |
| 573 | builtin.Os.linux => struct { | 654 | builtin.Os.linux => struct { |
| 574 | epollfd: i32, | 655 | epollfd: i32, |
| 575 | final_eventfd: i32, | 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 | builtin.Os.macosx => MacOsData, | 663 | builtin.Os.macosx => MacOsData, |
| 579 | builtin.Os.windows => struct { | 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,13 +415,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ |
| 415 | self.at_end = (read < left); | 415 | self.at_end = (read < left); |
| 416 | return pos + read; | 416 | return pos + read; |
| 417 | } | 417 | } |
| 418 | |||
| 419 | }; | 418 | }; |
| 420 | } | 419 | } |
| 421 | 420 | ||
| 422 | pub const SliceInStream = struct { | 421 | pub const SliceInStream = struct { |
| 423 | const Self = this; | 422 | const Self = this; |
| 424 | pub const Error = error { }; | 423 | pub const Error = error{}; |
| 425 | pub const Stream = InStream(Error); | 424 | pub const Stream = InStream(Error); |
| 426 | 425 | ||
| 427 | pub stream: Stream, | 426 | pub stream: Stream, |
| ... | @@ -481,13 +480,12 @@ pub const SliceOutStream = struct { | ... | @@ -481,13 +480,12 @@ pub const SliceOutStream = struct { |
| 481 | 480 | ||
| 482 | assert(self.pos <= self.slice.len); | 481 | assert(self.pos <= self.slice.len); |
| 483 | 482 | ||
| 484 | const n = | 483 | const n = if (self.pos + bytes.len <= self.slice.len) |
| 485 | if (self.pos + bytes.len <= self.slice.len) | 484 | bytes.len |
| 486 | bytes.len | 485 | else |
| 487 | else | 486 | self.slice.len - self.pos; |
| 488 | 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 | self.pos += n; | 489 | self.pos += n; |
| 492 | 490 | ||
| 493 | if (n < bytes.len) { | 491 | if (n < bytes.len) { |
| ... | @@ -586,7 +584,7 @@ pub const BufferedAtomicFile = struct { | ... | @@ -586,7 +584,7 @@ pub const BufferedAtomicFile = struct { |
| 586 | }); | 584 | }); |
| 587 | errdefer allocator.destroy(self); | 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 | errdefer self.atomic_file.deinit(); | 588 | errdefer self.atomic_file.deinit(); |
| 591 | 589 | ||
| 592 | self.file_stream = FileOutStream.init(&self.atomic_file.file); | 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,10 +15,21 @@ pub const File = struct { |
| 15 | /// The OS-specific file descriptor or file handle. | 15 | /// The OS-specific file descriptor or file handle. |
| 16 | handle: os.FileHandle, | 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 | pub const OpenError = os.WindowsOpenError || os.PosixOpenError; | 28 | pub const OpenError = os.WindowsOpenError || os.PosixOpenError; |
| 19 | 29 | ||
| 20 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | 30 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. |
| 21 | /// Call close to clean up. | 31 | /// Call close to clean up. |
| 32 | /// TODO deprecated, just use open | ||
| 22 | pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File { | 33 | pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File { |
| 23 | if (is_posix) { | 34 | if (is_posix) { |
| 24 | const flags = posix.O_LARGEFILE | posix.O_RDONLY; | 35 | const flags = posix.O_LARGEFILE | posix.O_RDONLY; |
| ... | @@ -39,16 +50,18 @@ pub const File = struct { | ... | @@ -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 | pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File { | 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 | /// If the path does not exist it will be created. | 59 | /// If the path does not exist it will be created. |
| 48 | /// If a file already exists in the destination it will be truncated. | 60 | /// If a file already exists in the destination it will be truncated. |
| 49 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | 61 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. |
| 50 | /// Call close to clean up. | 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 | if (is_posix) { | 65 | if (is_posix) { |
| 53 | const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; | 66 | const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC; |
| 54 | const fd = try os.posixOpen(allocator, path, flags, file_mode); | 67 | const fd = try os.posixOpen(allocator, path, flags, file_mode); |
| ... | @@ -72,7 +85,8 @@ pub const File = struct { | ... | @@ -72,7 +85,8 @@ pub const File = struct { |
| 72 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists | 85 | /// If a file already exists in the destination this returns OpenError.PathAlreadyExists |
| 73 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. | 86 | /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator. |
| 74 | /// Call close to clean up. | 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 | if (is_posix) { | 90 | if (is_posix) { |
| 77 | const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL; | 91 | const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL; |
| 78 | const fd = try os.posixOpen(allocator, path, flags, file_mode); | 92 | const fd = try os.posixOpen(allocator, path, flags, file_mode); |
| ... | @@ -282,7 +296,7 @@ pub const File = struct { | ... | @@ -282,7 +296,7 @@ pub const File = struct { |
| 282 | Unexpected, | 296 | Unexpected, |
| 283 | }; | 297 | }; |
| 284 | 298 | ||
| 285 | pub fn mode(self: *File) ModeError!os.FileMode { | 299 | pub fn mode(self: *File) ModeError!Mode { |
| 286 | if (is_posix) { | 300 | if (is_posix) { |
| 287 | var stat: posix.Stat = undefined; | 301 | var stat: posix.Stat = undefined; |
| 288 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); | 302 | const err = posix.getErrno(posix.fstat(self.handle, &stat)); |
| ... | @@ -296,7 +310,7 @@ pub const File = struct { | ... | @@ -296,7 +310,7 @@ pub const File = struct { |
| 296 | 310 | ||
| 297 | // TODO: we should be able to cast u16 to ModeError!u32, making this | 311 | // TODO: we should be able to cast u16 to ModeError!u32, making this |
| 298 | // explicit cast not necessary | 312 | // explicit cast not necessary |
| 299 | return os.FileMode(stat.mode); | 313 | return Mode(stat.mode); |
| 300 | } else if (is_windows) { | 314 | } else if (is_windows) { |
| 301 | return {}; | 315 | return {}; |
| 302 | } else { | 316 | } else { |
| ... | @@ -305,9 +319,11 @@ pub const File = struct { | ... | @@ -305,9 +319,11 @@ pub const File = struct { |
| 305 | } | 319 | } |
| 306 | 320 | ||
| 307 | pub const ReadError = error{ | 321 | pub const ReadError = error{ |
| 308 | BadFd, | 322 | FileClosed, |
| 309 | Io, | 323 | InputOutput, |
| 310 | IsDir, | 324 | IsDir, |
| 325 | WouldBlock, | ||
| 326 | SystemResources, | ||
| 311 | 327 | ||
| 312 | Unexpected, | 328 | Unexpected, |
| 313 | }; | 329 | }; |
| ... | @@ -323,9 +339,12 @@ pub const File = struct { | ... | @@ -323,9 +339,12 @@ pub const File = struct { |
| 323 | posix.EINTR => continue, | 339 | posix.EINTR => continue, |
| 324 | posix.EINVAL => unreachable, | 340 | posix.EINVAL => unreachable, |
| 325 | posix.EFAULT => unreachable, | 341 | posix.EFAULT => unreachable, |
| 326 | posix.EBADF => return error.BadFd, | 342 | posix.EAGAIN => return error.WouldBlock, |
| 327 | posix.EIO => return error.Io, | 343 | posix.EBADF => return error.FileClosed, |
| 344 | posix.EIO => return error.InputOutput, | ||
| 328 | posix.EISDIR => return error.IsDir, | 345 | posix.EISDIR => return error.IsDir, |
| 346 | posix.ENOBUFS => return error.SystemResources, | ||
| 347 | posix.ENOMEM => return error.SystemResources, | ||
| 329 | else => return os.unexpectedErrorPosix(read_err), | 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,16 +38,6 @@ pub const path = @import("path.zig"); |
| 38 | pub const File = @import("file.zig").File; | 38 | pub const File = @import("file.zig").File; |
| 39 | pub const time = @import("time.zig"); | 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 | pub const page_size = 4 * 1024; | 41 | pub const page_size = 4 * 1024; |
| 52 | 42 | ||
| 53 | pub const UserInfo = @import("get_user_id.zig").UserInfo; | 43 | pub const UserInfo = @import("get_user_id.zig").UserInfo; |
| ... | @@ -256,6 +246,26 @@ pub fn posixRead(fd: i32, buf: []u8) !void { | ... | @@ -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 | pub const PosixWriteError = error{ | 269 | pub const PosixWriteError = error{ |
| 260 | WouldBlock, | 270 | WouldBlock, |
| 261 | FileClosed, | 271 | FileClosed, |
| ... | @@ -853,7 +863,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con | ... | @@ -853,7 +863,7 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con |
| 853 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | 863 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is |
| 854 | /// merged and readily available, | 864 | /// merged and readily available, |
| 855 | /// there is a possibility of power loss or application termination leaving temporary files present | 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 | var in_file = try os.File.openRead(allocator, source_path); | 867 | var in_file = try os.File.openRead(allocator, source_path); |
| 858 | defer in_file.close(); | 868 | defer in_file.close(); |
| 859 | 869 | ||
| ... | @@ -879,7 +889,7 @@ pub const AtomicFile = struct { | ... | @@ -879,7 +889,7 @@ pub const AtomicFile = struct { |
| 879 | 889 | ||
| 880 | /// dest_path must remain valid for the lifetime of AtomicFile | 890 | /// dest_path must remain valid for the lifetime of AtomicFile |
| 881 | /// call finish to atomically replace dest_path with contents | 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 | const dirname = os.path.dirname(dest_path); | 893 | const dirname = os.path.dirname(dest_path); |
| 884 | 894 | ||
| 885 | var rand_buf: [12]u8 = undefined; | 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,6 +692,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us |
| 692 | return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout)); | 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 | pub fn getcwd(buf: [*]u8, size: usize) usize { | 699 | pub fn getcwd(buf: [*]u8, size: usize) usize { |
| 696 | return syscall2(SYS_getcwd, @ptrToInt(buf), size); | 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,6 +746,10 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize { |
| 742 | return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count); | 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 | // TODO https://github.com/ziglang/zig/issues/265 | 753 | // TODO https://github.com/ziglang/zig/issues/265 |
| 746 | pub fn rmdir(path: [*]const u8) usize { | 754 | pub fn rmdir(path: [*]const u8) usize { |
| 747 | return syscall1(SYS_rmdir, @ptrToInt(path)); | 755 | return syscall1(SYS_rmdir, @ptrToInt(path)); |