]> granicus.if.org Git - libevent/commitdiff
Replace last_with_data with a slightly smarter version
authorNick Mathewson <nickm@torproject.org>
Sat, 27 Mar 2010 03:18:40 +0000 (23:18 -0400)
committerNick Mathewson <nickm@torproject.org>
Wed, 31 Mar 2010 16:50:32 +0000 (12:50 -0400)
To implement evbuffer_expand() properly, you need to be able to
replace the last chunk that has data, which means that we need to keep
track of the the next pointer pointing to the last_with_data chunk,
not the last_with_data chunk itself.

buffer.c
buffer_iocp.c
evbuffer-internal.h
test/regress_buffer.c

index 3bec0c63c55a42a58dccb1f980a8ba77ca7cb5f0..82acaee0f2960da50e6307ebd5eb9f628c67c5e1 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -210,29 +210,33 @@ evbuffer_chain_free(struct evbuffer_chain *chain)
                }
 #endif
        }
+
        mm_free(chain);
 }
 
+
 static inline void
 evbuffer_chain_insert(struct evbuffer *buf, struct evbuffer_chain *chain)
 {
        ASSERT_EVBUFFER_LOCKED(buf);
        if (buf->first == NULL) {
                buf->first = buf->last = chain;
-               buf->last_with_data = chain;
+               buf->last_with_datap = &buf->first;
        } else {
                /* the last chain is empty so we can just drop it */
                if (buf->last->off == 0 && !CHAIN_PINNED(buf->last)) {
-                       if (buf->last_with_data == buf->last)
-                               buf->last_with_data = chain;
+                       /* NOT CLOSE TO RIGHT XXXX */
+                       if (*buf->last_with_datap == buf->last) {
+                               *buf->last_with_datap = chain;
+                       }
                        evbuffer_chain_free(buf->last);
                        buf->last = chain;
                } else {
                        buf->last->next = chain;
+                       if (chain->off)
+                               buf->last_with_datap = &buf->last->next;
                        buf->last = chain;
                }
-               if (chain->off)
-                       buf->last_with_data = chain;
        }
 
        buf->total_len += chain->off;
@@ -265,6 +269,7 @@ evbuffer_new(void)
 
        TAILQ_INIT(&buffer->callbacks);
        buffer->refcnt = 1;
+       buffer->last_with_datap = &buffer->first;
 
        return (buffer);
 }
@@ -507,7 +512,7 @@ int
 evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size,
     struct evbuffer_iovec *vec, int n_vecs)
 {
-       struct evbuffer_chain *chain;
+       struct evbuffer_chain *chain, **chainp;
        int n = -1;
 
        EVBUFFER_LOCK(buf);
@@ -526,7 +531,7 @@ evbuffer_reserve_space(struct evbuffer *buf, ev_ssize_t size,
        } else {
                if (_evbuffer_expand_fast(buf, size, n_vecs)<0)
                        goto done;
-               n = _evbuffer_read_setup_vecs(buf, size, vec, n_vecs, &chain, 0);
+               n = _evbuffer_read_setup_vecs(buf, size, vec, n_vecs, &chainp, 0);
        }
 
 done:
@@ -541,11 +546,11 @@ advance_last_with_data(struct evbuffer *buf)
        int n = 0;
        ASSERT_EVBUFFER_LOCKED(buf);
 
-       if (!buf->last_with_data)
+       if (!*buf->last_with_datap)
                return 0;
 
-       while (buf->last_with_data->next && buf->last_with_data->next->off) {
-               buf->last_with_data = buf->last_with_data->next;
+       while ((*buf->last_with_datap)->next && (*buf->last_with_datap)->next->off) {
+               buf->last_with_datap = &(*buf->last_with_datap)->next;
                ++n;
        }
        return n;
@@ -555,7 +560,7 @@ int
 evbuffer_commit_space(struct evbuffer *buf,
     struct evbuffer_iovec *vec, int n_vecs)
 {
-       struct evbuffer_chain *firstchain, *chain;
+       struct evbuffer_chain *chain, **firstchainp, **chainp;
        int result = -1;
        size_t added = 0;
        int i;
@@ -576,19 +581,19 @@ evbuffer_commit_space(struct evbuffer *buf,
                buf->last->off += vec[0].iov_len;
                added = vec[0].iov_len;
                if (added)
-                       buf->last_with_data = buf->last;
+                       advance_last_with_data(buf);
                goto okay;
        }
 
        /* Advance 'firstchain' to the first chain with space in it. */
-       firstchain = buf->last_with_data;
-       if (!firstchain)
+       firstchainp = buf->last_with_datap;
+       if (!*firstchainp)
                goto done;
-       if (CHAIN_SPACE_LEN(firstchain) == 0) {
-               firstchain = firstchain->next;
+       if (CHAIN_SPACE_LEN(*firstchainp) == 0) {
+               firstchainp = &(*firstchainp)->next;
        }
 
-       chain = firstchain;
+       chain = *firstchainp;
        /* pass 1: make sure that the pointers and lengths of vecs[] are in
         * bounds before we try to commit anything. */
        for (i=0; i<n_vecs; ++i) {
@@ -600,13 +605,14 @@ evbuffer_commit_space(struct evbuffer *buf,
                chain = chain->next;
        }
        /* pass 2: actually adjust all the chains. */
-       chain = firstchain;
+       chainp = firstchainp;
        for (i=0; i<n_vecs; ++i) {
-               chain->off += vec[i].iov_len;
+               (*chainp)->off += vec[i].iov_len;
                added += vec[i].iov_len;
-               if (vec[i].iov_len)
-                       buf->last_with_data = chain;
-               chain = chain->next;
+               if (vec[i].iov_len) {
+                       buf->last_with_datap = chainp;
+               }
+               chainp = &(*chainp)->next;
        }
 
 okay:
@@ -624,7 +630,7 @@ done:
                ASSERT_EVBUFFER_LOCKED(dst);    \
                (dst)->first = NULL;            \
                (dst)->last = NULL;             \
-               (dst)->last_with_data = NULL;   \
+               (dst)->last_with_datap = &(dst)->first; \
                (dst)->total_len = 0;           \
        } while (0)
 
@@ -632,7 +638,10 @@ done:
                ASSERT_EVBUFFER_LOCKED(dst);                       \
                ASSERT_EVBUFFER_LOCKED(src);                       \
                (dst)->first = (src)->first;                       \
-               (dst)->last_with_data = (src)->last_with_data;     \
+               if ((src)->last_with_datap == &(src)->first)       \
+                       (dst)->last_with_datap = &(dst)->first;    \
+               else                                                    \
+                       (dst)->last_with_datap = (src)->last_with_datap; \
                (dst)->last = (src)->last;                         \
                (dst)->total_len = (src)->total_len;               \
        } while (0)
@@ -641,20 +650,28 @@ done:
                ASSERT_EVBUFFER_LOCKED(dst);                            \
                ASSERT_EVBUFFER_LOCKED(src);                            \
                (dst)->last->next = (src)->first;                       \
-               if ((src)->last_with_data)                              \
-                       (dst)->last_with_data = (src)->last_with_data;  \
+               if ((src)->last_with_datap == &(src)->first)            \
+                       (dst)->last_with_datap = &(dst)->last->next;    \
+               else                                                    \
+                       (dst)->last_with_datap = (src)->last_with_datap; \
                (dst)->last = (src)->last;                              \
                (dst)->total_len += (src)->total_len;                   \
        } while (0)
 
-#define PREPEND_CHAIN(dst, src) do {                              \
-               ASSERT_EVBUFFER_LOCKED(dst);                       \
-               ASSERT_EVBUFFER_LOCKED(src);                       \
-               (src)->last->next = (dst)->first;                  \
-               (dst)->first = (src)->first;                       \
-               (dst)->total_len += (src)->total_len;              \
-               if ((dst)->last_with_data == NULL)                 \
-                       (dst)->last_with_data = (src)->last_with_data; \
+#define PREPEND_CHAIN(dst, src) do {                                   \
+               ASSERT_EVBUFFER_LOCKED(dst);                            \
+               ASSERT_EVBUFFER_LOCKED(src);                            \
+               (src)->last->next = (dst)->first;                       \
+               (dst)->first = (src)->first;                            \
+               (dst)->total_len += (src)->total_len;                   \
+               if (*(dst)->last_with_datap == NULL) {                  \
+                       if ((src)->last_with_datap == &(src)->first)    \
+                               (dst)->last_with_datap = &(dst)->first; \
+                       else                                            \
+                               (dst)->last_with_datap = (src)->last_with_datap; \
+               } else if ((dst)->last_with_datap == &(dst)->first) {   \
+                       (dst)->last_with_datap = &(src)->last->next;    \
+               }                                                       \
        } while (0)
 
 int
@@ -676,6 +693,7 @@ evbuffer_add_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
        }
 
        if (out_total_len == 0) {
+               /* XXX need to free old outbuf chains */
                COPY_CHAIN(outbuf, inbuf);
        } else {
                APPEND_CHAIN(outbuf, inbuf);
@@ -714,6 +732,7 @@ evbuffer_prepend_buffer(struct evbuffer *outbuf, struct evbuffer *inbuf)
        }
 
        if (out_total_len == 0) {
+               /* XXX need to free old outbuf chains */
                COPY_CHAIN(outbuf, inbuf);
        } else {
                PREPEND_CHAIN(outbuf, inbuf);
@@ -768,8 +787,12 @@ evbuffer_drain(struct evbuffer *buf, size_t len)
                for (chain = buf->first; len >= chain->off; chain = next) {
                        next = chain->next;
                        len -= chain->off;
-                       if (chain == buf->last_with_data)
-                               buf->last_with_data = next;
+
+                       if (chain == *buf->last_with_datap) {
+                               buf->last_with_datap = &buf->first;
+                       }
+                       if (&chain->next == buf->last_with_datap)
+                               buf->last_with_datap = &buf->first;
 
                        if (len == 0 && CHAIN_PINNED_R(chain))
                                break;
@@ -823,8 +846,9 @@ evbuffer_remove(struct evbuffer *buf, void *data_out, size_t datlen)
                data += chain->off;
                datlen -= chain->off;
 
-               if (chain == buf->last_with_data)
-                       buf->last_with_data = chain->next;
+               if (chain == *buf->last_with_datap) {
+                       buf->last_with_datap = &buf->first;
+               }
 
                tmp = chain;
                chain = chain->next;
@@ -893,10 +917,12 @@ evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
                /* We can't remove the last with data from src unless we
                 * remove all chains, in which case we would have done the if
                 * block above */
-               EVUTIL_ASSERT(chain != src->last_with_data);
+               EVUTIL_ASSERT(chain != *src->last_with_datap);
                nread += chain->off;
                datlen -= chain->off;
                previous = chain;
+               if (src->last_with_datap == &chain->next)
+                       src->last_with_datap = &src->first;
                chain = chain->next;
        }
 
@@ -908,9 +934,9 @@ evbuffer_remove_buffer(struct evbuffer *src, struct evbuffer *dst,
                        dst->last->next = src->first;
                }
                dst->last = previous;
-               dst->last_with_data = dst->last;
                previous->next = NULL;
                src->first = chain;
+               advance_last_with_data(dst);
 
                dst->total_len += nread;
                dst->n_add_for_cb += nread;
@@ -944,6 +970,7 @@ evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size)
        unsigned char *buffer, *result = NULL;
        ev_ssize_t remaining;
        int removed_last_with_data = 0;
+       int removed_last_with_datap = 0;
 
        EVBUFFER_LOCK(buf);
 
@@ -1013,8 +1040,10 @@ evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size)
                memcpy(buffer, chain->buffer + chain->misalign, chain->off);
                size -= chain->off;
                buffer += chain->off;
-               if (chain ==  buf->last_with_data)
+               if (chain == *buf->last_with_datap)
                        removed_last_with_data = 1;
+               if (&chain->next == buf->last_with_datap)
+                       removed_last_with_datap = 1;
 
                evbuffer_chain_free(chain);
        }
@@ -1030,10 +1059,12 @@ evbuffer_pullup(struct evbuffer *buf, ev_ssize_t size)
        tmp->next = chain;
 
        if (removed_last_with_data) {
-               int n;
-               buf->last_with_data = buf->first;
-               n = advance_last_with_data(buf);
-               EVUTIL_ASSERT(n == 0);
+               buf->last_with_datap = &buf->first;
+       } else if (removed_last_with_datap) {
+               if (buf->first->next && buf->first->next->off)
+                       buf->last_with_datap = &buf->first->next;
+               else
+                       buf->last_with_datap = &buf->first;
        }
 
        result = (tmp->buffer + tmp->misalign);
@@ -1429,10 +1460,8 @@ evbuffer_prepend(struct evbuffer *buf, const void *data, size_t datlen)
        if ((tmp = evbuffer_chain_new(datlen)) == NULL)
                goto done;
        buf->first = tmp;
-       if (buf->last_with_data == NULL)
-               buf->last_with_data = tmp;
-       else if (chain && buf->last_with_data == chain && 0==chain->off)
-               buf->last_with_data = tmp;
+       if (buf->last_with_datap == &buf->first)
+               buf->last_with_datap = &tmp->next;
 
        tmp->next = chain;
 
@@ -1515,8 +1544,6 @@ evbuffer_expand(struct evbuffer *buf, size_t datlen)
        if (buf->first == chain)
                buf->first = tmp;
        buf->last = tmp;
-       if (buf->last->off || buf->last_with_data == chain)
-               buf->last_with_data = tmp;
 
        evbuffer_chain_free(chain);
 
@@ -1555,10 +1582,10 @@ _evbuffer_expand_fast(struct evbuffer *buf, size_t datlen, int n)
        /* How many bytes can we stick at the end of buffer as it is?  Iterate
         * over the chains at the end of the buffer, tring to see how much
         * space we have in the first n. */
-       for (chain = buf->last_with_data; chain; chain = chain->next) {
+       for (chain = *buf->last_with_datap; chain; chain = chain->next) {
                if (chain->off) {
                        size_t space = CHAIN_SPACE_LEN(chain);
-                       EVUTIL_ASSERT(chain == buf->last_with_data);
+                       EVUTIL_ASSERT(chain == *buf->last_with_datap);
                        if (space) {
                                avail += space;
                                ++used;
@@ -1598,7 +1625,7 @@ _evbuffer_expand_fast(struct evbuffer *buf, size_t datlen, int n)
        } else {
                /* Nuke _all_ the empty chains. */
                int rmv_all = 0; /* True iff we removed last_with_data. */
-               chain = buf->last_with_data;
+               chain = *buf->last_with_datap;
                if (!chain->off) {
                        EVUTIL_ASSERT(chain == buf->first);
                        rmv_all = 1;
@@ -1619,16 +1646,17 @@ _evbuffer_expand_fast(struct evbuffer *buf, size_t datlen, int n)
                        if (rmv_all) {
                                ZERO_CHAIN(buf);
                        } else {
-                               buf->last = buf->last_with_data;
-                               buf->last_with_data->next = NULL;
+                               buf->last = *buf->last_with_datap;
+                               (*buf->last_with_datap)->next = NULL;
                        }
                        return (-1);
                }
 
                if (rmv_all) {
-                       buf->first = buf->last = buf->last_with_data = tmp;
+                       buf->first = buf->last = tmp;
+                       buf->last_with_datap = &buf->first;
                } else {
-                       buf->last_with_data->next = tmp;
+                       (*buf->last_with_datap)->next = tmp;
                        buf->last = tmp;
                }
                return (0);
@@ -1679,9 +1707,10 @@ _evbuffer_expand_fast(struct evbuffer *buf, size_t datlen, int n)
 int
 _evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
     struct evbuffer_iovec *vecs, int n_vecs_avail,
-    struct evbuffer_chain **chainp, int exact)
+    struct evbuffer_chain ***chainp, int exact)
 {
-       struct evbuffer_chain *chain, *firstchain;
+       struct evbuffer_chain *chain;
+       struct evbuffer_chain **firstchainp;
        size_t so_far;
        int i;
        ASSERT_EVBUFFER_LOCKED(buf);
@@ -1691,11 +1720,12 @@ _evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
 
        so_far = 0;
        /* Let firstchain be the first chain with any space on it */
-       firstchain =  buf->last_with_data;
-       if (CHAIN_SPACE_LEN(firstchain) == 0)
-               firstchain = firstchain->next;
+       firstchainp = buf->last_with_datap;
+       if (CHAIN_SPACE_LEN(*firstchainp) == 0) {
+               firstchainp = &(*firstchainp)->next;
+       }
 
-       chain = firstchain;
+       chain = *firstchainp;
        for (i = 0; i < n_vecs_avail && so_far < howmuch; ++i) {
                size_t avail = CHAIN_SPACE_LEN(chain);
                if (avail > howmuch && exact)
@@ -1706,7 +1736,7 @@ _evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
                chain = chain->next;
        }
 
-       *chainp = firstchain;
+       *chainp = firstchainp;
        return i;
 }
 
@@ -1715,7 +1745,7 @@ _evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
 int
 evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
 {
-       struct evbuffer_chain *chain;
+       struct evbuffer_chain *chain, **chainp;
        int n = EVBUFFER_MAX_READ;
        int result;
 
@@ -1771,13 +1801,13 @@ evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
                IOV_TYPE vecs[NUM_READ_IOVEC];
 #ifdef _EVBUFFER_IOVEC_IS_NATIVE
                nvecs = _evbuffer_read_setup_vecs(buf, howmuch, vecs,
-                   NUM_READ_IOVEC, &chain, 1);
+                   NUM_READ_IOVEC, &chainp, 1);
 #else
                /* We aren't using the native struct iovec.  Therefore,
                   we are on win32. */
                struct evbuffer_iovec ev_vecs[NUM_READ_IOVEC];
                nvecs = _evbuffer_read_setup_vecs(buf, howmuch, ev_vecs, 2,
-                   &chain, 1);
+                   &chainp, 1);
 
                for (i=0; i < nvecs; ++i)
                        WSABUF_FROM_EVBUFFER_IOV(&vecs[i], &ev_vecs[i]);
@@ -1835,20 +1865,20 @@ evbuffer_read(struct evbuffer *buf, evutil_socket_t fd, int howmuch)
 #ifdef USE_IOVEC_IMPL
        remaining = n;
        for (i=0; i < nvecs; ++i) {
-               ev_ssize_t space = CHAIN_SPACE_LEN(chain);
+               ev_ssize_t space = CHAIN_SPACE_LEN(*chainp);
                if (space < remaining) {
-                       chain->off += space;
+                       (*chainp)->off += space;
                        remaining -= space;
                } else {
-                       chain->off += remaining;
-                       buf->last_with_data = chain;
+                       (*chainp)->off += remaining;
+                       buf->last_with_datap = chainp;
                        break;
                }
-               chain = chain->next;
+               chainp = &(*chainp)->next;
        }
 #else
        chain->off += n;
-       buf->last_with_data = chain;
+       advance_last_with_data(buf);
 #endif
        buf->total_len += n;
        buf->n_add_for_cb += n;
index cdf2eaa575db5965eeea89f5fb9c53a64a7e2e8e..6983a6ac784a53a30aa6a90eb9580e2206df5c32 100644 (file)
@@ -233,7 +233,7 @@ evbuffer_launch_read(struct evbuffer *buf, size_t at_most,
        int r = -1, i;
        int nvecs;
        int npin=0;
-       struct evbuffer_chain *chain=NULL;
+       struct evbuffer_chain *chain=NULL, **chainp;
        DWORD bytesRead;
        DWORD flags = 0;
        struct evbuffer_iovec vecs[MAX_WSABUFS];
@@ -257,7 +257,7 @@ evbuffer_launch_read(struct evbuffer *buf, size_t at_most,
         * not "2".  But commit_read() above can't handle more than two
         * buffers yet. */
        nvecs = _evbuffer_read_setup_vecs(buf, at_most,
-           vecs, 2, &chain, 1);
+           vecs, 2, &chainp, 1);
        for (i=0;i<nvecs;++i) {
                WSABUF_FROM_EVBUFFER_IOV(
                        &buf_o->buffers[i],
@@ -265,7 +265,7 @@ evbuffer_launch_read(struct evbuffer *buf, size_t at_most,
        }
 
        buf_o->n_buffers = nvecs;
-       buf_o->first_pinned = chain;
+       buf_o->first_pinned = chain= *chainp;
        npin=0;
        for ( ; chain; chain = chain->next) {
                _evbuffer_chain_pin(chain, EVBUFFER_MEM_PINNED_R);
index ce1c347d0bad84891a2f45c426842b67128d5750..01f5703aeb3dc67e2111cd4c0dd400d48c9e19b2 100644 (file)
@@ -81,10 +81,20 @@ struct evbuffer {
        /** The last chain in this buffer's linked list of chains. */
        struct evbuffer_chain *last;
 
-       /** The last chain that has any data in it.  If all chains in the
-        * buffer are empty, points to the first chain.  If the buffer has no
-        * chains, this is NULL. */
-       struct evbuffer_chain *last_with_data;
+       /** Pointer to the next pointer pointing at the 'last_with_data' chain.
+        *
+        * To unpack:
+        *
+        * The last_with_data chain is the last chain that has any data in it.
+        * If all chains in the buffer are empty, it is the first chain.
+        * If the buffer has no chains, it is NULL.
+        *
+        * The last_with_datap pointer points at _whatever 'next' pointer_
+        * points at the last_with_datap chain.  If the last_with_data chain
+        * is the first chain, or it is NULL, then the last_with_datap pointer
+        * is &buf->first.
+        */
+       struct evbuffer_chain **last_with_datap;
 
        /** Total amount of bytes stored in all chains.*/
        size_t total_len;
@@ -246,7 +256,7 @@ int _evbuffer_expand_fast(struct evbuffer *, size_t, int);
  * Returns the number of vecs used.
  */
 int _evbuffer_read_setup_vecs(struct evbuffer *buf, ev_ssize_t howmuch,
-    struct evbuffer_iovec *vecs, int n_vecs, struct evbuffer_chain **chainp, int exact);
+    struct evbuffer_iovec *vecs, int n_vecs, struct evbuffer_chain ***chainp, int exact);
 
 /* Helper macro: copies an evbuffer_iovec in ei to a win32 WSABUF in i. */
 #define WSABUF_FROM_EVBUFFER_IOV(i,ei) do {            \
index a018d72dd96081fd08437410f2eac6ad17bd7c55..120d49839877887dd82a1d9ba3e52b90c316d1bb 100644 (file)
@@ -68,7 +68,7 @@ _evbuffer_validate(struct evbuffer *buf)
 {
        struct evbuffer_chain *chain;
        size_t sum = 0;
-       int found_last_with_data = 0;
+       int found_last_with_datap = 0;
 
        if (buf->first == NULL) {
                tt_assert(buf->last == NULL);
@@ -76,9 +76,14 @@ _evbuffer_validate(struct evbuffer *buf)
        }
 
        chain = buf->first;
+
+       tt_assert(buf->last_with_datap);
+       if (buf->last_with_datap == &buf->first)
+               found_last_with_datap = 1;
+
        while (chain != NULL) {
-               if (chain == buf->last_with_data)
-                       found_last_with_data = 1;
+               if (&chain->next == buf->last_with_datap)
+                       found_last_with_datap = 1;
                sum += chain->off;
                if (chain->next == NULL) {
                        tt_assert(buf->last == chain);
@@ -88,10 +93,10 @@ _evbuffer_validate(struct evbuffer *buf)
        }
 
        if (buf->first)
-               tt_assert(buf->last_with_data);
-       if (buf->last_with_data) {
-               tt_assert(found_last_with_data);
-               chain = buf->last_with_data;
+               tt_assert(*buf->last_with_datap);
+
+       if (*buf->last_with_datap) {
+               chain = *buf->last_with_datap;
                if (chain->off == 0 || buf->total_len == 0) {
                        tt_assert(chain->off == 0)
                        tt_assert(chain == buf->first);
@@ -102,7 +107,10 @@ _evbuffer_validate(struct evbuffer *buf)
                        tt_assert(chain->off == 0);
                        chain = chain->next;
                }
+       } else {
+               tt_assert(buf->last_with_datap == &buf->first);
        }
+       tt_assert(found_last_with_datap);
 
        tt_assert(sum == buf->total_len);
        return 1;
@@ -111,7 +119,7 @@ _evbuffer_validate(struct evbuffer *buf)
 }
 
 #define evbuffer_validate(buf)                 \
-       TT_STMT_BEGIN if (!_evbuffer_validate(buf)) goto end; TT_STMT_END
+       TT_STMT_BEGIN if (!_evbuffer_validate(buf)) TT_DIE(("Buffer format invalid")); TT_STMT_END
 
 static void
 test_evbuffer(void *ptr)
@@ -481,7 +489,9 @@ test_evbuffer_add_file(void *ptr)
                evbuffer_validate(src);
        }
 
+       evbuffer_validate(src);
        tt_assert(evbuffer_read(src, pair[1], strlen(data)) == strlen(data));
+       evbuffer_validate(src);
        compare = (char *)evbuffer_pullup(src, strlen(data));
        tt_assert(compare != NULL);
        if (memcmp(compare, data, strlen(data)))
@@ -933,16 +943,20 @@ test_evbuffer_callbacks(void *ptr)
         * adds a summary of length changes to buf_out1/buf_out2 when called. */
        /* size: 0-> 36. */
        evbuffer_add_printf(buf, "The %d magic words are spotty pudding", 2);
+       evbuffer_validate(buf);
        evbuffer_cb_clear_flags(buf, cb2, EVBUFFER_CB_ENABLED);
        evbuffer_drain(buf, 10); /*36->26*/
+       evbuffer_validate(buf);
        evbuffer_prepend(buf, "Hello", 5);/*26->31*/
        evbuffer_cb_set_flags(buf, cb2, EVBUFFER_CB_ENABLED);
        evbuffer_add_reference(buf, "Goodbye", 7, NULL, NULL); /*31->38*/
        evbuffer_remove_cb_entry(buf, cb1);
+       evbuffer_validate(buf);
        evbuffer_drain(buf, evbuffer_get_length(buf)); /*38->0*/;
        tt_assert(-1 == evbuffer_remove_cb(buf, log_change_callback, NULL));
        evbuffer_add(buf, "X", 1); /* 0->1 */
        tt_assert(!evbuffer_remove_cb(buf, log_change_callback, buf_out2));
+       evbuffer_validate(buf);
 
        tt_str_op(evbuffer_pullup(buf_out1, -1), ==,
                  "0->36; 36->26; 26->31; 31->38; ");
@@ -950,7 +964,6 @@ test_evbuffer_callbacks(void *ptr)
                  "0->36; 31->38; 38->0; 0->1; ");
        evbuffer_drain(buf_out1, evbuffer_get_length(buf_out1));
        evbuffer_drain(buf_out2, evbuffer_get_length(buf_out2));
-
        /* Let's test the obsolete buffer_setcb function too. */
        cb1 = evbuffer_add_cb(buf, log_change_callback, buf_out1);
        cb2 = evbuffer_add_cb(buf, log_change_callback, buf_out2);
@@ -962,11 +975,9 @@ test_evbuffer_callbacks(void *ptr)
        evbuffer_setcb(buf, NULL, NULL);
        evbuffer_add_printf(buf, "This will not.");
        tt_str_op(evbuffer_pullup(buf, -1), ==, "This will not.");
-
        evbuffer_validate(buf);
        evbuffer_drain(buf, evbuffer_get_length(buf));
        evbuffer_validate(buf);
-
 #if 0
        /* Now let's try a suspended callback. */
        cb1 = evbuffer_add_cb(buf, log_change_callback, buf_out1);
@@ -1128,6 +1139,7 @@ test_evbuffer_prepend(void *ptr)
        evbuffer_add_printf(buf1, "Here is string %d. ", n++);
        evbuffer_prepend_buffer(buf2, buf1);
        evbuffer_validate(buf2);
+       evbuffer_validate(buf1);
        n = evbuffer_remove(buf2, tmp, sizeof(tmp)-1);
        tmp[n]='\0';
        tt_str_op(tmp,==,"Here is string 1000. Here is string 999. ");
@@ -1362,13 +1374,13 @@ struct testcase_t evbuffer_testcases[] = {
        { "search", test_evbuffer_search, 0, NULL, NULL },
        { "callbacks", test_evbuffer_callbacks, 0, NULL, NULL },
        { "add_reference", test_evbuffer_add_reference, 0, NULL, NULL },
-       { "prepend", test_evbuffer_prepend, 0, NULL, NULL },
+       { "prepend", test_evbuffer_prepend, TT_FORK, NULL, NULL },
        { "peek", test_evbuffer_peek, 0, NULL, NULL },
        { "freeze_start", test_evbuffer_freeze, 0, &nil_setup, (void*)"start" },
        { "freeze_end", test_evbuffer_freeze, 0, &nil_setup, (void*)"end" },
 #ifndef WIN32
        /* TODO: need a temp file implementation for Windows */
-       { "add_file", test_evbuffer_add_file, 0, NULL, NULL },
+       { "add_file", test_evbuffer_add_file, TT_FORK, NULL, NULL },
 #endif
 
        END_OF_TESTCASES