First cleanup of README after comments by @vonj, more to come ...

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson committed 2014-12-30 19:36:18 +01:00
1 parent 23935c248a
commit 93d9d06802
1 file changed
+137 -74
+137 -74
View File
@@ -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 <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
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)