Annotation of embedaddon/bird2/lib/timer.c, revision 1.1.1.1
1.1 misho 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:
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: */
28:
29: #include <stdio.h>
30: #include <stdlib.h>
31: #include <time.h>
32:
33: #include "nest/bird.h"
34:
35: #include "lib/heap.h"
36: #include "lib/resource.h"
37: #include "lib/timer.h"
38:
39:
40: struct 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 */
48: pthread_key_t current_time_key;
49:
50: static inline struct timeloop *
51: timeloop_current(void)
52: {
53: return pthread_getspecific(current_time_key);
54: }
55:
56: static inline void
57: timeloop_init_current(void)
58: {
59: pthread_key_create(¤t_time_key, NULL);
60: pthread_setspecific(current_time_key, &main_timeloop);
61: }
62:
63: void wakeup_kick_current(void);
64:
65: #else
66:
67: /* Just use main timelooop */
68: static inline struct timeloop * timeloop_current(void) { return &main_timeloop; }
69: static inline void timeloop_init_current(void) { }
70:
71: #endif
72:
73: btime
74: current_time(void)
75: {
76: return timeloop_current()->last_time;
77: }
78:
79: btime
80: current_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:
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:
96: static void
97: tm_free(resource *r)
98: {
99: timer *t = (void *) r;
100:
101: tm_stop(t);
102: }
103:
104: static void
105: tm_dump(resource *r)
106: {
107: timer *t = (void *) r;
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:
121: static struct resclass tm_class = {
122: "Timer",
123: sizeof(timer),
124: tm_free,
125: tm_dump,
126: NULL,
127: NULL
128: };
129:
130: timer *
131: tm_new(pool *p)
132: {
133: timer *t = ralloc(p, &tm_class);
134: t->index = -1;
135: return t;
136: }
137:
138: void
139: tm_set(timer *t, btime when)
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;
149: HEAP_INSERT(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP);
150: }
151: else if (t->expires < when)
152: {
153: t->expires = when;
154: HEAP_INCREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
155: }
156: else if (t->expires > when)
157: {
158: t->expires = when;
159: HEAP_DECREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
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:
169: void
170: tm_start(timer *t, btime after)
171: {
172: tm_set(t, current_time() + MAX(after, 0));
173: }
174:
175: void
176: tm_stop(timer *t)
177: {
178: if (!t->expires)
179: return;
180:
181: struct timeloop *loop = timeloop_current();
182: uint tc = timers_count(loop);
183:
184: HEAP_DELETE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
185: BUFFER_POP(loop->timers);
186:
187: t->index = -1;
188: t->expires = 0;
189: }
190:
191: void
192: timers_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:
200: void io_log_event(void *hook, void *data);
201:
202: void
203: timers_fire(struct timeloop *loop)
204: {
205: btime base_time;
206: timer *t;
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:
226: tm_set(t, when);
227: }
228: else
229: tm_stop(t);
230:
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:
235: t->hook(t);
236: }
237: }
238:
239: void
240: timer_init(void)
241: {
242: timers_init(&main_timeloop, &root_pool);
243: timeloop_init_current();
244: }
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: */
255: btime
256: tm_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: */
301: void
302: tm_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:
308: if (!tm_format_real_time(x, TM_DATETIME_BUFFER_SIZE, v1 ? fmt->fmt1 : fmt->fmt2, rt))
309: strcpy(x, "<error>");
310: }
311:
312: /* Replace %f in format string with usec scaled to requested precision */
313: static int
314: strfusec(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:
357: int
358: tm_format_real_time(char *x, size_t max, const char *fmt, btime t)
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))
366: return 0;
367:
368: byte tbuf[TM_DATETIME_BUFFER_SIZE];
369: if (!strfusec(tbuf, max, fmt, t2))
370: return 0;
371:
372: if (!strftime(x, max, tbuf, &tm))
373: return 0;
374:
375: return 1;
376: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>