]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - lib/timer.c
Merge remote-tracking branch 'origin/master' into mq-filter-stack
[thirdparty/bird.git] / lib / timer.c
index 2c08b353460f3584413da25534f8eda72b4dfd5b..ddf41340c08778eed55a6b4cf421fc21a96fcdee 100644 (file)
@@ -7,8 +7,28 @@
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
 
+/**
+ * DOC: Timers
+ *
+ * Timers are resources which represent a wish of a module to call a function at
+ * the specified time. The timer code does not guarantee exact timing, only that
+ * a timer function will not be called before the requested time.
+ *
+ * In BIRD, time is represented by values of the &btime type which is signed
+ * 64-bit integer interpreted as a relative number of microseconds since some
+ * fixed time point in past. The current time can be obtained by current_time()
+ * function with reasonable accuracy and is monotonic. There is also a current
+ * 'wall-clock' real time obtainable by current_real_time() reported by OS.
+ *
+ * Each timer is described by a &timer structure containing a pointer to the
+ * handler function (@hook), data private to this function (@data), time the
+ * function should be called at (@expires, 0 for inactive timers), for the other
+ * fields see |timer.h|.
+ */
 
+#include <stdio.h>
 #include <stdlib.h>
+#include <time.h>
 
 #include "nest/bird.h"
 
@@ -74,17 +94,17 @@ current_real_time(void)
 
 
 static void
-tm2_free(resource *r)
+tm_free(resource *r)
 {
-  timer2 *t = (timer2 *) r;
+  timer *t = (void *) r;
 
-  tm2_stop(t);
+  tm_stop(t);
 }
 
 static void
-tm2_dump(resource *r)
+tm_dump(resource *r)
 {
-  timer2 *t = (timer2 *) r;
+  timer *t = (void *) r;
 
   debug("(code %p, data %p, ", t->hook, t->data);
   if (t->randomize)
@@ -98,25 +118,25 @@ tm2_dump(resource *r)
 }
 
 
-static struct resclass tm2_class = {
+static struct resclass tm_class = {
   "Timer",
-  sizeof(timer2),
-  tm2_free,
-  tm2_dump,
+  sizeof(timer),
+  tm_free,
+  tm_dump,
   NULL,
   NULL
 };
 
-timer2 *
-tm2_new(pool *p)
+timer *
+tm_new(pool *p)
 {
-  timer2 *t = ralloc(p, &tm2_class);
+  timer *t = ralloc(p, &tm_class);
   t->index = -1;
   return t;
 }
 
 void
-tm2_set(timer2 *t, btime when)
+tm_set(timer *t, btime when)
 {
   struct timeloop *loop = timeloop_current();
   uint tc = timers_count(loop);
@@ -126,17 +146,17 @@ tm2_set(timer2 *t, btime when)
     t->index = ++tc;
     t->expires = when;
     BUFFER_PUSH(loop->timers) = t;
-    HEAP_INSERT(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP);
+    HEAP_INSERT(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP);
   }
   else if (t->expires < when)
   {
     t->expires = when;
-    HEAP_INCREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
+    HEAP_INCREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
   }
   else if (t->expires > when)
   {
     t->expires = when;
-    HEAP_DECREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
+    HEAP_DECREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
   }
 
 #ifdef CONFIG_BFD
@@ -147,13 +167,13 @@ tm2_set(timer2 *t, btime when)
 }
 
 void
-tm2_start(timer2 *t, btime after)
+tm_start(timer *t, btime after)
 {
-  tm2_set(t, current_time() + MAX(after, 0));
+  tm_set(t, current_time() + MAX(after, 0));
 }
 
 void
-tm2_stop(timer2 *t)
+tm_stop(timer *t)
 {
   if (!t->expires)
     return;
@@ -161,7 +181,7 @@ tm2_stop(timer2 *t)
   struct timeloop *loop = timeloop_current();
   uint tc = timers_count(loop);
 
-  HEAP_DELETE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
+  HEAP_DELETE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
   BUFFER_POP(loop->timers);
 
   t->index = -1;
@@ -183,7 +203,7 @@ void
 timers_fire(struct timeloop *loop)
 {
   btime base_time;
-  timer2 *t;
+  timer *t;
 
   times_update(loop);
   base_time = loop->last_time;
@@ -203,10 +223,10 @@ timers_fire(struct timeloop *loop)
       if (t->randomize)
        when += random() % (t->randomize + 1);
 
-      tm2_set(t, when);
+      tm_set(t, when);
     }
     else
-      tm2_stop(t);
+      tm_stop(t);
 
     /* This is ugly hack, we want to log just timers executed from the main I/O loop */
     if (loop == &main_timeloop)
@@ -222,3 +242,135 @@ timer_init(void)
   timers_init(&main_timeloop, &root_pool);
   timeloop_init_current();
 }
+
+
+/**
+ * tm_parse_time - parse a date and time
+ * @x: time string
+ *
+ * tm_parse_time() takes a textual representation of a date and time
+ * (yyyy-mm-dd[ hh:mm:ss[.sss]]) and converts it to the corresponding value of
+ * type &btime.
+ */
+btime
+tm_parse_time(char *x)
+{
+  struct tm tm;
+  int usec, n1, n2, n3, r;
+
+  r = sscanf(x, "%d-%d-%d%n %d:%d:%d%n.%d%n",
+            &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &n1,
+            &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &n2,
+            &usec, &n3);
+
+  if ((r == 3) && !x[n1])
+    tm.tm_hour = tm.tm_min = tm.tm_sec = usec = 0;
+  else if ((r == 6) && !x[n2])
+    usec = 0;
+  else if ((r == 7) && !x[n3])
+  {
+    /* Convert subsecond digits to proper precision */
+    int digits = n3 - n2 - 1;
+    if ((usec < 0) || (usec > 999999) || (digits < 1) || (digits > 6))
+      return 0;
+
+    while (digits++ < 6)
+      usec *= 10;
+  }
+  else
+    return 0;
+
+  tm.tm_mon--;
+  tm.tm_year -= 1900;
+  s64 ts = mktime(&tm);
+  if ((ts == (s64) (time_t) -1) || (ts < 0) || (ts > ((s64) 1 << 40)))
+    return 0;
+
+  return ts S + usec;
+}
+
+/**
+ * tm_format_time - convert date and time to textual representation
+ * @x: destination buffer of size %TM_DATETIME_BUFFER_SIZE
+ * @fmt: specification of resulting textual representation of the time
+ * @t: time
+ *
+ * This function formats the given relative time value @t to a textual
+ * date/time representation (dd-mm-yyyy hh:mm:ss) in real time.
+ */
+void
+tm_format_time(char *x, struct timeformat *fmt, btime t)
+{
+  btime dt = current_time() - t;
+  btime rt = current_real_time() - dt;
+  int v1 = !fmt->limit || (dt < fmt->limit);
+
+  if (!tm_format_real_time(x, TM_DATETIME_BUFFER_SIZE, v1 ? fmt->fmt1 : fmt->fmt2, rt))
+    strcpy(x, "<error>");
+}
+
+/* Replace %f in format string with usec scaled to requested precision */
+static int
+strfusec(char *buf, int size, const char *fmt, uint usec)
+{
+  char *str = buf;
+  int parity = 0;
+
+  while (*fmt)
+  {
+    if (!size)
+      return 0;
+
+    if ((fmt[0] == '%') && (!parity) &&
+       ((fmt[1] == 'f') || (fmt[1] >= '1') && (fmt[1] <= '6') && (fmt[2] == 'f')))
+    {
+      int digits = (fmt[1] == 'f') ? 6 : (fmt[1] - '0');
+      uint d = digits, u = usec;
+
+      /* Convert microseconds to requested precision */
+      while (d++ < 6)
+       u /= 10;
+
+      int num = bsnprintf(str, size, "%0*u", digits, u);
+      if (num < 0)
+       return 0;
+
+      fmt += (fmt[1] == 'f') ? 2 : 3;
+      ADVANCE(str, size, num);
+    }
+    else
+    {
+      /* Handle '%%' expression */
+      parity = (*fmt == '%') ? !parity : 0;
+      *str++ = *fmt++;
+      size--;
+    }
+  }
+
+  if (!size)
+    return 0;
+
+  *str = 0;
+  return str - buf;
+}
+
+int
+tm_format_real_time(char *x, size_t max, const char *fmt, btime t)
+{
+  s64 t1 = t TO_S;
+  s64 t2 = t - t1 S;
+
+  time_t ts = t1;
+  struct tm tm;
+  if (!localtime_r(&ts, &tm))
+    return 0;
+
+  byte tbuf[TM_DATETIME_BUFFER_SIZE];
+  if (!strfusec(tbuf, max, fmt, t2))
+    return 0;
+
+  if (!strftime(x, max, tbuf, &tm))
+    return 0;
+
+  return 1;
+}