std.posix: remove close function

This commit is contained in:
Andrew Kelley 2026-02-10 23:31:37 -08:00
parent ea30f86113
commit b600b6e5e0
16 changed files with 165 additions and 234 deletions

View file

@ -14,6 +14,8 @@ comptime {
symbol(&acctLinux, "acct");
symbol(&chdirLinux, "chdir");
symbol(&chownLinux, "chown");
symbol(&close, "close");
symbol(&posix_close, "posix_close");
symbol(&fchownatLinux, "fchownat");
symbol(&lchownLinux, "lchown");
symbol(&chrootLinux, "chroot");
@ -49,6 +51,9 @@ comptime {
if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
symbol(&swab, "swab");
}
if (builtin.target.isWasiLibC()) {
symbol(&closeWasi, "close");
}
}
fn _exit(exit_code: c_int) callconv(.c) noreturn {
@ -226,3 +231,28 @@ test swab {
swab("abcd", &a, 3);
try std.testing.expectEqualSlices(u8, "ba\x00\x00", &a);
}
fn close(fd: std.c.fd_t) callconv(.c) c_int {
const signed: isize = @bitCast(linux.close(fd));
if (signed < 0) {
@branchHint(.unlikely);
if (-signed == @intFromEnum(linux.E.INTR)) return 0;
std.c._errno().* = @intCast(-signed);
return -1;
}
return 0;
}
fn posix_close(fd: std.c.fd_t, _: c_int) callconv(.c) c_int {
return close(fd);
}
fn closeWasi(fd: std.c.fd_t) callconv(.c) c_int {
switch (std.os.wasi.fd_close(fd)) {
.SUCCESS => return 0,
else => |e| {
std.c._errno().* = @intFromEnum(e);
return -1;
},
}
}

View file

@ -1,19 +0,0 @@
#include <unistd.h>
#include <errno.h>
#include "aio_impl.h"
#include "syscall.h"
static int dummy(int fd)
{
return fd;
}
weak_alias(dummy, __aio_close);
int close(int fd)
{
fd = __aio_close(fd);
int r = __syscall_cp(SYS_close, fd);
if (r == -EINTR) r = 0;
return __syscall_ret(r);
}

View file

@ -1,6 +0,0 @@
#include <unistd.h>
int posix_close(int fd, int flags)
{
return close(fd);
}

View file

@ -70,41 +70,3 @@ void drop_udp_socket(udp_socket_t socket) {
udp_udp_socket_drop_own(socket.socket);
}
#endif // __wasilibc_use_wasip2
int close(int fd) {
// Scan the preopen fds before making any changes.
__wasilibc_populate_preopens();
#ifdef __wasilibc_use_wasip2
descriptor_table_entry_t entry;
if (descriptor_table_remove(fd, &entry)) {
switch (entry.tag)
{
case DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET:
drop_tcp_socket(entry.tcp_socket);
break;
case DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET:
drop_udp_socket(entry.udp_socket);
break;
default: /* unreachable */ abort();
}
return 0;
}
#endif // __wasilibc_use_wasip2
__wasi_errno_t error = __wasi_fd_close(fd);
if (error != 0) {
errno = error;
return -1;
}
return 0;
}
weak void __wasilibc_populate_preopens(void) {
// This version does nothing. It may be overridden by a version which does
// something if `__wasilibc_find_abspath` or `__wasilibc_find_relpath` are
// used.
}

View file

@ -693,7 +693,7 @@ const Os = switch (builtin.os.tag) {
fatal("failed to open directory {f}: {t}", .{ path, err });
};
// Empirically the dir has to stay open or else no events are triggered.
errdefer if (!skip_open_dir) posix.close(dir_fd);
errdefer if (!skip_open_dir) std.Io.Threaded.closeFd(dir_fd);
const changes = [1]posix.Kevent{.{
.ident = @bitCast(@as(isize, dir_fd)),
.filter = std.c.EVFILT.VNODE,
@ -793,7 +793,7 @@ const Os = switch (builtin.os.tag) {
};
const filtered_changes = if (i == handles.len - 1) changes[0..1] else &changes;
_ = try Io.Kqueue.kevent(w.os.kq_fd, filtered_changes, &.{}, null);
if (path.sub_path.len != 0) posix.close(dir_fd);
if (path.sub_path.len != 0) std.Io.Threaded.closeFd(dir_fd);
w.dir_table.swapRemoveAt(i);
handles.swapRemove(i);

View file

@ -549,7 +549,7 @@ const CachedFd = struct {
.initializing => unreachable,
_ => |fd| {
assert(@intFromEnum(fd) >= 0);
std.posix.close(@intFromEnum(fd));
_ = std.os.linux.close(@intFromEnum(fd));
cached_fd.* = .init;
},
}

View file

@ -11,6 +11,7 @@ const Allocator = std.mem.Allocator;
const Alignment = std.mem.Alignment;
const IpAddress = std.Io.net.IpAddress;
const errnoBug = std.Io.Threaded.errnoBug;
const closeFd = std.Io.Threaded.closeFd;
const posix = std.posix;
/// Must be a thread-safe allocator.
@ -64,7 +65,7 @@ const Thread = struct {
};
fn deinit(thread: *Thread, gpa: Allocator) void {
posix.close(thread.kq_fd);
closeFd(thread.kq_fd);
assert(thread.wait_queues.count() == 0);
thread.wait_queues.deinit(gpa);
thread.* = undefined;
@ -212,7 +213,7 @@ pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void {
.steal_ready_search_index = 1,
.wait_queues = .empty,
};
errdefer std.posix.close(main_thread.kq_fd);
errdefer closeFd(main_thread.kq_fd);
std.log.debug("created main idle {*}", .{&main_thread.idle_context});
std.log.debug("created main {*}", .{main_fiber});
}
@ -371,7 +372,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void {
.stack_size = idle_stack_size,
.allocator = k.gpa,
}, threadEntry, .{ k, new_thread_index }) catch |err| {
posix.close(new_thread.kq_fd);
closeFd(new_thread.kq_fd);
@atomicStore(u32, &k.threads.reserved, new_thread_index, .release);
// no more access to `thread` after giving up reservation
std.log.warn("unable to create worker thread due spawn failure: {s}", .{@errorName(err)});
@ -1234,7 +1235,7 @@ fn netBindIp(
const k: *Kqueue = @ptrCast(@alignCast(userdata));
const family = Io.Threaded.posixAddressFamily(address);
const socket_fd = try openSocketPosix(k, family, options);
errdefer std.posix.close(socket_fd);
errdefer closeFd(socket_fd);
var storage: Io.Threaded.PosixAddress = undefined;
var addr_len = Io.Threaded.addressToPosix(address, &storage);
try posixBind(k, socket_fd, &storage.any, addr_len);
@ -1252,7 +1253,7 @@ fn netConnectIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: n
.mode = options.mode,
.protocol = options.protocol,
});
errdefer posix.close(socket_fd);
errdefer closeFd(socket_fd);
var storage: Io.Threaded.PosixAddress = undefined;
var addr_len = Io.Threaded.addressToPosix(address, &storage);
try posixConnect(k, socket_fd, &storage.any, addr_len);
@ -1565,7 +1566,7 @@ fn openSocketPosix(
switch (posix.errno(socket_rc)) {
.SUCCESS => {
const fd: posix.fd_t = @intCast(socket_rc);
errdefer posix.close(fd);
errdefer closeFd(fd);
if (Io.Threaded.socket_flags_unsupported) {
while (true) {
try k.checkCancel();
@ -1614,7 +1615,7 @@ fn openSocketPosix(
else => |err| return posix.unexpectedErrno(err),
}
};
errdefer posix.close(socket_fd);
errdefer closeFd(socket_fd);
if (options.ip6_only) {
if (posix.IPV6 == void) return error.OptionUnsupported;

View file

@ -303,7 +303,7 @@ pub const NullFile = switch (native_os) {
fn deinit(this: *@This()) void {
if (this.fd >= 0) {
posix.close(this.fd);
closeFd(this.fd);
this.fd = -1;
}
}
@ -4337,7 +4337,7 @@ fn dirCreateFilePosix(
}
}
};
errdefer posix.close(fd);
errdefer closeFd(fd);
if (have_flock and !have_flock_open_flags and flags.lock != .none) {
const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0;
@ -4917,7 +4917,7 @@ fn dirOpenFilePosix(
}
}
};
errdefer posix.close(fd);
errdefer closeFd(fd);
if (!flags.allow_directory) {
const is_dir = is_dir: {
@ -5241,7 +5241,7 @@ fn dirOpenFileWasi(
},
}
}
errdefer posix.close(fd);
errdefer closeFd(fd);
if (!flags.allow_directory) {
const is_dir = is_dir: {
@ -5457,7 +5457,13 @@ pub fn dirOpenDirWindows(
fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
for (dirs) |dir| posix.close(dir.handle);
for (dirs) |dir| {
if (is_windows) {
windows.CloseHandle(dir.handle);
} else {
closeFd(dir.handle);
}
}
}
const dirRead = switch (native_os) {
@ -6777,7 +6783,7 @@ fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, o
},
}
};
defer posix.close(fd);
defer closeFd(fd);
return realPathPosix(fd, out_buffer);
}
@ -8367,7 +8373,7 @@ fn fchmodatFallback(
}
}
};
defer posix.close(path_fd);
defer closeFd(path_fd);
const path_mode = mode: {
const sys = if (statx_use_c) std.c else std.os.linux;
@ -9592,7 +9598,13 @@ fn dirHardLink(
fn fileClose(userdata: ?*anyopaque, files: []const File) void {
const t: *Threaded = @ptrCast(@alignCast(userdata));
_ = t;
for (files) |file| posix.close(file.handle);
for (files) |file| {
if (is_windows) {
windows.CloseHandle(file.handle);
} else {
closeFd(file.handle);
}
}
}
fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.ReadStreamingError!usize {
@ -11783,7 +11795,7 @@ fn netListenIpPosix(
.mode = options.mode,
.protocol = options.protocol,
});
errdefer posix.close(socket_fd);
errdefer closeFd(socket_fd);
if (options.reuse_address) {
try setSocketOption(socket_fd, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1);
@ -11946,7 +11958,7 @@ fn netListenUnixPosix(
error.OptionUnsupported => return error.Unexpected,
else => |e| return e,
};
errdefer posix.close(socket_fd);
errdefer closeFd(socket_fd);
var storage: UnixAddress = undefined;
const addr_len = addressUnixToPosix(address, &storage);
@ -12362,7 +12374,7 @@ fn netConnectIpPosix(
.mode = options.mode,
.protocol = options.protocol,
});
errdefer posix.close(socket_fd);
errdefer closeFd(socket_fd);
var storage: PosixAddress = undefined;
var addr_len = addressToPosix(address, &storage);
try posixConnect(socket_fd, &storage.any, addr_len);
@ -12462,7 +12474,7 @@ fn netConnectUnixPosix(
error.OptionUnsupported => return error.Unexpected,
else => |e| return e,
};
errdefer posix.close(socket_fd);
errdefer closeFd(socket_fd);
var storage: UnixAddress = undefined;
const addr_len = addressUnixToPosix(address, &storage);
try posixConnectUnix(socket_fd, &storage.any, addr_len);
@ -12536,7 +12548,7 @@ fn netBindIpPosix(
_ = t;
const family = posixAddressFamily(address);
const socket_fd = try openSocketPosix(family, options);
errdefer posix.close(socket_fd);
errdefer closeFd(socket_fd);
var storage: PosixAddress = undefined;
var addr_len = addressToPosix(address, &storage);
try posixBind(socket_fd, &storage.any, addr_len);
@ -12642,7 +12654,7 @@ fn openSocketPosix(
.SUCCESS => {
syscall.finish();
const fd: posix.fd_t = @intCast(rc);
errdefer posix.close(fd);
errdefer closeFd(fd);
if (socket_flags_unsupported) try setCloexec(fd);
break fd;
},
@ -12661,7 +12673,7 @@ fn openSocketPosix(
else => |err| return syscall.unexpectedErrno(err),
}
};
errdefer posix.close(socket_fd);
errdefer closeFd(socket_fd);
if (options.ip6_only) {
if (posix.IPV6 == void) return error.OptionUnsupported;
@ -12707,8 +12719,8 @@ fn netSocketCreatePair(
.SUCCESS => {
syscall.finish();
errdefer {
posix.close(sockets[0]);
posix.close(sockets[1]);
closeFd(sockets[0]);
closeFd(sockets[1]);
}
if (socket_flags_unsupported) {
try setCloexec(sockets[0]);
@ -12809,7 +12821,7 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve
.SUCCESS => {
syscall.finish();
const fd: posix.fd_t = @intCast(rc);
errdefer posix.close(fd);
errdefer closeFd(fd);
if (!have_accept4) try setCloexec(fd);
break fd;
},
@ -13679,7 +13691,7 @@ fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
_ = t;
switch (native_os) {
.windows => for (handles) |handle| closeSocketWindows(handle),
else => for (handles) |handle| posix.close(handle),
else => for (handles) |handle| closeFd(handle),
}
}
@ -13787,7 +13799,7 @@ fn netInterfaceNameResolve(
error.OptionUnsupported => return error.Unexpected,
else => |e| return e,
};
defer posix.close(sock_fd);
defer closeFd(sock_fd);
var ifr: posix.ifreq = .{
.ifrn = .{ .name = @bitCast(name.bytes) },
@ -15329,13 +15341,13 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
const pid: posix.pid_t = @intCast(pid_result); // We are the parent.
errdefer comptime unreachable; // The child is forked; we must not error from now on
posix.close(err_pipe[1]); // make sure only the child holds the write end open
closeFd(err_pipe[1]); // make sure only the child holds the write end open
if (options.stdin == .pipe) posix.close(stdin_pipe[0]);
if (options.stdout == .pipe) posix.close(stdout_pipe[1]);
if (options.stderr == .pipe) posix.close(stderr_pipe[1]);
if (options.stdin == .pipe) closeFd(stdin_pipe[0]);
if (options.stdout == .pipe) closeFd(stdout_pipe[1]);
if (options.stderr == .pipe) closeFd(stderr_pipe[1]);
if (prog_pipe[1] != -1) posix.close(prog_pipe[1]);
if (prog_pipe[1] != -1) closeFd(prog_pipe[1]);
options.progress_node.setIpcFile(t, .{ .handle = prog_pipe[0], .flags = .{ .nonblocking = true } });
return .{
@ -15373,7 +15385,7 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {
mutexLock(&t.mutex); // Another thread might have won the race.
defer mutexUnlock(&t.mutex);
if (t.null_file.fd != -1) {
posix.close(fresh_fd);
closeFd(fresh_fd);
return t.null_file.fd;
} else {
t.null_file.fd = fresh_fd;
@ -15399,7 +15411,7 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {
fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
const t: *Threaded = @ptrCast(@alignCast(userdata));
const spawned = try spawnPosix(t, options);
defer posix.close(spawned.err_fd);
defer closeFd(spawned.err_fd);
// Wait for the child to report any errors in or before `execvpe`.
if (readIntFd(spawned.err_fd)) |child_err_int| {
@ -15666,15 +15678,15 @@ fn childKillPosix(child: *process.Child) !void {
fn childCleanupPosix(child: *process.Child) void {
if (child.stdin) |*stdin| {
posix.close(stdin.handle);
closeFd(stdin.handle);
child.stdin = null;
}
if (child.stdout) |*stdout| {
posix.close(stdout.handle);
closeFd(stdout.handle);
child.stdout = null;
}
if (child.stderr) |*stderr| {
posix.close(stderr.handle);
closeFd(stderr.handle);
child.stderr = null;
}
child.id = null;
@ -15743,14 +15755,14 @@ fn readIntFd(fd: posix.fd_t) !ErrInt {
const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
fn destroyPipe(pipe: [2]posix.fd_t) void {
if (pipe[0] != -1) posix.close(pipe[0]);
if (pipe[0] != pipe[1]) posix.close(pipe[1]);
if (pipe[0] != -1) closeFd(pipe[0]);
if (pipe[0] != pipe[1]) closeFd(pipe[1]);
}
fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
switch (stdio) {
.pipe => try dup2(pipe_fd, std_fileno),
.close => posix.close(std_fileno),
.close => closeFd(std_fileno),
.inherit => {},
.ignore => try dup2(dev_null_fd, std_fileno),
.file => @panic("TODO implement setUpChildIo when file is used"),
@ -17439,7 +17451,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
}
}
};
errdefer posix.close(fd);
errdefer closeFd(fd);
switch (native_os) {
.linux => {
@ -17454,7 +17466,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
mutexLock(&t.mutex); // Another thread might have won the race.
defer mutexUnlock(&t.mutex);
if (t.random_file.fd >= 0) {
posix.close(fd);
closeFd(fd);
return t.random_file.fd;
} else if (!posix.S.ISCHR(statx.mode)) {
t.random_file.fd = -2;
@ -17482,7 +17494,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
mutexLock(&t.mutex); // Another thread might have won the race.
defer mutexUnlock(&t.mutex);
if (t.random_file.fd >= 0) {
posix.close(fd);
closeFd(fd);
return t.random_file.fd;
} else if (!posix.S.ISCHR(stat.mode)) {
t.random_file.fd = -2;
@ -18170,8 +18182,8 @@ pub fn pipe2(flags: posix.O) PipeError![2]posix.fd_t {
else => |err| return posix.unexpectedErrno(err),
}
errdefer {
posix.close(fds[0]);
posix.close(fds[1]);
closeFd(fds[0]);
closeFd(fds[1]);
}
// https://github.com/ziglang/zig/issues/18882
@ -19164,3 +19176,17 @@ fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!windows
}
}
}
pub fn closeFd(fd: posix.fd_t) void {
if (native_os == .wasi and !builtin.link_libc) {
switch (std.os.wasi.fd_close(fd)) {
.SUCCESS, .INTR => {},
.BADF => recoverableOsBugDetected(), // use after free
else => recoverableOsBugDetected(), // unexpected failure
}
} else switch (posix.errno(posix.system.close(fd))) {
.SUCCESS, .INTR => {}, // INTR still a success, see https://github.com/ziglang/zig/issues/2425
.BADF => recoverableOsBugDetected(), // use after free
else => recoverableOsBugDetected(), // unexpected failure
}
}

View file

@ -1571,7 +1571,7 @@ pub fn clone2(flags: u32, child_stack_ptr: usize) usize {
return syscall2(.clone, flags, child_stack_ptr);
}
pub fn close(fd: i32) usize {
pub fn close(fd: fd_t) usize {
return syscall1(.close, @as(usize, @bitCast(@as(isize, fd))));
}

View file

@ -67,7 +67,7 @@ pub fn init_params(entries: u16, p: *linux.io_uring_params) !IoUring {
}
const fd = @as(linux.fd_t, @intCast(res));
assert(fd >= 0);
errdefer posix.close(fd);
errdefer _ = linux.close(fd);
// Kernel versions 5.4 and up use only one mmap() for the submission and completion queues.
// This is not an optional feature for us... if the kernel does it, we have to do it.
@ -125,7 +125,7 @@ pub fn deinit(self: *IoUring) void {
// The mmaps depend on the fd, so the order of these calls is important:
self.cq.deinit();
self.sq.deinit();
posix.close(self.fd);
_ = linux.close(self.fd);
self.fd = -1;
}

View file

@ -440,7 +440,7 @@ test "openat" {
try testing.expect(cqe_openat.res > 0);
try testing.expectEqual(@as(u32, 0), cqe_openat.flags);
posix.close(cqe_openat.res);
_ = linux.close(cqe_openat.res);
}
test "close" {
@ -530,7 +530,7 @@ test "sendmsg/recvmsg" {
};
const server = try socket(address_server.family, posix.SOCK.DGRAM, 0);
defer posix.close(server);
defer _ = linux.close(server);
try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));
try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
try bind(server, addrAny(&address_server), @sizeOf(linux.sockaddr.in));
@ -540,7 +540,7 @@ test "sendmsg/recvmsg" {
try getsockname(server, addrAny(&address_server), &slen);
const client = try socket(address_server.family, posix.SOCK.DGRAM, 0);
defer posix.close(client);
defer _ = linux.close(client);
const buffer_send = [_]u8{42} ** 128;
const iovecs_send = [_]iovec_const{
@ -1034,7 +1034,7 @@ test "shutdown" {
// Socket bound, expect shutdown to work
{
const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
defer posix.close(server);
defer _ = linux.close(server);
try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
try bind(server, addrAny(&address), @sizeOf(linux.sockaddr.in));
try listen(server, 1);
@ -1067,7 +1067,7 @@ test "shutdown" {
// Socket not bound, expect to fail with ENOTCONN
{
const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
defer posix.close(server);
defer _ = linux.close(server);
const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) {
else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),
@ -1741,7 +1741,7 @@ test "accept multishot" {
.addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
};
const listener_socket = try createListenerSocket(&address);
defer posix.close(listener_socket);
defer _ = linux.close(listener_socket);
// submit multishot accept operation
var addr: posix.sockaddr = undefined;
@ -1754,7 +1754,7 @@ test "accept multishot" {
while (nr > 0) : (nr -= 1) {
// connect client
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
errdefer posix.close(client);
errdefer _ = linux.close(client);
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
// test accept completion
@ -1764,7 +1764,7 @@ test "accept multishot" {
try testing.expect(cqe.user_data == userdata);
try testing.expect(cqe.flags & linux.IORING_CQE_F_MORE > 0); // more flag is set
posix.close(client);
_ = linux.close(client);
}
}
@ -1848,7 +1848,7 @@ test "accept_direct" {
try ring.register_files(registered_fds[0..]);
const listener_socket = try createListenerSocket(&address);
defer posix.close(listener_socket);
defer _ = linux.close(listener_socket);
const accept_userdata: u64 = 0xaaaaaaaa;
const read_userdata: u64 = 0xbbbbbbbb;
@ -1866,7 +1866,7 @@ test "accept_direct" {
// connect
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
defer posix.close(client);
defer _ = linux.close(client);
// accept completion
const cqe_accept = try ring.copy_cqe();
@ -1900,7 +1900,7 @@ test "accept_direct" {
// connect
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
defer posix.close(client);
defer _ = linux.close(client);
// completion with error
const cqe_accept = try ring.copy_cqe();
try testing.expect(cqe_accept.user_data == accept_userdata);
@ -1936,7 +1936,7 @@ test "accept_multishot_direct" {
try ring.register_files(registered_fds[0..]);
const listener_socket = try createListenerSocket(&address);
defer posix.close(listener_socket);
defer _ = linux.close(listener_socket);
const accept_userdata: u64 = 0xaaaaaaaa;
@ -1950,7 +1950,7 @@ test "accept_multishot_direct" {
// connect
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
defer posix.close(client);
defer _ = linux.close(client);
// accept completion
const cqe_accept = try ring.copy_cqe();
@ -1965,7 +1965,7 @@ test "accept_multishot_direct" {
// connect
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
defer posix.close(client);
defer _ = linux.close(client);
// completion with error
const cqe_accept = try ring.copy_cqe();
try testing.expect(cqe_accept.user_data == accept_userdata);
@ -1998,7 +1998,7 @@ test "socket" {
const fd: linux.fd_t = @intCast(cqe.res);
try testing.expect(fd > 2);
posix.close(fd);
_ = linux.close(fd);
}
test "socket_direct/socket_direct_alloc/close_direct" {
@ -2042,7 +2042,7 @@ test "socket_direct/socket_direct_alloc/close_direct" {
.addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
};
const listener_socket = try createListenerSocket(&address);
defer posix.close(listener_socket);
defer _ = linux.close(listener_socket);
const accept_userdata: u64 = 0xaaaaaaaa;
const connect_userdata: u64 = 0xbbbbbbbb;
const close_userdata: u64 = 0xcccccccc;
@ -2599,8 +2599,8 @@ pub const SocketTestHarness = struct {
client: posix.socket_t,
pub fn close(self: SocketTestHarness) void {
posix.close(self.client);
posix.close(self.listener);
_ = linux.close(self.client);
_ = linux.close(self.listener);
}
};
@ -2611,7 +2611,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
.addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
};
const listener_socket = try createListenerSocket(&address);
errdefer posix.close(listener_socket);
errdefer _ = linux.close(listener_socket);
// Submit 1 accept
var accept_addr: posix.sockaddr = undefined;
@ -2620,7 +2620,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
// Create a TCP client socket
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
errdefer posix.close(client);
errdefer _ = linux.close(client);
_ = try ring.connect(0xcccccccc, client, addrAny(&address), @sizeOf(linux.sockaddr.in));
try testing.expectEqual(@as(u32, 2), try ring.submit());
@ -2660,7 +2660,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
fn createListenerSocket(address: *linux.sockaddr.in) !posix.socket_t {
const kernel_backlog = 1;
const listener_socket = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
errdefer posix.close(listener_socket);
errdefer _ = linux.close(listener_socket);
try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
try bind(listener_socket, addrAny(address), @sizeOf(linux.sockaddr.in));

View file

@ -407,6 +407,42 @@ test "futex2_requeue" {
try expectEqual(0, rc);
}
test "timerfd" {
const tfd: linux.fd_t = rc: {
const rc = linux.timerfd_create(.MONOTONIC, .{ .CLOEXEC = true });
switch (linux.errno(rc)) {
.SUCCESS => break :rc @intCast(rc),
else => @panic("test failed"),
}
};
defer _ = linux.close(tfd);
// Fire event 10_000_000ns = 10ms after the posix.timerfd_settime call.
var sit: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 10 * (1000 * 1000) } };
const flags: linux.TFD.TIMER = .{};
switch (linux.errno(linux.timerfd_settime(tfd, @bitCast(flags), &sit, null))) {
.SUCCESS => {},
else => @panic("test failed"),
}
var fds: [1]std.posix.pollfd = .{.{ .fd = tfd, .events = linux.POLL.IN, .revents = 0 }};
try expectEqual(@as(usize, 1), try std.posix.poll(&fds, -1)); // -1 => infinite waiting
const git = rc: {
var curr_value: linux.itimerspec = undefined;
const rc = linux.timerfd_gettime(tfd, &curr_value);
switch (linux.errno(rc)) {
.SUCCESS => break :rc curr_value,
else => @panic("test failed"),
}
};
const expect_disarmed_timer: linux.itimerspec = .{
.it_interval = .{ .sec = 0, .nsec = 0 },
.it_value = .{ .sec = 0, .nsec = 0 },
};
try expectEqual(expect_disarmed_timer, git);
}
test {
_ = linux.IoUring;
}

View file

@ -278,30 +278,6 @@ pub const socket_t = if (native_os == .windows) windows.ws2_32.SOCKET else fd_t;
/// the system function call whose errno value is intended to be observed.
pub const errno = system.errno;
/// Closes the file descriptor.
///
/// Asserts the file descriptor is open.
///
/// This function is not capable of returning any indication of failure. An
/// application which wants to ensure writes have succeeded before closing must
/// call `fsync` before `close`.
///
/// The Zig standard library does not support POSIX thread cancellation.
pub fn close(fd: fd_t) void {
if (native_os == .windows) {
return windows.CloseHandle(fd);
}
if (native_os == .wasi and !builtin.link_libc) {
_ = std.os.wasi.fd_close(fd);
return;
}
switch (errno(system.close(fd))) {
.BADF => unreachable, // Always a race condition.
.INTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
else => return,
}
}
pub const RebootError = error{
PermissionDenied,
} || UnexpectedError;
@ -1541,60 +1517,6 @@ pub fn perf_event_open(
}
}
pub const TimerFdCreateError = error{
PermissionDenied,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
NoDevice,
SystemResources,
} || UnexpectedError;
pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError;
pub const TimerFdSetError = TimerFdGetError || error{Canceled};
pub fn timerfd_create(clock_id: system.timerfd_clockid_t, flags: system.TFD) TimerFdCreateError!fd_t {
const rc = system.timerfd_create(clock_id, @bitCast(flags));
return switch (errno(rc)) {
.SUCCESS => @intCast(rc),
.INVAL => unreachable,
.MFILE => return error.ProcessFdQuotaExceeded,
.NFILE => return error.SystemFdQuotaExceeded,
.NODEV => return error.NoDevice,
.NOMEM => return error.SystemResources,
.PERM => return error.PermissionDenied,
else => |err| return unexpectedErrno(err),
};
}
pub fn timerfd_settime(
fd: i32,
flags: system.TFD.TIMER,
new_value: *const system.itimerspec,
old_value: ?*system.itimerspec,
) TimerFdSetError!void {
const rc = system.timerfd_settime(fd, @bitCast(flags), new_value, old_value);
return switch (errno(rc)) {
.SUCCESS => {},
.BADF => error.InvalidHandle,
.FAULT => unreachable,
.INVAL => unreachable,
.CANCELED => error.Canceled,
else => |err| return unexpectedErrno(err),
};
}
pub fn timerfd_gettime(fd: i32) TimerFdGetError!system.itimerspec {
var curr_value: system.itimerspec = undefined;
const rc = system.timerfd_gettime(fd, &curr_value);
return switch (errno(rc)) {
.SUCCESS => return curr_value,
.BADF => error.InvalidHandle,
.FAULT => unreachable,
.INVAL => unreachable,
else => |err| return unexpectedErrno(err),
};
}
pub const PtraceError = error{
DeadLock,
DeviceBusy,

View file

@ -522,21 +522,3 @@ test "rename smoke test" {
try expectError(error.FileNotFound, Io.Dir.cwd().openDir(io, file_path, .{}));
}
}
test "timerfd" {
if (native_os != .linux) return error.SkipZigTest;
const tfd = try posix.timerfd_create(.MONOTONIC, .{ .CLOEXEC = true });
defer posix.close(tfd);
// Fire event 10_000_000ns = 10ms after the posix.timerfd_settime call.
var sit: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 10 * (1000 * 1000) } };
try posix.timerfd_settime(tfd, .{}, &sit, null);
var fds: [1]posix.pollfd = .{.{ .fd = tfd, .events = linux.POLL.IN, .revents = 0 }};
try expectEqual(@as(usize, 1), try posix.poll(&fds, -1)); // -1 => infinite waiting
const git = try posix.timerfd_gettime(tfd);
const expect_disarmed_timer: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 0 } };
try expectEqual(expect_disarmed_timer, git);
}

View file

@ -1830,7 +1830,6 @@ const src_files = [_][]const u8{
"musl/src/time/wcsftime.c",
"musl/src/time/__year_to_secs.c",
"musl/src/unistd/alarm.c",
"musl/src/unistd/close.c",
"musl/src/unistd/dup2.c",
"musl/src/unistd/dup3.c",
"musl/src/unistd/faccessat.c",
@ -1849,7 +1848,6 @@ const src_files = [_][]const u8{
"musl/src/unistd/nice.c",
"musl/src/unistd/pause.c",
"musl/src/unistd/pipe2.c",
"musl/src/unistd/posix_close.c",
"musl/src/unistd/pread.c",
"musl/src/unistd/preadv.c",
"musl/src/unistd/pwrite.c",

View file

@ -999,7 +999,6 @@ const libc_top_half_src_files = [_][]const u8{
"musl/src/time/strptime.c",
"musl/src/time/timespec_get.c",
"musl/src/time/__year_to_secs.c",
"musl/src/unistd/posix_close.c",
"wasi/libc-top-half/musl/src/conf/fpathconf.c",
"wasi/libc-top-half/musl/src/conf/sysconf.c",