| ... | ... | @@ -23,6 +23,7 @@ pub const Request = struct { |
| 23 | 23 | }; |
| 24 | 24 | |
| 25 | 25 | pub const Msg = union(enum) { |
| 26 | WriteV: WriteV, |
| 26 | 27 | PWriteV: PWriteV, |
| 27 | 28 | PReadV: PReadV, |
| 28 | 29 | Open: Open, |
| ... | ... | @@ -30,6 +31,14 @@ pub const Request = struct { |
| 30 | 31 | WriteFile: WriteFile, |
| 31 | 32 | End, // special - means the fs thread should exit |
| 32 | 33 | |
| 34 | pub const WriteV = struct { |
| 35 | fd: fd_t, |
| 36 | iov: []const os.iovec_const, |
| 37 | result: Error!void, |
| 38 | |
| 39 | pub const Error = os.WriteError; |
| 40 | }; |
| 41 | |
| 33 | 42 | pub const PWriteV = struct { |
| 34 | 43 | fd: fd_t, |
| 35 | 44 | iov: []const os.iovec_const, |
| ... | ... | @@ -77,7 +86,7 @@ pub const Request = struct { |
| 77 | 86 | pub const PWriteVError = error{OutOfMemory} || File.WriteError; |
| 78 | 87 | |
| 79 | 88 | /// data - just the inner references - must live until pwritev frame completes. |
| 80 | | pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void { |
| 89 | pub fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void { |
| 81 | 90 | switch (builtin.os) { |
| 82 | 91 | .macosx, |
| 83 | 92 | .linux, |
| ... | ... | @@ -94,31 +103,31 @@ pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: us |
| 94 | 103 | }; |
| 95 | 104 | } |
| 96 | 105 | |
| 97 | | return await (async pwritevPosix(loop, fd, iovecs, offset) catch unreachable); |
| 106 | return pwritevPosix(loop, fd, iovecs, offset); |
| 98 | 107 | }, |
| 99 | 108 | .windows => { |
| 100 | 109 | const data_copy = try std.mem.dupe(loop.allocator, []const u8, data); |
| 101 | 110 | defer loop.allocator.free(data_copy); |
| 102 | | return await (async pwritevWindows(loop, fd, data, offset) catch unreachable); |
| 111 | return pwritevWindows(loop, fd, data, offset); |
| 103 | 112 | }, |
| 104 | 113 | else => @compileError("Unsupported OS"), |
| 105 | 114 | } |
| 106 | 115 | } |
| 107 | 116 | |
| 108 | 117 | /// data must outlive the returned frame |
| 109 | | pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void { |
| 118 | pub fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void { |
| 110 | 119 | if (data.len == 0) return; |
| 111 | | if (data.len == 1) return await (async pwriteWindows(loop, fd, data[0], offset) catch unreachable); |
| 120 | if (data.len == 1) return pwriteWindows(loop, fd, data[0], offset); |
| 112 | 121 | |
| 113 | 122 | // TODO do these in parallel |
| 114 | 123 | var off = offset; |
| 115 | 124 | for (data) |buf| { |
| 116 | | try await (async pwriteWindows(loop, fd, buf, off) catch unreachable); |
| 125 | try pwriteWindows(loop, fd, buf, off); |
| 117 | 126 | off += buf.len; |
| 118 | 127 | } |
| 119 | 128 | } |
| 120 | 129 | |
| 121 | | pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void { |
| 130 | pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void { |
| 122 | 131 | var resume_node = Loop.ResumeNode.Basic{ |
| 123 | 132 | .base = Loop.ResumeNode{ |
| 124 | 133 | .id = Loop.ResumeNode.Id.Basic, |
| ... | ... | @@ -158,7 +167,7 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) |
| 158 | 167 | } |
| 159 | 168 | |
| 160 | 169 | /// iovecs must live until pwritev frame completes. |
| 161 | | pub async fn pwritevPosix( |
| 170 | pub fn pwritevPosix( |
| 162 | 171 | loop: *Loop, |
| 163 | 172 | fd: fd_t, |
| 164 | 173 | iovecs: []const os.iovec_const, |
| ... | ... | @@ -195,10 +204,44 @@ pub async fn pwritevPosix( |
| 195 | 204 | return req_node.data.msg.PWriteV.result; |
| 196 | 205 | } |
| 197 | 206 | |
| 207 | /// iovecs must live until pwritev frame completes. |
| 208 | pub fn writevPosix( |
| 209 | loop: *Loop, |
| 210 | fd: fd_t, |
| 211 | iovecs: []const os.iovec_const, |
| 212 | ) os.WriteError!void { |
| 213 | var req_node = RequestNode{ |
| 214 | .prev = null, |
| 215 | .next = null, |
| 216 | .data = Request{ |
| 217 | .msg = Request.Msg{ |
| 218 | .WriteV = Request.Msg.WriteV{ |
| 219 | .fd = fd, |
| 220 | .iov = iovecs, |
| 221 | .result = undefined, |
| 222 | }, |
| 223 | }, |
| 224 | .finish = Request.Finish{ |
| 225 | .TickNode = Loop.NextTickNode{ |
| 226 | .prev = null, |
| 227 | .next = null, |
| 228 | .data = @frame(), |
| 229 | }, |
| 230 | }, |
| 231 | }, |
| 232 | }; |
| 233 | |
| 234 | suspend { |
| 235 | loop.posixFsRequest(&req_node); |
| 236 | } |
| 237 | |
| 238 | return req_node.data.msg.WriteV.result; |
| 239 | } |
| 240 | |
| 198 | 241 | pub const PReadVError = error{OutOfMemory} || File.ReadError; |
| 199 | 242 | |
| 200 | 243 | /// data - just the inner references - must live until preadv frame completes. |
| 201 | | pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize { |
| 244 | pub fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize { |
| 202 | 245 | assert(data.len != 0); |
| 203 | 246 | switch (builtin.os) { |
| 204 | 247 | .macosx, |
| ... | ... | @@ -216,21 +259,21 @@ pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PR |
| 216 | 259 | }; |
| 217 | 260 | } |
| 218 | 261 | |
| 219 | | return await (async preadvPosix(loop, fd, iovecs, offset) catch unreachable); |
| 262 | return preadvPosix(loop, fd, iovecs, offset); |
| 220 | 263 | }, |
| 221 | 264 | .windows => { |
| 222 | 265 | const data_copy = try std.mem.dupe(loop.allocator, []u8, data); |
| 223 | 266 | defer loop.allocator.free(data_copy); |
| 224 | | return await (async preadvWindows(loop, fd, data_copy, offset) catch unreachable); |
| 267 | return preadvWindows(loop, fd, data_copy, offset); |
| 225 | 268 | }, |
| 226 | 269 | else => @compileError("Unsupported OS"), |
| 227 | 270 | } |
| 228 | 271 | } |
| 229 | 272 | |
| 230 | 273 | /// data must outlive the returned frame |
| 231 | | pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize { |
| 274 | pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize { |
| 232 | 275 | assert(data.len != 0); |
| 233 | | if (data.len == 1) return await (async preadWindows(loop, fd, data[0], offset) catch unreachable); |
| 276 | if (data.len == 1) return preadWindows(loop, fd, data[0], offset); |
| 234 | 277 | |
| 235 | 278 | // TODO do these in parallel? |
| 236 | 279 | var off: usize = 0; |
| ... | ... | @@ -238,7 +281,7 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6 |
| 238 | 281 | var inner_off: usize = 0; |
| 239 | 282 | while (true) { |
| 240 | 283 | const v = data[iov_i]; |
| 241 | | const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off) catch unreachable); |
| 284 | const amt_read = try preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off); |
| 242 | 285 | off += amt_read; |
| 243 | 286 | inner_off += amt_read; |
| 244 | 287 | if (inner_off == v.len) { |
| ... | ... | @@ -252,7 +295,7 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6 |
| 252 | 295 | } |
| 253 | 296 | } |
| 254 | 297 | |
| 255 | | pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize { |
| 298 | pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize { |
| 256 | 299 | var resume_node = Loop.ResumeNode.Basic{ |
| 257 | 300 | .base = Loop.ResumeNode{ |
| 258 | 301 | .id = Loop.ResumeNode.Id.Basic, |
| ... | ... | @@ -291,7 +334,7 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize |
| 291 | 334 | } |
| 292 | 335 | |
| 293 | 336 | /// iovecs must live until preadv frame completes |
| 294 | | pub async fn preadvPosix( |
| 337 | pub fn preadvPosix( |
| 295 | 338 | loop: *Loop, |
| 296 | 339 | fd: fd_t, |
| 297 | 340 | iovecs: []const os.iovec, |
| ... | ... | @@ -328,7 +371,7 @@ pub async fn preadvPosix( |
| 328 | 371 | return req_node.data.msg.PReadV.result; |
| 329 | 372 | } |
| 330 | 373 | |
| 331 | | pub async fn openPosix( |
| 374 | pub fn openPosix( |
| 332 | 375 | loop: *Loop, |
| 333 | 376 | path: []const u8, |
| 334 | 377 | flags: u32, |
| ... | ... | @@ -367,11 +410,11 @@ pub async fn openPosix( |
| 367 | 410 | return req_node.data.msg.Open.result; |
| 368 | 411 | } |
| 369 | 412 | |
| 370 | | pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t { |
| 413 | pub fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t { |
| 371 | 414 | switch (builtin.os) { |
| 372 | 415 | .macosx, .linux, .freebsd, .netbsd => { |
| 373 | 416 | const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC; |
| 374 | | return await (async openPosix(loop, path, flags, File.default_mode) catch unreachable); |
| 417 | return openPosix(loop, path, flags, File.default_mode); |
| 375 | 418 | }, |
| 376 | 419 | |
| 377 | 420 | .windows => return windows.CreateFile( |
| ... | ... | @@ -390,12 +433,12 @@ pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t { |
| 390 | 433 | |
| 391 | 434 | /// Creates if does not exist. Truncates the file if it exists. |
| 392 | 435 | /// Uses the default mode. |
| 393 | | pub async fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t { |
| 394 | | return await (async openWriteMode(loop, path, File.default_mode) catch unreachable); |
| 436 | pub fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t { |
| 437 | return openWriteMode(loop, path, File.default_mode); |
| 395 | 438 | } |
| 396 | 439 | |
| 397 | 440 | /// Creates if does not exist. Truncates the file if it exists. |
| 398 | | pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t { |
| 441 | pub fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t { |
| 399 | 442 | switch (builtin.os) { |
| 400 | 443 | .macosx, |
| 401 | 444 | .linux, |
| ... | ... | @@ -403,7 +446,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File. |
| 403 | 446 | .netbsd, |
| 404 | 447 | => { |
| 405 | 448 | const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC; |
| 406 | | return await (async openPosix(loop, path, flags, File.default_mode) catch unreachable); |
| 449 | return openPosix(loop, path, flags, File.default_mode); |
| 407 | 450 | }, |
| 408 | 451 | .windows => return windows.CreateFile( |
| 409 | 452 | path, |
| ... | ... | @@ -419,7 +462,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File. |
| 419 | 462 | } |
| 420 | 463 | |
| 421 | 464 | /// Creates if does not exist. Does not truncate. |
| 422 | | pub async fn openReadWrite( |
| 465 | pub fn openReadWrite( |
| 423 | 466 | loop: *Loop, |
| 424 | 467 | path: []const u8, |
| 425 | 468 | mode: File.Mode, |
| ... | ... | @@ -427,7 +470,7 @@ pub async fn openReadWrite( |
| 427 | 470 | switch (builtin.os) { |
| 428 | 471 | .macosx, .linux, .freebsd, .netbsd => { |
| 429 | 472 | const flags = os.O_LARGEFILE | os.O_RDWR | os.O_CREAT | os.O_CLOEXEC; |
| 430 | | return await (async openPosix(loop, path, flags, mode) catch unreachable); |
| 473 | return openPosix(loop, path, flags, mode); |
| 431 | 474 | }, |
| 432 | 475 | |
| 433 | 476 | .windows => return windows.CreateFile( |
| ... | ... | @@ -576,24 +619,24 @@ pub const CloseOperation = struct { |
| 576 | 619 | |
| 577 | 620 | /// contents must remain alive until writeFile completes. |
| 578 | 621 | /// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate |
| 579 | | pub async fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void { |
| 580 | | return await (async writeFileMode(loop, path, contents, File.default_mode) catch unreachable); |
| 622 | pub fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void { |
| 623 | return writeFileMode(loop, path, contents, File.default_mode); |
| 581 | 624 | } |
| 582 | 625 | |
| 583 | 626 | /// contents must remain alive until writeFile completes. |
| 584 | | pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { |
| 627 | pub fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { |
| 585 | 628 | switch (builtin.os) { |
| 586 | 629 | .linux, |
| 587 | 630 | .macosx, |
| 588 | 631 | .freebsd, |
| 589 | 632 | .netbsd, |
| 590 | | => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable), |
| 591 | | .windows => return await (async writeFileWindows(loop, path, contents) catch unreachable), |
| 633 | => return writeFileModeThread(loop, path, contents, mode), |
| 634 | .windows => return writeFileWindows(loop, path, contents), |
| 592 | 635 | else => @compileError("Unsupported OS"), |
| 593 | 636 | } |
| 594 | 637 | } |
| 595 | 638 | |
| 596 | | async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void { |
| 639 | fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void { |
| 597 | 640 | const handle = try windows.CreateFile( |
| 598 | 641 | path, |
| 599 | 642 | windows.GENERIC_WRITE, |
| ... | ... | @@ -605,10 +648,10 @@ async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) ! |
| 605 | 648 | ); |
| 606 | 649 | defer os.close(handle); |
| 607 | 650 | |
| 608 | | try await (async pwriteWindows(loop, handle, contents, 0) catch unreachable); |
| 651 | try pwriteWindows(loop, handle, contents, 0); |
| 609 | 652 | } |
| 610 | 653 | |
| 611 | | async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { |
| 654 | fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { |
| 612 | 655 | const path_with_null = try std.cstr.addNullByte(loop.allocator, path); |
| 613 | 656 | defer loop.allocator.free(path_with_null); |
| 614 | 657 | |
| ... | ... | @@ -646,11 +689,11 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8 |
| 646 | 689 | /// The frame resumes when the last data has been confirmed written, but before the file handle |
| 647 | 690 | /// is closed. |
| 648 | 691 | /// Caller owns returned memory. |
| 649 | | pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 { |
| 692 | pub fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 { |
| 650 | 693 | var close_op = try CloseOperation.start(loop); |
| 651 | 694 | defer close_op.finish(); |
| 652 | 695 | |
| 653 | | const fd = try await (async openRead(loop, file_path) catch unreachable); |
| 696 | const fd = try openRead(loop, file_path); |
| 654 | 697 | close_op.setHandle(fd); |
| 655 | 698 | |
| 656 | 699 | var list = std.ArrayList(u8).init(loop.allocator); |
| ... | ... | @@ -660,7 +703,7 @@ pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 |
| 660 | 703 | try list.ensureCapacity(list.len + mem.page_size); |
| 661 | 704 | const buf = list.items[list.len..]; |
| 662 | 705 | const buf_array = [_][]u8{buf}; |
| 663 | | const amt = try await (async preadv(loop, fd, buf_array, list.len) catch unreachable); |
| 706 | const amt = try preadv(loop, fd, buf_array, list.len); |
| 664 | 707 | list.len += amt; |
| 665 | 708 | if (list.len > max_size) { |
| 666 | 709 | return error.FileTooBig; |
| ... | ... | @@ -1273,11 +1316,11 @@ const test_tmp_dir = "std_event_fs_test"; |
| 1273 | 1316 | // return result; |
| 1274 | 1317 | //} |
| 1275 | 1318 | |
| 1276 | | async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { |
| 1277 | | result.* = await (async testFsWatch(loop) catch unreachable); |
| 1319 | fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { |
| 1320 | result.* = testFsWatch(loop); |
| 1278 | 1321 | } |
| 1279 | 1322 | |
| 1280 | | async fn testFsWatch(loop: *Loop) !void { |
| 1323 | fn testFsWatch(loop: *Loop) !void { |
| 1281 | 1324 | const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" }); |
| 1282 | 1325 | defer loop.allocator.free(file_path); |
| 1283 | 1326 | |
| ... | ... | @@ -1288,27 +1331,27 @@ async fn testFsWatch(loop: *Loop) !void { |
| 1288 | 1331 | const line2_offset = 7; |
| 1289 | 1332 | |
| 1290 | 1333 | // first just write then read the file |
| 1291 | | try await try async writeFile(loop, file_path, contents); |
| 1334 | try writeFile(loop, file_path, contents); |
| 1292 | 1335 | |
| 1293 | | const read_contents = try await try async readFile(loop, file_path, 1024 * 1024); |
| 1336 | const read_contents = try readFile(loop, file_path, 1024 * 1024); |
| 1294 | 1337 | testing.expectEqualSlices(u8, contents, read_contents); |
| 1295 | 1338 | |
| 1296 | 1339 | // now watch the file |
| 1297 | 1340 | var watch = try Watch(void).create(loop, 0); |
| 1298 | 1341 | defer watch.destroy(); |
| 1299 | 1342 | |
| 1300 | | testing.expect((try await try async watch.addFile(file_path, {})) == null); |
| 1343 | testing.expect((try watch.addFile(file_path, {})) == null); |
| 1301 | 1344 | |
| 1302 | | const ev = try async watch.channel.get(); |
| 1345 | const ev = async watch.channel.get(); |
| 1303 | 1346 | var ev_consumed = false; |
| 1304 | 1347 | defer if (!ev_consumed) await ev; |
| 1305 | 1348 | |
| 1306 | 1349 | // overwrite line 2 |
| 1307 | | const fd = try await try async openReadWrite(loop, file_path, File.default_mode); |
| 1350 | const fd = try await openReadWrite(loop, file_path, File.default_mode); |
| 1308 | 1351 | { |
| 1309 | 1352 | defer os.close(fd); |
| 1310 | 1353 | |
| 1311 | | try await try async pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset); |
| 1354 | try pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset); |
| 1312 | 1355 | } |
| 1313 | 1356 | |
| 1314 | 1357 | ev_consumed = true; |
| ... | ... | @@ -1316,7 +1359,7 @@ async fn testFsWatch(loop: *Loop) !void { |
| 1316 | 1359 | WatchEventId.CloseWrite => {}, |
| 1317 | 1360 | WatchEventId.Delete => @panic("wrong event"), |
| 1318 | 1361 | } |
| 1319 | | const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024); |
| 1362 | const contents_updated = try readFile(loop, file_path, 1024 * 1024); |
| 1320 | 1363 | testing.expectEqualSlices(u8, |
| 1321 | 1364 | \\line 1 |
| 1322 | 1365 | \\lorem ipsum |