From e9ebef83a068ca938887624042461d614070071e Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 9 Apr 2013 21:14:52 -0400 Subject: [PATCH] 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. --- event.c | 80 ++++++++++++++++++++++++++++++------------ include/event2/event.h | 4 +++ 2 files changed, 61 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. */ diff --git a/include/event2/event.h b/include/event2/event.h index 3dcf440d..3dfce377 100644 --- a/include/event2/event.h +++ b/include/event2/event.h @@ -599,10 +599,14 @@ struct event_base *event_base_new_with_config(const struct event_config *); Note that this function will not close any fds or free any memory passed to event_new as the argument to callback. + @param eb an event_base to be freed */ void event_base_free(struct event_base *); +/** DOCUMENT */ +void event_base_free_nofinalize(struct event_base *); + /** @name Log severities */ /**@{*/ -- 2.50.1