support for bufferevents; fix signal race; faster timeout insertion;
update man page and regression tests - this is a sync to libevent 0.9
This commit is contained in:
parent
c5b0509d65
commit
e128f30ca9
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: Makefile,v 1.1.1.1 2003/06/12 22:54:25 provos Exp $
|
||||
# $NetBSD: Makefile,v 1.2 2004/08/07 21:09:47 provos Exp $
|
||||
# @(#)Makefile 8.1 (Berkeley) 6/4/93
|
||||
|
||||
NOLINT= # Until someone explains to me how to avoid lint stupidity
|
||||
|
@ -8,7 +8,7 @@ CPPFLAGS+=-DHAVE_CONFIG_H
|
|||
.include <bsd.own.mk>
|
||||
|
||||
LIB= event
|
||||
SRCS= event.c kqueue.c poll.c signal.c
|
||||
SRCS= buffer.c evbuffer.c event.c kqueue.c poll.c signal.c
|
||||
|
||||
INCS= event.h
|
||||
INCSDIR=/usr/include
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* Copyright (c) 2002, 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.
|
||||
* 3. 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "event.h"
|
||||
|
||||
struct evbuffer *
|
||||
evbuffer_new(void)
|
||||
{
|
||||
struct evbuffer *buffer;
|
||||
|
||||
buffer = calloc(1, sizeof(struct evbuffer));
|
||||
|
||||
return (buffer);
|
||||
}
|
||||
|
||||
void
|
||||
evbuffer_free(struct evbuffer *buffer)
|
||||
{
|
||||
if (buffer->orig_buffer != NULL)
|
||||
free(buffer->orig_buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a destructive add. The data from one buffer moves into
|
||||
* the other buffer.
|
||||
*/
|
||||
|
||||
#define SWAP(x,y) do { \
|
||||
(x)->buffer = (y)->buffer; \
|
||||
(x)->orig_buffer = (y)->orig_buffer; \
|
||||
(x)->misalign = (y)->misalign; \
|
||||
(x)->totallen = (y)->totallen; \
|
||||
(x)->off = (y)->off; \
|
||||
} while (0)
|
||||
|
||||
int
|
||||
evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
|
||||
{
|
||||
int res;
|
||||
|
||||
/* Short cut for better performance */
|
||||
if (outbuf->off == 0) {
|
||||
struct evbuffer tmp;
|
||||
size_t oldoff = inbuf->off;
|
||||
|
||||
/* Swap them directly */
|
||||
SWAP(&tmp, outbuf);
|
||||
SWAP(outbuf, inbuf);
|
||||
SWAP(inbuf, &tmp);
|
||||
|
||||
/*
|
||||
* Optimization comes with a price; we need to notify the
|
||||
* buffer if necessary of the changes. oldoff is the amount
|
||||
* of data that we tranfered from inbuf to outbuf
|
||||
*/
|
||||
if (inbuf->off != oldoff && inbuf->cb != NULL)
|
||||
(*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg);
|
||||
if (oldoff && outbuf->cb != NULL)
|
||||
(*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
res = evbuffer_add(outbuf, inbuf->buffer, inbuf->off);
|
||||
if (res == 0)
|
||||
evbuffer_drain(inbuf, inbuf->off);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_add_printf(struct evbuffer *buf, char *fmt, ...)
|
||||
{
|
||||
int res = -1;
|
||||
char *msg;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
if (vasprintf(&msg, fmt, ap) == -1)
|
||||
goto end;
|
||||
|
||||
res = strlen(msg);
|
||||
if (evbuffer_add(buf, msg, res) == -1)
|
||||
res = -1;
|
||||
free(msg);
|
||||
|
||||
end:
|
||||
va_end(ap);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
/* Reads data from an event buffer and drains the bytes read */
|
||||
|
||||
int
|
||||
evbuffer_remove(struct evbuffer *buf, void *data, size_t datlen)
|
||||
{
|
||||
int nread = datlen;
|
||||
if (nread >= buf->off)
|
||||
nread = buf->off;
|
||||
|
||||
memcpy(data, buf->buffer, nread);
|
||||
evbuffer_drain(buf, nread);
|
||||
|
||||
return (nread);
|
||||
}
|
||||
|
||||
/* Adds data to an event buffer */
|
||||
|
||||
static inline void
|
||||
evbuffer_align(struct evbuffer *buf)
|
||||
{
|
||||
memmove(buf->orig_buffer, buf->buffer, buf->off);
|
||||
buf->buffer = buf->orig_buffer;
|
||||
buf->misalign = 0;
|
||||
}
|
||||
|
||||
/* Expands the available space in the event buffer to at least datlen */
|
||||
|
||||
int
|
||||
evbuffer_expand(struct evbuffer *buf, size_t datlen)
|
||||
{
|
||||
size_t need = buf->misalign + buf->off + datlen;
|
||||
|
||||
/* If we can fit all the data, then we don't have to do anything */
|
||||
if (buf->totallen >= need)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* If the misalignment fulfills our data needs, we just force an
|
||||
* alignment to happen. Afterwards, we have enough space.
|
||||
*/
|
||||
if (buf->misalign >= datlen) {
|
||||
evbuffer_align(buf);
|
||||
} else {
|
||||
void *newbuf;
|
||||
size_t length = buf->totallen;
|
||||
|
||||
if (length < 256)
|
||||
length = 256;
|
||||
while (length < need)
|
||||
length <<= 1;
|
||||
|
||||
if (buf->orig_buffer != buf->buffer)
|
||||
evbuffer_align(buf);
|
||||
if ((newbuf = realloc(buf->buffer, length)) == NULL)
|
||||
return (-1);
|
||||
|
||||
buf->orig_buffer = buf->buffer = newbuf;
|
||||
buf->totallen = length;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_add(struct evbuffer *buf, void *data, size_t datlen)
|
||||
{
|
||||
size_t need = buf->misalign + buf->off + datlen;
|
||||
size_t oldoff = buf->off;
|
||||
|
||||
if (buf->totallen < need) {
|
||||
if (evbuffer_expand(buf, datlen) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(buf->buffer + buf->off, data, datlen);
|
||||
buf->off += datlen;
|
||||
|
||||
if (datlen && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
evbuffer_drain(struct evbuffer *buf, size_t len)
|
||||
{
|
||||
size_t oldoff = buf->off;
|
||||
|
||||
if (len >= buf->off) {
|
||||
buf->off = 0;
|
||||
buf->buffer = buf->orig_buffer;
|
||||
buf->misalign = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
buf->buffer += len;
|
||||
buf->misalign += len;
|
||||
|
||||
buf->off -= len;
|
||||
|
||||
done:
|
||||
/* Tell someone about changes in this buffer */
|
||||
if (buf->off != oldoff && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads data from a file descriptor into a buffer.
|
||||
*/
|
||||
|
||||
#define EVBUFFER_MAX_READ 4096
|
||||
|
||||
int
|
||||
evbuffer_read(struct evbuffer *buf, int fd, int howmuch)
|
||||
{
|
||||
u_char *p;
|
||||
size_t oldoff = buf->off;
|
||||
int n = EVBUFFER_MAX_READ;
|
||||
|
||||
if (ioctl(fd, FIONREAD, &n) == -1)
|
||||
n = EVBUFFER_MAX_READ;
|
||||
|
||||
if (howmuch < 0 || howmuch > n)
|
||||
howmuch = n;
|
||||
|
||||
/* If we don't have FIONREAD, we might waste some space here */
|
||||
if (evbuffer_expand(buf, howmuch) == -1)
|
||||
return (-1);
|
||||
|
||||
/* We can append new data at this point */
|
||||
p = buf->buffer + buf->off;
|
||||
|
||||
n = read(fd, p, howmuch);
|
||||
if (n == -1)
|
||||
return (-1);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
|
||||
buf->off += n;
|
||||
|
||||
/* Tell someone about changes in this buffer */
|
||||
if (buf->off != oldoff && buf->cb != NULL)
|
||||
(*buf->cb)(buf, oldoff, buf->off, buf->cbarg);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
int
|
||||
evbuffer_write(struct evbuffer *buffer, int fd)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = write(fd, buffer->buffer, buffer->off);
|
||||
if (n == -1)
|
||||
return (-1);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
evbuffer_drain(buffer, n);
|
||||
|
||||
return (n);
|
||||
}
|
||||
|
||||
u_char *
|
||||
evbuffer_find(struct evbuffer *buffer, u_char *what, size_t len)
|
||||
{
|
||||
size_t remain = buffer->off;
|
||||
u_char *search = buffer->buffer;
|
||||
u_char *p;
|
||||
|
||||
while ((p = memchr(search, *what, remain)) != NULL && remain >= len) {
|
||||
if (memcmp(p, what, len) == 0)
|
||||
return (p);
|
||||
|
||||
search = p + 1;
|
||||
remain = buffer->off - (size_t)(search - buffer->buffer);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void evbuffer_setcb(struct evbuffer *buffer,
|
||||
void (*cb)(struct evbuffer *, size_t, size_t, void *),
|
||||
void *cbarg)
|
||||
{
|
||||
buffer->cb = cb;
|
||||
buffer->cbarg = cbarg;
|
||||
}
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* Copyright (c) 2002-2004 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.
|
||||
* 3. 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.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "event.h"
|
||||
|
||||
void bufferevent_read_pressure_cb(struct evbuffer *, size_t, size_t, void *);
|
||||
void bufferevent_setwatermark(struct bufferevent *, short, size_t, size_t);
|
||||
|
||||
static int
|
||||
bufferevent_add(struct event *ev, int timeout)
|
||||
{
|
||||
struct timeval tv, *ptv = NULL;
|
||||
|
||||
if (timeout) {
|
||||
timerclear(&tv);
|
||||
tv.tv_sec = timeout;
|
||||
ptv = &tv;
|
||||
}
|
||||
|
||||
return (event_add(ev, ptv));
|
||||
}
|
||||
|
||||
/*
|
||||
* This callback is executed when the size of the input buffer changes.
|
||||
* We use it to apply back pressure on the reading side.
|
||||
*/
|
||||
|
||||
void
|
||||
bufferevent_read_pressure_cb(struct evbuffer *buf, size_t old, size_t now,
|
||||
void *arg) {
|
||||
struct bufferevent *bufev = arg;
|
||||
/*
|
||||
* If we are below the watermak then reschedule reading if it's
|
||||
* still enabled.
|
||||
*/
|
||||
if (bufev->wm_read.high == 0 || now < bufev->wm_read.high) {
|
||||
evbuffer_setcb(buf, NULL, NULL);
|
||||
|
||||
if (bufev->enabled & EV_READ)
|
||||
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bufferevent_readcb(int fd, short event, void *arg)
|
||||
{
|
||||
struct bufferevent *bufev = arg;
|
||||
int res = 0;
|
||||
short what = EVBUFFER_READ;
|
||||
size_t len;
|
||||
|
||||
if (event == EV_TIMEOUT) {
|
||||
what |= EVBUFFER_TIMEOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
res = evbuffer_read(bufev->input, fd, -1);
|
||||
if (res == -1) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
goto reschedule;
|
||||
/* error case */
|
||||
what |= EVBUFFER_ERROR;
|
||||
} else if (res == 0) {
|
||||
/* eof case */
|
||||
what |= EVBUFFER_EOF;
|
||||
}
|
||||
|
||||
if (res <= 0)
|
||||
goto error;
|
||||
|
||||
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
|
||||
|
||||
/* See if this callbacks meets the water marks */
|
||||
len = EVBUFFER_LENGTH(bufev->input);
|
||||
if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
|
||||
return;
|
||||
if (bufev->wm_read.high != 0 && len > bufev->wm_read.high) {
|
||||
struct evbuffer *buf = bufev->input;
|
||||
event_del(&bufev->ev_read);
|
||||
|
||||
/* Now schedule a callback for us */
|
||||
evbuffer_setcb(buf, bufferevent_read_pressure_cb, bufev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Invoke the user callback - must always be called last */
|
||||
(*bufev->readcb)(bufev, bufev->cbarg);
|
||||
return;
|
||||
|
||||
reschedule:
|
||||
bufferevent_add(&bufev->ev_read, bufev->timeout_read);
|
||||
return;
|
||||
|
||||
error:
|
||||
(*bufev->errorcb)(bufev, what, bufev->cbarg);
|
||||
}
|
||||
|
||||
static void
|
||||
bufferevent_writecb(int fd, short event, void *arg)
|
||||
{
|
||||
struct bufferevent *bufev = arg;
|
||||
int res = 0;
|
||||
short what = EVBUFFER_WRITE;
|
||||
|
||||
if (event == EV_TIMEOUT) {
|
||||
what |= EVBUFFER_TIMEOUT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVBUFFER_LENGTH(bufev->output)) {
|
||||
res = evbuffer_write(bufev->output, fd);
|
||||
if (res == -1) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
goto reschedule;
|
||||
/* error case */
|
||||
what |= EVBUFFER_ERROR;
|
||||
} else if (res == 0) {
|
||||
/* eof case */
|
||||
what |= EVBUFFER_EOF;
|
||||
}
|
||||
if (res <= 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVBUFFER_LENGTH(bufev->output) != 0)
|
||||
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
|
||||
|
||||
/*
|
||||
* Invoke the user callback if our buffer is drained or below the
|
||||
* low watermark.
|
||||
*/
|
||||
if (EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
|
||||
(*bufev->writecb)(bufev, bufev->cbarg);
|
||||
|
||||
return;
|
||||
|
||||
reschedule:
|
||||
if (EVBUFFER_LENGTH(bufev->output) != 0)
|
||||
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
|
||||
return;
|
||||
|
||||
error:
|
||||
(*bufev->errorcb)(bufev, what, bufev->cbarg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new buffered event object.
|
||||
*
|
||||
* The read callback is invoked whenever we read new data.
|
||||
* The write callback is invoked whenever the output buffer is drained.
|
||||
* The error callback is invoked on a write/read error or on EOF.
|
||||
*/
|
||||
|
||||
struct bufferevent *
|
||||
bufferevent_new(int fd, evbuffercb readcb, evbuffercb writecb,
|
||||
everrorcb errorcb, void *cbarg)
|
||||
{
|
||||
struct bufferevent *bufev;
|
||||
|
||||
if ((bufev = calloc(1, sizeof(struct bufferevent))) == NULL)
|
||||
return (NULL);
|
||||
|
||||
if ((bufev->input = evbuffer_new()) == NULL) {
|
||||
free(bufev);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((bufev->output = evbuffer_new()) == NULL) {
|
||||
evbuffer_free(bufev->input);
|
||||
free(bufev);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
event_set(&bufev->ev_read, fd, EV_READ, bufferevent_readcb, bufev);
|
||||
event_set(&bufev->ev_write, fd, EV_WRITE, bufferevent_writecb, bufev);
|
||||
|
||||
bufev->readcb = readcb;
|
||||
bufev->writecb = writecb;
|
||||
bufev->errorcb = errorcb;
|
||||
|
||||
bufev->cbarg = cbarg;
|
||||
|
||||
bufev->enabled = EV_READ | EV_WRITE;
|
||||
|
||||
return (bufev);
|
||||
}
|
||||
|
||||
void
|
||||
bufferevent_free(struct bufferevent *bufev)
|
||||
{
|
||||
event_del(&bufev->ev_read);
|
||||
event_del(&bufev->ev_write);
|
||||
|
||||
evbuffer_free(bufev->input);
|
||||
evbuffer_free(bufev->output);
|
||||
|
||||
free(bufev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 on success;
|
||||
* -1 on failure.
|
||||
*/
|
||||
|
||||
int
|
||||
bufferevent_write(struct bufferevent *bufev, void *data, size_t size)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = evbuffer_add(bufev->output, data, size);
|
||||
|
||||
if (res == -1)
|
||||
return (res);
|
||||
|
||||
/* If everything is okay, we need to schedule a write */
|
||||
if (size > 0 && (bufev->enabled & EV_WRITE))
|
||||
bufferevent_add(&bufev->ev_write, bufev->timeout_write);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = bufferevent_write(bufev, buf->buffer, buf->off);
|
||||
if (res != -1)
|
||||
evbuffer_drain(buf, buf->off);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
size_t
|
||||
bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
|
||||
{
|
||||
struct evbuffer *buf = bufev->input;
|
||||
|
||||
if (buf->off < size)
|
||||
size = buf->off;
|
||||
|
||||
/* Copy the available data to the user buffer */
|
||||
memcpy(data, buf->buffer, size);
|
||||
|
||||
if (size)
|
||||
evbuffer_drain(buf, size);
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_enable(struct bufferevent *bufev, short event)
|
||||
{
|
||||
if (event & EV_READ) {
|
||||
if (bufferevent_add(&bufev->ev_read, bufev->timeout_read) == -1)
|
||||
return (-1);
|
||||
}
|
||||
if (event & EV_WRITE) {
|
||||
if (bufferevent_add(&bufev->ev_write, bufev->timeout_write) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
bufev->enabled |= event;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
bufferevent_disable(struct bufferevent *bufev, short event)
|
||||
{
|
||||
if (event & EV_READ) {
|
||||
if (event_del(&bufev->ev_read) == -1)
|
||||
return (-1);
|
||||
}
|
||||
if (event & EV_WRITE) {
|
||||
if (event_del(&bufev->ev_write) == -1)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
bufev->enabled &= ~event;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the read and write timeout for a buffered event.
|
||||
*/
|
||||
|
||||
void
|
||||
bufferevent_settimeout(struct bufferevent *bufev,
|
||||
int timeout_read, int timeout_write) {
|
||||
bufev->timeout_read = timeout_read;
|
||||
bufev->timeout_write = timeout_write;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the water marks
|
||||
*/
|
||||
|
||||
void
|
||||
bufferevent_setwatermark(struct bufferevent *bufev, short events,
|
||||
size_t lowmark, size_t highmark)
|
||||
{
|
||||
if (events & EV_READ) {
|
||||
bufev->wm_read.low = lowmark;
|
||||
bufev->wm_read.high = highmark;
|
||||
}
|
||||
|
||||
if (events & EV_WRITE) {
|
||||
bufev->wm_write.low = lowmark;
|
||||
bufev->wm_write.high = highmark;
|
||||
}
|
||||
|
||||
/* If the watermarks changed then see if we should call read again */
|
||||
bufferevent_read_pressure_cb(bufev->input,
|
||||
0, EVBUFFER_LENGTH(bufev->input), bufev);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.\" $NetBSD: event.3,v 1.4 2003/07/04 12:29:38 wiz Exp $
|
||||
.\" $NetBSD: event.3,v 1.5 2004/08/07 21:09:47 provos Exp $
|
||||
.\" $OpenBSD: event.3,v 1.4 2002/07/12 18:50:48 provos Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2000 Artur Grabowski <art@openbsd.org>
|
||||
|
@ -34,9 +34,11 @@
|
|||
.Nm event_init ,
|
||||
.Nm event_dispatch ,
|
||||
.Nm event_loop ,
|
||||
.Nm event_loopexit ,
|
||||
.Nm event_set ,
|
||||
.Nm event_add ,
|
||||
.Nm event_del ,
|
||||
.Nm event_once ,
|
||||
.Nm event_pending ,
|
||||
.Nm event_initialized ,
|
||||
.Nm evtimer_set ,
|
||||
|
@ -48,7 +50,24 @@
|
|||
.Nm signal_add ,
|
||||
.Nm signal_del
|
||||
.Nm signal_pending ,
|
||||
.Nm signal_initialized
|
||||
.Nm signal_initialized ,
|
||||
.Nm bufferevent_new ,
|
||||
.Nm bufferevent_free ,
|
||||
.Nm bufferevent_write ,
|
||||
.Nm bufferevent_write_buffer ,
|
||||
.Nm bufferevent_read ,
|
||||
.Nm bufferevent_enable ,
|
||||
.Nm bufferevent_disable ,
|
||||
.Nm bufferevent_settimeout ,
|
||||
.Nm evbuffer_new ,
|
||||
.Nm evbuffer_free ,
|
||||
.Nm evbuffer_add ,
|
||||
.Nm evbuffer_add_buffer ,
|
||||
.Nm evbuffer_add_printf ,
|
||||
.Nm evbuffer_drain ,
|
||||
.Nm evbuffer_write ,
|
||||
.Nm evbuffer_read ,
|
||||
.Nm evbuffer_find
|
||||
.Nd execute a function when a specific event occurs
|
||||
.Sh LIBRARY
|
||||
.Lb libevent
|
||||
|
@ -61,6 +80,8 @@
|
|||
.Fn "event_dispatch"
|
||||
.Ft int
|
||||
.Fn "event_loop" "int flags"
|
||||
.Ft int
|
||||
.Fn "event_loopexit" "struct timeval *tv"
|
||||
.Ft void
|
||||
.Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg"
|
||||
.Ft int
|
||||
|
@ -68,6 +89,8 @@
|
|||
.Ft int
|
||||
.Fn "event_del" "struct event *ev"
|
||||
.Ft int
|
||||
.Fn "event_once" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "event_pending" "struct event *ev" "short event" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "event_initialized" "struct event *ev"
|
||||
|
@ -91,6 +114,40 @@
|
|||
.Fn "signal_pending" "struct event *ev" "struct timeval *tv"
|
||||
.Ft int
|
||||
.Fn "signal_initialized" "struct event *ev"
|
||||
.Ft "struct bufferevent *"
|
||||
.Fn "bufferevent_new" "int fd" "evbuffercb readcb" "evbuffercb writecb" "everrorcb" "void *cbarg"
|
||||
.Ft void
|
||||
.Fn "bufferevent_free" "struct bufferevent *bufev"
|
||||
.Ft int
|
||||
.Fn "bufferevent_write" "struct bufferevent *bufev" "void *data" "size_t size"
|
||||
.Ft int
|
||||
.Fn "bufferevent_write_buffer" "struct bufferevent *bufev" "struct evbuffer *buf"
|
||||
.Ft size_t
|
||||
.Fn "bufferevent_read" "struct bufferevent *bufev" "void *data" "size_t size"
|
||||
.Ft int
|
||||
.Fn "bufferevent_enable" "struct bufferevent *bufev" "short event"
|
||||
.Ft int
|
||||
.Fn "bufferevent_disable" "struct bufferevent *bufev" "short event"
|
||||
.Ft void
|
||||
.Fn "bufferevent_settimeout" "struct bufferevent *bufev" "int timeout_read" "int timeout_write"
|
||||
.Ft "struct evbuffer *"
|
||||
.Fn "evbuffer_new" "void"
|
||||
.Ft void
|
||||
.Fn "evbuffer_free" "struct evbuffer *buf"
|
||||
.Ft int
|
||||
.Fn "evbuffer_add" "struct evbuffer *buf" "u_char *data" "size_t size"
|
||||
.Ft int
|
||||
.Fn "evbuffer_add_buffer" "struct evbuffer *dst" "struct evbuffer *src"
|
||||
.Ft int
|
||||
.Fn "evbuffer_add_printf" "struct evbuffer *buf" "char *fmt" "..."
|
||||
.Ft void
|
||||
.Fn "evbuffer_drain" "struct evbuffer *buf" "size_t size"
|
||||
.Ft int
|
||||
.Fn "evbuffer_write" "struct evbuffer *buf" "int fd"
|
||||
.Ft int
|
||||
.Fn "evbuffer_read" "struct evbuffer *buf" "int fd" "int size"
|
||||
.Ft "u_char *"
|
||||
.Fn "evbuffer_find" "struct evbuffer *buf" "u_char *data" "size_t size"
|
||||
.Ft int
|
||||
.Fn "(*event_sigcb)" "void"
|
||||
.Ft int
|
||||
|
@ -146,6 +203,11 @@ events. The flags
|
|||
and
|
||||
.Va EVLOOP_NONBLOCK
|
||||
are recognized.
|
||||
The
|
||||
.Nm event_loopexit
|
||||
function allows the loop to be terminated after some amount of time
|
||||
has passed.
|
||||
The parameter indicates the time after which the loop should terminate.
|
||||
.Pp
|
||||
It is the responsibility of the caller to provide these functions with
|
||||
pre-allocated event structures.
|
||||
|
@ -237,6 +299,20 @@ will cancel the event in the argument
|
|||
If the event has already executed or has never been added
|
||||
the call will have no effect.
|
||||
.Pp
|
||||
The function
|
||||
.Fn event_once
|
||||
is similiar to
|
||||
.Fn event_set .
|
||||
However, it schedules a callback to be called exactly once and does not
|
||||
require the caller to prepare an
|
||||
.Fa event
|
||||
structure.
|
||||
This function supports
|
||||
.Fa EV_TIMEOUT ,
|
||||
.Fa EV_READ
|
||||
and
|
||||
.Fa EV_WRITE .
|
||||
.Pp
|
||||
The
|
||||
.Fn event_pending
|
||||
function can be used to check if the event specified by
|
||||
|
@ -293,6 +369,46 @@ By setting the environment variable
|
|||
.Va EVENT_SHOW_METHOD ,
|
||||
.Nm libevent
|
||||
displays the kernel notification method that it uses.
|
||||
.Sh BUFFERED EVENTS
|
||||
.Nm libevent
|
||||
provides an abstraction on top of the regular event callbacks.
|
||||
This abstraction is called a
|
||||
.Va "buffered event" .
|
||||
A buffered event provides input and output buffer that get filled
|
||||
and drained automatically.
|
||||
The user of a buffered event no longer deals directly with the IO,
|
||||
but instead is reading from input and writing to output buffers.
|
||||
.Pp
|
||||
A new bufferevent is created by
|
||||
.Fn bufferevent_new .
|
||||
The parameter
|
||||
.Fa "fd"
|
||||
specifies the file descriptor from which data is read and written to.
|
||||
This file descriptor is not allowed to be a
|
||||
.Xr pipe 2 .
|
||||
The next three parameters are callbacks.
|
||||
The read and write callback have the following form
|
||||
.Ft void
|
||||
.Fn "(*cb)" "struct bufferevent *bufev" "void *arg"
|
||||
The argument is specified by the fourth parameter
|
||||
.Fa "cbarg" .
|
||||
.Pp
|
||||
By default the buffered event is read enabled and will try to read
|
||||
from the file descriptor.
|
||||
The write callback is executed whenever the output buffer is drained
|
||||
below the write low watermark which is
|
||||
.Va 0
|
||||
by default.
|
||||
.Pp
|
||||
The
|
||||
.Fn bufferevent_write
|
||||
function can be used to write data to the file descriptor.
|
||||
The data is appended to the output buffer and written to the descriptor
|
||||
automatically as it becomes available for writing.
|
||||
The
|
||||
.Fn bufferevent_read
|
||||
function is used to read data from the input buffer.
|
||||
Both functions return the amount of data written or read.
|
||||
.Sh RETURN VALUES
|
||||
Upon successful completion
|
||||
.Fn event_add
|
||||
|
@ -318,3 +434,8 @@ manpage by Artur Grabowski.
|
|||
The
|
||||
.Nm event
|
||||
library was written by Niels Provos.
|
||||
.Sh BUGS
|
||||
This documentation is neither complete nor authorative.
|
||||
If you are in doubt about the usage of this API then check the source
|
||||
code to find out how it works, write up the missing piece of
|
||||
documentation and send it to me for inclusion in this man page.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/* $NetBSD: event.c,v 1.3 2003/06/13 04:11:31 itojun Exp $ */
|
||||
/* $NetBSD: event.c,v 1.4 2004/08/07 21:09:47 provos Exp $ */
|
||||
/* $OpenBSD: event.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -89,19 +89,23 @@ struct eventop *eventops[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
struct eventop *evsel;
|
||||
void *evbase;
|
||||
/* Global state */
|
||||
|
||||
/* Handle signals */
|
||||
const struct eventop *evsel;
|
||||
void *evbase;
|
||||
static int event_count;
|
||||
|
||||
/* Handle signals - This is a deprecated interface */
|
||||
int (*event_sigcb)(void); /* Signal callback when gotsig is set */
|
||||
int event_gotsig; /* Set in signal handler */
|
||||
int event_gotterm; /* Set to terminate loop */
|
||||
|
||||
/* Prototypes */
|
||||
void event_queue_insert(struct event *, int);
|
||||
void event_queue_remove(struct event *, int);
|
||||
int event_haveevents(void);
|
||||
void event_process_active(void);
|
||||
void timeout_insert(struct event *);
|
||||
void event_queue_insert(struct event *, int);
|
||||
void event_queue_remove(struct event *, int);
|
||||
int event_haveevents(void);
|
||||
|
||||
static void event_process_active(void);
|
||||
|
||||
static RB_HEAD(event_tree, event) timetree;
|
||||
static struct event_list activequeue;
|
||||
|
@ -116,6 +120,10 @@ compare(struct event *a, struct event *b)
|
|||
return (-1);
|
||||
else if (timercmp(&a->ev_timeout, &b->ev_timeout, >))
|
||||
return (1);
|
||||
if (a < b)
|
||||
return (-1);
|
||||
else if (a > b)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -160,11 +168,10 @@ event_init(void)
|
|||
int
|
||||
event_haveevents(void)
|
||||
{
|
||||
return (RB_ROOT(&timetree) || TAILQ_FIRST(&eventqueue) ||
|
||||
TAILQ_FIRST(&signalqueue) || TAILQ_FIRST(&activequeue));
|
||||
return (event_count > 0);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
event_process_active(void)
|
||||
{
|
||||
struct event *ev;
|
||||
|
@ -185,12 +192,28 @@ event_process_active(void)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait continously for events. We exit only if no events are left.
|
||||
*/
|
||||
|
||||
int
|
||||
event_dispatch(void)
|
||||
{
|
||||
return (event_loop(0));
|
||||
}
|
||||
|
||||
static void
|
||||
event_loopexit_cb(int fd, short what, void *arg)
|
||||
{
|
||||
event_gotterm = 1;
|
||||
}
|
||||
|
||||
int
|
||||
event_loopexit(struct timeval *tv)
|
||||
{
|
||||
return (event_once(-1, EV_TIMEOUT, event_loopexit_cb, NULL, tv));
|
||||
}
|
||||
|
||||
int
|
||||
event_loop(int flags)
|
||||
{
|
||||
|
@ -203,6 +226,12 @@ event_loop(int flags)
|
|||
|
||||
done = 0;
|
||||
while (!done) {
|
||||
/* Terminate the loop if we have been asked to */
|
||||
if (event_gotterm) {
|
||||
event_gotterm = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
while (event_gotsig) {
|
||||
event_gotsig = 0;
|
||||
if (event_sigcb) {
|
||||
|
@ -218,7 +247,7 @@ event_loop(int flags)
|
|||
gettimeofday(&tv, NULL);
|
||||
if (timercmp(&tv, &event_tv, <)) {
|
||||
struct timeval off;
|
||||
LOG_DBG((LOG_MIST, 10,
|
||||
LOG_DBG((LOG_MISC, 10,
|
||||
"%s: time is running backwards, corrected",
|
||||
__func__));
|
||||
|
||||
|
@ -257,6 +286,66 @@ event_loop(int flags)
|
|||
return (0);
|
||||
}
|
||||
|
||||
/* Sets up an event for processing once */
|
||||
|
||||
struct event_once {
|
||||
struct event ev;
|
||||
|
||||
void (*cb)(int, short, void *);
|
||||
void *arg;
|
||||
};
|
||||
|
||||
/* One-time callback, it deletes itself */
|
||||
|
||||
static void
|
||||
event_once_cb(int fd, short events, void *arg)
|
||||
{
|
||||
struct event_once *eonce = arg;
|
||||
|
||||
(*eonce->cb)(fd, events, eonce->arg);
|
||||
free(eonce);
|
||||
}
|
||||
|
||||
/* Schedules an event once */
|
||||
|
||||
int
|
||||
event_once(int fd, short events,
|
||||
void (*callback)(int, short, void *), void *arg, struct timeval *tv)
|
||||
{
|
||||
struct event_once *eonce;
|
||||
struct timeval etv;
|
||||
|
||||
/* We cannot support signals that just fire once */
|
||||
if (events & EV_SIGNAL)
|
||||
return (-1);
|
||||
|
||||
if ((eonce = calloc(1, sizeof(struct event_once))) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (events == EV_TIMEOUT) {
|
||||
if (tv == NULL) {
|
||||
timerclear(&etv);
|
||||
tv = &etv;
|
||||
}
|
||||
|
||||
eonce->cb = callback;
|
||||
eonce->arg = arg;
|
||||
|
||||
evtimer_set(&eonce->ev, event_once_cb, eonce);
|
||||
} else if (events & (EV_READ|EV_WRITE)) {
|
||||
events &= EV_READ|EV_WRITE;
|
||||
|
||||
event_set(&eonce->ev, fd, events, event_once_cb, eonce);
|
||||
} else {
|
||||
/* Bad event combination */
|
||||
return (-1);
|
||||
}
|
||||
|
||||
event_add(&eonce->ev, tv);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
event_set(struct event *ev, int fd, short events,
|
||||
void (*callback)(int, short, void *), void *arg)
|
||||
|
@ -285,8 +374,10 @@ event_pending(struct event *ev, short event, struct timeval *tv)
|
|||
flags |= ev->ev_res;
|
||||
if (ev->ev_flags & EVLIST_TIMEOUT)
|
||||
flags |= EV_TIMEOUT;
|
||||
if (ev->ev_flags & EVLIST_SIGNAL)
|
||||
flags |= EV_SIGNAL;
|
||||
|
||||
event &= (EV_TIMEOUT|EV_READ|EV_WRITE);
|
||||
event &= (EV_TIMEOUT|EV_READ|EV_WRITE|EV_SIGNAL);
|
||||
|
||||
/* See if there is a timeout that we should report */
|
||||
if (tv != NULL && (flags & event & EV_TIMEOUT))
|
||||
|
@ -424,6 +515,9 @@ timeout_next(struct timeval *tv)
|
|||
|
||||
timersub(&ev->ev_timeout, &now, tv);
|
||||
|
||||
assert(tv->tv_sec >= 0);
|
||||
assert(tv->tv_usec >= 0);
|
||||
|
||||
LOG_DBG((LOG_MISC, 60, "timeout_next: in %d seconds", tv->tv_sec));
|
||||
return (0);
|
||||
}
|
||||
|
@ -433,7 +527,8 @@ timeout_correct(struct timeval *off)
|
|||
{
|
||||
struct event *ev;
|
||||
|
||||
/* We can modify the key element of the node without destroying
|
||||
/*
|
||||
* We can modify the key element of the node without destroying
|
||||
* the key, beause we apply it to all in the right order.
|
||||
*/
|
||||
RB_FOREACH(ev, event_tree, &timetree)
|
||||
|
@ -464,31 +559,6 @@ timeout_process(void)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
timeout_insert(struct event *ev)
|
||||
{
|
||||
struct event *tmp;
|
||||
|
||||
tmp = RB_FIND(event_tree, &timetree, ev);
|
||||
|
||||
if (tmp != NULL) {
|
||||
struct timeval tv;
|
||||
struct timeval add = {0,1};
|
||||
|
||||
/* Find unique time */
|
||||
tv = ev->ev_timeout;
|
||||
do {
|
||||
timeradd(&tv, &add, &tv);
|
||||
tmp = RB_NEXT(event_tree, &timetree, tmp);
|
||||
} while (tmp != NULL && timercmp(&tmp->ev_timeout, &tv, ==));
|
||||
|
||||
ev->ev_timeout = tv;
|
||||
}
|
||||
|
||||
tmp = RB_INSERT(event_tree, &timetree, ev);
|
||||
assert(tmp == NULL);
|
||||
}
|
||||
|
||||
void
|
||||
event_queue_remove(struct event *ev, int queue)
|
||||
{
|
||||
|
@ -496,6 +566,9 @@ event_queue_remove(struct event *ev, int queue)
|
|||
errx(1, "%s: %p(fd %d) not on queue %x", __func__,
|
||||
ev, ev->ev_fd, queue);
|
||||
|
||||
if (!(ev->ev_flags & EVLIST_INTERNAL))
|
||||
event_count--;
|
||||
|
||||
ev->ev_flags &= ~queue;
|
||||
switch (queue) {
|
||||
case EVLIST_ACTIVE:
|
||||
|
@ -522,6 +595,9 @@ event_queue_insert(struct event *ev, int queue)
|
|||
errx(1, "%s: %p(fd %d) already on queue %x", __func__,
|
||||
ev, ev->ev_fd, queue);
|
||||
|
||||
if (!(ev->ev_flags & EVLIST_INTERNAL))
|
||||
event_count++;
|
||||
|
||||
ev->ev_flags |= queue;
|
||||
switch (queue) {
|
||||
case EVLIST_ACTIVE:
|
||||
|
@ -530,9 +606,11 @@ event_queue_insert(struct event *ev, int queue)
|
|||
case EVLIST_SIGNAL:
|
||||
TAILQ_INSERT_TAIL(&signalqueue, ev, ev_signal_next);
|
||||
break;
|
||||
case EVLIST_TIMEOUT:
|
||||
timeout_insert(ev);
|
||||
break;
|
||||
case EVLIST_TIMEOUT: {
|
||||
struct event *tmp = RB_INSERT(event_tree, &timetree, ev);
|
||||
assert(tmp == NULL);
|
||||
break;
|
||||
}
|
||||
case EVLIST_INSERTED:
|
||||
TAILQ_INSERT_TAIL(&eventqueue, ev, ev_next);
|
||||
break;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/* $NetBSD: event.h,v 1.2 2003/06/13 04:11:31 itojun Exp $ */
|
||||
/* $NetBSD: event.h,v 1.3 2004/08/07 21:09:47 provos Exp $ */
|
||||
/* $OpenBSD: event.h,v 1.4 2002/07/12 18:50:48 provos Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
|
||||
* Copyright (c) 2000-2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -41,10 +41,11 @@ extern "C" {
|
|||
#define EVLIST_INSERTED 0x02
|
||||
#define EVLIST_SIGNAL 0x04
|
||||
#define EVLIST_ACTIVE 0x08
|
||||
#define EVLIST_INTERNAL 0x10
|
||||
#define EVLIST_INIT 0x80
|
||||
|
||||
/* EVLIST_X_ Private space: 0x1000-0xf000 */
|
||||
#define EVLIST_ALL (0xf000 | 0x8f)
|
||||
#define EVLIST_ALL (0xf000 | 0x9f)
|
||||
|
||||
#define EV_TIMEOUT 0x01
|
||||
#define EV_READ 0x02
|
||||
|
@ -123,6 +124,7 @@ int event_dispatch(void);
|
|||
#define EVLOOP_ONCE 0x01
|
||||
#define EVLOOP_NONBLOCK 0x02
|
||||
int event_loop(int);
|
||||
int event_loopexit(struct timeval *); /* Causes the loop to exit */
|
||||
|
||||
int timeout_next(struct timeval *);
|
||||
void timeout_correct(struct timeval *);
|
||||
|
@ -148,6 +150,8 @@ void timeout_process(void);
|
|||
#define signal_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
|
||||
|
||||
void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
|
||||
int event_once(int, short, void (*)(int, short, void *), void *, struct timeval *);
|
||||
|
||||
int event_add(struct event *, struct timeval *);
|
||||
int event_del(struct event *);
|
||||
void event_active(struct event *, int, short);
|
||||
|
@ -156,6 +160,86 @@ int event_pending(struct event *, short, struct timeval *);
|
|||
|
||||
#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
|
||||
|
||||
/* These functions deal with buffering input and output */
|
||||
|
||||
struct evbuffer {
|
||||
u_char *buffer;
|
||||
u_char *orig_buffer;
|
||||
|
||||
size_t misalign;
|
||||
size_t totallen;
|
||||
size_t off;
|
||||
|
||||
void (*cb)(struct evbuffer *, size_t, size_t, void *);
|
||||
void *cbarg;
|
||||
};
|
||||
|
||||
/* Just for error reporting - use other constants otherwise */
|
||||
#define EVBUFFER_READ 0x01
|
||||
#define EVBUFFER_WRITE 0x02
|
||||
#define EVBUFFER_EOF 0x10
|
||||
#define EVBUFFER_ERROR 0x20
|
||||
#define EVBUFFER_TIMEOUT 0x40
|
||||
|
||||
struct bufferevent;
|
||||
typedef void (*evbuffercb)(struct bufferevent *, void *);
|
||||
typedef void (*everrorcb)(struct bufferevent *, short what, void *);
|
||||
|
||||
struct event_watermark {
|
||||
size_t low;
|
||||
size_t high;
|
||||
};
|
||||
|
||||
struct bufferevent {
|
||||
struct event ev_read;
|
||||
struct event ev_write;
|
||||
|
||||
struct evbuffer *input;
|
||||
struct evbuffer *output;
|
||||
|
||||
struct event_watermark wm_read;
|
||||
struct event_watermark wm_write;
|
||||
|
||||
evbuffercb readcb;
|
||||
evbuffercb writecb;
|
||||
everrorcb errorcb;
|
||||
void *cbarg;
|
||||
|
||||
int timeout_read; /* in seconds */
|
||||
int timeout_write; /* in seconds */
|
||||
|
||||
short enabled; /* events that are currently enabled */
|
||||
};
|
||||
|
||||
struct bufferevent *bufferevent_new(int fd,
|
||||
evbuffercb readcb, evbuffercb writecb, everrorcb errorcb, void *cbarg);
|
||||
void bufferevent_free(struct bufferevent *bufev);
|
||||
int bufferevent_write(struct bufferevent *bufev, void *data, size_t size);
|
||||
int bufferevent_write_buffer(struct bufferevent *bufev, struct evbuffer *buf);
|
||||
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
|
||||
int bufferevent_enable(struct bufferevent *bufev, short event);
|
||||
int bufferevent_disable(struct bufferevent *bufev, short event);
|
||||
void bufferevent_settimeout(struct bufferevent *bufev,
|
||||
int timeout_read, int timeout_write);
|
||||
|
||||
#define EVBUFFER_LENGTH(x) (x)->off
|
||||
#define EVBUFFER_DATA(x) (x)->buffer
|
||||
#define EVBUFFER_INPUT(x) (x)->input
|
||||
#define EVBUFFER_OUTPUT(x) (x)->output
|
||||
|
||||
struct evbuffer *evbuffer_new(void);
|
||||
void evbuffer_free(struct evbuffer *);
|
||||
int evbuffer_expand(struct evbuffer *, size_t);
|
||||
int evbuffer_add(struct evbuffer *, void *, size_t);
|
||||
int evbuffer_remove(struct evbuffer *, void *, size_t);
|
||||
int evbuffer_add_buffer(struct evbuffer *, struct evbuffer *);
|
||||
int evbuffer_add_printf(struct evbuffer *, char *fmt, ...);
|
||||
void evbuffer_drain(struct evbuffer *, size_t);
|
||||
int evbuffer_write(struct evbuffer *, int);
|
||||
int evbuffer_read(struct evbuffer *, int, int);
|
||||
u_char *evbuffer_find(struct evbuffer *, u_char *, size_t);
|
||||
void evbuffer_setcb(struct evbuffer *, void (*)(struct evbuffer *, size_t, size_t, void *), void *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: poll.c,v 1.4 2003/09/28 20:02:44 provos Exp $ */
|
||||
/* $NetBSD: poll.c,v 1.5 2004/08/07 21:09:47 provos Exp $ */
|
||||
/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
|
||||
|
||||
/*
|
||||
|
@ -193,13 +193,18 @@ poll_dispatch(void *arg, struct timeval *tv)
|
|||
return (0);
|
||||
|
||||
for (i = 0; i < nfds; i++) {
|
||||
int what = pop->event_set[i].revents;
|
||||
|
||||
res = 0;
|
||||
|
||||
/* If the file gets closed notify */
|
||||
if (pop->event_set[i].revents & POLLHUP)
|
||||
pop->event_set[i].revents = POLLIN|POLLOUT;
|
||||
if (pop->event_set[i].revents & POLLIN)
|
||||
if (what & POLLHUP)
|
||||
what |= POLLIN|POLLOUT;
|
||||
if (what & POLLERR)
|
||||
what |= POLLIN|POLLOUT;
|
||||
if (what & POLLIN)
|
||||
res |= EV_READ;
|
||||
if (pop->event_set[i].revents & POLLOUT)
|
||||
if (what & POLLOUT)
|
||||
res |= EV_WRITE;
|
||||
if (res == 0)
|
||||
continue;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# $NetBSD: shlib_version,v 1.1.1.1 2003/06/12 22:54:26 provos Exp $
|
||||
# $NetBSD: shlib_version,v 1.2 2004/08/07 21:09:47 provos Exp $
|
||||
# Remember to update distrib/sets/lists/base/shl.* when changing
|
||||
#
|
||||
major=0
|
||||
minor=1
|
||||
minor=2
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: signal.c,v 1.3 2003/06/13 04:11:31 itojun Exp $ */
|
||||
/* $NetBSD: signal.c,v 1.4 2004/08/07 21:09:47 provos Exp $ */
|
||||
/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
|
||||
|
||||
/*
|
||||
|
@ -39,6 +39,7 @@
|
|||
#include <sys/_time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -67,10 +68,39 @@ void evsignal_process(void);
|
|||
int evsignal_recalc(sigset_t *);
|
||||
int evsignal_deliver(sigset_t *);
|
||||
|
||||
static struct event ev_signal;
|
||||
static int ev_signal_pair[2];
|
||||
static int ev_signal_added;
|
||||
|
||||
/* Callback for when the signal handler write a byte to our signaling socket */
|
||||
static void evsignal_cb(int fd, short what, void *arg)
|
||||
{
|
||||
static char signals[100];
|
||||
struct event *ev = arg;
|
||||
int n;
|
||||
|
||||
n = read(fd, signals, sizeof(signals));
|
||||
if (n == -1)
|
||||
err(1, "%s: read", __func__);
|
||||
event_add(ev, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
evsignal_init(sigset_t *evsigmask)
|
||||
{
|
||||
sigemptyset(evsigmask);
|
||||
|
||||
/*
|
||||
* Our signal handler is going to write to one end of the socket
|
||||
* pair to wake up our event loop. The event loop then scans for
|
||||
* signals that got delivered.
|
||||
*/
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ev_signal_pair) == -1)
|
||||
err(1, "%s: socketpair", __func__);
|
||||
|
||||
event_set(&ev_signal, ev_signal_pair[1], EV_READ,
|
||||
evsignal_cb, &ev_signal);
|
||||
ev_signal.ev_flags |= EVLIST_INTERNAL;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -107,6 +137,9 @@ evsignal_handler(int sig)
|
|||
{
|
||||
evsigcaught[sig]++;
|
||||
evsignal_caught = 1;
|
||||
|
||||
/* Wake up our notification mechanism */
|
||||
write(ev_signal_pair[0], "a", 1);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -114,6 +147,11 @@ evsignal_recalc(sigset_t *evsigmask)
|
|||
{
|
||||
struct sigaction sa;
|
||||
struct event *ev;
|
||||
|
||||
if (!ev_signal_added) {
|
||||
ev_signal_added = 1;
|
||||
event_add(&ev_signal, NULL);
|
||||
}
|
||||
|
||||
if (TAILQ_FIRST(&signalqueue) == NULL && !needrecalc)
|
||||
return (0);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* $NetBSD: eventtest.c,v 1.2 2003/06/13 04:09:18 itojun Exp $ */
|
||||
/* $NetBSD: eventtest.c,v 1.3 2004/08/07 21:09:47 provos Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright 2003 Niels Provos <provos@citi.umich.edu>
|
||||
* Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -30,10 +30,6 @@
|
|||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
|
@ -71,8 +67,10 @@ simple_read_cb(int fd, short event, void *arg)
|
|||
len = read(fd, buf, sizeof(buf));
|
||||
|
||||
if (len) {
|
||||
if (!called)
|
||||
event_add(arg, NULL);
|
||||
if (!called) {
|
||||
if (event_add(arg, NULL) == -1)
|
||||
exit(1);
|
||||
}
|
||||
} else if (called == 1)
|
||||
test_ok = 1;
|
||||
|
||||
|
@ -118,8 +116,10 @@ multiple_write_cb(int fd, short event, void *arg)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!usepersist)
|
||||
event_add(ev, NULL);
|
||||
if (!usepersist) {
|
||||
if (event_add(ev, NULL) == -1)
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -138,8 +138,10 @@ multiple_read_cb(int fd, short event, void *arg)
|
|||
}
|
||||
|
||||
roff += len;
|
||||
if (!usepersist)
|
||||
event_add(ev, NULL);
|
||||
if (!usepersist) {
|
||||
if (event_add(ev, NULL) == -1)
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -190,7 +192,8 @@ combined_read_cb(int fd, short event, void *arg)
|
|||
return;
|
||||
|
||||
both->nread += len;
|
||||
event_add(&both->ev, NULL);
|
||||
if (event_add(&both->ev, NULL) == -1)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -213,7 +216,8 @@ combined_write_cb(int fd, short event, void *arg)
|
|||
}
|
||||
|
||||
both->nread -= len;
|
||||
event_add(&both->ev, NULL);
|
||||
if (event_add(&both->ev, NULL) == -1)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Test infrastructure */
|
||||
|
@ -229,6 +233,12 @@ setup_test(char *name)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
if (fcntl(pair[0], F_SETFL, O_NONBLOCK) == -1)
|
||||
warn("fcntl(O_NONBLOCK)");
|
||||
|
||||
if (fcntl(pair[1], F_SETFL, O_NONBLOCK) == -1)
|
||||
warn("fcntl(O_NONBLOCK)");
|
||||
|
||||
test_ok = 0;
|
||||
called = 0;
|
||||
return (0);
|
||||
|
@ -250,19 +260,10 @@ cleanup_test(void)
|
|||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
void
|
||||
test1(void)
|
||||
{
|
||||
struct event ev, ev2;
|
||||
struct timeval tv;
|
||||
struct itimerval itv;
|
||||
struct both r1, r2, w1, w2;
|
||||
int i;
|
||||
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
/* Initalize the event library */
|
||||
event_init();
|
||||
struct event ev;
|
||||
|
||||
/* Very simple read test */
|
||||
setup_test("Simple read: ");
|
||||
|
@ -271,19 +272,34 @@ main (int argc, char **argv)
|
|||
shutdown(pair[0], SHUT_WR);
|
||||
|
||||
event_set(&ev, pair[1], EV_READ, simple_read_cb, &ev);
|
||||
event_add(&ev, NULL);
|
||||
if (event_add(&ev, NULL) == -1)
|
||||
exit(1);
|
||||
event_dispatch();
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
void
|
||||
test2(void)
|
||||
{
|
||||
struct event ev;
|
||||
|
||||
/* Very simple write test */
|
||||
setup_test("Simple write: ");
|
||||
|
||||
event_set(&ev, pair[0], EV_WRITE, simple_write_cb, &ev);
|
||||
event_add(&ev, NULL);
|
||||
if (event_add(&ev, NULL) == -1)
|
||||
exit(1);
|
||||
event_dispatch();
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
void
|
||||
test3(void)
|
||||
{
|
||||
struct event ev, ev2;
|
||||
int i;
|
||||
|
||||
/* Multiple read and write test */
|
||||
setup_test("Multiple read/write: ");
|
||||
|
@ -295,15 +311,24 @@ main (int argc, char **argv)
|
|||
usepersist = 0;
|
||||
|
||||
event_set(&ev, pair[0], EV_WRITE, multiple_write_cb, &ev);
|
||||
event_add(&ev, NULL);
|
||||
if (event_add(&ev, NULL) == -1)
|
||||
exit(1);
|
||||
event_set(&ev2, pair[1], EV_READ, multiple_read_cb, &ev2);
|
||||
event_add(&ev2, NULL);
|
||||
if (event_add(&ev2, NULL) == -1)
|
||||
exit(1);
|
||||
event_dispatch();
|
||||
|
||||
if (roff == woff)
|
||||
test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0;
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
void
|
||||
test4(void)
|
||||
{
|
||||
struct event ev, ev2;
|
||||
int i;
|
||||
|
||||
/* Multiple read and write test with persist */
|
||||
setup_test("Persist read/write: ");
|
||||
|
@ -315,15 +340,23 @@ main (int argc, char **argv)
|
|||
usepersist = 1;
|
||||
|
||||
event_set(&ev, pair[0], EV_WRITE|EV_PERSIST, multiple_write_cb, &ev);
|
||||
event_add(&ev, NULL);
|
||||
if (event_add(&ev, NULL) == -1)
|
||||
exit(1);
|
||||
event_set(&ev2, pair[1], EV_READ|EV_PERSIST, multiple_read_cb, &ev2);
|
||||
event_add(&ev2, NULL);
|
||||
if (event_add(&ev2, NULL) == -1)
|
||||
exit(1);
|
||||
event_dispatch();
|
||||
|
||||
if (roff == woff)
|
||||
test_ok = memcmp(rbuf, wbuf, sizeof(wbuf)) == 0;
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
void
|
||||
test5(void)
|
||||
{
|
||||
struct both r1, r2, w1, w2;
|
||||
|
||||
setup_test("Combined read/write: ");
|
||||
memset(&r1, 0, sizeof(r1));
|
||||
|
@ -338,10 +371,14 @@ main (int argc, char **argv)
|
|||
event_set(&w1.ev, pair[0], EV_WRITE, combined_write_cb, &w1);
|
||||
event_set(&r2.ev, pair[1], EV_READ, combined_read_cb, &r2);
|
||||
event_set(&w2.ev, pair[1], EV_WRITE, combined_write_cb, &w2);
|
||||
event_add(&r1.ev, NULL);
|
||||
event_add(&w1.ev, NULL);
|
||||
event_add(&r2.ev, NULL);
|
||||
event_add(&w2.ev, NULL);
|
||||
if (event_add(&r1.ev, NULL) == -1)
|
||||
exit(1);
|
||||
if (event_add(&w1.ev, NULL))
|
||||
exit(1);
|
||||
if (event_add(&r2.ev, NULL))
|
||||
exit(1);
|
||||
if (event_add(&w2.ev, NULL))
|
||||
exit(1);
|
||||
|
||||
event_dispatch();
|
||||
|
||||
|
@ -349,7 +386,13 @@ main (int argc, char **argv)
|
|||
test_ok = 1;
|
||||
|
||||
cleanup_test();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
test6(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct event ev;
|
||||
|
||||
setup_test("Simple timeout: ");
|
||||
|
||||
|
@ -362,6 +405,13 @@ main (int argc, char **argv)
|
|||
event_dispatch();
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
void
|
||||
test7(void)
|
||||
{
|
||||
struct event ev;
|
||||
struct itimerval itv;
|
||||
|
||||
setup_test("Simple signal: ");
|
||||
signal_set(&ev, SIGALRM, signal_cb, &ev);
|
||||
|
@ -377,6 +427,116 @@ main (int argc, char **argv)
|
|||
signal_del(&ev);
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
void
|
||||
test8(void)
|
||||
{
|
||||
struct timeval tv, tv_start, tv_end;
|
||||
struct event ev;
|
||||
|
||||
setup_test("Loop exit: ");
|
||||
|
||||
tv.tv_usec = 0;
|
||||
tv.tv_sec = 60*60*24;
|
||||
evtimer_set(&ev, timeout_cb, NULL);
|
||||
evtimer_add(&ev, &tv);
|
||||
|
||||
tv.tv_usec = 0;
|
||||
tv.tv_sec = 1;
|
||||
event_loopexit(&tv);
|
||||
|
||||
gettimeofday(&tv_start, NULL);
|
||||
event_dispatch();
|
||||
gettimeofday(&tv_end, NULL);
|
||||
timersub(&tv_end, &tv_start, &tv_end);
|
||||
|
||||
evtimer_del(&ev);
|
||||
|
||||
if (tv.tv_sec < 2)
|
||||
test_ok = 1;
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
void
|
||||
readcb(struct bufferevent *bev, void *arg)
|
||||
{
|
||||
if (EVBUFFER_LENGTH(bev->input) == 8333) {
|
||||
bufferevent_disable(bev, EV_READ);
|
||||
test_ok++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
writecb(struct bufferevent *bev, void *arg)
|
||||
{
|
||||
if (EVBUFFER_LENGTH(bev->output) == 0)
|
||||
test_ok++;
|
||||
}
|
||||
|
||||
void
|
||||
errorcb(struct bufferevent *bev, short what, void *arg)
|
||||
{
|
||||
test_ok = -2;
|
||||
}
|
||||
|
||||
void
|
||||
test9(void)
|
||||
{
|
||||
struct bufferevent *bev1, *bev2;
|
||||
char buffer[8333];
|
||||
int i;
|
||||
|
||||
setup_test("Bufferevent: ");
|
||||
|
||||
bev1 = bufferevent_new(pair[0], readcb, writecb, errorcb, NULL);
|
||||
bev2 = bufferevent_new(pair[1], readcb, writecb, errorcb, NULL);
|
||||
|
||||
bufferevent_disable(bev1, EV_READ);
|
||||
bufferevent_enable(bev2, EV_READ);
|
||||
|
||||
for (i = 0; i < sizeof(buffer); i++)
|
||||
buffer[0] = i;
|
||||
|
||||
bufferevent_write(bev1, buffer, sizeof(buffer));
|
||||
|
||||
event_dispatch();
|
||||
|
||||
bufferevent_free(bev1);
|
||||
bufferevent_free(bev2);
|
||||
|
||||
if (test_ok != 2)
|
||||
test_ok = 0;
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
|
||||
/* Initalize the event library */
|
||||
event_init();
|
||||
|
||||
test1();
|
||||
|
||||
test2();
|
||||
|
||||
test3();
|
||||
|
||||
test4();
|
||||
|
||||
test5();
|
||||
|
||||
test6();
|
||||
|
||||
test7();
|
||||
|
||||
test8();
|
||||
|
||||
test9();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue