authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-10 23:31:37-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-11 23:37:31+01:00
logb600b6e5e08bc443ef9742b36d51c95715c8150a
treeff9dc624970254de1b3500858df3064095cafb87
parentea30f86113cb1ef2ea53f8d35598061ebb153c8e

std.posix: remove close function


16 files changed, 165 insertions(+), 234 deletions(-)

lib/c/unistd.zig+30
......@@ -14,6 +14,8 @@ comptime {
1414 symbol(&acctLinux, "acct");
1515 symbol(&chdirLinux, "chdir");
1616 symbol(&chownLinux, "chown");
17 symbol(&close, "close");
18 symbol(&posix_close, "posix_close");
1719 symbol(&fchownatLinux, "fchownat");
1820 symbol(&lchownLinux, "lchown");
1921 symbol(&chrootLinux, "chroot");
......@@ -49,6 +51,9 @@ comptime {
4951 if (builtin.target.isMuslLibC() or builtin.target.isWasiLibC()) {
5052 symbol(&swab, "swab");
5153 }
54 if (builtin.target.isWasiLibC()) {
55 symbol(&closeWasi, "close");
56 }
5257}
5358
5459fn _exit(exit_code: c_int) callconv(.c) noreturn {
......@@ -226,3 +231,28 @@ test swab {
226231 swab("abcd", &a, 3);
227232 try std.testing.expectEqualSlices(u8, "ba\x00\x00", &a);
228233}
234
235fn close(fd: std.c.fd_t) callconv(.c) c_int {
236 const signed: isize = @bitCast(linux.close(fd));
237 if (signed < 0) {
238 @branchHint(.unlikely);
239 if (-signed == @intFromEnum(linux.E.INTR)) return 0;
240 std.c._errno().* = @intCast(-signed);
241 return -1;
242 }
243 return 0;
244}
245
246fn posix_close(fd: std.c.fd_t, _: c_int) callconv(.c) c_int {
247 return close(fd);
248}
249
250fn closeWasi(fd: std.c.fd_t) callconv(.c) c_int {
251 switch (std.os.wasi.fd_close(fd)) {
252 .SUCCESS => return 0,
253 else => |e| {
254 std.c._errno().* = @intFromEnum(e);
255 return -1;
256 },
257 }
258}
lib/libc/musl/src/unistd/close.c deleted-19
......@@ -1,19 +0,0 @@
1#include <unistd.h>
2#include <errno.h>
3#include "aio_impl.h"
4#include "syscall.h"
5
6static int dummy(int fd)
7{
8 return fd;
9}
10
11weak_alias(dummy, __aio_close);
12
13int close(int fd)
14{
15 fd = __aio_close(fd);
16 int r = __syscall_cp(SYS_close, fd);
17 if (r == -EINTR) r = 0;
18 return __syscall_ret(r);
19}
lib/libc/musl/src/unistd/posix_close.c deleted-6
......@@ -1,6 +0,0 @@
1#include <unistd.h>
2
3int posix_close(int fd, int flags)
4{
5 return close(fd);
6}
lib/libc/wasi/libc-bottom-half/sources/__wasilibc_fd_renumber.c-38
......@@ -70,41 +70,3 @@ void drop_udp_socket(udp_socket_t socket) {
7070 udp_udp_socket_drop_own(socket.socket);
7171}
7272#endif // __wasilibc_use_wasip2
73
74int close(int fd) {
75 // Scan the preopen fds before making any changes.
76 __wasilibc_populate_preopens();
77
78#ifdef __wasilibc_use_wasip2
79 descriptor_table_entry_t entry;
80 if (descriptor_table_remove(fd, &entry)) {
81
82 switch (entry.tag)
83 {
84 case DESCRIPTOR_TABLE_ENTRY_TCP_SOCKET:
85 drop_tcp_socket(entry.tcp_socket);
86 break;
87 case DESCRIPTOR_TABLE_ENTRY_UDP_SOCKET:
88 drop_udp_socket(entry.udp_socket);
89 break;
90 default: /* unreachable */ abort();
91 }
92
93 return 0;
94 }
95#endif // __wasilibc_use_wasip2
96
97 __wasi_errno_t error = __wasi_fd_close(fd);
98 if (error != 0) {
99 errno = error;
100 return -1;
101 }
102
103 return 0;
104}
105
106weak void __wasilibc_populate_preopens(void) {
107 // This version does nothing. It may be overridden by a version which does
108 // something if `__wasilibc_find_abspath` or `__wasilibc_find_relpath` are
109 // used.
110}
lib/std/Build/Watch.zig+2-2
......@@ -693,7 +693,7 @@ const Os = switch (builtin.os.tag) {
693693 fatal("failed to open directory {f}: {t}", .{ path, err });
694694 };
695695 // Empirically the dir has to stay open or else no events are triggered.
696 errdefer if (!skip_open_dir) posix.close(dir_fd);
696 errdefer if (!skip_open_dir) std.Io.Threaded.closeFd(dir_fd);
697697 const changes = [1]posix.Kevent{.{
698698 .ident = @bitCast(@as(isize, dir_fd)),
699699 .filter = std.c.EVFILT.VNODE,
......@@ -793,7 +793,7 @@ const Os = switch (builtin.os.tag) {
793793 };
794794 const filtered_changes = if (i == handles.len - 1) changes[0..1] else &changes;
795795 _ = try Io.Kqueue.kevent(w.os.kq_fd, filtered_changes, &.{}, null);
796 if (path.sub_path.len != 0) posix.close(dir_fd);
796 if (path.sub_path.len != 0) std.Io.Threaded.closeFd(dir_fd);
797797
798798 w.dir_table.swapRemoveAt(i);
799799 handles.swapRemove(i);
lib/std/Io/IoUring.zig+1-1
......@@ -549,7 +549,7 @@ const CachedFd = struct {
549549 .initializing => unreachable,
550550 _ => |fd| {
551551 assert(@intFromEnum(fd) >= 0);
552 std.posix.close(@intFromEnum(fd));
552 _ = std.os.linux.close(@intFromEnum(fd));
553553 cached_fd.* = .init;
554554 },
555555 }
lib/std/Io/Kqueue.zig+8-7
......@@ -11,6 +11,7 @@ const Allocator = std.mem.Allocator;
1111const Alignment = std.mem.Alignment;
1212const IpAddress = std.Io.net.IpAddress;
1313const errnoBug = std.Io.Threaded.errnoBug;
14const closeFd = std.Io.Threaded.closeFd;
1415const posix = std.posix;
1516
1617/// Must be a thread-safe allocator.
......@@ -64,7 +65,7 @@ const Thread = struct {
6465 };
6566
6667 fn deinit(thread: *Thread, gpa: Allocator) void {
67 posix.close(thread.kq_fd);
68 closeFd(thread.kq_fd);
6869 assert(thread.wait_queues.count() == 0);
6970 thread.wait_queues.deinit(gpa);
7071 thread.* = undefined;
......@@ -212,7 +213,7 @@ pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void {
212213 .steal_ready_search_index = 1,
213214 .wait_queues = .empty,
214215 };
215 errdefer std.posix.close(main_thread.kq_fd);
216 errdefer closeFd(main_thread.kq_fd);
216217 std.log.debug("created main idle {*}", .{&main_thread.idle_context});
217218 std.log.debug("created main {*}", .{main_fiber});
218219}
......@@ -371,7 +372,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void {
371372 .stack_size = idle_stack_size,
372373 .allocator = k.gpa,
373374 }, threadEntry, .{ k, new_thread_index }) catch |err| {
374 posix.close(new_thread.kq_fd);
375 closeFd(new_thread.kq_fd);
375376 @atomicStore(u32, &k.threads.reserved, new_thread_index, .release);
376377 // no more access to `thread` after giving up reservation
377378 std.log.warn("unable to create worker thread due spawn failure: {s}", .{@errorName(err)});
......@@ -1234,7 +1235,7 @@ fn netBindIp(
12341235 const k: *Kqueue = @ptrCast(@alignCast(userdata));
12351236 const family = Io.Threaded.posixAddressFamily(address);
12361237 const socket_fd = try openSocketPosix(k, family, options);
1237 errdefer std.posix.close(socket_fd);
1238 errdefer closeFd(socket_fd);
12381239 var storage: Io.Threaded.PosixAddress = undefined;
12391240 var addr_len = Io.Threaded.addressToPosix(address, &storage);
12401241 try posixBind(k, socket_fd, &storage.any, addr_len);
......@@ -1252,7 +1253,7 @@ fn netConnectIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: n
12521253 .mode = options.mode,
12531254 .protocol = options.protocol,
12541255 });
1255 errdefer posix.close(socket_fd);
1256 errdefer closeFd(socket_fd);
12561257 var storage: Io.Threaded.PosixAddress = undefined;
12571258 var addr_len = Io.Threaded.addressToPosix(address, &storage);
12581259 try posixConnect(k, socket_fd, &storage.any, addr_len);
......@@ -1565,7 +1566,7 @@ fn openSocketPosix(
15651566 switch (posix.errno(socket_rc)) {
15661567 .SUCCESS => {
15671568 const fd: posix.fd_t = @intCast(socket_rc);
1568 errdefer posix.close(fd);
1569 errdefer closeFd(fd);
15691570 if (Io.Threaded.socket_flags_unsupported) {
15701571 while (true) {
15711572 try k.checkCancel();
......@@ -1614,7 +1615,7 @@ fn openSocketPosix(
16141615 else => |err| return posix.unexpectedErrno(err),
16151616 }
16161617 };
1617 errdefer posix.close(socket_fd);
1618 errdefer closeFd(socket_fd);
16181619
16191620 if (options.ip6_only) {
16201621 if (posix.IPV6 == void) return error.OptionUnsupported;
lib/std/Io/Threaded.zig+64-38
......@@ -303,7 +303,7 @@ pub const NullFile = switch (native_os) {
303303
304304 fn deinit(this: *@This()) void {
305305 if (this.fd >= 0) {
306 posix.close(this.fd);
306 closeFd(this.fd);
307307 this.fd = -1;
308308 }
309309 }
......@@ -4337,7 +4337,7 @@ fn dirCreateFilePosix(
43374337 }
43384338 }
43394339 };
4340 errdefer posix.close(fd);
4340 errdefer closeFd(fd);
43414341
43424342 if (have_flock and !have_flock_open_flags and flags.lock != .none) {
43434343 const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0;
......@@ -4917,7 +4917,7 @@ fn dirOpenFilePosix(
49174917 }
49184918 }
49194919 };
4920 errdefer posix.close(fd);
4920 errdefer closeFd(fd);
49214921
49224922 if (!flags.allow_directory) {
49234923 const is_dir = is_dir: {
......@@ -5241,7 +5241,7 @@ fn dirOpenFileWasi(
52415241 },
52425242 }
52435243 }
5244 errdefer posix.close(fd);
5244 errdefer closeFd(fd);
52455245
52465246 if (!flags.allow_directory) {
52475247 const is_dir = is_dir: {
......@@ -5457,7 +5457,13 @@ pub fn dirOpenDirWindows(
54575457fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void {
54585458 const t: *Threaded = @ptrCast(@alignCast(userdata));
54595459 _ = t;
5460 for (dirs) |dir| posix.close(dir.handle);
5460 for (dirs) |dir| {
5461 if (is_windows) {
5462 windows.CloseHandle(dir.handle);
5463 } else {
5464 closeFd(dir.handle);
5465 }
5466 }
54615467}
54625468
54635469const dirRead = switch (native_os) {
......@@ -6777,7 +6783,7 @@ fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, o
67776783 },
67786784 }
67796785 };
6780 defer posix.close(fd);
6786 defer closeFd(fd);
67816787 return realPathPosix(fd, out_buffer);
67826788}
67836789
......@@ -8367,7 +8373,7 @@ fn fchmodatFallback(
83678373 }
83688374 }
83698375 };
8370 defer posix.close(path_fd);
8376 defer closeFd(path_fd);
83718377
83728378 const path_mode = mode: {
83738379 const sys = if (statx_use_c) std.c else std.os.linux;
......@@ -9592,7 +9598,13 @@ fn dirHardLink(
95929598fn fileClose(userdata: ?*anyopaque, files: []const File) void {
95939599 const t: *Threaded = @ptrCast(@alignCast(userdata));
95949600 _ = t;
9595 for (files) |file| posix.close(file.handle);
9601 for (files) |file| {
9602 if (is_windows) {
9603 windows.CloseHandle(file.handle);
9604 } else {
9605 closeFd(file.handle);
9606 }
9607 }
95969608}
95979609
95989610fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.ReadStreamingError!usize {
......@@ -11783,7 +11795,7 @@ fn netListenIpPosix(
1178311795 .mode = options.mode,
1178411796 .protocol = options.protocol,
1178511797 });
11786 errdefer posix.close(socket_fd);
11798 errdefer closeFd(socket_fd);
1178711799
1178811800 if (options.reuse_address) {
1178911801 try setSocketOption(socket_fd, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1);
......@@ -11946,7 +11958,7 @@ fn netListenUnixPosix(
1194611958 error.OptionUnsupported => return error.Unexpected,
1194711959 else => |e| return e,
1194811960 };
11949 errdefer posix.close(socket_fd);
11961 errdefer closeFd(socket_fd);
1195011962
1195111963 var storage: UnixAddress = undefined;
1195211964 const addr_len = addressUnixToPosix(address, &storage);
......@@ -12362,7 +12374,7 @@ fn netConnectIpPosix(
1236212374 .mode = options.mode,
1236312375 .protocol = options.protocol,
1236412376 });
12365 errdefer posix.close(socket_fd);
12377 errdefer closeFd(socket_fd);
1236612378 var storage: PosixAddress = undefined;
1236712379 var addr_len = addressToPosix(address, &storage);
1236812380 try posixConnect(socket_fd, &storage.any, addr_len);
......@@ -12462,7 +12474,7 @@ fn netConnectUnixPosix(
1246212474 error.OptionUnsupported => return error.Unexpected,
1246312475 else => |e| return e,
1246412476 };
12465 errdefer posix.close(socket_fd);
12477 errdefer closeFd(socket_fd);
1246612478 var storage: UnixAddress = undefined;
1246712479 const addr_len = addressUnixToPosix(address, &storage);
1246812480 try posixConnectUnix(socket_fd, &storage.any, addr_len);
......@@ -12536,7 +12548,7 @@ fn netBindIpPosix(
1253612548 _ = t;
1253712549 const family = posixAddressFamily(address);
1253812550 const socket_fd = try openSocketPosix(family, options);
12539 errdefer posix.close(socket_fd);
12551 errdefer closeFd(socket_fd);
1254012552 var storage: PosixAddress = undefined;
1254112553 var addr_len = addressToPosix(address, &storage);
1254212554 try posixBind(socket_fd, &storage.any, addr_len);
......@@ -12642,7 +12654,7 @@ fn openSocketPosix(
1264212654 .SUCCESS => {
1264312655 syscall.finish();
1264412656 const fd: posix.fd_t = @intCast(rc);
12645 errdefer posix.close(fd);
12657 errdefer closeFd(fd);
1264612658 if (socket_flags_unsupported) try setCloexec(fd);
1264712659 break fd;
1264812660 },
......@@ -12661,7 +12673,7 @@ fn openSocketPosix(
1266112673 else => |err| return syscall.unexpectedErrno(err),
1266212674 }
1266312675 };
12664 errdefer posix.close(socket_fd);
12676 errdefer closeFd(socket_fd);
1266512677
1266612678 if (options.ip6_only) {
1266712679 if (posix.IPV6 == void) return error.OptionUnsupported;
......@@ -12707,8 +12719,8 @@ fn netSocketCreatePair(
1270712719 .SUCCESS => {
1270812720 syscall.finish();
1270912721 errdefer {
12710 posix.close(sockets[0]);
12711 posix.close(sockets[1]);
12722 closeFd(sockets[0]);
12723 closeFd(sockets[1]);
1271212724 }
1271312725 if (socket_flags_unsupported) {
1271412726 try setCloexec(sockets[0]);
......@@ -12809,7 +12821,7 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve
1280912821 .SUCCESS => {
1281012822 syscall.finish();
1281112823 const fd: posix.fd_t = @intCast(rc);
12812 errdefer posix.close(fd);
12824 errdefer closeFd(fd);
1281312825 if (!have_accept4) try setCloexec(fd);
1281412826 break fd;
1281512827 },
......@@ -13679,7 +13691,7 @@ fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
1367913691 _ = t;
1368013692 switch (native_os) {
1368113693 .windows => for (handles) |handle| closeSocketWindows(handle),
13682 else => for (handles) |handle| posix.close(handle),
13694 else => for (handles) |handle| closeFd(handle),
1368313695 }
1368413696}
1368513697
......@@ -13787,7 +13799,7 @@ fn netInterfaceNameResolve(
1378713799 error.OptionUnsupported => return error.Unexpected,
1378813800 else => |e| return e,
1378913801 };
13790 defer posix.close(sock_fd);
13802 defer closeFd(sock_fd);
1379113803
1379213804 var ifr: posix.ifreq = .{
1379313805 .ifrn = .{ .name = @bitCast(name.bytes) },
......@@ -15329,13 +15341,13 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
1532915341 const pid: posix.pid_t = @intCast(pid_result); // We are the parent.
1533015342 errdefer comptime unreachable; // The child is forked; we must not error from now on
1533115343
15332 posix.close(err_pipe[1]); // make sure only the child holds the write end open
15344 closeFd(err_pipe[1]); // make sure only the child holds the write end open
1533315345
15334 if (options.stdin == .pipe) posix.close(stdin_pipe[0]);
15335 if (options.stdout == .pipe) posix.close(stdout_pipe[1]);
15336 if (options.stderr == .pipe) posix.close(stderr_pipe[1]);
15346 if (options.stdin == .pipe) closeFd(stdin_pipe[0]);
15347 if (options.stdout == .pipe) closeFd(stdout_pipe[1]);
15348 if (options.stderr == .pipe) closeFd(stderr_pipe[1]);
1533715349
15338 if (prog_pipe[1] != -1) posix.close(prog_pipe[1]);
15350 if (prog_pipe[1] != -1) closeFd(prog_pipe[1]);
1533915351 options.progress_node.setIpcFile(t, .{ .handle = prog_pipe[0], .flags = .{ .nonblocking = true } });
1534015352
1534115353 return .{
......@@ -15373,7 +15385,7 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {
1537315385 mutexLock(&t.mutex); // Another thread might have won the race.
1537415386 defer mutexUnlock(&t.mutex);
1537515387 if (t.null_file.fd != -1) {
15376 posix.close(fresh_fd);
15388 closeFd(fresh_fd);
1537715389 return t.null_file.fd;
1537815390 } else {
1537915391 t.null_file.fd = fresh_fd;
......@@ -15399,7 +15411,7 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {
1539915411fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
1540015412 const t: *Threaded = @ptrCast(@alignCast(userdata));
1540115413 const spawned = try spawnPosix(t, options);
15402 defer posix.close(spawned.err_fd);
15414 defer closeFd(spawned.err_fd);
1540315415
1540415416 // Wait for the child to report any errors in or before `execvpe`.
1540515417 if (readIntFd(spawned.err_fd)) |child_err_int| {
......@@ -15666,15 +15678,15 @@ fn childKillPosix(child: *process.Child) !void {
1566615678
1566715679fn childCleanupPosix(child: *process.Child) void {
1566815680 if (child.stdin) |*stdin| {
15669 posix.close(stdin.handle);
15681 closeFd(stdin.handle);
1567015682 child.stdin = null;
1567115683 }
1567215684 if (child.stdout) |*stdout| {
15673 posix.close(stdout.handle);
15685 closeFd(stdout.handle);
1567415686 child.stdout = null;
1567515687 }
1567615688 if (child.stderr) |*stderr| {
15677 posix.close(stderr.handle);
15689 closeFd(stderr.handle);
1567815690 child.stderr = null;
1567915691 }
1568015692 child.id = null;
......@@ -15743,14 +15755,14 @@ fn readIntFd(fd: posix.fd_t) !ErrInt {
1574315755const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
1574415756
1574515757fn destroyPipe(pipe: [2]posix.fd_t) void {
15746 if (pipe[0] != -1) posix.close(pipe[0]);
15747 if (pipe[0] != pipe[1]) posix.close(pipe[1]);
15758 if (pipe[0] != -1) closeFd(pipe[0]);
15759 if (pipe[0] != pipe[1]) closeFd(pipe[1]);
1574815760}
1574915761
1575015762fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
1575115763 switch (stdio) {
1575215764 .pipe => try dup2(pipe_fd, std_fileno),
15753 .close => posix.close(std_fileno),
15765 .close => closeFd(std_fileno),
1575415766 .inherit => {},
1575515767 .ignore => try dup2(dev_null_fd, std_fileno),
1575615768 .file => @panic("TODO implement setUpChildIo when file is used"),
......@@ -17439,7 +17451,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
1743917451 }
1744017452 }
1744117453 };
17442 errdefer posix.close(fd);
17454 errdefer closeFd(fd);
1744317455
1744417456 switch (native_os) {
1744517457 .linux => {
......@@ -17454,7 +17466,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
1745417466 mutexLock(&t.mutex); // Another thread might have won the race.
1745517467 defer mutexUnlock(&t.mutex);
1745617468 if (t.random_file.fd >= 0) {
17457 posix.close(fd);
17469 closeFd(fd);
1745817470 return t.random_file.fd;
1745917471 } else if (!posix.S.ISCHR(statx.mode)) {
1746017472 t.random_file.fd = -2;
......@@ -17482,7 +17494,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
1748217494 mutexLock(&t.mutex); // Another thread might have won the race.
1748317495 defer mutexUnlock(&t.mutex);
1748417496 if (t.random_file.fd >= 0) {
17485 posix.close(fd);
17497 closeFd(fd);
1748617498 return t.random_file.fd;
1748717499 } else if (!posix.S.ISCHR(stat.mode)) {
1748817500 t.random_file.fd = -2;
......@@ -18170,8 +18182,8 @@ pub fn pipe2(flags: posix.O) PipeError![2]posix.fd_t {
1817018182 else => |err| return posix.unexpectedErrno(err),
1817118183 }
1817218184 errdefer {
18173 posix.close(fds[0]);
18174 posix.close(fds[1]);
18185 closeFd(fds[0]);
18186 closeFd(fds[1]);
1817518187 }
1817618188
1817718189 // https://github.com/ziglang/zig/issues/18882
......@@ -19164,3 +19176,17 @@ fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!windows
1916419176 }
1916519177 }
1916619178}
19179
19180pub fn closeFd(fd: posix.fd_t) void {
19181 if (native_os == .wasi and !builtin.link_libc) {
19182 switch (std.os.wasi.fd_close(fd)) {
19183 .SUCCESS, .INTR => {},
19184 .BADF => recoverableOsBugDetected(), // use after free
19185 else => recoverableOsBugDetected(), // unexpected failure
19186 }
19187 } else switch (posix.errno(posix.system.close(fd))) {
19188 .SUCCESS, .INTR => {}, // INTR still a success, see https://github.com/ziglang/zig/issues/2425
19189 .BADF => recoverableOsBugDetected(), // use after free
19190 else => recoverableOsBugDetected(), // unexpected failure
19191 }
19192}
lib/std/os/linux.zig+1-1
......@@ -1571,7 +1571,7 @@ pub fn clone2(flags: u32, child_stack_ptr: usize) usize {
15711571 return syscall2(.clone, flags, child_stack_ptr);
15721572}
15731573
1574pub fn close(fd: i32) usize {
1574pub fn close(fd: fd_t) usize {
15751575 return syscall1(.close, @as(usize, @bitCast(@as(isize, fd))));
15761576}
15771577
lib/std/os/linux/IoUring.zig+2-2
......@@ -67,7 +67,7 @@ pub fn init_params(entries: u16, p: *linux.io_uring_params) !IoUring {
6767 }
6868 const fd = @as(linux.fd_t, @intCast(res));
6969 assert(fd >= 0);
70 errdefer posix.close(fd);
70 errdefer _ = linux.close(fd);
7171
7272 // Kernel versions 5.4 and up use only one mmap() for the submission and completion queues.
7373 // 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 {
125125 // The mmaps depend on the fd, so the order of these calls is important:
126126 self.cq.deinit();
127127 self.sq.deinit();
128 posix.close(self.fd);
128 _ = linux.close(self.fd);
129129 self.fd = -1;
130130}
131131
lib/std/os/linux/IoUring/test.zig+21-21
......@@ -440,7 +440,7 @@ test "openat" {
440440 try testing.expect(cqe_openat.res > 0);
441441 try testing.expectEqual(@as(u32, 0), cqe_openat.flags);
442442
443 posix.close(cqe_openat.res);
443 _ = linux.close(cqe_openat.res);
444444}
445445
446446test "close" {
......@@ -530,7 +530,7 @@ test "sendmsg/recvmsg" {
530530 };
531531
532532 const server = try socket(address_server.family, posix.SOCK.DGRAM, 0);
533 defer posix.close(server);
533 defer _ = linux.close(server);
534534 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));
535535 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
536536 try bind(server, addrAny(&address_server), @sizeOf(linux.sockaddr.in));
......@@ -540,7 +540,7 @@ test "sendmsg/recvmsg" {
540540 try getsockname(server, addrAny(&address_server), &slen);
541541
542542 const client = try socket(address_server.family, posix.SOCK.DGRAM, 0);
543 defer posix.close(client);
543 defer _ = linux.close(client);
544544
545545 const buffer_send = [_]u8{42} ** 128;
546546 const iovecs_send = [_]iovec_const{
......@@ -1034,7 +1034,7 @@ test "shutdown" {
10341034 // Socket bound, expect shutdown to work
10351035 {
10361036 const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1037 defer posix.close(server);
1037 defer _ = linux.close(server);
10381038 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
10391039 try bind(server, addrAny(&address), @sizeOf(linux.sockaddr.in));
10401040 try listen(server, 1);
......@@ -1067,7 +1067,7 @@ test "shutdown" {
10671067 // Socket not bound, expect to fail with ENOTCONN
10681068 {
10691069 const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1070 defer posix.close(server);
1070 defer _ = linux.close(server);
10711071
10721072 const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) {
10731073 else => |errno| std.debug.panic("unhandled errno: {}", .{errno}),
......@@ -1741,7 +1741,7 @@ test "accept multishot" {
17411741 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
17421742 };
17431743 const listener_socket = try createListenerSocket(&address);
1744 defer posix.close(listener_socket);
1744 defer _ = linux.close(listener_socket);
17451745
17461746 // submit multishot accept operation
17471747 var addr: posix.sockaddr = undefined;
......@@ -1754,7 +1754,7 @@ test "accept multishot" {
17541754 while (nr > 0) : (nr -= 1) {
17551755 // connect client
17561756 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
1757 errdefer posix.close(client);
1757 errdefer _ = linux.close(client);
17581758 try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
17591759
17601760 // test accept completion
......@@ -1764,7 +1764,7 @@ test "accept multishot" {
17641764 try testing.expect(cqe.user_data == userdata);
17651765 try testing.expect(cqe.flags & linux.IORING_CQE_F_MORE > 0); // more flag is set
17661766
1767 posix.close(client);
1767 _ = linux.close(client);
17681768 }
17691769}
17701770
......@@ -1848,7 +1848,7 @@ test "accept_direct" {
18481848 try ring.register_files(registered_fds[0..]);
18491849
18501850 const listener_socket = try createListenerSocket(&address);
1851 defer posix.close(listener_socket);
1851 defer _ = linux.close(listener_socket);
18521852
18531853 const accept_userdata: u64 = 0xaaaaaaaa;
18541854 const read_userdata: u64 = 0xbbbbbbbb;
......@@ -1866,7 +1866,7 @@ test "accept_direct" {
18661866 // connect
18671867 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
18681868 try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
1869 defer posix.close(client);
1869 defer _ = linux.close(client);
18701870
18711871 // accept completion
18721872 const cqe_accept = try ring.copy_cqe();
......@@ -1900,7 +1900,7 @@ test "accept_direct" {
19001900 // connect
19011901 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
19021902 try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
1903 defer posix.close(client);
1903 defer _ = linux.close(client);
19041904 // completion with error
19051905 const cqe_accept = try ring.copy_cqe();
19061906 try testing.expect(cqe_accept.user_data == accept_userdata);
......@@ -1936,7 +1936,7 @@ test "accept_multishot_direct" {
19361936 try ring.register_files(registered_fds[0..]);
19371937
19381938 const listener_socket = try createListenerSocket(&address);
1939 defer posix.close(listener_socket);
1939 defer _ = linux.close(listener_socket);
19401940
19411941 const accept_userdata: u64 = 0xaaaaaaaa;
19421942
......@@ -1950,7 +1950,7 @@ test "accept_multishot_direct" {
19501950 // connect
19511951 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
19521952 try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
1953 defer posix.close(client);
1953 defer _ = linux.close(client);
19541954
19551955 // accept completion
19561956 const cqe_accept = try ring.copy_cqe();
......@@ -1965,7 +1965,7 @@ test "accept_multishot_direct" {
19651965 // connect
19661966 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
19671967 try connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
1968 defer posix.close(client);
1968 defer _ = linux.close(client);
19691969 // completion with error
19701970 const cqe_accept = try ring.copy_cqe();
19711971 try testing.expect(cqe_accept.user_data == accept_userdata);
......@@ -1998,7 +1998,7 @@ test "socket" {
19981998 const fd: linux.fd_t = @intCast(cqe.res);
19991999 try testing.expect(fd > 2);
20002000
2001 posix.close(fd);
2001 _ = linux.close(fd);
20022002}
20032003
20042004test "socket_direct/socket_direct_alloc/close_direct" {
......@@ -2042,7 +2042,7 @@ test "socket_direct/socket_direct_alloc/close_direct" {
20422042 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
20432043 };
20442044 const listener_socket = try createListenerSocket(&address);
2045 defer posix.close(listener_socket);
2045 defer _ = linux.close(listener_socket);
20462046 const accept_userdata: u64 = 0xaaaaaaaa;
20472047 const connect_userdata: u64 = 0xbbbbbbbb;
20482048 const close_userdata: u64 = 0xcccccccc;
......@@ -2599,8 +2599,8 @@ pub const SocketTestHarness = struct {
25992599 client: posix.socket_t,
26002600
26012601 pub fn close(self: SocketTestHarness) void {
2602 posix.close(self.client);
2603 posix.close(self.listener);
2602 _ = linux.close(self.client);
2603 _ = linux.close(self.listener);
26042604 }
26052605};
26062606
......@@ -2611,7 +2611,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
26112611 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
26122612 };
26132613 const listener_socket = try createListenerSocket(&address);
2614 errdefer posix.close(listener_socket);
2614 errdefer _ = linux.close(listener_socket);
26152615
26162616 // Submit 1 accept
26172617 var accept_addr: posix.sockaddr = undefined;
......@@ -2620,7 +2620,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
26202620
26212621 // Create a TCP client socket
26222622 const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2623 errdefer posix.close(client);
2623 errdefer _ = linux.close(client);
26242624 _ = try ring.connect(0xcccccccc, client, addrAny(&address), @sizeOf(linux.sockaddr.in));
26252625
26262626 try testing.expectEqual(@as(u32, 2), try ring.submit());
......@@ -2660,7 +2660,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
26602660fn createListenerSocket(address: *linux.sockaddr.in) !posix.socket_t {
26612661 const kernel_backlog = 1;
26622662 const listener_socket = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2663 errdefer posix.close(listener_socket);
2663 errdefer _ = linux.close(listener_socket);
26642664
26652665 try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
26662666 try bind(listener_socket, addrAny(address), @sizeOf(linux.sockaddr.in));
lib/std/os/linux/test.zig+36
......@@ -407,6 +407,42 @@ test "futex2_requeue" {
407407 try expectEqual(0, rc);
408408}
409409
410test "timerfd" {
411 const tfd: linux.fd_t = rc: {
412 const rc = linux.timerfd_create(.MONOTONIC, .{ .CLOEXEC = true });
413 switch (linux.errno(rc)) {
414 .SUCCESS => break :rc @intCast(rc),
415 else => @panic("test failed"),
416 }
417 };
418 defer _ = linux.close(tfd);
419
420 // Fire event 10_000_000ns = 10ms after the posix.timerfd_settime call.
421 var sit: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 10 * (1000 * 1000) } };
422 const flags: linux.TFD.TIMER = .{};
423 switch (linux.errno(linux.timerfd_settime(tfd, @bitCast(flags), &sit, null))) {
424 .SUCCESS => {},
425 else => @panic("test failed"),
426 }
427
428 var fds: [1]std.posix.pollfd = .{.{ .fd = tfd, .events = linux.POLL.IN, .revents = 0 }};
429 try expectEqual(@as(usize, 1), try std.posix.poll(&fds, -1)); // -1 => infinite waiting
430
431 const git = rc: {
432 var curr_value: linux.itimerspec = undefined;
433 const rc = linux.timerfd_gettime(tfd, &curr_value);
434 switch (linux.errno(rc)) {
435 .SUCCESS => break :rc curr_value,
436 else => @panic("test failed"),
437 }
438 };
439 const expect_disarmed_timer: linux.itimerspec = .{
440 .it_interval = .{ .sec = 0, .nsec = 0 },
441 .it_value = .{ .sec = 0, .nsec = 0 },
442 };
443 try expectEqual(expect_disarmed_timer, git);
444}
445
410446test {
411447 _ = linux.IoUring;
412448}
lib/std/posix.zig-78
......@@ -278,30 +278,6 @@ pub const socket_t = if (native_os == .windows) windows.ws2_32.SOCKET else fd_t;
278278/// the system function call whose errno value is intended to be observed.
279279pub const errno = system.errno;
280280
281/// Closes the file descriptor.
282///
283/// Asserts the file descriptor is open.
284///
285/// This function is not capable of returning any indication of failure. An
286/// application which wants to ensure writes have succeeded before closing must
287/// call `fsync` before `close`.
288///
289/// The Zig standard library does not support POSIX thread cancellation.
290pub fn close(fd: fd_t) void {
291 if (native_os == .windows) {
292 return windows.CloseHandle(fd);
293 }
294 if (native_os == .wasi and !builtin.link_libc) {
295 _ = std.os.wasi.fd_close(fd);
296 return;
297 }
298 switch (errno(system.close(fd))) {
299 .BADF => unreachable, // Always a race condition.
300 .INTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
301 else => return,
302 }
303}
304
305281pub const RebootError = error{
306282 PermissionDenied,
307283} || UnexpectedError;
......@@ -1541,60 +1517,6 @@ pub fn perf_event_open(
15411517 }
15421518}
15431519
1544pub const TimerFdCreateError = error{
1545 PermissionDenied,
1546 ProcessFdQuotaExceeded,
1547 SystemFdQuotaExceeded,
1548 NoDevice,
1549 SystemResources,
1550} || UnexpectedError;
1551
1552pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError;
1553pub const TimerFdSetError = TimerFdGetError || error{Canceled};
1554
1555pub fn timerfd_create(clock_id: system.timerfd_clockid_t, flags: system.TFD) TimerFdCreateError!fd_t {
1556 const rc = system.timerfd_create(clock_id, @bitCast(flags));
1557 return switch (errno(rc)) {
1558 .SUCCESS => @intCast(rc),
1559 .INVAL => unreachable,
1560 .MFILE => return error.ProcessFdQuotaExceeded,
1561 .NFILE => return error.SystemFdQuotaExceeded,
1562 .NODEV => return error.NoDevice,
1563 .NOMEM => return error.SystemResources,
1564 .PERM => return error.PermissionDenied,
1565 else => |err| return unexpectedErrno(err),
1566 };
1567}
1568
1569pub fn timerfd_settime(
1570 fd: i32,
1571 flags: system.TFD.TIMER,
1572 new_value: *const system.itimerspec,
1573 old_value: ?*system.itimerspec,
1574) TimerFdSetError!void {
1575 const rc = system.timerfd_settime(fd, @bitCast(flags), new_value, old_value);
1576 return switch (errno(rc)) {
1577 .SUCCESS => {},
1578 .BADF => error.InvalidHandle,
1579 .FAULT => unreachable,
1580 .INVAL => unreachable,
1581 .CANCELED => error.Canceled,
1582 else => |err| return unexpectedErrno(err),
1583 };
1584}
1585
1586pub fn timerfd_gettime(fd: i32) TimerFdGetError!system.itimerspec {
1587 var curr_value: system.itimerspec = undefined;
1588 const rc = system.timerfd_gettime(fd, &curr_value);
1589 return switch (errno(rc)) {
1590 .SUCCESS => return curr_value,
1591 .BADF => error.InvalidHandle,
1592 .FAULT => unreachable,
1593 .INVAL => unreachable,
1594 else => |err| return unexpectedErrno(err),
1595 };
1596}
1597
15981520pub const PtraceError = error{
15991521 DeadLock,
16001522 DeviceBusy,
lib/std/posix/test.zig-18
......@@ -522,21 +522,3 @@ test "rename smoke test" {
522522 try expectError(error.FileNotFound, Io.Dir.cwd().openDir(io, file_path, .{}));
523523 }
524524}
525
526test "timerfd" {
527 if (native_os != .linux) return error.SkipZigTest;
528
529 const tfd = try posix.timerfd_create(.MONOTONIC, .{ .CLOEXEC = true });
530 defer posix.close(tfd);
531
532 // Fire event 10_000_000ns = 10ms after the posix.timerfd_settime call.
533 var sit: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 10 * (1000 * 1000) } };
534 try posix.timerfd_settime(tfd, .{}, &sit, null);
535
536 var fds: [1]posix.pollfd = .{.{ .fd = tfd, .events = linux.POLL.IN, .revents = 0 }};
537 try expectEqual(@as(usize, 1), try posix.poll(&fds, -1)); // -1 => infinite waiting
538
539 const git = try posix.timerfd_gettime(tfd);
540 const expect_disarmed_timer: linux.itimerspec = .{ .it_interval = .{ .sec = 0, .nsec = 0 }, .it_value = .{ .sec = 0, .nsec = 0 } };
541 try expectEqual(expect_disarmed_timer, git);
542}
src/libs/musl.zig-2
......@@ -1830,7 +1830,6 @@ const src_files = [_][]const u8{
18301830 "musl/src/time/wcsftime.c",
18311831 "musl/src/time/__year_to_secs.c",
18321832 "musl/src/unistd/alarm.c",
1833 "musl/src/unistd/close.c",
18341833 "musl/src/unistd/dup2.c",
18351834 "musl/src/unistd/dup3.c",
18361835 "musl/src/unistd/faccessat.c",
......@@ -1849,7 +1848,6 @@ const src_files = [_][]const u8{
18491848 "musl/src/unistd/nice.c",
18501849 "musl/src/unistd/pause.c",
18511850 "musl/src/unistd/pipe2.c",
1852 "musl/src/unistd/posix_close.c",
18531851 "musl/src/unistd/pread.c",
18541852 "musl/src/unistd/preadv.c",
18551853 "musl/src/unistd/pwrite.c",
src/libs/wasi_libc.zig-1
......@@ -999,7 +999,6 @@ const libc_top_half_src_files = [_][]const u8{
999999 "musl/src/time/strptime.c",
10001000 "musl/src/time/timespec_get.c",
10011001 "musl/src/time/__year_to_secs.c",
1002 "musl/src/unistd/posix_close.c",
10031002
10041003 "wasi/libc-top-half/musl/src/conf/fpathconf.c",
10051004 "wasi/libc-top-half/musl/src/conf/sysconf.c",