libstddjb
skalibs
Software
www.skarnet.org
The following functions are declared in the iopause.h header, and implemented in the libstddjb.a or libstddjb.so library.
iopause is the skalibs API for event loop selection. It's a wrapper around the system's poll() (if available) or select() (if poll() is unavailable) function. It works around some system-dependent quirks; also it works with absolute dates instead of timeouts. This is a good thing: see below.
iopause is a derived work from Dan J. Bernstein's iopause library, but the skalibs implementation is subtly different.
An iopause_fd structure is similar to a struct pollfd structure, and must be filled the same way. Usually, the user declares an array of iopause_fd and fills it, one element per descriptor to select on. If x is an iopause_fd:
Unlike poll() or select(), which use a timeout argument, the iopause() function uses a deadline argument, i.e. an absolute time at which it must return 0 if no event has happened so far, as well as a stamp argument, i.e. an absolute time meaning now. Those arguments are stored in struct taias. Here is why:
The event loop pattern is mostly used to multiplex several asynchronous events that can happen independently, at the same time or not. Let's say you have 3 events, x, y and z. Each of those has a separate timeout: if x happens before x-timeout milliseconds, you call the x-event-handler function, but if x-timeout milliseconds elapse without x happening, you call x-timeout-handler function. And similarly with y and z.
But the selection function returning does not mean x has happened or that x has timed out. It might also mean that y has happened, that y has timed out, that z has happened, that z has timed out, or something else entirely. In the post-selection part of the loop, the proper handler is called for the event or timeout that has happened; then the loop is executed again, and in the pre-selection part of the loop, the array describing the events is filled, and the selection timeout is computed.
How are you going to compute that global selection timeout? Easy: it's the shortest of the three. But we just spent some amount of time waiting, so the individual timeouts must be recomputed! This means:
That is really cumbersome. A much simpler way of doing things is:
Maintaining a global timestamp and using absolute times instead of relative times really is the right way to work with event loops, and the iopause interface reflects that. Of course, you need a reliable, bug-free time library and a monotonic, constant system clock to handle absolute times correctly; that is why iopause relies on the tai library.
int iopause (iopause_fd *x, unsigned int len, struct taia const *deadline, struct taia const *stamp)
Blocks until one of the events described in the x array, of length
len, happens, or until the absolute date *deadline is
reached. deadline may be null, in which case the function blocks
indefinitely until an event happens. If deadline is not null, then
stamp must not be null, and must contain an accurate estimation
of the current time. The function returns the number of events that have
happened, 0 for a timeout, or -1 (and sets errno) for an error.
int iopause_stamp (iopause_fd *x, unsigned int len, struct taia const *deadline, struct taia *stamp)
Like iopause(), but if stamp is not null, it is updated
right before the function returns. This helps the user always keep a
reasonably accurate estimation of the current time in stamp;
it is recommended to use this function instead of the lower-level
iopause().