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.
This commit is contained in:
Pat Tullmann 2025-04-22 14:19:06 -07:00
parent f0aefa625b
commit 120c4789c3
11 changed files with 65 additions and 63 deletions

View file

@ -410,12 +410,11 @@ pub fn start(options: Options) Node {
} }
if (have_sigwinch) { if (have_sigwinch) {
var act: posix.Sigaction = .{ const act: posix.Sigaction = .{
.handler = .{ .sigaction = handleSigWinch }, .handler = .{ .sigaction = handleSigWinch },
.mask = undefined, .mask = posix.sigemptyset(),
.flags = (posix.SA.SIGINFO | posix.SA.RESTART), .flags = (posix.SA.SIGINFO | posix.SA.RESTART),
}; };
posix.sigemptyset(&act.mask);
posix.sigaction(posix.SIG.WINCH, &act, null); posix.sigaction(posix.SIG.WINCH, &act, null);
} }

View file

@ -1387,13 +1387,11 @@ pub fn attachSegfaultHandler() void {
windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
return; return;
} }
var act = posix.Sigaction{ const act = posix.Sigaction{
.handler = .{ .sigaction = handleSegfaultPosix }, .handler = .{ .sigaction = handleSegfaultPosix },
.mask = undefined, .mask = posix.sigemptyset(),
.flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND), .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
}; };
posix.sigemptyset(&act.mask);
updateSegfaultHandler(&act); updateSegfaultHandler(&act);
} }
@ -1405,12 +1403,11 @@ fn resetSegfaultHandler() void {
} }
return; return;
} }
var act = posix.Sigaction{ const act = posix.Sigaction{
.handler = .{ .handler = posix.SIG.DFL }, .handler = .{ .handler = posix.SIG.DFL },
.mask = undefined, .mask = posix.sigemptyset(),
.flags = 0, .flags = 0,
}; };
posix.sigemptyset(&act.mask);
updateSegfaultHandler(&act); updateSegfaultHandler(&act);
} }

View file

@ -560,7 +560,9 @@ pub const Sigaction = extern struct {
}; };
pub const sigset_t = [1024 / 32]u32; 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 { pub const siginfo_t = extern struct {
signo: i32, signo: i32,
errno: i32, errno: i32,

View file

@ -1800,11 +1800,15 @@ const SigsetElement = c_ulong;
const sigset_len = @typeInfo(sigset_t).array.len; const sigset_len = @typeInfo(sigset_t).array.len;
/// Empty set to initialize sigset_t instances from. No need for `sigemptyset`. /// Zig's version of sigemptyset. Returns initialized sigset_t.
pub const empty_sigset: sigset_t = [_]SigsetElement{0} ** sigset_len; pub fn sigemptyset() sigset_t {
return [_]SigsetElement{0} ** sigset_len;
}
/// Filled set to initialize sigset_t instances from. No need for `sigfillset`. /// Zig's version of sigfillset. Returns initalized sigset_t.
pub const filled_sigset: sigset_t = [_]SigsetElement{~@as(SigsetElement, 0)} ** sigset_len; pub fn sigfillset() sigset_t {
return [_]SigsetElement{~@as(SigsetElement, 0)} ** sigset_len;
}
fn sigset_bit_index(sig: usize) struct { word: usize, mask: SigsetElement } { fn sigset_bit_index(sig: usize) struct { word: usize, mask: SigsetElement } {
assert(sig > 0); assert(sig > 0);

View file

@ -128,7 +128,7 @@ test "fadvise" {
test "sigset_t" { test "sigset_t" {
std.debug.assert(@sizeOf(linux.sigset_t) == (linux.NSIG / 8)); 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 // 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. // remove them all, and then see that none are set.
@ -140,8 +140,6 @@ test "sigset_t" {
} }
for (1..linux.NSIG) |i| { for (1..linux.NSIG) |i| {
try expectEqual(linux.sigismember(&sigset, @truncate(i)), true); 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| { for (1..linux.NSIG) |i| {
linux.sigdelset(&sigset, @truncate(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 // 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| { for (1..linux.NSIG) |i| {
try expectEqual(linux.sigismember(&sigset, @truncate(i)), true); try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
} }
} }
test "empty_sigset" { test "sigemptyset" {
const sigset = linux.empty_sigset; const sigset = linux.sigemptyset();
for (1..linux.NSIG) |i| { for (1..linux.NSIG) |i| {
try expectEqual(linux.sigismember(&sigset, @truncate(i)), false); try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);
} }

View file

@ -182,7 +182,6 @@ pub const SIG = struct {
pub const TTOU = 20; pub const TTOU = 20;
}; };
pub const sigset_t = c_long; pub const sigset_t = c_long;
pub const empty_sigset = 0;
pub const siginfo_t = c_long; 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. // 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 { pub const Sigaction = extern struct {
@ -199,6 +198,10 @@ pub const Sigaction = extern struct {
pub const AT = struct { pub const AT = struct {
pub const FDCWD = -100; // we just make up a constant; FDCWD and openat don't actually exist in plan9 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 // TODO implement sigaction
// right now it is just a shim to allow using start.zig code // 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 { pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {

View file

@ -677,7 +677,8 @@ pub fn abort() noreturn {
raise(SIG.ABRT) catch {}; raise(SIG.ABRT) catch {};
// Disable all signal handlers. // 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(). // Only one thread may proceed to the rest of abort().
if (!builtin.single_threaded) { if (!builtin.single_threaded) {
@ -690,14 +691,14 @@ pub fn abort() noreturn {
// Install default handler so that the tkill below will terminate. // Install default handler so that the tkill below will terminate.
const sigact = Sigaction{ const sigact = Sigaction{
.handler = .{ .handler = SIG.DFL }, .handler = .{ .handler = SIG.DFL },
.mask = linux.empty_sigset, .mask = sigemptyset(),
.flags = 0, .flags = 0,
}; };
sigaction(SIG.ABRT, &sigact, null); sigaction(SIG.ABRT, &sigact, null);
_ = linux.tkill(linux.gettid(), SIG.ABRT); _ = linux.tkill(linux.gettid(), SIG.ABRT);
var sigabrtmask = linux.empty_sigset; var sigabrtmask = sigemptyset();
sigaddset(&sigabrtmask, SIG.ABRT); sigaddset(&sigabrtmask, SIG.ABRT);
sigprocmask(SIG.UNBLOCK, &sigabrtmask, null); 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. // cannot trigger an extra, unexpected, inter-process signal. Signal paranoia inherited from Musl.
const filled = linux.sigfillset(); const filled = linux.sigfillset();
var orig: sigset_t = undefined; var orig: sigset_t = undefined;
sigprocmask(SIG.BLOCK, &linux.filled_sigset, &orig); sigprocmask(SIG.BLOCK, &filled, &orig);
const rc = linux.tkill(linux.gettid(), sig); const rc = linux.tkill(linux.gettid(), sig);
sigprocmask(SIG.SETMASK, &orig, null); 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) { if (builtin.link_libc) {
switch (errno(system.sigfillset(set))) { var set: sigset_t = undefined;
.SUCCESS => return, switch (errno(system.sigfillset(&set))) {
.SUCCESS => return set,
else => unreachable, 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) { if (builtin.link_libc) {
switch (errno(system.sigemptyset(set))) { var set: sigset_t = undefined;
.SUCCESS => return, switch (errno(system.sigemptyset(&set))) {
.SUCCESS => return set,
else => unreachable, else => unreachable,
} }
} }
set.* = mem.zeroes(sigset_t); return system.sigemptyset();
} }
pub fn sigaddset(set: *sigset_t, sig: u8) void { pub fn sigaddset(set: *sigset_t, sig: u8) void {

View file

@ -863,17 +863,15 @@ test "sigset empty/full" {
if (native_os == .wasi or native_os == .windows) if (native_os == .wasi or native_os == .windows)
return error.SkipZigTest; return error.SkipZigTest;
var set: posix.sigset_t = undefined; var set: posix.sigset_t = posix.sigemptyset();
posix.sigemptyset(&set);
for (1..posix.NSIG) |i| { for (1..posix.NSIG) |i| {
try expectEqual(false, posix.sigismember(&set, @truncate(i))); try expectEqual(false, posix.sigismember(&set, @truncate(i)));
} }
// The C library can reserve some (unnamed) signals, so can't check the full // The C library can reserve some (unnamed) signals, so can't check the full
// NSIG set is defined, but just test a couple: // NSIG set is defined, but just test a couple:
posix.sigfillset(&set); set = posix.sigfillset();
try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.USR1))); try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.CHLD)));
try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.INT))); 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) if (native_os == .wasi or native_os == .windows)
return error.SkipZigTest; return error.SkipZigTest;
var sigset: posix.sigset_t = undefined; var sigset: posix.sigset_t = posix.sigemptyset();
posix.sigemptyset(&sigset);
// See that none are set, then set each one, see that they're all set, then // 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. // remove them all, and then see that none are set.
@ -924,7 +921,7 @@ test "sigaction" {
return error.SkipZigTest; 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 { const S = struct {
var handler_called_count: u32 = 0; var handler_called_count: u32 = 0;
@ -944,10 +941,10 @@ test "sigaction" {
var sa: posix.Sigaction = .{ var sa: posix.Sigaction = .{
.handler = .{ .sigaction = &S.handler }, .handler = .{ .sigaction = &S.handler },
.mask = undefined, .mask = posix.sigemptyset(),
.flags = posix.SA.SIGINFO | posix.SA.RESETHAND, .flags = posix.SA.SIGINFO | posix.SA.RESETHAND,
}; };
posix.sigemptyset(&sa.mask);
var old_sa: posix.Sigaction = undefined; var old_sa: posix.Sigaction = undefined;
// Install the new signal handler. // Install the new signal handler.
@ -1009,29 +1006,29 @@ test "sigset_t bits" {
const self_pid = posix.system.getpid(); const self_pid = posix.system.getpid();
// To check that sigset_t mapping matches kernel (think u32/u64 // To check that sigset_t mapping matches kernel (think u32/u64 mismatches on
// mismatches on big-endian), try sending a blocked signal to make // big-endian), try sending a blocked signal to make sure the mask matches the
// sure the mask matches the signal. // signal. (Send URG and CHLD because they're ignored by default in the
inline for ([_]usize{ posix.SIG.INT, posix.SIG.USR1, 62, 94, 126 }) |test_signo| { // 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; if (test_signo >= posix.NSIG) continue;
S.expected_sig = test_signo; S.expected_sig = test_signo;
S.handler_called_count = 0; S.handler_called_count = 0;
var sa: posix.Sigaction = .{ const sa: posix.Sigaction = .{
.handler = .{ .sigaction = &S.handler }, .handler = .{ .sigaction = &S.handler },
.mask = undefined, .mask = posix.sigemptyset(),
.flags = posix.SA.SIGINFO | posix.SA.RESETHAND, .flags = posix.SA.SIGINFO | posix.SA.RESETHAND,
}; };
posix.sigemptyset(&sa.mask);
var old_sa: posix.Sigaction = undefined; var old_sa: posix.Sigaction = undefined;
// Install the new signal handler. // Install the new signal handler.
posix.sigaction(test_signo, &sa, &old_sa); posix.sigaction(test_signo, &sa, &old_sa);
// block the signal and see that its delayed until unblocked // block the signal and see that its delayed until unblocked
var block_one: posix.sigset_t = undefined; var block_one: posix.sigset_t = posix.sigemptyset();
posix.sigemptyset(&block_one);
posix.sigaddset(&block_one, test_signo); posix.sigaddset(&block_one, test_signo);
posix.sigprocmask(posix.SIG.BLOCK, &block_one, null); posix.sigprocmask(posix.SIG.BLOCK, &block_one, null);

View file

@ -745,14 +745,13 @@ fn maybeIgnoreSigpipe() void {
if (have_sigpipe_support and !std.options.keep_sigpipe) { if (have_sigpipe_support and !std.options.keep_sigpipe) {
const posix = std.posix; const posix = std.posix;
var act: posix.Sigaction = .{ const act: posix.Sigaction = .{
// Set handler to a noop function instead of `SIG.IGN` to prevent // Set handler to a noop function instead of `SIG.IGN` to prevent
// leaking signal disposition to a child process. // leaking signal disposition to a child process.
.handler = .{ .handler = noopSigHandler }, .handler = .{ .handler = noopSigHandler },
.mask = undefined, .mask = posix.sigemptyset(),
.flags = 0, .flags = 0,
}; };
posix.sigemptyset(&act.mask);
posix.sigaction(posix.SIG.PIPE, &act, null); posix.sigaction(posix.SIG.PIPE, &act, null);
} }
} }

View file

@ -175,12 +175,11 @@ pub fn attachSegfaultHandler() void {
_ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); _ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
return; return;
} }
var act: posix.Sigaction = .{ const act: posix.Sigaction = .{
.handler = .{ .sigaction = handleSegfaultPosix }, .handler = .{ .sigaction = handleSegfaultPosix },
.mask = undefined, .mask = posix.sigemptyset(),
.flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND), .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
}; };
posix.sigemptyset(&act.mask);
debug.updateSegfaultHandler(&act); debug.updateSegfaultHandler(&act);
} }

View file

@ -16,12 +16,11 @@ pub fn build(b: *std.build.Builder) !void {
// This test runs "breakpipe" as a child process and that process // This test runs "breakpipe" as a child process and that process
// depends on inheriting a SIGPIPE disposition of "default". // depends on inheriting a SIGPIPE disposition of "default".
{ {
var act = posix.Sigaction{ const act = posix.Sigaction{
.handler = .{ .handler = posix.SIG.DFL }, .handler = .{ .handler = posix.SIG.DFL },
.mask = undefined, .mask = posix.sigemptyset(),
.flags = 0, .flags = 0,
}; };
posix.sigemptyset(&act.mask);
try posix.sigaction(posix.SIG.PIPE, &act, null); try posix.sigaction(posix.SIG.PIPE, &act, null);
} }