code wiki / (root) / nx_signalfd.nx

nx_signalfd.nx source

↩ module page · 109 lines · 4103 B

1// nx_signalfd.nx -- Linux signalfd(2) wrapper. 2// 3// signalfd lets the kernel deliver signals via file descriptor 4// reads instead of signal handlers. This is the modern way to 5// integrate signal handling with poll/epoll-driven event loops: 6// you add the signalfd to your poll set and react to signals 7// inline, no async-signal-safe constraints. 8// 9// Linux RV64 syscall: 74 (signalfd4). 10// 11// To receive a signal via signalfd, you ALSO need to mask the 12// signal in the process's normal signal mask via rt_sigprocmask 13// (otherwise the kernel still delivers it normally). We expose 14// nx_signalfd_install which does both steps. 15// 16// Read returns one or more struct signalfd_siginfo records (128 17// bytes each); the first 4 bytes are the signal number (i32). 18// 19// Pairs with nx_signal (constants + sigaction) + nx_poll 20// (consumer in event loops). 21 22// nx_safety_envelope: 23// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 24// sil_target: SIL1 25// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 26// verdict: NOT_YET_EVALUATED 27 28import "syscalls.nx" 29import "nx_signal.nx" 30 31const NX_SYS_SIGNALFD4: i64 = 74 32const NX_SYS_RT_SIGPROCMASK: i64 = 135 33 34const NX_SFD_NONBLOCK: i64 = 0x800 35const NX_SFD_CLOEXEC: i64 = 0x80000 36 37// SIG_BLOCK / SIG_UNBLOCK / SIG_SETMASK constants. 38const NX_SIG_BLOCK: i64 = 0 39const NX_SIG_UNBLOCK: i64 = 1 40const NX_SIG_SETMASK: i64 = 2 41 42// signalfd_siginfo.ssi_signo offset = 0 (i32). 43const NX_SSI_BYTES: i64 = 128 44 45// Build a sigset_t mask containing exactly `sig`. RV64 sigset_t 46// is a single 64-bit word for signals 1..64; bit (sig-1) selects 47// the signal. 48func nx_signalfd_mask_one(sig: i64) -> i64 { 49 return 1 << (sig - 1) 50} 51 52// Block `sig` in the current thread's signal mask (so the kernel 53// queues it for signalfd consumption instead of running a 54// handler). Returns 0 on success or -errno. 55func nx_signalfd_block(sig: i64) -> i64 { 56 let mask_raw: *u8 = sys_mmap(16) 57 let mask: *i64 = mask_raw as *i64 58 *mask = nx_signalfd_mask_one(sig) 59 return __syscall(NX_SYS_RT_SIGPROCMASK, NX_SIG_BLOCK, mask_raw as i64, 0, 8, 0, 0) 60} 61 62// Create a signalfd that delivers `sig` (mask containing only that 63// signal). Returns fd >= 0 or -errno. Caller must ALSO call 64// nx_signalfd_block(sig) for delivery to actually go through this fd. 65func nx_signalfd_new(sig: i64, flags: i64) -> i64 { 66 let mask_raw: *u8 = sys_mmap(16) 67 let mask: *i64 = mask_raw as *i64 68 *mask = nx_signalfd_mask_one(sig) 69 return __syscall(NX_SYS_SIGNALFD4, 0 - 1, mask_raw as i64, 8, flags, 0, 0) 70} 71 72// Convenience: block + create signalfd in one call. Returns the 73// fd or -errno. 74func nx_signalfd_install(sig: i64, flags: i64) -> i64 { 75 let r: i64 = nx_signalfd_block(sig) 76 if r < 0 { return r } 77 return nx_signalfd_new(sig, flags) 78} 79 80// Read one signalfd_siginfo record into `out` (caller supplies 81// >= 128 bytes). Returns the signal number (>0) or negative errno. 82// Note: this differs from raw read; it returns the SSI signo 83// extracted from the struct as a convenience. 84func nx_signalfd_read_one(fd: i64, out: *u8) -> i64 { 85 let n: i64 = sys_read(fd, out, NX_SSI_BYTES) 86 if n != NX_SSI_BYTES { return n } 87 let signo_p: *i32 = out as *i32 88 return signo_p[0] 89} 90 91// ---- self-test --------------------------------------------------- 92 93func main() -> i64 { 94 if NX_SFD_CLOEXEC != 0x80000 { return __syscall(93, 1, 0, 0, 0, 0, 0) } 95 if NX_SIG_BLOCK != 0 { return __syscall(93, 2, 0, 0, 0, 0, 0) } 96 97 // Mask helper: SIGUSR1 = 10, bit 9. 98 if nx_signalfd_mask_one(NX_SIGUSR1) != (1 << 9) { return __syscall(93, 3, 0, 0, 0, 0, 0) } 99 // SIGTERM = 15, bit 14. 100 if nx_signalfd_mask_one(NX_SIGTERM) != (1 << 14) { return __syscall(93, 4, 0, 0, 0, 0, 0) } 101 102 // Create signalfd for SIGUSR1. Don't block delivery (would 103 // disturb the test harness). Just verify fd creation succeeds. 104 let fd: i64 = nx_signalfd_new(NX_SIGUSR1, NX_SFD_NONBLOCK | NX_SFD_CLOEXEC) 105 if fd < 0 { return __syscall(93, 5, 0, 0, 0, 0, 0) } 106 107 sys_close(fd) 108 return 0 109}