17 Commits
Author SHA1 Message Date
Joachim Nilsson 6397edc2c4 Update ChangeLog and bump version for v1.2.0 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-09 13:18:39 +02:00
Joachim Nilsson cbd962c5b6 Fix bench.c so it actually reads from the pipe/socket, update callback API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-09 13:14:49 +02:00
Joachim Nilsson 1f9558059a API change -- Remove first ctx argument and add event mask to callback
There really does not exist a good reason for us sending a `uev_ctx_t *`
pointer as the first argument to event callbacks.  If the callback needs
to reference the context, it can find the ctx pointer in the `uev_t *w`.

This patch removes the first argument from all callbacks and also fixes
a slight bug in that the event loop never returned the actual event that
triggered the callback!

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-09 13:10:20 +02:00
Joachim Nilsson 9e7f6865fc uev_timer_set(): Bugfix, call uev_watcher_start() even if not running yet!
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-09 13:05:55 +02:00
Joachim Nilsson 3b31079a1c README: Refactor API section in an attempt to make it more accessible.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-29 09:31:14 +02:00
Joachim Nilsson 63697e9eda Add support for building static-only libuev.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-06 21:33:49 +01:00
Joachim Nilsson ce3930f0cf Update .gitignore, dep files now named *.d
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-03-04 15:55:44 +01:00
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
13 changed files with 267 additions and 116 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
bench bench
test test
.*.d *.d
*.o *.o
*~ *~
libuev.a libuev.a
-1
View File
@@ -2,7 +2,6 @@
language: c language: c
compiler: compiler:
- gcc - gcc
- clang
env: env:
global: global:
+42 -4
View File
@@ -3,8 +3,44 @@ Change Log
All notable changes to the project are documented in this file. All notable changes to the project are documented in this file.
[v1.0.5]
-------- [v1.2.0] - 2015-06-09
---------------------
API change in event callbacks and fix timers that never start.
### Changes
- Remove first `uev_ctx_t *` argument in callbacks, incompatible API
change! Please update all your callbacks if you upgrade.
### Fixes
- Fix timers that accidentally broke in [v1.1.0].
- Fixes to `bench.c`, it now actually listens to the pipe/socket.
[v1.1.0] - 2015-03-04
---------------------
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] - 2015-02-15
---------------------
### Changes ### Changes
- Add `uev_*_start()` functions. - Add `uev_*_start()` functions.
@@ -85,8 +121,10 @@ v0.0.1 - 2012-03-17
[Initial announcement] of [libuevent] by [Flemming Madsen] to the Lua [Initial announcement] of [libuevent] by [Flemming Madsen] to the Lua
users mailing list. users mailing list.
[UNRELEASED]: https://github.com/troglobit/libuev/compare/v1.2.0...HEAD
[v1.0.4]: https://github.com/troglobit/libuev/compare/v1.0.4...v1.0.5 [v1.2.0]: https://github.com/troglobit/libuev/compare/v1.1.0...v1.2.0
[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.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.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 [v1.0.2]: https://github.com/troglobit/libuev/compare/v1.0.1...v1.0.2
+19 -10
View File
@@ -22,10 +22,10 @@
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# #
.PHONY: all test clean joystick test examples .PHONY: all test clean joystick test examples bench
#VERSION = $(shell git tag -l | tail -1) #VERSION = $(shell git tag -l | tail -1)
VERSION ?= 1.0.5 VERSION ?= 1.2.0
NAME = libuev NAME = libuev
PKG = $(NAME)-$(VERSION) PKG = $(NAME)-$(VERSION)
ARCHIVE = $(PKG).tar.xz ARCHIVE = $(PKG).tar.xz
@@ -33,7 +33,11 @@ ARCHIVE = $(PKG).tar.xz
CC ?= $(CROSS)gcc CC ?= $(CROSS)gcc
AR ?= $(CROSS)ar AR ?= $(CROSS)ar
STRIP ?= $(CROSS)strip STRIP ?= $(CROSS)strip
CFLAGS += -fPIC -Os
ifneq ($(STATIC), 1)
CFLAGS += -fPIC
endif
CFLAGS += -Os
CPPFLAGS += -W -Wall -Werror CPPFLAGS += -W -Wall -Werror
ARFLAGS = crus ARFLAGS = crus
JUNK = *~ *.bak *.map .*.d DEADJOE *.gdb *.elf core core.* *.html JUNK = *~ *.bak *.map .*.d DEADJOE *.gdb *.elf core core.* *.html
@@ -46,7 +50,7 @@ prefix ?= /usr/local
libdir ?= $(prefix)/lib libdir ?= $(prefix)/lib
datadir ?= $(prefix)/share/doc/$(LIBNAME) datadir ?= $(prefix)/share/doc/$(LIBNAME)
incdir ?= $(prefix)/include incdir ?= $(prefix)/include
DISTFILES = README CHANGELOG TODO LICENSE test.c DISTFILES = README CHANGELOG TODO LICENSE
HEADER = uev.h HEADER = uev.h
OBJS := uev.o io.o timer.o signal.o OBJS := uev.o io.o timer.o signal.o
@@ -56,7 +60,11 @@ VER = 1
SOLIB = $(LIBNAME).so.$(VER) SOLIB = $(LIBNAME).so.$(VER)
SYMLIB = $(LIBNAME).so SYMLIB = $(LIBNAME).so
STATICLIB = $(LIBNAME).a STATICLIB = $(LIBNAME).a
ifeq ($(STATIC), 1)
TARGET = $(STATICLIB)
else
TARGET = $(STATICLIB) $(SOLIB) TARGET = $(STATICLIB) $(SOLIB)
endif
export STATICLIB JUNK ROOTDIR CFLAGS CPPFLAGS export STATICLIB JUNK ROOTDIR CFLAGS CPPFLAGS
@@ -77,9 +85,10 @@ $(STATICLIB): Makefile $(OBJS)
install: strip install: strip
@install -d $(DESTDIR)$(libdir) @install -d $(DESTDIR)$(libdir)
@install $(SOLIB) $(DESTDIR)$(prefix)/lib/$(SOLIB) @printf " INSTALL $(DESTDIR)$(libdir)/$(TARGET)\n"
@install $(STATICLIB) $(DESTDIR)$(prefix)/lib/$(STATICLIB) @install $(SOLIB) $(DESTDIR)$(libdir)/$(SOLIB)
@ln -sf $(SOLIB) $(DESTDIR)$(prefix)/lib/$(SYMLIB) @install $(STATICLIB) $(DESTDIR)$(libdir)/$(STATICLIB)
@ln -sf $(SOLIB) $(DESTDIR)$(libdir)/$(SYMLIB)
@install -d $(DESTDIR)$(incdir) @install -d $(DESTDIR)$(incdir)
@install -m 0644 $(HEADER) $(DESTDIR)$(incdir)/$(HEADER) @install -m 0644 $(HEADER) $(DESTDIR)$(incdir)/$(HEADER)
@install -d $(DESTDIR)$(datadir) @install -d $(DESTDIR)$(datadir)
@@ -97,9 +106,9 @@ strip: $(TARGET)
@$(STRIP) --strip-unneeded $(TARGET) @$(STRIP) --strip-unneeded $(TARGET)
@size $(TARGET) @size $(TARGET)
bench: Makefile bench.o $(STATICLIB) bench: Makefile $(STATICLIB)
@printf " BENCH %s\n" $(STATICLIB) @printf " RUN %s\n" $@
@$(CC) $(CPPFLAGS) -g -o bench bench.c $(STATICLIB) && ./bench @$(CC) $(CPPFLAGS) -D_GNU_SOURCE -g -o bench bench.c $(STATICLIB) && ./bench
examples: all examples: all
@$(MAKE) -C examples @$(MAKE) -C examples
+85 -45
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. often a far easier approach, as well as less error prone.
The purpose of many applications is, with a little logic sprinkled on The purpose of many applications is, with a little logic sprinkled on
top, to act upon: network packets entering an interface, timeouts top, to act on network packets entering an interface, timeouts expiring,
expiring, mouse clicks, or other types of events. Such applications are mouse clicks, or other types of events. Such applications are often
often very well suited to use an event loop. very well suited to use an event loop.
Applications that need to churn massively parallel algorithms are more Applications that need to churn massively parallel algorithms are more
suitable for running multiple (independent) threads on several CPU suitable for running multiple (independent) threads on several CPU
cores. However, threaded applications must deal with the side effects 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 Writing error free threaded applications is hard, debugging them can be
even harder. even harder.
@@ -61,52 +61,82 @@ details, see [this article][4] at [lwn.net](http://lwn.net).
API API
--- ---
Here is the interface to [libuEv]. It handles three different types of The C interface to [libuEv] is very simple. It handles three different
events: I/O (files, sockets, message queues, etc.), timers, and signals. types of events: I/O (files, sockets, message queues, etc.), timers, and
signals. With a slight caveat on signals
```C ```C
/* Callback example, arg is passed from watcher's *_init() /* Callback example, arg is passed from watcher's *_init()
* w->fd holds the file descriptor, events is set by libuEv * w->fd holds the file descriptor, events is set by libuEv
* to indicate if any of UEV_READ and/or UEV_WRITE is ready. * 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); void callback (uev_t *w, void *arg, int events);
/* Event loop functions, notice use of flags! */ /* Event loop functions, notice use of flags! */
int uev_init (uev_ctx_t *ctx); int uev_init (uev_ctx_t *ctx);
int uev_exit (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 */ 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 */ /* 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_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_set (uev_t *w, int fd, int events);
int uev_io_stop (uev_t *w); 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); /* Timer watcher: timeout and period in milliseconds */
int uev_timer_set (uev_t *w, int timeout, int period); /* Change timeout or period */ int uev_timer_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period);
int uev_timer_stop (uev_t *w); /* Stop a timer */ 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 */
/* Signal watcher: signo is the signal to wait for, e.g., SIGTERM */ int uev_timer_stop (uev_t *w); /* Stop a timer */
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 */ /* Signal watcher: signo is the signal to wait for, e.g., SIGTERM */
int uev_signal_stop(uev_t *w); /* Stop 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); /* 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 To monitor events the developer first creates an *event context*, this
create an *event context*, achieved by calling `uev_init()` with a is achieved by calling `uev_init()` with a pointer to a (thread) local
pointer to a local `uev_ctx_t` variable. `uev_ctx_t` variable.
Events are monitored by watchers in [libuEv]. A watcher is nothing more ```C
than a mechnism that polls a file descriptor. Register a watcher with a uev_ctx_t ctx;
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()` uev_init(&ctx);
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 Then, for each event to be monitored, a *watcher* is registered with the
served the first event. If `flags` is set to `UEV_ONCE | UEV_NONBLOCK` event context. The watcher, an `uev_t`, is initialized with the proper
the event loop returns immediately if no event is available. file descriptor to monitor, and an event callback function, by calling
the event type's `_init()` function with the `uev_ctx_t` context.
```C
void cleanup_exit(uev_t *w, void *arg, int events)
{
/* Graceful exit, with optional cleanup ... */
uev_exit(w->ctx);
}
uev_t termw;
uev_signal_init(&ctx, &term, cleanup_exit, NULL, SIGTERM);
```
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. 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.
```C
uev_run(&ctx, UEV_NONE);
```
In 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: Summary:
@@ -120,7 +150,11 @@ Summary:
bugs in event driven applications are due to sockets and files being bugs in event driven applications are due to sockets and files being
opened in blocking mode. Be careful out there! 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 dealing with signalfd. This means that if your application, for
instance, uses `system()` you must redesign that to use `fork()`, and instance, uses `system()` you must redesign that to use `fork()`, and
then in the child, unblock all signals blocked by your parent process, then in the child, unblock all signals blocked by your parent process,
@@ -153,7 +187,7 @@ struct js_event {
uint8_t number; /* axis/button number */ uint8_t number; /* axis/button number */
} e; } e;
static void joystick_cb(uev_ctx_t *ctx, uev_t *w, void *arg, int events) static void joystick_cb(uev_t *w, void *arg, int events)
{ {
read (w->fd, &e, sizeof(e)); read (w->fd, &e, sizeof(e));
@@ -260,3 +294,9 @@ used for inspiration is the very small [Picoev][9] by [Oku Kazuho].
[Dave Zarzycki, Apple]: http://www.youtube.com/watch?v=cD_s6Fjdri8 [Dave Zarzycki, Apple]: http://www.youtube.com/watch?v=cD_s6Fjdri8
[libuEv] is developed and maintained by [Joachim Nilsson] [libuEv] is developed and maintained by [Joachim Nilsson]
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+6 -5
View File
@@ -70,7 +70,7 @@ static int *pipes;
static uev_t *evio; static uev_t *evio;
static uev_t *evto; static uev_t *evto;
static void read_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *arg, int UNUSED(events)) static void read_cb(uev_t *w, void *arg, int UNUSED(events))
{ {
int idx, widx; int idx, widx;
u_char ch; u_char ch;
@@ -96,7 +96,7 @@ static void read_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *arg, int UNUSED(even
} }
} }
static void timer_cb(uev_ctx_t UNUSED(*ctx), uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events)) static void timer_cb(uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{ {
/* nop */ /* nop */
} }
@@ -208,16 +208,17 @@ int main(int argc, char **argv)
if (timers) if (timers)
uev_timer_init(&ctx, &evto[i], timer_cb, NULL, 0, 0); uev_timer_init(&ctx, &evto[i], timer_cb, NULL, 0, 0);
args[i].index = i; args[i].index = i;
uev_io_init(&ctx, &evio[i], read_cb, &args[i], -1, UEV_READ);
#ifdef USE_PIPES #ifdef USE_PIPES
if (pipe(cp) == -1) { if (pipe2(cp, O_NONBLOCK) == -1) {
#else #else
if (socketpair(AF_UNIX, SOCK_STREAM, 0, cp) == -1) { if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, cp) == -1) {
#endif #endif
perror("pipe"); perror("pipe");
exit(1); exit(1);
} }
uev_io_init(&ctx, &evio[i], read_cb, &args[i], cp[0], UEV_READ);
} }
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
+1 -1
View File
@@ -40,7 +40,7 @@ struct js_event {
uint8_t number; /* axis/button number */ uint8_t number; /* axis/button number */
} e; } e;
static void joystick_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *UNUSED(arg), int UNUSED(events)) static void joystick_cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
{ {
if (read (w->fd, &e, sizeof(e)) < 0) if (read (w->fd, &e, sizeof(e)) < 0)
errx(errno, "Failed reading joystick event"); errx(errno, "Failed reading joystick event");
+12 -6
View File
@@ -69,13 +69,13 @@ static int callback(arg_t *arg, int event, void *foo)
return 42; return 42;
} }
static void sigsegv_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events)) static void sigsegv_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
{ {
warnx("PID %d caused segfault.", getpid()); warnx("PID %d caused segfault.", getpid());
exit(-1); exit(-1);
} }
static void sigchld_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events)) static void sigchld_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
{ {
pid_t pid = waitpid(-1, NULL, WNOHANG); pid_t pid = waitpid(-1, NULL, WNOHANG);
@@ -83,7 +83,7 @@ static void sigchld_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *UNUSED(ar
warnx("PID %d exited, bye.", pid); warnx("PID %d exited, bye.", pid);
} }
static void work_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *arg, int UNUSED(events)) static void work_cb(uev_t *UNUSED(w), void *arg, int UNUSED(events))
{ {
int status = 0; int status = 0;
pid_t pid; pid_t pid;
@@ -102,13 +102,17 @@ static void work_cb(uev_ctx_t *UNUSED(ctx), uev_t *UNUSED(w), void *arg, int UNU
if (WIFEXITED(status)) if (WIFEXITED(status))
printf("Child exited normally => %d\n", WEXITSTATUS(status)); printf("Child exited normally => %d\n", WEXITSTATUS(status));
else if (WCOREDUMP(status)) else if (WCOREDUMP(status))
printf("Child crashed!\n"); printf("Child crashed! %s\n", DO_SEGFAULT
? "As expected, everything is OK."
: "This should not happen!");
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)) static void exit_cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
{ {
printf("Process deadline reached, exiting!\n"); printf("Process deadline reached, exiting!\n");
uev_exit(ctx); uev_exit(w->ctx);
} }
int main(void) int main(void)
@@ -118,6 +122,8 @@ int main(void)
/* Initialize libuEv */ /* Initialize libuEv */
uev_init(&ctx); uev_init(&ctx);
/* Setup callbacks */
uev_signal_init(&ctx, &sigsegv_watcher, sigsegv_cb, NULL, SIGSEGV); uev_signal_init(&ctx, &sigsegv_watcher, sigsegv_cb, NULL, SIGSEGV);
uev_signal_init(&ctx, &sigchld_watcher, sigchld_cb, NULL, SIGCHLD); uev_signal_init(&ctx, &sigchld_watcher, sigchld_cb, NULL, SIGCHLD);
uev_timer_init(&ctx, &timeout_watcher, work_cb, &arg, 400, 0); uev_timer_init(&ctx, &timeout_watcher, work_cb, &arg, 400, 0);
+8 -8
View File
@@ -40,33 +40,33 @@ static int in, out;
static int period = 0; static int period = 0;
static uev_t *watchdog; static uev_t *watchdog;
static void lifetime_cb(uev_ctx_t *ctx, uev_t UNUSED(*w), void *arg, int UNUSED(events)) static void lifetime_cb(uev_t *w, void *arg, int UNUSED(events))
{ {
fprintf(stderr, "\nLifetime exceeded, program completed successfully! (arg:%p)\n", arg); fprintf(stderr, "\nLifetime exceeded, program completed successfully! (arg:%p)\n", arg);
uev_exit(ctx); uev_exit(w->ctx);
} }
/* The pipe watchdog, if it triggers we haven't received data in time. */ /* The pipe watchdog, if it triggers we haven't received data in time. */
static void timeout_cb(uev_ctx_t *ctx, uev_t *UNUSED(w), void *arg, int UNUSED(events)) static void timeout_cb(uev_t *w, void *arg, int UNUSED(events))
{ {
watchdog = NULL; watchdog = NULL;
fprintf(stderr, "\nTimeout exceeded %p\n", arg); fprintf(stderr, "\nTimeout exceeded %p\n", arg);
// uev_timer_stop(w); <-- No need to stop timers with period=0 :) // uev_timer_stop(w); <-- No need to stop timers with period=0 :)
uev_exit(ctx); uev_exit(w->ctx);
} }
static void periodic_task(uev_ctx_t UNUSED(*ctx), uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events)) static void periodic_task(uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{ {
fprintf(stderr, "|"); fprintf(stderr, "|");
} }
static void signal_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *UNUSED(arg), int UNUSED(events)) static void signal_cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
{ {
fprintf(stderr, w->signo == SIGINT ? "^Cv" : "^\v"); fprintf(stderr, w->signo == SIGINT ? "^Cv" : "^\v");
} }
static void pipe_read_cb(uev_ctx_t *UNUSED(ctx), uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events)) static void pipe_read_cb(uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{ {
int cnt; int cnt;
char msg[50]; char msg[50];
@@ -80,7 +80,7 @@ static void pipe_read_cb(uev_ctx_t *UNUSED(ctx), uev_t UNUSED(*w), void UNUSED(*
fprintf(stderr, "%.*s.%d ", cnt, msg, cnt); fprintf(stderr, "%.*s.%d ", cnt, msg, cnt);
} }
static void pipe_write_cb(uev_ctx_t UNUSED(*ctx), uev_t *w, void *arg, int UNUSED(events)) static void pipe_write_cb(uev_t *w, void *arg, int UNUSED(events))
{ {
my_t *my = arg; my_t *my = arg;
char *msg = "TESTING"; char *msg = "TESTING";
+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) int uev_io_set(uev_t *w, int fd, int events)
{ {
if (uev_io_stop(w)) /* Ignore any errors, only to clean up anything lingering ... */
return -1; uev_io_stop(w);
/* Remove from internal list */
LIST_REMOVE(w, link);
return uev_io_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, fd, events); 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 uev_io_stop(uev_t *w)
{ {
int status = uev_watcher_stop(w); return uev_watcher_stop(w);
return status;
} }
/** /**
+1 -3
View File
@@ -123,11 +123,9 @@ int uev_timer_set(uev_t *w, int timeout, int period)
msec2tspec(timeout, &time.it_value); msec2tspec(timeout, &time.it_value);
msec2tspec(period, &time.it_interval); msec2tspec(period, &time.it_interval);
timerfd_settime(w->fd, 0, &time, NULL); timerfd_settime(w->fd, 0, &time, NULL);
return uev_watcher_start(w);
} }
return 0; return uev_watcher_start(w);
} }
/** /**
+85 -22
View File
@@ -24,6 +24,7 @@
*/ */
#include <errno.h> #include <errno.h>
#include <fcntl.h> /* O_CLOEXEC */
#include <string.h> /* memset() */ #include <string.h> /* memset() */
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/signalfd.h> /* struct signalfd_siginfo */ #include <sys/signalfd.h> /* struct signalfd_siginfo */
@@ -33,6 +34,28 @@
#define UNUSED(arg) arg __attribute__ ((unused)) #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! */ /* 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) 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; return -1;
} }
memset(w, 0, sizeof(*w));
w->ctx = ctx; w->ctx = ctx;
w->fd = fd;
w->type = type; w->type = type;
w->active = 0;
w->fd = fd;
w->cb = (void *)cb; w->cb = (void *)cb;
w->arg = arg; w->arg = arg;
w->events = events; w->events = events;
LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
return 0; return 0;
} }
@@ -67,13 +88,16 @@ int uev_watcher_start(uev_t *w)
if (w->active) if (w->active)
return 0; return 0;
ev.events = w->events; ev.events = w->events | EPOLLRDHUP;
ev.data.ptr = w; ev.data.ptr = w;
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
return -1; return -1;
w->active = 1; w->active = 1;
/* Add to internal list for bookkeeping */
LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
return 0; return 0;
} }
@@ -88,10 +112,15 @@ int uev_watcher_stop(uev_t *w)
if (!w->active) if (!w->active)
return 0; return 0;
/* Remove from kernel */
epoll_ctl(w->ctx->fd, EPOLL_CTL_DEL, w->fd, NULL);
w->active = 0; 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; return 0;
} }
@@ -103,17 +132,15 @@ int uev_watcher_stop(uev_t *w)
*/ */
int uev_init(uev_ctx_t *ctx) int uev_init(uev_ctx_t *ctx)
{ {
int fd; if (!ctx) {
errno = EINVAL;
fd = epoll_create(1);
if (fd < 0)
return -1; return -1;
}
memset(ctx, 0, sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx));
ctx->fd = fd;
LIST_INIT(&ctx->watchers); 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) if (EINTR == errno)
continue; /* Signalled, try again */ continue; /* Signalled, try again */
exit:
result = -1; /* Unrecoverable error, cleanup and exit with error. */
ctx->running = 0; uev_exit(ctx);
break; return -1;
} }
for (i = 0; ctx->running && i < nfds; i++) { for (i = 0; ctx->running && i < nfds; i++) {
w = (uev_t *)events[i].data.ptr; 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) { if (UEV_TIMER_TYPE == w->type) {
uint64_t exp; uint64_t exp;
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
goto exit; uev_exit(ctx);
return -3;
}
if (!w->period) if (!w->period)
w->timeout = 0; w->timeout = 0;
@@ -229,12 +290,14 @@ int uev_run(uev_ctx_t *ctx, int flags)
struct signalfd_siginfo fdsi; struct signalfd_siginfo fdsi;
ssize_t sz = sizeof(fdsi); ssize_t sz = sizeof(fdsi);
if (read(w->fd, &fdsi, sz) != sz) if (read(w->fd, &fdsi, sz) != sz) {
goto exit; uev_exit(ctx);
return -4;
}
} }
if (w->cb) if (w->cb)
w->cb((struct uev *)ctx, w, w->arg); w->cb(w, w->arg, events[i].events & UEV_EVENT_MASK);
if (UEV_TIMER_TYPE == w->type) { if (UEV_TIMER_TYPE == w->type) {
if (!w->timeout) if (!w->timeout)
+4 -2
View File
@@ -38,6 +38,7 @@
#define UEV_NONE 0 #define UEV_NONE 0
#define UEV_READ EPOLLIN #define UEV_READ EPOLLIN
#define UEV_WRITE EPOLLOUT #define UEV_WRITE EPOLLOUT
#define UEV_EVENT_MASK (UEV_READ | UEV_WRITE)
/* Run flags */ /* Run flags */
#define UEV_ONCE 1 #define UEV_ONCE 1
@@ -57,6 +58,7 @@ struct uev;
typedef struct { typedef struct {
int running; int running;
int fd; /* For epoll() */ int fd; /* For epoll() */
uint32_t errors;
LIST_HEAD(,uev) watchers; LIST_HEAD(,uev) watchers;
} uev_ctx_t; } uev_ctx_t;
@@ -72,7 +74,7 @@ typedef struct uev {
int events; int events;
/* Watcher callback with optional argument */ /* Watcher callback with optional argument */
void (*cb)(struct uev *, struct uev *, void *); void (*cb)(struct uev *, void *, int);
void *arg; void *arg;
/* Timer watchers, time in milliseconds */ /* Timer watchers, time in milliseconds */
@@ -84,7 +86,7 @@ typedef struct uev {
} uev_t; } uev_t;
/* Generic callback for watchers */ /* Generic callback for watchers */
typedef void (uev_cb_t)(uev_ctx_t *ctx, uev_t *w, void *arg, int events); typedef void (uev_cb_t)(uev_t *w, void *arg, int events);
/* Private methods, do not use directly! */ /* Private methods, 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); 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);