From e3b2e0869ec4718eb0985444adf24e8c30bdeb92 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 16 Nov 2012 16:15:03 -0500 Subject: [PATCH] Add an event_remove_timer() to remove timer on an event without deleting it --- event-internal.h | 1 + event.c | 39 +++++++++++++++++++++++++ include/event2/event.h | 14 +++++++-- test/regress.c | 66 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 2 deletions(-) diff --git a/event-internal.h b/event-internal.h index 36ece843..614f5d80 100644 --- a/event-internal.h +++ b/event-internal.h @@ -383,6 +383,7 @@ int evsig_restore_handler_(struct event_base *base, int evsignal); int event_add_nolock_(struct event *ev, const struct timeval *tv, int tv_is_absolute); int event_del_nolock_(struct event *ev); +int event_remove_timer_nolock_(struct event *ev); void event_active_nolock_(struct event *ev, int res, short count); int event_callback_activate_(struct event_base *, struct event_callback *); diff --git a/event.c b/event.c index fc3ceef5..0fdd5d33 100644 --- a/event.c +++ b/event.c @@ -2193,6 +2193,45 @@ evthread_notify_base(struct event_base *base) return base->th_notify_fn(base); } +/* Implementation function to remove a timeout on a currently pending event. + */ +int +event_remove_timer_nolock_(struct event *ev) +{ + struct event_base *base = ev->ev_base; + + EVENT_BASE_ASSERT_LOCKED(base); + event_debug_assert_is_setup_(ev); + + event_debug(("event_remove_timer_nolock: event: %p", ev)); + + /* If it's not pending on a timeout, we don't need to do anything. */ + if (ev->ev_flags & EVLIST_TIMEOUT) { + event_queue_remove_timeout(base, ev); + } + + return (0); +} + +int +event_remove_timer(struct event *ev) +{ + int res; + + if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) { + event_warnx("%s: event has no event_base set.", __func__); + return -1; + } + + EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock); + + res = event_remove_timer_nolock_(ev); + + EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock); + + return (res); +} + /* Implementation function to add an event. Works just like event_add, * except: 1) it requires that we have the lock. 2) if tv_is_absolute is set, * we treat tv as an absolute time, not as an interval to add to the current diff --git a/include/event2/event.h b/include/event2/event.h index 3eed8128..4f177d9d 100644 --- a/include/event2/event.h +++ b/include/event2/event.h @@ -1037,8 +1037,7 @@ int event_base_once(struct event_base *, evutil_socket_t, short, event_callback_ in calls to event_assign() until it is no longer pending. If the event in the ev argument already has a scheduled timeout, calling - event_add() replaces the old timeout with the new one, or clears the old - timeout if the timeout argument is NULL. + event_add() replaces the old timeout with the new one if tv is non-NULL. @param ev an event struct initialized via event_set() @param timeout the maximum amount of time to wait for the event, or NULL @@ -1048,6 +1047,17 @@ int event_base_once(struct event_base *, evutil_socket_t, short, event_callback_ */ int event_add(struct event *ev, const struct timeval *timeout); +/** + Remove a timer from a pending event without removing the event itself. + + If the event has a scheduled timeout, this function unschedules it but + leaves the event otherwise pending. + + @param ev an event struct initialized via event_assign() or event_new() + @return 0 on success, or -1 if an error occurrect. +*/ +int event_remove_timer(struct event *ev); + /** Remove an event from the set of monitored events. diff --git a/test/regress.c b/test/regress.c index 460ed581..e9e23ce5 100644 --- a/test/regress.c +++ b/test/regress.c @@ -1410,6 +1410,71 @@ end: ; } + +static void incr_arg_cb(evutil_socket_t fd, short what, void *arg) +{ + int *intptr = arg; + (void) fd; (void) what; + ++*intptr; +} +static void remove_timers_cb(evutil_socket_t fd, short what, void *arg) +{ + struct event **ep = arg; + (void) fd; (void) what; + event_remove_timer(ep[0]); + event_remove_timer(ep[1]); +} +static void send_a_byte_cb(evutil_socket_t fd, short what, void *arg) +{ + evutil_socket_t *sockp = arg; + (void) fd; (void) what; + write(*sockp, "A", 1); +} +static void read_not_timeout_cb(evutil_socket_t fd, short what, void *arg) +{ + int *intp = arg; + (void) fd; (void) what; + *intp |= what; +} + +static void +test_event_remove_timeout(void *ptr) +{ + struct basic_test_data *data = ptr; + struct event_base *base = data->base; + struct event *ev[4]; + int ev0_fired=0, ev1_fired=0; + struct timeval ms25 = { 0, 25*1000 }, + ms75 = { 0, 75*1000 }, + ms125 = { 0, 125*1000 }; + + event_base_assert_ok_(base); + + ev[0] = event_new(base, data->pair[0], EV_READ, + read_not_timeout_cb, &ev0_fired); + ev[1] = evtimer_new(base, incr_arg_cb, &ev1_fired); + ev[2] = evtimer_new(base, remove_timers_cb, ev); + ev[3] = evtimer_new(base, send_a_byte_cb, &data->pair[1]); + tt_assert(base); + event_add(ev[2], &ms25); /* remove timers */ + event_add(ev[0], &ms75); /* read */ + event_add(ev[1], &ms75); /* timer */ + event_add(ev[3], &ms125); /* timeout. */ + event_base_assert_ok_(base); + + event_base_dispatch(base); + + tt_int_op(ev1_fired, ==, 0); + tt_int_op(ev0_fired, ==, EV_READ); + + event_base_assert_ok_(base); +end: + event_free(ev[0]); + event_free(ev[1]); + event_free(ev[2]); + event_free(ev[3]); +} + static void test_event_base_new(void *ptr) { @@ -2559,6 +2624,7 @@ struct testcase_t main_testcases[] = { BASIC(bad_assign, TT_FORK|TT_NEED_BASE|TT_NO_LOGS), BASIC(bad_reentrant, TT_FORK|TT_NEED_BASE|TT_NO_LOGS), BASIC(active_later, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR), + BASIC(event_remove_timeout, TT_FORK|TT_NEED_BASE|TT_NEED_SOCKETPAIR), /* These are still using the old API */ LEGACY(persistent_timeout, TT_FORK|TT_NEED_BASE),