authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-30 20:24:00+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-30 20:24:00+01:00
log1eb8fe7c61d1de3b23afb00c0bb4729f3a0b037f
tree53021761cebbf7e7ddb53d77617b104fac54804f
parent2a02c7a0d59e25bac07a6ed2948a29438fb05527
parent4b26c49076a1c163e5fdd1bd3a657e0794e5a917

Merge pull request 'bsd-futex' (#30626) from mikdusan/zig:bsd-futex into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30626 Reviewed-by: Andrew Kelley <andrewrk@noreply.codeberg.org>

6 files changed, 97 insertions(+), 11 deletions(-)

lib/compiler/test_runner.zig+10-6
......@@ -66,13 +66,13 @@ pub fn main() void {
6666 }
6767
6868 if (listen) {
69 return mainServer() catch @panic("internal test runner failure");
69 return mainServer(args) catch @panic("internal test runner failure");
7070 } else {
71 return mainTerminal();
71 return mainTerminal(args);
7272 }
7373}
7474
75fn mainServer() !void {
75fn mainServer(args: []const [:0]const u8) !void {
7676 @disableInstrumentation();
7777 var stdin_reader = Io.File.stdin().readerStreaming(runner_threaded_io, &stdin_buffer);
7878 var stdout_writer = Io.File.stdout().writerStreaming(runner_threaded_io, &stdout_buffer);
......@@ -131,7 +131,9 @@ fn mainServer() !void {
131131
132132 .run_test => {
133133 testing.allocator_instance = .{};
134 testing.io_instance = .init(testing.allocator, .{});
134 testing.io_instance = .init(testing.allocator, .{
135 .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{},
136 });
135137 log_err_count = 0;
136138 const index = try server.receiveBody_u32();
137139 const test_fn = builtin.test_functions[index];
......@@ -215,7 +217,7 @@ fn mainServer() !void {
215217 }
216218}
217219
218fn mainTerminal() void {
220fn mainTerminal(args: []const [:0]const u8) void {
219221 @disableInstrumentation();
220222 if (builtin.fuzz) @panic("fuzz test requires server");
221223
......@@ -233,7 +235,9 @@ fn mainTerminal() void {
233235 var leaks: usize = 0;
234236 for (test_fn_list, 0..) |test_fn, i| {
235237 testing.allocator_instance = .{};
236 testing.io_instance = .init(testing.allocator, .{});
238 testing.io_instance = .init(testing.allocator, .{
239 .argv0 = if (@hasField(Io.Threaded.Argv0, "value")) .{ .value = args[0] } else .{},
240 });
237241 defer {
238242 testing.io_instance.deinit();
239243 if (testing.allocator_instance.deinit() == .leak) leaks += 1;
lib/std/Io/Dir.zig+1
......@@ -113,6 +113,7 @@ pub const Reader = struct {
113113 },
114114 .wasi => @sizeOf(std.os.wasi.dirent_t) +
115115 std.mem.alignForward(usize, max_name_bytes, @alignOf(std.os.wasi.dirent_t)),
116 .openbsd => std.c.S.BLKSIZE,
116117 else => if (builtin.link_libc) @sizeOf(std.c.dirent) else std.mem.alignForward(usize, max_name_bytes, @alignOf(usize)),
117118 };
118119
lib/std/Io/Threaded.zig+63
......@@ -390,6 +390,52 @@ const Thread = struct {
390390 else => unreachable,
391391 };
392392 },
393 .openbsd => {
394 var tm: std.c.timespec = undefined;
395 var tm_ptr: ?*const std.c.timespec = null;
396 if (timeout_ns) |ns| {
397 tm_ptr = &tm;
398 tm = timestampToPosix(ns);
399 }
400 if (thread) |t| try t.beginSyscall();
401 const rc = std.c.futex(
402 ptr,
403 std.c.FUTEX.WAIT | std.c.FUTEX.PRIVATE_FLAG,
404 @as(c_int, @bitCast(expect)),
405 tm_ptr,
406 null, // uaddr2 is ignored
407 );
408 if (thread) |t| t.endSyscall();
409 if (is_debug) switch (posix.errno(rc)) {
410 .SUCCESS => {},
411 .NOSYS => unreachable, // constant op known good value
412 .AGAIN => {}, // contents of uaddr != val
413 .INVAL => unreachable, // invalid timeout
414 .TIMEDOUT => {}, // timeout
415 .INTR => {}, // a signal arrived
416 .CANCELED => {}, // a signal arrived and SA_RESTART was set
417 else => unreachable,
418 };
419 },
420 .dragonfly => {
421 var timeout_us: c_int = undefined;
422 if (timeout_ns) |ns| {
423 timeout_us = std.math.cast(c_int, ns / std.time.ns_per_us) orelse std.math.maxInt(c_int);
424 } else {
425 timeout_us = 0;
426 }
427 if (thread) |t| try t.beginSyscall();
428 const rc = std.c.umtx_sleep(@ptrCast(ptr), @bitCast(expect), timeout_us);
429 if (thread) |t| t.endSyscall();
430 if (is_debug) switch (std.posix.errno(rc)) {
431 .SUCCESS => {},
432 .BUSY => {}, // ptr != expect
433 .AGAIN => {}, // maybe timed out, or paged out, or hit 2s kernel refresh
434 .INTR => {}, // spurious wake
435 .INVAL => unreachable, // invalid timeout
436 else => unreachable,
437 };
438 },
393439 else => if (std.Thread.use_pthreads) {
394440 // TODO integrate the following function being called with robust cancelation.
395441 return pthreads_futex.wait(ptr, expect, timeout_ns) catch |err| switch (err) {
......@@ -473,6 +519,23 @@ const Thread = struct {
473519 else => unreachable, // deadlock due to operating system bug
474520 }
475521 },
522 .openbsd => {
523 const rc = std.c.futex(
524 ptr,
525 std.c.FUTEX.WAKE | std.c.FUTEX.PRIVATE_FLAG,
526 @min(max_waiters, std.math.maxInt(c_int)),
527 null, // timeout is ignored
528 null, // uaddr2 is ignored
529 );
530 assert(rc >= 0);
531 },
532 .dragonfly => {
533 // will generally return 0 unless the address is bad
534 _ = std.c.umtx_wakeup(
535 @ptrCast(ptr),
536 @min(max_waiters, std.math.maxInt(c_int)),
537 );
538 },
476539 else => if (std.Thread.use_pthreads) {
477540 return pthreads_futex.wake(ptr, max_waiters);
478541 } else {
lib/std/c.zig+15
......@@ -167,6 +167,18 @@ pub const timespec = switch (native_os) {
167167 .openbsd, .haiku => extern struct {
168168 sec: time_t,
169169 nsec: isize,
170
171 /// For use with `utimensat` and `futimens`.
172 pub const NOW: timespec = .{
173 .sec = 0, // ignored
174 .nsec = -2,
175 };
176
177 /// For use with `utimensat` and `futimens`.
178 pub const OMIT: timespec = .{
179 .sec = 0, // ignored
180 .nsec = -1,
181 };
170182 },
171183 else => void,
172184};
......@@ -2365,6 +2377,8 @@ pub const S = switch (native_os) {
23652377 pub const IWOTH = 0o002;
23662378 pub const IXOTH = 0o001;
23672379
2380 pub const BLKSIZE = 512;
2381
23682382 pub fn ISFIFO(m: u32) bool {
23692383 return m & IFMT == IFIFO;
23702384 }
......@@ -9676,6 +9690,7 @@ pub const NSIG = switch (native_os) {
96769690 .illumos => 75,
96779691 // https://github.com/SerenityOS/serenity/blob/046c23f567a17758d762a33bdf04bacbfd088f9f/Kernel/API/POSIX/signal_numbers.h#L42
96789692 .openbsd, .serenity => 33,
9693 .dragonfly => 64,
96799694 else => {},
96809695};
96819696
lib/std/fs/test.zig+3-1
......@@ -645,6 +645,7 @@ fn contains(entries: *const std.array_list.Managed(Dir.Entry), el: Dir.Entry) bo
645645
646646test "Dir.realPath smoke test" {
647647 if (native_os == .wasi) return error.SkipZigTest;
648 if (native_os == .openbsd) return error.SkipZigTest;
648649
649650 try testWithAllSupportedPathTypes(struct {
650651 fn impl(ctx: *TestContext) !void {
......@@ -820,7 +821,7 @@ test "file operations on directories" {
820821 try expectError(error.IsDir, ctx.dir.createFile(io, test_dir_name, .{}));
821822 try expectError(error.IsDir, ctx.dir.deleteFile(io, test_dir_name));
822823 switch (native_os) {
823 .dragonfly, .netbsd => {
824 .netbsd => {
824825 // no error when reading a directory. See https://github.com/ziglang/zig/issues/5732
825826 const buf = try ctx.dir.readFileAlloc(io, test_dir_name, testing.allocator, .unlimited);
826827 testing.allocator.free(buf);
......@@ -1240,6 +1241,7 @@ test "createDirPath, put some files in it, deleteTreeMinStackSize" {
12401241
12411242test "createDirPath in a directory that no longer exists" {
12421243 if (native_os == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir
1244 if (native_os == .dragonfly) return error.SkipZigTest; // DragonflyBSD does not produce error (hammer2 fs)
12431245
12441246 const io = testing.io;
12451247
lib/std/posix/test.zig+5-4
......@@ -364,9 +364,10 @@ test "getrlimit and setrlimit" {
364364}
365365
366366test "sigrtmin/max" {
367 if (native_os == .wasi or native_os == .windows or native_os.isDarwin() or native_os == .openbsd) {
368 return error.SkipZigTest;
369 }
367 if (native_os.isDarwin() or switch (native_os) {
368 .wasi, .windows, .openbsd, .dragonfly => true,
369 else => false,
370 }) return error.SkipZigTest;
370371
371372 try expect(posix.sigrtmin() >= 32);
372373 try expect(posix.sigrtmin() >= posix.system.sigrtmin());
......@@ -397,7 +398,7 @@ fn reserved_signo(i: usize) bool {
397398 if (!builtin.link_libc) return false;
398399 const max = if (native_os == .netbsd) 32 else 31;
399400 if (i > max) return true;
400 if (native_os == .openbsd) return false; // no RT signals
401 if (native_os == .openbsd or native_os == .dragonfly) return false; // no RT signals
401402 return i < posix.sigrtmin();
402403}
403404