More work on kqlite

git-svn-id: svn://svn.code.sf.net/p/libkqueue/code/trunk@658 fb4e3144-bc1c-4b72-a658-5bcd248dd7f7
This commit is contained in:
mheily 2013-10-18 00:03:02 +00:00
parent f09868e45b
commit 74c794d2b9
4 changed files with 276 additions and 91 deletions

26
kqlite/README Normal file
View File

@ -0,0 +1,26 @@
kqlite has the following goals:
* be lightweight and efficient
* provide a strict subset of the functionality of kqueue(2) and kevent(2)
* closely resemble the kqueue API, but not guarantee 100% compatibility
* support modern POSIX operating systems
It should be possible to switch between kqlite and the full libkqueue
using a few preprocessor macros:
#if LIBKQUEUE
#define kqueue_t int
#define kq_init kqueue
#define kq_event kevent
#define kq_free close
#endif
Here are the differences between kqlite and kqueue:
* Function names are different:
kqueue() == kq_init()
kevent() == kq_event()
close() == kq_free()
* kqueue() returns an int, while kq_init returns an opaque kqueue_t type.

View File

@ -16,9 +16,12 @@
#include "./lite.h"
#include <unistd.h>
/* Determine what type of kernel event system to use. */
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
#define USE_KQUEUE
#include <sys/event.h>
#elif defined(__linux__)
#define USE_EPOLL
#include <pthread.h>
@ -31,47 +34,88 @@
#error Unsupported operating system type
#endif
#if defined(USE_KQUEUE)
typedef int kqueue_t;
#elif defined(USE_EPOLL)
struct kqueue {
int epfd; /* epoll */
int inofd; /* inotify */
int timefd; /* timerfd */
int sigfd; /* signalfd */
sigset_t sigmask;
pthread_mutex_t kq_mtx;
};
/* Linux supports a subset of these filters. */
#if defined(USE_EPOLL)
#define EVFILT_READ (0)
#define EVFILT_WRITE (1)
#define EVFILT_VNODE (2)
#define EVFILT_SIGNAL (3)
#define EVFILT_TIMER (4)
#define EVFILT_SYSCOUNT (5)
#endif
/* Initialize the event descriptor */
int
kq_init(kqueue_t kq)
{
struct kqueue {
#if defined(USE_KQUEUE)
kq = kqueue();
int kqfd; /* kqueue(2) descriptor */
#elif defined(USE_EPOLL)
int epfd; /* epoll */
int wfd[EVFILT_SYSCOUNT]; /* an event wait descriptor for each EVFILT_* */
sigset_t sigmask;
pthread_mutex_t kq_mtx;
#else
#error Undefined event system
#endif
};
return (kq);
/* Initialize the event descriptor */
kqueue_t
kq_init(void)
{
struct kqueue *kq;
if ((kq = malloc(sizeof(*kq))) == NULL)
return (NULL);
#if defined(USE_KQUEUE)
kq->kqfd = kqueue();
if (kq->kqfd < 0) {
free(kq);
return (NULL);
}
#elif defined(USE_EPOLL)
if (pthread_mutex_init(&kq->kq_mtx, NULL) != 0)
return (-1);
goto errout;
kq->epfd = epoll_create(10);
if (kq->epfd < 0)
goto errout;
sigemptyset(&kq->sigmask);
kq->wfd[EVFILT_SIGNAL] = signalfd(-1, &kq->sigmask, 0);
return (kq->epfd);
// FIXME: check that all members of kq->wfd are valid
return (kq);
errout:
free(kq);
if (kq->epfd >= 0) close(kq->epfd);
//FIXME: something like: if (kq->wfd[EVFILT_SIGNAL] >= 0) free(kq->epfd);
return (NULL);
#endif
}
void
kq_free(kqueue_t kq)
{
#if defined(USE_KQUEUE)
close(kq.kqfd);
#elif defined(USE_EPOLL)
close(kq->epfd);
#endif
free(kq);
}
#if defined(USE_EPOLL)
/* Add a new item to the list of events to be monitored */
int
kq_add(kqueue_t kq, const kevent_t *ev)
static inline int
kq_add(kqueue_t kq, const struct kevent *ev)
{
int rv = 0;
#if defined(USE_KQUEUE)
//TODO
#elif defined(USE_EPOLL)
struct epoll_event epev;
kevent_t *evcopy;
struct kevent *evcopy;
int sigfd;
/* Save a copy of the kevent so kq_wait() can use it later */
@ -81,25 +125,25 @@ kq_add(kqueue_t kq, const kevent_t *ev)
memcpy (evcopy, ev, sizeof(*evcopy));
switch (ev->ident) {
case SOURCE_READ:
case EVFILT_READ:
epev.events = EPOLLIN;
epev.data.ptr = evcopy;
rv = epoll_ctl(kq->epfd, EPOLL_CTL_ADD, ev->ident, &epev);
case SOURCE_WRITE:
case EVFILT_WRITE:
epev.events = EPOLLOUT;
epev.data.ptr = evcopy;
rv = epoll_ctl(kq->epfd, EPOLL_CTL_ADD, ev->ident, &epev);
case SOURCE_VNODE:
case EVFILT_VNODE:
//TODO: create an inotifyfd, create an epollfd, add the inotifyfd to the epollfd, set epollfd.data.ptr = evcopy, add epollfd to kq->epfd.
rv = -1;
break;
case SOURCE_SIGNAL:
case EVFILT_SIGNAL:
pthread_mutex_lock(&kq->kq_mtx);
sigaddset(&kq->sigmask, ev->ident);
sigfd = signalfd(kq->sigfd, &kq->sigmask, 0);
sigfd = signalfd(kq->wfd[EVFILT_SIGNAL], &kq->sigmask, 0);
pthread_mutex_unlock(&kq->kq_mtx);
if (sigfd < 0) {
rv = -1;
@ -108,7 +152,7 @@ kq_add(kqueue_t kq, const kevent_t *ev)
}
break;
case SOURCE_TIMER:
case EVFILT_TIMER:
//TODO
rv = -1;
break;
@ -120,35 +164,31 @@ kq_add(kqueue_t kq, const kevent_t *ev)
// if (rv < 0)
// free(evcopy);
#endif
return (rv);
}
/* Delete an item from the list of events to be monitored */
int
kq_remove(kqueue_t kq, const kevent_t *ev)
static int
kq_delete(kqueue_t kq, const struct kevent *ev)
{
int rv = 0;
int sigfd;
#if defined(USE_KQUEUE)
//TODO
#elif defined(USE_EPOLL)
struct epoll_event epev;
switch (ev->ident) {
case SOURCE_READ:
case SOURCE_WRITE:
case EVFILT_READ:
case EVFILT_WRITE:
rv = epoll_ctl(kq->epfd, EPOLL_CTL_DEL, ev->ident, &epev);
break;
case SOURCE_VNODE:
case EVFILT_VNODE:
//TODO
break;
case SOURCE_SIGNAL:
case EVFILT_SIGNAL:
pthread_mutex_lock(&kq->kq_mtx);
sigdelset(&kq->sigmask, ev->ident);
sigfd = signalfd(kq->sigfd, &kq->sigmask, 0);
sigfd = signalfd(kq->wfd[EVFILT_SIGNAL], &kq->sigmask, 0);
pthread_mutex_unlock(&kq->kq_mtx);
if (sigfd < 0) {
rv = -1;
@ -157,7 +197,7 @@ kq_remove(kqueue_t kq, const kevent_t *ev)
}
break;
case SOURCE_TIMER:
case EVFILT_TIMER:
//TODO
break;
@ -165,20 +205,37 @@ kq_remove(kqueue_t kq, const kevent_t *ev)
rv = 0;
break;
}
#endif
return (rv);
}
/* Wait for an event */
int
kq_wait(kqueue_t kq, kevent_t *ev, const struct timespec *timeout)
#endif /* defined(USE_EPOLL) */
/* Equivalent to kevent() */
int kq_event(kqueue_t kq, const struct kevent *changelist, int nchanges,
struct kevent *eventlist, int nevents,
const struct timespec *timeout)
{
int rv = 0;
#if defined(USE_KQUEUE)
//TODO
return kevent(kq->kqfd, changelist, nchanges, eventlist, nevents, timeout);
#elif defined(USE_EPOLL)
struct epoll_event epev;
int eptimeout;
int i, eptimeout;
/* Process each item on the changelist */
for (i = 0; i < nchanges; i++) {
if (changelist[i].flags & EV_ADD) {
rv = kq_add(kq, &changelist[i]);
} else if (changelist[i].flags & EV_DELETE) {
rv = kq_delete(kq, &changelist[i]);
} else {
rv = -1;
}
if (rv < 0)
return (-1);
}
/* Convert timeout to the format used by epoll_wait() */
if (timeout == NULL)
@ -189,15 +246,16 @@ kq_wait(kqueue_t kq, kevent_t *ev, const struct timespec *timeout)
rv = epoll_wait(kq->epfd, &epev, 1, eptimeout);
//FIXME: handle timeout
if (rv > 0) {
if (epev.data.fd == kq->sigfd) {
//if (epev.data.fd == kq->sigfd) {
// FIXME: data.fd isn't actually set :(
// Special case: a signal was received
// ...
} else {
// Normal case: data.ptr is a kevent_t (see evcopy from above)
//} else {
// Normal case: data.ptr is a struct kevent (see evcopy from above)
// ...
}
//}
}
#endif
return (rv == 1 ? 0 : -1);
#endif
}

View File

@ -1,57 +1,153 @@
/*
/*-
* Copyright (c) 2013 Mark Heily <mark@heily.com>
* Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* 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.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $FreeBSD SVN Revision 197533$
*/
#ifndef _KQUEUE_LITE_H
#define _KQUEUE_LITE_H
#include <stdint.h>
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__NetBSD__)
#include <sys/event.h>
#else
#include <sys/time.h>
#include <sys/types.h>
#include <stdint.h>
/* Equilavent to the list of kevent filters */
typedef enum {
SOURCE_READ,
SOURCE_WRITE,
SOURCE_VNODE,
SOURCE_SIGNAL,
SOURCE_TIMER
} kevent_source_t;
#define EV_SET(kevp_, a, b, c, d, e, f) do { \
struct kevent *kevp = (kevp_); \
(kevp)->ident = (a); \
(kevp)->filter = (b); \
(kevp)->flags = (c); \
(kevp)->fflags = (d); \
(kevp)->data = (e); \
(kevp)->udata = (f); \
} while(0)
/* Equivalent to 'struct kevent' */
typedef struct {
uintptr_t ident; /* identifier for this event */
short filter; /* filter for event */
unsigned short flags;
unsigned int fflags;
intptr_t data;
void *udata; /* opaque user data identifier */
} kevent_t;
struct kevent {
uintptr_t ident; /* identifier for this event */
short filter; /* filter for event */
unsigned short flags;
unsigned int fflags;
intptr_t data;
void *udata; /* opaque user data identifier */
};
/* actions */
#define EV_ADD 0x0001 /* add event to kq (implies enable) */
#define EV_DELETE 0x0002 /* delete event from kq */
#define EV_ENABLE 0x0004 /* enable event */
#define EV_DISABLE 0x0008 /* disable event (not reported) */
/* flags */
#define EV_ONESHOT 0x0010 /* only report one occurrence */
#define EV_CLEAR 0x0020 /* clear event state after reporting */
#define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */
#define EV_DISPATCH 0x0080 /* disable event after reporting */
#define EV_SYSFLAGS 0xF000 /* reserved by system */
#define EV_FLAG1 0x2000 /* filter-specific flag */
/* returned values */
#define EV_EOF 0x8000 /* EOF detected */
#define EV_ERROR 0x4000 /* error, data contains errno */
/*
* data/hint flags/masks for EVFILT_USER
*
* On input, the top two bits of fflags specifies how the lower twenty four
* bits should be applied to the stored value of fflags.
*
* On output, the top two bits will always be set to NOTE_FFNOP and the
* remaining twenty four bits will contain the stored fflags value.
*/
#define NOTE_FFNOP 0x00000000 /* ignore input fflags */
#define NOTE_FFAND 0x40000000 /* AND fflags */
#define NOTE_FFOR 0x80000000 /* OR fflags */
#define NOTE_FFCOPY 0xc0000000 /* copy fflags */
#define NOTE_FFCTRLMASK 0xc0000000 /* masks for operations */
#define NOTE_FFLAGSMASK 0x00ffffff
#define NOTE_TRIGGER 0x01000000 /* Cause the event to be
triggered for output. */
/*
* data/hint flags for EVFILT_{READ|WRITE}
*/
#define NOTE_LOWAT 0x0001 /* low water mark */
#undef NOTE_LOWAT /* Not supported on Linux */
/*
* data/hint flags for EVFILT_VNODE
*/
#define NOTE_DELETE 0x0001 /* vnode was removed */
#define NOTE_WRITE 0x0002 /* data contents changed */
#define NOTE_EXTEND 0x0004 /* size increased */
#define NOTE_ATTRIB 0x0008 /* attributes changed */
#define NOTE_LINK 0x0010 /* link count changed */
#define NOTE_RENAME 0x0020 /* vnode was renamed */
#define NOTE_REVOKE 0x0040 /* vnode access was revoked */
#undef NOTE_REVOKE /* Not supported on Linux */
/*
* data/hint flags for EVFILT_PROC
*/
#define NOTE_EXIT 0x80000000 /* process exited */
#define NOTE_FORK 0x40000000 /* process forked */
#define NOTE_EXEC 0x20000000 /* process exec'd */
#define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */
#define NOTE_PDATAMASK 0x000fffff /* mask for pid */
/* additional flags for EVFILT_PROC */
#define NOTE_TRACK 0x00000001 /* follow across forks */
#define NOTE_TRACKERR 0x00000002 /* could not track child */
#define NOTE_CHILD 0x00000004 /* am a child process */
/*
* data/hint flags for EVFILT_NETDEV
*/
#define NOTE_LINKUP 0x0001 /* link is up */
#define NOTE_LINKDOWN 0x0002 /* link is down */
#define NOTE_LINKINV 0x0004 /* link state is invalid */
#endif /* defined(__FreeBSD__) etc.. */
/* kqueue_t - the event descriptor */
typedef struct kqueue *kqueue_t;
/* Initialize the event descriptor */
int kq_init(kqueue_t kq);
kqueue_t kq_init();
/* Add a new item to the list of events to be monitored */
int kq_add(kqueue_t kq, const kevent_t *ev);
/* Free the event descriptor */
void kq_free(kqueue_t kq);
/* Delete an item from the list of events to be monitored */
int kq_remove(kqueue_t kq, const kevent_t *ev);
/* Wait for an event */
int kq_wait(kqueue_t kq, kevent_t *ev, const struct timespec *timeout);
/* Equivalent to kevent() */
int kq_event(kqueue_t kq, const struct kevent *changelist, int nchanges,
struct kevent *eventlist, int nevents,
const struct timespec *timeout);
#endif /* ! _KQUEUE_LITE_H */

View File

@ -5,6 +5,11 @@
#include <unistd.h>
int main() {
kqueue_t kq;
kq = kq_init();
kq_free(kq);
puts("ok");
exit(0);
}