platform/kqueue
skip to the library navigationTarget scope is enforced by the module assertion below.
compiler.assert(compiler.os == linux or compiler.os == darwin
or compiler.os == freebsd)
kqueue, the event interface Darwin and FreeBSD share, through their C libraries, raw: a failure is -1 with the reason in libc.errno_cell.
Every declaration is Darwin's and FreeBSD's. The module's scope is the hosted POSIX systems, as platform/linux's is, so a provider written once imports both, and on Linux it declares nothing (D481).
| Name | What it is |
|---|---|
kqueue | Make a queue. It is not inherited by a child fork makes; set libc.fd_cloexec with libc.fcntl so a program executed does not keep it. |
kevent | Make the changes given, then wait for events or a libc.timespec limit; none_given for no changes, no room or no limit. A change that fails comes back as an event with ev_error and the errno in its datum. |
change | A change record: an identifier, a filter, flags, filter flags and a datum. |
event_record | struct kevent: what is watched, the filter, the flags, the filter's flags, its datum and a word handed back with each event. |
evfilt_read, evfilt_write, evfilt_signal, evfilt_proc, evfilt_timer | Filters: readiness, a signal, a process's end with note_exit, a timer. |
ev_add, ev_delete, ev_enable, ev_disable, ev_oneshot, ev_clear | Change flags. |
ev_eof, ev_error | Reported flags. |
note_seconds, note_useconds, note_nseconds, and FreeBSD's note_mseconds | A timer's unit; with none it is milliseconds. |
A signal the queue watches is recorded even when it is ignored or blocked, and is still delivered as its disposition says, so a program ignores or blocks it.
| Fact | Darwin | FreeBSD |
|---|---|---|
event_record | 32 bytes | 64 bytes, with four extension words |
note_useconds, note_nseconds | 2, 4 | 4, 8 |
Items
Atoms 1
Values 14
Also on this page
Values by target
Each name is declared once for each target condition below; a dash means the name is not declared there.
compiler.os == darwin: whencompiler.os == darwin or compiler.os == freebsd; compiler.os == darwinotherwise: whencompiler.os == darwin or compiler.os == freebsd; not (compiler.os == darwin)
| Name | Type | compiler.os == darwin |
otherwise |
Documentation |
|---|---|---|---|---|
| note_mseconds | u32 |
— | 2 |
evfilt_timer filter flag: the period is in milliseconds, NOTE_MSECONDS, as with none. |
| note_nseconds | u32 |
4 |
8 |
evfilt_timer filter flag: the period is in nanoseconds, NOTE_NSECONDS. FreeBSD / Darwin numbers it otherwise. |
| note_seconds | u32 |
1 |
1 |
evfilt_timer filter flag: the period is in seconds, NOTE_SECONDS; with none of these it is in milliseconds. |
| note_useconds | u32 |
2 |
4 |
evfilt_timer filter flag: the period is in microseconds, NOTE_USECONDS. FreeBSD / Darwin numbers it otherwise. |
When compiler.os == darwin or compiler.os == freebsd
public changes_given: type = none_given | ptr event_record
The changes kevent makes first, or none_given.
When compiler.os == darwin or compiler.os == freebsd; compiler.os == darwin
platform/kqueue/kqueue.ldn:37
public event_record: type = layout(c) struct
identifier: usize
filter: i16
flags: u16
filter_flags: u32
data: i64
user_data: usize
end event_record
Darwin's struct kevent, 32 bytes: what is watched, as a descriptor, a signal's number, a process's ID or a timer's own number; the filter that watches it; the ev_* flags; the filter's own flags; the filter's datum, a count of bytes, of signals or of timer expiries, C's intptr_t, which is 64 bits there; and a word handed back unread with each event.
When compiler.os == darwin or compiler.os == freebsd; not (compiler.os == darwin)
platform/kqueue/kqueue.ldn:59
public event_record: type = layout(c) struct
identifier: usize
filter: i16
flags: u16
filter_flags: u32
data: i64
user_data: usize
extensions: [4]u64
end event_record
FreeBSD's struct kevent since FreeBSD 12, 64 bytes: Darwin's fields, the datum C's int64_t, and four words of filter extensions after them.
When compiler.os == darwin or compiler.os == freebsd
public events_filled: type = none_given | ptr mut event_record
Where kevent puts the events it reports, or none_given.
When compiler.os == darwin or compiler.os == freebsd
public wait_limit: type = none_given | ptr libc.timespec
How long kevent waits, or none_given to wait until an event comes. A zero time does not wait.
When compiler.os == darwin or compiler.os == freebsd
public none_given: atom
What kevent takes for no records and no time limit: C's null pointer.
When compiler.os == darwin or compiler.os == freebsd
public ev_add: u16 = 1
Flag: add the event, or change one added already, EV_ADD.
When compiler.os == darwin or compiler.os == freebsd
public ev_clear: u16 = 32
Flag: reset the event's state once it is reported, so that it is reported again only when something new happens, EV_CLEAR.
When compiler.os == darwin or compiler.os == freebsd
public ev_delete: u16 = 2
Flag: remove the event, EV_DELETE. An event on a descriptor is removed when the descriptor's last reference closes.
When compiler.os == darwin or compiler.os == freebsd
public ev_disable: u16 = 8
Flag: keep the event but do not report it, EV_DISABLE.
When compiler.os == darwin or compiler.os == freebsd
public ev_enable: u16 = 4
Flag: report the event when it happens, EV_ENABLE.
When compiler.os == darwin or compiler.os == freebsd
public ev_eof: u16 = 32768
Reported flag: a descriptor's end was reached, or its peer shut its side, EV_EOF; the filter flags may hold an errno.
When compiler.os == darwin or compiler.os == freebsd
public ev_error: u16 = 16384
Reported flag: a change could not be made, and the datum is its errno, EV_ERROR.
When compiler.os == darwin or compiler.os == freebsd
public ev_oneshot: u16 = 16
Flag: remove the event once it is reported, EV_ONESHOT.
When compiler.os == darwin or compiler.os == freebsd
public evfilt_proc: i16 = -5
Filter: something happened to a process, the identifier its ID, as the filter flags ask: note_exit for its end. EVFILT_PROC.
When compiler.os == darwin or compiler.os == freebsd
public evfilt_read: i16 = -1
Filter: a descriptor has data to read, or a listener a connection to accept; the datum is how much. EVFILT_READ, which the two systems number alike, as every filter and flag below.
When compiler.os == darwin or compiler.os == freebsd
public evfilt_signal: i16 = -6
Filter: a signal was sent to this process, the identifier its number; the datum counts the signals since the last report. The signal is recorded even when it is ignored or blocked, and is still delivered as its disposition says, so a program ignores or blocks each signal it watches. EVFILT_SIGNAL.
When compiler.os == darwin or compiler.os == freebsd
public evfilt_timer: i16 = -7
Filter: a timer, the identifier a number of the program's own, expiring after the datum's period in milliseconds unless a note_* unit says otherwise, and again after each period unless ev_oneshot is set. EVFILT_TIMER.
When compiler.os == darwin or compiler.os == freebsd
public evfilt_write: i16 = -2
Filter: a descriptor has room to write; the datum is how much. EVFILT_WRITE.
When compiler.os == darwin or compiler.os == freebsd
public note_exit: u32 = 2147483648
evfilt_proc filter flag: the process ended, NOTE_EXIT; the datum is its status as waitpid gives it.
When compiler.os == darwin or compiler.os == freebsd
public change:
(identifier: usize, filter: i16, flags: u16, filter_flags: u32,
data: i64) -> (record: event_record)
A change to what a queue watches: identifier with filter, and the flags, filter flags and datum it is made with, as the record kevent reads, with no user data.
When compiler.os == darwin or compiler.os == freebsd
public extern(c) kevent: (queue: i32, changes: changes_given,
change_count: i32, events: events_filled,
event_count: i32, limit: wait_limit)
-> (count: i32)
Make the change_count changes at changes to what queue watches, then wait until an event comes or limit passes and put at most event_count of them at events: how many it put there, zero when the limit passed first, or -1 with the reason in errno, EINTR when a signal came first. A change that fails is reported as an event with ev_error when there is room for it, and fails the call when there is none.
When compiler.os == darwin or compiler.os == freebsd
public extern(c) kqueue: () -> (descriptor: i32)
Make a kernel event queue: its descriptor, or -1 with the reason in errno. The descriptor is not inherited by a child fork makes, but an executed program keeps it unless libc.fd_cloexec is set on it with libc.fcntl.
Example program
This complete program is one of the compiler's runtime tests. View source.
The program, 132 lines
import platform/kqueue import platform/libc -- platform/kqueue on Darwin and FreeBSD. A queue watches a pipe's -- reading end, reports nothing while the pipe is empty, reports it -- readable with the bytes waiting and the word it was watched with once a -- byte is written, and reports its end once the writing end is closed. -- SIGUSR1, ignored, is sent to the process itself: the queue records it -- all the same. `sigprocmask` blocks SIGUSR2 and puts the mask back. The -- failure oracles: removing an event never added is reported as an event -- with `ev_error` and ENOENT, adding an event for a closed descriptor as -- one with EBADF, and a wait with nothing ready reports nothing. Removing -- one from a closed descriptor is no oracle: closing removed it, and -- Darwin reports ENOENT where FreeBSD reports EBADF. -- `struct kevent`: FreeBSD's carries four extension words. on_darwin: bool = compiler.os == darwin -- ENOENT and EBADF, the same on both systems. no_entry: i64 = 2 bad_descriptor: i64 = 9 -- The word the pipe is watched with, handed back by each report. pipe_word: usize = 0x0123456789abcdef public main: () -> (code: i32) = code = 1 record_size: usize = if on_darwin then 32 else 64 end if return when sizeof kqueue.event_record <> record_size code = 2 queue: i32 = kqueue.kqueue() return when queue < 0 return when libc.fcntl(queue, libc.f_setfd, libc.fd_cloexec) <> 0 code = 3 mut ends: [2]i32 = zeroed return when libc.pipe(addr ends[0]) <> 0 mut watch_pipe: kqueue.event_record = kqueue.change(usize(ends[0]), kqueue.evfilt_read, kqueue.ev_add, 0, 0) watch_pipe.user_data = pipe_word mut reports: [4]kqueue.event_record = zeroed no_wait: libc.timespec = (seconds: 0, nanoseconds: 0) second: libc.timespec = (seconds: 1, nanoseconds: 0) -- Added, and nothing written: nothing ready, without waiting. return when kqueue.kevent(queue, addr watch_pipe, 1, addr reports[0], 4, addr no_wait) <> 0 -- A byte written: the reading end, its byte and its word. code = 4 byte: [1]u8 = [33] return when libc.write(ends[1], addr byte[0], 1) <> 1 return when kqueue.kevent(queue, kqueue.none_given, 0, addr reports[0], 4, addr second) <> 1 first: kqueue.event_record = reports[0] return when first.filter <> kqueue.evfilt_read or first.identifier <> usize(ends[0]) or first.user_data <> pipe_word return when first.data <> 1 mut taken: [1]u8 = zeroed return when libc.read(ends[0], addr taken[0], 1) <> 1 -- The writing end closed: the reading end's end. code = 5 return when libc.close(ends[1]) <> 0 return when kqueue.kevent(queue, kqueue.none_given, 0, addr reports[0], 4, addr second) <> 1 ended: kqueue.event_record = reports[0] return when (ended.flags & kqueue.ev_eof) == 0 or ended.identifier <> usize(ends[0]) -- Removing an event never added is an error event, ENOENT, and so is -- adding one for a descriptor once it is closed, EBADF, on both systems. code = 6 never: kqueue.event_record = kqueue.change(usize(ends[0]), kqueue.evfilt_write, kqueue.ev_delete, 0, 0) return when kqueue.kevent(queue, addr never, 1, addr reports[0], 4, addr no_wait) <> 1 refused: kqueue.event_record = reports[0] return when (refused.flags & kqueue.ev_error) == 0 or refused.data <> no_entry return when libc.close(ends[0]) <> 0 addition: kqueue.event_record = kqueue.change(usize(ends[0]), kqueue.evfilt_read, kqueue.ev_add, 0, 0) return when kqueue.kevent(queue, addr addition, 1, addr reports[0], 4, addr no_wait) <> 1 closed: kqueue.event_record = reports[0] return when (closed.flags & kqueue.ev_error) == 0 or closed.data <> bad_descriptor -- SIGUSR1 ignored, sent, and recorded. code = 7 mut ignoring: libc.signal_action = zeroed ignoring.handler = libc.sig_ign return when libc.sigaction(libc.sigusr1, addr ignoring, libc.no_address) <> 0 watch_signal: kqueue.event_record = kqueue.change(usize(libc.sigusr1), kqueue.evfilt_signal, kqueue.ev_add, 0, 0) return when kqueue.kevent(queue, addr watch_signal, 1, addr reports[0], 4, addr no_wait) <> 0 return when libc.kill(libc.getpid(), libc.sigusr1) <> 0 code = 8 return when kqueue.kevent(queue, kqueue.none_given, 0, addr reports[0], 4, addr second) <> 1 signalled: kqueue.event_record = reports[0] return when signalled.filter <> kqueue.evfilt_signal or signalled.identifier <> usize(libc.sigusr1) or signalled.data < 1 mut restored: libc.signal_action = zeroed restored.handler = libc.sig_dfl return when libc.sigaction(libc.sigusr1, addr restored, libc.no_address) <> 0 -- SIGUSR2 blocked, and not blocked before; then the mask as it was. code = 9 mut blocking: libc.signal_set = zeroed blocking.bits[0] = u32(1) << u32(libc.sigusr2 - 1) mut before: libc.signal_set = zeroed return when libc.sigprocmask(libc.sig_block, addr blocking, addr before) <> 0 return when (before.bits[0] & blocking.bits[0]) <> 0 mut during: libc.signal_set = zeroed return when libc.sigprocmask(libc.sig_setmask, addr before, addr during) <> 0 return when (during.bits[0] & blocking.bits[0]) == 0 return when libc.close(queue) <> 0 code = 42 end main