]> granicus.if.org Git - libevent/commitdiff
Free event queues even for recursive finalizers
authorAzat Khuzhin <a3at.mail@gmail.com>
Fri, 30 Oct 2015 10:34:30 +0000 (13:34 +0300)
committerAzat Khuzhin <a3at.mail@gmail.com>
Fri, 30 Oct 2015 11:48:14 +0000 (14:48 +0300)
For finalizers we can register yet another finalizer out from finalizer, and
iff finalizer will be in active_later_queue we can add finalizer to
activequeues, and we will have events in activequeues after event_base_free()
returns, which is not what we want (we even have an assertion for this).

A simple case is bufferevent with underlying (i.e. filters) in inactive queue.

Fixes: regress bufferevent/bufferevent_socket_filter_inactive
event.c

diff --git a/event.c b/event.c
index 7125adb9f80ac4307ef4af1e172fdaf3980ee8f9..def17665f972cd802ba4cab26bfab06cb0de9151 100644 (file)
--- a/event.c
+++ b/event.c
@@ -769,6 +769,29 @@ event_base_cancel_single_callback_(struct event_base *base,
        return result;
 }
 
+static int event_base_free_queues_(struct event_base *base, int run_finalizers)
+{
+       int deleted = 0, i;
+
+       for (i = 0; i < base->nactivequeues; ++i) {
+               struct event_callback *evcb, *next;
+               for (evcb = TAILQ_FIRST(&base->activequeues[i]); evcb; ) {
+                       next = TAILQ_NEXT(evcb, evcb_active_next);
+                       deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers);
+                       evcb = next;
+               }
+       }
+
+       {
+               struct event_callback *evcb;
+               while ((evcb = TAILQ_FIRST(&base->active_later_queue))) {
+                       deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers);
+               }
+       }
+
+       return deleted;
+}
+
 static void
 event_base_free_(struct event_base *base, int run_finalizers)
 {
@@ -829,22 +852,22 @@ event_base_free_(struct event_base *base, int run_finalizers)
        if (base->common_timeout_queues)
                mm_free(base->common_timeout_queues);
 
-       for (i = 0; i < base->nactivequeues; ++i) {
-               struct event_callback *evcb, *next;
-               for (evcb = TAILQ_FIRST(&base->activequeues[i]); evcb; ) {
-                       next = TAILQ_NEXT(evcb, evcb_active_next);
-                       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))) {
-                       n_deleted += event_base_cancel_single_callback_(base, evcb, run_finalizers);
+       for (;;) {
+               /* For finalizers we can register yet another finalizer out from
+                * finalizer, and iff finalizer will be in active_later_queue we can
+                * add finalizer to activequeues, and we will have events in
+                * activequeues after this function returns, which is not what we want
+                * (we even have an assertion for this).
+                *
+                * A simple case is bufferevent with underlying (i.e. filters).
+                */
+               int i = event_base_free_queues_(base, run_finalizers);
+               if (!i) {
+                       break;
                }
+               n_deleted += i;
        }
 
-
        if (n_deleted)
                event_debug(("%s: %d events were still set in base",
                        __func__, n_deleted));