Annotation of embedaddon/bird2/lib/timer.c, revision 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>