compiler/tests/fixtures/runtime/kqueue-events/main.ldn
skip to the library navigation
1import platform/kqueue
2import platform/libc
3
4-- platform/kqueue on Darwin and FreeBSD. A queue watches a pipe's
5-- reading end, reports nothing while the pipe is empty, reports it
6-- readable with the bytes waiting and the word it was watched with once a
7-- byte is written, and reports its end once the writing end is closed.
8-- SIGUSR1, ignored, is sent to the process itself: the queue records it
9-- all the same. `sigprocmask` blocks SIGUSR2 and puts the mask back. The
10-- failure oracles: removing an event never added is reported as an event
11-- with `ev_error` and ENOENT, adding an event for a closed descriptor as
12-- one with EBADF, and a wait with nothing ready reports nothing. Removing
13-- one from a closed descriptor is no oracle: closing removed it, and
14-- Darwin reports ENOENT where FreeBSD reports EBADF.
15
16-- `struct kevent`: FreeBSD's carries four extension words.
17on_darwin: bool = compiler.os == darwin
18
19-- ENOENT and EBADF, the same on both systems.
20no_entry: i64 = 2
21bad_descriptor: i64 = 9
22
23-- The word the pipe is watched with, handed back by each report.
24pipe_word: usize = 0x0123456789abcdef
25
26public main: () -> (code: i32) =
27 code = 1
28 record_size: usize = if on_darwin then 32 else 64 end if
29 return when sizeof kqueue.event_record <> record_size
30
31 code = 2
32 queue: i32 = kqueue.kqueue()
33 return when queue < 0
34 return when libc.fcntl(queue, libc.f_setfd, libc.fd_cloexec) <> 0
35
36 code = 3
37 mut ends: [2]i32 = zeroed
38 return when libc.pipe(addr ends[0]) <> 0
39 mut watch_pipe: kqueue.event_record =
40 kqueue.change(usize(ends[0]), kqueue.evfilt_read, kqueue.ev_add, 0,
41 0)
42 watch_pipe.user_data = pipe_word
43 mut reports: [4]kqueue.event_record = zeroed
44 no_wait: libc.timespec = (seconds: 0, nanoseconds: 0)
45 second: libc.timespec = (seconds: 1, nanoseconds: 0)
46 -- Added, and nothing written: nothing ready, without waiting.
47 return when kqueue.kevent(queue, addr watch_pipe, 1, addr reports[0], 4,
48 addr no_wait) <> 0
49
50 -- A byte written: the reading end, its byte and its word.
51 code = 4
52 byte: [1]u8 = [33]
53 return when libc.write(ends[1], addr byte[0], 1) <> 1
54 return when kqueue.kevent(queue, kqueue.none_given, 0, addr reports[0],
55 4, addr second) <> 1
56 first: kqueue.event_record = reports[0]
57 return when first.filter <> kqueue.evfilt_read
58 or first.identifier <> usize(ends[0]) or first.user_data <> pipe_word
59 return when first.data <> 1
60 mut taken: [1]u8 = zeroed
61 return when libc.read(ends[0], addr taken[0], 1) <> 1
62
63 -- The writing end closed: the reading end's end.
64 code = 5
65 return when libc.close(ends[1]) <> 0
66 return when kqueue.kevent(queue, kqueue.none_given, 0, addr reports[0],
67 4, addr second) <> 1
68 ended: kqueue.event_record = reports[0]
69 return when (ended.flags & kqueue.ev_eof) == 0
70 or ended.identifier <> usize(ends[0])
71
72 -- Removing an event never added is an error event, ENOENT, and so is
73 -- adding one for a descriptor once it is closed, EBADF, on both systems.
74 code = 6
75 never: kqueue.event_record =
76 kqueue.change(usize(ends[0]), kqueue.evfilt_write, kqueue.ev_delete,
77 0, 0)
78 return when kqueue.kevent(queue, addr never, 1, addr reports[0], 4,
79 addr no_wait) <> 1
80 refused: kqueue.event_record = reports[0]
81 return when (refused.flags & kqueue.ev_error) == 0
82 or refused.data <> no_entry
83 return when libc.close(ends[0]) <> 0
84 addition: kqueue.event_record =
85 kqueue.change(usize(ends[0]), kqueue.evfilt_read, kqueue.ev_add,
86 0, 0)
87 return when kqueue.kevent(queue, addr addition, 1, addr reports[0], 4,
88 addr no_wait) <> 1
89 closed: kqueue.event_record = reports[0]
90 return when (closed.flags & kqueue.ev_error) == 0
91 or closed.data <> bad_descriptor
92
93 -- SIGUSR1 ignored, sent, and recorded.
94 code = 7
95 mut ignoring: libc.signal_action = zeroed
96 ignoring.handler = libc.sig_ign
97 return when libc.sigaction(libc.sigusr1, addr ignoring,
98 libc.no_address) <> 0
99 watch_signal: kqueue.event_record =
100 kqueue.change(usize(libc.sigusr1), kqueue.evfilt_signal,
101 kqueue.ev_add, 0, 0)
102 return when kqueue.kevent(queue, addr watch_signal, 1, addr reports[0],
103 4, addr no_wait) <> 0
104 return when libc.kill(libc.getpid(), libc.sigusr1) <> 0
105
106 code = 8
107 return when kqueue.kevent(queue, kqueue.none_given, 0, addr reports[0],
108 4, addr second) <> 1
109 signalled: kqueue.event_record = reports[0]
110 return when signalled.filter <> kqueue.evfilt_signal
111 or signalled.identifier <> usize(libc.sigusr1) or signalled.data < 1
112 mut restored: libc.signal_action = zeroed
113 restored.handler = libc.sig_dfl
114 return when libc.sigaction(libc.sigusr1, addr restored,
115 libc.no_address) <> 0
116
117 -- SIGUSR2 blocked, and not blocked before; then the mask as it was.
118 code = 9
119 mut blocking: libc.signal_set = zeroed
120 blocking.bits[0] = u32(1) << u32(libc.sigusr2 - 1)
121 mut before: libc.signal_set = zeroed
122 return when libc.sigprocmask(libc.sig_block, addr blocking,
123 addr before) <> 0
124 return when (before.bits[0] & blocking.bits[0]) <> 0
125 mut during: libc.signal_set = zeroed
126 return when libc.sigprocmask(libc.sig_setmask, addr before,
127 addr during) <> 0
128 return when (during.bits[0] & blocking.bits[0]) == 0
129
130 return when libc.close(queue) <> 0
131 code = 42
132end main