#if !defined(LINT) && !defined(CODECENTER)
static const char rcsid[] = "$Id: eventlib.c,v 1.1.1.1 2003/01/10 00:48:15 bbraun Exp $";
#endif
#include "port_before.h"
#include "fd_setsize.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#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"
#ifdef NEED_PSELECT
static int pselect(int, void *, void *, void *,
struct timespec *,
const sigset_t *);
#endif
int
evCreate(evContext *opaqueCtx) {
evContext_p *ctx;
if (meminit(0, 0) < 0 && errno != EEXIST)
return (-1);
OKNEW(ctx);
ctx->cur = NULL;
ctx->debug = 0;
ctx->output = NULL;
ctx->conns = NULL;
INIT_LIST(ctx->accepts);
ctx->files = NULL;
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;
ctx->highestFD = FD_SETSIZE - 1;
#ifdef EVENTLIB_TIME_CHECKS
ctx->lastFdCount = 0;
#endif
memset(ctx->fdTable, 0, sizeof ctx->fdTable);
ctx->streams = NULL;
ctx->strDone = NULL;
ctx->strLast = NULL;
ctx->lastEventTime = evNowTime();
#ifdef EVENTLIB_TIME_CHECKS
ctx->lastSelectTime = ctx->lastEventTime;
#endif
ctx->timers = evCreateTimers(ctx);
if (ctx->timers == NULL)
return (-1);
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;
evWaitList *this_wl, *next_wl;
evWait *this_wait, *next_wait;
while (revs-- > 0 && ctx->conns != NULL) {
evConnID id;
id.opaque = ctx->conns;
(void) evCancelConn(opaqueCtx, id);
}
INSIST(revs >= 0);
while (revs-- > 0 && ctx->streams != NULL) {
evStreamID id;
id.opaque = ctx->streams;
(void) evCancelRW(opaqueCtx, id);
}
while (revs-- > 0 && ctx->files != NULL) {
evFileID id;
id.opaque = ctx->files;
(void) evDeselectFD(opaqueCtx, id);
}
INSIST(revs >= 0);
evDestroyTimers(ctx);
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
x = ((options & EV_POLL) != 0) + ((options & EV_WAIT) != 0);
if (x != 1)
EV_ERR(EINVAL);
ctx->lastEventTime = evNowTime();
again:
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);
}
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);
}
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);
}
if ((nextTimer = heap_element(ctx->timers, 1)) != NULL) {
nextTime = nextTimer->due;
timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
} else
timerPast = 0;
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;
if ((options & EV_WAIT) != 0 && !nextTimer && ctx->fdMax == -1)
EV_ERR(ENOENT);
if ((options & EV_POLL) != 0) {
m = JustPoll;
t = NoTime;
tp = &t;
} else if (nextTimer == NULL) {
m = Block;
tp = NULL;
} else if (timerPast) {
m = JustPoll;
t = NoTime;
tp = &t;
} else {
m = Timer;
tp = &t;
}
#ifdef EVENTLIB_TIME_CHECKS
if (ctx->debug > 0) {
interval = evSubTime(ctx->lastEventTime,
ctx->lastSelectTime);
if (interval.tv_sec > 0)
evPrintf(ctx, 1,
"time between pselect() %u.%09u count %d\n",
interval.tv_sec, interval.tv_nsec,
ctx->lastFdCount);
}
#endif
do {
ctx->rdLast = ctx->rdNext;
ctx->wrLast = ctx->wrNext;
ctx->exLast = ctx->exNext;
if (m == Timer) {
INSIST(tp == &t);
t = evSubTime(nextTime, ctx->lastEventTime);
}
evPrintf(ctx, 4,
"pselect(%d, 0x%lx, 0x%lx, 0x%lx, %d.%09ld)\n",
ctx->fdMax+1,
(u_long)ctx->rdLast.fds_bits[0],
(u_long)ctx->wrLast.fds_bits[0],
(u_long)ctx->exLast.fds_bits[0],
tp ? tp->tv_sec : -1,
tp ? tp->tv_nsec : -1);
x = pselect(ctx->fdMax+1,
&ctx->rdLast, &ctx->wrLast, &ctx->exLast,
tp, NULL);
pselect_errno = errno;
evPrintf(ctx, 4, "select() returns %d (err: %s)\n",
x, (x == -1) ? strerror(errno) : "none");
if (m != JustPoll)
ctx->lastEventTime = evNowTime();
} 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;
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);
if (nextTimer && !timerPast) {
timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
}
if (nextTimer && timerPast) {
OKNEW(new);
new->type = Timer;
new->u.timer.this = nextTimer;
opaqueEv->opaque = new;
return (0);
}
x = 0;
while (ctx->fdCount > 0) {
evFile *fid;
int fd, eventmask;
if (ctx->fdNext == NULL) {
if (++x == 2) {
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) {
evPrintf(ctx, 4, "fdCount < 0 (%d)\n", ctx->fdCount);
ctx->fdCount = 0;
}
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 %#x, uap %#x\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 %#x, uap %#x\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 %#x, uap %#x\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 %#x, uap %#x\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 %#x, func %#x, uap %#x\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: {
#ifdef EVENTLIB_TIME_CHECKS
func = NULL;
#endif
break;
}
default: {
abort();
}
}
#ifdef EVENTLIB_TIME_CHECKS
if (ctx->debug > 0) {
interval = evSubTime(evNowTime(), start_time);
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: {
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;
if (heap_element(ctx->timers, this->index) != this) {
evPrintf(ctx, 5, "Dispatch.Timer: timer rm'd?\n");
break;
}
if (this->inter.tv_sec == 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(ctx->lastEventTime,
this->inter),
this->inter);
}
break;
}
case Wait: {
FREE(ev->u.wait.this);
break;
}
case Null: {
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);
}
#ifdef NEED_PSELECT
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;
if (tsp) {
tvp = &tv;
tv = evTimeVal(*tsp);
} else
tvp = NULL;
if (sigmask)
sigprocmask(SIG_SETMASK, sigmask, &sigs);
n = select(nfds, rfds, wfds, efds, tvp);
if (sigmask)
sigprocmask(SIG_SETMASK, &sigs, NULL);
if (tsp)
*tsp = evTimeSpec(tv);
return (n);
}
#endif