diff --git a/README.md b/README.md index 4c77670..04f1099 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,85 @@ -libuev | Micro event loop library -================================= +libuEv | Simple event loop for Linux +==================================== -Libuev is a small event library in the style of libevent, the excellent -libev and the venerable Xt(3) event loop. The primary target is modern -embedded Linux systems. For power users, libuev is mainly a convenience -wrapper for epoll, timerfd and signalfd. +> "Why an event loop library, why not use threads?" + +With the advent of light-weight processes (threads) programmers these +days have a [golden hammer](http://c2.com/cgi/wiki?GoldenHammer) they +often swing without consideration. Event loops and non-blocking I/O is +often a far easier approach, as well as less error prone. + +The purpose of many applications is, with a little logic sprikled on +top, to act on: network packets entering an interface, timeouts +expiring, mouse clicks, or other types of events. Such applications are +often very well suited to use an event loop. + +Applications that need to churn massively parallel algorithms are more +suitable for running multiple (independent) threads on several CPU +cores. However, threaded applications need to deal with all the issues +concurrency can cause, like: race conditions, deadlocks, live locks, +etc. Writing error free threaded applications is hard, debugging them +can be even harder. + +Sometimes the combination of multiple threads *and* an event loop per +thread can be the best approach, but each application of course needs to +be broken down individually to find the most optimal approach. Do keep +in mind, however, that not all systems your application will run on have +multiple CPU cores -- some small embedded systems still use a single CPU +core, even though they run Linux. + +LibuEv is a simple event loop in the style of the more established +[libevent](http://libevent.org/), +[libev](http://software.schmorp.de/pkg/libev.html) and the venerable +[Xt(3)](http://unix.com/man-page/All/3x/XtDispatchEvent) event loop. +The *u* (micro) in the name refers to both the small feature set and the +small size overhead impact of the library. The primary target of libuEv +is modern Linux systems. + +Experienced developers may appreciate that libuEv is built on top of +modern Linux APIs: epoll, timerfd and signalfd. -Usage ------ +API +--- -The user initializes an event context to which timer and I/O stream -watchers are registered. A typical application then enters the event -loop that will only return at program termination. +Here is the interface to libuEv. It handles three different types of +events: I/O (files, sockets, message queues, etc.), timers, and +signals. + + /* Event loop functions */ + int uev_init (uev_ctx_t *ctx); + int uev_exit (uev_ctx_t *ctx); + int uev_run (uev_ctx_t *ctx, int flags); /* UEV_ONCE, UEV_NONBLOCK, or zero(0) */ + + /* Event callback definition, arg is the same arg as passed to below *_init() functions */ + typedef void (uev_cb_t)(uev_ctx_t *ctx, uev_t *w, void *arg, int events); + + /* I/O watcher */ + 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_stop (uev_t *w); + + /* Timer watcher, timeout 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); + int uev_timer_stop (uev_t *w); + + /* Signal watcher */ + 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); + int uev_signal_stop (uev_t *w); + +To be able to setup callbacks to events the developer first need to +create an *event context*, achieved by calling `uev_init()` with a +pointer to a local `uev_ctx_t` variable. + +Events are monitored by watchers and each watcher needs to be +registered, with a callback, with the event context. This is done by +passing the `uev_ctx_t` variable, along with an `uev_t` variable to each +event's `_init()` function. 1. Prepare an event context with `uev_init()` - 2. Register callbacks with `uev_io_init()`, `uev_signal_init()` and + 2. Register callback with `uev_io_init()`, `uev_signal_init()` or `uev_timer_init()` 3. Enter the event loop with `uev_run()` 4. Exit the event loop with `uev_exit()`, possibly from a callback @@ -28,63 +92,71 @@ loop that will only return at program termination. Example ------- -Here is a very brief example to illustrate how one can use libuev. But -please, pay more attention to return values than the author did below. +Here follows a very brief example to illustrate how one can use libuEv +to act on joystick input. - #include "uev.h" - - static void signal_cb(uev_ctx_t *ctx, uev_t *w, void *UNUSED(arg), int UNUSED(events)) - { - printf("Received signal %d, exiting ...\n", w->signo); - uev_exit(ctx); - } - - static void read_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void UNUSED(*arg), int UNUSED(events)) - { - char buf[80]; +```C +#include +#include +#include +#include +#include +#include - read(w->fd, buf, sizeof(buf)); - /* Do stuff ... watcher restarted when callback exits */ - } - - int main(void) - { - int fd; - uev_t io_watcher, sig_watcher; - uev_ctx_t ctx; - - uev_init(&ctx); - - fd = open("/etc/passwd", O_RDONLY, O_NONBLOCK); - uev_io_init(&ctx, &io_watcher, read_cb, NULL, fd, UEV_READ); +#include "uev.h" - uev_signal_init(&ctx, &sig_watcher, signal_cb, NULL, SIGINT); +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; - return uev_run(&ctx); - } +static void joystick_cb(uev_ctx_t *ctx, uev_t *w, void *arg, int events) +{ + read (w->fd, &e, sizeof(e)); + + switch (e.type) { + case 1: + if (e.value) printf("Button %d pressed\n", e.number); + else printf("Button %d released\n", e.number); + break; + + case 2: + printf("Joystick axis %d moved, value %d!\n", e.number, e.value); + break; + } +} + +int main(void) +{ + int fd = open("/dev/input/js1", O_RDONLY, O_NONBLOCK); + uev_t js1_watcher; + uev_ctx_t ctx; + + if (fd < 0) + errx(errno, "Cannot find a joystick attached."); + + uev_init(&ctx); + uev_io_init(&ctx, &js1_watcher, joystick_cb, NULL, fd, UEV_READ); + + puts("Starting, press Ctrl-C to exit."); + + return uev_run(&ctx, 0); +} +``` + +To compile the program, save the code as `joystick.c` and call GCC with +`gcc -o joystick joystick.c io.c timer.c signal.c main.c` from this +directory, skips using a Makefile altogether. For a more complete, and perhaps more relevant example, see the code for -the TFTP/FTP server [uftpd](https://github.com/troglobit/uftpd). +the TFTP/FTP server [uftpd](https://github.com/troglobit/uftpd). It +uses libuEv as a GIT submodule. - -API ---- - - int uev_init (uev_ctx_t *ctx); - int uev_exit (uev_ctx_t *ctx); - int uev_run (uev_ctx_t *ctx, int flags); - - 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_stop (uev_t *w); - - 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); - int uev_timer_stop (uev_t *w); - - 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); - int uev_signal_stop (uev_t *w); +Also, see the `bench.c` program (`make bench` from within the library) +for [reference benchmarks](http://libev.schmorp.de/bench.html) against +libevent and libev. Build & Install @@ -98,7 +170,7 @@ kqueue are most welcome. * `make test`: Test and showcase * `make install`: Honors `$prefix` and `$DESTDIR` environment variables -Size of libuev: +Size of libuEv: $ make strip CC main.o @@ -117,19 +189,10 @@ Size of libuev: 6306 768 8 7082 1baa libuev.so.1 -Caveat ------- - -Please be advised, libuev is not thread safe! It is primarily intended -for developers who want to entirely avoid using threads. To support -threads the signal handling needs to be patched first, other than that -an event context per thread should be sufficient. - - Origin & References -------------------- -Libuev was originally based on +LibuEv was originally based on [LibUEvent](http://code.google.com/p/libuevent/) by [Flemming Madsen](http://www.madsensoft.dk/) but has been completely rewritten and is now more similar to the famous @@ -145,6 +208,6 @@ Another small event library used for inspiration is the very small * http://developer.cybozu.co.jp/archives/kazuho/2009/08/picoev-a-tiny-e.html * http://coderepos.org/share/browser/lang/c/picoev/ -Libuev is maintained by [Joachim Nilsson](mailto:troglobit@gmail.com) at +LibuEv is maintained by [Joachim Nilsson](mailto:troglobit@gmail.com) at [GitHub](https://github.com/troglobit/libuev)