nx_eventfd.nx source
↩ module page · 105 lines · 3297 B
1// nx_eventfd.nx -- Linux eventfd(2) wrapper.
2//
3// eventfd creates a file descriptor backed by a kernel-managed
4// 64-bit counter. Useful for:
5// - Lightweight thread / coroutine signaling
6// - Adding a "wake up the poll loop" channel to a poll/epoll set
7// - Cross-process counters that don't need shared memory
8//
9// Read returns the counter and resets it to 0 (or decrements by 1
10// if SEMAPHORE flag was set). Write adds the value to the counter
11// (saturating at 0xFFFFFFFFFFFFFFFE).
12//
13// Linux RV64 syscall: 19 (eventfd2).
14// Flags: NX_EFD_SEMAPHORE / NX_EFD_NONBLOCK / NX_EFD_CLOEXEC.
15
16// nx_safety_envelope:
17// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
18// sil_target: SIL1
19// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
20// verdict: NOT_YET_EVALUATED
21
22import "syscalls.nx"
23
24const NX_SYS_EVENTFD2: i64 = 19
25
26const NX_EFD_SEMAPHORE: i64 = 1
27const NX_EFD_NONBLOCK: i64 = 0x800
28const NX_EFD_CLOEXEC: i64 = 0x80000
29
30// Create an eventfd with initial counter value `init` and flag
31// mask `flags`. Returns fd >= 0 on success or -errno.
32func nx_eventfd_new(init: i64, flags: i64) -> i64 {
33 return __syscall(NX_SYS_EVENTFD2, init, flags, 0, 0, 0, 0)
34}
35
36// Read the counter into `out`. Returns 8 on success, negative on
37// error (in particular -EAGAIN if NONBLOCK and counter == 0).
38func nx_eventfd_read(fd: i64, out: *i64) -> i64 {
39 return sys_read(fd, out as *u8, 8)
40}
41
42// Add `v` to the counter. Returns 8 on success or negative on error.
43func nx_eventfd_write(fd: i64, v: i64) -> i64 {
44 let buf_raw: *u8 = sys_mmap(16)
45 let buf: *i64 = buf_raw as *i64
46 *buf = v
47 return sys_write(fd, buf_raw, 8)
48}
49
50// Convenience: signal a single wakeup (write 1).
51func nx_eventfd_signal(fd: i64) -> i64 {
52 return nx_eventfd_write(fd, 1)
53}
54
55// ---- self-test ---------------------------------------------------
56
57func main() -> i64 {
58 if NX_EFD_SEMAPHORE != 1 { return __syscall(93, 1, 0, 0, 0, 0, 0) }
59 if NX_EFD_CLOEXEC != 0x80000 { return __syscall(93, 2, 0, 0, 0, 0, 0) }
60
61 // Create eventfd with initial counter = 5.
62 let fd: i64 = nx_eventfd_new(5, NX_EFD_NONBLOCK | NX_EFD_CLOEXEC)
63 if fd < 0 { return __syscall(93, 3, 0, 0, 0, 0, 0) }
64
65 // Read should return the counter (5) and reset to 0.
66 let val_raw: *u8 = sys_mmap(16)
67 let val: *i64 = val_raw as *i64
68 let n1: i64 = nx_eventfd_read(fd, val)
69 if n1 != 8 {
70 sys_close(fd)
71 return __syscall(93, 4, 0, 0, 0, 0, 0)
72 }
73 if *val != 5 {
74 sys_close(fd)
75 return __syscall(93, 5, 0, 0, 0, 0, 0)
76 }
77
78 // Subsequent read should return -EAGAIN (counter == 0, NONBLOCK).
79 let n2: i64 = nx_eventfd_read(fd, val)
80 if n2 == 8 {
81 sys_close(fd)
82 return __syscall(93, 6, 0, 0, 0, 0, 0)
83 }
84
85 // Signal: write 7, read should return 7.
86 nx_eventfd_write(fd, 7)
87 nx_eventfd_read(fd, val)
88 if *val != 7 {
89 sys_close(fd)
90 return __syscall(93, 7, 0, 0, 0, 0, 0)
91 }
92
93 // signal helper writes 1.
94 nx_eventfd_signal(fd)
95 nx_eventfd_signal(fd)
96 nx_eventfd_signal(fd)
97 nx_eventfd_read(fd, val)
98 if *val != 3 {
99 sys_close(fd)
100 return __syscall(93, 8, 0, 0, 0, 0, 0)
101 }
102
103 sys_close(fd)
104 return 0
105}