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