Fix notifying the base in a different thread after removing active timer event

The base should be notified in case of timer removal if that was the
minimal timer in the base.

Reported-by: @moihn (who is also provided the reproducer on which this
test is based on)
Fixes: https://github.com/libevent/libevent/issues/1727
This commit is contained in:
Azat Khuzhin 2024-10-20 23:52:33 +02:00
parent 970a258bc6
commit b219226982
2 changed files with 56 additions and 7 deletions

10
event.c
View File

@ -2926,13 +2926,9 @@ event_del_nolock_(struct event *ev, int blocking)
}
if (ev->ev_flags & EVLIST_TIMEOUT) {
/* NOTE: We never need to notify the main thread because of a
* deleted timeout event: all that could happen if we don't is
* that the dispatch loop might wake up too early. But the
* point of notifying the main thread _is_ to wake up the
* dispatch loop early anyway, so we wouldn't gain anything by
* doing it.
*/
/* Notify the base if this was the minimal timeout */
if (min_heap_top_(&base->timeheap) == ev)
notify = 1;
event_queue_remove_timeout(base, ev);
}

View File

@ -2063,6 +2063,58 @@ end:
event_free(ev[4]);
}
/* del_timeout_notify */
#ifndef EVENT__DISABLE_THREAD_SUPPORT
static THREAD_FN
event_base_dispatch_threadcb(void *arg)
{
event_base_dispatch(arg);
THREAD_RETURN();
}
/* Regression test for the case when removing active event with EV_TIMEOUT does
* not notifies the base properly like it should */
static void
test_del_timeout_notify(void *ptr)
{
struct basic_test_data *data = ptr;
struct event_base *base = data->base;
struct event *ev;
struct timeval start_tv, now_tv;
THREAD_T thread;
ev = event_new(base, -1, EV_PERSIST, null_cb, NULL);
{
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
event_add(ev, &tv);
}
THREAD_START(thread, event_base_dispatch_threadcb, base);
/* FIXME: let's consider that 1 second is enough for the OS to spawn the
* thread and enter event loop */
{
struct timeval delay = { 1, 0 };
evutil_usleep_(&delay);
}
evutil_gettimeofday(&start_tv, NULL);
event_del(ev);
THREAD_JOIN(thread);
evutil_gettimeofday(&now_tv, NULL);
/* Let's consider that 1 second is enough to notify the base thread */
tt_int_op(timeval_msec_diff(&start_tv, &now_tv), <, 1000);
event_base_assert_ok_(base);
end:
event_free(ev);
}
#endif
static void
test_event_base_new(void *ptr)
{
@ -3664,6 +3716,7 @@ struct testcase_t main_testcases[] = {
#ifndef EVENT__DISABLE_THREAD_SUPPORT
LEGACY(del_wait, TT_ISOLATED|TT_NEED_THREADS|TT_RETRIABLE),
LEGACY(del_notify, TT_ISOLATED|TT_NEED_THREADS),
BASIC(del_timeout_notify, TT_NEED_THREADS|TT_FORK|TT_NEED_BASE),
#endif
END_OF_TESTCASES