10 Commits
Author SHA1 Message Date
Joachim Nilsson bfd046054b Slight update before release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 10:54:32 +01:00
Joachim Nilsson 6a40d51d26 Bump version to v1.1.0 for release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 10:44:39 +01:00
Joachim Nilsson f513408316 Remove clang from Travis build to avoid double Coverity runs
There currenty is no *simple* way of preventing Coverity from being run
twice, so we just remove the clang build in the meantime :-/

See: travis-ci/travis-ci#1975

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 10:41:57 +01:00
Joachim Nilsson 30b5faa318 Update ChangeLog and bump dev version to 1.1 due to the major changes
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 09:53:18 +01:00
Joachim Nilsson fe8f3f19da Massively improve error handling, check for EPOLLHUP & EPOLLERR etc.
- Make sure to listen for EPOLLRDHUP in watchers so users can be
  notified when a peer closes a connection.
- Handle EPOLLHUP and EPOLLERR gracefully.  Even handle spurious
  EPOLLHUP by means of an error counter.  If we receive too many errors
  we now restart the epoll fd and re-add all watchers.
- Return error from `epoll_ctl()` in `uev_watcher_stop()`
- Ignore any error from `uev_watcher_stop()` in `uev_watcher_set()`
- Move LIST_REMOVE() et al from io.c to core uev.c, should be
  done for timer and signal callbacks as well, of course.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 09:41:22 +01:00
Joachim Nilsson 71208b7e21 Update ChangeLog
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-16 00:27:17 +01:00
Joachim Nilsson 312b8257fb Fix issue #2: 'make install' stopped working in v1.0.5
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-16 00:26:55 +01:00
Joachim Nilsson f29f3b7f38 Minor followup to bfcea55, add else statment.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-16 00:25:38 +01:00
Joachim Nilsson cd4daf0d36 README: Update API section with new _start() functions.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-15 18:42:56 +01:00
Joachim Nilsson 43acbadc13 Open v1.0.6-dev window.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-15 18:35:43 +01:00
8 changed files with 157 additions and 63 deletions

No files matched your search

-1
View File
@@ -2,7 +2,6 @@
language: c
compiler:
- gcc
- clang
env:
global:
+25 -2
View File
@@ -3,6 +3,28 @@ Change Log
All notable changes to the project are documented in this file.
[v1.1.0]
--------
Massively improved error handling.
### Changes
- Handle case when user closes a descriptor *before* stopping a watcher.
- Handle `EPOLLHUP` and `EPOLLERR`. Restart `epoll(7)` descriptor and
all watchers when an error count reaches a MAX value -- handles stale
descriptors or cases when kernel does not notice updated descriptors.
- Return error when stopping a watcher fails.
- Update [README.md] with new `uev_*_start()` functions.
- Bump dev version to 1.1 due to the number of significant changes.
### Fixes
- Remove `test.c` from `DISTFILES` in `Makefile`. You need the
comeplete sources to build the examples now. Thanks to @karasz for
the heads up on this and the musl libc issue with missing `queue.h`!
- Fix broken link to [v1.0.5] in this file.
[v1.0.5]
--------
@@ -85,8 +107,9 @@ v0.0.1 - 2012-03-17
[Initial announcement] of [libuevent] by [Flemming Madsen] to the Lua
users mailing list.
[v1.0.4]: https://github.com/troglobit/libuev/compare/v1.0.4...v1.0.5
[UNRELEASED]: https://github.com/troglobit/libuev/compare/v1.1,0...HEAD
[v1.1.0]: https://github.com/troglobit/libuev/compare/v1.0.5...v1.1.0
[v1.0.5]: https://github.com/troglobit/libuev/compare/v1.0.4...v1.0.5
[v1.0.4]: https://github.com/troglobit/libuev/compare/v1.0.3...v1.0.4
[v1.0.3]: https://github.com/troglobit/libuev/compare/v1.0.2...v1.0.3
[v1.0.2]: https://github.com/troglobit/libuev/compare/v1.0.1...v1.0.2
+2 -2
View File
@@ -25,7 +25,7 @@
.PHONY: all test clean joystick test examples
#VERSION = $(shell git tag -l | tail -1)
VERSION ?= 1.0.5
VERSION ?= 1.1.0
NAME = libuev
PKG = $(NAME)-$(VERSION)
ARCHIVE = $(PKG).tar.xz
@@ -46,7 +46,7 @@ prefix ?= /usr/local
libdir ?= $(prefix)/lib
datadir ?= $(prefix)/share/doc/$(LIBNAME)
incdir ?= $(prefix)/include
DISTFILES = README CHANGELOG TODO LICENSE test.c
DISTFILES = README CHANGELOG TODO LICENSE
HEADER = uev.h
OBJS := uev.o io.o timer.o signal.o
+40 -29
View File
@@ -24,14 +24,14 @@ 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.
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 must deal with the side effects
of concurrency, like: race conditions, deadlocks, live locks, etc.
of concurrency, like race conditions, deadlocks, live locks, etc.
Writing error free threaded applications is hard, debugging them can be
even harder.
@@ -65,31 +65,34 @@ 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);
/* 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 */
/* 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_start (uev_t *w);
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_start (uev_t *w); /* Restart a stopped timer */
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_start(uev_t *w); /* Restart a stopped signal watcher */
int uev_signal_stop (uev_t *w); /* Stop signal watcher */
```
To be able to set up callbacks to events the developer first need to
@@ -108,6 +111,10 @@ 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.
In the case of errors, stream close, or peer shutdown libuEv handles
much internally, but also lets the callback run. This is useful for
stateful connections to be able to detect EOF.
Summary:
1. Prepare an event context with `uev_init()`
@@ -120,7 +127,11 @@ Summary:
bugs in event driven applications are due to sockets and files being
opened in blocking mode. Be careful out there!
**Note 2:** As mentioned above, a certain amount of care is needed when
**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:** As mentioned above, a certain amount of care is needed when
dealing with signalfd. This means that if your application, for
instance, uses `system()` you must redesign that to use `fork()`, and
then in the child, unblock all signals blocked by your parent process,
+2
View File
@@ -103,6 +103,8 @@ static void work_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *arg, int UNU
printf("Child exited normally => %d\n", WEXITSTATUS(status));
else if (WCOREDUMP(status))
printf("Child crashed!\n");
else
printf("Child did not exit normally!\n");
}
static void exit_cb(uev_ctx_t *ctx, uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
+3 -8
View File
@@ -62,11 +62,8 @@ int uev_io_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int e
*/
int uev_io_set(uev_t *w, int fd, int events)
{
if (uev_io_stop(w))
return -1;
/* Remove from internal list */
LIST_REMOVE(w, link);
/* Ignore any errors, only to clean up anything lingering ... */
uev_io_stop(w);
return uev_io_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, fd, events);
}
@@ -90,9 +87,7 @@ int uev_io_start(uev_t *w)
*/
int uev_io_stop(uev_t *w)
{
int status = uev_watcher_stop(w);
return status;
return uev_watcher_stop(w);
}
/**
+84 -21
View File
@@ -24,6 +24,7 @@
*/
#include <errno.h>
#include <fcntl.h> /* O_CLOEXEC */
#include <string.h> /* memset() */
#include <sys/epoll.h>
#include <sys/signalfd.h> /* struct signalfd_siginfo */
@@ -33,6 +34,28 @@
#define UNUSED(arg) arg __attribute__ ((unused))
static int _init(uev_ctx_t *ctx, int close_old)
{
int fd = epoll_create1(O_CLOEXEC);
if (fd < 0)
return -1;
if (close_old)
close(ctx->fd);
ctx->fd = fd;
return 0;
}
/* Simple check if a descriptor is still valid in the kernel */
static int is_valid_fd(int fd)
{
return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
}
/* 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)
{
@@ -41,16 +64,14 @@ int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, vo
return -1;
}
memset(w, 0, sizeof(*w));
w->ctx = ctx;
w->fd = fd;
w->type = type;
w->active = 0;
w->fd = fd;
w->cb = (void *)cb;
w->arg = arg;
w->events = events;
LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
return 0;
}
@@ -67,13 +88,16 @@ int uev_watcher_start(uev_t *w)
if (w->active)
return 0;
ev.events = w->events;
ev.events = w->events | EPOLLRDHUP;
ev.data.ptr = w;
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
return -1;
w->active = 1;
/* Add to internal list for bookkeeping */
LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
return 0;
}
@@ -88,10 +112,15 @@ int uev_watcher_stop(uev_t *w)
if (!w->active)
return 0;
/* Remove from kernel */
epoll_ctl(w->ctx->fd, EPOLL_CTL_DEL, w->fd, NULL);
w->active = 0;
/* Remove from internal list */
LIST_REMOVE(w, link);
/* Remove from kernel */
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_DEL, w->fd, NULL) < 0)
return -1;
return 0;
}
@@ -103,17 +132,15 @@ int uev_watcher_stop(uev_t *w)
*/
int uev_init(uev_ctx_t *ctx)
{
int fd;
fd = epoll_create(1);
if (fd < 0)
if (!ctx) {
errno = EINVAL;
return -1;
}
memset(ctx, 0, sizeof(*ctx));
ctx->fd = fd;
LIST_INIT(&ctx->watchers);
return 0;
return _init(ctx, 0);
}
/**
@@ -206,20 +233,54 @@ int uev_run(uev_ctx_t *ctx, int flags)
if (EINTR == errno)
continue; /* Signalled, try again */
exit:
result = -1;
ctx->running = 0;
break;
/* Unrecoverable error, cleanup and exit with error. */
uev_exit(ctx);
return -1;
}
for (i = 0; ctx->running && i < nfds; i++) {
w = (uev_t *)events[i].data.ptr;
if (events[i].events & (EPOLLHUP | EPOLLERR)) {
ctx->errors++;
if (ctx->errors >= 42) {
uev_t *tmp, *retry = w;;
/* If not valid anymore, try to remove, ignore any errors. */
if (!is_valid_fd(w->fd))
uev_watcher_stop(w);
/* Must recreate epoll fd now ... */
if (_init(ctx, 1)) {
uev_exit(ctx);
return -2;
}
/* Restart watchers in new efd */
LIST_FOREACH_SAFE(w, &ctx->watchers, link, tmp) {
if (w->active) {
w->active = 0;
LIST_REMOVE(w, link);
uev_watcher_start(w);
}
}
uev_watcher_start(retry);
/* New efd, restart everything! */
ctx->errors = 0;
continue;
}
}
if (UEV_TIMER_TYPE == w->type) {
uint64_t exp;
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp))
goto exit;
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
uev_exit(ctx);
return -3;
}
if (!w->period)
w->timeout = 0;
@@ -229,8 +290,10 @@ int uev_run(uev_ctx_t *ctx, int flags)
struct signalfd_siginfo fdsi;
ssize_t sz = sizeof(fdsi);
if (read(w->fd, &fdsi, sz) != sz)
goto exit;
if (read(w->fd, &fdsi, sz) != sz) {
uev_exit(ctx);
return -4;
}
}
if (w->cb)
+1
View File
@@ -57,6 +57,7 @@ struct uev;
typedef struct {
int running;
int fd; /* For epoll() */
uint32_t errors;
LIST_HEAD(,uev) watchers;
} uev_ctx_t;