From c17dd5919152a7870dd784fa8814f6197898998e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 15 Jul 2011 11:10:54 -0400 Subject: [PATCH] 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(). --- event-internal.h | 13 +++++++++++++ event.c | 41 ++++++++++++++++++++++++++--------------- include/event2/event.h | 8 +++++--- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/event-internal.h b/event-internal.h index 8da0edf8..36ece843 100644 --- a/event-internal.h +++ b/event-internal.h @@ -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 { diff --git a/event.c b/event.c index 6e6e64dd..8963e22e 100644 --- a/event.c +++ b/event.c @@ -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); diff --git a/include/event2/event.h b/include/event2/event.h index 36a42df1..1c32f6e0 100644 --- a/include/event2/event.h +++ b/include/event2/event.h @@ -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.