Fix base unlocking in event_del() if event_base_set() runned in another thread

Image next situation:
  T1:                                        T2:
   event_del_()
     lock the event.ev_base.th_base_lock
     event_del_nolock_()                     event_set_base()
     unlock the event.ev_base.th_base_lock

In this case we will unlock the wrong base after event_del_nolock_()
returns, and deadlock is likely to happens, since event_base_set() do
not check any mutexes (due to it is possible to do this only if event is
not inserted anywhere).

So event_del_() has to cache the base before removing the event, and
cached base.th_base_lock after.

(cherry picked from commit 08a0d366073eacf800027725891c2f668f2f0144)
This commit is contained in:
Azat Khuzhin 2018-02-27 21:12:14 +03:00 committed by Azat Khuzhin
parent f45543e2f8
commit 4f0f40e31e
No known key found for this signature in database
GPG Key ID: B86086848EF8686D

View File

@ -2749,17 +2749,16 @@ static int
event_del_(struct event *ev, int blocking)
{
int res;
struct event_base *base = ev->ev_base;
if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
if (EVUTIL_FAILURE_CHECK(!base)) {
event_warnx("%s: event has no event_base set.", __func__);
return -1;
}
EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
res = event_del_nolock_(ev, blocking);
EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);
EVBASE_RELEASE_LOCK(base, th_base_lock);
return (res);
}