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:
provos 2004-08-07 21:09:47 +00:00
parent c5b0509d65
commit e128f30ca9
10 changed files with 1248 additions and 96 deletions

View File

@ -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

318
lib/libevent/buffer.c Normal file
View File

@ -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;
}

348
lib/libevent/evbuffer.c Normal file
View File

@ -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);
}

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);
}