mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-12 13:58:58 -04:00
Detect and refuse reentrant event_base_loop() calls
Calling event_base_loop on a base from inside a callback invoked by that same base, or from two threads at once, has long been a way to get exceedingly hard-to-diagnose errors. This patch adds code to detect such reentrant invocatinos, and exit quickly with a warning that should explain what went wrong.
This commit is contained in:
parent
fb366c1d88
commit
b557b175c0
@ -190,6 +190,10 @@ struct event_base {
|
|||||||
/** Set if we should terminate the loop immediately */
|
/** Set if we should terminate the loop immediately */
|
||||||
int event_break;
|
int event_break;
|
||||||
|
|
||||||
|
/** Set if we're running the event_base_loop function, to prevent
|
||||||
|
* reentrant invocation. */
|
||||||
|
int running_loop;
|
||||||
|
|
||||||
/* Active event management. */
|
/* Active event management. */
|
||||||
/** An array of nactivequeues queues for active events (ones that
|
/** An array of nactivequeues queues for active events (ones that
|
||||||
* have triggered, and whose callbacks need to be called). Low
|
* have triggered, and whose callbacks need to be called). Low
|
||||||
|
10
event.c
10
event.c
@ -1399,6 +1399,15 @@ event_base_loop(struct event_base *base, int flags)
|
|||||||
* as we invoke user callbacks. */
|
* as we invoke user callbacks. */
|
||||||
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
|
EVBASE_ACQUIRE_LOCK(base, th_base_lock);
|
||||||
|
|
||||||
|
if (base->running_loop) {
|
||||||
|
event_warn("%s: reentrant invocation. Only one event_base_loop"
|
||||||
|
" can run on each event_base at once.", __func__);
|
||||||
|
EVBASE_RELEASE_LOCK(base, th_base_lock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
base->running_loop = 1;
|
||||||
|
|
||||||
clear_time_cache(base);
|
clear_time_cache(base);
|
||||||
|
|
||||||
if (base->sig.ev_signal_added)
|
if (base->sig.ev_signal_added)
|
||||||
@ -1470,6 +1479,7 @@ event_base_loop(struct event_base *base, int flags)
|
|||||||
|
|
||||||
done:
|
done:
|
||||||
clear_time_cache(base);
|
clear_time_cache(base);
|
||||||
|
base->running_loop = 0;
|
||||||
|
|
||||||
EVBASE_RELEASE_LOCK(base, th_base_lock);
|
EVBASE_RELEASE_LOCK(base, th_base_lock);
|
||||||
|
|
||||||
|
@ -1156,6 +1156,40 @@ end:
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int reentrant_cb_run = 0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
bad_reentrant_run_loop_cb(evutil_socket_t fd, short what, void *ptr)
|
||||||
|
{
|
||||||
|
struct event_base *base = ptr;
|
||||||
|
int r;
|
||||||
|
reentrant_cb_run = 1;
|
||||||
|
/* This reentrant call to event_base_loop should be detected and
|
||||||
|
* should fail */
|
||||||
|
r = event_base_loop(base, 0);
|
||||||
|
tt_int_op(r, ==, -1);
|
||||||
|
end:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_bad_reentrant(void *ptr)
|
||||||
|
{
|
||||||
|
struct basic_test_data *data = ptr;
|
||||||
|
struct event_base *base = data->base;
|
||||||
|
struct event ev;
|
||||||
|
int r;
|
||||||
|
event_assign(&ev, base, -1,
|
||||||
|
0, bad_reentrant_run_loop_cb, base);
|
||||||
|
|
||||||
|
event_active(&ev, EV_WRITE, 1);
|
||||||
|
r = event_base_loop(base, 0);
|
||||||
|
tt_int_op(r, ==, 1);
|
||||||
|
tt_int_op(reentrant_cb_run, ==, 1);
|
||||||
|
end:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_event_base_new(void *ptr)
|
test_event_base_new(void *ptr)
|
||||||
{
|
{
|
||||||
@ -2072,6 +2106,7 @@ struct testcase_t main_testcases[] = {
|
|||||||
BASIC(manipulate_active_events, TT_FORK|TT_NEED_BASE),
|
BASIC(manipulate_active_events, TT_FORK|TT_NEED_BASE),
|
||||||
|
|
||||||
BASIC(bad_assign, TT_FORK|TT_NEED_BASE|TT_NO_LOGS),
|
BASIC(bad_assign, TT_FORK|TT_NEED_BASE|TT_NO_LOGS),
|
||||||
|
BASIC(bad_reentrant, TT_FORK|TT_NEED_BASE|TT_NO_LOGS),
|
||||||
|
|
||||||
/* These are still using the old API */
|
/* These are still using the old API */
|
||||||
LEGACY(persistent_timeout, TT_FORK|TT_NEED_BASE),
|
LEGACY(persistent_timeout, TT_FORK|TT_NEED_BASE),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user