| author | |
| committer | |
| log | 774df26835069039ba739828a7619393de01a5f2 |
| tree | 25a6a8c5eff8043d3f08f757fe9087e4360cb388 |
| parent | 00f26cb0a4d60e908719309f4daa6598316ef74e |
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 | 1317 | } |
| 1318 | 1318 | |
| 1319 | 1319 | test "cache file and then recall it" { |
| 1320 | const io = std.testing.io; | |
| 1321 | ||
| 1320 | 1322 | var tmp = testing.tmpDir(.{}); |
| 1321 | 1323 | defer tmp.cleanup(); |
| 1322 | 1324 | |
| ... | ... | @@ -1328,7 +1330,7 @@ test "cache file and then recall it" { |
| 1328 | 1330 | // Wait for file timestamps to tick |
| 1329 | 1331 | const initial_time = try testGetCurrentFileTimestamp(tmp.dir); |
| 1330 | 1332 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { |
| 1331 | std.Thread.sleep(1); | |
| 1333 | try std.Io.Duration.sleep(.fromNanoseconds(1), io); | |
| 1332 | 1334 | } |
| 1333 | 1335 | |
| 1334 | 1336 | var digest1: HexDigest = undefined; |
| ... | ... | @@ -1378,6 +1380,8 @@ test "cache file and then recall it" { |
| 1378 | 1380 | } |
| 1379 | 1381 | |
| 1380 | 1382 | test "check that changing a file makes cache fail" { |
| 1383 | const io = std.testing.io; | |
| 1384 | ||
| 1381 | 1385 | var tmp = testing.tmpDir(.{}); |
| 1382 | 1386 | defer tmp.cleanup(); |
| 1383 | 1387 | |
| ... | ... | @@ -1391,7 +1395,7 @@ test "check that changing a file makes cache fail" { |
| 1391 | 1395 | // Wait for file timestamps to tick |
| 1392 | 1396 | const initial_time = try testGetCurrentFileTimestamp(tmp.dir); |
| 1393 | 1397 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { |
| 1394 | std.Thread.sleep(1); | |
| 1398 | try std.Io.Duration.sleep(.fromNanoseconds(1), io); | |
| 1395 | 1399 | } |
| 1396 | 1400 | |
| 1397 | 1401 | var digest1: HexDigest = undefined; |
| ... | ... | @@ -1490,6 +1494,8 @@ test "no file inputs" { |
| 1490 | 1494 | } |
| 1491 | 1495 | |
| 1492 | 1496 | test "Manifest with files added after initial hash work" { |
| 1497 | const io = std.testing.io; | |
| 1498 | ||
| 1493 | 1499 | var tmp = testing.tmpDir(.{}); |
| 1494 | 1500 | defer tmp.cleanup(); |
| 1495 | 1501 | |
| ... | ... | @@ -1503,7 +1509,7 @@ test "Manifest with files added after initial hash work" { |
| 1503 | 1509 | // Wait for file timestamps to tick |
| 1504 | 1510 | const initial_time = try testGetCurrentFileTimestamp(tmp.dir); |
| 1505 | 1511 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time) { |
| 1506 | std.Thread.sleep(1); | |
| 1512 | try std.Io.Duration.sleep(.fromNanoseconds(1), io); | |
| 1507 | 1513 | } |
| 1508 | 1514 | |
| 1509 | 1515 | var digest1: HexDigest = undefined; |
| ... | ... | @@ -1553,7 +1559,7 @@ test "Manifest with files added after initial hash work" { |
| 1553 | 1559 | // Wait for file timestamps to tick |
| 1554 | 1560 | const initial_time2 = try testGetCurrentFileTimestamp(tmp.dir); |
| 1555 | 1561 | while ((try testGetCurrentFileTimestamp(tmp.dir)) == initial_time2) { |
| 1556 | std.Thread.sleep(1); | |
| 1562 | try std.Io.Duration.sleep(.fromNanoseconds(1), io); | |
| 1557 | 1563 | } |
| 1558 | 1564 | |
| 1559 | 1565 | { |
lib/std/Io.zig+40-15| ... | ... | @@ -670,8 +670,9 @@ pub const VTable = struct { |
| 670 | 670 | |
| 671 | 671 | listen: *const fn (?*anyopaque, address: net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Server, |
| 672 | 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, | |
| 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 }, | |
| 675 | 676 | netReceive: *const fn (?*anyopaque, net.Socket.Handle, message_buffer: []net.IncomingMessage, data_buffer: []u8, net.ReceiveFlags, Timeout) struct { ?net.Socket.ReceiveTimeoutError, usize }, |
| 676 | 677 | netRead: *const fn (?*anyopaque, src: net.Stream, data: [][]u8) net.Stream.Reader.Error!usize, |
| 677 | 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 | 711 | /// time (e.g., if the system administrator manually changes the |
| 711 | 712 | /// clock), and by frequency adjust‐ ments performed by NTP and similar |
| 712 | 713 | /// 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. | |
| 717 | 722 | realtime, |
| 718 | 723 | /// A nonsettable system-wide clock that represents time since some |
| 719 | 724 | /// unspecified point in the past. |
| ... | ... | @@ -729,10 +734,16 @@ pub const Timestamp = struct { |
| 729 | 734 | /// Guarantees that the time returned by consecutive calls will not go |
| 730 | 735 | /// backwards, but successive calls may return identical |
| 731 | 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 | 740 | monotonic, |
| 733 | 741 | /// 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 | 744 | boottime, |
| 745 | process_cputime_id, | |
| 746 | thread_cputime_id, | |
| 736 | 747 | }; |
| 737 | 748 | |
| 738 | 749 | pub fn durationTo(from: Timestamp, to: Timestamp) Duration { |
| ... | ... | @@ -791,6 +802,12 @@ pub const Timestamp = struct { |
| 791 | 802 | pub const Duration = struct { |
| 792 | 803 | nanoseconds: i96, |
| 793 | 804 | |
| 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 | 811 | pub fn fromMilliseconds(x: i64) Duration { |
| 795 | 812 | return .{ .nanoseconds = @as(i96, x) * std.time.ns_per_ms }; |
| 796 | 813 | } |
| ... | ... | @@ -806,6 +823,10 @@ pub const Duration = struct { |
| 806 | 823 | pub fn toSeconds(d: Duration) i64 { |
| 807 | 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 | }; |
| 810 | 831 | |
| 811 | 832 | /// Declares under what conditions an operation should return `error.Timeout`. |
| ... | ... | @@ -828,6 +849,18 @@ pub const Timeout = union(enum) { |
| 828 | 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 | }; |
| 832 | 865 | |
| 833 | 866 | pub const AnyFuture = opaque {}; |
| ... | ... | @@ -1322,14 +1355,6 @@ pub fn cancelRequested(io: Io) bool { |
| 1322 | 1355 | |
| 1323 | 1356 | pub const SleepError = error{UnsupportedClock} || UnexpectedError || Cancelable; |
| 1324 | 1357 | |
| 1325 | pub fn sleep(io: Io, timeout: Timeout) SleepError!void { | |
| 1326 | return io.vtable.sleep(io.userdata, timeout); | |
| 1327 | } | |
| 1328 | ||
| 1329 | pub fn sleepDuration(io: Io, duration: Duration) SleepError!void { | |
| 1330 | return io.vtable.sleep(io.userdata, .MONOTONIC, .{ .duration = duration }); | |
| 1331 | } | |
| 1332 | ||
| 1333 | 1358 | /// Given a struct with each field a `*Future`, returns a union with the same |
| 1334 | 1359 | /// fields, each field type the future's result. |
| 1335 | 1360 | pub fn SelectUnion(S: type) type { |
lib/std/Io/Threaded.zig+318-61| ... | ... | @@ -145,8 +145,17 @@ pub fn io(pool: *Pool) Io { |
| 145 | 145 | .fileSeekBy = fileSeekBy, |
| 146 | 146 | .fileSeekTo = fileSeekTo, |
| 147 | 147 | |
| 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 | }, | |
| 150 | 159 | |
| 151 | 160 | .listen = switch (builtin.os.tag) { |
| 152 | 161 | .windows => @panic("TODO"), |
| ... | ... | @@ -160,6 +169,10 @@ pub fn io(pool: *Pool) Io { |
| 160 | 169 | .windows => @panic("TODO"), |
| 161 | 170 | else => ipBindPosix, |
| 162 | 171 | }, |
| 172 | .ipConnect = switch (builtin.os.tag) { | |
| 173 | .windows => @panic("TODO"), | |
| 174 | else => ipConnectPosix, | |
| 175 | }, | |
| 163 | 176 | .netClose = netClose, |
| 164 | 177 | .netRead = switch (builtin.os.tag) { |
| 165 | 178 | .windows => @panic("TODO"), |
| ... | ... | @@ -797,16 +810,15 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File |
| 797 | 810 | const dest = iovecs_buffer[0..i]; |
| 798 | 811 | assert(dest[0].len > 0); |
| 799 | 812 | |
| 800 | if (native_os == .wasi and !builtin.link_libc) { | |
| 813 | if (native_os == .wasi and !builtin.link_libc) while (true) { | |
| 801 | 814 | try pool.checkCancel(); |
| 802 | 815 | var nread: usize = undefined; |
| 803 | 816 | switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) { |
| 804 | 817 | .SUCCESS => return nread, |
| 805 | .INTR => unreachable, | |
| 806 | .INVAL => unreachable, | |
| 818 | .INTR => continue, | |
| 819 | .INVAL => |err| return errnoBug(err), | |
| 807 | 820 | .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), | |
| 810 | 822 | .IO => return error.InputOutput, |
| 811 | 823 | .ISDIR => return error.IsDir, |
| 812 | 824 | .NOBUFS => return error.SystemResources, |
| ... | ... | @@ -817,15 +829,15 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File |
| 817 | 829 | .NOTCAPABLE => return error.AccessDenied, |
| 818 | 830 | else => |err| return posix.unexpectedErrno(err), |
| 819 | 831 | } |
| 820 | } | |
| 832 | }; | |
| 821 | 833 | |
| 822 | 834 | while (true) { |
| 823 | 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 | 837 | switch (posix.errno(rc)) { |
| 826 | 838 | .SUCCESS => return @intCast(rc), |
| 827 | 839 | .INTR => continue, |
| 828 | .INVAL => unreachable, | |
| 840 | .INVAL => |err| return errnoBug(err), | |
| 829 | 841 | .FAULT => |err| return errnoBug(err), |
| 830 | 842 | .SRCH => return error.ProcessNotFound, |
| 831 | 843 | .AGAIN => return error.WouldBlock, |
| ... | ... | @@ -845,14 +857,6 @@ fn fileReadStreaming(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File |
| 845 | 857 | fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize { |
| 846 | 858 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 847 | 859 | |
| 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 | 860 | if (is_windows) { |
| 857 | 861 | const DWORD = windows.DWORD; |
| 858 | 862 | const OVERLAPPED = windows.OVERLAPPED; |
| ... | ... | @@ -907,6 +911,14 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset |
| 907 | 911 | return total; |
| 908 | 912 | } |
| 909 | 913 | |
| 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 | 922 | var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; |
| 911 | 923 | var i: usize = 0; |
| 912 | 924 | for (data) |buf| { |
| ... | ... | @@ -919,15 +931,15 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset |
| 919 | 931 | const dest = iovecs_buffer[0..i]; |
| 920 | 932 | assert(dest[0].len > 0); |
| 921 | 933 | |
| 922 | if (native_os == .wasi and !builtin.link_libc) { | |
| 934 | if (native_os == .wasi and !builtin.link_libc) while (true) { | |
| 923 | 935 | try pool.checkCancel(); |
| 924 | 936 | var nread: usize = undefined; |
| 925 | 937 | switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) { |
| 926 | 938 | .SUCCESS => return nread, |
| 927 | .INTR => unreachable, | |
| 928 | .INVAL => unreachable, | |
| 939 | .INTR => continue, | |
| 940 | .INVAL => |err| return errnoBug(err), | |
| 929 | 941 | .FAULT => |err| return errnoBug(err), |
| 930 | .AGAIN => unreachable, | |
| 942 | .AGAIN => |err| return errnoBug(err), | |
| 931 | 943 | .BADF => return error.NotOpenForReading, // can be a race condition |
| 932 | 944 | .IO => return error.InputOutput, |
| 933 | 945 | .ISDIR => return error.IsDir, |
| ... | ... | @@ -942,16 +954,16 @@ fn fileReadPositional(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset |
| 942 | 954 | .NOTCAPABLE => return error.AccessDenied, |
| 943 | 955 | else => |err| return posix.unexpectedErrno(err), |
| 944 | 956 | } |
| 945 | } | |
| 957 | }; | |
| 946 | 958 | |
| 947 | 959 | const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv; |
| 948 | 960 | while (true) { |
| 949 | 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 | 963 | switch (posix.errno(rc)) { |
| 952 | 964 | .SUCCESS => return @bitCast(rc), |
| 953 | 965 | .INTR => continue, |
| 954 | .INVAL => unreachable, | |
| 966 | .INVAL => |err| return errnoBug(err), | |
| 955 | 967 | .FAULT => |err| return errnoBug(err), |
| 956 | 968 | .SRCH => return error.ProcessNotFound, |
| 957 | 969 | .AGAIN => return error.WouldBlock, |
| ... | ... | @@ -999,7 +1011,7 @@ fn pwrite(userdata: ?*anyopaque, file: Io.File, buffer: []const u8, offset: posi |
| 999 | 1011 | }; |
| 1000 | 1012 | } |
| 1001 | 1013 | |
| 1002 | fn now(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 { | |
| 1014 | fn nowPosix(userdata: ?*anyopaque, clock: Io.Timestamp.Clock) Io.Timestamp.Error!i96 { | |
| 1003 | 1015 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1004 | 1016 | _ = pool; |
| 1005 | 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 | 1023 | } |
| 1012 | 1024 | } |
| 1013 | 1025 | |
| 1014 | fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { | |
| 1026 | fn 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 | ||
| 1045 | fn 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 | ||
| 1054 | fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { | |
| 1015 | 1055 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1016 | 1056 | const clock_id: posix.clockid_t = clockToPosix(switch (timeout) { |
| 1017 | 1057 | .none => .monotonic, |
| ... | ... | @@ -1041,6 +1081,73 @@ fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { |
| 1041 | 1081 | } |
| 1042 | 1082 | } |
| 1043 | 1083 | |
| 1084 | fn 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 | ||
| 1096 | fn 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 | ||
| 1125 | fn 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 | ||
| 1044 | 1151 | fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) usize { |
| 1045 | 1152 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1046 | 1153 | _ = pool; |
| ... | ... | @@ -1091,7 +1198,7 @@ fn listenPosix( |
| 1091 | 1198 | errdefer posix.close(fd); |
| 1092 | 1199 | if (socket_flags_unsupported) while (true) { |
| 1093 | 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 | 1202 | .SUCCESS => break, |
| 1096 | 1203 | .INTR => continue, |
| 1097 | 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 | 1265 | } |
| 1159 | 1266 | } |
| 1160 | 1267 | |
| 1268 | fn 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 | ||
| 1161 | 1299 | fn posixGetSockName(pool: *Pool, socket_fd: posix.fd_t, addr: *posix.sockaddr, addr_len: *posix.socklen_t) !void { |
| 1162 | 1300 | while (true) { |
| 1163 | 1301 | try pool.checkCancel(); |
| ... | ... | @@ -1190,14 +1328,45 @@ fn setSocketOption(pool: *Pool, fd: posix.fd_t, level: i32, opt_name: u32, optio |
| 1190 | 1328 | } |
| 1191 | 1329 | } |
| 1192 | 1330 | |
| 1331 | fn 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 | ||
| 1193 | 1349 | fn ipBindPosix( |
| 1194 | 1350 | userdata: ?*anyopaque, |
| 1195 | address: Io.net.IpAddress, | |
| 1351 | address: *const Io.net.IpAddress, | |
| 1196 | 1352 | options: Io.net.IpAddress.BindOptions, |
| 1197 | 1353 | ) Io.net.IpAddress.BindError!Io.net.Socket { |
| 1198 | 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 | ||
| 1368 | fn openSocketPosix(pool: *Pool, family: posix.sa_family_t, options: Io.net.IpAddress.BindOptions) !posix.socket_t { | |
| 1199 | 1369 | const mode = posixSocketMode(options.mode); |
| 1200 | const family = posixAddressFamily(&address); | |
| 1201 | 1370 | const protocol = posixProtocol(options.protocol); |
| 1202 | 1371 | const socket_fd = while (true) { |
| 1203 | 1372 | try pool.checkCancel(); |
| ... | ... | @@ -1209,7 +1378,7 @@ fn ipBindPosix( |
| 1209 | 1378 | errdefer posix.close(fd); |
| 1210 | 1379 | if (socket_flags_unsupported) while (true) { |
| 1211 | 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 | 1382 | .SUCCESS => break, |
| 1214 | 1383 | .INTR => continue, |
| 1215 | 1384 | else => |err| return posix.unexpectedErrno(err), |
| ... | ... | @@ -1229,19 +1398,14 @@ fn ipBindPosix( |
| 1229 | 1398 | else => |err| return posix.unexpectedErrno(err), |
| 1230 | 1399 | } |
| 1231 | 1400 | }; |
| 1401 | errdefer posix.close(socket_fd); | |
| 1232 | 1402 | |
| 1233 | 1403 | if (options.ip6_only) { |
| 1404 | if (posix.IPV6 == void) return error.OptionUnsupported; | |
| 1234 | 1405 | try setSocketOption(pool, socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0); |
| 1235 | 1406 | } |
| 1236 | 1407 | |
| 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; | |
| 1245 | 1409 | } |
| 1246 | 1410 | |
| 1247 | 1411 | const 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 | 1428 | errdefer posix.close(fd); |
| 1265 | 1429 | if (!have_accept4) while (true) { |
| 1266 | 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 | 1432 | .SUCCESS => break, |
| 1269 | 1433 | .INTR => continue, |
| 1270 | 1434 | else => |err| return posix.unexpectedErrno(err), |
| ... | ... | @@ -1322,29 +1486,118 @@ fn netSend( |
| 1322 | 1486 | handle: Io.net.Socket.Handle, |
| 1323 | 1487 | messages: []Io.net.OutgoingMessage, |
| 1324 | 1488 | flags: Io.net.SendFlags, |
| 1325 | ) Io.net.SendResult { | |
| 1489 | ) struct { ?Io.net.Socket.SendError, usize } { | |
| 1326 | 1490 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 1327 | 1491 | |
| 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; | |
| 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 | } | |
| 1338 | 1511 | |
| 1339 | try pool.checkCancel(); | |
| 1340 | @panic("TODO"); | |
| 1512 | fn 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 | } | |
| 1341 | 1594 | } |
| 1342 | 1595 | |
| 1343 | 1596 | fn netSendMany( |
| 1344 | 1597 | pool: *Pool, |
| 1345 | 1598 | handle: Io.net.Socket.Handle, |
| 1346 | 1599 | messages: []Io.net.OutgoingMessage, |
| 1347 | flags: Io.net.SendFlags, | |
| 1600 | flags: u32, | |
| 1348 | 1601 | ) Io.net.Socket.SendError!usize { |
| 1349 | 1602 | var msg_buffer: [64]std.os.linux.mmsghdr = undefined; |
| 1350 | 1603 | var addr_buffer: [msg_buffer.len]PosixAddress = undefined; |
| ... | ... | @@ -1371,17 +1624,9 @@ fn netSendMany( |
| 1371 | 1624 | }; |
| 1372 | 1625 | } |
| 1373 | 1626 | |
| 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 | 1627 | while (true) { |
| 1383 | 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 | 1630 | switch (posix.errno(rc)) { |
| 1386 | 1631 | .SUCCESS => { |
| 1387 | 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 | 2027 | .realtime => posix.CLOCK.REALTIME, |
| 1783 | 2028 | .monotonic => posix.CLOCK.MONOTONIC, |
| 1784 | 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 | ||
| 2035 | fn 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 | 208 | /// System-wide limit on the total number of open files has been reached. |
| 209 | 209 | SystemFdQuotaExceeded, |
| 210 | 210 | SocketModeUnsupported, |
| 211 | /// One of the `BindOptions` is not supported by the Io | |
| 212 | /// implementation. | |
| 213 | OptionUnsupported, | |
| 211 | 214 | } || Io.UnexpectedError || Io.Cancelable; |
| 212 | 215 | |
| 213 | 216 | pub const BindOptions = struct { |
| ... | ... | @@ -228,6 +231,29 @@ pub const IpAddress = union(enum) { |
| 228 | 231 | pub fn bind(address: IpAddress, io: Io, options: BindOptions) BindError!Socket { |
| 229 | 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 | }; |
| 232 | 258 | |
| 233 | 259 | /// An IPv4 address in binary memory layout. |
| ... | ... | @@ -758,14 +784,6 @@ pub const SendFlags = packed struct(u8) { |
| 758 | 784 | _: u3 = 0, |
| 759 | 785 | }; |
| 760 | 786 | |
| 761 | pub const SendResult = union(enum) { | |
| 762 | success, | |
| 763 | fail: struct { | |
| 764 | err: Socket.SendError, | |
| 765 | sent: usize, | |
| 766 | }, | |
| 767 | }; | |
| 768 | ||
| 769 | 787 | pub const Interface = struct { |
| 770 | 788 | /// Value 0 indicates `none`. |
| 771 | 789 | index: u32, |
| ... | ... | @@ -978,8 +996,9 @@ pub const Socket = struct { |
| 978 | 996 | pub const Stream = struct { |
| 979 | 997 | socket: Socket, |
| 980 | 998 | |
| 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; | |
| 983 | 1002 | } |
| 984 | 1003 | |
| 985 | 1004 | pub const Reader = struct { |
| ... | ... | @@ -996,8 +1015,9 @@ pub const Stream = struct { |
| 996 | 1015 | SocketUnconnected, |
| 997 | 1016 | } || Io.Cancelable || Io.Writer.Error || error{EndOfStream}; |
| 998 | 1017 | |
| 999 | pub fn init(stream: Stream, buffer: []u8) Reader { | |
| 1018 | pub fn init(stream: Stream, io: Io, buffer: []u8) Reader { | |
| 1000 | 1019 | return .{ |
| 1020 | .io = io, | |
| 1001 | 1021 | .interface = .{ |
| 1002 | 1022 | .vtable = &.{ |
| 1003 | 1023 | .stream = streamImpl, |
| ... | ... | @@ -1043,8 +1063,9 @@ pub const Stream = struct { |
| 1043 | 1063 | Unexpected, |
| 1044 | 1064 | } || Io.Cancelable; |
| 1045 | 1065 | |
| 1046 | pub fn init(stream: Stream, buffer: []u8) Writer { | |
| 1066 | pub fn init(stream: Stream, io: Io, buffer: []u8) Writer { | |
| 1047 | 1067 | return .{ |
| 1068 | .io = io, | |
| 1048 | 1069 | .stream = stream, |
| 1049 | 1070 | .interface = .{ |
| 1050 | 1071 | .vtable = &.{ .drain = drain }, |
| ... | ... | @@ -1062,12 +1083,12 @@ pub const Stream = struct { |
| 1062 | 1083 | } |
| 1063 | 1084 | }; |
| 1064 | 1085 | |
| 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); | |
| 1067 | 1088 | } |
| 1068 | 1089 | |
| 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); | |
| 1071 | 1092 | } |
| 1072 | 1093 | }; |
| 1073 | 1094 |
lib/std/Io/net/test.zig+11-11| ... | ... | @@ -6,16 +6,16 @@ const testing = std.testing; |
| 6 | 6 | |
| 7 | 7 | test "parse and render IP addresses at comptime" { |
| 8 | 8 | comptime { |
| 9 | const ipv6addr = net.IpAddress.parseIp("::1", 0) catch unreachable; | |
| 9 | const ipv6addr = net.IpAddress.parse("::1", 0) catch unreachable; | |
| 10 | 10 | try std.testing.expectFmt("[::1]:0", "{f}", .{ipv6addr}); |
| 11 | 11 | |
| 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 | 13 | try std.testing.expectFmt("127.0.0.1:0", "{f}", .{ipv4addr}); |
| 14 | 14 | |
| 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)); | |
| 19 | 19 | } |
| 20 | 20 | } |
| 21 | 21 | |
| ... | ... | @@ -161,8 +161,8 @@ test "resolve DNS" { |
| 161 | 161 | |
| 162 | 162 | // Resolve localhost, this should not fail. |
| 163 | 163 | { |
| 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); | |
| 166 | 166 | |
| 167 | 167 | const result = try net.getAddressList(testing.allocator, "localhost", 80); |
| 168 | 168 | defer result.deinit(); |
| ... | ... | @@ -198,7 +198,7 @@ test "listen on a port, send bytes, receive bytes" { |
| 198 | 198 | |
| 199 | 199 | // Try only the IPv4 variant as some CI builders have no IPv6 localhost |
| 200 | 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); | |
| 202 | 202 | |
| 203 | 203 | var server = try localhost.listen(.{}); |
| 204 | 204 | defer server.deinit(); |
| ... | ... | @@ -232,7 +232,7 @@ test "listen on an in use port" { |
| 232 | 232 | return error.SkipZigTest; |
| 233 | 233 | } |
| 234 | 234 | |
| 235 | const localhost = try net.IpAddress.parseIp("127.0.0.1", 0); | |
| 235 | const localhost = try net.IpAddress.parse("127.0.0.1", 0); | |
| 236 | 236 | |
| 237 | 237 | var server1 = try localhost.listen(.{ .reuse_address = true }); |
| 238 | 238 | defer server1.deinit(); |
| ... | ... | @@ -351,7 +351,7 @@ test "non-blocking tcp server" { |
| 351 | 351 | return error.SkipZigTest; |
| 352 | 352 | } |
| 353 | 353 | |
| 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 | 355 | var server = localhost.listen(.{ .force_nonblocking = true }); |
| 356 | 356 | defer server.deinit(); |
| 357 | 357 |
lib/std/Thread.zig+1-80| ... | ... | @@ -73,10 +73,7 @@ pub const ResetEvent = enum(u32) { |
| 73 | 73 | /// timedWait() returns without error. |
| 74 | 74 | pub fn timedWait(re: *ResetEvent, timeout_ns: u64) error{Timeout}!void { |
| 75 | 75 | if (builtin.single_threaded) switch (re.*) { |
| 76 | .unset => { | |
| 77 | sleep(timeout_ns); | |
| 78 | return error.Timeout; | |
| 79 | }, | |
| 76 | .unset => return error.Timeout, | |
| 80 | 77 | .waiting => unreachable, // Invalid state. |
| 81 | 78 | .is_set => return, |
| 82 | 79 | }; |
| ... | ... | @@ -142,82 +139,6 @@ pub const ResetEvent = enum(u32) { |
| 142 | 139 | } |
| 143 | 140 | }; |
| 144 | 141 | |
| 145 | /// Spurious wakeups are possible and no precision of timing is guaranteed. | |
| 146 | pub 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 | ||
| 217 | test sleep { | |
| 218 | sleep(1); | |
| 219 | } | |
| 220 | ||
| 221 | 142 | const Thread = @This(); |
| 222 | 143 | const Impl = if (native_os == .windows) |
| 223 | 144 | WindowsThreadImpl |
lib/std/fs/test.zig+3-1| ... | ... | @@ -2265,6 +2265,8 @@ test "seekTo flushes buffered data" { |
| 2265 | 2265 | var tmp = std.testing.tmpDir(.{}); |
| 2266 | 2266 | defer tmp.cleanup(); |
| 2267 | 2267 | |
| 2268 | const io = std.testing.io; | |
| 2269 | ||
| 2268 | 2270 | const contents = "data"; |
| 2269 | 2271 | |
| 2270 | 2272 | const file = try tmp.dir.createFile("seek.bin", .{ .read = true }); |
| ... | ... | @@ -2279,7 +2281,7 @@ test "seekTo flushes buffered data" { |
| 2279 | 2281 | } |
| 2280 | 2282 | |
| 2281 | 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); | |
| 2283 | 2285 | |
| 2284 | 2286 | var buf: [4]u8 = undefined; |
| 2285 | 2287 | try file_reader.interface.readSliceAll(&buf); |
lib/std/http/test.zig+87-69| ... | ... | @@ -1,27 +1,31 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | const native_endian = builtin.cpu.arch.endian(); | |
| 3 | ||
| 2 | 4 | const std = @import("std"); |
| 3 | 5 | const http = std.http; |
| 4 | 6 | const mem = std.mem; |
| 5 | const native_endian = builtin.cpu.arch.endian(); | |
| 7 | const net = std.Io.net; | |
| 8 | const Io = std.Io; | |
| 6 | 9 | const expect = std.testing.expect; |
| 7 | 10 | const expectEqual = std.testing.expectEqual; |
| 8 | 11 | const expectEqualStrings = std.testing.expectEqualStrings; |
| 9 | 12 | const expectError = std.testing.expectError; |
| 10 | 13 | |
| 11 | 14 | test "trailers" { |
| 12 | const test_server = try createTestServer(struct { | |
| 15 | const io = std.testing.io; | |
| 16 | const test_server = try createTestServer(io, struct { | |
| 13 | 17 | fn run(test_server: *TestServer) anyerror!void { |
| 14 | 18 | const net_server = &test_server.net_server; |
| 15 | 19 | var recv_buffer: [1024]u8 = undefined; |
| 16 | 20 | var send_buffer: [1024]u8 = undefined; |
| 17 | 21 | var remaining: usize = 1; |
| 18 | 22 | 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); | |
| 21 | 25 | |
| 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); | |
| 25 | 29 | |
| 26 | 30 | try expectEqual(.ready, server.reader.state); |
| 27 | 31 | var request = try server.receiveHead(); |
| ... | ... | @@ -92,17 +96,18 @@ test "trailers" { |
| 92 | 96 | } |
| 93 | 97 | |
| 94 | 98 | test "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 | 101 | fn run(test_server: *TestServer) anyerror!void { |
| 97 | 102 | const net_server = &test_server.net_server; |
| 98 | 103 | var recv_buffer: [8192]u8 = undefined; |
| 99 | 104 | 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); | |
| 102 | 107 | |
| 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); | |
| 106 | 111 | var request = try server.receiveHead(); |
| 107 | 112 | |
| 108 | 113 | try expect(request.head.transfer_encoding == .chunked); |
| ... | ... | @@ -137,8 +142,8 @@ test "HTTP server handles a chunked transfer coding request" { |
| 137 | 142 | "\r\n"; |
| 138 | 143 | |
| 139 | 144 | 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); | |
| 142 | 147 | var stream_writer = stream.writer(&.{}); |
| 143 | 148 | try stream_writer.interface.writeAll(request_bytes); |
| 144 | 149 | |
| ... | ... | @@ -156,19 +161,20 @@ test "HTTP server handles a chunked transfer coding request" { |
| 156 | 161 | } |
| 157 | 162 | |
| 158 | 163 | test "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 | 166 | fn run(test_server: *TestServer) anyerror!void { |
| 161 | 167 | const net_server = &test_server.net_server; |
| 162 | 168 | var recv_buffer: [1024]u8 = undefined; |
| 163 | 169 | var send_buffer: [100]u8 = undefined; |
| 164 | 170 | |
| 165 | 171 | 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); | |
| 168 | 174 | |
| 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); | |
| 172 | 178 | |
| 173 | 179 | while (http_server.reader.state == .ready) { |
| 174 | 180 | var request = http_server.receiveHead() catch |err| switch (err) { |
| ... | ... | @@ -243,6 +249,8 @@ test "echo content server" { |
| 243 | 249 | } |
| 244 | 250 | |
| 245 | 251 | test "Server.Request.respondStreaming non-chunked, unknown content-length" { |
| 252 | const io = std.testing.io; | |
| 253 | ||
| 246 | 254 | if (builtin.os.tag == .windows) { |
| 247 | 255 | // https://github.com/ziglang/zig/issues/21457 |
| 248 | 256 | return error.SkipZigTest; |
| ... | ... | @@ -250,19 +258,19 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" { |
| 250 | 258 | |
| 251 | 259 | // In this case, the response is expected to stream until the connection is |
| 252 | 260 | // closed, indicating the end of the body. |
| 253 | const test_server = try createTestServer(struct { | |
| 261 | const test_server = try createTestServer(io, struct { | |
| 254 | 262 | fn run(test_server: *TestServer) anyerror!void { |
| 255 | 263 | const net_server = &test_server.net_server; |
| 256 | 264 | var recv_buffer: [1000]u8 = undefined; |
| 257 | 265 | var send_buffer: [500]u8 = undefined; |
| 258 | 266 | var remaining: usize = 1; |
| 259 | 267 | 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); | |
| 262 | 270 | |
| 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); | |
| 266 | 274 | |
| 267 | 275 | try expectEqual(.ready, server.reader.state); |
| 268 | 276 | var request = try server.receiveHead(); |
| ... | ... | @@ -287,8 +295,8 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" { |
| 287 | 295 | |
| 288 | 296 | const request_bytes = "GET /foo HTTP/1.1\r\n\r\n"; |
| 289 | 297 | 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); | |
| 292 | 300 | var stream_writer = stream.writer(&.{}); |
| 293 | 301 | try stream_writer.interface.writeAll(request_bytes); |
| 294 | 302 | |
| ... | ... | @@ -316,19 +324,21 @@ test "Server.Request.respondStreaming non-chunked, unknown content-length" { |
| 316 | 324 | } |
| 317 | 325 | |
| 318 | 326 | test "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 | 330 | fn run(test_server: *TestServer) anyerror!void { |
| 321 | 331 | const net_server = &test_server.net_server; |
| 322 | 332 | var recv_buffer: [666]u8 = undefined; |
| 323 | 333 | var send_buffer: [777]u8 = undefined; |
| 324 | 334 | var remaining: usize = 1; |
| 325 | 335 | 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); | |
| 328 | 338 | |
| 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); | |
| 332 | 342 | |
| 333 | 343 | try expectEqual(.ready, server.reader.state); |
| 334 | 344 | var request = try server.receiveHead(); |
| ... | ... | @@ -357,8 +367,8 @@ test "receiving arbitrary http headers from the client" { |
| 357 | 367 | "aoeu: asdf \r\n" ++ |
| 358 | 368 | "\r\n"; |
| 359 | 369 | 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); | |
| 362 | 372 | var stream_writer = stream.writer(&.{}); |
| 363 | 373 | try stream_writer.interface.writeAll(request_bytes); |
| 364 | 374 | |
| ... | ... | @@ -376,24 +386,26 @@ test "receiving arbitrary http headers from the client" { |
| 376 | 386 | } |
| 377 | 387 | |
| 378 | 388 | test "general client/server API coverage" { |
| 389 | const io = std.testing.io; | |
| 390 | ||
| 379 | 391 | if (builtin.os.tag == .windows) { |
| 380 | 392 | // This test was never passing on Windows. |
| 381 | 393 | return error.SkipZigTest; |
| 382 | 394 | } |
| 383 | 395 | |
| 384 | const test_server = try createTestServer(struct { | |
| 396 | const test_server = try createTestServer(io, struct { | |
| 385 | 397 | fn run(test_server: *TestServer) anyerror!void { |
| 386 | 398 | const net_server = &test_server.net_server; |
| 387 | 399 | var recv_buffer: [1024]u8 = undefined; |
| 388 | 400 | var send_buffer: [100]u8 = undefined; |
| 389 | 401 | |
| 390 | 402 | 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); | |
| 393 | 405 | |
| 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); | |
| 397 | 409 | |
| 398 | 410 | while (http_server.reader.state == .ready) { |
| 399 | 411 | var request = http_server.receiveHead() catch |err| switch (err) { |
| ... | ... | @@ -530,7 +542,7 @@ test "general client/server API coverage" { |
| 530 | 542 | } |
| 531 | 543 | |
| 532 | 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 | 546 | var s = try addr.listen(.{}); |
| 535 | 547 | defer s.deinit(); |
| 536 | 548 | return s.listen_address.in.getPort(); |
| ... | ... | @@ -867,18 +879,20 @@ test "general client/server API coverage" { |
| 867 | 879 | } |
| 868 | 880 | |
| 869 | 881 | test "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 | 885 | fn run(test_server: *TestServer) anyerror!void { |
| 872 | 886 | const net_server = &test_server.net_server; |
| 873 | 887 | var recv_buffer: [1024]u8 = undefined; |
| 874 | 888 | var send_buffer: [777]u8 = undefined; |
| 875 | 889 | |
| 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); | |
| 878 | 892 | |
| 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); | |
| 882 | 896 | var request = try server.receiveHead(); |
| 883 | 897 | var read_buffer: [100]u8 = undefined; |
| 884 | 898 | var br = try request.readerExpectContinue(&read_buffer); |
| ... | ... | @@ -1077,11 +1091,11 @@ fn echoTests(client: *http.Client, port: u16) !void { |
| 1077 | 1091 | const TestServer = struct { |
| 1078 | 1092 | shutting_down: bool, |
| 1079 | 1093 | server_thread: std.Thread, |
| 1080 | net_server: std.net.Server, | |
| 1094 | net_server: net.Server, | |
| 1081 | 1095 | |
| 1082 | 1096 | fn destroy(self: *@This()) void { |
| 1083 | 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 | 1099 | conn.close(); |
| 1086 | 1100 | |
| 1087 | 1101 | self.server_thread.join(); |
| ... | ... | @@ -1090,21 +1104,21 @@ const TestServer = struct { |
| 1090 | 1104 | } |
| 1091 | 1105 | |
| 1092 | 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 | }; |
| 1096 | 1110 | |
| 1097 | fn createTestServer(S: type) !*TestServer { | |
| 1111 | fn createTestServer(io: Io, S: type) !*TestServer { | |
| 1098 | 1112 | if (builtin.single_threaded) return error.SkipZigTest; |
| 1099 | 1113 | if (builtin.zig_backend == .stage2_llvm and native_endian == .big) { |
| 1100 | 1114 | // https://github.com/ziglang/zig/issues/13782 |
| 1101 | 1115 | return error.SkipZigTest; |
| 1102 | 1116 | } |
| 1103 | 1117 | |
| 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 | 1119 | const test_server = try std.testing.allocator.create(TestServer); |
| 1106 | 1120 | test_server.* = .{ |
| 1107 | .net_server = try address.listen(.{ .reuse_address = true }), | |
| 1121 | .net_server = try address.listen(io, .{ .reuse_address = true }), | |
| 1108 | 1122 | .shutting_down = false, |
| 1109 | 1123 | .server_thread = try std.Thread.spawn(.{}, S.run, .{test_server}), |
| 1110 | 1124 | }; |
| ... | ... | @@ -1112,18 +1126,19 @@ fn createTestServer(S: type) !*TestServer { |
| 1112 | 1126 | } |
| 1113 | 1127 | |
| 1114 | 1128 | test "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 | 1131 | fn run(test_server: *TestServer) anyerror!void { |
| 1117 | 1132 | const net_server = &test_server.net_server; |
| 1118 | 1133 | var recv_buffer: [888]u8 = undefined; |
| 1119 | 1134 | var send_buffer: [777]u8 = undefined; |
| 1120 | 1135 | |
| 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); | |
| 1123 | 1138 | |
| 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); | |
| 1127 | 1142 | var request = try server.receiveHead(); |
| 1128 | 1143 | try expectEqualStrings(request.head.target, "/ok"); |
| 1129 | 1144 | try request.respond("good job, you pass", .{}); |
| ... | ... | @@ -1136,23 +1151,23 @@ test "redirect to different connection" { |
| 1136 | 1151 | }; |
| 1137 | 1152 | global.other_port = test_server_new.port(); |
| 1138 | 1153 | |
| 1139 | const test_server_orig = try createTestServer(struct { | |
| 1154 | const test_server_orig = try createTestServer(io, struct { | |
| 1140 | 1155 | fn run(test_server: *TestServer) anyerror!void { |
| 1141 | 1156 | const net_server = &test_server.net_server; |
| 1142 | 1157 | var recv_buffer: [999]u8 = undefined; |
| 1143 | 1158 | var send_buffer: [100]u8 = undefined; |
| 1144 | 1159 | |
| 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); | |
| 1147 | 1162 | |
| 1148 | 1163 | var loc_buf: [50]u8 = undefined; |
| 1149 | 1164 | const new_loc = try std.fmt.bufPrint(&loc_buf, "http://127.0.0.1:{d}/ok", .{ |
| 1150 | 1165 | global.other_port.?, |
| 1151 | 1166 | }); |
| 1152 | 1167 | |
| 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); | |
| 1156 | 1171 | var request = try server.receiveHead(); |
| 1157 | 1172 | try expectEqualStrings(request.head.target, "/help"); |
| 1158 | 1173 | try request.respond("", .{ |
| ... | ... | @@ -1167,7 +1182,10 @@ test "redirect to different connection" { |
| 1167 | 1182 | |
| 1168 | 1183 | const gpa = std.testing.allocator; |
| 1169 | 1184 | |
| 1170 | var client: http.Client = .{ .allocator = gpa }; | |
| 1185 | var client: http.Client = .{ | |
| 1186 | .allocator = gpa, | |
| 1187 | .io = io, | |
| 1188 | }; | |
| 1171 | 1189 | defer client.deinit(); |
| 1172 | 1190 | |
| 1173 | 1191 | var loc_buf: [100]u8 = undefined; |
lib/std/posix/test.zig+1-1| ... | ... | @@ -637,7 +637,7 @@ test "shutdown socket" { |
| 637 | 637 | error.SocketUnconnected => {}, |
| 638 | 638 | else => |e| return e, |
| 639 | 639 | }; |
| 640 | std.net.Stream.close(.{ .handle = sock }); | |
| 640 | std.posix.close(sock); | |
| 641 | 641 | } |
| 642 | 642 | |
| 643 | 643 | test "sigrtmin/max" { |
lib/std/time.zig+3-69| ... | ... | @@ -8,74 +8,6 @@ const posix = std.posix; |
| 8 | 8 | |
| 9 | 9 | pub const epoch = @import("time/epoch.zig"); |
| 10 | 10 | |
| 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. | |
| 16 | pub 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. | |
| 25 | pub 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. | |
| 34 | pub 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. | |
| 44 | pub 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 | ||
| 71 | test 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 | 11 | // Divisions of a nanosecond. |
| 80 | 12 | pub const ns_per_us = 1000; |
| 81 | 13 | pub const ns_per_ms = 1000 * ns_per_us; |
| ... | ... | @@ -268,9 +200,11 @@ pub const Timer = struct { |
| 268 | 200 | }; |
| 269 | 201 | |
| 270 | 202 | test Timer { |
| 203 | const io = std.testing.io; | |
| 204 | ||
| 271 | 205 | var timer = try Timer.start(); |
| 272 | 206 | |
| 273 | std.Thread.sleep(10 * ns_per_ms); | |
| 207 | try std.Io.Duration.sleep(.fromMilliseconds(10), io); | |
| 274 | 208 | const time_0 = timer.read(); |
| 275 | 209 | try testing.expect(time_0 > 0); |
| 276 | 210 |