Refactor to use modern timerfd and epoll

This is a major refactor of the original design by Flemming Madsen.  It
converts the library timers from poll() to timerfd, which at the same
time also unifies the file and timer watchers into one design.  The idea
is to also add signalfd to the mix later on.

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

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson committed 2013-07-22 20:25:08 +02:00
1 parent 30f6eccecd
commit 499cbd2032
4 files changed
+208 -324

No files matched your search

+1 -1
View File
@@ -27,7 +27,7 @@
CC = $(CROSS_COMPILE)gcc CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar AR = $(CROSS_COMPILE)ar
STRIP = $(CROSS_COMPILE)strip STRIP = $(CROSS_COMPILE)strip
CFLAGS += -fPIC -g -Os CFLAGS += -fPIC -Os
CPPFLAGS += -W -Wall -Iinclude CPPFLAGS += -W -Wall -Iinclude
ARFLAGS = crus ARFLAGS = crus
JUNK = *~ *.bak *.map .*.d DEADJOE *.gdb *.elf core core.* JUNK = *~ *.bak *.map .*.d DEADJOE *.gdb *.elf core core.*
+30 -35
View File
@@ -28,6 +28,9 @@
#include "queue.h" #include "queue.h"
/* Max. number of simulateneous events */
#define UEV_MAX_EVENTS 10
/* Elapsed time between two times() tick measurements in msec */ /* Elapsed time between two times() tick measurements in msec */
#define TIME_DIFF_MSEC(now, then) \ #define TIME_DIFF_MSEC(now, then) \
( (now - then) / clock_tick * 1000 + \ ( (now - then) / clock_tick * 1000 + \
@@ -42,64 +45,56 @@ typedef enum {
UEV_DIR_OUTBOUND = 1 UEV_DIR_OUTBOUND = 1
} uev_dir_t; } uev_dir_t;
/* File, Timer, ... */
typedef enum {
UEV_FILE_TYPE = 1,
UEV_TIMER_TYPE,
} uev_type_t;
/* I/O event watcher */ /* I/O event watcher */
typedef struct uev_io { typedef struct uev_io {
TAILQ_ENTRY(uev_io) link; TAILQ_ENTRY(uev_io) link;
TAILQ_ENTRY(uev_io) gc; TAILQ_ENTRY(uev_io) gc;
uev_type_t type;
int fd; ///< File descriptor int fd; ///< File descriptor
uev_dir_t dir; ///< Direction: in or out uev_dir_t dir; ///< Direction: in or out
int index; ///< Index in poll array
int timeout; ///< Timeout in milliseconds
int period; ///< Period time, in milliseconds
void (*handler)(struct uev *, struct uev_io *, void *); void (*handler)(struct uev *, struct uev_io *, void *);
void *data; void *data;
} uev_io_t; } uev_io_t;
/* Timer event watcher */
typedef struct uev_timer {
TAILQ_ENTRY(uev_timer) link;
int timeout; ///< Timeout in milliseconds
int period; ///< Period time, in milliseconds
void (*handler)(struct uev *, struct uev_timer *, void *);
void *data;
} uev_timer_t;
/* Main libuev context type */ /* Main libuev context type */
typedef struct { typedef struct {
TAILQ_HEAD(, uev_io) io_list; ///< File handlers int running;
TAILQ_HEAD(, uev_timer) timer_list; ///< Timer handlers
TAILQ_HEAD(, uev_io) io_delist; ///< List of file handles to be garbage collected int efd; /* For epoll() */
TAILQ_HEAD(, uev_timer) timer_delist; ///< List of timer handles to be garbage collected struct epoll_event *events;
clock_t base_time; ///< Time at last timer tick TAILQ_HEAD(, uev_io) active_list;
clock_t next_time; ///< Next timer tick epoch TAILQ_HEAD(, uev_io) inactive_list;
int use_next; ///< True if next timer epoch is set
int exiting; ///< True if the application is terminating
} uev_t; } uev_t;
/* Callbacks for different watchers */ /* Generic callback for watchers */
typedef void (*uev_io_cb_t) (uev_t *ctx, uev_io_t *w, int fd, void *data); typedef void (uev_cb_t) (uev_t *ctx, uev_io_t *w, void *data);
typedef void (*uev_timer_cb_t)(uev_t *ctx, uev_timer_t *w, void *data);
/* Public interface */ /* Public interface */
uev_io_t *uev_io_create (uev_t *ctx, uev_io_cb_t cb, void *data, int fd, uev_dir_t dir); uev_io_t *uev_io_create (uev_t *ctx, uev_cb_t *cb, void *data, int fd, uev_dir_t dir);
int uev_io_delete (uev_t *ctx, uev_io_t *w); int uev_io_delete (uev_t *ctx, uev_io_t *w);
int uev_timer_set (uev_t *ctx, uev_timer_t *w, int timeout, int period); int uev_timer_set (uev_t *ctx, uev_io_t *w, int timeout, int period);
uev_timer_t *uev_timer_create (uev_t *ctx, uev_timer_cb_t cb, void *data, int timeout, int period); uev_io_t *uev_timer_create(uev_t *ctx, uev_cb_t *cb, void *data, int timeout, int period);
int uev_timer_delete (uev_t *ctx, uev_timer_t *w); int uev_timer_delete(uev_t *ctx, uev_io_t *w);
void uev_run (uev_t *ctx); uev_t *uev_ctx_create (void);
void uev_run_tick (uev_t *ctx, int msec); void uev_ctx_delete (uev_t *uev);
int uev_run_pending (uev_t *ctx);
void uev_exit (uev_t *ctx);
uev_t *uev_ctx_create (void); int uev_run (uev_t *ctx);
void uev_ctx_delete (uev_t *uev); void uev_exit (uev_t *ctx);
#endif /* LIBUEV_UEV_H_ */ #endif /* LIBUEV_UEV_H_ */
+167 -278
View File
@@ -26,29 +26,27 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <poll.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sys/times.h> #include <sys/times.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include "libuev/uev.h" #include "libuev/uev.h"
static clock_t clock_tick = 0; static uev_io_t *new_watcher(uev_t *ctx, uev_type_t type, int fd, uev_dir_t dir, uev_cb_t *handler, void *data)
static clock_t uev_time_now(uev_t *ctx)
{
if (!ctx->use_next)
return times(NULL);
return ctx->next_time;
}
uev_io_t *uev_io_create(uev_t *ctx, uev_io_cb_t handler, void *data, int fd, uev_dir_t dir)
{ {
uev_io_t *w; uev_io_t *w;
struct epoll_event ev;
if (!ctx || !handler) {
errno = EINVAL;
return NULL;
}
w = (uev_io_t *)calloc(1, sizeof(*w)); w = (uev_io_t *)calloc(1, sizeof(*w));
if (!w) if (!w)
@@ -56,16 +54,23 @@ uev_io_t *uev_io_create(uev_t *ctx, uev_io_cb_t handler, void *data, int fd, uev
w->fd = fd; w->fd = fd;
w->dir = dir; w->dir = dir;
w->type = type;
w->handler = (void *)handler; w->handler = (void *)handler;
w->data = data; w->data = data;
w->index = -1;
TAILQ_INSERT_TAIL(&ctx->io_list, w, link); 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;
}
TAILQ_INSERT_TAIL(&ctx->active_list, w, link);
return w; return w;
} }
int uev_io_delete(uev_t *ctx, uev_io_t *w) static int delete_watcher(uev_t *ctx, uev_io_t *w)
{ {
uev_io_t *tmp; uev_io_t *tmp;
@@ -75,29 +80,31 @@ int uev_io_delete(uev_t *ctx, uev_io_t *w)
} }
/* Check if already removed */ /* Check if already removed */
TAILQ_FOREACH(tmp, &ctx->io_delist, gc) { TAILQ_FOREACH(tmp, &ctx->inactive_list, gc) {
if (tmp == w) { if (tmp == w) {
errno = EALREADY; errno = EALREADY;
return -1; return -1;
} }
} }
/* Remove from kernel */
epoll_ctl(ctx->efd, EPOLL_CTL_DEL, w->fd, NULL);
/* Mark as inactive */
w->handler = NULL; w->handler = NULL;
TAILQ_INSERT_TAIL(&ctx->io_delist, w, gc); TAILQ_INSERT_TAIL(&ctx->inactive_list, w, gc);
return 0; return 0;
} }
static int timer_active(uev_t *ctx, uev_timer_t *w) uev_io_t *uev_io_create(uev_t *ctx, uev_cb_t *handler, void *data, int fd, uev_dir_t dir)
{ {
uev_timer_t *tmp; return new_watcher(ctx, UEV_FILE_TYPE, fd, dir, handler, data);
}
TAILQ_FOREACH(tmp, &ctx->timer_list, link) { int uev_io_delete(uev_t *ctx, uev_io_t *w)
if (tmp == w) {
return 1; return delete_watcher(ctx, w);
}
return 0;
} }
/** /**
@@ -118,41 +125,45 @@ static int timer_active(uev_t *ctx, uev_timer_t *w)
* *
* @return The new timer, or %NULL if invalid pointers or out or memory. * @return The new timer, or %NULL if invalid pointers or out or memory.
*/ */
uev_timer_t *uev_timer_create(uev_t *ctx, uev_timer_cb_t handler, void *data, int timeout, int period) uev_io_t *uev_timer_create(uev_t *ctx, uev_cb_t *handler, void *data, int timeout, int period)
{ {
uev_timer_t *w; int fd;
uev_io_t *w;
if (!ctx || !handler) { fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
errno = EINVAL; if (fd < 0)
return NULL;
w = new_watcher(ctx, UEV_TIMER_TYPE, fd, UEV_DIR_INBOUND, handler, data);
if (!w) {
close(fd);
return NULL; return NULL;
} }
w = (uev_timer_t *)calloc(1, sizeof(*w)); if (uev_timer_set(ctx, w, timeout, period)) {
if (!w) delete_watcher(ctx, w);
close(fd);
return NULL; return NULL;
w->handler = (void *)handler;
w->data = data;
/* Zero delay is a special case: A oneshot workproc: Must go first */
if (0 == timeout) {
TAILQ_INSERT_HEAD(&ctx->timer_list, w, link);
return w;
} }
uev_timer_set(ctx, w, timeout, period);
return w; return w;
} }
/** /**
* Reset or reschedule a timer * Reset or reschedule a timer
*/ */
int uev_timer_set(uev_t *ctx, uev_timer_t *w, int timeout, int period) int uev_timer_set(uev_t *ctx, uev_io_t *w, int timeout, int period)
{ {
int diff; struct itimerspec time = {
clock_t now; .it_value = {
uev_timer_t *tmp; .tv_sec = timeout / 1000,
.tv_nsec = (timeout % 1000) * 1000000
},
.it_interval = {
.tv_sec = period / 1000,
.tv_nsec = (period % 1000) * 1000000
}
};
if (!ctx || !w) { if (!ctx || !w) {
errno = EINVAL; errno = EINVAL;
@@ -162,248 +173,50 @@ int uev_timer_set(uev_t *ctx, uev_timer_t *w, int timeout, int period)
w->timeout = timeout; w->timeout = timeout;
w->period = period; w->period = period;
/* Adjust timers. if (!ctx->running)
* Jiffie wraparound is OK since we only care about diff time */ return 0;
now = uev_time_now(ctx);
diff = TIME_DIFF_MSEC(now, ctx->base_time);
/* When rescheduling we need to remove the entry first */ return timerfd_settime(w->fd, 0, &time, NULL);
if (timer_active(ctx, w))
TAILQ_REMOVE(&ctx->timer_list, w, link);
/* Update all timers and add this entry (ordered) to the list */
TAILQ_FOREACH(tmp, &ctx->timer_list, link) {
if (tmp->timeout > diff)
tmp->timeout -= diff;
else
tmp->timeout = 0;
if (w && tmp->timeout > timeout) {
TAILQ_INSERT_BEFORE(tmp, w, link);
w = NULL;
}
}
if (w)
TAILQ_INSERT_TAIL(&ctx->timer_list, w, link);
/* Update time base */
ctx->base_time = now;
return 0;
} }
/** /**
* Remove a timer event * Remove a timer event
*/ */
int uev_timer_delete(uev_t *ctx, uev_timer_t *w) int uev_timer_delete(uev_t *ctx, uev_io_t *w)
{ {
uev_timer_t *tmp; uev_timer_set(ctx, w, 0, 0);
return delete_watcher(ctx, w);
if (!ctx || !w) {
errno = EINVAL;
return -1;
}
/* Check if already removed */
TAILQ_FOREACH(tmp, &ctx->timer_delist, link) {
if (tmp == w) {
errno = EALREADY;
return -1;
}
}
TAILQ_REMOVE(&ctx->timer_list, w, link);
TAILQ_INSERT_TAIL(&ctx->timer_delist, w, link);
return 0;
} }
/**
* Process pending events
*/
static int do_run_pending(uev_t *ctx, int immediate)
{
int i, result, diff = 0;
size_t num = 0;
clock_t now = 0;
uev_io_t *w;
uev_timer_t *timer, *tmp;
/* Do due timers */
if (!TAILQ_EMPTY(&ctx->timer_list)) {
now = uev_time_now(ctx);
diff = TIME_DIFF_MSEC(now, ctx->base_time);
TAILQ_FOREACH(timer, &ctx->timer_list, link) {
if (timer->timeout > diff)
continue;
timer->handler((struct uev *)ctx, timer, timer->data);
if (!timer->period)
uev_timer_delete(ctx, timer);
else
uev_timer_set(ctx, timer, timer->period, timer->period);
/* base_time changes for every uev_timer_create(), which
* can be called in every timer->handler() above. */
diff = TIME_DIFF_MSEC(now, ctx->base_time);
}
/* Garbage collect */
TAILQ_FOREACH_SAFE(timer, &ctx->timer_delist, link, tmp) {
TAILQ_REMOVE(&ctx->timer_delist, timer, link);
free(timer);
}
}
/* Calculate waiting time */
if (ctx->exiting)
diff = 0;
else if ((timer = TAILQ_FIRST(&ctx->timer_list)) && timer->timeout > diff)
diff = timer->timeout - diff;
else if (NULL != timer)
diff = 0;
else
diff = -1;
/* Do pipe handlers */
TAILQ_FOREACH(w, &ctx->io_list, link)
num++;
if (!TAILQ_EMPTY(&ctx->io_list) && !ctx->exiting) {
struct pollfd polls[num];
/* Prepare all I/O watchers for poll() */
i = 0;
TAILQ_FOREACH(w, &ctx->io_list, link) {
w->index = i;
polls[i].fd = w->fd;
polls[i].events = UEV_DIR_INBOUND == w->dir ? POLLIN : POLLOUT;
polls[i].revents = 0;
i++;
}
while ((result = poll(polls, i, immediate ? 0 : diff)) < 0) {
if (EINTR == errno)
continue; /* Signalled, try again */
if (EBADF == errno) {
TAILQ_FOREACH(w, &ctx->io_list, link) {
if (fcntl(w->fd, F_GETFD) == -1 && EBADF == errno)
w->handler((struct uev *)ctx, w, w->data);
}
} else {
/* Error in poll. Cannot continue */
assert(false);
return -2;
}
}
if (result > 0) {
TAILQ_FOREACH(w, &ctx->io_list, link) {
if (w->handler && w->index >= 0 && polls[w->index].revents)
w->handler((struct uev *)ctx, w, w->data);
}
}
/* Garbage collect */
if (!TAILQ_EMPTY(&ctx->io_delist)) {
TAILQ_FOREACH(w, &ctx->io_delist, gc) {
TAILQ_REMOVE(&ctx->io_delist, w, gc);
TAILQ_REMOVE(&ctx->io_list, w, link);
free(w);
}
}
} else if (diff > 0) {
if (!immediate)
poll(NULL, 0, diff);
} else if (diff == -1) {
diff = -2; /* Nothing more to do */
}
return diff;
}
/**
* Run the application
*/
void uev_run(uev_t *ctx)
{
int ret = 0;
if (!ctx)
return;
while (!ctx->exiting && ret != -2)
ret = do_run_pending(ctx, 0);
ctx->exiting = false;
}
/**
* Drive the timeouts ourself (from now on until an increment of -1)
* This is intended for use in testing frameworks
*
* @param ctx libuev context object
* @param msec Milliseconds to advance the timer. Use -1 to return to realtime
*/
void uev_run_tick(uev_t *ctx, int msec)
{
/* Obviously we are executing a callback right now (arent we always)
* The time leap is taken into account before falling back into poll() below */
if (msec >= 0) {
if (!ctx->use_next) {
ctx->use_next = true;
ctx->next_time = times(NULL) + msec;
} else {
ctx->next_time += msec;
}
} else if (msec == -1) {
/* Go back to realtime */
ctx->use_next = false;
ctx->base_time += times(NULL) - ctx->next_time;
ctx->next_time = 0;
}
}
/**
* Process pending events
*/
int uev_run_pending(uev_t *ctx)
{
return do_run_pending(ctx, 1);
}
/**
* Terminate the application
*/
void uev_exit(uev_t *ctx)
{
ctx->exiting = true;
}
/** /**
* Create an application context * Create an application context
*/ */
uev_t *uev_ctx_create(void) uev_t *uev_ctx_create(void)
{ {
int fd;
uev_t *ctx; uev_t *ctx;
ctx = (uev_t *)calloc(1, sizeof(*ctx)); fd = epoll_create(1);
if (!ctx) if (fd < 0)
return NULL; return NULL;
TAILQ_INIT(&ctx->io_list); ctx = (uev_t *)calloc(1, sizeof(*ctx));
TAILQ_INIT(&ctx->timer_list); if (!ctx) {
TAILQ_INIT(&ctx->io_delist); close(fd);
TAILQ_INIT(&ctx->timer_delist); return NULL;
}
/* Get system time and clock resolution, in ticks/second */ ctx->events = (struct epoll_event *)calloc(UEV_MAX_EVENTS, sizeof(struct epoll_event));
ctx->base_time = times(NULL); if (!ctx->events) {
if (0 == clock_tick) close(fd);
clock_tick = sysconf(_SC_CLK_TCK); free(ctx);
return NULL;
}
ctx->efd = fd;
TAILQ_INIT(&ctx->active_list);
TAILQ_INIT(&ctx->inactive_list);
return ctx; return ctx;
} }
@@ -413,22 +226,98 @@ uev_t *uev_ctx_create(void)
*/ */
void uev_ctx_delete(uev_t *ctx) void uev_ctx_delete(uev_t *ctx)
{ {
uev_io_t *io, *tmp_io; uev_io_t *w, *tmp;
uev_timer_t *timer, *tmp_timer;
TAILQ_FOREACH_SAFE(io, &ctx->io_list, link, tmp_io) { TAILQ_FOREACH_SAFE(w, &ctx->active_list, link, tmp) {
TAILQ_REMOVE(&ctx->io_list, io, link); TAILQ_REMOVE(&ctx->active_list, w, link);
free(io); free(w);
}
TAILQ_FOREACH_SAFE(timer, &ctx->timer_list, link, tmp_timer) {
TAILQ_REMOVE(&ctx->timer_list, timer, link);
free(timer);
} }
close(ctx->efd);
free(ctx->events);
free(ctx); free(ctx);
} }
/**
* Run the application
*/
int uev_run(uev_t *ctx)
{
int result = 0;
uev_io_t *w, *tmp;
if (!ctx) {
errno = EINVAL;
return 1;
}
/* Start the event loop */
ctx->running = 1;
/* Start all dormant timers */
TAILQ_FOREACH(w, &ctx->active_list, link) {
if (UEV_TIMER_TYPE == w->type)
uev_timer_set(ctx, w, w->timeout, w->period);
}
while (ctx->running) {
int i, nfds;
while ((nfds = epoll_wait(ctx->efd, ctx->events, UEV_MAX_EVENTS, -1)) < 0) {
if (EINTR == errno)
continue; /* Signalled, try again */
/* Error in poll. Cannot continue */
result = 2;
break;
}
for (i = 0; i < nfds; i++) {
uev_io_t *w = (uev_io_t *)ctx->events[i].data.ptr;
if (w->handler)
w->handler((struct uev *)ctx, w, w->data);
if (UEV_TIMER_TYPE == w->type) {
int result;
uint64_t exp;
result = read(w->fd, &exp, sizeof(exp));
if (result != sizeof(exp)) {
/* Error in timerfd. Cannot continue */
result = 3;
ctx->running = 0;
}
if (!w->period)
uev_timer_delete(ctx, w);
}
}
/* Garbage collect */
if (!TAILQ_EMPTY(&ctx->inactive_list)) {
TAILQ_FOREACH_SAFE(w, &ctx->inactive_list, gc, tmp) {
TAILQ_REMOVE(&ctx->inactive_list, w, gc);
TAILQ_REMOVE(&ctx->active_list, w, link);
free(w);
}
}
}
ctx->running = 0;
return result;
}
/**
* Terminate the application
*/
void uev_exit(uev_t *ctx)
{
ctx->running = 0;
}
/** /**
* Local Variables: * Local Variables:
* version-control: t * version-control: t
+10 -10
View File
@@ -31,16 +31,16 @@
static int in, out; static int in, out;
static int period = 0; static int period = 0;
static uev_timer_t *timer = NULL; static uev_io_t *timer = NULL;
static void lifetime_cb(uev_t *ctx, uev_timer_t *w __attribute__ ((unused)), void *data) static void lifetime_cb(uev_t *ctx, uev_io_t *w __attribute__ ((unused)), void *data)
{ {
fprintf(stderr, "\nLifetime exceeded %p\n", data); fprintf(stderr, "\nLifetime exceeded %p\n", data);
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_t *ctx, uev_timer_t *w, void *data) static void timeout_cb(uev_t *ctx, uev_io_t *w, void *data)
{ {
timer = NULL; timer = NULL;
fprintf(stderr, "\nTimeout exceeded %p\n", data); fprintf(stderr, "\nTimeout exceeded %p\n", data);
@@ -49,7 +49,7 @@ static void timeout_cb(uev_t *ctx, uev_timer_t *w, void *data)
uev_exit(ctx); uev_exit(ctx);
} }
static void periodic_task(uev_t *ctx __attribute__ ((unused)), uev_timer_t *w __attribute__ ((unused)), void *data __attribute__ ((unused))) static void periodic_task(uev_t *ctx __attribute__ ((unused)), uev_io_t *w __attribute__ ((unused)), void *data __attribute__ ((unused)))
{ {
fprintf(stderr, "|"); fprintf(stderr, "|");
} }
@@ -68,7 +68,7 @@ static void pipe_read_cb(uev_t *ctx, uev_io_t *w __attribute__ ((unused)), void
fprintf(stderr, "%.*s.%d ", cnt, msg, cnt); fprintf(stderr, "%.*s.%d ", cnt, msg, cnt);
} }
static void pipe_write_cb(uev_t *ctx, uev_timer_t *w __attribute__ ((unused)), void *data) static void pipe_write_cb(uev_t *ctx, uev_io_t *w __attribute__ ((unused)), void *data)
{ {
int cnt = (int)(intptr_t)data; int cnt = (int)(intptr_t)data;
char *msg = "TESTING"; char *msg = "TESTING";
@@ -91,7 +91,7 @@ int main(void)
uev_t *ctx = uev_ctx_create(); uev_t *ctx = uev_ctx_create();
/* Total program execution time */ /* Total program execution time */
uev_timer_create(ctx, (uev_timer_cb_t)lifetime_cb, (void *)(intptr_t)2, 4000, 0); 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
@@ -104,15 +104,15 @@ int main(void)
in = fd[0]; in = fd[0];
out = fd[1]; out = fd[1];
uev_timer_create(ctx, (uev_timer_cb_t)pipe_write_cb, (void *)(intptr_t)1, 400, 0); uev_timer_create(ctx, pipe_write_cb, (void *)(intptr_t)1, 400, 0);
uev_io_create(ctx, (uev_io_cb_t)pipe_read_cb, NULL, in, UEV_DIR_INBOUND); uev_io_create(ctx, pipe_read_cb, NULL, in, UEV_DIR_INBOUND);
/* 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, (uev_timer_cb_t)timeout_cb, (void *)(intptr_t)1, 950, 0); timer = uev_timer_create(ctx, timeout_cb, (void *)(intptr_t)1, 950, 0);
/* Periodic background task */ /* Periodic background task */
uev_timer_create(ctx, (uev_timer_cb_t)periodic_task, NULL, 200, 200); uev_timer_create(ctx, periodic_task, NULL, 200, 200);
/* Start event loop */ /* Start event loop */
uev_run(ctx); uev_run(ctx);