summaryrefslogtreecommitdiff
path: root/lib/timer.c
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2017-05-30 19:12:35 +0200
committerOndrej Zajicek (work) <santiago@crfreenet.org>2017-12-07 13:46:53 +0100
commit534215a18fb3fb7ce5b26c9e6ec1fdb32bf22ae6 (patch)
treeb805e7f9dcf6e999dfad01117bc72f4bee6f2a6d /lib/timer.c
parent7c454d918682c072a6ae6ad8e0cd8d35b9edd2aa (diff)
Timers: Split microsecond timers from BFD code to lib
Diffstat (limited to 'lib/timer.c')
-rw-r--r--lib/timer.c207
1 files changed, 207 insertions, 0 deletions
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(&current_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();
+}