diff --git a/lib/Makefile b/lib/Makefile index 0c352869..01f3114d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,4 +1,4 @@ -src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c xmalloc.c +src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c timer.c xmalloc.c obj := $(src-o-files) $(all-daemon) diff --git a/lib/timer.c b/lib/timer.c new file mode 100644 index 00000000..00ac4b03 --- /dev/null +++ b/lib/timer.c @@ -0,0 +1,207 @@ +/* + * BIRD -- Timers + * + * (c) 2013--2017 Ondrej Zajicek + * (c) 2013--2017 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + + +#include + +#include "nest/bird.h" + +#include "lib/heap.h" +#include "lib/resource.h" +#include "lib/timer.h" + + +struct timeloop main_timeloop; + + +#ifdef USE_PTHREADS + +#include + +/* Data accessed and modified from proto/bfd/io.c */ +pthread_key_t current_time_key; + +static inline struct timeloop * +timeloop_current(void) +{ + return pthread_getspecific(current_time_key); +} + +static inline void +timeloop_init_current(void) +{ + pthread_key_create(¤t_time_key, NULL); + pthread_setspecific(current_time_key, &main_timeloop); +} + +void wakeup_kick_current(void); + +#else + +/* Just use main timelooop */ +static inline struct timeloop * timeloop_current(void) { return &main_timeloop; } +static inline void timeloop_init_current(void) { } + +#endif + +btime +current_time(void) +{ + return timeloop_current()->last_time; +} + + +#define TIMER_LESS(a,b) ((a)->expires < (b)->expires) +#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \ + heap[a]->index = (a), heap[b]->index = (b)) + + +static void +tm2_free(resource *r) +{ + timer2 *t = (timer2 *) r; + + tm2_stop(t); +} + +static void +tm2_dump(resource *r) +{ + timer2 *t = (timer2 *) r; + + debug("(code %p, data %p, ", t->hook, t->data); + if (t->randomize) + debug("rand %d, ", t->randomize); + if (t->recurrent) + debug("recur %d, ", t->recurrent); + if (t->expires) + debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS); + else + debug("inactive)\n"); +} + + +static struct resclass tm2_class = { + "Timer", + sizeof(timer2), + tm2_free, + tm2_dump, + NULL, + NULL +}; + +timer2 * +tm2_new(pool *p) +{ + timer2 *t = ralloc(p, &tm2_class); + t->index = -1; + return t; +} + +void +tm2_set(timer2 *t, btime when) +{ + struct timeloop *loop = timeloop_current(); + uint tc = timers_count(loop); + + if (!t->expires) + { + t->index = ++tc; + t->expires = when; + BUFFER_PUSH(loop->timers) = t; + HEAP_INSERT(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP); + } + else if (t->expires < when) + { + t->expires = when; + HEAP_INCREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index); + } + else if (t->expires > when) + { + t->expires = when; + HEAP_DECREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index); + } + +#ifdef CONFIG_BFD + /* Hack to notify BFD loops */ + if ((loop != &main_timeloop) && (t->index == 1)) + wakeup_kick_current(); +#endif +} + +void +tm2_start(timer2 *t, btime after) +{ + tm2_set(t, current_time() + MAX(after, 0)); +} + +void +tm2_stop(timer2 *t) +{ + if (!t->expires) + return; + + struct timeloop *loop = timeloop_current(); + uint tc = timers_count(loop); + + HEAP_DELETE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index); + BUFFER_POP(loop->timers); + + t->index = -1; + t->expires = 0; +} + +void +timers_init(struct timeloop *loop, pool *p) +{ + times_init(loop); + + BUFFER_INIT(loop->timers, p, 4); + BUFFER_PUSH(loop->timers) = NULL; +} + +void +timers_fire(struct timeloop *loop) +{ + btime base_time; + timer2 *t; + + times_update(loop); + base_time = loop->last_time; + + while (t = timers_first(loop)) + { + if (t->expires > base_time) + return; + + if (t->recurrent) + { + btime when = t->expires + t->recurrent; + + if (when <= loop->last_time) + when = loop->last_time + t->recurrent; + + if (t->randomize) + when += random() % (t->randomize + 1); + + tm2_set(t, when); + } + else + tm2_stop(t); + + t->hook(t); + } +} + +void +timer_init(void) +{ + timers_init(&main_timeloop, &root_pool); + timeloop_init_current(); +} diff --git a/lib/timer.h b/lib/timer.h new file mode 100644 index 00000000..88c53547 --- /dev/null +++ b/lib/timer.h @@ -0,0 +1,104 @@ +/* + * BIRD -- Timers + * + * (c) 2013--2017 Ondrej Zajicek + * (c) 2013--2017 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#ifndef _BIRD_TIMER2_H_ +#define _BIRD_TIMER2_H_ + +#include "nest/bird.h" +#include "lib/buffer.h" +#include "lib/resource.h" + + +typedef struct timer2 +{ + resource r; + void (*hook)(struct timer2 *); + void *data; + + btime expires; /* 0=inactive */ + uint randomize; /* Amount of randomization */ + uint recurrent; /* Timer recurrence */ + + int index; +} timer2; + +struct timeloop +{ + BUFFER(timer2 *) timers; + btime last_time; + btime real_time; +}; + +static inline uint timers_count(struct timeloop *loop) +{ return loop->timers.used - 1; } + +static inline timer2 *timers_first(struct timeloop *loop) +{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; } + +extern struct timeloop main_timeloop; + +btime current_time(void); + +timer2 *tm2_new(pool *p); +void tm2_set(timer2 *t, btime when); +void tm2_start(timer2 *t, btime after); +void tm2_stop(timer2 *t); + +static inline int +tm2_active(timer2 *t) +{ + return t->expires != 0; +} + +static inline btime +tm2_remains(timer2 *t) +{ + btime now = current_time(); + return (t->expires > now) ? (t->expires - now) : 0; +} + +static inline timer2 * +tm2_new_init(pool *p, void (*hook)(struct timer2 *), void *data, uint rec, uint rand) +{ + timer2 *t = tm2_new(p); + t->hook = hook; + t->data = data; + t->recurrent = rec; + t->randomize = rand; + return t; +} + +static inline void +tm2_set_max(timer2 *t, btime when) +{ + if (when > t->expires) + tm2_set(t, when); +} + +/* +static inline void +tm2_start_max(timer2 *t, btime after) +{ + btime rem = tm2_remains(t); + tm2_start(t, MAX_(rem, after)); +} +*/ + +/* In sysdep code */ +void times_init(struct timeloop *loop); +void times_update(struct timeloop *loop); + +/* For I/O loop */ +void timers_init(struct timeloop *loop, pool *p); +void timers_fire(struct timeloop *loop); + +void timer_init(void); + + +#endif diff --git a/proto/bfd/io.c b/proto/bfd/io.c index 8f4f5007..ab846113 100644 --- a/proto/bfd/io.c +++ b/proto/bfd/io.c @@ -18,10 +18,10 @@ #include "proto/bfd/io.h" #include "lib/buffer.h" -#include "lib/heap.h" #include "lib/lists.h" #include "lib/resource.h" #include "lib/event.h" +#include "lib/timer.h" #include "lib/socket.h" @@ -31,16 +31,12 @@ struct birdloop pthread_t thread; pthread_mutex_t mutex; - btime last_time; - btime real_time; - u8 use_monotonic_clock; - u8 stop_called; u8 poll_active; u8 wakeup_masked; int wakeup_fds[2]; - BUFFER(timer2 *) timers; + struct timeloop time; list event_list; list sock_list; uint sock_num; @@ -57,6 +53,7 @@ struct birdloop */ static pthread_key_t current_loop_key; +extern pthread_key_t current_time_key; static inline struct birdloop * birdloop_current(void) @@ -68,6 +65,7 @@ static inline void birdloop_set_current(struct birdloop *loop) { pthread_setspecific(current_loop_key, loop); + pthread_setspecific(current_time_key, loop ? &loop->time : &main_timeloop); } static inline void @@ -77,98 +75,6 @@ birdloop_init_current(void) } -/* - * Time clock - */ - -static void times_update_alt(struct birdloop *loop); - -static void -times_init(struct birdloop *loop) -{ - struct timespec ts; - int rv; - - rv = clock_gettime(CLOCK_MONOTONIC, &ts); - if (rv < 0) - { - log(L_WARN "Monotonic clock is missing"); - - loop->use_monotonic_clock = 0; - loop->last_time = 0; - loop->real_time = 0; - times_update_alt(loop); - return; - } - - if ((ts.tv_sec < 0) || (((s64) ts.tv_sec) > ((s64) 1 << 40))) - log(L_WARN "Monotonic clock is crazy"); - - loop->use_monotonic_clock = 1; - loop->last_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000); - loop->real_time = 0; -} - -static void -times_update_pri(struct birdloop *loop) -{ - struct timespec ts; - int rv; - - rv = clock_gettime(CLOCK_MONOTONIC, &ts); - if (rv < 0) - die("clock_gettime: %m"); - - btime new_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000); - - if (new_time < loop->last_time) - log(L_ERR "Monotonic clock is broken"); - - loop->last_time = new_time; - loop->real_time = 0; -} - -static void -times_update_alt(struct birdloop *loop) -{ - struct timeval tv; - int rv; - - rv = gettimeofday(&tv, NULL); - if (rv < 0) - die("gettimeofday: %m"); - - btime new_time = ((s64) tv.tv_sec S) + tv.tv_usec; - btime delta = new_time - loop->real_time; - - if ((delta < 0) || (delta > (60 S))) - { - if (loop->real_time) - log(L_WARN "Time jump, delta %d us", (int) delta); - - delta = 100 MS; - } - - loop->last_time += delta; - loop->real_time = new_time; -} - -static void -times_update(struct birdloop *loop) -{ - if (loop->use_monotonic_clock) - times_update_pri(loop); - else - times_update_alt(loop); -} - -btime -current_time(void) -{ - return birdloop_current()->last_time; -} - - /* * Wakeup code for birdloop */ @@ -238,7 +144,7 @@ wakeup_drain(struct birdloop *loop) } static inline void -wakeup_do_kick(struct birdloop *loop) +wakeup_do_kick(struct birdloop *loop) { pipe_kick(loop->wakeup_fds[1]); } @@ -252,6 +158,16 @@ wakeup_kick(struct birdloop *loop) loop->wakeup_masked = 2; } +/* For notifications from outside */ +void +wakeup_kick_current(void) +{ + struct birdloop *loop = birdloop_current(); + + if (loop && loop->poll_active) + wakeup_kick(loop); +} + /* * Events @@ -272,7 +188,7 @@ events_init(struct birdloop *loop) static void events_fire(struct birdloop *loop) { - times_update(loop); + times_update(&loop->time); ev_run_list(&loop->event_list); } @@ -291,154 +207,6 @@ ev2_schedule(event *e) } -/* - * Timers - */ - -#define TIMER_LESS(a,b) ((a)->expires < (b)->expires) -#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \ - heap[a]->index = (a), heap[b]->index = (b)) - -static inline uint timers_count(struct birdloop *loop) -{ return loop->timers.used - 1; } - -static inline timer2 *timers_first(struct birdloop *loop) -{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; } - - -static void -tm2_free(resource *r) -{ - timer2 *t = (timer2 *) r; - - tm2_stop(t); -} - -static void -tm2_dump(resource *r) -{ - timer2 *t = (timer2 *) r; - - debug("(code %p, data %p, ", t->hook, t->data); - if (t->randomize) - debug("rand %d, ", t->randomize); - if (t->recurrent) - debug("recur %d, ", t->recurrent); - if (t->expires) - debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS); - else - debug("inactive)\n"); -} - - -static struct resclass tm2_class = { - "Timer", - sizeof(timer2), - tm2_free, - tm2_dump, - NULL, - NULL -}; - -timer2 * -tm2_new(pool *p) -{ - timer2 *t = ralloc(p, &tm2_class); - t->index = -1; - return t; -} - -void -tm2_set(timer2 *t, btime when) -{ - struct birdloop *loop = birdloop_current(); - uint tc = timers_count(loop); - - if (!t->expires) - { - t->index = ++tc; - t->expires = when; - BUFFER_PUSH(loop->timers) = t; - HEAP_INSERT(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP); - } - else if (t->expires < when) - { - t->expires = when; - HEAP_INCREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index); - } - else if (t->expires > when) - { - t->expires = when; - HEAP_DECREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index); - } - - if (loop->poll_active && (t->index == 1)) - wakeup_kick(loop); -} - -void -tm2_start(timer2 *t, btime after) -{ - tm2_set(t, current_time() + MAX(after, 0)); -} - -void -tm2_stop(timer2 *t) -{ - if (!t->expires) - return; - - struct birdloop *loop = birdloop_current(); - uint tc = timers_count(loop); - - HEAP_DELETE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index); - BUFFER_POP(loop->timers); - - t->index = -1; - t->expires = 0; -} - -static void -timers_init(struct birdloop *loop) -{ - BUFFER_INIT(loop->timers, loop->pool, 4); - BUFFER_PUSH(loop->timers) = NULL; -} - -static void -timers_fire(struct birdloop *loop) -{ - btime base_time; - timer2 *t; - - times_update(loop); - base_time = loop->last_time; - - while (t = timers_first(loop)) - { - if (t->expires > base_time) - return; - - if (t->recurrent) - { - btime when = t->expires + t->recurrent; - - if (when <= loop->last_time) - when = loop->last_time + t->recurrent; - - if (t->randomize) - when += random() % (t->randomize + 1); - - tm2_set(t, when); - } - else - tm2_stop(t); - - t->hook(t); - } -} - - /* * Sockets */ @@ -586,7 +354,7 @@ sockets_fire(struct birdloop *loop) sock **psk = loop->poll_sk.data; int poll_num = loop->poll_fd.used - 1; - times_update(loop); + times_update(&loop->time); /* Last fd is internal wakeup fd */ if (pfd[poll_num].revents & POLLIN) @@ -634,11 +402,10 @@ birdloop_new(void) loop->pool = p; pthread_mutex_init(&loop->mutex, NULL); - times_init(loop); wakeup_init(loop); events_init(loop); - timers_init(loop); + timers_init(&loop->time, p); sockets_init(loop); return loop; @@ -719,12 +486,12 @@ birdloop_main(void *arg) while (1) { events_fire(loop); - timers_fire(loop); + timers_fire(&loop->time); - times_update(loop); + times_update(&loop->time); if (events_waiting(loop)) timeout = 0; - else if (t = timers_first(loop)) + else if (t = timers_first(&loop->time)) timeout = (tm2_remains(t) TO_MS) + 1; else timeout = -1; @@ -756,7 +523,7 @@ birdloop_main(void *arg) if (rv) sockets_fire(loop); - timers_fire(loop); + timers_fire(&loop->time); } loop->stop_called = 0; diff --git a/proto/bfd/io.h b/proto/bfd/io.h index 45836f84..ec706e9a 100644 --- a/proto/bfd/io.h +++ b/proto/bfd/io.h @@ -11,80 +11,15 @@ #include "lib/lists.h" #include "lib/resource.h" #include "lib/event.h" +#include "lib/timer.h" #include "lib/socket.h" -// #include "sysdep/unix/timer.h" -typedef struct timer2 -{ - resource r; - void (*hook)(struct timer2 *); - void *data; - - btime expires; /* 0=inactive */ - uint randomize; /* Amount of randomization */ - uint recurrent; /* Timer recurrence */ - - int index; -} timer2; - - -btime current_time(void); - void ev2_schedule(event *e); - -timer2 *tm2_new(pool *p); -void tm2_set(timer2 *t, btime when); -void tm2_start(timer2 *t, btime after); -void tm2_stop(timer2 *t); - -static inline int -tm2_active(timer2 *t) -{ - return t->expires != 0; -} - -static inline btime -tm2_remains(timer2 *t) -{ - btime now = current_time(); - return (t->expires > now) ? (t->expires - now) : 0; -} - -static inline timer2 * -tm2_new_init(pool *p, void (*hook)(struct timer2 *), void *data, uint rec, uint rand) -{ - timer2 *t = tm2_new(p); - t->hook = hook; - t->data = data; - t->recurrent = rec; - t->randomize = rand; - return t; -} - -static inline void -tm2_set_max(timer2 *t, btime when) -{ - if (when > t->expires) - tm2_set(t, when); -} - -/* -static inline void -tm2_start_max(timer2 *t, btime after) -{ - btime rem = tm2_remains(t); - tm2_start(t, MAX_(rem, after)); -} -*/ - - void sk_start(sock *s); void sk_stop(sock *s); - - struct birdloop *birdloop_new(void); void birdloop_start(struct birdloop *loop); void birdloop_stop(struct birdloop *loop); diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 0cf48c9d..ebd380ba 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -37,6 +37,7 @@ #include "sysdep/unix/timer.h" #include "lib/socket.h" #include "lib/event.h" +#include "lib/timer.h" #include "lib/string.h" #include "nest/iface.h" @@ -479,6 +480,47 @@ tm_format_datetime(char *x, struct timeformat *fmt_spec, bird_clock_t t) } +/* + * Time clock + */ + +void +times_init(struct timeloop *loop) +{ + struct timespec ts; + int rv; + + rv = clock_gettime(CLOCK_MONOTONIC, &ts); + if (rv < 0) + die("Monotonic clock is missing"); + + if ((ts.tv_sec < 0) || (((s64) ts.tv_sec) > ((s64) 1 << 40))) + log(L_WARN "Monotonic clock is crazy"); + + loop->last_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000); + loop->real_time = 0; +} + +void +times_update(struct timeloop *loop) +{ + struct timespec ts; + int rv; + + rv = clock_gettime(CLOCK_MONOTONIC, &ts); + if (rv < 0) + die("clock_gettime: %m"); + + btime new_time = ((s64) ts.tv_sec S) + (ts.tv_nsec / 1000); + + if (new_time < loop->last_time) + log(L_ERR "Monotonic clock is broken"); + + loop->last_time = new_time; + loop->real_time = 0; +} + + /** * DOC: Sockets * diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index c1b92b7e..396310fd 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -27,6 +27,7 @@ #include "lib/resource.h" #include "lib/socket.h" #include "lib/event.h" +#include "lib/timer.h" #include "lib/string.h" #include "nest/route.h" #include "nest/protocol.h" @@ -820,6 +821,7 @@ main(int argc, char **argv) log_switch(debug_flag, NULL, NULL); resource_init(); + timer_init(); olock_init(); io_init(); rt_init();