]> granicus.if.org Git - libevent/commitdiff
evwatch: fix race condition
authorDan Rosen <mergeconflict@google.com>
Wed, 17 Apr 2019 19:44:59 +0000 (15:44 -0400)
committerDan Rosen <mergeconflict@google.com>
Wed, 17 Apr 2019 19:52:05 +0000 (15:52 -0400)
There was a race between event_base_loop and evwatch_new (adding a
prepare/check watcher while iterating over the watcher list). Only
release the mutex immediately before invoking each watcher callback,
and reacquire it immediately afterwards (same as is done for normal
event handlers).

event.c

diff --git a/event.c b/event.c
index 34f28ba25bb3d1b00c3d958ff358d0841a753571..a82d50943dc709d0e4624a7693426947679611b0 100644 (file)
--- a/event.c
+++ b/event.c
@@ -2006,11 +2006,12 @@ event_base_loop(struct event_base *base, int flags)
                event_queue_make_later_events_active(base);
 
                /* Invoke prepare watchers before polling for events */
-               EVBASE_RELEASE_LOCK(base, th_base_lock);
                prepare_info.timeout = tv_p;
-               TAILQ_FOREACH(watcher, &base->watchers[EVWATCH_PREPARE], next)
+               TAILQ_FOREACH(watcher, &base->watchers[EVWATCH_PREPARE], next) {
+                       EVBASE_RELEASE_LOCK(base, th_base_lock);
                        (*watcher->callback.prepare)(watcher, &prepare_info, watcher->arg);
-               EVBASE_ACQUIRE_LOCK(base, th_base_lock);
+                       EVBASE_ACQUIRE_LOCK(base, th_base_lock);
+               }
 
                clear_time_cache(base);
 
@@ -2027,10 +2028,11 @@ event_base_loop(struct event_base *base, int flags)
 
                /* Invoke check watchers after polling for events, and before
                 * processing them */
-               EVBASE_RELEASE_LOCK(base, th_base_lock);
-               TAILQ_FOREACH(watcher, &base->watchers[EVWATCH_CHECK], next)
+               TAILQ_FOREACH(watcher, &base->watchers[EVWATCH_CHECK], next) {
+                       EVBASE_RELEASE_LOCK(base, th_base_lock);
                        (*watcher->callback.check)(watcher, &check_info, watcher->arg);
-               EVBASE_ACQUIRE_LOCK(base, th_base_lock);
+                       EVBASE_ACQUIRE_LOCK(base, th_base_lock);
+               }
 
                timeout_process(base);