194 Commits
Author SHA1 Message Date
Joachim Nilsson 847532f935 Update ChangeLog and bump version for v1.5 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-10-30 15:50:40 +01:00
Joachim Nilsson ec1290a46e ABI revision bump
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-10-30 15:50:09 +01:00
Joachim Nilsson 3bee117a85 Makefile.am: Add missing cron.c
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-10-30 15:29:22 +01:00
Joachim Nilsson 75f59b7b11 Bump version for v1.5.0-dev release cycle
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-10-30 15:09:51 +01:00
Joachim Nilsson 59e1d376db cron: New API for absolute timers
This patch adds support for at/cron-like jobs that need an absolute
timer.  The API supports waking up with an UEV_HUP callback event if the
wall clock changed due to NTP or user input while waiting.

- uev_cron_init()
- uev_cron_set()
- uev_cron_start()
- uev_cron_stop()

A very simple cronrun example/test program is also included.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-10-30 15:08:34 +01:00
Joachim Nilsson 52d8d7109b uev_timer_set(): Check return value of timerfd API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-10-30 14:49:49 +01:00
Joachim Nilsson 7e38c11b87 Signal handlers require signo to be public
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-10-30 11:20:36 +01:00
Joachim Nilsson e5bc49a8e6 Refactor: move private watcher members to a union
This will save a few bytes of stack/heap for users.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-10-30 10:58:29 +01:00
Joachim Nilsson 1f6fa93314 Bump version for v1.4.3 dev
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-22 01:38:52 +02:00
Joachim Nilsson fe9b7264d0 Remove pointer casting, not needed and forbidden in ISO C
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-08-22 01:37:46 +02:00
Joachim Nilsson 6447c74722 Update .gitignore
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-07-01 01:00:20 +02:00
Joachim Nilsson f2148d41a4 Add sudo to install and ldconfig
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-30 18:54:23 +02:00
Joachim Nilsson 0cb4416280 Add ldconfig to build instructions, thanks rooth!
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-30 18:52:56 +02:00
Joachim Nilsson 4255ebb9c6 Update ChangeLog and bump version for v1.4.2 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-25 11:38:26 +02:00
Joachim Nilsson 930cce21fa Add reference to Mark Heily's libkqueue
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-06-22 14:49:54 +02:00
Joachim Nilsson addd60db22 Merge pull request #8 from westermo/master
Check timer period and timeout arguments
2016-06-17 13:51:02 +02:00
Joachim Nilsson e3d40ef010 Check timer period and timeout arguments
The uev_timer_init() and uev_timer_set() functions allow timeout and
period to be set < 0, which is not supported.  This patch adds a very
simple range check.

Signed-off-by: Joachim Nilsson <joachim.nilsson@westermo.se>
2016-06-17 13:45:11 +02:00
Joachim Nilsson 618b50ee88 Play around a bit more with the intro.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-25 22:17:47 +02:00
Joachim Nilsson f64301194a Move rant to end of README and simplify intro
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-25 20:23:03 +02:00
Joachim Nilsson bdbfe809ab Start planning for uEv 1.5 -- support Lua bindings
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-23 15:01:19 +02:00
Joachim Nilsson 2742adfe98 Add missing Markdown link to v1.4.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-22 14:57:16 +02:00
Joachim Nilsson 35d3a0f185 Update ChangeLog for v1.4.1 bug fix release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-22 14:54:53 +02:00
Joachim Nilsson 441d53e46e Bump ABI revision, v1.0.1, slight semantic change (CLOEXEC)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-22 14:53:29 +02:00
Joachim Nilsson 0b19880f80 Bump version to v1.4.1 for CLOEXEC fix
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-22 14:37:48 +02:00
Joachim Nilsson 852bd506e8 Fix Markdown syntax in older releases
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-22 14:34:46 +02:00
Joachim Nilsson 98c8e9f8b1 Fix leaking descriptors, use CLOEXEC
The signal and timer fd's used in libuEv were not created properly with
the CLOEXEC flag.  Hence, all forked off children calling exec*() could
access all file descriptor, sockets, etc., used by the parent process.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-22 14:31:51 +02:00
Joachim Nilsson 9fc5227343 Update copyright years
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-22 14:30:56 +02:00
Joachim Nilsson e613248392 Minor style changes and whitespace fixes
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-22 14:30:07 +02:00
Joachim Nilsson ac5f01ea22 Remove Emacs version-control handling
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-22 14:29:19 +02:00
Joachim Nilsson a0ddb4eae3 Simplify configure script
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-17 00:42:43 +02:00
Joachim Nilsson 1e7720735f Remove bogus autoconf version requirement
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-17 00:42:25 +02:00
Joachim Nilsson 8010d76133 Remove superfluous space
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-17 00:41:32 +02:00
Joachim Nilsson 73459ca609 Add some integration tips when using GNU autotools
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-04-17 00:27:54 +02:00
Joachim Nilsson bd6d519315 Update ChangeLog and bump version for v1.4.0 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-22 20:26:28 +01:00
Joachim Nilsson ece9788f5a Merge pull request #7 from villains/master
Add support for EPOLLPRI events for i/o watchers

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-16 16:28:36 +01:00
Markus Svilans a9a78e8bf0 Add support for EPOLLPRI events for i/o watchers 2016-03-16 09:21:07 -04:00
Joachim Nilsson 71697266ed Simplify and clean up joystick example
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-13 22:36:46 +01:00
Joachim Nilsson d5d6f9d844 README: Explicitly link to Summary section about caveat on signalfd
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-13 02:22:14 +01:00
Joachim Nilsson 57a7bbe1b0 README: Minor polish and clarifications
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-13 02:14:58 +01:00
Joachim Nilsson acb6d82ccf Minor update, mention when namespacing was silently introduced
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-13 01:55:59 +01:00
Joachim Nilsson 7de9dafacc Bump version to v1.4.0-dev and update ChangeLog
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-13 01:32:07 +01:00
Joachim Nilsson 2a1561b449 Update README with new header namespace and some basic housekeeping
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-13 01:30:36 +01:00
Joachim Nilsson 7704e46b48 Change namespace for header files libuev/uev.h --> uev/uev.h
The `libuev/` namespace for header files was introduced in v1.2.1.  This
patch changes it to the less surprising `uev/`.  Hopefully there aren't
that many users of this library out there yet, apart from myself.

Apologies if you are affected!

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-13 01:20:40 +01:00
Joachim Nilsson 8e3225999c Add pkg-config support
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-03-13 01:10:38 +01:00
Joachim Nilsson f856f299e4 Update ChangeLog and bump version for v1.3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-02 09:41:10 +01:00
Joachim Nilsson 7012ab4779 Update ChangeLog in preparation for v1.3.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-02 08:21:02 +01:00
Joachim Nilsson 23a7773f5c Makefile.am: Update to install and distribute README.md, LICENSE, etc.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-02 08:15:31 +01:00
Joachim Nilsson 232e476429 Remove confusing symlinks, it's a brave Markdown world
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-02-02 08:11:47 +01:00
Joachim Nilsson 33ad2f5e87 Bump version for 1.3.1-dev cycle
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-22 01:00:11 +01:00
Joachim Nilsson ae52fbdf2e Update ChangeLog with GIT diff links and fix minor Markdown formatting
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-22 00:59:33 +01:00
Joachim Nilsson b3541da7cb Update ChangeLog and bump version for v1.3.0 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-22 00:54:06 +01:00
Joachim Nilsson 315bd4558a Use pkginclude_HEADERS for libuEv include files
Fix include file namespace lost in initial conversion to GNU autotools,
use $(prefix)/include/$(PACKAGE)/*.h

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-22 00:52:13 +01:00
Joachim Nilsson a0bed33445 Drop -Werror from default CFLAGS, for maintainer(s) only
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-22 00:31:12 +01:00
Joachim Nilsson bf127b0dab Updates and cleanup of README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-22 00:24:32 +01:00
Joachim Nilsson 7d6d90cdb1 Remove AM_MAINTAINER_MODE from configure script, not recommended anymore
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-21 22:31:58 +01:00
Joachim Nilsson 78b7d0962a Make sure to distribute examples as well, even if --disable-examples
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-15 00:58:05 +01:00
Joachim Nilsson 1370275e61 Run clean pass first, then parallel build test
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-14 21:45:16 +01:00
Joachim Nilsson d01cd60dae Update .gitignore to mask out all generated files from GNU build system
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-14 21:41:14 +01:00
Joachim Nilsson fb942fcea9 Makefile.am: Prune unused libuev_la_LIBADD
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-14 21:38:32 +01:00
Joachim Nilsson f49232cce0 README: Further updates after conversion to GNU configure and build
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-12 21:40:30 +01:00
Joachim Nilsson 6933f7f7c2 README: Small update about the new GNU configure and build system
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-11 17:30:38 +01:00
Joachim Nilsson cd522704c7 Create empty m4 directory for the benefit of autogen.sh and Travis-CI
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-10 19:00:16 +01:00
Joachim Nilsson a0ebd468b6 Debug Travis-CI, fails to run autogen.sh
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-10 18:57:58 +01:00
Joachim Nilsson b621062719 Fix Travis-CI build script
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-10 18:51:07 +01:00
Joachim Nilsson f3e12a98a0 Update build script for Travis-CI with GNU configure and build system
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-10 18:49:30 +01:00
Joachim Nilsson acf6bbcbaf Initial conversion to the GNU configure and build system
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-10 18:21:52 +01:00
Joachim Nilsson a8604938e9 Remove default -Os optimization and add -Wextra
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-08 21:06:20 +01:00
Joachim Nilsson cbb7455603 Correct Markdown syntax to version links
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-01-05 12:58:55 +01:00
Joachim Nilsson cf5e729dd3 Update ChangeLog and bump version for v1.2.4 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-11-23 00:41:33 +01:00
Joachim Nilsson 352baa8270 Make sure we don't inherit LDFLAGS from environment.
To pass custom LDFLAGS to libite, use EXTRA_LDFLAGS.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-10-22 18:36:37 +02:00
Joachim Nilsson 8721566d74 Do not allow VERSION to be overloaded by build system.
When integrating libuEv as a component in another piece of software,
that build system may set VERSION as an environment variable.  We do
not want that version to be set as the version of libuEv.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-10-03 09:14:31 +02:00
Joachim Nilsson a8d8705c34 Bump version for v1.2.3 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-17 11:13:12 +02:00
Joachim Nilsson 4c03342c77 ChangeLog: Fix minor Markdown syntax error.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-17 11:11:33 +02:00
Joachim Nilsson c1fe3be3e7 README: Further simplifications.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-17 11:09:41 +02:00
Joachim Nilsson 861bf14d71 Update ChangeLog for v1.2.3 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-17 10:58:15 +02:00
Joachim Nilsson 395d2df573 README: Add more sub-sections to API chapter, readability.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-17 10:21:11 +02:00
Joachim Nilsson 2132ab1bd5 Let make test also run the test, like in the automake world.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-16 22:55:15 +02:00
Joachim Nilsson d62ae8f041 Rename CHANGELOG.md --> ChangeLog.md
- Rename to more common ChangeLog
- Drop TODO from distribution, only for devs

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-16 18:07:40 +02:00
Joachim Nilsson 1b1397b4ee Bump version to 1.2.3-beta1, prepare for upcoming v1.2.3 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-16 18:06:10 +02:00
Joachim Nilsson 7bf5c8d8d7 Let Travis run some tests as well.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-16 18:05:31 +02:00
Joachim Nilsson 1374661d4b README: Fix syntax error in example.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-16 18:05:04 +02:00
Joachim Nilsson 694e2b5ab9 Silence annoying warnings from GNU ar
In (very) modern versions of binutils, for instance v2.25.1 in Ubuntu
15.10, complain about VERY common `crus` argument.  This patch attempts
to silence this noise:

    $ make -j5
      CC      uev.o
      CC      io.o
      CC      timer.o
      CC      signal.o
      ARCHIVE libuev.a
      LINK    libuev.so.1
    ar: `u' modifier ignored since `D' is the default (see `U')

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-16 18:04:06 +02:00
Joachim Nilsson c2751af452 README: Further fixes to missing Markdown links.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-13 01:10:02 +02:00
Joachim Nilsson 1ce9fde395 README: Fix Markdown for code blocks.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-13 00:51:49 +02:00
Joachim Nilsson 7e9f8c4913 README: Fix Travis-CI and Coverity Markdown links.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-13 00:49:31 +02:00
Joachim Nilsson dd09c7c3a9 README: More Markdown syntax fixes.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-13 00:47:49 +02:00
Joachim Nilsson fe04723353 README: Markdown syntax (non-GitHub) fixes.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-09-13 00:46:20 +02:00
Joachim Nilsson 78995951ae Update ChangeLog and bump version for v1.2.2 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-08-30 18:19:22 +02:00
Joachim Nilsson 3adc7a03c0 Fix issue #6: Segfault when stopping timer before calling uev_exit()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-08-30 18:02:47 +02:00
Joachim Nilsson 2abeb39fe8 Use dockerized build, quicker and less resource intensive for Travis-CI
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-08-22 16:06:21 +02:00
Joachim Nilsson 1480976724 Fix data ordering bug causing problems on Debian Jessie
Got very odd results on Debian Jessie when having the uev_private_t last
in uev_t.  Turns out that when uev.c set "w->active = 0;" the w->fd data
member was written to instead.

Placing the private data first in uev_t is better practise, generally a
good thing for linked lists built on the BSD sys/queue.h API, looks a
lot better, but foremost works.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-07-19 04:40:30 +02:00
Joachim Nilsson 6ffa76c2f8 Remove STATIC ifdef, always build .a and .so files
It is non-trivial to set a separate environment variable (STATIC=1) when
descending into the libuEv subdirectory from an autoconf/automake based
project.

It is better to instead simplify the build process and always build both
.a and .so files.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-07-12 23:13:04 +02:00
Joachim Nilsson 8a4e8c4af6 ChangeLog: Fix cut-and-paste error in Markdown link.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-07-02 13:21:21 +02:00
Joachim Nilsson 950015715d Update ChangeLog and bump version for v1.2.1 release!
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-07-02 13:19:32 +02:00
Joachim Nilsson fb6ac1ecac Also install queue.h, dito needed by external plugins.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-18 08:43:20 +02:00
Joachim Nilsson 0909decabc Make sure to also install new private.h, unfortunately necessary.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-18 08:37:37 +02:00
Joachim Nilsson 3452fbd381 Default to -fPIC, bloats archives slightly, but works for everyone.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-18 02:24:14 +02:00
Joachim Nilsson f64561b708 Cleanup: fix install/uninstall of both static and dynamic builds.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-18 02:14:15 +02:00
Joachim Nilsson b94023faed Reflow paragraphs to match original line breaks, no change in licensing terms!
While comparing the library layout with libite, I noticed how the
formatting of the license paragraphs in all source files had been
garbled.  This patch restores the orginal line breaks.

Note: Only whitespace changes, no change in licensing terms!

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-18 01:36:48 +02:00
Joachim Nilsson 6637804b80 Merge pull request #5 from vonj/master
O_CLOEXEC does not exist in Debian 6 GLIBC/EGLIBC, use EPOLL_CLOEXEC instead.
2015-06-13 19:42:27 +02:00
jakob 361b013da5 O_CLOEXEC does not exist in Debian 6, and apparently EPOLL_CLOEXEC is the correct to use anyway. 2015-06-13 19:32:34 +02:00
Joachim Nilsson 052b08e0b5 Followup to fix indentation, leading tabs, then spaces.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-09 15:51:07 +02:00
Joachim Nilsson ca803bb12f Move private data members and methods from uev.h to private.h
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-06-09 15:46:19 +02:00
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
Joachim Nilsson e316226ef1 Update ChangeLog and bump version for v1.0.5 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-15 18:30:30 +01:00
Joachim Nilsson 2f8c45f438 Fix TODO: Add uev_*_start() functions.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-15 18:13:36 +01:00
Joachim Nilsson bfcea55b1e Add slightly odd signal testing + fork() example
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-15 18:13:26 +01:00
Joachim Nilsson 6e7234fa4d Fix issue #1: Use local queue.h from OpenBSD, may be missing on target OS
Use OpenBSD version of queue.h since the C-library may not have it (at
all), or may come with an older (buggy) version.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-15 18:13:18 +01:00
Joachim Nilsson 51a802716d Rename main.c --> uev.c
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-15 18:13:14 +01:00
Joachim Nilsson 5ecc6d64b4 Move test.c and joystick.c to examples/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-15 18:13:09 +01:00
Joachim Nilsson d1b9fd17f3 Proposal to add uev_*_start(), for symmetry and finit ..
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-10 07:58:23 +01:00
Joachim Nilsson 3192923687 Add another notice to the API section, again highlighting signalfd caveats!
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-04 20:36:32 +01:00
Joachim Nilsson 83822a664e Fix cut-and-paste error in git tag links.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-02-01 09:00:39 +01:00
Joachim Nilsson 8e40133c23 Add CHANGELOG and TODO to Makefile and symlink to .md CHANGELOG
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-30 23:43:32 +01:00
Joachim Nilsson b55702a97d Simplify autodep handling for Make, thank you GCC.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-30 22:49:37 +01:00
Joachim Nilsson e6a27743a4 Change Coverity Scan branch to use 'dev' instead of master.
We don't want to run out of scan tokens for every README update.  This
also means the introduction of a new workflow.  All new development must
be staged to the dev branch before being merged to master.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-30 22:44:25 +01:00
Joachim Nilsson e797247308 New practice, up version to VER-dev after each release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-30 22:37:49 +01:00
Joachim Nilsson 7eb3d56c83 Add basic TODO.md, currently wishing for Santa to bring kqueue support
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-30 22:35:29 +01:00
Joachim Nilsson 9027249b69 Add CHANGELOG.md in http://keepachangelog.com format
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-30 22:34:37 +01:00
Joachim Nilsson e5c4bb9e4b Actually bump release to v1.0.4
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-30 22:34:37 +01:00
Joachim Nilsson 006eed0033 Improve Markdown linking to make the text version more readable.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-25 13:10:24 +01:00
Joachim Nilsson 0e3d6ebb1d Minor improvement in how build is echoed to screen.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-24 23:51:24 +01:00
Joachim Nilsson 66194a8645 Update Introduction with cautionary warning about signalfd.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-24 14:27:32 +01:00
Joachim Nilsson 0f18e4fcc0 Minor update.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-22 01:22:56 +01:00
Joachim Nilsson 96a497b03e Simplify test.c example, no need to stop timer before exit.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-11 14:05:41 +01:00
Joachim Nilsson 2a6697d514 Add ToC and some kbd markup
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-11 14:04:48 +01:00
Joachim Nilsson 5c7a6f1163 Bump version for v1.0.3 release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 02:14:17 +01:00
Joachim Nilsson 618480140c Update copyright years and reflow LICENSE file.
Update copyright years of all sources.

Reflow/reformat paragraphs in MIT license to match Finit,
i.e., no content changes only whitespace/linebreak changes.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 02:11:09 +01:00
Joachim Nilsson 55f92d25ba Change from Sparse to Clang's scan-build for make check target
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 02:03:20 +01:00
Joachim Nilsson 82d0b0dcb8 Add Coverity Scan integration.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 01:42:38 +01:00
Joachim Nilsson b0aec77056 Add Travis CI integration.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 01:36:45 +01:00
Joachim Nilsson 64f32c9086 Fix make dist build target and bump version again.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 01:30:30 +01:00
Joachim Nilsson 5065440f52 Minor change libuev --> libuEv and bump version for release.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-07 01:18:07 +01:00
Joachim Nilsson 8e1f384c38 README: Minor cleanup, after comments by @vonj
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2015-01-06 22:50:09 +01:00
Joachim Nilsson 3094ef2887 README.md: Last update, possibly. Mostly tweaks and cleanup.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-31 02:19:20 +01:00
Joachim Nilsson e50f8aee94 Yet another API section update.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:22:05 +01:00
Joachim Nilsson 0bf22de8e0 More API section updates.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:13:34 +01:00
Joachim Nilsson 78d140f310 Cleanup API.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:08:38 +01:00
Joachim Nilsson e681657ca7 Syntax highlight API on GitHub
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:04:02 +01:00
Joachim Nilsson e707d24290 Update README on the uev_run() flags parameter and slight fixes.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 21:02:45 +01:00
Joachim Nilsson 7f10528d80 main.c:uev_run() - Document mysterious flags parameter.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 20:49:08 +01:00
Joachim Nilsson d352e176dc Minor spell check fixes and additions.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 19:53:56 +01:00
Joachim Nilsson 93d9d06802 First cleanup of README after comments by @vonj, more to come ...
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 19:36:18 +01:00
Joachim Nilsson 23935c248a Add joystick reading example, for documentation.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-30 18:07:22 +01:00
Joachim Nilsson 59afe57995 Minor update to README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-28 21:54:40 +01:00
Joachim Nilsson 1dfdc875d7 Update README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2014-12-28 21:36:01 +01:00
Joachim Nilsson 3d93de3806 Update REAMDE 2013-08-06 14:12:54 +02:00
Joachim Nilsson 70881326d0 Minor fixes to bench.c when running many descriptors.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:54:09 +02:00
Joachim Nilsson 1b34da7a1a Update REAMDE 2013-08-06 02:15:41 +02:00
Joachim Nilsson 0de9cb6321 Adapt bench.c (from libev) to libuev, no libev/libevent API wrappers available
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:04:41 +02:00
Joachim Nilsson d9f078a119 Reindent bench.c to Linux coding style
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:04:41 +02:00
Joachim Nilsson f7e93f9d43 Import Niels Provos bench.c, this version from http://libev.schmorp.de/bench.c
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:03:33 +02:00
Joachim Nilsson 82a09196f0 Add support for make check using Sparse
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:00:24 +02:00
Joachim Nilsson 66c5606e34 Add support for UEV_ONCE and UEV_NONBLOCK run flags to uev_run()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:00:24 +02:00
Joachim Nilsson d6e0146803 Major refactor: API change, code cleanup, remove calloc()/free()
- Rename all _create() functions --> _init()
- Rename all _delete() functions --> _stop()
- Watchers are automatically started, except
- Timer watchers are automatically started by uev_run()
- Allow I/O watchers to watch either EV_READ/EV_WRITE or both events
- Extend callbacks with a received events field
- Remove calloc()/free(), let user handle this
- Make event has-a context when initializing it => simplifies API
- Internal uev_watcher_*() API mostly handles epoll stop/start

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:00:24 +02:00
Joachim Nilsson c69ce87eb8 Rename uev_t --> uev_ctx_t and uev_watcher_t --> uev_t
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-08-06 02:00:24 +02:00
Joachim Nilsson 689b407c8f Update .gitignore, improved filtering *.o, .*.d, *~ 2013-08-06 02:00:24 +02:00
Joachim Nilsson baa02de05f Makefile: Add dist target. 2013-07-23 13:28:49 +02:00
Joachim Nilsson 1319f2ba5a Add support for signals using signalfd(2)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 03:19:13 +02:00
Joachim Nilsson b1de765841 Further cleanup, rename uev_io_t --> uev_watcher_t
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 02:25:51 +02:00
Joachim Nilsson 85da186489 Simplify epoll handling slightly
No need to allocate the epoll events array since it's static
in size anyway.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 02:09:13 +02:00
Joachim Nilsson 9b5be13e62 Split libuev.c into smaller files to help linker
Split libuev.c into one file per watcher type, mostly to give a better
overview and ease of maintenance, but also to help the linker.

Unfortunately this steals a few bytes when linking a full blown app, but
for apps that, e.g., only need I/O watchers and no timers the resulting
binary will be smaller.  This applies to static binaries that are more
common in embedded systems.

$ size libuev.a libuev.so.1
   text	   data	    bss	    dec	    hex	filename
    999	      0	      0	    999	    3e7	main.o (ex libuev.a)
     90	      0	      0	     90	     5a	io.o (ex libuev.a)
    490	      0	      0	    490	    1ea	timer.o (ex libuev.a)
   4380	    696	      8	   5084	   13dc	libuev.so.1

Also, move uev.h back to top from include/libuev/uev.h since a separate
namespace is not needed after the removal of the OpenBSD queue.h copy.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 00:54:52 +02:00
Joachim Nilsson 069294f28a Further reduce size and remove queue.h, we can use GLIBC's old queue.h now
- Cleanup code
- Further polish on total size using black magic
- Remove OpenBSD queue.h, code simplied so much now it's not needed anymore.
  Instead we can rely on the standard GNU/Linux sys/queue.h (old BSD copy)

$ size libuev.a libuev.so.1
   text	   data	    bss	    dec	    hex	filename
   1520	      0	      0	   1520	    5f0	libuev.o (ex libuev.a)
   4194	    680	      8	   4882	   1312	libuev.so.1

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-23 00:07:57 +02:00
Joachim Nilsson 6ed1352404 Optimize for size a bit
- Remove double book keeping, the kernel does all the heavy lifting now
- Trim the amount of #includes
- Use LIST_ instead of TAILQ_ now that we no longer handle timers ourselves

Original LibUEvent

$ size libuevent.a libuevent.so.1
   text	   data	    bss	    dec	    hex	filename
   2610	      0	      8	   2618	    a3a	libuevent.o (ex libuevent.a)
   5266	    648	     16	   5930	   172a	libuevent.so.1

Refactored libuev:

$ size libuev.a libuev.so.1
   text	   data	    bss	    dec	    hex	filename
   1557	      0	      0	   1557	    615	libuev.o (ex libuev.a)
   4186	    672	      8	   4866	   1302	libuev.so.1

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-22 23:07:15 +02:00
Joachim Nilsson ed22e88c81 Update README 2013-07-22 20:45:10 +02:00
Joachim Nilsson bccd32a7ee Update with info on original LibUEvent author vs current libuev maintainer. 2013-07-22 20:35:05 +02:00
Joachim Nilsson ce5f29db50 Refer to Wikipedia for information about the MIT license. 2013-07-22 20:33:23 +02:00
Joachim Nilsson 499cbd2032 Refactor to use modern timerfd and epoll
This is a major refactor of the original design by Flemming Madsen.  It
converts the library timers from poll() to timerfd, which at the same
time also unifies the file and timer watchers into one design.  The idea
is to also add signalfd to the mix later on.

Also a couple of hundred kilobytes smaller, although size a secondary
goal.  The primary goal is to further simplify the internal design and
to continuously strive to employ modern APIs.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-22 20:25:08 +02:00
Joachim Nilsson 30f6eccecd Update .gitignore 2013-07-22 20:25:05 +02:00
Joachim Nilsson 49c72c0914 Create unique include file namespace and fix install/uninstall make targets
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 13:08:40 +02:00
Joachim Nilsson f590237403 Update README 2013-07-21 11:55:07 +02:00
Joachim Nilsson ec9351bfdd Add support for peridic timers and resetting timers
Refactor uev_timer_create() to support a periodic (msec) argument. For
such timers libuev automaticlly respawns them.  Effectively resetting
the timeout using the period value.

Also, refactor argument order to uev_timer_create() and uev_io_create().

Rename UEV_FHIN, UEV_FHOUT to UEV_DIR_INBOUND and UEV_DIR_OUTBOUND.

Remove unnecessary return value for watcher callbacks.  What would those
have been usefu for, ever?  Not even the original code checked the return
value after calling a handler.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 11:39:26 +02:00
Joachim Nilsson 97398e0a7d Fix Makefile dependency handling and add install/uninstall targets
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 10:20:19 +02:00
Joachim Nilsson e1b1daf5e3 Simplify linked list of I/O watchers and its garbage collect mechanism.
Simplifed uev_io_t by removing uev_io_rem_t completely.  A refactor that
would not have been possible without the BSD queue.h API.  This makes
the code readable and thus maintainable and less error prone.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 08:31:33 +02:00
Joachim Nilsson ece4e31c52 Convert from homebrew linked list to BSD TAILQ using OpenBSD r1.38 sys/queue.h
- OpenBSD http://www.openbsd.org/cgi-bin/cvsweb/src/sys/sys/queue.h rev 1.38
- Convert lList*() to TAILQ_*()
- Minor local variable rename to clarify usage

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-21 07:53:25 +02:00
Joachim Nilsson ab8c70d501 Slight reorder and renaming to clarify, also update README. 2013-07-20 22:48:52 +02:00
Joachim Nilsson 7e87644bd1 Major cleanup, use Linux coding style, remove CamelCase.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-19 12:41:59 +02:00
Joachim Nilsson 0bba080660 Initial import of Flemming Madsen's LibUEvent
This fork is called libuev and will focus solely on embedded Linux
systems using C.  Patches for other UNIX-like operating systems are
welcome.

- Remove Lua support, will not be used.
- Rename from LibUEvent to libuev where applicable
- Add project's MIT license to top of all files, as is customary
- Add a LICENSE file, as is customary

This MIT licensed code was cloned from the Mercurial repository at
Google http://code.google.com/p/libuevent/ which had mislabled the
license as "New BSD License", which is wrong.  The actual license
of the project is https://en.wikipedia.org/wiki/MIT_License

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2013-07-18 23:37:07 +02:00
35 changed files with 3325 additions and 1393 deletions

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
*~
*.d
*.o
*.so
*.so.*
*.a
*.la
*.lo
*.html
.deps
.libs
compile
config.*
configure
depcomp
install-sh
libtool
ltmain.sh
missing
stamp-h1
Makefile
Makefile.in
aclocal.m4
ar-lib
autom4te.cache
bench
libuev*.tar.*
libuev.pc
!libuev.pc.in
+30
View File
@@ -0,0 +1,30 @@
# Travis CI integration -- https://travis-ci.org/troglobit/libuev
# Defaults to GNU GCC and autotools: ./configure && make && make test
language: c
# We don't need to install packages, use dockerized build, quicker
sudo: false
env:
global:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- secure: "ikQcWMCLFoxq0+wMiqrNT5HRn0d4e3AxuF8VzNUNFrk6jUvHIufC/OKvr8VWPTjCaAQXZjWfdQAAARUXzJV10WZ7xpiv5qt4bt26Qih2AwMPMJeUOYrqXgQqhTPBp05iLYpo4ybICteXRI3zKKUA1wkdDC0jyu3rl+Fbveekbe0="
addons:
coverity_scan:
project:
name: "troglobit/libuev"
description: "libuEv | Simple event loop for Linux"
notification_email: troglobit@gmail.com
build_command_prepend: "./autogen && ./configure --enable-examples"
build_command: "make clean all"
branch_pattern: dev
# We don't use configure, so we must define our own build script.
script:
- ./autogen.sh
- ./configure --disable-silent-rules --enable-examples
- make clean
- make -j5
- make test
+5
View File
@@ -0,0 +1,5 @@
Current maintainer of libuEv:
Joachim Nilsson <troglobit()gmail!com>
Original LibUEvent author:
Flemming Madsen <flemming!madsen()madsensoft!dk>
+288
View File
@@ -0,0 +1,288 @@
Change Log
==========
All notable changes to the project are documented in this file.
[v1.5.0][] - 2016-10-30
-----------------------
### Changes
- Add support for absolute timers with the `uev_cron_*()` API.
- Update build & install instructions in README
### Fixes
- Fix `uev_timer_set()` so that it returns error in case the underlying
Linux timerfd API fails.
[v1.4.2][] - 2016-06-25
-----------------------
### Changes
- Add range checks for `period` and `timeout` to `uev_timer_init()` and
`uev_timer_set()`. If either on is `< 0` libuEv now return `ERANGE`.
[v1.4.1][] - 2016-04-22
-----------------------
### Changes
- Add some `pkg-config` integration tips for developers using libuEv
with GNU autotools in their projects.
### Fixes
- Add missing `SFD_CLOEXEC` and `TFD_CLOEXEC` to signal and timer file
descriptors. This prevents these file descriptors from "leaking" into
sub-processes of the parent thread. The kernel will atomically close
these descriptors for forked-off children calling the exec*() family
of syscalls.
- Minor coding style fixes and simplification of build scripts.
[v1.4.0][] - 2016-03-22
-----------------------
This release changes the header file namespace, which was silently
introduced in [v1.2.1][]. Apologies for any problems this may cause!
### Changes
- Change namespace for header files: `libuev/` to `uev/`, e.g. using
`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
[v1.3.1][] - 2016-02-02
-----------------------
### Fixes
- Remove symlinks to Markdown files from GIT
- Distribute and install Markdown files: README.md, etc.
[v1.3.0][] - 2016-01-22
-----------------------
### Changes
- Change to GNU configure and build system
- Default optimization is now `-O2`, not `-Os`, which can be a bit
unpredictable with some cross compiler toolchains. `-O2` is the
tried and tested path, and default in autotools
- Added `-Wextra` to the previous `-W -Wall`, drop `-Werror`
which is supposed to be used by maintainer(s) only.
[v1.2.4][] - 2015-11-23
-----------------------
### Fixes
- Do not allow `VERSION` to be overloaded by build system.
- Make sure we don't inherit `LDFLAGS` from environment.
[v1.2.3][] - 2015-09-17
-----------------------
Very minor release.
### Changes
- [README.md][] updates, mostly cleanup and readability improvements
- `make test` now runs the test case, on behalf of Travis-CI
- Renamed CHANGELOG.md to [ChangeLog.md][]
- Dropped [TODO.md][] from distribution archives, only for devs
### Fixes
- Lots of Markdown syntax fixes in both README and ChangeLog
- Silence annoying warning from newer GNU ar in Ubuntu 15.10
[v1.2.2][] - 2015-08-30
-----------------------
Minor bugfix release.
### Changes
- The `ifdef STATIC` in the Makefile has been removed. Now both the
.a and .so libraries are built. The user may then select what to
install. This is a change for convenience when using libuEv from
a GNU Configure & Build based project.
### Fixes
- Fix odd data ordering issue on Debian Jessie in new `uev_private_t`
- Fix issue #6: Segfault when stopping timer before calling `uev_exit()`
[v1.2.1][] - 2015-07-02
-----------------------
### Changes
- Private data members in `uev.h` have now been moved to a new file
called `private.h`. This will hopefully make it easier to understand
what a user of libuEv is allowed to play around with. Thanks to @vonj
for the discussions around this!
- All builds of libuEv now default to use `-fPIC`, this bloats the code
slightly, but ensures that linking works for all use cases, withouth
introducing unnecessary complexity.
### Fixes
- Fix install/uninstall Makefile recipes so they work for both static
and dynamic builds. Also, make sure to install all required headers.
- Jakob Eriksson noticed that `O_CLOEXEC` does not exist in the Debian 6
EGLIBC, but `EPOLL_CLOEXEC` does, and is also what `epoll_create1()`
should use. Thank you @vonj!
[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
- Add `uev_*_start()` functions.
- Add slightly odd `examples/signal.c` example that utilises `fork()`
and causes segfault in child.
- Renamed `main.c` to `uev.c`
- Move examples to `examples/` subdirectory
- Simplify automatic dependency calculation
- Add [TODO.md][] for wishlist items
- Add [CHANGELOG.md][ChangeLog.md], to align with http://keepachangelog.com
- Further updates to [README.md][]
[v1.0.4][] - 2015-01-24
-----------------------
Minor documentation and build fixes.
Release mainly targeted for [Finit development](https://github.com/troglobit/finit/).
### Changes
* `test.c` has been simplified/clarified
* Further updates to [README.md][]
* Minor update to Makefile, change how build progress is echoed
[v1.0.3][] - 2015-01-24
-----------------------
This is a very minor release, with a strong focus on documentation.
### Changes
- [README.md][], massively updated
- API documentation, updated
- Travis CI integration added, https://travis-ci.org/troglobit/libuev
- Coverity Scan integration, https://scan.coverity.com/projects/3846
- Clang scan-build support added
[v1.0.2][] - 2015-01-07
-----------------------
### Fixes
- Fix broken `make dist` target in [v1.0.1][].
[v1.0.1][] - 2015-01-07
-----------------------
### Changes
- Cleanup and rewrite of [README.md][] after first audit by @vonj
- Added API and example section to [README.md][]
- New [joystick.c][] example added
- `main.c:uev_run()`: Document mysterious flags parameter
[v1.0.0][] - 2013-08-06
-----------------------
First release in the new guise and API.
### Changes
- Add support for timers, using `timerfd_*()` and `epoll()` API's
- Add support for signals using `signalfd()` API
- Convert to use BSD `sys/queue.h` API in GLIBC instead of homebrew
linked list implementation
- Massive changes to code structure, API naming
- Reindent to use Linux KNF
- Change library name to libuev
- Lua support removed
- Make sure to state the [MIT license][] correctly in all files
- Import [Niels Provos][]' `bench.c`, the [libev version][]
v0.0.1 - 2012-03-17
-------------------
[Initial announcement][] of [libuevent][] by [Flemming Madsen][] to the
Lua users mailing list.
[UNRELEASED]: https://github.com/troglobit/libuev/compare/v1.4.2...HEAD
[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
[v1.3.1]: https://github.com/troglobit/libuev/compare/v1.3.0...v1.3.1
[v1.3.0]: https://github.com/troglobit/libuev/compare/v1.2.3...v1.3.0
[v1.2.3]: https://github.com/troglobit/libuev/compare/v1.2.2...v1.2.3
[v1.2.2]: https://github.com/troglobit/libuev/compare/v1.2.1...v1.2.2
[v1.2.1]: https://github.com/troglobit/libuev/compare/v1.2.0...v1.2.1
[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.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.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
[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
[Niels Provos]: http://en.wikipedia.org/wiki/Niels_Provos
[MIT license]: http://en.wikipedia.org/wiki/MIT_License
[libev version]: http://libev.schmorp.de/bench.c
[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:
-->
+20
View File
@@ -0,0 +1,20 @@
Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-47
View File
@@ -1,47 +0,0 @@
##
# @file Makefile
# Make the micro eventlib, lua bindings and tests
#
# $Id$
#
# (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
##
CROSS_COMPILE=
#CROSS_COMPILE=arm-linux-uclibc-
CC=$(CROSS_COMPILE)gcc
AR=$(CROSS_COMPILE)ar
INCLUDE += -I /usr/include/lua5.1/
LDFLAGS=-g
CFLAGS=-fPIC -g -Os $(INCLUDE)
MAJOR = 1
all: libuevent.a libuevent.so.$(MAJOR)
lua: luevent.so
libuevent.a: libuevent.o
$(AR) r $@ $^
libuevent.so.$(MAJOR): libuevent.o
$(CC) $(LDFLAGS) -shared -o $@ $^ -Wl,-soname=libuevent
luevent.so: luevent.o all
$(CC) $(LDFLAGS) -shared -o $@ luevent.o -L . -l uevent -Wl,-soname=luevent
stripped: all lua
$(CROSS_COMPILE)strip --strip-unneeded libuevent.a libuevent.so.$(MAJOR) luevent.so
test: all
$(CC) -g -o test -D NO_DEBUG test.c libuevent.c && ./test
luatest: lua
lua test.lua
clean:
rm -f libuevent.o luevent.o libuevent.so.$(MAJOR) luevent.so test
.PHONY: all test clean luatest lua
+69
View File
@@ -0,0 +1,69 @@
ACLOCAL_AMFLAGS = -I m4
lib_LTLIBRARIES = libuev.la
pkgincludedir = $(includedir)/uev
pkginclude_HEADERS = uev.h private.h queue.h
libuev_la_SOURCES = uev.c io.c timer.c signal.c cron.c
check_PROGRAMS = bench
bench_CPPFLAGS = -D_GNU_SOURCE
bench_LDADD = $(top_srcdir)/libuev.la
libuev_la_CPPFLAGS = -D_GNU_SOURCE
libuev_la_CFLAGS = -W -Wall -Wextra
libuev_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:2:0
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libuev.pc
doc_DATA = README.md LICENSE
EXTRA_DIST = README.md LICENSE AUTHORS ChangeLog.md
DIST_SUBDIRS = examples
SUBDIRS = .
if ENABLE_EXAMPLES
SUBDIRS += examples
endif
test joystick: all-recursive
./examples/$@
check-local:
@echo " BENCH $(PACKAGE)"
@./bench
if ENABLE_EXAMPLES
@echo " TEST $(PACKAGE)"
@./examples/test
endif
## Generate MD5 checksum file
MD5 = md5sum
md5-dist:
@for file in $(DIST_ARCHIVES); do \
$(MD5) $$file > $$file.md5; \
done
## Check if tagged in git
release-hook:
if [ ! `git tag | grep $(PACKAGE_VERSION)` ]; then \
echo; \
printf "\e[1m\e[41mCannot find release tag $(PACKAGE_VERSION)\e[0m\n"; \
printf "\e[1m\e[5mDo release anyway?\e[0m "; read yorn; \
if [ "$$yorn" != "y" -a "$$yorn" != "Y" ]; then \
printf "OK, aborting release.\n"; \
exit 1; \
fi; \
echo; \
else \
echo; \
printf "\e[1m\e[42mFound GIT release tag $(PACKAGE_VERSION)\e[0m\n"; \
printf "\e[1m\e[44m>>Remember to push tags!\e[0m\n"; \
echo; \
fi
## Target to run when building a release
release: dist 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' '; \
mv $$file* ../; \
done
-70
View File
@@ -1,70 +0,0 @@
LibUEvent - A Microscopic event library.
========================================
The libuevent library is a small event library in the style of the venerable
Xt(3) event loop.
Basically an event context is created,and handlers for timeounts and stream
events can be registered with this context.
After initial setup, the typical application will enter the event loop that
will only return at program termination.
The C API
---------
The typical application will do the following steps
1. Create the event context with lueCtxtCreate()
2. Register initial callbacks with lueAddInput, lueAddOutput and lueAddTimer
3. Enter the event loop
Lua binding
-----------
A lua binding is available that should mix well with luasocket and luaposix.
Time resolutions are in milli second integers in order to support integer-only
embedded environments
### function luevent.addFileIO(fdesc, handler, mode)
Add a handler on a stream. The handler will sustain itself GC wise.
Use *object*:clear() to unregister the handler
* fdesc The posix file descriptor "small integer".
Use the ":getfd()" method on a "normal" lua stream or socket to obtain fdesc.
* handler A function object. The handler object is supplied as the first parameter when called.
* mode Optional. Input ("r") or output ("w") event. Default is input
* return A new filehandler object
### function luevent.addTimer(msecs, handler)
Create a new timer object. Implicitly cleared when handler is called.
Typical usage:
local hdl
hdl = luevent.addTimer(50, function() hdl = nil ..... end)
......
if hdl then hdl:clear() hdl = nil end
Here *hdl* is used to keep track of whether the timer is active.
* msecs The number of milliseconds until the timer expires
* handler A function object. The handler object is supplied as the first parameter when called.
* return A new timer object
### function object:clear()
Cancel a callback registration. Renders the object ready for GC.
### function object:handler()
Obtain the handler function from a callback registration.
Building
--------
Use make:
* make all: The library
* make test: Test and showcase
* make lua: Build the lua binding
Installation
------------
Is purely manual, but would be something along the lines of:
sudo install -m 444 libuevent.so.1 /usr/lib/libuevent.so.1
sudo install -m 444 luevent.so /usr/lib/lua/5.1/luevent.so
<!-- vim: set syntax=mkd textwidth=79 nofoldenable: -->
+374
View File
@@ -0,0 +1,374 @@
libuEv | Simple event loop for Linux
====================================
[![Travis Status][]][Travis] [![Coverity Status][]][Coverity Scan]
* [Introduction](#introduction)
* [API](#api)
* [Create an Event Context](#create-an-event-context)
* [Register an Event Watcher](#register-an-event-watcher)
* [Start Event Loop](#start-event-loop)
* [Summary](#summary)
* [Using -luev](#using--luev)
* [Joystick Example](#joystick-example)
* [Build & Install](#build--install)
* [Background](#background)
* [Origin & References](#origin--references)
Introduction
------------
[libuEv][] is a simple event loop in the style of the more established
[libevent][1], [libev][2] and the venerable [Xt(3)][3] event loop. The
*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. Note however, a certain
amount of care is needed when dealing with APIs that employ signalfd.
For details, see [this article][4] at [lwn.net](http://lwn.net).
> “Event driven software improves concurrency” -- [Dave Zarzycki, Apple][]
API
---
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
signals. The [Summary](#summary) details a slight caveat on signals.
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
the wall clock changes, either via NTP or user input.
**NOTE:** On some systems, embedded systems in particular, `time_t` is a
32-bit integer that wraps around in the year 2038. [libuEv][] cannot
protect you against this problem, unfortunately.
```C
/*
* Callback example, arg comes from the watcher's *_init() function,
* w->fd holds the file descriptor or socket, and events is set by
* libuEv to indicate status: UEV_HUP, UEV_READ and/or UEV_WRITE
*/
void callback (uev_t *w, void *arg, int events);
/* Event loop: Notice the use of flags! */
int uev_init (uev_ctx_t *ctx);
int uev_exit (uev_ctx_t *ctx);
int uev_run (uev_ctx_t *ctx, int flags); /* UEV_NONE, UEV_ONCE, and/or UEV_NONBLOCK */
/* I/O watcher: fd is non-blocking, events is UEV_READ and/or UEV_WRITE */
int uev_io_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events);
int uev_io_set (uev_t *w, int fd, int events);
int uev_io_start (uev_t *w);
int uev_io_stop (uev_t *w);
/* Timer watcher: schedule a relative timer, timeout and period in milliseconds */
int uev_timer_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period);
int uev_timer_set (uev_t *w, int timeout, int period); /* Change timeout or period */
int uev_timer_start (uev_t *w); /* Restart a stopped timer */
int uev_timer_stop (uev_t *w); /* Stop a timer */
/* Cron watcher: schedule an absolute timer, when and period in time_t seconds */
int uev_cron_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, time_t when, time_t period);
int uev_cron_set (uev_t *w, int when, time_t period); /* Change when or period */
int uev_cron_start (uev_t *w); /* Restart a stopped cron */
int uev_cron_stop (uev_t *w); /* Stop a cron */
/* Signal watcher: signo is the signal to wait for, e.g., SIGTERM */
int uev_signal_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo);
int uev_signal_set (uev_t *w, int signo); /* Change signal to wait for */
int uev_signal_start(uev_t *w); /* Restart a stopped signal watcher */
int uev_signal_stop (uev_t *w); /* Stop signal watcher */
```
### Create an Event Context
To monitor events the developer first creates an *event context*, this
is achieved by calling `uev_init()` with a pointer to a (thread) local
`uev_ctx_t` variable.
```C
uev_ctx_t ctx;
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.
Here is a signal example:
```C
void cleanup_exit(uev_t *w, void *arg, int events)
{
/* Graceful exit, with optional cleanup ... */
uev_exit(w->ctx);
}
int main(void)
{
uev_t sigterm_watcher;
.
.
.
uev_signal_init(&ctx, &sigterm_watcher, cleanup_exit, NULL, SIGTERM);
.
.
.
}
```
### Start Event Loop
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.
In this example we set `flags` to none:
```C
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.
**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.
### Summary
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
**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
opened in blocking mode. Be careful out there!
**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. If your application use `system()` you replace
that with `fork()`, and then in the child, unblock all signals blocked
by your parent process, before you run `exec()`. This because Linux
does not unblock signals for your children, and neither does most (all?)
C-libraries. See the [finit][6] project's implementation of `run()` for
an example of this.
Using -luev
-----------
LibuEv is by default installed as a library with a few header files, you
should only ever need to include one:
#include <uev/uev.h>
The output from the `pkg-config` tool holds no surprises:
$ pkg-config --libs --static --cflags libuev
-I/usr/local/include -L/usr/local/lib -luev
The prefix path `/usr/local/` shown here is only the default. Use the
`configure` script to select a different prefix when installing libuEv.
For GNU autotools based projects, use the following in `configure.ac`:
# Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 1.4.0])
and in your `Makefile.am`:
proggy_CFLAGS = $(uev_CFLAGS)
proggy_LDADD = $(uev_LIBS)
Joystick Example
----------------
Here follows a very brief example to illustrate how one can use libuEv
to act upon joystick input.
```C
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <uev/uev.h>
struct js_event {
uint32_t time; /* event timestamp in milliseconds */
int16_t value; /* value */
uint8_t type; /* event type */
uint8_t number; /* axis/button number */
} e;
static void joystick_cb(uev_t *w, void *arg, int events)
{
read(w->fd, &e, sizeof(e));
switch (e.type) {
case 1:
printf("Button %d %s\n", e.number, e.value ? "pressed" : "released");
break;
case 2:
printf("Joystick axis %d moved, value %d!\n", e.number, e.value);
break;
}
}
int main(void)
{
int fd;
uev_t watcher;
uev_ctx_t ctx;
fd = open("/dev/input/js1", O_RDONLY, O_NONBLOCK);
if (fd < 0)
errx(errno, "Cannot find a joystick attached.");
uev_init(&ctx);
uev_io_init(&ctx, &watcher, joystick_cb, NULL, fd, UEV_READ);
puts("Starting, press Ctrl-C to exit.");
return uev_run(&ctx, 0);
}
```
To build the example, follow installation instructions below, then save
the code as `joystick.c` and call GCC
gcc `pkg-config --libs --static --cflags libuev` -o joystick joystick.c
Alternatively, call the `Makefile` with <kbd>make joystick</kbd> from
the unpacked [libuEv][] distribution.
More complete and relevant example uses of [libuEv][] is the FTP/TFTP
server [uftpd][5], and the Linux `/sbin/init` replacement [finit][6].
Both successfully employ [libuEv][].
Also see the `bench.c` program (<kbd>make bench</kbd> from within the
library) for [reference benchmarks][7] against [libevent][1] and
[libev][2].
Build & Install
---------------
The library is built for and developed on GNU/Linux systems, patches to
support *BSD and its [kqueue][] interface are most welcome.
libuEv use the GNU configure and build system. To try out the bundled
examples, use the `--enable-examples` switch to the `configure` script.
./configure
make -j5
make test
sudo make install-strip
sudo ldconfig
The resulting .so file is ~14 kiB (<kbd>make install-strip</kbd>).
Background
----------
> “Why an event loop, why not use threads?”
With the advent of light-weight processes (threads) programmers these
days have a [golden hammer](http://c2.com/cgi/wiki?GoldenHammer) they
often swing without consideration. Event loops and non-blocking I/O is
often a far easier approach, as well as less error prone.
The purpose of many applications is, with a little logic sprinkled on
top, to act on network packets entering an interface, timeouts expiring,
mouse clicks, or other types of events. Such applications are often
very well suited to use an event loop.
Applications that need to churn massively parallel algorithms are more
suitable for running multiple (independent) threads on several CPU
cores. However, threaded applications must deal with the side effects
of concurrency, like race conditions, deadlocks, live locks, etc.
Writing error free threaded applications is hard, debugging them can be
even harder.
Sometimes the combination of multiple threads *and* an event loop per
thread can be the best approach, but each application of course needs to
be broken down individually to find the most optimal approach. Do keep
in mind, however, that not all systems your application will run on have
multiple CPU cores -- some small embedded systems still use a single CPU
core, even though they run Linux, with multiple threads a program may
actually run slower! Always profile your program, and if possible, test
it on different architectures.
Origin & References
-------------------
[libuEv][] was originally based on [LibUEvent][8] by [Flemming Madsen][]
but has been completely rewritten with a much clearer API. 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][] is developed and maintained by [Joachim Nilsson][].
[1]: http://libevent.org
[2]: http://software.schmorp.de/pkg/libev.html
[3]: http://unix.com/man-page/All/3x/XtDispatchEvent
[4]: http://lwn.net/Articles/415684/
[5]: https://github.com/troglobit/uftpd
[6]: https://github.com/troglobit/finit
[7]: http://libev.schmorp.de/bench.html
[8]: http://code.google.com/p/libuevent/
[9]: https://github.com/kazuho/picoev
[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
[Coverity Status]: https://scan.coverity.com/projects/3846/badge.svg
[LibuEv]: https://github.com/troglobit/libuev
[kqueue]: https://github.com/mheily/libkqueue
[Oku Kazuho]: https://github.com/kazuho
[Mark Lehmann]: http://software.schmorp.de
[Joachim Nilsson]: http://troglobit.com
[Flemming Madsen]: http://www.madsensoft.dk
[Dave Zarzycki, Apple]: http://www.youtube.com/watch?v=cD_s6Fjdri8
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+7
View File
@@ -0,0 +1,7 @@
LibuEv TODO
===========
* Port to *BSD kqueue API, http://en.wikipedia.org/wiki/Kqueue
Also in UNIX Network Progamming, by W. Richard Stevens 3rd ed.
* Restore Lua bindings now that libuEv itself has stabilized
Executable
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
autoreconf -W portability -visfm
+235
View File
@@ -0,0 +1,235 @@
/*
* Copyright 2003 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Mon 03/10/2003 - Modified by Davide Libenzi <davidel@xmailserver.org>
*
* Added chain event propagation to improve the sensitivity of
* the measure respect to the event loop efficency.
*
* Sun 04/08/2013 - Modified by Joachim Nilsson <troglobit@gmail.com>
*
* Adaptations for libuEv, no libev/libevent API wrappers available.
* Reindent to Linux coding style
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#else
#define USE_PIPES
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
typedef struct {
int index;
} myarg_t;
static int num_pipes, num_active, num_writes;
static int timers, count, writes, fired;
static myarg_t *args;
static int *pipes;
static uev_t *evio;
static uev_t *evto;
static void read_cb(uev_t *w, void *arg, int UNUSED(events))
{
int idx, widx;
u_char ch;
myarg_t *m = arg;
idx = m->index;
widx = idx + 1;
if (timers)
uev_timer_set(&evto[idx], 10000 + drand48() * 1000, 0);
count += read(w->fd, &ch, sizeof(ch));
if (writes) {
if (widx >= num_pipes)
widx -= num_pipes;
if (write(pipes[2 * widx + 1], "e", 1) < 0) {
perror("write()");
abort();
}
writes--;
fired++;
}
}
static void timer_cb(uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{
/* nop */
}
static struct timeval *run_once(uev_ctx_t *ctx)
{
int *cp, i, space;
static struct timeval ta, ts, te;
gettimeofday(&ta, NULL);
for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
uev_io_set(&evio[i], cp[0], UEV_READ);
if (timers)
uev_timer_set(&evto[i], 10000 + drand48() * 1000, 0);
}
uev_run(ctx, UEV_ONCE | UEV_NONBLOCK);
fired = 0;
space = num_pipes / num_active;
space = space * 2;
for (i = 0; i < num_active; i++, fired++) {
if (write(pipes[i * space + 1], "e", 1) < 0) {
perror("write()");
abort();
}
}
count = 0;
writes = num_writes;
{
int xcount = 0;
gettimeofday(&ts, NULL);
do {
uev_run(ctx, UEV_ONCE | UEV_NONBLOCK);
xcount++;
} while (count != fired);
gettimeofday(&te, NULL);
// if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count);
}
timersub(&te, &ta, &ta);
timersub(&te, &ts, &ts);
fprintf(stdout, "%8ld %8ld\n",
ta.tv_sec * 1000000L + ta.tv_usec,
ts.tv_sec * 1000000L + ts.tv_usec);
return &te;
}
int main(int argc, char **argv)
{
struct rlimit rl;
int i, c;
int *cp;
uev_ctx_t ctx;
extern char *optarg;
num_pipes = 100;
num_active = 1;
num_writes = num_pipes;
while ((c = getopt(argc, argv, "a:n:tw:")) != -1) {
switch (c) {
case 'a':
num_active = atoi(optarg);
break;
case 'n':
num_pipes = atoi(optarg);
break;
case 't':
timers = 1;
break;
case 'w':
num_writes = atoi(optarg);
break;
default:
fprintf(stderr, "Illegal argument \"%c\"\n", c);
return 1;
}
}
rl.rlim_cur = rl.rlim_max = num_pipes * 3 + 50;
if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
perror("setrlimit");
return 1;
}
args = calloc(num_pipes, sizeof(myarg_t));
evio = calloc(num_pipes, sizeof(uev_t));
evto = calloc(num_pipes, sizeof(uev_t));
pipes = calloc(num_pipes * 2, sizeof(int));
if (!args || !evio || !evto || !pipes) {
perror("calloc");
return 1;
}
uev_init(&ctx);
for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
if (timers)
uev_timer_init(&ctx, &evto[i], timer_cb, NULL, 0, 0);
args[i].index = i;
#ifdef USE_PIPES
if (pipe2(cp, O_NONBLOCK) == -1) {
#else
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, cp) == -1) {
#endif
perror("pipe");
exit(1);
}
uev_io_init(&ctx, &evio[i], read_cb, &args[i], cp[0], UEV_READ);
}
for (i = 0; i < 2; i++)
run_once(&ctx);
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+19
View File
@@ -0,0 +1,19 @@
AC_INIT(libuev, 1.5.0, https://github.com/troglobit/libuev/issues)
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
AM_SILENT_RULES([yes])
AC_CONFIG_SRCDIR(uev.c)
AM_CONFIG_HEADER(config.h)
AC_CONFIG_FILES([Makefile libuev.pc examples/Makefile])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
AM_PROG_AR
LT_INIT
AC_ARG_ENABLE([examples],
[AS_HELP_STRING([--enable-examples], [build examples])],
[enable_examples=yes], [])
AM_CONDITIONAL([ENABLE_EXAMPLES], [test "$enable_examples" = yes])
AC_OUTPUT
+165
View File
@@ -0,0 +1,165 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <string.h> /* memset() */
#include <sys/timerfd.h>
#include <unistd.h> /* close(), read() */
#include "uev.h"
/* Missing defines in GLIBC <= 2.24 */
#ifndef TFD_TIMER_CANCEL_ON_SET
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
#endif
#ifndef TFD_SETTIME_FLAGS
#define TFD_SETTIME_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)
#endif
/**
* Create and start an at/cron job watcher
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb Callback function for cron job
* @param arg Optional callback argument
* @param when First point in time to call @param cb
* @param interval For an at job this is zero, for cron the offset interval
*
* For at jobs set @param interval to zero and only use @param when. For
* cron jobs, set @param interval to the offset. E.g., if the job should
* run every five minutes set the @param tm_min of struct tm to five.
*
* Use mktime() to create the time_t arguments. The special value zero
* may be used for @param when to denote 'now', where 'now' is when the
* event loop is started. You can also treat time_t simply as a signed
* integer. E.g., set @param interval to 3600 to create a cron job that
* runs every hour.
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_cron_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, time_t when, time_t interval)
{
int fd;
if (when < 0 || interval < 0) {
errno = ERANGE;
return -1;
}
fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
if (fd < 0)
return -1;
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);
exit:
close(fd);
return -1;
}
return 0;
}
/**
* Reset an at/cron job watcher
* @param w Watcher to reset
* @param when First point in time to call @param cb
* @param interval For an at job this is zero, for cron the offset interval
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_cron_set(uev_t *w, time_t when, time_t interval)
{
/* Every watcher must be registered to a context */
if (!w || !w->ctx) {
errno = EINVAL;
return -1;
}
if (when < 0 || interval < 0) {
errno = ERANGE;
return -1;
}
/* Handle stopped timers */
if (w->fd < 0) {
/* Timer already stopped */
if (!when && !interval)
return 0;
/* Remove from internal list */
LIST_REMOVE(w, link);
if (uev_cron_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, when, interval))
return -1;
}
w->u.c.when = when;
w->u.c.interval = interval;
if (w->ctx->running) {
struct itimerspec time;
memset(&time, 0, sizeof(time));
time.it_value.tv_sec = when;
time.it_interval.tv_sec = interval;
if (timerfd_settime(w->fd, TFD_SETTIME_FLAGS, &time, NULL) < 0)
return 1;
}
return uev_watcher_start(w);
}
/**
* Start a stopped at/cron job watcher
* @param w Watcher to start (again)
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_cron_start(uev_t *w)
{
return uev_timer_start(w);
}
/**
* Stop and unregister an at/cron job watcher
* @param w Watcher to stop
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_cron_stop(uev_t *w)
{
return uev_timer_stop(w);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+4
View File
@@ -0,0 +1,4 @@
joystick
signal
cronrun
test
+5
View File
@@ -0,0 +1,5 @@
noinst_PROGRAMS = signal test joystick cronrun
AM_CPPFLAGS = -I$(top_srcdir)
AM_LDFLAGS = -L$(top_srcdir)
LIBS = $(LTLIBINTL)
LDADD = $(top_srcdir)/libuev.la
+68
View File
@@ -0,0 +1,68 @@
/* Test cron API in libuEv event library
*
* Copyright (c) 2016 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
static void cron_job(uev_t *w, void *arg, int events)
{
struct timeval tv;
gettimeofday(&tv, NULL);
printf("Cron job HELO %s", ctime(&tv.tv_sec));
}
int main(void)
{
uev_t cron_watcher;
uev_ctx_t ctx;
time_t when, interval;
struct timeval tv;
/* Initialize libuEv */
uev_init(&ctx);
/* Cron job */
gettimeofday(&tv, NULL);
when = tv.tv_sec + 30;
interval = 30;
uev_cron_init(&ctx, &cron_watcher, cron_job, NULL, when, interval);
/* Start event loop */
printf("Start of test %s", ctime(&tv.tv_sec));
printf("Expected cron %s", ctime(&when));
return uev_run(&ctx, 0);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+80
View File
@@ -0,0 +1,80 @@
/* Simple example of how to use libuEv with Linux joystick API
*
* Copyright (c) 2014-2016 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include "uev.h"
struct js_event {
uint32_t time; /* event timestamp in milliseconds */
int16_t value; /* value */
uint8_t type; /* event type */
uint8_t number; /* axis/button number */
} e;
static void joystick_cb(uev_t *w, void *arg, int events)
{
if (read(w->fd, &e, sizeof(e)) < 0)
errx(errno, "Failed reading joystick event");
switch (e.type) {
case 1:
printf("Button %d %s\n", e.number, e.value ? "pressed" : "released");
break;
case 2:
printf("Joystick axis %d moved, value %d!\n", e.number, e.value);
break;
}
}
int main(void)
{
int fd;
uev_t js1_watcher;
uev_ctx_t ctx;
fd = open("/dev/input/js1", O_RDONLY, O_NONBLOCK);
if (fd < 0)
errx(errno, "Cannot find a joystick attached.");
uev_init(&ctx);
uev_io_init(&ctx, &js1_watcher, joystick_cb, NULL, fd, UEV_READ);
puts("Starting, press Ctrl-C to exit.");
return uev_run(&ctx, 0);
}
/**
* Local Variables:
* compile-command: "make joystick"
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+140
View File
@@ -0,0 +1,140 @@
/* Multiple signals, segfault and fork() to test sub-processes.
*
* Copyright (c) 2015 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#define _GNU_SOURCE
#include <err.h>
#include <sched.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
#define DO_SEGFAULT 1
typedef struct {
int magic;
void *ptr;
} arg_t;
/* Stack is not shared between parent and child, only the heap. */
static arg_t arg = {
.magic = 1337,
.ptr = NULL
};
static int callback(arg_t *arg, int event, void *foo)
{
arg_t *event_arg = (arg_t *)foo;
printf("child: got magic %d and ptr %p, with event %d and event arg %p\n",
arg->magic, arg->ptr, event, event_arg);
arg->magic = 7331;
arg->ptr = &event_arg;
/* Cause segfault! */
#if DO_SEGFAULT == 1
event_arg->ptr = arg->ptr;
printf("child: segfaulting failed!\n");
#endif
return 42;
}
static void sigsegv_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
{
warnx("PID %d caused segfault.", getpid());
exit(-1);
}
static void sigchld_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
{
pid_t pid = waitpid(-1, NULL, WNOHANG);
if (-1 != pid)
warnx("PID %d exited, bye.", pid);
}
static void work_cb(uev_t *UNUSED(w), void *arg, int UNUSED(events))
{
int status = 0;
pid_t pid;
pid = fork();
if (-1 == pid)
err(1, "fork");
if (!pid) {
status = callback(arg, 6137, NULL);
exit(status);
}
if (waitpid(pid, &status, 0) == -1)
err(1, "waitpid");
if (WIFEXITED(status))
printf("Child exited normally => %d\n", WEXITSTATUS(status));
else if (WCOREDUMP(status))
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_t *w, void *UNUSED(arg), int UNUSED(events))
{
printf("Process deadline reached, exiting!\n");
uev_exit(w->ctx);
}
int main(void)
{
uev_t sigsegv_watcher, sigchld_watcher, timeout_watcher, deadline_watcher;
uev_ctx_t ctx;
/* Initialize libuEv */
uev_init(&ctx);
/* Setup callbacks */
uev_signal_init(&ctx, &sigsegv_watcher, sigsegv_cb, NULL, SIGSEGV);
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, &deadline_watcher, exit_cb, NULL, 1000, 0);
/* Start event loop */
return uev_run(&ctx, 0);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+152
View File
@@ -0,0 +1,152 @@
/* Test libuEv event library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2015 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* intptr_t */
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
typedef struct {
int counter;
} my_t;
static int in, out;
static int period = 0;
static uev_t *watchdog;
static void lifetime_cb(uev_t *w, void *arg, int UNUSED(events))
{
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))
{
watchdog = NULL;
fprintf(stderr, "\nTimeout exceeded %p\n", arg);
// uev_timer_stop(w); <-- No need to stop timers with period=0 :)
uev_exit(w->ctx);
}
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))
{
fprintf(stderr, w->signo == SIGINT ? "^Cv" : "^\v");
}
static void pipe_read_cb(uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{
int cnt;
char msg[50];
/* 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);
}
static void pipe_write_cb(uev_t *w, void *arg, int UNUSED(events))
{
my_t *my = arg;
char *msg = "TESTING";
if (write(out, msg, my->counter) < 0) {
perror("\nFailed writing to pipe");
return;
}
// fprintf(stderr, "WRITE %.*s %d\n", my->counter, msg, my->counter);
fprintf(stderr, "%d ", my->counter);
period = my->counter + 5;
my->counter++;
uev_timer_set(w, period * 100, 0);
}
int main(void)
{
int fd[2];
my_t my = { .counter = 1 };
uev_t timeout, wdt, periodic, writer, reader, sigint_watcher, sigquit_watcher;
uev_ctx_t ctx;
/* Work load, one timer callback writes to a pipe periodically,
* and one I/O watcher that is called every time the pipe has
* some data. */
if (pipe(fd) < 0)
return 1;
/* Initialize libuEv */
uev_init(&ctx);
/* Signal watchers, Ctrl-C => SIGINT, Ctrl-\ => SIGQUIT */
uev_signal_init(&ctx, &sigint_watcher, signal_cb, NULL, SIGINT);
uev_signal_init(&ctx, &sigquit_watcher, signal_cb, NULL, SIGQUIT);
/* Total program execution time */
uev_timer_init(&ctx, &timeout, lifetime_cb, (void *)(intptr_t)2, 4000, 0);
/* Main pipe worker and consumer */
in = fd[0];
out = fd[1];
uev_io_init(&ctx, &reader, pipe_read_cb, NULL, in, UEV_READ);
uev_timer_init(&ctx, &writer, pipe_write_cb, &my, 400, 0);
/* Watchdog for the above timer callback, if it doesn't wake up
* and write to the pipe within a given deadline it will bark. */
uev_timer_init(&ctx, &wdt, timeout_cb, (void *)(intptr_t)1, 950, 0);
watchdog = &wdt;
/* Periodic background task */
uev_timer_init(&ctx, &periodic, periodic_task, NULL, 200, 200);
/* Start event loop */
uev_run(&ctx, 0);
fprintf(stderr, "Period is %d must be 10: %s\n", period, 10 == period ? "OK" : "ERROR!");
if (10 != period)
return 1;
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+96
View File
@@ -0,0 +1,96 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include "uev.h"
/**
* Create an I/O watcher
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb I/O callback
* @param arg Optional callback argument
* @param fd File descriptor to watch, or -1 to register an empty watcher
* @param events Requested events to watch for, a mask of %UEV_READ, %UEV_WRITE and %UEV_PRI
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_io_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events)
{
if (fd < 0) {
errno = EINVAL;
return -1;
}
if (uev_watcher_init(ctx, w, UEV_IO_TYPE, cb, arg, fd, events))
return -1;
return uev_watcher_start(w);
}
/**
* Reset an I/O watcher
* @param w Pointer to an uev_t watcher
* @param fd New file descriptor to monitor
* @param events Requested events to watch for, a mask of %UEV_READ and %UEV_WRITE
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_io_set(uev_t *w, int fd, int events)
{
/* Ignore any errors, only to clean up anything lingering ... */
uev_io_stop(w);
return uev_io_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, fd, events);
}
/**
* Start an I/O watcher
* @param w Watcher to start (again)
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_io_start(uev_t *w)
{
return uev_io_set(w, w->fd, w->events);
}
/**
* Stop an I/O watcher
* @param w Watcher to stop
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_io_stop(uev_t *w)
{
return uev_watcher_stop(w);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+12
View File
@@ -0,0 +1,12 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: @PACKAGE@
Description: Simple event loop for Linux
Version: @VERSION@
Requires:
Libs: -L${libdir} -luev
Cflags: -I${includedir}
-476
View File
@@ -1,476 +0,0 @@
/**
* @file
* Micro event library libuevent.
* Asynchronous event / mainloop library.
* Not thread safe!
* The ctxt structure needs mutex protection in case of multithreaded access
* $Id:$
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/times.h>
#include <fcntl.h>
#include <time.h>
#include <poll.h>
#include <errno.h>
#include <assert.h>
#include "llist.h"
#include "libuevent.h"
#ifdef DEBUG
#define Debug(f, args...) printf(":LUE: " f "\n", ##args)
#else
#define Debug(f, args...)
#endif
static clock_t clock_tick = 0;
/// Enumerate file handle direction
enum LUEFhDirectionT {LUE_FHIN = 0, LUE_FHOUT = 1};
struct LUEFileEventH;
struct LUEFileEventRem
{
LListField(struct LUEFileEventRem);
struct LUEFileEventH *ent;
};
/// File event handle
struct LUEFileEventH
{
LListField(struct LUEFileEventH); ///< Elements for linked list
struct LUEFileEventRem rem; ///< For postponed removal
int fd; ///< File descriptor
enum LUEFhDirectionT direction; ///< Direction: in or out
int pollIx; ///< This file handle index into list of file handles to poll
LUEFileHandler handler; ///< File handler callback
void *data; ///< File handler callback parameter
};
/// Timer context object
struct LUETimerH
{
LListField(struct LUETimerH); ///< Linked list elements
int dueTime; ///< Epoch timestamp
LUETimerHandler handler; ///< Timer callback
void *data; ///< Timer callback parameter
};
/**
* @cond doxygen does not like this construction
*/
typedef LList(struct LUETimerH) LUETimerHandlerDL;
typedef LList(struct LUEFileEventH) LUEFileEventDL;
typedef LList(struct LUEFileEventRem) LUEFileEventRemL;
/**
* @endcond
*/
/// Main lue context object
struct LUECtxt
{
LUEFileEventDL fileHandlers; ///< File handlers
LUETimerHandlerDL timeoutHandlers; ///< Timer handlers
LUEFileEventRemL fileRem; ///< List of file handles to be garbage collected
LUETimerHandlerDL timeRem; ///< List of timer handles to be garbage collected
clock_t baseTime; ///< Time at last timer tick
clock_t setTime; ///< Next timer tick epoch
int timeSet; ///< True if next timer epoch is set
int exiting; ///< True if the application is terminating
};
static
struct LUEFileEventH *lueAddIO(struct LUECtxt * ctxt,
int fd, LUEFileHandler handler, void *data,
enum LUEFhDirectionT direction)
{
struct LUEFileEventH *ent;
ent = (struct LUEFileEventH *) malloc(sizeof(*ent));
Debug("Add IO %d %p", fd, ent);
assert(ent);
ent->rem.ent = ent;
ent->fd = fd;
ent->direction = direction;
ent->handler = handler;
ent->data = data;
ent->pollIx = -1;
lListAppend(&ctxt->fileHandlers, ent);
return ent;
}
/**
* Add an input file handler
*/
struct LUEFileEventH *lueAddInput(struct LUECtxt *ctxt,
int fd, LUEFileHandler handler, void *data)
{
return lueAddIO(ctxt, fd, handler, data, LUE_FHIN);
}
/**
* Add an output file handler
*/
struct LUEFileEventH *lueAddOutput(struct LUECtxt *ctxt,
int fd, LUEFileHandler handler, void *data)
{
return lueAddIO(ctxt, fd, handler, data, LUE_FHOUT);
}
static
int lueRemIO(struct LUECtxt *ctxt, struct LUEFileEventH *hdl)
{
struct LUEFileEventRem *ent;
Debug("Rem IO %p", hdl);
lListForeachIn (ent, &ctxt->fileRem)
if (ent->ent == hdl)
return -1; // Already removed
Debug("Rem FD %d", hdl->fd);
hdl->handler = NULL;
lListAppend(&ctxt->fileRem, &(hdl->rem));
return 0;
}
/**
* Remove all handlers for an fd
*/
int lueRemFd(struct LUECtxt *ctxt, int fd)
{
struct LUEFileEventH *ent;
struct LUEFileEventRem *rem;
Debug("Remfd FD %d", ent->fd);
lListForeachIn(ent, &ctxt->fileHandlers)
{
if (ent->fd != fd)
continue;
lListForeachIn(rem, &ctxt->fileRem)
if (rem->ent == ent)
return -1; // Already removed
Debug("Remfd ent %p", ent);
ent->handler = NULL;
lListAppend(&ctxt->fileRem, &(ent->rem));
}
return 0;
}
/**
* Remove an input file handler
*/
int lueRemInput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl)
{
return lueRemIO(ctxt, hdl);
}
/**
* Remove an output file handler
*/
int lueRemOutput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl)
{
return lueRemIO(ctxt, hdl);
}
/**
* Remove a timer event
*/
int lueRemTimer(struct LUECtxt *ctxt, struct LUETimerH *hdl)
{
if (hdl)
{
struct LUETimerH *ent;
lListForeachIn (ent, &ctxt->timeRem)
if (ent == hdl)
return -1; // Already removed
Debug("Remtimer %p", hdl);
lListRemove(&ctxt->timeoutHandlers, hdl);
lListAppend(&ctxt->timeRem, hdl); // Remember to free him (at a safe time)
}
return 0;
}
/**
* Add a timer event
*/
struct LUETimerH *lueAddTimer(struct LUECtxt * ctxt, int periodMs,
LUETimerHandler handler, void *data)
{
int timeDiff;
clock_t curTime;
struct LUETimerH *nent,
*ret,
*ent;
nent = (struct LUETimerH *) malloc(sizeof(*ent));
assert(nent);
nent->dueTime = periodMs;
nent->handler = handler;
nent->data = data;
if (0 == periodMs)
{
// Zero delay is a special case: A oneshot workproc: Must go first
lListInsertFirst(&ctxt->timeoutHandlers, nent);
return nent;
}
// Adjust timers.
// Jiffie wraparound is OK since we only care about diff time
curTime = ctxt->timeSet ? ctxt->setTime : times(NULL);
timeDiff = (curTime - ctxt->baseTime) / clock_tick * 1000
+ ((curTime - ctxt->baseTime) % clock_tick) * 1000 / clock_tick;
ret = nent;
lListForeachIn(ent, &ctxt->timeoutHandlers)
{
if (ent->dueTime > timeDiff)
ent->dueTime -= timeDiff;
else
ent->dueTime = 0;
if (nent && ent->dueTime > periodMs)
{
Debug("Timer Insert %d before %d %p", periodMs, ent->dueTime, nent);
lListInsert(&ctxt->timeoutHandlers, nent, ent);
nent = NULL;
}
}
if (nent)
{
Debug("Timer Append %d at end %p", periodMs, nent);
lListAppend(&ctxt->timeoutHandlers, nent);
}
ctxt->baseTime = curTime;
return ret;
}
/**
* Create an application context
*/
struct LUECtxt *lueCtxtCreate()
{
struct LUECtxt *lue;
lue = (struct LUECtxt *) malloc(sizeof(*lue));
assert(lue);
lListInit(&lue->fileHandlers);
lListInit(&lue->timeoutHandlers);
lListInit(&lue->fileRem);
lListInit(&lue->timeRem);
lue->baseTime = times(NULL);
lue->setTime = 0;
lue->timeSet = false;
lue->exiting = false;
if (0 == clock_tick)
clock_tick = sysconf(_SC_CLK_TCK);
return lue;
}
/**
* Destroy an application context
*/
void lueCtxtDestroy(struct LUECtxt *lue)
{
lListPurge(&lue->fileHandlers);
lListPurge(&lue->timeoutHandlers);
free(lue);
}
/**
* Drive the timeouts ourself (from now on until an increment of -1)
* This is intended for use in testing frameworks
* Caveat emptor: Currently, interactions with the scheduler (agent) API is undefined.
* Not for use in lues that use the agent scheduler
* @param ctxt LUElication context object
* @param mSecs Ms to advance. Use -1 to return to realtime
*/
void lueTimeAdvance(struct LUECtxt *ctxt, int mSecs)
{
// Obviously we are executing a callback right now (arent we always)
// The time leap is taken into account before falling back into poll() below
if (mSecs >= 0)
{
if (!ctxt->timeSet)
{
ctxt->timeSet = true;
ctxt->setTime = times(NULL) + mSecs;
}
else
{
ctxt->setTime += mSecs;
}
}
else if (mSecs == -1)
{
// Go back to realtime
ctxt->timeSet = false;
ctxt->baseTime += times(NULL) - ctxt->setTime;
ctxt->setTime = 0;
}
}
/**
* Process pending events
*/
static int _lueProcessPending(struct LUECtxt *ctxt, int immediate)
{
struct LUETimerH *timer;
int pollRet;
clock_t curTime;
int curPeriod = 0;
// Do due timers
if (lListHead(&ctxt->timeoutHandlers))
{
curTime = ctxt->timeSet ? ctxt->setTime : times(NULL);
curPeriod = (curTime - ctxt->baseTime) / clock_tick * 1000
+ ((curTime - ctxt->baseTime) % clock_tick) * 1000 / clock_tick;
Debug("Period for timer execution %d", curPeriod);
while((timer = lListHead(&ctxt->timeoutHandlers)) &&
timer->dueTime <= curPeriod)
{
Debug("Timer execution %d", timer->dueTime);
timer->handler(ctxt, timer, timer->data);
lueRemTimer(ctxt, timer);
// baseTime changes for every lueAddTimer
curPeriod = (curTime - ctxt->baseTime) / clock_tick * 1000
+ ((curTime - ctxt->baseTime) % clock_tick) * 1000 / clock_tick;
}
lListPurge(&ctxt->timeRem); // Reap timer zombies
}
// Calculate waiting time
if (ctxt->exiting)
curPeriod = 0;
else if ((timer = lListHead(&ctxt->timeoutHandlers)) &&
timer->dueTime > curPeriod)
{
Debug("Period from timer head %d - %d -> %d",
timer->dueTime, curPeriod, timer->dueTime - curPeriod);
curPeriod = timer->dueTime - curPeriod;
}
else if (NULL != timer)
curPeriod = 0;
else
curPeriod = -1;
// Do pipe handlers
if (lListHead(&ctxt->fileHandlers) && !ctxt->exiting)
{
struct pollfd polls[lListLength(&ctxt->fileHandlers)];
struct LUEFileEventH *ent;
int ix = 0;
lListForeachIn(ent, &ctxt->fileHandlers)
{
ent->pollIx = ix;
polls[ix].fd = ent->fd;
polls[ix].events = LUE_FHIN == ent->direction ? POLLIN : POLLOUT;
polls[ix].revents = 0;
ix++;
}
Debug("Polling %d fds in %d ms", ix, curPeriod);
pollRet = poll(polls, ix, immediate ? 0 : curPeriod);
if (pollRet < 0)
{
if (EINTR == errno)
return 0;
else if (EBADF == errno)
{
lListForeachIn(ent, &ctxt->fileHandlers)
{
if (fcntl(ent->fd, F_GETFD) == -1 && EBADF == errno)
{
ent->handler(ctxt, ent, ent->fd, ent->data);
}
}
}
else
{
// Error in poll. Cannot continue
assert(false);
return;
}
}
else if (pollRet > 0)
{
lListForeachIn(ent, &ctxt->fileHandlers)
{
if (ent->handler && ent->pollIx >= 0 && polls[ent->pollIx].revents)
{
ent->handler(ctxt, ent, ent->fd, ent->data);
}
}
}
if (lListHead(&ctxt->fileRem))
{
struct LUEFileEventRem *ent;
while (NULL != (ent = lListHead(&ctxt->fileRem)))
{
lListRemove(&ctxt->fileRem, ent);
lListRemove(&ctxt->fileHandlers, ent->ent);
free(ent->ent);
}
}
}
else if (curPeriod > 0)
{
if (!immediate)
poll(NULL, 0, curPeriod);
}
else if (curPeriod == -1)
curPeriod = -2; // Nothing more to do
return curPeriod;
}
/**
* Process pending events
*/
int lueProcessPending(struct LUECtxt *ctxt)
{
return _lueProcessPending(ctxt, 1);
}
/**
* Run the application
*/
void lueRun(struct LUECtxt *ctxt)
{
int ret;
do {
ret = _lueProcessPending(ctxt, 0);
} while (!ctxt->exiting && ret != -2);
ctxt->exiting = false;
Debug("LUE exiting - bye");
}
/**
* Terminate the application
*/
void lueTerminate(struct LUECtxt *ctxt)
{
ctxt->exiting = true;
}
// vim: set ts=8 sw=3 sts=3 et:
-75
View File
@@ -1,75 +0,0 @@
/**
* @file
* Micro event library libuevent.
* Asynchronous event / mainloop library.
* Not thread safe!
* The ctxt structure needs mutex protection in case of multithreaded access
* $Id:$
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. Notice at end of file
*/
#ifndef LIBUEVENT_H
#define LIBUEVENT_H
struct LUETimerH;
struct LUEFileEventH;
struct LUECtxt;
/**
* Typedef file handler callbacks
*/
typedef int (*LUEFileHandler)(struct LUECtxt *ctxt,
struct LUEFileEventH *handle,
int fd,
void *data);
/**
* Typedef timer handler callbacks
*/
typedef int (*LUETimerHandler)(struct LUECtxt *ctxt,
struct LUETimerH *handle,
void *data);
struct LUEFileEventH *lueAddInput(struct LUECtxt *ctxt,
int fd, LUEFileHandler handler, void *data);
int lueRemInput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl);
struct LUEFileEventH *lueAddOutput(struct LUECtxt *ctxt,
int fd, LUEFileHandler handler, void *data);
int lueRemOutput(struct LUECtxt *ctxt, struct LUEFileEventH *hdl);
int lueRemFd(struct LUECtxt *ctxt, int fd);
struct LUETimerH *lueAddTimer(struct LUECtxt *ctxt, int periodMs,
LUETimerHandler handler, void *data);
int lueRemTimer(struct LUECtxt *ctxt, struct LUETimerH *hdl);
struct LUECtxt *lueCtxtCreate();
void lueCtxtDestroy(struct LUECtxt *lue);
void lueTimeAdvance(struct LUECtxt *ctxt, int mSecs);
void lueRun(struct LUECtxt *ctxt);
int lueProcessPending(struct LUECtxt *ctxt);
void lueTerminate(struct LUECtxt *ctxt);
#endif
/******************************************************************************
* Copyright (C) 2012 flemming.madsen at madsensoft.dk. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
-150
View File
@@ -1,150 +0,0 @@
/**
* @file llist.h
* (Doubly) Linked lists anchored at both ends
*
* $Id$
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. Notice at end of file
*/
/// Struct mix-in. Use this inside your struct declaration
#define LListField(tn) tn * _llNext; tn * _llPrev
/// Anchor Declaration. Use like: LList(typename) varname;
#define LList(tn) struct { tn *head; tn *tail; int numElm; }
/// List init
#define lListInit(a) ((a)->head = (a)->tail = NULL, (a)->numElm = 0, (a))
/// List empty
#define lListEmpty(a) ((a)->head == NULL)
/// List head
#define lListHead(a) ((a)->head)
/// List tail
#define lListTail(a) ((a)->tail)
/// List length
#define lListLength(a) ((a)->numElm)
/// List element next
#define lListNext(e) ((e)->_llNext)
/// List element prev
#define lListPrev(e) ((e)->_llPrev)
/// Append element e to list e
#define lListAppend(a, e) \
{ \
if (lListEmpty(a)) (a)->head = (e); \
else (a)->tail->_llNext = (e); \
(e)->_llPrev = (a)->tail; \
(a)->tail = (e); \
(e)->_llNext = NULL; \
(a)->numElm++; \
}
/// Insert element n before element e in list a
#define lListInsertFirst(a, n) lListInsert(a, n, (a)->head)
/// Insert element n before element e in list a
#define lListInsert(a, n, e) \
{ \
if (!(e) || (a)->head == (e)) { \
n->_llNext = (a)->head; \
n->_llPrev = NULL; \
(a)->head = (n); \
} else { \
n->_llNext = (e); \
n->_llPrev = (e)->_llPrev; \
(e)->_llPrev->_llNext = n; \
} \
if (n->_llNext) n->_llNext->_llPrev = n; \
(a)->numElm++; \
}
/// Remove element e from list a. Caller must free element.
#define lListRemove(a, e) \
{ \
if ((a)->head) { \
if ((e) == (a)->head) (a)->head = (e)->_llNext; \
else (e)->_llPrev->_llNext = (e)->_llNext; \
if ((e) == (a)->tail) (a)->tail = (e)->_llPrev; \
else (e)->_llNext->_llPrev = (e)->_llPrev; \
(a)->numElm--; \
} \
}
/// Purge list a elements with free()
#define lListPurgeT(a, T) \
{ \
T p1, p2; \
for (p1 = (a)->head; p1; ) { \
p2 = p1; p1 = p1->_llNext; free(p2); \
} \
lListInit(a); \
}
/// Remove last element from list a
#define lListRemoveTailT(a, T) \
{ \
T p; \
if ((a)->tail) { \
p = (a)->tail; \
(a)->tail = (a)->tail->_llPrev; \
if ((a)->tail) (a)->tail->_llNext = NULL; \
else (a)->head = NULL; \
free(p); \
(a)->numElm--; \
} \
}
/// Remove first element from list a
#define lListRemoveHeadT(a, T) \
{ \
T p; \
if ((a)->head) { \
p = (a)->head; \
(a)->head = (a)->head->_llNext; \
if ((a)->head) (a)->head->_llPrev = NULL; \
else (a)->tail = NULL; \
free(p); \
(a)->numElm--; \
} \
}
// Convenience
// Only use if you have typeof() eg. gcc
#define lListPurge(a) lListPurgeT(a, typeof((a)->head))
#define lListRemoveTail(a) lListRemoveTailT(a, typeof((a)->head))
#define lListRemoveHead(a) lListRemoveHeadT(a, typeof((a)->head))
/// Traverse the list foreach e in a
#define lListForeachIn(e, a) for (e = (a)->head; e; e = e->_llNext)
/******************************************************************************
* Copyright (C) 2012 flemming.madsen at madsensoft.dk. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
// vim: set sw=3 sts=3 et:
-419
View File
@@ -1,419 +0,0 @@
/**
* @file
* Lua binding to libuevent
* $Id:$
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>
#include <assert.h>
#include <lua.h>
#include <lauxlib.h>
#include "libuevent.h"
static char * LUA_LTIMER_MT = "libuevent-timer";
static char * LUA_LFILEH_MT = "libuevent-fileh";
static char * LUA_LUEOBJ = "libuevent-objects";
typedef struct lue_data
{
lua_State *L; // Index to our world
struct LUETimerH *timer;
struct LUEFileEventH *file;
} lue_data;
#define toptimer(L) ((struct lue_data *)luaL_checkudata(L, 1, LUA_LTIMER_MT))
#define topfileh(L) ((struct lue_data *)luaL_checkudata(L, 1, LUA_LFILEH_MT))
static struct LUECtxt *getCtxt(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_rawgeti(L, -1, 1);
struct LUECtxt *ret = lua_touserdata(L, -1);
lua_pop(L, 2);
return ret;
}
static void l_newType(lua_State* L, const char *name)
{
luaL_newmetatable(L, name); // create metatable
lua_pushvalue(L, -1); // dup
lua_setfield(L, -2, "__index"); // metatable.__index = metatable
}
static int l_traceback (lua_State *L)
{
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return 1;
}
lua_getfield(L, -1, "traceback");
if (!lua_isfunction(L, -1)) {
lua_pop(L, 2);
return 1;
}
lua_pushvalue(L, 1); /* pass error message */
lua_pushinteger(L, 2); /* skip this function and traceback */
lua_call(L, 2, 1); /* call debug.traceback */
return 1;
}
// Top half
static int _lueHandler(lua_State *L, void *data, bool remObj)
{
lua_pushcfunction(L, l_traceback); /* push traceback function */
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_pushlightuserdata(L, data);
lua_rawget(L, -2); // objects[object_p] => The object
lua_pushvalue(L, -1);
lua_rawget(L, -3); // object[object] => closure
if (remObj)
{
lua_pushlightuserdata(L, data);
lua_pushnil(L);
lua_rawset(L, -5); // objects[object_p] = nil -> GC
lua_pushvalue(L, -2);
lua_pushnil(L);
lua_rawset(L, -5); // objects[object] = nil -> GC
}
lua_remove(L, -3); // objects table
int ret;
lua_insert(L, -2); // exch: closure <-> obj
if ((ret = lua_pcall(L, 1, 0, -3))) // Call closure(object)
{
fprintf(stderr, "luevent: Error: %d:\n%s\n",
ret, lua_isnil(L, -1) ? "(nil)" : lua_tostring(L, -1));
//exit(1);
}
lua_pop(L, 1); // Error handler, metatable
return 0;
}
static int _ltimerHandler(struct LUECtxt *ctxt, struct LUETimerH *handle, void *data)
{
lue_data *self = (lue_data *) data;
if (!self || !self->timer)
return 0;
int ret = _lueHandler(self->L, data, true);
self->timer = NULL;
return ret;
}
static int _lfileHandler(struct LUECtxt *ctxt, struct LUEFileEventH *handle, int fd, void *data)
{
lue_data *self = (lue_data *) data;
bool remObj = false;
if (!self || !self->file)
return 0;
if (fcntl(fd, F_GETFD) == -1 && EBADF == errno)
{
remObj = true;
lueRemInput(getCtxt(self->L), self->file); // Works on both types
self->file = NULL;
}
return _lueHandler(self->L, data, remObj);
}
// Bottom half
static void lueAddObj(lua_State *L, lue_data *self, int cbix)
{
self->L = L;
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_pushlightuserdata(L, self);
lua_pushvalue(L, -3);
lua_rawset(L, -3); // objects[self_p] = obj
lua_pushvalue(L, -2);
lua_pushvalue(L, cbix);
lua_rawset(L, -3); // objects[obj] = callback
lua_pop(L, 1); // objects
}
/* LUADOC
* Add a handler on a stream.
* The handler will sustain itself GC wise. Use &lt:object>:clear() to unregister the handler
* @param fdesc The posix file descriptor "small integer".
* <br> Use the ":getfd()" method on a "normal" lua stream or socket to obtain fdesc.
* @param handler A function object. No paramaeter supplied when called.
* @param mode [Optional] Input ("r") or output ("w") event. Default is input
* @return A new filehandler object
function luevent.addFileIO(fdesc, handler, mode)
*/
static int lfileh_add(lua_State *L)
{
int fd = luaL_checkint(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
const char *mode = luaL_optstring(L, 3, "r");
lue_data *self = (lue_data *) lua_newuserdata(L, sizeof(lue_data));
struct LUEFileEventH *h = *mode == 'r' ?
lueAddInput(getCtxt(L), fd, _lfileHandler, self) :
lueAddOutput(getCtxt(L), fd, _lfileHandler, self);
if (!h) luaL_error(L, "lueAddFileIO error");
self->file = h;
luaL_getmetatable(L, LUA_LFILEH_MT);
lua_setmetatable(L, -2);
lueAddObj(L, self, 2);
return 1;
}
/* LUADOC
* Create a new timer object. Implicitly cleared when handler is called.
* @usage <pre>
* local hdl<br>
* hdl = luevent.addTimer(50, function() hdl = nil xxx end)<br>
* ......<br>
* if hdl then hdl:clear() hdl = nil end<br>
* </pre>
* Here <tt>hdl</tt> is used to keep track of whether the timer is active.
* @param msecs The number of milliseconds until the timer expires
* @param handler A function object
* @return A new timer object
function luevent.addTimer(msecs, handler)
*/
static int ltimer_add(lua_State *L)
{
int mSecs = luaL_checkint(L, 1);
luaL_checktype(L, 2, LUA_TFUNCTION);
lue_data *self = (lue_data *) lua_newuserdata(L, sizeof(lue_data));
struct LUETimerH *h = self ? lueAddTimer(getCtxt(L), mSecs, _ltimerHandler, self) : NULL;
if (!h)
luaL_error(L, "lueAddTimer error");
self->timer = h;
luaL_getmetatable(L, LUA_LTIMER_MT);
lua_setmetatable(L, 3);
lueAddObj(L, self, 2);
return 1;
}
static void lobj_clear(lua_State *L, lue_data *self)
{
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_pushlightuserdata(L, self);
lua_rawget(L, -2); // objects[object_p] => The object
lua_pushnil(L);
lua_rawset(L, -3); // objects[object] = nil -> GC
lua_pushlightuserdata(L, self);
lua_pushnil(L);
lua_rawset(L, -3); // objects[object_p] = nil -> GC
lua_pop(L, 1); // pop objects
}
/* LUADOC
* Cancel a timer callback. Renders the object ready for GC.
function timer:clear()
*/
static int ltimer_clear(lua_State *L)
{
lue_data *self = toptimer(L);
if (self->timer)
{
lueRemTimer(getCtxt(L), self->timer);
self->timer = NULL;
}
lobj_clear(L, self);
return 0;
}
/* LUADOC
* Cancel a fileio callback. Renders the object ready for GC.
function fileio:clear()
*/
static int lfileh_clear(lua_State *L)
{
lue_data *self = topfileh(L);
if (self->file)
{
lueRemInput(getCtxt(L), self->file); // Works on both types
self->file = NULL;
}
lobj_clear(L, self);
return 0;
}
/* LUADOC
* Obtain the registered handler on the object
* @return The handler
function luevent_object:handler()
*/
static int luevent_handler(lua_State *L)
{
lue_data *self = lua_touserdata(L, 1);
if (self)
{
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_getfield(L, -1, "_objects");
lua_getfield(L, -2, "_callbacks");
if (lua_istable(L, -1) && lua_istable(L, -2))
{
lua_pushlightuserdata(L, self);
lua_rawget(L, -2); // The object
lua_rawget(L, -2); // callbacks[obj] -> closure
return 1;
}
}
return 0;
}
static int ltimer_gc(lua_State *L)
{
//STACKDUMP(L);
lue_data *self = toptimer(L);
if (self->timer)
{
lueRemTimer(getCtxt(L), self->timer);
self->timer = NULL;
}
return 0;
}
static int lfileh_gc(lua_State *L)
{
lue_data *self = topfileh(L);
if (self->file)
{
lueRemInput(getCtxt(L), self->file);
self->file = NULL;
}
return 0;
}
static int luevent_tostring(lua_State *L)
{
lue_data *self = lua_touserdata(L, 1);
lua_getmetatable(L, 1);
lua_getfield(L, -1, "_typename");
const char *t = lua_tostring(L, -1);
if (!self->timer && !self->file)
lua_pushfstring(L, "%s (cleared)", t);
else
lua_pushfstring(L, "%s (%p)", t, self);
return 1;
}
/* LUADOC
* Get the system time in hight resolution
* @return two numbers: seconds and milliseconds since the epoch
function luevent.systemTime()
*/
static int l_systemTime (lua_State *L)
{
struct timeval v;
gettimeofday(&v, (struct timezone *) NULL);
lua_pushinteger(L, v.tv_sec);
lua_pushinteger(L, v.tv_usec / 1000);
return 2; // num of results
}
static int l_run(lua_State * L)
{
lueRun(getCtxt(L));
return 0;
}
static int l_terminate(lua_State * L)
{
lueTerminate(getCtxt(L));
return 0;
}
static int l_cleanup(lua_State * L)
{
struct LUECtxt *ctxt = getCtxt(L);
lua_pushnil(L);
lua_setfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_gc(L, LUA_GCCOLLECT, 0); // Make sure all timers & schedules & callbacks are gone before context
lueCtxtDestroy(ctxt);
return 0;
}
static const luaL_Reg lueventlib[] = {
{"addFileIO", lfileh_add},
{"clearFileIO", lfileh_clear},
{"addTimer", ltimer_add},
{"clearTimer", ltimer_clear},
{"systemTime", l_systemTime},
{"run", l_run},
{"terminate", l_terminate},
{"__gc", l_cleanup},
{ NULL, NULL}
};
static const luaL_Reg ltimermeta[] = {
{"clear", ltimer_clear},
{"handler", luevent_handler},
{"__gc", ltimer_gc},
{"__tostring", luevent_tostring},
{ NULL, NULL}
};
static const luaL_Reg lfilehmeta[] = {
{"clear", lfileh_clear},
{"handler", luevent_handler},
{"__gc", lfileh_gc},
{"__tostring", luevent_tostring},
{ NULL, NULL}
};
int luaopen_luevent(lua_State * L)
{
struct LUECtxt *lueContext = lueCtxtCreate();
assert(lueContext);
luaL_register(L, lua_tostring(L, 1), lueventlib); // calls
lua_pushvalue(L, -1); // Dup
lua_setmetatable(L, -2); // Call GC at module cleanup.
l_newType(L, LUA_LTIMER_MT); // create metatable for timers
luaL_register(L, NULL, ltimermeta); // methods
lua_setfield(L, -2, "_meta_timer"); // easy access from test code
l_newType(L, LUA_LFILEH_MT); // create metatable for file handlers
luaL_register(L, NULL, lfilehmeta); // methods
lua_setfield(L, -2, "_meta_fileh"); // easy access from test code
lua_newtable(L); // Table[obj] -> Callback-closure
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, LUA_LUEOBJ);
lua_pushlightuserdata(L, lueContext);// Table[1] = lueContext
lua_rawseti(L, -2, 1);
lua_setfield(L, -2, "_lue_objects");// easy access from test code
lua_pop(L, 1); // pop module
return 0;
}
// vim: set sw=3 sts=3 et:
+1
View File
@@ -0,0 +1 @@
*.m4
+97
View File
@@ -0,0 +1,97 @@
/* libuEv - Private methods and data types, do not use directly!
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2015 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBUEV_PRIVATE_H_
#define LIBUEV_PRIVATE_H_
#include <stdio.h>
#include <sys/epoll.h>
#include "queue.h" /* OpenBSD queue.h > old GLIBC version */
/* I/O, timer, or signal watcher */
typedef enum {
UEV_CRON_TYPE = 1,
UEV_IO_TYPE,
UEV_SIGNAL_TYPE,
UEV_TIMER_TYPE,
} uev_type_t;
/* Event mask, used internally only. */
#define UEV_EVENT_MASK (UEV_READ | UEV_WRITE | UEV_PRI | UEV_HUP)
/* Main libuEv context type */
typedef struct {
int running;
int fd; /* For epoll() */
uint32_t errors;
LIST_HEAD(,uev) watchers;
} uev_ctx_t;
/* Forward declare due to dependencys, don't try this at home kids. */
struct uev;
/* This is used to hide all private data members in uev_t */
#define uev_private_t \
LIST_ENTRY(uev) link; /* For queue.h linked list */ \
\
int active; \
int events; \
\
/* Watcher callback with optional argument */ \
void (*cb)(struct uev *, void *, int); \
void *arg; \
\
/* Arguments for different watchers */ \
union { \
/* Cron watchers */ \
struct { \
time_t when; \
time_t interval; \
} c; \
\
/* Timer watchers, time in milliseconds */ \
struct { \
int timeout; \
int period; \
} t; \
} u; \
\
/* Watcher type */ \
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);
#endif /* LIBUEV_PRIVATE_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+648
View File
@@ -0,0 +1,648 @@
/* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues, and circular queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* A circle queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the list.
* A circle queue may be traversed in either direction, but has a more
* complex end of list detection.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
#else
#define _Q_INVALIDATE(a)
#endif
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST(head); \
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->slh_first; \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
_Q_INVALIDATE((elm)->field.sle_next); \
} \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST(head); \
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SIMPLEQ_FIRST(head); \
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
== NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
/*
* XOR Simple queue definitions.
*/
#define XSIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqx_first; /* first element */ \
struct type **sqx_last; /* addr of last next element */ \
unsigned long sqx_cookie; \
}
#define XSIMPLEQ_ENTRY(type) \
struct { \
struct type *sqx_next; /* next element */ \
}
/*
* XOR Simple queue access methods.
*/
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
(unsigned long)(ptr)))
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
#define XSIMPLEQ_END(head) NULL
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
#define XSIMPLEQ_FOREACH(var, head, field) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) != XSIMPLEQ_END(head); \
(var) = XSIMPLEQ_NEXT(head, var, field))
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
(var) = (tvar))
/*
* XOR Simple queue functions.
*/
#define XSIMPLEQ_INIT(head) do { \
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqx_next = (head)->sqx_first) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
(elm)->field.sqx_next)->field.sqx_next) \
== XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = \
XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* tail queue access methods
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
/*
* Circular queue definitions.
*/
#define CIRCLEQ_HEAD(name, type) \
struct name { \
struct type *cqh_first; /* first element */ \
struct type *cqh_last; /* last element */ \
}
#define CIRCLEQ_HEAD_INITIALIZER(head) \
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
#define CIRCLEQ_ENTRY(type) \
struct { \
struct type *cqe_next; /* next element */ \
struct type *cqe_prev; /* previous element */ \
}
/*
* Circular queue access methods
*/
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
#define CIRCLEQ_END(head) ((void *)(head))
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
#define CIRCLEQ_EMPTY(head) \
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
#define CIRCLEQ_FOREACH(var, head, field) \
for((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_NEXT(var, field))
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = CIRCLEQ_FIRST(head); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
(var) = (tvar))
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
for((var) = CIRCLEQ_LAST(head); \
(var) != CIRCLEQ_END(head); \
(var) = CIRCLEQ_PREV(var, field))
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = CIRCLEQ_LAST(head, headname); \
(var) != CIRCLEQ_END(head) && \
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Circular queue functions.
*/
#define CIRCLEQ_INIT(head) do { \
(head)->cqh_first = CIRCLEQ_END(head); \
(head)->cqh_last = CIRCLEQ_END(head); \
} while (0)
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
(elm)->field.cqe_prev = (listelm); \
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
(listelm)->field.cqe_next = (elm); \
} while (0)
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
(elm)->field.cqe_next = (listelm); \
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
(listelm)->field.cqe_prev = (elm); \
} while (0)
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
(elm)->field.cqe_next = (head)->cqh_first; \
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
if ((head)->cqh_last == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm); \
else \
(head)->cqh_first->field.cqe_prev = (elm); \
(head)->cqh_first = (elm); \
} while (0)
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.cqe_next = CIRCLEQ_END(head); \
(elm)->field.cqe_prev = (head)->cqh_last; \
if ((head)->cqh_first == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm); \
else \
(head)->cqh_last->field.cqe_next = (elm); \
(head)->cqh_last = (elm); \
} while (0)
#define CIRCLEQ_REMOVE(head, elm, field) do { \
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
(head)->cqh_last = (elm)->field.cqe_prev; \
else \
(elm)->field.cqe_next->field.cqe_prev = \
(elm)->field.cqe_prev; \
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
(head)->cqh_first = (elm)->field.cqe_next; \
else \
(elm)->field.cqe_prev->field.cqe_next = \
(elm)->field.cqe_next; \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
CIRCLEQ_END(head)) \
(head)->cqh_last = (elm2); \
else \
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
CIRCLEQ_END(head)) \
(head)->cqh_first = (elm2); \
else \
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
_Q_INVALIDATE((elm)->field.cqe_prev); \
_Q_INVALIDATE((elm)->field.cqe_next); \
} while (0)
#endif /* !_SYS_QUEUE_H_ */
+157
View File
@@ -0,0 +1,157 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <unistd.h> /* close(), read() */
#include "uev.h"
/**
* Create a signal watcher
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb Timer callback
* @param arg Optional callback argument
* @param signo Signal to watch for
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_signal_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo)
{
int fd;
sigset_t mask;
if (!w || !ctx) {
errno = EINVAL;
return -1;
}
w->fd = -1;
sigemptyset(&mask);
fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (fd < 0)
return -1;
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);
exit:
close(fd);
return -1;
}
return 0;
}
/**
* Reset a signal watcher
* @param w Watcher to reset
* @param signo New signal to watch for
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_signal_set(uev_t *w, int signo)
{
sigset_t mask;
/* Every watcher must be registered to a context */
if (!w || !w->ctx) {
errno = EINVAL;
return -1;
}
/* Remember for callbacks and start/stop */
w->signo = signo;
/* Handle stopped signal watchers */
if (w->fd < 0) {
/* Remove from internal list */
LIST_REMOVE(w, link);
if (uev_signal_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, signo))
return -1;
}
sigemptyset(&mask);
sigaddset(&mask, signo);
/* Block signals so that they aren't handled
according to their default dispositions */
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
return -1;
if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0)
return -1;
return uev_watcher_start(w);
}
/**
* Start a stopped signal watcher
* @param w Watcher to start (again)
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_signal_start(uev_t *w)
{
if (!w) {
errno = EINVAL;
return -1;
}
if (-1 != w->fd)
uev_signal_stop(w);
return uev_signal_set(w, w->signo);
}
/**
* Stop a signal watcher
* @param w Watcher to stop
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_signal_stop(uev_t *w)
{
if (uev_watcher_stop(w))
return -1;
close(w->fd);
w->fd = -1;
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
-80
View File
@@ -1,80 +0,0 @@
/**
* @file
* Micro event library libuevent.
* Test & showcase of libuevent
*
* (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
*/
#include <stdlib.h>
#include <stdio.h>
#include "libuevent.h"
static period;
static client_fd, server_fd, service_fd;
static struct LUETimerH *timeOut;
int readFd, writeFd;
static timeOutHdl(struct LUECtxt *ctxt, struct LUETimerH *handle, void *data)
{
timeOut = NULL;
printf("Timeout is exceeded %p\n", data);
lueRemFd(ctxt, readFd);
lueTerminate(ctxt);
}
static appTimeout(struct LUECtxt *ctxt, struct LUETimerH *handle, void *data)
{
printf("Lifetime is exceeded %p\n", data);
exit(1);
}
static int readCb(struct LUECtxt *ctxt, struct LUEFileEventH *handle, int fd, void *data)
{
if (timeOut) {
lueRemTimer(ctxt, timeOut);
timeOut = lueAddTimer(ctxt, 950, timeOutHdl, NULL);
}
char msg[50];
int cnt = read(readFd, msg, sizeof(msg));
//printf("READ %.*s %d\n", cnt, msg, cnt);
printf("%.*s.%d ", cnt, msg, cnt); fflush(stdout);
return 0;
}
static writeThread(struct LUECtxt *ctxt, struct LUETimerH *handle, void *data)
{
int cnt = (int) data;
char *msg = "TESTING";
write(writeFd, msg, cnt);
//printf("WRITE %.*s %d\n", cnt, msg, cnt);
printf("%d ", cnt); fflush(stdout);
period = cnt + 5;
lueAddTimer(ctxt, period * 100, writeThread, (void *) (cnt + 1));
}
main()
{
struct LUECtxt *ctxt = lueCtxtCreate();
timeOut = lueAddTimer(ctxt, 950, timeOutHdl, (void *)1);
lueAddTimer(ctxt, 5000, appTimeout, (void *)2);
lueAddTimer(ctxt, 500, writeThread, (void *) 1);
int fd[2];
pipe(fd);
readFd = fd[0];
writeFd = fd[1];
lueAddInput(ctxt, readFd, readCb, NULL);
lueRun(ctxt);
lueCtxtDestroy(ctxt);
printf("Period is %d must be 10: %s\n", period, 10 == period ? "OK" : "ERROR!");
exit(10 == period ? 0 : 1);
}
// vim: set sw=3 sts=3 et:
-76
View File
@@ -1,76 +0,0 @@
#!/usr/bin/env lua
--/usr/bin/env luajit
---
-- @file
-- Micro event library libuevent.
-- Test & showcase of libuevent
--
-- (C) Copyright 2012 flemming.madsen at madsensoft.dk. See libuevent.h
---
require "socket"
require "luevent"
local ADDR = "127.0.0.1"
local PORT = 42555
local timer
local period
local appTimer, readHdl, writeTimer
local function timeOut()
print("Timeout is exceeded")
appTimer:clear()
readHdl:clear()
writeTimer:clear()
luevent.terminate()
end
local function writeThread(sck, cnt)
sck:send(("TESTING"):sub(1, cnt))
period = cnt + 5
writeTimer = luevent.addTimer(100 * period, function() writeThread(sck, cnt + 1) end)
end
local function main()
appTimer = luevent.addTimer(5000, function()
print("Lifetime is exceeded")
os.exit(1);
end)
local isrv = assert(socket.bind(ADDR, PORT))
luevent.addFileIO(isrv:getfd(), function(self)
local isock = isrv:accept()
isock:settimeout(0)
readHdl = luevent.addFileIO(isock:getfd(), function(self)
-- Pipe readable
if timer then
timer:clear()
timer = luevent.addTimer(950, timeOut)
end
local str, err, prt = isock:receive(50)
if not str and err == "timeout" then str = prt end
io.write(str.."."..#str.." ") io.flush()
end)
self:clear() -- This is a oneshot
end)
timer = luevent.addTimer(950, timeOut)
local osck = socket.tcp() osck:settimeout(0) osck:connect(ADDR, PORT)
luevent.addFileIO(osck:getfd(), function(self)
writeThread(osck, 1)
self:clear() -- This is a oneshot
end, "w")
luevent.run()
print "DONE"
-- Only index '1' should be there now
for k,v in pairs(luevent._lue_objects) do print(k,v) end
package.loaded.luevent = nil
collectgarbage("collect")
print(("Period is %d must be 10: %s"):format(period, 10 == period and "OK" or "ERROR!"))
os.exit(period == 10 and 0 or 1)
end
main()
-- vim: set sw=3 sts=3 et nu:
+186
View File
@@ -0,0 +1,186 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <sys/timerfd.h>
#include <unistd.h> /* close(), read() */
#include "uev.h"
static void msec2tspec(int msec, struct timespec *ts)
{
if (msec) {
ts->tv_sec = msec / 1000;
ts->tv_nsec = (msec % 1000) * 1000000;
} else {
ts->tv_sec = 0;
ts->tv_nsec = 0;
}
}
/**
* Create and start a timer watcher
* @param ctx A valid libuEv context
* @param w Pointer to an uev_t watcher
* @param cb Callback function
* @param arg Optional callback argument
* @param timeout Timeout in milliseconds before @param cb is called
* @param period For periodic timers this is the period time that @param timeout is reset to
*
* For one-shot timers you set @param period to zero and only use @param
* timeout. For periodic timers you likely set @param timeout to either
* zero, to call it as soon as the event loop starts, or to the same
* value as @param period. When the timer expires, the @param cb
* is called, with the optional @param arg argument. A non-periodic
* timer ends its life there, while a periodic task's @param timeout is
* reset to the @param period and restarted.
*
* A timer is automatically started if the event loop is already
* running, otherwise it is kept on hold until triggered by calling
* uev_run().
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_timer_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period)
{
int fd;
if (timeout < 0 || period < 0) {
errno = ERANGE;
return -1;
}
fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
if (fd < 0)
return -1;
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);
exit:
close(fd);
return -1;
}
return 0;
}
/**
* Reset a timer
* @param w Watcher to reset
* @param timeout Timeout in milliseconds before @param cb is called
* @param period For periodic timers this is the period time that @param timeout is reset to
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_timer_set(uev_t *w, int timeout, int period)
{
/* Every watcher must be registered to a context */
if (!w || !w->ctx) {
errno = EINVAL;
return -1;
}
if (timeout < 0 || period < 0) {
errno = ERANGE;
return -1;
}
/* Handle stopped timers */
if (w->fd < 0) {
/* Timer already stopped */
if (!timeout && !period)
return 0;
/* Remove from internal list */
LIST_REMOVE(w, link);
if (uev_timer_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, timeout, period))
return -1;
}
w->u.t.timeout = timeout;
w->u.t.period = period;
if (w->ctx->running) {
struct itimerspec time;
msec2tspec(timeout, &time.it_value);
msec2tspec(period, &time.it_interval);
if (timerfd_settime(w->fd, 0, &time, NULL) < 0)
return 1;
}
return uev_watcher_start(w);
}
/**
* Start a stopped timer watcher
* @param w Watcher to start (again)
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_timer_start(uev_t *w)
{
if (!w) {
errno = EINVAL;
return -1;
}
if (-1 != w->fd)
uev_watcher_stop(w);
return uev_timer_set(w, w->u.t.timeout, w->u.t.period);
}
/**
* Stop and unregister a timer watcher
* @param w Watcher to stop
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_timer_stop(uev_t *w)
{
/* Stop kernel timer */
uev_timer_set(w, 0, 0);
if (uev_watcher_stop(w))
return -1;
/* Close timerfd, will have to be reopened again on reset */
close(w->fd);
w->fd = -1;
return 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+345
View File
@@ -0,0 +1,345 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <fcntl.h> /* O_CLOEXEC */
#include <string.h> /* memset() */
#include <sys/epoll.h>
#include <sys/signalfd.h> /* struct signalfd_siginfo */
#include <unistd.h> /* close(), read() */
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
static int _init(uev_ctx_t *ctx, int close_old)
{
int fd;
fd = epoll_create1(EPOLL_CLOEXEC);
if (fd < 0)
return -1;
if (close_old)
close(ctx->fd);
ctx->fd = fd;
return 0;
}
/* Simple check if a descriptor is still valid in the kernel */
static int is_valid_fd(int fd)
{
return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
}
/* Private to libuEv, do not use directly! */
int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events)
{
if (!ctx || !w) {
errno = EINVAL;
return -1;
}
w->ctx = ctx;
w->type = type;
w->active = 0;
w->fd = fd;
w->cb = cb;
w->arg = arg;
w->events = events;
return 0;
}
/* Private to libuEv, do not use directly! */
int uev_watcher_start(uev_t *w)
{
struct epoll_event ev;
if (!w || w->fd < 0) {
errno = EINVAL;
return -1;
}
if (w->active)
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;
w->active = 1;
/* Add to internal list for bookkeeping */
LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
return 0;
}
/* Private to libuEv, do not use directly! */
int uev_watcher_stop(uev_t *w)
{
if (!w) {
errno = EINVAL;
return -1;
}
if (!w->active)
return 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;
}
/**
* Create an event loop context
* @param ctx Pointer to an uev_ctx_t context to be initialized
*
* @return POSIX OK(0) on success, or non-zero on error.
*/
int uev_init(uev_ctx_t *ctx)
{
if (!ctx) {
errno = EINVAL;
return -1;
}
memset(ctx, 0, sizeof(*ctx));
LIST_INIT(&ctx->watchers);
return _init(ctx, 0);
}
/**
* Terminate the event loop
* @param ctx A valid libuEv context
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_exit(uev_ctx_t *ctx)
{
if (!ctx) {
errno = EINVAL;
return -1;
}
while (!LIST_EMPTY(&ctx->watchers)) {
uev_t *w = LIST_FIRST(&ctx->watchers);
/* Remove from internal list */
LIST_REMOVE(w, link);
if (!w->active)
continue;
switch (w->type) {
case UEV_CRON_TYPE:
case UEV_TIMER_TYPE:
uev_timer_stop(w);
break;
case UEV_SIGNAL_TYPE:
uev_signal_stop(w);
break;
case UEV_IO_TYPE:
uev_io_stop(w);
break;
}
}
ctx->running = 0;
close(ctx->fd);
ctx->fd = -1;
return 0;
}
/**
* Start the event loop
* @param ctx A valid libuEv context
* @param flags A mask of %UEV_ONCE and %UEV_NONBLOCK, or zero
*
* With @flags set to %UEV_ONCE the event loop returns after the first
* event has been served, useful for instance to set a timeout on a file
* descriptor. If @flags also has the %UEV_NONBLOCK flag set the event
* loop will return immediately if no event is pending, useful when run
* inside another event loop.
*
* @return POSIX OK(0) upon successful termination of the event loop, or
* non-zero on error.
*/
int uev_run(uev_ctx_t *ctx, int flags)
{
int result = 0, timeout = -1;
uev_t *w;
if (!ctx || ctx->fd < 0) {
errno = EINVAL;
return -1;
}
if (flags & UEV_NONBLOCK)
timeout = 0;
/* Start the event loop */
ctx->running = 1;
/* Start all dormant timers */
LIST_FOREACH(w, &ctx->watchers, link) {
if (UEV_CRON_TYPE == w->type)
uev_cron_set(w, w->u.c.when, w->u.c.interval);
if (UEV_TIMER_TYPE == w->type)
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 ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) {
if (!ctx->running)
break;
if (EINTR == errno)
continue; /* Signalled, try again */
/* Unrecoverable error, cleanup and exit with error. */
uev_exit(ctx);
return -1;
}
for (i = 0; ctx->running && i < nfds; i++) {
w = (uev_t *)events[i].data.ptr;
if (events[i].events & (EPOLLHUP | EPOLLERR)) {
ctx->errors++;
if (ctx->errors >= 42) {
uev_t *tmp, *retry = w;;
/* If not valid anymore, try to remove, ignore any errors. */
if (!is_valid_fd(w->fd))
uev_watcher_stop(w);
/* Must recreate epoll fd now ... */
if (_init(ctx, 1)) {
uev_exit(ctx);
return -2;
}
/* Restart watchers in new efd */
LIST_FOREACH_SAFE(w, &ctx->watchers, link, tmp) {
if (w->active) {
w->active = 0;
LIST_REMOVE(w, link);
uev_watcher_start(w);
}
}
uev_watcher_start(retry);
/* New efd, restart everything! */
ctx->errors = 0;
continue;
}
}
if (UEV_CRON_TYPE == w->type) {
uint64_t exp;
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
if (errno != ECANCELED) {
uev_exit(ctx);
return -3;
}
events[i].events = UEV_HUP;
}
if (!w->u.c.interval)
w->u.c.when = 0;
else
w->u.c.when += w->u.c.interval;
}
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);
}
if (UEV_TIMER_TYPE == w->type) {
if (!w->u.t.timeout)
uev_timer_stop(w);
}
}
if ((flags & UEV_ONCE) || LIST_EMPTY(&ctx->watchers))
break;
}
return result;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+90
View File
@@ -0,0 +1,90 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2015 Joachim Nilsson <troglobit()gmail!com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIBUEV_UEV_H_
#define LIBUEV_UEV_H_
#include "private.h"
/* Max. number of simulateneous events */
#define UEV_MAX_EVENTS 10
/* I/O events, for signals and timers revents will always be UEV_READ */
#define UEV_NONE 0
#define UEV_READ EPOLLIN
#define UEV_WRITE EPOLLOUT
#define UEV_PRI EPOLLPRI
#define UEV_HUP EPOLLHUP
/* Run flags */
#define UEV_ONCE 1
#define UEV_NONBLOCK 2
/* Event watcher */
typedef struct uev {
/* Private data for libuEv internal engine */
uev_private_t type;
/* Public data for users to reference */
int signo;
int fd;
uev_ctx_t *ctx;
} uev_t;
/* Generic callback for watchers */
typedef void (uev_cb_t)(uev_t *w, void *arg, int events);
/* Public interface */
int uev_init (uev_ctx_t *ctx);
int uev_exit (uev_ctx_t *ctx);
int uev_run (uev_ctx_t *ctx, int flags);
int uev_io_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events);
int uev_io_set (uev_t *w, int fd, int events);
int uev_io_start (uev_t *w);
int uev_io_stop (uev_t *w);
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);
int uev_timer_start (uev_t *w);
int uev_timer_stop (uev_t *w);
int uev_cron_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, time_t when, time_t interval);
int uev_cron_set (uev_t *w, time_t when, time_t interval);
int uev_cron_start (uev_t *w);
int uev_cron_stop (uev_t *w);
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);
int uev_signal_start (uev_t *w);
int uev_signal_stop (uev_t *w);
#endif /* LIBUEV_UEV_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/