| ... | @@ -626,17 +626,21 @@ pub const VTable = struct { | ... | @@ -626,17 +626,21 @@ pub const VTable = struct { |
| 626 | /// Thread-safe. | 626 | /// Thread-safe. |
| 627 | cancelRequested: *const fn (?*anyopaque) bool, | 627 | cancelRequested: *const fn (?*anyopaque) bool, |
| 628 | | 628 | |
| | 629 | /// Blocks until one of the futures from the list has a result ready, such |
| | 630 | /// that awaiting it will not block. Returns that index. |
| | 631 | select: *const fn (?*anyopaque, futures: []const *AnyFuture) usize, |
| | 632 | |
| 629 | mutexLock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) Cancelable!void, | 633 | mutexLock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) Cancelable!void, |
| 630 | mutexUnlock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) void, | 634 | mutexUnlock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) void, |
| 631 | | 635 | |
| 632 | conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void, | 636 | conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void, |
| 633 | conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void, | 637 | conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void, |
| 634 | | 638 | |
| 635 | createFile: *const fn (?*anyopaque, dir: fs.Dir, sub_path: []const u8, flags: fs.File.CreateFlags) FileOpenError!fs.File, | 639 | createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File, |
| 636 | openFile: *const fn (?*anyopaque, dir: fs.Dir, sub_path: []const u8, flags: fs.File.OpenFlags) FileOpenError!fs.File, | 640 | openFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File, |
| 637 | closeFile: *const fn (?*anyopaque, fs.File) void, | 641 | closeFile: *const fn (?*anyopaque, File) void, |
| 638 | pread: *const fn (?*anyopaque, file: fs.File, buffer: []u8, offset: std.posix.off_t) FilePReadError!usize, | 642 | pread: *const fn (?*anyopaque, file: File, buffer: []u8, offset: std.posix.off_t) File.PReadError!usize, |
| 639 | pwrite: *const fn (?*anyopaque, file: fs.File, buffer: []const u8, offset: std.posix.off_t) FilePWriteError!usize, | 643 | pwrite: *const fn (?*anyopaque, file: File, buffer: []const u8, offset: std.posix.off_t) File.PWriteError!usize, |
| 640 | | 644 | |
| 641 | now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp, | 645 | now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp, |
| 642 | sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void, | 646 | sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void, |
| ... | @@ -647,28 +651,118 @@ pub const Cancelable = error{ | ... | @@ -647,28 +651,118 @@ pub const Cancelable = error{ |
| 647 | Canceled, | 651 | Canceled, |
| 648 | }; | 652 | }; |
| 649 | | 653 | |
| 650 | pub const OpenFlags = fs.File.OpenFlags; | 654 | pub const Dir = struct { |
| 651 | pub const CreateFlags = fs.File.CreateFlags; | 655 | handle: Handle, |
| | 656 | |
| | 657 | pub fn cwd() Dir { |
| | 658 | return .{ .handle = std.fs.cwd().fd }; |
| | 659 | } |
| | 660 | |
| | 661 | pub const Handle = std.posix.fd_t; |
| | 662 | |
| | 663 | pub fn openFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { |
| | 664 | return io.vtable.openFile(io.userdata, dir, sub_path, flags); |
| | 665 | } |
| | 666 | |
| | 667 | pub fn createFile(dir: Dir, io: Io, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { |
| | 668 | return io.vtable.createFile(io.userdata, dir, sub_path, flags); |
| | 669 | } |
| | 670 | |
| | 671 | pub const WriteFileOptions = struct { |
| | 672 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). |
| | 673 | /// On WASI, `sub_path` should be encoded as valid UTF-8. |
| | 674 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. |
| | 675 | sub_path: []const u8, |
| | 676 | data: []const u8, |
| | 677 | flags: File.CreateFlags = .{}, |
| | 678 | }; |
| | 679 | |
| | 680 | pub const WriteFileError = File.WriteError || File.OpenError || Cancelable; |
| | 681 | |
| | 682 | /// Writes content to the file system, using the file creation flags provided. |
| | 683 | pub fn writeFile(dir: Dir, io: Io, options: WriteFileOptions) WriteFileError!void { |
| | 684 | var file = try dir.createFile(io, options.sub_path, options.flags); |
| | 685 | defer file.close(io); |
| | 686 | try file.writeAll(io, options.data); |
| | 687 | } |
| | 688 | }; |
| | 689 | |
| | 690 | pub const File = struct { |
| | 691 | handle: Handle, |
| | 692 | |
| | 693 | pub const Handle = std.posix.fd_t; |
| | 694 | |
| | 695 | pub const OpenFlags = fs.File.OpenFlags; |
| | 696 | pub const CreateFlags = fs.File.CreateFlags; |
| | 697 | |
| | 698 | pub const OpenError = fs.File.OpenError || Cancelable; |
| | 699 | |
| | 700 | pub fn close(file: File, io: Io) void { |
| | 701 | return io.vtable.closeFile(io.userdata, file); |
| | 702 | } |
| | 703 | |
| | 704 | pub const ReadError = fs.File.ReadError || Cancelable; |
| | 705 | |
| | 706 | pub fn read(file: File, io: Io, buffer: []u8) ReadError!usize { |
| | 707 | return @errorCast(file.pread(io, buffer, -1)); |
| | 708 | } |
| | 709 | |
| | 710 | pub const PReadError = fs.File.PReadError || Cancelable; |
| | 711 | |
| | 712 | pub fn pread(file: File, io: Io, buffer: []u8, offset: std.posix.off_t) PReadError!usize { |
| | 713 | return io.vtable.pread(io.userdata, file, buffer, offset); |
| | 714 | } |
| | 715 | |
| | 716 | pub const WriteError = fs.File.WriteError || Cancelable; |
| | 717 | |
| | 718 | pub fn write(file: File, io: Io, buffer: []const u8) WriteError!usize { |
| | 719 | return @errorCast(file.pwrite(io, buffer, -1)); |
| | 720 | } |
| | 721 | |
| | 722 | pub const PWriteError = fs.File.PWriteError || Cancelable; |
| | 723 | |
| | 724 | pub fn pwrite(file: File, io: Io, buffer: []const u8, offset: std.posix.off_t) PWriteError!usize { |
| | 725 | return io.vtable.pwrite(io.userdata, file, buffer, offset); |
| | 726 | } |
| | 727 | |
| | 728 | pub fn writeAll(file: File, io: Io, bytes: []const u8) WriteError!void { |
| | 729 | var index: usize = 0; |
| | 730 | while (index < bytes.len) { |
| | 731 | index += try file.write(io, bytes[index..]); |
| | 732 | } |
| | 733 | } |
| 652 | | 734 | |
| 653 | pub const FileOpenError = fs.File.OpenError || Cancelable; | 735 | pub fn readAll(file: File, io: Io, buffer: []u8) ReadError!usize { |
| 654 | pub const FileReadError = fs.File.ReadError || Cancelable; | 736 | var index: usize = 0; |
| 655 | pub const FilePReadError = fs.File.PReadError || Cancelable; | 737 | while (index != buffer.len) { |
| 656 | pub const FileWriteError = fs.File.WriteError || Cancelable; | 738 | const amt = try file.read(io, buffer[index..]); |
| 657 | pub const FilePWriteError = fs.File.PWriteError || Cancelable; | 739 | if (amt == 0) break; |
| | 740 | index += amt; |
| | 741 | } |
| | 742 | return index; |
| | 743 | } |
| | 744 | }; |
| 658 | | 745 | |
| 659 | pub const Timestamp = enum(i96) { | 746 | pub const Timestamp = enum(i96) { |
| 660 | _, | 747 | _, |
| 661 | | 748 | |
| 662 | pub fn durationTo(from: Timestamp, to: Timestamp) i96 { | 749 | pub fn durationTo(from: Timestamp, to: Timestamp) Duration { |
| 663 | return @intFromEnum(to) - @intFromEnum(from); | 750 | return .{ .nanoseconds = @intFromEnum(to) - @intFromEnum(from) }; |
| 664 | } | 751 | } |
| 665 | | 752 | |
| 666 | pub fn addDuration(from: Timestamp, duration: i96) Timestamp { | 753 | pub fn addDuration(from: Timestamp, duration: Duration) Timestamp { |
| 667 | return @enumFromInt(@intFromEnum(from) + duration); | 754 | return @enumFromInt(@intFromEnum(from) + duration.nanoseconds); |
| 668 | } | 755 | } |
| 669 | }; | 756 | }; |
| 670 | pub const Deadline = union(enum) { | 757 | pub const Duration = struct { |
| 671 | nanoseconds: i96, | 758 | nanoseconds: i96, |
| | 759 | |
| | 760 | pub fn ms(x: u64) Duration { |
| | 761 | return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_ms }; |
| | 762 | } |
| | 763 | }; |
| | 764 | pub const Deadline = union(enum) { |
| | 765 | duration: Duration, |
| 672 | timestamp: Timestamp, | 766 | timestamp: Timestamp, |
| 673 | }; | 767 | }; |
| 674 | pub const ClockGetTimeError = std.posix.ClockGetTimeError || Cancelable; | 768 | pub const ClockGetTimeError = std.posix.ClockGetTimeError || Cancelable; |
| ... | @@ -1055,7 +1149,7 @@ pub fn Queue(Elem: type) type { | ... | @@ -1055,7 +1149,7 @@ pub fn Queue(Elem: type) type { |
| 1055 | | 1149 | |
| 1056 | /// Calls `function` with `args`, such that the return value of the function is | 1150 | /// Calls `function` with `args`, such that the return value of the function is |
| 1057 | /// not guaranteed to be available until `await` is called. | 1151 | /// not guaranteed to be available until `await` is called. |
| 1058 | pub fn async(io: Io, function: anytype, args: anytype) Future(@typeInfo(@TypeOf(function)).@"fn".return_type.?) { | 1152 | pub fn async(io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) Future(@typeInfo(@TypeOf(function)).@"fn".return_type.?) { |
| 1059 | const Result = @typeInfo(@TypeOf(function)).@"fn".return_type.?; | 1153 | const Result = @typeInfo(@TypeOf(function)).@"fn".return_type.?; |
| 1060 | const Args = @TypeOf(args); | 1154 | const Args = @TypeOf(args); |
| 1061 | const TypeErased = struct { | 1155 | const TypeErased = struct { |
| ... | @@ -1079,7 +1173,7 @@ pub fn async(io: Io, function: anytype, args: anytype) Future(@typeInfo(@TypeOf( | ... | @@ -1079,7 +1173,7 @@ pub fn async(io: Io, function: anytype, args: anytype) Future(@typeInfo(@TypeOf( |
| 1079 | | 1173 | |
| 1080 | /// Calls `function` with `args` asynchronously. The resource cleans itself up | 1174 | /// Calls `function` with `args` asynchronously. The resource cleans itself up |
| 1081 | /// when the function returns. Does not support await, cancel, or a return value. | 1175 | /// when the function returns. Does not support await, cancel, or a return value. |
| 1082 | pub fn go(io: Io, function: anytype, args: anytype) void { | 1176 | pub fn go(io: Io, function: anytype, args: std.meta.ArgsTuple(@TypeOf(function))) void { |
| 1083 | const Args = @TypeOf(args); | 1177 | const Args = @TypeOf(args); |
| 1084 | const TypeErased = struct { | 1178 | const TypeErased = struct { |
| 1085 | fn start(context: *const anyopaque) void { | 1179 | fn start(context: *const anyopaque) void { |
| ... | @@ -1095,55 +1189,56 @@ pub fn go(io: Io, function: anytype, args: anytype) void { | ... | @@ -1095,55 +1189,56 @@ pub fn go(io: Io, function: anytype, args: anytype) void { |
| 1095 | ); | 1189 | ); |
| 1096 | } | 1190 | } |
| 1097 | | 1191 | |
| 1098 | pub fn openFile(io: Io, dir: fs.Dir, sub_path: []const u8, flags: fs.File.OpenFlags) FileOpenError!fs.File { | 1192 | pub fn now(io: Io, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp { |
| 1099 | return io.vtable.openFile(io.userdata, dir, sub_path, flags); | 1193 | return io.vtable.now(io.userdata, clockid); |
| 1100 | } | | |
| 1101 | | | |
| 1102 | pub fn createFile(io: Io, dir: fs.Dir, sub_path: []const u8, flags: fs.File.CreateFlags) FileOpenError!fs.File { | | |
| 1103 | return io.vtable.createFile(io.userdata, dir, sub_path, flags); | | |
| 1104 | } | | |
| 1105 | | | |
| 1106 | pub fn closeFile(io: Io, file: fs.File) void { | | |
| 1107 | return io.vtable.closeFile(io.userdata, file); | | |
| 1108 | } | | |
| 1109 | | | |
| 1110 | pub fn read(io: Io, file: fs.File, buffer: []u8) FileReadError!usize { | | |
| 1111 | return @errorCast(io.pread(file, buffer, -1)); | | |
| 1112 | } | | |
| 1113 | | | |
| 1114 | pub fn pread(io: Io, file: fs.File, buffer: []u8, offset: std.posix.off_t) FilePReadError!usize { | | |
| 1115 | return io.vtable.pread(io.userdata, file, buffer, offset); | | |
| 1116 | } | 1194 | } |
| 1117 | | 1195 | |
| 1118 | pub fn write(io: Io, file: fs.File, buffer: []const u8) FileWriteError!usize { | 1196 | pub fn sleep(io: Io, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void { |
| 1119 | return @errorCast(io.pwrite(file, buffer, -1)); | 1197 | return io.vtable.sleep(io.userdata, clockid, deadline); |
| 1120 | } | 1198 | } |
| 1121 | | 1199 | |
| 1122 | pub fn pwrite(io: Io, file: fs.File, buffer: []const u8, offset: std.posix.off_t) FilePWriteError!usize { | 1200 | pub fn sleepDuration(io: Io, duration: Duration) SleepError!void { |
| 1123 | return io.vtable.pwrite(io.userdata, file, buffer, offset); | 1201 | return io.vtable.sleep(io.userdata, .MONOTONIC, .{ .duration = duration }); |
| 1124 | } | 1202 | } |
| 1125 | | 1203 | |
| 1126 | pub fn writeAll(io: Io, file: fs.File, bytes: []const u8) FileWriteError!void { | 1204 | /// Given a struct with each field a `*Future`, returns a union with the same |
| 1127 | var index: usize = 0; | 1205 | /// fields, each field type the future's result. |
| 1128 | while (index < bytes.len) { | 1206 | pub fn SelectUnion(S: type) type { |
| 1129 | index += try io.write(file, bytes[index..]); | 1207 | const struct_fields = @typeInfo(S).@"struct".fields; |
| | 1208 | var fields: [struct_fields.len]std.builtin.Type.UnionField = undefined; |
| | 1209 | for (&fields, struct_fields) |*union_field, struct_field| { |
| | 1210 | const F = @typeInfo(struct_field.type).pointer.child; |
| | 1211 | const Result = @TypeOf(@as(F, undefined).result); |
| | 1212 | union_field.* = .{ |
| | 1213 | .name = struct_field.name, |
| | 1214 | .type = Result, |
| | 1215 | .alignment = struct_field.alignment, |
| | 1216 | }; |
| 1130 | } | 1217 | } |
| | 1218 | return @Type(.{ .@"union" = .{ |
| | 1219 | .layout = .auto, |
| | 1220 | .tag_type = std.meta.FieldEnum(S), |
| | 1221 | .fields = &fields, |
| | 1222 | .decls = &.{}, |
| | 1223 | } }); |
| 1131 | } | 1224 | } |
| 1132 | | 1225 | |
| 1133 | pub fn readAll(io: Io, file: fs.File, buffer: []u8) FileReadError!usize { | 1226 | /// `s` is a struct with every field a `*Future(T)`, where `T` can be any type, |
| 1134 | var index: usize = 0; | 1227 | /// and can be different for each field. |
| 1135 | while (index != buffer.len) { | 1228 | pub fn select(io: Io, s: anytype) SelectUnion(@TypeOf(s)) { |
| 1136 | const amt = try io.read(file, buffer[index..]); | 1229 | const U = SelectUnion(@TypeOf(s)); |
| 1137 | if (amt == 0) break; | 1230 | const S = @TypeOf(s); |
| 1138 | index += amt; | 1231 | const fields = @typeInfo(S).@"struct".fields; |
| | 1232 | var futures: [fields.len]*AnyFuture = undefined; |
| | 1233 | inline for (fields, &futures) |field, *any_future| { |
| | 1234 | const future = @field(s, field.name); |
| | 1235 | any_future.* = future.any_future orelse return @unionInit(U, field.name, future.result); |
| | 1236 | } |
| | 1237 | switch (io.vtable.select(io.userdata, &futures)) { |
| | 1238 | inline 0...(fields.len - 1) => |selected_index| { |
| | 1239 | const field_name = fields[selected_index].name; |
| | 1240 | return @unionInit(U, field_name, @field(s, field_name).await(io)); |
| | 1241 | }, |
| | 1242 | else => unreachable, |
| 1139 | } | 1243 | } |
| 1140 | return index; | | |
| 1141 | } | | |
| 1142 | | | |
| 1143 | pub fn now(io: Io, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp { | | |
| 1144 | return io.vtable.now(io.userdata, clockid); | | |
| 1145 | } | | |
| 1146 | | | |
| 1147 | pub fn sleep(io: Io, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void { | | |
| 1148 | return io.vtable.sleep(io.userdata, clockid, deadline); | | |
| 1149 | } | 1244 | } |