mirror of
https://github.com/troglobit/libuev.git
synced 2026-10-19 10:49:31 +00:00
Relocate source fiiles to src/ subdirectory
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
1 parent
aae505fb33
commit
ed5c9bb06c
15 files changed
+19
-19
No files matched your search
@@ -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 1:2: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
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright 2003 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*
|
||||
* Mon 03/10/2003 - Modified by Davide Libenzi <davidel@xmailserver.org>
|
||||
*
|
||||
* Added chain event propagation to improve the sensitivity of
|
||||
* the measure respect to the event loop efficency.
|
||||
*
|
||||
* Sun 04/08/2013 - Modified by Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Adaptations for libuEv, no libev/libevent API wrappers available.
|
||||
* Reindent to Linux coding style
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#else
|
||||
#define USE_PIPES
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "uev.h"
|
||||
|
||||
#define UNUSED(arg) arg __attribute__ ((unused))
|
||||
|
||||
typedef struct {
|
||||
int index;
|
||||
} myarg_t;
|
||||
|
||||
static int num_pipes, num_active, num_writes;
|
||||
static int timers, count, writes, fired;
|
||||
static myarg_t *args;
|
||||
static int *pipes;
|
||||
static uev_t *evio;
|
||||
static uev_t *evto;
|
||||
|
||||
static void read_cb(uev_t *w, void *arg, int UNUSED(events))
|
||||
{
|
||||
int idx, widx;
|
||||
u_char ch;
|
||||
myarg_t *m = arg;
|
||||
|
||||
idx = m->index;
|
||||
widx = idx + 1;
|
||||
if (timers)
|
||||
uev_timer_set(&evto[idx], 10000 + drand48() * 1000, 0);
|
||||
|
||||
count += read(w->fd, &ch, sizeof(ch));
|
||||
if (writes) {
|
||||
if (widx >= num_pipes)
|
||||
widx -= num_pipes;
|
||||
|
||||
if (write(pipes[2 * widx + 1], "e", 1) < 0) {
|
||||
perror("write()");
|
||||
abort();
|
||||
}
|
||||
|
||||
writes--;
|
||||
fired++;
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_cb(uev_t UNUSED(*w), void UNUSED(*arg), int UNUSED(events))
|
||||
{
|
||||
/* nop */
|
||||
}
|
||||
|
||||
static struct timeval *run_once(uev_ctx_t *ctx)
|
||||
{
|
||||
int *cp, i, space;
|
||||
static struct timeval ta, ts, te;
|
||||
|
||||
gettimeofday(&ta, NULL);
|
||||
for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
|
||||
uev_io_set(&evio[i], cp[0], UEV_READ);
|
||||
|
||||
if (timers)
|
||||
uev_timer_set(&evto[i], 10000 + drand48() * 1000, 0);
|
||||
}
|
||||
|
||||
uev_run(ctx, UEV_ONCE | UEV_NONBLOCK);
|
||||
|
||||
fired = 0;
|
||||
space = num_pipes / num_active;
|
||||
space = space * 2;
|
||||
for (i = 0; i < num_active; i++, fired++) {
|
||||
if (write(pipes[i * space + 1], "e", 1) < 0) {
|
||||
perror("write()");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
writes = num_writes;
|
||||
{
|
||||
int xcount = 0;
|
||||
|
||||
gettimeofday(&ts, NULL);
|
||||
|
||||
do {
|
||||
uev_run(ctx, UEV_ONCE | UEV_NONBLOCK);
|
||||
xcount++;
|
||||
} while (count != fired);
|
||||
|
||||
gettimeofday(&te, NULL);
|
||||
|
||||
if (xcount != count)
|
||||
fprintf(stderr, "Xcount: %d, Rcount: %d\n", xcount, count);
|
||||
}
|
||||
|
||||
timersub(&te, &ta, &ta);
|
||||
timersub(&te, &ts, &ts);
|
||||
fprintf(stdout, "%8ld %8ld\n",
|
||||
ta.tv_sec * 1000000L + ta.tv_usec,
|
||||
ts.tv_sec * 1000000L + ts.tv_usec);
|
||||
|
||||
return &te;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct rlimit rl;
|
||||
int i, c;
|
||||
int *cp;
|
||||
uev_ctx_t ctx;
|
||||
extern char *optarg;
|
||||
|
||||
num_pipes = 100;
|
||||
num_active = 1;
|
||||
num_writes = num_pipes;
|
||||
while ((c = getopt(argc, argv, "a:n:tw:")) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
num_active = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
num_pipes = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 't':
|
||||
timers = 1;
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
num_writes = atoi(optarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Illegal argument \"%c\"\n", c);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
rl.rlim_cur = rl.rlim_max = num_pipes * 3 + 50;
|
||||
if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
|
||||
perror("setrlimit");
|
||||
return 1;
|
||||
}
|
||||
|
||||
args = calloc(num_pipes, sizeof(myarg_t));
|
||||
evio = calloc(num_pipes, sizeof(uev_t));
|
||||
evto = calloc(num_pipes, sizeof(uev_t));
|
||||
pipes = calloc(num_pipes * 2, sizeof(int));
|
||||
if (!args || !evio || !evto || !pipes) {
|
||||
perror("calloc");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uev_init(&ctx);
|
||||
|
||||
for (cp = pipes, i = 0; i < num_pipes; i++, cp += 2) {
|
||||
if (timers)
|
||||
uev_timer_init(&ctx, &evto[i], timer_cb, NULL, 0, 0);
|
||||
args[i].index = i;
|
||||
|
||||
#ifdef USE_PIPES
|
||||
if (pipe2(cp, O_NONBLOCK) == -1) {
|
||||
#else
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, cp) == -1) {
|
||||
#endif
|
||||
perror("pipe");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
uev_io_init(&ctx, &evio[i], read_cb, &args[i], cp[0], UEV_READ);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
run_once(&ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/* libuEv - Micro event loop library
|
||||
*
|
||||
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
|
||||
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h> /* memset() */
|
||||
#include <sys/timerfd.h>
|
||||
#include <unistd.h> /* close(), read() */
|
||||
|
||||
#include "uev.h"
|
||||
|
||||
/* Missing defines in GLIBC <= 2.24 */
|
||||
#ifndef TFD_TIMER_CANCEL_ON_SET
|
||||
#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
|
||||
#endif
|
||||
#ifndef TFD_SETTIME_FLAGS
|
||||
#define TFD_SETTIME_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Create and start an at/cron job watcher
|
||||
* @param ctx A valid libuEv context
|
||||
* @param w Pointer to an uev_t watcher
|
||||
* @param cb Callback function for cron job
|
||||
* @param arg Optional callback argument
|
||||
* @param when First point in time to call @param cb
|
||||
* @param interval For an at job this is zero, for cron the offset interval
|
||||
*
|
||||
* For at jobs set @param interval to zero and only use @param when. For
|
||||
* cron jobs, set @param interval to the offset. E.g., if the job should
|
||||
* run every five minutes set the @param tm_min of struct tm to five.
|
||||
*
|
||||
* Use mktime() to create the time_t arguments. The special value zero
|
||||
* may be used for @param when to denote 'now', where 'now' is when the
|
||||
* event loop is started. You can also treat time_t simply as a signed
|
||||
* integer. E.g., set @param interval to 3600 to create a cron job that
|
||||
* runs every hour.
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_cron_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, time_t when, time_t interval)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (when < 0 || interval < 0) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
if (uev_watcher_init(ctx, w, UEV_CRON_TYPE, cb, arg, fd, UEV_READ))
|
||||
goto exit;
|
||||
|
||||
if (uev_cron_set(w, when, interval)) {
|
||||
uev_watcher_stop(w);
|
||||
exit:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset an at/cron job watcher
|
||||
* @param w Watcher to reset
|
||||
* @param when First point in time to call @param cb
|
||||
* @param interval For an at job this is zero, for cron the offset interval
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_cron_set(uev_t *w, time_t when, time_t interval)
|
||||
{
|
||||
/* Every watcher must be registered to a context */
|
||||
if (!w || !w->ctx) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (when < 0 || interval < 0) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Handle stopped timers */
|
||||
if (w->fd < 0) {
|
||||
/* Timer already stopped */
|
||||
if (!when && !interval)
|
||||
return 0;
|
||||
|
||||
/* Remove from internal list */
|
||||
LIST_REMOVE(w, link);
|
||||
|
||||
if (uev_cron_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, when, interval))
|
||||
return -1;
|
||||
}
|
||||
|
||||
w->u.c.when = when;
|
||||
w->u.c.interval = interval;
|
||||
|
||||
if (w->ctx->running) {
|
||||
struct itimerspec time;
|
||||
|
||||
memset(&time, 0, sizeof(time));
|
||||
time.it_value.tv_sec = when;
|
||||
time.it_interval.tv_sec = interval;
|
||||
if (timerfd_settime(w->fd, TFD_SETTIME_FLAGS, &time, NULL) < 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return uev_watcher_start(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a stopped at/cron job watcher
|
||||
* @param w Watcher to start (again)
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_cron_start(uev_t *w)
|
||||
{
|
||||
return uev_timer_start(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop and unregister an at/cron job watcher
|
||||
* @param w Watcher to stop
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_cron_stop(uev_t *w)
|
||||
{
|
||||
return uev_timer_stop(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,100 @@
|
||||
/* libuEv - Micro event loop library
|
||||
*
|
||||
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
|
||||
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include "uev.h"
|
||||
|
||||
|
||||
/**
|
||||
* Create an I/O watcher
|
||||
* @param ctx A valid libuEv context
|
||||
* @param w Pointer to an uev_t watcher
|
||||
* @param cb I/O callback
|
||||
* @param arg Optional callback argument
|
||||
* @param fd File descriptor to watch, or -1 to register an empty watcher
|
||||
* @param events Requested events to watch for, a mask of %UEV_READ, %UEV_WRITE and %UEV_PRI
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_io_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events)
|
||||
{
|
||||
if (fd < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (uev_watcher_init(ctx, w, UEV_IO_TYPE, cb, arg, fd, events))
|
||||
return -1;
|
||||
|
||||
return uev_watcher_start(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset an I/O watcher
|
||||
* @param w Pointer to an uev_t watcher
|
||||
* @param fd New file descriptor to monitor
|
||||
* @param events Requested events to watch for, a mask of %UEV_READ and %UEV_WRITE
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_io_set(uev_t *w, int fd, int events)
|
||||
{
|
||||
|
||||
if ((events & UEV_ONESHOT) && w->active)
|
||||
return uev_watcher_rearm(w);
|
||||
|
||||
/* Ignore any errors, only to clean up anything lingering ... */
|
||||
uev_io_stop(w);
|
||||
|
||||
return uev_io_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, fd, events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an I/O watcher
|
||||
* @param w Watcher to start (again)
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_io_start(uev_t *w)
|
||||
{
|
||||
return uev_io_set(w, w->fd, w->events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop an I/O watcher
|
||||
* @param w Watcher to stop
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_io_stop(uev_t *w)
|
||||
{
|
||||
return uev_watcher_stop(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: @PACKAGE@
|
||||
Description: Simple event loop for Linux
|
||||
Version: @VERSION@
|
||||
Requires:
|
||||
Libs: -L${libdir} -luev
|
||||
Cflags: -I${includedir}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/* libuEv - Private methods and data types, do not use directly!
|
||||
*
|
||||
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
|
||||
* Copyright (c) 2013-2015 Joachim Nilsson <troglobit()gmail!com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LIBUEV_PRIVATE_H_
|
||||
#define LIBUEV_PRIVATE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/epoll.h>
|
||||
#include "queue.h" /* OpenBSD queue.h > old GLIBC version */
|
||||
|
||||
/* I/O, timer, or signal watcher */
|
||||
typedef enum {
|
||||
UEV_CRON_TYPE = 1,
|
||||
UEV_IO_TYPE,
|
||||
UEV_SIGNAL_TYPE,
|
||||
UEV_TIMER_TYPE,
|
||||
} uev_type_t;
|
||||
|
||||
/* Event mask, used internally only. */
|
||||
#define UEV_EVENT_MASK (UEV_READ | UEV_WRITE | UEV_PRI | UEV_HUP | UEV_RDHUP | UEV_EDGE | UEV_ONESHOT)
|
||||
|
||||
/* Main libuEv context type */
|
||||
typedef struct {
|
||||
int running;
|
||||
int fd; /* For epoll() */
|
||||
uint32_t errors;
|
||||
LIST_HEAD(,uev) watchers;
|
||||
} uev_ctx_t;
|
||||
|
||||
/* Forward declare due to dependencys, don't try this at home kids. */
|
||||
struct uev;
|
||||
|
||||
/* This is used to hide all private data members in uev_t */
|
||||
#define uev_private_t \
|
||||
LIST_ENTRY(uev) link; /* For queue.h linked list */ \
|
||||
\
|
||||
int active; \
|
||||
int events; \
|
||||
\
|
||||
/* Watcher callback with optional argument */ \
|
||||
void (*cb)(struct uev *, void *, int); \
|
||||
void *arg; \
|
||||
\
|
||||
/* Arguments for different watchers */ \
|
||||
union { \
|
||||
/* Cron watchers */ \
|
||||
struct { \
|
||||
time_t when; \
|
||||
time_t interval; \
|
||||
} c; \
|
||||
\
|
||||
/* Timer watchers, time in milliseconds */ \
|
||||
struct { \
|
||||
int timeout; \
|
||||
int period; \
|
||||
} t; \
|
||||
} u; \
|
||||
\
|
||||
/* Watcher type */ \
|
||||
uev_type_t
|
||||
|
||||
/* Internal API for dealing with generic watchers */
|
||||
int uev_watcher_init (uev_ctx_t *ctx, struct uev *w, uev_type_t type,
|
||||
void (*cb)(struct uev *, void *, int), void *arg,
|
||||
int fd, int events);
|
||||
int uev_watcher_start (struct uev *w);
|
||||
int uev_watcher_stop (struct uev *w);
|
||||
int uev_watcher_active(struct uev *w);
|
||||
int uev_watcher_rearm (struct uev *w);
|
||||
|
||||
#endif /* LIBUEV_PRIVATE_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+648
@@ -0,0 +1,648 @@
|
||||
/* $OpenBSD: queue.h,v 1.38 2013/07/03 15:05:21 fgsch Exp $ */
|
||||
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* lists, simple queues, tail queues, and circular queues.
|
||||
*
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the list.
|
||||
* A circle queue may be traversed in either direction, but has a more
|
||||
* complex end of list detection.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
|
||||
#define _Q_INVALIDATE(a) (a) = ((void *)-1)
|
||||
#else
|
||||
#define _Q_INVALIDATE(a)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List access methods.
|
||||
*/
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
#define SLIST_END(head) NULL
|
||||
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for((var) = SLIST_FIRST(head); \
|
||||
(var) != SLIST_END(head); \
|
||||
(var) = SLIST_NEXT(var, field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST(head); \
|
||||
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) { \
|
||||
SLIST_FIRST(head) = SLIST_END(head); \
|
||||
}
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->slh_first; \
|
||||
\
|
||||
while (curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = \
|
||||
curelm->field.sle_next->field.sle_next; \
|
||||
_Q_INVALIDATE((elm)->field.sle_next); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List access methods
|
||||
*/
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
#define LIST_END(head) NULL
|
||||
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for((var) = LIST_FIRST(head); \
|
||||
(var)!= LIST_END(head); \
|
||||
(var) = LIST_NEXT(var, field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST(head); \
|
||||
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST(head) = LIST_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||
(elm2)->field.le_next->field.le_prev = \
|
||||
&(elm2)->field.le_next; \
|
||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm2)->field.le_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define SIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define SIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define SIMPLEQ_END(head) NULL
|
||||
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
|
||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||
for((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) != SIMPLEQ_END(head); \
|
||||
(var) = SIMPLEQ_NEXT(var, field))
|
||||
|
||||
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
|
||||
== NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* XOR Simple queue definitions.
|
||||
*/
|
||||
#define XSIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqx_first; /* first element */ \
|
||||
struct type **sqx_last; /* addr of last next element */ \
|
||||
unsigned long sqx_cookie; \
|
||||
}
|
||||
|
||||
#define XSIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqx_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* XOR Simple queue access methods.
|
||||
*/
|
||||
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
|
||||
(unsigned long)(ptr)))
|
||||
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
|
||||
#define XSIMPLEQ_END(head) NULL
|
||||
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
|
||||
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
|
||||
|
||||
|
||||
#define XSIMPLEQ_FOREACH(var, head, field) \
|
||||
for ((var) = XSIMPLEQ_FIRST(head); \
|
||||
(var) != XSIMPLEQ_END(head); \
|
||||
(var) = XSIMPLEQ_NEXT(head, var, field))
|
||||
|
||||
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = XSIMPLEQ_FIRST(head); \
|
||||
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* XOR Simple queue functions.
|
||||
*/
|
||||
#define XSIMPLEQ_INIT(head) do { \
|
||||
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
|
||||
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = (head)->sqx_first) == \
|
||||
XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
|
||||
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
|
||||
XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
|
||||
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
|
||||
(elm)->field.sqx_next)->field.sqx_next) \
|
||||
== XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = \
|
||||
XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* tail queue access methods
|
||||
*/
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_END(head) NULL
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
/* XXX */
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
#define TAILQ_EMPTY(head) \
|
||||
(TAILQ_FIRST(head) == TAILQ_END(head))
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_NEXT(var, field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head) && \
|
||||
((tvar) = TAILQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_PREV(var, headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head) && \
|
||||
((tvar) = TAILQ_PREV(var, headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm2)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
*(elm2)->field.tqe_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Circular queue definitions.
|
||||
*/
|
||||
#define CIRCLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *cqh_first; /* first element */ \
|
||||
struct type *cqh_last; /* last element */ \
|
||||
}
|
||||
|
||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
||||
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
||||
|
||||
#define CIRCLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *cqe_next; /* next element */ \
|
||||
struct type *cqe_prev; /* previous element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Circular queue access methods
|
||||
*/
|
||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
||||
#define CIRCLEQ_END(head) ((void *)(head))
|
||||
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
||||
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
||||
#define CIRCLEQ_EMPTY(head) \
|
||||
(CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
|
||||
|
||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
||||
for((var) = CIRCLEQ_FIRST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_NEXT(var, field))
|
||||
|
||||
#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = CIRCLEQ_FIRST(head); \
|
||||
(var) != CIRCLEQ_END(head) && \
|
||||
((tvar) = CIRCLEQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
||||
for((var) = CIRCLEQ_LAST(head); \
|
||||
(var) != CIRCLEQ_END(head); \
|
||||
(var) = CIRCLEQ_PREV(var, field))
|
||||
|
||||
#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = CIRCLEQ_LAST(head, headname); \
|
||||
(var) != CIRCLEQ_END(head) && \
|
||||
((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Circular queue functions.
|
||||
*/
|
||||
#define CIRCLEQ_INIT(head) do { \
|
||||
(head)->cqh_first = CIRCLEQ_END(head); \
|
||||
(head)->cqh_last = CIRCLEQ_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
||||
(elm)->field.cqe_prev = (listelm); \
|
||||
if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
||||
(listelm)->field.cqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
||||
(elm)->field.cqe_next = (listelm); \
|
||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
||||
if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
||||
(listelm)->field.cqe_prev = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
||||
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
||||
if ((head)->cqh_last == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm); \
|
||||
else \
|
||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
||||
(head)->cqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.cqe_next = CIRCLEQ_END(head); \
|
||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
||||
if ((head)->cqh_first == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm); \
|
||||
else \
|
||||
(head)->cqh_last->field.cqe_next = (elm); \
|
||||
(head)->cqh_last = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
||||
if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
||||
else \
|
||||
(elm)->field.cqe_next->field.cqe_prev = \
|
||||
(elm)->field.cqe_prev; \
|
||||
if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
||||
else \
|
||||
(elm)->field.cqe_prev->field.cqe_next = \
|
||||
(elm)->field.cqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head)->cqh_last = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_next->field.cqe_prev = (elm2); \
|
||||
if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
|
||||
CIRCLEQ_END(head)) \
|
||||
(head)->cqh_first = (elm2); \
|
||||
else \
|
||||
(elm2)->field.cqe_prev->field.cqe_next = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.cqe_next); \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/* libuEv - Micro event loop library
|
||||
*
|
||||
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
|
||||
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/signalfd.h>
|
||||
#include <unistd.h> /* close(), read() */
|
||||
|
||||
#include "uev.h"
|
||||
|
||||
|
||||
/**
|
||||
* Create a signal watcher
|
||||
* @param ctx A valid libuEv context
|
||||
* @param w Pointer to an uev_t watcher
|
||||
* @param cb Timer callback
|
||||
* @param arg Optional callback argument
|
||||
* @param signo Signal to watch for
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_signal_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo)
|
||||
{
|
||||
int fd;
|
||||
sigset_t mask;
|
||||
|
||||
if (!w || !ctx) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
w->fd = -1;
|
||||
|
||||
sigemptyset(&mask);
|
||||
fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
if (uev_watcher_init(ctx, w, UEV_SIGNAL_TYPE, cb, arg, fd, UEV_READ))
|
||||
goto exit;
|
||||
|
||||
if (uev_signal_set(w, signo)) {
|
||||
uev_watcher_stop(w);
|
||||
exit:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset a signal watcher
|
||||
* @param w Watcher to reset
|
||||
* @param signo New signal to watch for
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_signal_set(uev_t *w, int signo)
|
||||
{
|
||||
sigset_t mask;
|
||||
|
||||
/* Every watcher must be registered to a context */
|
||||
if (!w || !w->ctx) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Remember for callbacks and start/stop */
|
||||
w->signo = signo;
|
||||
|
||||
/* Handle stopped signal watchers */
|
||||
if (w->fd < 0) {
|
||||
/* Remove from internal list */
|
||||
LIST_REMOVE(w, link);
|
||||
|
||||
if (uev_signal_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, signo))
|
||||
return -1;
|
||||
}
|
||||
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, signo);
|
||||
|
||||
/* Block signals so that they aren't handled
|
||||
according to their default dispositions */
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
return -1;
|
||||
|
||||
if (signalfd(w->fd, &mask, SFD_NONBLOCK) < 0)
|
||||
return -1;
|
||||
|
||||
return uev_watcher_start(w);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start a stopped signal watcher
|
||||
* @param w Watcher to start (again)
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_signal_start(uev_t *w)
|
||||
{
|
||||
if (!w) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (-1 != w->fd)
|
||||
uev_signal_stop(w);
|
||||
|
||||
return uev_signal_set(w, w->signo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a signal watcher
|
||||
* @param w Watcher to stop
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_signal_stop(uev_t *w)
|
||||
{
|
||||
if (uev_watcher_stop(w))
|
||||
return -1;
|
||||
|
||||
close(w->fd);
|
||||
w->fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
/* libuEv - Micro event loop library
|
||||
*
|
||||
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
|
||||
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <unistd.h> /* close(), read() */
|
||||
|
||||
#include "uev.h"
|
||||
|
||||
|
||||
static void msec2tspec(int msec, struct timespec *ts)
|
||||
{
|
||||
if (msec) {
|
||||
ts->tv_sec = msec / 1000;
|
||||
ts->tv_nsec = (msec % 1000) * 1000000;
|
||||
} else {
|
||||
ts->tv_sec = 0;
|
||||
ts->tv_nsec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and start a timer watcher
|
||||
* @param ctx A valid libuEv context
|
||||
* @param w Pointer to an uev_t watcher
|
||||
* @param cb Callback function
|
||||
* @param arg Optional callback argument
|
||||
* @param timeout Timeout in milliseconds before @param cb is called
|
||||
* @param period For periodic timers this is the period time that @param timeout is reset to
|
||||
*
|
||||
* For one-shot timers you set @param period to zero and only use @param
|
||||
* timeout. For periodic timers you likely set @param timeout to either
|
||||
* zero, to call it as soon as the event loop starts, or to the same
|
||||
* value as @param period. When the timer expires, the @param cb
|
||||
* is called, with the optional @param arg argument. A non-periodic
|
||||
* timer ends its life there, while a periodic task's @param timeout is
|
||||
* reset to the @param period and restarted.
|
||||
*
|
||||
* A timer is automatically started if the event loop is already
|
||||
* running, otherwise it is kept on hold until triggered by calling
|
||||
* uev_run().
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_timer_init(uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if (timeout < 0 || period < 0) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
if (uev_watcher_init(ctx, w, UEV_TIMER_TYPE, cb, arg, fd, UEV_READ))
|
||||
goto exit;
|
||||
|
||||
if (uev_timer_set(w, timeout, period)) {
|
||||
uev_watcher_stop(w);
|
||||
exit:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset a timer
|
||||
* @param w Watcher to reset
|
||||
* @param timeout Timeout in milliseconds before @param cb is called
|
||||
* @param period For periodic timers this is the period time that @param timeout is reset to
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_timer_set(uev_t *w, int timeout, int period)
|
||||
{
|
||||
/* Every watcher must be registered to a context */
|
||||
if (!w || !w->ctx) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (timeout < 0 || period < 0) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Handle stopped timers */
|
||||
if (w->fd < 0) {
|
||||
/* Timer already stopped */
|
||||
if (!timeout && !period)
|
||||
return 0;
|
||||
|
||||
/* Remove from internal list */
|
||||
LIST_REMOVE(w, link);
|
||||
|
||||
if (uev_timer_init(w->ctx, w, (uev_cb_t *)w->cb, w->arg, timeout, period))
|
||||
return -1;
|
||||
}
|
||||
|
||||
w->u.t.timeout = timeout;
|
||||
w->u.t.period = period;
|
||||
|
||||
if (w->ctx->running) {
|
||||
struct itimerspec time;
|
||||
|
||||
msec2tspec(timeout, &time.it_value);
|
||||
msec2tspec(period, &time.it_interval);
|
||||
if (timerfd_settime(w->fd, 0, &time, NULL) < 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return uev_watcher_start(w);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a stopped timer watcher
|
||||
* @param w Watcher to start (again)
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_timer_start(uev_t *w)
|
||||
{
|
||||
if (!w) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (-1 != w->fd)
|
||||
uev_watcher_stop(w);
|
||||
|
||||
return uev_timer_set(w, w->u.t.timeout, w->u.t.period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop and unregister a timer watcher
|
||||
* @param w Watcher to stop
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_timer_stop(uev_t *w)
|
||||
{
|
||||
/* Stop kernel timer */
|
||||
uev_timer_set(w, 0, 0);
|
||||
|
||||
if (uev_watcher_stop(w))
|
||||
return -1;
|
||||
|
||||
/* Close timerfd, will have to be reopened again on reset */
|
||||
close(w->fd);
|
||||
w->fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,372 @@
|
||||
/* libuEv - Micro event loop library
|
||||
*
|
||||
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
|
||||
* Copyright (c) 2013-2016 Joachim Nilsson <troglobit()gmail!com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> /* O_CLOEXEC */
|
||||
#include <string.h> /* memset() */
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/signalfd.h> /* struct signalfd_siginfo */
|
||||
#include <unistd.h> /* close(), read() */
|
||||
|
||||
#include "uev.h"
|
||||
|
||||
#define UNUSED(arg) arg __attribute__ ((unused))
|
||||
|
||||
|
||||
static int _init(uev_ctx_t *ctx, int close_old)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = epoll_create1(EPOLL_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
if (close_old)
|
||||
close(ctx->fd);
|
||||
|
||||
ctx->fd = fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Simple check if a descriptor is still valid in the kernel */
|
||||
static int is_valid_fd(int fd)
|
||||
{
|
||||
return fcntl(fd, F_GETFL) != -1 || errno != EBADF;
|
||||
}
|
||||
|
||||
/* Private to libuEv, do not use directly! */
|
||||
int uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events)
|
||||
{
|
||||
if (!ctx || !w) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
w->ctx = ctx;
|
||||
w->type = type;
|
||||
w->active = 0;
|
||||
w->fd = fd;
|
||||
w->cb = cb;
|
||||
w->arg = arg;
|
||||
w->events = events;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Private to libuEv, do not use directly! */
|
||||
int uev_watcher_start(uev_t *w)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
|
||||
if (!w || w->fd < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (w->active)
|
||||
return 0;
|
||||
|
||||
ev.events = w->events | EPOLLRDHUP;
|
||||
ev.data.ptr = w;
|
||||
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0)
|
||||
return -1;
|
||||
|
||||
w->active = 1;
|
||||
|
||||
/* Add to internal list for bookkeeping */
|
||||
LIST_INSERT_HEAD(&w->ctx->watchers, w, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Private to libuEv, do not use directly! */
|
||||
int uev_watcher_stop(uev_t *w)
|
||||
{
|
||||
if (!w) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!w->active)
|
||||
return 0;
|
||||
|
||||
w->active = 0;
|
||||
|
||||
/* Remove from internal list */
|
||||
LIST_REMOVE(w, link);
|
||||
|
||||
/* Remove from kernel */
|
||||
if (epoll_ctl(w->ctx->fd, EPOLL_CTL_DEL, w->fd, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Private to libuEv, do not use directly! */
|
||||
int uev_watcher_active(uev_t *w)
|
||||
{
|
||||
if (!w)
|
||||
return 0;
|
||||
|
||||
return w->active;
|
||||
}
|
||||
|
||||
/* 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
|
||||
* @param ctx Pointer to an uev_ctx_t context to be initialized
|
||||
*
|
||||
* @return POSIX OK(0) on success, or non-zero on error.
|
||||
*/
|
||||
int uev_init(uev_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
LIST_INIT(&ctx->watchers);
|
||||
|
||||
return _init(ctx, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the event loop
|
||||
* @param ctx A valid libuEv context
|
||||
*
|
||||
* @return POSIX OK(0) or non-zero with @param errno set on error.
|
||||
*/
|
||||
int uev_exit(uev_ctx_t *ctx)
|
||||
{
|
||||
if (!ctx) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!LIST_EMPTY(&ctx->watchers)) {
|
||||
uev_t *w = LIST_FIRST(&ctx->watchers);
|
||||
|
||||
/* Remove from internal list */
|
||||
LIST_REMOVE(w, link);
|
||||
|
||||
if (!w->active)
|
||||
continue;
|
||||
|
||||
switch (w->type) {
|
||||
case UEV_CRON_TYPE:
|
||||
case UEV_TIMER_TYPE:
|
||||
uev_timer_stop(w);
|
||||
break;
|
||||
|
||||
case UEV_SIGNAL_TYPE:
|
||||
uev_signal_stop(w);
|
||||
break;
|
||||
|
||||
case UEV_IO_TYPE:
|
||||
uev_io_stop(w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->running = 0;
|
||||
close(ctx->fd);
|
||||
ctx->fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the event loop
|
||||
* @param ctx A valid libuEv context
|
||||
* @param flags A mask of %UEV_ONCE and %UEV_NONBLOCK, or zero
|
||||
*
|
||||
* With @flags set to %UEV_ONCE the event loop returns after the first
|
||||
* event has been served, useful for instance to set a timeout on a file
|
||||
* descriptor. If @flags also has the %UEV_NONBLOCK flag set the event
|
||||
* loop will return immediately if no event is pending, useful when run
|
||||
* inside another event loop.
|
||||
*
|
||||
* @return POSIX OK(0) upon successful termination of the event loop, or
|
||||
* non-zero on error.
|
||||
*/
|
||||
int uev_run(uev_ctx_t *ctx, int flags)
|
||||
{
|
||||
int result = 0, timeout = -1;
|
||||
uev_t *w;
|
||||
|
||||
if (!ctx || ctx->fd < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (flags & UEV_NONBLOCK)
|
||||
timeout = 0;
|
||||
|
||||
/* Start the event loop */
|
||||
ctx->running = 1;
|
||||
|
||||
/* Start all dormant timers */
|
||||
LIST_FOREACH(w, &ctx->watchers, link) {
|
||||
if (UEV_CRON_TYPE == w->type)
|
||||
uev_cron_set(w, w->u.c.when, w->u.c.interval);
|
||||
if (UEV_TIMER_TYPE == w->type)
|
||||
uev_timer_set(w, w->u.t.timeout, w->u.t.period);
|
||||
}
|
||||
|
||||
while (ctx->running) {
|
||||
int i, nfds;
|
||||
struct epoll_event events[UEV_MAX_EVENTS];
|
||||
|
||||
while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) {
|
||||
if (!ctx->running)
|
||||
break;
|
||||
|
||||
if (EINTR == errno)
|
||||
continue; /* Signalled, try again */
|
||||
|
||||
/* Unrecoverable error, cleanup and exit with error. */
|
||||
uev_exit(ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; ctx->running && i < nfds; i++) {
|
||||
w = (uev_t *)events[i].data.ptr;
|
||||
|
||||
if (events[i].events & (EPOLLHUP | EPOLLERR)) {
|
||||
ctx->errors++;
|
||||
|
||||
if (ctx->errors >= 42) {
|
||||
uev_t *tmp, *retry = w;;
|
||||
|
||||
/* If not valid anymore, try to remove, ignore any errors. */
|
||||
if (!is_valid_fd(w->fd))
|
||||
uev_watcher_stop(w);
|
||||
|
||||
/* Must recreate epoll fd now ... */
|
||||
if (_init(ctx, 1)) {
|
||||
uev_exit(ctx);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Restart watchers in new efd */
|
||||
LIST_FOREACH_SAFE(w, &ctx->watchers, link, tmp) {
|
||||
if (w->active) {
|
||||
w->active = 0;
|
||||
LIST_REMOVE(w, link);
|
||||
uev_watcher_start(w);
|
||||
}
|
||||
}
|
||||
uev_watcher_start(retry);
|
||||
|
||||
/* New efd, restart everything! */
|
||||
ctx->errors = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (UEV_CRON_TYPE == w->type) {
|
||||
uint64_t exp;
|
||||
|
||||
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
|
||||
if (errno != ECANCELED) {
|
||||
uev_exit(ctx);
|
||||
return -3;
|
||||
}
|
||||
|
||||
events[i].events = UEV_HUP;
|
||||
}
|
||||
|
||||
if (!w->u.c.interval)
|
||||
w->u.c.when = 0;
|
||||
else
|
||||
w->u.c.when += w->u.c.interval;
|
||||
}
|
||||
|
||||
if (UEV_TIMER_TYPE == w->type) {
|
||||
uint64_t exp;
|
||||
|
||||
if (read(w->fd, &exp, sizeof(exp)) != sizeof(exp)) {
|
||||
uev_exit(ctx);
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (!w->u.t.period)
|
||||
w->u.t.timeout = 0;
|
||||
}
|
||||
|
||||
if (UEV_SIGNAL_TYPE == w->type) {
|
||||
struct signalfd_siginfo fdsi;
|
||||
ssize_t sz = sizeof(fdsi);
|
||||
|
||||
if (read(w->fd, &fdsi, sz) != sz) {
|
||||
uev_exit(ctx);
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
|
||||
if (w->cb)
|
||||
w->cb(w, w->arg, events[i].events & UEV_EVENT_MASK);
|
||||
|
||||
if (UEV_CRON_TYPE == w->type) {
|
||||
if (!w->u.c.when)
|
||||
uev_timer_stop(w);
|
||||
}
|
||||
if (UEV_TIMER_TYPE == w->type) {
|
||||
if (!w->u.t.timeout)
|
||||
uev_timer_stop(w);
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & UEV_ONCE) || LIST_EMPTY(&ctx->watchers))
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,99 @@
|
||||
/* libuEv - Micro event loop library
|
||||
*
|
||||
* Copyright (c) 2012 Flemming Madsen <flemming!madsen()madsensoft!dk>
|
||||
* Copyright (c) 2013-2015 Joachim Nilsson <troglobit()gmail!com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef LIBUEV_UEV_H_
|
||||
#define LIBUEV_UEV_H_
|
||||
|
||||
#include "private.h"
|
||||
|
||||
/* Max. number of simulateneous events */
|
||||
#define UEV_MAX_EVENTS 10
|
||||
|
||||
/* I/O events, for signals and timers revents will always be UEV_READ */
|
||||
#define UEV_NONE 0
|
||||
#define UEV_READ EPOLLIN
|
||||
#define UEV_WRITE EPOLLOUT
|
||||
#define UEV_PRI EPOLLPRI
|
||||
#define UEV_HUP EPOLLHUP
|
||||
#define UEV_RDHUP EPOLLRDHUP
|
||||
#define UEV_EDGE EPOLLET
|
||||
#define UEV_ONESHOT EPOLLONESHOT
|
||||
|
||||
/* Run flags */
|
||||
#define UEV_ONCE 1
|
||||
#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 */
|
||||
typedef struct uev {
|
||||
/* Private data for libuEv internal engine */
|
||||
uev_private_t type;
|
||||
|
||||
/* Public data for users to reference */
|
||||
int signo;
|
||||
int fd;
|
||||
uev_ctx_t *ctx;
|
||||
} uev_t;
|
||||
|
||||
/* Generic callback for watchers */
|
||||
typedef void (uev_cb_t)(uev_t *w, void *arg, int events);
|
||||
|
||||
/* Public interface */
|
||||
int uev_init (uev_ctx_t *ctx);
|
||||
int uev_exit (uev_ctx_t *ctx);
|
||||
int uev_run (uev_ctx_t *ctx, int flags);
|
||||
|
||||
int uev_io_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int fd, int events);
|
||||
int uev_io_set (uev_t *w, int fd, int events);
|
||||
int uev_io_start (uev_t *w);
|
||||
int uev_io_stop (uev_t *w);
|
||||
|
||||
int uev_timer_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int timeout, int period);
|
||||
int uev_timer_set (uev_t *w, int timeout, int period);
|
||||
int uev_timer_start (uev_t *w);
|
||||
int uev_timer_stop (uev_t *w);
|
||||
|
||||
int uev_cron_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, time_t when, time_t interval);
|
||||
int uev_cron_set (uev_t *w, time_t when, time_t interval);
|
||||
int uev_cron_start (uev_t *w);
|
||||
int uev_cron_stop (uev_t *w);
|
||||
|
||||
int uev_signal_init (uev_ctx_t *ctx, uev_t *w, uev_cb_t *cb, void *arg, int signo);
|
||||
int uev_signal_set (uev_t *w, int signo);
|
||||
int uev_signal_start (uev_t *w);
|
||||
int uev_signal_stop (uev_t *w);
|
||||
|
||||
#endif /* LIBUEV_UEV_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
Reference in new issue
Block a user