Avoid loosing previously active events in case of EV_TIMEOUT

Previously all the existing events was lost if the timeout had been
triggered on that event.

Fixes: #1530
This commit is contained in:
Azat Khuzhin 2024-10-27 16:41:12 +01:00 committed by Azat Khuzhin
parent 528fbed184
commit 342a0faa50
2 changed files with 57 additions and 5 deletions

19
event.c
View File

@ -1505,12 +1505,17 @@ common_timeout_callback(evutil_socket_t fd, short what, void *arg)
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
gettime(base, &now);
while (1) {
int was_active;
ev = TAILQ_FIRST(&ctl->events);
if (!ev || ev->ev_timeout.tv_sec > now.tv_sec ||
(ev->ev_timeout.tv_sec == now.tv_sec &&
(ev->ev_timeout.tv_usec&MICROSECONDS_MASK) > now.tv_usec))
break;
event_del_nolock_(ev, EVENT_DEL_NOBLOCK);
was_active = ev->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER);
if (!was_active)
event_del_nolock_(ev, EVENT_DEL_NOBLOCK);
else
event_queue_remove_timeout(base, ev);
event_active_nolock_(ev, EV_TIMEOUT, 1);
}
if (ev)
@ -3272,14 +3277,18 @@ timeout_process(struct event_base *base)
gettime(base, &now);
while ((ev = min_heap_top_(&base->timeheap))) {
int was_active = ev->ev_flags & (EVLIST_ACTIVE|EVLIST_ACTIVE_LATER);
if (evutil_timercmp(&ev->ev_timeout, &now, >))
break;
/* delete this event from the I/O queues */
event_del_nolock_(ev, EVENT_DEL_NOBLOCK);
if (!was_active)
event_del_nolock_(ev, EVENT_DEL_NOBLOCK);
else
event_queue_remove_timeout(base, ev);
event_debug(("timeout_process: event: %p, call %p",
(void *)ev, (void *)ev->ev_callback));
event_debug(("timeout_process: event: %p, call %p (was active: %i)",
(void *)ev, (void *)ev->ev_callback, was_active));
event_active_nolock_(ev, EV_TIMEOUT, 1);
}
}

View File

@ -2063,6 +2063,48 @@ end:
event_free(ev[4]);
}
static void
test_event_timeout_lost_cb(evutil_socket_t fd, short events, void *arg)
{
short *res_events = arg;
*res_events = events;
}
static void
test_event_timeout_lost(void *ptr)
{
struct basic_test_data *data = ptr;
struct event_base *base = data->base;
struct event *ev;
short res_events = 0;
event_base_assert_ok_(base);
ev = event_new(base, data->pair[0], EV_TIMEOUT|EV_READ, test_event_timeout_lost_cb, &res_events);
tt_assert(ev);
{
struct timeval timeout = { 0, 100 };
event_add(ev, &timeout);
}
event_active(ev, EV_READ, 1);
/* Ensure that timeout had been elapsed */
{
struct timeval delay = { 1, 0 };
evutil_usleep_(&delay);
}
event_base_assert_ok_(base);
event_base_loop(base, EVLOOP_ONCE|EVLOOP_NONBLOCK);
event_base_assert_ok_(base);
tt_int_op(res_events, ==, EV_READ|EV_TIMEOUT);
end:
event_free(ev);
}
/* del_timeout_notify */
#ifndef EVENT__DISABLE_THREAD_SUPPORT
static THREAD_FN
@ -3629,6 +3671,7 @@ struct testcase_t main_testcases[] = {
BASIC(bad_reentrant, TT_FORK|TT_NEED_BASE|TT_NO_LOGS),
BASIC(active_later, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR|TT_RETRIABLE),
BASIC(event_remove_timeout, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR),
BASIC(event_timeout_lost, TT_FORK|TT_NEED_BASE),
/* These are still using the old API */
LEGACY(persistent_timeout, TT_FORK|TT_NEED_BASE),