From 084e68f3f2b005966d9aba6eb755c845e4d875d9 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Dec 2010 21:53:08 -0500 Subject: [PATCH] New EVLOOP_NO_EXIT_ON_EMPTY option to keep looping even when no events are pending This can be useful if you want to start an event loop and then add or remove events to it from another thread. --- event.c | 3 +- include/event2/event.h | 5 +++ test/regress_thread.c | 70 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/event.c b/event.c index f8093286..d1fae9c1 100644 --- a/event.c +++ b/event.c @@ -1636,7 +1636,8 @@ event_base_loop(struct event_base *base, int flags) } /* If we have no events, we just exit */ - if (!event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) { + if (0==(flags&EVLOOP_NO_EXIT_ON_EMPTY) && + !event_haveevents(base) && !N_ACTIVE_CALLBACKS(base)) { event_debug(("%s: no events registered.", __func__)); retval = 1; goto done; diff --git a/include/event2/event.h b/include/event2/event.h index b86bcc0c..580cde15 100644 --- a/include/event2/event.h +++ b/include/event2/event.h @@ -681,6 +681,11 @@ int event_base_set(struct event_base *, struct event *); /** Do not block: see which events are ready now, run the callbacks * of the highest-priority ones, then exit. */ #define EVLOOP_NONBLOCK 0x02 +/** Do not exit the loop because we have no pending events. Instead, keep + * running until event_base_loopexit() or event_base_loopbreak() makes us + * stop. + */ +#define EVLOOP_NO_EXIT_ON_EMPTY 0x04 /**@}*/ /** diff --git a/test/regress_thread.c b/test/regress_thread.c index d08edf36..398b1fcf 100644 --- a/test/regress_thread.c +++ b/test/regress_thread.c @@ -493,6 +493,75 @@ end: THREAD_JOIN(load_threads[i]); } +static struct event time_events[5]; +static struct timeval times[5]; +static struct event_base *exit_base = NULL; +static void +note_time_cb(evutil_socket_t fd, short what, void *arg) +{ + evutil_gettimeofday(arg, NULL); + if (arg == ×[4]) { + event_base_loopbreak(exit_base); + } +} +static THREAD_FN +register_events_subthread(void *arg) +{ + struct timeval tv = {0,0}; + SLEEP_MS(100); + event_active(&time_events[0], EV_TIMEOUT, 1); + SLEEP_MS(100); + event_active(&time_events[1], EV_TIMEOUT, 1); + SLEEP_MS(100); + tv.tv_usec = 100*1000; + event_add(&time_events[2], &tv); + tv.tv_usec = 150*1000; + event_add(&time_events[3], &tv); + SLEEP_MS(200); + event_active(&time_events[4], EV_TIMEOUT, 1); + + THREAD_RETURN(); +} + +static void +thread_no_events(void *arg) +{ + THREAD_T thread; + struct basic_test_data *data = arg; + struct timeval starttime, endtime; + int i; + exit_base = data->base; + + memset(times,0,sizeof(times)); + for (i=0;i<5;++i) { + event_assign(&time_events[i], data->base, + -1, 0, note_time_cb, ×[i]); + } + + evutil_gettimeofday(&starttime, NULL); + THREAD_START(thread, register_events_subthread, data->base); + event_base_loop(data->base, EVLOOP_NO_EXIT_ON_EMPTY); + evutil_gettimeofday(&endtime, NULL); + tt_assert(event_base_got_break(data->base)); + THREAD_JOIN(thread); + for (i=0; i<5; ++i) { + struct timeval diff; + double sec; + evutil_timersub(×[i], &starttime, &diff); + sec = diff.tv_sec + diff.tv_usec/1.0e6; + TT_BLATHER(("event %d at %.4f seconds", i, sec)); + } + test_timeval_diff_eq(&starttime, ×[0], 100); + test_timeval_diff_eq(&starttime, ×[1], 200); + test_timeval_diff_eq(&starttime, ×[2], 400); + test_timeval_diff_eq(&starttime, ×[3], 450); + test_timeval_diff_eq(&starttime, ×[4], 500); + test_timeval_diff_eq(&starttime, &endtime, 500); + +end: + ; +} + #define TEST(name) \ { #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, \ &basic_setup, NULL } @@ -506,6 +575,7 @@ struct testcase_t thread_testcases[] = { #endif TEST(conditions_simple), TEST(deferred_cb_skew), + TEST(no_events), END_OF_TESTCASES };