Landin library reference discuss source

compiler/tests/fixtures/runtime/kqueue-events/main.ldn

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