diff options
author | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2017-05-30 19:12:35 +0200 |
---|---|---|
committer | Ondrej Zajicek (work) <santiago@crfreenet.org> | 2017-12-07 13:46:53 +0100 |
commit | 534215a18fb3fb7ce5b26c9e6ec1fdb32bf22ae6 (patch) | |
tree | b805e7f9dcf6e999dfad01117bc72f4bee6f2a6d /lib | |
parent | 7c454d918682c072a6ae6ad8e0cd8d35b9edd2aa (diff) |
Timers: Split microsecond timers from BFD code to lib
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/timer.c | 207 | ||||
-rw-r--r-- | lib/timer.h | 104 |
3 files changed, 312 insertions, 1 deletions
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 <santiago@crfreenet.org> + * (c) 2013--2017 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + + +#include <stdlib.h> + +#include "nest/bird.h" + +#include "lib/heap.h" +#include "lib/resource.h" +#include "lib/timer.h" + + +struct timeloop main_timeloop; + + +#ifdef USE_PTHREADS + +#include <pthread.h> + +/* 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 <santiago@crfreenet.org> + * (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 |