From 3600508fe18ecc61ea889a65127a2641a76c2933 Mon Sep 17 00:00:00 2001 From: lithdew Date: Wed, 12 May 2021 22:43:34 +0900 Subject: [PATCH 01/11] 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. --- lib/std/os.zig | 2 +- lib/std/os/bits/dragonfly.zig | 9 -- lib/std/os/bits/freebsd.zig | 46 ------- lib/std/os/bits/haiku.zig | 46 ------- lib/std/os/bits/linux.zig | 4 +- lib/std/os/bits/linux/arm-eabi.zig | 20 --- lib/std/os/bits/linux/arm64.zig | 24 ---- lib/std/os/bits/linux/i386.zig | 20 --- lib/std/os/bits/linux/powerpc.zig | 20 --- lib/std/os/bits/linux/powerpc64.zig | 20 --- lib/std/os/bits/linux/sparc64.zig | 20 --- lib/std/os/bits/linux/x86_64.zig | 24 ---- lib/std/os/bits/netbsd.zig | 46 ------- lib/std/os/bits/openbsd.zig | 46 ------- lib/std/os/windows/ws2_32.zig | 30 +--- lib/std/x.zig | 1 + lib/std/x/net/tcp.zig | 74 +++++++--- lib/std/x/os/io.zig | 205 ++++++++++++++++++++++++++++ lib/std/x/os/socket.zig | 199 +++++++++++++++++++++++++++ lib/std/x/os/socket_posix.zig | 77 ++++++----- lib/std/x/os/socket_windows.zig | 66 +++++---- 21 files changed, 548 insertions(+), 451 deletions(-) create mode 100644 lib/std/x/os/io.zig diff --git a/lib/std/os.zig b/lib/std/os.zig index 2fe7ba9c5a..4ed0be8f62 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4994,7 +4994,7 @@ pub fn sendmsg( /// The file descriptor of the sending socket. sockfd: socket_t, /// Message header and iovecs - msg: msghdr_const, + msg: std.x.os.Socket.Message, flags: u32, ) SendMsgError!usize { while (true) { diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig index 7686fec96e..f8f89bd07a 100644 --- a/lib/std/os/bits/dragonfly.zig +++ b/lib/std/os/bits/dragonfly.zig @@ -768,15 +768,6 @@ pub const dl_phdr_info = extern struct { dlpi_phdr: [*]std.elf.Phdr, dlpi_phnum: u16, }; -pub const msghdr = extern struct { - msg_name: ?*c_void, - msg_namelen: socklen_t, - msg_iov: [*c]iovec, - msg_iovlen: c_int, - msg_control: ?*c_void, - msg_controllen: socklen_t, - msg_flags: c_int, -}; pub const cmsghdr = extern struct { cmsg_len: socklen_t, cmsg_level: c_int, diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig index a679b0fe70..cd32972b89 100644 --- a/lib/std/os/bits/freebsd.zig +++ b/lib/std/os/bits/freebsd.zig @@ -82,52 +82,6 @@ pub const Flock = extern struct { __unused: [4]u8, }; -pub const msghdr = extern struct { - /// optional address - msg_name: ?*sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - /// optional address - msg_name: ?*const sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec_const, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - pub const libc_stat = extern struct { dev: dev_t, ino: ino_t, diff --git a/lib/std/os/bits/haiku.zig b/lib/std/os/bits/haiku.zig index c9a420e957..b229a4744d 100644 --- a/lib/std/os/bits/haiku.zig +++ b/lib/std/os/bits/haiku.zig @@ -73,52 +73,6 @@ pub const Flock = extern struct { __unused: [4]u8, }; -pub const msghdr = extern struct { - /// optional address - msg_name: ?*sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - /// optional address - msg_name: ?*const sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec_const, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - pub const off_t = i64; pub const ino_t = u64; diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 72ea62c32c..5ecdcf24a3 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1180,12 +1180,12 @@ pub const sockaddr_un = extern struct { }; pub const mmsghdr = extern struct { - msg_hdr: msghdr, + msg_hdr: std.x.os.Socket.Message, msg_len: u32, }; pub const mmsghdr_const = extern struct { - msg_hdr: msghdr_const, + msg_hdr: std.x.os.Socket.Message, msg_len: u32, }; diff --git a/lib/std/os/bits/linux/arm-eabi.zig b/lib/std/os/bits/linux/arm-eabi.zig index 335ea074ec..6f596ab3c5 100644 --- a/lib/std/os/bits/linux/arm-eabi.zig +++ b/lib/std/os/bits/linux/arm-eabi.zig @@ -526,26 +526,6 @@ pub const Flock = extern struct { __unused: [4]u8, }; -pub const msghdr = extern struct { - msg_name: ?*sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec, - msg_iovlen: i32, - msg_control: ?*c_void, - msg_controllen: socklen_t, - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - msg_name: ?*const sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec_const, - msg_iovlen: i32, - msg_control: ?*c_void, - msg_controllen: socklen_t, - msg_flags: i32, -}; - pub const blksize_t = i32; pub const nlink_t = u32; pub const time_t = isize; diff --git a/lib/std/os/bits/linux/arm64.zig b/lib/std/os/bits/linux/arm64.zig index e0771e7f66..46126b69fd 100644 --- a/lib/std/os/bits/linux/arm64.zig +++ b/lib/std/os/bits/linux/arm64.zig @@ -395,30 +395,6 @@ pub const Flock = extern struct { __unused: [4]u8, }; -pub const msghdr = extern struct { - msg_name: ?*sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec, - msg_iovlen: i32, - __pad1: i32 = 0, - msg_control: ?*c_void, - msg_controllen: socklen_t, - __pad2: socklen_t = 0, - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - msg_name: ?*const sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec_const, - msg_iovlen: i32, - __pad1: i32 = 0, - msg_control: ?*c_void, - msg_controllen: socklen_t, - __pad2: socklen_t = 0, - msg_flags: i32, -}; - pub const blksize_t = i32; pub const nlink_t = u32; pub const time_t = isize; diff --git a/lib/std/os/bits/linux/i386.zig b/lib/std/os/bits/linux/i386.zig index f8dadb8a60..7e5f7935a3 100644 --- a/lib/std/os/bits/linux/i386.zig +++ b/lib/std/os/bits/linux/i386.zig @@ -523,26 +523,6 @@ pub const Flock = extern struct { l_pid: pid_t, }; -pub const msghdr = extern struct { - msg_name: ?*sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec, - msg_iovlen: i32, - msg_control: ?*c_void, - msg_controllen: socklen_t, - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - msg_name: ?*const sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec_const, - msg_iovlen: i32, - msg_control: ?*c_void, - msg_controllen: socklen_t, - msg_flags: i32, -}; - pub const blksize_t = i32; pub const nlink_t = u32; pub const time_t = isize; diff --git a/lib/std/os/bits/linux/powerpc.zig b/lib/std/os/bits/linux/powerpc.zig index 96908cb714..908593ae90 100644 --- a/lib/std/os/bits/linux/powerpc.zig +++ b/lib/std/os/bits/linux/powerpc.zig @@ -515,26 +515,6 @@ pub const Flock = extern struct { l_pid: pid_t, }; -pub const msghdr = extern struct { - msg_name: ?*sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec, - msg_iovlen: usize, - msg_control: ?*c_void, - msg_controllen: socklen_t, - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - msg_name: ?*const sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec_const, - msg_iovlen: usize, - msg_control: ?*c_void, - msg_controllen: socklen_t, - msg_flags: i32, -}; - pub const blksize_t = i32; pub const nlink_t = u32; pub const time_t = isize; diff --git a/lib/std/os/bits/linux/powerpc64.zig b/lib/std/os/bits/linux/powerpc64.zig index 52b9109247..d7c554fb86 100644 --- a/lib/std/os/bits/linux/powerpc64.zig +++ b/lib/std/os/bits/linux/powerpc64.zig @@ -491,26 +491,6 @@ pub const Flock = extern struct { __unused: [4]u8, }; -pub const msghdr = extern struct { - msg_name: ?*sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec, - msg_iovlen: usize, - msg_control: ?*c_void, - msg_controllen: usize, - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - msg_name: ?*const sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec_const, - msg_iovlen: usize, - msg_control: ?*c_void, - msg_controllen: usize, - msg_flags: i32, -}; - pub const blksize_t = i64; pub const nlink_t = u64; pub const time_t = i64; diff --git a/lib/std/os/bits/linux/sparc64.zig b/lib/std/os/bits/linux/sparc64.zig index 5c67b745f0..e40fb994fd 100644 --- a/lib/std/os/bits/linux/sparc64.zig +++ b/lib/std/os/bits/linux/sparc64.zig @@ -465,26 +465,6 @@ pub const Flock = extern struct { l_pid: pid_t, }; -pub const msghdr = extern struct { - msg_name: ?*sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec, - msg_iovlen: u64, - msg_control: ?*c_void, - msg_controllen: u64, - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - msg_name: ?*const sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec_const, - msg_iovlen: u64, - msg_control: ?*c_void, - msg_controllen: u64, - msg_flags: i32, -}; - pub const off_t = i64; pub const ino_t = u64; pub const mode_t = u32; diff --git a/lib/std/os/bits/linux/x86_64.zig b/lib/std/os/bits/linux/x86_64.zig index 30e5af384f..b82c1469e9 100644 --- a/lib/std/os/bits/linux/x86_64.zig +++ b/lib/std/os/bits/linux/x86_64.zig @@ -489,30 +489,6 @@ pub const Flock = extern struct { l_pid: pid_t, }; -pub const msghdr = extern struct { - msg_name: ?*sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec, - msg_iovlen: i32, - __pad1: i32 = 0, - msg_control: ?*c_void, - msg_controllen: socklen_t, - __pad2: socklen_t = 0, - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - msg_name: ?*const sockaddr, - msg_namelen: socklen_t, - msg_iov: [*]iovec_const, - msg_iovlen: i32, - __pad1: i32 = 0, - msg_control: ?*c_void, - msg_controllen: socklen_t, - __pad2: socklen_t = 0, - msg_flags: i32, -}; - pub const off_t = i64; pub const ino_t = u64; pub const dev_t = u64; diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig index 221baad864..0a3ea0d807 100644 --- a/lib/std/os/bits/netbsd.zig +++ b/lib/std/os/bits/netbsd.zig @@ -108,52 +108,6 @@ pub const EAI = enum(c_int) { pub const EAI_MAX = 15; -pub const msghdr = extern struct { - /// optional address - msg_name: ?*sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - -pub const msghdr_const = extern struct { - /// optional address - msg_name: ?*const sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec_const, - - /// # elements in msg_iov - msg_iovlen: i32, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: i32, -}; - pub const libc_stat = extern struct { dev: dev_t, mode: mode_t, diff --git a/lib/std/os/bits/openbsd.zig b/lib/std/os/bits/openbsd.zig index 8d42300817..44fe9cc5f2 100644 --- a/lib/std/os/bits/openbsd.zig +++ b/lib/std/os/bits/openbsd.zig @@ -124,52 +124,6 @@ pub const EAI = enum(c_int) { pub const EAI_MAX = 15; -pub const msghdr = extern struct { - /// optional address - msg_name: ?*sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec, - - /// # elements in msg_iov - msg_iovlen: c_uint, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: c_int, -}; - -pub const msghdr_const = extern struct { - /// optional address - msg_name: ?*const sockaddr, - - /// size of address - msg_namelen: socklen_t, - - /// scatter/gather array - msg_iov: [*]iovec_const, - - /// # elements in msg_iov - msg_iovlen: c_uint, - - /// ancillary data - msg_control: ?*c_void, - - /// ancillary data buffer len - msg_controllen: socklen_t, - - /// flags on received message - msg_flags: c_int, -}; - pub const libc_stat = extern struct { mode: mode_t, dev: dev_t, diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index 15aa0d487f..39f68bb234 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/lib/std/os/windows/ws2_32.zig @@ -3,6 +3,7 @@ // This file is part of [zig](https://ziglang.org/), which is MIT licensed. // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. +const std = @import("../../std.zig"); usingnamespace @import("bits.zig"); pub const SOCKET = *opaque {}; @@ -1093,27 +1094,6 @@ pub const WSABUF = extern struct { buf: [*]u8, }; -pub const msghdr = WSAMSG; -pub const msghdr_const = WSAMSG_const; - -pub const WSAMSG_const = extern struct { - name: *const sockaddr, - namelen: INT, - lpBuffers: [*]WSABUF, - dwBufferCount: DWORD, - Control: WSABUF, - dwFlags: DWORD, -}; - -pub const WSAMSG = extern struct { - name: *sockaddr, - namelen: INT, - lpBuffers: [*]WSABUF, - dwBufferCount: DWORD, - Control: WSABUF, - dwFlags: DWORD, -}; - pub const WSAPOLLFD = pollfd; pub const pollfd = extern struct { @@ -1163,7 +1143,7 @@ pub const LPFN_GETACCEPTEXSOCKADDRS = fn ( pub const LPFN_WSASENDMSG = fn ( s: SOCKET, - lpMsg: *const WSAMSG_const, + lpMsg: *const std.x.os.Socket.Message, dwFlags: u32, lpNumberOfBytesSent: ?*u32, lpOverlapped: ?*OVERLAPPED, @@ -1172,7 +1152,7 @@ pub const LPFN_WSASENDMSG = fn ( pub const LPFN_WSARECVMSG = fn ( s: SOCKET, - lpMsg: *WSAMSG, + lpMsg: *std.x.os.Socket.Message, lpdwNumberOfBytesRecv: ?*u32, lpOverlapped: ?*OVERLAPPED, lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE, @@ -2046,7 +2026,7 @@ pub extern "ws2_32" fn WSASend( pub extern "ws2_32" fn WSASendMsg( s: SOCKET, - lpMsg: *const WSAMSG_const, + lpMsg: *const std.x.os.Socket.Message, dwFlags: u32, lpNumberOfBytesSent: ?*u32, lpOverlapped: ?*OVERLAPPED, @@ -2055,7 +2035,7 @@ pub extern "ws2_32" fn WSASendMsg( pub extern "ws2_32" fn WSARecvMsg( s: SOCKET, - lpMsg: *WSAMSG, + lpMsg: *std.x.os.Socket.Message, lpdwNumberOfBytesRecv: ?*u32, lpOverlapped: ?*OVERLAPPED, lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE, diff --git a/lib/std/x.zig b/lib/std/x.zig index 022261bf3b..3ac8b10f4a 100644 --- a/lib/std/x.zig +++ b/lib/std/x.zig @@ -8,6 +8,7 @@ const std = @import("std.zig"); pub const os = struct { pub const Socket = @import("x/os/socket.zig").Socket; + pub usingnamespace @import("x/os/io.zig"); pub usingnamespace @import("x/os/net.zig"); }; diff --git a/lib/std/x/net/tcp.zig b/lib/std/x/net/tcp.zig index f0e341234a..abf1191dfd 100644 --- a/lib/std/x/net/tcp.zig +++ b/lib/std/x/net/tcp.zig @@ -18,6 +18,7 @@ const testing = std.testing; const IPv4 = std.x.os.IPv4; const IPv6 = std.x.os.IPv6; const Socket = std.x.os.Socket; +const Buffer = std.x.os.Buffer; /// A generic TCP socket abstraction. const tcp = @This(); @@ -82,12 +83,13 @@ pub const Client = struct { }; /// Opens a new client. - pub fn init(domain: tcp.Domain, flags: u32) !Client { + pub fn init(domain: tcp.Domain, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Client { return Client{ .socket = try Socket.init( @enumToInt(domain), - os.SOCK_STREAM | flags, + os.SOCK_STREAM, os.IPPROTO_TCP, + flags, ), }; } @@ -143,14 +145,14 @@ pub const Client = struct { /// Writes multiple I/O vectors with a prepended message header to the socket /// with a set of flags specified. It returns the number of bytes that are /// written to the socket. - pub fn writeVectorized(self: Client, msg: os.msghdr_const, flags: u32) !usize { + pub fn writeVectorized(self: Client, msg: Socket.Message, flags: u32) !usize { return self.socket.writeVectorized(msg, flags); } /// Read multiple I/O vectors with a prepended message header from the socket /// with a set of flags specified. It returns the number of bytes that were /// read into the buffer provided. - pub fn readVectorized(self: Client, msg: *os.msghdr, flags: u32) !usize { + pub fn readVectorized(self: Client, msg: *Socket.Message, flags: u32) !usize { return self.socket.readVectorized(msg, flags); } @@ -244,12 +246,13 @@ pub const Listener = struct { socket: Socket, /// Opens a new listener. - pub fn init(domain: tcp.Domain, flags: u32) !Listener { + pub fn init(domain: tcp.Domain, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Listener { return Listener{ .socket = try Socket.init( @enumToInt(domain), - os.SOCK_STREAM | flags, + os.SOCK_STREAM, os.IPPROTO_TCP, + flags, ), }; } @@ -278,7 +281,7 @@ pub const Listener = struct { /// Accept a pending incoming connection queued to the kernel backlog /// of the listener's socket. - pub fn accept(self: Listener, flags: u32) !tcp.Connection { + pub fn accept(self: Listener, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !tcp.Connection { return tcp.Connection.from(try self.socket.accept(flags)); } @@ -324,7 +327,7 @@ pub const Listener = struct { test "tcp: create client/listener pair" { if (builtin.os.tag == .wasi) return error.SkipZigTest; - const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); + const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true }); defer listener.deinit(); try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); @@ -336,19 +339,19 @@ test "tcp: create client/listener pair" { .ipv6 => |*ipv6| ipv6.host = IPv6.localhost, } - const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC); + const client = try tcp.Client.init(.ip, .{ .close_on_exec = true }); defer client.deinit(); try client.connect(binded_address); - const conn = try listener.accept(os.SOCK_CLOEXEC); + const conn = try listener.accept(.{ .close_on_exec = true }); defer conn.deinit(); } -test "tcp/client: set read timeout of 1 millisecond on blocking client" { +test "tcp/client: 1ms read timeout" { if (builtin.os.tag == .wasi) return error.SkipZigTest; - const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); + const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true }); defer listener.deinit(); 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" { .ipv6 => |*ipv6| ipv6.host = IPv6.localhost, } - const client = try tcp.Client.init(.ip, os.SOCK_CLOEXEC); + const client = try tcp.Client.init(.ip, .{ .close_on_exec = true }); defer client.deinit(); try client.connect(binded_address); try client.setReadTimeout(1); - const conn = try listener.accept(os.SOCK_CLOEXEC); + const conn = try listener.accept(.{ .close_on_exec = true }); defer conn.deinit(); var buf: [1]u8 = undefined; try testing.expectError(error.WouldBlock, client.reader(0).read(&buf)); } +test "tcp/client: read and write multiple vectors" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true }); + defer listener.deinit(); + + try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); + try listener.listen(128); + + var binded_address = try listener.getLocalAddress(); + switch (binded_address) { + .ipv4 => |*ipv4| ipv4.host = IPv4.localhost, + .ipv6 => |*ipv6| ipv6.host = IPv6.localhost, + } + + const client = try tcp.Client.init(.ip, .{ .close_on_exec = true }); + defer client.deinit(); + + try client.connect(binded_address); + + const conn = try listener.accept(.{ .close_on_exec = true }); + defer conn.deinit(); + + const message = "hello world"; + _ = try conn.client.writeVectorized(Socket.Message.fromBuffers(&[_]Buffer{ + Buffer.from(message[0 .. message.len / 2]), + Buffer.from(message[message.len / 2 ..]), + }), 0); + + var buf: [message.len]u8 = undefined; + var msg = Socket.Message.fromBuffers(&[_]Buffer{ + Buffer.from(buf[0 .. message.len / 2]), + Buffer.from(buf[message.len / 2 ..]), + }); + _ = try client.readVectorized(&msg, 0); + + try testing.expectEqualStrings(message, &buf); +} + test "tcp/listener: bind to unspecified ipv4 address" { if (builtin.os.tag == .wasi) return error.SkipZigTest; - const listener = try tcp.Listener.init(.ip, os.SOCK_CLOEXEC); + const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true }); defer listener.deinit(); try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); @@ -389,7 +431,7 @@ test "tcp/listener: bind to unspecified ipv4 address" { test "tcp/listener: bind to unspecified ipv6 address" { if (builtin.os.tag == .wasi) return error.SkipZigTest; - const listener = try tcp.Listener.init(.ipv6, os.SOCK_CLOEXEC); + const listener = try tcp.Listener.init(.ipv6, .{ .close_on_exec = true }); defer listener.deinit(); try listener.bind(ip.Address.initIPv6(IPv6.unspecified, 0)); diff --git a/lib/std/x/os/io.zig b/lib/std/x/os/io.zig new file mode 100644 index 0000000000..597f7d268c --- /dev/null +++ b/lib/std/x/os/io.zig @@ -0,0 +1,205 @@ +const std = @import("../../std.zig"); + +const os = std.os; +const mem = std.mem; +const builtin = std.builtin; +const testing = std.testing; + +/// POSIX `iovec`, or Windows `WSABUF`. The difference between the two are the ordering +/// of fields, alongside the length being represented as either a ULONG or a size_t. +pub const Buffer = if (builtin.os.tag == .windows) + extern struct { + len: c_ulong, + ptr: usize, + + pub fn from(slice: []const u8) Buffer { + return .{ .len = @intCast(c_ulong, slice.len), .ptr = @ptrToInt(slice.ptr) }; + } + + pub fn into(self: Buffer) []const u8 { + return @intToPtr([*]const u8, self.ptr)[0..self.len]; + } + + pub fn intoMutable(self: Buffer) []u8 { + return @intToPtr([*]u8, self.ptr)[0..self.len]; + } + } +else + extern struct { + ptr: usize, + len: usize, + + pub fn from(slice: []const u8) Buffer { + return .{ .ptr = @ptrToInt(slice.ptr), .len = slice.len }; + } + + pub fn into(self: Buffer) []const u8 { + return @intToPtr([*]const u8, self.ptr)[0..self.len]; + } + + pub fn intoMutable(self: Buffer) []u8 { + return @intToptr([*]u8, self.ptr)[0..self.len]; + } + }; + +pub const Reactor = struct { + pub const InitFlags = enum { + close_on_exec, + }; + + pub const Event = struct { + data: usize, + is_error: bool, + is_hup: bool, + is_readable: bool, + is_writable: bool, + }; + + pub const Interest = struct { + hup: bool = false, + oneshot: bool = false, + readable: bool = false, + writable: bool = false, + }; + + fd: os.fd_t, + + pub fn init(flags: std.enums.EnumFieldStruct(Reactor.InitFlags, bool, false)) !Reactor { + var raw_flags: u32 = 0; + const set = std.EnumSet(Reactor.InitFlags).init(flags); + if (set.contains(.close_on_exec)) raw_flags |= os.EPOLL_CLOEXEC; + return Reactor{ .fd = try os.epoll_create1(raw_flags) }; + } + + pub fn deinit(self: Reactor) void { + os.close(self.fd); + } + + pub fn update(self: Reactor, fd: os.fd_t, identifier: usize, interest: Reactor.Interest) !void { + var flags: u32 = 0; + flags |= if (interest.oneshot) os.EPOLLONESHOT else os.EPOLLET; + if (interest.hup) flags |= os.EPOLLRDHUP; + if (interest.readable) flags |= os.EPOLLIN; + if (interest.writable) flags |= os.EPOLLOUT; + + const event = &os.epoll_event{ + .events = flags, + .data = .{ .ptr = identifier }, + }; + + os.epoll_ctl(self.fd, os.EPOLL_CTL_MOD, fd, event) catch |err| switch (err) { + error.FileDescriptorNotRegistered => try os.epoll_ctl(self.fd, os.EPOLL_CTL_ADD, fd, event), + else => return err, + }; + } + + pub fn poll(self: Reactor, comptime max_num_events: comptime_int, closure: anytype, timeout_milliseconds: ?u64) !void { + var events: [max_num_events]os.epoll_event = undefined; + + const num_events = os.epoll_wait(self.fd, &events, if (timeout_milliseconds) |ms| @intCast(i32, ms) else -1); + for (events[0..num_events]) |ev| { + const is_error = ev.events & os.EPOLLERR != 0; + const is_hup = ev.events & (os.EPOLLHUP | os.EPOLLRDHUP) != 0; + const is_readable = ev.events & os.EPOLLIN != 0; + const is_writable = ev.events & os.EPOLLOUT != 0; + + try closure.call(Reactor.Event{ + .data = ev.data.ptr, + .is_error = is_error, + .is_hup = is_hup, + .is_readable = is_readable, + .is_writable = is_writable, + }); + } + } +}; + +test "reactor/linux: drive async tcp client/listener pair" { + if (builtin.os.tag != .linux) return error.SkipZigTest; + + const ip = std.x.net.ip; + const tcp = std.x.net.tcp; + + const IPv4 = std.x.os.IPv4; + const IPv6 = std.x.os.IPv6; + const Socket = std.x.os.Socket; + + const reactor = try Reactor.init(.{ .close_on_exec = true }); + defer reactor.deinit(); + + const listener = try tcp.Listener.init(.ip, .{ + .close_on_exec = true, + .nonblocking = true, + }); + defer listener.deinit(); + + try reactor.update(listener.socket.fd, 0, .{ .readable = true }); + try reactor.poll(1, struct { + fn call(event: Reactor.Event) !void { + try testing.expectEqual(Reactor.Event{ + .data = 0, + .is_error = false, + .is_hup = true, + .is_readable = false, + .is_writable = false, + }, event); + } + }, null); + + try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0)); + try listener.listen(128); + + var binded_address = try listener.getLocalAddress(); + switch (binded_address) { + .ipv4 => |*ipv4| ipv4.host = IPv4.localhost, + .ipv6 => |*ipv6| ipv6.host = IPv6.localhost, + } + + const client = try tcp.Client.init(.ip, .{ + .close_on_exec = true, + .nonblocking = true, + }); + defer client.deinit(); + + try reactor.update(client.socket.fd, 1, .{ .readable = true, .writable = true }); + try reactor.poll(1, struct { + fn call(event: Reactor.Event) !void { + try testing.expectEqual(Reactor.Event{ + .data = 1, + .is_error = false, + .is_hup = true, + .is_readable = false, + .is_writable = true, + }, event); + } + }, null); + + client.connect(binded_address) catch |err| switch (err) { + error.WouldBlock => {}, + else => return err, + }; + + try reactor.poll(1, struct { + fn call(event: Reactor.Event) !void { + try testing.expectEqual(Reactor.Event{ + .data = 1, + .is_error = false, + .is_hup = false, + .is_readable = false, + .is_writable = true, + }, event); + } + }, null); + + try reactor.poll(1, struct { + fn call(event: Reactor.Event) !void { + try testing.expectEqual(Reactor.Event{ + .data = 0, + .is_error = false, + .is_hup = false, + .is_readable = true, + .is_writable = false, + }, event); + } + }, null); +} diff --git a/lib/std/x/os/socket.zig b/lib/std/x/os/socket.zig index 963a1adca6..05bd0499d1 100644 --- a/lib/std/x/os/socket.zig +++ b/lib/std/x/os/socket.zig @@ -11,8 +11,13 @@ const os = std.os; const fmt = std.fmt; const mem = std.mem; const time = std.time; +const meta = std.meta; const builtin = std.builtin; +const Buffer = std.x.os.Buffer; + +const assert = std.debug.assert; + /// A generic, cross-platform socket abstraction. pub const Socket = struct { /// A socket-address pair. @@ -29,6 +34,32 @@ pub const Socket = struct { /// A generic socket address abstraction. It is safe to directly access and modify /// the fields of a `Socket.Address`. pub const Address = union(enum) { + pub const Native = struct { + pub const requires_prepended_length = builtin.os.getVersionRange() == .semver; + pub const Length = if (requires_prepended_length) u8 else [0]u8; + + pub const Family = if (requires_prepended_length) u8 else c_ushort; + + /// POSIX `sockaddr_storage`. The expected size and alignment is specified in IETF RFC 2553. + pub const Storage = extern struct { + pub const expected_size = 128; + pub const expected_alignment = 8; + + pub const padding_size = expected_size - + mem.alignForward(@sizeOf(Address.Native.Length), expected_alignment) - + mem.alignForward(@sizeOf(Address.Native.Family), expected_alignment); + + len: Address.Native.Length align(expected_alignment) = undefined, + family: Address.Native.Family align(expected_alignment) = undefined, + padding: [padding_size]u8 align(expected_alignment) = undefined, + + comptime { + assert(@sizeOf(Storage) == Storage.expected_size); + assert(@alignOf(Storage) == Storage.expected_alignment); + } + }; + }; + ipv4: net.IPv4.Address, ipv6: net.IPv6.Address, @@ -107,6 +138,174 @@ pub const Socket = struct { } }; + /// POSIX `msghdr`. Denotes a destination address, set of buffers, control data, and flags. Ported + /// directly from musl. + pub const Message = if (builtin.os.isAtLeast(.windows, .vista) != null and builtin.os.isAtLeast(.windows, .vista).?) + extern struct { + name: usize = @ptrToInt(@as(?[*]u8, null)), + name_len: c_int = 0, + + buffers: usize = undefined, + buffers_len: c_ulong = undefined, + + control: Buffer = .{ + .ptr = @ptrToInt(@as(?[*]u8, null)), + .len = 0, + }, + flags: c_ulong = 0, + + pub usingnamespace MessageMixin(Message); + } + else if (builtin.os.tag == .windows) + extern struct { + name: usize = @ptrToInt(@as(?[*]u8, null)), + name_len: c_int = 0, + + buffers: usize = undefined, + buffers_len: u32 = undefined, + + control: Buffer = .{ + .ptr = @ptrToInt(@as(?[*]u8, null)), + .len = 0, + }, + flags: u32 = 0, + + pub usingnamespace MessageMixin(Message); + } + else if (@sizeOf(usize) > 4 and builtin.endian == .Big) + extern struct { + name: usize = @ptrToInt(@as(?[*]u8, null)), + name_len: c_uint = 0, + + buffers: usize = undefined, + _pad_1: c_int = 0, + buffers_len: c_int = undefined, + + control: usize = @ptrToInt(@as(?[*]u8, null)), + _pad_2: c_int = 0, + control_len: c_uint = 0, + + flags: c_int = 0, + + pub usingnamespace MessageMixin(Message); + } + else if (@sizeOf(usize) > 4 and builtin.endian == .Little) + extern struct { + name: usize = @ptrToInt(@as(?[*]u8, null)), + name_len: c_uint = 0, + + buffers: usize = undefined, + buffers_len: c_int = undefined, + _pad_1: c_int = 0, + + control: usize = @ptrToInt(@as(?[*]u8, null)), + control_len: c_uint = 0, + _pad_2: c_int = 0, + + flags: c_int = 0, + + pub usingnamespace MessageMixin(Message); + } + else + extern struct { + name: usize = @ptrToInt(@as(?[*]u8, null)), + name_len: c_uint = 0, + + buffers: usize = undefined, + buffers_len: c_int, + + control: usize = null, + control_len: c_uint = 0, + + flags: c_int = 0, + + pub usingnamespace MessageMixin(Message); + }; + + fn MessageMixin(comptime Self: type) type { + return struct { + pub fn fromBuffers(buffers: []const Buffer) Self { + var self: Self = .{}; + self.setBuffers(buffers); + return self; + } + + pub fn setName(self: *Self, name: []const u8) void { + self.name = @ptrToInt(name.ptr); + self.name_len = @intCast(meta.fieldInfo(Self, .name_len).field_type, name.len); + } + + pub fn setBuffers(self: *Self, buffers: []const Buffer) void { + self.buffers = @ptrToInt(buffers.ptr); + self.buffers_len = @intCast(meta.fieldInfo(Self, .buffers_len).field_type, buffers.len); + } + + pub fn setControl(self: *Self, control: []const u8) void { + if (builtin.os.tag == .windows) { + self.control = Buffer.from(control); + } else { + self.control = @ptrToInt(control.ptr); + self.control_len = @intCast(meta.fieldInfo(Self, .control_len).field_type, control.len); + } + } + + pub fn setFlags(self: *Self, flags: u32) void { + self.flags = @intCast(meta.fieldInfo(Self, .flags).field_type, flags); + } + + pub fn getName(self: Self) []const u8 { + return @intToPtr([*]const u8, self.name)[0..@intCast(usize, self.name_len)]; + } + + pub fn getBuffers(self: Self) []const Buffer { + return @intToPtr([*]const Buffer, self.buffers)[0..@intCast(usize, self.buffers_len)]; + } + + pub fn getControl(self: Self) []const u8 { + if (builtin.os.tag == .windows) { + return self.control.into(); + } else { + return @intToPtr([*]const u8, self.control)[0..@intCast(usize, self.control_len)]; + } + } + + pub fn getFlags(self: Self) u32 { + return @intCast(u32, self.flags); + } + }; + } + + /// POSIX `linger`, denoting the linger settings of a socket. + /// + /// Microsoft's documentation and glibc denote the fields to be unsigned + /// short's on Windows, whereas glibc and musl denote the fields to be + /// int's on every other platform. + pub const Linger = extern struct { + pub const Field = switch (builtin.os.tag) { + .windows => c_ushort, + else => c_int, + }; + + enabled: Field, + timeout_seconds: Field, + + pub fn init(timeout_seconds: ?u16) Socket.Linger { + return .{ + .enabled = @intCast(Socket.Linger.Field, @boolToInt(timeout_seconds != null)), + .timeout_seconds = if (timeout_seconds) |seconds| @intCast(Socket.Linger.Field, seconds) else 0, + }; + } + }; + + /// Possible set of flags to initialize a socket with. + pub const InitFlags = enum { + // Initialize a socket to be non-blocking. + nonblocking, + + // Have a socket close itself on exec syscalls. + close_on_exec, + }; + /// The underlying handle of a socket. fd: os.socket_t, diff --git a/lib/std/x/os/socket_posix.zig b/lib/std/x/os/socket_posix.zig index 1e54c5c7a2..b9fc21a40d 100644 --- a/lib/std/x/os/socket_posix.zig +++ b/lib/std/x/os/socket_posix.zig @@ -13,8 +13,12 @@ const time = std.time; pub fn Mixin(comptime Socket: type) type { return struct { /// Open a new socket. - pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket { - return Socket{ .fd = try os.socket(domain, socket_type, protocol) }; + pub fn init(domain: u32, socket_type: u32, protocol: u32, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket { + var raw_flags: u32 = socket_type; + const set = std.EnumSet(Socket.InitFlags).init(flags); + if (set.contains(.close_on_exec)) raw_flags |= os.SOCK_CLOEXEC; + if (set.contains(.nonblocking)) raw_flags |= os.SOCK_NONBLOCK; + return Socket{ .fd = try os.socket(domain, raw_flags, protocol) }; } /// Closes the socket. @@ -44,11 +48,16 @@ pub fn Mixin(comptime Socket: type) type { /// Accept a pending incoming connection queued to the kernel backlog /// of the socket. - pub fn accept(self: Socket, flags: u32) !Socket.Connection { - var address: os.sockaddr_storage = undefined; - var address_len: u32 = @sizeOf(os.sockaddr_storage); + pub fn accept(self: Socket, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket.Connection { + var address: Socket.Address.Native.Storage = undefined; + var address_len: u32 = @sizeOf(Socket.Address.Native.Storage); - const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags) }; + var raw_flags: u32 = 0; + const set = std.EnumSet(Socket.InitFlags).init(flags); + if (set.contains(.close_on_exec)) raw_flags |= os.SOCK_CLOEXEC; + if (set.contains(.nonblocking)) raw_flags |= os.SOCK_NONBLOCK; + + const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, raw_flags) }; const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); return Socket.Connection.from(socket, socket_address); @@ -69,48 +78,45 @@ pub fn Mixin(comptime Socket: type) type { /// Writes multiple I/O vectors with a prepended message header to the socket /// with a set of flags specified. It returns the number of bytes that are /// written to the socket. - pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize { + pub fn writeVectorized(self: Socket, msg: Socket.Message, flags: u32) !usize { return os.sendmsg(self.fd, msg, flags); } /// Read multiple I/O vectors with a prepended message header from the socket /// with a set of flags specified. It returns the number of bytes that were /// read into the buffer provided. - pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize { - if (comptime @hasDecl(os.system, "recvmsg")) { - while (true) { - const rc = os.system.recvmsg(self.fd, msg, flags); - return switch (os.errno(rc)) { - 0 => @intCast(usize, rc), - os.EBADF => unreachable, // always a race condition - os.EFAULT => unreachable, - os.EINVAL => unreachable, - os.ENOTCONN => unreachable, - os.ENOTSOCK => unreachable, - os.EINTR => continue, - os.EAGAIN => error.WouldBlock, - os.ENOMEM => error.SystemResources, - os.ECONNREFUSED => error.ConnectionRefused, - os.ECONNRESET => error.ConnectionResetByPeer, - else => |err| os.unexpectedErrno(err), - }; - } + pub fn readVectorized(self: Socket, msg: *Socket.Message, flags: u32) !usize { + while (true) { + const rc = os.system.recvmsg(self.fd, msg, flags); + return switch (os.errno(rc)) { + 0 => @intCast(usize, rc), + os.EBADF => unreachable, // always a race condition + os.EFAULT => unreachable, + os.EINVAL => unreachable, + os.ENOTCONN => unreachable, + os.ENOTSOCK => unreachable, + os.EINTR => continue, + os.EAGAIN => error.WouldBlock, + os.ENOMEM => error.SystemResources, + os.ECONNREFUSED => error.ConnectionRefused, + os.ECONNRESET => error.ConnectionResetByPeer, + else => |err| os.unexpectedErrno(err), + }; } - return error.NotSupported; } /// Query the address that the socket is locally bounded to. pub fn getLocalAddress(self: Socket) !Socket.Address { - var address: os.sockaddr_storage = undefined; - var address_len: u32 = @sizeOf(os.sockaddr_storage); + var address: Socket.Address.Native.Storage = undefined; + var address_len: u32 = @sizeOf(Socket.Address.Native.Storage); try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len); return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); } /// Query the address that the socket is connected to. pub fn getRemoteAddress(self: Socket) !Socket.Address { - var address: os.sockaddr_storage = undefined; - var address_len: u32 = @sizeOf(os.sockaddr_storage); + var address: Socket.Address.Native.Storage = undefined; + var address_len: u32 = @sizeOf(Socket.Address.Native.Storage); try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len); return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); } @@ -165,14 +171,7 @@ pub fn Mixin(comptime Socket: type) type { /// seconds. pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void { if (comptime @hasDecl(os, "SO_LINGER")) { - const settings = extern struct { - l_onoff: c_int, - l_linger: c_int, - }{ - .l_onoff = @intCast(c_int, @boolToInt(timeout_seconds != null)), - .l_linger = if (timeout_seconds) |seconds| @intCast(c_int, seconds) else 0, - }; - + const settings = Socket.Linger.init(timeout_seconds); return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings)); } diff --git a/lib/std/x/os/socket_windows.zig b/lib/std/x/os/socket_windows.zig index 6dd1f9a6a9..ed1db4f62f 100644 --- a/lib/std/x/os/socket_windows.zig +++ b/lib/std/x/os/socket_windows.zig @@ -16,27 +16,24 @@ const ws2_32 = windows.ws2_32; pub fn Mixin(comptime Socket: type) type { return struct { /// Open a new socket. - pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket { - var filtered_socket_type = socket_type & ~@as(u32, os.SOCK_CLOEXEC); - - var filtered_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED; - if (socket_type & os.SOCK_CLOEXEC != 0) { - filtered_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; - } + pub fn init(domain: u32, socket_type: u32, protocol: u32, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket { + var raw_flags: u32 = 0; + const set = std.EnumSet(Socket.InitFlags).init(flags); + if (set.contains(.close_on_exec)) raw_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; const fd = ws2_32.WSASocketW( @intCast(i32, domain), - @intCast(i32, filtered_socket_type), + @intCast(i32, socket_type), @intCast(i32, protocol), null, 0, - filtered_flags, + raw_flags, ); if (fd == ws2_32.INVALID_SOCKET) { return switch (ws2_32.WSAGetLastError()) { .WSANOTINITIALISED => { _ = try windows.WSAStartup(2, 2); - return Socket.init(domain, socket_type, protocol); + return Socket.init(domain, socket_type, protocol, flags); }, .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported, .WSAEMFILE => error.ProcessFdQuotaExceeded, @@ -46,6 +43,14 @@ pub fn Mixin(comptime Socket: type) type { }; } + if (set.contains(.nonblocking)) { + var enabled: c_ulong = 1; + const rc = ws2_32.ioctlsocket(fd, ws2_32.FIONBIO, &enabled); + if (rc == ws2_32.SOCKET_ERROR) { + return windows.unexpectedWSAError(ws2_32.WSAGetLastError()); + } + } + return Socket{ .fd = fd }; } @@ -138,12 +143,12 @@ pub fn Mixin(comptime Socket: type) type { /// Accept a pending incoming connection queued to the kernel backlog /// of the socket. - pub fn accept(self: Socket, flags: u32) !Socket.Connection { - var address: ws2_32.sockaddr_storage = undefined; - var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage); + pub fn accept(self: Socket, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket.Connection { + var address: Socket.Address.Native.Storage = undefined; + var address_len: c_int = @sizeOf(Socket.Address.Native.Storage); - const rc = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); - if (rc == ws2_32.INVALID_SOCKET) { + const fd = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); + if (fd == ws2_32.INVALID_SOCKET) { return switch (ws2_32.WSAGetLastError()) { .WSANOTINITIALISED => unreachable, .WSAECONNRESET => error.ConnectionResetByPeer, @@ -158,9 +163,20 @@ pub fn Mixin(comptime Socket: type) type { }; } - const socket = Socket.from(rc); + const socket = Socket.from(fd); + errdefer socket.deinit(); + const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address)); + const set = std.EnumSet(Socket.InitFlags).init(flags); + if (set.contains(.nonblocking)) { + var enabled: c_ulong = 1; + const rc = ws2_32.ioctlsocket(fd, ws2_32.FIONBIO, &enabled); + if (rc == ws2_32.SOCKET_ERROR) { + return windows.unexpectedWSAError(ws2_32.WSAGetLastError()); + } + } + return Socket.Connection.from(socket, socket_address); } @@ -238,7 +254,7 @@ pub fn Mixin(comptime Socket: type) type { /// Writes multiple I/O vectors with a prepended message header to the socket /// with a set of flags specified. It returns the number of bytes that are /// written to the socket. - pub fn writeVectorized(self: Socket, msg: ws2_32.msghdr_const, flags: u32) !usize { + pub fn writeVectorized(self: Socket, msg: Socket.Message, flags: u32) !usize { const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSASENDMSG, self.fd, ws2_32.WSAID_WSASENDMSG); var num_bytes: u32 = undefined; @@ -275,7 +291,7 @@ pub fn Mixin(comptime Socket: type) type { /// Read multiple I/O vectors with a prepended message header from the socket /// with a set of flags specified. It returns the number of bytes that were /// read into the buffer provided. - pub fn readVectorized(self: Socket, msg: *ws2_32.msghdr, flags: u32) !usize { + pub fn readVectorized(self: Socket, msg: *Socket.Message, flags: u32) !usize { const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSARECVMSG, self.fd, ws2_32.WSAID_WSARECVMSG); var num_bytes: u32 = undefined; @@ -311,8 +327,8 @@ pub fn Mixin(comptime Socket: type) type { /// Query the address that the socket is locally bounded to. pub fn getLocalAddress(self: Socket) !Socket.Address { - var address: ws2_32.sockaddr_storage = undefined; - var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage); + var address: Socket.Address.Native.Storage = undefined; + var address_len: c_int = @sizeOf(Socket.Address.Native.Storage); const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); if (rc == ws2_32.SOCKET_ERROR) { @@ -331,8 +347,8 @@ pub fn Mixin(comptime Socket: type) type { /// Query the address that the socket is connected to. pub fn getRemoteAddress(self: Socket) !Socket.Address { - var address: ws2_32.sockaddr_storage = undefined; - var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage); + var address: Socket.Address.Native.Storage = undefined; + var address_len: c_int = @sizeOf(Socket.Address.Native.Storage); const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); if (rc == ws2_32.SOCKET_ERROR) { @@ -384,11 +400,7 @@ pub fn Mixin(comptime Socket: type) type { /// if the host does not support the option for a socket to linger around up until a timeout specified in /// seconds. pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void { - const settings = ws2_32.linger{ - .l_onoff = @as(u16, @boolToInt(timeout_seconds != null)), - .l_linger = if (timeout_seconds) |seconds| seconds else 0, - }; - + const settings = Socket.Linger.init(timeout_seconds); return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings)); } From d41575737e50757abe6998c123c3e3dd8a64365f Mon Sep 17 00:00:00 2001 From: lithdew Date: Fri, 14 May 2021 13:29:40 +0900 Subject: [PATCH 02/11] std/c: add recvmsg, sendmsg --- lib/std/c.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 1d60af6747..a91fee7274 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -166,9 +166,10 @@ pub extern "c" fn sendto( dest_addr: ?*const sockaddr, addrlen: socklen_t, ) isize; +pub extern "c" fn sendmsg(sockfd: fd_t, msg: *const std.x.os.Socket.Message, flags: c_int) isize; -pub extern fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize; -pub extern fn recvfrom( +pub extern "c" fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize; +pub extern "c" fn recvfrom( sockfd: fd_t, noalias buf: *c_void, len: usize, @@ -176,6 +177,7 @@ pub extern fn recvfrom( noalias src_addr: ?*sockaddr, noalias addrlen: ?*socklen_t, ) isize; +pub extern "c" fn recvmsg(sockfd: fd_t, msg: *std.x.os.Socket.Message, flags: c_int) isize; pub usingnamespace switch (builtin.os.tag) { .netbsd => struct { From 494665e40866639878aac62848bf6e8ce9a730a8 Mon Sep 17 00:00:00 2001 From: lithdew Date: Fri, 14 May 2021 13:54:17 +0900 Subject: [PATCH 03/11] std/c: have sendmsg, recvmsg flags be u32 --- lib/std/c.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index a91fee7274..2da0fea614 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -166,7 +166,7 @@ pub extern "c" fn sendto( dest_addr: ?*const sockaddr, addrlen: socklen_t, ) isize; -pub extern "c" fn sendmsg(sockfd: fd_t, msg: *const std.x.os.Socket.Message, flags: c_int) isize; +pub extern "c" fn sendmsg(sockfd: fd_t, msg: *const std.x.os.Socket.Message, flags: u32) isize; pub extern "c" fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize; pub extern "c" fn recvfrom( @@ -177,7 +177,7 @@ pub extern "c" fn recvfrom( noalias src_addr: ?*sockaddr, noalias addrlen: ?*socklen_t, ) isize; -pub extern "c" fn recvmsg(sockfd: fd_t, msg: *std.x.os.Socket.Message, flags: c_int) isize; +pub extern "c" fn recvmsg(sockfd: fd_t, msg: *std.x.os.Socket.Message, flags: u32) isize; pub usingnamespace switch (builtin.os.tag) { .netbsd => struct { From 12650bcda4584828799d837388a7b728f7f650ec Mon Sep 17 00:00:00 2001 From: lithdew Date: Fri, 14 May 2021 15:42:22 +0900 Subject: [PATCH 04/11] x/os, x/tcp: fix Socket.Message init values and tcp test buf len --- lib/std/x/net/tcp.zig | 4 ++-- lib/std/x/os/socket.zig | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/x/net/tcp.zig b/lib/std/x/net/tcp.zig index abf1191dfd..05b0f50c10 100644 --- a/lib/std/x/net/tcp.zig +++ b/lib/std/x/net/tcp.zig @@ -405,14 +405,14 @@ test "tcp/client: read and write multiple vectors" { Buffer.from(message[message.len / 2 ..]), }), 0); - var buf: [message.len]u8 = undefined; + var buf: [message.len + 1]u8 = undefined; var msg = Socket.Message.fromBuffers(&[_]Buffer{ Buffer.from(buf[0 .. message.len / 2]), Buffer.from(buf[message.len / 2 ..]), }); _ = try client.readVectorized(&msg, 0); - try testing.expectEqualStrings(message, &buf); + try testing.expectEqualStrings(message, buf[0..message.len]); } test "tcp/listener: bind to unspecified ipv4 address" { diff --git a/lib/std/x/os/socket.zig b/lib/std/x/os/socket.zig index 05bd0499d1..1825d9f00c 100644 --- a/lib/std/x/os/socket.zig +++ b/lib/std/x/os/socket.zig @@ -212,9 +212,9 @@ pub const Socket = struct { name_len: c_uint = 0, buffers: usize = undefined, - buffers_len: c_int, + buffers_len: c_int = undefined, - control: usize = null, + control: usize = @ptrToInt(@as(?[*]u8, null)), control_len: c_uint = 0, flags: c_int = 0, From 9ba65592d64f53d886b00402659cfce775ea77d3 Mon Sep 17 00:00:00 2001 From: lithdew Date: Fri, 14 May 2021 15:50:09 +0900 Subject: [PATCH 05/11] os: have sendmsg, recvmsg flags be c_int --- lib/std/c.zig | 4 ++-- lib/std/os.zig | 2 +- lib/std/os/linux.zig | 4 ++-- lib/std/x/os/socket_posix.zig | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 2da0fea614..a91fee7274 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -166,7 +166,7 @@ pub extern "c" fn sendto( dest_addr: ?*const sockaddr, addrlen: socklen_t, ) isize; -pub extern "c" fn sendmsg(sockfd: fd_t, msg: *const std.x.os.Socket.Message, flags: u32) isize; +pub extern "c" fn sendmsg(sockfd: fd_t, msg: *const std.x.os.Socket.Message, flags: c_int) isize; pub extern "c" fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize; pub extern "c" fn recvfrom( @@ -177,7 +177,7 @@ pub extern "c" fn recvfrom( noalias src_addr: ?*sockaddr, noalias addrlen: ?*socklen_t, ) isize; -pub extern "c" fn recvmsg(sockfd: fd_t, msg: *std.x.os.Socket.Message, flags: u32) isize; +pub extern "c" fn recvmsg(sockfd: fd_t, msg: *std.x.os.Socket.Message, flags: c_int) isize; pub usingnamespace switch (builtin.os.tag) { .netbsd => struct { diff --git a/lib/std/os.zig b/lib/std/os.zig index 4ed0be8f62..c2c4c16bd7 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4998,7 +4998,7 @@ pub fn sendmsg( flags: u32, ) SendMsgError!usize { while (true) { - const rc = system.sendmsg(sockfd, &msg, flags); + const rc = system.sendmsg(sockfd, &msg, @intCast(c_int, flags)); if (builtin.os.tag == .windows) { if (rc == windows.ws2_32.SOCKET_ERROR) { switch (windows.ws2_32.WSAGetLastError()) { diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 0d6b87c15f..a3a8c8e431 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1004,7 +1004,7 @@ pub fn sendmsg(fd: i32, msg: *const msghdr_const, flags: u32) usize { if (native_arch == .i386) { return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags }); } - return syscall3(.sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags); + return syscall3(.sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags))); } pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize { @@ -1058,7 +1058,7 @@ pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { if (native_arch == .i386) { return socketcall(SC_recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags }); } - return syscall3(.recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags); + return syscall3(.recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags))); } pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { diff --git a/lib/std/x/os/socket_posix.zig b/lib/std/x/os/socket_posix.zig index b9fc21a40d..70c8b35b5f 100644 --- a/lib/std/x/os/socket_posix.zig +++ b/lib/std/x/os/socket_posix.zig @@ -87,7 +87,7 @@ pub fn Mixin(comptime Socket: type) type { /// read into the buffer provided. pub fn readVectorized(self: Socket, msg: *Socket.Message, flags: u32) !usize { while (true) { - const rc = os.system.recvmsg(self.fd, msg, flags); + const rc = os.system.recvmsg(self.fd, msg, @intCast(c_int, flags)); return switch (os.errno(rc)) { 0 => @intCast(usize, rc), os.EBADF => unreachable, // always a race condition From 7b6ec3e3548dadc597b6aa396d70ba4c6595da26 Mon Sep 17 00:00:00 2001 From: Kenta Iwasaki Date: Tue, 18 May 2021 17:23:59 +0900 Subject: [PATCH 06/11] x/os: {read, write}Vectorized() -> {read, write}Message() --- lib/std/json.zig | 5 +---- lib/std/x/net/tcp.zig | 12 ++++++------ lib/std/x/os/socket_posix.zig | 4 ++-- lib/std/x/os/socket_windows.zig | 4 ++-- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/std/json.zig b/lib/std/json.zig index 9fb77d501f..ed90efb791 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2111,10 +2111,7 @@ test "parse into struct with duplicate field" { const ballast = try testing.allocator.alloc(u64, 1); defer testing.allocator.free(ballast); - const options_first = ParseOptions{ - .allocator = testing.allocator, - .duplicate_field_behavior = .UseFirst, - }; + const options_first = ParseOptions{ .allocator = testing.allocator, .duplicate_field_behavior = .UseFirst }; const options_last = ParseOptions{ .allocator = testing.allocator, diff --git a/lib/std/x/net/tcp.zig b/lib/std/x/net/tcp.zig index 05b0f50c10..8740c17e61 100644 --- a/lib/std/x/net/tcp.zig +++ b/lib/std/x/net/tcp.zig @@ -145,15 +145,15 @@ pub const Client = struct { /// Writes multiple I/O vectors with a prepended message header to the socket /// with a set of flags specified. It returns the number of bytes that are /// written to the socket. - pub fn writeVectorized(self: Client, msg: Socket.Message, flags: u32) !usize { - return self.socket.writeVectorized(msg, flags); + pub fn writeMessage(self: Client, msg: Socket.Message, flags: u32) !usize { + return self.socket.writeMessage(msg, flags); } /// Read multiple I/O vectors with a prepended message header from the socket /// with a set of flags specified. It returns the number of bytes that were /// read into the buffer provided. - pub fn readVectorized(self: Client, msg: *Socket.Message, flags: u32) !usize { - return self.socket.readVectorized(msg, flags); + pub fn readMessage(self: Client, msg: *Socket.Message, flags: u32) !usize { + return self.socket.readMessage(msg, flags); } /// Query and return the latest cached error on the client's underlying socket. @@ -400,7 +400,7 @@ test "tcp/client: read and write multiple vectors" { defer conn.deinit(); const message = "hello world"; - _ = try conn.client.writeVectorized(Socket.Message.fromBuffers(&[_]Buffer{ + _ = try conn.client.writeMessage(Socket.Message.fromBuffers(&[_]Buffer{ Buffer.from(message[0 .. message.len / 2]), Buffer.from(message[message.len / 2 ..]), }), 0); @@ -410,7 +410,7 @@ test "tcp/client: read and write multiple vectors" { Buffer.from(buf[0 .. message.len / 2]), Buffer.from(buf[message.len / 2 ..]), }); - _ = try client.readVectorized(&msg, 0); + _ = try client.readMessage(&msg, 0); try testing.expectEqualStrings(message, buf[0..message.len]); } diff --git a/lib/std/x/os/socket_posix.zig b/lib/std/x/os/socket_posix.zig index 70c8b35b5f..ebaadcbd5d 100644 --- a/lib/std/x/os/socket_posix.zig +++ b/lib/std/x/os/socket_posix.zig @@ -78,14 +78,14 @@ pub fn Mixin(comptime Socket: type) type { /// Writes multiple I/O vectors with a prepended message header to the socket /// with a set of flags specified. It returns the number of bytes that are /// written to the socket. - pub fn writeVectorized(self: Socket, msg: Socket.Message, flags: u32) !usize { + pub fn writeMessage(self: Socket, msg: Socket.Message, flags: u32) !usize { return os.sendmsg(self.fd, msg, flags); } /// Read multiple I/O vectors with a prepended message header from the socket /// with a set of flags specified. It returns the number of bytes that were /// read into the buffer provided. - pub fn readVectorized(self: Socket, msg: *Socket.Message, flags: u32) !usize { + pub fn readMessage(self: Socket, msg: *Socket.Message, flags: u32) !usize { while (true) { const rc = os.system.recvmsg(self.fd, msg, @intCast(c_int, flags)); return switch (os.errno(rc)) { diff --git a/lib/std/x/os/socket_windows.zig b/lib/std/x/os/socket_windows.zig index ed1db4f62f..d479d8619a 100644 --- a/lib/std/x/os/socket_windows.zig +++ b/lib/std/x/os/socket_windows.zig @@ -254,7 +254,7 @@ pub fn Mixin(comptime Socket: type) type { /// Writes multiple I/O vectors with a prepended message header to the socket /// with a set of flags specified. It returns the number of bytes that are /// written to the socket. - pub fn writeVectorized(self: Socket, msg: Socket.Message, flags: u32) !usize { + pub fn writeMessage(self: Socket, msg: Socket.Message, flags: u32) !usize { const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSASENDMSG, self.fd, ws2_32.WSAID_WSASENDMSG); var num_bytes: u32 = undefined; @@ -291,7 +291,7 @@ pub fn Mixin(comptime Socket: type) type { /// Read multiple I/O vectors with a prepended message header from the socket /// with a set of flags specified. It returns the number of bytes that were /// read into the buffer provided. - pub fn readVectorized(self: Socket, msg: *Socket.Message, flags: u32) !usize { + pub fn readMessage(self: Socket, msg: *Socket.Message, flags: u32) !usize { const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSARECVMSG, self.fd, ws2_32.WSAID_WSARECVMSG); var num_bytes: u32 = undefined; From 21ec0158a1b76cb52014b5dc79fb35ba3449d226 Mon Sep 17 00:00:00 2001 From: Kenta Iwasaki Date: Tue, 18 May 2021 17:28:06 +0900 Subject: [PATCH 07/11] os: `sockaddr_storage` -> `std.x.os.Socket.Address.Native.Storage` --- lib/std/os/bits/darwin.zig | 7 ------- lib/std/os/bits/freebsd.zig | 8 -------- lib/std/os/bits/haiku.zig | 8 -------- lib/std/os/bits/linux.zig | 7 ------- lib/std/os/bits/netbsd.zig | 8 -------- lib/std/os/bits/openbsd.zig | 8 -------- lib/std/os/windows/ws2_32.zig | 7 ------- 7 files changed, 53 deletions(-) diff --git a/lib/std/os/bits/darwin.zig b/lib/std/os/bits/darwin.zig index 0c2d8dc01e..3f8599a08c 100644 --- a/lib/std/os/bits/darwin.zig +++ b/lib/std/os/bits/darwin.zig @@ -23,13 +23,6 @@ pub const sockaddr = extern struct { family: sa_family_t, data: [14]u8, }; -pub const sockaddr_storage = extern struct { - len: u8, - family: sa_family_t, - __pad1: [5]u8, - __align: i64, - __pad2: [112]u8, -}; pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig index cd32972b89..381ceb7b3a 100644 --- a/lib/std/os/bits/freebsd.zig +++ b/lib/std/os/bits/freebsd.zig @@ -160,14 +160,6 @@ pub const sockaddr = extern struct { data: [14]u8, }; -pub const sockaddr_storage = extern struct { - len: u8, - family: sa_family_t, - __pad1: [5]u8, - __align: i64, - __pad2: [112]u8, -}; - pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/bits/haiku.zig b/lib/std/os/bits/haiku.zig index b229a4744d..316f6d7993 100644 --- a/lib/std/os/bits/haiku.zig +++ b/lib/std/os/bits/haiku.zig @@ -193,14 +193,6 @@ pub const sockaddr = extern struct { data: [14]u8, }; -pub const sockaddr_storage = extern struct { - len: u8, - family: sa_family_t, - __pad1: [5]u8, - __align: i64, - __pad2: [112]u8, -}; - pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 5ecdcf24a3..c58b7cfa50 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1149,13 +1149,6 @@ pub const sockaddr = extern struct { data: [14]u8, }; -pub const sockaddr_storage = extern struct { - family: sa_family_t, - __pad1: [6]u8, - __align: i64, - __pad2: [112]u8, -}; - /// IPv4 socket address pub const sockaddr_in = extern struct { family: sa_family_t = AF_INET, diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig index 0a3ea0d807..a3d89620b7 100644 --- a/lib/std/os/bits/netbsd.zig +++ b/lib/std/os/bits/netbsd.zig @@ -180,14 +180,6 @@ pub const sockaddr = extern struct { data: [14]u8, }; -pub const sockaddr_storage = extern struct { - len: u8, - family: sa_family_t, - __pad1: [5]u8, - __align: i64, - __pad2: [112]u8, -}; - pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/bits/openbsd.zig b/lib/std/os/bits/openbsd.zig index 44fe9cc5f2..5335ebac22 100644 --- a/lib/std/os/bits/openbsd.zig +++ b/lib/std/os/bits/openbsd.zig @@ -200,14 +200,6 @@ pub const sockaddr = extern struct { data: [14]u8, }; -pub const sockaddr_storage = extern struct { - len: u8, - family: sa_family_t, - __pad1: [5]u8, - __align: i64, - __pad2: [112]u8, -}; - pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index 39f68bb234..219c32d928 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/lib/std/os/windows/ws2_32.zig @@ -1059,13 +1059,6 @@ pub const sockaddr = extern struct { data: [14]u8, }; -pub const sockaddr_storage = extern struct { - family: ADDRESS_FAMILY, - __pad1: [6]u8, - __align: i64, - __pad2: [112]u8, -}; - /// IPv4 socket address pub const sockaddr_in = extern struct { family: ADDRESS_FAMILY = AF_INET, From 278e5d398ea137a4c81bd00b1f40e50a16755684 Mon Sep 17 00:00:00 2001 From: Kenta Iwasaki Date: Wed, 19 May 2021 14:30:04 +0900 Subject: [PATCH 08/11] x: replace std.builtin with std.Target.current --- lib/std/os/linux.zig | 8 ++++---- lib/std/x/net/tcp.zig | 12 ++++++------ lib/std/x/os/io.zig | 8 ++++---- lib/std/x/os/net.zig | 4 ++-- lib/std/x/os/socket.zig | 21 +++++++++++---------- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index a3a8c8e431..9e7b38fe90 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1000,9 +1000,9 @@ pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noal return syscall5(.getsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } -pub fn sendmsg(fd: i32, msg: *const msghdr_const, flags: u32) usize { +pub fn sendmsg(fd: i32, msg: *const std.x.os.Socket.Message, flags: c_int) usize { if (native_arch == .i386) { - return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags }); + return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)) }); } return syscall3(.sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags))); } @@ -1054,9 +1054,9 @@ pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { return syscall3(.connect, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len); } -pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { +pub fn recvmsg(fd: i32, msg: *std.x.os.Socket.Message, flags: c_int) usize { if (native_arch == .i386) { - return socketcall(SC_recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags }); + return socketcall(SC_recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)) }); } return syscall3(.recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags))); } diff --git a/lib/std/x/net/tcp.zig b/lib/std/x/net/tcp.zig index 8740c17e61..25efbae673 100644 --- a/lib/std/x/net/tcp.zig +++ b/lib/std/x/net/tcp.zig @@ -12,8 +12,8 @@ const ip = std.x.net.ip; const fmt = std.fmt; const mem = std.mem; -const builtin = std.builtin; const testing = std.testing; +const native_os = std.Target.current.os; const IPv4 = std.x.os.IPv4; const IPv6 = std.x.os.IPv6; @@ -325,7 +325,7 @@ pub const Listener = struct { }; test "tcp: create client/listener pair" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; + if (native_os.tag == .wasi) return error.SkipZigTest; const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true }); defer listener.deinit(); @@ -349,7 +349,7 @@ test "tcp: create client/listener pair" { } test "tcp/client: 1ms read timeout" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; + if (native_os.tag == .wasi) return error.SkipZigTest; const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true }); defer listener.deinit(); @@ -377,7 +377,7 @@ test "tcp/client: 1ms read timeout" { } test "tcp/client: read and write multiple vectors" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; + if (native_os.tag == .wasi) return error.SkipZigTest; const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true }); defer listener.deinit(); @@ -416,7 +416,7 @@ test "tcp/client: read and write multiple vectors" { } test "tcp/listener: bind to unspecified ipv4 address" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; + if (native_os.tag == .wasi) return error.SkipZigTest; const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true }); defer listener.deinit(); @@ -429,7 +429,7 @@ test "tcp/listener: bind to unspecified ipv4 address" { } test "tcp/listener: bind to unspecified ipv6 address" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; + if (native_os.tag == .wasi) return error.SkipZigTest; const listener = try tcp.Listener.init(.ipv6, .{ .close_on_exec = true }); defer listener.deinit(); diff --git a/lib/std/x/os/io.zig b/lib/std/x/os/io.zig index 597f7d268c..1c0e0e9130 100644 --- a/lib/std/x/os/io.zig +++ b/lib/std/x/os/io.zig @@ -2,12 +2,12 @@ const std = @import("../../std.zig"); const os = std.os; const mem = std.mem; -const builtin = std.builtin; const testing = std.testing; +const native_os = std.Target.current.os; /// POSIX `iovec`, or Windows `WSABUF`. The difference between the two are the ordering /// of fields, alongside the length being represented as either a ULONG or a size_t. -pub const Buffer = if (builtin.os.tag == .windows) +pub const Buffer = if (native_os.tag == .windows) extern struct { len: c_ulong, ptr: usize, @@ -38,7 +38,7 @@ else } pub fn intoMutable(self: Buffer) []u8 { - return @intToptr([*]u8, self.ptr)[0..self.len]; + return @intToPtr([*]u8, self.ptr)[0..self.len]; } }; @@ -115,7 +115,7 @@ pub const Reactor = struct { }; test "reactor/linux: drive async tcp client/listener pair" { - if (builtin.os.tag != .linux) return error.SkipZigTest; + if (native_os.tag != .linux) return error.SkipZigTest; const ip = std.x.net.ip; const tcp = std.x.net.tcp; diff --git a/lib/std/x/os/net.zig b/lib/std/x/os/net.zig index f2f49ea62f..3d32019bb2 100644 --- a/lib/std/x/os/net.zig +++ b/lib/std/x/os/net.zig @@ -10,8 +10,8 @@ const os = std.os; const fmt = std.fmt; const mem = std.mem; const math = std.math; -const builtin = std.builtin; const testing = std.testing; +const native_os = std.Target.current.os; /// Resolves a network interface name into a scope/zone ID. It returns /// an error if either resolution fails, or if the interface name is @@ -20,7 +20,7 @@ pub fn resolveScopeID(name: []const u8) !u32 { if (comptime @hasDecl(os, "IFNAMESIZE")) { if (name.len >= os.IFNAMESIZE - 1) return error.NameTooLong; - if (comptime builtin.os.tag == .windows) { + if (comptime native_os.tag == .windows) { var interface_name: [os.IFNAMESIZE]u8 = undefined; mem.copy(u8, &interface_name, name); interface_name[name.len] = 0; diff --git a/lib/std/x/os/socket.zig b/lib/std/x/os/socket.zig index 1825d9f00c..61e47939f8 100644 --- a/lib/std/x/os/socket.zig +++ b/lib/std/x/os/socket.zig @@ -12,7 +12,8 @@ const fmt = std.fmt; const mem = std.mem; const time = std.time; const meta = std.meta; -const builtin = std.builtin; +const native_os = std.Target.current.os; +const native_endian = std.Target.current.cpu.arch.endian(); const Buffer = std.x.os.Buffer; @@ -35,7 +36,7 @@ pub const Socket = struct { /// the fields of a `Socket.Address`. pub const Address = union(enum) { pub const Native = struct { - pub const requires_prepended_length = builtin.os.getVersionRange() == .semver; + pub const requires_prepended_length = native_os.getVersionRange() == .semver; pub const Length = if (requires_prepended_length) u8 else [0]u8; pub const Family = if (requires_prepended_length) u8 else c_ushort; @@ -140,7 +141,7 @@ pub const Socket = struct { /// POSIX `msghdr`. Denotes a destination address, set of buffers, control data, and flags. Ported /// directly from musl. - pub const Message = if (builtin.os.isAtLeast(.windows, .vista) != null and builtin.os.isAtLeast(.windows, .vista).?) + pub const Message = if (native_os.isAtLeast(.windows, .vista) != null and native_os.isAtLeast(.windows, .vista).?) extern struct { name: usize = @ptrToInt(@as(?[*]u8, null)), name_len: c_int = 0, @@ -156,7 +157,7 @@ pub const Socket = struct { pub usingnamespace MessageMixin(Message); } - else if (builtin.os.tag == .windows) + else if (native_os.tag == .windows) extern struct { name: usize = @ptrToInt(@as(?[*]u8, null)), name_len: c_int = 0, @@ -172,7 +173,7 @@ pub const Socket = struct { pub usingnamespace MessageMixin(Message); } - else if (@sizeOf(usize) > 4 and builtin.endian == .Big) + else if (@sizeOf(usize) > 4 and native_endian == .Big) extern struct { name: usize = @ptrToInt(@as(?[*]u8, null)), name_len: c_uint = 0, @@ -189,7 +190,7 @@ pub const Socket = struct { pub usingnamespace MessageMixin(Message); } - else if (@sizeOf(usize) > 4 and builtin.endian == .Little) + else if (@sizeOf(usize) > 4 and native_endian == .Little) extern struct { name: usize = @ptrToInt(@as(?[*]u8, null)), name_len: c_uint = 0, @@ -241,7 +242,7 @@ pub const Socket = struct { } pub fn setControl(self: *Self, control: []const u8) void { - if (builtin.os.tag == .windows) { + if (native_os.tag == .windows) { self.control = Buffer.from(control); } else { self.control = @ptrToInt(control.ptr); @@ -262,7 +263,7 @@ pub const Socket = struct { } pub fn getControl(self: Self) []const u8 { - if (builtin.os.tag == .windows) { + if (native_os.tag == .windows) { return self.control.into(); } else { return @intToPtr([*]const u8, self.control)[0..@intCast(usize, self.control_len)]; @@ -281,7 +282,7 @@ pub const Socket = struct { /// short's on Windows, whereas glibc and musl denote the fields to be /// int's on every other platform. pub const Linger = extern struct { - pub const Field = switch (builtin.os.tag) { + pub const Field = switch (native_os.tag) { .windows => c_ushort, else => c_int, }; @@ -315,7 +316,7 @@ pub const Socket = struct { } /// Mix in socket syscalls depending on the platform we are compiling against. - pub usingnamespace switch (builtin.os.tag) { + pub usingnamespace switch (native_os.tag) { .windows => @import("socket_windows.zig"), else => @import("socket_posix.zig"), }.Mixin(Socket); From 6950e4c2943d7eee1868aff1bf9e24f6bb310f60 Mon Sep 17 00:00:00 2001 From: Kenta Iwasaki Date: Mon, 31 May 2021 15:57:48 +0900 Subject: [PATCH 09/11] x/os/net: remove unnecessary comptime prefix in resolveScopeID() --- lib/std/mem.zig | 2 +- lib/std/os/windows.zig | 2 +- lib/std/x/os/net.zig | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index f365ffe2e4..4cf879cf05 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -2201,7 +2201,7 @@ pub fn collapseRepeatsLen(comptime T: type, slice: []T, elem: T) usize { /// Collapse consecutive duplicate elements into one entry. pub fn collapseRepeats(comptime T: type, slice: []T, elem: T) []T { - return slice[0 .. collapseRepeatsLen(T, slice, elem)]; + return slice[0..collapseRepeatsLen(T, slice, elem)]; } fn testCollapseRepeats(str: []const u8, elem: u8, expected: []const u8) !void { diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 4bdbe64fbf..8e8351e446 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1844,7 +1844,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace { } fn getFullPathNameW(path: [*:0]const u16, out: []u16) !usize { - const result= kernel32.GetFullPathNameW(path, @intCast(u32, out.len), std.meta.assumeSentinel(out.ptr, 0), null); + const result = kernel32.GetFullPathNameW(path, @intCast(u32, out.len), std.meta.assumeSentinel(out.ptr, 0), null); if (result == 0) { switch (kernel32.GetLastError()) { else => |err| return unexpectedError(err), diff --git a/lib/std/x/os/net.zig b/lib/std/x/os/net.zig index 3d32019bb2..bbb2c105a7 100644 --- a/lib/std/x/os/net.zig +++ b/lib/std/x/os/net.zig @@ -17,10 +17,10 @@ const native_os = std.Target.current.os; /// an error if either resolution fails, or if the interface name is /// too long. pub fn resolveScopeID(name: []const u8) !u32 { - if (comptime @hasDecl(os, "IFNAMESIZE")) { + if (@hasDecl(os, "IFNAMESIZE")) { if (name.len >= os.IFNAMESIZE - 1) return error.NameTooLong; - if (comptime native_os.tag == .windows) { + if (native_os.tag == .windows) { var interface_name: [os.IFNAMESIZE]u8 = undefined; mem.copy(u8, &interface_name, name); interface_name[name.len] = 0; From aad8491dbd98052e97b395ea572b186482d3b38f Mon Sep 17 00:00:00 2001 From: Kenta Iwasaki Date: Mon, 31 May 2021 19:29:17 +0900 Subject: [PATCH 10/11] os: make msghdr, msghdr_const, and sockaddr_storage backwards-compatible `msghdr` and `msghdr_const` definitions have been added back the way they were in std.os. std.os.sendmsg has also been modified to accept a msghdr_const again to ensure backwards-compatibility with this PR. Underneath the hood, std.os.sendmsg will @ptrCast the provided msghdr_const into a std.x.os.Socket.Message. `sockaddr_storage` definitions have been added back the way they were in std.os, except that it now simply aliases std.x.os.Socket.Address.Native.Storage as all of std.x.os.Socket.Address.Native.Storage's fields are equivalent to the fields that were previously defined for std.x.os.bits.sockaddr_storage. std.x.os.Socket.sendMessage now no longer is a stub that aliases std.os.sendmsg, but instead calls and handles errors from std.os.system.sendmsg directly. Addresses feedback to urge backwards compatibility from @andrewrk. --- lib/std/os.zig | 4 +-- lib/std/os/bits/darwin.zig | 1 + lib/std/os/bits/dragonfly.zig | 11 +++++++ lib/std/os/bits/freebsd.zig | 48 +++++++++++++++++++++++++++++ lib/std/os/bits/haiku.zig | 48 +++++++++++++++++++++++++++++ lib/std/os/bits/linux.zig | 6 ++-- lib/std/os/bits/linux/arm-eabi.zig | 20 ++++++++++++ lib/std/os/bits/linux/arm64.zig | 24 +++++++++++++++ lib/std/os/bits/linux/i386.zig | 20 ++++++++++++ lib/std/os/bits/linux/powerpc.zig | 20 ++++++++++++ lib/std/os/bits/linux/powerpc64.zig | 20 ++++++++++++ lib/std/os/bits/linux/sparc64.zig | 20 ++++++++++++ lib/std/os/bits/linux/x86_64.zig | 24 +++++++++++++++ lib/std/os/bits/netbsd.zig | 48 +++++++++++++++++++++++++++++ lib/std/os/bits/openbsd.zig | 48 +++++++++++++++++++++++++++++ lib/std/os/windows/ws2_32.zig | 23 ++++++++++++++ lib/std/x/os/socket_posix.zig | 33 +++++++++++++++++++- 17 files changed, 413 insertions(+), 5 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index c2c4c16bd7..f411b4fdc1 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4994,11 +4994,11 @@ pub fn sendmsg( /// The file descriptor of the sending socket. sockfd: socket_t, /// Message header and iovecs - msg: std.x.os.Socket.Message, + msg: msghdr_const, flags: u32, ) SendMsgError!usize { while (true) { - const rc = system.sendmsg(sockfd, &msg, @intCast(c_int, flags)); + const rc = system.sendmsg(sockfd, @ptrCast(*const std.x.os.Socket.Message, &msg), @intCast(c_int, flags)); if (builtin.os.tag == .windows) { if (rc == windows.ws2_32.SOCKET_ERROR) { switch (windows.ws2_32.WSAGetLastError()) { diff --git a/lib/std/os/bits/darwin.zig b/lib/std/os/bits/darwin.zig index 3f8599a08c..3ec373bbbb 100644 --- a/lib/std/os/bits/darwin.zig +++ b/lib/std/os/bits/darwin.zig @@ -23,6 +23,7 @@ pub const sockaddr = extern struct { family: sa_family_t, data: [14]u8, }; +pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage; pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig index f8f89bd07a..88199aee7e 100644 --- a/lib/std/os/bits/dragonfly.zig +++ b/lib/std/os/bits/dragonfly.zig @@ -396,6 +396,8 @@ pub const sockaddr = extern struct { data: [14]u8, }; +pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage; + pub const Kevent = extern struct { ident: usize, filter: c_short, @@ -773,6 +775,15 @@ pub const cmsghdr = extern struct { cmsg_level: c_int, cmsg_type: c_int, }; +pub const msghdr = extern struct { + msg_name: ?*c_void, + msg_namelen: socklen_t, + msg_iov: [*c]iovec, + msg_iovlen: c_int, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: c_int, +}; pub const cmsgcred = extern struct { cmcred_pid: pid_t, cmcred_uid: uid_t, diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig index 381ceb7b3a..c3bcafaa65 100644 --- a/lib/std/os/bits/freebsd.zig +++ b/lib/std/os/bits/freebsd.zig @@ -82,6 +82,52 @@ pub const Flock = extern struct { __unused: [4]u8, }; +pub const msghdr = extern struct { + /// optional address + msg_name: ?*sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + /// optional address + msg_name: ?*const sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec_const, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + pub const libc_stat = extern struct { dev: dev_t, ino: ino_t, @@ -160,6 +206,8 @@ pub const sockaddr = extern struct { data: [14]u8, }; +pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage; + pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/bits/haiku.zig b/lib/std/os/bits/haiku.zig index 316f6d7993..bd677e22e8 100644 --- a/lib/std/os/bits/haiku.zig +++ b/lib/std/os/bits/haiku.zig @@ -73,6 +73,52 @@ pub const Flock = extern struct { __unused: [4]u8, }; +pub const msghdr = extern struct { + /// optional address + msg_name: ?*sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + /// optional address + msg_name: ?*const sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec_const, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + pub const off_t = i64; pub const ino_t = u64; @@ -193,6 +239,8 @@ pub const sockaddr = extern struct { data: [14]u8, }; +pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage; + pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index c58b7cfa50..c0e22e447f 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1149,6 +1149,8 @@ pub const sockaddr = extern struct { data: [14]u8, }; +pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage; + /// IPv4 socket address pub const sockaddr_in = extern struct { family: sa_family_t = AF_INET, @@ -1173,12 +1175,12 @@ pub const sockaddr_un = extern struct { }; pub const mmsghdr = extern struct { - msg_hdr: std.x.os.Socket.Message, + msg_hdr: msghdr, msg_len: u32, }; pub const mmsghdr_const = extern struct { - msg_hdr: std.x.os.Socket.Message, + msg_hdr: msghdr_const, msg_len: u32, }; diff --git a/lib/std/os/bits/linux/arm-eabi.zig b/lib/std/os/bits/linux/arm-eabi.zig index 6f596ab3c5..335ea074ec 100644 --- a/lib/std/os/bits/linux/arm-eabi.zig +++ b/lib/std/os/bits/linux/arm-eabi.zig @@ -526,6 +526,26 @@ pub const Flock = extern struct { __unused: [4]u8, }; +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + pub const blksize_t = i32; pub const nlink_t = u32; pub const time_t = isize; diff --git a/lib/std/os/bits/linux/arm64.zig b/lib/std/os/bits/linux/arm64.zig index 46126b69fd..e0771e7f66 100644 --- a/lib/std/os/bits/linux/arm64.zig +++ b/lib/std/os/bits/linux/arm64.zig @@ -395,6 +395,30 @@ pub const Flock = extern struct { __unused: [4]u8, }; +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: i32, + __pad1: i32 = 0, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t = 0, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + __pad1: i32 = 0, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t = 0, + msg_flags: i32, +}; + pub const blksize_t = i32; pub const nlink_t = u32; pub const time_t = isize; diff --git a/lib/std/os/bits/linux/i386.zig b/lib/std/os/bits/linux/i386.zig index 7e5f7935a3..f8dadb8a60 100644 --- a/lib/std/os/bits/linux/i386.zig +++ b/lib/std/os/bits/linux/i386.zig @@ -523,6 +523,26 @@ pub const Flock = extern struct { l_pid: pid_t, }; +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + pub const blksize_t = i32; pub const nlink_t = u32; pub const time_t = isize; diff --git a/lib/std/os/bits/linux/powerpc.zig b/lib/std/os/bits/linux/powerpc.zig index 908593ae90..96908cb714 100644 --- a/lib/std/os/bits/linux/powerpc.zig +++ b/lib/std/os/bits/linux/powerpc.zig @@ -515,6 +515,26 @@ pub const Flock = extern struct { l_pid: pid_t, }; +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: usize, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: usize, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + pub const blksize_t = i32; pub const nlink_t = u32; pub const time_t = isize; diff --git a/lib/std/os/bits/linux/powerpc64.zig b/lib/std/os/bits/linux/powerpc64.zig index d7c554fb86..52b9109247 100644 --- a/lib/std/os/bits/linux/powerpc64.zig +++ b/lib/std/os/bits/linux/powerpc64.zig @@ -491,6 +491,26 @@ pub const Flock = extern struct { __unused: [4]u8, }; +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: usize, + msg_control: ?*c_void, + msg_controllen: usize, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: usize, + msg_control: ?*c_void, + msg_controllen: usize, + msg_flags: i32, +}; + pub const blksize_t = i64; pub const nlink_t = u64; pub const time_t = i64; diff --git a/lib/std/os/bits/linux/sparc64.zig b/lib/std/os/bits/linux/sparc64.zig index e40fb994fd..5c67b745f0 100644 --- a/lib/std/os/bits/linux/sparc64.zig +++ b/lib/std/os/bits/linux/sparc64.zig @@ -465,6 +465,26 @@ pub const Flock = extern struct { l_pid: pid_t, }; +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: u64, + msg_control: ?*c_void, + msg_controllen: u64, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: u64, + msg_control: ?*c_void, + msg_controllen: u64, + msg_flags: i32, +}; + pub const off_t = i64; pub const ino_t = u64; pub const mode_t = u32; diff --git a/lib/std/os/bits/linux/x86_64.zig b/lib/std/os/bits/linux/x86_64.zig index b82c1469e9..30e5af384f 100644 --- a/lib/std/os/bits/linux/x86_64.zig +++ b/lib/std/os/bits/linux/x86_64.zig @@ -489,6 +489,30 @@ pub const Flock = extern struct { l_pid: pid_t, }; +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: i32, + __pad1: i32 = 0, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t = 0, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + __pad1: i32 = 0, + msg_control: ?*c_void, + msg_controllen: socklen_t, + __pad2: socklen_t = 0, + msg_flags: i32, +}; + pub const off_t = i64; pub const ino_t = u64; pub const dev_t = u64; diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig index a3d89620b7..168d0f1c48 100644 --- a/lib/std/os/bits/netbsd.zig +++ b/lib/std/os/bits/netbsd.zig @@ -108,6 +108,52 @@ pub const EAI = enum(c_int) { pub const EAI_MAX = 15; +pub const msghdr = extern struct { + /// optional address + msg_name: ?*sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + /// optional address + msg_name: ?*const sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec_const, + + /// # elements in msg_iov + msg_iovlen: i32, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: i32, +}; + pub const libc_stat = extern struct { dev: dev_t, mode: mode_t, @@ -180,6 +226,8 @@ pub const sockaddr = extern struct { data: [14]u8, }; +pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage; + pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/bits/openbsd.zig b/lib/std/os/bits/openbsd.zig index 5335ebac22..2ff81fae0d 100644 --- a/lib/std/os/bits/openbsd.zig +++ b/lib/std/os/bits/openbsd.zig @@ -124,6 +124,52 @@ pub const EAI = enum(c_int) { pub const EAI_MAX = 15; +pub const msghdr = extern struct { + /// optional address + msg_name: ?*sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec, + + /// # elements in msg_iov + msg_iovlen: c_uint, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: c_int, +}; + +pub const msghdr_const = extern struct { + /// optional address + msg_name: ?*const sockaddr, + + /// size of address + msg_namelen: socklen_t, + + /// scatter/gather array + msg_iov: [*]iovec_const, + + /// # elements in msg_iov + msg_iovlen: c_uint, + + /// ancillary data + msg_control: ?*c_void, + + /// ancillary data buffer len + msg_controllen: socklen_t, + + /// flags on received message + msg_flags: c_int, +}; + pub const libc_stat = extern struct { mode: mode_t, dev: dev_t, @@ -200,6 +246,8 @@ pub const sockaddr = extern struct { data: [14]u8, }; +pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage; + pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig index 219c32d928..1aa6daa3e6 100644 --- a/lib/std/os/windows/ws2_32.zig +++ b/lib/std/os/windows/ws2_32.zig @@ -1059,6 +1059,8 @@ pub const sockaddr = extern struct { data: [14]u8, }; +pub const sockaddr_storage = std.x.os.Socket.Address.Native.Storage; + /// IPv4 socket address pub const sockaddr_in = extern struct { family: ADDRESS_FAMILY = AF_INET, @@ -1087,6 +1089,27 @@ pub const WSABUF = extern struct { buf: [*]u8, }; +pub const msghdr = WSAMSG; +pub const msghdr_const = WSAMSG_const; + +pub const WSAMSG_const = extern struct { + name: *const sockaddr, + namelen: INT, + lpBuffers: [*]WSABUF, + dwBufferCount: DWORD, + Control: WSABUF, + dwFlags: DWORD, +}; + +pub const WSAMSG = extern struct { + name: *sockaddr, + namelen: INT, + lpBuffers: [*]WSABUF, + dwBufferCount: DWORD, + Control: WSABUF, + dwFlags: DWORD, +}; + pub const WSAPOLLFD = pollfd; pub const pollfd = extern struct { diff --git a/lib/std/x/os/socket_posix.zig b/lib/std/x/os/socket_posix.zig index ebaadcbd5d..5deff22317 100644 --- a/lib/std/x/os/socket_posix.zig +++ b/lib/std/x/os/socket_posix.zig @@ -79,7 +79,38 @@ pub fn Mixin(comptime Socket: type) type { /// with a set of flags specified. It returns the number of bytes that are /// written to the socket. pub fn writeMessage(self: Socket, msg: Socket.Message, flags: u32) !usize { - return os.sendmsg(self.fd, msg, flags); + while (true) { + const rc = os.system.sendmsg(self.fd, &msg, @intCast(c_int, flags)); + return switch (os.errno(rc)) { + 0 => return @intCast(usize, rc), + os.EACCES => error.AccessDenied, + os.EAGAIN => error.WouldBlock, + os.EALREADY => error.FastOpenAlreadyInProgress, + os.EBADF => unreachable, // always a race condition + os.ECONNRESET => error.ConnectionResetByPeer, + os.EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set. + os.EFAULT => unreachable, // An invalid user space address was specified for an argument. + os.EINTR => continue, + os.EINVAL => unreachable, // Invalid argument passed. + os.EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified + os.EMSGSIZE => error.MessageTooBig, + os.ENOBUFS => error.SystemResources, + os.ENOMEM => error.SystemResources, + os.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + os.EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. + os.EPIPE => error.BrokenPipe, + os.EAFNOSUPPORT => error.AddressFamilyNotSupported, + os.ELOOP => error.SymLinkLoop, + os.ENAMETOOLONG => error.NameTooLong, + os.ENOENT => error.FileNotFound, + os.ENOTDIR => error.NotDir, + os.EHOSTUNREACH => error.NetworkUnreachable, + os.ENETUNREACH => error.NetworkUnreachable, + os.ENOTCONN => error.SocketNotConnected, + os.ENETDOWN => error.NetworkSubsystemFailed, + else => |err| os.unexpectedErrno(err), + }; + } } /// Read multiple I/O vectors with a prepended message header from the socket From 4909aa1da43d227ad85b2fe03a58ef1a8c12b769 Mon Sep 17 00:00:00 2001 From: Kenta Iwasaki Date: Tue, 1 Jun 2021 18:35:13 +0900 Subject: [PATCH 11/11] os/bits: remove duplicate `sockaddr_storage` for dragonfly --- lib/std/atomic.zig | 6 ++--- lib/std/atomic/Atomic.zig | 44 +++++++++++++++++------------------ lib/std/mem.zig | 4 ++-- lib/std/os/bits/dragonfly.zig | 8 ------- lib/std/target.zig | 3 +-- 5 files changed, 28 insertions(+), 37 deletions(-) diff --git a/lib/std/atomic.zig b/lib/std/atomic.zig index 224b57d1d2..1944e5346b 100644 --- a/lib/std/atomic.zig +++ b/lib/std/atomic.zig @@ -19,7 +19,7 @@ test "std.atomic" { _ = @import("atomic/Atomic.zig"); } -pub fn fence(comptime ordering: Ordering) callconv(.Inline) void { +pub inline fn fence(comptime ordering: Ordering) void { switch (ordering) { .Acquire, .Release, .AcqRel, .SeqCst => { @fence(ordering); @@ -30,7 +30,7 @@ pub fn fence(comptime ordering: Ordering) callconv(.Inline) void { } } -pub fn compilerFence(comptime ordering: Ordering) callconv(.Inline) void { +pub inline fn compilerFence(comptime ordering: Ordering) void { switch (ordering) { .Acquire, .Release, .AcqRel, .SeqCst => asm volatile ("" ::: "memory"), else => @compileLog(ordering, " only applies to a given memory location"), @@ -45,7 +45,7 @@ test "fence/compilerFence" { } /// Signals to the processor that the caller is inside a busy-wait spin-loop. -pub fn spinLoopHint() callconv(.Inline) void { +pub inline fn spinLoopHint() void { const hint_instruction = switch (target.cpu.arch) { // No-op instruction that can hint to save (or share with a hardware-thread) pipelining/power resources // https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html diff --git a/lib/std/atomic/Atomic.zig b/lib/std/atomic/Atomic.zig index 5c3b865a6a..d137bc7552 100644 --- a/lib/std/atomic/Atomic.zig +++ b/lib/std/atomic/Atomic.zig @@ -48,38 +48,38 @@ pub fn Atomic(comptime T: type) type { }; } - pub fn swap(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { + pub inline fn swap(self: *Self, value: T, comptime ordering: Ordering) T { return self.rmw(.Xchg, value, ordering); } - pub fn compareAndSwap( + pub inline fn compareAndSwap( self: *Self, compare: T, exchange: T, comptime success: Ordering, comptime failure: Ordering, - ) callconv(.Inline) ?T { + ) ?T { return self.cmpxchg(true, compare, exchange, success, failure); } - pub fn tryCompareAndSwap( + pub inline fn tryCompareAndSwap( self: *Self, compare: T, exchange: T, comptime success: Ordering, comptime failure: Ordering, - ) callconv(.Inline) ?T { + ) ?T { return self.cmpxchg(false, compare, exchange, success, failure); } - fn cmpxchg( + inline fn cmpxchg( self: *Self, comptime is_strong: bool, compare: T, exchange: T, comptime success: Ordering, comptime failure: Ordering, - ) callconv(.Inline) ?T { + ) ?T { if (success == .Unordered or failure == .Unordered) { @compileError(@tagName(Ordering.Unordered) ++ " is only allowed on atomic loads and stores"); } @@ -103,12 +103,12 @@ pub fn Atomic(comptime T: type) type { }; } - fn rmw( + inline fn rmw( self: *Self, comptime op: std.builtin.AtomicRmwOp, value: T, comptime ordering: Ordering, - ) callconv(.Inline) T { + ) T { return @atomicRmw(T, &self.value, op, value, ordering); } @@ -117,37 +117,37 @@ pub fn Atomic(comptime T: type) type { } pub usingnamespace exportWhen(std.meta.trait.isNumber(T), struct { - pub fn fetchAdd(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { + pub inline fn fetchAdd(self: *Self, value: T, comptime ordering: Ordering) T { return self.rmw(.Add, value, ordering); } - pub fn fetchSub(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { + pub inline fn fetchSub(self: *Self, value: T, comptime ordering: Ordering) T { return self.rmw(.Sub, value, ordering); } - pub fn fetchMin(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { + pub inline fn fetchMin(self: *Self, value: T, comptime ordering: Ordering) T { return self.rmw(.Min, value, ordering); } - pub fn fetchMax(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { + pub inline fn fetchMax(self: *Self, value: T, comptime ordering: Ordering) T { return self.rmw(.Max, value, ordering); } }); pub usingnamespace exportWhen(std.meta.trait.isIntegral(T), struct { - pub fn fetchAnd(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { + pub inline fn fetchAnd(self: *Self, value: T, comptime ordering: Ordering) T { return self.rmw(.And, value, ordering); } - pub fn fetchNand(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { + pub inline fn fetchNand(self: *Self, value: T, comptime ordering: Ordering) T { return self.rmw(.Nand, value, ordering); } - pub fn fetchOr(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { + pub inline fn fetchOr(self: *Self, value: T, comptime ordering: Ordering) T { return self.rmw(.Or, value, ordering); } - pub fn fetchXor(self: *Self, value: T, comptime ordering: Ordering) callconv(.Inline) T { + pub inline fn fetchXor(self: *Self, value: T, comptime ordering: Ordering) T { return self.rmw(.Xor, value, ordering); } @@ -158,24 +158,24 @@ pub fn Atomic(comptime T: type) type { Toggle, }; - pub fn bitSet(self: *Self, bit: Bit, comptime ordering: Ordering) callconv(.Inline) u1 { + pub inline fn bitSet(self: *Self, bit: Bit, comptime ordering: Ordering) u1 { return bitRmw(self, .Set, bit, ordering); } - pub fn bitReset(self: *Self, bit: Bit, comptime ordering: Ordering) callconv(.Inline) u1 { + pub inline fn bitReset(self: *Self, bit: Bit, comptime ordering: Ordering) u1 { return bitRmw(self, .Reset, bit, ordering); } - pub fn bitToggle(self: *Self, bit: Bit, comptime ordering: Ordering) callconv(.Inline) u1 { + pub inline fn bitToggle(self: *Self, bit: Bit, comptime ordering: Ordering) u1 { return bitRmw(self, .Toggle, bit, ordering); } - fn bitRmw( + inline fn bitRmw( self: *Self, comptime op: BitRmwOp, bit: Bit, comptime ordering: Ordering, - ) callconv(.Inline) u1 { + ) u1 { // x86 supports dedicated bitwise instructions if (comptime target.cpu.arch.isX86() and @sizeOf(T) >= 2 and @sizeOf(T) <= 8) { const instruction = switch (op) { diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 4cf879cf05..fd9af71e4b 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -1171,7 +1171,7 @@ test "mem.indexOf" { test "mem.indexOf multibyte" { { // make haystack and needle long enough to trigger boyer-moore-horspool algorithm - const haystack = [1]u16{0} ** 100 ++ [_]u16 { 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff }; + const haystack = [1]u16{0} ** 100 ++ [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff }; const needle = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee }; try testing.expectEqual(indexOfPos(u16, &haystack, 0, &needle), 100); @@ -1184,7 +1184,7 @@ test "mem.indexOf multibyte" { { // make haystack and needle long enough to trigger boyer-moore-horspool algorithm - const haystack = [_]u16 { 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff } ++ [1]u16{0} ** 100; + const haystack = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee, 0x00ff } ++ [1]u16{0} ** 100; const needle = [_]u16{ 0xbbaa, 0xccbb, 0xddcc, 0xeedd, 0xffee }; try testing.expectEqual(lastIndexOf(u16, &haystack, &needle), 0); diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig index 88199aee7e..5c3ad305da 100644 --- a/lib/std/os/bits/dragonfly.zig +++ b/lib/std/os/bits/dragonfly.zig @@ -696,14 +696,6 @@ pub const in_port_t = u16; pub const sa_family_t = u8; pub const socklen_t = u32; -pub const sockaddr_storage = extern struct { - ss_len: u8, - ss_family: sa_family_t, - __ss_pad1: [5]u8, - __ss_align: i64, - __ss_pad2: [112]u8, -}; - pub const sockaddr_in = extern struct { len: u8 = @sizeOf(sockaddr_in), family: sa_family_t = AF_INET, diff --git a/lib/std/target.zig b/lib/std/target.zig index 692d29b1c7..9a2be37ee8 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -500,8 +500,7 @@ pub const Target = struct { .haiku, .windows, => return .gnu, - .uefi, - => return .msvc, + .uefi => return .msvc, .linux, .wasi, .emscripten,