1-- kqueue, the event interface Darwin and FreeBSD share: one descriptor
2-- that a program asks to watch descriptors, signals, child processes and
3-- timers, and waits on for whichever happens first. It is reached
4-- through each system's C library, which is its system interface, as
5-- platform/libc's declarations are, and is raw as they are: a failure is
6-- -1 with the reason in errno, which platform/libc's `errno_cell` reads.
7-- The two systems lay out `struct kevent` differently, and number a
8-- timer's units differently, so each record and number is chosen by
9-- `compiler.os`.
10--
11-- It is named for the facility rather than a system because it is
12-- Darwin's and FreeBSD's alike (D481). Everything is declared on those
13-- two only. Landin has no conditional import, so a provider written once
14-- for every POSIX system imports this module beside platform/linux, and
15-- the module's scope is theirs: Linux, Darwin and FreeBSD. On Linux it
16-- declares nothing.
17
18import platform/libc
19
20compiler.assert(compiler.os == linux or compiler.os == darwin
21 or compiler.os == freebsd)
22
23fixed if compiler.os == darwin or compiler.os == freebsd then
24 --- Make a kernel event queue: its descriptor, or -1 with the reason in
25 --- errno. The descriptor is not inherited by a child `fork` makes, but
26 --- an executed program keeps it unless `libc.fd_cloexec` is set on it
27 --- with `libc.fcntl`.
28 public extern(c) kqueue: () -> (descriptor: i32)
29
30 fixed if compiler.os == darwin then
31 --- Darwin's `struct kevent`, 32 bytes: what is watched, as a
32 --- descriptor, a signal's number, a process's ID or a timer's own
33 --- number; the filter that watches it; the `ev_*` flags; the
34 --- filter's own flags; the filter's datum, a count of bytes, of
35 --- signals or of timer expiries, C's `intptr_t`, which is 64 bits
36 --- there; and a word handed back unread with each event.
37 public event_record: type = layout(c) struct
38 identifier: usize
39 filter: i16
40 flags: u16
41 filter_flags: u32
42 data: i64
43 user_data: usize
44 end event_record
45
46 --- `evfilt_timer` filter flag: the period is in seconds,
47 --- `NOTE_SECONDS`; with none of these it is in milliseconds.
48 public note_seconds: u32 = 1
49 --- `evfilt_timer` filter flag: the period is in microseconds,
50 --- `NOTE_USECONDS`. FreeBSD numbers it otherwise.
51 public note_useconds: u32 = 2
52 --- `evfilt_timer` filter flag: the period is in nanoseconds,
53 --- `NOTE_NSECONDS`. FreeBSD numbers it otherwise.
54 public note_nseconds: u32 = 4
55 else
56 --- FreeBSD's `struct kevent` since FreeBSD 12, 64 bytes: Darwin's
57 --- fields, the datum C's `int64_t`, and four words of filter
58 --- extensions after them.
59 public event_record: type = layout(c) struct
60 identifier: usize
61 filter: i16
62 flags: u16
63 filter_flags: u32
64 data: i64
65 user_data: usize
66 extensions: [4]u64
67 end event_record
68
69 --- `evfilt_timer` filter flag: the period is in seconds,
70 --- `NOTE_SECONDS`; with none of these it is in milliseconds.
71 public note_seconds: u32 = 1
72 --- `evfilt_timer` filter flag: the period is in milliseconds,
73 --- `NOTE_MSECONDS`, as with none.
74 public note_mseconds: u32 = 2
75 --- `evfilt_timer` filter flag: the period is in microseconds,
76 --- `NOTE_USECONDS`. Darwin numbers it otherwise.
77 public note_useconds: u32 = 4
78 --- `evfilt_timer` filter flag: the period is in nanoseconds,
79 --- `NOTE_NSECONDS`. Darwin numbers it otherwise.
80 public note_nseconds: u32 = 8
81 end if
82
83 --- Filter: a descriptor has data to read, or a listener a connection
84 --- to accept; the datum is how much. `EVFILT_READ`, which the two
85 --- systems number alike, as every filter and flag below.
86 public evfilt_read: i16 = -1
87 --- Filter: a descriptor has room to write; the datum is how much.
88 --- `EVFILT_WRITE`.
89 public evfilt_write: i16 = -2
90 --- Filter: something happened to a process, the identifier its ID, as
91 --- the filter flags ask: `note_exit` for its end. `EVFILT_PROC`.
92 public evfilt_proc: i16 = -5
93 --- Filter: a signal was sent to this process, the identifier its
94 --- number; the datum counts the signals since the last report. The
95 --- signal is recorded even when it is ignored or blocked, and is still
96 --- delivered as its disposition says, so a program ignores or blocks
97 --- each signal it watches. `EVFILT_SIGNAL`.
98 public evfilt_signal: i16 = -6
99 --- Filter: a timer, the identifier a number of the program's own,
100 --- expiring after the datum's period in milliseconds unless a
101 --- `note_*` unit says otherwise, and again after each period unless
102 --- `ev_oneshot` is set. `EVFILT_TIMER`.
103 public evfilt_timer: i16 = -7
104
105 --- Flag: add the event, or change one added already, `EV_ADD`.
106 public ev_add: u16 = 1
107 --- Flag: remove the event, `EV_DELETE`. An event on a descriptor is
108 --- removed when the descriptor's last reference closes.
109 public ev_delete: u16 = 2
110 --- Flag: report the event when it happens, `EV_ENABLE`.
111 public ev_enable: u16 = 4
112 --- Flag: keep the event but do not report it, `EV_DISABLE`.
113 public ev_disable: u16 = 8
114 --- Flag: remove the event once it is reported, `EV_ONESHOT`.
115 public ev_oneshot: u16 = 16
116 --- Flag: reset the event's state once it is reported, so that it is
117 --- reported again only when something new happens, `EV_CLEAR`.
118 public ev_clear: u16 = 32
119 --- Reported flag: a descriptor's end was reached, or its peer shut
120 --- its side, `EV_EOF`; the filter flags may hold an errno.
121 public ev_eof: u16 = 32768
122 --- Reported flag: a change could not be made, and the datum is its
123 --- errno, `EV_ERROR`.
124 public ev_error: u16 = 16384
125
126 --- `evfilt_proc` filter flag: the process ended, `NOTE_EXIT`; the datum
127 --- is its status as `waitpid` gives it.
128 public note_exit: u32 = 2147483648
129
130 --- What `kevent` takes for no records and no time limit: C's null
131 --- pointer.
132 public none_given: atom
133 --- The changes `kevent` makes first, or `none_given`.
134 public changes_given: type = none_given | ptr event_record
135 --- Where `kevent` puts the events it reports, or `none_given`.
136 public events_filled: type = none_given | ptr mut event_record
137 --- How long `kevent` waits, or `none_given` to wait until an event
138 --- comes. A zero time does not wait.
139 public wait_limit: type = none_given | ptr libc.timespec
140
141 --- Make the `change_count` changes at `changes` to what `queue`
142 --- watches, then wait until an event comes or `limit` passes and put
143 --- at most `event_count` of them at `events`: how many it put there,
144 --- zero when the limit passed first, or -1 with the reason in errno,
145 --- EINTR when a signal came first. A change that fails is reported as
146 --- an event with `ev_error` when there is room for it, and fails the
147 --- call when there is none.
148 public extern(c) kevent: (queue: i32, changes: changes_given,
149 change_count: i32, events: events_filled,
150 event_count: i32, limit: wait_limit)
151 -> (count: i32)
152
153 --- A change to what a queue watches: `identifier` with `filter`, and
154 --- the `flags`, filter flags and datum it is made with, as the record
155 --- `kevent` reads, with no user data.
156 public change:
157 (identifier: usize, filter: i16, flags: u16, filter_flags: u32,
158 data: i64) -> (record: event_record) =
159 record = zeroed
160 record.identifier = identifier
161 record.filter = filter
162 record.flags = flags
163 record.filter_flags = filter_flags
164 record.data = data
165 end change
166end if