Landin library reference discuss source

platform/kqueue/kqueue.ldn

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