Commit 4ba10490 authored by Rusty Russell's avatar Rusty Russell

timer: change to use time_mono (api break!)

Remove timer_add() in favor of explicit timer_addrel and timer_addmono.

Someone hit a real-life case where time went backwards, and we asserted.
The correct fix is to use time_mono() where available, but as all known
users actually want a relative timeout, have a helper for that case.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 88ed9bfe
......@@ -31,21 +31,20 @@
* struct timer *t;
* struct timed_string *s;
*
* timers_init(&timers, time_now());
* timers_init(&timers, time_mono());
* list_head_init(&strings);
*
* while (argv[1]) {
* s = malloc(sizeof(*s));
* s->string = argv[1];
* timer_add(&timers, &s->timer,
* timeabs_add(time_now(),
* time_from_msec(atol(argv[2]))));
* timer_addrel(&timers, &s->timer,
* time_from_msec(atol(argv[2])));
* list_add_tail(&strings, &s->node);
* argv += 2;
* }
*
* while (!list_empty(&strings)) {
* struct timeabs now = time_now();
* struct timemono now = time_mono();
* list_for_each(&strings, s, node)
* printf("%s", s->string);
* while ((t = timers_expire(&timers, now)) != NULL) {
......
......@@ -20,7 +20,7 @@ int main(void)
struct timer t;
uint64_t diff;
unsigned int i;
struct timeabs epoch = { { 0, 0 } };
struct timemono epoch = { { 0, 0 } };
/* This is how many tests you plan to run */
plan_tests(2 + (18 + (MAX_ORD - 4) * 3) * (18 + (MAX_ORD - 4) * 3));
......@@ -38,7 +38,7 @@ int main(void)
for (timers.base = 0;
timers.base < (1ULL << MAX_ORD)+2;
timers.base = next(timers.base)) {
timer_add(&timers, &t, grains_to_time(timers.base + diff));
timer_addmono(&timers, &t, grains_to_time(timers.base + diff));
ok1(timers_check(&timers, NULL));
timer_del(&timers, &t);
}
......
......@@ -7,17 +7,17 @@
static void new_timer(struct timers *timers, unsigned long nsec)
{
struct timer *timer;
struct timeabs when;
struct timemono when;
timer = malloc(sizeof(*timer));
timer_init(timer);
when.ts.tv_sec = 0; when.ts.tv_nsec = nsec;
timer_add(timers, timer, when);
timer_addmono(timers, timer, when);
}
static void update_and_expire(struct timers *timers)
{
struct timeabs when;
struct timemono when;
timer_earliest(timers, &when);
free(timers_expire(timers, when));
......@@ -25,7 +25,7 @@ static void update_and_expire(struct timers *timers)
int main(int argc, char *argv[])
{
struct timeabs when;
struct timemono when;
struct timers timers;
plan_tests(7);
......
This diff is collapsed.
......@@ -14,7 +14,7 @@ int main(void)
timers_init(&timers, grains_to_time(1364984760903400ULL));
ok1(timers.base == 1364984760903400ULL);
timer_init(&t);
timer_add(&timers, &t, grains_to_time(1364984761003398ULL));
timer_addmono(&timers, &t, grains_to_time(1364984761003398ULL));
ok1(t.time == 1364984761003398ULL);
ok1(timers.first == 1364984761003398ULL);
ok1(!timers_expire(&timers, grains_to_time(1364984760903444ULL)));
......
......@@ -3,10 +3,10 @@
#include <ccan/timer/timer.c>
#include <ccan/tap/tap.h>
static struct timeabs timeabs_from_usec(unsigned long long usec)
static struct timemono timemono_from_usec(unsigned long long usec)
{
struct timeabs epoch = { { 0, 0 } };
return timeabs_add(epoch, time_from_usec(usec));
struct timemono epoch = { { 0, 0 } };
return timemono_add(epoch, time_from_usec(usec));
}
int main(void)
......@@ -17,13 +17,13 @@ int main(void)
/* This is how many tests you plan to run */
plan_tests(3);
timers_init(&timers, timeabs_from_usec(1364726722653919ULL));
timers_init(&timers, timemono_from_usec(1364726722653919ULL));
timer_init(&t);
timer_add(&timers, &t, timeabs_from_usec(1364726722703919ULL));
ok1(!timers_expire(&timers, timeabs_from_usec(1364726722653920ULL)));
expired = timers_expire(&timers, timeabs_from_usec(1364726725454187ULL));
timer_addmono(&timers, &t, timemono_from_usec(1364726722703919ULL));
ok1(!timers_expire(&timers, timemono_from_usec(1364726722653920ULL)));
expired = timers_expire(&timers, timemono_from_usec(1364726725454187ULL));
ok1(expired == &t);
ok1(!timers_expire(&timers, timeabs_from_usec(1364726725454187ULL)));
ok1(!timers_expire(&timers, timemono_from_usec(1364726725454187ULL)));
timers_cleanup(&timers);
/* This exits depending on whether all tests passed */
......
This diff is collapsed.
#define CCAN_TIMER_DEBUG
#include <ccan/timer/timer.h>
#include <ccan/time/time.h>
#define time_mono() fake_mono_time
static struct timemono fake_mono_time;
/* Include the C files directly. */
#include <ccan/timer/timer.c>
#include <ccan/tap/tap.h>
static struct timeabs timeabs_from_nsec(unsigned long long nsec)
static struct timemono timemono_from_nsec(unsigned long long nsec)
{
struct timeabs epoch = { { 0, 0 } };
return timeabs_add(epoch, time_from_nsec(nsec));
struct timemono epoch = { { 0, 0 } };
return timemono_add(epoch, time_from_nsec(nsec));
}
int main(void)
{
struct timers timers;
struct timer t[64];
struct timeabs earliest;
struct timemono earliest;
uint64_t i;
struct timeabs epoch = { { 0, 0 } };
const struct timemono epoch = { { 0, 0 } };
/* This is how many tests you plan to run */
plan_tests(488);
plan_tests(495);
timers_init(&timers, epoch);
ok1(timers_check(&timers, NULL));
......@@ -29,10 +35,10 @@ int main(void)
/* timer_del can be called immediately after init. */
timer_del(&timers, &t[0]);
timer_add(&timers, &t[0], timeabs_from_nsec(1));
timer_addmono(&timers, &t[0], timemono_from_nsec(1));
ok1(timers_check(&timers, NULL));
ok1(timer_earliest(&timers, &earliest));
ok1(timeabs_eq(earliest, grains_to_time(t[0].time)));
ok1(timemono_eq(earliest, grains_to_time(t[0].time)));
timer_del(&timers, &t[0]);
ok1(timers_check(&timers, NULL));
ok1(!timer_earliest(&timers, &earliest));
......@@ -43,10 +49,10 @@ int main(void)
/* Check timer ordering. */
for (i = 0; i < 32; i++) {
timer_init(&t[i*2]);
timer_add(&timers, &t[i*2], timeabs_from_nsec(1ULL << i));
timer_addmono(&timers, &t[i*2], timemono_from_nsec(1ULL << i));
ok1(timers_check(&timers, NULL));
timer_init(&t[i*2+1]);
timer_add(&timers, &t[i*2+1], timeabs_from_nsec((1ULL << i) + 1));
timer_addmono(&timers, &t[i*2+1], timemono_from_nsec((1ULL << i) + 1));
ok1(timers_check(&timers, NULL));
}
......@@ -68,9 +74,9 @@ int main(void)
for (i = 0; i < 32; i++) {
uint64_t exp = (uint64_t)TIMER_GRANULARITY << i;
timer_add(&timers, &t[i*2], timeabs_from_nsec(exp));
timer_addmono(&timers, &t[i*2], timemono_from_nsec(exp));
ok1(timers_check(&timers, NULL));
timer_add(&timers, &t[i*2+1], timeabs_from_nsec(exp + 1));
timer_addmono(&timers, &t[i*2+1], timemono_from_nsec(exp + 1));
ok1(timers_check(&timers, NULL));
}
......@@ -90,7 +96,19 @@ int main(void)
}
ok1(!timer_earliest(&timers, &earliest));
ok1(timers_check(&timers, NULL));
timers_cleanup(&timers);
/* Relative timers. */
timers_init(&timers, epoch);
fake_mono_time = timemono_from_nsec(TIMER_GRANULARITY);
timer_addrel(&timers, &t[0], time_from_sec(1));
ok1(timer_earliest(&timers, &earliest));
ok1(timers_check(&timers, NULL));
ok1(earliest.ts.tv_sec == 1 && earliest.ts.tv_nsec == TIMER_GRANULARITY);
ok1(timers_expire(&timers, earliest) == &t[0]);
ok1(!timer_earliest(&timers, &earliest));
ok1(timers_check(&timers, NULL));
timers_cleanup(&timers);
/* This exits depending on whether all tests passed */
......
......@@ -12,15 +12,15 @@ struct timer_level {
struct list_head list[PER_LEVEL];
};
static uint64_t time_to_grains(struct timeabs t)
static uint64_t time_to_grains(struct timemono t)
{
return t.ts.tv_sec * ((uint64_t)1000000000 / TIMER_GRANULARITY)
+ (t.ts.tv_nsec / TIMER_GRANULARITY);
}
static struct timeabs grains_to_time(uint64_t grains)
static struct timemono grains_to_time(uint64_t grains)
{
struct timeabs t;
struct timemono t;
t.ts.tv_sec = grains / (1000000000 / TIMER_GRANULARITY);
t.ts.tv_nsec = (grains % (1000000000 / TIMER_GRANULARITY))
......@@ -28,7 +28,7 @@ static struct timeabs grains_to_time(uint64_t grains)
return t;
}
void timers_init(struct timers *timers, struct timeabs start)
void timers_init(struct timers *timers, struct timemono start)
{
unsigned int i;
......@@ -79,7 +79,26 @@ static bool list_node_initted(const struct list_node *n)
return n->prev == n;
}
void timer_add(struct timers *timers, struct timer *t, struct timeabs when)
void timer_addrel(struct timers *timers, struct timer *t, struct timerel rel)
{
assert(list_node_initted(&t->list));
t->time = time_to_grains(timemono_add(time_mono(), rel));
#if TIME_HAVE_MONOTONIC
assert(t->time >= timers->base);
#else
/* Added in the past? Treat it as imminent. */
if (t->time < timers->base)
t->time = timers->base;
#endif
if (t->time < timers->first)
timers->first = t->time;
timer_add_raw(timers, t);
}
void timer_addmono(struct timers *timers, struct timer *t, struct timemono when)
{
assert(list_node_initted(&t->list));
......@@ -241,7 +260,7 @@ static bool update_first(struct timers *timers)
return true;
}
bool timer_earliest(struct timers *timers, struct timeabs *first)
bool timer_earliest(struct timers *timers, struct timemono *first)
{
if (!update_first(timers))
return false;
......@@ -298,7 +317,7 @@ static void timer_fast_forward(struct timers *timers, uint64_t time)
}
/* Returns an expired timer. */
struct timer *timers_expire(struct timers *timers, struct timeabs expire)
struct timer *timers_expire(struct timers *timers, struct timemono expire)
{
uint64_t now = time_to_grains(expire);
unsigned int off;
......
......@@ -29,9 +29,9 @@ struct timer;
* Example:
* struct timers timeouts;
*
* timers_init(&timeouts, time_now());
* timers_init(&timeouts, time_mono());
*/
void timers_init(struct timers *timers, struct timeabs start);
void timers_init(struct timers *timers, struct timemono start);
/**
* timers_cleanup - free allocations within timers struct.
......@@ -56,19 +56,39 @@ void timers_cleanup(struct timers *timers);
void timer_init(struct timer *t);
/**
* timer_add - insert a timer.
* timer_addrel - insert a relative timer.
* @timers: the struct timers
* @timer: the (initialized or timer_del'd) timer to add
* @when: when @timer expires.
* @rel: when @timer expires (relative).
*
* This efficiently adds @timer to @timers, to expire @rel (rounded to
* TIMER_GRANULARITY nanoseconds) after the current time. This
* is a convenient wrapper around timer_addmono().
*
* Example:
* // Timeout in 100ms.
* timer_addrel(&timeouts, &t, time_from_msec(100));
*/
void timer_addrel(struct timers *timers, struct timer *timer, struct timerel rel);
/**
* timer_addmono - insert an absolute timer.
* @timers: the struct timers
* @timer: the (initialized or timer_del'd) timer to add
* @when: when @timer expires (absolute).
*
* This efficiently adds @timer to @timers, to expire @when (rounded to
* TIMER_GRANULARITY nanoseconds).
*
* Note that if @when is before time_mono(), then it will be set to expire
* immediately.
*
* Example:
* // Timeout in 100ms.
* timer_add(&timeouts, &t, timeabs_add(time_now(), time_from_msec(100)));
* timer_addmono(&timeouts, &t, timemono_add(time_mono(), time_from_msec(100)));
*/
void timer_add(struct timers *timers, struct timer *timer, struct timeabs when);
void timer_addmono(struct timers *timers, struct timer *timer,
struct timemono when);
/**
* timer_del - remove a timer.
......@@ -94,10 +114,10 @@ void timer_del(struct timers *timers, struct timer *timer);
* timer (rounded to TIMER_GRANULARITY nanoseconds), and returns true.
*
* Example:
* struct timeabs next = { { (time_t)-1ULL, -1UL } };
* struct timemono next = { { (time_t)-1ULL, -1UL } };
* timer_earliest(&timeouts, &next);
*/
bool timer_earliest(struct timers *timers, struct timeabs *first);
bool timer_earliest(struct timers *timers, struct timemono *first);
/**
* timers_expire - update timers structure and remove one expire timer.
......@@ -118,11 +138,11 @@ bool timer_earliest(struct timers *timers, struct timeabs *first);
* Example:
* struct timer *expired;
*
* while ((expired = timers_expire(&timeouts, time_now())) != NULL)
* while ((expired = timers_expire(&timeouts, time_mono())) != NULL)
* printf("Timer expired!\n");
*
*/
struct timer *timers_expire(struct timers *timers, struct timeabs expire);
struct timer *timers_expire(struct timers *timers, struct timemono expire);
/**
* timers_check - check timer structure for consistency
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment