Return 0 if tv1 == tv2
Return 1 if tv1 > tv2
*/
-static int ev_timeval_compare(const struct timeval *tv1, const struct timeval *tv2)
+int tevent_timeval_compare(const struct timeval *tv1, const struct timeval *tv2)
{
if (tv1->tv_sec > tv2->tv_sec) return 1;
if (tv1->tv_sec < tv2->tv_sec) return -1;
/**
return a zero timeval
*/
-struct timeval ev_timeval_zero(void)
+struct timeval tevent_timeval_zero(void)
{
struct timeval tv;
tv.tv_sec = 0;
/**
return a timeval for the current time
*/
-static struct timeval ev_timeval_current(void)
+struct timeval tevent_timeval_current(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
/**
return a timeval struct with the given elements
*/
-static struct timeval ev_timeval_set(uint32_t secs, uint32_t usecs)
+struct timeval tevent_timeval_set(uint32_t secs, uint32_t usecs)
{
struct timeval tv;
tv.tv_sec = secs;
if tv1 comes after tv2, then return a zero timeval
(this is *tv2 - *tv1)
*/
-static struct timeval ev_timeval_until(const struct timeval *tv1,
- const struct timeval *tv2)
+struct timeval tevent_timeval_until(const struct timeval *tv1,
+ const struct timeval *tv2)
{
struct timeval t;
- if (ev_timeval_compare(tv1, tv2) >= 0) {
- return ev_timeval_zero();
+ if (tevent_timeval_compare(tv1, tv2) >= 0) {
+ return tevent_timeval_zero();
}
t.tv_sec = tv2->tv_sec - tv1->tv_sec;
if (tv1->tv_usec > tv2->tv_usec) {
/**
return true if a timeval is zero
*/
-bool ev_timeval_is_zero(const struct timeval *tv)
+bool tevent_timeval_is_zero(const struct timeval *tv)
{
return tv->tv_sec == 0 && tv->tv_usec == 0;
}
+struct timeval tevent_timeval_add(const struct timeval *tv, uint32_t secs,
+ uint32_t usecs)
+{
+ struct timeval tv2 = *tv;
+ tv2.tv_sec += secs;
+ tv2.tv_usec += usecs;
+ tv2.tv_sec += tv2.tv_usec / 1000000;
+ tv2.tv_usec = tv2.tv_usec % 1000000;
+
+ return tv2;
+}
+
+/**
+ return a timeval in the future with a specified offset
+*/
+struct timeval tevent_timeval_current_ofs(uint32_t secs, uint32_t usecs)
+{
+ struct timeval tv = tevent_timeval_current();
+ return tevent_timeval_add(&tv, secs, usecs);
+}
+
/*
destroy a timed event
*/
static int tevent_common_timed_destructor(struct tevent_timer *te)
{
+ if (te->event_ctx == NULL) {
+ return 0;
+ }
+
tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
"Destroying timer event %p \"%s\"\n",
te, te->handler_name);
- if (te->event_ctx) {
- DLIST_REMOVE(te->event_ctx->timer_events, te);
+ if (te->event_ctx->last_zero_timer == te) {
+ te->event_ctx->last_zero_timer = DLIST_PREV(te);
}
+ DLIST_REMOVE(te->event_ctx->timer_events, te);
return 0;
}
return -1;
}
+static void tevent_common_insert_timer(struct tevent_context *ev,
+ struct tevent_timer *te,
+ bool optimize_zero)
+{
+ struct tevent_timer *prev_te = NULL;
+
+ /* keep the list ordered */
+ if (optimize_zero && tevent_timeval_is_zero(&te->next_event)) {
+ /*
+ * Some callers use zero tevent_timer
+ * instead of tevent_immediate events.
+ *
+ * As these can happen very often,
+ * we remember the last zero timer
+ * in the list.
+ */
+ prev_te = ev->last_zero_timer;
+ ev->last_zero_timer = te;
+ } else {
+ struct tevent_timer *cur_te;
+
+ /*
+ * we traverse the list from the tail
+ * because it's much more likely that
+ * timers are added at the end of the list
+ */
+ for (cur_te = DLIST_TAIL(ev->timer_events);
+ cur_te != NULL;
+ cur_te = DLIST_PREV(cur_te))
+ {
+ int ret;
+
+ /*
+ * if the new event comes before the current
+ * we continue searching
+ */
+ ret = tevent_timeval_compare(&te->next_event,
+ &cur_te->next_event);
+ if (ret < 0) {
+ continue;
+ }
+
+ break;
+ }
+
+ prev_te = cur_te;
+ }
+
+ DLIST_ADD_AFTER(ev->timer_events, te, prev_te);
+}
+
/*
add a timed event
return NULL on failure (memory allocation error)
*/
-struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev, TALLOC_CTX *mem_ctx,
- struct timeval next_event,
- tevent_timer_handler_t handler,
- void *private_data,
- const char *handler_name,
- const char *location)
+static struct tevent_timer *tevent_common_add_timer_internal(
+ struct tevent_context *ev,
+ TALLOC_CTX *mem_ctx,
+ struct timeval next_event,
+ tevent_timer_handler_t handler,
+ void *private_data,
+ const char *handler_name,
+ const char *location,
+ bool optimize_zero)
{
- struct tevent_timer *te, *last_te, *cur_te;
+ struct tevent_timer *te;
te = talloc(mem_ctx?mem_ctx:ev, struct tevent_timer);
if (te == NULL) return NULL;
te->location = location;
te->additional_data = NULL;
- /* keep the list ordered */
- last_te = NULL;
- for (cur_te = ev->timer_events; cur_te; cur_te = cur_te->next) {
- /* if the new event comes before the current one break */
- if (ev_timeval_compare(&te->next_event, &cur_te->next_event) < 0) {
- break;
- }
-
- last_te = cur_te;
+ if (ev->timer_events == NULL) {
+ ev->last_zero_timer = NULL;
}
- DLIST_ADD_AFTER(ev->timer_events, te, last_te);
+ tevent_common_insert_timer(ev, te, optimize_zero);
talloc_set_destructor(te, tevent_common_timed_destructor);
return te;
}
+struct tevent_timer *tevent_common_add_timer(struct tevent_context *ev,
+ TALLOC_CTX *mem_ctx,
+ struct timeval next_event,
+ tevent_timer_handler_t handler,
+ void *private_data,
+ const char *handler_name,
+ const char *location)
+{
+ /*
+ * do not use optimization, there are broken Samba
+ * versions which use tevent_common_add_timer()
+ * without using tevent_common_loop_timer_delay(),
+ * it just uses DLIST_REMOVE(ev->timer_events, te)
+ * and would leave ev->last_zero_timer behind.
+ */
+ return tevent_common_add_timer_internal(ev, mem_ctx, next_event,
+ handler, private_data,
+ handler_name, location,
+ false);
+}
+
+struct tevent_timer *tevent_common_add_timer_v2(struct tevent_context *ev,
+ TALLOC_CTX *mem_ctx,
+ struct timeval next_event,
+ tevent_timer_handler_t handler,
+ void *private_data,
+ const char *handler_name,
+ const char *location)
+{
+ /*
+ * Here we turn on last_zero_timer optimization
+ */
+ return tevent_common_add_timer_internal(ev, mem_ctx, next_event,
+ handler, private_data,
+ handler_name, location,
+ true);
+}
+
+void tevent_update_timer(struct tevent_timer *te, struct timeval next_event)
+{
+ struct tevent_context *ev = te->event_ctx;
+
+ if (ev->last_zero_timer == te) {
+ te->event_ctx->last_zero_timer = DLIST_PREV(te);
+ }
+ DLIST_REMOVE(ev->timer_events, te);
+
+ te->next_event = next_event;
+
+ /*
+ * Not doing the zero_timer optimization. This is for new code
+ * that should know about immediates.
+ */
+ tevent_common_insert_timer(ev, te, false);
+}
+
/*
do a single event loop using the events defined in ev
- return the delay untill the next timed event,
+ return the delay until the next timed event,
or zero if a timed event was triggered
*/
struct timeval tevent_common_loop_timer_delay(struct tevent_context *ev)
{
- struct timeval current_time = ev_timeval_zero();
+ struct timeval current_time = tevent_timeval_zero();
struct tevent_timer *te = ev->timer_events;
if (!te) {
/* have a default tick time of 30 seconds. This guarantees
that code that uses its own timeout checking will be
- able to proceeed eventually */
- return ev_timeval_set(30, 0);
+ able to proceed eventually */
+ return tevent_timeval_set(30, 0);
}
/*
* if there's a delay till the next timed event, we're done
* with just returning the delay
*/
- if (!ev_timeval_is_zero(&te->next_event)) {
+ if (!tevent_timeval_is_zero(&te->next_event)) {
struct timeval delay;
- current_time = ev_timeval_current();
+ current_time = tevent_timeval_current();
- delay = ev_timeval_until(¤t_time, &te->next_event);
- if (!ev_timeval_is_zero(&delay)) {
+ delay = tevent_timeval_until(¤t_time, &te->next_event);
+ if (!tevent_timeval_is_zero(&delay)) {
return delay;
}
}
/* We need to remove the timer from the list before calling the
* handler because in a semi-async inner event loop called from the
* handler we don't want to come across this event again -- vl */
+ if (ev->last_zero_timer == te) {
+ ev->last_zero_timer = DLIST_PREV(te);
+ }
DLIST_REMOVE(ev->timer_events, te);
+ tevent_debug(te->event_ctx, TEVENT_DEBUG_TRACE,
+ "Running timer event %p \"%s\"\n",
+ te, te->handler_name);
+
/*
* If the timed event was registered for a zero current_time,
* then we pass a zero timeval here too! To avoid the
talloc_free(te);
- return ev_timeval_zero();
+ return tevent_timeval_zero();
}