From 6eb5e56306620e72ae80d7ad6626f8d0ceb24af5 Mon Sep 17 00:00:00 2001 From: Pat Tullmann Date: Mon, 28 Apr 2025 21:03:23 -0700 Subject: [PATCH] std.posix: Add sigrtmin() and sigrtmax() For C code the macros SIGRTMIN and SIGRTMAX provide these values. In practice what looks like a constant is actually provided by a libc call. So the Zig implementations are explicitly function calls. glibc (and Musl) export a run-time minimum "real-time" signal number, based on how many signals are reserved for internal implementation details (generally threading). In practice, on Linux, sigrtmin() is 35 on glibc with the older LinuxThread and 34 with the newer NPTL-based implementation. Musl always returns 35. The maximum "real-time" signal number is NSIG - 1 (64 on most Linux kernels, but 128 on MIPS). When not linking a C Library, Zig can report the full range of "rt" signals (none are reserved by Zig). Fixes #21189 --- lib/std/c.zig | 13 +++++++++++++ lib/std/os/linux.zig | 13 +++++++++++++ lib/std/posix.zig | 2 ++ lib/std/posix/test.zig | 18 ++++++++++++++++-- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index d5560c3f6f..3e1ddacf01 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -10347,6 +10347,16 @@ pub const sigaction = switch (native_os) { else => private.sigaction, }; +/// Zig's version of SIGRTMIN. Actually a function. +pub fn sigrtmin() u8 { + return @truncate(@as(c_uint, @bitCast(private.__libc_current_sigrtmin()))); +} + +/// Zig's version of SIGRTMAX. Actually a function. +pub fn sigrtmax() u8 { + return @truncate(@as(c_uint, @bitCast(private.__libc_current_sigrtmax()))); +} + pub const sigfillset = switch (native_os) { .netbsd => private.__sigfillset14, else => private.sigfillset, @@ -11213,6 +11223,9 @@ const private = struct { extern "c" fn __getdents30(fd: c_int, buf_ptr: [*]u8, nbytes: usize) c_int; extern "c" fn __sigaltstack14(ss: ?*stack_t, old_ss: ?*stack_t) c_int; + extern "c" fn __libc_current_sigrtmin() c_int; + extern "c" fn __libc_current_sigrtmax() c_int; + // Don't forget to add another clown when an OS picks yet another unique // symbol name for errno location! // 🤡🤡🤡🤡🤡🤡 diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index f055022203..5c8a7ca20d 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1801,6 +1801,19 @@ const SigsetElement = c_ulong; const sigset_len = @typeInfo(sigset_t).array.len; +/// Zig's SIGRTMIN, but is a function for compatibility with glibc +pub fn sigrtmin() u8 { + // Default is 32 in the kernel UAPI: https://github.com/torvalds/linux/blob/78109c591b806e41987e0b83390e61d675d1f724/include/uapi/asm-generic/signal.h#L50 + // AFAICT, all architectures that override this also set it to 32: + // https://github.com/search?q=repo%3Atorvalds%2Flinux+sigrtmin+path%3Auapi&type=code + return 32; +} + +/// Zig's SIGRTMAX, but is a function for compatibility with glibc +pub fn sigrtmax() u8 { + return NSIG - 1; +} + /// Zig's version of sigemptyset. Returns initialized sigset_t. pub fn sigemptyset() sigset_t { return [_]SigsetElement{0} ** sigset_len; diff --git a/lib/std/posix.zig b/lib/std/posix.zig index f414aa801b..936c19591a 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -151,6 +151,8 @@ pub const rusage = system.rusage; pub const sa_family_t = system.sa_family_t; pub const siginfo_t = system.siginfo_t; pub const sigset_t = system.sigset_t; +pub const sigrtmin = system.sigrtmin; +pub const sigrtmax = system.sigrtmax; pub const sockaddr = system.sockaddr; pub const socklen_t = system.socklen_t; pub const stack_t = system.stack_t; diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index c886ed6153..7d4b44ed04 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -859,6 +859,17 @@ test "shutdown socket" { std.net.Stream.close(.{ .handle = sock }); } +test "sigrtmin/max" { + if (native_os == .wasi or native_os == .windows or native_os == .macos) { + return error.SkipZigTest; + } + + try std.testing.expect(posix.sigrtmin() >= 32); + try std.testing.expect(posix.sigrtmin() >= posix.system.sigrtmin()); + try std.testing.expect(posix.sigrtmin() < posix.system.sigrtmax()); + try std.testing.expect(posix.sigrtmax() < posix.NSIG); +} + test "sigset empty/full" { if (native_os == .wasi or native_os == .windows) return error.SkipZigTest; @@ -875,10 +886,13 @@ test "sigset empty/full" { try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.INT))); } -// Some signals (32 - 34 on glibc/musl) are not allowed to be added to a +// Some signals (i.e., 32 - 34 on glibc/musl) are not allowed to be added to a // sigset by the C library, so avoid testing them. fn reserved_signo(i: usize) bool { - return builtin.link_libc and (i >= 32 and i <= 34); + if (native_os == .macos) { + return false; + } + return builtin.link_libc and (i >= 32 and i < posix.sigrtmin()); } test "sigset add/del" {