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 {
13171317}
13181318
13191319test "cache file and then recall it" {
1320 const io = std.testing.io;
1321
13201322 var tmp = testing.tmpDir(.{});
13211323 defer tmp.cleanup();
13221324
......@@ -1328,7 +1330,7 @@ test "cache file and then recall it" {
13281330 // Wait for file timestamps to tick
13291331 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
13301332 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
1331 std.Thread.sleep(1);
1333 try std.Io.Duration.sleep(.fromNanoseconds(1), io);
13321334 }
13331335
13341336 var digest1: HexDigest = undefined;
......@@ -1378,6 +1380,8 @@ test "cache file and then recall it" {
13781380}
13791381
13801382test "check that changing a file makes cache fail" {
1383 const io = std.testing.io;
1384
13811385 var tmp = testing.tmpDir(.{});
13821386 defer tmp.cleanup();
13831387
......@@ -1391,7 +1395,7 @@ test "check that changing a file makes cache fail" {
13911395 // Wait for file timestamps to tick
13921396 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
13931397 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
1394 std.Thread.sleep(1);
1398 try std.Io.Duration.sleep(.fromNanoseconds(1), io);
13951399 }
13961400
13971401 var digest1: HexDigest = undefined;
......@@ -1490,6 +1494,8 @@ test "no file inputs" {
14901494}
14911495
14921496test "Manifest with files added after initial hash work" {
1497 const io = std.testing.io;
1498
14931499 var tmp = testing.tmpDir(.{});
14941500 defer tmp.cleanup();
14951501
......@@ -1503,7 +1509,7 @@ test "Manifest with files added after initial hash work" {
15031509 // Wait for file timestamps to tick
15041510 const initial_time = try testGetCurrentFileTimestamp(tmp.dir);
15051511 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) {
1506 std.Thread.sleep(1);
1512 try std.Io.Duration.sleep(.fromNanoseconds(1), io);
15071513 }
15081514
15091515 var digest1: HexDigest = undefined;
......@@ -1553,7 +1559,7 @@ test "Manifest with files added after initial hash work" {
15531559 // Wait for file timestamps to tick
15541560 const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir);
15551561 while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) {
1556 std.Thread.sleep(1);
1562 try std.Io.Duration.sleep(.fromNanoseconds(1), io);
15571563 }
15581564
15591565 {
lib/std/Io.zig+40-15
......@@ -670,8 +670,9 @@ pub const VTable = struct {
670670
671671 listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server,
672672 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,
674 netSend: *const fn (?*anyopaque, net.Socket.Handle, []net.OutgoingMessage, net.SendFlags) net.SendResult,
673 ipBind: *const fn (?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket,
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 },
675676 netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize },
676677 netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize,
677678 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 {
710711 /// time (e.g., if the system administrator manually changes the
711712 /// clock), and by frequency adjust‐ ments performed by NTP and similar
712713 /// applications.
713 /// This clock normally counts the number of seconds since
714 /// 1970-01-01 00:00:00 Coordinated Universal Time (UTC) except that it
715 /// ignores leap seconds; near a leap second it is typically
716 /// adjusted by NTP to stay roughly in sync with UTC.
714 ///
715 /// This clock normally counts the number of seconds since 1970-01-01
716 /// 00:00:00 Coordinated Universal Time (UTC) except that it ignores
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.
717722 realtime,
718723 /// A nonsettable system-wide clock that represents time since some
719724 /// unspecified point in the past.
......@@ -729,10 +734,16 @@ pub const Timestamp = struct {
729734 /// Guarantees that the time returned by consecutive calls will not go
730735 /// backwards, but successive calls may return identical
731736 /// (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.
732740 monotonic,
733741 /// 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`.
735744 boottime,
745 process_cputime_id,
746 thread_cputime_id,
736747 };
737748
738749 pub fn durationTo(from: Timestamp, to: Timestamp) Duration {
......@@ -791,6 +802,12 @@ pub const Timestamp = struct {
791802pub const Duration = struct {
792803 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
794811 pub fn fromMilliseconds(x: i64) Duration {
795812 return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_ms };
796813 }
......@@ -806,6 +823,10 @@ pub const Duration = struct {
806823 pub fn toSeconds(d: Duration) i64 {
807824 return @intCast(@divTrunc(d.nanoseconds, std.time.ns_per_s));
808825 }
826
827 pub fn sleep(duration: Duration, io: Io) SleepError!void {
828 return io.vtable.sleep(io.userdata, .{ .duration = .{ .duration = duration, .clock = .monotonic } });
829 }
809830};
810831
811832/// Declares under what conditions an operation should return `error.Timeout`.
......@@ -828,6 +849,18 @@ pub const Timeout = union(enum) {
828849 .deadline => |d| d,
829850 };
830851 }
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 }
831864};
832865
833866pub const AnyFuture = opaque {};
......@@ -1322,14 +1355,6 @@ pub fn cancelRequested(io: Io) bool {
13221355
13231356pub 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
13331358/// Given a struct with each field a `*Future`, returns a union with the same
13341359/// fields, each field type the future's result.
13351360pub fn SelectUnion(S: type) type {
lib/std/Io/Threaded.zig+318-61
......@@ -145,8 +145,17 @@ pub fn io(pool: *Pool) Io {
145145 .fileSeekBy = fileSeekBy,
146146 .fileSeekTo = fileSeekTo,
147147
148 .now = now,
149 .sleep = sleep,
148 .now = switch (builtin.os.tag) {
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
151160 .listen = switch (builtin.os.tag) {
152161 .windows => @panic("TODO"),
......@@ -160,6 +169,10 @@ pub fn io(pool: *Pool) Io {
160169 .windows => @panic("TODO"),
161170 else => ipBindPosix,
162171 },
172 .ipConnect = switch (builtin.os.tag) {
173 .windows => @panic("TODO"),
174 else => ipConnectPosix,
175 },
163176 .netClose = netClose,
164177 .netRead = switch (builtin.os.tag) {
165178 .windows => @panic("TODO"),
......@@ -797,16 +810,15 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
797810 const dest = iovecs_buffer[0..i];
798811 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) {
801814 try pool.checkCancel();
802815 var nread: usize = undefined;
803816 switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) {
804817 .SUCCESS => return nread,
805 .INTR => unreachable,
806 .INVAL => unreachable,
818 .INTR => continue,
819 .INVAL => |err| return errnoBug(err),
807820 .FAULT => |err| return errnoBug(err),
808 .AGAIN => unreachable, // currently not support in WASI
809 .BADF => return error.NotOpenForReading, // can be a race condition
821 .BADF => |err| return errnoBug(err),
810822 .IO => return error.InputOutput,
811823 .ISDIR => return error.IsDir,
812824 .NOBUFS => return error.SystemResources,
......@@ -817,15 +829,15 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
817829 .NOTCAPABLE => return error.AccessDenied,
818830 else => |err| return posix.unexpectedErrno(err),
819831 }
820 }
832 };
821833
822834 while (true) {
823835 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));
825837 switch (posix.errno(rc)) {
826838 .SUCCESS => return @intCast(rc),
827839 .INTR => continue,
828 .INVAL => unreachable,
840 .INVAL => |err| return errnoBug(err),
829841 .FAULT => |err| return errnoBug(err),
830842 .SRCH => return error.ProcessNotFound,
831843 .AGAIN => return error.WouldBlock,
......@@ -845,14 +857,6 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File
845857fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize {
846858 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
856860 if (is_windows) {
857861 const DWORD = windows.DWORD;
858862 const OVERLAPPED = windows.OVERLAPPED;
......@@ -907,6 +911,14 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
907911 return total;
908912 }
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
910922 var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
911923 var i: usize = 0;
912924 for (data) |buf| {
......@@ -919,15 +931,15 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
919931 const dest = iovecs_buffer[0..i];
920932 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) {
923935 try pool.checkCancel();
924936 var nread: usize = undefined;
925937 switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) {
926938 .SUCCESS => return nread,
927 .INTR => unreachable,
928 .INVAL => unreachable,
939 .INTR => continue,
940 .INVAL => |err| return errnoBug(err),
929941 .FAULT => |err| return errnoBug(err),
930 .AGAIN => unreachable,
942 .AGAIN => |err| return errnoBug(err),
931943 .BADF => return error.NotOpenForReading, // can be a race condition
932944 .IO => return error.InputOutput,
933945 .ISDIR => return error.IsDir,
......@@ -942,16 +954,16 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset
942954 .NOTCAPABLE => return error.AccessDenied,
943955 else => |err| return posix.unexpectedErrno(err),
944956 }
945 }
957 };
946958
947959 const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;
948960 while (true) {
949961 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));
951963 switch (posix.errno(rc)) {
952964 .SUCCESS => return @bitCast(rc),
953965 .INTR => continue,
954 .INVAL => unreachable,
966 .INVAL => |err| return errnoBug(err),
955967 .FAULT => |err| return errnoBug(err),
956968 .SRCH => return error.ProcessNotFound,
957969 .AGAIN => return error.WouldBlock,
......@@ -999,7 +1011,7 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi
9991011 };
10001012}
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 {
10031015 const pool: *Pool = @ptrCast(@alignCast(userdata));
10041016 _ = pool;
10051017 const clock_id: posix.clockid_t = clockToPosix(clock);
......@@ -1011,7 +1023,35 @@ fn now(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96
10111023 }
10121024}
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 {
10151055 const pool: *Pool = @ptrCast(@alignCast(userdata));
10161056 const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
10171057 .none => .monotonic,
......@@ -1041,6 +1081,73 @@ fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
10411081 }
10421082}
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
10441151fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize {
10451152 const pool: *Pool = @ptrCast(@alignCast(userdata));
10461153 _ = pool;
......@@ -1091,7 +1198,7 @@ fn listenPosix(
10911198 errdefer posix.close(fd);
10921199 if (socket_flags_unsupported) while (true) {
10931200 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)))) {
10951202 .SUCCESS => break,
10961203 .INTR => continue,
10971204 else => |err| return posix.unexpectedErrno(err),
......@@ -1158,6 +1265,37 @@ fn posixBind(pool: *Pool, socket_fd: posix.socket_t, addr: *const posix.sockaddr
11581265 }
11591266}
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
11611299fn posixGetSockName(pool: *Pool, socket_fd: posix.fd_t, addr: *posix.sockaddr, addr_len: *posix.socklen_t) !void {
11621300 while (true) {
11631301 try pool.checkCancel();
......@@ -1190,14 +1328,45 @@ fn setSocketOption(pool: *Pool, fd: posix.fd_t, level: i32, opt_name: u32, optio
11901328 }
11911329}
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
11931349fn ipBindPosix(
11941350 userdata: ?*anyopaque,
1195 address: Io.net.IpAddress,
1351 address: *const Io.net.IpAddress,
11961352 options: Io.net.IpAddress.BindOptions,
11971353) Io.net.IpAddress.BindError!Io.net.Socket {
11981354 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 {
11991369 const mode = posixSocketMode(options.mode);
1200 const family = posixAddressFamily(&address);
12011370 const protocol = posixProtocol(options.protocol);
12021371 const socket_fd = while (true) {
12031372 try pool.checkCancel();
......@@ -1209,7 +1378,7 @@ fn ipBindPosix(
12091378 errdefer posix.close(fd);
12101379 if (socket_flags_unsupported) while (true) {
12111380 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)))) {
12131382 .SUCCESS => break,
12141383 .INTR => continue,
12151384 else => |err| return posix.unexpectedErrno(err),
......@@ -1229,19 +1398,14 @@ fn ipBindPosix(
12291398 else => |err| return posix.unexpectedErrno(err),
12301399 }
12311400 };
1401 errdefer posix.close(socket_fd);
12321402
12331403 if (options.ip6_only) {
1404 if (posix.IPV6 == void) return error.OptionUnsupported;
12341405 try setSocketOption(pool, socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0);
12351406 }
12361407
1237 var storage: PosixAddress = undefined;
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 };
1408 return socket_fd;
12451409}
12461410
12471411const 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
12641428 errdefer posix.close(fd);
12651429 if (!have_accept4) while (true) {
12661430 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)))) {
12681432 .SUCCESS => break,
12691433 .INTR => continue,
12701434 else => |err| return posix.unexpectedErrno(err),
......@@ -1322,29 +1486,118 @@ fn netSend(
13221486 handle: Io.net.Socket.Handle,
13231487 messages: []Io.net.OutgoingMessage,
13241488 flags: Io.net.SendFlags,
1325) Io.net.SendResult {
1489) struct { ?Io.net.Socket.SendError, usize } {
13261490 const pool: *Pool = @ptrCast(@alignCast(userdata));
13271491
1328 if (have_sendmmsg) {
1329 var i: usize = 0;
1330 while (messages.len - i != 0) {
1331 i += netSendMany(pool, handle, messages[i..], flags) catch |err| return .{ .fail = .{
1332 .err = err,
1333 .sent = i,
1334 } };
1492 const posix_flags: u32 =
1493 @as(u32, if (flags.confirm) posix.MSG.CONFIRM else 0) |
1494 @as(u32, if (flags.dont_route) posix.MSG.DONTROUTE else 0) |
1495 @as(u32, if (flags.eor) posix.MSG.EOR else 0) |
1496 @as(u32, if (flags.oob) posix.MSG.OOB else 0) |
1497 @as(u32, if (flags.fastopen) posix.MSG.FASTOPEN else 0) |
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;
13351505 }
1336 return .success;
1506 netSendOne(pool, handle, &messages[i], posix_flags) catch |err| return .{ err, i };
1507 i += 1;
13371508 }
1509 return .{ null, i };
1510}
13381511
1339 try pool.checkCancel();
1340 @panic("TODO");
1512fn netSendOne(
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 }
13411594}
13421595
13431596fn netSendMany(
13441597 pool: *Pool,
13451598 handle: Io.net.Socket.Handle,
13461599 messages: []Io.net.OutgoingMessage,
1347 flags: Io.net.SendFlags,
1600 flags: u32,
13481601) Io.net.Socket.SendError!usize {
13491602 var msg_buffer: [64]std.os.linux.mmsghdr = undefined;
13501603 var addr_buffer: [msg_buffer.len]PosixAddress = undefined;
......@@ -1371,17 +1624,9 @@ fn netSendMany(
13711624 };
13721625 }
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
13821627 while (true) {
13831628 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);
13851630 switch (posix.errno(rc)) {
13861631 .SUCCESS => {
13871632 for (clamped_messages[0..rc], clamped_msgs[0..rc]) |*message, *msg| {
......@@ -1782,5 +2027,17 @@ fn clockToPosix(clock: Io.Timestamp.Clock) posix.clockid_t {
17822027 .realtime => posix.CLOCK.REALTIME,
17832028 .monotonic => posix.CLOCK.MONOTONIC,
17842029 .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,
17852042 };
17862043}
lib/std/Io/net.zig+37-16
......@@ -208,6 +208,9 @@ pub const IpAddress = union(enum) {
208208 /// System-wide limit on the total number of open files has been reached.
209209 SystemFdQuotaExceeded,
210210 SocketModeUnsupported,
211 /// One of the `BindOptions` is not supported by the Io
212 /// implementation.
213 OptionUnsupported,
211214 } || Io.UnexpectedError || Io.Cancelable;
212215
213216 pub const BindOptions = struct {
......@@ -228,6 +231,29 @@ pub const IpAddress = union(enum) {
228231 pub fn bind(address: IpAddress, io: Io, options: BindOptions) BindError!Socket {
229232 return io.vtable.ipBind(io.userdata, address, options);
230233 }
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 }
231257};
232258
233259/// An IPv4 address in binary memory layout.
......@@ -758,14 +784,6 @@ pub const SendFlags = packed struct(u8) {
758784 _: u3 = 0,
759785};
760786
761pub const SendResult = union(enum) {
762 success,
763 fail: struct {
764 err: Socket.SendError,
765 sent: usize,
766 },
767};
768
769787pub const Interface = struct {
770788 /// Value 0 indicates `none`.
771789 index: u32,
......@@ -978,8 +996,9 @@ pub const Socket = struct {
978996pub const Stream = struct {
979997 socket: Socket,
980998
981 pub fn close(s: Stream, io: Io) void {
982 return io.vtable.netClose(io.userdata, s.socket);
999 pub fn close(s: *Stream, io: Io) void {
1000 io.vtable.netClose(io.userdata, s.socket);
1001 s.* = undefined;
9831002 }
9841003
9851004 pub const Reader = struct {
......@@ -996,8 +1015,9 @@ pub const Stream = struct {
9961015 SocketUnconnected,
9971016 } || 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 {
10001019 return .{
1020 .io = io,
10011021 .interface = .{
10021022 .vtable = &.{
10031023 .stream = streamImpl,
......@@ -1043,8 +1063,9 @@ pub const Stream = struct {
10431063 Unexpected,
10441064 } || Io.Cancelable;
10451065
1046 pub fn init(stream: Stream, buffer: []u8) Writer {
1066 pub fn init(stream: Stream, io: Io, buffer: []u8) Writer {
10471067 return .{
1068 .io = io,
10481069 .stream = stream,
10491070 .interface = .{
10501071 .vtable = &.{ .drain = drain },
......@@ -1062,12 +1083,12 @@ pub const Stream = struct {
10621083 }
10631084 };
10641085
1065 pub fn reader(stream: Stream, buffer: []u8) Reader {
1066 return .init(stream, buffer);
1086 pub fn reader(stream: Stream, io: Io, buffer: []u8) Reader {
1087 return .init(stream, io, buffer);
10671088 }
10681089
1069 pub fn writer(stream: Stream, buffer: []u8) Writer {
1070 return .init(stream, buffer);
1090 pub fn writer(stream: Stream, io: Io, buffer: []u8) Writer {
1091 return .init(stream, io, buffer);
10711092 }
10721093};
10731094
lib/std/Io/net/test.zig+11-11
......@@ -6,16 +6,16 @@ const testing = std.testing;
66
77test "parse and render IP addresses at comptime" {
88 comptime {
9 const ipv6addr = net.IpAddress.parseIp("::1", 0) catch unreachable;
9 const ipv6addr = net.IpAddress.parse("::1", 0) catch unreachable;
1010 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;
1313 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));
16 try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.parseIp("127.01.0.1", 0));
17 try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.resolveIp("::123.123.123.123", 0));
18 try testing.expectError(error.InvalidIpAddressFormat, net.IpAddress.resolveIp("127.01.0.1", 0));
15 try testing.expectError(error.ParseFailed, net.IpAddress.parse("::123.123.123.123", 0));
16 try testing.expectError(error.ParseFailed, net.IpAddress.parse("127.01.0.1", 0));
17 try testing.expectError(error.ParseFailed, net.IpAddress.resolveIp("::123.123.123.123", 0));
18 try testing.expectError(error.ParseFailed, net.IpAddress.resolveIp("127.01.0.1", 0));
1919 }
2020}
2121
......@@ -161,8 +161,8 @@ test "resolve DNS" {
161161
162162 // Resolve localhost, this should not fail.
163163 {
164 const localhost_v4 = try net.IpAddress.parseIp("127.0.0.1", 80);
165 const localhost_v6 = try net.IpAddress.parseIp("::2", 80);
164 const localhost_v4 = try net.IpAddress.parse("127.0.0.1", 80);
165 const localhost_v6 = try net.IpAddress.parse("::2", 80);
166166
167167 const result = try net.getAddressList(testing.allocator, "localhost", 80);
168168 defer result.deinit();
......@@ -198,7 +198,7 @@ test "listen on a port, send bytes, receive bytes" {
198198
199199 // Try only the IPv4 variant as some CI builders have no IPv6 localhost
200200 // 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
203203 var server = try localhost.listen(.{});
204204 defer server.deinit();
......@@ -232,7 +232,7 @@ test "listen on an in use port" {
232232 return error.SkipZigTest;
233233 }
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
237237 var server1 = try localhost.listen(.{ .reuse_address = true });
238238 defer server1.deinit();
......@@ -351,7 +351,7 @@ test "non-blocking tcp server" {
351351 return error.SkipZigTest;
352352 }
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);
355355 var server = localhost.listen(.{ .force_nonblocking = true });
356356 defer server.deinit();
357357
lib/std/Thread.zig+1-80
......@@ -73,10 +73,7 @@ pub const ResetEvent = enum(u32) {
7373 /// timedWait() returns without error.
7474 pub fn timedWait(re: *ResetEvent, timeout_ns: u64) error{Timeout}!void {
7575 if (builtin.single_threaded) switch (re.*) {
76 .unset => {
77 sleep(timeout_ns);
78 return error.Timeout;
79 },
76 .unset => return error.Timeout,
8077 .waiting => unreachable, // Invalid state.
8178 .is_set => return,
8279 };
......@@ -142,82 +139,6 @@ pub const ResetEvent = enum(u32) {
142139 }
143140};
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
221142const Thread = @This();
222143const Impl = if (native_os == .windows)
223144 WindowsThreadImpl
lib/std/fs/test.zig+3-1
......@@ -2265,6 +2265,8 @@ test "seekTo flushes buffered data" {
22652265 var tmp = std.testing.tmpDir(.{});
22662266 defer tmp.cleanup();
22672267
2268 const io = std.testing.io;
2269
22682270 const contents = "data";
22692271
22702272 const file = try tmp.dir.createFile("seek.bin", .{ .read = true });
......@@ -2279,7 +2281,7 @@ test "seekTo flushes buffered data" {
22792281 }
22802282
22812283 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
22842286 var buf: [4]u8 = undefined;
22852287 try file_reader.interface.readSliceAll(&buf);
lib/std/http/test.zig+87-69
......@@ -1,27 +1,31 @@
11const builtin = @import("builtin");
2const native_endian = builtin.cpu.arch.endian();
3
24const std = @import("std");
35const http = std.http;
46const mem = std.mem;
5const native_endian = builtin.cpu.arch.endian();
7const net = std.Io.net;
8const Io = std.Io;
69const expect = std.testing.expect;
710const expectEqual = std.testing.expectEqual;
811const expectEqualStrings = std.testing.expectEqualStrings;
912const expectError = std.testing.expectError;
1013
1114test "trailers" {
12 const test_server = try createTestServer(struct {
15 const io = std.testing.io;
16 const test_server = try createTestServer(io, struct {
1317 fn run(test_server: *TestServer) anyerror!void {
1418 const net_server = &test_server.net_server;
1519 var recv_buffer: [1024]u8 = undefined;
1620 var send_buffer: [1024]u8 = undefined;
1721 var remaining: usize = 1;
1822 while (remaining != 0) : (remaining -= 1) {
19 const connection = try net_server.accept();
20 defer connection.stream.close();
23 var stream = try net_server.accept(io);
24 defer stream.close(io);
2125
22 var connection_br = connection.stream.reader(&recv_buffer);
23 var connection_bw = connection.stream.writer(&send_buffer);
24 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
26 var connection_br = stream.reader(io, &recv_buffer);
27 var connection_bw = stream.writer(io, &send_buffer);
28 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
2529
2630 try expectEqual(.ready, server.reader.state);
2731 var request = try server.receiveHead();
......@@ -92,17 +96,18 @@ test "trailers" {
9296}
9397
9498test "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 {
96101 fn run(test_server: *TestServer) anyerror!void {
97102 const net_server = &test_server.net_server;
98103 var recv_buffer: [8192]u8 = undefined;
99104 var send_buffer: [500]u8 = undefined;
100 const connection = try net_server.accept();
101 defer connection.stream.close();
105 var stream = try net_server.accept(io);
106 defer stream.close(io);
102107
103 var connection_br = connection.stream.reader(&recv_buffer);
104 var connection_bw = connection.stream.writer(&send_buffer);
105 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
108 var connection_br = stream.reader(io, &recv_buffer);
109 var connection_bw = stream.writer(io, &send_buffer);
110 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
106111 var request = try server.receiveHead();
107112
108113 try expect(request.head.transfer_encoding == .chunked);
......@@ -137,8 +142,8 @@ test "HTTP server handles a chunked transfer coding request" {
137142 "\r\n";
138143
139144 const gpa = std.testing.allocator;
140 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
141 defer stream.close();
145 var stream = try net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
146 defer stream.close(io);
142147 var stream_writer = stream.writer(&.{});
143148 try stream_writer.interface.writeAll(request_bytes);
144149
......@@ -156,19 +161,20 @@ test "HTTP server handles a chunked transfer coding request" {
156161}
157162
158163test "echo content server" {
159 const test_server = try createTestServer(struct {
164 const io = std.testing.io;
165 const test_server = try createTestServer(io, struct {
160166 fn run(test_server: *TestServer) anyerror!void {
161167 const net_server = &test_server.net_server;
162168 var recv_buffer: [1024]u8 = undefined;
163169 var send_buffer: [100]u8 = undefined;
164170
165171 accept: while (!test_server.shutting_down) {
166 const connection = try net_server.accept();
167 defer connection.stream.close();
172 var stream = try net_server.accept(io);
173 defer stream.close(io);
168174
169 var connection_br = connection.stream.reader(&recv_buffer);
170 var connection_bw = connection.stream.writer(&send_buffer);
171 var http_server = http.Server.init(connection_br.interface(), &connection_bw.interface);
175 var connection_br = stream.reader(io, &recv_buffer);
176 var connection_bw = stream.writer(io, &send_buffer);
177 var http_server = http.Server.init(&connection_br.interface, &connection_bw.interface);
172178
173179 while (http_server.reader.state == .ready) {
174180 var request = http_server.receiveHead() catch |err| switch (err) {
......@@ -243,6 +249,8 @@ test "echo content server" {
243249}
244250
245251test "Server.Request.respondStreaming non-chunked, unknown content-length" {
252 const io = std.testing.io;
253
246254 if (builtin.os.tag == .windows) {
247255 // https://github.com/ziglang/zig/issues/21457
248256 return error.SkipZigTest;
......@@ -250,19 +258,19 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
250258
251259 // In this case, the response is expected to stream until the connection is
252260 // closed, indicating the end of the body.
253 const test_server = try createTestServer(struct {
261 const test_server = try createTestServer(io, struct {
254262 fn run(test_server: *TestServer) anyerror!void {
255263 const net_server = &test_server.net_server;
256264 var recv_buffer: [1000]u8 = undefined;
257265 var send_buffer: [500]u8 = undefined;
258266 var remaining: usize = 1;
259267 while (remaining != 0) : (remaining -= 1) {
260 const connection = try net_server.accept();
261 defer connection.stream.close();
268 var stream = try net_server.accept(io);
269 defer stream.close(io);
262270
263 var connection_br = connection.stream.reader(&recv_buffer);
264 var connection_bw = connection.stream.writer(&send_buffer);
265 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
271 var connection_br = stream.reader(io, &recv_buffer);
272 var connection_bw = stream.writer(io, &send_buffer);
273 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
266274
267275 try expectEqual(.ready, server.reader.state);
268276 var request = try server.receiveHead();
......@@ -287,8 +295,8 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
287295
288296 const request_bytes = "GET /foo HTTP/1.1\r\n\r\n";
289297 const gpa = std.testing.allocator;
290 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
291 defer stream.close();
298 var stream = try net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
299 defer stream.close(io);
292300 var stream_writer = stream.writer(&.{});
293301 try stream_writer.interface.writeAll(request_bytes);
294302
......@@ -316,19 +324,21 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" {
316324}
317325
318326test "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 {
320330 fn run(test_server: *TestServer) anyerror!void {
321331 const net_server = &test_server.net_server;
322332 var recv_buffer: [666]u8 = undefined;
323333 var send_buffer: [777]u8 = undefined;
324334 var remaining: usize = 1;
325335 while (remaining != 0) : (remaining -= 1) {
326 const connection = try net_server.accept();
327 defer connection.stream.close();
336 var stream = try net_server.accept(io);
337 defer stream.close(io);
328338
329 var connection_br = connection.stream.reader(&recv_buffer);
330 var connection_bw = connection.stream.writer(&send_buffer);
331 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
339 var connection_br = stream.reader(io, &recv_buffer);
340 var connection_bw = stream.writer(io, &send_buffer);
341 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
332342
333343 try expectEqual(.ready, server.reader.state);
334344 var request = try server.receiveHead();
......@@ -357,8 +367,8 @@ test "receiving arbitrary http headers from the client" {
357367 "aoeu: asdf \r\n" ++
358368 "\r\n";
359369 const gpa = std.testing.allocator;
360 const stream = try std.net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
361 defer stream.close();
370 var stream = try net.tcpConnectToHost(gpa, "127.0.0.1", test_server.port());
371 defer stream.close(io);
362372 var stream_writer = stream.writer(&.{});
363373 try stream_writer.interface.writeAll(request_bytes);
364374
......@@ -376,24 +386,26 @@ test "receiving arbitrary http headers from the client" {
376386}
377387
378388test "general client/server API coverage" {
389 const io = std.testing.io;
390
379391 if (builtin.os.tag == .windows) {
380392 // This test was never passing on Windows.
381393 return error.SkipZigTest;
382394 }
383395
384 const test_server = try createTestServer(struct {
396 const test_server = try createTestServer(io, struct {
385397 fn run(test_server: *TestServer) anyerror!void {
386398 const net_server = &test_server.net_server;
387399 var recv_buffer: [1024]u8 = undefined;
388400 var send_buffer: [100]u8 = undefined;
389401
390402 outer: while (!test_server.shutting_down) {
391 var connection = try net_server.accept();
392 defer connection.stream.close();
403 var stream = try net_server.accept(io);
404 defer stream.close(io);
393405
394 var connection_br = connection.stream.reader(&recv_buffer);
395 var connection_bw = connection.stream.writer(&send_buffer);
396 var http_server = http.Server.init(connection_br.interface(), &connection_bw.interface);
406 var connection_br = stream.reader(io, &recv_buffer);
407 var connection_bw = stream.writer(io, &send_buffer);
408 var http_server = http.Server.init(&connection_br.interface, &connection_bw.interface);
397409
398410 while (http_server.reader.state == .ready) {
399411 var request = http_server.receiveHead() catch |err| switch (err) {
......@@ -530,7 +542,7 @@ test "general client/server API coverage" {
530542 }
531543
532544 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);
534546 var s = try addr.listen(.{});
535547 defer s.deinit();
536548 return s.listen_address.in.getPort();
......@@ -867,18 +879,20 @@ test "general client/server API coverage" {
867879}
868880
869881test "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 {
871885 fn run(test_server: *TestServer) anyerror!void {
872886 const net_server = &test_server.net_server;
873887 var recv_buffer: [1024]u8 = undefined;
874888 var send_buffer: [777]u8 = undefined;
875889
876 const connection = try net_server.accept();
877 defer connection.stream.close();
890 var stream = try net_server.accept(io);
891 defer stream.close(io);
878892
879 var connection_br = connection.stream.reader(&recv_buffer);
880 var connection_bw = connection.stream.writer(&send_buffer);
881 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
893 var connection_br = stream.reader(io, &recv_buffer);
894 var connection_bw = stream.writer(io, &send_buffer);
895 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
882896 var request = try server.receiveHead();
883897 var read_buffer: [100]u8 = undefined;
884898 var br = try request.readerExpectContinue(&read_buffer);
......@@ -1077,11 +1091,11 @@ fn echoTests(client: *http.Client, port: u16) !void {
10771091const TestServer = struct {
10781092 shutting_down: bool,
10791093 server_thread: std.Thread,
1080 net_server: std.net.Server,
1094 net_server: net.Server,
10811095
10821096 fn destroy(self: *@This()) void {
10831097 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");
10851099 conn.close();
10861100
10871101 self.server_thread.join();
......@@ -1090,21 +1104,21 @@ const TestServer = struct {
10901104 }
10911105
10921106 fn port(self: @This()) u16 {
1093 return self.net_server.listen_address.in.getPort();
1107 return self.net_server.socket.address.getPort();
10941108 }
10951109};
10961110
1097fn createTestServer(S: type) !*TestServer {
1111fn createTestServer(io: Io, S: type) !*TestServer {
10981112 if (builtin.single_threaded) return error.SkipZigTest;
10991113 if (builtin.zig_backend == .stage2_llvm and native_endian == .big) {
11001114 // https://github.com/ziglang/zig/issues/13782
11011115 return error.SkipZigTest;
11021116 }
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);
11051119 const test_server = try std.testing.allocator.create(TestServer);
11061120 test_server.* = .{
1107 .net_server = try address.listen(.{ .reuse_address = true }),
1121 .net_server = try address.listen(io, .{ .reuse_address = true }),
11081122 .shutting_down = false,
11091123 .server_thread = try std.Thread.spawn(.{}, S.run, .{test_server}),
11101124 };
......@@ -1112,18 +1126,19 @@ fn createTestServer(S: type) !*TestServer {
11121126}
11131127
11141128test "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 {
11161131 fn run(test_server: *TestServer) anyerror!void {
11171132 const net_server = &test_server.net_server;
11181133 var recv_buffer: [888]u8 = undefined;
11191134 var send_buffer: [777]u8 = undefined;
11201135
1121 const connection = try net_server.accept();
1122 defer connection.stream.close();
1136 var stream = try net_server.accept(io);
1137 defer stream.close(io);
11231138
1124 var connection_br = connection.stream.reader(&recv_buffer);
1125 var connection_bw = connection.stream.writer(&send_buffer);
1126 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
1139 var connection_br = stream.reader(io, &recv_buffer);
1140 var connection_bw = stream.writer(io, &send_buffer);
1141 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
11271142 var request = try server.receiveHead();
11281143 try expectEqualStrings(request.head.target, "/ok");
11291144 try request.respond("good job, you pass", .{});
......@@ -1136,23 +1151,23 @@ test "redirect to different connection" {
11361151 };
11371152 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 {
11401155 fn run(test_server: *TestServer) anyerror!void {
11411156 const net_server = &test_server.net_server;
11421157 var recv_buffer: [999]u8 = undefined;
11431158 var send_buffer: [100]u8 = undefined;
11441159
1145 const connection = try net_server.accept();
1146 defer connection.stream.close();
1160 var stream = try net_server.accept(io);
1161 defer stream.close(io);
11471162
11481163 var loc_buf: [50]u8 = undefined;
11491164 const new_loc = try std.fmt.bufPrint(&loc_buf, "http://127.0.0.1:{d}/ok", .{
11501165 global.other_port.?,
11511166 });
11521167
1153 var connection_br = connection.stream.reader(&recv_buffer);
1154 var connection_bw = connection.stream.writer(&send_buffer);
1155 var server = http.Server.init(connection_br.interface(), &connection_bw.interface);
1168 var connection_br = stream.reader(io, &recv_buffer);
1169 var connection_bw = stream.writer(io, &send_buffer);
1170 var server = http.Server.init(&connection_br.interface, &connection_bw.interface);
11561171 var request = try server.receiveHead();
11571172 try expectEqualStrings(request.head.target, "/help");
11581173 try request.respond("", .{
......@@ -1167,7 +1182,10 @@ test "redirect to different connection" {
11671182
11681183 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 };
11711189 defer client.deinit();
11721190
11731191 var loc_buf: [100]u8 = undefined;
lib/std/posix/test.zig+1-1
......@@ -637,7 +637,7 @@ test "shutdown socket" {
637637 error.SocketUnconnected => {},
638638 else => |e| return e,
639639 };
640 std.net.Stream.close(.{ .handle = sock });
640 std.posix.close(sock);
641641}
642642
643643test "sigrtmin/max" {
lib/std/time.zig+3-69
......@@ -8,74 +8,6 @@ const posix = std.posix;
88
99pub 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
7911// Divisions of a nanosecond.
8012pub const ns_per_us = 1000;
8113pub const ns_per_ms = 1000 * ns_per_us;
......@@ -268,9 +200,11 @@ pub const Timer = struct {
268200};
269201
270202test Timer {
203 const io = std.testing.io;
204
271205 var timer = try Timer.start();
272206
273 std.Thread.sleep(10 * ns_per_ms);
207 try std.Io.Duration.sleep(.fromMilliseconds(10), io);
274208 const time_0 = timer.read();
275209 try testing.expect(time_0 > 0);
276210