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(&current_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>