Code to allow multiple callbacks per evbuffer.

svn:r1042
This commit is contained in:
Nick Mathewson 2009-01-23 01:11:13 +00:00
parent 86d526a064
commit c735f2b45a
5 changed files with 151 additions and 51 deletions

124
buffer.c
View File

@ -106,9 +106,39 @@ evbuffer_new(void)
buffer = mm_calloc(1, sizeof(struct evbuffer));
TAILQ_INIT(&buffer->callbacks);
return (buffer);
}
static inline void
evbuffer_invoke_callbacks(struct evbuffer *buffer, size_t old_size)
{
size_t new_size = buffer->total_len;
if (!TAILQ_EMPTY(&buffer->callbacks) && old_size != new_size
&& !buffer->in_callbacks) {
struct evbuffer_cb_entry *cbent, *next;
buffer->in_callbacks = 1;
for (cbent = TAILQ_FIRST(&buffer->callbacks);
cbent != TAILQ_END(&buffer->callbacks);
cbent = next) {
next = TAILQ_NEXT(cbent, next);
cbent->cb(buffer, old_size, new_size, cbent->cbarg);
}
buffer->in_callbacks = 0;
}
}
static void
evbuffer_remove_all_callbacks(struct evbuffer *buffer)
{
struct evbuffer_cb_entry *cbent, *next;
while ((cbent = TAILQ_FIRST(&buffer->callbacks))) {
TAILQ_REMOVE(&buffer->callbacks, cbent, next);
mm_free(cbent);
}
}
void
evbuffer_free(struct evbuffer *buffer)
{
@ -117,6 +147,7 @@ evbuffer_free(struct evbuffer *buffer)
next = chain->next;
mm_free(chain);
}
evbuffer_remove_all_callbacks(buffer);
mm_free(buffer);
}
@ -211,12 +242,8 @@ evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
/* remove everything from inbuf */
ZERO_CHAIN(inbuf);
if (inbuf->cb != NULL && inbuf->total_len != in_total_len)
(*inbuf->cb)(inbuf, in_total_len, inbuf->total_len,
inbuf->cbarg);
if (outbuf->cb != NULL && outbuf->total_len != out_total_len)
(*outbuf->cb)(outbuf, out_total_len, outbuf->total_len,
outbuf->cbarg);
evbuffer_invoke_callbacks(inbuf, in_total_len);
evbuffer_invoke_callbacks(outbuf, out_total_len);
return (0);
}
@ -239,12 +266,8 @@ evbuffer_prepend_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
/* remove everything from inbuf */
ZERO_CHAIN(inbuf);
if (inbuf->cb != NULL && inbuf->total_len != in_total_len)
(*inbuf->cb)(inbuf, in_total_len, inbuf->total_len,
inbuf->cbarg);
if (outbuf->cb != NULL && outbuf->total_len != out_total_len)
(*outbuf->cb)(outbuf, out_total_len, outbuf->total_len,
outbuf->cbarg);
evbuffer_invoke_callbacks(inbuf, in_total_len);
evbuffer_invoke_callbacks(outbuf, out_total_len);
}
void
@ -282,9 +305,7 @@ evbuffer_drain(struct evbuffer *buf, size_t len)
}
/* Tell someone about changes in this buffer */
if (buf->cb != NULL && buf->total_len != old_len)
(*buf->cb)(buf, old_len, buf->total_len, buf->cbarg);
evbuffer_invoke_callbacks(buf, old_len);
}
/* Reads data from an event buffer and drains the bytes read */
@ -328,9 +349,8 @@ evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
buf->total_len -= nread;
if (buf->cb != NULL && nread)
(*buf->cb)(buf, buf->total_len + nread, buf->total_len,
buf->cbarg);
if (nread)
evbuffer_invoke_callbacks(buf, buf->total_len + nread);
return (nread);
}
@ -390,12 +410,10 @@ evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
src->total_len -= nread;
if (dst->cb != NULL && nread)
(*dst->cb)(dst, dst->total_len - nread, dst->total_len,
dst->cbarg);
if (src->cb != NULL && nread)
(*src->cb)(src, src->total_len + nread, src->total_len,
src->cbarg);
if (nread) {
evbuffer_invoke_callbacks(dst, dst->total_len - nread);
evbuffer_invoke_callbacks(src, src->total_len + nread);
}
return (nread);
}
@ -709,8 +727,7 @@ evbuffer_add(struct evbuffer *buf, const void *data_in, size_t datlen)
chain->off = datlen;
out:
if (buf->cb != NULL && datlen)
(*buf->cb)(buf, old_len, buf->total_len, buf->cbarg);
evbuffer_invoke_callbacks(buf, old_len);
return (0);
}
@ -754,8 +771,7 @@ evbuffer_prepend(struct evbuffer *buf, const void *data, size_t datlen)
}
buf->total_len += datlen;
if (buf->cb != NULL && datlen)
(*buf->cb)(buf, old_len, buf->total_len, buf->cbarg);
evbuffer_invoke_callbacks(buf, old_len);
return (0);
}
@ -1052,8 +1068,7 @@ evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
buf->total_len += n;
/* Tell someone about changes in this buffer */
if (buf->cb != NULL)
(*buf->cb)(buf, old_len, buf->total_len, buf->cbarg);
evbuffer_invoke_callbacks(buf, old_len);
return (n);
}
@ -1176,9 +1191,8 @@ evbuffer_add_vprintf(struct evbuffer *buf, const char *fmt, va_list ap)
if (sz < space) {
chain->off += sz;
buf->total_len += sz;
if (buf->cb != NULL)
(*buf->cb)(buf, old_len,
buf->total_len, buf->cbarg);
evbuffer_invoke_callbacks(buf, old_len);
return (sz);
}
if (evbuffer_expand(buf, sz + 1) == -1)
@ -1202,10 +1216,44 @@ evbuffer_add_printf(struct evbuffer *buf, const char *fmt, ...)
}
void
evbuffer_setcb(struct evbuffer *buffer,
void (*cb)(struct evbuffer *, size_t, size_t, void *),
void *cbarg)
evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
{
buffer->cb = cb;
buffer->cbarg = cbarg;
if (!TAILQ_EMPTY(&buffer->callbacks))
evbuffer_remove_all_callbacks(buffer);
if (cb)
evbuffer_add_cb(buffer, cb, cbarg);
}
struct evbuffer_cb_entry *
evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
{
struct evbuffer_cb_entry *e;
if (! (e = mm_malloc(sizeof(struct evbuffer_cb_entry))))
return NULL;
e->cb = cb;
e->cbarg = cbarg;
TAILQ_INSERT_HEAD(&buffer->callbacks, e, next);
return e;
}
int
evbuffer_remove_cb_entry(struct evbuffer *buffer,
struct evbuffer_cb_entry *ent)
{
TAILQ_REMOVE(&buffer->callbacks, ent, next);
mm_free(ent);
return 0;
}
int
evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
{
struct evbuffer_cb_entry *cbent;
TAILQ_FOREACH(cbent, &buffer->callbacks, next) {
if (cb == cbent->cb && cbarg == cbent->cbarg) {
return evbuffer_remove_cb_entry(buffer, cbent);
}
}
return -1;
}

View File

@ -34,9 +34,16 @@ extern "C" {
#include "config.h"
#include "evutil.h"
#include <sys/queue.h>
/* minimum allocation */
#define MIN_BUFFER_SIZE 256
struct evbuffer_cb_entry {
TAILQ_ENTRY(evbuffer_cb_entry) next;
evbuffer_cb cb;
void *cbarg;
};
struct evbuffer_chain;
struct evbuffer {
struct evbuffer_chain *first;
@ -45,8 +52,11 @@ struct evbuffer {
size_t total_len; /* total length of all buffers */
void (*cb)(struct evbuffer *, size_t, size_t, void *);
evbuffer_cb cb;
void *cbarg;
TAILQ_HEAD(evbuffer_cb_queue, evbuffer_cb_entry) callbacks;
unsigned char in_callbacks;
};
struct evbuffer_chain {

View File

@ -326,22 +326,59 @@ int evbuffer_read(struct evbuffer *buffer, evutil_socket_t fd, int howmuch);
*/
unsigned char *evbuffer_find(struct evbuffer *buffer, const unsigned char *what, size_t len);
/**
Set a callback to invoke when the evbuffer is modified.
The callback takes four arguments. In order, they are: the evbuffer that
was modified, the buffer's old length, the buffer's new length, and the
value passed to this function in the 'cbarg' argument.
/** Type definition for a callback that is invoked whenever[1] data is added or
removed from an evbuffer.
Subsequent calls to evbuffer_setcb() replace callbacks set by previous
calls. Setting the callback to NULL removes any previously set callback.
An evbuffer may have one or more callbacks set at a time. The order
in which they are exectuded is undefined.
A callback function may add more callbacks, or remove itself from the
list of callbacks, or add or remove data from the buffer. It may not
remove another callback from the list.
[1] If the length of the buffer changes because of a callback, the
callbacks are not invoked again, to prevent an infinite loop.
@param buffer the buffer whose size has changed
@param old_len the previous length of the buffer
@param new_len thee current length of the buffer
@param arg a pointer to user data
*/
typedef void (*evbuffer_cb)(struct evbuffer *buffer, size_t old_len, size_t new_len, void *arg);
struct evbuffer_cb_entry;
/** Add a new callback to an evbuffer.
Subsequent calls to evbuffer_add_cb() add new callbacks. To remove this
callback, call evbuffer_remove_cb or evbuffer_remove_cb_entry.
@param buffer the evbuffer to be monitored
@param cb the callback function to invoke when the evbuffer is modified
@param cb the callback function to invoke when the evbuffer is modified,
or NULL to remove all callbacks.
@param cbarg an argument to be provided to the callback function
@return a handle to the callback on success, or NULL on failure.
*/
void evbuffer_setcb(struct evbuffer *buffer,
void (*cb)(struct evbuffer *, size_t, size_t, void *), void *cbarg);
struct evbuffer_cb_entry *evbuffer_add_cb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg);
/** Remove a callback from an evbuffer, given a handle returned from
evbuffer_add_cb.
Calling this function invalidates the handle.
@return 0 if a callback was removed, or -1 if no matching callback was
found.
*/
int evbuffer_remove_cb_entry(struct evbuffer *buffer,
struct evbuffer_cb_entry *ent);
/** Remove a callback from an evbuffer, given the function and argument
used to add it.
@return 0 if a callback was removed, or -1 if no matching callback was
found.
*/
int evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg);
/**
Makes the data at the begging of an evbuffer contiguous.

View File

@ -81,6 +81,8 @@ struct evbuffer;
@param bev the bufferevent that triggered the callback
@param ctx the user specified context for this bufferevent
*/
/* XXXX we should rename this to bufferevent_cb; evbuffercb implies that it's
* a cb on an evbuffer. We should retain the old name in bufferevent_compat. */
typedef void (*evbuffercb)(struct bufferevent *bev, void *ctx);
/**
@ -96,6 +98,7 @@ typedef void (*evbuffercb)(struct bufferevent *bev, void *ctx);
EVBUFFER_TIMEOUT.
@param ctx the user specified context for this bufferevent
*/
/* XXXX we should rename this to bufferevent_error_cb; see above. */
typedef void (*everrorcb)(struct bufferevent *bev, short what, void *ctx);

View File

@ -175,6 +175,8 @@ What's New In Libevent 2.0 so far:
2.X. Edge-triggered events on some backends.
2.X. Multiple callbacks per evbuffer
3. Big bugfixes
3.X. Win32 bufferevents work