From 1c23e219527b8342639f49e0884e209f7232502a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sat, 17 Nov 2007 22:21:42 +0000 Subject: [PATCH] r14931@tombo: nickm | 2007-11-17 17:21:09 -0500 Patch from Scott Lamb: Implement event_{base_}loopbreak. Includes documentation and tests. From sf.net Feature Request 1826546. svn:r535 --- ChangeLog | 1 + event-internal.h | 1 + event.3 | 17 ++++++++++++++++- event.c | 26 +++++++++++++++++++++++++- event.h | 29 +++++++++++++++++++++++++++++ test/regress.c | 37 +++++++++++++++++++++++++++++++++++++ 6 files changed, 109 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7429c476..086a1df4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ Changes in current version: o provide event_base_new() that does not set the current_base global o bufferevent_write now uses a const source argument; report from Charles Kerr o improve documentation on event_base_loopexit; patch from Scott Lamb + 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: o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. diff --git a/event-internal.h b/event-internal.h index de9c7879..56ef877b 100644 --- a/event-internal.h +++ b/event-internal.h @@ -42,6 +42,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 3e6295cb..d0e8d670 100644 --- a/event.c +++ b/event.c @@ -330,7 +330,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; } } @@ -374,6 +374,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 @@ -405,6 +424,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 4685e936..e2673086 100644 --- a/event.h +++ b/event.h @@ -407,6 +407,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 a153da48..274f62cb 100644 --- a/test/regress.c +++ b/test/regress.c @@ -699,6 +699,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) { @@ -1201,6 +1237,7 @@ main (int argc, char **argv) test_immediatesignal(); #endif test_loopexit(); + test_loopbreak(); test_multiple_events_for_same_fd(); -- 2.40.0