1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
|
API Documentation
=================
* [Introduction](#introduction)
* [Overview](#overview)
* [Create an Event Context](#create-an-event-context)
* [Register an Event Watcher](#register-an-event-watcher)
* [Start Event Loop](#start-event-loop)
* [Summary](#summary)
* [Using -luev](#using--luev)
* [Joystick Example](#joystick-example)
> “Event driven software improves concurrency” -- [Dave Zarzycki, Apple][]
Introduction
------------
The C API to [libuEv][], listed in `uev/uev.h`, handles three different
types of events: I/O (pipes, sockets, message queues, etc.), timers, and
signals. The [Summary](#summary) details a slight caveat on signals.
Notice the *lack of support for regular files* and directories. This is
a limitation of the underlying Linux `epoll` interface which will return
`EPERM` for regular files. Except for the special case when `stdin` is
redirected from the command line. See `examples/redirect.c` for more on
this particular case.
Timers can be either relative, timeout in milliseconds, or absolute with
a time given in `time_t`, see `mktime()` et al. Absolute timers are
called cron timers and their callbacks get an `UEV_HUP` error event if
the wall clock changes, either via NTP or user input.
**NOTE:** On some systems, embedded in particular, `time_t` is a 32-bit
integer that wraps around in the year 2038. A [GLIBC workaround][]
(`-D_TIME_BITS=64`) protects those systems, but users of other C
libraries have no known workarounds. It is strongly recommended to
use relative timers as often as possible.
Overview
--------
```C
/*
* Callback example, arg comes from the watcher's *_init() function,
* w->fd holds the file descriptor or socket, and events is set by
* libuEv to indicate status: UEV_READ and/or UEV_WRITE with any of
* the optional UEV_HUP, UEV_RDHUP, or UEV_PRI for urgent read data.
*
* Note: UEV_ERROR may be returned for any watcher and must be checked
* by all callbacks. I/O watchers may also need to check UEV_HUP.
* Appropriate action, e.g. restart the watcher, is up to the
* application and is thus delegated to the callback.
*/
void callback (uev_t *w, void *arg, int events);
/* Event loop: Notice the use of flags! */
int uev_init (uev_ctx_t *ctx);
int uev_init1 (uev_ctx_t *ctx, int maxevents);
int uev_exit (uev_ctx_t *ctx);
int uev_run (uev_ctx_t *ctx, int flags); /* UEV_NONE, UEV_ONCE, and/or UEV_NONBLOCK */
/* I/O watcher: fd *MUST* be non-blocking!
* events combination of the main flags: UEV_READ, UEV_WRITE,
* UEV_EDGE, UEV_ONESHOT
*/
int uev_io_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events);
int uev_io_set (uev_t *w, int fd, int events);
int uev_io_start (uev_t *w);
int uev_io_stop (uev_t *w);
/* Timer watcher: schedule a relative timer, timeout (must be non-zero) and period in milliseconds */
int uev_timer_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period);
int uev_timer_set (uev_t *w, int timeout, int period); /* Change timeout or period */
int uev_timer_start (uev_t *w); /* Restart a stopped timer */
int uev_timer_stop (uev_t *w); /* Stop a timer */
/* Cron watcher: schedule an absolute timer, when and period in time_t seconds */
int uev_cron_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, time_t when, time_t period);
int uev_cron_set (uev_t *w, int when, time_t period); /* Change when or period */
int uev_cron_start (uev_t *w); /* Restart a stopped cron */
int uev_cron_stop (uev_t *w); /* Stop a cron */
/* Signal watcher: signo is the signal to wait for, e.g., SIGTERM */
int uev_signal_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo);
int uev_signal_set (uev_t *w, int signo); /* Change signal to wait for */
int uev_signal_start(uev_t *w); /* Restart a stopped signal watcher */
int uev_signal_stop (uev_t *w); /* Stop signal watcher */
/* Generic event watcher, post events for later processing, or from forked child */
int uev_event_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg);
int uev_event_post (uev_t *w);
int uev_event_stop (uev_t *w);
```
### Create an Event Context
To monitor events the developer first creates an *event context*, this
is achieved by calling `uev_init()` with a pointer to a (thread) local
`uev_ctx_t` variable.
```C
uev_ctx_t ctx;
uev_init(&ctx);
```
### Register an Event Watcher
For each event to monitor, be it a signal, cron/timer or a file/network
descriptor, a *watcher* must be registered with the event context. The
watcher, an `uev_t`, is registered by calling the event type's `_init()`
function with the `uev_ctx_t` context, the callback, and an optional
argument.
Here is a signal example:
```C
void cleanup_exit(uev_t *w, void *arg, int events)
{
if (UEV_ERROR == events)
puts("Ignoring signal watcher error ...");
else
printf("Got signal (signo %d) from PID %d\n",
w->siginfo.ssi_signo, w->siginfo.ssi_pid);
/* Graceful exit, with optional cleanup ... */
uev_exit(w->ctx);
}
int main(void)
{
uev_t sigterm_watcher;
.
.
uev_signal_init(&ctx, &sigterm_watcher, cleanup_exit, NULL, SIGTERM);
.
.
}
```
Notice that the callback must be prepared to handle `UEV_ERROR`. I/O
watchers in particular, but also timer watchers, must be restarted if
required by the application. libuEv automatically tries to restart a
signal watcher, but should that fail the callback will return error as
well.
I/O watchers should also check for `UEV_HUP`, preferably when handling
any short `read()` or `write()` system calls. A short read on a socket
may be due to the remote end having performed a `shutdown()`. This is
signaled to the callback using `UEV_HUP` in the `events` mask.
### Start Event Loop
When all watchers are registered, call the *event loop* with `uev_run()`
and the argument to the event context. The `flags` parameter can be
used to integrate [libuEv][] into another event loop.
In this example we set `flags` to none:
```C
result = uev_run(&ctx, UEV_NONE);
```
With `flags` set to `UEV_ONCE` the event loop returns as soon as it has
served the first event. If `flags` is set to `UEV_ONCE | UEV_NONBLOCK`
the event loop returns immediately if no event is available.
```
if (result < 0)
errx(result, "Unrecoverable event loop error, error %d", result);
```
If the call to `uev_run()` fails you should notify the user somehow.
libuEv fails if there is an invalid pointer, if `uev_init()` was not
called, or recurring `epoll()` errors thare are impossible to recover
from should occur. This is true for individual watchers as well, in
particular signal and timer watchers which can fail in miserable ways.
**Note:** libuEv handles many types of errors, stream close, or peer
shutdowns internally, but also lets the callback run. This is useful
for stateful connections to be able to detect EOF.
### Summary
1. Set up an event context with `uev_init()`
2. Register event callbacks with the event context using
`uev_io_init()`, `uev_signal_init()` or `uev_timer_init()`
3. Make sure callbacks checks their `events` mask and handles:
- `UEV_ERROR`, e.g. I/O watchers must be restarted
- `UEV_HUP`, reading any remaining data on the descriptor
In both of these cases the watcher is stopped by libuEv. On HUP the
descriptor/connection must be reopened and the watcher reinitialized
with `uev_io_set()`, if required by the application.
4. Start the event loop with `uev_run()`
5. Exit the event loop with `uev_exit()`, possibly from a callback
**Note 1:** Make sure to use non-blocking stream I/O! Most hard to find
bugs in event driven applications are due to sockets and files being
opened in blocking mode. Be careful out there!
**Note 2:** When closing a descriptor or socket, make sure to first stop
your watcher, if possible. This will help prevent any nasty side
effects on your program.
**Note 3:** a certain amount of care is needed when dealing with APIs
that employ signalfd. If your application use `system()` you replace
that with `fork()`, and then in the child, unblock all signals blocked
by your parent process, before you run `exec()`. This because Linux
does not unblock signals for your children, and neither does most
(all?) C-libraries. See the [finit][6] project's implementation of
`run()` for an example of this. For more details on this issue, see
[this article][4] at [lwn.net](http://lwn.net).
Using -luev
-----------
libuEv is by default installed as a library with a few header files, you
should only ever need to include one:
```C
#include <uev/uev.h>
```
The output from the `pkg-config` tool holds no surprises:
```sh
$ pkg-config --libs --static --cflags libuev
-I/usr/local/include -L/usr/local/lib -luev
```
The prefix path `/usr/local/` shown here is only the default. Use the
`configure` script to select a different prefix when installing libuEv.
For GNU autotools based projects, use the following in `configure.ac`:
```sh
# Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 1.4.0])
```
and in your `Makefile.am`:
```sh
proggy_CFLAGS = $(uev_CFLAGS)
proggy_LDADD = $(uev_LIBS)
```
Joystick Example
----------------
Here follows a very brief example to illustrate how one can use libuEv
to act upon joystick input.
```C
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <uev/uev.h>
struct js_event {
uint32_t time; /* event timestamp in milliseconds */
int16_t value; /* value */
uint8_t type; /* event type */
uint8_t number; /* axis/button number */
} e;
/*
* Called on any joystick event
*
* Note: We handle errors here by exiting, for a background daemon with
* multiple watchers you may want another approach.
*/
static void joystick_cb(uev_t *w, void *arg, int events)
{
ssize_t cnt;
if (UEV_ERROR == events) {
/* Possibly joystick was unplugged */
warnx("Spurious problem with the joystick watcher, restarting.");
uev_io_start(w);
return;
}
cnt = read(w->fd, &e, sizeof(e));
if (cnt < 0) {
warn("Failed reading joystick event");
return;
}
if (cnt == 0 || UEV_HUP == events) {
warn("Joystick disconnected");
return;
}
switch (e.type) {
case 1:
printf("Button %d %s\n", e.number, e.value ? "pressed" : "released");
break;
case 2:
printf("Joystick axis %d moved, value %d!\n", e.number, e.value);
break;
}
}
int main(void)
{
uev_ctx_t ctx;
uev_t js;
int fd;
fd = open("/dev/input/js0", O_RDONLY, O_NONBLOCK);
if (fd < 0)
errx(errno, "Cannot find a joystick attached.");
uev_init(&ctx);
uev_io_init(&ctx, &js, joystick_cb, NULL, fd, UEV_READ);
puts("Starting, press Ctrl-C to exit.");
return uev_run(&ctx, 0);
}
```
To build the example, follow installation instructions below, then save
the code as `joystick.c` and call GCC
```sh
$ gcc `pkg-config --libs --static --cflags libuev` -o joystick joystick.c
```
Alternatively, call the `Makefile` with <kbd>make joystick</kbd> from
the unpacked [libuEv][] distribution.
More complete and relevant example uses of [libuEv][] is the FTP/TFTP
server [uftpd][5], and the Linux `/sbin/init` replacement [finit][6].
Both successfully employ [libuEv][].
Also see the `bench.c` program (<kbd>make bench</kbd> from within the
library) for [reference benchmarks][7] against [libevent][1] and
[libev][2].
[1]: http://libevent.org
[2]: http://software.schmorp.de/pkg/libev.html
[4]: http://lwn.net/Articles/415684/
[5]: https://github.com/troglobit/uftpd
[6]: https://github.com/troglobit/finit
[7]: http://libev.schmorp.de/bench.html
[libuEv]: https://github.com/troglobit/libuev
[GLIBC workaround]: https://sourceware.org/glibc/wiki/Y2038ProofnessDesign
[Dave Zarzycki, Apple]: http://www.youtube.com/watch?v=cD_s6Fjdri8
|