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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 19:10:04 +02:00
Joachim Nilsson 82a1018dd2 Ignore TroglOS build files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 18:47:31 +02:00
Joachim Nilsson 58ec8e1d6f Simple example of stdin redirect problem, issue #12
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 18:46:55 +02:00
Joachim Nilsson 704d7d5612 Refactor, leverage internal API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 12:47:01 +02:00
Joachim Nilsson ce4f8d6ef5 Follow-up to ed5c9bb, fix examples Makefile after relocation
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 12:21:51 +02:00
Joachim Nilsson 6f5e6e5232 Make internal API more private
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-10-21 12:04:13 +02:00
Joachim Nilsson 9751be5859 Update ChangeLog diff URLs
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-22 16:26:02 +02:00
Joachim Nilsson 5e24736652 Bump version for v1.6.0 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-22 08:01:04 +02:00
Joachim Nilsson c8c84d12cd Add standard README.debian
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-21 21:29:56 +02:00
Joachim Nilsson 0bc466d112 Minor, reorder build vars a bit
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-21 21:29:21 +02:00
Joachim Nilsson 199e38c8dc debian/rules: Don't compress README.md and API.md in -dev package
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-21 21:28:54 +02:00
Joachim Nilsson f70693fd87 debian/docs: Also install new API.md docs
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-21 21:28:32 +02:00
Joachim Nilsson 777810cc83 Spellcheck ChangeLog(s)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-21 07:55:06 +02:00
Joachim Nilsson e0b0924135 Add src/* to distribution archive files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-21 07:53:30 +02:00
Joachim Nilsson 31fabccf7f README: Fix obvious error in example
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 13:38:39 +02:00
Joachim Nilsson f2d5192aa2 Reword note on time_t and GLIBC and add URL after user feedback
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 09:24:45 +02:00
Joachim Nilsson 891e641abe Sectionalize API docs better and update README ToC
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 09:04:54 +02:00
Joachim Nilsson b67c62045f Improve documentation for event flags on init and callback
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:59:39 +02:00
Joachim Nilsson 649f40e5d3 Reduce indentation in all code + shell examples
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:41:47 +02:00
Joachim Nilsson d92b4ce2ae Reduce indentation for very wide API desc.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:39:25 +02:00
Joachim Nilsson 653f605c25 Update doc string for possible I/O watcher events
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:38:21 +02:00
Joachim Nilsson fd6a2b02d8 Simplify comment
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:38:05 +02:00
Joachim Nilsson 310e8a7607 Try quoute at top of text
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:18:48 +02:00
Joachim Nilsson f7f14a4a64 Reduce indentation and remove '$' in shell build example
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:18:05 +02:00
Joachim Nilsson a6e185d4e9 Minor refactor of layout
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:15:31 +02:00
Joachim Nilsson 2f96d2fcd4 Move quote back to README
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:15:11 +02:00
Joachim Nilsson 25701b4fc4 API.md: We handle Y2038 on GLIBC systems, but use relative timers
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:14:12 +02:00
Joachim Nilsson cc7fdc747a Simplify and correct project name
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:05:29 +02:00
Joachim Nilsson 16892b0673 Move caveat about using signalfd from README to API doc
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-20 08:05:01 +02:00
Joachim Nilsson b6b4afc11b Update copyright years
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 09:09:29 +02:00
Joachim Nilsson 8f6a0a6847 README: Move around text a bit to get a better flow
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 09:06:44 +02:00
Joachim Nilsson 36599097d5 README: Add a simple example
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 09:05:08 +02:00
Joachim Nilsson ca7a99aba3 README: Simplify origin a bit
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 09:04:46 +02:00
Joachim Nilsson 4e0bb82899 Remove MOTIVATION.md, wrote another blog post about it instead
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 08:59:15 +02:00
Joachim Nilsson 77738130e3 Relocate API.md, again, and add to distribution archive and doc/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 08:59:15 +02:00
Joachim Nilsson 7a316468b5 Rename src/README.md --> src/API.md, add README symlink for GitHub
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 08:32:25 +02:00
Joachim Nilsson 10d69fdee7 Fix URLs in API document
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 08:30:47 +02:00
Joachim Nilsson 03ea12a41c Move background to a separate motivation document
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 08:29:43 +02:00
Joachim Nilsson 543b49fb08 Fix src/README.md URL
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 08:19:59 +02:00
Joachim Nilsson 17fd42fa87 Update and add ToC
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 08:18:29 +02:00
Joachim Nilsson 7d6a37e4c5 Move API documentation to src/README.md
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 08:16:20 +02:00
Joachim Nilsson ed5c9bb06c Relocate source fiiles to src/ subdirectory
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-19 08:12:21 +02:00
Joachim Nilsson aae505fb33 Bump version, v1.6.0-rc1
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-18 07:53:20 +02:00
Joachim Nilsson b290451eb0 Update ChangeLog(s) for upcoming v1.6.0 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-18 07:52:49 +02:00
Joachim Nilsson 1282e2e716 Fix #9: Y2038 fix for 32 bit GLIBC systems
This patch enables 64 bit time_t support on GLIBC based 32 bit systems.
See https://sourceware.org/glibc/wiki/Y2038ProofnessDesign for more on
this problem.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-09-18 07:51:59 +02:00
Joachim Nilsson 1d415cfd50 Refactor, further additions of syntax highlighting
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-05-27 17:52:41 +02:00
Joachim Nilsson 6ca04123e2 Try shell script highlighting
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-05-27 17:43:03 +02:00
Joachim Nilsson 0f5842e6fe Prettify verbatim text
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-05-27 17:42:01 +02:00
Joachim Nilsson 1450e7e48a Whitesapce fixes
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-05-27 17:39:02 +02:00
Joachim Nilsson ee74602502 Clarify 32 bit time_t limitations in libuEv
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-05-07 20:36:38 +02:00
Joachim Nilsson 37453b821a Minor, remove unnecessary braces for one-liner conditional
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-05-06 18:07:01 +02:00
Joachim Nilsson 00afc27e46 Merge pull request #10 from Martijn81/master
Add edgetrigger and oneshot event types
2017-05-06 18:03:46 +02:00
martijn81 48fc8a38bc indentation problem fixed. 2017-05-06 18:00:07 +02:00
martijn81 6354b33316 UEV_EVENT_MASK change 2017-05-06 17:48:05 +02:00
martijn81 e18d980606 Added some spaces. 2017-05-06 17:41:28 +02:00
martijn81 92cfe557ca Minor changes 2017-05-06 17:37:36 +02:00
martijn81 047cc44c83 Add edgetrigger and oneshot event types 2017-05-06 13:53:38 +02:00
Joachim Nilsson 2c675519a3 TODO: Generic UNIX backend
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-05-01 12:29:37 +02:00
Joachim Nilsson c136d199f4 Bump Debian revision
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-03-12 17:49:47 +01:00
Joachim Nilsson 4c648d23d6 Add make package to build .deb files
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-03-12 17:49:33 +01:00
Joachim Nilsson 163d7e06f7 debian/source/format: Change to native mode to allow building from GIT
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2017-03-12 17:49:17 +01:00
Joachim Nilsson f630ed7d4b bench.c: Include signal.h, not non-std sys/signal.h
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-12-15 23:34:45 +01:00
Joachim Nilsson 59542e3672 debian/: Update .gitignore file
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-29 08:32:19 +01:00
Joachim Nilsson d19cd6fd32 debian/: Support for building .deb packages
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-28 23:28:01 +01:00
Joachim Nilsson 2e6fac00bb Update ChangeLog and bump version for v1.5.2 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-27 20:33:01 +01:00
Joachim Nilsson 8a3ab17435 Fix make distcheck and missing tests/Makefile.in
This patch fixes the build regressions in libuev v1.5.1, which are due
to the new unit test framework.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-27 20:30:52 +01:00
Joachim Nilsson aa62e60137 Ensure check.h is included for all unit tests
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-27 20:30:19 +01:00
Joachim Nilsson 69c5b7f538 Update ChangeLog and bump version for v1.5.1 release
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-27 18:07:37 +01:00
Joachim Nilsson e765e42eb9 Verify that a timer can be stopped *before* uev_run()
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-25 17:28:15 +01:00
Joachim Nilsson 56a2e7ff30 Unit test for new active API
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-23 23:20:48 +01:00
Joachim Nilsson b6f92e2b15 New uev_*_active() API: check if watcher is active
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-23 23:18:33 +01:00
Joachim Nilsson 604afc6cec Travis-CI: Run new unit tests
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:33:17 +01:00
Joachim Nilsson 73ae8ba098 complete: Update test for unit test framework
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:30:40 +01:00
Joachim Nilsson 6a19fa7e93 cronrun: Update cron test to verify interval as well
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:27:49 +01:00
Joachim Nilsson 737ceb4398 Travis-CI: Minor cleanup
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:19:45 +01:00
Joachim Nilsson a8798ea6a7 configure: Remove --enable-examples option
Now unit tests and examples are separate things.  Unit tests build on
"make check" and examples must be built separately by the user.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:18:45 +01:00
Joachim Nilsson d976171a14 Minor cleanup
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:18:07 +01:00
Joachim Nilsson 398251ade6 Make bench a noinst program
Must now be run separately, but will be built along with the library.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:17:26 +01:00
Joachim Nilsson 628565b746 bench.c: Uncomment Xcount log message
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:13:03 +01:00
Joachim Nilsson de4d969ca0 Move complete test from examples/ --> tests/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:12:47 +01:00
Joachim Nilsson 389c3b27ab Move cronrun test from examples/ --> tests/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:09:38 +01:00
Joachim Nilsson 03a29440ef Move signal test from examples/ --> tests/
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 01:06:48 +01:00
Joachim Nilsson 9b3af80082 Add .gitignore for unit tests
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 00:37:39 +01:00
Joachim Nilsson 8f2c9f7f0e Add timer.c unit test
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 00:36:21 +01:00
Joachim Nilsson 6a21b7a90b Add unit testing framework from libite (-lite)
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
2016-11-22 00:35:19 +01:00
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
47 changed files with 1356 additions and 445 deletions

No files matched your search

+8 -1
View File
@@ -6,8 +6,15 @@
*.a *.a
*.la *.la
*.lo *.lo
*.html
.config
.deps .deps
.libs .libs
.unpacked
ID
GTAGS
GRTAGS
GPATH
compile compile
config.* config.*
configure configure
@@ -23,7 +30,7 @@ aclocal.m4
ar-lib ar-lib
autom4te.cache autom4te.cache
bench bench
test
libuev*.tar.* libuev*.tar.*
libuev.pc libuev.pc
!libuev.pc.in !libuev.pc.in
test-driver
+4 -5
View File
@@ -2,7 +2,7 @@
# Defaults to GNU GCC and autotools: ./configure && make && make test # Defaults to GNU GCC and autotools: ./configure && make && make test
language: c language: c
# We don't need to install packages, use dockerized build, quicker # Use docker for quicker builds, it now allows https://docs.travis-ci.com/user/apt/
sudo: false sudo: false
env: env:
@@ -17,14 +17,13 @@ addons:
name: "troglobit/libuev" name: "troglobit/libuev"
description: "libuEv | Simple event loop for Linux" description: "libuEv | Simple event loop for Linux"
notification_email: troglobit@gmail.com notification_email: troglobit@gmail.com
build_command_prepend: "./autogen && ./configure --enable-examples" build_command_prepend: "./autogen && ./configure"
build_command: "make clean all" build_command: "make clean all"
branch_pattern: dev branch_pattern: dev
# We don't use configure, so we must define our own build script.
script: script:
- ./autogen.sh - ./autogen.sh
- ./configure --disable-silent-rules --enable-examples - ./configure --disable-silent-rules
- make clean - make clean
- make -j5 - make -j5
- make test - make check
+315
View File
@@ -0,0 +1,315 @@
API Documentation
=================
* [Introduction](#introduction)
* [Overview](#overview)
* [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)
> “Event driven software improves concurrency” -- [Dave Zarzycki, Apple][]
Introduction
------------
The C API to [libuEv][], listed in `uev/uev.h`, handles three different
types of events: I/O (pipes, sockets, message queues, etc.), timers, and
signals. The [Summary](#summary) details a slight caveat on signals.
Notice the *lack of support for regular files* and directories. This is
a limitation of the underlying Linux `epoll` interface which will return
`EPERM` for regular files. Except for the special case when `stdin` is
redirected from the command line. See `examples/redirect.c` for more on
this particular case.
Timers can be either relative, timeout in milliseconds, or absolute with
a time given in `time_t`, see `mktime()` et al. Absolute timers are
called cron timers and their callbacks get an `UEV_HUP` error event if
the wall clock changes, either via NTP or user input.
**NOTE:** On some systems, embedded in particular, `time_t` is a 32-bit
integer that wraps around in the year 2038. A [GLIBC workaround][]
(`-D_TIME_BITS=64`) protects those systems, but users of other C
libraries have no known workarounds. It is strongly recommended to
use relative timers as often as possible.
Overview
--------
```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_READ and/or UEV_WRITE with any of
* the optional UEV_HUP, UEV_RDHUP, or UEV_PRI for urgent read data.
*/
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 *MUST* be non-blocking!
* events combination of the main flags: UEV_READ, UEV_WRITE,
* UEV_EDGE, UEV_ONESHOT
*/
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
result = uev_run(&ctx, UEV_NONE);
```
With `flags` set to `UEV_ONCE` the event loop returns as soon as it has
served the first event. If `flags` is set to `UEV_ONCE | UEV_NONBLOCK`
the event loop returns immediately if no event is available.
```
if (result < 0)
errx(result, "Unrecoverable event loop error, error %d", result);
```
If the call to `uev_run()` fails you should notify the user somehow.
libuEv fails if there is an invalid pointer, if `uev_init()` was not
called, or recurring `epoll()` errors thare are impossible to recover
from should occur. This is true for individual watchers as well, in
particular signal and timer watchers which can fail in miserable ways.
**Note:** libuEv handles many types of errors, stream close, or peer
shutdowns internally, but also lets the callback run. This is useful
for stateful connections to be able to detect EOF.
### 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:** a certain amount of care is needed when dealing with APIs
that employ 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. For more details on this issue, see
[this article][4] at [lwn.net](http://lwn.net).
Using -luev
-----------
LibuEv is by default installed as a library with a few header files, you
should only ever need to include one:
```C
#include <uev/uev.h>
```
The output from the `pkg-config` tool holds no surprises:
```sh
$ 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`:
```sh
# Check for required libraries
PKG_CHECK_MODULES([uev], [libuev >= 1.4.0])
```
and in your `Makefile.am`:
```sh
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;
/*
* Called on any joystick event
*
* Note: We handle errors here by exiting, for a background daemon with
* multiple watchers you may want another approach.
*/
static void joystick_cb(uev_t *w, void *arg, int events)
{
if (UEV_ERROR == events) {
/* Possibly joystick was unplugged */
warn("Unrecoverable error, exiting");
uev_exit(w->ctx);
}
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
```sh
$ 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].
[1]: http://libevent.org
[2]: http://software.schmorp.de/pkg/libev.html
[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
[LibuEv]: https://github.com/troglobit/libuev
[GLIBC workaround]: https://sourceware.org/glibc/wiki/Y2038ProofnessDesign
[Dave Zarzycki, Apple]: http://www.youtube.com/watch?v=cD_s6Fjdri8
+71 -7
View File
@@ -4,6 +4,69 @@ Change Log
All notable changes to the project are documented in this file. All notable changes to the project are documented in this file.
[v2.0.0][] - 2017-11-11
-----------------------
Beware, this is a major release, introducing incompatible changes to the
failure modes of `uev_run()` and watcher callbacks. Most users will most
likely not notice any difference, but please read on.
### Changes
- `uev_run()` no longer exits the main event loop if an unrecoverable
error with a watcher occurs. Instead, the watcher is disabled and the
callback is run one last time with `events` set to `UEV_ERROR`.
- Watcher callbacks must handle `UEV_ERROR` conditions. This pertains
in particular to signal and timer watchers.
- Examples and API docs updated with the new failure modes.
[v1.6.0][] - 2017-09-18
-----------------------
### Changes
- Support for edge triggered and oneshot event types
- Add `make package` build target to trigger a `.deb` package build
- Support 64 bit `time_t` on 32 bit GLIBC systems
### Fixes
- `bench.c`: Use `signal.h`, not non-standard `sys/signal.h`
[v1.5.2][] - 2016-11-27
-----------------------
### Fixes
- Fix build regression in v1.5.1
[v1.5.1][] - 2016-11-27
-----------------------
### Changes
- Add support for checking if a watcher is active.
- Refactor unit testing framework
[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 [v1.4.1][] - 2016-04-22
----------------------- -----------------------
@@ -233,7 +296,14 @@ v0.0.1 - 2012-03-17
Lua users mailing list. Lua users mailing list.
[UNRELEASED]: https://github.com/troglobit/libuev/compare/v1.4.0...HEAD [UNRELEASED]: https://github.com/troglobit/libuev/compare/v2.0.0...HEAD
[v2.0.0]: https://github.com/troglobit/libuev/compare/v1.6.0...v2.0.0
[v1.6.0]: https://github.com/troglobit/libuev/compare/v1.5.2...v1.6.0
[v1.5.2]: https://github.com/troglobit/libuev/compare/v1.5.1...v1.5.2
[v1.5.1]: https://github.com/troglobit/libuev/compare/v1.5.0...v1.5.1
[v1.5.0]: https://github.com/troglobit/libuev/compare/v1.4.2...v1.5.0
[v1.4.2]: https://github.com/troglobit/libuev/compare/v1.4.1...v1.4.2
[v1.4.1]: https://github.com/troglobit/libuev/compare/v1.4.0...v1.4.1
[v1.4.0]: https://github.com/troglobit/libuev/compare/v1.3.1...v1.4.0 [v1.4.0]: https://github.com/troglobit/libuev/compare/v1.3.1...v1.4.0
[v1.3.1]: https://github.com/troglobit/libuev/compare/v1.3.0...v1.3.1 [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.3.0]: https://github.com/troglobit/libuev/compare/v1.2.3...v1.3.0
@@ -258,9 +328,3 @@ Lua users mailing list.
[libuevent]: https://code.google.com/p/libuevent/ [libuevent]: https://code.google.com/p/libuevent/
[Flemming Madsen]: http://www.madsensoft.dk [Flemming Madsen]: http://www.madsensoft.dk
[Initial announcement]: http://lua-users.org/lists/lua-l/2012-03/msg00510.html [Initial announcement]: http://lua-users.org/lists/lua-l/2012-03/msg00510.html
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+1 -1
View File
@@ -1,5 +1,5 @@
Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk> Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com> Copyright (c) 2013-2017 Joachim Nilsson <troglobit()gmail!com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+8 -35
View File
@@ -1,39 +1,12 @@
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src tests
DIST_SUBDIRS = src examples tests
doc_DATA = API.md README.md LICENSE
EXTRA_DIST = API.md README.md LICENSE AUTHORS ChangeLog.md
lib_LTLIBRARIES = libuev.la ## Generate .deb package
pkgincludedir = $(includedir)/uev package:
pkginclude_HEADERS = uev.h private.h queue.h @dpkg-buildpackage -uc -us -B
libuev_la_SOURCES = uev.c io.c timer.c signal.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:1: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 ## Generate MD5 checksum file
MD5 = md5sum MD5 = md5sum
@@ -61,7 +34,7 @@ release-hook:
fi fi
## Target to run when building a release ## Target to run when building a release
release: dist release-hook md5-dist release: distcheck release-hook md5-dist
@for file in $(DIST_ARCHIVES); do \ @for file in $(DIST_ARCHIVES); do \
printf "$$file \tDistribution tarball\n"; \ printf "$$file \tDistribution tarball\n"; \
printf "$$file.md5\t"; cat $$file.md5 | cut -f1 -d' '; \ printf "$$file.md5\t"; cat $$file.md5 | cut -f1 -d' '; \
+51 -300
View File
@@ -3,355 +3,106 @@ libuEv | Simple event loop for Linux
[![Travis Status][]][Travis] [![Coverity Status][]][Coverity Scan] [![Travis Status][]][Travis] [![Coverity Status][]][Coverity Scan]
Table of Contents
-----------------
* [Introduction](#introduction) * [Introduction](#introduction)
* [API](#api) * [API](API.md#overview)
* [Create an Event Context](#create-an-event-context) * [Create an Event Context](API.md#create-an-event-context)
* [Register an Event Watcher](#register-an-event-watcher) * [Register an Event Watcher](API.md#register-an-event-watcher)
* [Start Event Loop](#start-event-loop) * [Start Event Loop](API.md#start-event-loop)
* [Summary](#summary) * [Summary](API.md#summary)
* [Using -luev](#using--luev) * [Using -luev](API.md#using--luev)
* [Joystick Example](#joystick-example) * [Joystick Example](API.md#joystick-example)
* [Build & Install](#build--install) * [Build & Install](#build--install)
* [Origin & References](#origin--references) * [Origin & References](#origin--references)
> **NOTE:** Incompatible failure mode changes in v2.0 compared to v1.x!
Introduction Introduction
------------ ------------
> “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.
[libuEv][] is a simple event loop in the style of the more established [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 [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 *u* (micro) in the name refers to both the small feature set and the
small size overhead impact of the library. The primary target of small size overhead impact of the library.
[libuEv][] is a modern Linux system.
Experienced developers may appreciate that [libuEv][] is built on top of Failure mode changes introduced in v2.0 may affect users of v1.x, See
modern Linux APIs: epoll, timerfd and signalfd. Note, a certain amount the [ChangeLog][] for the full details.
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 Example
--- -------
The C interface to [libuEv][] is listed in `uev.h`. It handles three The library documentation is available as a [separate document](API.md).
different types of events: I/O (files, sockets, message queues, etc.),
timers, and signals. With a slight caveat on signals detailed below
in the [Summary](#summary).
```C ```C
#include <stdio.h>
#include <uev/uev.h>
/* static void cb(uev_t *w, void *arg, int events)
* Callback example, arg is passed from watcher's *_init() {
* w->fd holds the file descriptor, events is set by libuEv puts("Every other second");
* to indicate if any of UEV_READ and/or UEV_WRITE is ready. }
*/
void callback (uev_t *w, void *arg, int events);
/* Event loop: Notice the use of flags! */ int main(void)
int uev_init (uev_ctx_t *ctx); {
int uev_exit (uev_ctx_t *ctx); uev_t timer;
int uev_run (uev_ctx_t *ctx, int flags); /* UEV_NONE, UEV_ONCE, and/or UEV_NONBLOCK */ uev_ctx_t ctx;
/* I/O watcher: fd is non-blocking, events is UEV_READ and/or UEV_WRITE */
int uev_io_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events);
int uev_io_set (uev_t *w, int fd, int events);
int uev_io_start (uev_t *w);
int uev_io_stop (uev_t *w);
/* Timer watcher: timeout and period in milliseconds */
int uev_timer_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period);
int uev_timer_set (uev_t *w, int timeout, int period); /* Change timeout or period */
int uev_timer_start (uev_t *w); /* Restart a stopped timer */
int uev_timer_stop (uev_t *w); /* Stop a timer */
/* Signal watcher: signo is the signal to wait for, e.g., SIGTERM */
int uev_signal_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo);
int uev_signal_set (uev_t *w, int signo); /* Change signal to wait for */
int uev_signal_start(uev_t *w); /* Restart a stopped signal watcher */
int uev_signal_stop (uev_t *w); /* Stop signal watcher */
uev_init(&ctx);
/* Set up a timer watcher to call cb() every other second */
uev_timer_init(&ctx, &timer, cb, NULL, 0, 2 * 1000);
return uev_run(&ctx, 0);
}
``` ```
### 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 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 libuEv use the GNU configure and build system. To try out the bundled
examples, use the `--enable-examples` switch to the `configure` script. examples, use the `--enable-examples` switch to the `configure` script.
There is also a limited unit test suite that can be useful to learn how
the library works.
./configure ```sh
make all ./configure
make test make -j5
make install make test
sudo make install-strip
sudo ldconfig
```
The resulting .so file is ~14 kiB (<kbd>make install-strip</kbd>). The resulting .so file is ~14 kiB.
Origin & References Origin & References
------------------- -------------------
[libuEv][] was originally based on [LibUEvent][8] by [Flemming Madsen][] [libuEv][] originates from [LibUEvent][8] by [Flemming Madsen][], but
but has been completely rewritten with a much clearer API. Now more with a rewritten and much cleaner API. It is now more similar to the
similar to the famous [libev][2] by [Mark Lehmann][]. Another small famous [libev][2] by [Mark Lehmann][]. Another library inspiring this
event library used for inspiration is the very small [Picoev][9] by project is [picoev][9] by [Oku Kazuho][].
[Oku Kazuho][].
[libuEv][] is developed and maintained by [Joachim Nilsson][]. It is
built for and developed on GNU/Linux systems, patches to support *BSD
and its [kqueue][] interface are most welcome.
[libuEv][] is developed and maintained by [Joachim Nilsson][].
[1]: http://libevent.org [1]: http://libevent.org
[2]: http://software.schmorp.de/pkg/libev.html [2]: http://software.schmorp.de/pkg/libev.html
[3]: http://unix.com/man-page/All/3x/XtDispatchEvent [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/ [8]: http://code.google.com/p/libuevent/
[9]: https://github.com/kazuho/picoev [9]: https://github.com/kazuho/picoev
[ChangeLog]: https://github.com/troglobit/libuev/blob/master/ChangeLog.md
[Travis]: https://travis-ci.org/troglobit/libuev [Travis]: https://travis-ci.org/troglobit/libuev
[Travis Status]: https://travis-ci.org/troglobit/libuev.png?branch=master [Travis Status]: https://travis-ci.org/troglobit/libuev.png?branch=master
[Coverity Scan]: https://scan.coverity.com/projects/3846 [Coverity Scan]: https://scan.coverity.com/projects/3846
[Coverity Status]: https://scan.coverity.com/projects/3846/badge.svg [Coverity Status]: https://scan.coverity.com/projects/3846/badge.svg
[LibuEv]: https://github.com/troglobit/libuev [LibuEv]: https://github.com/troglobit/libuev
[kqueue]: https://github.com/mheily/libkqueue
[Oku Kazuho]: https://github.com/kazuho [Oku Kazuho]: https://github.com/kazuho
[Mark Lehmann]: http://software.schmorp.de [Mark Lehmann]: http://software.schmorp.de
[Joachim Nilsson]: http://troglobit.com [Joachim Nilsson]: http://troglobit.com
[Flemming Madsen]: http://www.madsensoft.dk [Flemming Madsen]: http://www.madsensoft.dk
[Dave Zarzycki, Apple]: http://www.youtube.com/watch?v=cD_s6Fjdri8 [Dave Zarzycki, Apple]: http://www.youtube.com/watch?v=cD_s6Fjdri8
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->
+5 -1
View File
@@ -1,5 +1,9 @@
LibuEv TODO LibuEv TODO
=========== ===========
* Make port to *BSD kqueue API, http://en.wikipedia.org/wiki/Kqueue * Add standard UNIX backend with select() and timer_create()
See SMCRoute and toolbox for examples.
* 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
+3 -8
View File
@@ -1,19 +1,14 @@
AC_INIT(libuev, 1.4.1, https://github.com/troglobit/libuev/issues) AC_INIT(libuev, 2.0.0, https://github.com/troglobit/libuev/issues)
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
AC_CONFIG_SRCDIR(uev.c) AC_CONFIG_SRCDIR(src/uev.c)
AM_CONFIG_HEADER(config.h) AM_CONFIG_HEADER(config.h)
AC_CONFIG_FILES([Makefile libuev.pc examples/Makefile]) AC_CONFIG_FILES([Makefile src/libuev.pc src/Makefile examples/Makefile tests/Makefile])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC AC_PROG_CC
AM_PROG_AR AM_PROG_AR
LT_INIT 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 AC_OUTPUT
+8
View File
@@ -0,0 +1,8 @@
tmp/*
libuev/*
libuev*
autoreconf*
debhelper*
files
!*.dirs
!*.install
+14
View File
@@ -0,0 +1,14 @@
libuEv for Debian
-----------------
This is the upstream pre-packaged version of libuEv. It consists of two
.deb packages: one with only the .so library and one -dev package with
headers and the .a library for static linking. It builds on any Debian
based operating system, e.g. Debian, Ubuntu, Linux Mint, etc.
Recommended reading is the upstream README and API docs.
less /usr/share/doc/libuev-dev/README.md
zless /usr/share/doc/libuev-dev/API.md.gz
-- Joachim Nilsson <troglobit@gmail.com> Thu, 21 Sep 18:10, 2017
+31
View File
@@ -0,0 +1,31 @@
libuev (2.0.0-1) unstable; urgency=medium
* Major new upstream version, possibly incompatible changes to both
uev_run() and how watchers are called in various failure modes.
- uev_run() no longer exits the main event loop if an unrecoverable
error with a watcher occurs. Instead, the watcher is disabled and
the callback is run one last time with events set to UEV_ERROR.
- Watcher callbacks must handle UEV_ERROR conditions. This pertains
in particular to signal and timer watchers.
-- Joachim Nilsson <troglobit@gmail.com> Sat, 11 Nov 2017 16:31:46 +0100
libuev (1.6.0-1) unstable; urgency=medium
* Support for edge triggered and oneshot event types
* Support for 64 bit time_t on 32 bit GLIBC systems
-- Joachim Nilsson <troglobit@gmail.com> Mon, 18 Sep 2017 07:49:23 +0200
libuev (1.5.2-2) unstable; urgency=low
* Minor packaging fixes.
-- Joachim Nilsson <troglobit@gmail.com> Sun, 12 Mar 2017 17:48:00 +0100
libuev (1.5.2-1) unstable; urgency=low
* Initial release.
-- Joachim Nilsson <troglobit@gmail.com> Mon, 28 Nov 2016 22:53:35 +0100
+1
View File
@@ -0,0 +1 @@
config.log
+1
View File
@@ -0,0 +1 @@
9
+39
View File
@@ -0,0 +1,39 @@
Source: libuev
Priority: optional
Maintainer: Joachim Nilsson <troglobit@gmail.com>
Build-Depends: dpkg-dev (>= 1.14.9), debhelper (>= 9), dh-autoreconf
Standards-Version: 3.9.6
Section: libs
Homepage: https://github.com/troglobit/libuev/
Vcs-Git: https://github.com/troglobit/libuev.git
Vcs-Browser: https://github.com/troglobit/libuev/
Package: libuev-dev
Priority: extra
Section: libdevel
Architecture: any
Depends: ${misc:Depends}, libuev (= ${binary:Version})
Description: static library, header files, and docs for libuev
Static library, header files, and documentation for libuev.
.
libuEv is a simple event loop in the style of the more established
libevent, libev and the venerable Xt(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.
Package: libuev
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: Lightweight event loop library
libuEv is a simple event loop in the style of the more established
libevent, libev and the venerable Xt(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.
+26
View File
@@ -0,0 +1,26 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Source: https.//github.com/troglobit/libuev/
Files: *
Copyright: 2013-2017 Joachim Nilsson <troglobit@gmail.com>
License: MIT
Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
Copyright (c) 2013-2017 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.
Vendored
+2
View File
@@ -0,0 +1,2 @@
API.md
README.md
+2
View File
@@ -0,0 +1,2 @@
usr/lib
usr/include
+6
View File
@@ -0,0 +1,6 @@
usr/include/uev/uev.h
usr/include/uev/private.h
usr/include/uev/queue.h
usr/lib/*/lib*.a
usr/lib/*/lib*.so
usr/lib/*/pkgconfig/lib*.pc
+1
View File
@@ -0,0 +1 @@
usr/lib
+1
View File
@@ -0,0 +1 @@
usr/lib/*/lib*.so.*
Vendored Executable
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/make -f
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
%:
dh $@ --with autoreconf
override_dh_compress:
dh_compress -A -X.md
+1
View File
@@ -0,0 +1 @@
3.0 (native)
+5
View File
@@ -0,0 +1,5 @@
joystick
signal
cronrun
test
redirect
+3 -5
View File
@@ -1,5 +1,3 @@
noinst_PROGRAMS = signal test joystick noinst_PROGRAMS = joystick redirect
AM_CPPFLAGS = -I$(top_srcdir) AM_CPPFLAGS = -I$(top_srcdir)/src
AM_LDFLAGS = -L$(top_srcdir) LDADD = $(top_srcdir)/src/libuev.la
LIBS = $(LTLIBINTL)
LDADD = $(top_srcdir)/libuev.la
+8 -1
View File
@@ -1,6 +1,6 @@
/* Simple example of how to use libuEv with Linux joystick API /* Simple example of how to use libuEv with Linux joystick API
* *
* Copyright (c) 2014-2016 Joachim Nilsson <troglobit()gmail!com> * Copyright (c) 2014-2017 Joachim Nilsson <troglobit()gmail!com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -37,8 +37,15 @@ struct js_event {
uint8_t number; /* axis/button number */ uint8_t number; /* axis/button number */
} e; } e;
/*
* Always check for UEV_ERROR in watcher callback!
* Possibly joystick was unplugged
*/
static void joystick_cb(uev_t *w, void *arg, int events) static void joystick_cb(uev_t *w, void *arg, int events)
{ {
if (UEV_ERROR == events)
errx(EBADF, "Unrecoverable error, exiting");
if (read(w->fd, &e, sizeof(e)) < 0) if (read(w->fd, &e, sizeof(e)) < 0)
errx(errno, "Failed reading joystick event"); errx(errno, "Failed reading joystick event");
+67
View File
@@ -0,0 +1,67 @@
/* Example of how to handle stdin redirect
*
* The problem is non-trivial since Linux epoll, which libuEv uses, does
* not support I/O watchers for regular files or directories.
*
* This works:
* echo foo | redirect
* This doesn't:
* redirect < foo.txt
*
* However, with a little bit of hacking libuEv can be made to handle
* reading from stdin as a special case.
*/
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include "src/uev.h"
void process_stdin(uev_t *w, void *arg, int events)
{
char buf[256];
int len;
if (UEV_ERROR == events)
errx(EBADF, "Unrecoverable error, exiting");
len = read(w->fd, buf, sizeof(buf));
if (len == -1) {
warn("Error reading from stdin");
return;
}
if (len == 0) {
warnx("Connection closed.");
return;
}
printf("Read %d bytes\n", len);
if (write(STDOUT_FILENO, buf, len) != len)
warn("Failed writing to stdout");
}
int main(int argc, char **argv)
{
int ret;
uev_t watcher;
uev_ctx_t ctx;
uev_init(&ctx);
ret = uev_io_init(&ctx, &watcher, process_stdin, NULL, STDIN_FILENO, UEV_READ);
if (ret)
err(errno, "Failed setting up STDIN watcher");
return uev_run(&ctx, 0);
}
/**
* Local Variables:
* compile-command: "make redirect; echo hej > hej.txt; ./redirect < hej.txt"
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+14
View File
@@ -0,0 +1,14 @@
lib_LTLIBRARIES = libuev.la
libuev_la_SOURCES = uev.c io.c timer.c signal.c cron.c
libuev_la_CPPFLAGS = -D_GNU_SOURCE -D_TIME_BITS=64
libuev_la_CFLAGS = -W -Wall -Wextra
libuev_la_LDFLAGS = $(AM_LDFLAGS) -version-info 2:0:0
noinst_PROGRAMS = bench
bench_CPPFLAGS = -D_GNU_SOURCE
bench_LDADD = libuev.la
pkgconfigdir = $(libdir)/pkgconfig
pkgincludedir = $(includedir)/uev
pkgconfig_DATA = libuev.pc
pkginclude_HEADERS = uev.h private.h queue.h
+1
View File
@@ -0,0 +1 @@
../API.md
+3 -2
View File
@@ -44,11 +44,11 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/signal.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
@@ -140,7 +140,8 @@ static struct timeval *run_once(uev_ctx_t *ctx)
gettimeofday(&te, NULL); gettimeofday(&te, NULL);
// if (xcount != count) fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count); if (xcount != count)
fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count);
} }
timersub(&te, &ta, &ta); timersub(&te, &ta, &ta);
+165
View File
@@ -0,0 +1,165 @@
/* libuEv - Micro event loop library
*
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2017 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:
*/
+9 -5
View File
@@ -1,7 +1,7 @@
/* libuEv - Micro event loop library /* libuEv - Micro event loop library
* *
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk> * Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com> * Copyright (c) 2013-2017 Joachim Nilsson <troglobit()gmail!com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -33,7 +33,7 @@
* @param cb I/O callback * @param cb I/O callback
* @param arg Optional callback argument * @param arg Optional callback argument
* @param fd File descriptor to watch, or -1 to register an empty watcher * @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 * @param events Events to watch for: %UEV_READ, %UEV_WRITE, %UEV_EDGE, %UEV_ONESHOW
* *
* @return POSIX OK(0) or non-zero with @param errno set on error. * @return POSIX OK(0) or non-zero with @param errno set on error.
*/ */
@@ -44,10 +44,10 @@ int uev_io_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int e
return -1; return -1;
} }
if (uev_watcher_init(ctx, w, UEV_IO_TYPE, cb, arg, fd, events)) if (_uev_watcher_init(ctx, w, UEV_IO_TYPE, cb, arg, fd, events))
return -1; return -1;
return uev_watcher_start(w); return _uev_watcher_start(w);
} }
/** /**
@@ -60,6 +60,10 @@ int uev_io_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int e
*/ */
int uev_io_set(uev_t *w, int fd, int events) int uev_io_set(uev_t *w, int fd, int events)
{ {
if ((events & UEV_ONESHOT) && _uev_watcher_active(w))
return _uev_watcher_rearm(w);
/* Ignore any errors, only to clean up anything lingering ... */ /* Ignore any errors, only to clean up anything lingering ... */
uev_io_stop(w); uev_io_stop(w);
@@ -85,7 +89,7 @@ int uev_io_start(uev_t *w)
*/ */
int uev_io_stop(uev_t *w) int uev_io_stop(uev_t *w)
{ {
return uev_watcher_stop(w); return _uev_watcher_stop(w);
} }
/** /**
View File
File renamed without changes.
+25 -13
View File
@@ -31,13 +31,14 @@
/* I/O, timer, or signal watcher */ /* I/O, timer, or signal watcher */
typedef enum { typedef enum {
UEV_IO_TYPE = 1, UEV_CRON_TYPE = 1,
UEV_TIMER_TYPE, UEV_IO_TYPE,
UEV_SIGNAL_TYPE, UEV_SIGNAL_TYPE,
UEV_TIMER_TYPE,
} uev_type_t; } uev_type_t;
/* Event mask, used internally only. */ /* Event mask, used internally only. */
#define UEV_EVENT_MASK (UEV_READ | UEV_WRITE | UEV_PRI) #define UEV_EVENT_MASK (UEV_ERROR | UEV_READ | UEV_WRITE | UEV_PRI | UEV_HUP | UEV_RDHUP | UEV_EDGE | UEV_ONESHOT)
/* Main libuEv context type */ /* Main libuEv context type */
typedef struct { typedef struct {
@@ -45,6 +46,7 @@ typedef struct {
int fd; /* For epoll() */ int fd; /* For epoll() */
uint32_t errors; uint32_t errors;
LIST_HEAD(,uev) watchers; LIST_HEAD(,uev) watchers;
uint32_t workaround; /* For workarounds, e.g. redirected stdin */
} uev_ctx_t; } uev_ctx_t;
/* Forward declare due to dependencys, don't try this at home kids. */ /* Forward declare due to dependencys, don't try this at home kids. */
@@ -61,22 +63,32 @@ struct uev;
void (*cb)(struct uev *, void *, int); \ void (*cb)(struct uev *, void *, int); \
void *arg; \ void *arg; \
\ \
/* Timer watchers, time in milliseconds */ \ /* Arguments for different watchers */ \
int timeout; \ union { \
int period; \ /* Cron watchers */ \
struct { \
time_t when; \
time_t interval; \
} c; \
\ \
/* Signal watchers */ \ /* Timer watchers, time in milliseconds */ \
int signo; \ struct { \
int timeout; \
int period; \
} t; \
} u; \
\ \
/* Watcher type */ \ /* Watcher type */ \
uev_type_t uev_type_t
/* Internal API for dealing with generic watchers */ /* Internal API for dealing with generic watchers */
int uev_watcher_init (uev_ctx_t *ctx, struct uev *w, uev_type_t type, int _uev_watcher_init (uev_ctx_t *ctx, struct uev *w, uev_type_t type,
void (*cb)(struct uev *, void *, int), void *arg, void (*cb)(struct uev *, void *, int), void *arg,
int fd, int events); int fd, int events);
int uev_watcher_start(struct uev *w); int _uev_watcher_start (struct uev *w);
int uev_watcher_stop (struct uev *w); int _uev_watcher_stop (struct uev *w);
int _uev_watcher_active(struct uev *w);
int _uev_watcher_rearm (struct uev *w);
#endif /* LIBUEV_PRIVATE_H_ */ #endif /* LIBUEV_PRIVATE_H_ */
View File
File renamed without changes.
+5 -5
View File
@@ -1,7 +1,7 @@
/* libuEv - Micro event loop library /* libuEv - Micro event loop library
* *
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk> * Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com> * Copyright (c) 2013-2017 Joachim Nilsson <troglobit()gmail!com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -56,11 +56,11 @@ int uev_signal_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo
if (fd < 0) if (fd < 0)
return -1; return -1;
if (uev_watcher_init(ctx, w, UEV_SIGNAL_TYPE, cb, arg, fd, UEV_READ)) if (_uev_watcher_init(ctx, w, UEV_SIGNAL_TYPE, cb, arg, fd, UEV_READ))
goto exit; goto exit;
if (uev_signal_set(w, signo)) { if (uev_signal_set(w, signo)) {
uev_watcher_stop(w); _uev_watcher_stop(w);
exit: exit:
close(fd); close(fd);
return -1; return -1;
@@ -109,7 +109,7 @@ int uev_signal_set(uev_t *w, int signo)
if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0) if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0)
return -1; return -1;
return uev_watcher_start(w); return _uev_watcher_start(w);
} }
@@ -140,7 +140,7 @@ int uev_signal_start(uev_t *w)
*/ */
int uev_signal_stop(uev_t *w) int uev_signal_stop(uev_t *w)
{ {
if (uev_watcher_stop(w)) if (_uev_watcher_stop(w))
return -1; return -1;
close(w->fd); close(w->fd);
+21 -10
View File
@@ -1,7 +1,7 @@
/* libuEv - Micro event loop library /* libuEv - Micro event loop library
* *
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk> * Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com> * Copyright (c) 2013-2017 Joachim Nilsson <troglobit()gmail!com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -67,15 +67,20 @@ int uev_timer_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeou
{ {
int fd; int fd;
if (timeout < 0 || period < 0) {
errno = ERANGE;
return -1;
}
fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
if (fd < 0) if (fd < 0)
return -1; return -1;
if (uev_watcher_init(ctx, w, UEV_TIMER_TYPE, cb, arg, fd, UEV_READ)) if (_uev_watcher_init(ctx, w, UEV_TIMER_TYPE, cb, arg, fd, UEV_READ))
goto exit; goto exit;
if (uev_timer_set(w, timeout, period)) { if (uev_timer_set(w, timeout, period)) {
uev_watcher_stop(w); _uev_watcher_stop(w);
exit: exit:
close(fd); close(fd);
return -1; return -1;
@@ -100,6 +105,11 @@ int uev_timer_set(uev_t *w, int timeout, int period)
return -1; return -1;
} }
if (timeout < 0 || period < 0) {
errno = ERANGE;
return -1;
}
/* Handle stopped timers */ /* Handle stopped timers */
if (w->fd < 0) { if (w->fd < 0) {
/* Timer already stopped */ /* Timer already stopped */
@@ -113,18 +123,19 @@ int uev_timer_set(uev_t *w, int timeout, int period)
return -1; return -1;
} }
w->timeout = timeout; w->u.t.timeout = timeout;
w->period = period; w->u.t.period = period;
if (w->ctx->running) { if (w->ctx->running) {
struct itimerspec time; struct itimerspec time;
msec2tspec(timeout, &time.it_value); msec2tspec(timeout, &time.it_value);
msec2tspec(period, &time.it_interval); msec2tspec(period, &time.it_interval);
timerfd_settime(w->fd, 0, &time, NULL); if (timerfd_settime(w->fd, 0, &time, NULL) < 0)
return 1;
} }
return uev_watcher_start(w); return _uev_watcher_start(w);
} }
/** /**
@@ -141,9 +152,9 @@ int uev_timer_start(uev_t *w)
} }
if (-1 != w->fd) if (-1 != w->fd)
uev_watcher_stop(w); _uev_watcher_stop(w);
return uev_timer_set(w, w->timeout, w->period); return uev_timer_set(w, w->u.t.timeout, w->u.t.period);
} }
/** /**
@@ -157,7 +168,7 @@ int uev_timer_stop(uev_t *w)
/* Stop kernel timer */ /* Stop kernel timer */
uev_timer_set(w, 0, 0); uev_timer_set(w, 0, 0);
if (uev_watcher_stop(w)) if (_uev_watcher_stop(w))
return -1; return -1;
/* Close timerfd, will have to be reopened again on reset */ /* Close timerfd, will have to be reopened again on reset */
+139 -32
View File
@@ -1,7 +1,7 @@
/* libuEv - Micro event loop library /* libuEv - Micro event loop library
* *
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk> * Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com> * Copyright (c) 2013-2017 Joachim Nilsson <troglobit()gmail!com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,8 @@
#include <fcntl.h> /* O_CLOEXEC */ #include <fcntl.h> /* O_CLOEXEC */
#include <string.h> /* memset() */ #include <string.h> /* memset() */
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/select.h> /* for select() workaround */
#include <sys/signalfd.h> /* struct signalfd_siginfo */ #include <sys/signalfd.h> /* struct signalfd_siginfo */
#include <unistd.h> /* close(), read() */ #include <unistd.h> /* close(), read() */
@@ -56,8 +58,32 @@ static int is_valid_fd(int fd)
return fcntl(fd, F_GETFL) != -1 || errno != EBADF; return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
} }
/* Used by file i/o workaround when epoll => EPERM */
static int has_data(int fd)
{
struct timeval timeout = { 0, 0 };
fd_set fds;
int n = 0;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if (select(1, &fds, NULL, NULL, &timeout) > 0)
return ioctl(0, FIONREAD, &n) == 0 && n > 0;
return 0;
}
/* Set error condition and remove watcher from set */
static void invalidate_watcher(uev_t *w, struct epoll_event *event)
{
event->events = UEV_ERROR;
w->active = 0;
LIST_REMOVE(w, link);
}
/* Private to libuEv, do not use directly! */ /* Private to libuEv, do not use directly! */
int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events) int _uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events)
{ {
if (!ctx || !w) { if (!ctx || !w) {
errno = EINVAL; errno = EINVAL;
@@ -68,7 +94,7 @@ int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, vo
w->type = type; w->type = type;
w->active = 0; w->active = 0;
w->fd = fd; w->fd = fd;
w->cb = (void *)cb; w->cb = cb;
w->arg = arg; w->arg = arg;
w->events = events; w->events = events;
@@ -76,7 +102,7 @@ int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, vo
} }
/* Private to libuEv, do not use directly! */ /* Private to libuEv, do not use directly! */
int uev_watcher_start(uev_t *w) int _uev_watcher_start(uev_t *w)
{ {
struct epoll_event ev; struct epoll_event ev;
@@ -85,15 +111,28 @@ int uev_watcher_start(uev_t *w)
return -1; return -1;
} }
if (w->active) if (_uev_watcher_active(w))
return 0; return 0;
ev.events = w->events | EPOLLRDHUP; ev.events = w->events | EPOLLRDHUP;
ev.data.ptr = w; ev.data.ptr = w;
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) {
return -1; if (errno != EPERM)
return -1;
w->active = 1; /* Handle special case: `application < file.txt` */
if (w->type != UEV_IO_TYPE || w->events != UEV_READ)
return -1;
/* Only allow this special handling for stdin */
if (w->fd != STDIN_FILENO)
return -1;
w->ctx->workaround = 1;
w->active = -1;
} else {
w->active = 1;
}
/* Add to internal list for bookkeeping */ /* Add to internal list for bookkeeping */
LIST_INSERT_HEAD(&w->ctx->watchers, w, link); LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
@@ -102,14 +141,14 @@ int uev_watcher_start(uev_t *w)
} }
/* Private to libuEv, do not use directly! */ /* Private to libuEv, do not use directly! */
int uev_watcher_stop(uev_t *w) int _uev_watcher_stop(uev_t *w)
{ {
if (!w) { if (!w) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
if (!w->active) if (!_uev_watcher_active(w))
return 0; return 0;
w->active = 0; w->active = 0;
@@ -124,6 +163,33 @@ int uev_watcher_stop(uev_t *w)
return 0; return 0;
} }
/* Private to libuEv, do not use directly! */
int _uev_watcher_active(uev_t *w)
{
if (!w)
return 0;
return w->active > 0;
}
/* Private to libuEv, do not use directly! */
int _uev_watcher_rearm(uev_t *w)
{
struct epoll_event ev;
if (!w || w->fd < 0) {
errno = EINVAL;
return -1;
}
ev.events = w->events | EPOLLRDHUP;
ev.data.ptr = w;
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_MOD, w->fd, &ev) < 0)
return -1;
return 0;
}
/** /**
* Create an event loop context * Create an event loop context
* @param ctx Pointer to an uev_ctx_t context to be initialized * @param ctx Pointer to an uev_ctx_t context to be initialized
@@ -162,10 +228,11 @@ int uev_exit(uev_ctx_t *ctx)
/* Remove from internal list */ /* Remove from internal list */
LIST_REMOVE(w, link); LIST_REMOVE(w, link);
if (!w->active) if (!_uev_watcher_active(w))
continue; continue;
switch (w->type) { switch (w->type) {
case UEV_CRON_TYPE:
case UEV_TIMER_TYPE: case UEV_TIMER_TYPE:
uev_timer_stop(w); uev_timer_stop(w);
break; break;
@@ -203,7 +270,7 @@ int uev_exit(uev_ctx_t *ctx)
*/ */
int uev_run(uev_ctx_t *ctx, int flags) int uev_run(uev_ctx_t *ctx, int flags)
{ {
int result = 0, timeout = -1; int timeout = -1;
uev_t *w; uev_t *w;
if (!ctx || ctx->fd < 0) { if (!ctx || ctx->fd < 0) {
@@ -219,14 +286,36 @@ int uev_run(uev_ctx_t *ctx, int flags)
/* Start all dormant timers */ /* Start all dormant timers */
LIST_FOREACH(w, &ctx->watchers, link) { 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) if (UEV_TIMER_TYPE == w->type)
uev_timer_set(w, w->timeout, w->period); uev_timer_set(w, w->u.t.timeout, w->u.t.period);
} }
while (ctx->running) { while (ctx->running && !LIST_EMPTY(&ctx->watchers)) {
int i, nfds; int i, nfds, rerun = 0;
struct epoll_event events[UEV_MAX_EVENTS]; struct epoll_event events[UEV_MAX_EVENTS];
/* Handle special case: `application < file.txt` */
if (ctx->workaround) {
LIST_FOREACH(w, &ctx->watchers, link) {
if (w->active != -1 || !w->cb)
continue;
if (!has_data(w->fd)) {
w->active = 0;
LIST_REMOVE(w, link);
}
rerun++;
w->cb(w, w->arg, UEV_READ);
}
}
if (rerun)
continue;
ctx->workaround = 0;
while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) { while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) {
if (!ctx->running) if (!ctx->running)
break; break;
@@ -236,7 +325,7 @@ int uev_run(uev_ctx_t *ctx, int flags)
/* Unrecoverable error, cleanup and exit with error. */ /* Unrecoverable error, cleanup and exit with error. */
uev_exit(ctx); uev_exit(ctx);
return -1; return -2;
} }
for (i = 0; ctx->running && i < nfds; i++) { for (i = 0; ctx->running && i < nfds; i++) {
@@ -250,23 +339,24 @@ int uev_run(uev_ctx_t *ctx, int flags)
/* If not valid anymore, try to remove, ignore any errors. */ /* If not valid anymore, try to remove, ignore any errors. */
if (!is_valid_fd(w->fd)) if (!is_valid_fd(w->fd))
uev_watcher_stop(w); _uev_watcher_stop(w);
/* Must recreate epoll fd now ... */ /* Must recreate epoll fd now ... */
if (_init(ctx, 1)) { if (_init(ctx, 1)) {
/* Catastrophic error, no way to recover. */
uev_exit(ctx); uev_exit(ctx);
return -2; return -3;
} }
/* Restart watchers in new efd */ /* Restart watchers in new efd */
LIST_FOREACH_SAFE(w, &ctx->watchers, link, tmp) { LIST_FOREACH_SAFE(w, &ctx->watchers, link, tmp) {
if (w->active) { if (_uev_watcher_active(w)) {
w->active = 0; w->active = 0;
LIST_REMOVE(w, link); LIST_REMOVE(w, link);
uev_watcher_start(w); _uev_watcher_start(w);
} }
} }
uev_watcher_start(retry); _uev_watcher_start(retry);
/* New efd, restart everything! */ /* New efd, restart everything! */
ctx->errors = 0; ctx->errors = 0;
@@ -274,42 +364,59 @@ int uev_run(uev_ctx_t *ctx, int flags)
} }
} }
if (UEV_CRON_TYPE == w->type) {
uint64_t exp;
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
if (errno != ECANCELED)
invalidate_watcher(w, &events[i]);
else
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) { if (UEV_TIMER_TYPE == w->type) {
uint64_t exp; uint64_t exp;
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) { if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
uev_exit(ctx); invalidate_watcher(w, &events[i]);
return -3;
} }
if (!w->period) if (!w->u.t.period)
w->timeout = 0; w->u.t.timeout = 0;
} }
if (UEV_SIGNAL_TYPE == w->type) { if (UEV_SIGNAL_TYPE == w->type) {
struct signalfd_siginfo fdsi; struct signalfd_siginfo fdsi;
ssize_t sz = sizeof(fdsi); ssize_t sz = sizeof(fdsi);
if (read(w->fd, &fdsi, sz) != sz) { if (read(w->fd, &fdsi, sz) != sz)
uev_exit(ctx); invalidate_watcher(w, &events[i]);
return -4;
}
} }
if (w->cb) if (w->cb)
w->cb(w, w->arg, events[i].events & UEV_EVENT_MASK); 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 (UEV_TIMER_TYPE == w->type) {
if (!w->timeout) if (!w->u.t.timeout)
uev_timer_stop(w); uev_timer_stop(w);
} }
} }
if ((flags & UEV_ONCE) || LIST_EMPTY(&ctx->watchers)) if (flags & UEV_ONCE)
break; break;
} }
return result; return 0;
} }
/** /**
+23 -2
View File
@@ -30,27 +30,43 @@
/* Max. number of simulateneous events */ /* Max. number of simulateneous events */
#define UEV_MAX_EVENTS 10 #define UEV_MAX_EVENTS 10
/* I/O events, for signals and timers revents will always be UEV_READ */ /* I/O events, signal and timer revents are always UEV_READ */
#define UEV_NONE 0 #define UEV_NONE 0
#define UEV_ERROR EPOLLERR
#define UEV_READ EPOLLIN #define UEV_READ EPOLLIN
#define UEV_WRITE EPOLLOUT #define UEV_WRITE EPOLLOUT
#define UEV_PRI EPOLLPRI #define UEV_PRI EPOLLPRI
#define UEV_HUP EPOLLHUP
#define UEV_RDHUP EPOLLRDHUP
#define UEV_EDGE EPOLLET
#define UEV_ONESHOT EPOLLONESHOT
/* Run flags */ /* Run flags */
#define UEV_ONCE 1 #define UEV_ONCE 1
#define UEV_NONBLOCK 2 #define UEV_NONBLOCK 2
/* Macros */
#define uev_io_active(w) _uev_watcher_active(w)
#define uev_timer_active(w) _uev_watcher_active(w)
#define uev_cron_active(w) _uev_watcher_active(w)
#define uev_signal_active(w) _uev_watcher_active(w)
/* Event watcher */ /* Event watcher */
typedef struct uev { typedef struct uev {
/* Private data for libuEv internal engine */ /* Private data for libuEv internal engine */
uev_private_t type; uev_private_t type;
/* Public data for users to reference */ /* Public data for users to reference */
int signo;
int fd; int fd;
uev_ctx_t *ctx; uev_ctx_t *ctx;
} uev_t; } uev_t;
/* Generic callback for watchers */ /*
* Generic callback for watchers, @events holds %UEV_READ and/or %UEV_WRITE
* with optional %UEV_PRI (priority data available to read) and any of the
* %UEV_HUP and/or %UEV_RDHUP, which may be used to signal hang-up events.
*/
typedef void (uev_cb_t)(uev_t *w, void *arg, int events); typedef void (uev_cb_t)(uev_t *w, void *arg, int events);
/* Public interface */ /* Public interface */
@@ -68,6 +84,11 @@ int uev_timer_set (uev_t *w, int timeout, int period);
int uev_timer_start (uev_t *w); int uev_timer_start (uev_t *w);
int uev_timer_stop (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_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_set (uev_t *w, int signo);
int uev_signal_start (uev_t *w); int uev_signal_start (uev_t *w);
+7
View File
@@ -0,0 +1,7 @@
*.trs
*.log
active
complete
cronrun
signal
timer
+16
View File
@@ -0,0 +1,16 @@
EXTRA_DIST = check.h
CLEANFILES = *~ *.trs *.log
TESTS =
TESTS += active
TESTS += complete
TESTS += cronrun
TESTS += signal
TESTS += timer
check_PROGRAMS = $(TESTS)
# Ignore warnings about unused result, in e.g. write()
CFLAGS = -W -Wall -Wextra -Wno-unused-result
CPPFLAGS = -D_GNU_SOURCE
LDADD = -L../src ../src/libuev.la
+51
View File
@@ -0,0 +1,51 @@
#include "check.h"
#include <time.h>
#include <sys/time.h>
#define TIMEOUT1 2 /* 2 sec */
#define TIMEOUT2 4 /* 4 sec */
#define DAYLY (24 * 60 * 60) /* Seconds in one day */
uev_t timer1;
uev_t timer2;
uev_t timer3;
uev_t cron1;
uev_t cron2;
static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
{
fail_unless(w == &timer1);
fail_unless(uev_timer_active(&timer2));
fail_unless(!uev_timer_active(&timer3));
fail_unless(uev_cron_active(&cron1));
fail_unless(uev_cron_active(&cron2));
uev_timer_stop(&timer2);
uev_timer_stop(&cron2);
fail_unless(!uev_timer_active(&timer2));
fail_unless(!uev_cron_active(&cron2));
uev_exit(w->ctx);
}
int main(void)
{
uev_ctx_t ctx;
struct timeval tomorrow;
gettimeofday(&tomorrow, NULL);
tomorrow.tv_sec += 24 * 60 * 60;
uev_init(&ctx);
uev_timer_init(&ctx, &timer1, cb, NULL, TIMEOUT1 * 1000, 0);
uev_timer_init(&ctx, &timer2, cb, NULL, TIMEOUT2 * 1000, 0);
uev_timer_init(&ctx, &timer3, cb, NULL, TIMEOUT2 * 1000, 0);
uev_timer_stop(&timer3);
uev_cron_init(&ctx, &cron1, cb, NULL, tomorrow.tv_sec, 24 * 60 * 60);
uev_cron_init(&ctx, &cron2, cb, NULL, tomorrow.tv_sec, 24 * 60 * 60);
return uev_run(&ctx, 0);
}
+81
View File
@@ -0,0 +1,81 @@
#ifndef UEV_TESTS_CHECK_H_
#define UEV_TESTS_CHECK_H_
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "../src/uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
#define fail_unless(test) \
do { \
if (!(test)) { \
fprintf(stderr, \
"----------------------------------------------\n" \
"%s:%d: test FAILED:\nFailed test: %s\n" \
"----------------------------------------------\n", \
__FILE__, __LINE__, #test); \
exit(1); \
} \
} while (0)
static inline int test(int result, const char *fmt, ...)
{
char buf[80];
size_t len;
va_list ap;
const char success[] = " \033[1m[ OK ]\033[0m\n";
const char failure[] = " \033[7m[FAIL]\033[0m\n";
const char dots[] = " .....................................................................";
va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
write(STDERR_FILENO, buf, len);
write(STDERR_FILENO, dots, 60 - len); /* pad with dots. */
if (!result)
write(STDERR_FILENO, success, strlen(success));
else
write(STDERR_FILENO, failure, strlen(failure));
return result;
}
static inline int timespec_newer(const struct timespec *a, const struct timespec *b)
{
if (a->tv_sec != b->tv_sec)
return a->tv_sec > b->tv_sec;
return a->tv_nsec > b->tv_nsec;
}
static inline char *timespec2str(struct timespec *ts, char *buf, size_t len)
{
size_t ret, pos;
struct tm t;
memset(buf, 0, len);
tzset();
if (localtime_r(&(ts->tv_sec), &t) == NULL)
return buf;
ret = strftime(buf, len, "%F %T", &t);
if (ret == 0)
return buf;
len -= ret - 1;
pos = strlen(buf);
len -= pos;
snprintf(&buf[pos], len, ".%09ld", ts->tv_nsec);
return buf;
}
#endif /* UEV_TESTS_CHECK_H_ */
+2 -9
View File
@@ -22,14 +22,8 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "check.h"
#include <signal.h> #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 { typedef struct {
int counter; int counter;
@@ -138,8 +132,7 @@ int main(void)
uev_run(&ctx, 0); uev_run(&ctx, 0);
fprintf(stderr, "Period is %d must be 10: %s\n", period, 10 == period ? "OK" : "ERROR!"); fprintf(stderr, "Period is %d must be 10: %s\n", period, 10 == period ? "OK" : "ERROR!");
if (10 != period) fail_unless(10 == period);
return 1;
return 0; return 0;
} }
+73
View File
@@ -0,0 +1,73 @@
/* 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 "check.h"
#include <time.h>
#include <sys/time.h>
#define TIMEOUT 2
#define INTERVAL 2
struct timeval tv;
static void cron_job(uev_t *w, void *UNUSED(arg), int UNUSED(events))
{
static int laps = 3;
struct timeval now;
/* Update timer every lap */
tv.tv_sec += TIMEOUT;
gettimeofday(&now, NULL);
printf("Cron job HELO %s", ctime(&now.tv_sec));
fail_unless(now.tv_sec == tv.tv_sec);
if (!laps--)
uev_exit(w->ctx);
}
int main(void)
{
uev_t cron_watcher;
uev_ctx_t ctx;
time_t when, interval;
uev_init(&ctx);
gettimeofday(&tv, NULL);
printf("Start of test %s", ctime(&tv.tv_sec));
printf("Expected cron %s", ctime(&when));
when = tv.tv_sec + TIMEOUT;
interval = INTERVAL;
uev_cron_init(&ctx, &cron_watcher, cron_job, NULL, when, interval);
return uev_run(&ctx, 0);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+1 -3
View File
@@ -21,7 +21,7 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#define _GNU_SOURCE #include "check.h"
#include <err.h> #include <err.h>
#include <sched.h> #include <sched.h>
#include <signal.h> #include <signal.h>
@@ -33,8 +33,6 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused)) #define UNUSED(arg) arg __attribute__ ((unused))
#define DO_SEGFAULT 1 #define DO_SEGFAULT 1
+28
View File
@@ -0,0 +1,28 @@
#include "check.h"
#include <time.h>
#include <sys/time.h>
#define TIMEOUT 2 /* 2 sec */
struct timeval start;
static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
{
struct timeval now;
gettimeofday(&now, NULL);
fail_unless(now.tv_sec == start.tv_sec + TIMEOUT);
uev_exit(w->ctx);
}
int main(void)
{
uev_t w;
uev_ctx_t ctx;
uev_init(&ctx);
uev_timer_init(&ctx, &w, cb, NULL, TIMEOUT * 1000, 0);
gettimeofday(&start, NULL);
return uev_run(&ctx, 0);
}