diff --git a/ChangeLog b/ChangeLog index 508e684f..14db3f54 100644 --- a/ChangeLog +++ b/ChangeLog @@ -139,7 +139,8 @@ Changes in current version: o Refactor internal notify-main-thread logic to prefer eventfd to pipe, then pipe to socketpair, and only use socketpairs as a last resort. o Try harder to pack all evbuffer reads into as few chains as possible, using readv/WSARecv as appropriate. o New evthread_use_windows_threads() and evthread_use_pthreads() functions to set up the evthread callbacks with reasonable defaults. - + o Change the semantics of timeouts in conjunction with EV_PERSIST; timeouts in that case will now repeat until deleted. + Changes in 1.4.0: o allow \r or \n individually to separate HTTP headers instead of the standard "\r\n"; from Charles Kerr. o demote most http warnings to debug messages diff --git a/event-internal.h b/event-internal.h index 3d0134f3..8ee32f4b 100644 --- a/event-internal.h +++ b/event-internal.h @@ -40,7 +40,9 @@ extern "C" { /* mutually exclusive */ #define ev_signal_next _ev.ev_signal.ev_signal_next + #define ev_io_next _ev.ev_io.ev_io_next +#define ev_io_timeout _ev.ev_io.ev_timeout /* used only by signals */ #define ev_ncalls _ev.ev_signal.ev_ncalls diff --git a/event.c b/event.c index 4a577b9d..7bcbe75e 100644 --- a/event.c +++ b/event.c @@ -146,6 +146,7 @@ static void timeout_correct(struct event_base *, struct timeval *); static void event_signal_closure(struct event_base *, struct event *ev); static void event_periodic_closure(struct event_base *, struct event *ev); +static void event_persist_closure(struct event_base *, struct event *ev); static int evthread_notify_base(struct event_base *base); @@ -572,6 +573,15 @@ event_periodic_closure(struct event_base *base, struct event *ev) (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); } +static void +event_persist_closure(struct event_base *base, struct event *ev) +{ + /* reschedule the persistent event if we have a timeout */ + if (ev->ev_io_timeout.tv_sec || ev->ev_io_timeout.tv_usec) + event_add(ev, &ev->ev_io_timeout); + (*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); +} + static void event_signal_closure(struct event_base *base, struct event *ev) { @@ -904,7 +914,12 @@ event_set(struct event *ev, evutil_socket_t fd, short events, __func__); ev->ev_closure = event_signal_closure; } else { - ev->ev_closure = NULL; + if (events & EV_PERSIST) { + timerclear(&ev->ev_io_timeout); + ev->ev_closure = event_persist_closure; + } else { + ev->ev_closure = NULL; + } } min_heap_elem_init(ev); @@ -1127,6 +1142,13 @@ event_add_internal(struct event *ev, const struct timeval *tv) if (res != -1 && tv != NULL) { struct timeval now; + /* + * for persistent timeout events, we remember the + * timeout value and re-add the event. + */ + if (ev->ev_closure == event_persist_closure) + ev->ev_io_timeout = *tv; + /* * we already reserved memory above for the case where we * are not replacing an exisiting timeout. diff --git a/include/event2/event_struct.h b/include/event2/event_struct.h index 331b64a0..e33738d2 100644 --- a/include/event2/event_struct.h +++ b/include/event2/event_struct.h @@ -88,6 +88,7 @@ struct event { /* used for io events */ struct { TAILQ_ENTRY (event) (ev_io_next); + struct timeval ev_timeout; } ev_io; /* used by signal events */ diff --git a/test/regress.c b/test/regress.c index 033f61e1..00fc8f92 100644 --- a/test/regress.c +++ b/test/regress.c @@ -507,6 +507,29 @@ test_periodictimeout(void) cleanup_test(); } +static void +test_persistent_timeout(void) +{ + struct timeval tv; + struct event ev; + int count = 0; + + setup_test("Periodic timeout via EV_PERSIST: "); + + timerclear(&tv); + tv.tv_usec = 10000; + + event_assign(&ev, global_base, -1, EV_TIMEOUT|EV_PERSIST, + periodic_timeout_cb, &count); + event_add(&ev, &tv); + + event_dispatch(); + + event_del(&ev); + + cleanup_test(); +} + #ifndef WIN32 static void signal_cb(int fd, short event, void *arg); @@ -2310,6 +2333,7 @@ main (int argc, char **argv) util_suite(); test_periodictimeout(); + test_persistent_timeout(); /* use the global event base and need to be called first */ test_priorities(1);