main zmx / src / signal.zig
Chance Zibolski  ·  2026-08-01
 1const std = @import("std");
 2const lib_posix = @import("posix.zig");
 3
 4/// Self-pipe woken by signal handlers. std.posix.poll loops on .INTR internally
 5/// (PollError has no Interrupted member), so a signal that lands during poll()
 6/// never surfaces; the handler writes a byte here and poll() wakes on POLLIN.
 7pub var sig_pipe: [2]lib_posix.fd_t = .{ -1, -1 };
 8
 9pub fn wakeSignalPipe(_: lib_posix.SIG, _: *const lib_posix.siginfo_t, _: ?*anyopaque) callconv(.c) void {
10    const saved = std.c._errno().*;
11    _ = std.c.write(sig_pipe[1], "x", 1);
12    std.c._errno().* = saved;
13}
14
15// std.posix.poll retries EINTR internally, so SA_RESTART is moot -- neither
16// setting wakes the loop. The handler writes to sig_pipe instead; poll()
17// wakes on its read end.
18pub fn installWakeHandler(sig: u6) void {
19    const act: lib_posix.Sigaction = .{
20        .handler = .{ .sigaction = wakeSignalPipe },
21        .mask = lib_posix.sigemptyset(),
22        .flags = lib_posix.SA.SIGINFO,
23    };
24    lib_posix.sigaction(@as(lib_posix.SIG, @enumFromInt(sig)), &act, null);
25}
26
27pub fn ignoreSigpipe() void {
28    const act: lib_posix.Sigaction = .{
29        .handler = .{ .handler = lib_posix.SIG.IGN },
30        .mask = lib_posix.sigemptyset(),
31        .flags = 0,
32    };
33    lib_posix.sigaction(lib_posix.SIG.PIPE, &act, null);
34}
35
36pub fn openSignalPipe() !void {
37    sig_pipe = try lib_posix.pipe2(.{ .CLOEXEC = true, .NONBLOCK = true });
38}
39
40pub fn drainSignalPipe() void {
41    var b: [16]u8 = undefined;
42    while (true) {
43        const n = lib_posix.read(sig_pipe[0], &b) catch return;
44        if (n == 0) return;
45    }
46}