diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index c547f5036591..c3bf2495ff01 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -720,6 +720,50 @@ pub const Loop = struct { } } + /// ------- I/0 APIs ------- + pub fn accept( + self: *Loop, + /// This argument is a socket that has been created with `socket`, bound to a local address + /// with `bind`, and is listening for connections after a `listen`. + sockfd: os.fd_t, + /// This argument is a pointer to a sockaddr structure. This structure is filled in with the + /// address of the peer socket, as known to the communications layer. The exact format of the + /// address returned addr is determined by the socket's address family (see `socket` and the + /// respective protocol man pages). + addr: *os.sockaddr, + /// This argument is a value-result argument: the caller must initialize it to contain the + /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size + /// of the peer address. + /// + /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size` + /// will return a value greater than was supplied to the call. + addr_size: *os.socklen_t, + /// The following values can be bitwise ORed in flags to obtain different behavior: + /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the + /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful. + flags: u32, + ) os.AcceptError!os.fd_t { + while (true) { + return os.accept(sockfd, addr, addr_size, flags | os.SOCK_NONBLOCK) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdReadable(sockfd); + continue; + }, + else => return err, + }; + } + } + + pub fn connect(self: *Loop, sockfd: os.socket_t, sock_addr: *const os.sockaddr, len: os.socklen_t) os.ConnectError!void { + os.connect(sockfd, sock_addr, len) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdWritable(sockfd); + return os.getsockoptError(sockfd); + }, + else => return err, + }; + } + /// Performs an async `os.open` using a separate thread. pub fn openZ(self: *Loop, file_path: [*:0]const u8, flags: u32, mode: os.mode_t) os.OpenError!os.fd_t { var req_node = Request.Node{ @@ -778,152 +822,309 @@ pub const Loop = struct { /// Performs an async `os.read` using a separate thread. /// `fd` must block and not return EAGAIN. - pub fn read(self: *Loop, fd: os.fd_t, buf: []u8) os.ReadError!usize { - var req_node = Request.Node{ - .data = .{ - .msg = .{ - .read = .{ - .fd = fd, - .buf = buf, - .result = undefined, + pub fn read(self: *Loop, fd: os.fd_t, buf: []u8, simulate_evented: bool) os.ReadError!usize { + if (simulate_evented) { + var req_node = Request.Node{ + .data = .{ + .msg = .{ + .read = .{ + .fd = fd, + .buf = buf, + .result = undefined, + }, }, + .finish = .{ .TickNode = .{ .data = @frame() } }, }, - .finish = .{ .TickNode = .{ .data = @frame() } }, - }, - }; - suspend { - self.posixFsRequest(&req_node); + }; + suspend { + self.posixFsRequest(&req_node); + } + return req_node.data.msg.read.result; + } else { + while (true) { + return os.read(fd, buf) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdReadable(fd); + continue; + }, + else => return err, + }; + } } - return req_node.data.msg.read.result; } /// Performs an async `os.readv` using a separate thread. /// `fd` must block and not return EAGAIN. - pub fn readv(self: *Loop, fd: os.fd_t, iov: []const os.iovec) os.ReadError!usize { - var req_node = Request.Node{ - .data = .{ - .msg = .{ - .readv = .{ - .fd = fd, - .iov = iov, - .result = undefined, + pub fn readv(self: *Loop, fd: os.fd_t, iov: []const os.iovec, simulate_evented: bool) os.ReadError!usize { + if (simulate_evented) { + var req_node = Request.Node{ + .data = .{ + .msg = .{ + .readv = .{ + .fd = fd, + .iov = iov, + .result = undefined, + }, }, + .finish = .{ .TickNode = .{ .data = @frame() } }, }, - .finish = .{ .TickNode = .{ .data = @frame() } }, - }, - }; - suspend { - self.posixFsRequest(&req_node); + }; + suspend { + self.posixFsRequest(&req_node); + } + return req_node.data.msg.readv.result; + } else { + while (true) { + return os.readv(fd, iov) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdReadable(fd); + continue; + }, + else => return err, + }; + } } - return req_node.data.msg.readv.result; } /// Performs an async `os.pread` using a separate thread. /// `fd` must block and not return EAGAIN. - pub fn pread(self: *Loop, fd: os.fd_t, buf: []u8, offset: u64) os.PReadError!usize { - var req_node = Request.Node{ - .data = .{ - .msg = .{ - .pread = .{ - .fd = fd, - .buf = buf, - .offset = offset, - .result = undefined, + pub fn pread(self: *Loop, fd: os.fd_t, buf: []u8, offset: u64, simulate_evented: bool) os.PReadError!usize { + if (simulate_evented) { + var req_node = Request.Node{ + .data = .{ + .msg = .{ + .pread = .{ + .fd = fd, + .buf = buf, + .offset = offset, + .result = undefined, + }, }, + .finish = .{ .TickNode = .{ .data = @frame() } }, }, - .finish = .{ .TickNode = .{ .data = @frame() } }, - }, - }; - suspend { - self.posixFsRequest(&req_node); + }; + suspend { + self.posixFsRequest(&req_node); + } + return req_node.data.msg.pread.result; + } else { + while (true) { + return os.pread(fd, buf, offset) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdReadable(fd); + continue; + }, + else => return err, + }; + } } - return req_node.data.msg.pread.result; } /// Performs an async `os.preadv` using a separate thread. /// `fd` must block and not return EAGAIN. - pub fn preadv(self: *Loop, fd: os.fd_t, iov: []const os.iovec, offset: u64) os.ReadError!usize { - var req_node = Request.Node{ - .data = .{ - .msg = .{ - .preadv = .{ - .fd = fd, - .iov = iov, - .offset = offset, - .result = undefined, + pub fn preadv(self: *Loop, fd: os.fd_t, iov: []const os.iovec, offset: u64, simulate_evented: bool) os.ReadError!usize { + if (simulate_evented) { + var req_node = Request.Node{ + .data = .{ + .msg = .{ + .preadv = .{ + .fd = fd, + .iov = iov, + .offset = offset, + .result = undefined, + }, }, + .finish = .{ .TickNode = .{ .data = @frame() } }, }, - .finish = .{ .TickNode = .{ .data = @frame() } }, - }, - }; - suspend { - self.posixFsRequest(&req_node); + }; + suspend { + self.posixFsRequest(&req_node); + } + return req_node.data.msg.preadv.result; + } else { + while (true) { + return os.preadv(fd, iov, offset) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdReadable(fd); + continue; + }, + else => return err, + }; + } } - return req_node.data.msg.preadv.result; } /// Performs an async `os.write` using a separate thread. /// `fd` must block and not return EAGAIN. - pub fn write(self: *Loop, fd: os.fd_t, bytes: []const u8) os.WriteError!usize { - var req_node = Request.Node{ - .data = .{ - .msg = .{ - .write = .{ - .fd = fd, - .bytes = bytes, - .result = undefined, + pub fn write(self: *Loop, fd: os.fd_t, bytes: []const u8, simulate_evented: bool) os.WriteError!usize { + if (simulate_evented) { + var req_node = Request.Node{ + .data = .{ + .msg = .{ + .write = .{ + .fd = fd, + .bytes = bytes, + .result = undefined, + }, }, + .finish = .{ .TickNode = .{ .data = @frame() } }, }, - .finish = .{ .TickNode = .{ .data = @frame() } }, - }, - }; - suspend { - self.posixFsRequest(&req_node); + }; + suspend { + self.posixFsRequest(&req_node); + } + return req_node.data.msg.write.result; + } else { + while (true) { + return os.write(fd, bytes) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdWritable(fd); + continue; + }, + else => return err, + }; + } } - return req_node.data.msg.write.result; } /// Performs an async `os.writev` using a separate thread. /// `fd` must block and not return EAGAIN. - pub fn writev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const) os.WriteError!usize { - var req_node = Request.Node{ - .data = .{ - .msg = .{ - .writev = .{ - .fd = fd, - .iov = iov, - .result = undefined, + pub fn writev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const, simulate_evented: bool) os.WriteError!usize { + if (simulate_evented) { + var req_node = Request.Node{ + .data = .{ + .msg = .{ + .writev = .{ + .fd = fd, + .iov = iov, + .result = undefined, + }, }, + .finish = .{ .TickNode = .{ .data = @frame() } }, }, - .finish = .{ .TickNode = .{ .data = @frame() } }, - }, - }; - suspend { - self.posixFsRequest(&req_node); + }; + suspend { + self.posixFsRequest(&req_node); + } + return req_node.data.msg.writev.result; + } else { + while (true) { + return os.writev(fd, iov) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdWritable(fd); + continue; + }, + else => return err, + }; + } + } + } + + /// Performs an async `os.pwrite` using a separate thread. + /// `fd` must block and not return EAGAIN. + pub fn pwrite(self: *Loop, fd: os.fd_t, bytes: []const u8, offset: u64, simulate_evented: bool) os.PerformsWriteError!usize { + if (simulate_evented) { + var req_node = Request.Node{ + .data = .{ + .msg = .{ + .pwrite = .{ + .fd = fd, + .bytes = bytes, + .offset = offset, + .result = undefined, + }, + }, + .finish = .{ .TickNode = .{ .data = @frame() } }, + }, + }; + suspend { + self.posixFsRequest(&req_node); + } + return req_node.data.msg.pwrite.result; + } else { + while (true) { + return os.pwrite(fd, bytes, offset) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdWritable(fd); + continue; + }, + else => return err, + }; + } } - return req_node.data.msg.writev.result; } /// Performs an async `os.pwritev` using a separate thread. /// `fd` must block and not return EAGAIN. - pub fn pwritev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const, offset: u64) os.WriteError!usize { - var req_node = Request.Node{ - .data = .{ - .msg = .{ - .pwritev = .{ - .fd = fd, - .iov = iov, - .offset = offset, - .result = undefined, + pub fn pwritev(self: *Loop, fd: os.fd_t, iov: []const os.iovec_const, offset: u64, simulate_evented: bool) os.PWriteError!usize { + if (simulate_evented) { + var req_node = Request.Node{ + .data = .{ + .msg = .{ + .pwritev = .{ + .fd = fd, + .iov = iov, + .offset = offset, + .result = undefined, + }, }, + .finish = .{ .TickNode = .{ .data = @frame() } }, }, - .finish = .{ .TickNode = .{ .data = @frame() } }, - }, - }; - suspend { - self.posixFsRequest(&req_node); + }; + suspend { + self.posixFsRequest(&req_node); + } + return req_node.data.msg.pwritev.result; + } else { + while (true) { + return os.pwritev(fd, iov, offset) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdWritable(fd); + continue; + }, + else => return err, + }; + } + } + } + + pub fn sendto( + self: *Loop, + /// The file descriptor of the sending socket. + sockfd: os.fd_t, + /// Message to send. + buf: []const u8, + flags: u32, + dest_addr: ?*const os.sockaddr, + addrlen: os.socklen_t, + ) os.SendError!usize { + while (true) { + return os.sendto(sockfd, buf, flags, dest_addr, addrlen) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdWritable(sockfd); + continue; + }, + else => return err, + }; + } + } + + pub fn recvfrom( + sockfd: os.fd_t, + buf: []u8, + flags: u32, + src_addr: ?*os.sockaddr, + addrlen: ?*os.socklen_t, + ) os.RecvFromError!usize { + while (true) { + return os.recvfrom(sockfd, buf, flags, src_addr, addrlen) catch |err| switch (err) { + error.WouldBlock => { + self.waitUntilFdReadable(sockfd); + continue; + }, + else => return err, + }; } - return req_node.data.msg.pwritev.result; } /// Performs an async `os.faccessatZ` using a separate thread. @@ -1078,6 +1279,9 @@ pub const Loop = struct { .writev => |*msg| { msg.result = os.writev(msg.fd, msg.iov); }, + .pwrite => |*msg| { + msg.result = os.pwrite(msg.fd, msg.bytes, msg.offset); + }, .pwritev => |*msg| { msg.result = os.pwritev(msg.fd, msg.iov, msg.offset); }, @@ -1147,6 +1351,7 @@ pub const Loop = struct { readv: ReadV, write: Write, writev: WriteV, + pwrite: PWrite, pwritev: PWriteV, pread: PRead, preadv: PReadV, @@ -1190,6 +1395,15 @@ pub const Loop = struct { pub const Error = os.WriteError; }; + pub const PWrite = struct { + fd: os.fd_t, + bytes: []const u8, + offset: usize, + result: Error!usize, + + pub const Error = os.PWriteError; + }; + pub const PWriteV = struct { fd: os.fd_t, iov: []const os.iovec_const, diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 73babf5fa2b6..8d4f5df2e87a 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -414,10 +414,12 @@ pub const File = struct { pub fn read(self: File, buffer: []u8) ReadError!usize { if (is_windows) { return windows.ReadFile(self.handle, buffer, null, self.intended_io_mode); - } else if (self.capable_io_mode != self.intended_io_mode) { - return std.event.Loop.instance.?.read(self.handle, buffer); - } else { + } + + if (self.intended_io_mode == .blocking) { return os.read(self.handle, buffer); + } else { + return std.event.Loop.instance.?.read(self.handle, buffer, self.capable_io_mode != self.intended_io_mode); } } @@ -436,10 +438,12 @@ pub const File = struct { pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize { if (is_windows) { return windows.ReadFile(self.handle, buffer, offset, self.intended_io_mode); - } else if (self.capable_io_mode != self.intended_io_mode) { - return std.event.Loop.instance.?.pread(self.handle, buffer, offset); - } else { + } + + if (self.intended_io_mode == .blocking) { return os.pread(self.handle, buffer, offset); + } else { + return std.event.Loop.instance.?.pread(self.handle, buffer, offset, self.capable_io_mode != self.intended_io_mode); } } @@ -461,10 +465,12 @@ pub const File = struct { if (iovecs.len == 0) return @as(usize, 0); const first = iovecs[0]; return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode); - } else if (self.capable_io_mode != self.intended_io_mode) { - return std.event.Loop.instance.?.readv(self.handle, iovecs); - } else { + } + + if (self.intended_io_mode == .blocking) { return os.readv(self.handle, iovecs); + } else { + return std.event.Loop.instance.?.readv(self.handle, iovecs, self.capable_io_mode != self.intended_io_mode); } } @@ -500,10 +506,12 @@ pub const File = struct { if (iovecs.len == 0) return @as(usize, 0); const first = iovecs[0]; return windows.ReadFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode); - } else if (self.capable_io_mode != self.intended_io_mode) { - return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset); - } else { + } + + if (self.intended_io_mode == .blocking) { return os.preadv(self.handle, iovecs, offset); + } else { + return std.event.Loop.instance.?.preadv(self.handle, iovecs, offset, self.capable_io_mode != self.intended_io_mode); } } @@ -539,10 +547,12 @@ pub const File = struct { pub fn write(self: File, bytes: []const u8) WriteError!usize { if (is_windows) { return windows.WriteFile(self.handle, bytes, null, self.intended_io_mode); - } else if (self.capable_io_mode != self.intended_io_mode) { - return std.event.Loop.instance.?.write(self.handle, bytes); - } else { + } + + if (self.intended_io_mode == .blocking) { return os.write(self.handle, bytes); + } else { + return std.event.Loop.instance.?.write(self.handle, bytes, self.capable_io_mode != self.intended_io_mode); } } @@ -556,10 +566,12 @@ pub const File = struct { pub fn pwrite(self: File, bytes: []const u8, offset: u64) PWriteError!usize { if (is_windows) { return windows.WriteFile(self.handle, bytes, offset, self.intended_io_mode); - } else if (self.capable_io_mode != self.intended_io_mode) { - return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset); - } else { + } + + if (self.intended_io_mode == .blocking) { return os.pwrite(self.handle, bytes, offset); + } else { + return std.event.Loop.instance.?.pwrite(self.handle, bytes, offset, self.capable_io_mode != self.intended_io_mode); } } @@ -576,10 +588,12 @@ pub const File = struct { if (iovecs.len == 0) return @as(usize, 0); const first = iovecs[0]; return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], null, self.intended_io_mode); - } else if (self.capable_io_mode != self.intended_io_mode) { - return std.event.Loop.instance.?.writev(self.handle, iovecs); - } else { + } + + if (self.intended_io_mode == .blocking) { return os.writev(self.handle, iovecs); + } else { + return std.event.Loop.instance.?.writev(self.handle, iovecs, self.capable_io_mode != self.intended_io_mode); } } @@ -607,10 +621,12 @@ pub const File = struct { if (iovecs.len == 0) return @as(usize, 0); const first = iovecs[0]; return windows.WriteFile(self.handle, first.iov_base[0..first.iov_len], offset, self.intended_io_mode); - } else if (self.capable_io_mode != self.intended_io_mode) { - return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset); - } else { + } + + if (self.intended_io_mode == .blocking) { return os.pwritev(self.handle, iovecs, offset); + } else { + return std.event.Loop.instance.?.pwritev(self.handle, iovecs, offset, self.capable_io_mode != self.intended_io_mode); } } diff --git a/lib/std/net.zig b/lib/std/net.zig index 45d8f07f046a..fe7d0fafe6d1 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -614,11 +614,11 @@ pub fn connectUnixSocket(path: []const u8) !fs.File { var addr = try std.net.Address.initUnix(path); - try os.connect( - sockfd, - &addr.any, - addr.getOsSockLen(), - ); + if (std.io.is_async) { + try loop.connect(sockfd, &addr.any, addr.getOsSockLen()); + } else { + try os.connect(sockfd, &addr.any, addr.getOsSockLen()); + } return fs.File{ .handle = sockfd, @@ -677,7 +677,13 @@ pub fn tcpConnectToAddress(address: Address) !fs.File { (if (builtin.os.tag == .windows) 0 else os.SOCK_CLOEXEC); const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP); errdefer os.close(sockfd); - try os.connect(sockfd, &address.any, address.getOsSockLen()); + + if (std.io.is_async) { + const loop = std.event.Loop.instance orelse return error.WouldBlock; + try loop.connect(sockfd, &address.any, address.getOsSockLen()); + } else { + try os.connect(sockfd, &address.any, address.getOsSockLen()); + } return fs.File{ .handle = sockfd }; } @@ -1429,7 +1435,11 @@ fn resMSendRc( if (answers[i].len == 0) { var j: usize = 0; while (j < ns.len) : (j += 1) { - _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; + if (std.io.is_async) { + _ = std.event.Loop.instance.?.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; + } else { + _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; + } } } } @@ -1444,7 +1454,10 @@ fn resMSendRc( while (true) { var sl_copy = sl; - const rlen = os.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break; + const rlen = if (std.io.is_async) + std.event.Loop.instance.?.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break + else + os.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break; // Ignore non-identifiable packets if (rlen < 4) continue; @@ -1470,7 +1483,11 @@ fn resMSendRc( 0, 3 => {}, 2 => if (servfail_retry != 0) { servfail_retry -= 1; - _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; + if (std.io.is_async) { + _ = std.event.Loop.instance.?.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; + } else { + _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; + } }, else => continue, } @@ -1661,18 +1678,23 @@ pub const StreamServer = struct { /// If this function succeeds, the returned `Connection` is a caller-managed resource. pub fn accept(self: *StreamServer) AcceptError!Connection { - const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; - const accept_flags = nonblock | os.SOCK_CLOEXEC; var accepted_addr: Address = undefined; var adr_len: os.socklen_t = @sizeOf(Address); - if (os.accept(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| { + const accept_result = blk: { + if (std.io.is_async) { + const loop = std.event.Loop.instance orelse return error.UnexpectedError; + break :blk loop.accept(self.sockfd.?, &accepted_addr.any, &adr_len, os.SOCK_CLOEXEC); + } else { + break :blk os.accept(self.sockfd.?, &accepted_addr.any, &adr_len, os.SOCK_CLOEXEC); + } + }; + + if (accept_result) |fd| { return Connection{ .file = fs.File{ .handle = fd }, .address = accepted_addr, }; } else |err| switch (err) { - // We only give SOCK_NONBLOCK when I/O mode is async, in which case this error - // is handled by os.accept4. error.WouldBlock => unreachable, else => |e| return e, } diff --git a/lib/std/os.zig b/lib/std/os.zig index 0b09b1f82a59..c06ce4ed005b 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -314,8 +314,8 @@ pub const ReadError = error{ /// Returns the number of bytes that were read, which can be less than /// buf.len. If 0 bytes were read, that means EOF. -/// If the application has a global event loop enabled, EAGAIN is handled -/// via the event loop. Otherwise EAGAIN results in error.WouldBlock. +/// If `fd` is opened in non blocking mode, the function will return error.WouldBlock +/// when EAGAIN is received. /// /// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000` /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as @@ -366,12 +366,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdReadable(fd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EBADF => return error.NotOpenForReading, // Can be a race condition. EIO => return error.InputOutput, EISDIR => return error.IsDir, @@ -387,8 +382,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. /// -/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled -/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. +/// For POSIX systems, if `fd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received. /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. /// @@ -428,12 +423,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdReadable(fd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EBADF => return error.NotOpenForReading, // can be a race condition EIO => return error.InputOutput, EISDIR => return error.IsDir, @@ -450,8 +440,8 @@ pub const PReadError = ReadError || error{Unseekable}; /// /// Retries when interrupted by a signal. /// -/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled -/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. +/// For POSIX systems, if `fd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received. /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { @@ -492,12 +482,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdReadable(fd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EBADF => return error.NotOpenForReading, // Can be a race condition. EIO => return error.InputOutput, EISDIR => return error.IsDir, @@ -586,8 +571,8 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { /// /// Retries when interrupted by a signal. /// -/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled -/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. +/// For POSIX systems, if `fd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received. /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. /// @@ -637,12 +622,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdReadable(fd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EBADF => return error.NotOpenForReading, // can be a race condition EIO => return error.InputOutput, EISDIR => return error.IsDir, @@ -687,8 +667,8 @@ pub const WriteError = error{ /// another write() call to transfer the remaining bytes. The subsequent call will either /// transfer further bytes or may result in an error (e.g., if the disk is now full). /// -/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled -/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. +/// For POSIX systems, if `fd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received. /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. /// @@ -741,12 +721,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdWritable(fd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EBADF => return error.NotOpenForWriting, // can be a race condition. EDESTADDRREQ => unreachable, // `connect` was never called. EDQUOT => return error.DiskQuota, @@ -772,8 +747,8 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { /// another write() call to transfer the remaining bytes. The subsequent call will either /// transfer further bytes or may result in an error (e.g., if the disk is now full). /// -/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled -/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. +/// For POSIX systems, if `fd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received.k`. /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. /// @@ -814,12 +789,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdWritable(fd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EBADF => return error.NotOpenForWriting, // Can be a race condition. EDESTADDRREQ => unreachable, // `connect` was never called. EDQUOT => return error.DiskQuota, @@ -847,8 +817,8 @@ pub const PWriteError = WriteError || error{Unseekable}; /// another write() call to transfer the remaining bytes. The subsequent call will either /// transfer further bytes or may result in an error (e.g., if the disk is now full). /// -/// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled -/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. +/// For POSIX systems, if `fd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received. /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. /// @@ -905,12 +875,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdWritable(fd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EBADF => return error.NotOpenForWriting, // Can be a race condition. EDESTADDRREQ => unreachable, // `connect` was never called. EDQUOT => return error.DiskQuota, @@ -939,8 +904,8 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { /// another write() call to transfer the remaining bytes. The subsequent call will either /// transfer further bytes or may result in an error (e.g., if the disk is now full). /// -/// If the application has a global event loop enabled, EAGAIN is handled -/// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. +/// If `fd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received. /// /// The following systems do not have this syscall, and will return partial writes if more than one /// vector is provided: @@ -993,12 +958,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz EINTR => continue, EINVAL => unreachable, EFAULT => unreachable, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdWritable(fd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EBADF => return error.NotOpenForWriting, // Can be a race condition. EDESTADDRREQ => unreachable, // `connect` was never called. EDQUOT => return error.DiskQuota, @@ -2846,8 +2806,8 @@ pub const AcceptError = error{ } || UnexpectedError; /// Accept a connection on a socket. -/// If the application has a global event loop enabled, EAGAIN is handled -/// via the event loop. Otherwise EAGAIN results in error.WouldBlock. +/// If `sockfd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received. pub fn accept( /// This argument is a socket that has been created with `socket`, bound to a local address /// with `bind`, and is listening for connections after a `listen`. @@ -2890,12 +2850,7 @@ pub fn accept( return fd; }, EINTR => continue, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdReadable(sockfd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EBADF => unreachable, // always a race condition ECONNABORTED => return error.ConnectionAborted, EFAULT => unreachable, @@ -3081,6 +3036,8 @@ pub const ConnectError = error{ } || UnexpectedError; /// Initiate a connection on a socket. +/// If `sockfd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN or EINPROGRESS is received. pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void { if (builtin.os.tag == .windows) { const rc = windows.ws2_32.connect(sockfd, sock_addr, len); @@ -3113,11 +3070,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con EADDRINUSE => return error.AddressInUse, EADDRNOTAVAIL => return error.AddressNotAvailable, EAFNOSUPPORT => return error.AddressFamilyNotSupported, - EAGAIN, EINPROGRESS => { - const loop = std.event.Loop.instance orelse return error.WouldBlock; - loop.waitUntilFdWritable(sockfd); - return getsockoptError(sockfd); - }, + EAGAIN, EINPROGRESS => return error.WouldBlock, EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. EBADF => unreachable, // sockfd is not a valid open file descriptor. ECONNREFUSED => return error.ConnectionRefused, @@ -4620,14 +4573,8 @@ pub fn sendto( const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen); switch (errno(rc)) { 0 => return @intCast(usize, rc), - EACCES => return error.AccessDenied, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdWritable(sockfd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, EALREADY => return error.FastOpenAlreadyInProgress, EBADF => unreachable, // always a race condition ECONNRESET => return error.ConnectionResetByPeer, @@ -5106,6 +5053,8 @@ pub const RecvFromError = error{ SystemResources, } || UnexpectedError; +/// If `sockfd` is opened in non blocking mode, the function will +/// return error.WouldBlock when EAGAIN is received. pub fn recvfrom( sockfd: fd_t, buf: []u8, @@ -5123,12 +5072,7 @@ pub fn recvfrom( ENOTCONN => unreachable, ENOTSOCK => unreachable, EINTR => continue, - EAGAIN => if (std.event.Loop.instance) |loop| { - loop.waitUntilFdReadable(sockfd); - continue; - } else { - return error.WouldBlock; - }, + EAGAIN => return error.WouldBlock, ENOMEM => return error.SystemResources, ECONNREFUSED => return error.ConnectionRefused, else => |err| return unexpectedErrno(err),