mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-09 20:41:27 -04:00
Improve robustness for refcounting
Document that we do intend to double-decref underlying bufferevents under some circumstances. Check to make sure that we don't decref past 0.
This commit is contained in:
parent
6c83e6c972
commit
f1bc125eb4
2
buffer.c
2
buffer.c
@ -435,6 +435,8 @@ _evbuffer_decref_and_unlock(struct evbuffer *buffer)
|
|||||||
struct evbuffer_chain *chain, *next;
|
struct evbuffer_chain *chain, *next;
|
||||||
ASSERT_EVBUFFER_LOCKED(buffer);
|
ASSERT_EVBUFFER_LOCKED(buffer);
|
||||||
|
|
||||||
|
EVUTIL_ASSERT(buffer->refcnt > 0);
|
||||||
|
|
||||||
if (--buffer->refcnt > 0) {
|
if (--buffer->refcnt > 0) {
|
||||||
EVBUFFER_UNLOCK(buffer);
|
EVBUFFER_UNLOCK(buffer);
|
||||||
return;
|
return;
|
||||||
|
@ -280,11 +280,12 @@ void bufferevent_incref(struct bufferevent *bufev);
|
|||||||
/** Internal: Lock bufev and increase its reference count.
|
/** Internal: Lock bufev and increase its reference count.
|
||||||
* unlocking it otherwise. */
|
* unlocking it otherwise. */
|
||||||
void _bufferevent_incref_and_lock(struct bufferevent *bufev);
|
void _bufferevent_incref_and_lock(struct bufferevent *bufev);
|
||||||
/** Internal: Increment the reference count on bufev. */
|
/** Internal: Decrement the reference count on bufev. Returns 1 if it freed
|
||||||
void bufferevent_decref(struct bufferevent *bufev);
|
* the bufferevent.*/
|
||||||
|
int bufferevent_decref(struct bufferevent *bufev);
|
||||||
/** Internal: Drop the reference count on bufev, freeing as necessary, and
|
/** Internal: Drop the reference count on bufev, freeing as necessary, and
|
||||||
* unlocking it otherwise. */
|
* unlocking it otherwise. Returns 1 if it freed the bufferevent. */
|
||||||
void _bufferevent_decref_and_unlock(struct bufferevent *bufev);
|
int _bufferevent_decref_and_unlock(struct bufferevent *bufev);
|
||||||
|
|
||||||
/** Internal: If callbacks are deferred and we have a read callback, schedule
|
/** Internal: If callbacks are deferred and we have a read callback, schedule
|
||||||
* a readcb. Otherwise just run the readcb. */
|
* a readcb. Otherwise just run the readcb. */
|
||||||
|
@ -504,16 +504,36 @@ _bufferevent_incref_and_lock(struct bufferevent *bufev)
|
|||||||
++bufev_private->refcnt;
|
++bufev_private->refcnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
#if 0
|
||||||
|
static void
|
||||||
|
_bufferevent_transfer_lock_ownership(struct bufferevent *donor,
|
||||||
|
struct bufferevent *recipient)
|
||||||
|
{
|
||||||
|
struct bufferevent_private *d = BEV_UPCAST(donor);
|
||||||
|
struct bufferevent_private *r = BEV_UPCAST(recipient);
|
||||||
|
if (d->lock != r->lock)
|
||||||
|
return;
|
||||||
|
if (r->own_lock)
|
||||||
|
return;
|
||||||
|
if (d->own_lock) {
|
||||||
|
d->own_lock = 0;
|
||||||
|
r->own_lock = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
_bufferevent_decref_and_unlock(struct bufferevent *bufev)
|
_bufferevent_decref_and_unlock(struct bufferevent *bufev)
|
||||||
{
|
{
|
||||||
struct bufferevent_private *bufev_private =
|
struct bufferevent_private *bufev_private =
|
||||||
EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
|
EVUTIL_UPCAST(bufev, struct bufferevent_private, bev);
|
||||||
struct bufferevent *underlying;
|
struct bufferevent *underlying;
|
||||||
|
|
||||||
|
EVUTIL_ASSERT(bufev_private->refcnt > 0);
|
||||||
|
|
||||||
if (--bufev_private->refcnt) {
|
if (--bufev_private->refcnt) {
|
||||||
BEV_UNLOCK(bufev);
|
BEV_UNLOCK(bufev);
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
underlying = bufferevent_get_underlying(bufev);
|
underlying = bufferevent_get_underlying(bufev);
|
||||||
@ -550,21 +570,27 @@ _bufferevent_decref_and_unlock(struct bufferevent *bufev)
|
|||||||
/* Free the actual allocated memory. */
|
/* Free the actual allocated memory. */
|
||||||
mm_free(bufev - bufev->be_ops->mem_offset);
|
mm_free(bufev - bufev->be_ops->mem_offset);
|
||||||
|
|
||||||
/* release the reference to underlying now that we no longer need
|
/* Release the reference to underlying now that we no longer need the
|
||||||
* the reference to it. This is mainly in case our lock is shared
|
* reference to it. We wait this long mainly in case our lock is
|
||||||
* with underlying.
|
* shared with underlying.
|
||||||
|
*
|
||||||
|
* The 'destruct' function will also drop a reference to underlying
|
||||||
|
* if BEV_OPT_CLOSE_ON_FREE is set.
|
||||||
|
*
|
||||||
* XXX Should we/can we just refcount evbuffer/bufferevent locks?
|
* XXX Should we/can we just refcount evbuffer/bufferevent locks?
|
||||||
* It would probably save us some headaches.
|
* It would probably save us some headaches.
|
||||||
*/
|
*/
|
||||||
if (underlying)
|
if (underlying)
|
||||||
bufferevent_decref(underlying);
|
bufferevent_decref(underlying);
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
int
|
||||||
bufferevent_decref(struct bufferevent *bufev)
|
bufferevent_decref(struct bufferevent *bufev)
|
||||||
{
|
{
|
||||||
BEV_LOCK(bufev);
|
BEV_LOCK(bufev);
|
||||||
_bufferevent_decref_and_unlock(bufev);
|
return _bufferevent_decref_and_unlock(bufev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -187,6 +187,7 @@ bufferevent_filter_new(struct bufferevent *underlying,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bufev_f->underlying = underlying;
|
bufev_f->underlying = underlying;
|
||||||
|
|
||||||
bufev_f->process_in = input_filter;
|
bufev_f->process_in = input_filter;
|
||||||
bufev_f->process_out = output_filter;
|
bufev_f->process_out = output_filter;
|
||||||
bufev_f->free_context = free_context;
|
bufev_f->free_context = free_context;
|
||||||
@ -212,8 +213,19 @@ be_filter_destruct(struct bufferevent *bev)
|
|||||||
if (bevf->free_context)
|
if (bevf->free_context)
|
||||||
bevf->free_context(bevf->context);
|
bevf->free_context(bevf->context);
|
||||||
|
|
||||||
if (bevf->bev.options & BEV_OPT_CLOSE_ON_FREE)
|
if (bevf->bev.options & BEV_OPT_CLOSE_ON_FREE) {
|
||||||
bufferevent_free(bevf->underlying);
|
/* Yes, there is also a decref in bufferevent_decref.
|
||||||
|
* That decref corresponds to the incref when we set
|
||||||
|
* underlying for the first time. This decref is an
|
||||||
|
* extra one to remove the last reference.
|
||||||
|
*/
|
||||||
|
if (BEV_UPCAST(bevf->underlying)->refcnt < 2) {
|
||||||
|
event_warnx("BEV_OPT_CLOSE_ON_FREE set on an "
|
||||||
|
"bufferevent with too few references");
|
||||||
|
} else {
|
||||||
|
bufferevent_free(bevf->underlying);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_bufferevent_del_generic_timeout_cbs(bev);
|
_bufferevent_del_generic_timeout_cbs(bev);
|
||||||
}
|
}
|
||||||
|
@ -1030,8 +1030,13 @@ be_openssl_destruct(struct bufferevent *bev)
|
|||||||
|
|
||||||
if (bev_ssl->bev.options & BEV_OPT_CLOSE_ON_FREE) {
|
if (bev_ssl->bev.options & BEV_OPT_CLOSE_ON_FREE) {
|
||||||
if (bev_ssl->underlying) {
|
if (bev_ssl->underlying) {
|
||||||
bufferevent_free(bev_ssl->underlying);
|
if (BEV_UPCAST(bev_ssl->underlying)->refcnt < 2) {
|
||||||
bev_ssl->underlying = NULL;
|
event_warnx("BEV_OPT_CLOSE_ON_FREE set on an "
|
||||||
|
"bufferevent with too few references");
|
||||||
|
} else {
|
||||||
|
bufferevent_free(bev_ssl->underlying);
|
||||||
|
bev_ssl->underlying = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SSL_free(bev_ssl->ssl);
|
SSL_free(bev_ssl->ssl);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user