From 90048e599618c0337b1cf8fecdc065ad8b32a00a Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Mon, 8 Oct 2018 07:15:14 +0200 Subject: [PATCH] Add support for generic events using Linux eventfd Signed-off-by: Joachim Nilsson --- API.md | 5 +++ src/Makefile.am | 2 +- src/event.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++ src/private.h | 1 + src/uev.c | 8 ++++ src/uev.h | 5 +++ tests/.gitignore | 1 + tests/Makefile.am | 1 + tests/event.c | 78 ++++++++++++++++++++++++++++++++++ 9 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 src/event.c create mode 100644 tests/event.c diff --git a/API.md b/API.md index 3bcbf6a..3d1bef2 100644 --- a/API.md +++ b/API.md @@ -87,6 +87,11 @@ int uev_signal_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int sign 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 */ + +/* Generic event watcher, post events for later processing, or from forked child */ +int uev_event_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg); +int uev_event_post (uev_t *w); +int uev_event_stop (uev_t *w); ``` diff --git a/src/Makefile.am b/src/Makefile.am index cad0823..e7e8f0f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ lib_LTLIBRARIES = libuev.la -libuev_la_SOURCES = uev.c uev.h private.h io.c timer.c signal.c cron.c +libuev_la_SOURCES = uev.c uev.h private.h io.c timer.c signal.c cron.c event.c libuev_la_CPPFLAGS = -D_GNU_SOURCE -D_TIME_BITS=64 libuev_la_CFLAGS = -W -Wall -Wextra -std=gnu11 libuev_la_LDFLAGS = $(AM_LDFLAGS) -version-info 3:0:1 diff --git a/src/event.c b/src/event.c new file mode 100644 index 0000000..15ecff0 --- /dev/null +++ b/src/event.c @@ -0,0 +1,106 @@ +/* libuEv - Micro event loop library + * + * Copyright (c) 2012 Flemming Madsen + * Copyright (c) 2013-2017 Joachim Nilsson + * + * 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 +#include +#include /* close(), read() */ + +#include "uev.h" + + +/** + * Create a generic event watcher + * @param ctx A valid libuEv context + * @param w Pointer to an uev_t watcher + * @param cb Callback when an event is posted + * @param arg Optional callback argument + * + * @return POSIX OK(0) or non-zero with @param errno set on error. + */ +int uev_event_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg) +{ + int fd; + + if (!w || !ctx) { + errno = EINVAL; + return -1; + } + w->fd = -1; + + fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (fd < 0) + return -1; + + return _uev_watcher_init(ctx, w, UEV_EVENT_TYPE, cb, arg, fd, UEV_READ) + || _uev_watcher_start(w); +} + +/** + * Post a generic event + * @param w Watcher to post to + * + * @return POSIX OK(0) or non-zero with @param errno set on error. + */ +int uev_event_post(uev_t *w) +{ + uint64_t val = 1; + + if (!w || -1 == w->fd) { + errno = EINVAL; + return -1; + } + + + if (write(w->fd, &val, sizeof(val)) != sizeof(val)) + return -1; + + return 0; +} + +/** + * Stop a generic event watcher + * @param w Watcher to stop + * + * @return POSIX OK(0) or non-zero with @param errno set on error. + */ +int uev_event_stop(uev_t *w) +{ + if (!_uev_watcher_active(w)) + return 0; + + if (_uev_watcher_stop(w)) + return -1; + + close(w->fd); + w->fd = -1; + + return 0; +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/src/private.h b/src/private.h index ad96c7c..835f0cd 100644 --- a/src/private.h +++ b/src/private.h @@ -66,6 +66,7 @@ typedef enum { UEV_SIGNAL_TYPE, UEV_TIMER_TYPE, UEV_CRON_TYPE, + UEV_EVENT_TYPE, } uev_type_t; /* Event mask, used internally only. */ diff --git a/src/uev.c b/src/uev.c index e47c724..addcf6b 100644 --- a/src/uev.c +++ b/src/uev.c @@ -257,6 +257,9 @@ int uev_exit(uev_ctx_t *ctx) case UEV_CRON_TYPE: uev_timer_stop(w); break; + + case UEV_EVENT_TYPE: + uev_event_stop(w); break; } } @@ -397,6 +400,11 @@ int uev_run(uev_ctx_t *ctx, int flags) if (!w->u.c.when) uev_timer_stop(w); break; + + case UEV_EVENT_TYPE: + if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) + events = UEV_HUP; + break; } /* diff --git a/src/uev.h b/src/uev.h index 14693d2..c144f41 100644 --- a/src/uev.h +++ b/src/uev.h @@ -50,6 +50,7 @@ #define uev_signal_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_event_active(w) _uev_watcher_active(w) /* Event watcher */ typedef struct uev { @@ -100,6 +101,10 @@ int uev_signal_set (uev_t *w, int signo); int uev_signal_start (uev_t *w); int uev_signal_stop (uev_t *w); +int uev_event_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg); +int uev_event_post (uev_t *w); +int uev_event_stop (uev_t *w); + #endif /* LIBUEV_UEV_H_ */ /** diff --git a/tests/.gitignore b/tests/.gitignore index 136958f..86dfc70 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -6,3 +6,4 @@ complete cronrun signal timer +event diff --git a/tests/Makefile.am b/tests/Makefile.am index f255f4b..d6bf898 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -8,6 +8,7 @@ TESTS += complete TESTS += cronrun TESTS += signal TESTS += timer +TESTS += event check_PROGRAMS = $(TESTS) diff --git a/tests/event.c b/tests/event.c new file mode 100644 index 0000000..0e9f69a --- /dev/null +++ b/tests/event.c @@ -0,0 +1,78 @@ +/* Verifies eventfd support + * + * Copyright (c) 2018 Joachim Nilsson + * + * 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 +#include +#include +#include + +#define LAPS 10 +pid_t pid; + +static void cb(uev_t *w, void *arg, int events) +{ + static int num = LAPS; + + num--; +// printf("Got event %d ...\n", LAPS - num); + if (num == 0) { + waitpid(pid, NULL, 0); + uev_exit(w->ctx); + } +} + +int main(void) +{ + uev_ctx_t ctx; + uev_t ev; + int i; + + /* Initialize libuEv */ + uev_init(&ctx); + + /* Setup callbacks */ + uev_event_init(&ctx, &ev, cb, NULL); + + pid = fork(); + if (-1 == pid) + err(1, "fork"); + + /* Start event loop */ + if (pid > 0) + return uev_run(&ctx, 0); + + for (i = 0; i < LAPS; i++) { + uev_event_post(&ev); + usleep(10000); + } + + return 0; +} + +/** + * Local Variables: + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */