diff --git a/ChangeLog b/ChangeLog index 1c07c16e..8fd8b25d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,7 @@ Changes in current version: o small improvements to evhttp documentation o always generate Date and Content-Length headers for HTTP/1.1 replies o set the correct event base for HTTP close events + o New function, event_{base_}loopbreak. Like event_loopexit, it makes an event loop stop executing and return. Unlike event_loopexit, it keeps subsequent pending events from getting executed. Patch from Scott Lamb Changes in 1.4.0-beta: diff --git a/event-internal.h b/event-internal.h index fbffa69e..3594ed6f 100644 --- a/event-internal.h +++ b/event-internal.h @@ -54,6 +54,7 @@ struct event_base { int event_count_active; /* counts number of active events */ int event_gotterm; /* Set to terminate loop */ + int event_break; /* Set to terminate loop immediately */ /* active event management */ struct event_list **activequeues; diff --git a/event.3 b/event.3 index d5717f22..5b33ec64 100644 --- a/event.3 +++ b/event.3 @@ -34,10 +34,12 @@ .Nm event_dispatch , .Nm event_loop , .Nm event_loopexit , +.Nm event_loopbreak , .Nm event_set , .Nm event_base_dispatch , .Nm event_base_loop , .Nm event_base_loopexit , +.Nm event_base_loopbreak , .Nm event_base_set , .Nm event_base_free , .Nm event_add , @@ -93,6 +95,8 @@ .Fn "event_loop" "int flags" .Ft int .Fn "event_loopexit" "struct timeval *tv" +.Ft int +.Fn "event_loopbreak" "void" .Ft void .Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg" .Ft int @@ -102,6 +106,8 @@ .Ft int .Fn "event_base_loopexit" "struct event_base *base" "struct timeval *tv" .Ft int +.Fn "event_base_loopbreak" "struct event_base *base" +.Ft int .Fn "event_base_set" "struct event_base *base" "struct event *" .Ft void .Fn "event_base_free" "struct event_base *base" @@ -419,7 +425,16 @@ given timer expires will complete normally (handling all queued events) then exit without blocking for events again. Subsequent invocations of .Fn event_loop will proceed normally. -The parameter indicates the time after which the loop should terminate. +The +.Nm event_loopbreak +function exits from the event loop immediately. +.Fn event_loop +will abort after the next event is completed; +.Fn event_loopbreak +is typically invoked from this event's callback. This behavior is analogous +to the "break;" statement. Subsequent invocations of +.Fn event_loop +will proceed normally. .Pp It is the responsibility of the caller to provide these functions with pre-allocated event structures. diff --git a/event.c b/event.c index eb4e1f88..98637b69 100644 --- a/event.c +++ b/event.c @@ -358,7 +358,7 @@ event_process_active(struct event_base *base) ncalls--; ev->ev_ncalls = ncalls; (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); - if (event_gotsig) + if (event_gotsig || base->event_break) return; } } @@ -402,6 +402,25 @@ event_base_loopexit(struct event_base *event_base, struct timeval *tv) event_base, tv)); } +/* not thread safe */ +int +event_loopbreak(void) +{ + return (event_base_loopbreak(current_base)); +} + +int +event_base_loopbreak(struct event_base *event_base) +{ + if (event_base == NULL) + return (-1); + + event_base->event_break = 1; + return (0); +} + + + /* not thread safe */ int @@ -433,6 +452,11 @@ event_base_loop(struct event_base *base, int flags) break; } + if (base->event_break) { + base->event_break = 0; + break; + } + /* You cannot use this interface for multi-threaded apps */ while (event_gotsig) { event_gotsig = 0; diff --git a/event.h b/event.h index 283041c4..b9d74976 100644 --- a/event.h +++ b/event.h @@ -408,6 +408,35 @@ int event_loopexit(struct timeval *); */ int event_base_loopexit(struct event_base *, struct timeval *); +/** + Abort the active event_loop() immediately. + + event_loop() will abort the loop after the next event is completed; + event_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "break;" statement. + + Subsequent invocations of event_loop() will proceed normally. + + @return 0 if successful, or -1 if an error occurred + @see event_base_loopbreak(), event_loopexit() + */ +int event_loopbreak(void); + +/** + Abort the active event_base_loop() immediately. + + event_base_loop() will abort the loop after the next event is completed; + event_base_loopbreak() is typically invoked from this event's callback. + This behavior is analogous to the "break;" statement. + + Subsequent invocations of event_loop() will proceed normally. + + @param eb the event_base structure returned by event_init() + @return 0 if successful, or -1 if an error occurred + @see event_base_loopexit + */ +int event_base_loopbreak(struct event_base *); + /** Add a timer event. diff --git a/test/regress.c b/test/regress.c index bb6425b0..82820ddc 100644 --- a/test/regress.c +++ b/test/regress.c @@ -769,6 +769,42 @@ test_loopexit(void) cleanup_test(); } +static void +break_cb(int fd, short events, void *arg) +{ + test_ok = 1; + event_loopbreak(); +} + +static void +fail_cb(int fd, short events, void *arg) +{ + test_ok = 0; +} + +void +test_loopbreak(void) +{ + struct event ev1, ev2; + struct timeval tv; + + setup_test("Loop break: "); + + tv.tv_sec = 0; + tv.tv_usec = 0; + evtimer_set(&ev1, break_cb, NULL); + evtimer_add(&ev1, &tv); + evtimer_set(&ev2, fail_cb, NULL); + evtimer_add(&ev2, &tv); + + event_dispatch(); + + evtimer_del(&ev1); + evtimer_del(&ev2); + + cleanup_test(); +} + void test_evbuffer(void) { @@ -1275,6 +1311,7 @@ main (int argc, char **argv) test_immediatesignal(); #endif test_loopexit(); + test_loopbreak(); test_multiple_events_for_same_fd();