summaryrefslogtreecommitdiff
path: root/event.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2013-04-09 21:14:52 -0400
committerNick Mathewson <nickm@torproject.org>2013-04-26 12:18:07 -0400
commite9ebef83a068ca938887624042461d614070071e (patch)
treee013e6c896a3efee5dffefc493a70efa7d289b81 /event.c
parent02fbf68770d3dcb864c867124e159b3680036206 (diff)
downloadlibevent-e9ebef83a068ca938887624042461d614070071e.tar.gz
Always run pending finalizers when event_base_free() is called
There was actually a bug in the original version of this: it tried to run the finalizers after (potentially) setting current_base to NULL; but those finalizers could themselves (potentially) be invoking stuff that needed to know about the current event_base. So the right time to do it is _before_ clearing current_base.
Diffstat (limited to 'event.c')
-rw-r--r--event.c80
1 files changed, 57 insertions, 23 deletions
diff --git a/event.c b/event.c
index bad277ce..0722fad5 100644
--- a/event.c
+++ b/event.c
@@ -706,8 +706,46 @@ event_base_stop_iocp_(struct event_base *base)
#endif
}
-void
-event_base_free(struct event_base *base)
+static int
+event_base_cancel_single_callback_(struct event_base *base,
+ struct event_callback *evcb,
+ int run_finalizers)
+{
+ int result = 0;
+
+ if (evcb->evcb_flags & EVLIST_INIT) {
+ struct event *ev = event_callback_to_event(evcb);
+ if (!(ev->ev_flags & EVLIST_INTERNAL)) {
+ event_del_(ev, EVENT_DEL_EVEN_IF_FINALIZING);
+ result = 1;
+ }
+ } else {
+ event_callback_cancel_nolock_(base, evcb, 1);
+ result = 1;
+ }
+
+ if (run_finalizers && (evcb->evcb_flags & EVLIST_FINALIZING)) {
+ switch (evcb->evcb_closure) {
+ case EV_CLOSURE_EVENT_FINALIZE:
+ case EV_CLOSURE_EVENT_FINALIZE_FREE: {
+ struct event *ev = event_callback_to_event(evcb);
+ ev->ev_evcallback.evcb_cb_union.evcb_evfinalize(ev, ev->ev_arg);
+ if (evcb->evcb_closure == EV_CLOSURE_EVENT_FINALIZE_FREE)
+ mm_free(ev);
+ break;
+ }
+ case EV_CLOSURE_CB_FINALIZE:
+ evcb->evcb_cb_union.evcb_cbfinalize(evcb, evcb->evcb_arg);
+ break;
+ default:
+ break;
+ }
+ }
+ return result;
+}
+
+static void
+event_base_free_(struct event_base *base, int run_finalizers)
{
int i, n_deleted=0;
struct event *ev;
@@ -718,9 +756,6 @@ event_base_free(struct event_base *base)
* made it with event_init and forgot to hold a reference to it. */
if (base == NULL && current_base)
base = current_base;
- /* If we're freeing current_base, there won't be a current_base. */
- if (base == current_base)
- current_base = NULL;
/* Don't actually free NULL. */
if (base == NULL) {
event_warnx("%s: no base to free", __func__);
@@ -773,30 +808,14 @@ event_base_free(struct event_base *base)
struct event_callback *evcb, *next;
for (evcb = TAILQ_FIRST(&base->activequeues[i]); evcb; ) {
next = TAILQ_NEXT(evcb, evcb_active_next);
- if (evcb->evcb_flags & EVLIST_INIT) {
- ev = event_callback_to_event(evcb);
- if (!(ev->ev_flags & EVLIST_INTERNAL)) {
- event_del_(ev, EVENT_DEL_EVEN_IF_FINALIZING);
- ++n_deleted;
- }
- } else {
- event_callback_cancel_nolock_(base, evcb, 1);
- ++n_deleted;
- }
+ n_deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers);
evcb = next;
}
}
{
struct event_callback *evcb;
while ((evcb = TAILQ_FIRST(&base->active_later_queue))) {
- if (evcb->evcb_flags & EVLIST_INIT) {
- ev = event_callback_to_event(evcb);
- event_del(ev);
- ++n_deleted;
- } else {
- event_callback_cancel_nolock_(base, evcb, 1);
- ++n_deleted;
- }
+ n_deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers);
}
}
@@ -829,9 +848,24 @@ event_base_free(struct event_base *base)
EVTHREAD_FREE_LOCK(base->th_base_lock, 0);
EVTHREAD_FREE_COND(base->current_event_cond);
+ /* If we're freeing current_base, there won't be a current_base. */
+ if (base == current_base)
+ current_base = NULL;
mm_free(base);
}
+void
+event_base_free_nofinalize(struct event_base *base)
+{
+ event_base_free_(base, 0);
+}
+
+void
+event_base_free(struct event_base *base)
+{
+ event_base_free_(base, 1);
+}
+
/* Fake eventop; used to disable the backend temporarily inside event_reinit
* so that we can call event_del() on an event without telling the backend.
*/