authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-03 16:15:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:48-07:00
log774df26835069039ba739828a7619393de01a5f2
tree25a6a8c5eff8043d3f08f757fe9087e4360cb388
parent00f26cb0a4d60e908719309f4daa6598316ef74e

WIP: hack at std.Io on a plane


10 files changed, 511 insertions(+), 327 deletions(-)

lib/std/Build/Cache.zig+10-4
...@@ -1317,6 +1317,8 @@ fn testGetCurrentFileTimestamp(dir: fs.Dir) !i128 {...@@ -1317,6 +1317,8 @@ fn testGetCurrentFileTimestamp(dir: fs.Dir) !i128 {
1317}1317}
13181318
1319test "cache file and then recall it" {1319test "cache file and then recall it" {
1320 const io = std.testing.io;
1321
1320 var tmp = testing.tmpDir(.{});1322 var tmp = testing.tmpDir(.{});
1321 defer tmp.cleanup();1323 defer tmp.cleanup();
13221324
...@@ -1328,7 +1330,7 @@ test "cache file and then recall it" {...@@ -1328,7 +1330,7 @@ test "cache file and then recall it" {
1328 // Wait for file timestamps to tick1330 // Wait for file timestamps to tick
1329 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);1331 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
1330 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {1332 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
1331 std.Thread.sleep(1);1333 try std.Io.Duration.sleep(.fromNanoseconds(1), io);
1332 }1334 }
13331335
1334 var digest1: HexDigest = undefined;1336 var digest1: HexDigest = undefined;
...@@ -1378,6 +1380,8 @@ test "cache file and then recall it" {...@@ -1378,6 +1380,8 @@ test "cache file and then recall it" {
1378}1380}
13791381
1380test "check that changing a file makes cache fail" {1382test "check that changing a file makes cache fail" {
1383 const io = std.testing.io;
1384
1381 var tmp = testing.tmpDir(.{});1385 var tmp = testing.tmpDir(.{});
1382 defer tmp.cleanup();1386 defer tmp.cleanup();
13831387
...@@ -1391,7 +1395,7 @@ test "check that changing a file makes cache fail" {...@@ -1391,7 +1395,7 @@ test "check that changing a file makes cache fail" {
1391 // Wait for file timestamps to tick1395 // Wait for file timestamps to tick
1392 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);1396 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
1393 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {1397 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
1394 std.Thread.sleep(1);1398 try std.Io.Duration.sleep(.fromNanoseconds(1), io);
1395 }1399 }
13961400
1397 var digest1: HexDigest = undefined;1401 var digest1: HexDigest = undefined;
...@@ -1490,6 +1494,8 @@ test "no file inputs" {...@@ -1490,6 +1494,8 @@ test "no file inputs" {
1490}1494}
14911495
1492test "Manifest with files added after initial hash work" {1496test "Manifest with files added after initial hash work" {
1497 const io = std.testing.io;
1498
1493 var tmp = testing.tmpDir(.{});1499 var tmp = testing.tmpDir(.{});
1494 defer tmp.cleanup();1500 defer tmp.cleanup();
14951501
...@@ -1503,7 +1509,7 @@ test "Manifest with files added after initial hash work" {...@@ -1503,7 +1509,7 @@ test "Manifest with files added after initial hash work" {
1503 // Wait for file timestamps to tick1509 // Wait for file timestamps to tick
1504 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);1510 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
1505 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {1511 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
1506 std.Thread.sleep(1);1512 try std.Io.Duration.sleep(.fromNanoseconds(1), io);
1507 }1513 }
15081514
1509 var digest1: HexDigest = undefined;1515 var digest1: HexDigest = undefined;
...@@ -1553,7 +1559,7 @@ test "Manifest with files added after initial hash work" {...@@ -1553,7 +1559,7 @@ test "Manifest with files added after initial hash work" {
1553 // Wait for file timestamps to tick1559 // Wait for file timestamps to tick
1554 const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir);1560 const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir);
1555 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) {1561 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) {
1556 std.Thread.sleep(1);1562 try std.Io.Duration.sleep(.fromNanoseconds(1), io);
1557 }1563 }
15581564
1559 {1565 {
lib/std/Io.zig+40-15
...@@ -670,8 +670,9 @@ pub const VTable = struct {...@@ -670,8 +670,9 @@ pub const VTable = struct {
670670
671 listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server,671 listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server,
672 accept: *const fn (?*anyopaque, server: *net.Server) net.Server.AcceptError!net.Stream,672 accept: *const fn (?*anyopaque, server: *net.Server) net.Server.AcceptError!net.Stream,
673 ipBind: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket,673 ipBind: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket,
674 netSend: *const fn (?*anyopaque, net.Socket.Handle, []net.OutgoingMessage, net.SendFlags) net.SendResult,674 ipConnect: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ConnectOptions) net.IpAddress.ConnectError!net.Stream,
675 netSend: *const fn (?*anyopaque, net.Socket.Handle, []net.OutgoingMessage, net.SendFlags) struct { ?net.Socket.SendError, usize },
675 netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize },676 netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize },
676 netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize,677 netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize,
677 netWrite: *const fn (?*anyopaque, dest: net.Stream, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize,678 netWrite: *const fn (?*anyopaque, dest: net.Stream, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize,
...@@ -710,10 +711,14 @@ pub const Timestamp = struct {...@@ -710,10 +711,14 @@ pub const Timestamp = struct {
710 /// time (e.g., if the system administrator manually changes the711 /// time (e.g., if the system administrator manually changes the
711 /// clock), and by frequency adjust‐ ments performed by NTP and similar712 /// clock), and by frequency adjust‐ ments performed by NTP and similar
712 /// applications.713 /// applications.
713 /// This clock normally counts the number of seconds since714 ///
714 /// 1970-01-01 00:00:00 Coordinated Universal Time (UTC) except that it715 /// This clock normally counts the number of seconds since 1970-01-01
715 /// ignores leap seconds; near a leap second it is typically716 /// 00:00:00 Coordinated Universal Time (UTC) except that it ignores
716 /// adjusted by NTP to stay roughly in sync with UTC.717 /// leap seconds; near a leap second it is typically adjusted by NTP to
718 /// stay roughly in sync with UTC.
719 ///
720 /// The epoch is implementation-defined. For example NTFS/Windows uses
721 /// 1601-01-01.
717 realtime,722 realtime,
718 /// A nonsettable system-wide clock that represents time since some723 /// A nonsettable system-wide clock that represents time since some
719 /// unspecified point in the past.724 /// unspecified point in the past.
...@@ -729,10 +734,16 @@ pub const Timestamp = struct {...@@ -729,10 +734,16 @@ pub const Timestamp = struct {
729 /// Guarantees that the time returned by consecutive calls will not go734 /// Guarantees that the time returned by consecutive calls will not go
730 /// backwards, but successive calls may return identical735 /// backwards, but successive calls may return identical
731 /// (not-increased) time values.736 /// (not-increased) time values.
737 ///
738 /// May or may not include time the system is suspended, but
739 /// implementations should exclude that time if possible.
732 monotonic,740 monotonic,
733 /// Identical to `monotonic` except it also includes any time that the741 /// Identical to `monotonic` except it also includes any time that the
734 /// system is suspended.742 /// system is suspended, if possible. However, it may be implemented
743 /// identically to `monotonic`.
735 boottime,744 boottime,
745 process_cputime_id,
746 thread_cputime_id,
736 };747 };
737748
738 pub fn durationTo(from: Timestamp, to: Timestamp) Duration {749 pub fn durationTo(from: Timestamp, to: Timestamp) Duration {
...@@ -791,6 +802,12 @@ pub const Timestamp = struct {...@@ -791,6 +802,12 @@ pub const Timestamp = struct {
791pub const Duration = struct {802pub const Duration = struct {
792 nanoseconds: i96,803 nanoseconds: i96,
793804
805 pub const max: Duration = .{ .nanoseconds = std.math.maxInt(i96) };
806
807 pub fn fromNanoseconds(x: i96) Duration {
808 return .{ .nanoseconds = x };
809 }
810
794 pub fn fromMilliseconds(x: i64) Duration {811 pub fn fromMilliseconds(x: i64) Duration {
795 return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_ms };812 return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_ms };
796 }813 }
...@@ -806,6 +823,10 @@ pub const Duration = struct {...@@ -806,6 +823,10 @@ pub const Duration = struct {
806 pub fn toSeconds(d: Duration) i64 {823 pub fn toSeconds(d: Duration) i64 {
807 return @intCast(@divTrunc(d.nanoseconds, std.time.ns_per_s));824 return @intCast(@divTrunc(d.nanoseconds, std.time.ns_per_s));
808 }825 }
826
827 pub fn sleep(duration: Duration, io: Io) SleepError!void {
828 return io.vtable.sleep(io.userdata, .{ .duration = .{ .duration = duration, .clock = .monotonic } });
829 }
809};830};
810831
811/// Declares under what conditions an operation should return `error.Timeout`.832/// Declares under what conditions an operation should return `error.Timeout`.
...@@ -828,6 +849,18 @@ pub const Timeout = union(enum) {...@@ -828,6 +849,18 @@ pub const Timeout = union(enum) {
828 .deadline => |d| d,849 .deadline => |d| d,
829 };850 };
830 }851 }
852
853 pub fn toDurationFromNow(t: Timeout, io: Io) Timestamp.Error!?ClockAndDuration {
854 return switch (t) {
855 .none => null,
856 .duration => |d| d,
857 .deadline => |d| .{ .clock = d.clock, .duration = try d.durationFromNow(io) },
858 };
859 }
860
861 pub fn sleep(timeout: Timeout, io: Io) SleepError!void {
862 return io.vtable.sleep(io.userdata, timeout);
863 }
831};864};
832865
833pub const AnyFuture = opaque {};866pub const AnyFuture = opaque {};
...@@ -1322,14 +1355,6 @@ pub fn cancelRequested(io: Io) bool {...@@ -1322,14 +1355,6 @@ pub fn cancelRequested(io: Io) bool {
13221355
1323pub const SleepError = error{UnsupportedClock} || UnexpectedError || Cancelable;1356pub const SleepError = error{UnsupportedClock} || UnexpectedError || Cancelable;
13241357
1325pub fn sleep(io: Io, timeout: Timeout) SleepError!void {
1326 return io.vtable.sleep(io.userdata, timeout);
1327}
1328
1329pub fn sleepDuration(io: Io, duration: Duration) SleepError!void {
1330 return io.vtable.sleep(io.userdata, .MONOTONIC, .{ .duration = duration });
1331}
1332
1333/// Given a struct with each field a `*Future`, returns a union with the same1358/// Given a struct with each field a `*Future`, returns a union with the same
1334/// fields, each field type the future's result.1359/// fields, each field type the future's result.
1335pub fn SelectUnion(S: type) type {1360pub fn SelectUnion(S: type) type {
lib/std/Io/Threaded.zig+318-61
...@@ -145,8 +145,17 @@ pub fn io(pool: *Pool) Io {...@@ -145,8 +145,17 @@ pub fn io(pool: *Pool) Io {
145 .fileSeekBy = fileSeekBy,145 .fileSeekBy = fileSeekBy,
146 .fileSeekTo = fileSeekTo,146 .fileSeekTo = fileSeekTo,
147147
148 .now = now,148 .now = switch (builtin.os.tag) {
149 .sleep = sleep,149 .windows => nowWindows,
150 .wasi => nowWasi,
151 else => nowPosix,
152 },
153 .sleep = switch (builtin.os.tag) {
154 .windows => sleepWindows,
155 .wasi => sleepWasi,
156 .linux => sleepLinux,
157 else => sleepPosix,
158 },
150159
151 .listen = switch (builtin.os.tag) {160 .listen = switch (builtin.os.tag) {
152 .windows => @panic("TODO"),161 .windows => @panic("TODO"),
...@@ -160,6 +169,10 @@ pub fn io(pool: *Pool) Io {...@@ -160,6 +169,10 @@ pub fn io(pool: *Pool) Io {
160 .windows => @panic("TODO"),169 .windows => @panic("TODO"),
161 else => ipBindPosix,170 else => ipBindPosix,
162 },171 },
172 .ipConnect = switch (builtin.os.tag) {
173 .windows => @panic("TODO"),
174 else => ipConnectPosix,
175 },
163 .netClose = netClose,176 .netClose = netClose,
164 .netRead = switch (builtin.os.tag) {177 .netRead = switch (builtin.os.tag) {
165 .windows => @panic("TODO"),178 .windows => @panic("TODO"),
...@@ -797,16 +810,15 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File...@@ -797,16 +810,15 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
797 const dest = iovecs_buffer[0..i];810 const dest = iovecs_buffer[0..i];
798 assert(dest[0].len > 0);811 assert(dest[0].len > 0);
799812
800 if (native_os == .wasi and !builtin.link_libc) {813 if (native_os == .wasi and !builtin.link_libc) while (true) {
801 try pool.checkCancel();814 try pool.checkCancel();
802 var nread: usize = undefined;815 var nread: usize = undefined;
803 switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) {816 switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) {
804 .SUCCESS => return nread,817 .SUCCESS => return nread,
805 .INTR => unreachable,818 .INTR => continue,
806 .INVAL => unreachable,819 .INVAL => |err| return errnoBug(err),
807 .FAULT => |err| return errnoBug(err),820 .FAULT => |err| return errnoBug(err),
808 .AGAIN => unreachable, // currently not support in WASI821 .BADF => |err| return errnoBug(err),
809 .BADF => return error.NotOpenForReading, // can be a race condition
810 .IO => return error.InputOutput,822 .IO => return error.InputOutput,
811 .ISDIR => return error.IsDir,823 .ISDIR => return error.IsDir,
812 .NOBUFS => return error.SystemResources,824 .NOBUFS => return error.SystemResources,
...@@ -817,15 +829,15 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File...@@ -817,15 +829,15 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
817 .NOTCAPABLE => return error.AccessDenied,829 .NOTCAPABLE => return error.AccessDenied,
818 else => |err| return posix.unexpectedErrno(err),830 else => |err| return posix.unexpectedErrno(err),
819 }831 }
820 }832 };
821833
822 while (true) {834 while (true) {
823 try pool.checkCancel();835 try pool.checkCancel();
824 const rc = posix.system.readv(file.handle, dest.ptr, dest.len);836 const rc = posix.system.readv(file.handle, dest.ptr, @intCast(dest.len));
825 switch (posix.errno(rc)) {837 switch (posix.errno(rc)) {
826 .SUCCESS => return @intCast(rc),838 .SUCCESS => return @intCast(rc),
827 .INTR => continue,839 .INTR => continue,
828 .INVAL => unreachable,840 .INVAL => |err| return errnoBug(err),
829 .FAULT => |err| return errnoBug(err),841 .FAULT => |err| return errnoBug(err),
830 .SRCH => return error.ProcessNotFound,842 .SRCH => return error.ProcessNotFound,
831 .AGAIN => return error.WouldBlock,843 .AGAIN => return error.WouldBlock,
...@@ -845,14 +857,6 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File...@@ -845,14 +857,6 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
845fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize {857fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize {
846 const pool: *Pool = @ptrCast(@alignCast(userdata));858 const pool: *Pool = @ptrCast(@alignCast(userdata));
847859
848 const have_pread_but_not_preadv = switch (native_os) {
849 .windows, .macos, .ios, .watchos, .tvos, .visionos, .haiku, .serenity => true,
850 else => false,
851 };
852 if (have_pread_but_not_preadv) {
853 @compileError("TODO");
854 }
855
856 if (is_windows) {860 if (is_windows) {
857 const DWORD = windows.DWORD;861 const DWORD = windows.DWORD;
858 const OVERLAPPED = windows.OVERLAPPED;862 const OVERLAPPED = windows.OVERLAPPED;
...@@ -907,6 +911,14 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset...@@ -907,6 +911,14 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
907 return total;911 return total;
908 }912 }
909913
914 const have_pread_but_not_preadv = switch (native_os) {
915 .windows, .haiku, .serenity => true,
916 else => false,
917 };
918 if (have_pread_but_not_preadv) {
919 @compileError("TODO");
920 }
921
910 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;922 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
911 var i: usize = 0;923 var i: usize = 0;
912 for (data) |buf| {924 for (data) |buf| {
...@@ -919,15 +931,15 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset...@@ -919,15 +931,15 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
919 const dest = iovecs_buffer[0..i];931 const dest = iovecs_buffer[0..i];
920 assert(dest[0].len > 0);932 assert(dest[0].len > 0);
921933
922 if (native_os == .wasi and !builtin.link_libc) {934 if (native_os == .wasi and !builtin.link_libc) while (true) {
923 try pool.checkCancel();935 try pool.checkCancel();
924 var nread: usize = undefined;936 var nread: usize = undefined;
925 switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) {937 switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) {
926 .SUCCESS => return nread,938 .SUCCESS => return nread,
927 .INTR => unreachable,939 .INTR => continue,
928 .INVAL => unreachable,940 .INVAL => |err| return errnoBug(err),
929 .FAULT => |err| return errnoBug(err),941 .FAULT => |err| return errnoBug(err),
930 .AGAIN => unreachable,942 .AGAIN => |err| return errnoBug(err),
931 .BADF => return error.NotOpenForReading, // can be a race condition943 .BADF => return error.NotOpenForReading, // can be a race condition
932 .IO => return error.InputOutput,944 .IO => return error.InputOutput,
933 .ISDIR => return error.IsDir,945 .ISDIR => return error.IsDir,
...@@ -942,16 +954,16 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset...@@ -942,16 +954,16 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
942 .NOTCAPABLE => return error.AccessDenied,954 .NOTCAPABLE => return error.AccessDenied,
943 else => |err| return posix.unexpectedErrno(err),955 else => |err| return posix.unexpectedErrno(err),
944 }956 }
945 }957 };
946958
947 const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;959 const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;
948 while (true) {960 while (true) {
949 try pool.checkCancel();961 try pool.checkCancel();
950 const rc = preadv_sym(file.handle, dest.ptr, dest.len, @bitCast(offset));962 const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
951 switch (posix.errno(rc)) {963 switch (posix.errno(rc)) {
952 .SUCCESS => return @bitCast(rc),964 .SUCCESS => return @bitCast(rc),
953 .INTR => continue,965 .INTR => continue,
954 .INVAL => unreachable,966 .INVAL => |err| return errnoBug(err),
955 .FAULT => |err| return errnoBug(err),967 .FAULT => |err| return errnoBug(err),
956 .SRCH => return error.ProcessNotFound,968 .SRCH => return error.ProcessNotFound,
957 .AGAIN => return error.WouldBlock,969 .AGAIN => return error.WouldBlock,
...@@ -999,7 +1011,7 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi...@@ -999,7 +1011,7 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi
999 };1011 };
1000}1012}
10011013
1002fn now(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 {1014fn nowPosix(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 {
1003 const pool: *Pool = @ptrCast(@alignCast(userdata));1015 const pool: *Pool = @ptrCast(@alignCast(userdata));
1004 _ = pool;1016 _ = pool;
1005 const clock_id: posix.clockid_t = clockToPosix(clock);1017 const clock_id: posix.clockid_t = clockToPosix(clock);
...@@ -1011,7 +1023,35 @@ fn now(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96...@@ -1011,7 +1023,35 @@ fn now(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96
1011 }1023 }
1012}1024}
10131025
1014fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {1026fn nowWindows(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 {
1027 const pool: *Pool = @ptrCast(@alignCast(userdata));
1028 _ = pool;
1029 switch (clock) {
1030 .realtime => {
1031 // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds
1032 // and uses the NTFS/Windows epoch, which is 1601-01-01.
1033 return @as(i96, windows.ntdll.RtlGetSystemTimePrecise()) * 100;
1034 },
1035 .monotonic, .boottime => {
1036 // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
1037 return .{ .timestamp = windows.QueryPerformanceCounter() };
1038 },
1039 .process_cputime_id,
1040 .thread_cputime_id,
1041 => return error.UnsupportedClock,
1042 }
1043}
1044
1045fn nowWasi(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 {
1046 const pool: *Pool = @ptrCast(@alignCast(userdata));
1047 _ = pool;
1048 var ns: std.os.wasi.timestamp_t = undefined;
1049 const err = std.os.wasi.clock_time_get(clockToWasi(clock), 1, &ns);
1050 if (err != .SUCCESS) return error.Unexpected;
1051 return ns;
1052}
1053
1054fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1015 const pool: *Pool = @ptrCast(@alignCast(userdata));1055 const pool: *Pool = @ptrCast(@alignCast(userdata));
1016 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {1056 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
1017 .none => .monotonic,1057 .none => .monotonic,
...@@ -1041,6 +1081,73 @@ fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {...@@ -1041,6 +1081,73 @@ fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1041 }1081 }
1042}1082}
10431083
1084fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1085 const pool: *Pool = @ptrCast(@alignCast(userdata));
1086 try pool.checkCancel();
1087 const ms = ms: {
1088 const duration_and_clock = (try timeout.toDurationFromNow(pool.io())) orelse
1089 break :ms std.math.maxInt(windows.DWORD);
1090 if (duration_and_clock.clock != .monotonic) return error.UnsupportedClock;
1091 break :ms std.math.lossyCast(windows.DWORD, duration_and_clock.duration.toMilliseconds());
1092 };
1093 windows.kernel32.Sleep(ms);
1094}
1095
1096fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1097 const pool: *Pool = @ptrCast(@alignCast(userdata));
1098 try pool.checkCancel();
1099
1100 const w = std.os.wasi;
1101
1102 const clock: w.subscription_clock_t = if (try timeout.toDurationFromNow(pool.io())) |d| .{
1103 .id = clockToWasi(d.clock),
1104 .timeout = std.math.lossyCast(u64, d.duration.nanoseconds),
1105 .precision = 0,
1106 .flags = 0,
1107 } else .{
1108 .id = .MONOTONIC,
1109 .timeout = std.math.maxInt(u64),
1110 .precision = 0,
1111 .flags = 0,
1112 };
1113 const in: w.subscription_t = .{
1114 .userdata = 0,
1115 .u = .{
1116 .tag = .CLOCK,
1117 .u = .{ .clock = clock },
1118 },
1119 };
1120 var event: w.event_t = undefined;
1121 var nevents: usize = undefined;
1122 _ = w.poll_oneoff(&in, &event, 1, &nevents);
1123}
1124
1125fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
1126 const pool: *Pool = @ptrCast(@alignCast(userdata));
1127 const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type;
1128 const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type;
1129
1130 var timespec: posix.timespec = t: {
1131 const d = (try timeout.toDurationFromNow(pool.io())) orelse break :t .{
1132 .sec = std.math.maxInt(sec_type),
1133 .nsec = std.math.maxInt(nsec_type),
1134 };
1135 if (d.clock != .monotonic) return error.UnsupportedClock;
1136 const ns = d.duration.nanoseconds;
1137 break :t .{
1138 .sec = @intCast(@divFloor(ns, std.time.ns_per_s)),
1139 .nsec = @intCast(@mod(ns, std.time.ns_per_s)),
1140 };
1141 };
1142 while (true) {
1143 try pool.checkCancel();
1144 switch (posix.errno(posix.system.nanosleep(&timespec, &timespec))) {
1145 .INTR => continue,
1146 else => return, // This prong handles success as well as unexpected errors.
1147 }
1148 }
1149}
1150
1044fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize {1151fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize {
1045 const pool: *Pool = @ptrCast(@alignCast(userdata));1152 const pool: *Pool = @ptrCast(@alignCast(userdata));
1046 _ = pool;1153 _ = pool;
...@@ -1091,7 +1198,7 @@ fn listenPosix(...@@ -1091,7 +1198,7 @@ fn listenPosix(
1091 errdefer posix.close(fd);1198 errdefer posix.close(fd);
1092 if (socket_flags_unsupported) while (true) {1199 if (socket_flags_unsupported) while (true) {
1093 try pool.checkCancel();1200 try pool.checkCancel();
1094 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, posix.FD_CLOEXEC))) {1201 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
1095 .SUCCESS => break,1202 .SUCCESS => break,
1096 .INTR => continue,1203 .INTR => continue,
1097 else => |err| return posix.unexpectedErrno(err),1204 else => |err| return posix.unexpectedErrno(err),
...@@ -1158,6 +1265,37 @@ fn posixBind(pool: *Pool, socket_fd: posix.socket_t, addr: *const posix.sockaddr...@@ -1158,6 +1265,37 @@ fn posixBind(pool: *Pool, socket_fd: posix.socket_t, addr: *const posix.sockaddr
1158 }1265 }
1159}1266}
11601267
1268fn posixConnect(pool: *Pool, socket_fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void {
1269 while (true) {
1270 try pool.checkCancel();
1271 switch (posix.errno(posix.system.connect(socket_fd, addr, addr_len))) {
1272 .SUCCESS => return,
1273 .INTR => continue,
1274 .ADDRINUSE => return error.AddressInUse,
1275 .ADDRNOTAVAIL => return error.AddressUnavailable,
1276 .AFNOSUPPORT => return error.AddressFamilyUnsupported,
1277 .AGAIN, .INPROGRESS => |err| return errnoBug(err),
1278 .ALREADY => return error.ConnectionPending,
1279 .BADF => |err| return errnoBug(err),
1280 .CONNREFUSED => return error.ConnectionRefused,
1281 .CONNRESET => return error.ConnectionResetByPeer,
1282 .FAULT => |err| return errnoBug(err),
1283 .ISCONN => return error.AlreadyConnected,
1284 .HOSTUNREACH => return error.HostUnreachable,
1285 .NETUNREACH => return error.NetworkUnreachable,
1286 .NOTSOCK => |err| return errnoBug(err),
1287 .PROTOTYPE => |err| return errnoBug(err),
1288 .TIMEDOUT => return error.ConnectionTimedOut,
1289 .CONNABORTED => |err| return errnoBug(err),
1290 // UNIX socket error codes:
1291 .ACCES => |err| return errnoBug(err),
1292 .PERM => |err| return errnoBug(err),
1293 .NOENT => |err| return errnoBug(err),
1294 else => |err| return posix.unexpectedErrno(err),
1295 }
1296 }
1297}
1298
1161fn posixGetSockName(pool: *Pool, socket_fd: posix.fd_t, addr: *posix.sockaddr, addr_len: *posix.socklen_t) !void {1299fn posixGetSockName(pool: *Pool, socket_fd: posix.fd_t, addr: *posix.sockaddr, addr_len: *posix.socklen_t) !void {
1162 while (true) {1300 while (true) {
1163 try pool.checkCancel();1301 try pool.checkCancel();
...@@ -1190,14 +1328,45 @@ fn setSocketOption(pool: *Pool, fd: posix.fd_t, level: i32, opt_name: u32, optio...@@ -1190,14 +1328,45 @@ fn setSocketOption(pool: *Pool, fd: posix.fd_t, level: i32, opt_name: u32, optio
1190 }1328 }
1191}1329}
11921330
1331fn ipConnectPosix(
1332 userdata: ?*anyopaque,
1333 address: *const Io.net.IpAddress,
1334 options: Io.net.IpAddress.BindOptions,
1335) Io.net.IpAddress.ConnectError!Io.net.Stream {
1336 const pool: *Pool = @ptrCast(@alignCast(userdata));
1337 const family = posixAddressFamily(address);
1338 const socket_fd = try openSocketPosix(pool, family, options);
1339 var storage: PosixAddress = undefined;
1340 var addr_len = addressToPosix(address, &storage);
1341 try posixConnect(pool, socket_fd, &storage.any, addr_len);
1342 try posixGetSockName(pool, socket_fd, &storage.any, &addr_len);
1343 return .{ .socket = .{
1344 .handle = socket_fd,
1345 .address = addressFromPosix(&storage),
1346 } };
1347}
1348
1193fn ipBindPosix(1349fn ipBindPosix(
1194 userdata: ?*anyopaque,1350 userdata: ?*anyopaque,
1195 address: Io.net.IpAddress,1351 address: *const Io.net.IpAddress,
1196 options: Io.net.IpAddress.BindOptions,1352 options: Io.net.IpAddress.BindOptions,
1197) Io.net.IpAddress.BindError!Io.net.Socket {1353) Io.net.IpAddress.BindError!Io.net.Socket {
1198 const pool: *Pool = @ptrCast(@alignCast(userdata));1354 const pool: *Pool = @ptrCast(@alignCast(userdata));
1355 const family = posixAddressFamily(address);
1356 const socket_fd = try openSocketPosix(pool, family, options);
1357 errdefer posix.close(socket_fd);
1358 var storage: PosixAddress = undefined;
1359 var addr_len = addressToPosix(address, &storage);
1360 try posixBind(pool, socket_fd, &storage.any, addr_len);
1361 try posixGetSockName(pool, socket_fd, &storage.any, &addr_len);
1362 return .{
1363 .handle = socket_fd,
1364 .address = addressFromPosix(&storage),
1365 };
1366}
1367
1368fn openSocketPosix(pool: *Pool, family: posix.sa_family_t, options: Io.net.IpAddress.BindOptions) !posix.socket_t {
1199 const mode = posixSocketMode(options.mode);1369 const mode = posixSocketMode(options.mode);
1200 const family = posixAddressFamily(&address);
1201 const protocol = posixProtocol(options.protocol);1370 const protocol = posixProtocol(options.protocol);
1202 const socket_fd = while (true) {1371 const socket_fd = while (true) {
1203 try pool.checkCancel();1372 try pool.checkCancel();
...@@ -1209,7 +1378,7 @@ fn ipBindPosix(...@@ -1209,7 +1378,7 @@ fn ipBindPosix(
1209 errdefer posix.close(fd);1378 errdefer posix.close(fd);
1210 if (socket_flags_unsupported) while (true) {1379 if (socket_flags_unsupported) while (true) {
1211 try pool.checkCancel();1380 try pool.checkCancel();
1212 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, posix.FD_CLOEXEC))) {1381 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
1213 .SUCCESS => break,1382 .SUCCESS => break,
1214 .INTR => continue,1383 .INTR => continue,
1215 else => |err| return posix.unexpectedErrno(err),1384 else => |err| return posix.unexpectedErrno(err),
...@@ -1229,19 +1398,14 @@ fn ipBindPosix(...@@ -1229,19 +1398,14 @@ fn ipBindPosix(
1229 else => |err| return posix.unexpectedErrno(err),1398 else => |err| return posix.unexpectedErrno(err),
1230 }1399 }
1231 };1400 };
1401 errdefer posix.close(socket_fd);
12321402
1233 if (options.ip6_only) {1403 if (options.ip6_only) {
1404 if (posix.IPV6 == void) return error.OptionUnsupported;
1234 try setSocketOption(pool, socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0);1405 try setSocketOption(pool, socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0);
1235 }1406 }
12361407
1237 var storage: PosixAddress = undefined;1408 return socket_fd;
1238 var addr_len = addressToPosix(&address, &storage);
1239 try posixBind(pool, socket_fd, &storage.any, addr_len);
1240 try posixGetSockName(pool, socket_fd, &storage.any, &addr_len);
1241 return .{
1242 .handle = socket_fd,
1243 .address = addressFromPosix(&storage),
1244 };
1245}1409}
12461410
1247const socket_flags_unsupported = builtin.os.tag.isDarwin() or native_os == .haiku; // 💩💩1411const socket_flags_unsupported = builtin.os.tag.isDarwin() or native_os == .haiku; // 💩💩
...@@ -1264,7 +1428,7 @@ fn acceptPosix(userdata: ?*anyopaque, server: *Io.net.Server) Io.net.Server.Acce...@@ -1264,7 +1428,7 @@ fn acceptPosix(userdata: ?*anyopaque, server: *Io.net.Server) Io.net.Server.Acce
1264 errdefer posix.close(fd);1428 errdefer posix.close(fd);
1265 if (!have_accept4) while (true) {1429 if (!have_accept4) while (true) {
1266 try pool.checkCancel();1430 try pool.checkCancel();
1267 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, posix.FD_CLOEXEC))) {1431 switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
1268 .SUCCESS => break,1432 .SUCCESS => break,
1269 .INTR => continue,1433 .INTR => continue,
1270 else => |err| return posix.unexpectedErrno(err),1434 else => |err| return posix.unexpectedErrno(err),
...@@ -1322,29 +1486,118 @@ fn netSend(...@@ -1322,29 +1486,118 @@ fn netSend(
1322 handle: Io.net.Socket.Handle,1486 handle: Io.net.Socket.Handle,
1323 messages: []Io.net.OutgoingMessage,1487 messages: []Io.net.OutgoingMessage,
1324 flags: Io.net.SendFlags,1488 flags: Io.net.SendFlags,
1325) Io.net.SendResult {1489) struct { ?Io.net.Socket.SendError, usize } {
1326 const pool: *Pool = @ptrCast(@alignCast(userdata));1490 const pool: *Pool = @ptrCast(@alignCast(userdata));
13271491
1328 if (have_sendmmsg) {1492 const posix_flags: u32 =
1329 var i: usize = 0;1493 @as(u32, if (flags.confirm) posix.MSG.CONFIRM else 0) |
1330 while (messages.len - i != 0) {1494 @as(u32, if (flags.dont_route) posix.MSG.DONTROUTE else 0) |
1331 i += netSendMany(pool, handle, messages[i..], flags) catch |err| return .{ .fail = .{1495 @as(u32, if (flags.eor) posix.MSG.EOR else 0) |
1332 .err = err,1496 @as(u32, if (flags.oob) posix.MSG.OOB else 0) |
1333 .sent = i,1497 @as(u32, if (flags.fastopen) posix.MSG.FASTOPEN else 0) |
1334 } };1498 posix.MSG.NOSIGNAL;
1499
1500 var i: usize = 0;
1501 while (messages.len - i != 0) {
1502 if (have_sendmmsg) {
1503 i += netSendMany(pool, handle, messages[i..], posix_flags) catch |err| return .{ err, i };
1504 continue;
1335 }1505 }
1336 return .success;1506 netSendOne(pool, handle, &messages[i], posix_flags) catch |err| return .{ err, i };
1507 i += 1;
1337 }1508 }
1509 return .{ null, i };
1510}
13381511
1339 try pool.checkCancel();1512fn netSendOne(
1340 @panic("TODO");1513 pool: *Pool,
1514 handle: Io.net.Socket.Handle,
1515 message: *Io.net.OutgoingMessage,
1516 flags: u32,
1517) Io.net.Socket.SendError!void {
1518 var addr: PosixAddress = undefined;
1519 var iovec: posix.iovec = .{ .base = @constCast(message.data_ptr), .len = message.data_len };
1520 const msg: posix.msghdr = .{
1521 .name = &addr.any,
1522 .namelen = addressToPosix(message.address, &addr),
1523 .iov = iovec[0..1],
1524 .iovlen = 1,
1525 .control = @constCast(message.control.ptr),
1526 .controllen = message.control.len,
1527 .flags = 0,
1528 };
1529 while (true) {
1530 try pool.checkCancel();
1531 const rc = posix.system.sendmsg(handle, msg, flags);
1532 if (is_windows) {
1533 if (rc == windows.ws2_32.SOCKET_ERROR) {
1534 switch (windows.ws2_32.WSAGetLastError()) {
1535 .WSAEACCES => return error.AccessDenied,
1536 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
1537 .WSAECONNRESET => return error.ConnectionResetByPeer,
1538 .WSAEMSGSIZE => return error.MessageTooBig,
1539 .WSAENOBUFS => return error.SystemResources,
1540 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
1541 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
1542 .WSAEDESTADDRREQ => unreachable, // A destination address is required.
1543 .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
1544 .WSAEHOSTUNREACH => return error.NetworkUnreachable,
1545 // TODO: WSAEINPROGRESS, WSAEINTR
1546 .WSAEINVAL => unreachable,
1547 .WSAENETDOWN => return error.NetworkSubsystemFailed,
1548 .WSAENETRESET => return error.ConnectionResetByPeer,
1549 .WSAENETUNREACH => return error.NetworkUnreachable,
1550 .WSAENOTCONN => return error.SocketUnconnected,
1551 .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
1552 .WSAEWOULDBLOCK => return error.WouldBlock,
1553 .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
1554 else => |err| return windows.unexpectedWSAError(err),
1555 }
1556 } else {
1557 message.data_len = @intCast(rc);
1558 return;
1559 }
1560 }
1561 switch (posix.errno(rc)) {
1562 .SUCCESS => {
1563 message.data_len = @intCast(rc);
1564 return;
1565 },
1566 .ACCES => return error.AccessDenied,
1567 .AGAIN => return error.WouldBlock,
1568 .ALREADY => return error.FastOpenAlreadyInProgress,
1569 .BADF => |err| return errnoBug(err),
1570 .CONNRESET => return error.ConnectionResetByPeer,
1571 .DESTADDRREQ => |err| return errnoBug(err),
1572 .FAULT => |err| return errnoBug(err),
1573 .INTR => continue,
1574 .INVAL => |err| return errnoBug(err),
1575 .ISCONN => |err| return errnoBug(err),
1576 .MSGSIZE => return error.MessageTooBig,
1577 .NOBUFS => return error.SystemResources,
1578 .NOMEM => return error.SystemResources,
1579 .NOTSOCK => |err| return errnoBug(err),
1580 .OPNOTSUPP => |err| return errnoBug(err),
1581 .PIPE => return error.BrokenPipe,
1582 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
1583 .LOOP => return error.SymLinkLoop,
1584 .NAMETOOLONG => return error.NameTooLong,
1585 .NOENT => return error.FileNotFound,
1586 .NOTDIR => return error.NotDir,
1587 .HOSTUNREACH => return error.NetworkUnreachable,
1588 .NETUNREACH => return error.NetworkUnreachable,
1589 .NOTCONN => return error.SocketUnconnected,
1590 .NETDOWN => return error.NetworkSubsystemFailed,
1591 else => |err| return posix.unexpectedErrno(err),
1592 }
1593 }
1341}1594}
13421595
1343fn netSendMany(1596fn netSendMany(
1344 pool: *Pool,1597 pool: *Pool,
1345 handle: Io.net.Socket.Handle,1598 handle: Io.net.Socket.Handle,
1346 messages: []Io.net.OutgoingMessage,1599 messages: []Io.net.OutgoingMessage,
1347 flags: Io.net.SendFlags,1600 flags: u32,
1348) Io.net.Socket.SendError!usize {1601) Io.net.Socket.SendError!usize {
1349 var msg_buffer: [64]std.os.linux.mmsghdr = undefined;1602 var msg_buffer: [64]std.os.linux.mmsghdr = undefined;
1350 var addr_buffer: [msg_buffer.len]PosixAddress = undefined;1603 var addr_buffer: [msg_buffer.len]PosixAddress = undefined;
...@@ -1371,17 +1624,9 @@ fn netSendMany(...@@ -1371,17 +1624,9 @@ fn netSendMany(
1371 };1624 };
1372 }1625 }
13731626
1374 const posix_flags: u32 =
1375 @as(u32, if (flags.confirm) posix.MSG.CONFIRM else 0) |
1376 @as(u32, if (flags.dont_route) posix.MSG.DONTROUTE else 0) |
1377 @as(u32, if (flags.eor) posix.MSG.EOR else 0) |
1378 @as(u32, if (flags.oob) posix.MSG.OOB else 0) |
1379 @as(u32, if (flags.fastopen) posix.MSG.FASTOPEN else 0) |
1380 posix.MSG.NOSIGNAL;
1381
1382 while (true) {1627 while (true) {
1383 try pool.checkCancel();1628 try pool.checkCancel();
1384 const rc = posix.system.sendmmsg(handle, clamped_msgs.ptr, @intCast(clamped_msgs.len), posix_flags);1629 const rc = posix.system.sendmmsg(handle, clamped_msgs.ptr, @intCast(clamped_msgs.len), flags);
1385 switch (posix.errno(rc)) {1630 switch (posix.errno(rc)) {
1386 .SUCCESS => {1631 .SUCCESS => {
1387 for (clamped_messages[0..rc], clamped_msgs[0..rc]) |*message, *msg| {1632 for (clamped_messages[0..rc], clamped_msgs[0..rc]) |*message, *msg| {
...@@ -1782,5 +2027,17 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {...@@ -1782,5 +2027,17 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {
1782 .realtime => posix.CLOCK.REALTIME,2027 .realtime => posix.CLOCK.REALTIME,
1783 .monotonic => posix.CLOCK.MONOTONIC,2028 .monotonic => posix.CLOCK.MONOTONIC,
1784 .boottime => posix.CLOCK.BOOTTIME,2029 .boottime => posix.CLOCK.BOOTTIME,
2030 .process_cputime_id => posix.CLOCK.PROCESS_CPUTIME_ID,
2031 .thread_cputime_id => posix.CLOCK.THREAD_CPUTIME_ID,
2032 };
2033}
2034
2035fn clockToWasi(clock: Io.Timestamp.Clock) std.os.wasi.clockid_t {
2036 return switch (clock) {
2037 .realtime => .REALTIME,
2038 .monotonic => .MONOTONIC,
2039 .boottime => .MONOTONIC,
2040 .process_cputime_id => .PROCESS_CPUTIME_ID,
2041 .thread_cputime_id => .THREAD_CPUTIME_ID,
1785 };2042 };
1786}2043}
lib/std/Io/net.zig+37-16
...@@ -208,6 +208,9 @@ pub const IpAddress = union(enum) {...@@ -208,6 +208,9 @@ pub const IpAddress = union(enum) {
208 /// System-wide limit on the total number of open files has been reached.208 /// System-wide limit on the total number of open files has been reached.
209 SystemFdQuotaExceeded,209 SystemFdQuotaExceeded,
210 SocketModeUnsupported,210 SocketModeUnsupported,
211 /// One of the `BindOptions` is not supported by the Io
212 /// implementation.
213 OptionUnsupported,
211 } || Io.UnexpectedError || Io.Cancelable;214 } || Io.UnexpectedError || Io.Cancelable;
212215
213 pub const BindOptions = struct {216 pub const BindOptions = struct {
...@@ -228,6 +231,29 @@ pub const IpAddress = union(enum) {...@@ -228,6 +231,29 @@ pub const IpAddress = union(enum) {
228 pub fn bind(address: IpAddress, io: Io, options: BindOptions) BindError!Socket {231 pub fn bind(address: IpAddress, io: Io, options: BindOptions) BindError!Socket {
229 return io.vtable.ipBind(io.userdata, address, options);232 return io.vtable.ipBind(io.userdata, address, options);
230 }233 }
234
235 pub const ConnectError = error{
236 AddressInUse,
237 AddressUnavailable,
238 AddressFamilyUnsupported,
239 ConnectionPending,
240 ConnectionRefused,
241 ConnectionResetByPeer,
242 AlreadyConnected,
243 HostUnreachable,
244 NetworkUnreachable,
245 ConnectionTimedOut,
246 /// One of the `ConnectOptions` is not supported by the Io
247 /// implementation.
248 OptionUnsupported,
249 } || Io.UnexpectedError || Io.Cancelable;
250
251 pub const ConnectOptions = BindOptions;
252
253 /// Initiates a connection-oriented network stream.
254 pub fn connect(address: IpAddress, io: Io, options: ConnectOptions) ConnectError!Stream {
255 return io.vtable.ipConnect(io.userdata, address, options);
256 }
231};257};
232258
233/// An IPv4 address in binary memory layout.259/// An IPv4 address in binary memory layout.
...@@ -758,14 +784,6 @@ pub const SendFlags = packed struct(u8) {...@@ -758,14 +784,6 @@ pub const SendFlags = packed struct(u8) {
758 _: u3 = 0,784 _: u3 = 0,
759};785};
760786
761pub const SendResult = union(enum) {
762 success,
763 fail: struct {
764 err: Socket.SendError,
765 sent: usize,
766 },
767};
768
769pub const Interface = struct {787pub const Interface = struct {
770 /// Value 0 indicates `none`.788 /// Value 0 indicates `none`.
771 index: u32,789 index: u32,
...@@ -978,8 +996,9 @@ pub const Socket = struct {...@@ -978,8 +996,9 @@ pub const Socket = struct {
978pub const Stream = struct {996pub const Stream = struct {
979 socket: Socket,997 socket: Socket,
980998
981 pub fn close(s: Stream, io: Io) void {999 pub fn close(s: *Stream, io: Io) void {
982 return io.vtable.netClose(io.userdata, s.socket);1000 io.vtable.netClose(io.userdata, s.socket);
1001 s.* = undefined;
983 }1002 }
9841003
985 pub const Reader = struct {1004 pub const Reader = struct {
...@@ -996,8 +1015,9 @@ pub const Stream = struct {...@@ -996,8 +1015,9 @@ pub const Stream = struct {
996 SocketUnconnected,1015 SocketUnconnected,
997 } || Io.Cancelable || Io.Writer.Error || error{EndOfStream};1016 } || Io.Cancelable || Io.Writer.Error || error{EndOfStream};
9981017
999 pub fn init(stream: Stream, buffer: []u8) Reader {1018 pub fn init(stream: Stream, io: Io, buffer: []u8) Reader {
1000 return .{1019 return .{
1020 .io = io,
1001 .interface = .{1021 .interface = .{
1002 .vtable = &.{1022 .vtable = &.{
1003 .stream = streamImpl,1023 .stream = streamImpl,
...@@ -1043,8 +1063,9 @@ pub const Stream = struct {...@@ -1043,8 +1063,9 @@ pub const Stream = struct {
1043 Unexpected,1063 Unexpected,
1044 } || Io.Cancelable;1064 } || Io.Cancelable;
10451065
1046 pub fn init(stream: Stream, buffer: []u8) Writer {1066 pub fn init(stream: Stream, io: Io, buffer: []u8) Writer {
1047 return .{1067 return .{
1068 .io = io,
1048 .stream = stream,1069 .stream = stream,
1049 .interface = .{1070 .interface = .{
1050 .vtable = &.{ .drain = drain },1071 .vtable = &.{ .drain = drain },
...@@ -1062,12 +1083,12 @@ pub const Stream = struct {...@@ -1062,12 +1083,12 @@ pub const Stream = struct {
1062 }1083 }
1063 };1084 };
10641085
1065 pub fn reader(stream: Stream, buffer: []u8) Reader {1086 pub fn reader(stream: Stream, io: Io, buffer: []u8) Reader {
1066 return .init(stream, buffer);1087 return .init(stream, io, buffer);
1067 }1088 }
10681089
1069 pub fn writer(stream: Stream, buffer: []u8) Writer {1090 pub fn writer(stream: Stream, io: Io, buffer: []u8) Writer {
1070 return .init(stream, buffer);1091 return .init(stream, io, buffer);
1071 }1092 }
1072};1093};
10731094
lib/std/Io/net/test.zig+11-11
...@@ -6,16 +6,16 @@ const testing = std.testing;...@@ -6,16 +6,16 @@ const testing = std.testing;
66
7test "parse and render IP addresses at comptime" {7test "parse and render IP addresses at comptime" {
8 comptime {8 comptime {
9 const ipv6addr = net.IpAddress.parseIp("::1", 0) catch unreachable;9 const ipv6addr = net.IpAddress.parse("::1", 0) catch unreachable;
10 try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr});10 try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr});
1111
12 const ipv4addr = net.IpAddress.parseIp("127.0.0.1", 0) catch unreachable;12 const ipv4addr = net.IpAddress.parse("127.0.0.1", 0) catch unreachable;
13 try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr});13 try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr});
1414
15 try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.parseIp("::123.123.123.123", 0));15 try testing.expectError(error.ParseFailed, net.IpAddress.parse("::123.123.123.123", 0));
16 try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.parseIp("127.01.0.1", 0));16 try testing.expectError(error.ParseFailed, net.IpAddress.parse("127.01.0.1", 0));
17 try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.resolveIp("::123.123.123.123", 0));17 try testing.expectError(error.ParseFailed, net.IpAddress.resolveIp("::123.123.123.123", 0));
18 try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.resolveIp("127.01.0.1", 0));18 try testing.expectError(error.ParseFailed, net.IpAddress.resolveIp("127.01.0.1", 0));
19 }19 }
20}20}
2121
...@@ -161,8 +161,8 @@ test "resolve DNS" {...@@ -161,8 +161,8 @@ test "resolve DNS" {
161161
162 // Resolve localhost, this should not fail.162 // Resolve localhost, this should not fail.
163 {163 {
164 const localhost_v4 = try net.IpAddress.parseIp("127.0.0.1", 80);164 const localhost_v4 = try net.IpAddress.parse("127.0.0.1", 80);
165 const localhost_v6 = try net.IpAddress.parseIp("::2", 80);165 const localhost_v6 = try net.IpAddress.parse("::2", 80);
166166
167 const result = try net.getAddressList(testing.allocator, "localhost", 80);167 const result = try net.getAddressList(testing.allocator, "localhost", 80);
168 defer result.deinit();168 defer result.deinit();
...@@ -198,7 +198,7 @@ test "listen on a port, send bytes, receive bytes" {...@@ -198,7 +198,7 @@ test "listen on a port, send bytes, receive bytes" {
198198
199 // Try only the IPv4 variant as some CI builders have no IPv6 localhost199 // Try only the IPv4 variant as some CI builders have no IPv6 localhost
200 // configured.200 // configured.
201 const localhost = try net.IpAddress.parseIp("127.0.0.1", 0);201 const localhost = try net.IpAddress.parse("127.0.0.1", 0);
202202
203 var server = try localhost.listen(.{});203 var server = try localhost.listen(.{});
204 defer server.deinit();204 defer server.deinit();
...@@ -232,7 +232,7 @@ test "listen on an in use port" {...@@ -232,7 +232,7 @@ test "listen on an in use port" {
232 return error.SkipZigTest;232 return error.SkipZigTest;
233 }233 }
234234
235 const localhost = try net.IpAddress.parseIp("127.0.0.1", 0);235 const localhost = try net.IpAddress.parse("127.0.0.1", 0);
236236
237 var server1 = try localhost.listen(.{ .reuse_address = true });237 var server1 = try localhost.listen(.{ .reuse_address = true });
238 defer server1.deinit();238 defer server1.deinit();
...@@ -351,7 +351,7 @@ test "non-blocking tcp server" {...@@ -351,7 +351,7 @@ test "non-blocking tcp server" {
351 return error.SkipZigTest;351 return error.SkipZigTest;
352 }352 }
353353
354 const localhost = try net.IpAddress.parseIp("127.0.0.1", 0);354 const localhost = try net.IpAddress.parse("127.0.0.1", 0);
355 var server = localhost.listen(.{ .force_nonblocking = true });355 var server = localhost.listen(.{ .force_nonblocking = true });
356 defer server.deinit();356 defer server.deinit();
357357
lib/std/Thread.zig+1-80
...@@ -73,10 +73,7 @@ pub const ResetEvent = enum(u32) {...@@ -73,10 +73,7 @@ pub const ResetEvent = enum(u32) {
73 /// timedWait() returns without error.73 /// timedWait() returns without error.
74 pub fn timedWait(re: *ResetEvent, timeout_ns: u64) error{Timeout}!void {74 pub fn timedWait(re: *ResetEvent, timeout_ns: u64) error{Timeout}!void {
75 if (builtin.single_threaded) switch (re.*) {75 if (builtin.single_threaded) switch (re.*) {
76 .unset => {76 .unset => return error.Timeout,
77 sleep(timeout_ns);
78 return error.Timeout;
79 },
80 .waiting => unreachable, // Invalid state.77 .waiting => unreachable, // Invalid state.
81 .is_set => return,78 .is_set => return,
82 };79 };
...@@ -142,82 +139,6 @@ pub const ResetEvent = enum(u32) {...@@ -142,82 +139,6 @@ pub const ResetEvent = enum(u32) {
142 }139 }
143};140};
144141
145/// Spurious wakeups are possible and no precision of timing is guaranteed.
146pub fn sleep(nanoseconds: u64) void {
147 if (builtin.os.tag == .windows) {
148 const big_ms_from_ns = nanoseconds / std.time.ns_per_ms;
149 const ms = math.cast(windows.DWORD, big_ms_from_ns) orelse math.maxInt(windows.DWORD);
150 windows.kernel32.Sleep(ms);
151 return;
152 }
153
154 if (builtin.os.tag == .wasi) {
155 const w = std.os.wasi;
156 const userdata: w.userdata_t = 0x0123_45678;
157 const clock: w.subscription_clock_t = .{
158 .id = .MONOTONIC,
159 .timeout = nanoseconds,
160 .precision = 0,
161 .flags = 0,
162 };
163 const in: w.subscription_t = .{
164 .userdata = userdata,
165 .u = .{
166 .tag = .CLOCK,
167 .u = .{ .clock = clock },
168 },
169 };
170
171 var event: w.event_t = undefined;
172 var nevents: usize = undefined;
173 _ = w.poll_oneoff(&in, &event, 1, &nevents);
174 return;
175 }
176
177 if (builtin.os.tag == .uefi) {
178 const boot_services = std.os.uefi.system_table.boot_services.?;
179 const us_from_ns = nanoseconds / std.time.ns_per_us;
180 const us = math.cast(usize, us_from_ns) orelse math.maxInt(usize);
181 boot_services.stall(us) catch unreachable;
182 return;
183 }
184
185 const s = nanoseconds / std.time.ns_per_s;
186 const ns = nanoseconds % std.time.ns_per_s;
187
188 // Newer kernel ports don't have old `nanosleep()` and `clock_nanosleep()` has been around
189 // since Linux 2.6 and glibc 2.1 anyway.
190 if (builtin.os.tag == .linux) {
191 const linux = std.os.linux;
192
193 var req: linux.timespec = .{
194 .sec = std.math.cast(linux.time_t, s) orelse std.math.maxInt(linux.time_t),
195 .nsec = std.math.cast(linux.time_t, ns) orelse std.math.maxInt(linux.time_t),
196 };
197 var rem: linux.timespec = undefined;
198
199 while (true) {
200 switch (linux.E.init(linux.clock_nanosleep(.MONOTONIC, .{ .ABSTIME = false }, &req, &rem))) {
201 .SUCCESS => return,
202 .INTR => {
203 req = rem;
204 continue;
205 },
206 .FAULT => unreachable,
207 .INVAL => unreachable,
208 .OPNOTSUPP => unreachable,
209 else => return,
210 }
211 }
212 }
213
214 posix.nanosleep(s, ns);
215}
216
217test sleep {
218 sleep(1);
219}
220
221const Thread = @This();142const Thread = @This();
222const Impl = if (native_os == .windows)143const Impl = if (native_os == .windows)
223 WindowsThreadImpl144 WindowsThreadImpl
lib/std/fs/test.zig+3-1
...@@ -2265,6 +2265,8 @@ test "seekTo flushes buffered data" {...@@ -2265,6 +2265,8 @@ test "seekTo flushes buffered data" {
2265 var tmp = std.testing.tmpDir(.{});2265 var tmp = std.testing.tmpDir(.{});
2266 defer tmp.cleanup();2266 defer tmp.cleanup();
22672267
2268 const io = std.testing.io;
2269
2268 const contents = "data";2270 const contents = "data";
22692271
2270 const file = try tmp.dir.createFile("seek.bin", .{ .read = true });2272 const file = try tmp.dir.createFile("seek.bin", .{ .read = true });
...@@ -2279,7 +2281,7 @@ test "seekTo flushes buffered data" {...@@ -2279,7 +2281,7 @@ test "seekTo flushes buffered data" {
2279 }2281 }
22802282
2281 var read_buffer: [16]u8 = undefined;2283 var read_buffer: [16]u8 = undefined;
2282 var file_reader: std.fs.File.Reader = .init(file, &read_buffer);2284 var file_reader: std.Io.File.Reader = .init(file, io, &read_buffer);
22832285
2284 var buf: [4]u8 = undefined;2286 var buf: [4]u8 = undefined;
2285 try file_reader.interface.readSliceAll(&buf);2287 try file_reader.interface.readSliceAll(&buf);
lib/std/http/test.zig+87-69
...@@ -1,27 +1,31 @@...@@ -1,27 +1,31 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const native_endian = builtin.cpu.arch.endian();
3
2const std = @import("std");4const std = @import("std");
3const http = std.http;5const http = std.http;
4const mem = std.mem;6const mem = std.mem;
5const native_endian = builtin.cpu.arch.endian();7const net = std.Io.net;
8const Io = std.Io;
6const expect = std.testing.expect;9const expect = std.testing.expect;
7const expectEqual = std.testing.expectEqual;10const expectEqual = std.testing.expectEqual;
8const expectEqualStrings = std.testing.expectEqualStrings;11const expectEqualStrings = std.testing.expectEqualStrings;
9const expectError = std.testing.expectError;12const expectError = std.testing.expectError;
1013
11test "trailers" {14test "trailers" {
12 const test_server = try createTestServer(struct {15 const io = std.testing.io;
16 const test_server = try createTestServer(io, struct {
13 fn run(test_server: *TestServer) anyerror!void {17 fn run(test_server: *TestServer) anyerror!void {
14 const net_server = &test_server.net_server;18 const net_server = &test_server.net_server;
15 var recv_buffer: [1024]u8 = undefined;19 var recv_buffer: [1024]u8 = undefined;
16 var send_buffer: [1024]u8 = undefined;20 var send_buffer: [1024]u8 = undefined;
17 var remaining: usize = 1;21 var remaining: usize = 1;
18 while (remaining != 0) : (remaining -= 1) {22 while (remaining != 0) : (remaining -= 1) {
19 const connection = try net_server.accept();23 var stream = try net_server.accept(io);
20 defer connection.stream.close();24 defer stream.close(io);
2125
22 var connection_br = connection.stream.reader(&recv_buffer);26 var connection_br = stream.reader(io, &recv_buffer);
23 var connection_bw = connection.stream.writer(&send_buffer);27 var connection_bw = stream.writer(io, &send_buffer);
24 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);28 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
2529
26 try expectEqual(.ready, server.reader.state);30 try expectEqual(.ready, server.reader.state);
27 var request = try server.receiveHead();31 var request = try server.receiveHead();
...@@ -92,17 +96,18 @@ test "trailers" {...@@ -92,17 +96,18 @@ test "trailers" {
92}96}
9397
94test "HTTP server handles a chunked transfer coding request" {98test "HTTP server handles a chunked transfer coding request" {
95 const test_server = try createTestServer(struct {99 const io = std.testing.io;
100 const test_server = try createTestServer(io, struct {
96 fn run(test_server: *TestServer) anyerror!void {101 fn run(test_server: *TestServer) anyerror!void {
97 const net_server = &test_server.net_server;102 const net_server = &test_server.net_server;
98 var recv_buffer: [8192]u8 = undefined;103 var recv_buffer: [8192]u8 = undefined;
99 var send_buffer: [500]u8 = undefined;104 var send_buffer: [500]u8 = undefined;
100 const connection = try net_server.accept();105 var stream = try net_server.accept(io);
101 defer connection.stream.close();106 defer stream.close(io);
102107
103 var connection_br = connection.stream.reader(&recv_buffer);108 var connection_br = stream.reader(io, &recv_buffer);
104 var connection_bw = connection.stream.writer(&send_buffer);109 var connection_bw = stream.writer(io, &send_buffer);
105 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);110 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
106 var request = try server.receiveHead();111 var request = try server.receiveHead();
107112
108 try expect(request.head.transfer_encoding == .chunked);113 try expect(request.head.transfer_encoding == .chunked);
...@@ -137,8 +142,8 @@ test "HTTP server handles a chunked transfer coding request" {...@@ -137,8 +142,8 @@ test "HTTP server handles a chunked transfer coding request" {
137 "\r\n";142 "\r\n";
138143
139 const gpa = std.testing.allocator;144 const gpa = std.testing.allocator;
140 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());145 var stream = try net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
141 defer stream.close();146 defer stream.close(io);
142 var stream_writer = stream.writer(&.{});147 var stream_writer = stream.writer(&.{});
143 try stream_writer.interface.writeAll(request_bytes);148 try stream_writer.interface.writeAll(request_bytes);
144149
...@@ -156,19 +161,20 @@ test "HTTP server handles a chunked transfer coding request" {...@@ -156,19 +161,20 @@ test "HTTP server handles a chunked transfer coding request" {
156}161}
157162
158test "echo content server" {163test "echo content server" {
159 const test_server = try createTestServer(struct {164 const io = std.testing.io;
165 const test_server = try createTestServer(io, struct {
160 fn run(test_server: *TestServer) anyerror!void {166 fn run(test_server: *TestServer) anyerror!void {
161 const net_server = &test_server.net_server;167 const net_server = &test_server.net_server;
162 var recv_buffer: [1024]u8 = undefined;168 var recv_buffer: [1024]u8 = undefined;
163 var send_buffer: [100]u8 = undefined;169 var send_buffer: [100]u8 = undefined;
164170
165 accept: while (!test_server.shutting_down) {171 accept: while (!test_server.shutting_down) {
166 const connection = try net_server.accept();172 var stream = try net_server.accept(io);
167 defer connection.stream.close();173 defer stream.close(io);
168174
169 var connection_br = connection.stream.reader(&recv_buffer);175 var connection_br = stream.reader(io, &recv_buffer);
170 var connection_bw = connection.stream.writer(&send_buffer);176 var connection_bw = stream.writer(io, &send_buffer);
171 var http_server = http.Server.init(connection_br.interface(), &connection_bw.interface);177 var http_server = http.Server.init(&connection_br.interface, &connection_bw.interface);
172178
173 while (http_server.reader.state == .ready) {179 while (http_server.reader.state == .ready) {
174 var request = http_server.receiveHead() catch |err| switch (err) {180 var request = http_server.receiveHead() catch |err| switch (err) {
...@@ -243,6 +249,8 @@ test "echo content server" {...@@ -243,6 +249,8 @@ test "echo content server" {
243}249}
244250
245test "Server.Request.respondStreaming non-chunked, unknown content-length" {251test "Server.Request.respondStreaming non-chunked, unknown content-length" {
252 const io = std.testing.io;
253
246 if (builtin.os.tag == .windows) {254 if (builtin.os.tag == .windows) {
247 // https://github.com/ziglang/zig/issues/21457255 // https://github.com/ziglang/zig/issues/21457
248 return error.SkipZigTest;256 return error.SkipZigTest;
...@@ -250,19 +258,19 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {...@@ -250,19 +258,19 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
250258
251 // In this case, the response is expected to stream until the connection is259 // In this case, the response is expected to stream until the connection is
252 // closed, indicating the end of the body.260 // closed, indicating the end of the body.
253 const test_server = try createTestServer(struct {261 const test_server = try createTestServer(io, struct {
254 fn run(test_server: *TestServer) anyerror!void {262 fn run(test_server: *TestServer) anyerror!void {
255 const net_server = &test_server.net_server;263 const net_server = &test_server.net_server;
256 var recv_buffer: [1000]u8 = undefined;264 var recv_buffer: [1000]u8 = undefined;
257 var send_buffer: [500]u8 = undefined;265 var send_buffer: [500]u8 = undefined;
258 var remaining: usize = 1;266 var remaining: usize = 1;
259 while (remaining != 0) : (remaining -= 1) {267 while (remaining != 0) : (remaining -= 1) {
260 const connection = try net_server.accept();268 var stream = try net_server.accept(io);
261 defer connection.stream.close();269 defer stream.close(io);
262270
263 var connection_br = connection.stream.reader(&recv_buffer);271 var connection_br = stream.reader(io, &recv_buffer);
264 var connection_bw = connection.stream.writer(&send_buffer);272 var connection_bw = stream.writer(io, &send_buffer);
265 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);273 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
266274
267 try expectEqual(.ready, server.reader.state);275 try expectEqual(.ready, server.reader.state);
268 var request = try server.receiveHead();276 var request = try server.receiveHead();
...@@ -287,8 +295,8 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {...@@ -287,8 +295,8 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
287295
288 const request_bytes = "GET /foo HTTP/1.1\r\n\r\n";296 const request_bytes = "GET /foo HTTP/1.1\r\n\r\n";
289 const gpa = std.testing.allocator;297 const gpa = std.testing.allocator;
290 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());298 var stream = try net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
291 defer stream.close();299 defer stream.close(io);
292 var stream_writer = stream.writer(&.{});300 var stream_writer = stream.writer(&.{});
293 try stream_writer.interface.writeAll(request_bytes);301 try stream_writer.interface.writeAll(request_bytes);
294302
...@@ -316,19 +324,21 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {...@@ -316,19 +324,21 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
316}324}
317325
318test "receiving arbitrary http headers from the client" {326test "receiving arbitrary http headers from the client" {
319 const test_server = try createTestServer(struct {327 const io = std.testing.io;
328
329 const test_server = try createTestServer(io, struct {
320 fn run(test_server: *TestServer) anyerror!void {330 fn run(test_server: *TestServer) anyerror!void {
321 const net_server = &test_server.net_server;331 const net_server = &test_server.net_server;
322 var recv_buffer: [666]u8 = undefined;332 var recv_buffer: [666]u8 = undefined;
323 var send_buffer: [777]u8 = undefined;333 var send_buffer: [777]u8 = undefined;
324 var remaining: usize = 1;334 var remaining: usize = 1;
325 while (remaining != 0) : (remaining -= 1) {335 while (remaining != 0) : (remaining -= 1) {
326 const connection = try net_server.accept();336 var stream = try net_server.accept(io);
327 defer connection.stream.close();337 defer stream.close(io);
328338
329 var connection_br = connection.stream.reader(&recv_buffer);339 var connection_br = stream.reader(io, &recv_buffer);
330 var connection_bw = connection.stream.writer(&send_buffer);340 var connection_bw = stream.writer(io, &send_buffer);
331 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);341 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
332342
333 try expectEqual(.ready, server.reader.state);343 try expectEqual(.ready, server.reader.state);
334 var request = try server.receiveHead();344 var request = try server.receiveHead();
...@@ -357,8 +367,8 @@ test "receiving arbitrary http headers from the client" {...@@ -357,8 +367,8 @@ test "receiving arbitrary http headers from the client" {
357 "aoeu: asdf \r\n" ++367 "aoeu: asdf \r\n" ++
358 "\r\n";368 "\r\n";
359 const gpa = std.testing.allocator;369 const gpa = std.testing.allocator;
360 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());370 var stream = try net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
361 defer stream.close();371 defer stream.close(io);
362 var stream_writer = stream.writer(&.{});372 var stream_writer = stream.writer(&.{});
363 try stream_writer.interface.writeAll(request_bytes);373 try stream_writer.interface.writeAll(request_bytes);
364374
...@@ -376,24 +386,26 @@ test "receiving arbitrary http headers from the client" {...@@ -376,24 +386,26 @@ test "receiving arbitrary http headers from the client" {
376}386}
377387
378test "general client/server API coverage" {388test "general client/server API coverage" {
389 const io = std.testing.io;
390
379 if (builtin.os.tag == .windows) {391 if (builtin.os.tag == .windows) {
380 // This test was never passing on Windows.392 // This test was never passing on Windows.
381 return error.SkipZigTest;393 return error.SkipZigTest;
382 }394 }
383395
384 const test_server = try createTestServer(struct {396 const test_server = try createTestServer(io, struct {
385 fn run(test_server: *TestServer) anyerror!void {397 fn run(test_server: *TestServer) anyerror!void {
386 const net_server = &test_server.net_server;398 const net_server = &test_server.net_server;
387 var recv_buffer: [1024]u8 = undefined;399 var recv_buffer: [1024]u8 = undefined;
388 var send_buffer: [100]u8 = undefined;400 var send_buffer: [100]u8 = undefined;
389401
390 outer: while (!test_server.shutting_down) {402 outer: while (!test_server.shutting_down) {
391 var connection = try net_server.accept();403 var stream = try net_server.accept(io);
392 defer connection.stream.close();404 defer stream.close(io);
393405
394 var connection_br = connection.stream.reader(&recv_buffer);406 var connection_br = stream.reader(io, &recv_buffer);
395 var connection_bw = connection.stream.writer(&send_buffer);407 var connection_bw = stream.writer(io, &send_buffer);
396 var http_server = http.Server.init(connection_br.interface(), &connection_bw.interface);408 var http_server = http.Server.init(&connection_br.interface, &connection_bw.interface);
397409
398 while (http_server.reader.state == .ready) {410 while (http_server.reader.state == .ready) {
399 var request = http_server.receiveHead() catch |err| switch (err) {411 var request = http_server.receiveHead() catch |err| switch (err) {
...@@ -530,7 +542,7 @@ test "general client/server API coverage" {...@@ -530,7 +542,7 @@ test "general client/server API coverage" {
530 }542 }
531543
532 fn getUnusedTcpPort() !u16 {544 fn getUnusedTcpPort() !u16 {
533 const addr = try std.net.Address.parseIp("127.0.0.1", 0);545 const addr = try net.IpAddress.parse("127.0.0.1", 0);
534 var s = try addr.listen(.{});546 var s = try addr.listen(.{});
535 defer s.deinit();547 defer s.deinit();
536 return s.listen_address.in.getPort();548 return s.listen_address.in.getPort();
...@@ -867,18 +879,20 @@ test "general client/server API coverage" {...@@ -867,18 +879,20 @@ test "general client/server API coverage" {
867}879}
868880
869test "Server streams both reading and writing" {881test "Server streams both reading and writing" {
870 const test_server = try createTestServer(struct {882 const io = std.testing.io;
883
884 const test_server = try createTestServer(io, struct {
871 fn run(test_server: *TestServer) anyerror!void {885 fn run(test_server: *TestServer) anyerror!void {
872 const net_server = &test_server.net_server;886 const net_server = &test_server.net_server;
873 var recv_buffer: [1024]u8 = undefined;887 var recv_buffer: [1024]u8 = undefined;
874 var send_buffer: [777]u8 = undefined;888 var send_buffer: [777]u8 = undefined;
875889
876 const connection = try net_server.accept();890 var stream = try net_server.accept(io);
877 defer connection.stream.close();891 defer stream.close(io);
878892
879 var connection_br = connection.stream.reader(&recv_buffer);893 var connection_br = stream.reader(io, &recv_buffer);
880 var connection_bw = connection.stream.writer(&send_buffer);894 var connection_bw = stream.writer(io, &send_buffer);
881 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);895 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
882 var request = try server.receiveHead();896 var request = try server.receiveHead();
883 var read_buffer: [100]u8 = undefined;897 var read_buffer: [100]u8 = undefined;
884 var br = try request.readerExpectContinue(&read_buffer);898 var br = try request.readerExpectContinue(&read_buffer);
...@@ -1077,11 +1091,11 @@ fn echoTests(client: *http.Client, port: u16) !void {...@@ -1077,11 +1091,11 @@ fn echoTests(client: *http.Client, port: u16) !void {
1077const TestServer = struct {1091const TestServer = struct {
1078 shutting_down: bool,1092 shutting_down: bool,
1079 server_thread: std.Thread,1093 server_thread: std.Thread,
1080 net_server: std.net.Server,1094 net_server: net.Server,
10811095
1082 fn destroy(self: *@This()) void {1096 fn destroy(self: *@This()) void {
1083 self.shutting_down = true;1097 self.shutting_down = true;
1084 const conn = std.net.tcpConnectToAddress(self.net_server.listen_address) catch @panic("shutdown failure");1098 const conn = net.tcpConnectToAddress(self.net_server.listen_address) catch @panic("shutdown failure");
1085 conn.close();1099 conn.close();
10861100
1087 self.server_thread.join();1101 self.server_thread.join();
...@@ -1090,21 +1104,21 @@ const TestServer = struct {...@@ -1090,21 +1104,21 @@ const TestServer = struct {
1090 }1104 }
10911105
1092 fn port(self: @This()) u16 {1106 fn port(self: @This()) u16 {
1093 return self.net_server.listen_address.in.getPort();1107 return self.net_server.socket.address.getPort();
1094 }1108 }
1095};1109};
10961110
1097fn createTestServer(S: type) !*TestServer {1111fn createTestServer(io: Io, S: type) !*TestServer {
1098 if (builtin.single_threaded) return error.SkipZigTest;1112 if (builtin.single_threaded) return error.SkipZigTest;
1099 if (builtin.zig_backend == .stage2_llvm and native_endian == .big) {1113 if (builtin.zig_backend == .stage2_llvm and native_endian == .big) {
1100 // https://github.com/ziglang/zig/issues/137821114 // https://github.com/ziglang/zig/issues/13782
1101 return error.SkipZigTest;1115 return error.SkipZigTest;
1102 }1116 }
11031117
1104 const address = try std.net.Address.parseIp("127.0.0.1", 0);1118 const address = try net.IpAddress.parse("127.0.0.1", 0);
1105 const test_server = try std.testing.allocator.create(TestServer);1119 const test_server = try std.testing.allocator.create(TestServer);
1106 test_server.* = .{1120 test_server.* = .{
1107 .net_server = try address.listen(.{ .reuse_address = true }),1121 .net_server = try address.listen(io, .{ .reuse_address = true }),
1108 .shutting_down = false,1122 .shutting_down = false,
1109 .server_thread = try std.Thread.spawn(.{}, S.run, .{test_server}),1123 .server_thread = try std.Thread.spawn(.{}, S.run, .{test_server}),
1110 };1124 };
...@@ -1112,18 +1126,19 @@ fn createTestServer(S: type) !*TestServer {...@@ -1112,18 +1126,19 @@ fn createTestServer(S: type) !*TestServer {
1112}1126}
11131127
1114test "redirect to different connection" {1128test "redirect to different connection" {
1115 const test_server_new = try createTestServer(struct {1129 const io = std.testing.io;
1130 const test_server_new = try createTestServer(io, struct {
1116 fn run(test_server: *TestServer) anyerror!void {1131 fn run(test_server: *TestServer) anyerror!void {
1117 const net_server = &test_server.net_server;1132 const net_server = &test_server.net_server;
1118 var recv_buffer: [888]u8 = undefined;1133 var recv_buffer: [888]u8 = undefined;
1119 var send_buffer: [777]u8 = undefined;1134 var send_buffer: [777]u8 = undefined;
11201135
1121 const connection = try net_server.accept();1136 var stream = try net_server.accept(io);
1122 defer connection.stream.close();1137 defer stream.close(io);
11231138
1124 var connection_br = connection.stream.reader(&recv_buffer);1139 var connection_br = stream.reader(io, &recv_buffer);
1125 var connection_bw = connection.stream.writer(&send_buffer);1140 var connection_bw = stream.writer(io, &send_buffer);
1126 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);1141 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
1127 var request = try server.receiveHead();1142 var request = try server.receiveHead();
1128 try expectEqualStrings(request.head.target, "/ok");1143 try expectEqualStrings(request.head.target, "/ok");
1129 try request.respond("good job, you pass", .{});1144 try request.respond("good job, you pass", .{});
...@@ -1136,23 +1151,23 @@ test "redirect to different connection" {...@@ -1136,23 +1151,23 @@ test "redirect to different connection" {
1136 };1151 };
1137 global.other_port = test_server_new.port();1152 global.other_port = test_server_new.port();
11381153
1139 const test_server_orig = try createTestServer(struct {1154 const test_server_orig = try createTestServer(io, struct {
1140 fn run(test_server: *TestServer) anyerror!void {1155 fn run(test_server: *TestServer) anyerror!void {
1141 const net_server = &test_server.net_server;1156 const net_server = &test_server.net_server;
1142 var recv_buffer: [999]u8 = undefined;1157 var recv_buffer: [999]u8 = undefined;
1143 var send_buffer: [100]u8 = undefined;1158 var send_buffer: [100]u8 = undefined;
11441159
1145 const connection = try net_server.accept();1160 var stream = try net_server.accept(io);
1146 defer connection.stream.close();1161 defer stream.close(io);
11471162
1148 var loc_buf: [50]u8 = undefined;1163 var loc_buf: [50]u8 = undefined;
1149 const new_loc = try std.fmt.bufPrint(&loc_buf, "http://127.0.0.1:{d}/ok", .{1164 const new_loc = try std.fmt.bufPrint(&loc_buf, "http://127.0.0.1:{d}/ok", .{
1150 global.other_port.?,1165 global.other_port.?,
1151 });1166 });
11521167
1153 var connection_br = connection.stream.reader(&recv_buffer);1168 var connection_br = stream.reader(io, &recv_buffer);
1154 var connection_bw = connection.stream.writer(&send_buffer);1169 var connection_bw = stream.writer(io, &send_buffer);
1155 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);1170 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
1156 var request = try server.receiveHead();1171 var request = try server.receiveHead();
1157 try expectEqualStrings(request.head.target, "/help");1172 try expectEqualStrings(request.head.target, "/help");
1158 try request.respond("", .{1173 try request.respond("", .{
...@@ -1167,7 +1182,10 @@ test "redirect to different connection" {...@@ -1167,7 +1182,10 @@ test "redirect to different connection" {
11671182
1168 const gpa = std.testing.allocator;1183 const gpa = std.testing.allocator;
11691184
1170 var client: http.Client = .{ .allocator = gpa };1185 var client: http.Client = .{
1186 .allocator = gpa,
1187 .io = io,
1188 };
1171 defer client.deinit();1189 defer client.deinit();
11721190
1173 var loc_buf: [100]u8 = undefined;1191 var loc_buf: [100]u8 = undefined;
lib/std/posix/test.zig+1-1
...@@ -637,7 +637,7 @@ test "shutdown socket" {...@@ -637,7 +637,7 @@ test "shutdown socket" {
637 error.SocketUnconnected => {},637 error.SocketUnconnected => {},
638 else => |e| return e,638 else => |e| return e,
639 };639 };
640 std.net.Stream.close(.{ .handle = sock });640 std.posix.close(sock);
641}641}
642642
643test "sigrtmin/max" {643test "sigrtmin/max" {
lib/std/time.zig+3-69
...@@ -8,74 +8,6 @@ const posix = std.posix;...@@ -8,74 +8,6 @@ const posix = std.posix;
88
9pub const epoch = @import("time/epoch.zig");9pub const epoch = @import("time/epoch.zig");
1010
11/// Get a calendar timestamp, in seconds, relative to UTC 1970-01-01.
12/// Precision of timing depends on the hardware and operating system.
13/// The return value is signed because it is possible to have a date that is
14/// before the epoch.
15/// See `posix.clock_gettime` for a POSIX timestamp.
16pub fn timestamp() i64 {
17 return @divFloor(milliTimestamp(), ms_per_s);
18}
19
20/// Get a calendar timestamp, in milliseconds, relative to UTC 1970-01-01.
21/// Precision of timing depends on the hardware and operating system.
22/// The return value is signed because it is possible to have a date that is
23/// before the epoch.
24/// See `posix.clock_gettime` for a POSIX timestamp.
25pub fn milliTimestamp() i64 {
26 return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_ms)));
27}
28
29/// Get a calendar timestamp, in microseconds, relative to UTC 1970-01-01.
30/// Precision of timing depends on the hardware and operating system.
31/// The return value is signed because it is possible to have a date that is
32/// before the epoch.
33/// See `posix.clock_gettime` for a POSIX timestamp.
34pub fn microTimestamp() i64 {
35 return @as(i64, @intCast(@divFloor(nanoTimestamp(), ns_per_us)));
36}
37
38/// Get a calendar timestamp, in nanoseconds, relative to UTC 1970-01-01.
39/// Precision of timing depends on the hardware and operating system.
40/// On Windows this has a maximum granularity of 100 nanoseconds.
41/// The return value is signed because it is possible to have a date that is
42/// before the epoch.
43/// See `posix.clock_gettime` for a POSIX timestamp.
44pub fn nanoTimestamp() i128 {
45 switch (builtin.os.tag) {
46 .windows => {
47 // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch,
48 // which is 1601-01-01.
49 const epoch_adj = epoch.windows * (ns_per_s / 100);
50 return @as(i128, windows.ntdll.RtlGetSystemTimePrecise() + epoch_adj) * 100;
51 },
52 .wasi => {
53 var ns: std.os.wasi.timestamp_t = undefined;
54 const err = std.os.wasi.clock_time_get(.REALTIME, 1, &ns);
55 assert(err == .SUCCESS);
56 return ns;
57 },
58 .uefi => {
59 const value, _ = std.os.uefi.system_table.runtime_services.getTime() catch return 0;
60 return value.toEpoch();
61 },
62 else => {
63 const ts = posix.clock_gettime(.REALTIME) catch |err| switch (err) {
64 error.UnsupportedClock, error.Unexpected => return 0, // "Precision of timing depends on hardware and OS".
65 };
66 return (@as(i128, ts.sec) * ns_per_s) + ts.nsec;
67 },
68 }
69}
70
71test milliTimestamp {
72 const time_0 = milliTimestamp();
73 std.Thread.sleep(ns_per_ms);
74 const time_1 = milliTimestamp();
75 const interval = time_1 - time_0;
76 try testing.expect(interval > 0);
77}
78
79// Divisions of a nanosecond.11// Divisions of a nanosecond.
80pub const ns_per_us = 1000;12pub const ns_per_us = 1000;
81pub const ns_per_ms = 1000 * ns_per_us;13pub const ns_per_ms = 1000 * ns_per_us;
...@@ -268,9 +200,11 @@ pub const Timer = struct {...@@ -268,9 +200,11 @@ pub const Timer = struct {
268};200};
269201
270test Timer {202test Timer {
203 const io = std.testing.io;
204
271 var timer = try Timer.start();205 var timer = try Timer.start();
272206
273 std.Thread.sleep(10 * ns_per_ms);207 try std.Io.Duration.sleep(.fromMilliseconds(10), io);
274 const time_0 = timer.read();208 const time_0 = timer.read();
275 try testing.expect(time_0 > 0);209 try testing.expect(time_0 > 0);
276210