Statistics
| Branch: | Revision:

iof-bird / bird-2.0.1 / lib / timer.c @ 6b3f1a54

History | View | Annotate | Download (7.77 KB)

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
  tm_format_real_time(x, v1 ? fmt->fmt1 : fmt->fmt2, rt);
309
}
310

    
311
/* Replace %f in format string with usec scaled to requested precision */
312
static int
313
strfusec(char *buf, int size, const char *fmt, uint usec)
314
{
315
  char *str = buf;
316
  int parity = 0;
317

    
318
  while (*fmt)
319
  {
320
    if (!size)
321
      return 0;
322

    
323
    if ((fmt[0] == '%') && (!parity) &&
324
        ((fmt[1] == 'f') || (fmt[1] >= '1') && (fmt[1] <= '6') && (fmt[2] == 'f')))
325
    {
326
      int digits = (fmt[1] == 'f') ? 6 : (fmt[1] - '0');
327
      uint d = digits, u = usec;
328

    
329
      /* Convert microseconds to requested precision */
330
      while (d++ < 6)
331
        u /= 10;
332

    
333
      int num = bsnprintf(str, size, "%0*u", digits, u);
334
      if (num < 0)
335
        return 0;
336

    
337
      fmt += (fmt[1] == 'f') ? 2 : 3;
338
      ADVANCE(str, size, num);
339
    }
340
    else
341
    {
342
      /* Handle '%%' expression */
343
      parity = (*fmt == '%') ? !parity : 0;
344
      *str++ = *fmt++;
345
      size--;
346
    }
347
  }
348

    
349
  if (!size)
350
    return 0;
351

    
352
  *str = 0;
353
  return str - buf;
354
}
355

    
356
void
357
tm_format_real_time(char *x, const char *fmt, btime t)
358
{
359
  s64 t1 = t TO_S;
360
  s64 t2 = t - t1 S;
361

    
362
  time_t ts = t1;
363
  struct tm tm;
364
  if (!localtime_r(&ts, &tm))
365
    goto err;
366

    
367
  byte tbuf[TM_DATETIME_BUFFER_SIZE];
368
  if (!strfusec(tbuf, TM_DATETIME_BUFFER_SIZE, fmt, t2))
369
    goto err;
370

    
371
  if (!strftime(x, TM_DATETIME_BUFFER_SIZE, tbuf, &tm))
372
    goto err;
373

    
374
  return;
375

    
376
err:
377
  strcpy(x, "<error>");
378
}