Commit 120c4789c3

Pat Tullmann <pat.github@tullmann.org>
2025-04-22 23:19:06
sigset_t: sigemptyset() and sigfillset() are functions that return sigset_t
By returning an initialized sigset (instead of taking the set as an output parameter), these functions can be used to directly initialize the `mask` parameter of a `Sigaction` instance.
1 parent f0aefa6
lib/std/os/linux/test.zig
@@ -128,7 +128,7 @@ test "fadvise" {
 test "sigset_t" {
     std.debug.assert(@sizeOf(linux.sigset_t) == (linux.NSIG / 8));
 
-    var sigset = linux.empty_sigset;
+    var sigset = linux.sigemptyset();
 
     // See that none are set, then set each one, see that they're all set, then
     // remove them all, and then see that none are set.
@@ -140,8 +140,6 @@ test "sigset_t" {
     }
     for (1..linux.NSIG) |i| {
         try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
-        try expectEqual(linux.sigismember(&linux.filled_sigset, @truncate(i)), true);
-        try expectEqual(linux.sigismember(&linux.empty_sigset, @truncate(i)), false);
     }
     for (1..linux.NSIG) |i| {
         linux.sigdelset(&sigset, @truncate(i));
@@ -183,16 +181,16 @@ test "sigset_t" {
     }
 }
 
-test "filled_sigset" {
+test "sigfillset" {
     // unlike the C library, all the signals are set in the kernel-level fillset
-    const sigset = linux.filled_sigset;
+    const sigset = linux.sigfillset();
     for (1..linux.NSIG) |i| {
         try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
     }
 }
 
-test "empty_sigset" {
-    const sigset = linux.empty_sigset;
+test "sigemptyset" {
+    const sigset = linux.sigemptyset();
     for (1..linux.NSIG) |i| {
         try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);
     }
lib/std/os/emscripten.zig
@@ -560,7 +560,9 @@ pub const Sigaction = extern struct {
 };
 
 pub const sigset_t = [1024 / 32]u32;
-pub const empty_sigset = [_]u32{0} ** @typeInfo(sigset_t).array.len;
+pub fn sigemptyset() sigset_t {
+    return [_]u32{0} ** @typeInfo(sigset_t).array.len;
+}
 pub const siginfo_t = extern struct {
     signo: i32,
     errno: i32,
lib/std/os/linux.zig
@@ -1800,11 +1800,15 @@ const SigsetElement = c_ulong;
 
 const sigset_len = @typeInfo(sigset_t).array.len;
 
-/// Empty set to initialize sigset_t instances from.  No need for `sigemptyset`.
-pub const empty_sigset: sigset_t = [_]SigsetElement{0} ** sigset_len;
+/// Zig's version of sigemptyset.  Returns initialized sigset_t.
+pub fn sigemptyset() sigset_t {
+    return [_]SigsetElement{0} ** sigset_len;
+}
 
-/// Filled set to initialize sigset_t instances from.  No need for `sigfillset`.
-pub const filled_sigset: sigset_t = [_]SigsetElement{~@as(SigsetElement, 0)} ** sigset_len;
+/// Zig's version of sigfillset.  Returns initalized sigset_t.
+pub fn sigfillset() sigset_t {
+    return [_]SigsetElement{~@as(SigsetElement, 0)} ** sigset_len;
+}
 
 fn sigset_bit_index(sig: usize) struct { word: usize, mask: SigsetElement } {
     assert(sig > 0);
lib/std/os/plan9.zig
@@ -182,7 +182,6 @@ pub const SIG = struct {
     pub const TTOU = 20;
 };
 pub const sigset_t = c_long;
-pub const empty_sigset = 0;
 pub const siginfo_t = c_long;
 // TODO plan9 doesn't have sigaction_fn. Sigaction is not a union, but we include it here to be compatible.
 pub const Sigaction = extern struct {
@@ -199,6 +198,10 @@ pub const Sigaction = extern struct {
 pub const AT = struct {
     pub const FDCWD = -100; // we just make up a constant; FDCWD and openat don't actually exist in plan9
 };
+// Plan 9 doesn't do signals.  This is just needed to get through start.zig.
+pub fn sigemptyset() sigset_t {
+    return 0;
+}
 // TODO implement sigaction
 // right now it is just a shim to allow using start.zig code
 pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
lib/std/posix/test.zig
@@ -863,17 +863,15 @@ test "sigset empty/full" {
     if (native_os == .wasi or native_os == .windows)
         return error.SkipZigTest;
 
-    var set: posix.sigset_t = undefined;
-
-    posix.sigemptyset(&set);
+    var set: posix.sigset_t = posix.sigemptyset();
     for (1..posix.NSIG) |i| {
         try expectEqual(false, posix.sigismember(&set, @truncate(i)));
     }
 
     // The C library can reserve some (unnamed) signals, so can't check the full
     // NSIG set is defined, but just test a couple:
-    posix.sigfillset(&set);
-    try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.USR1)));
+    set = posix.sigfillset();
+    try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.CHLD)));
     try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.INT)));
 }
 
@@ -887,8 +885,7 @@ test "sigset add/del" {
     if (native_os == .wasi or native_os == .windows)
         return error.SkipZigTest;
 
-    var sigset: posix.sigset_t = undefined;
-    posix.sigemptyset(&sigset);
+    var sigset: posix.sigset_t = posix.sigemptyset();
 
     // See that none are set, then set each one, see that they're all set, then
     // remove them all, and then see that none are set.
@@ -924,7 +921,7 @@ test "sigaction" {
         return error.SkipZigTest;
     }
 
-    const test_signo = posix.SIG.USR1;
+    const test_signo = posix.SIG.URG; // URG only because it is ignored by default in debuggers
 
     const S = struct {
         var handler_called_count: u32 = 0;
@@ -944,10 +941,10 @@ test "sigaction" {
 
     var sa: posix.Sigaction = .{
         .handler = .{ .sigaction = &S.handler },
-        .mask = undefined,
+        .mask = posix.sigemptyset(),
         .flags = posix.SA.SIGINFO | posix.SA.RESETHAND,
     };
-    posix.sigemptyset(&sa.mask);
+
     var old_sa: posix.Sigaction = undefined;
 
     // Install the new signal handler.
@@ -1009,29 +1006,29 @@ test "sigset_t bits" {
 
     const self_pid = posix.system.getpid();
 
-    // To check that sigset_t mapping matches kernel (think u32/u64
-    // mismatches on big-endian), try sending a blocked signal to make
-    // sure the mask matches the signal.
-    inline for ([_]usize{ posix.SIG.INT, posix.SIG.USR1, 62, 94, 126 }) |test_signo| {
+    // To check that sigset_t mapping matches kernel (think u32/u64 mismatches on
+    // big-endian), try sending a blocked signal to make sure the mask matches the
+    // signal.  (Send URG and CHLD because they're ignored by default in the
+    // debugger, vs. USR1 or other named signals)
+    inline for ([_]usize{ posix.SIG.URG, posix.SIG.CHLD, 62, 94, 126 }) |test_signo| {
         if (test_signo >= posix.NSIG) continue;
 
         S.expected_sig = test_signo;
         S.handler_called_count = 0;
 
-        var sa: posix.Sigaction = .{
+        const sa: posix.Sigaction = .{
             .handler = .{ .sigaction = &S.handler },
-            .mask = undefined,
+            .mask = posix.sigemptyset(),
             .flags = posix.SA.SIGINFO | posix.SA.RESETHAND,
         };
-        posix.sigemptyset(&sa.mask);
+
         var old_sa: posix.Sigaction = undefined;
 
         // Install the new signal handler.
         posix.sigaction(test_signo, &sa, &old_sa);
 
         // block the signal and see that its delayed until unblocked
-        var block_one: posix.sigset_t = undefined;
-        posix.sigemptyset(&block_one);
+        var block_one: posix.sigset_t = posix.sigemptyset();
         posix.sigaddset(&block_one, test_signo);
         posix.sigprocmask(posix.SIG.BLOCK, &block_one, null);
 
lib/std/debug.zig
@@ -1387,13 +1387,11 @@ pub fn attachSegfaultHandler() void {
         windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
         return;
     }
-    var act = posix.Sigaction{
+    const act = posix.Sigaction{
         .handler = .{ .sigaction = handleSegfaultPosix },
-        .mask = undefined,
+        .mask = posix.sigemptyset(),
         .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
     };
-    posix.sigemptyset(&act.mask);
-
     updateSegfaultHandler(&act);
 }
 
@@ -1405,12 +1403,11 @@ fn resetSegfaultHandler() void {
         }
         return;
     }
-    var act = posix.Sigaction{
+    const act = posix.Sigaction{
         .handler = .{ .handler = posix.SIG.DFL },
-        .mask = undefined,
+        .mask = posix.sigemptyset(),
         .flags = 0,
     };
-    posix.sigemptyset(&act.mask);
     updateSegfaultHandler(&act);
 }
 
lib/std/posix.zig
@@ -677,7 +677,8 @@ pub fn abort() noreturn {
         raise(SIG.ABRT) catch {};
 
         // Disable all signal handlers.
-        sigprocmask(SIG.BLOCK, &linux.filled_sigset, null);
+        const filledset = linux.sigfillset();
+        sigprocmask(SIG.BLOCK, &filledset, null);
 
         // Only one thread may proceed to the rest of abort().
         if (!builtin.single_threaded) {
@@ -690,14 +691,14 @@ pub fn abort() noreturn {
         // Install default handler so that the tkill below will terminate.
         const sigact = Sigaction{
             .handler = .{ .handler = SIG.DFL },
-            .mask = linux.empty_sigset,
+            .mask = sigemptyset(),
             .flags = 0,
         };
         sigaction(SIG.ABRT, &sigact, null);
 
         _ = linux.tkill(linux.gettid(), SIG.ABRT);
 
-        var sigabrtmask = linux.empty_sigset;
+        var sigabrtmask = sigemptyset();
         sigaddset(&sigabrtmask, SIG.ABRT);
         sigprocmask(SIG.UNBLOCK, &sigabrtmask, null);
 
@@ -727,7 +728,7 @@ pub fn raise(sig: u8) RaiseError!void {
         // cannot trigger an extra, unexpected, inter-process signal.  Signal paranoia inherited from Musl.
         const filled = linux.sigfillset();
         var orig: sigset_t = undefined;
-        sigprocmask(SIG.BLOCK, &linux.filled_sigset, &orig);
+        sigprocmask(SIG.BLOCK, &filled, &orig);
         const rc = linux.tkill(linux.gettid(), sig);
         sigprocmask(SIG.SETMASK, &orig, null);
 
@@ -5813,24 +5814,28 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
     }
 }
 
-pub fn sigfillset(set: *sigset_t) void {
+/// Return a filled sigset_t.
+pub fn sigfillset() sigset_t {
     if (builtin.link_libc) {
-        switch (errno(system.sigfillset(set))) {
-            .SUCCESS => return,
+        var set: sigset_t = undefined;
+        switch (errno(system.sigfillset(&set))) {
+            .SUCCESS => return set,
             else => unreachable,
         }
     }
-    set.* = system.filled_sigset;
+    return system.sigfillset();
 }
 
-pub fn sigemptyset(set: *sigset_t) void {
+/// Return an empty sigset_t.
+pub fn sigemptyset() sigset_t {
     if (builtin.link_libc) {
-        switch (errno(system.sigemptyset(set))) {
-            .SUCCESS => return,
+        var set: sigset_t = undefined;
+        switch (errno(system.sigemptyset(&set))) {
+            .SUCCESS => return set,
             else => unreachable,
         }
     }
-    set.* = mem.zeroes(sigset_t);
+    return system.sigemptyset();
 }
 
 pub fn sigaddset(set: *sigset_t, sig: u8) void {
lib/std/Progress.zig
@@ -410,12 +410,11 @@ pub fn start(options: Options) Node {
             }
 
             if (have_sigwinch) {
-                var act: posix.Sigaction = .{
+                const act: posix.Sigaction = .{
                     .handler = .{ .sigaction = handleSigWinch },
-                    .mask = undefined,
+                    .mask = posix.sigemptyset(),
                     .flags = (posix.SA.SIGINFO | posix.SA.RESTART),
                 };
-                posix.sigemptyset(&act.mask);
                 posix.sigaction(posix.SIG.WINCH, &act, null);
             }
 
lib/std/start.zig
@@ -745,14 +745,13 @@ fn maybeIgnoreSigpipe() void {
 
     if (have_sigpipe_support and !std.options.keep_sigpipe) {
         const posix = std.posix;
-        var act: posix.Sigaction = .{
+        const act: posix.Sigaction = .{
             // Set handler to a noop function instead of `SIG.IGN` to prevent
             // leaking signal disposition to a child process.
             .handler = .{ .handler = noopSigHandler },
-            .mask = undefined,
+            .mask = posix.sigemptyset(),
             .flags = 0,
         };
-        posix.sigemptyset(&act.mask);
         posix.sigaction(posix.SIG.PIPE, &act, null);
     }
 }
src/crash_report.zig
@@ -175,12 +175,11 @@ pub fn attachSegfaultHandler() void {
         _ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
         return;
     }
-    var act: posix.Sigaction = .{
+    const act: posix.Sigaction = .{
         .handler = .{ .sigaction = handleSegfaultPosix },
-        .mask = undefined,
+        .mask = posix.sigemptyset(),
         .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
     };
-    posix.sigemptyset(&act.mask);
     debug.updateSegfaultHandler(&act);
 }
 
test/standalone/sigpipe/build.zig
@@ -16,12 +16,11 @@ pub fn build(b: *std.build.Builder) !void {
     // This test runs "breakpipe" as a child process and that process
     // depends on inheriting a SIGPIPE disposition of "default".
     {
-        var act = posix.Sigaction{
+        const act = posix.Sigaction{
             .handler = .{ .handler = posix.SIG.DFL },
-            .mask = undefined,
+            .mask = posix.sigemptyset(),
             .flags = 0,
         };
-        posix.sigemptyset(&act.mask);
         try posix.sigaction(posix.SIG.PIPE, &act, null);
     }