authorgravatar for kenta@lithdew.netlithdew <kenta@lithdew.net> 2021-05-12 22:43:34+09:00
committergravatar for kenta@lithdew.netKenta Iwasaki <kenta@lithdew.net> 2021-06-01 18:22:57+09:00
log3600508fe18ecc61ea889a65127a2641a76c2933
treedcb1d00874c9abe2b465055dffba9c2cca69e896
parentd496400cff8b025dea262a9544e1b20482233089

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

Cross-platform versions of msghdr, sendmsg, recvmsg, linger, and iovec were provided based on findings from glibc, musl, and Microsoft's documentation. Implemented initial Reactor interface for epoll (linux) which wraps around I/O reactor subsystems such as epoll, kqueue, select, etc. across different platforms. The Reactor interface allows for driving async I/O in Zig applications. A test was added for the Reactor interface to drive a TCP client/listener socket pair. A greatest-common-subset of possible socket initialization flags (close socket on exec syscalls, initialize socket to be non-blocking) were implemented. A test was added for using sendmsg/recvmsg syscalls across different platforms for a TCP client/listener socket pair.

21 files changed, 548 insertions(+), 451 deletions(-)

lib/std/os.zig+1-1
...@@ -4994,7 +4994,7 @@ pub fn sendmsg(...@@ -4994,7 +4994,7 @@ pub fn sendmsg(
4994 /// The file descriptor of the sending socket.4994 /// The file descriptor of the sending socket.
4995 sockfd: socket_t,4995 sockfd: socket_t,
4996 /// Message header and iovecs4996 /// Message header and iovecs
4997 msg: msghdr_const,4997 msg: std.x.os.Socket.Message,
4998 flags: u32,4998 flags: u32,
4999) SendMsgError!usize {4999) SendMsgError!usize {
5000 while (true) {5000 while (true) {
lib/std/os/bits/dragonfly.zig-9
...@@ -768,15 +768,6 @@ pub const dl_phdr_info = extern struct {...@@ -768,15 +768,6 @@ pub const dl_phdr_info = extern struct {
768 dlpi_phdr: [*]std.elf.Phdr,768 dlpi_phdr: [*]std.elf.Phdr,
769 dlpi_phnum: u16,769 dlpi_phnum: u16,
770};770};
771pub const msghdr = extern struct {
772 msg_name: ?*c_void,
773 msg_namelen: socklen_t,
774 msg_iov: [*c]iovec,
775 msg_iovlen: c_int,
776 msg_control: ?*c_void,
777 msg_controllen: socklen_t,
778 msg_flags: c_int,
779};
780pub const cmsghdr = extern struct {771pub const cmsghdr = extern struct {
781 cmsg_len: socklen_t,772 cmsg_len: socklen_t,
782 cmsg_level: c_int,773 cmsg_level: c_int,
lib/std/os/bits/freebsd.zig-46
...@@ -82,52 +82,6 @@ pub const Flock = extern struct {...@@ -82,52 +82,6 @@ pub const Flock = extern struct {
82 __unused: [4]u8,82 __unused: [4]u8,
83};83};
8484
85pub const msghdr = extern struct {
86 /// optional address
87 msg_name: ?*sockaddr,
88
89 /// size of address
90 msg_namelen: socklen_t,
91
92 /// scatter/gather array
93 msg_iov: [*]iovec,
94
95 /// # elements in msg_iov
96 msg_iovlen: i32,
97
98 /// ancillary data
99 msg_control: ?*c_void,
100
101 /// ancillary data buffer len
102 msg_controllen: socklen_t,
103
104 /// flags on received message
105 msg_flags: i32,
106};
107
108pub const msghdr_const = extern struct {
109 /// optional address
110 msg_name: ?*const sockaddr,
111
112 /// size of address
113 msg_namelen: socklen_t,
114
115 /// scatter/gather array
116 msg_iov: [*]iovec_const,
117
118 /// # elements in msg_iov
119 msg_iovlen: i32,
120
121 /// ancillary data
122 msg_control: ?*c_void,
123
124 /// ancillary data buffer len
125 msg_controllen: socklen_t,
126
127 /// flags on received message
128 msg_flags: i32,
129};
130
131pub const libc_stat = extern struct {85pub const libc_stat = extern struct {
132 dev: dev_t,86 dev: dev_t,
133 ino: ino_t,87 ino: ino_t,
lib/std/os/bits/haiku.zig-46
...@@ -73,52 +73,6 @@ pub const Flock = extern struct {...@@ -73,52 +73,6 @@ pub const Flock = extern struct {
73 __unused: [4]u8,73 __unused: [4]u8,
74};74};
7575
76pub const msghdr = extern struct {
77 /// optional address
78 msg_name: ?*sockaddr,
79
80 /// size of address
81 msg_namelen: socklen_t,
82
83 /// scatter/gather array
84 msg_iov: [*]iovec,
85
86 /// # elements in msg_iov
87 msg_iovlen: i32,
88
89 /// ancillary data
90 msg_control: ?*c_void,
91
92 /// ancillary data buffer len
93 msg_controllen: socklen_t,
94
95 /// flags on received message
96 msg_flags: i32,
97};
98
99pub const msghdr_const = extern struct {
100 /// optional address
101 msg_name: ?*const sockaddr,
102
103 /// size of address
104 msg_namelen: socklen_t,
105
106 /// scatter/gather array
107 msg_iov: [*]iovec_const,
108
109 /// # elements in msg_iov
110 msg_iovlen: i32,
111
112 /// ancillary data
113 msg_control: ?*c_void,
114
115 /// ancillary data buffer len
116 msg_controllen: socklen_t,
117
118 /// flags on received message
119 msg_flags: i32,
120};
121
122pub const off_t = i64;76pub const off_t = i64;
123pub const ino_t = u64;77pub const ino_t = u64;
12478
lib/std/os/bits/linux.zig+2-2
...@@ -1180,12 +1180,12 @@ pub const sockaddr_un = extern struct {...@@ -1180,12 +1180,12 @@ pub const sockaddr_un = extern struct {
1180};1180};
11811181
1182pub const mmsghdr = extern struct {1182pub const mmsghdr = extern struct {
1183 msg_hdr: msghdr,1183 msg_hdr: std.x.os.Socket.Message,
1184 msg_len: u32,1184 msg_len: u32,
1185};1185};
11861186
1187pub const mmsghdr_const = extern struct {1187pub const mmsghdr_const = extern struct {
1188 msg_hdr: msghdr_const,1188 msg_hdr: std.x.os.Socket.Message,
1189 msg_len: u32,1189 msg_len: u32,
1190};1190};
11911191
lib/std/os/bits/linux/arm-eabi.zig-20
...@@ -526,26 +526,6 @@ pub const Flock = extern struct {...@@ -526,26 +526,6 @@ pub const Flock = extern struct {
526 __unused: [4]u8,526 __unused: [4]u8,
527};527};
528528
529pub const msghdr = extern struct {
530 msg_name: ?*sockaddr,
531 msg_namelen: socklen_t,
532 msg_iov: [*]iovec,
533 msg_iovlen: i32,
534 msg_control: ?*c_void,
535 msg_controllen: socklen_t,
536 msg_flags: i32,
537};
538
539pub const msghdr_const = extern struct {
540 msg_name: ?*const sockaddr,
541 msg_namelen: socklen_t,
542 msg_iov: [*]iovec_const,
543 msg_iovlen: i32,
544 msg_control: ?*c_void,
545 msg_controllen: socklen_t,
546 msg_flags: i32,
547};
548
549pub const blksize_t = i32;529pub const blksize_t = i32;
550pub const nlink_t = u32;530pub const nlink_t = u32;
551pub const time_t = isize;531pub const time_t = isize;
lib/std/os/bits/linux/arm64.zig-24
...@@ -395,30 +395,6 @@ pub const Flock = extern struct {...@@ -395,30 +395,6 @@ pub const Flock = extern struct {
395 __unused: [4]u8,395 __unused: [4]u8,
396};396};
397397
398pub const msghdr = extern struct {
399 msg_name: ?*sockaddr,
400 msg_namelen: socklen_t,
401 msg_iov: [*]iovec,
402 msg_iovlen: i32,
403 __pad1: i32 = 0,
404 msg_control: ?*c_void,
405 msg_controllen: socklen_t,
406 __pad2: socklen_t = 0,
407 msg_flags: i32,
408};
409
410pub const msghdr_const = extern struct {
411 msg_name: ?*const sockaddr,
412 msg_namelen: socklen_t,
413 msg_iov: [*]iovec_const,
414 msg_iovlen: i32,
415 __pad1: i32 = 0,
416 msg_control: ?*c_void,
417 msg_controllen: socklen_t,
418 __pad2: socklen_t = 0,
419 msg_flags: i32,
420};
421
422pub const blksize_t = i32;398pub const blksize_t = i32;
423pub const nlink_t = u32;399pub const nlink_t = u32;
424pub const time_t = isize;400pub const time_t = isize;
lib/std/os/bits/linux/i386.zig-20
...@@ -523,26 +523,6 @@ pub const Flock = extern struct {...@@ -523,26 +523,6 @@ pub const Flock = extern struct {
523 l_pid: pid_t,523 l_pid: pid_t,
524};524};
525525
526pub const msghdr = extern struct {
527 msg_name: ?*sockaddr,
528 msg_namelen: socklen_t,
529 msg_iov: [*]iovec,
530 msg_iovlen: i32,
531 msg_control: ?*c_void,
532 msg_controllen: socklen_t,
533 msg_flags: i32,
534};
535
536pub const msghdr_const = extern struct {
537 msg_name: ?*const sockaddr,
538 msg_namelen: socklen_t,
539 msg_iov: [*]iovec_const,
540 msg_iovlen: i32,
541 msg_control: ?*c_void,
542 msg_controllen: socklen_t,
543 msg_flags: i32,
544};
545
546pub const blksize_t = i32;526pub const blksize_t = i32;
547pub const nlink_t = u32;527pub const nlink_t = u32;
548pub const time_t = isize;528pub const time_t = isize;
lib/std/os/bits/linux/powerpc.zig-20
...@@ -515,26 +515,6 @@ pub const Flock = extern struct {...@@ -515,26 +515,6 @@ pub const Flock = extern struct {
515 l_pid: pid_t,515 l_pid: pid_t,
516};516};
517517
518pub const msghdr = extern struct {
519 msg_name: ?*sockaddr,
520 msg_namelen: socklen_t,
521 msg_iov: [*]iovec,
522 msg_iovlen: usize,
523 msg_control: ?*c_void,
524 msg_controllen: socklen_t,
525 msg_flags: i32,
526};
527
528pub const msghdr_const = extern struct {
529 msg_name: ?*const sockaddr,
530 msg_namelen: socklen_t,
531 msg_iov: [*]iovec_const,
532 msg_iovlen: usize,
533 msg_control: ?*c_void,
534 msg_controllen: socklen_t,
535 msg_flags: i32,
536};
537
538pub const blksize_t = i32;518pub const blksize_t = i32;
539pub const nlink_t = u32;519pub const nlink_t = u32;
540pub const time_t = isize;520pub const time_t = isize;
lib/std/os/bits/linux/powerpc64.zig-20
...@@ -491,26 +491,6 @@ pub const Flock = extern struct {...@@ -491,26 +491,6 @@ pub const Flock = extern struct {
491 __unused: [4]u8,491 __unused: [4]u8,
492};492};
493493
494pub const msghdr = extern struct {
495 msg_name: ?*sockaddr,
496 msg_namelen: socklen_t,
497 msg_iov: [*]iovec,
498 msg_iovlen: usize,
499 msg_control: ?*c_void,
500 msg_controllen: usize,
501 msg_flags: i32,
502};
503
504pub const msghdr_const = extern struct {
505 msg_name: ?*const sockaddr,
506 msg_namelen: socklen_t,
507 msg_iov: [*]iovec_const,
508 msg_iovlen: usize,
509 msg_control: ?*c_void,
510 msg_controllen: usize,
511 msg_flags: i32,
512};
513
514pub const blksize_t = i64;494pub const blksize_t = i64;
515pub const nlink_t = u64;495pub const nlink_t = u64;
516pub const time_t = i64;496pub const time_t = i64;
lib/std/os/bits/linux/sparc64.zig-20
...@@ -465,26 +465,6 @@ pub const Flock = extern struct {...@@ -465,26 +465,6 @@ pub const Flock = extern struct {
465 l_pid: pid_t,465 l_pid: pid_t,
466};466};
467467
468pub const msghdr = extern struct {
469 msg_name: ?*sockaddr,
470 msg_namelen: socklen_t,
471 msg_iov: [*]iovec,
472 msg_iovlen: u64,
473 msg_control: ?*c_void,
474 msg_controllen: u64,
475 msg_flags: i32,
476};
477
478pub const msghdr_const = extern struct {
479 msg_name: ?*const sockaddr,
480 msg_namelen: socklen_t,
481 msg_iov: [*]iovec_const,
482 msg_iovlen: u64,
483 msg_control: ?*c_void,
484 msg_controllen: u64,
485 msg_flags: i32,
486};
487
488pub const off_t = i64;468pub const off_t = i64;
489pub const ino_t = u64;469pub const ino_t = u64;
490pub const mode_t = u32;470pub const mode_t = u32;
lib/std/os/bits/linux/x86_64.zig-24
...@@ -489,30 +489,6 @@ pub const Flock = extern struct {...@@ -489,30 +489,6 @@ pub const Flock = extern struct {
489 l_pid: pid_t,489 l_pid: pid_t,
490};490};
491491
492pub const msghdr = extern struct {
493 msg_name: ?*sockaddr,
494 msg_namelen: socklen_t,
495 msg_iov: [*]iovec,
496 msg_iovlen: i32,
497 __pad1: i32 = 0,
498 msg_control: ?*c_void,
499 msg_controllen: socklen_t,
500 __pad2: socklen_t = 0,
501 msg_flags: i32,
502};
503
504pub const msghdr_const = extern struct {
505 msg_name: ?*const sockaddr,
506 msg_namelen: socklen_t,
507 msg_iov: [*]iovec_const,
508 msg_iovlen: i32,
509 __pad1: i32 = 0,
510 msg_control: ?*c_void,
511 msg_controllen: socklen_t,
512 __pad2: socklen_t = 0,
513 msg_flags: i32,
514};
515
516pub const off_t = i64;492pub const off_t = i64;
517pub const ino_t = u64;493pub const ino_t = u64;
518pub const dev_t = u64;494pub const dev_t = u64;
lib/std/os/bits/netbsd.zig-46
...@@ -108,52 +108,6 @@ pub const EAI = enum(c_int) {...@@ -108,52 +108,6 @@ pub const EAI = enum(c_int) {
108108
109pub const EAI_MAX = 15;109pub const EAI_MAX = 15;
110110
111pub const msghdr = extern struct {
112 /// optional address
113 msg_name: ?*sockaddr,
114
115 /// size of address
116 msg_namelen: socklen_t,
117
118 /// scatter/gather array
119 msg_iov: [*]iovec,
120
121 /// # elements in msg_iov
122 msg_iovlen: i32,
123
124 /// ancillary data
125 msg_control: ?*c_void,
126
127 /// ancillary data buffer len
128 msg_controllen: socklen_t,
129
130 /// flags on received message
131 msg_flags: i32,
132};
133
134pub const msghdr_const = extern struct {
135 /// optional address
136 msg_name: ?*const sockaddr,
137
138 /// size of address
139 msg_namelen: socklen_t,
140
141 /// scatter/gather array
142 msg_iov: [*]iovec_const,
143
144 /// # elements in msg_iov
145 msg_iovlen: i32,
146
147 /// ancillary data
148 msg_control: ?*c_void,
149
150 /// ancillary data buffer len
151 msg_controllen: socklen_t,
152
153 /// flags on received message
154 msg_flags: i32,
155};
156
157pub const libc_stat = extern struct {111pub const libc_stat = extern struct {
158 dev: dev_t,112 dev: dev_t,
159 mode: mode_t,113 mode: mode_t,
lib/std/os/bits/openbsd.zig-46
...@@ -124,52 +124,6 @@ pub const EAI = enum(c_int) {...@@ -124,52 +124,6 @@ pub const EAI = enum(c_int) {
124124
125pub const EAI_MAX = 15;125pub const EAI_MAX = 15;
126126
127pub const msghdr = extern struct {
128 /// optional address
129 msg_name: ?*sockaddr,
130
131 /// size of address
132 msg_namelen: socklen_t,
133
134 /// scatter/gather array
135 msg_iov: [*]iovec,
136
137 /// # elements in msg_iov
138 msg_iovlen: c_uint,
139
140 /// ancillary data
141 msg_control: ?*c_void,
142
143 /// ancillary data buffer len
144 msg_controllen: socklen_t,
145
146 /// flags on received message
147 msg_flags: c_int,
148};
149
150pub const msghdr_const = extern struct {
151 /// optional address
152 msg_name: ?*const sockaddr,
153
154 /// size of address
155 msg_namelen: socklen_t,
156
157 /// scatter/gather array
158 msg_iov: [*]iovec_const,
159
160 /// # elements in msg_iov
161 msg_iovlen: c_uint,
162
163 /// ancillary data
164 msg_control: ?*c_void,
165
166 /// ancillary data buffer len
167 msg_controllen: socklen_t,
168
169 /// flags on received message
170 msg_flags: c_int,
171};
172
173pub const libc_stat = extern struct {127pub const libc_stat = extern struct {
174 mode: mode_t,128 mode: mode_t,
175 dev: dev_t,129 dev: dev_t,
lib/std/os/windows/ws2_32.zig+5-25
...@@ -3,6 +3,7 @@...@@ -3,6 +3,7 @@
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
6const std = @import("../../std.zig");
6usingnamespace @import("bits.zig");7usingnamespace @import("bits.zig");
78
8pub const SOCKET = *opaque {};9pub const SOCKET = *opaque {};
...@@ -1093,27 +1094,6 @@ pub const WSABUF = extern struct {...@@ -1093,27 +1094,6 @@ pub const WSABUF = extern struct {
1093 buf: [*]u8,1094 buf: [*]u8,
1094};1095};
10951096
1096pub const msghdr = WSAMSG;
1097pub const msghdr_const = WSAMSG_const;
1098
1099pub const WSAMSG_const = extern struct {
1100 name: *const sockaddr,
1101 namelen: INT,
1102 lpBuffers: [*]WSABUF,
1103 dwBufferCount: DWORD,
1104 Control: WSABUF,
1105 dwFlags: DWORD,
1106};
1107
1108pub const WSAMSG = extern struct {
1109 name: *sockaddr,
1110 namelen: INT,
1111 lpBuffers: [*]WSABUF,
1112 dwBufferCount: DWORD,
1113 Control: WSABUF,
1114 dwFlags: DWORD,
1115};
1116
1117pub const WSAPOLLFD = pollfd;1097pub const WSAPOLLFD = pollfd;
11181098
1119pub const pollfd = extern struct {1099pub const pollfd = extern struct {
...@@ -1163,7 +1143,7 @@ pub const LPFN_GETACCEPTEXSOCKADDRS = fn (...@@ -1163,7 +1143,7 @@ pub const LPFN_GETACCEPTEXSOCKADDRS = fn (
11631143
1164pub const LPFN_WSASENDMSG = fn (1144pub const LPFN_WSASENDMSG = fn (
1165 s: SOCKET,1145 s: SOCKET,
1166 lpMsg: *const WSAMSG_const,1146 lpMsg: *const std.x.os.Socket.Message,
1167 dwFlags: u32,1147 dwFlags: u32,
1168 lpNumberOfBytesSent: ?*u32,1148 lpNumberOfBytesSent: ?*u32,
1169 lpOverlapped: ?*OVERLAPPED,1149 lpOverlapped: ?*OVERLAPPED,
...@@ -1172,7 +1152,7 @@ pub const LPFN_WSASENDMSG = fn (...@@ -1172,7 +1152,7 @@ pub const LPFN_WSASENDMSG = fn (
11721152
1173pub const LPFN_WSARECVMSG = fn (1153pub const LPFN_WSARECVMSG = fn (
1174 s: SOCKET,1154 s: SOCKET,
1175 lpMsg: *WSAMSG,1155 lpMsg: *std.x.os.Socket.Message,
1176 lpdwNumberOfBytesRecv: ?*u32,1156 lpdwNumberOfBytesRecv: ?*u32,
1177 lpOverlapped: ?*OVERLAPPED,1157 lpOverlapped: ?*OVERLAPPED,
1178 lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,1158 lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
...@@ -2046,7 +2026,7 @@ pub extern "ws2_32" fn WSASend(...@@ -2046,7 +2026,7 @@ pub extern "ws2_32" fn WSASend(
20462026
2047pub extern "ws2_32" fn WSASendMsg(2027pub extern "ws2_32" fn WSASendMsg(
2048 s: SOCKET,2028 s: SOCKET,
2049 lpMsg: *const WSAMSG_const,2029 lpMsg: *const std.x.os.Socket.Message,
2050 dwFlags: u32,2030 dwFlags: u32,
2051 lpNumberOfBytesSent: ?*u32,2031 lpNumberOfBytesSent: ?*u32,
2052 lpOverlapped: ?*OVERLAPPED,2032 lpOverlapped: ?*OVERLAPPED,
...@@ -2055,7 +2035,7 @@ pub extern "ws2_32" fn WSASendMsg(...@@ -2055,7 +2035,7 @@ pub extern "ws2_32" fn WSASendMsg(
20552035
2056pub extern "ws2_32" fn WSARecvMsg(2036pub extern "ws2_32" fn WSARecvMsg(
2057 s: SOCKET,2037 s: SOCKET,
2058 lpMsg: *WSAMSG,2038 lpMsg: *std.x.os.Socket.Message,
2059 lpdwNumberOfBytesRecv: ?*u32,2039 lpdwNumberOfBytesRecv: ?*u32,
2060 lpOverlapped: ?*OVERLAPPED,2040 lpOverlapped: ?*OVERLAPPED,
2061 lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,2041 lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
lib/std/x.zig+1
...@@ -8,6 +8,7 @@ const std = @import("std.zig");...@@ -8,6 +8,7 @@ const std = @import("std.zig");
88
9pub const os = struct {9pub const os = struct {
10 pub const Socket = @import("x/os/socket.zig").Socket;10 pub const Socket = @import("x/os/socket.zig").Socket;
11 pub usingnamespace @import("x/os/io.zig");
11 pub usingnamespace @import("x/os/net.zig");12 pub usingnamespace @import("x/os/net.zig");
12};13};
1314
lib/std/x/net/tcp.zig+58-16
...@@ -18,6 +18,7 @@ const testing = std.testing;...@@ -18,6 +18,7 @@ const testing = std.testing;
18const IPv4 = std.x.os.IPv4;18const IPv4 = std.x.os.IPv4;
19const IPv6 = std.x.os.IPv6;19const IPv6 = std.x.os.IPv6;
20const Socket = std.x.os.Socket;20const Socket = std.x.os.Socket;
21const Buffer = std.x.os.Buffer;
2122
22/// A generic TCP socket abstraction.23/// A generic TCP socket abstraction.
23const tcp = @This();24const tcp = @This();
...@@ -82,12 +83,13 @@ pub const Client = struct {...@@ -82,12 +83,13 @@ pub const Client = struct {
82 };83 };
8384
84 /// Opens a new client.85 /// 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 {
86 return Client{87 return Client{
87 .socket = try Socket.init(88 .socket = try Socket.init(
88 @enumToInt(domain),89 @enumToInt(domain),
89 os.SOCK_STREAM | flags,90 os.SOCK_STREAM,
90 os.IPPROTO_TCP,91 os.IPPROTO_TCP,
92 flags,
91 ),93 ),
92 };94 };
93 }95 }
...@@ -143,14 +145,14 @@ pub const Client = struct {...@@ -143,14 +145,14 @@ pub const Client = struct {
143 /// Writes multiple I/O vectors with a prepended message header to the socket145 /// Writes multiple I/O vectors with a prepended message header to the socket
144 /// with a set of flags specified. It returns the number of bytes that are146 /// with a set of flags specified. It returns the number of bytes that are
145 /// written to the socket.147 /// written to the socket.
146 pub fn writeVectorized(self: Client, msg: os.msghdr_const, flags: u32) !usize {148 pub fn writeVectorized(self: Client, msg: Socket.Message, flags: u32) !usize {
147 return self.socket.writeVectorized(msg, flags);149 return self.socket.writeVectorized(msg, flags);
148 }150 }
149151
150 /// Read multiple I/O vectors with a prepended message header from the socket152 /// Read multiple I/O vectors with a prepended message header from the socket
151 /// with a set of flags specified. It returns the number of bytes that were153 /// with a set of flags specified. It returns the number of bytes that were
152 /// read into the buffer provided.154 /// read into the buffer provided.
153 pub fn readVectorized(self: Client, msg: *os.msghdr, flags: u32) !usize {155 pub fn readVectorized(self: Client, msg: *Socket.Message, flags: u32) !usize {
154 return self.socket.readVectorized(msg, flags);156 return self.socket.readVectorized(msg, flags);
155 }157 }
156158
...@@ -244,12 +246,13 @@ pub const Listener = struct {...@@ -244,12 +246,13 @@ pub const Listener = struct {
244 socket: Socket,246 socket: Socket,
245247
246 /// Opens a new listener.248 /// 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 {
248 return Listener{250 return Listener{
249 .socket = try Socket.init(251 .socket = try Socket.init(
250 @enumToInt(domain),252 @enumToInt(domain),
251 os.SOCK_STREAM | flags,253 os.SOCK_STREAM,
252 os.IPPROTO_TCP,254 os.IPPROTO_TCP,
255 flags,
253 ),256 ),
254 };257 };
255 }258 }
...@@ -278,7 +281,7 @@ pub const Listener = struct {...@@ -278,7 +281,7 @@ pub const Listener = struct {
278281
279 /// Accept a pending incoming connection queued to the kernel backlog282 /// Accept a pending incoming connection queued to the kernel backlog
280 /// of the listener's socket.283 /// 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 {
282 return tcp.Connection.from(try self.socket.accept(flags));285 return tcp.Connection.from(try self.socket.accept(flags));
283 }286 }
284287
...@@ -324,7 +327,7 @@ pub const Listener = struct {...@@ -324,7 +327,7 @@ pub const Listener = struct {
324test "tcp: create client/listener pair" {327test "tcp: create client/listener pair" {
325 if (builtin.os.tag == .wasi) return error.SkipZigTest;328 if (builtin.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 });
328 defer listener.deinit();331 defer listener.deinit();
329332
330 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));333 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
...@@ -336,19 +339,19 @@ test "tcp: create client/listener pair" {...@@ -336,19 +339,19 @@ test "tcp: create client/listener pair" {
336 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,339 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
337 }340 }
338341
339 const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC);342 const client = try tcp.Client.init(.ip, .{ .close_on_exec = true });
340 defer client.deinit();343 defer client.deinit();
341344
342 try client.connect(binded_address);345 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 });
345 defer conn.deinit();348 defer conn.deinit();
346}349}
347350
348test "tcp/client: set read timeout of 1 millisecond on blocking client" {351test "tcp/client: 1ms read timeout" {
349 if (builtin.os.tag == .wasi) return error.SkipZigTest;352 if (builtin.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 });
352 defer listener.deinit();355 defer listener.deinit();
353356
354 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));357 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" {...@@ -360,23 +363,62 @@ test "tcp/client: set read timeout of 1 millisecond on blocking client" {
360 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,363 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
361 }364 }
362365
363 const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC);366 const client = try tcp.Client.init(.ip, .{ .close_on_exec = true });
364 defer client.deinit();367 defer client.deinit();
365368
366 try client.connect(binded_address);369 try client.connect(binded_address);
367 try client.setReadTimeout(1);370 try client.setReadTimeout(1);
368371
369 const conn = try listener.accept(os.SOCK_CLOEXEC);372 const conn = try listener.accept(.{ .close_on_exec = true });
370 defer conn.deinit();373 defer conn.deinit();
371374
372 var buf: [1]u8 = undefined;375 var buf: [1]u8 = undefined;
373 try testing.expectError(error.WouldBlock, client.reader(0).read(&buf));376 try testing.expectError(error.WouldBlock, client.reader(0).read(&buf));
374}377}
375378
379test "tcp/client: read and write multiple vectors" {
380 if (builtin.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.writeVectorized(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]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.readVectorized(&msg, 0);
414
415 try testing.expectEqualStrings(message, &buf);
416}
417
376test "tcp/listener: bind to unspecified ipv4 address" {418test "tcp/listener: bind to unspecified ipv4 address" {
377 if (builtin.os.tag == .wasi) return error.SkipZigTest;419 if (builtin.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 });
380 defer listener.deinit();422 defer listener.deinit();
381423
382 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));424 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
...@@ -389,7 +431,7 @@ test "tcp/listener: bind to unspecified ipv4 address" {...@@ -389,7 +431,7 @@ test "tcp/listener: bind to unspecified ipv4 address" {
389test "tcp/listener: bind to unspecified ipv6 address" {431test "tcp/listener: bind to unspecified ipv6 address" {
390 if (builtin.os.tag == .wasi) return error.SkipZigTest;432 if (builtin.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 });
393 defer listener.deinit();435 defer listener.deinit();
394436
395 try listener.bind(ip.Address.initIPv6(IPv6.unspecified, 0));437 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 builtin = std.builtin;
6const testing = std.testing;
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 (builtin.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 (builtin.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/socket.zig+199
...@@ -11,8 +11,13 @@ const os = std.os;...@@ -11,8 +11,13 @@ const os = std.os;
11const fmt = std.fmt;11const fmt = std.fmt;
12const mem = std.mem;12const mem = std.mem;
13const time = std.time;13const time = std.time;
14const meta = std.meta;
14const builtin = std.builtin;15const builtin = std.builtin;
1516
17const Buffer = std.x.os.Buffer;
18
19const assert = std.debug.assert;
20
16/// A generic, cross-platform socket abstraction.21/// A generic, cross-platform socket abstraction.
17pub const Socket = struct {22pub const Socket = struct {
18 /// A socket-address pair.23 /// A socket-address pair.
...@@ -29,6 +34,32 @@ pub const Socket = struct {...@@ -29,6 +34,32 @@ pub const Socket = struct {
29 /// A generic socket address abstraction. It is safe to directly access and modify34 /// A generic socket address abstraction. It is safe to directly access and modify
30 /// the fields of a `Socket.Address`.35 /// the fields of a `Socket.Address`.
31 pub const Address = union(enum) {36 pub const Address = union(enum) {
37 pub const Native = struct {
38 pub const requires_prepended_length = builtin.os.getVersionRange() == .semver;
39 pub const Length = if (requires_prepended_length) u8 else [0]u8;
40
41 pub const Family = if (requires_prepended_length) u8 else c_ushort;
42
43 /// POSIX `sockaddr_storage`. The expected size and alignment is specified in IETF RFC 2553.
44 pub const Storage = extern struct {
45 pub const expected_size = 128;
46 pub const expected_alignment = 8;
47
48 pub const padding_size = expected_size -
49 mem.alignForward(@sizeOf(Address.Native.Length), expected_alignment) -
50 mem.alignForward(@sizeOf(Address.Native.Family), expected_alignment);
51
52 len: Address.Native.Length align(expected_alignment) = undefined,
53 family: Address.Native.Family align(expected_alignment) = undefined,
54 padding: [padding_size]u8 align(expected_alignment) = undefined,
55
56 comptime {
57 assert(@sizeOf(Storage) == Storage.expected_size);
58 assert(@alignOf(Storage) == Storage.expected_alignment);
59 }
60 };
61 };
62
32 ipv4: net.IPv4.Address,63 ipv4: net.IPv4.Address,
33 ipv6: net.IPv6.Address,64 ipv6: net.IPv6.Address,
3465
...@@ -107,6 +138,174 @@ pub const Socket = struct {...@@ -107,6 +138,174 @@ pub const Socket = struct {
107 }138 }
108 };139 };
109140
141 /// POSIX `msghdr`. Denotes a destination address, set of buffers, control data, and flags. Ported
142 /// directly from musl.
143 pub const Message = if (builtin.os.isAtLeast(.windows, .vista) != null and builtin.os.isAtLeast(.windows, .vista).?)
144 extern struct {
145 name: usize = @ptrToInt(@as(?[*]u8, null)),
146 name_len: c_int = 0,
147
148 buffers: usize = undefined,
149 buffers_len: c_ulong = undefined,
150
151 control: Buffer = .{
152 .ptr = @ptrToInt(@as(?[*]u8, null)),
153 .len = 0,
154 },
155 flags: c_ulong = 0,
156
157 pub usingnamespace MessageMixin(Message);
158 }
159 else if (builtin.os.tag == .windows)
160 extern struct {
161 name: usize = @ptrToInt(@as(?[*]u8, null)),
162 name_len: c_int = 0,
163
164 buffers: usize = undefined,
165 buffers_len: u32 = undefined,
166
167 control: Buffer = .{
168 .ptr = @ptrToInt(@as(?[*]u8, null)),
169 .len = 0,
170 },
171 flags: u32 = 0,
172
173 pub usingnamespace MessageMixin(Message);
174 }
175 else if (@sizeOf(usize) > 4 and builtin.endian == .Big)
176 extern struct {
177 name: usize = @ptrToInt(@as(?[*]u8, null)),
178 name_len: c_uint = 0,
179
180 buffers: usize = undefined,
181 _pad_1: c_int = 0,
182 buffers_len: c_int = undefined,
183
184 control: usize = @ptrToInt(@as(?[*]u8, null)),
185 _pad_2: c_int = 0,
186 control_len: c_uint = 0,
187
188 flags: c_int = 0,
189
190 pub usingnamespace MessageMixin(Message);
191 }
192 else if (@sizeOf(usize) > 4 and builtin.endian == .Little)
193 extern struct {
194 name: usize = @ptrToInt(@as(?[*]u8, null)),
195 name_len: c_uint = 0,
196
197 buffers: usize = undefined,
198 buffers_len: c_int = undefined,
199 _pad_1: c_int = 0,
200
201 control: usize = @ptrToInt(@as(?[*]u8, null)),
202 control_len: c_uint = 0,
203 _pad_2: c_int = 0,
204
205 flags: c_int = 0,
206
207 pub usingnamespace MessageMixin(Message);
208 }
209 else
210 extern struct {
211 name: usize = @ptrToInt(@as(?[*]u8, null)),
212 name_len: c_uint = 0,
213
214 buffers: usize = undefined,
215 buffers_len: c_int,
216
217 control: usize = null,
218 control_len: c_uint = 0,
219
220 flags: c_int = 0,
221
222 pub usingnamespace MessageMixin(Message);
223 };
224
225 fn MessageMixin(comptime Self: type) type {
226 return struct {
227 pub fn fromBuffers(buffers: []const Buffer) Self {
228 var self: Self = .{};
229 self.setBuffers(buffers);
230 return self;
231 }
232
233 pub fn setName(self: *Self, name: []const u8) void {
234 self.name = @ptrToInt(name.ptr);
235 self.name_len = @intCast(meta.fieldInfo(Self, .name_len).field_type, name.len);
236 }
237
238 pub fn setBuffers(self: *Self, buffers: []const Buffer) void {
239 self.buffers = @ptrToInt(buffers.ptr);
240 self.buffers_len = @intCast(meta.fieldInfo(Self, .buffers_len).field_type, buffers.len);
241 }
242
243 pub fn setControl(self: *Self, control: []const u8) void {
244 if (builtin.os.tag == .windows) {
245 self.control = Buffer.from(control);
246 } else {
247 self.control = @ptrToInt(control.ptr);
248 self.control_len = @intCast(meta.fieldInfo(Self, .control_len).field_type, control.len);
249 }
250 }
251
252 pub fn setFlags(self: *Self, flags: u32) void {
253 self.flags = @intCast(meta.fieldInfo(Self, .flags).field_type, flags);
254 }
255
256 pub fn getName(self: Self) []const u8 {
257 return @intToPtr([*]const u8, self.name)[0..@intCast(usize, self.name_len)];
258 }
259
260 pub fn getBuffers(self: Self) []const Buffer {
261 return @intToPtr([*]const Buffer, self.buffers)[0..@intCast(usize, self.buffers_len)];
262 }
263
264 pub fn getControl(self: Self) []const u8 {
265 if (builtin.os.tag == .windows) {
266 return self.control.into();
267 } else {
268 return @intToPtr([*]const u8, self.control)[0..@intCast(usize, self.control_len)];
269 }
270 }
271
272 pub fn getFlags(self: Self) u32 {
273 return @intCast(u32, self.flags);
274 }
275 };
276 }
277
278 /// POSIX `linger`, denoting the linger settings of a socket.
279 ///
280 /// Microsoft's documentation and glibc denote the fields to be unsigned
281 /// short's on Windows, whereas glibc and musl denote the fields to be
282 /// int's on every other platform.
283 pub const Linger = extern struct {
284 pub const Field = switch (builtin.os.tag) {
285 .windows => c_ushort,
286 else => c_int,
287 };
288
289 enabled: Field,
290 timeout_seconds: Field,
291
292 pub fn init(timeout_seconds: ?u16) Socket.Linger {
293 return .{
294 .enabled = @intCast(Socket.Linger.Field, @boolToInt(timeout_seconds != null)),
295 .timeout_seconds = if (timeout_seconds) |seconds| @intCast(Socket.Linger.Field, seconds) else 0,
296 };
297 }
298 };
299
300 /// Possible set of flags to initialize a socket with.
301 pub const InitFlags = enum {
302 // Initialize a socket to be non-blocking.
303 nonblocking,
304
305 // Have a socket close itself on exec syscalls.
306 close_on_exec,
307 };
308
110 /// The underlying handle of a socket.309 /// The underlying handle of a socket.
111 fd: os.socket_t,310 fd: os.socket_t,
112311
lib/std/x/os/socket_posix.zig+38-39
...@@ -13,8 +13,12 @@ const time = std.time;...@@ -13,8 +13,12 @@ const time = std.time;
13pub fn Mixin(comptime Socket: type) type {13pub fn Mixin(comptime Socket: type) type {
14 return struct {14 return struct {
15 /// Open a new socket.15 /// Open a new socket.
16 pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {16 pub fn init(domain: u32, socket_type: u32, protocol: u32, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket {
17 return Socket{ .fd = try os.socket(domain, socket_type, protocol) };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) };
18 }22 }
1923
20 /// Closes the socket.24 /// Closes the socket.
...@@ -44,11 +48,16 @@ pub fn Mixin(comptime Socket: type) type {...@@ -44,11 +48,16 @@ pub fn Mixin(comptime Socket: type) type {
4448
45 /// Accept a pending incoming connection queued to the kernel backlog49 /// Accept a pending incoming connection queued to the kernel backlog
46 /// of the socket.50 /// of the socket.
47 pub fn accept(self: Socket, flags: u32) !Socket.Connection {51 pub fn accept(self: Socket, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket.Connection {
48 var address: os.sockaddr_storage = undefined;52 var address: Socket.Address.Native.Storage = undefined;
49 var address_len: u32 = @sizeOf(os.sockaddr_storage);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) };
52 const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));61 const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
5362
54 return Socket.Connection.from(socket, socket_address);63 return Socket.Connection.from(socket, socket_address);
...@@ -69,48 +78,45 @@ pub fn Mixin(comptime Socket: type) type {...@@ -69,48 +78,45 @@ pub fn Mixin(comptime Socket: type) type {
69 /// Writes multiple I/O vectors with a prepended message header to the socket78 /// Writes multiple I/O vectors with a prepended message header to the socket
70 /// with a set of flags specified. It returns the number of bytes that are79 /// with a set of flags specified. It returns the number of bytes that are
71 /// written to the socket.80 /// written to the socket.
72 pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize {81 pub fn writeVectorized(self: Socket, msg: Socket.Message, flags: u32) !usize {
73 return os.sendmsg(self.fd, msg, flags);82 return os.sendmsg(self.fd, msg, flags);
74 }83 }
7584
76 /// Read multiple I/O vectors with a prepended message header from the socket85 /// Read multiple I/O vectors with a prepended message header from the socket
77 /// with a set of flags specified. It returns the number of bytes that were86 /// with a set of flags specified. It returns the number of bytes that were
78 /// read into the buffer provided.87 /// read into the buffer provided.
79 pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize {88 pub fn readVectorized(self: Socket, msg: *Socket.Message, flags: u32) !usize {
80 if (comptime @hasDecl(os.system, "recvmsg")) {89 while (true) {
81 while (true) {90 const rc = os.system.recvmsg(self.fd, msg, flags);
82 const rc = os.system.recvmsg(self.fd, msg, flags);91 return switch (os.errno(rc)) {
83 return switch (os.errno(rc)) {92 0 => @intCast(usize, rc),
84 0 => @intCast(usize, rc),93 os.EBADF => unreachable, // always a race condition
85 os.EBADF => unreachable, // always a race condition94 os.EFAULT => unreachable,
86 os.EFAULT => unreachable,95 os.EINVAL => unreachable,
87 os.EINVAL => unreachable,96 os.ENOTCONN => unreachable,
88 os.ENOTCONN => unreachable,97 os.ENOTSOCK => unreachable,
89 os.ENOTSOCK => unreachable,98 os.EINTR => continue,
90 os.EINTR => continue,99 os.EAGAIN => error.WouldBlock,
91 os.EAGAIN => error.WouldBlock,100 os.ENOMEM => error.SystemResources,
92 os.ENOMEM => error.SystemResources,101 os.ECONNREFUSED => error.ConnectionRefused,
93 os.ECONNREFUSED => error.ConnectionRefused,102 os.ECONNRESET => error.ConnectionResetByPeer,
94 os.ECONNRESET => error.ConnectionResetByPeer,103 else => |err| os.unexpectedErrno(err),
95 else => |err| os.unexpectedErrno(err),104 };
96 };
97 }
98 }105 }
99 return error.NotSupported;
100 }106 }
101107
102 /// Query the address that the socket is locally bounded to.108 /// Query the address that the socket is locally bounded to.
103 pub fn getLocalAddress(self: Socket) !Socket.Address {109 pub fn getLocalAddress(self: Socket) !Socket.Address {
104 var address: os.sockaddr_storage = undefined;110 var address: Socket.Address.Native.Storage = undefined;
105 var address_len: u32 = @sizeOf(os.sockaddr_storage);111 var address_len: u32 = @sizeOf(Socket.Address.Native.Storage);
106 try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);112 try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
107 return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));113 return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
108 }114 }
109115
110 /// Query the address that the socket is connected to.116 /// Query the address that the socket is connected to.
111 pub fn getRemoteAddress(self: Socket) !Socket.Address {117 pub fn getRemoteAddress(self: Socket) !Socket.Address {
112 var address: os.sockaddr_storage = undefined;118 var address: Socket.Address.Native.Storage = undefined;
113 var address_len: u32 = @sizeOf(os.sockaddr_storage);119 var address_len: u32 = @sizeOf(Socket.Address.Native.Storage);
114 try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);120 try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
115 return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));121 return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
116 }122 }
...@@ -165,14 +171,7 @@ pub fn Mixin(comptime Socket: type) type {...@@ -165,14 +171,7 @@ pub fn Mixin(comptime Socket: type) type {
165 /// seconds.171 /// seconds.
166 pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {172 pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
167 if (comptime @hasDecl(os, "SO_LINGER")) {173 if (comptime @hasDecl(os, "SO_LINGER")) {
168 const settings = extern struct {174 const settings = Socket.Linger.init(timeout_seconds);
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
176 return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings));175 return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings));
177 }176 }
178177
lib/std/x/os/socket_windows.zig+39-27
...@@ -16,27 +16,24 @@ const ws2_32 = windows.ws2_32;...@@ -16,27 +16,24 @@ const ws2_32 = windows.ws2_32;
16pub fn Mixin(comptime Socket: type) type {16pub fn Mixin(comptime Socket: type) type {
17 return struct {17 return struct {
18 /// Open a new socket.18 /// Open a new socket.
19 pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {19 pub fn init(domain: u32, socket_type: u32, protocol: u32, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket {
20 var filtered_socket_type = socket_type & ~@as(u32, os.SOCK_CLOEXEC);20 var raw_flags: u32 = 0;
2121 const set = std.EnumSet(Socket.InitFlags).init(flags);
22 var filtered_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED;22 if (set.contains(.close_on_exec)) raw_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
23 if (socket_type & os.SOCK_CLOEXEC != 0) {
24 filtered_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
25 }
2623
27 const fd = ws2_32.WSASocketW(24 const fd = ws2_32.WSASocketW(
28 @intCast(i32, domain),25 @intCast(i32, domain),
29 @intCast(i32, filtered_socket_type),26 @intCast(i32, socket_type),
30 @intCast(i32, protocol),27 @intCast(i32, protocol),
31 null,28 null,
32 0,29 0,
33 filtered_flags,30 raw_flags,
34 );31 );
35 if (fd == ws2_32.INVALID_SOCKET) {32 if (fd == ws2_32.INVALID_SOCKET) {
36 return switch (ws2_32.WSAGetLastError()) {33 return switch (ws2_32.WSAGetLastError()) {
37 .WSANOTINITIALISED => {34 .WSANOTINITIALISED => {
38 _ = try windows.WSAStartup(2, 2);35 _ = try windows.WSAStartup(2, 2);
39 return Socket.init(domain, socket_type, protocol);36 return Socket.init(domain, socket_type, protocol, flags);
40 },37 },
41 .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,38 .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
42 .WSAEMFILE => error.ProcessFdQuotaExceeded,39 .WSAEMFILE => error.ProcessFdQuotaExceeded,
...@@ -46,6 +43,14 @@ pub fn Mixin(comptime Socket: type) type {...@@ -46,6 +43,14 @@ pub fn Mixin(comptime Socket: type) type {
46 };43 };
47 }44 }
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
49 return Socket{ .fd = fd };54 return Socket{ .fd = fd };
50 }55 }
5156
...@@ -138,12 +143,12 @@ pub fn Mixin(comptime Socket: type) type {...@@ -138,12 +143,12 @@ pub fn Mixin(comptime Socket: type) type {
138143
139 /// Accept a pending incoming connection queued to the kernel backlog144 /// Accept a pending incoming connection queued to the kernel backlog
140 /// of the socket.145 /// of the socket.
141 pub fn accept(self: Socket, flags: u32) !Socket.Connection {146 pub fn accept(self: Socket, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket.Connection {
142 var address: ws2_32.sockaddr_storage = undefined;147 var address: Socket.Address.Native.Storage = undefined;
143 var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);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);150 const fd = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
146 if (rc == ws2_32.INVALID_SOCKET) {151 if (fd == ws2_32.INVALID_SOCKET) {
147 return switch (ws2_32.WSAGetLastError()) {152 return switch (ws2_32.WSAGetLastError()) {
148 .WSANOTINITIALISED => unreachable,153 .WSANOTINITIALISED => unreachable,
149 .WSAECONNRESET => error.ConnectionResetByPeer,154 .WSAECONNRESET => error.ConnectionResetByPeer,
...@@ -158,9 +163,20 @@ pub fn Mixin(comptime Socket: type) type {...@@ -158,9 +163,20 @@ pub fn Mixin(comptime Socket: type) type {
158 };163 };
159 }164 }
160165
161 const socket = Socket.from(rc);166 const socket = Socket.from(fd);
167 errdefer socket.deinit();
168
162 const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));169 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
164 return Socket.Connection.from(socket, socket_address);180 return Socket.Connection.from(socket, socket_address);
165 }181 }
166182
...@@ -238,7 +254,7 @@ pub fn Mixin(comptime Socket: type) type {...@@ -238,7 +254,7 @@ pub fn Mixin(comptime Socket: type) type {
238 /// Writes multiple I/O vectors with a prepended message header to the socket254 /// Writes multiple I/O vectors with a prepended message header to the socket
239 /// with a set of flags specified. It returns the number of bytes that are255 /// with a set of flags specified. It returns the number of bytes that are
240 /// written to the socket.256 /// written to the socket.
241 pub fn writeVectorized(self: Socket, msg: ws2_32.msghdr_const, flags: u32) !usize {257 pub fn writeVectorized(self: Socket, msg: Socket.Message, flags: u32) !usize {
242 const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSASENDMSG, self.fd, ws2_32.WSAID_WSASENDMSG);258 const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSASENDMSG, self.fd, ws2_32.WSAID_WSASENDMSG);
243259
244 var num_bytes: u32 = undefined;260 var num_bytes: u32 = undefined;
...@@ -275,7 +291,7 @@ pub fn Mixin(comptime Socket: type) type {...@@ -275,7 +291,7 @@ pub fn Mixin(comptime Socket: type) type {
275 /// Read multiple I/O vectors with a prepended message header from the socket291 /// Read multiple I/O vectors with a prepended message header from the socket
276 /// with a set of flags specified. It returns the number of bytes that were292 /// with a set of flags specified. It returns the number of bytes that were
277 /// read into the buffer provided.293 /// read into the buffer provided.
278 pub fn readVectorized(self: Socket, msg: *ws2_32.msghdr, flags: u32) !usize {294 pub fn readVectorized(self: Socket, msg: *Socket.Message, flags: u32) !usize {
279 const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSARECVMSG, self.fd, ws2_32.WSAID_WSARECVMSG);295 const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSARECVMSG, self.fd, ws2_32.WSAID_WSARECVMSG);
280296
281 var num_bytes: u32 = undefined;297 var num_bytes: u32 = undefined;
...@@ -311,8 +327,8 @@ pub fn Mixin(comptime Socket: type) type {...@@ -311,8 +327,8 @@ pub fn Mixin(comptime Socket: type) type {
311327
312 /// Query the address that the socket is locally bounded to.328 /// Query the address that the socket is locally bounded to.
313 pub fn getLocalAddress(self: Socket) !Socket.Address {329 pub fn getLocalAddress(self: Socket) !Socket.Address {
314 var address: ws2_32.sockaddr_storage = undefined;330 var address: Socket.Address.Native.Storage = undefined;
315 var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);331 var address_len: c_int = @sizeOf(Socket.Address.Native.Storage);
316332
317 const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);333 const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
318 if (rc == ws2_32.SOCKET_ERROR) {334 if (rc == ws2_32.SOCKET_ERROR) {
...@@ -331,8 +347,8 @@ pub fn Mixin(comptime Socket: type) type {...@@ -331,8 +347,8 @@ pub fn Mixin(comptime Socket: type) type {
331347
332 /// Query the address that the socket is connected to.348 /// Query the address that the socket is connected to.
333 pub fn getRemoteAddress(self: Socket) !Socket.Address {349 pub fn getRemoteAddress(self: Socket) !Socket.Address {
334 var address: ws2_32.sockaddr_storage = undefined;350 var address: Socket.Address.Native.Storage = undefined;
335 var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);351 var address_len: c_int = @sizeOf(Socket.Address.Native.Storage);
336352
337 const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);353 const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
338 if (rc == ws2_32.SOCKET_ERROR) {354 if (rc == ws2_32.SOCKET_ERROR) {
...@@ -384,11 +400,7 @@ pub fn Mixin(comptime Socket: type) type {...@@ -384,11 +400,7 @@ pub fn Mixin(comptime Socket: type) type {
384 /// if the host does not support the option for a socket to linger around up until a timeout specified in400 /// if the host does not support the option for a socket to linger around up until a timeout specified in
385 /// seconds.401 /// seconds.
386 pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {402 pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
387 const settings = ws2_32.linger{403 const settings = Socket.Linger.init(timeout_seconds);
388 .l_onoff = @as(u16, @boolToInt(timeout_seconds != null)),
389 .l_linger = if (timeout_seconds) |seconds| seconds else 0,
390 };
391
392 return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings));404 return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings));
393 }405 }
394406