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.
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);
}