]> git.ipfire.org Git - thirdparty/bird.git/blame - lib/timer.c
Merge remote-tracking branch 'origin/master' into mq-filter-stack
[thirdparty/bird.git] / lib / timer.c
CommitLineData
534215a1
OZ
1/*
2 * BIRD -- Timers
3 *
4 * (c) 2013--2017 Ondrej Zajicek <santiago@crfreenet.org>
5 * (c) 2013--2017 CZ.NIC z.s.p.o.
6 *
7 * Can be freely distributed and used under the terms of the GNU GPL.
8 */
9
6b5cd7c0
OZ
10/**
11 * DOC: Timers
12 *
13 * Timers are resources which represent a wish of a module to call a function at
14 * the specified time. The timer code does not guarantee exact timing, only that
15 * a timer function will not be called before the requested time.
16 *
17 * In BIRD, time is represented by values of the &btime type which is signed
18 * 64-bit integer interpreted as a relative number of microseconds since some
19 * fixed time point in past. The current time can be obtained by current_time()
20 * function with reasonable accuracy and is monotonic. There is also a current
21 * 'wall-clock' real time obtainable by current_real_time() reported by OS.
22 *
23 * Each timer is described by a &timer structure containing a pointer to the
24 * handler function (@hook), data private to this function (@data), time the
25 * function should be called at (@expires, 0 for inactive timers), for the other
26 * fields see |timer.h|.
27 */
534215a1 28
f047271c 29#include <stdio.h>
534215a1 30#include <stdlib.h>
abd4367f 31#include <time.h>
534215a1
OZ
32
33#include "nest/bird.h"
34
35#include "lib/heap.h"
36#include "lib/resource.h"
37#include "lib/timer.h"
38
39
40struct timeloop main_timeloop;
41
42
43#ifdef USE_PTHREADS
44
45#include <pthread.h>
46
47/* Data accessed and modified from proto/bfd/io.c */
48pthread_key_t current_time_key;
49
50static inline struct timeloop *
51timeloop_current(void)
52{
53 return pthread_getspecific(current_time_key);
54}
55
56static inline void
57timeloop_init_current(void)
58{
59 pthread_key_create(&current_time_key, NULL);
60 pthread_setspecific(current_time_key, &main_timeloop);
61}
62
63void wakeup_kick_current(void);
64
65#else
66
67/* Just use main timelooop */
68static inline struct timeloop * timeloop_current(void) { return &main_timeloop; }
69static inline void timeloop_init_current(void) { }
70
71#endif
72
73btime
74current_time(void)
75{
76 return timeloop_current()->last_time;
77}
78
02552526
OZ
79btime
80current_real_time(void)
81{
82 struct timeloop *loop = timeloop_current();
83
84 if (!loop->real_time)
85 times_update_real_time(loop);
86
87 return loop->real_time;
88}
89
534215a1
OZ
90
91#define TIMER_LESS(a,b) ((a)->expires < (b)->expires)
92#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \
93 heap[a]->index = (a), heap[b]->index = (b))
94
95
96static void
a6f79ca5 97tm_free(resource *r)
534215a1 98{
a6f79ca5 99 timer *t = (void *) r;
534215a1 100
a6f79ca5 101 tm_stop(t);
534215a1
OZ
102}
103
104static void
a6f79ca5 105tm_dump(resource *r)
534215a1 106{
a6f79ca5 107 timer *t = (void *) r;
534215a1
OZ
108
109 debug("(code %p, data %p, ", t->hook, t->data);
110 if (t->randomize)
111 debug("rand %d, ", t->randomize);
112 if (t->recurrent)
113 debug("recur %d, ", t->recurrent);
114 if (t->expires)
115 debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS);
116 else
117 debug("inactive)\n");
118}
119
120
a6f79ca5 121static struct resclass tm_class = {
534215a1 122 "Timer",
a6f79ca5
OZ
123 sizeof(timer),
124 tm_free,
125 tm_dump,
534215a1
OZ
126 NULL,
127 NULL
128};
129
a6f79ca5
OZ
130timer *
131tm_new(pool *p)
534215a1 132{
a6f79ca5 133 timer *t = ralloc(p, &tm_class);
534215a1
OZ
134 t->index = -1;
135 return t;
136}
137
138void
a6f79ca5 139tm_set(timer *t, btime when)
534215a1
OZ
140{
141 struct timeloop *loop = timeloop_current();
142 uint tc = timers_count(loop);
143
144 if (!t->expires)
145 {
146 t->index = ++tc;
147 t->expires = when;
148 BUFFER_PUSH(loop->timers) = t;
a6f79ca5 149 HEAP_INSERT(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP);
534215a1
OZ
150 }
151 else if (t->expires < when)
152 {
153 t->expires = when;
a6f79ca5 154 HEAP_INCREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
534215a1
OZ
155 }
156 else if (t->expires > when)
157 {
158 t->expires = when;
a6f79ca5 159 HEAP_DECREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
534215a1
OZ
160 }
161
162#ifdef CONFIG_BFD
163 /* Hack to notify BFD loops */
164 if ((loop != &main_timeloop) && (t->index == 1))
165 wakeup_kick_current();
166#endif
167}
168
169void
a6f79ca5 170tm_start(timer *t, btime after)
534215a1 171{
a6f79ca5 172 tm_set(t, current_time() + MAX(after, 0));
534215a1
OZ
173}
174
175void
a6f79ca5 176tm_stop(timer *t)
534215a1
OZ
177{
178 if (!t->expires)
179 return;
180
181 struct timeloop *loop = timeloop_current();
182 uint tc = timers_count(loop);
183
a6f79ca5 184 HEAP_DELETE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
534215a1
OZ
185 BUFFER_POP(loop->timers);
186
187 t->index = -1;
188 t->expires = 0;
189}
190
191void
192timers_init(struct timeloop *loop, pool *p)
193{
194 times_init(loop);
195
196 BUFFER_INIT(loop->timers, p, 4);
197 BUFFER_PUSH(loop->timers) = NULL;
198}
199
28a7d394
OZ
200void io_log_event(void *hook, void *data);
201
534215a1
OZ
202void
203timers_fire(struct timeloop *loop)
204{
205 btime base_time;
a6f79ca5 206 timer *t;
534215a1
OZ
207
208 times_update(loop);
209 base_time = loop->last_time;
210
211 while (t = timers_first(loop))
212 {
213 if (t->expires > base_time)
214 return;
215
216 if (t->recurrent)
217 {
218 btime when = t->expires + t->recurrent;
219
220 if (when <= loop->last_time)
221 when = loop->last_time + t->recurrent;
222
223 if (t->randomize)
224 when += random() % (t->randomize + 1);
225
a6f79ca5 226 tm_set(t, when);
534215a1
OZ
227 }
228 else
a6f79ca5 229 tm_stop(t);
534215a1 230
28a7d394
OZ
231 /* This is ugly hack, we want to log just timers executed from the main I/O loop */
232 if (loop == &main_timeloop)
233 io_log_event(t->hook, t->data);
234
534215a1
OZ
235 t->hook(t);
236 }
237}
238
239void
240timer_init(void)
241{
242 timers_init(&main_timeloop, &root_pool);
243 timeloop_init_current();
244}
f047271c
OZ
245
246
247/**
248 * tm_parse_time - parse a date and time
249 * @x: time string
250 *
251 * tm_parse_time() takes a textual representation of a date and time
252 * (yyyy-mm-dd[ hh:mm:ss[.sss]]) and converts it to the corresponding value of
253 * type &btime.
254 */
255btime
256tm_parse_time(char *x)
257{
258 struct tm tm;
259 int usec, n1, n2, n3, r;
260
261 r = sscanf(x, "%d-%d-%d%n %d:%d:%d%n.%d%n",
262 &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &n1,
263 &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n2,
264 &usec, &n3);
265
266 if ((r == 3) && !x[n1])
267 tm.tm_hour = tm.tm_min = tm.tm_sec = usec = 0;
268 else if ((r == 6) && !x[n2])
269 usec = 0;
270 else if ((r == 7) && !x[n3])
271 {
272 /* Convert subsecond digits to proper precision */
273 int digits = n3 - n2 - 1;
274 if ((usec < 0) || (usec > 999999) || (digits < 1) || (digits > 6))
275 return 0;
276
277 while (digits++ < 6)
278 usec *= 10;
279 }
280 else
281 return 0;
282
283 tm.tm_mon--;
284 tm.tm_year -= 1900;
285 s64 ts = mktime(&tm);
286 if ((ts == (s64) (time_t) -1) || (ts < 0) || (ts > ((s64) 1 << 40)))
287 return 0;
288
289 return ts S + usec;
290}
291
292/**
293 * tm_format_time - convert date and time to textual representation
294 * @x: destination buffer of size %TM_DATETIME_BUFFER_SIZE
295 * @fmt: specification of resulting textual representation of the time
296 * @t: time
297 *
298 * This function formats the given relative time value @t to a textual
299 * date/time representation (dd-mm-yyyy hh:mm:ss) in real time.
300 */
301void
302tm_format_time(char *x, struct timeformat *fmt, btime t)
303{
304 btime dt = current_time() - t;
305 btime rt = current_real_time() - dt;
306 int v1 = !fmt->limit || (dt < fmt->limit);
307
863ecfc7
OZ
308 if (!tm_format_real_time(x, TM_DATETIME_BUFFER_SIZE, v1 ? fmt->fmt1 : fmt->fmt2, rt))
309 strcpy(x, "<error>");
f047271c
OZ
310}
311
312/* Replace %f in format string with usec scaled to requested precision */
313static int
314strfusec(char *buf, int size, const char *fmt, uint usec)
315{
316 char *str = buf;
317 int parity = 0;
318
319 while (*fmt)
320 {
321 if (!size)
322 return 0;
323
324 if ((fmt[0] == '%') && (!parity) &&
325 ((fmt[1] == 'f') || (fmt[1] >= '1') && (fmt[1] <= '6') && (fmt[2] == 'f')))
326 {
327 int digits = (fmt[1] == 'f') ? 6 : (fmt[1] - '0');
328 uint d = digits, u = usec;
329
330 /* Convert microseconds to requested precision */
331 while (d++ < 6)
332 u /= 10;
333
334 int num = bsnprintf(str, size, "%0*u", digits, u);
335 if (num < 0)
336 return 0;
337
338 fmt += (fmt[1] == 'f') ? 2 : 3;
339 ADVANCE(str, size, num);
340 }
341 else
342 {
343 /* Handle '%%' expression */
344 parity = (*fmt == '%') ? !parity : 0;
345 *str++ = *fmt++;
346 size--;
347 }
348 }
349
350 if (!size)
351 return 0;
352
353 *str = 0;
354 return str - buf;
355}
356
863ecfc7
OZ
357int
358tm_format_real_time(char *x, size_t max, const char *fmt, btime t)
f047271c
OZ
359{
360 s64 t1 = t TO_S;
361 s64 t2 = t - t1 S;
362
363 time_t ts = t1;
364 struct tm tm;
365 if (!localtime_r(&ts, &tm))
863ecfc7 366 return 0;
f047271c
OZ
367
368 byte tbuf[TM_DATETIME_BUFFER_SIZE];
863ecfc7
OZ
369 if (!strfusec(tbuf, max, fmt, t2))
370 return 0;
f047271c 371
863ecfc7
OZ
372 if (!strftime(x, max, tbuf, &tm))
373 return 0;
f047271c 374
863ecfc7 375 return 1;
f047271c 376}