Major refactor: API change, code cleanup, remove calloc()/free()

- Rename all _create() functions --> _init()
- Rename all _delete() functions --> _stop()
- Watchers are automatically started, except
- Timer watchers are automatically started by uev_run()
- Allow I/O watchers to watch either EV_READ/EV_WRITE or both events
- Extend callbacks with a received events field
- Remove calloc()/free(), let user handle this
- Make event has-a context when initializing it => simplifies API
- Internal uev_watcher_*() API mostly handles epoll stop/start

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson committed 2013-08-06 02:00:24 +02:00
1 parent c69ce87eb8
commit d6e0146803
7 files changed
+362 -279

No files matched your search

+1 -1
View File
@@ -34,7 +34,7 @@ CC = $(CROSS)gcc
AR = $(CROSS)ar AR = $(CROSS)ar
STRIP = $(CROSS)strip STRIP = $(CROSS)strip
CFLAGS += -fPIC -Os CFLAGS += -fPIC -Os
CPPFLAGS += -W -Wall -Iinclude CPPFLAGS += -W -Wall -Werror -Iinclude
ARFLAGS = crus ARFLAGS = crus
JUNK = *~ *.bak *.map .*.d DEADJOE *.gdb *.elf core core.* JUNK = *~ *.bak *.map .*.d DEADJOE *.gdb *.elf core core.*
+38 -13
View File
@@ -29,28 +29,53 @@
/** /**
* Create an I/O watcher * Create an I/O watcher
* @param ctx A valid libuev context * @param ctx A valid libuev context
* @param handler I/O callback * @param w Pointer to an uev_t watcher
* @param data Optional callback argument * @param cb I/O callback
* @param fd File descriptor to watch * @param arg Optional callback argument
* @param dir Direction of I/O to watch for: %UEV_DIR_INBOUND, or %UEV_DIR_OUTBOUND * @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 and %UEV_WRITE
* *
* @return The new I/O watcher, or %NULL if invalid pointers or out or memory. * @return POSIX OK(0) or non-zero with @param errno set on error.
*/ */
uev_t *uev_io_create(uev_ctx_t *ctx, uev_cb_t *handler, void *data, int fd, uev_dir_t dir) int uev_io_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events)
{ {
return uev_watcher_create(ctx, UEV_FILE_TYPE, fd, dir, handler, data); if (uev_watcher_init(ctx, w, UEV_IO_TYPE, cb, arg, fd, events))
return -1;
if (fd < 0)
return 0;
return uev_watcher_start(w);
} }
/** /**
* Delete an I/O watcher * Reset an I/O watcher
* @param ctx A valid libuev context * @param w Pointer to an uev_t watcher
* @param w I/O watcher * @param fd New file descriptor to monitor
* @param events Requested events to watch for, a mask of %UEV_READ and %UEV_WRITE
* *
* @return POSIX OK(0) or non-zero with @param errno set. * @return POSIX OK(0) or non-zero with @param errno set on error.
*/ */
int uev_io_delete(uev_ctx_t *ctx, uev_t *w) int uev_io_set(uev_t *w, int fd, int events)
{ {
return uev_watcher_delete(ctx, w); if (uev_io_stop(w))
return -1;
/* Remove from internal list */
LIST_REMOVE(w, link);
return uev_io_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, fd, events);
}
/**
* Stop an I/O watcher
* @param w Pointer to an uev_t watcher
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_io_stop(uev_t *w)
{
return uev_watcher_stop(w);
} }
/** /**
+105 -76
View File
@@ -24,7 +24,7 @@
*/ */
#include <errno.h> #include <errno.h>
#include <stdlib.h> /* calloc(), free() */ #include <string.h> /* memset() */
#include <sys/epoll.h> #include <sys/epoll.h>
#include <sys/signalfd.h> /* struct signalfd_siginfo */ #include <sys/signalfd.h> /* struct signalfd_siginfo */
#include <unistd.h> /* close(), read() */ #include <unistd.h> /* close(), read() */
@@ -32,103 +32,135 @@
#include "uev.h" #include "uev.h"
/* Private to libuev, do not use directly! */ /* Private to libuev, do not use directly! */
uev_t *uev_watcher_create(uev_ctx_t *ctx, uev_type_t type, int fd, uev_dir_t dir, uev_cb_t *cb, void *arg) 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)
{
uev_t *w;
struct epoll_event ev;
if (!ctx || !cb) {
errno = EINVAL;
return NULL;
}
w = (uev_t *)calloc(1, sizeof(*w));
if (!w)
return NULL;
w->fd = fd;
w->type = type;
w->cb = (void *)cb;
w->arg = arg;
ev.events = dir == UEV_DIR_OUTBOUND ? EPOLLOUT : EPOLLIN;
ev.data.ptr = w;
if (epoll_ctl(ctx->efd, EPOLL_CTL_ADD, fd, &ev) < 0) {
free(w);
return NULL;
}
LIST_INSERT_HEAD(&ctx->watchers, w, link);
return w;
}
/* Private to libuev, do not use directly! */
int uev_watcher_delete(uev_ctx_t *ctx, uev_t *w)
{ {
if (!ctx || !w) { if (!ctx || !w) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
/* Remove from kernel */ memset(w, 0, sizeof(*w));
epoll_ctl(ctx->efd, EPOLL_CTL_DEL, w->fd, NULL); w->ctx = ctx;
w->fd = fd;
w->type = type;
w->cb = (void *)cb;
w->arg = arg;
w->events = events;
/* Remove from internal list */ LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
LIST_REMOVE(w, link);
free(w); return 0;
}
/* Private to libuev, do not use directly! */
int uev_watcher_start(uev_t *w)
{
struct epoll_event ev;
if (!w || w->fd < 0) {
errno = EINVAL;
return -1;
}
if (w->active)
return 0;
ev.events = w->events;
ev.data.ptr = w;
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
return -1;
w->active = 1;
return 0;
}
/* Private to libuev, do not use directly! */
int uev_watcher_stop(uev_t *w)
{
if (!w) {
errno = EINVAL;
return -1;
}
if (!w->active)
return 0;
/* Remove from kernel */
epoll_ctl(w->ctx->fd, EPOLL_CTL_DEL, w->fd, NULL);
w->active = 0;
return 0; return 0;
} }
/** /**
* Create an event loop context * Create an event loop context
* @param ctx Pointer to an uev_ctx_t context to be initialized
* *
* @return Returns a new uev_ctx_t context, or %NULL on error. * @return POSIX OK(0) on success, or non-zero on error.
*/ */
uev_ctx_t *uev_ctx_create(void) int uev_init(uev_ctx_t *ctx)
{ {
int fd; int fd;
uev_ctx_t *ctx;
fd = epoll_create(1); fd = epoll_create(1);
if (fd < 0) if (fd < 0)
return NULL; return -1;
ctx = (uev_ctx_t *)calloc(1, sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx));
if (!ctx) { ctx->fd = fd;
close(fd);
return NULL;
}
ctx->efd = fd;
LIST_INIT(&ctx->watchers); LIST_INIT(&ctx->watchers);
return ctx; return 0;
} }
/** /**
* Destroy an event loop context * Terminate the event loop
* @param ctx A valid libuev context * @param ctx A valid libuev context
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/ */
void uev_ctx_delete(uev_ctx_t *ctx) int uev_exit(uev_ctx_t *ctx)
{ {
if (!ctx) {
errno = EINVAL;
return -1;
}
while (!LIST_EMPTY(&ctx->watchers)) { while (!LIST_EMPTY(&ctx->watchers)) {
uev_t *w = LIST_FIRST(&ctx->watchers); uev_t *w = LIST_FIRST(&ctx->watchers);
if (UEV_TIMER_TYPE == w->type) /* Remove from internal list */
uev_timer_delete(ctx, w); LIST_REMOVE(w, link);
else
uev_io_delete(ctx, w); if (!w->active)
continue;
switch (w->type) {
case UEV_TIMER_TYPE:
uev_timer_stop(w);
break;
case UEV_SIGNAL_TYPE:
uev_signal_stop(w);
break;
case UEV_IO_TYPE:
uev_io_stop(w);
break;
}
} }
close(ctx->efd); ctx->running = 0;
free(ctx); close(ctx->fd);
ctx->fd = -1;
return 0;
} }
/** /**
* Start the event loop * Start the event loop
* @param ctx A valid libuev context * @param ctx A valid libuev context
* *
* @return POSIX OK(0) upon successful termination of the event loop, or non-zero on error. * @return POSIX OK(0) upon successful termination of the event loop, or non-zero on error.
*/ */
@@ -137,7 +169,7 @@ int uev_run(uev_ctx_t *ctx)
int result = 0; int result = 0;
uev_t *w; uev_t *w;
if (!ctx) { if (!ctx || ctx->fd < 0) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
@@ -148,14 +180,17 @@ int uev_run(uev_ctx_t *ctx)
/* Start all dormant timers */ /* Start all dormant timers */
LIST_FOREACH(w, &ctx->watchers, link) { LIST_FOREACH(w, &ctx->watchers, link) {
if (UEV_TIMER_TYPE == w->type) if (UEV_TIMER_TYPE == w->type)
uev_timer_set(ctx, w, w->timeout, w->period); uev_timer_set(w, w->timeout, w->period);
} }
while (ctx->running) { while (ctx->running) {
int i, nfds; int i, nfds;
struct epoll_event events[UEV_MAX_EVENTS]; struct epoll_event events[UEV_MAX_EVENTS];
while ((nfds = epoll_wait(ctx->efd, events, UEV_MAX_EVENTS, -1)) < 0) { while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, -1)) < 0) {
if (!ctx->running)
break;
if (EINTR == errno) if (EINTR == errno)
continue; /* Signalled, try again */ continue; /* Signalled, try again */
exit: exit:
@@ -164,7 +199,7 @@ int uev_run(uev_ctx_t *ctx)
break; break;
} }
for (i = 0; i < nfds; i++) { for (i = 0; ctx->running && i < nfds; i++) {
w = (uev_t *)events[i].data.ptr; w = (uev_t *)events[i].data.ptr;
if (UEV_TIMER_TYPE == w->type) { if (UEV_TIMER_TYPE == w->type) {
@@ -172,6 +207,9 @@ int uev_run(uev_ctx_t *ctx)
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp))
goto exit; goto exit;
if (!w->period)
w->timeout = 0;
} }
if (UEV_SIGNAL_TYPE == w->type) { if (UEV_SIGNAL_TYPE == w->type) {
@@ -188,8 +226,8 @@ int uev_run(uev_ctx_t *ctx)
w->cb((struct uev *)ctx, w, w->arg); w->cb((struct uev *)ctx, w, w->arg);
if (UEV_TIMER_TYPE == w->type) { if (UEV_TIMER_TYPE == w->type) {
if (!w->period) if (!w->timeout)
uev_timer_delete(ctx, w); uev_timer_stop(w);
} }
} }
} }
@@ -197,15 +235,6 @@ int uev_run(uev_ctx_t *ctx)
return result; return result;
} }
/**
* Terminate the event loop
* @param ctx A valid libuev context
*/
void uev_exit(uev_ctx_t *ctx)
{
ctx->running = 0;
}
/** /**
* Local Variables: * Local Variables:
* version-control: t * version-control: t
+36 -36
View File
@@ -33,90 +33,90 @@
/** /**
* Create a signal watcher * Create a signal watcher
* @param ctx A valid libuev context * @param ctx A valid libuev context
* @param handler Timer callback * @param w Pointer to an uev_t watcher
* @param data Optional callback argument * @param cb Timer callback
* @param signo Signal to watch for * @param arg Optional callback argument
* @param signo Signal to watch for
* *
* @return The new watcher, or %NULL with @param errno set on error. * @return POSIX OK(0) or non-zero with @param errno set on error.
*/ */
uev_t *uev_signal_create(uev_ctx_t *ctx, uev_cb_t *handler, void *data, int signo) int uev_signal_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo)
{ {
int fd; int fd;
sigset_t mask; sigset_t mask;
uev_t *w;
sigemptyset(&mask); sigemptyset(&mask);
fd = signalfd(-1, &mask, SFD_NONBLOCK); fd = signalfd(-1, &mask, SFD_NONBLOCK);
if (fd < 0) if (fd < 0)
return NULL; return -1;
w = uev_watcher_create(ctx, UEV_SIGNAL_TYPE, fd, UEV_DIR_INBOUND, handler, data); if (uev_watcher_init(ctx, w, UEV_SIGNAL_TYPE, cb, arg, fd, UEV_READ))
if (!w)
goto exit; goto exit;
if (uev_signal_set(ctx, w, signo)) { if (uev_signal_set(w, signo)) {
uev_watcher_delete(ctx, w); uev_watcher_stop(w);
exit: exit:
close(fd); close(fd);
return NULL; return -1;
} }
return w; return 0;
} }
/** /**
* Reset a signal watcher * Reset a signal watcher
* @param ctx A valid libuev context * @param w Watcher to reset
* @param w Watcher to reset * @param signo New signal to watch for
* @param signo New signal to watch for
* *
* @return POSIX OK(0) or non-zero with @param errno set. * @return POSIX OK(0) or non-zero with @param errno set on error.
*/ */
int uev_signal_set(uev_ctx_t *ctx, uev_t *w, int signo) int uev_signal_set(uev_t *w, int signo)
{ {
sigset_t mask; sigset_t mask;
if (!ctx || !w) { /* Every watcher must be registered to a context */
if (!w || !w->ctx) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
/* Handle stopped signal watchers */
if (w->fd < 0) {
/* Remove from internal list */
LIST_REMOVE(w, link);
if (uev_signal_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, signo))
return -1;
}
sigemptyset(&mask); sigemptyset(&mask);
sigaddset(&mask, signo); sigaddset(&mask, signo);
/* Block signals so that they aren't handled /* Block signals so that they aren't handled
according to their default dispositions */ according to their default dispositions */
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
return -1; return -1;
if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0) if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0)
return -1; return -1;
return 0; return uev_watcher_start(w);
} }
/** /**
* Delete a signal watcher * Stop a signal watcher
* @param ctx A valid libuev context * @param w Watcher to stop
* @param w Watcher to delete
* *
* @return POSIX OK(0) or non-zero with @param errno set. * @return POSIX OK(0) or non-zero with @param errno set on error.
*/ */
int uev_signal_delete(uev_ctx_t *ctx, uev_t *w) int uev_signal_stop(uev_t *w)
{ {
int fd; if (uev_watcher_stop(w))
if (!ctx || !w) {
errno = EINVAL;
return -1; return -1;
}
uev_timer_set(ctx, w, 0, 0); close(w->fd);
fd = w->fd; w->fd = -1;
uev_watcher_delete(ctx, w);
close(fd);
return 0; return 0;
} }
+47 -39
View File
@@ -30,105 +30,113 @@
#include "uev.h" #include "uev.h"
#define UNUSED(arg) arg __attribute__ ((unused))
typedef struct {
int counter;
} my_t;
static int in, out; static int in, out;
static int period = 0; static int period = 0;
static uev_t *timer = NULL; static uev_t *watchdog;
static void lifetime_cb(uev_ctx_t *ctx, uev_t *w __attribute__ ((unused)), void *data) static void lifetime_cb(uev_ctx_t *ctx, uev_t UNUSED(*w), void *arg, int UNUSED(events))
{ {
fprintf(stderr, "\nLifetime exceeded %p\n", data); fprintf(stderr, "\nLifetime exceeded %p\n", arg);
uev_exit(ctx); uev_exit(ctx);
} }
/* The pipe watchdog, if it triggers we haven't received data in time. */ /* The pipe watchdog, if it triggers we haven't received data in time. */
static void timeout_cb(uev_ctx_t *ctx, uev_t *w, void *data) static void timeout_cb(uev_ctx_t *ctx, uev_t *w, void *arg, int UNUSED(events))
{ {
timer = NULL; watchdog = NULL;
fprintf(stderr, "\nTimeout exceeded %p\n", data); fprintf(stderr, "\nTimeout exceeded %p\n", arg);
uev_timer_delete(ctx, w); uev_timer_stop(w);
uev_exit(ctx); uev_exit(ctx);
} }
static void periodic_task(uev_ctx_t *ctx __attribute__ ((unused)), uev_t *w __attribute__ ((unused)), void *data __attribute__ ((unused))) static void periodic_task(uev_ctx_t UNUSED(*ctx), uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{ {
fprintf(stderr, "|"); fprintf(stderr, "|");
} }
static void signal_cb(uev_ctx_t *ctx __attribute__ ((unused)), uev_t *w, void *data __attribute__ ((unused))) static void signal_cb(uev_ctx_t *UNUSED(ctx), uev_t *w, void *UNUSED(arg), int UNUSED(events))
{ {
fprintf(stderr, w->signo == SIGINT ? "^Cv" : "^\v"); fprintf(stderr, w->signo == SIGINT ? "^Cv" : "^\v");
} }
static void pipe_read_cb(uev_ctx_t *ctx, uev_t *w __attribute__ ((unused)), void *data __attribute__ ((unused))) static void pipe_read_cb(uev_ctx_t *UNUSED(ctx), uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
{ {
int cnt; int cnt;
char msg[50]; char msg[50];
/* Kick watchdog */ /* Kick watchdog */
if (timer) if (watchdog)
uev_timer_set(ctx, timer, 1000, 0); uev_timer_set(watchdog, 1000, 0);
cnt = read(in, msg, sizeof(msg)); cnt = read(in, msg, sizeof(msg));
// fprintf(stderr, "READ %.*s %d\n", cnt, msg, cnt); // fprintf(stderr, "READ %.*s %d\n", cnt, msg, cnt);
fprintf(stderr, "%.*s.%d ", cnt, msg, cnt); fprintf(stderr, "%.*s.%d ", cnt, msg, cnt);
} }
static void pipe_write_cb(uev_ctx_t *ctx, uev_t *w __attribute__ ((unused)), void *data) static void pipe_write_cb(uev_ctx_t UNUSED(*ctx), uev_t *w, void *arg, int UNUSED(events))
{ {
int cnt = (int)(intptr_t)data; my_t *my = arg;
char *msg = "TESTING"; char *msg = "TESTING";
if (write(out, msg, cnt) < 0) { if (write(out, msg, my->counter) < 0) {
perror("\nFailed writing to pipe"); perror("\nFailed writing to pipe");
return; return;
} }
// fprintf(stderr, "WRITE %.*s %d\n", cnt, msg, cnt); // fprintf(stderr, "WRITE %.*s %d\n", my->counter, msg, my->counter);
fprintf(stderr, "%d ", cnt); fprintf(stderr, "%d ", my->counter);
period = cnt + 5; period = my->counter + 5;
my->counter++;
uev_timer_create(ctx, pipe_write_cb, (void *)(intptr_t)(cnt + 1), period * 100, 0); uev_timer_set(w, period * 100, 0);
} }
int main(void) int main(void)
{ {
int fd[2]; int fd[2];
uev_ctx_t *ctx = uev_ctx_create(); my_t my = { .counter = 1 };
uev_t timeout, wdt, periodic, writer, reader, sigint_watcher, sigquit_watcher;
/* Total program execution time */ uev_ctx_t ctx;
uev_timer_create(ctx, lifetime_cb, (void *)(intptr_t)2, 4000, 0);
/* Work load, one timer callback writes to a pipe periodically, /* Work load, one timer callback writes to a pipe periodically,
* and one I/O watcher that is called every time the pipe has * and one I/O watcher that is called every time the pipe has
* some data. */ * some data. */
if (pipe(fd) < 0) { if (pipe(fd) < 0)
/* Cleanup */
uev_ctx_delete(ctx);
return 1; return 1;
}
/* */
uev_init(&ctx);
/* Signal watchers, Ctrl-C => SIGINT, Ctrl-\ => SIGQUIT */
uev_signal_init(&ctx, &sigint_watcher, signal_cb, NULL, SIGINT);
uev_signal_init(&ctx, &sigquit_watcher, signal_cb, NULL, SIGQUIT);
/* Total program execution time */
uev_timer_init(&ctx, &timeout, lifetime_cb, (void *)(intptr_t)2, 4000, 0);
/* Main pipe worker and consumer */
in = fd[0]; in = fd[0];
out = fd[1]; out = fd[1];
uev_timer_create(ctx, pipe_write_cb, (void *)(intptr_t)1, 400, 0); uev_io_init(&ctx, &reader, pipe_read_cb, NULL, in, UEV_READ);
uev_io_create(ctx, pipe_read_cb, NULL, in, UEV_DIR_INBOUND); uev_timer_init(&ctx, &writer, pipe_write_cb, &my, 400, 0);
/* Watchdog for the above timer callback, if it doesn't wake up /* Watchdog for the above timer callback, if it doesn't wake up
* and write to the pipe within a given deadline it will bark. */ * and write to the pipe within a given deadline it will bark. */
timer = uev_timer_create(ctx, timeout_cb, (void *)(intptr_t)1, 950, 0); uev_timer_init(&ctx, &wdt, timeout_cb, (void *)(intptr_t)1, 950, 0);
watchdog = &wdt;
/* Periodic background task */ /* Periodic background task */
uev_timer_create(ctx, periodic_task, NULL, 200, 200); uev_timer_init(&ctx, &periodic, periodic_task, NULL, 200, 200);
/* Signal watchers, Ctrl-C => SIGINT, Ctrl-\ => SIGQUIT */
uev_signal_create(ctx, signal_cb, NULL, SIGINT);
uev_signal_create(ctx, signal_cb, NULL, SIGQUIT);
/* Start event loop */ /* Start event loop */
uev_run(ctx); uev_run(&ctx);
/* Tear down event context */
uev_ctx_delete(ctx);
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) if (10 != period)
+83 -67
View File
@@ -30,107 +30,123 @@
#include "uev.h" #include "uev.h"
static struct timespec msec2tspec(int msec) static void msec2tspec(int msec, struct timespec *ts)
{ {
struct timespec ts; if (msec) {
ts->tv_sec = msec / 1000;
ts.tv_sec = msec / 1000; ts->tv_nsec = (msec % 1000) * 1000000;
ts.tv_nsec = (msec % 1000) * 1000000; } else {
ts->tv_sec = 0;
return ts; ts->tv_nsec = 0;
}
} }
/** /**
* Create a timer watcher * Create and start a timer watcher
* @param ctx A valid libuev context * @param ctx A valid libuev context
* @param handler Timer callback * @param w Pointer to an uev_t watcher
* @param data Optional callback argument * @param cb Callback function
* @param timeout Timeout in milliseconds before @param handler is called * @param arg Optional callback argument
* @param period For periodic timers this is the period time that @param timeout is reset to * @param timeout Timeout in milliseconds before @param cb is called
* @param period For periodic timers this is the period time that @param timeout is reset to
* *
* For one-shot timers you set @param period to zero and only use @param * For one-shot timers you set @param period to zero and only use @param
* timeout. For periodic timers you likely set @param timeout to either * timeout. For periodic timers you likely set @param timeout to either
* zero, to call it as soon as the event loop starts, or to the same * zero, to call it as soon as the event loop starts, or to the same
* value as @param period. When the timer expires, the @param handler * value as @param period. When the timer expires, the @param cb
* is called, with the optional @param data argument. A non-periodic * is called, with the optional @param arg argument. A non-periodic
* timer ends its life there, while a periodic task's @param timeout is * timer ends its life there, while a periodic task's @param timeout is
* reset to the @param period and restarted. * reset to the @param period and restarted.
* *
* @return The new watcher, or %NULL with @param errno set on error. * A timer is automatically started if the event loop is already
*/ * running, otherwise it is kept on hold until triggered by calling
uev_t *uev_timer_create(uev_ctx_t *ctx, uev_cb_t *handler, void *data, int timeout, int period) * uev_run().
{
int fd;
uev_t *w;
fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (fd < 0)
return NULL;
w = uev_watcher_create(ctx, UEV_TIMER_TYPE, fd, UEV_DIR_INBOUND, handler, data);
if (!w)
goto exit;
if (uev_timer_set(ctx, w, timeout, period)) {
uev_watcher_delete(ctx, w);
exit:
close(fd);
return NULL;
}
return w;
}
/**
* Reset or reschedule a timer
* @param ctx A valid libuev context
* @param w Watcher to reset
* @param timeout Timeout in milliseconds before @param handler is called
* @param period For periodic timers this is the period time that @param timeout is reset to
* *
* @return POSIX OK(0) or non-zero with @param errno set on error. * @return POSIX OK(0) or non-zero with @param errno set on error.
*/ */
int uev_timer_set(uev_ctx_t *ctx, uev_t *w, int timeout, int period) int uev_timer_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period)
{ {
struct itimerspec time; int fd;
if (!ctx || !w) { fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
if (fd < 0)
return -1;
if (uev_watcher_init(ctx, w, UEV_TIMER_TYPE, cb, arg, fd, UEV_READ))
goto exit;
if (uev_timer_set(w, timeout, period)) {
uev_watcher_stop(w);
exit:
close(fd);
return -1;
}
return 0;
}
/**
* Reset a timer
* @param w Watcher to reset
* @param timeout Timeout in milliseconds before @param cb is called
* @param period For periodic timers this is the period time that @param timeout is reset to
*
* @return POSIX OK(0) or non-zero with @param errno set on error.
*/
int uev_timer_set(uev_t *w, int timeout, int period)
{
/* Every watcher must be registered to a context */
if (!w || !w->ctx) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
/* Handle stopped timers */
if (w->fd < 0) {
/* Timer already stopped */
if (!timeout && !period)
return 0;
/* Remove from internal list */
LIST_REMOVE(w, link);
if (uev_timer_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, timeout, period))
return -1;
}
w->timeout = timeout; w->timeout = timeout;
w->period = period; w->period = period;
if (!ctx->running) if (w->ctx->running) {
return 0; struct itimerspec time;
time.it_value = msec2tspec(timeout); msec2tspec(timeout, &time.it_value);
time.it_interval = msec2tspec(period); msec2tspec(period, &time.it_interval);
timerfd_settime(w->fd, 0, &time, NULL);
return timerfd_settime(w->fd, 0, &time, NULL); return uev_watcher_start(w);
}
return 0;
} }
/** /**
* Delete a timer watcher * Stop and unregister a timer watcher
* @param ctx A valid libuev context * @param w Watcher to stop
* @param w Watcher to delete
* *
* @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.
*/ */
int uev_timer_delete(uev_ctx_t *ctx, uev_t *w) int uev_timer_stop(uev_t *w)
{ {
int fd; if (uev_watcher_stop(w))
if (!ctx || !w) {
errno = EINVAL;
return -1; return -1;
}
uev_timer_set(ctx, w, 0, 0); /* Stop kernel timer */
fd = w->fd; uev_timer_set(w, 0, 0);
uev_watcher_delete(ctx, w);
close(fd); /* Close timerfd, will have to be reopened again on reset */
close(w->fd);
w->fd = -1;
return 0; return 0;
} }
+52 -47
View File
@@ -26,78 +26,83 @@
#ifndef LIBUEV_UEV_H_ #ifndef LIBUEV_UEV_H_
#define LIBUEV_UEV_H_ #define LIBUEV_UEV_H_
#include <stdio.h>
#include <sys/epoll.h>
#include <sys/queue.h> #include <sys/queue.h>
/* Max. number of simulateneous events */ /* Max. number of simulateneous events */
#define UEV_MAX_EVENTS 10 #define UEV_MAX_EVENTS 10
/* Forward declare due to dependencys, don't try this at home. */ /* I/O events, for signals and timers revents will always be UEV_READ */
struct uev; #define UEV_NONE 0
#define UEV_READ EPOLLIN
#define UEV_WRITE EPOLLOUT
/* I/O direction */ /* I/O, timer, or signal watcher */
typedef enum { typedef enum {
UEV_DIR_INBOUND = 0, UEV_IO_TYPE = 1,
UEV_DIR_OUTBOUND = 1
} uev_dir_t;
/* I/O, timer, or signal */
typedef enum {
UEV_FILE_TYPE = 1,
UEV_TIMER_TYPE, UEV_TIMER_TYPE,
UEV_SIGNAL_TYPE, UEV_SIGNAL_TYPE,
} uev_type_t; } uev_type_t;
/* I/O event watcher */ /* Forward declare due to dependencys, don't try this at home kids. */
typedef struct uev { struct uev;
LIST_ENTRY(uev) link; /* For queue.h linked list */
/* Common to all watchers */
uev_type_t type;
int fd;
/* Watcher callback with optional argument */
void (*cb)(struct uev *, struct uev *, void *);
void *arg;
/* Timer watchers, time in milliseconds */
int timeout;
int period;
/* Signal watchers */
int signo;
} uev_t;
/* Main libuev context type */ /* Main libuev context type */
typedef struct { typedef struct {
int running; int running;
int efd; /* For epoll() */ int fd; /* For epoll() */
LIST_HEAD(, uev) watchers; LIST_HEAD(,uev) watchers;
} uev_ctx_t; } uev_ctx_t;
/* I/O event watcher */
typedef struct uev {
LIST_ENTRY(uev) link; /* For queue.h linked list */
/* Common to all watchers */
uev_ctx_t *ctx;
uev_type_t type;
int active;
int fd;
int events;
/* Watcher callback with optional argument */
void (*cb)(struct uev *, struct uev *, void *);
void *arg;
/* Timer watchers, time in milliseconds */
int timeout;
int period;
/* Signal watchers */
int signo;
} uev_t;
/* Generic callback for watchers */ /* Generic callback for watchers */
typedef void (uev_cb_t) (uev_ctx_t *ctx, uev_t *w, void *data); typedef void (uev_cb_t)(uev_ctx_t *ctx, uev_t *w, void *arg, int events);
/* Private methods, do not use directly! */ /* Private methods, do not use directly! */
uev_t *uev_watcher_create(uev_ctx_t *ctx, uev_type_t type, int fd, uev_dir_t dir, uev_cb_t *cb, void *data); 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_delete(uev_ctx_t *ctx, uev_t *w); int uev_watcher_start (uev_t *w);
int uev_watcher_stop (uev_t *w);
/* Public interface */ /* Public interface */
uev_t *uev_io_create (uev_ctx_t *ctx, uev_cb_t *cb, void *data, int fd, uev_dir_t dir); int uev_init (uev_ctx_t *ctx);
int uev_io_delete (uev_ctx_t *ctx, uev_t *w); int uev_exit (uev_ctx_t *ctx);
int uev_timer_set (uev_ctx_t *ctx, uev_t *w, int timeout, int period); int uev_run (uev_ctx_t *ctx);
uev_t *uev_timer_create (uev_ctx_t *ctx, uev_cb_t *cb, void *data, int timeout, int period);
int uev_timer_delete (uev_ctx_t *ctx, uev_t *w);
uev_t *uev_signal_create (uev_ctx_t *ctx, uev_cb_t *handler, void *data, int signo); int uev_io_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events);
int uev_signal_set (uev_ctx_t *ctx, uev_t *w, int signo); int uev_io_set (uev_t *w, int fd, int events);
int uev_signal_delete (uev_ctx_t *ctx, uev_t *w); int uev_io_stop (uev_t *w);
uev_ctx_t *uev_ctx_create (void); int uev_timer_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period);
void uev_ctx_delete (uev_ctx_t *uev); int uev_timer_set (uev_t *w, int timeout, int period);
int uev_timer_stop (uev_t *w);
int uev_run (uev_ctx_t *ctx); int uev_signal_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo);
void uev_exit (uev_ctx_t *ctx); int uev_signal_set (uev_t *w, int signo);
int uev_signal_stop (uev_t *w);
#endif /* LIBUEV_UEV_H_ */ #endif /* LIBUEV_UEV_H_ */