| ... | @@ -979,17 +979,21 @@ pub const VTable = struct { | ... | @@ -979,17 +979,21 @@ pub const VTable = struct { |
| 979 | /// Thread-safe. | 979 | /// Thread-safe. |
| 980 | cancelRequested: *const fn (?*anyopaque) bool, | 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 | mutexLock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) Cancelable!void, | 986 | mutexLock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) Cancelable!void, |
| 983 | mutexUnlock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) void, | 987 | mutexUnlock: *const fn (?*anyopaque, prev_state: Mutex.State, mutex: *Mutex) void, |
| 984 | | 988 | |
| 985 | conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void, | 989 | conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void, |
| 986 | conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void, | 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, | 992 | createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File, |
| 989 | openFile: *const fn (?*anyopaque, dir: fs.Dir, sub_path: []const u8, flags: fs.File.OpenFlags) FileOpenError!fs.File, | 993 | openFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File, |
| 990 | closeFile: *const fn (?*anyopaque, fs.File) void, | 994 | closeFile: *const fn (?*anyopaque, File) void, |
| 991 | pread: *const fn (?*anyopaque, file: fs.File, buffer: []u8, offset: std.posix.off_t) FilePReadError!usize, | 995 | pread: *const fn (?*anyopaque, file: File, buffer: []u8, offset: std.posix.off_t) File.PReadError!usize, |
| 992 | pwrite: *const fn (?*anyopaque, file: fs.File, buffer: []const u8, offset: std.posix.off_t) FilePWriteError!usize, | 996 | pwrite: *const fn (?*anyopaque, file: File, buffer: []const u8, offset: std.posix.off_t) File.PWriteError!usize, |
| 993 | | 997 | |
| 994 | now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp, | 998 | now: *const fn (?*anyopaque, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp, |
| 995 | sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void, | 999 | sleep: *const fn (?*anyopaque, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void, |
| ... | @@ -1000,28 +1004,118 @@ pub const Cancelable = error{ | ... | @@ -1000,28 +1004,118 @@ pub const Cancelable = error{ |
| 1000 | Canceled, | 1004 | Canceled, |
| 1001 | }; | 1005 | }; |
| 1002 | | 1006 | |
| 1003 | pub const OpenFlags = fs.File.OpenFlags; | 1007 | pub const Dir = struct { |
| 1004 | pub const CreateFlags = fs.File.CreateFlags; | 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; | 1048 | pub const OpenFlags = fs.File.OpenFlags; |
| 1007 | pub const FileReadError = fs.File.ReadError || Cancelable; | 1049 | pub const CreateFlags = fs.File.CreateFlags; |
| 1008 | pub const FilePReadError = fs.File.PReadError || Cancelable; | 1050 | |
| 1009 | pub const FileWriteError = fs.File.WriteError || Cancelable; | 1051 | pub const OpenError = fs.File.OpenError || Cancelable; |
| 1010 | pub const FilePWriteError = fs.File.PWriteError || 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 | pub const Timestamp = enum(i96) { | 1099 | pub const Timestamp = enum(i96) { |
| 1013 | _, | 1100 | _, |
| 1014 | | 1101 | |
| 1015 | pub fn durationTo(from: Timestamp, to: Timestamp) i96 { | 1102 | pub fn durationTo(from: Timestamp, to: Timestamp) Duration { |
| 1016 | return @intFromEnum(to) - @intFromEnum(from); | 1103 | return .{ .nanoseconds = @intFromEnum(to) - @intFromEnum(from) }; |
| 1017 | } | 1104 | } |
| 1018 | | 1105 | |
| 1019 | pub fn addDuration(from: Timestamp, duration: i96) Timestamp { | 1106 | pub fn addDuration(from: Timestamp, duration: Duration) Timestamp { |
| 1020 | return @enumFromInt(@intFromEnum(from) + duration); | 1107 | return @enumFromInt(@intFromEnum(from) + duration.nanoseconds); |
| 1021 | } | 1108 | } |
| 1022 | }; | 1109 | }; |
| 1023 | pub const Deadline = union(enum) { | 1110 | pub const Duration = struct { |
| 1024 | nanoseconds: i96, | 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 | timestamp: Timestamp, | 1119 | timestamp: Timestamp, |
| 1026 | }; | 1120 | }; |
| 1027 | pub const ClockGetTimeError = std.posix.ClockGetTimeError || Cancelable; | 1121 | pub const ClockGetTimeError = std.posix.ClockGetTimeError || Cancelable; |
| ... | @@ -1408,7 +1502,7 @@ pub fn Queue(Elem: type) type { | ... | @@ -1408,7 +1502,7 @@ pub fn Queue(Elem: type) type { |
| 1408 | | 1502 | |
| 1409 | /// Calls `function` with `args`, such that the return value of the function is | 1503 | /// Calls `function` with `args`, such that the return value of the function is |
| 1410 | /// not guaranteed to be available until `await` is called. | 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 | const Result = @typeInfo(@TypeOf(function)).@"fn".return_type.?; | 1506 | const Result = @typeInfo(@TypeOf(function)).@"fn".return_type.?; |
| 1413 | const Args = @TypeOf(args); | 1507 | const Args = @TypeOf(args); |
| 1414 | const TypeErased = struct { | 1508 | const TypeErased = struct { |
| ... | @@ -1432,7 +1526,7 @@ pub fn async(io: Io, function: anytype, args: anytype) Future(@typeInfo(@TypeOf( | ... | @@ -1432,7 +1526,7 @@ pub fn async(io: Io, function: anytype, args: anytype) Future(@typeInfo(@TypeOf( |
| 1432 | | 1526 | |
| 1433 | /// Calls `function` with `args` asynchronously. The resource cleans itself up | 1527 | /// Calls `function` with `args` asynchronously. The resource cleans itself up |
| 1434 | /// when the function returns. Does not support await, cancel, or a return value. | 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 | const Args = @TypeOf(args); | 1530 | const Args = @TypeOf(args); |
| 1437 | const TypeErased = struct { | 1531 | const TypeErased = struct { |
| 1438 | fn start(context: *const anyopaque) void { | 1532 | fn start(context: *const anyopaque) void { |
| ... | @@ -1448,55 +1542,56 @@ pub fn go(io: Io, function: anytype, args: anytype) 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 { | 1545 | pub fn now(io: Io, clockid: std.posix.clockid_t) ClockGetTimeError!Timestamp { |
| 1452 | return io.vtable.openFile(io.userdata, dir, sub_path, flags); | 1546 | return io.vtable.now(io.userdata, clockid); |
| 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); | | |
| 1469 | } | 1547 | } |
| 1470 | | 1548 | |
| 1471 | pub fn write(io: Io, file: fs.File, buffer: []const u8) FileWriteError!usize { | 1549 | pub fn sleep(io: Io, clockid: std.posix.clockid_t, deadline: Deadline) SleepError!void { |
| 1472 | return @errorCast(io.pwrite(file, buffer, -1)); | 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 { | 1553 | pub fn sleepDuration(io: Io, duration: Duration) SleepError!void { |
| 1476 | return io.vtable.pwrite(io.userdata, file, buffer, offset); | 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 { | 1557 | /// Given a struct with each field a `*Future`, returns a union with the same |
| 1480 | var index: usize = 0; | 1558 | /// fields, each field type the future's result. |
| 1481 | while (index < bytes.len) { | 1559 | pub fn SelectUnion(S: type) type { |
| 1482 | index += try io.write(file, bytes[index..]); | 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 { | 1579 | /// `s` is a struct with every field a `*Future(T)`, where `T` can be any type, |
| 1487 | var index: usize = 0; | 1580 | /// and can be different for each field. |
| 1488 | while (index != buffer.len) { | 1581 | pub fn select(io: Io, s: anytype) SelectUnion(@TypeOf(s)) { |
| 1489 | const amt = try io.read(file, buffer[index..]); | 1582 | const U = SelectUnion(@TypeOf(s)); |
| 1490 | if (amt == 0) break; | 1583 | const S = @TypeOf(s); |
| 1491 | index += amt; | 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 | } |