diff --git a/lib/c/unistd.zig b/lib/c/unistd.zig index ef0f8895c7..676f943f36 100644 --- a/lib/c/unistd.zig +++ b/lib/c/unistd.zig @@ -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; + }, + } +} diff --git a/lib/libc/musl/src/unistd/close.c b/lib/libc/musl/src/unistd/close.c deleted file mode 100644 index a2105f5060..0000000000 --- a/lib/libc/musl/src/unistd/close.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include -#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); -} diff --git a/lib/libc/musl/src/unistd/posix_close.c b/lib/libc/musl/src/unistd/posix_close.c deleted file mode 100644 index 90f51a82e3..0000000000 --- a/lib/libc/musl/src/unistd/posix_close.c +++ /dev/null @@ -1,6 +0,0 @@ -#include - -int posix_close(int fd, int flags) -{ - return close(fd); -} diff --git a/lib/libc/wasi/libc-bottom-half/sources/__wasilibc_fd_renumber.c b/lib/libc/wasi/libc-bottom-half/sources/__wasilibc_fd_renumber.c index 7690d1359e..bf2b3a2e93 100644 --- a/lib/libc/wasi/libc-bottom-half/sources/__wasilibc_fd_renumber.c +++ b/lib/libc/wasi/libc-bottom-half/sources/__wasilibc_fd_renumber.c @@ -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. -} diff --git a/lib/std/Build/Watch.zig b/lib/std/Build/Watch.zig index c559210b92..fe0a532395 100644 --- a/lib/std/Build/Watch.zig +++ b/lib/std/Build/Watch.zig @@ -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); diff --git a/lib/std/Io/IoUring.zig b/lib/std/Io/IoUring.zig index e4b5bc0fe5..542f5e5fb2 100644 --- a/lib/std/Io/IoUring.zig +++ b/lib/std/Io/IoUring.zig @@ -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; }, } diff --git a/lib/std/Io/Kqueue.zig b/lib/std/Io/Kqueue.zig index 822c34f789..3c9a0cbe1c 100644 --- a/lib/std/Io/Kqueue.zig +++ b/lib/std/Io/Kqueue.zig @@ -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; diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index e378797544..60ee5df945 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -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 + } +} diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index a0e066b546..32dd627e09 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -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)))); } diff --git a/lib/std/os/linux/IoUring.zig b/lib/std/os/linux/IoUring.zig index 5afb5d248c..77fda7389d 100644 --- a/lib/std/os/linux/IoUring.zig +++ b/lib/std/os/linux/IoUring.zig @@ -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; } diff --git a/lib/std/os/linux/IoUring/test.zig b/lib/std/os/linux/IoUring/test.zig index 240a60c2e7..c75253a80f 100644 --- a/lib/std/os/linux/IoUring/test.zig +++ b/lib/std/os/linux/IoUring/test.zig @@ -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)); diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 974ce0a25c..63e0d2f96a 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -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; } diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 1742325026..1e92b39c6f 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -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, diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index e2a52473f3..96d4515dcc 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -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); -} diff --git a/src/libs/musl.zig b/src/libs/musl.zig index ff359a1e93..8cdc4a966f 100644 --- a/src/libs/musl.zig +++ b/src/libs/musl.zig @@ -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", diff --git a/src/libs/wasi_libc.zig b/src/libs/wasi_libc.zig index 49090795ec..ca6231bf53 100644 --- a/src/libs/wasi_libc.zig +++ b/src/libs/wasi_libc.zig @@ -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",