| ... | ... | @@ -979,17 +979,21 @@ pub const VTable = struct { |
| 979 | 979 | /// Thread-safe. |
| 980 | 980 | cancelRequested: *const fn (?*anyopaque) bool, |
| 981 | 981 | |
| 982 | /// Blocks until one of the futures from the list has a result ready, such |
| 983 | /// that awaiting it will not block. Returns that index. |
| 984 | select: *const fn (?*anyopaque, futures: []const *AnyFuture) usize, |
| 985 | |
| 982 | 986 | mutexLock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) Cancelable!void, |
| 983 | 987 | mutexUnlock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) void, |
| 984 | 988 | |
| 985 | 989 | conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void, |
| 986 | 990 | conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void, |
| 987 | 991 | |
| 988 | | createFile: *const fn (?*anyopaque, dir: fs.Dir, sub_path: []const u8, flags: fs.File.CreateFlags) FileOpenError!fs.File, |
| 989 | | openFile: *const fn (?*anyopaque, dir: fs.Dir, sub_path: []const u8, flags: fs.File.OpenFlags) FileOpenError!fs.File, |
| 990 | | closeFile: *const fn (?*anyopaque, fs.File) void, |
| 991 | | pread: *const fn (?*anyopaque, file: fs.File, buffer: []u8, offset: std.posix.off_t) FilePReadError!usize, |
| 992 | | pwrite: *const fn (?*anyopaque, file: fs.File, buffer: []const u8, offset: std.posix.off_t) FilePWriteError!usize, |
| 992 | createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File, |
| 993 | openFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File, |
| 994 | closeFile: *const fn (?*anyopaque, File) void, |
| 995 | pread: *const fn (?*anyopaque, file: File, buffer: []u8, offset: std.posix.off_t) File.PReadError!usize, |
| 996 | pwrite: *const fn (?*anyopaque, file: File, buffer: []const u8, offset: std.posix.off_t) File.PWriteError!usize, |
| 993 | 997 | |
| 994 | 998 | now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp, |
| 995 | 999 | sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void, |
| ... | ... | @@ -1000,28 +1004,118 @@ pub const Cancelable = error{ |
| 1000 | 1004 | Canceled, |
| 1001 | 1005 | }; |
| 1002 | 1006 | |
| 1003 | | pub const OpenFlags = fs.File.OpenFlags; |
| 1004 | | pub const CreateFlags = fs.File.CreateFlags; |
| 1007 | pub const Dir = struct { |
| 1008 | handle: Handle, |
| 1009 | |
| 1010 | pub fn cwd() Dir { |
| 1011 | return .{ .handle = std.fs.cwd().fd }; |
| 1012 | } |
| 1013 | |
| 1014 | pub const Handle = std.posix.fd_t; |
| 1015 | |
| 1016 | pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { |
| 1017 | return io.vtable.openFile(io.userdata, dir, sub_path, flags); |
| 1018 | } |
| 1019 | |
| 1020 | pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { |
| 1021 | return io.vtable.createFile(io.userdata, dir, sub_path, flags); |
| 1022 | } |
| 1023 | |
| 1024 | pub const WriteFileOptions = struct { |
| 1025 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| 1026 | /// On WASI, `sub_path` should be encoded as valid UTF-8. |
| 1027 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| 1028 | sub_path: []const u8, |
| 1029 | data: []const u8, |
| 1030 | flags: File.CreateFlags = .{}, |
| 1031 | }; |
| 1032 | |
| 1033 | pub const WriteFileError = File.WriteError || File.OpenError || Cancelable; |
| 1034 | |
| 1035 | /// Writes content to the file system, using the file creation flags provided. |
| 1036 | pub fn writeFile(dir: Dir, io: Io, options: WriteFileOptions) WriteFileError!void { |
| 1037 | var file = try dir.createFile(io, options.sub_path, options.flags); |
| 1038 | defer file.close(io); |
| 1039 | try file.writeAll(io, options.data); |
| 1040 | } |
| 1041 | }; |
| 1042 | |
| 1043 | pub const File = struct { |
| 1044 | handle: Handle, |
| 1045 | |
| 1046 | pub const Handle = std.posix.fd_t; |
| 1005 | 1047 | |
| 1006 | | pub const FileOpenError = fs.File.OpenError || Cancelable; |
| 1007 | | pub const FileReadError = fs.File.ReadError || Cancelable; |
| 1008 | | pub const FilePReadError = fs.File.PReadError || Cancelable; |
| 1009 | | pub const FileWriteError = fs.File.WriteError || Cancelable; |
| 1010 | | pub const FilePWriteError = fs.File.PWriteError || Cancelable; |
| 1048 | pub const OpenFlags = fs.File.OpenFlags; |
| 1049 | pub const CreateFlags = fs.File.CreateFlags; |
| 1050 | |
| 1051 | pub const OpenError = fs.File.OpenError || Cancelable; |
| 1052 | |
| 1053 | pub fn close(file: File, io: Io) void { |
| 1054 | return io.vtable.closeFile(io.userdata, file); |
| 1055 | } |
| 1056 | |
| 1057 | pub const ReadError = fs.File.ReadError || Cancelable; |
| 1058 | |
| 1059 | pub fn read(file: File, io: Io, buffer: []u8) ReadError!usize { |
| 1060 | return @errorCast(file.pread(io, buffer, -1)); |
| 1061 | } |
| 1062 | |
| 1063 | pub const PReadError = fs.File.PReadError || Cancelable; |
| 1064 | |
| 1065 | pub fn pread(file: File, io: Io, buffer: []u8, offset: std.posix.off_t) PReadError!usize { |
| 1066 | return io.vtable.pread(io.userdata, file, buffer, offset); |
| 1067 | } |
| 1068 | |
| 1069 | pub const WriteError = fs.File.WriteError || Cancelable; |
| 1070 | |
| 1071 | pub fn write(file: File, io: Io, buffer: []const u8) WriteError!usize { |
| 1072 | return @errorCast(file.pwrite(io, buffer, -1)); |
| 1073 | } |
| 1074 | |
| 1075 | pub const PWriteError = fs.File.PWriteError || Cancelable; |
| 1076 | |
| 1077 | pub fn pwrite(file: File, io: Io, buffer: []const u8, offset: std.posix.off_t) PWriteError!usize { |
| 1078 | return io.vtable.pwrite(io.userdata, file, buffer, offset); |
| 1079 | } |
| 1080 | |
| 1081 | pub fn writeAll(file: File, io: Io, bytes: []const u8) WriteError!void { |
| 1082 | var index: usize = 0; |
| 1083 | while (index < bytes.len) { |
| 1084 | index += try file.write(io, bytes[index..]); |
| 1085 | } |
| 1086 | } |
| 1087 | |
| 1088 | pub fn readAll(file: File, io: Io, buffer: []u8) ReadError!usize { |
| 1089 | var index: usize = 0; |
| 1090 | while (index != buffer.len) { |
| 1091 | const amt = try file.read(io, buffer[index..]); |
| 1092 | if (amt == 0) break; |
| 1093 | index += amt; |
| 1094 | } |
| 1095 | return index; |
| 1096 | } |
| 1097 | }; |
| 1011 | 1098 | |
| 1012 | 1099 | pub const Timestamp = enum(i96) { |
| 1013 | 1100 | _, |
| 1014 | 1101 | |
| 1015 | | pub fn durationTo(from: Timestamp, to: Timestamp) i96 { |
| 1016 | | return @intFromEnum(to) - @intFromEnum(from); |
| 1102 | pub fn durationTo(from: Timestamp, to: Timestamp) Duration { |
| 1103 | return .{ .nanoseconds = @intFromEnum(to) - @intFromEnum(from) }; |
| 1017 | 1104 | } |
| 1018 | 1105 | |
| 1019 | | pub fn addDuration(from: Timestamp, duration: i96) Timestamp { |
| 1020 | | return @enumFromInt(@intFromEnum(from) + duration); |
| 1106 | pub fn addDuration(from: Timestamp, duration: Duration) Timestamp { |
| 1107 | return @enumFromInt(@intFromEnum(from) + duration.nanoseconds); |
| 1021 | 1108 | } |
| 1022 | 1109 | }; |
| 1023 | | pub const Deadline = union(enum) { |
| 1110 | pub const Duration = struct { |
| 1024 | 1111 | nanoseconds: i96, |
| 1112 | |
| 1113 | pub fn ms(x: u64) Duration { |
| 1114 | return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_ms }; |
| 1115 | } |
| 1116 | }; |
| 1117 | pub const Deadline = union(enum) { |
| 1118 | duration: Duration, |
| 1025 | 1119 | timestamp: Timestamp, |
| 1026 | 1120 | }; |
| 1027 | 1121 | pub const ClockGetTimeError = std.posix.ClockGetTimeError || Cancelable; |
| ... | ... | @@ -1408,7 +1502,7 @@ pub fn Queue(Elem: type) type { |
| 1408 | 1502 | |
| 1409 | 1503 | /// Calls `function` with `args`, such that the return value of the function is |
| 1410 | 1504 | /// not guaranteed to be available until `await` is called. |
| 1411 | | pub fn async(io: Io, function: anytype, args: anytype) Future(@typeInfo(@TypeOf(function)).@"fn".return_type.?) { |
| 1505 | pub fn async(io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) Future(@typeInfo(@TypeOf(function)).@"fn".return_type.?) { |
| 1412 | 1506 | const Result = @typeInfo(@TypeOf(function)).@"fn".return_type.?; |
| 1413 | 1507 | const Args = @TypeOf(args); |
| 1414 | 1508 | const TypeErased = struct { |
| ... | ... | @@ -1432,7 +1526,7 @@ pub fn async(io: Io, function: anytype, args: anytype) Future(@typeInfo(@TypeOf( |
| 1432 | 1526 | |
| 1433 | 1527 | /// Calls `function` with `args` asynchronously. The resource cleans itself up |
| 1434 | 1528 | /// when the function returns. Does not support await, cancel, or a return value. |
| 1435 | | pub fn go(io: Io, function: anytype, args: anytype) void { |
| 1529 | pub fn go(io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) void { |
| 1436 | 1530 | const Args = @TypeOf(args); |
| 1437 | 1531 | const TypeErased = struct { |
| 1438 | 1532 | fn start(context: *const anyopaque) void { |
| ... | ... | @@ -1448,55 +1542,56 @@ pub fn go(io: Io, function: anytype, args: anytype) void { |
| 1448 | 1542 | ); |
| 1449 | 1543 | } |
| 1450 | 1544 | |
| 1451 | | pub fn openFile(io: Io, dir: fs.Dir, sub_path: []const u8, flags: fs.File.OpenFlags) FileOpenError!fs.File { |
| 1452 | | return io.vtable.openFile(io.userdata, dir, sub_path, flags); |
| 1453 | | } |
| 1454 | | |
| 1455 | | pub fn createFile(io: Io, dir: fs.Dir, sub_path: []const u8, flags: fs.File.CreateFlags) FileOpenError!fs.File { |
| 1456 | | return io.vtable.createFile(io.userdata, dir, sub_path, flags); |
| 1457 | | } |
| 1458 | | |
| 1459 | | pub fn closeFile(io: Io, file: fs.File) void { |
| 1460 | | return io.vtable.closeFile(io.userdata, file); |
| 1461 | | } |
| 1462 | | |
| 1463 | | pub fn read(io: Io, file: fs.File, buffer: []u8) FileReadError!usize { |
| 1464 | | return @errorCast(io.pread(file, buffer, -1)); |
| 1465 | | } |
| 1466 | | |
| 1467 | | pub fn pread(io: Io, file: fs.File, buffer: []u8, offset: std.posix.off_t) FilePReadError!usize { |
| 1468 | | return io.vtable.pread(io.userdata, file, buffer, offset); |
| 1545 | pub fn now(io: Io, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp { |
| 1546 | return io.vtable.now(io.userdata, clockid); |
| 1469 | 1547 | } |
| 1470 | 1548 | |
| 1471 | | pub fn write(io: Io, file: fs.File, buffer: []const u8) FileWriteError!usize { |
| 1472 | | return @errorCast(io.pwrite(file, buffer, -1)); |
| 1549 | pub fn sleep(io: Io, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void { |
| 1550 | return io.vtable.sleep(io.userdata, clockid, deadline); |
| 1473 | 1551 | } |
| 1474 | 1552 | |
| 1475 | | pub fn pwrite(io: Io, file: fs.File, buffer: []const u8, offset: std.posix.off_t) FilePWriteError!usize { |
| 1476 | | return io.vtable.pwrite(io.userdata, file, buffer, offset); |
| 1553 | pub fn sleepDuration(io: Io, duration: Duration) SleepError!void { |
| 1554 | return io.vtable.sleep(io.userdata, .MONOTONIC, .{ .duration = duration }); |
| 1477 | 1555 | } |
| 1478 | 1556 | |
| 1479 | | pub fn writeAll(io: Io, file: fs.File, bytes: []const u8) FileWriteError!void { |
| 1480 | | var index: usize = 0; |
| 1481 | | while (index < bytes.len) { |
| 1482 | | index += try io.write(file, bytes[index..]); |
| 1557 | /// Given a struct with each field a `*Future`, returns a union with the same |
| 1558 | /// fields, each field type the future's result. |
| 1559 | pub fn SelectUnion(S: type) type { |
| 1560 | const struct_fields = @typeInfo(S).@"struct".fields; |
| 1561 | var fields: [struct_fields.len]std.builtin.Type.UnionField = undefined; |
| 1562 | for (&fields, struct_fields) |*union_field, struct_field| { |
| 1563 | const F = @typeInfo(struct_field.type).pointer.child; |
| 1564 | const Result = @TypeOf(@as(F, undefined).result); |
| 1565 | union_field.* = .{ |
| 1566 | .name = struct_field.name, |
| 1567 | .type = Result, |
| 1568 | .alignment = struct_field.alignment, |
| 1569 | }; |
| 1483 | 1570 | } |
| 1571 | return @Type(.{ .@"union" = .{ |
| 1572 | .layout = .auto, |
| 1573 | .tag_type = std.meta.FieldEnum(S), |
| 1574 | .fields = &fields, |
| 1575 | .decls = &.{}, |
| 1576 | } }); |
| 1484 | 1577 | } |
| 1485 | 1578 | |
| 1486 | | pub fn readAll(io: Io, file: fs.File, buffer: []u8) FileReadError!usize { |
| 1487 | | var index: usize = 0; |
| 1488 | | while (index != buffer.len) { |
| 1489 | | const amt = try io.read(file, buffer[index..]); |
| 1490 | | if (amt == 0) break; |
| 1491 | | index += amt; |
| 1579 | /// `s` is a struct with every field a `*Future(T)`, where `T` can be any type, |
| 1580 | /// and can be different for each field. |
| 1581 | pub fn select(io: Io, s: anytype) SelectUnion(@TypeOf(s)) { |
| 1582 | const U = SelectUnion(@TypeOf(s)); |
| 1583 | const S = @TypeOf(s); |
| 1584 | const fields = @typeInfo(S).@"struct".fields; |
| 1585 | var futures: [fields.len]*AnyFuture = undefined; |
| 1586 | inline for (fields, &futures) |field, *any_future| { |
| 1587 | const future = @field(s, field.name); |
| 1588 | any_future.* = future.any_future orelse return @unionInit(U, field.name, future.result); |
| 1589 | } |
| 1590 | switch (io.vtable.select(io.userdata, &futures)) { |
| 1591 | inline 0...(fields.len - 1) => |selected_index| { |
| 1592 | const field_name = fields[selected_index].name; |
| 1593 | return @unionInit(U, field_name, @field(s, field_name).await(io)); |
| 1594 | }, |
| 1595 | else => unreachable, |
| 1492 | 1596 | } |
| 1493 | | return index; |
| 1494 | | } |
| 1495 | | |
| 1496 | | pub fn now(io: Io, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp { |
| 1497 | | return io.vtable.now(io.userdata, clockid); |
| 1498 | | } |
| 1499 | | |
| 1500 | | pub fn sleep(io: Io, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void { |
| 1501 | | return io.vtable.sleep(io.userdata, clockid, deadline); |
| 1502 | 1597 | } |