34 Commits
Author SHA1 Message Date
Joachim Nilsson 3e5694cb76 Bump version for v2.0.0 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 16:46:34 +01:00
Joachim Nilsson f5806a28c0 Bump ABI, major change in how uev_run() and callbacks work
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 16:46:03 +01:00
Joachim Nilsson ef57250586 Change release target to run distcheck instead of dist
With distcheck we not only build the distribution files, but also runs
test suite and verifies that the resulting dist file(s) can be built.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 16:43:47 +01:00
Joachim Nilsson febace818b ChangeLog: Finishing touches for v2.0.0
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 16:41:46 +01:00
Joachim Nilsson 5496bf0931 Update debian/changelog for v2 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 16:41:18 +01:00
Joachim Nilsson cb93dd9c9d ChangeLog: Fix GIT diff URL
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 14:19:23 +01:00
Joachim Nilsson 341f7801fd Alert users of ABI breakage
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 14:17:49 +01:00
Joachim Nilsson dd475b5058 Move quote to API.md, need space to warn about ABI breakage in 2.0
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 14:17:49 +01:00
Joachim Nilsson 0a1a0ebffb Preparations for upcoming v2 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 14:17:49 +01:00
Joachim Nilsson 91bfe9faa7 Ignore global index files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 13:59:55 +01:00
Joachim Nilsson ddfc1460eb redirect: Update example to handle new failure mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 13:59:05 +01:00
Joachim Nilsson 71e6d78fdc redirect: Use err.h functions for error handling
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 13:58:35 +01:00
Joachim Nilsson 5929295c37 Update joystick example with new failure mode
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-11 13:57:43 +01:00
Joachim Nilsson 412f04f413 API.md: Document uev_run() failure modes.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-10 23:41:17 +01:00
Joachim Nilsson f78348a92a uev_run(): Return unique error codes in case of actual error.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-10 23:40:49 +01:00
Joachim Nilsson d1f5062e79 Bump major version, for upcoming v2.0 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-10 22:26:03 +01:00
Joachim Nilsson 1037eed481 Minor simplification, result is never changed
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-10 22:25:12 +01:00
Joachim Nilsson 2f9cce9150 Do NOT exit event loop on timer/signal watcher error
This patch fixes a blocking issue discovered in the Finit project.
At shutdown, or when there is any problem with a timer or signal
watcher, the event loop is abruptly terminated.

Instead of unconditionally exiting the event loop on problems with a
single watcher, invalidate the watcher and run the callback one last
time with UEV_ERROR in the events argument.

This is a semantic change wrt. watchers and their callbacks, so the
major version number will be bumped accordingly to signal the severity
of this change.  From now on it is STRONGLY recommended that each call
back function check for UEV_ERROR.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-10 22:18:40 +01:00
Joachim Nilsson f3b3177963 Introduce UEV_ERROR, for unexpected error on timer+signal watchers
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-11-10 21:49:57 +01:00
Joachim Nilsson ad262efa97 Update documentation to mention lack of support for regular files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-24 08:10:36 +02:00
Joachim Nilsson 0dfc9152bd uev_run(): Exit event loop as soon as there are no more watchers
The purpose of the event loop is to process events on all registered
watches.  When all watchers have completed the event loop should exit.

Having implemented a workaround for issue #12 in commit e0cebc3 it has
become evident we need to check the list of watchers at the beginning
of the event loop rather than the end, since the workaround skips past
the end.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-24 00:33:28 +02:00
Joachim Nilsson 9642443a7c Clean up redirect example a bit
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-22 11:16:10 +02:00
Joachim Nilsson 98748277f3 Add Emacs hints for indentation
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 20:52:30 +02:00
Joachim Nilsson 1de445535e Fix periodic timer example
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 20:51:08 +02:00
Joachim Nilsson 151f9294c9 Simplify Origin & References section
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 20:50:52 +02:00
Joachim Nilsson 61bb69795d Use FIONREAD to detect if stdin has data, safer than just select()
When handling the stdin redirect workaround we should use FIONREAD to
figure out if there is any data to read.

When there is no more data to read from stdin we remove the watcher from
internal bookkeeping but allow the callback to run one last time so it
can detect a short read.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 19:55:17 +02:00
Joachim Nilsson d46ff8e396 redirect: Make sure example application does not exit on len==0
We want to continue running the event loop until it is exited properly,
not if the callback for stdin detects a short read.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 19:54:00 +02:00
Joachim Nilsson e0cebc33b1 Fix #12: Implement workaround for application < foo.txt
This patch implements a workaround for issue #12, i.e. when an I/O
watcher for redirected stdin is registered and causes epoll_ctl()
to return EPERM.

The workaround registers the watcher as `active = -1` and bypasses
the epoll_wait() call in the main loop until all data has been read
from stdin and the temporary -1 watcher(s) have completed.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 19:10:04 +02:00
Joachim Nilsson 82a1018dd2 Ignore TroglOS build files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 18:47:31 +02:00
Joachim Nilsson 58ec8e1d6f Simple example of stdin redirect problem, issue #12
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 18:46:55 +02:00
Joachim Nilsson 704d7d5612 Refactor, leverage internal API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 12:47:01 +02:00
Joachim Nilsson ce4f8d6ef5 Follow-up to ed5c9bb, fix examples Makefile after relocation
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 12:21:51 +02:00
Joachim Nilsson 6f5e6e5232 Make internal API more private
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 12:04:13 +02:00
Joachim Nilsson 9751be5859 Update ChangeLog diff URLs
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-22 16:26:02 +02:00
19 changed files with 288 additions and 91 deletions

No files matched your search

+7 -1
View File
@@ -7,8 +7,14 @@
*.la *.la
*.lo *.lo
*.html *.html
.config
.deps .deps
.libs .libs
.unpacked
ID
GTAGS
GRTAGS
GPATH
compile compile
config.* config.*
configure configure
@@ -27,4 +33,4 @@ bench
libuev*.tar.* libuev*.tar.*
libuev.pc libuev.pc
!libuev.pc.in !libuev.pc.in
test-driver test-driver
+34 -2
View File
@@ -11,13 +11,21 @@ API Documentation
* [Joystick Example](#joystick-example) * [Joystick Example](#joystick-example)
> “Event driven software improves concurrency” -- [Dave Zarzycki, Apple][]
Introduction Introduction
------------ ------------
The C API to [libuEv][], listed in `uev/uev.h`, handles three different The C API to [libuEv][], listed in `uev/uev.h`, handles three different
types of events: I/O (files, sockets, message queues, etc.), timers, and types of events: I/O (pipes, sockets, message queues, etc.), timers, and
signals. The [Summary](#summary) details a slight caveat on signals. signals. The [Summary](#summary) details a slight caveat on signals.
Notice the *lack of support for regular files* and directories. This is
a limitation of the underlying Linux `epoll` interface which will return
`EPERM` for regular files. Except for the special case when `stdin` is
redirected from the command line. See `examples/redirect.c` for more on
this particular case.
Timers can be either relative, timeout in milliseconds, or absolute with Timers can be either relative, timeout in milliseconds, or absolute with
a time given in `time_t`, see `mktime()` et al. Absolute timers are a time given in `time_t`, see `mktime()` et al. Absolute timers are
called cron timers and their callbacks get an `UEV_HUP` error event if called cron timers and their callbacks get an `UEV_HUP` error event if
@@ -127,13 +135,24 @@ used to integrate [libuEv][] into another event loop.
In this example we set `flags` to none: In this example we set `flags` to none:
```C ```C
uev_run(&ctx, UEV_NONE); result = uev_run(&ctx, UEV_NONE);
``` ```
With `flags` set to `UEV_ONCE` the event loop returns as soon as it has With `flags` set to `UEV_ONCE` the event loop returns as soon as it has
served the first event. If `flags` is set to `UEV_ONCE | UEV_NONBLOCK` served the first event. If `flags` is set to `UEV_ONCE | UEV_NONBLOCK`
the event loop returns immediately if no event is available. the event loop returns immediately if no event is available.
```
if (result < 0)
errx(result, "Unrecoverable event loop error, error %d", result);
```
If the call to `uev_run()` fails you should notify the user somehow.
libuEv fails if there is an invalid pointer, if `uev_init()` was not
called, or recurring `epoll()` errors thare are impossible to recover
from should occur. This is true for individual watchers as well, in
particular signal and timer watchers which can fail in miserable ways.
**Note:** libuEv handles many types of errors, stream close, or peer **Note:** libuEv handles many types of errors, stream close, or peer
shutdowns internally, but also lets the callback run. This is useful shutdowns internally, but also lets the callback run. This is useful
for stateful connections to be able to detect EOF. for stateful connections to be able to detect EOF.
@@ -221,8 +240,20 @@ struct js_event {
uint8_t number; /* axis/button number */ uint8_t number; /* axis/button number */
} e; } e;
/*
* Called on any joystick event
*
* Note: We handle errors here by exiting, for a background daemon with
* multiple watchers you may want another approach.
*/
static void joystick_cb(uev_t *w, void *arg, int events) static void joystick_cb(uev_t *w, void *arg, int events)
{ {
if (UEV_ERROR == events) {
/* Possibly joystick was unplugged */
warn("Unrecoverable error, exiting");
uev_exit(w->ctx);
}
read(w->fd, &e, sizeof(e)); read(w->fd, &e, sizeof(e));
switch (e.type) { switch (e.type) {
@@ -281,3 +312,4 @@ library) for [reference benchmarks][7] against [libevent][1] and
[7]: http://libev.schmorp.de/bench.html [7]: http://libev.schmorp.de/bench.html
[LibuEv]: https://github.com/troglobit/libuev [LibuEv]: https://github.com/troglobit/libuev
[GLIBC workaround]: https://sourceware.org/glibc/wiki/Y2038ProofnessDesign [GLIBC workaround]: https://sourceware.org/glibc/wiki/Y2038ProofnessDesign
[Dave Zarzycki, Apple]: http://www.youtube.com/watch?v=cD_s6Fjdri8
+22 -7
View File
@@ -4,6 +4,22 @@ Change Log
All notable changes to the project are documented in this file. All notable changes to the project are documented in this file.
[v2.0.0][] - 2017-11-11
-----------------------
Beware, this is a major release, introducing incompatible changes to the
failure modes of `uev_run()` and watcher callbacks. Most users will most
likely not notice any difference, but please read on.
### Changes
- `uev_run()` no longer exits the main event loop if an unrecoverable
error with a watcher occurs. Instead, the watcher is disabled and the
callback is run one last time with `events` set to `UEV_ERROR`.
- Watcher callbacks must handle `UEV_ERROR` conditions. This pertains
in particular to signal and timer watchers.
- Examples and API docs updated with the new failure modes.
[v1.6.0][] - 2017-09-18 [v1.6.0][] - 2017-09-18
----------------------- -----------------------
@@ -280,7 +296,12 @@ v0.0.1 - 2012-03-17
Lua users mailing list. Lua users mailing list.
[UNRELEASED]: https://github.com/troglobit/libuev/compare/v1.4.2...HEAD [UNRELEASED]: https://github.com/troglobit/libuev/compare/v2.0.0...HEAD
[v2.0.0]: https://github.com/troglobit/libuev/compare/v1.6.0...v2.0.0
[v1.6.0]: https://github.com/troglobit/libuev/compare/v1.5.2...v1.6.0
[v1.5.2]: https://github.com/troglobit/libuev/compare/v1.5.1...v1.5.2
[v1.5.1]: https://github.com/troglobit/libuev/compare/v1.5.0...v1.5.1
[v1.5.0]: https://github.com/troglobit/libuev/compare/v1.4.2...v1.5.0
[v1.4.2]: https://github.com/troglobit/libuev/compare/v1.4.1...v1.4.2 [v1.4.2]: https://github.com/troglobit/libuev/compare/v1.4.1...v1.4.2
[v1.4.1]: https://github.com/troglobit/libuev/compare/v1.4.0...v1.4.1 [v1.4.1]: https://github.com/troglobit/libuev/compare/v1.4.0...v1.4.1
[v1.4.0]: https://github.com/troglobit/libuev/compare/v1.3.1...v1.4.0 [v1.4.0]: https://github.com/troglobit/libuev/compare/v1.3.1...v1.4.0
@@ -307,9 +328,3 @@ Lua users mailing list.
[libuevent]: https://code.google.com/p/libuevent/ [libuevent]: https://code.google.com/p/libuevent/
[Flemming Madsen]: http://www.madsensoft.dk [Flemming Madsen]: http://www.madsensoft.dk
[Initial announcement]: http://lua-users.org/lists/lua-l/2012-03/msg00510.html [Initial announcement]: http://lua-users.org/lists/lua-l/2012-03/msg00510.html
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+1 -1
View File
@@ -34,7 +34,7 @@ release-hook:
fi fi
## Target to run when building a release ## Target to run when building a release
release: dist release-hook md5-dist release: distcheck release-hook md5-dist
@for file in $(DIST_ARCHIVES); do \ @for file in $(DIST_ARCHIVES); do \
printf "$$file \tDistribution tarball\n"; \ printf "$$file \tDistribution tarball\n"; \
printf "$$file.md5\t"; cat $$file.md5 | cut -f1 -d' '; \ printf "$$file.md5\t"; cat $$file.md5 | cut -f1 -d' '; \
+10 -9
View File
@@ -15,7 +15,7 @@ libuEv | Simple event loop for Linux
* [Origin & References](#origin--references) * [Origin & References](#origin--references)
> “Event driven software improves concurrency” -- [Dave Zarzycki, Apple][] > **NOTE:** Incompatible failure mode changes in v2.0 compared to v1.x!
Introduction Introduction
------------ ------------
@@ -25,8 +25,8 @@ Introduction
*u* (micro) in the name refers to both the small feature set and the *u* (micro) in the name refers to both the small feature set and the
small size overhead impact of the library. small size overhead impact of the library.
Experienced developers may appreciate that [libuEv][] is built on top of Failure mode changes introduced in v2.0 may affect users of v1.x, See
modern Linux APIs: epoll, timerfd and signalfd. the [ChangeLog][] for the full details.
Example Example
@@ -49,7 +49,8 @@ int main(void)
uev_ctx_t ctx; uev_ctx_t ctx;
uev_init(&ctx); uev_init(&ctx);
uev_timer_init(&ctx, &timer, cb, NULL, 2 * 1000, 0); /* Set up a timer watcher to call cb() every other second */
uev_timer_init(&ctx, &timer, cb, NULL, 0, 2 * 1000);
return uev_run(&ctx, 0); return uev_run(&ctx, 0);
} }
@@ -78,11 +79,10 @@ The resulting .so file is ~14 kiB.
Origin & References Origin & References
------------------- -------------------
[libuEv][] was originally based on [LibUEvent][8] by [Flemming Madsen][] [libuEv][] originates from [LibUEvent][8] by [Flemming Madsen][], but
but has been completely rewritten to provide a cleaner API. It is now with a rewritten and much cleaner API. It is now more similar to the
more similar to the famous [libev][2] by [Mark Lehmann][]. Another famous [libev][2] by [Mark Lehmann][]. Another library inspiring this
small event library used for inspiration is the very small [picoev][9] project is [picoev][9] by [Oku Kazuho][].
by [Oku Kazuho][].
[libuEv][] is developed and maintained by [Joachim Nilsson][]. It is [libuEv][] is developed and maintained by [Joachim Nilsson][]. It is
built for and developed on GNU/Linux systems, patches to support *BSD built for and developed on GNU/Linux systems, patches to support *BSD
@@ -94,6 +94,7 @@ and its [kqueue][] interface are most welcome.
[3]: http://unix.com/man-page/All/3x/XtDispatchEvent [3]: http://unix.com/man-page/All/3x/XtDispatchEvent
[8]: http://code.google.com/p/libuevent/ [8]: http://code.google.com/p/libuevent/
[9]: https://github.com/kazuho/picoev [9]: https://github.com/kazuho/picoev
[ChangeLog]: https://github.com/troglobit/libuev/blob/master/ChangeLog.md
[Travis]: https://travis-ci.org/troglobit/libuev [Travis]: https://travis-ci.org/troglobit/libuev
[Travis Status]: https://travis-ci.org/troglobit/libuev.png?branch=master [Travis Status]: https://travis-ci.org/troglobit/libuev.png?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3846 [Coverity Scan]: https://scan.coverity.com/projects/3846
+1 -1
View File
@@ -1,4 +1,4 @@
AC_INIT(libuev, 1.6.0, https://github.com/troglobit/libuev/issues) AC_INIT(libuev, 2.0.0, https://github.com/troglobit/libuev/issues)
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
+13
View File
@@ -1,3 +1,16 @@
libuev (2.0.0-1) unstable; urgency=medium
* Major new upstream version, possibly incompatible changes to both
uev_run() and how watchers are called in various failure modes.
- uev_run() no longer exits the main event loop if an unrecoverable
error with a watcher occurs. Instead, the watcher is disabled and
the callback is run one last time with events set to UEV_ERROR.
- Watcher callbacks must handle UEV_ERROR conditions. This pertains
in particular to signal and timer watchers.
-- Joachim Nilsson <troglobit@gmail.com> Sat, 11 Nov 2017 16:31:46 +0100
libuev (1.6.0-1) unstable; urgency=medium libuev (1.6.0-1) unstable; urgency=medium
* Support for edge triggered and oneshot event types * Support for edge triggered and oneshot event types
+1
View File
@@ -2,3 +2,4 @@ joystick
signal signal
cronrun cronrun
test test
redirect
+3 -5
View File
@@ -1,5 +1,3 @@
noinst_PROGRAMS = joystick noinst_PROGRAMS = joystick redirect
AM_CPPFLAGS = -I$(top_srcdir) AM_CPPFLAGS = -I$(top_srcdir)/src
AM_LDFLAGS = -L$(top_srcdir) LDADD = $(top_srcdir)/src/libuev.la
LIBS = $(LTLIBINTL)
LDADD = $(top_srcdir)/libuev.la
+7
View File
@@ -37,8 +37,15 @@ struct js_event {
uint8_t number; /* axis/button number */ uint8_t number; /* axis/button number */
} e; } e;
/*
* Always check for UEV_ERROR in watcher callback!
* Possibly joystick was unplugged
*/
static void joystick_cb(uev_t *w, void *arg, int events) static void joystick_cb(uev_t *w, void *arg, int events)
{ {
if (UEV_ERROR == events)
errx(EBADF, "Unrecoverable error, exiting");
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");
+67
View File
@@ -0,0 +1,67 @@
/* Example of how to handle stdin redirect
*
* The problem is non-trivial since Linux epoll, which libuEv uses, does
* not support I/O watchers for regular files or directories.
*
* This works:
* echo foo | redirect
* This doesn't:
* redirect < foo.txt
*
* However, with a little bit of hacking libuEv can be made to handle
* reading from stdin as a special case.
*/
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include "src/uev.h"
void process_stdin(uev_t *w, void *arg, int events)
{
char buf[256];
int len;
if (UEV_ERROR == events)
errx(EBADF, "Unrecoverable error, exiting");
len = read(w->fd, buf, sizeof(buf));
if (len == -1) {
warn("Error reading from stdin");
return;
}
if (len == 0) {
warnx("Connection closed.");
return;
}
printf("Read %d bytes\n", len);
if (write(STDOUT_FILENO, buf, len) != len)
warn("Failed writing to stdout");
}
int main(int argc, char **argv)
{
int ret;
uev_t watcher;
uev_ctx_t ctx;
uev_init(&ctx);
ret = uev_io_init(&ctx, &watcher, process_stdin, NULL, STDIN_FILENO, UEV_READ);
if (ret)
err(errno, "Failed setting up STDIN watcher");
return uev_run(&ctx, 0);
}
/**
* Local Variables:
* compile-command: "make redirect; echo hej > hej.txt; ./redirect < hej.txt"
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -1
View File
@@ -2,7 +2,7 @@ lib_LTLIBRARIES = libuev.la
libuev_la_SOURCES = uev.c io.c timer.c signal.c cron.c libuev_la_SOURCES = uev.c io.c timer.c signal.c cron.c
libuev_la_CPPFLAGS = -D_GNU_SOURCE -D_TIME_BITS=64 libuev_la_CPPFLAGS = -D_GNU_SOURCE -D_TIME_BITS=64
libuev_la_CFLAGS = -W -Wall -Wextra libuev_la_CFLAGS = -W -Wall -Wextra
libuev_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:2:0 libuev_la_LDFLAGS = $(AM_LDFLAGS) -version-info 2:0:0
noinst_PROGRAMS = bench noinst_PROGRAMS = bench
bench_CPPFLAGS = -D_GNU_SOURCE bench_CPPFLAGS = -D_GNU_SOURCE
+3 -3
View File
@@ -72,11 +72,11 @@ int uev_cron_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, time_t when
if (fd < 0) if (fd < 0)
return -1; return -1;
if (uev_watcher_init(ctx, w, UEV_CRON_TYPE, cb, arg, fd, UEV_READ)) if (_uev_watcher_init(ctx, w, UEV_CRON_TYPE, cb, arg, fd, UEV_READ))
goto exit; goto exit;
if (uev_cron_set(w, when, interval)) { if (uev_cron_set(w, when, interval)) {
uev_watcher_stop(w); _uev_watcher_stop(w);
exit: exit:
close(fd); close(fd);
return -1; return -1;
@@ -132,7 +132,7 @@ int uev_cron_set(uev_t *w, time_t when, time_t interval)
return 1; return 1;
} }
return uev_watcher_start(w); return _uev_watcher_start(w);
} }
/** /**
+5 -5
View File
@@ -44,10 +44,10 @@ int uev_io_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int e
return -1; return -1;
} }
if (uev_watcher_init(ctx, w, UEV_IO_TYPE, cb, arg, fd, events)) if (_uev_watcher_init(ctx, w, UEV_IO_TYPE, cb, arg, fd, events))
return -1; return -1;
return uev_watcher_start(w); return _uev_watcher_start(w);
} }
/** /**
@@ -61,8 +61,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 ((events & UEV_ONESHOT) && w->active) if ((events & UEV_ONESHOT) && _uev_watcher_active(w))
return uev_watcher_rearm(w); return _uev_watcher_rearm(w);
/* Ignore any errors, only to clean up anything lingering ... */ /* Ignore any errors, only to clean up anything lingering ... */
uev_io_stop(w); uev_io_stop(w);
@@ -89,7 +89,7 @@ int uev_io_start(uev_t *w)
*/ */
int uev_io_stop(uev_t *w) int uev_io_stop(uev_t *w)
{ {
return uev_watcher_stop(w); return _uev_watcher_stop(w);
} }
/** /**
+9 -8
View File
@@ -38,7 +38,7 @@ typedef enum {
} uev_type_t; } uev_type_t;
/* Event mask, used internally only. */ /* Event mask, used internally only. */
#define UEV_EVENT_MASK (UEV_READ | UEV_WRITE | UEV_PRI | UEV_HUP | UEV_RDHUP | UEV_EDGE | UEV_ONESHOT) #define UEV_EVENT_MASK (UEV_ERROR | UEV_READ | UEV_WRITE | UEV_PRI | UEV_HUP | UEV_RDHUP | UEV_EDGE | UEV_ONESHOT)
/* Main libuEv context type */ /* Main libuEv context type */
typedef struct { typedef struct {
@@ -46,6 +46,7 @@ typedef struct {
int fd; /* For epoll() */ int fd; /* For epoll() */
uint32_t errors; uint32_t errors;
LIST_HEAD(,uev) watchers; LIST_HEAD(,uev) watchers;
uint32_t workaround; /* For workarounds, e.g. redirected stdin */
} uev_ctx_t; } uev_ctx_t;
/* Forward declare due to dependencys, don't try this at home kids. */ /* Forward declare due to dependencys, don't try this at home kids. */
@@ -81,13 +82,13 @@ struct uev;
uev_type_t uev_type_t
/* Internal API for dealing with generic watchers */ /* Internal API for dealing with generic watchers */
int uev_watcher_init (uev_ctx_t *ctx, struct uev *w, uev_type_t type, int _uev_watcher_init (uev_ctx_t *ctx, struct uev *w, uev_type_t type,
void (*cb)(struct uev *, void *, int), void *arg, void (*cb)(struct uev *, void *, int), void *arg,
int fd, int events); int fd, int events);
int uev_watcher_start (struct uev *w); int _uev_watcher_start (struct uev *w);
int uev_watcher_stop (struct uev *w); int _uev_watcher_stop (struct uev *w);
int uev_watcher_active(struct uev *w); int _uev_watcher_active(struct uev *w);
int uev_watcher_rearm (struct uev *w); int _uev_watcher_rearm (struct uev *w);
#endif /* LIBUEV_PRIVATE_H_ */ #endif /* LIBUEV_PRIVATE_H_ */
+4 -4
View File
@@ -56,11 +56,11 @@ int uev_signal_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo
if (fd < 0) if (fd < 0)
return -1; return -1;
if (uev_watcher_init(ctx, w, UEV_SIGNAL_TYPE, cb, arg, fd, UEV_READ)) if (_uev_watcher_init(ctx, w, UEV_SIGNAL_TYPE, cb, arg, fd, UEV_READ))
goto exit; goto exit;
if (uev_signal_set(w, signo)) { if (uev_signal_set(w, signo)) {
uev_watcher_stop(w); _uev_watcher_stop(w);
exit: exit:
close(fd); close(fd);
return -1; return -1;
@@ -109,7 +109,7 @@ int uev_signal_set(uev_t *w, int signo)
if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0) if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0)
return -1; return -1;
return uev_watcher_start(w); return _uev_watcher_start(w);
} }
@@ -140,7 +140,7 @@ int uev_signal_start(uev_t *w)
*/ */
int uev_signal_stop(uev_t *w) int uev_signal_stop(uev_t *w)
{ {
if (uev_watcher_stop(w)) if (_uev_watcher_stop(w))
return -1; return -1;
close(w->fd); close(w->fd);
+5 -5
View File
@@ -76,11 +76,11 @@ int uev_timer_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeou
if (fd < 0) if (fd < 0)
return -1; return -1;
if (uev_watcher_init(ctx, w, UEV_TIMER_TYPE, cb, arg, fd, UEV_READ)) if (_uev_watcher_init(ctx, w, UEV_TIMER_TYPE, cb, arg, fd, UEV_READ))
goto exit; goto exit;
if (uev_timer_set(w, timeout, period)) { if (uev_timer_set(w, timeout, period)) {
uev_watcher_stop(w); _uev_watcher_stop(w);
exit: exit:
close(fd); close(fd);
return -1; return -1;
@@ -135,7 +135,7 @@ int uev_timer_set(uev_t *w, int timeout, int period)
return 1; return 1;
} }
return uev_watcher_start(w); return _uev_watcher_start(w);
} }
/** /**
@@ -152,7 +152,7 @@ int uev_timer_start(uev_t *w)
} }
if (-1 != w->fd) if (-1 != w->fd)
uev_watcher_stop(w); _uev_watcher_stop(w);
return uev_timer_set(w, w->u.t.timeout, w->u.t.period); return uev_timer_set(w, w->u.t.timeout, w->u.t.period);
} }
@@ -168,7 +168,7 @@ int uev_timer_stop(uev_t *w)
/* Stop kernel timer */ /* Stop kernel timer */
uev_timer_set(w, 0, 0); uev_timer_set(w, 0, 0);
if (uev_watcher_stop(w)) if (_uev_watcher_stop(w))
return -1; return -1;
/* Close timerfd, will have to be reopened again on reset */ /* Close timerfd, will have to be reopened again on reset */
+90 -35
View File
@@ -26,6 +26,8 @@
#include <fcntl.h> /* O_CLOEXEC */ #include <fcntl.h> /* O_CLOEXEC */
#include <string.h> /* memset() */ #include <string.h> /* memset() */
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/select.h> /* for select() workaround */
#include <sys/signalfd.h> /* struct signalfd_siginfo */ #include <sys/signalfd.h> /* struct signalfd_siginfo */
#include <unistd.h> /* close(), read() */ #include <unistd.h> /* close(), read() */
@@ -56,8 +58,32 @@ static int is_valid_fd(int fd)
return fcntl(fd, F_GETFL) != -1 || errno != EBADF; return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
} }
/* Used by file i/o workaround when epoll => EPERM */
static int has_data(int fd)
{
struct timeval timeout = { 0, 0 };
fd_set fds;
int n = 0;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if (select(1, &fds, NULL, NULL, &timeout) > 0)
return ioctl(0, FIONREAD, &n) == 0 && n > 0;
return 0;
}
/* Set error condition and remove watcher from set */
static void invalidate_watcher(uev_t *w, struct epoll_event *event)
{
event->events = UEV_ERROR;
w->active = 0;
LIST_REMOVE(w, link);
}
/* 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)
{ {
if (!ctx || !w) { if (!ctx || !w) {
errno = EINVAL; errno = EINVAL;
@@ -76,7 +102,7 @@ int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, vo
} }
/* Private to libuEv, do not use directly! */ /* Private to libuEv, do not use directly! */
int uev_watcher_start(uev_t *w) int _uev_watcher_start(uev_t *w)
{ {
struct epoll_event ev; struct epoll_event ev;
@@ -85,15 +111,28 @@ int uev_watcher_start(uev_t *w)
return -1; return -1;
} }
if (w->active) if (_uev_watcher_active(w))
return 0; return 0;
ev.events = w->events | EPOLLRDHUP; 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; if (errno != EPERM)
return -1;
w->active = 1; /* Handle special case: `application < file.txt` */
if (w->type != UEV_IO_TYPE || w->events != UEV_READ)
return -1;
/* Only allow this special handling for stdin */
if (w->fd != STDIN_FILENO)
return -1;
w->ctx->workaround = 1;
w->active = -1;
} else {
w->active = 1;
}
/* Add to internal list for bookkeeping */ /* Add to internal list for bookkeeping */
LIST_INSERT_HEAD(&w->ctx->watchers, w, link); LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
@@ -102,14 +141,14 @@ int uev_watcher_start(uev_t *w)
} }
/* Private to libuEv, do not use directly! */ /* Private to libuEv, do not use directly! */
int uev_watcher_stop(uev_t *w) int _uev_watcher_stop(uev_t *w)
{ {
if (!w) { if (!w) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
if (!w->active) if (!_uev_watcher_active(w))
return 0; return 0;
w->active = 0; w->active = 0;
@@ -125,16 +164,16 @@ int uev_watcher_stop(uev_t *w)
} }
/* Private to libuEv, do not use directly! */ /* Private to libuEv, do not use directly! */
int uev_watcher_active(uev_t *w) int _uev_watcher_active(uev_t *w)
{ {
if (!w) if (!w)
return 0; return 0;
return w->active; return w->active > 0;
} }
/* Private to libuEv, do not use directly! */ /* Private to libuEv, do not use directly! */
int uev_watcher_rearm(uev_t *w) int _uev_watcher_rearm(uev_t *w)
{ {
struct epoll_event ev; struct epoll_event ev;
@@ -189,7 +228,7 @@ int uev_exit(uev_ctx_t *ctx)
/* Remove from internal list */ /* Remove from internal list */
LIST_REMOVE(w, link); LIST_REMOVE(w, link);
if (!w->active) if (!_uev_watcher_active(w))
continue; continue;
switch (w->type) { switch (w->type) {
@@ -231,7 +270,7 @@ int uev_exit(uev_ctx_t *ctx)
*/ */
int uev_run(uev_ctx_t *ctx, int flags) int uev_run(uev_ctx_t *ctx, int flags)
{ {
int result = 0, timeout = -1; int timeout = -1;
uev_t *w; uev_t *w;
if (!ctx || ctx->fd < 0) { if (!ctx || ctx->fd < 0) {
@@ -253,10 +292,30 @@ int uev_run(uev_ctx_t *ctx, int flags)
uev_timer_set(w, w->u.t.timeout, w->u.t.period); uev_timer_set(w, w->u.t.timeout, w->u.t.period);
} }
while (ctx->running) { while (ctx->running && !LIST_EMPTY(&ctx->watchers)) {
int i, nfds; int i, nfds, rerun = 0;
struct epoll_event events[UEV_MAX_EVENTS]; struct epoll_event events[UEV_MAX_EVENTS];
/* Handle special case: `application < file.txt` */
if (ctx->workaround) {
LIST_FOREACH(w, &ctx->watchers, link) {
if (w->active != -1 || !w->cb)
continue;
if (!has_data(w->fd)) {
w->active = 0;
LIST_REMOVE(w, link);
}
rerun++;
w->cb(w, w->arg, UEV_READ);
}
}
if (rerun)
continue;
ctx->workaround = 0;
while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) { while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) {
if (!ctx->running) if (!ctx->running)
break; break;
@@ -266,7 +325,7 @@ int uev_run(uev_ctx_t *ctx, int flags)
/* Unrecoverable error, cleanup and exit with error. */ /* Unrecoverable error, cleanup and exit with error. */
uev_exit(ctx); uev_exit(ctx);
return -1; return -2;
} }
for (i = 0; ctx->running && i < nfds; i++) { for (i = 0; ctx->running && i < nfds; i++) {
@@ -280,23 +339,24 @@ int uev_run(uev_ctx_t *ctx, int flags)
/* If not valid anymore, try to remove, ignore any errors. */ /* If not valid anymore, try to remove, ignore any errors. */
if (!is_valid_fd(w->fd)) if (!is_valid_fd(w->fd))
uev_watcher_stop(w); _uev_watcher_stop(w);
/* Must recreate epoll fd now ... */ /* Must recreate epoll fd now ... */
if (_init(ctx, 1)) { if (_init(ctx, 1)) {
/* Catastrophic error, no way to recover. */
uev_exit(ctx); uev_exit(ctx);
return -2; return -3;
} }
/* Restart watchers in new efd */ /* Restart watchers in new efd */
LIST_FOREACH_SAFE(w, &ctx->watchers, link, tmp) { LIST_FOREACH_SAFE(w, &ctx->watchers, link, tmp) {
if (w->active) { if (_uev_watcher_active(w)) {
w->active = 0; w->active = 0;
LIST_REMOVE(w, link); LIST_REMOVE(w, link);
uev_watcher_start(w); _uev_watcher_start(w);
} }
} }
uev_watcher_start(retry); _uev_watcher_start(retry);
/* New efd, restart everything! */ /* New efd, restart everything! */
ctx->errors = 0; ctx->errors = 0;
@@ -308,12 +368,10 @@ int uev_run(uev_ctx_t *ctx, int flags)
uint64_t exp; uint64_t exp;
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) { if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
if (errno != ECANCELED) { if (errno != ECANCELED)
uev_exit(ctx); invalidate_watcher(w, &events[i]);
return -3; else
} events[i].events = UEV_HUP;
events[i].events = UEV_HUP;
} }
if (!w->u.c.interval) if (!w->u.c.interval)
@@ -326,8 +384,7 @@ int uev_run(uev_ctx_t *ctx, int flags)
uint64_t exp; uint64_t exp;
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) { if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
uev_exit(ctx); invalidate_watcher(w, &events[i]);
return -3;
} }
if (!w->u.t.period) if (!w->u.t.period)
@@ -338,10 +395,8 @@ 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)
uev_exit(ctx); invalidate_watcher(w, &events[i]);
return -4;
}
} }
if (w->cb) if (w->cb)
@@ -357,11 +412,11 @@ int uev_run(uev_ctx_t *ctx, int flags)
} }
} }
if ((flags & UEV_ONCE) || LIST_EMPTY(&ctx->watchers)) if (flags & UEV_ONCE)
break; break;
} }
return result; return 0;
} }
/** /**
+5 -4
View File
@@ -32,6 +32,7 @@
/* I/O events, signal and timer revents are always UEV_READ */ /* I/O events, signal and timer revents are always UEV_READ */
#define UEV_NONE 0 #define UEV_NONE 0
#define UEV_ERROR EPOLLERR
#define UEV_READ EPOLLIN #define UEV_READ EPOLLIN
#define UEV_WRITE EPOLLOUT #define UEV_WRITE EPOLLOUT
#define UEV_PRI EPOLLPRI #define UEV_PRI EPOLLPRI
@@ -45,10 +46,10 @@
#define UEV_NONBLOCK 2 #define UEV_NONBLOCK 2
/* Macros */ /* Macros */
#define uev_io_active(w) uev_watcher_active(w) #define uev_io_active(w) _uev_watcher_active(w)
#define uev_timer_active(w) uev_watcher_active(w) #define uev_timer_active(w) _uev_watcher_active(w)
#define uev_cron_active(w) uev_watcher_active(w) #define uev_cron_active(w) _uev_watcher_active(w)
#define uev_signal_active(w) uev_watcher_active(w) #define uev_signal_active(w) _uev_watcher_active(w)
/* Event watcher */ /* Event watcher */
typedef struct uev { typedef struct uev {