15 Commits
Author SHA1 Message Date
Joachim Nilsson 64f32c9086 Fix make dist build target and bump version again.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 01:30:30 +01:00
Joachim Nilsson 5065440f52 Minor change libuev --> libuEv and bump version for release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 01:18:07 +01:00
Joachim Nilsson 8e1f384c38 README: Minor cleanup, after comments by @vonj
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-06 22:50:09 +01:00
Joachim Nilsson 3094ef2887 README.md: Last update, possibly. Mostly tweaks and cleanup.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-31 02:19:20 +01:00
Joachim Nilsson e50f8aee94 Yet another API section update.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:22:05 +01:00
Joachim Nilsson 0bf22de8e0 More API section updates.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:13:34 +01:00
Joachim Nilsson 78d140f310 Cleanup API.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:08:38 +01:00
Joachim Nilsson e681657ca7 Syntax highlight API on GitHub
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:04:02 +01:00
Joachim Nilsson e707d24290 Update README on the uev_run() flags parameter and slight fixes.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:02:45 +01:00
Joachim Nilsson 7f10528d80 main.c:uev_run() - Document mysterious flags parameter.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 20:49:08 +01:00
Joachim Nilsson d352e176dc Minor spell check fixes and additions.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 19:53:56 +01:00
Joachim Nilsson 93d9d06802 First cleanup of README after comments by @vonj, more to come ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 19:36:18 +01:00
Joachim Nilsson 23935c248a Add joystick reading example, for documentation.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 18:07:22 +01:00
Joachim Nilsson 59afe57995 Minor update to README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-28 21:54:40 +01:00
Joachim Nilsson 1dfdc875d7 Update README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-28 21:36:01 +01:00
11 changed files with 325 additions and 67 deletions

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
Current maintainer of libuev:
Current maintainer of libuEv:
Joachim Nilsson <troglobit()gmail!com>
Original LibUEvent author:
+7 -3
View File
@@ -1,4 +1,4 @@
# -*-Makefile-*- for libuev
# -*-Makefile-*- for libuEv
#
# Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
# Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
@@ -25,7 +25,7 @@
.PHONY: all test clean
#VERSION = $(shell git tag -l | tail -1)
VERSION ?= 1.0.0
VERSION ?= 1.0.2
NAME = libuev
PKG = $(NAME)-$(VERSION)
ARCHIVE = $(PKG).tar.xz
@@ -111,6 +111,10 @@ bench: Makefile bench.o $(STATICLIB)
@printf " BENCH %s\n" $(STATICLIB)
@$(CC) $(CPPFLAGS) -g -o bench bench.c $(STATICLIB) && ./bench
joystick: Makefile joystick.o $(STATICLIB)
@printf " JOTST %s\n" $(STATICLIB)
@$(CC) $(CPPFLAGS) -g -o joystick joystick.c $(STATICLIB) && ./joystick
check:
@$(CHECK) $(CHECK_FLAGS) $(CPPFLAGS) $(SRCS) bench.c
@@ -122,7 +126,7 @@ distclean: clean
dist:
@echo "Building .xz tarball of $(PKG) in parent dir..."
git archive --format=tar --prefix=$(PKG)/ $(VERSION) | xz >../$(ARCHIVE)
git archive --format=tar --prefix=$(PKG)/ v$(VERSION) | xz >../$(ARCHIVE)
@(cd ..; md5sum $(ARCHIVE) | tee $(ARCHIVE).md5)
# Include automatically generated rules
+209 -46
View File
@@ -1,68 +1,231 @@
libuev - Micro event loop library
=================================
libuEv | Simple event loop for Linux
====================================
The libuev library is a small event library in the style of libevent,
the excellent libev and the venerable Xt(3) event loop.
> “Why an event loop, why not use threads?”
It is heavily modelled after the libev API, but will never be as
portable to other UNIX systems. The primary target of this project is
modern embedded Linux systems. You will need kernel and C-library
support for epoll(7), timerfd and signalfd.
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 sprinkled on
top, to act upon: 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 must deal with the side effects
of concurrency, 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, with multiple threads a program may
actually run slower! Always profile your program, and if possible, test
it on different architectures.
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 a modern Linux system.
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.
```C
/* Callback example, arg is passed from watcher's *_init()
* w->fd holds the file descriptor, events is set by libuEv
* to indicate if any of UEV_READ and/or UEV_WRITE is ready.
*/
void callback(uev_ctx_t *ctx, uev_t *w, void *arg, int events);
/* Event loop functions, notice use of flags! */
int uev_init (uev_ctx_t *ctx);
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 is non-blocking, events is UEV_READ and/or UEV_WRITE */
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); /* Change timeout or period */
int uev_timer_stop (uev_t *w); /* Stop a timer */
/* 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_stop(uev_t *w); /* Stop signal watcher */
```
To be able to set up 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 in libuEV. A watcher is nothing more
than a mechnism that polls a file descriptor. Register a watcher with a
callback to the event context by passing the `uev_ctx_t` variable, along
with an `uev_t` variable to each event's `_init()` function.
When all watchers are registered call the event loop with `uev_run()`
and the argument to the event context. The `flags` parameter is slightly
mysterious, but can be used to integrate libuEv into another event loop.
With `flags` set to `UEV_ONCE` the event loop returns after having
served the first event. If `flags` is set to `UEV_ONCE | UEV_NONBLOCK`
the event loop returns immediately if no event is available.
Summary:
1. Prepare an event context with `uev_init()`
2. Register event callbacks with `uev_io_init()`, `uev_signal_init()`
or `uev_timer_init()`
3. Enter the event loop with `uev_run()`
4. Leave the event loop with `uev_exit()`, possibly from a callback
**Note:** 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!
The C API
---------
Example
-------
The typical application will do the following steps
Here follows a very brief example to illustrate how one can use libuEv
to act upon joystick input.
1. Prepare an event context with `uev_init()`
2. Register callbacks with `uev_io_init()` and `uev_timer_init()`
3. Enter the event loop with `uev_run()`
```C
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
**Note:** Make sure to use non-blocking stream I/O!
#include "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;
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. Alternatively, call the
`Makefile` with `make joystick` from this directory.
For a more complete, and perhaps more relevant example, see the code for
the TFTP/FTP server [uftpd](https://github.com/troglobit/uftpd). It
uses libuEv as a GIT submodule.
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
---------------
The library is built and developed for GNU/Linux systems, as such it may
use GNU GCC and GNU Make specific features. Patches to support *BSD
kqueue are most welcome.
The library is built for and developed on GNU/Linux systems, so it may
use GCC and GNU Make specific extensions here and there. This is not on
purpose and patches to correct this are most welcome. Particularly
patches to support *BSD and its kqueue interface.
* `make all`: The library
* `make test`: Test and showcase
* `make install`: Honors $prefix and $DESTDIR environment variables
* `make all`: The library
* `make test`: Test and showcase
* `make install`: Honors `$prefix` and `$DESTDIR` environment variables
Size of libuEv:
$ make strip
CC main.o
CC io.o
CC timer.o
CC signal.o
ARCHIVE libuev.a
LINK libuev.so.1
STRIP libuev.a
STRIP libuev.so.1
text data bss dec hex filename
1177 0 0 1177 499 main.o (ex libuev.a)
308 0 0 308 134 io.o (ex libuev.a)
682 0 0 682 2aa timer.o (ex libuev.a)
563 0 0 563 233 signal.o (ex libuev.a)
6306 768 8 7082 1baa libuev.so.1
Warning
-------
Origin & References
--------------------
Please be advised, libuev is not thread safe! It is primarily intended
for use-cases where the engineer wants to avoid the use of threads. To
support threads the signal handling needs to be patched first, other
than that an event context per thread should be sufficient.
LibuEv (this library) was originally based on
[LibUEvent](http://code.google.com/p/libuevent/) by
[Flemming Madsen](http://www.madsensoft.dk/) but has been completely
rewritten with a much clearer API. Now more similar to the famous
[libev](http://software.schmorp.de/pkg/libev.html) by Mark Lehmann.
Another small event library used for inspiration is the very small
[Picoev](https://github.com/kazuho/picoev) by
[Oku Kazuho](https://github.com/kazuho).
* http://code.google.com/p/libuevent/
* http://software.schmorp.de/pkg/libev.html
* http://libev.schmorp.de/bench.html
* http://libevent.org/
* http://developer.cybozu.co.jp/archives/kazuho/2009/08/picoev-a-tiny-e.html
* http://coderepos.org/share/browser/lang/c/picoev/
History and References
----------------------
Libuev is a fork of the original LibUEvent by Flemming Madsen but has
been completely rewritten and is now more similar to the famous libev by
Mark Lehmann. Another small event library used for inspiration is the
very small Picoev by Oku Kazuho.
* http://code.google.com/p/libuevent/
* http://software.schmorp.de/pkg/libev.html
* http://libev.schmorp.de/bench.html
* http://libevent.org/
* 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
[GitHub](https://github.com/troglobit/libuev)
+1 -1
View File
@@ -32,7 +32,7 @@
*
* Sun 04/08/2013 - Modified by Joachim Nilsson <troglobit@gmail.com>
*
* Adaptations for libuev, no libev/libevent API wrappers available.
* Adaptations for libuEv, no libev/libevent API wrappers available.
* Reindent to Linux coding style
*/
+2 -2
View File
@@ -1,4 +1,4 @@
/* libuev - Micro event loop library
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
@@ -28,7 +28,7 @@
/**
* Create an I/O watcher
* @param ctx A valid libuev context
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb I/O callback
* @param arg Optional callback argument
+84
View File
@@ -0,0 +1,84 @@
/* Simple example of how to use libuEv with Linux joystick API
*
* Copyright (c) 2014 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
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;
static void joystick_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *UNUSED(arg), int UNUSED(events))
{
if (read (w->fd, &e, sizeof(e)) < 0)
errx(errno, "Failed reading joystick event");
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);
}
/**
* Local Variables:
* compile-command: "gcc -o joystick joystick.c io.o timer.o signal.o main.o"
* version-control: t
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+14 -7
View File
@@ -1,4 +1,4 @@
/* libuev - Micro event loop library
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
@@ -33,7 +33,7 @@
#define UNUSED(arg) arg __attribute__ ((unused))
/* Private to libuev, do not use directly! */
/* Private to libuEv, do not use directly! */
int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events)
{
if (!ctx || !w) {
@@ -54,7 +54,7 @@ int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, vo
return 0;
}
/* Private to libuev, do not use directly! */
/* Private to libuEv, do not use directly! */
int uev_watcher_start(uev_t *w)
{
struct epoll_event ev;
@@ -77,7 +77,7 @@ int uev_watcher_start(uev_t *w)
return 0;
}
/* Private to libuev, do not use directly! */
/* Private to libuEv, do not use directly! */
int uev_watcher_stop(uev_t *w)
{
if (!w) {
@@ -118,7 +118,7 @@ int uev_init(uev_ctx_t *ctx)
/**
* Terminate the event loop
* @param ctx A valid libuev context
* @param ctx A valid libuEv context
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
@@ -162,10 +162,17 @@ int uev_exit(uev_ctx_t *ctx)
/**
* Start the event loop
* @param ctx A valid libuev context
* @param ctx A valid libuEv context
* @param flags A mask of %UEV_ONCE and %UEV_NONBLOCK, or zero
*
* @return POSIX OK(0) upon successful termination of the event loop, or non-zero on error.
* With @flags set to %UEV_ONCE the event loop returns after the first
* event has been served, useful for instance to set a timeout on a file
* descriptor. If @flags also has the %UEV_NONBLOCK flag set the event
* loop will return immediately if no event is pending, useful when run
* inside another event loop.
*
* @return POSIX OK(0) upon successful termination of the event loop, or
* non-zero on error.
*/
int uev_run(uev_ctx_t *ctx, int flags)
{
+2 -2
View File
@@ -1,4 +1,4 @@
/* libuev - Micro event loop library
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
@@ -33,7 +33,7 @@
/**
* Create a signal watcher
* @param ctx A valid libuev context
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb Timer callback
* @param arg Optional callback argument
+1 -1
View File
@@ -1,4 +1,4 @@
/* Test libuev event library
/* Test libuEv event library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
+2 -2
View File
@@ -1,4 +1,4 @@
/* libuev - Micro event loop library
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
@@ -43,7 +43,7 @@ static void msec2tspec(int msec, struct timespec *ts)
/**
* Create and start a timer watcher
* @param ctx A valid libuev context
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb Callback function
* @param arg Optional callback argument
+2 -2
View File
@@ -1,4 +1,4 @@
/* libuev - Micro event loop library
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013 Joachim Nilsson <troglobit()gmail!com>
@@ -52,7 +52,7 @@ typedef enum {
/* Forward declare due to dependencys, don't try this at home kids. */
struct uev;
/* Main libuev context type */
/* Main libuEv context type */
typedef struct {
int running;
int fd; /* For epoll() */