mirror of
https://github.com/troglobit/libuev.git
synced 2026-10-23 11:24:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cebe66e8a4 | ||
|
|
dd651ae791 | ||
|
|
a528405258 | ||
|
|
70f04c10e1 | ||
|
|
e3dca8255d | ||
|
|
dbf8c0dae2 | ||
|
|
f75b1d5080 | ||
|
|
7f16876932 | ||
|
|
d7451cadd7 | ||
|
|
804218ba44 | ||
|
|
4d1875e49c | ||
|
|
3b356222cb | ||
|
|
9ec5dff692 | ||
|
|
e35c065d77 | ||
|
|
7d97b9f04a | ||
|
|
9499052989 | ||
|
|
c77cee19e8 | ||
|
|
fbcd200fd5 | ||
|
|
84661a1b12 | ||
|
|
ed313a1422 | ||
|
|
28e735fd81 | ||
|
|
2bea4192db | ||
|
|
d52254b4ec | ||
|
|
ebb574136b | ||
|
|
d7485c59ec | ||
|
|
505a7214a8 | ||
|
|
9df7cbfc52 | ||
|
|
6cbeb057b8 | ||
|
|
853604a65a | ||
|
|
35a54ef75e | ||
|
|
966f29c207 | ||
|
|
6bc1b88973 | ||
|
|
7c86cc31f1 | ||
|
|
d9f8caf0e8 | ||
|
|
3e5694cb76 | ||
|
|
f5806a28c0 | ||
|
|
ef57250586 | ||
|
|
febace818b | ||
|
|
5496bf0931 | ||
|
|
cb93dd9c9d | ||
|
|
341f7801fd | ||
|
|
dd475b5058 | ||
|
|
0a1a0ebffb | ||
|
|
91bfe9faa7 | ||
|
|
ddfc1460eb | ||
|
|
71e6d78fdc | ||
|
|
5929295c37 | ||
|
|
412f04f413 | ||
|
|
f78348a92a | ||
|
|
d1f5062e79 | ||
|
|
1037eed481 | ||
|
|
2f9cce9150 | ||
|
|
f3b3177963 | ||
|
|
ad262efa97 | ||
|
|
0dfc9152bd | ||
|
|
9642443a7c | ||
|
|
98748277f3 | ||
|
|
1de445535e | ||
|
|
151f9294c9 | ||
|
|
61bb69795d | ||
|
|
d46ff8e396 | ||
|
|
e0cebc33b1 | ||
|
|
82a1018dd2 | ||
|
|
58ec8e1d6f | ||
|
|
704d7d5612 | ||
|
|
ce4f8d6ef5 | ||
|
|
6f5e6e5232 | ||
|
|
9751be5859 |
No files matched your search
+7
-1
@@ -7,8 +7,14 @@
|
||||
*.la
|
||||
*.lo
|
||||
*.html
|
||||
.config
|
||||
.deps
|
||||
.libs
|
||||
.unpacked
|
||||
ID
|
||||
GTAGS
|
||||
GRTAGS
|
||||
GPATH
|
||||
compile
|
||||
config.*
|
||||
configure
|
||||
@@ -27,4 +33,4 @@ bench
|
||||
libuev*.tar.*
|
||||
libuev.pc
|
||||
!libuev.pc.in
|
||||
test-driver
|
||||
test-driver
|
||||
+7
-2
@@ -5,6 +5,11 @@ language: c
|
||||
# Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
|
||||
sudo: false
|
||||
|
||||
# Test build with both GCC and Clang (LLVM)
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
env:
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
@@ -17,13 +22,13 @@ addons:
|
||||
name: "troglobit/libuev"
|
||||
description: "libuEv | Simple event loop for Linux"
|
||||
notification_email: troglobit@gmail.com
|
||||
build_command_prepend: "./autogen && ./configure"
|
||||
build_command_prepend: "./autogen.sh && ./configure --enable-examples"
|
||||
build_command: "make clean all"
|
||||
branch_pattern: dev
|
||||
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- ./configure --disable-silent-rules
|
||||
- ./configure --disable-silent-rules --enable-examples
|
||||
- make clean
|
||||
- make -j5
|
||||
- make check
|
||||
@@ -11,13 +11,21 @@ API Documentation
|
||||
* [Joystick Example](#joystick-example)
|
||||
|
||||
|
||||
> “Event driven software improves concurrency” -- [Dave Zarzycki, Apple][]
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
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
|
||||
@@ -39,6 +47,11 @@ Overview
|
||||
* w->fd holds the file descriptor or socket, and events is set by
|
||||
* libuEv to indicate status: UEV_READ and/or UEV_WRITE with any of
|
||||
* the optional UEV_HUP, UEV_RDHUP, or UEV_PRI for urgent read data.
|
||||
*
|
||||
* Note: UEV_ERROR may be returned for any watcher and must be checked
|
||||
* by all callbacks. I/O watchers may also need to check UEV_HUP.
|
||||
* Appropriate action, e.g. restart the watcher, is up to the
|
||||
* application and is thus delegated to the callback.
|
||||
*/
|
||||
void callback (uev_t *w, void *arg, int events);
|
||||
|
||||
@@ -56,7 +69,7 @@ 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: schedule a relative timer, timeout and period in milliseconds */
|
||||
/* Timer watcher: schedule a relative timer, timeout (must be non-zero) 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 */
|
||||
@@ -91,16 +104,20 @@ uev_init(&ctx);
|
||||
|
||||
### Register an Event Watcher
|
||||
|
||||
For each event to monitor, be it a signal, timer or file descriptor, a
|
||||
*watcher* must be registered with the event context. The watcher, an
|
||||
`uev_t`, is registered by calling the event type's `_init()` function
|
||||
with the `uev_ctx_t` context, the callback, and an optional argument.
|
||||
For each event to monitor, be it a signal, cron/timer or a file/network
|
||||
descriptor, a *watcher* must be registered with the event context. The
|
||||
watcher, an `uev_t`, is registered by calling the event type's `_init()`
|
||||
function with the `uev_ctx_t` context, the callback, and an optional
|
||||
argument.
|
||||
|
||||
Here is a signal example:
|
||||
|
||||
```C
|
||||
void cleanup_exit(uev_t *w, void *arg, int events)
|
||||
{
|
||||
if (UEV_ERROR == events)
|
||||
puts("Ignoring signal watcher error ...");
|
||||
|
||||
/* Graceful exit, with optional cleanup ... */
|
||||
uev_exit(w->ctx);
|
||||
}
|
||||
@@ -117,6 +134,17 @@ int main(void)
|
||||
}
|
||||
```
|
||||
|
||||
Notice that the callback must be prepared to handle `UEV_ERROR`. I/O
|
||||
watchers in particular, but also timer watchers, must be restarted if
|
||||
required by the application. libuEv automatically tries to restart a
|
||||
signal watcher, but should that fail the callback will return error as
|
||||
well.
|
||||
|
||||
I/O watchers should also check for `UEV_HUP`, preferably when handling
|
||||
any short `read()` or `write()` system calls. A short read on a socket
|
||||
may be due to the remote end having performed a `shutdown()`. This is
|
||||
signaled to the callback using `UEV_HUP` in the `events` mask.
|
||||
|
||||
|
||||
### Start Event Loop
|
||||
|
||||
@@ -127,13 +155,24 @@ used to integrate [libuEv][] into another event loop.
|
||||
In this example we set `flags` to none:
|
||||
|
||||
```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
|
||||
served the first event. If `flags` is set to `UEV_ONCE | UEV_NONBLOCK`
|
||||
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
|
||||
shutdowns internally, but also lets the callback run. This is useful
|
||||
for stateful connections to be able to detect EOF.
|
||||
@@ -143,8 +182,16 @@ for stateful connections to be able to detect EOF.
|
||||
1. Set up an event context with `uev_init()`
|
||||
2. Register event callbacks with the event context using
|
||||
`uev_io_init()`, `uev_signal_init()` or `uev_timer_init()`
|
||||
3. Start the event loop with `uev_run()`
|
||||
4. Exit the event loop with `uev_exit()`, possibly from a callback
|
||||
3. Make sure callbacks checks their `events` mask and handles:
|
||||
|
||||
- `UEV_ERROR`, e.g. I/O watchers must be restarted
|
||||
- `UEV_HUP`, reading any remaining data on the descriptor
|
||||
|
||||
In both of these cases the watcher is stopped by libuEv. On HUP the
|
||||
descriptor/connection must be reopened and the watcher reinitialized
|
||||
with `uev_io_set()`, if required by the application.
|
||||
4. Start the event loop with `uev_run()`
|
||||
5. Exit the event loop with `uev_exit()`, possibly from a callback
|
||||
|
||||
**Note 1:** 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
|
||||
@@ -167,7 +214,7 @@ for stateful connections to be able to detect EOF.
|
||||
Using -luev
|
||||
-----------
|
||||
|
||||
LibuEv is by default installed as a library with a few header files, you
|
||||
libuEv is by default installed as a library with a few header files, you
|
||||
should only ever need to include one:
|
||||
|
||||
```C
|
||||
@@ -221,9 +268,32 @@ struct js_event {
|
||||
uint8_t number; /* axis/button number */
|
||||
} 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)
|
||||
{
|
||||
read(w->fd, &e, sizeof(e));
|
||||
ssize_t cnt;
|
||||
|
||||
if (UEV_ERROR == events) {
|
||||
/* Possibly joystick was unplugged */
|
||||
warnx("Spurious problem with the joystick watcher, restarting.");
|
||||
uev_io_start(w);
|
||||
}
|
||||
|
||||
cnt = read(w->fd, &e, sizeof(e));
|
||||
if (cnt < 0) {
|
||||
warn("Failed reading joystick event");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cnt == 0 || UEV_HUP == events) {
|
||||
warn("Joystick disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.type) {
|
||||
case 1:
|
||||
@@ -279,5 +349,6 @@ library) for [reference benchmarks][7] against [libevent][1] and
|
||||
[5]: https://github.com/troglobit/uftpd
|
||||
[6]: https://github.com/troglobit/finit
|
||||
[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
|
||||
[Dave Zarzycki, Apple]: http://www.youtube.com/watch?v=cD_s6Fjdri8
|
||||
+69
-11
@@ -3,6 +3,62 @@ Change Log
|
||||
|
||||
All notable changes to the project are documented in this file.
|
||||
|
||||
[v2.1.1][] - 2018-01-28
|
||||
-----------------------
|
||||
|
||||
### Fixes
|
||||
- Fix use after free in main event loop if watcher deletes itself in
|
||||
the callback. I.e., the callback must be the last action for the
|
||||
watcher in the event loop.
|
||||
- Doc timer example fix by @tisyang
|
||||
- Doc timer updates, non-zero timeout required
|
||||
|
||||
|
||||
[v2.1.0][] - 2017-11-14
|
||||
-----------------------
|
||||
|
||||
### Changes
|
||||
- Remove event loop error tracking used to trigger a `epoll_create1()`
|
||||
at a certain error threshold. This tracking was first introduced in
|
||||
[v1.1.0][], triggered by spurious `EPOLLERR` on I/O watchers
|
||||
- Unconditionally stop I/O watchers that return `EPOLLERR` or `EPOLLHUP`
|
||||
it is up to the watcher callback to clear the error and/or `read()`
|
||||
the last few bytes from the descriptor. HUP usally means EOF, or that
|
||||
the remote end of a stream or pipe closed, this may also be signaled
|
||||
by `read()` returning zero
|
||||
- Add missing `--enable-examples` to `configure` script
|
||||
- Update documentation, both [README.md][] and [API.md][]
|
||||
|
||||
### Fixes
|
||||
- Properly stop and de-register signal and cron/timer watchers from the
|
||||
epoll socket in case of errors, problem introduced in [v2.0.0][]
|
||||
- Mark watcher file descriptor as unintialized on internal error
|
||||
- Fix double-close of cron/timer watchers. Problem triggered when the
|
||||
timer expires and calls `uev_exit()`, which stops all watchers. When
|
||||
the timer callback returns another call to stop the watcher triggered
|
||||
the double `close()`
|
||||
- Fix unit test's error handling in watcher callbacks, for reference
|
||||
- Fix use-before-set in cronrun unit test
|
||||
- Make sure to restart unit test's I/O watchers on `UEV_ERROR`
|
||||
- Make sure to restart example I/O watchers on `UEV_ERROR`
|
||||
- Properly check for `UEV_HUP` in unit tests and examples
|
||||
|
||||
|
||||
[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
|
||||
-----------------------
|
||||
@@ -36,7 +92,7 @@ All notable changes to the project are documented in this file.
|
||||
|
||||
### Changes
|
||||
- Add support for absolute timers with the `uev_cron_*()` API.
|
||||
- Update build & install instructions in README
|
||||
- Update build & install instructions in [README.md][]
|
||||
|
||||
### Fixes
|
||||
- Fix `uev_timer_set()` so that it returns error in case the underlying
|
||||
@@ -78,7 +134,7 @@ introduced in [v1.2.1][]. Apologies for any problems this may cause!
|
||||
`pkg-config` your C program must now `#include <uev/uev.h>`
|
||||
- Support for `EPOLLPRI` events for I/O watchers, thanks to Markus Svilans
|
||||
- Simplified joystick example
|
||||
- Updated `README` slightly
|
||||
- Updated [README.md][] slightly
|
||||
|
||||
|
||||
[v1.3.1][] - 2016-02-02
|
||||
@@ -86,7 +142,7 @@ introduced in [v1.2.1][]. Apologies for any problems this may cause!
|
||||
|
||||
### Fixes
|
||||
- Remove symlinks to Markdown files from GIT
|
||||
- Distribute and install Markdown files: README.md, etc.
|
||||
- Distribute and install Markdown files: [README.md][], etc.
|
||||
|
||||
|
||||
[v1.3.0][] - 2016-01-22
|
||||
@@ -122,7 +178,7 @@ Very minor release.
|
||||
- Dropped [TODO.md][] from distribution archives, only for devs
|
||||
|
||||
### Fixes
|
||||
- Lots of Markdown syntax fixes in both README and ChangeLog
|
||||
- Lots of Markdown syntax fixes in both [README.md][] and ChangeLog
|
||||
- Silence annoying warning from newer GNU ar in Ubuntu 15.10
|
||||
|
||||
|
||||
@@ -280,7 +336,14 @@ v0.0.1 - 2012-03-17
|
||||
Lua users mailing list.
|
||||
|
||||
|
||||
[UNRELEASED]: https://github.com/troglobit/libuev/compare/v1.4.2...HEAD
|
||||
[UNRELEASED]: https://github.com/troglobit/libuev/compare/v2.1.1...HEAD
|
||||
[v2.1.1]: https://github.com/troglobit/libuev/compare/v2.1.0...v2.1.1
|
||||
[v2.1.0]: https://github.com/troglobit/libuev/compare/v2.0.0...v2.1.0
|
||||
[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.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
|
||||
@@ -298,6 +361,7 @@ Lua users mailing list.
|
||||
[v1.0.1]: https://github.com/troglobit/libuev/compare/v1.0.0...v1.0.1
|
||||
[v1.0.0]: https://github.com/troglobit/libuev/compare/v0.0.1...v1.0.0
|
||||
[TODO.md]: https://github.com/troglobit/libuev/blob/master/TODO.md
|
||||
[API.md]: https://github.com/troglobit/libuev/blob/master/API.md
|
||||
[README.md]: https://github.com/troglobit/libuev/blob/master/README.md
|
||||
[ChangeLog.md]: https://github.com/troglobit/libuev/blob/master/ChangeLog.md
|
||||
[joystick.c]: https://github.com/troglobit/libuev/blob/master/joystick.c
|
||||
@@ -307,9 +371,3 @@ Lua users mailing list.
|
||||
[libuevent]: https://code.google.com/p/libuevent/
|
||||
[Flemming Madsen]: http://www.madsensoft.dk
|
||||
[Initial announcement]: http://lua-users.org/lists/lua-l/2012-03/msg00510.html
|
||||
|
||||
<!--
|
||||
-- Local Variables:
|
||||
-- mode: markdown
|
||||
-- End:
|
||||
-->
|
||||
+5
-1
@@ -4,6 +4,10 @@ DIST_SUBDIRS = src examples tests
|
||||
doc_DATA = API.md README.md LICENSE
|
||||
EXTRA_DIST = API.md README.md LICENSE AUTHORS ChangeLog.md
|
||||
|
||||
if ENABLE_EXAMPLES
|
||||
SUBDIRS += examples
|
||||
endif
|
||||
|
||||
## Generate .deb package
|
||||
package:
|
||||
@dpkg-buildpackage -uc -us -B
|
||||
@@ -34,7 +38,7 @@ release-hook:
|
||||
fi
|
||||
|
||||
## 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 \
|
||||
printf "$$file \tDistribution tarball\n"; \
|
||||
printf "$$file.md5\t"; cat $$file.md5 | cut -f1 -d' '; \
|
||||
|
||||
@@ -15,7 +15,7 @@ libuEv | Simple event loop for Linux
|
||||
* [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
|
||||
------------
|
||||
@@ -25,14 +25,19 @@ Introduction
|
||||
*u* (micro) in the name refers to both the small feature set and the
|
||||
small size overhead impact of the library.
|
||||
|
||||
Experienced developers may appreciate that [libuEv][] is built on top of
|
||||
modern Linux APIs: epoll, timerfd and signalfd.
|
||||
Failure mode changes introduced in v2.0 may affect users of v1.x, See
|
||||
the [ChangeLog][] for the full details.
|
||||
|
||||
The [API documentation](API.md) is available as a separate document.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
The library documentation is available as a [separate document](API.md).
|
||||
Notice below how watcher `UEV_ERROR` conditions must be handled by each
|
||||
callback. I/O watchers must also check for `UEV_HUP`. Both errors are
|
||||
usually fatal, libuEv makes sure to stop each watcher before a callback
|
||||
runs, leaving it up to the callback to take appropriate action.
|
||||
|
||||
```C
|
||||
#include <stdio.h>
|
||||
@@ -40,6 +45,10 @@ The library documentation is available as a [separate document](API.md).
|
||||
|
||||
static void cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
if (UEV_ERROR == events) {
|
||||
puts("Problem with timer, attempting to restart.");
|
||||
uev_timer_start(w);
|
||||
}
|
||||
puts("Every other second");
|
||||
}
|
||||
|
||||
@@ -49,7 +58,8 @@ int main(void)
|
||||
uev_ctx_t 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, 2 * 1000, 2 * 1000);
|
||||
|
||||
return uev_run(&ctx, 0);
|
||||
}
|
||||
@@ -78,11 +88,10 @@ The resulting .so file is ~14 kiB.
|
||||
Origin & References
|
||||
-------------------
|
||||
|
||||
[libuEv][] was originally based on [LibUEvent][8] by [Flemming Madsen][]
|
||||
but has been completely rewritten to provide a cleaner API. It is now
|
||||
more similar to the famous [libev][2] by [Mark Lehmann][]. Another
|
||||
small event library used for inspiration is the very small [picoev][9]
|
||||
by [Oku Kazuho][].
|
||||
[libuEv][] originates from [LibUEvent][8] by [Flemming Madsen][], but
|
||||
with a rewritten and much cleaner API. It is now more similar to the
|
||||
famous [libev][2] by [Mark Lehmann][]. Another library inspiring this
|
||||
project is [picoev][9] by [Oku Kazuho][].
|
||||
|
||||
[libuEv][] is developed and maintained by [Joachim Nilsson][]. It is
|
||||
built for and developed on GNU/Linux systems, patches to support *BSD
|
||||
@@ -94,6 +103,7 @@ and its [kqueue][] interface are most welcome.
|
||||
[3]: http://unix.com/man-page/All/3x/XtDispatchEvent
|
||||
[8]: http://code.google.com/p/libuevent/
|
||||
[9]: https://github.com/kazuho/picoev
|
||||
[ChangeLog]: https://github.com/troglobit/libuev/blob/master/ChangeLog.md
|
||||
[Travis]: https://travis-ci.org/troglobit/libuev
|
||||
[Travis Status]: https://travis-ci.org/troglobit/libuev.png?branch=master
|
||||
[Coverity Scan]: https://scan.coverity.com/projects/3846
|
||||
|
||||
+7
-1
@@ -1,4 +1,4 @@
|
||||
AC_INIT(libuev, 1.6.0, https://github.com/troglobit/libuev/issues)
|
||||
AC_INIT(libuev, 2.1.1, https://github.com/troglobit/libuev/issues)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
@@ -11,4 +11,10 @@ AC_PROG_CC
|
||||
AM_PROG_AR
|
||||
LT_INIT
|
||||
|
||||
# Optional features
|
||||
AC_ARG_ENABLE([examples],
|
||||
[AC_HELP_STRING([--enable-examples], [Build libuEv examples/ directory])],
|
||||
[], [enable_examples=no])
|
||||
AM_CONDITIONAL([ENABLE_EXAMPLES], [test "$enable_examples" = yes])
|
||||
|
||||
AC_OUTPUT
|
||||
Vendored
+33
@@ -1,3 +1,36 @@
|
||||
libuev (2.1.1) unstable; urgency=medium
|
||||
|
||||
* Minor bugfix release
|
||||
* Use native version numbering for package
|
||||
|
||||
-- Joachim Nilsson <troglobit@gmail.com> Sun, 28 Jan 2018 13:38:00 +0100
|
||||
|
||||
libuev (2.1.0-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release. Further improvements to failure modes, some
|
||||
fixes to regressions introduced in v2.0.
|
||||
|
||||
- Unconditionally stop I/O watchers on UEV_HUP and UEV_ERR conditions.
|
||||
- Attempt to restart failing signal watchers before dropping to error.
|
||||
- Properly de-register signal and cron/timer watchers on error.
|
||||
- Fix possible double close() on cron/timer watchers.
|
||||
- Documentation updates, improved examples, and unit tests.
|
||||
|
||||
-- Joachim Nilsson <troglobit@gmail.com> Tue, 14 Nov 2017 17:40:43 +0100
|
||||
|
||||
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
|
||||
|
||||
* Support for edge triggered and oneshot event types
|
||||
|
||||
@@ -2,3 +2,5 @@ joystick
|
||||
signal
|
||||
cronrun
|
||||
test
|
||||
redirect
|
||||
hej.txt
|
||||
@@ -1,5 +1,3 @@
|
||||
noinst_PROGRAMS = joystick
|
||||
AM_CPPFLAGS = -I$(top_srcdir)
|
||||
AM_LDFLAGS = -L$(top_srcdir)
|
||||
LIBS = $(LTLIBINTL)
|
||||
LDADD = $(top_srcdir)/libuev.la
|
||||
noinst_PROGRAMS = joystick redirect
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
LDADD = $(top_srcdir)/src/libuev.la
|
||||
+21
-2
@@ -37,10 +37,29 @@ struct js_event {
|
||||
uint8_t number; /* axis/button number */
|
||||
} e;
|
||||
|
||||
/*
|
||||
* Always check for UEV_ERROR in watcher callback!
|
||||
* Possibly joystick was unplugged
|
||||
*/
|
||||
static void joystick_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
if (read(w->fd, &e, sizeof(e)) < 0)
|
||||
errx(errno, "Failed reading joystick event");
|
||||
ssize_t len;
|
||||
|
||||
if (UEV_ERROR == events) {
|
||||
warnx("Spurious problem with the joystick watcher, restarting.");
|
||||
uev_io_start(w);
|
||||
}
|
||||
|
||||
len = read(w->fd, &e, sizeof(e));
|
||||
if (len < 0) {
|
||||
warn("Failed reading joystick event");
|
||||
return;
|
||||
}
|
||||
|
||||
if (len == 0 || UEV_HUP == events) {
|
||||
warn("Joystick disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e.type) {
|
||||
case 1:
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/* 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) {
|
||||
warnx("Spurious problem with the stdin watcher, restarting.");
|
||||
uev_io_start(w);
|
||||
}
|
||||
|
||||
len = read(w->fd, buf, sizeof(buf));
|
||||
if (len == -1) {
|
||||
warn("Error reading from stdin");
|
||||
return;
|
||||
}
|
||||
|
||||
if (len == 0 || UEV_HUP == events) {
|
||||
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
@@ -2,7 +2,7 @@ lib_LTLIBRARIES = libuev.la
|
||||
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_CFLAGS = -W -Wall -Wextra
|
||||
libuev_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:2:0
|
||||
libuev_la_LDFLAGS = $(AM_LDFLAGS) -version-info 2:1:0
|
||||
|
||||
noinst_PROGRAMS = bench
|
||||
bench_CPPFLAGS = -D_GNU_SOURCE
|
||||
|
||||
+3
-3
@@ -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)
|
||||
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;
|
||||
|
||||
if (uev_cron_set(w, when, interval)) {
|
||||
uev_watcher_stop(w);
|
||||
_uev_watcher_stop(w);
|
||||
exit:
|
||||
close(fd);
|
||||
return -1;
|
||||
@@ -132,7 +132,7 @@ int uev_cron_set(uev_t *w, time_t when, time_t interval)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return uev_watcher_start(w);
|
||||
return _uev_watcher_start(w);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
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 uev_watcher_start(w);
|
||||
return _uev_watcher_start(w);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,9 +60,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 ((events & UEV_ONESHOT) && w->active)
|
||||
return uev_watcher_rearm(w);
|
||||
if ((events & UEV_ONESHOT) && _uev_watcher_active(w))
|
||||
return _uev_watcher_rearm(w);
|
||||
|
||||
/* Ignore any errors, only to clean up anything lingering ... */
|
||||
uev_io_stop(w);
|
||||
@@ -89,7 +88,7 @@ int uev_io_start(uev_t *w)
|
||||
*/
|
||||
int uev_io_stop(uev_t *w)
|
||||
{
|
||||
return uev_watcher_stop(w);
|
||||
return _uev_watcher_stop(w);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+11
-11
@@ -31,21 +31,21 @@
|
||||
|
||||
/* I/O, timer, or signal watcher */
|
||||
typedef enum {
|
||||
UEV_CRON_TYPE = 1,
|
||||
UEV_IO_TYPE,
|
||||
UEV_IO_TYPE = 1,
|
||||
UEV_SIGNAL_TYPE,
|
||||
UEV_TIMER_TYPE,
|
||||
UEV_CRON_TYPE,
|
||||
} uev_type_t;
|
||||
|
||||
/* 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 */
|
||||
typedef struct {
|
||||
int running;
|
||||
int fd; /* For epoll() */
|
||||
uint32_t errors;
|
||||
LIST_HEAD(,uev) watchers;
|
||||
uint32_t workaround; /* For workarounds, e.g. redirected stdin */
|
||||
} uev_ctx_t;
|
||||
|
||||
/* Forward declare due to dependencys, don't try this at home kids. */
|
||||
@@ -81,13 +81,13 @@ struct uev;
|
||||
uev_type_t
|
||||
|
||||
/* Internal API for dealing with generic watchers */
|
||||
int uev_watcher_init (uev_ctx_t *ctx, struct uev *w, uev_type_t type,
|
||||
void (*cb)(struct uev *, void *, int), void *arg,
|
||||
int fd, int events);
|
||||
int uev_watcher_start (struct uev *w);
|
||||
int uev_watcher_stop (struct uev *w);
|
||||
int uev_watcher_active(struct uev *w);
|
||||
int uev_watcher_rearm (struct uev *w);
|
||||
int _uev_watcher_init (uev_ctx_t *ctx, struct uev *w, uev_type_t type,
|
||||
void (*cb)(struct uev *, void *, int), void *arg,
|
||||
int fd, int events);
|
||||
int _uev_watcher_start (struct uev *w);
|
||||
int _uev_watcher_stop (struct uev *w);
|
||||
int _uev_watcher_active(struct uev *w);
|
||||
int _uev_watcher_rearm (struct uev *w);
|
||||
|
||||
#endif /* LIBUEV_PRIVATE_H_ */
|
||||
|
||||
|
||||
+4
-4
@@ -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)
|
||||
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;
|
||||
|
||||
if (uev_signal_set(w, signo)) {
|
||||
uev_watcher_stop(w);
|
||||
_uev_watcher_stop(w);
|
||||
exit:
|
||||
close(fd);
|
||||
return -1;
|
||||
@@ -109,7 +109,7 @@ int uev_signal_set(uev_t *w, int signo)
|
||||
if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0)
|
||||
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)
|
||||
{
|
||||
if (uev_watcher_stop(w))
|
||||
if (_uev_watcher_stop(w))
|
||||
return -1;
|
||||
|
||||
close(w->fd);
|
||||
|
||||
+15
-6
@@ -76,13 +76,14 @@ int uev_timer_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeou
|
||||
if (fd < 0)
|
||||
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;
|
||||
|
||||
if (uev_timer_set(w, timeout, period)) {
|
||||
uev_watcher_stop(w);
|
||||
_uev_watcher_stop(w);
|
||||
exit:
|
||||
close(fd);
|
||||
w->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -92,9 +93,13 @@ int uev_timer_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeou
|
||||
/**
|
||||
* Reset a timer
|
||||
* @param w Watcher to reset
|
||||
* @param timeout Timeout in milliseconds before @param cb is called
|
||||
* @param timeout Timeout in milliseconds before @param cb is called, zero disarms timer
|
||||
* @param period For periodic timers this is the period time that @param timeout is reset to
|
||||
*
|
||||
* Note, the @param timeout value must be non-zero. Setting it to zero
|
||||
* will disarm the timer. This is the underlying Linux function @func
|
||||
* timerfd_settimer() which has this behavior.
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_timer_set(uev_t *w, int timeout, int period)
|
||||
@@ -135,7 +140,7 @@ int uev_timer_set(uev_t *w, int timeout, int period)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return uev_watcher_start(w);
|
||||
return _uev_watcher_start(w);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,7 +157,7 @@ int uev_timer_start(uev_t *w)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -165,10 +170,14 @@ int uev_timer_start(uev_t *w)
|
||||
*/
|
||||
int uev_timer_stop(uev_t *w)
|
||||
{
|
||||
/* Check if already stopped in uev_run() after cb() or _init() */
|
||||
if (!_uev_watcher_active(w))
|
||||
return 0;
|
||||
|
||||
/* Stop kernel timer */
|
||||
uev_timer_set(w, 0, 0);
|
||||
|
||||
if (uev_watcher_stop(w))
|
||||
if (_uev_watcher_stop(w))
|
||||
return -1;
|
||||
|
||||
/* Close timerfd, will have to be reopened again on reset */
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <fcntl.h> /* O_CLOEXEC */
|
||||
#include <string.h> /* memset() */
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h> /* for select() workaround */
|
||||
#include <sys/signalfd.h> /* struct signalfd_siginfo */
|
||||
#include <unistd.h> /* close(), read() */
|
||||
|
||||
@@ -50,14 +52,24 @@ static int _init(uev_ctx_t *ctx, int close_old)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Simple check if a descriptor is still valid in the kernel */
|
||||
static int is_valid_fd(int fd)
|
||||
/* Used by file i/o workaround when epoll => EPERM */
|
||||
static int has_data(int fd)
|
||||
{
|
||||
return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
errno = EINVAL;
|
||||
@@ -76,7 +88,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! */
|
||||
int uev_watcher_start(uev_t *w)
|
||||
int _uev_watcher_start(uev_t *w)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
@@ -85,15 +97,28 @@ int uev_watcher_start(uev_t *w)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (w->active)
|
||||
if (_uev_watcher_active(w))
|
||||
return 0;
|
||||
|
||||
ev.events = w->events | EPOLLRDHUP;
|
||||
ev.data.ptr = w;
|
||||
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
|
||||
return -1;
|
||||
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
|
||||
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 */
|
||||
LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
|
||||
@@ -102,14 +127,14 @@ int uev_watcher_start(uev_t *w)
|
||||
}
|
||||
|
||||
/* Private to libuEv, do not use directly! */
|
||||
int uev_watcher_stop(uev_t *w)
|
||||
int _uev_watcher_stop(uev_t *w)
|
||||
{
|
||||
if (!w) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!w->active)
|
||||
if (!_uev_watcher_active(w))
|
||||
return 0;
|
||||
|
||||
w->active = 0;
|
||||
@@ -125,16 +150,16 @@ int uev_watcher_stop(uev_t *w)
|
||||
}
|
||||
|
||||
/* Private to libuEv, do not use directly! */
|
||||
int uev_watcher_active(uev_t *w)
|
||||
int _uev_watcher_active(uev_t *w)
|
||||
{
|
||||
if (!w)
|
||||
return 0;
|
||||
|
||||
return w->active;
|
||||
return w->active > 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -189,7 +214,7 @@ int uev_exit(uev_ctx_t *ctx)
|
||||
/* Remove from internal list */
|
||||
LIST_REMOVE(w, link);
|
||||
|
||||
if (!w->active)
|
||||
if (!_uev_watcher_active(w))
|
||||
continue;
|
||||
|
||||
switch (w->type) {
|
||||
@@ -231,7 +256,7 @@ int uev_exit(uev_ctx_t *ctx)
|
||||
*/
|
||||
int uev_run(uev_ctx_t *ctx, int flags)
|
||||
{
|
||||
int result = 0, timeout = -1;
|
||||
int timeout = -1;
|
||||
uev_t *w;
|
||||
|
||||
if (!ctx || ctx->fd < 0) {
|
||||
@@ -253,11 +278,31 @@ int uev_run(uev_ctx_t *ctx, int flags)
|
||||
uev_timer_set(w, w->u.t.timeout, w->u.t.period);
|
||||
}
|
||||
|
||||
while (ctx->running) {
|
||||
int i, nfds;
|
||||
struct epoll_event events[UEV_MAX_EVENTS];
|
||||
while (ctx->running && !LIST_EMPTY(&ctx->watchers)) {
|
||||
int i, nfds, rerun = 0;
|
||||
struct epoll_event ee[UEV_MAX_EVENTS];
|
||||
|
||||
while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) {
|
||||
/* 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, ee, UEV_MAX_EVENTS, timeout)) < 0) {
|
||||
if (!ctx->running)
|
||||
break;
|
||||
|
||||
@@ -266,87 +311,60 @@ int uev_run(uev_ctx_t *ctx, int flags)
|
||||
|
||||
/* Unrecoverable error, cleanup and exit with error. */
|
||||
uev_exit(ctx);
|
||||
return -1;
|
||||
|
||||
return -2;
|
||||
}
|
||||
|
||||
for (i = 0; ctx->running && i < nfds; i++) {
|
||||
w = (uev_t *)events[i].data.ptr;
|
||||
uint64_t exp;
|
||||
uint32_t events;
|
||||
struct signalfd_siginfo fdsi;
|
||||
ssize_t sz = sizeof(fdsi);
|
||||
|
||||
if (events[i].events & (EPOLLHUP | EPOLLERR)) {
|
||||
ctx->errors++;
|
||||
w = (uev_t *)ee[i].data.ptr;
|
||||
events = ee[i].events;
|
||||
|
||||
if (ctx->errors >= 42) {
|
||||
uev_t *tmp, *retry = w;;
|
||||
switch (w->type) {
|
||||
case UEV_IO_TYPE:
|
||||
if (events & (EPOLLHUP | EPOLLERR))
|
||||
uev_io_stop(w);
|
||||
break;
|
||||
|
||||
/* 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;
|
||||
case UEV_SIGNAL_TYPE:
|
||||
if (read(w->fd, &fdsi, sz) != sz) {
|
||||
if (uev_signal_start(w)) {
|
||||
uev_signal_stop(w);
|
||||
events = UEV_ERROR;
|
||||
}
|
||||
|
||||
/* 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_CRON_TYPE == w->type) {
|
||||
uint64_t exp;
|
||||
break;
|
||||
|
||||
case UEV_TIMER_TYPE:
|
||||
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
|
||||
if (errno != ECANCELED) {
|
||||
uev_exit(ctx);
|
||||
return -3;
|
||||
}
|
||||
uev_timer_stop(w);
|
||||
events = UEV_ERROR;
|
||||
}
|
||||
|
||||
events[i].events = UEV_HUP;
|
||||
if (!w->u.t.period)
|
||||
w->u.t.timeout = 0;
|
||||
break;
|
||||
|
||||
case UEV_CRON_TYPE:
|
||||
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
|
||||
events = UEV_HUP;
|
||||
if (errno != ECANCELED) {
|
||||
uev_cron_stop(w);
|
||||
events = UEV_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!w->u.c.interval)
|
||||
w->u.c.when = 0;
|
||||
else
|
||||
w->u.c.when += w->u.c.interval;
|
||||
break;
|
||||
}
|
||||
|
||||
if (UEV_TIMER_TYPE == w->type) {
|
||||
uint64_t exp;
|
||||
|
||||
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
|
||||
uev_exit(ctx);
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (!w->u.t.period)
|
||||
w->u.t.timeout = 0;
|
||||
}
|
||||
|
||||
if (UEV_SIGNAL_TYPE == w->type) {
|
||||
struct signalfd_siginfo fdsi;
|
||||
ssize_t sz = sizeof(fdsi);
|
||||
|
||||
if (read(w->fd, &fdsi, sz) != sz) {
|
||||
uev_exit(ctx);
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
|
||||
if (w->cb)
|
||||
w->cb(w, w->arg, events[i].events & UEV_EVENT_MASK);
|
||||
|
||||
if (UEV_CRON_TYPE == w->type) {
|
||||
if (!w->u.c.when)
|
||||
uev_timer_stop(w);
|
||||
@@ -355,13 +373,20 @@ int uev_run(uev_ctx_t *ctx, int flags)
|
||||
if (!w->u.t.timeout)
|
||||
uev_timer_stop(w);
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: Must be last action for watcher, the
|
||||
* callback may delete itself.
|
||||
*/
|
||||
if (w->cb)
|
||||
w->cb(w, w->arg, events & UEV_EVENT_MASK);
|
||||
}
|
||||
|
||||
if ((flags & UEV_ONCE) || LIST_EMPTY(&ctx->watchers))
|
||||
if (flags & UEV_ONCE)
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
/* I/O events, signal and timer revents are always UEV_READ */
|
||||
#define UEV_NONE 0
|
||||
#define UEV_ERROR EPOLLERR
|
||||
#define UEV_READ EPOLLIN
|
||||
#define UEV_WRITE EPOLLOUT
|
||||
#define UEV_PRI EPOLLPRI
|
||||
@@ -45,10 +46,10 @@
|
||||
#define UEV_NONBLOCK 2
|
||||
|
||||
/* Macros */
|
||||
#define uev_io_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_signal_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_cron_active(w) _uev_watcher_active(w)
|
||||
#define uev_signal_active(w) _uev_watcher_active(w)
|
||||
|
||||
/* Event watcher */
|
||||
typedef struct uev {
|
||||
@@ -65,6 +66,11 @@ typedef struct uev {
|
||||
* Generic callback for watchers, @events holds %UEV_READ and/or %UEV_WRITE
|
||||
* with optional %UEV_PRI (priority data available to read) and any of the
|
||||
* %UEV_HUP and/or %UEV_RDHUP, which may be used to signal hang-up events.
|
||||
*
|
||||
* Note: UEV_ERROR conditions must be handled by all callbacks!
|
||||
* I/O watchers may also need to check UEV_HUP. Appropriate action,
|
||||
* e.g. restart the watcher, is up to the application and is thus
|
||||
* delegated to the callback.
|
||||
*/
|
||||
typedef void (uev_cb_t)(uev_t *w, void *arg, int events);
|
||||
|
||||
|
||||
+4
-1
@@ -12,8 +12,11 @@ uev_t timer3;
|
||||
uev_t cron1;
|
||||
uev_t cron2;
|
||||
|
||||
static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
static void cb(uev_t *w, void *UNUSED(arg), int events)
|
||||
{
|
||||
if (UEV_ERROR == events)
|
||||
fprintf(stderr, "timer watcher failed, ignoring ...\n");
|
||||
|
||||
fail_unless(w == &timer1);
|
||||
|
||||
fail_unless(uev_timer_active(&timer2));
|
||||
|
||||
+41
-8
@@ -33,15 +33,21 @@ static int in, out;
|
||||
static int period = 0;
|
||||
static uev_t *watchdog;
|
||||
|
||||
static void lifetime_cb(uev_t *w, void *arg, int UNUSED(events))
|
||||
static void lifetime_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
if (UEV_ERROR == events)
|
||||
fprintf(stderr, "timer watcher failed, ignoring ...\n");
|
||||
|
||||
fprintf(stderr, "\nLifetime exceeded, program completed successfully! (arg:%p)\n", arg);
|
||||
uev_exit(w->ctx);
|
||||
}
|
||||
|
||||
/* The pipe watchdog, if it triggers we haven't received data in time. */
|
||||
static void timeout_cb(uev_t *w, void *arg, int UNUSED(events))
|
||||
static void timeout_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
if (UEV_ERROR == events)
|
||||
fprintf(stderr, "timer watcher failed, ignoring ...\n");
|
||||
|
||||
watchdog = NULL;
|
||||
fprintf(stderr, "\nTimeout exceeded %p\n", arg);
|
||||
|
||||
@@ -54,23 +60,46 @@ static void periodic_task(uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events
|
||||
fprintf(stderr, "|");
|
||||
}
|
||||
|
||||
static void signal_cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
static void signal_cb(uev_t *w, void *UNUSED(arg), int events)
|
||||
{
|
||||
if (UEV_ERROR == events) {
|
||||
fprintf(stderr, "Signal watcher failed, unrecoverable error.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, w->signo == SIGINT ? "^Cv" : "^\v");
|
||||
}
|
||||
|
||||
static void pipe_read_cb(uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
|
||||
static void pipe_read_cb(uev_t *w, void UNUSED(*arg), int events)
|
||||
{
|
||||
int cnt;
|
||||
int len;
|
||||
char msg[50];
|
||||
|
||||
if (UEV_ERROR == events) {
|
||||
error:
|
||||
fprintf(stderr, "pipe watcher failed, restarting ...\n");
|
||||
uev_io_start(w);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Kick watchdog */
|
||||
if (watchdog)
|
||||
uev_timer_set(watchdog, 1000, 0);
|
||||
|
||||
cnt = read(in, msg, sizeof(msg));
|
||||
// fprintf(stderr, "READ %.*s %d\n", cnt, msg, cnt);
|
||||
fprintf(stderr, "%.*s.%d ", cnt, msg, cnt);
|
||||
/* Here we can read() from in or w->fd, either works. */
|
||||
len = read(w->fd, msg, sizeof(msg));
|
||||
if (len < 0) {
|
||||
perror(__func__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (len == 0 || UEV_HUP == events) {
|
||||
fprintf(stderr, "Other end of pipe closed, no more data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// fprintf(stderr, "READ %.*s %d\n", len, msg, len);
|
||||
fprintf(stderr, "%.*s.%d ", len, msg, len);
|
||||
}
|
||||
|
||||
static void pipe_write_cb(uev_t *w, void *arg, int UNUSED(events))
|
||||
@@ -78,6 +107,10 @@ static void pipe_write_cb(uev_t *w, void *arg, int UNUSED(events))
|
||||
my_t *my = arg;
|
||||
char *msg = "TESTING";
|
||||
|
||||
if (UEV_ERROR == events)
|
||||
fprintf(stderr, "timer watcher failed, ignoring ...\n");
|
||||
|
||||
/* Here we *must* use out, not w->fd, since we're a timer callback */
|
||||
if (write(out, msg, my->counter) < 0) {
|
||||
perror("\nFailed writing to pipe");
|
||||
return;
|
||||
|
||||
+6
-2
@@ -30,11 +30,14 @@
|
||||
|
||||
struct timeval tv;
|
||||
|
||||
static void cron_job(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
static void cron_job(uev_t *w, void *UNUSED(arg), int events)
|
||||
{
|
||||
static int laps = 3;
|
||||
struct timeval now;
|
||||
|
||||
if (UEV_ERROR == events)
|
||||
fprintf(stderr, "cron watcher failed, ignoring ...\n");
|
||||
|
||||
/* Update timer every lap */
|
||||
tv.tv_sec += TIMEOUT;
|
||||
|
||||
@@ -55,10 +58,11 @@ int main(void)
|
||||
uev_init(&ctx);
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
when = tv.tv_sec + TIMEOUT;
|
||||
|
||||
printf("Start of test %s", ctime(&tv.tv_sec));
|
||||
printf("Expected cron %s", ctime(&when));
|
||||
|
||||
when = tv.tv_sec + TIMEOUT;
|
||||
interval = INTERVAL;
|
||||
uev_cron_init(&ctx, &cron_watcher, cron_job, NULL, when, interval);
|
||||
|
||||
|
||||
+4
-1
@@ -5,10 +5,13 @@
|
||||
#define TIMEOUT 2 /* 2 sec */
|
||||
struct timeval start;
|
||||
|
||||
static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
static void cb(uev_t *w, void *UNUSED(arg), int events)
|
||||
{
|
||||
struct timeval now;
|
||||
|
||||
if (UEV_ERROR == events)
|
||||
fprintf(stderr, "timer watcher failed, ignoring ...\n");
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
fail_unless(now.tv_sec == start.tv_sec + TIMEOUT);
|
||||
|
||||
|
||||
Reference in new issue
Block a user