Changed use of refcounts to make sure referenced chains are freed in all cases.

This commit is contained in:
Joachim Bauch 2011-12-07 21:06:10 +01:00
parent 95a8b87a23
commit a8e5e2fce4
3 changed files with 49 additions and 15 deletions

View File

@ -150,7 +150,6 @@ static struct evbuffer_chain *evbuffer_expand_singlechain(struct evbuffer *buf,
static int evbuffer_ptr_subtract(struct evbuffer *buf, struct evbuffer_ptr *pos,
size_t howfar);
static inline void evbuffer_chain_incref(struct evbuffer_chain *chain);
static inline void evbuffer_chain_decref(struct evbuffer_chain *chain);
static struct evbuffer_chain *
evbuffer_chain_new(size_t size)
@ -178,18 +177,24 @@ evbuffer_chain_new(size_t size)
*/
chain->buffer = EVBUFFER_CHAIN_EXTRA(u_char, chain);
chain->refcnt = 1;
return (chain);
}
static inline void
evbuffer_chain_free(struct evbuffer_chain *chain)
{
if (CHAIN_PINNED(chain)) {
chain->flags |= EVBUFFER_DANGLING;
EVUTIL_ASSERT(chain->refcnt > 0);
if (--chain->refcnt > 0) {
// chain is still referenced by other chains
return;
}
if (chain->refcnt > 0) {
// chain is still referenced by other chains
if (CHAIN_PINNED(chain)) {
// will get freed once no longer dangling
chain->refcnt++;
chain->flags |= EVBUFFER_DANGLING;
return;
}
@ -230,7 +235,7 @@ evbuffer_chain_free(struct evbuffer_chain *chain)
EVUTIL_ASSERT(info->source != NULL);
EVUTIL_ASSERT(info->parent != NULL);
EVBUFFER_LOCK(info->source);
evbuffer_chain_decref(info->parent);
evbuffer_chain_free(info->parent);
_evbuffer_decref_and_unlock(info->source);
}
@ -328,14 +333,6 @@ evbuffer_chain_incref(struct evbuffer_chain *chain)
++chain->refcnt;
}
static inline void
evbuffer_chain_decref(struct evbuffer_chain *chain)
{
EVUTIL_ASSERT(chain->refcnt > 0);
--chain->refcnt;
// chain will be freed when parent buffer is released
}
struct evbuffer *
evbuffer_new(void)
{

View File

@ -186,7 +186,7 @@ struct evbuffer_chain {
/** a chain that is a referenced copy of another chain */
#define EVBUFFER_MULTICAST 0x0080
/** number of multicast references to this chain */
/** number of references to this chain */
int refcnt;
/** Usually points to the read-write memory belonging to this

View File

@ -1590,6 +1590,42 @@ end:
evbuffer_free(buf2);
}
static void
test_evbuffer_multicast_drain(void *ptr)
{
const char chunk1[] = "If you have found the answer to such a problem";
const char chunk2[] = "you ought to write it up for publication";
/* -- Knuth's "Notes on the Exercises" from TAOCP */
size_t len1 = strlen(chunk1), len2=strlen(chunk2);
struct evbuffer *buf1 = NULL, *buf2 = NULL;
buf1 = evbuffer_new();
tt_assert(buf1);
evbuffer_add(buf1, chunk1, len1);
evbuffer_add(buf1, ", ", 2);
evbuffer_add(buf1, chunk2, len2);
tt_int_op(evbuffer_get_length(buf1), ==, len1+len2+2);
buf2 = evbuffer_new();
tt_assert(buf2);
tt_int_op(evbuffer_add_buffer_reference(buf2, buf1), ==, 0);
tt_int_op(evbuffer_get_length(buf2), ==, len1+len2+2);
tt_int_op(evbuffer_drain(buf1, evbuffer_get_length(buf1)), ==, 0);
tt_int_op(evbuffer_get_length(buf2), ==, len1+len2+2);
tt_int_op(evbuffer_drain(buf2, evbuffer_get_length(buf2)), ==, 0);
evbuffer_validate(buf1);
evbuffer_validate(buf2);
end:
if (buf1)
evbuffer_free(buf1);
if (buf2)
evbuffer_free(buf2);
}
/* Some cases that we didn't get in test_evbuffer() above, for more coverage. */
static void
test_evbuffer_prepend(void *ptr)
@ -1881,6 +1917,7 @@ struct testcase_t evbuffer_testcases[] = {
{ "callbacks", test_evbuffer_callbacks, 0, NULL, NULL },
{ "add_reference", test_evbuffer_add_reference, 0, NULL, NULL },
{ "multicast", test_evbuffer_multicast, 0, NULL, NULL },
{ "multicast_drain", test_evbuffer_multicast_drain, 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" },