Free dangling event_once objects on event_base_free()

This patch makes us keep event_once objects in a doubly linked list
so we can free any once that haven't triggered when we call
event_base_free().
This commit is contained in:
Nick Mathewson 2011-07-15 11:10:54 -04:00
parent 1f5a48d1d0
commit c17dd59191
3 changed files with 44 additions and 18 deletions

View File

@ -179,6 +179,15 @@ extern int event_debug_mode_on_;
TAILQ_HEAD(evcallback_list, event_callback);
/* Sets up an event for processing once */
struct event_once {
LIST_ENTRY(event_once) next_once;
struct event ev;
void (*cb)(evutil_socket_t, short, void *);
void *arg;
};
struct event_base {
/** Function pointers and other data to describe this event_base's
* backend. */
@ -310,6 +319,10 @@ struct event_base {
/** Saved seed for weak random number generator. Some backends use
* this to produce fairness among sockets. Protected by th_base_lock. */
struct evutil_weakrand_state weakrand_seed;
/** List of event_onces that have not yet fired. */
LIST_HEAD(once_event_list, event_once) once_events;
};
struct event_config_entry {

41
event.c
View File

@ -805,6 +805,12 @@ event_base_free(struct event_base *base)
event_debug(("%s: %d events were still set in base",
__func__, n_deleted));
while (LIST_FIRST(&base->once_events)) {
struct event_once *eonce = LIST_FIRST(&base->once_events);
LIST_REMOVE(eonce, next_once);
mm_free(eonce);
}
if (base->evsel != NULL && base->evsel->dealloc != NULL)
base->evsel->dealloc(base);
@ -1771,22 +1777,18 @@ done:
return (retval);
}
/* Sets up an event for processing once */
struct event_once {
struct event ev;
void (*cb)(evutil_socket_t, short, void *);
void *arg;
};
/* One-time callback to implement event_base_once: invokes the user callback,
* then deletes the allocated storage */
static void
event_once_cb(evutil_socket_t fd, short events, void *arg)
{
struct event_once *eonce = arg;
struct event_base *base = eonce->ev.ev_base;
(*eonce->cb)(fd, events, eonce->arg);
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
LIST_REMOVE(eonce, next_once);
EVBASE_RELEASE_LOCK(base, th_base_lock);
event_debug_unassign(&eonce->ev);
mm_free(eonce);
}
@ -1808,6 +1810,7 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events,
{
struct event_once *eonce;
int res = 0;
int activate = 0;
/* We cannot support signals that just fire once, or persistent
* events. */
@ -1828,8 +1831,7 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events,
* don't put it on the timeout queue. This is one
* idiom for scheduling a callback, so let's make
* it fast (and order-preserving). */
event_active(&eonce->ev, EV_TIMEOUT, 1);
return 0;
activate = 1;
}
} else if (events & (EV_READ|EV_WRITE)) {
events &= EV_READ|EV_WRITE;
@ -1841,11 +1843,20 @@ event_base_once(struct event_base *base, evutil_socket_t fd, short events,
return (-1);
}
if (res == 0)
res = event_add(&eonce->ev, tv);
if (res != 0) {
mm_free(eonce);
return (res);
if (res == 0) {
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
if (activate)
event_active_nolock_(&eonce->ev, EV_TIMEOUT, 1);
else
res = event_add_nolock_(&eonce->ev, tv, 0);
if (res != 0) {
mm_free(eonce);
return (res);
} else {
LIST_INSERT_HEAD(&base->once_events, eonce, next_once);
}
EVBASE_RELEASE_LOCK(base, th_base_lock);
}
return (0);

View File

@ -1005,9 +1005,11 @@ void event_free(struct event *);
schedules a callback to be called exactly once, and does not require the
caller to prepare an event structure.
Note that in Libevent 2.0 and earlier, if the event is never triggered,
the internal memory used to hold it will never be freed. This may be
fixed in a later version of Libevent.
Note that in Libevent 2.0 and earlier, if the event is never triggered, the
internal memory used to hold it will never be freed. In Libevent 2.1,
the internal memory will get freed by event_base_free() if the event
is never triggered. The 'arg' value, however, will not get freed in either
case--you'll need to free that on your own if you want it to go away.
@param base an event_base
@param fd a file descriptor to monitor, or -1 for no fd.