]> granicus.if.org Git - libevent/commitdiff
Code to allow multiple callbacks per evbuffer.
authorNick Mathewson <nickm@torproject.org>
Fri, 23 Jan 2009 01:11:13 +0000 (01:11 +0000)
committerNick Mathewson <nickm@torproject.org>
Fri, 23 Jan 2009 01:11:13 +0000 (01:11 +0000)
svn:r1042

buffer.c
evbuffer-internal.h
include/event2/buffer.h
include/event2/bufferevent.h
whatsnew-xx.txt

index cca2e7f2f9fc3e35b4a574505e2a1091aef71d29..78e91c63b7329aab974f0b43ff35166dcaf92dc8 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -103,12 +103,42 @@ struct evbuffer *
 evbuffer_new(void)
 {
        struct evbuffer *buffer;
-       
+
        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,13 +242,9 @@ 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)
+{
+       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)
 {
-       buffer->cb = cb;
-       buffer->cbarg = 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;
 }
index 4637d87f34c3a1af14060020437ac5c1e5daa87b..5f7fc109fcdf5e14c4eb9c3c4abb18d854127d70 100644 (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 {
index 1194c05635726afe50eb8a9c11dd9918473a3201..66e54dd179f2b48d13eb0e1037a487a0643d0869 100644 (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.
+
+       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_setcb() replace callbacks set by previous
-  calls.  Setting the callback to NULL removes any previously set callback.
+  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.
+ */
+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.
  */
-void evbuffer_setcb(struct evbuffer *buffer,
-    void (*cb)(struct evbuffer *, size_t, size_t, void *), void *cbarg);
+int evbuffer_remove_cb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg);
 
 /**
   Makes the data at the begging of an evbuffer contiguous.
index ffe14738178890a5db10f36e3c6a9754d102f1cf..d0b5419133a65073272799662655a71f4332db22 100644 (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);
 
 
index c7290426746b1384cce3b49ff692fbb435c4a3fb..6ec5bf7dd015e96961d7a724ebe1a94237be23bd 100644 (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