/** Set if we should terminate the loop immediately */
int event_break;
+ /** Set if we're running the event_base_loop function, to prevent
+ * reentrant invocation. */
+ int running_loop;
+
/* Active event management. */
/** An array of nactivequeues queues for active events (ones that
* have triggered, and whose callbacks need to be called). Low
* as we invoke user callbacks. */
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);
if (base->sig.ev_signal_added)
done:
clear_time_cache(base);
+ base->running_loop = 0;
EVBASE_RELEASE_LOCK(base, th_base_lock);
;
}
+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
test_event_base_new(void *ptr)
{
BASIC(manipulate_active_events, TT_FORK|TT_NEED_BASE),
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 */
LEGACY(persistent_timeout, TT_FORK|TT_NEED_BASE),