authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-02 17:36:28-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-02 17:36:28-04:00
log8a8beefa36da8257a328a86340d0d60e1bd569a6
tree5bd6f42f9338830975c02fe102bdcf1213944450
parent07bee9da42977420916ca0f1044b47fab4a694dc

solve the problem with Darwin shims in std.os instead

* implement SOCK_NONBLOCK and SOCK_CLOEXEC Darwin shims in std.os * revert changes to std.net * remove os.accept and rename os.accept4 to os.accept

3 files changed, 80 insertions(+), 107 deletions(-)

lib/std/net.zig+15-61
...@@ -358,44 +358,13 @@ pub const Address = extern union {...@@ -358,44 +358,13 @@ pub const Address = extern union {
358 }358 }
359};359};
360360
361fn getUnixSocketInitFlags() u16 {
362 comptime {
363 var flags = 0;
364 switch (builtin.os.tag) {
365 .linux, .freebsd, .netbsd, .dragonfly => {
366 flags |= os.SOCK_CLOEXEC;
367 flags |= if (std.io.is_async) os.SOCK_NONBLOCK else 0;
368 },
369 else => {},
370 }
371
372 return flags;
373 }
374}
375
376// These are primarily needed for UNIX-based platforms without
377// SOCK_CLOEXEC and SOCK_NONBLOCK flags when creating sockets
378// or accepting connections
379fn setUnixSocketFlags(sock: os.fd_t) os.FcntlError!void {
380 var fdflags = try os.fcntl(sock, os.F_GETFD, 0);
381 fdflags |= os.FD_CLOEXEC;
382 _ = try os.fcntl(sock, os.F_SETFD, fdflags);
383
384 if (std.io.is_async) {
385 var flflags = try os.fcntl(sock, os.F_GETFL, 0);
386 flflags |= os.O_NONBLOCK;
387 _ = try os.fcntl(sock, os.F_SETFL, fdflags);
388 }
389}
390
391pub fn connectUnixSocket(path: []const u8) !fs.File {361pub fn connectUnixSocket(path: []const u8) !fs.File {
392 const flags = os.SOCK_STREAM | getUnixSocketInitFlags();362 const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
393 const sockfd = try os.socket(os.AF_UNIX, flags, 0);363 const sockfd = try os.socket(
394364 os.AF_UNIX,
395 if (comptime builtin.os.tag.isDarwin()) {365 os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block,
396 try setUnixSocketFlags(sockfd);366 0,
397 }367 );
398
399 errdefer os.close(sockfd);368 errdefer os.close(sockfd);
400369
401 var addr = try std.net.Address.initUnix(path);370 var addr = try std.net.Address.initUnix(path);
...@@ -437,13 +406,9 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16)...@@ -437,13 +406,9 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16)
437}406}
438407
439pub fn tcpConnectToAddress(address: Address) !fs.File {408pub fn tcpConnectToAddress(address: Address) !fs.File {
440 const sock_flags = os.SOCK_STREAM | getUnixSocketInitFlags();409 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
410 const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
441 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);411 const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP);
442
443 if (comptime builtin.os.tag.isDarwin()) {
444 try setUnixSocketFlags(sockfd);
445 }
446
447 errdefer os.close(sockfd);412 errdefer os.close(sockfd);
448 try os.connect(sockfd, &address.any, address.getOsSockLen());413 try os.connect(sockfd, &address.any, address.getOsSockLen());
449414
...@@ -1347,14 +1312,11 @@ pub const StreamServer = struct {...@@ -1347,14 +1312,11 @@ pub const StreamServer = struct {
1347 }1312 }
13481313
1349 pub fn listen(self: *StreamServer, address: Address) !void {1314 pub fn listen(self: *StreamServer, address: Address) !void {
1350 const flags = os.SOCK_STREAM | getUnixSocketInitFlags();1315 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
1316 const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock;
1351 const proto = if (address.any.family == os.AF_UNIX) @as(u32, 0) else os.IPPROTO_TCP;1317 const proto = if (address.any.family == os.AF_UNIX) @as(u32, 0) else os.IPPROTO_TCP;
1352 const sockfd = try os.socket(address.any.family, flags, proto);
1353
1354 if (comptime builtin.os.tag.isDarwin()) {
1355 try setUnixSocketFlags(sockfd);
1356 }
13571318
1319 const sockfd = try os.socket(address.any.family, sock_flags, proto);
1358 self.sockfd = sockfd;1320 self.sockfd = sockfd;
1359 errdefer {1321 errdefer {
1360 os.close(sockfd);1322 os.close(sockfd);
...@@ -1412,20 +1374,12 @@ pub const StreamServer = struct {...@@ -1412,20 +1374,12 @@ pub const StreamServer = struct {
1412 };1374 };
14131375
1414 /// If this function succeeds, the returned `Connection` is a caller-managed resource.1376 /// If this function succeeds, the returned `Connection` is a caller-managed resource.
1415 pub fn accept(self: *StreamServer) !Connection {1377 pub fn accept(self: *StreamServer) AcceptError!Connection {
1378 const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
1379 const accept_flags = nonblock | os.SOCK_CLOEXEC;
1416 var accepted_addr: Address = undefined;1380 var accepted_addr: Address = undefined;
1417 var adr_len: os.socklen_t = @sizeOf(Address);1381 var adr_len: os.socklen_t = @sizeOf(Address);
1418 var _accept: os.AcceptError!os.fd_t = undefined;1382 if (os.accept(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| {
1419
1420 if (comptime builtin.os.tag.isDarwin()) {
1421 try setUnixSocketFlags(self.sockfd.?);
1422 _accept = os.accept(self.sockfd.?, &accepted_addr.any, &adr_len);
1423 } else {
1424 const flags = getUnixSocketInitFlags();
1425 _accept = os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, flags);
1426 }
1427
1428 if (_accept) |fd| {
1429 return Connection{1383 return Connection{
1430 .file = fs.File{ .handle = fd },1384 .file = fs.File{ .handle = fd },
1431 .address = accepted_addr,1385 .address = accepted_addr,
lib/std/os.zig+56-46
...@@ -2159,9 +2159,20 @@ pub const SocketError = error{...@@ -2159,9 +2159,20 @@ pub const SocketError = error{
2159} || UnexpectedError;2159} || UnexpectedError;
21602160
2161pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t {2161pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t {
2162 const have_sock_flags = comptime !std.Target.current.isDarwin();
2163 const filtered_sock_type = if (!have_sock_flags)
2164 socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC)
2165 else
2166 socket_type;
2162 const rc = system.socket(domain, socket_type, protocol);2167 const rc = system.socket(domain, socket_type, protocol);
2163 switch (errno(rc)) {2168 switch (errno(rc)) {
2164 0 => return @intCast(fd_t, rc),2169 0 => {
2170 const fd = @intCast(fd_t, rc);
2171 if (!have_sock_flags and filtered_sock_type != socket_type) {
2172 try setSockFlags(fd, socket_type);
2173 }
2174 return fd;
2175 },
2165 EACCES => return error.PermissionDenied,2176 EACCES => return error.PermissionDenied,
2166 EAFNOSUPPORT => return error.AddressFamilyNotSupported,2177 EAFNOSUPPORT => return error.AddressFamilyNotSupported,
2167 EINVAL => return error.ProtocolFamilyNotAvailable,2178 EINVAL => return error.ProtocolFamilyNotAvailable,
...@@ -2284,7 +2295,7 @@ pub const AcceptError = error{...@@ -2284,7 +2295,7 @@ pub const AcceptError = error{
2284/// Accept a connection on a socket.2295/// Accept a connection on a socket.
2285/// If the application has a global event loop enabled, EAGAIN is handled2296/// If the application has a global event loop enabled, EAGAIN is handled
2286/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.2297/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
2287pub fn accept4(2298pub fn accept(
2288 /// This argument is a socket that has been created with `socket`, bound to a local address2299 /// This argument is a socket that has been created with `socket`, bound to a local address
2289 /// with `bind`, and is listening for connections after a `listen`.2300 /// with `bind`, and is listening for connections after a `listen`.
2290 sockfd: fd_t,2301 sockfd: fd_t,
...@@ -2300,8 +2311,7 @@ pub fn accept4(...@@ -2300,8 +2311,7 @@ pub fn accept4(
2300 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`2311 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`
2301 /// will return a value greater than was supplied to the call.2312 /// will return a value greater than was supplied to the call.
2302 addr_size: *socklen_t,2313 addr_size: *socklen_t,
2303 /// If flags is 0, then `accept4` is the same as `accept`. The following values can be bitwise2314 /// The following values can be bitwise ORed in flags to obtain different behavior:
2304 /// ORed in flags to obtain different behavior:
2305 /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`)2315 /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`)
2306 /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve2316 /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve
2307 /// the same result.2317 /// the same result.
...@@ -2309,52 +2319,23 @@ pub fn accept4(...@@ -2309,52 +2319,23 @@ pub fn accept4(
2309 /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.2319 /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
2310 flags: u32,2320 flags: u32,
2311) AcceptError!fd_t {2321) AcceptError!fd_t {
2312 if (comptime builtin.os.tag.isDarwin()) {2322 const have_accept4 = comptime !std.Target.current.isDarwin();
2313 @compileError("accept4 not available for target Darwin, use accept");2323 assert(0 == (flags & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC))); // Unsupported flag(s)
2314 }
23152324
2316 return try _accept(sockfd, addr, addr_size, flags);
2317}
2318
2319/// Accept a connection on a socket.
2320/// If the application has a global event loop enabled, EAGAIN is handled
2321/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
2322pub fn accept(
2323 /// This argument is a socket that has been created with `socket`, bound to a local address
2324 /// with `bind`, and is listening for connections after a `listen`.
2325 sockfd: fd_t,
2326 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the
2327 /// address of the peer socket, as known to the communications layer. The exact format of the
2328 /// address returned addr is determined by the socket's address family (see `socket` and the
2329 /// respective protocol man pages).
2330 addr: *sockaddr,
2331 /// This argument is a value-result argument: the caller must initialize it to contain the
2332 /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
2333 /// of the peer address.
2334 ///
2335 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`
2336 /// will return a value greater than was supplied to the call.
2337 addr_size: *socklen_t,
2338) AcceptError!fd_t {
2339 return try _accept(sockfd, addr, addr_size, 0);
2340}
2341
2342fn _accept(sockfd: fd_t, addr: *sockaddr, addr_size: *socklen_t, flags: u32) AcceptError!fd_t {
2343 while (true) {2325 while (true) {
2344 const rc = func: {2326 const rc = if (have_accept4)
2345 switch (comptime builtin.os.tag) {2327 system.accept4(sockfd, addr, addr_size, flags)
2346 .linux, .freebsd, .netbsd, .dragonfly => 2328 else
2347 break :func system.accept4(sockfd, addr, addr_size, flags),2329 system.accept(sockfd, addr, addr_size);
2348 .ios, .macosx, .watchos, .tvos => {
2349 assert(flags == 0);
2350 break :func system.accept(sockfd, addr, addr_size);
2351 },
2352 else => @compileError("accept not available for target"),
2353 }
2354 };
23552330
2356 switch (errno(rc)) {2331 switch (errno(rc)) {
2357 0 => return @intCast(fd_t, rc),2332 0 => {
2333 const fd = @intCast(fd_t, rc);
2334 if (!have_accept4 and flags != 0) {
2335 try setSockFlags(fd, flags);
2336 }
2337 return fd;
2338 },
2358 EINTR => continue,2339 EINTR => continue,
2359 EAGAIN => if (std.event.Loop.instance) |loop| {2340 EAGAIN => if (std.event.Loop.instance) |loop| {
2360 loop.waitUntilFdReadable(sockfd);2341 loop.waitUntilFdReadable(sockfd);
...@@ -3285,6 +3266,35 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {...@@ -3285,6 +3266,35 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
3285 }3266 }
3286}3267}
32873268
3269fn setSockFlags(fd: fd_t, flags: u32) !void {
3270 {
3271 var fd_flags = fcntl(fd, F_GETFD, 0) catch |err| switch (err) {
3272 error.FileBusy => unreachable,
3273 error.Locked => unreachable,
3274 else => |e| return e,
3275 };
3276 if ((flags & SOCK_NONBLOCK) != 0) fd_flags |= FD_CLOEXEC;
3277 _ = fcntl(fd, F_SETFD, fd_flags) catch |err| switch (err) {
3278 error.FileBusy => unreachable,
3279 error.Locked => unreachable,
3280 else => |e| return e,
3281 };
3282 }
3283 {
3284 var fl_flags = fcntl(fd, F_GETFL, 0) catch |err| switch (err) {
3285 error.FileBusy => unreachable,
3286 error.Locked => unreachable,
3287 else => |e| return e,
3288 };
3289 if ((flags & SOCK_CLOEXEC) != 0) fl_flags |= O_NONBLOCK;
3290 _ = fcntl(fd, F_SETFL, fl_flags) catch |err| switch (err) {
3291 error.FileBusy => unreachable,
3292 error.Locked => unreachable,
3293 else => |e| return e,
3294 };
3295 }
3296}
3297
3288pub const FlockError = error{3298pub const FlockError = error{
3289 WouldBlock,3299 WouldBlock,
32903300
lib/std/os/bits/darwin.zig+9
...@@ -764,6 +764,15 @@ pub const SOCK_RDM = 4;...@@ -764,6 +764,15 @@ pub const SOCK_RDM = 4;
764pub const SOCK_SEQPACKET = 5;764pub const SOCK_SEQPACKET = 5;
765pub const SOCK_MAXADDRLEN = 255;765pub const SOCK_MAXADDRLEN = 255;
766766
767/// Not actually supported by Darwin, but Zig supplies a shim.
768/// This numerical value is not ABI-stable. It need only not conflict
769/// with any other "SOCK_" bits.
770pub const SOCK_CLOEXEC = 1 << 15;
771/// Not actually supported by Darwin, but Zig supplies a shim.
772/// This numerical value is not ABI-stable. It need only not conflict
773/// with any other "SOCK_" bits.
774pub const SOCK_NONBLOCK = 1 << 16;
775
767pub const IPPROTO_ICMP = 1;776pub const IPPROTO_ICMP = 1;
768pub const IPPROTO_ICMPV6 = 58;777pub const IPPROTO_ICMPV6 = 58;
769pub const IPPROTO_TCP = 6;778pub const IPPROTO_TCP = 6;