authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-04 01:21:28-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-06-04 01:21:28-04:00
log9c08a33b2226239b8e0cf08ebcef17d710a54d8a
treedd55d02ef4c45ffdeeb9b003b0bf7bd3456e7fd0
parent05b677f0c484181bcbd7eb86b41a70b8e508644b
parent4909aa1da43d227ad85b2fe03a58ef1a8c12b769
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8750 from lithdew/master

x/io, x/os: async i/o reactor, cross-platform socket syscalls and bits

24 files changed, 646 insertions(+), 203 deletions(-)

lib/std/atomic.zig+3-3
......@@ -19,7 +19,7 @@ test "std.atomic" {
1919 _ = @import("atomic/Atomic.zig");
2020}
2121
22pub fn fence(comptime ordering: Ordering) callconv(.Inline) void {
22pub inline fn fence(comptime ordering: Ordering) void {
2323 switch (ordering) {
2424 .Acquire, .Release, .AcqRel, .SeqCst => {
2525 @fence(ordering);
......@@ -30,7 +30,7 @@ pub fn fence(comptime ordering: Ordering) callconv(.Inline) void {
3030 }
3131}
3232
33pub fn compilerFence(comptime ordering: Ordering) callconv(.Inline) void {
33pub inline fn compilerFence(comptime ordering: Ordering) void {
3434 switch (ordering) {
3535 .Acquire, .Release, .AcqRel, .SeqCst => asm volatile ("" ::: "memory"),
3636 else => @compileLog(ordering, " only applies to a given memory location"),
......@@ -45,7 +45,7 @@ test "fence/compilerFence" {
4545}
4646
4747/// Signals to the processor that the caller is inside a busy-wait spin-loop.
48pub fn spinLoopHint() callconv(.Inline) void {
48pub inline fn spinLoopHint() void {
4949 const hint_instruction = switch (target.cpu.arch) {
5050 // No-op instruction that can hint to save (or share with a hardware-thread) pipelining/power resources
5151 // https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html
lib/std/atomic/Atomic.zig+22-22
......@@ -48,38 +48,38 @@ pub fn Atomic(comptime T: type) type {
4848 };
4949 }
5050
51 pub fn swap(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T {
51 pub inline fn swap(self: *Self, value: T, comptime ordering: Ordering) T {
5252 return self.rmw(.Xchg, value, ordering);
5353 }
5454
55 pub fn compareAndSwap(
55 pub inline fn compareAndSwap(
5656 self: *Self,
5757 compare: T,
5858 exchange: T,
5959 comptime success: Ordering,
6060 comptime failure: Ordering,
61 ) callconv(.Inline) ?T {
61 ) ?T {
6262 return self.cmpxchg(true, compare, exchange, success, failure);
6363 }
6464
65 pub fn tryCompareAndSwap(
65 pub inline fn tryCompareAndSwap(
6666 self: *Self,
6767 compare: T,
6868 exchange: T,
6969 comptime success: Ordering,
7070 comptime failure: Ordering,
71 ) callconv(.Inline) ?T {
71 ) ?T {
7272 return self.cmpxchg(false, compare, exchange, success, failure);
7373 }
7474
75 fn cmpxchg(
75 inline fn cmpxchg(
7676 self: *Self,
7777 comptime is_strong: bool,
7878 compare: T,
7979 exchange: T,
8080 comptime success: Ordering,
8181 comptime failure: Ordering,
82 ) callconv(.Inline) ?T {
82 ) ?T {
8383 if (success == .Unordered or failure == .Unordered) {
8484 @compileError(@tagName(Ordering.Unordered) ++ " is only allowed on atomic loads and stores");
8585 }
......@@ -103,12 +103,12 @@ pub fn Atomic(comptime T: type) type {
103103 };
104104 }
105105
106 fn rmw(
106 inline fn rmw(
107107 self: *Self,
108108 comptime op: std.builtin.AtomicRmwOp,
109109 value: T,
110110 comptime ordering: Ordering,
111 ) callconv(.Inline) T {
111 ) T {
112112 return @atomicRmw(T, &self.value, op, value, ordering);
113113 }
114114
......@@ -117,37 +117,37 @@ pub fn Atomic(comptime T: type) type {
117117 }
118118
119119 pub usingnamespace exportWhen(std.meta.trait.isNumber(T), struct {
120 pub fn fetchAdd(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T {
120 pub inline fn fetchAdd(self: *Self, value: T, comptime ordering: Ordering) T {
121121 return self.rmw(.Add, value, ordering);
122122 }
123123
124 pub fn fetchSub(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T {
124 pub inline fn fetchSub(self: *Self, value: T, comptime ordering: Ordering) T {
125125 return self.rmw(.Sub, value, ordering);
126126 }
127127
128 pub fn fetchMin(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T {
128 pub inline fn fetchMin(self: *Self, value: T, comptime ordering: Ordering) T {
129129 return self.rmw(.Min, value, ordering);
130130 }
131131
132 pub fn fetchMax(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T {
132 pub inline fn fetchMax(self: *Self, value: T, comptime ordering: Ordering) T {
133133 return self.rmw(.Max, value, ordering);
134134 }
135135 });
136136
137137 pub usingnamespace exportWhen(std.meta.trait.isIntegral(T), struct {
138 pub fn fetchAnd(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T {
138 pub inline fn fetchAnd(self: *Self, value: T, comptime ordering: Ordering) T {
139139 return self.rmw(.And, value, ordering);
140140 }
141141
142 pub fn fetchNand(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T {
142 pub inline fn fetchNand(self: *Self, value: T, comptime ordering: Ordering) T {
143143 return self.rmw(.Nand, value, ordering);
144144 }
145145
146 pub fn fetchOr(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T {
146 pub inline fn fetchOr(self: *Self, value: T, comptime ordering: Ordering) T {
147147 return self.rmw(.Or, value, ordering);
148148 }
149149
150 pub fn fetchXor(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T {
150 pub inline fn fetchXor(self: *Self, value: T, comptime ordering: Ordering) T {
151151 return self.rmw(.Xor, value, ordering);
152152 }
153153
......@@ -158,24 +158,24 @@ pub fn Atomic(comptime T: type) type {
158158 Toggle,
159159 };
160160
161 pub fn bitSet(self: *Self, bit: Bit, comptime ordering: Ordering) callconv(.Inline) u1 {
161 pub inline fn bitSet(self: *Self, bit: Bit, comptime ordering: Ordering) u1 {
162162 return bitRmw(self, .Set, bit, ordering);
163163 }
164164
165 pub fn bitReset(self: *Self, bit: Bit, comptime ordering: Ordering) callconv(.Inline) u1 {
165 pub inline fn bitReset(self: *Self, bit: Bit, comptime ordering: Ordering) u1 {
166166 return bitRmw(self, .Reset, bit, ordering);
167167 }
168168
169 pub fn bitToggle(self: *Self, bit: Bit, comptime ordering: Ordering) callconv(.Inline) u1 {
169 pub inline fn bitToggle(self: *Self, bit: Bit, comptime ordering: Ordering) u1 {
170170 return bitRmw(self, .Toggle, bit, ordering);
171171 }
172172
173 fn bitRmw(
173 inline fn bitRmw(
174174 self: *Self,
175175 comptime op: BitRmwOp,
176176 bit: Bit,
177177 comptime ordering: Ordering,
178 ) callconv(.Inline) u1 {
178 ) u1 {
179179 // x86 supports dedicated bitwise instructions
180180 if (comptime target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) {
181181 const instruction = switch (op) {
lib/std/c.zig+4-2
......@@ -166,9 +166,10 @@ pub extern "c" fn sendto(
166166 dest_addr: ?*const sockaddr,
167167 addrlen: socklen_t,
168168) isize;
169pub extern "c" fn sendmsg(sockfd: fd_t, msg: *const std.x.os.Socket.Message, flags: c_int) isize;
169170
170pub extern fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize;
171pub extern fn recvfrom(
171pub extern "c" fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize;
172pub extern "c" fn recvfrom(
172173 sockfd: fd_t,
173174 noalias buf: *c_void,
174175 len: usize,
......@@ -176,6 +177,7 @@ pub extern fn recvfrom(
176177 noalias src_addr: ?*sockaddr,
177178 noalias addrlen: ?*socklen_t,
178179) isize;
180pub extern "c" fn recvmsg(sockfd: fd_t, msg: *std.x.os.Socket.Message, flags: c_int) isize;
179181
180182pub usingnamespace switch (builtin.os.tag) {
181183 .netbsd => struct {
lib/std/json.zig+1-4
......@@ -2111,10 +2111,7 @@ test "parse into struct with duplicate field" {
21112111 const ballast = try testing.allocator.alloc(u64, 1);
21122112 defer testing.allocator.free(ballast);
21132113
2114 const options_first = ParseOptions{
2115 .allocator = testing.allocator,
2116 .duplicate_field_behavior = .UseFirst,
2117 };
2114 const options_first = ParseOptions{ .allocator = testing.allocator, .duplicate_field_behavior = .UseFirst };
21182115
21192116 const options_last = ParseOptions{
21202117 .allocator = testing.allocator,
lib/std/mem.zig+3-3
......@@ -1171,7 +1171,7 @@ test "mem.indexOf" {
11711171test "mem.indexOf multibyte" {
11721172 {
11731173 // make haystack and needle long enough to trigger boyer-moore-horspool algorithm
1174 const haystack = [1]u16{0} ** 100 ++ [_]u16 { 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff };
1174 const haystack = [1]u16{0} ** 100 ++ [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff };
11751175 const needle = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee };
11761176 try testing.expectEqual(indexOfPos(u16, &haystack, 0, &needle), 100);
11771177
......@@ -1184,7 +1184,7 @@ test "mem.indexOf multibyte" {
11841184
11851185 {
11861186 // make haystack and needle long enough to trigger boyer-moore-horspool algorithm
1187 const haystack = [_]u16 { 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff } ++ [1]u16{0} ** 100;
1187 const haystack = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff } ++ [1]u16{0} ** 100;
11881188 const needle = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee };
11891189 try testing.expectEqual(lastIndexOf(u16, &haystack, &needle), 0);
11901190
......@@ -2201,7 +2201,7 @@ pub fn collapseRepeatsLen(comptime T: type, slice: []T, elem: T) usize {
22012201
22022202/// Collapse consecutive duplicate elements into one entry.
22032203pub fn collapseRepeats(comptime T: type, slice: []T, elem: T) []T {
2204 return slice[0 .. collapseRepeatsLen(T, slice, elem)];
2204 return slice[0..collapseRepeatsLen(T, slice, elem)];
22052205}
22062206
22072207fn testCollapseRepeats(str: []const u8, elem: u8, expected: []const u8) !void {
lib/std/os.zig+1-1
......@@ -4998,7 +4998,7 @@ pub fn sendmsg(
49984998 flags: u32,
49994999) SendMsgError!usize {
50005000 while (true) {
5001 const rc = system.sendmsg(sockfd, &msg, flags);
5001 const rc = system.sendmsg(sockfd, @ptrCast(*const std.x.os.Socket.Message, &msg), @intCast(c_int, flags));
50025002 if (builtin.os.tag == .windows) {
50035003 if (rc == windows.ws2_32.SOCKET_ERROR) {
50045004 switch (windows.ws2_32.WSAGetLastError()) {
lib/std/os/bits/darwin.zig+1-7
......@@ -23,13 +23,7 @@ pub const sockaddr = extern struct {
2323 family: sa_family_t,
2424 data: [14]u8,
2525};
26pub const sockaddr_storage = extern struct {
27 len: u8,
28 family: sa_family_t,
29 __pad1: [5]u8,
30 __align: i64,
31 __pad2: [112]u8,
32};
26pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage;
3327pub const sockaddr_in = extern struct {
3428 len: u8 = @sizeOf(sockaddr_in),
3529 family: sa_family_t = AF_INET,
lib/std/os/bits/dragonfly.zig+7-13
......@@ -396,6 +396,8 @@ pub const sockaddr = extern struct {
396396 data: [14]u8,
397397};
398398
399pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage;
400
399401pub const Kevent = extern struct {
400402 ident: usize,
401403 filter: c_short,
......@@ -694,14 +696,6 @@ pub const in_port_t = u16;
694696pub const sa_family_t = u8;
695697pub const socklen_t = u32;
696698
697pub const sockaddr_storage = extern struct {
698 ss_len: u8,
699 ss_family: sa_family_t,
700 __ss_pad1: [5]u8,
701 __ss_align: i64,
702 __ss_pad2: [112]u8,
703};
704
705699pub const sockaddr_in = extern struct {
706700 len: u8 = @sizeOf(sockaddr_in),
707701 family: sa_family_t = AF_INET,
......@@ -768,6 +762,11 @@ pub const dl_phdr_info = extern struct {
768762 dlpi_phdr: [*]std.elf.Phdr,
769763 dlpi_phnum: u16,
770764};
765pub const cmsghdr = extern struct {
766 cmsg_len: socklen_t,
767 cmsg_level: c_int,
768 cmsg_type: c_int,
769};
771770pub const msghdr = extern struct {
772771 msg_name: ?*c_void,
773772 msg_namelen: socklen_t,
......@@ -777,11 +776,6 @@ pub const msghdr = extern struct {
777776 msg_controllen: socklen_t,
778777 msg_flags: c_int,
779778};
780pub const cmsghdr = extern struct {
781 cmsg_len: socklen_t,
782 cmsg_level: c_int,
783 cmsg_type: c_int,
784};
785779pub const cmsgcred = extern struct {
786780 cmcred_pid: pid_t,
787781 cmcred_uid: uid_t,
lib/std/os/bits/freebsd.zig+1-7
......@@ -206,13 +206,7 @@ pub const sockaddr = extern struct {
206206 data: [14]u8,
207207};
208208
209pub const sockaddr_storage = extern struct {
210 len: u8,
211 family: sa_family_t,
212 __pad1: [5]u8,
213 __align: i64,
214 __pad2: [112]u8,
215};
209pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage;
216210
217211pub const sockaddr_in = extern struct {
218212 len: u8 = @sizeOf(sockaddr_in),
lib/std/os/bits/haiku.zig+1-7
......@@ -239,13 +239,7 @@ pub const sockaddr = extern struct {
239239 data: [14]u8,
240240};
241241
242pub const sockaddr_storage = extern struct {
243 len: u8,
244 family: sa_family_t,
245 __pad1: [5]u8,
246 __align: i64,
247 __pad2: [112]u8,
248};
242pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage;
249243
250244pub const sockaddr_in = extern struct {
251245 len: u8 = @sizeOf(sockaddr_in),
lib/std/os/bits/linux.zig+1-6
......@@ -1149,12 +1149,7 @@ pub const sockaddr = extern struct {
11491149 data: [14]u8,
11501150};
11511151
1152pub const sockaddr_storage = extern struct {
1153 family: sa_family_t,
1154 __pad1: [6]u8,
1155 __align: i64,
1156 __pad2: [112]u8,
1157};
1152pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage;
11581153
11591154/// IPv4 socket address
11601155pub const sockaddr_in = extern struct {
lib/std/os/bits/netbsd.zig+1-7
......@@ -226,13 +226,7 @@ pub const sockaddr = extern struct {
226226 data: [14]u8,
227227};
228228
229pub const sockaddr_storage = extern struct {
230 len: u8,
231 family: sa_family_t,
232 __pad1: [5]u8,
233 __align: i64,
234 __pad2: [112]u8,
235};
229pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage;
236230
237231pub const sockaddr_in = extern struct {
238232 len: u8 = @sizeOf(sockaddr_in),
lib/std/os/bits/openbsd.zig+1-7
......@@ -246,13 +246,7 @@ pub const sockaddr = extern struct {
246246 data: [14]u8,
247247};
248248
249pub const sockaddr_storage = extern struct {
250 len: u8,
251 family: sa_family_t,
252 __pad1: [5]u8,
253 __align: i64,
254 __pad2: [112]u8,
255};
249pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage;
256250
257251pub const sockaddr_in = extern struct {
258252 len: u8 = @sizeOf(sockaddr_in),
lib/std/os/linux.zig+6-6
......@@ -1000,11 +1000,11 @@ pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noal
10001000 return syscall5(.getsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
10011001}
10021002
1003pub fn sendmsg(fd: i32, msg: *const msghdr_const, flags: u32) usize {
1003pub fn sendmsg(fd: i32, msg: *const std.x.os.Socket.Message, flags: c_int) usize {
10041004 if (native_arch == .i386) {
1005 return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags });
1005 return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)) });
10061006 }
1007 return syscall3(.sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags);
1007 return syscall3(.sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)));
10081008}
10091009
10101010pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize {
......@@ -1054,11 +1054,11 @@ pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize {
10541054 return syscall3(.connect, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len);
10551055}
10561056
1057pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize {
1057pub fn recvmsg(fd: i32, msg: *std.x.os.Socket.Message, flags: c_int) usize {
10581058 if (native_arch == .i386) {
1059 return socketcall(SC_recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags });
1059 return socketcall(SC_recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)) });
10601060 }
1061 return syscall3(.recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags);
1061 return syscall3(.recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)));
10621062}
10631063
10641064pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize {
lib/std/os/windows.zig+1-1
......@@ -1844,7 +1844,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {
18441844}
18451845
18461846fn getFullPathNameW(path: [*:0]const u16, out: []u16) !usize {
1847 const result= kernel32.GetFullPathNameW(path, @intCast(u32, out.len), std.meta.assumeSentinel(out.ptr, 0), null);
1847 const result = kernel32.GetFullPathNameW(path, @intCast(u32, out.len), std.meta.assumeSentinel(out.ptr, 0), null);
18481848 if (result == 0) {
18491849 switch (kernel32.GetLastError()) {
18501850 else => |err| return unexpectedError(err),
lib/std/os/windows/ws2_32.zig+6-10
......@@ -3,6 +3,7 @@
33// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
6const std = @import("../../std.zig");
67usingnamespace @import("bits.zig");
78
89pub const SOCKET = *opaque {};
......@@ -1058,12 +1059,7 @@ pub const sockaddr = extern struct {
10581059 data: [14]u8,
10591060};
10601061
1061pub const sockaddr_storage = extern struct {
1062 family: ADDRESS_FAMILY,
1063 __pad1: [6]u8,
1064 __align: i64,
1065 __pad2: [112]u8,
1066};
1062pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage;
10671063
10681064/// IPv4 socket address
10691065pub const sockaddr_in = extern struct {
......@@ -1163,7 +1159,7 @@ pub const LPFN_GETACCEPTEXSOCKADDRS = fn (
11631159
11641160pub const LPFN_WSASENDMSG = fn (
11651161 s: SOCKET,
1166 lpMsg: *const WSAMSG_const,
1162 lpMsg: *const std.x.os.Socket.Message,
11671163 dwFlags: u32,
11681164 lpNumberOfBytesSent: ?*u32,
11691165 lpOverlapped: ?*OVERLAPPED,
......@@ -1172,7 +1168,7 @@ pub const LPFN_WSASENDMSG = fn (
11721168
11731169pub const LPFN_WSARECVMSG = fn (
11741170 s: SOCKET,
1175 lpMsg: *WSAMSG,
1171 lpMsg: *std.x.os.Socket.Message,
11761172 lpdwNumberOfBytesRecv: ?*u32,
11771173 lpOverlapped: ?*OVERLAPPED,
11781174 lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
......@@ -2046,7 +2042,7 @@ pub extern "ws2_32" fn WSASend(
20462042
20472043pub extern "ws2_32" fn WSASendMsg(
20482044 s: SOCKET,
2049 lpMsg: *const WSAMSG_const,
2045 lpMsg: *const std.x.os.Socket.Message,
20502046 dwFlags: u32,
20512047 lpNumberOfBytesSent: ?*u32,
20522048 lpOverlapped: ?*OVERLAPPED,
......@@ -2055,7 +2051,7 @@ pub extern "ws2_32" fn WSASendMsg(
20552051
20562052pub extern "ws2_32" fn WSARecvMsg(
20572053 s: SOCKET,
2058 lpMsg: *WSAMSG,
2054 lpMsg: *std.x.os.Socket.Message,
20592055 lpdwNumberOfBytesRecv: ?*u32,
20602056 lpOverlapped: ?*OVERLAPPED,
20612057 lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
lib/std/target.zig+1-2
......@@ -500,8 +500,7 @@ pub const Target = struct {
500500 .haiku,
501501 .windows,
502502 => return .gnu,
503 .uefi,
504 => return .msvc,
503 .uefi => return .msvc,
505504 .linux,
506505 .wasi,
507506 .emscripten,
lib/std/x.zig+1
......@@ -8,6 +8,7 @@ const std = @import("std.zig");
88
99pub const os = struct {
1010 pub const Socket = @import("x/os/socket.zig").Socket;
11 pub usingnamespace @import("x/os/io.zig");
1112 pub usingnamespace @import("x/os/net.zig");
1213};
1314
lib/std/x/net/tcp.zig+65-23
......@@ -12,12 +12,13 @@ const ip = std.x.net.ip;
1212
1313const fmt = std.fmt;
1414const mem = std.mem;
15const builtin = std.builtin;
1615const testing = std.testing;
16const native_os = std.Target.current.os;
1717
1818const IPv4 = std.x.os.IPv4;
1919const IPv6 = std.x.os.IPv6;
2020const Socket = std.x.os.Socket;
21const Buffer = std.x.os.Buffer;
2122
2223/// A generic TCP socket abstraction.
2324const tcp = @This();
......@@ -82,12 +83,13 @@ pub const Client = struct {
8283 };
8384
8485 /// Opens a new client.
85 pub fn init(domain: tcp.Domain, flags: u32) !Client {
86 pub fn init(domain: tcp.Domain, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Client {
8687 return Client{
8788 .socket = try Socket.init(
8889 @enumToInt(domain),
89 os.SOCK_STREAM | flags,
90 os.SOCK_STREAM,
9091 os.IPPROTO_TCP,
92 flags,
9193 ),
9294 };
9395 }
......@@ -143,15 +145,15 @@ pub const Client = struct {
143145 /// Writes multiple I/O vectors with a prepended message header to the socket
144146 /// with a set of flags specified. It returns the number of bytes that are
145147 /// written to the socket.
146 pub fn writeVectorized(self: Client, msg: os.msghdr_const, flags: u32) !usize {
147 return self.socket.writeVectorized(msg, flags);
148 pub fn writeMessage(self: Client, msg: Socket.Message, flags: u32) !usize {
149 return self.socket.writeMessage(msg, flags);
148150 }
149151
150152 /// Read multiple I/O vectors with a prepended message header from the socket
151153 /// with a set of flags specified. It returns the number of bytes that were
152154 /// read into the buffer provided.
153 pub fn readVectorized(self: Client, msg: *os.msghdr, flags: u32) !usize {
154 return self.socket.readVectorized(msg, flags);
155 pub fn readMessage(self: Client, msg: *Socket.Message, flags: u32) !usize {
156 return self.socket.readMessage(msg, flags);
155157 }
156158
157159 /// Query and return the latest cached error on the client's underlying socket.
......@@ -244,12 +246,13 @@ pub const Listener = struct {
244246 socket: Socket,
245247
246248 /// Opens a new listener.
247 pub fn init(domain: tcp.Domain, flags: u32) !Listener {
249 pub fn init(domain: tcp.Domain, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Listener {
248250 return Listener{
249251 .socket = try Socket.init(
250252 @enumToInt(domain),
251 os.SOCK_STREAM | flags,
253 os.SOCK_STREAM,
252254 os.IPPROTO_TCP,
255 flags,
253256 ),
254257 };
255258 }
......@@ -278,7 +281,7 @@ pub const Listener = struct {
278281
279282 /// Accept a pending incoming connection queued to the kernel backlog
280283 /// of the listener's socket.
281 pub fn accept(self: Listener, flags: u32) !tcp.Connection {
284 pub fn accept(self: Listener, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !tcp.Connection {
282285 return tcp.Connection.from(try self.socket.accept(flags));
283286 }
284287
......@@ -322,9 +325,9 @@ pub const Listener = struct {
322325};
323326
324327test "tcp: create client/listener pair" {
325 if (builtin.os.tag == .wasi) return error.SkipZigTest;
328 if (native_os.tag == .wasi) return error.SkipZigTest;
326329
327 const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC);
330 const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true });
328331 defer listener.deinit();
329332
330333 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
......@@ -336,19 +339,19 @@ test "tcp: create client/listener pair" {
336339 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
337340 }
338341
339 const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC);
342 const client = try tcp.Client.init(.ip, .{ .close_on_exec = true });
340343 defer client.deinit();
341344
342345 try client.connect(binded_address);
343346
344 const conn = try listener.accept(os.SOCK_CLOEXEC);
347 const conn = try listener.accept(.{ .close_on_exec = true });
345348 defer conn.deinit();
346349}
347350
348test "tcp/client: set read timeout of 1 millisecond on blocking client" {
349 if (builtin.os.tag == .wasi) return error.SkipZigTest;
351test "tcp/client: 1ms read timeout" {
352 if (native_os.tag == .wasi) return error.SkipZigTest;
350353
351 const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC);
354 const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true });
352355 defer listener.deinit();
353356
354357 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
......@@ -360,23 +363,62 @@ test "tcp/client: set read timeout of 1 millisecond on blocking client" {
360363 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
361364 }
362365
363 const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC);
366 const client = try tcp.Client.init(.ip, .{ .close_on_exec = true });
364367 defer client.deinit();
365368
366369 try client.connect(binded_address);
367370 try client.setReadTimeout(1);
368371
369 const conn = try listener.accept(os.SOCK_CLOEXEC);
372 const conn = try listener.accept(.{ .close_on_exec = true });
370373 defer conn.deinit();
371374
372375 var buf: [1]u8 = undefined;
373376 try testing.expectError(error.WouldBlock, client.reader(0).read(&buf));
374377}
375378
379test "tcp/client: read and write multiple vectors" {
380 if (native_os.tag == .wasi) return error.SkipZigTest;
381
382 const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true });
383 defer listener.deinit();
384
385 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
386 try listener.listen(128);
387
388 var binded_address = try listener.getLocalAddress();
389 switch (binded_address) {
390 .ipv4 => |*ipv4| ipv4.host = IPv4.localhost,
391 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
392 }
393
394 const client = try tcp.Client.init(.ip, .{ .close_on_exec = true });
395 defer client.deinit();
396
397 try client.connect(binded_address);
398
399 const conn = try listener.accept(.{ .close_on_exec = true });
400 defer conn.deinit();
401
402 const message = "hello world";
403 _ = try conn.client.writeMessage(Socket.Message.fromBuffers(&[_]Buffer{
404 Buffer.from(message[0 .. message.len / 2]),
405 Buffer.from(message[message.len / 2 ..]),
406 }), 0);
407
408 var buf: [message.len + 1]u8 = undefined;
409 var msg = Socket.Message.fromBuffers(&[_]Buffer{
410 Buffer.from(buf[0 .. message.len / 2]),
411 Buffer.from(buf[message.len / 2 ..]),
412 });
413 _ = try client.readMessage(&msg, 0);
414
415 try testing.expectEqualStrings(message, buf[0..message.len]);
416}
417
376418test "tcp/listener: bind to unspecified ipv4 address" {
377 if (builtin.os.tag == .wasi) return error.SkipZigTest;
419 if (native_os.tag == .wasi) return error.SkipZigTest;
378420
379 const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC);
421 const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true });
380422 defer listener.deinit();
381423
382424 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
......@@ -387,9 +429,9 @@ test "tcp/listener: bind to unspecified ipv4 address" {
387429}
388430
389431test "tcp/listener: bind to unspecified ipv6 address" {
390 if (builtin.os.tag == .wasi) return error.SkipZigTest;
432 if (native_os.tag == .wasi) return error.SkipZigTest;
391433
392 const listener = try tcp.Listener.init(.ipv6, os.SOCK_CLOEXEC);
434 const listener = try tcp.Listener.init(.ipv6, .{ .close_on_exec = true });
393435 defer listener.deinit();
394436
395437 try listener.bind(ip.Address.initIPv6(IPv6.unspecified, 0));
lib/std/x/os/io.zig created+205
......@@ -0,0 +1,205 @@
1const std = @import("../../std.zig");
2
3const os = std.os;
4const mem = std.mem;
5const testing = std.testing;
6const native_os = std.Target.current.os;
7
8/// POSIX `iovec`, or Windows `WSABUF`. The difference between the two are the ordering
9/// of fields, alongside the length being represented as either a ULONG or a size_t.
10pub const Buffer = if (native_os.tag == .windows)
11 extern struct {
12 len: c_ulong,
13 ptr: usize,
14
15 pub fn from(slice: []const u8) Buffer {
16 return .{ .len = @intCast(c_ulong, slice.len), .ptr = @ptrToInt(slice.ptr) };
17 }
18
19 pub fn into(self: Buffer) []const u8 {
20 return @intToPtr([*]const u8, self.ptr)[0..self.len];
21 }
22
23 pub fn intoMutable(self: Buffer) []u8 {
24 return @intToPtr([*]u8, self.ptr)[0..self.len];
25 }
26 }
27else
28 extern struct {
29 ptr: usize,
30 len: usize,
31
32 pub fn from(slice: []const u8) Buffer {
33 return .{ .ptr = @ptrToInt(slice.ptr), .len = slice.len };
34 }
35
36 pub fn into(self: Buffer) []const u8 {
37 return @intToPtr([*]const u8, self.ptr)[0..self.len];
38 }
39
40 pub fn intoMutable(self: Buffer) []u8 {
41 return @intToPtr([*]u8, self.ptr)[0..self.len];
42 }
43 };
44
45pub const Reactor = struct {
46 pub const InitFlags = enum {
47 close_on_exec,
48 };
49
50 pub const Event = struct {
51 data: usize,
52 is_error: bool,
53 is_hup: bool,
54 is_readable: bool,
55 is_writable: bool,
56 };
57
58 pub const Interest = struct {
59 hup: bool = false,
60 oneshot: bool = false,
61 readable: bool = false,
62 writable: bool = false,
63 };
64
65 fd: os.fd_t,
66
67 pub fn init(flags: std.enums.EnumFieldStruct(Reactor.InitFlags, bool, false)) !Reactor {
68 var raw_flags: u32 = 0;
69 const set = std.EnumSet(Reactor.InitFlags).init(flags);
70 if (set.contains(.close_on_exec)) raw_flags |= os.EPOLL_CLOEXEC;
71 return Reactor{ .fd = try os.epoll_create1(raw_flags) };
72 }
73
74 pub fn deinit(self: Reactor) void {
75 os.close(self.fd);
76 }
77
78 pub fn update(self: Reactor, fd: os.fd_t, identifier: usize, interest: Reactor.Interest) !void {
79 var flags: u32 = 0;
80 flags |= if (interest.oneshot) os.EPOLLONESHOT else os.EPOLLET;
81 if (interest.hup) flags |= os.EPOLLRDHUP;
82 if (interest.readable) flags |= os.EPOLLIN;
83 if (interest.writable) flags |= os.EPOLLOUT;
84
85 const event = &os.epoll_event{
86 .events = flags,
87 .data = .{ .ptr = identifier },
88 };
89
90 os.epoll_ctl(self.fd, os.EPOLL_CTL_MOD, fd, event) catch |err| switch (err) {
91 error.FileDescriptorNotRegistered => try os.epoll_ctl(self.fd, os.EPOLL_CTL_ADD, fd, event),
92 else => return err,
93 };
94 }
95
96 pub fn poll(self: Reactor, comptime max_num_events: comptime_int, closure: anytype, timeout_milliseconds: ?u64) !void {
97 var events: [max_num_events]os.epoll_event = undefined;
98
99 const num_events = os.epoll_wait(self.fd, &events, if (timeout_milliseconds) |ms| @intCast(i32, ms) else -1);
100 for (events[0..num_events]) |ev| {
101 const is_error = ev.events & os.EPOLLERR != 0;
102 const is_hup = ev.events & (os.EPOLLHUP | os.EPOLLRDHUP) != 0;
103 const is_readable = ev.events & os.EPOLLIN != 0;
104 const is_writable = ev.events & os.EPOLLOUT != 0;
105
106 try closure.call(Reactor.Event{
107 .data = ev.data.ptr,
108 .is_error = is_error,
109 .is_hup = is_hup,
110 .is_readable = is_readable,
111 .is_writable = is_writable,
112 });
113 }
114 }
115};
116
117test "reactor/linux: drive async tcp client/listener pair" {
118 if (native_os.tag != .linux) return error.SkipZigTest;
119
120 const ip = std.x.net.ip;
121 const tcp = std.x.net.tcp;
122
123 const IPv4 = std.x.os.IPv4;
124 const IPv6 = std.x.os.IPv6;
125 const Socket = std.x.os.Socket;
126
127 const reactor = try Reactor.init(.{ .close_on_exec = true });
128 defer reactor.deinit();
129
130 const listener = try tcp.Listener.init(.ip, .{
131 .close_on_exec = true,
132 .nonblocking = true,
133 });
134 defer listener.deinit();
135
136 try reactor.update(listener.socket.fd, 0, .{ .readable = true });
137 try reactor.poll(1, struct {
138 fn call(event: Reactor.Event) !void {
139 try testing.expectEqual(Reactor.Event{
140 .data = 0,
141 .is_error = false,
142 .is_hup = true,
143 .is_readable = false,
144 .is_writable = false,
145 }, event);
146 }
147 }, null);
148
149 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
150 try listener.listen(128);
151
152 var binded_address = try listener.getLocalAddress();
153 switch (binded_address) {
154 .ipv4 => |*ipv4| ipv4.host = IPv4.localhost,
155 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
156 }
157
158 const client = try tcp.Client.init(.ip, .{
159 .close_on_exec = true,
160 .nonblocking = true,
161 });
162 defer client.deinit();
163
164 try reactor.update(client.socket.fd, 1, .{ .readable = true, .writable = true });
165 try reactor.poll(1, struct {
166 fn call(event: Reactor.Event) !void {
167 try testing.expectEqual(Reactor.Event{
168 .data = 1,
169 .is_error = false,
170 .is_hup = true,
171 .is_readable = false,
172 .is_writable = true,
173 }, event);
174 }
175 }, null);
176
177 client.connect(binded_address) catch |err| switch (err) {
178 error.WouldBlock => {},
179 else => return err,
180 };
181
182 try reactor.poll(1, struct {
183 fn call(event: Reactor.Event) !void {
184 try testing.expectEqual(Reactor.Event{
185 .data = 1,
186 .is_error = false,
187 .is_hup = false,
188 .is_readable = false,
189 .is_writable = true,
190 }, event);
191 }
192 }, null);
193
194 try reactor.poll(1, struct {
195 fn call(event: Reactor.Event) !void {
196 try testing.expectEqual(Reactor.Event{
197 .data = 0,
198 .is_error = false,
199 .is_hup = false,
200 .is_readable = true,
201 .is_writable = false,
202 }, event);
203 }
204 }, null);
205}
lib/std/x/os/net.zig+3-3
......@@ -10,17 +10,17 @@ const os = std.os;
1010const fmt = std.fmt;
1111const mem = std.mem;
1212const math = std.math;
13const builtin = std.builtin;
1413const testing = std.testing;
14const native_os = std.Target.current.os;
1515
1616/// Resolves a network interface name into a scope/zone ID. It returns
1717/// an error if either resolution fails, or if the interface name is
1818/// too long.
1919pub fn resolveScopeID(name: []const u8) !u32 {
20 if (comptime @hasDecl(os, "IFNAMESIZE")) {
20 if (@hasDecl(os, "IFNAMESIZE")) {
2121 if (name.len >= os.IFNAMESIZE - 1) return error.NameTooLong;
2222
23 if (comptime builtin.os.tag == .windows) {
23 if (native_os.tag == .windows) {
2424 var interface_name: [os.IFNAMESIZE]u8 = undefined;
2525 mem.copy(u8, &interface_name, name);
2626 interface_name[name.len] = 0;
lib/std/x/os/socket.zig+202-2
......@@ -11,7 +11,13 @@ const os = std.os;
1111const fmt = std.fmt;
1212const mem = std.mem;
1313const time = std.time;
14const builtin = std.builtin;
14const meta = std.meta;
15const native_os = std.Target.current.os;
16const native_endian = std.Target.current.cpu.arch.endian();
17
18const Buffer = std.x.os.Buffer;
19
20const assert = std.debug.assert;
1521
1622/// A generic, cross-platform socket abstraction.
1723pub const Socket = struct {
......@@ -29,6 +35,32 @@ pub const Socket = struct {
2935 /// A generic socket address abstraction. It is safe to directly access and modify
3036 /// the fields of a `Socket.Address`.
3137 pub const Address = union(enum) {
38 pub const Native = struct {
39 pub const requires_prepended_length = native_os.getVersionRange() == .semver;
40 pub const Length = if (requires_prepended_length) u8 else [0]u8;
41
42 pub const Family = if (requires_prepended_length) u8 else c_ushort;
43
44 /// POSIX `sockaddr_storage`. The expected size and alignment is specified in IETF RFC 2553.
45 pub const Storage = extern struct {
46 pub const expected_size = 128;
47 pub const expected_alignment = 8;
48
49 pub const padding_size = expected_size -
50 mem.alignForward(@sizeOf(Address.Native.Length), expected_alignment) -
51 mem.alignForward(@sizeOf(Address.Native.Family), expected_alignment);
52
53 len: Address.Native.Length align(expected_alignment) = undefined,
54 family: Address.Native.Family align(expected_alignment) = undefined,
55 padding: [padding_size]u8 align(expected_alignment) = undefined,
56
57 comptime {
58 assert(@sizeOf(Storage) == Storage.expected_size);
59 assert(@alignOf(Storage) == Storage.expected_alignment);
60 }
61 };
62 };
63
3264 ipv4: net.IPv4.Address,
3365 ipv6: net.IPv6.Address,
3466
......@@ -107,6 +139,174 @@ pub const Socket = struct {
107139 }
108140 };
109141
142 /// POSIX `msghdr`. Denotes a destination address, set of buffers, control data, and flags. Ported
143 /// directly from musl.
144 pub const Message = if (native_os.isAtLeast(.windows, .vista) != null and native_os.isAtLeast(.windows, .vista).?)
145 extern struct {
146 name: usize = @ptrToInt(@as(?[*]u8, null)),
147 name_len: c_int = 0,
148
149 buffers: usize = undefined,
150 buffers_len: c_ulong = undefined,
151
152 control: Buffer = .{
153 .ptr = @ptrToInt(@as(?[*]u8, null)),
154 .len = 0,
155 },
156 flags: c_ulong = 0,
157
158 pub usingnamespace MessageMixin(Message);
159 }
160 else if (native_os.tag == .windows)
161 extern struct {
162 name: usize = @ptrToInt(@as(?[*]u8, null)),
163 name_len: c_int = 0,
164
165 buffers: usize = undefined,
166 buffers_len: u32 = undefined,
167
168 control: Buffer = .{
169 .ptr = @ptrToInt(@as(?[*]u8, null)),
170 .len = 0,
171 },
172 flags: u32 = 0,
173
174 pub usingnamespace MessageMixin(Message);
175 }
176 else if (@sizeOf(usize) > 4 and native_endian == .Big)
177 extern struct {
178 name: usize = @ptrToInt(@as(?[*]u8, null)),
179 name_len: c_uint = 0,
180
181 buffers: usize = undefined,
182 _pad_1: c_int = 0,
183 buffers_len: c_int = undefined,
184
185 control: usize = @ptrToInt(@as(?[*]u8, null)),
186 _pad_2: c_int = 0,
187 control_len: c_uint = 0,
188
189 flags: c_int = 0,
190
191 pub usingnamespace MessageMixin(Message);
192 }
193 else if (@sizeOf(usize) > 4 and native_endian == .Little)
194 extern struct {
195 name: usize = @ptrToInt(@as(?[*]u8, null)),
196 name_len: c_uint = 0,
197
198 buffers: usize = undefined,
199 buffers_len: c_int = undefined,
200 _pad_1: c_int = 0,
201
202 control: usize = @ptrToInt(@as(?[*]u8, null)),
203 control_len: c_uint = 0,
204 _pad_2: c_int = 0,
205
206 flags: c_int = 0,
207
208 pub usingnamespace MessageMixin(Message);
209 }
210 else
211 extern struct {
212 name: usize = @ptrToInt(@as(?[*]u8, null)),
213 name_len: c_uint = 0,
214
215 buffers: usize = undefined,
216 buffers_len: c_int = undefined,
217
218 control: usize = @ptrToInt(@as(?[*]u8, null)),
219 control_len: c_uint = 0,
220
221 flags: c_int = 0,
222
223 pub usingnamespace MessageMixin(Message);
224 };
225
226 fn MessageMixin(comptime Self: type) type {
227 return struct {
228 pub fn fromBuffers(buffers: []const Buffer) Self {
229 var self: Self = .{};
230 self.setBuffers(buffers);
231 return self;
232 }
233
234 pub fn setName(self: *Self, name: []const u8) void {
235 self.name = @ptrToInt(name.ptr);
236 self.name_len = @intCast(meta.fieldInfo(Self, .name_len).field_type, name.len);
237 }
238
239 pub fn setBuffers(self: *Self, buffers: []const Buffer) void {
240 self.buffers = @ptrToInt(buffers.ptr);
241 self.buffers_len = @intCast(meta.fieldInfo(Self, .buffers_len).field_type, buffers.len);
242 }
243
244 pub fn setControl(self: *Self, control: []const u8) void {
245 if (native_os.tag == .windows) {
246 self.control = Buffer.from(control);
247 } else {
248 self.control = @ptrToInt(control.ptr);
249 self.control_len = @intCast(meta.fieldInfo(Self, .control_len).field_type, control.len);
250 }
251 }
252
253 pub fn setFlags(self: *Self, flags: u32) void {
254 self.flags = @intCast(meta.fieldInfo(Self, .flags).field_type, flags);
255 }
256
257 pub fn getName(self: Self) []const u8 {
258 return @intToPtr([*]const u8, self.name)[0..@intCast(usize, self.name_len)];
259 }
260
261 pub fn getBuffers(self: Self) []const Buffer {
262 return @intToPtr([*]const Buffer, self.buffers)[0..@intCast(usize, self.buffers_len)];
263 }
264
265 pub fn getControl(self: Self) []const u8 {
266 if (native_os.tag == .windows) {
267 return self.control.into();
268 } else {
269 return @intToPtr([*]const u8, self.control)[0..@intCast(usize, self.control_len)];
270 }
271 }
272
273 pub fn getFlags(self: Self) u32 {
274 return @intCast(u32, self.flags);
275 }
276 };
277 }
278
279 /// POSIX `linger`, denoting the linger settings of a socket.
280 ///
281 /// Microsoft's documentation and glibc denote the fields to be unsigned
282 /// short's on Windows, whereas glibc and musl denote the fields to be
283 /// int's on every other platform.
284 pub const Linger = extern struct {
285 pub const Field = switch (native_os.tag) {
286 .windows => c_ushort,
287 else => c_int,
288 };
289
290 enabled: Field,
291 timeout_seconds: Field,
292
293 pub fn init(timeout_seconds: ?u16) Socket.Linger {
294 return .{
295 .enabled = @intCast(Socket.Linger.Field, @boolToInt(timeout_seconds != null)),
296 .timeout_seconds = if (timeout_seconds) |seconds| @intCast(Socket.Linger.Field, seconds) else 0,
297 };
298 }
299 };
300
301 /// Possible set of flags to initialize a socket with.
302 pub const InitFlags = enum {
303 // Initialize a socket to be non-blocking.
304 nonblocking,
305
306 // Have a socket close itself on exec syscalls.
307 close_on_exec,
308 };
309
110310 /// The underlying handle of a socket.
111311 fd: os.socket_t,
112312
......@@ -116,7 +316,7 @@ pub const Socket = struct {
116316 }
117317
118318 /// Mix in socket syscalls depending on the platform we are compiling against.
119 pub usingnamespace switch (builtin.os.tag) {
319 pub usingnamespace switch (native_os.tag) {
120320 .windows => @import("socket_windows.zig"),
121321 else => @import("socket_posix.zig"),
122322 }.Mixin(Socket);
lib/std/x/os/socket_posix.zig+70-40
......@@ -13,8 +13,12 @@ const time = std.time;
1313pub fn Mixin(comptime Socket: type) type {
1414 return struct {
1515 /// Open a new socket.
16 pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
17 return Socket{ .fd = try os.socket(domain, socket_type, protocol) };
16 pub fn init(domain: u32, socket_type: u32, protocol: u32, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket {
17 var raw_flags: u32 = socket_type;
18 const set = std.EnumSet(Socket.InitFlags).init(flags);
19 if (set.contains(.close_on_exec)) raw_flags |= os.SOCK_CLOEXEC;
20 if (set.contains(.nonblocking)) raw_flags |= os.SOCK_NONBLOCK;
21 return Socket{ .fd = try os.socket(domain, raw_flags, protocol) };
1822 }
1923
2024 /// Closes the socket.
......@@ -44,11 +48,16 @@ pub fn Mixin(comptime Socket: type) type {
4448
4549 /// Accept a pending incoming connection queued to the kernel backlog
4650 /// of the socket.
47 pub fn accept(self: Socket, flags: u32) !Socket.Connection {
48 var address: os.sockaddr_storage = undefined;
49 var address_len: u32 = @sizeOf(os.sockaddr_storage);
51 pub fn accept(self: Socket, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket.Connection {
52 var address: Socket.Address.Native.Storage = undefined;
53 var address_len: u32 = @sizeOf(Socket.Address.Native.Storage);
5054
51 const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags) };
55 var raw_flags: u32 = 0;
56 const set = std.EnumSet(Socket.InitFlags).init(flags);
57 if (set.contains(.close_on_exec)) raw_flags |= os.SOCK_CLOEXEC;
58 if (set.contains(.nonblocking)) raw_flags |= os.SOCK_NONBLOCK;
59
60 const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, raw_flags) };
5261 const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
5362
5463 return Socket.Connection.from(socket, socket_address);
......@@ -69,48 +78,76 @@ pub fn Mixin(comptime Socket: type) type {
6978 /// Writes multiple I/O vectors with a prepended message header to the socket
7079 /// with a set of flags specified. It returns the number of bytes that are
7180 /// written to the socket.
72 pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
73 return os.sendmsg(self.fd, msg, flags);
81 pub fn writeMessage(self: Socket, msg: Socket.Message, flags: u32) !usize {
82 while (true) {
83 const rc = os.system.sendmsg(self.fd, &msg, @intCast(c_int, flags));
84 return switch (os.errno(rc)) {
85 0 => return @intCast(usize, rc),
86 os.EACCES => error.AccessDenied,
87 os.EAGAIN => error.WouldBlock,
88 os.EALREADY => error.FastOpenAlreadyInProgress,
89 os.EBADF => unreachable, // always a race condition
90 os.ECONNRESET => error.ConnectionResetByPeer,
91 os.EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
92 os.EFAULT => unreachable, // An invalid user space address was specified for an argument.
93 os.EINTR => continue,
94 os.EINVAL => unreachable, // Invalid argument passed.
95 os.EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
96 os.EMSGSIZE => error.MessageTooBig,
97 os.ENOBUFS => error.SystemResources,
98 os.ENOMEM => error.SystemResources,
99 os.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
100 os.EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
101 os.EPIPE => error.BrokenPipe,
102 os.EAFNOSUPPORT => error.AddressFamilyNotSupported,
103 os.ELOOP => error.SymLinkLoop,
104 os.ENAMETOOLONG => error.NameTooLong,
105 os.ENOENT => error.FileNotFound,
106 os.ENOTDIR => error.NotDir,
107 os.EHOSTUNREACH => error.NetworkUnreachable,
108 os.ENETUNREACH => error.NetworkUnreachable,
109 os.ENOTCONN => error.SocketNotConnected,
110 os.ENETDOWN => error.NetworkSubsystemFailed,
111 else => |err| os.unexpectedErrno(err),
112 };
113 }
74114 }
75115
76116 /// Read multiple I/O vectors with a prepended message header from the socket
77117 /// with a set of flags specified. It returns the number of bytes that were
78118 /// read into the buffer provided.
79 pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize {
80 if (comptime @hasDecl(os.system, "recvmsg")) {
81 while (true) {
82 const rc = os.system.recvmsg(self.fd, msg, flags);
83 return switch (os.errno(rc)) {
84 0 => @intCast(usize, rc),
85 os.EBADF => unreachable, // always a race condition
86 os.EFAULT => unreachable,
87 os.EINVAL => unreachable,
88 os.ENOTCONN => unreachable,
89 os.ENOTSOCK => unreachable,
90 os.EINTR => continue,
91 os.EAGAIN => error.WouldBlock,
92 os.ENOMEM => error.SystemResources,
93 os.ECONNREFUSED => error.ConnectionRefused,
94 os.ECONNRESET => error.ConnectionResetByPeer,
95 else => |err| os.unexpectedErrno(err),
96 };
97 }
119 pub fn readMessage(self: Socket, msg: *Socket.Message, flags: u32) !usize {
120 while (true) {
121 const rc = os.system.recvmsg(self.fd, msg, @intCast(c_int, flags));
122 return switch (os.errno(rc)) {
123 0 => @intCast(usize, rc),
124 os.EBADF => unreachable, // always a race condition
125 os.EFAULT => unreachable,
126 os.EINVAL => unreachable,
127 os.ENOTCONN => unreachable,
128 os.ENOTSOCK => unreachable,
129 os.EINTR => continue,
130 os.EAGAIN => error.WouldBlock,
131 os.ENOMEM => error.SystemResources,
132 os.ECONNREFUSED => error.ConnectionRefused,
133 os.ECONNRESET => error.ConnectionResetByPeer,
134 else => |err| os.unexpectedErrno(err),
135 };
98136 }
99 return error.NotSupported;
100137 }
101138
102139 /// Query the address that the socket is locally bounded to.
103140 pub fn getLocalAddress(self: Socket) !Socket.Address {
104 var address: os.sockaddr_storage = undefined;
105 var address_len: u32 = @sizeOf(os.sockaddr_storage);
141 var address: Socket.Address.Native.Storage = undefined;
142 var address_len: u32 = @sizeOf(Socket.Address.Native.Storage);
106143 try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
107144 return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
108145 }
109146
110147 /// Query the address that the socket is connected to.
111148 pub fn getRemoteAddress(self: Socket) !Socket.Address {
112 var address: os.sockaddr_storage = undefined;
113 var address_len: u32 = @sizeOf(os.sockaddr_storage);
149 var address: Socket.Address.Native.Storage = undefined;
150 var address_len: u32 = @sizeOf(Socket.Address.Native.Storage);
114151 try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
115152 return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
116153 }
......@@ -165,14 +202,7 @@ pub fn Mixin(comptime Socket: type) type {
165202 /// seconds.
166203 pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
167204 if (comptime @hasDecl(os, "SO_LINGER")) {
168 const settings = extern struct {
169 l_onoff: c_int,
170 l_linger: c_int,
171 }{
172 .l_onoff = @intCast(c_int, @boolToInt(timeout_seconds != null)),
173 .l_linger = if (timeout_seconds) |seconds| @intCast(c_int, seconds) else 0,
174 };
175
205 const settings = Socket.Linger.init(timeout_seconds);
176206 return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings));
177207 }
178208
lib/std/x/os/socket_windows.zig+39-27
......@@ -16,27 +16,24 @@ const ws2_32 = windows.ws2_32;
1616pub fn Mixin(comptime Socket: type) type {
1717 return struct {
1818 /// Open a new socket.
19 pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
20 var filtered_socket_type = socket_type & ~@as(u32, os.SOCK_CLOEXEC);
21
22 var filtered_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED;
23 if (socket_type & os.SOCK_CLOEXEC != 0) {
24 filtered_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
25 }
19 pub fn init(domain: u32, socket_type: u32, protocol: u32, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket {
20 var raw_flags: u32 = 0;
21 const set = std.EnumSet(Socket.InitFlags).init(flags);
22 if (set.contains(.close_on_exec)) raw_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
2623
2724 const fd = ws2_32.WSASocketW(
2825 @intCast(i32, domain),
29 @intCast(i32, filtered_socket_type),
26 @intCast(i32, socket_type),
3027 @intCast(i32, protocol),
3128 null,
3229 0,
33 filtered_flags,
30 raw_flags,
3431 );
3532 if (fd == ws2_32.INVALID_SOCKET) {
3633 return switch (ws2_32.WSAGetLastError()) {
3734 .WSANOTINITIALISED => {
3835 _ = try windows.WSAStartup(2, 2);
39 return Socket.init(domain, socket_type, protocol);
36 return Socket.init(domain, socket_type, protocol, flags);
4037 },
4138 .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
4239 .WSAEMFILE => error.ProcessFdQuotaExceeded,
......@@ -46,6 +43,14 @@ pub fn Mixin(comptime Socket: type) type {
4643 };
4744 }
4845
46 if (set.contains(.nonblocking)) {
47 var enabled: c_ulong = 1;
48 const rc = ws2_32.ioctlsocket(fd, ws2_32.FIONBIO, &enabled);
49 if (rc == ws2_32.SOCKET_ERROR) {
50 return windows.unexpectedWSAError(ws2_32.WSAGetLastError());
51 }
52 }
53
4954 return Socket{ .fd = fd };
5055 }
5156
......@@ -138,12 +143,12 @@ pub fn Mixin(comptime Socket: type) type {
138143
139144 /// Accept a pending incoming connection queued to the kernel backlog
140145 /// of the socket.
141 pub fn accept(self: Socket, flags: u32) !Socket.Connection {
142 var address: ws2_32.sockaddr_storage = undefined;
143 var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
146 pub fn accept(self: Socket, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket.Connection {
147 var address: Socket.Address.Native.Storage = undefined;
148 var address_len: c_int = @sizeOf(Socket.Address.Native.Storage);
144149
145 const rc = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
146 if (rc == ws2_32.INVALID_SOCKET) {
150 const fd = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
151 if (fd == ws2_32.INVALID_SOCKET) {
147152 return switch (ws2_32.WSAGetLastError()) {
148153 .WSANOTINITIALISED => unreachable,
149154 .WSAECONNRESET => error.ConnectionResetByPeer,
......@@ -158,9 +163,20 @@ pub fn Mixin(comptime Socket: type) type {
158163 };
159164 }
160165
161 const socket = Socket.from(rc);
166 const socket = Socket.from(fd);
167 errdefer socket.deinit();
168
162169 const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
163170
171 const set = std.EnumSet(Socket.InitFlags).init(flags);
172 if (set.contains(.nonblocking)) {
173 var enabled: c_ulong = 1;
174 const rc = ws2_32.ioctlsocket(fd, ws2_32.FIONBIO, &enabled);
175 if (rc == ws2_32.SOCKET_ERROR) {
176 return windows.unexpectedWSAError(ws2_32.WSAGetLastError());
177 }
178 }
179
164180 return Socket.Connection.from(socket, socket_address);
165181 }
166182
......@@ -238,7 +254,7 @@ pub fn Mixin(comptime Socket: type) type {
238254 /// Writes multiple I/O vectors with a prepended message header to the socket
239255 /// with a set of flags specified. It returns the number of bytes that are
240256 /// written to the socket.
241 pub fn writeVectorized(self: Socket, msg: ws2_32.msghdr_const, flags: u32) !usize {
257 pub fn writeMessage(self: Socket, msg: Socket.Message, flags: u32) !usize {
242258 const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSASENDMSG, self.fd, ws2_32.WSAID_WSASENDMSG);
243259
244260 var num_bytes: u32 = undefined;
......@@ -275,7 +291,7 @@ pub fn Mixin(comptime Socket: type) type {
275291 /// Read multiple I/O vectors with a prepended message header from the socket
276292 /// with a set of flags specified. It returns the number of bytes that were
277293 /// read into the buffer provided.
278 pub fn readVectorized(self: Socket, msg: *ws2_32.msghdr, flags: u32) !usize {
294 pub fn readMessage(self: Socket, msg: *Socket.Message, flags: u32) !usize {
279295 const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSARECVMSG, self.fd, ws2_32.WSAID_WSARECVMSG);
280296
281297 var num_bytes: u32 = undefined;
......@@ -311,8 +327,8 @@ pub fn Mixin(comptime Socket: type) type {
311327
312328 /// Query the address that the socket is locally bounded to.
313329 pub fn getLocalAddress(self: Socket) !Socket.Address {
314 var address: ws2_32.sockaddr_storage = undefined;
315 var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
330 var address: Socket.Address.Native.Storage = undefined;
331 var address_len: c_int = @sizeOf(Socket.Address.Native.Storage);
316332
317333 const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
318334 if (rc == ws2_32.SOCKET_ERROR) {
......@@ -331,8 +347,8 @@ pub fn Mixin(comptime Socket: type) type {
331347
332348 /// Query the address that the socket is connected to.
333349 pub fn getRemoteAddress(self: Socket) !Socket.Address {
334 var address: ws2_32.sockaddr_storage = undefined;
335 var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
350 var address: Socket.Address.Native.Storage = undefined;
351 var address_len: c_int = @sizeOf(Socket.Address.Native.Storage);
336352
337353 const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
338354 if (rc == ws2_32.SOCKET_ERROR) {
......@@ -384,11 +400,7 @@ pub fn Mixin(comptime Socket: type) type {
384400 /// if the host does not support the option for a socket to linger around up until a timeout specified in
385401 /// seconds.
386402 pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
387 const settings = ws2_32.linger{
388 .l_onoff = @as(u16, @boolToInt(timeout_seconds != null)),
389 .l_linger = if (timeout_seconds) |seconds| seconds else 0,
390 };
391
403 const settings = Socket.Linger.init(timeout_seconds);
392404 return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings));
393405 }
394406