936 lines
21 KiB
C
936 lines
21 KiB
C
/* $NetBSD: eventlib.c,v 1.1.1.2 2012/09/09 16:08:00 christos Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
|
|
* Copyright (c) 1995-1999 by Internet Software Consortium
|
|
*
|
|
* 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.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC 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.
|
|
*/
|
|
|
|
/* eventlib.c - implement glue for the eventlib
|
|
* vix 09sep95 [initial]
|
|
*/
|
|
|
|
#if !defined(LINT) && !defined(CODECENTER)
|
|
static const char rcsid[] = "Id: eventlib.c,v 1.10 2006/03/09 23:57:56 marka Exp ";
|
|
#endif
|
|
|
|
#include "port_before.h"
|
|
#include "fd_setsize.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/stat.h>
|
|
#ifdef SOLARIS2
|
|
#include <limits.h>
|
|
#endif /* SOLARIS2 */
|
|
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <isc/eventlib.h>
|
|
#include <isc/assertions.h>
|
|
#include "eventlib_p.h"
|
|
|
|
#include "port_after.h"
|
|
|
|
int __evOptMonoTime;
|
|
|
|
#ifdef USE_POLL
|
|
#define pselect Pselect
|
|
#endif /* USE_POLL */
|
|
|
|
/* Forward. */
|
|
|
|
#if defined(NEED_PSELECT) || defined(USE_POLL)
|
|
static int pselect(int, void *, void *, void *,
|
|
struct timespec *,
|
|
const sigset_t *);
|
|
#endif
|
|
|
|
int __evOptMonoTime;
|
|
|
|
/* Public. */
|
|
|
|
int
|
|
evCreate(evContext *opaqueCtx) {
|
|
evContext_p *ctx;
|
|
|
|
/* Make sure the memory heap is initialized. */
|
|
if (meminit(0, 0) < 0 && errno != EEXIST)
|
|
return (-1);
|
|
|
|
OKNEW(ctx);
|
|
|
|
/* Global. */
|
|
ctx->cur = NULL;
|
|
|
|
/* Debugging. */
|
|
ctx->debug = 0;
|
|
ctx->output = NULL;
|
|
|
|
/* Connections. */
|
|
ctx->conns = NULL;
|
|
INIT_LIST(ctx->accepts);
|
|
|
|
/* Files. */
|
|
ctx->files = NULL;
|
|
#ifdef USE_POLL
|
|
ctx->pollfds = NULL;
|
|
ctx->maxnfds = 0;
|
|
ctx->firstfd = 0;
|
|
emulMaskInit(ctx, rdLast, EV_READ, 1);
|
|
emulMaskInit(ctx, rdNext, EV_READ, 0);
|
|
emulMaskInit(ctx, wrLast, EV_WRITE, 1);
|
|
emulMaskInit(ctx, wrNext, EV_WRITE, 0);
|
|
emulMaskInit(ctx, exLast, EV_EXCEPT, 1);
|
|
emulMaskInit(ctx, exNext, EV_EXCEPT, 0);
|
|
emulMaskInit(ctx, nonblockBefore, EV_WASNONBLOCKING, 0);
|
|
#endif /* USE_POLL */
|
|
FD_ZERO(&ctx->rdNext);
|
|
FD_ZERO(&ctx->wrNext);
|
|
FD_ZERO(&ctx->exNext);
|
|
FD_ZERO(&ctx->nonblockBefore);
|
|
ctx->fdMax = -1;
|
|
ctx->fdNext = NULL;
|
|
ctx->fdCount = 0; /*%< Invalidate {rd,wr,ex}Last. */
|
|
#ifndef USE_POLL
|
|
ctx->highestFD = FD_SETSIZE - 1;
|
|
memset(ctx->fdTable, 0, sizeof ctx->fdTable);
|
|
#else
|
|
ctx->highestFD = INT_MAX / sizeof(struct pollfd);
|
|
ctx->fdTable = NULL;
|
|
#endif /* USE_POLL */
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
ctx->lastFdCount = 0;
|
|
#endif
|
|
|
|
/* Streams. */
|
|
ctx->streams = NULL;
|
|
ctx->strDone = NULL;
|
|
ctx->strLast = NULL;
|
|
|
|
/* Timers. */
|
|
ctx->lastEventTime = evNowTime();
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
ctx->lastSelectTime = ctx->lastEventTime;
|
|
#endif
|
|
ctx->timers = evCreateTimers(ctx);
|
|
if (ctx->timers == NULL)
|
|
return (-1);
|
|
|
|
/* Waits. */
|
|
ctx->waitLists = NULL;
|
|
ctx->waitDone.first = ctx->waitDone.last = NULL;
|
|
ctx->waitDone.prev = ctx->waitDone.next = NULL;
|
|
|
|
opaqueCtx->opaque = ctx;
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
evSetDebug(evContext opaqueCtx, int level, FILE *output) {
|
|
evContext_p *ctx = opaqueCtx.opaque;
|
|
|
|
ctx->debug = level;
|
|
ctx->output = output;
|
|
}
|
|
|
|
int
|
|
evDestroy(evContext opaqueCtx) {
|
|
evContext_p *ctx = opaqueCtx.opaque;
|
|
int revs = 424242; /*%< Doug Adams. */
|
|
evWaitList *this_wl, *next_wl;
|
|
evWait *this_wait, *next_wait;
|
|
|
|
/* Connections. */
|
|
while (revs-- > 0 && ctx->conns != NULL) {
|
|
evConnID id;
|
|
|
|
id.opaque = ctx->conns;
|
|
(void) evCancelConn(opaqueCtx, id);
|
|
}
|
|
INSIST(revs >= 0);
|
|
|
|
/* Streams. */
|
|
while (revs-- > 0 && ctx->streams != NULL) {
|
|
evStreamID id;
|
|
|
|
id.opaque = ctx->streams;
|
|
(void) evCancelRW(opaqueCtx, id);
|
|
}
|
|
|
|
/* Files. */
|
|
while (revs-- > 0 && ctx->files != NULL) {
|
|
evFileID id;
|
|
|
|
id.opaque = ctx->files;
|
|
(void) evDeselectFD(opaqueCtx, id);
|
|
}
|
|
INSIST(revs >= 0);
|
|
|
|
/* Timers. */
|
|
evDestroyTimers(ctx);
|
|
|
|
/* Waits. */
|
|
for (this_wl = ctx->waitLists;
|
|
revs-- > 0 && this_wl != NULL;
|
|
this_wl = next_wl) {
|
|
next_wl = this_wl->next;
|
|
for (this_wait = this_wl->first;
|
|
revs-- > 0 && this_wait != NULL;
|
|
this_wait = next_wait) {
|
|
next_wait = this_wait->next;
|
|
FREE(this_wait);
|
|
}
|
|
FREE(this_wl);
|
|
}
|
|
for (this_wait = ctx->waitDone.first;
|
|
revs-- > 0 && this_wait != NULL;
|
|
this_wait = next_wait) {
|
|
next_wait = this_wait->next;
|
|
FREE(this_wait);
|
|
}
|
|
|
|
FREE(ctx);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
evGetNext(evContext opaqueCtx, evEvent *opaqueEv, int options) {
|
|
evContext_p *ctx = opaqueCtx.opaque;
|
|
struct timespec nextTime;
|
|
evTimer *nextTimer;
|
|
evEvent_p *new;
|
|
int x, pselect_errno, timerPast;
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
struct timespec interval;
|
|
#endif
|
|
|
|
/* Ensure that exactly one of EV_POLL or EV_WAIT was specified. */
|
|
x = ((options & EV_POLL) != 0) + ((options & EV_WAIT) != 0);
|
|
if (x != 1)
|
|
EV_ERR(EINVAL);
|
|
|
|
/* Get the time of day. We'll do this again after select() blocks. */
|
|
ctx->lastEventTime = evNowTime();
|
|
|
|
again:
|
|
/* Finished accept()'s do not require a select(). */
|
|
if (!EMPTY(ctx->accepts)) {
|
|
OKNEW(new);
|
|
new->type = Accept;
|
|
new->u.accept.this = HEAD(ctx->accepts);
|
|
UNLINK(ctx->accepts, HEAD(ctx->accepts), link);
|
|
opaqueEv->opaque = new;
|
|
return (0);
|
|
}
|
|
|
|
/* Stream IO does not require a select(). */
|
|
if (ctx->strDone != NULL) {
|
|
OKNEW(new);
|
|
new->type = Stream;
|
|
new->u.stream.this = ctx->strDone;
|
|
ctx->strDone = ctx->strDone->nextDone;
|
|
if (ctx->strDone == NULL)
|
|
ctx->strLast = NULL;
|
|
opaqueEv->opaque = new;
|
|
return (0);
|
|
}
|
|
|
|
/* Waits do not require a select(). */
|
|
if (ctx->waitDone.first != NULL) {
|
|
OKNEW(new);
|
|
new->type = Wait;
|
|
new->u.wait.this = ctx->waitDone.first;
|
|
ctx->waitDone.first = ctx->waitDone.first->next;
|
|
if (ctx->waitDone.first == NULL)
|
|
ctx->waitDone.last = NULL;
|
|
opaqueEv->opaque = new;
|
|
return (0);
|
|
}
|
|
|
|
/* Get the status and content of the next timer. */
|
|
if ((nextTimer = heap_element(ctx->timers, 1)) != NULL) {
|
|
nextTime = nextTimer->due;
|
|
timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
|
|
} else
|
|
timerPast = 0; /*%< Make gcc happy. */
|
|
evPrintf(ctx, 9, "evGetNext: fdCount %d\n", ctx->fdCount);
|
|
if (ctx->fdCount == 0) {
|
|
static const struct timespec NoTime = {0, 0L};
|
|
enum { JustPoll, Block, Timer } m;
|
|
struct timespec t, *tp;
|
|
|
|
/* Are there any events at all? */
|
|
if ((options & EV_WAIT) != 0 && !nextTimer && ctx->fdMax == -1)
|
|
EV_ERR(ENOENT);
|
|
|
|
/* Figure out what select()'s timeout parameter should be. */
|
|
if ((options & EV_POLL) != 0) {
|
|
m = JustPoll;
|
|
t = NoTime;
|
|
tp = &t;
|
|
} else if (nextTimer == NULL) {
|
|
m = Block;
|
|
/* ``t'' unused. */
|
|
tp = NULL;
|
|
} else if (timerPast) {
|
|
m = JustPoll;
|
|
t = NoTime;
|
|
tp = &t;
|
|
} else {
|
|
m = Timer;
|
|
/* ``t'' filled in later. */
|
|
tp = &t;
|
|
}
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
if (ctx->debug > 0) {
|
|
interval = evSubTime(ctx->lastEventTime,
|
|
ctx->lastSelectTime);
|
|
if (interval.tv_sec > 0 || interval.tv_nsec > 0)
|
|
evPrintf(ctx, 1,
|
|
"time between pselect() %u.%09u count %d\n",
|
|
interval.tv_sec, interval.tv_nsec,
|
|
ctx->lastFdCount);
|
|
}
|
|
#endif
|
|
do {
|
|
#ifndef USE_POLL
|
|
/* XXX need to copy only the bits we are using. */
|
|
ctx->rdLast = ctx->rdNext;
|
|
ctx->wrLast = ctx->wrNext;
|
|
ctx->exLast = ctx->exNext;
|
|
#else
|
|
/*
|
|
* The pollfd structure uses separate fields for
|
|
* the input and output events (corresponding to
|
|
* the ??Next and ??Last fd sets), so there's no
|
|
* need to copy one to the other.
|
|
*/
|
|
#endif /* USE_POLL */
|
|
if (m == Timer) {
|
|
INSIST(tp == &t);
|
|
t = evSubTime(nextTime, ctx->lastEventTime);
|
|
}
|
|
|
|
/* XXX should predict system's earliness and adjust. */
|
|
x = pselect(ctx->fdMax+1,
|
|
&ctx->rdLast, &ctx->wrLast, &ctx->exLast,
|
|
tp, NULL);
|
|
pselect_errno = errno;
|
|
|
|
#ifndef USE_POLL
|
|
evPrintf(ctx, 4, "select() returns %d (err: %s)\n",
|
|
x, (x == -1) ? strerror(errno) : "none");
|
|
#else
|
|
evPrintf(ctx, 4, "poll() returns %d (err: %s)\n",
|
|
x, (x == -1) ? strerror(errno) : "none");
|
|
#endif /* USE_POLL */
|
|
/* Anything but a poll can change the time. */
|
|
if (m != JustPoll)
|
|
ctx->lastEventTime = evNowTime();
|
|
|
|
/* Select() likes to finish about 10ms early. */
|
|
} while (x == 0 && m == Timer &&
|
|
evCmpTime(ctx->lastEventTime, nextTime) < 0);
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
ctx->lastSelectTime = ctx->lastEventTime;
|
|
#endif
|
|
if (x < 0) {
|
|
if (pselect_errno == EINTR) {
|
|
if ((options & EV_NULL) != 0)
|
|
goto again;
|
|
OKNEW(new);
|
|
new->type = Null;
|
|
/* No data. */
|
|
opaqueEv->opaque = new;
|
|
return (0);
|
|
}
|
|
if (pselect_errno == EBADF) {
|
|
for (x = 0; x <= ctx->fdMax; x++) {
|
|
struct stat sb;
|
|
|
|
if (FD_ISSET(x, &ctx->rdNext) == 0 &&
|
|
FD_ISSET(x, &ctx->wrNext) == 0 &&
|
|
FD_ISSET(x, &ctx->exNext) == 0)
|
|
continue;
|
|
if (fstat(x, &sb) == -1 &&
|
|
errno == EBADF)
|
|
evPrintf(ctx, 1, "EBADF: %d\n",
|
|
x);
|
|
}
|
|
abort();
|
|
}
|
|
EV_ERR(pselect_errno);
|
|
}
|
|
if (x == 0 && (nextTimer == NULL || !timerPast) &&
|
|
(options & EV_POLL))
|
|
EV_ERR(EWOULDBLOCK);
|
|
ctx->fdCount = x;
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
ctx->lastFdCount = x;
|
|
#endif
|
|
}
|
|
INSIST(nextTimer || ctx->fdCount);
|
|
|
|
/* Timers go first since we'd like them to be accurate. */
|
|
if (nextTimer && !timerPast) {
|
|
/* Has anything happened since we blocked? */
|
|
timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
|
|
}
|
|
if (nextTimer && timerPast) {
|
|
OKNEW(new);
|
|
new->type = Timer;
|
|
new->u.timer.this = nextTimer;
|
|
opaqueEv->opaque = new;
|
|
return (0);
|
|
}
|
|
|
|
/* No timers, so there should be a ready file descriptor. */
|
|
x = 0;
|
|
while (ctx->fdCount > 0) {
|
|
evFile *fid;
|
|
int fd, eventmask;
|
|
|
|
if (ctx->fdNext == NULL) {
|
|
if (++x == 2) {
|
|
/*
|
|
* Hitting the end twice means that the last
|
|
* select() found some FD's which have since
|
|
* been deselected.
|
|
*
|
|
* On some systems, the count returned by
|
|
* selects is the total number of bits in
|
|
* all masks that are set, and on others it's
|
|
* the number of fd's that have some bit set,
|
|
* and on others, it's just broken. We
|
|
* always assume that it's the number of
|
|
* bits set in all masks, because that's what
|
|
* the man page says it should do, and
|
|
* the worst that can happen is we do an
|
|
* extra select().
|
|
*/
|
|
ctx->fdCount = 0;
|
|
break;
|
|
}
|
|
ctx->fdNext = ctx->files;
|
|
}
|
|
fid = ctx->fdNext;
|
|
ctx->fdNext = fid->next;
|
|
|
|
fd = fid->fd;
|
|
eventmask = 0;
|
|
if (FD_ISSET(fd, &ctx->rdLast))
|
|
eventmask |= EV_READ;
|
|
if (FD_ISSET(fd, &ctx->wrLast))
|
|
eventmask |= EV_WRITE;
|
|
if (FD_ISSET(fd, &ctx->exLast))
|
|
eventmask |= EV_EXCEPT;
|
|
eventmask &= fid->eventmask;
|
|
if (eventmask != 0) {
|
|
if ((eventmask & EV_READ) != 0) {
|
|
FD_CLR(fd, &ctx->rdLast);
|
|
ctx->fdCount--;
|
|
}
|
|
if ((eventmask & EV_WRITE) != 0) {
|
|
FD_CLR(fd, &ctx->wrLast);
|
|
ctx->fdCount--;
|
|
}
|
|
if ((eventmask & EV_EXCEPT) != 0) {
|
|
FD_CLR(fd, &ctx->exLast);
|
|
ctx->fdCount--;
|
|
}
|
|
OKNEW(new);
|
|
new->type = File;
|
|
new->u.file.this = fid;
|
|
new->u.file.eventmask = eventmask;
|
|
opaqueEv->opaque = new;
|
|
return (0);
|
|
}
|
|
}
|
|
if (ctx->fdCount < 0) {
|
|
/*
|
|
* select()'s count is off on a number of systems, and
|
|
* can result in fdCount < 0.
|
|
*/
|
|
evPrintf(ctx, 4, "fdCount < 0 (%d)\n", ctx->fdCount);
|
|
ctx->fdCount = 0;
|
|
}
|
|
|
|
/* We get here if the caller deselect()'s an FD. Gag me with a goto. */
|
|
goto again;
|
|
}
|
|
|
|
int
|
|
evDispatch(evContext opaqueCtx, evEvent opaqueEv) {
|
|
evContext_p *ctx = opaqueCtx.opaque;
|
|
evEvent_p *ev = opaqueEv.opaque;
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
void *func;
|
|
struct timespec start_time;
|
|
struct timespec interval;
|
|
#endif
|
|
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
if (ctx->debug > 0)
|
|
start_time = evNowTime();
|
|
#endif
|
|
ctx->cur = ev;
|
|
switch (ev->type) {
|
|
case Accept: {
|
|
evAccept *this = ev->u.accept.this;
|
|
|
|
evPrintf(ctx, 5,
|
|
"Dispatch.Accept: fd %d -> %d, func %p, uap %p\n",
|
|
this->conn->fd, this->fd,
|
|
this->conn->func, this->conn->uap);
|
|
errno = this->ioErrno;
|
|
(this->conn->func)(opaqueCtx, this->conn->uap, this->fd,
|
|
&this->la, this->lalen,
|
|
&this->ra, this->ralen);
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
func = this->conn->func;
|
|
#endif
|
|
break;
|
|
}
|
|
case File: {
|
|
evFile *this = ev->u.file.this;
|
|
int eventmask = ev->u.file.eventmask;
|
|
|
|
evPrintf(ctx, 5,
|
|
"Dispatch.File: fd %d, mask 0x%x, func %p, uap %p\n",
|
|
this->fd, this->eventmask, this->func, this->uap);
|
|
(this->func)(opaqueCtx, this->uap, this->fd, eventmask);
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
func = this->func;
|
|
#endif
|
|
break;
|
|
}
|
|
case Stream: {
|
|
evStream *this = ev->u.stream.this;
|
|
|
|
evPrintf(ctx, 5,
|
|
"Dispatch.Stream: fd %d, func %p, uap %p\n",
|
|
this->fd, this->func, this->uap);
|
|
errno = this->ioErrno;
|
|
(this->func)(opaqueCtx, this->uap, this->fd, this->ioDone);
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
func = this->func;
|
|
#endif
|
|
break;
|
|
}
|
|
case Timer: {
|
|
evTimer *this = ev->u.timer.this;
|
|
|
|
evPrintf(ctx, 5, "Dispatch.Timer: func %p, uap %p\n",
|
|
this->func, this->uap);
|
|
(this->func)(opaqueCtx, this->uap, this->due, this->inter);
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
func = this->func;
|
|
#endif
|
|
break;
|
|
}
|
|
case Wait: {
|
|
evWait *this = ev->u.wait.this;
|
|
|
|
evPrintf(ctx, 5,
|
|
"Dispatch.Wait: tag %p, func %p, uap %p\n",
|
|
this->tag, this->func, this->uap);
|
|
(this->func)(opaqueCtx, this->uap, this->tag);
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
func = this->func;
|
|
#endif
|
|
break;
|
|
}
|
|
case Null: {
|
|
/* No work. */
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
func = NULL;
|
|
#endif
|
|
break;
|
|
}
|
|
default: {
|
|
abort();
|
|
}
|
|
}
|
|
#ifdef EVENTLIB_TIME_CHECKS
|
|
if (ctx->debug > 0) {
|
|
interval = evSubTime(evNowTime(), start_time);
|
|
/*
|
|
* Complain if it took longer than 50 milliseconds.
|
|
*
|
|
* We call getuid() to make an easy to find mark in a kernel
|
|
* trace.
|
|
*/
|
|
if (interval.tv_sec > 0 || interval.tv_nsec > 50000000)
|
|
evPrintf(ctx, 1,
|
|
"dispatch interval %u.%09u uid %d type %d func %p\n",
|
|
interval.tv_sec, interval.tv_nsec,
|
|
getuid(), ev->type, func);
|
|
}
|
|
#endif
|
|
ctx->cur = NULL;
|
|
evDrop(opaqueCtx, opaqueEv);
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
evDrop(evContext opaqueCtx, evEvent opaqueEv) {
|
|
evContext_p *ctx = opaqueCtx.opaque;
|
|
evEvent_p *ev = opaqueEv.opaque;
|
|
|
|
switch (ev->type) {
|
|
case Accept: {
|
|
FREE(ev->u.accept.this);
|
|
break;
|
|
}
|
|
case File: {
|
|
/* No work. */
|
|
break;
|
|
}
|
|
case Stream: {
|
|
evStreamID id;
|
|
|
|
id.opaque = ev->u.stream.this;
|
|
(void) evCancelRW(opaqueCtx, id);
|
|
break;
|
|
}
|
|
case Timer: {
|
|
evTimer *this = ev->u.timer.this;
|
|
evTimerID opaque;
|
|
|
|
/* Check to see whether the user func cleared the timer. */
|
|
if (heap_element(ctx->timers, this->index) != this) {
|
|
evPrintf(ctx, 5, "Dispatch.Timer: timer rm'd?\n");
|
|
break;
|
|
}
|
|
/*
|
|
* Timer is still there. Delete it if it has expired,
|
|
* otherwise set it according to its next interval.
|
|
*/
|
|
if (this->inter.tv_sec == (time_t)0 &&
|
|
this->inter.tv_nsec == 0L) {
|
|
opaque.opaque = this;
|
|
(void) evClearTimer(opaqueCtx, opaque);
|
|
} else {
|
|
opaque.opaque = this;
|
|
(void) evResetTimer(opaqueCtx, opaque, this->func,
|
|
this->uap,
|
|
evAddTime((this->mode & EV_TMR_RATE) ?
|
|
this->due :
|
|
ctx->lastEventTime,
|
|
this->inter),
|
|
this->inter);
|
|
}
|
|
break;
|
|
}
|
|
case Wait: {
|
|
FREE(ev->u.wait.this);
|
|
break;
|
|
}
|
|
case Null: {
|
|
/* No work. */
|
|
break;
|
|
}
|
|
default: {
|
|
abort();
|
|
}
|
|
}
|
|
FREE(ev);
|
|
}
|
|
|
|
int
|
|
evMainLoop(evContext opaqueCtx) {
|
|
evEvent event;
|
|
int x;
|
|
|
|
while ((x = evGetNext(opaqueCtx, &event, EV_WAIT)) == 0)
|
|
if ((x = evDispatch(opaqueCtx, event)) < 0)
|
|
break;
|
|
return (x);
|
|
}
|
|
|
|
int
|
|
evHighestFD(evContext opaqueCtx) {
|
|
evContext_p *ctx = opaqueCtx.opaque;
|
|
|
|
return (ctx->highestFD);
|
|
}
|
|
|
|
void
|
|
evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) {
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
if (ctx->output != NULL && ctx->debug >= level) {
|
|
vfprintf(ctx->output, fmt, ap);
|
|
fflush(ctx->output);
|
|
}
|
|
va_end(ap);
|
|
}
|
|
|
|
int
|
|
evSetOption(evContext *opaqueCtx, const char *option, int value) {
|
|
/* evContext_p *ctx = opaqueCtx->opaque; */
|
|
|
|
UNUSED(opaqueCtx);
|
|
UNUSED(value);
|
|
#ifndef CLOCK_MONOTONIC
|
|
UNUSED(option);
|
|
#endif
|
|
|
|
#ifdef CLOCK_MONOTONIC
|
|
if (strcmp(option, "monotime") == 0) {
|
|
if (opaqueCtx != NULL)
|
|
errno = EINVAL;
|
|
if (value == 0 || value == 1) {
|
|
__evOptMonoTime = value;
|
|
return (0);
|
|
} else {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
}
|
|
#endif
|
|
errno = ENOENT;
|
|
return (-1);
|
|
}
|
|
|
|
int
|
|
evGetOption(evContext *opaqueCtx, const char *option, int *value) {
|
|
/* evContext_p *ctx = opaqueCtx->opaque; */
|
|
|
|
UNUSED(opaqueCtx);
|
|
#ifndef CLOCK_MONOTONIC
|
|
UNUSED(value);
|
|
UNUSED(option);
|
|
#endif
|
|
|
|
#ifdef CLOCK_MONOTONIC
|
|
if (strcmp(option, "monotime") == 0) {
|
|
if (opaqueCtx != NULL)
|
|
errno = EINVAL;
|
|
*value = __evOptMonoTime;
|
|
return (0);
|
|
}
|
|
#endif
|
|
errno = ENOENT;
|
|
return (-1);
|
|
}
|
|
|
|
#if defined(NEED_PSELECT) || defined(USE_POLL)
|
|
/* XXX needs to move to the porting library. */
|
|
static int
|
|
pselect(int nfds, void *rfds, void *wfds, void *efds,
|
|
struct timespec *tsp,
|
|
const sigset_t *sigmask)
|
|
{
|
|
struct timeval tv, *tvp;
|
|
sigset_t sigs;
|
|
int n;
|
|
#ifdef USE_POLL
|
|
int polltimeout = INFTIM;
|
|
evContext_p *ctx;
|
|
struct pollfd *fds;
|
|
nfds_t pnfds;
|
|
|
|
UNUSED(nfds);
|
|
#endif /* USE_POLL */
|
|
|
|
if (tsp) {
|
|
tvp = &tv;
|
|
tv = evTimeVal(*tsp);
|
|
#ifdef USE_POLL
|
|
polltimeout = 1000 * tv.tv_sec + tv.tv_usec / 1000;
|
|
#endif /* USE_POLL */
|
|
} else
|
|
tvp = NULL;
|
|
if (sigmask)
|
|
sigprocmask(SIG_SETMASK, sigmask, &sigs);
|
|
#ifndef USE_POLL
|
|
n = select(nfds, rfds, wfds, efds, tvp);
|
|
#else
|
|
/*
|
|
* rfds, wfds, and efds should all be from the same evContext_p,
|
|
* so any of them will do. If they're all NULL, the caller is
|
|
* presumably calling us to block.
|
|
*/
|
|
if (rfds != NULL)
|
|
ctx = ((__evEmulMask *)rfds)->ctx;
|
|
else if (wfds != NULL)
|
|
ctx = ((__evEmulMask *)wfds)->ctx;
|
|
else if (efds != NULL)
|
|
ctx = ((__evEmulMask *)efds)->ctx;
|
|
else
|
|
ctx = NULL;
|
|
if (ctx != NULL && ctx->fdMax != -1) {
|
|
fds = &(ctx->pollfds[ctx->firstfd]);
|
|
pnfds = ctx->fdMax - ctx->firstfd + 1;
|
|
} else {
|
|
fds = NULL;
|
|
pnfds = 0;
|
|
}
|
|
n = poll(fds, pnfds, polltimeout);
|
|
if (n > 0) {
|
|
int i, e;
|
|
|
|
INSIST(ctx != NULL);
|
|
for (e = 0, i = ctx->firstfd; i <= ctx->fdMax; i++) {
|
|
if (ctx->pollfds[i].fd < 0)
|
|
continue;
|
|
if (FD_ISSET(i, &ctx->rdLast))
|
|
e++;
|
|
if (FD_ISSET(i, &ctx->wrLast))
|
|
e++;
|
|
if (FD_ISSET(i, &ctx->exLast))
|
|
e++;
|
|
}
|
|
n = e;
|
|
}
|
|
#endif /* USE_POLL */
|
|
if (sigmask)
|
|
sigprocmask(SIG_SETMASK, &sigs, NULL);
|
|
if (tsp)
|
|
*tsp = evTimeSpec(tv);
|
|
return (n);
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_POLL
|
|
int
|
|
evPollfdRealloc(evContext_p *ctx, int pollfd_chunk_size, int fd) {
|
|
|
|
int i, maxnfds;
|
|
void *pollfds, *fdTable;
|
|
|
|
if (fd < ctx->maxnfds)
|
|
return (0);
|
|
|
|
/* Don't allow ridiculously small values for pollfd_chunk_size */
|
|
if (pollfd_chunk_size < 20)
|
|
pollfd_chunk_size = 20;
|
|
|
|
maxnfds = (1 + (fd/pollfd_chunk_size)) * pollfd_chunk_size;
|
|
|
|
pollfds = realloc(ctx->pollfds, maxnfds * sizeof(*ctx->pollfds));
|
|
if (pollfds != NULL)
|
|
ctx->pollfds = pollfds;
|
|
fdTable = realloc(ctx->fdTable, maxnfds * sizeof(*ctx->fdTable));
|
|
if (fdTable != NULL)
|
|
ctx->fdTable = fdTable;
|
|
|
|
if (pollfds == NULL || fdTable == NULL) {
|
|
evPrintf(ctx, 2, "pollfd() realloc (%ld) failed\n",
|
|
(long)maxnfds*sizeof(struct pollfd));
|
|
return (-1);
|
|
}
|
|
|
|
for (i = ctx->maxnfds; i < maxnfds; i++) {
|
|
ctx->pollfds[i].fd = -1;
|
|
ctx->pollfds[i].events = 0;
|
|
ctx->fdTable[i] = 0;
|
|
}
|
|
|
|
ctx->maxnfds = maxnfds;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Find the appropriate 'events' or 'revents' field in the pollfds array */
|
|
short *
|
|
__fd_eventfield(int fd, __evEmulMask *maskp) {
|
|
|
|
evContext_p *ctx = (evContext_p *)maskp->ctx;
|
|
|
|
if (!maskp->result || maskp->type == EV_WASNONBLOCKING)
|
|
return (&(ctx->pollfds[fd].events));
|
|
else
|
|
return (&(ctx->pollfds[fd].revents));
|
|
}
|
|
|
|
/* Translate to poll(2) event */
|
|
short
|
|
__poll_event(__evEmulMask *maskp) {
|
|
|
|
switch ((maskp)->type) {
|
|
case EV_READ:
|
|
return (POLLRDNORM);
|
|
case EV_WRITE:
|
|
return (POLLWRNORM);
|
|
case EV_EXCEPT:
|
|
return (POLLRDBAND | POLLPRI | POLLWRBAND);
|
|
case EV_WASNONBLOCKING:
|
|
return (POLLHUP);
|
|
default:
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clear the events corresponding to the specified mask. If this leaves
|
|
* the events mask empty (apart from the POLLHUP bit), set the fd field
|
|
* to -1 so that poll(2) will ignore this fd.
|
|
*/
|
|
void
|
|
__fd_clr(int fd, __evEmulMask *maskp) {
|
|
|
|
evContext_p *ctx = maskp->ctx;
|
|
|
|
*__fd_eventfield(fd, maskp) &= ~__poll_event(maskp);
|
|
if ((ctx->pollfds[fd].events & ~POLLHUP) == 0) {
|
|
ctx->pollfds[fd].fd = -1;
|
|
if (fd == ctx->fdMax)
|
|
while (ctx->fdMax > ctx->firstfd &&
|
|
ctx->pollfds[ctx->fdMax].fd < 0)
|
|
ctx->fdMax--;
|
|
if (fd == ctx->firstfd)
|
|
while (ctx->firstfd <= ctx->fdMax &&
|
|
ctx->pollfds[ctx->firstfd].fd < 0)
|
|
ctx->firstfd++;
|
|
/*
|
|
* Do we have a empty set of descriptors?
|
|
*/
|
|
if (ctx->firstfd > ctx->fdMax) {
|
|
ctx->fdMax = -1;
|
|
ctx->firstfd = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set the events bit(s) corresponding to the specified mask. If the events
|
|
* field has any other bits than POLLHUP set, also set the fd field so that
|
|
* poll(2) will watch this fd.
|
|
*/
|
|
void
|
|
__fd_set(int fd, __evEmulMask *maskp) {
|
|
|
|
evContext_p *ctx = maskp->ctx;
|
|
|
|
*__fd_eventfield(fd, maskp) |= __poll_event(maskp);
|
|
if ((ctx->pollfds[fd].events & ~POLLHUP) != 0) {
|
|
ctx->pollfds[fd].fd = fd;
|
|
if (fd < ctx->firstfd || ctx->fdMax == -1)
|
|
ctx->firstfd = fd;
|
|
if (fd > ctx->fdMax)
|
|
ctx->fdMax = fd;
|
|
}
|
|
}
|
|
#endif /* USE_POLL */
|
|
|
|
/*! \file */
|