move ansn handling into tc_set.c
[olsrd.git] / src / scheduler.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2009, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 #include "scheduler.h"
43 #include "log.h"
44 #include "link_set.h"
45 #include "olsr.h"
46 #include "olsr_cookie.h"
47 #include "net_os.h"
48 #include "olsr_logging.h"
49
50 #include <sys/times.h>
51
52 #include <unistd.h>
53 #include <assert.h>
54
55 /* Timer data, global. Externed in scheduler.h */
56 uint32_t now_times;                    /* relative time compared to startup (in milliseconds */
57 struct timeval first_tv;               /* timevalue during startup */
58 struct timeval last_tv;                /* timevalue used for last olsr_times() calculation */
59
60 /* Hashed root of all timers */
61 static struct list_node timer_wheel[TIMER_WHEEL_SLOTS];
62 static uint32_t timer_last_run;        /* remember the last timeslot walk */
63
64 /* Memory cookie for the block based memory manager */
65 static struct olsr_cookie_info *timer_mem_cookie = NULL;
66
67 /* Head of all OLSR used sockets */
68 static struct list_node socket_head = { &socket_head, &socket_head };
69
70 /* Prototypes */
71 static void walk_timers(uint32_t *);
72 static void poll_sockets(void);
73 static uint32_t calc_jitter(unsigned int rel_time, uint8_t jitter_pct, unsigned int random_val);
74
75 /*
76  * A wrapper around times(2). Note, that this function has some
77  * portability problems, so do not rely on absolute values returned.
78  * Under Linux, uclibc and libc directly call the sys_times() located
79  * in kernel/sys.c and will only return an error if the tms_buf is
80  * not writeable.
81  */
82 static uint32_t
83 olsr_times(void)
84 {
85   struct timeval tv;
86   uint32_t t;
87
88   if (gettimeofday(&tv, NULL) != 0) {
89     OLSR_ERROR(LOG_SCHEDULER, "OS clock is not working, have to shut down OLSR (%s)\n", strerror(errno));
90     olsr_exit(1);
91   }
92
93   /* test if time jumped backward or more than 60 seconds forward */
94   if (tv.tv_sec < last_tv.tv_sec || (tv.tv_sec == last_tv.tv_sec && tv.tv_usec < last_tv.tv_usec)
95       || tv.tv_sec - last_tv.tv_sec > 60) {
96     OLSR_WARN(LOG_SCHEDULER, "Time jump (%d.%06d to %d.%06d)\n",
97               (int32_t) (last_tv.tv_sec), (int32_t) (last_tv.tv_usec), (int32_t) (tv.tv_sec), (int32_t) (tv.tv_usec));
98
99     t = (last_tv.tv_sec - first_tv.tv_sec) * 1000 + (last_tv.tv_usec - first_tv.tv_usec) / 1000;
100     t++;                        /* advance time by one millisecond */
101
102     first_tv = tv;
103     first_tv.tv_sec -= (t / 1000);
104     first_tv.tv_usec -= ((t % 1000) * 1000);
105
106     if (first_tv.tv_usec < 0) {
107       first_tv.tv_sec--;
108       first_tv.tv_usec += 1000000;
109     }
110     last_tv = tv;
111     return t;
112   }
113   last_tv = tv;
114   return (tv.tv_sec - first_tv.tv_sec) * 1000 + (tv.tv_usec - first_tv.tv_usec) / 1000;
115 }
116
117 /**
118  * Returns a timestamp s seconds in the future
119  */
120 uint32_t
121 olsr_getTimestamp(uint32_t s)
122 {
123   return now_times + s;
124 }
125
126 /**
127  * Returns the number of milliseconds until the timestamp will happen
128  */
129
130 int32_t
131 olsr_getTimeDue(uint32_t s)
132 {
133   uint32_t diff;
134   if (s > now_times) {
135     diff = s - now_times;
136
137     /* overflow ? */
138     if (diff > (1u << 31)) {
139       return -(int32_t) (0xffffffff - diff);
140     }
141     return (int32_t) (diff);
142   }
143
144   diff = now_times - s;
145   /* overflow ? */
146   if (diff > (1u << 31)) {
147     return (int32_t) (0xffffffff - diff);
148   }
149   return -(int32_t) (diff);
150 }
151
152 bool
153 olsr_isTimedOut(uint32_t s)
154 {
155   if (s > now_times) {
156     return s - now_times > (1u << 31);
157   }
158
159   return now_times - s <= (1u << 31);
160 }
161
162 /**
163  * Add a socket and handler to the socketset
164  * beeing used in the main select(2) loop
165  * in listen_loop
166  *
167  *@param fd the socket
168  *@param pf the processing function
169  */
170 void
171 add_olsr_socket(int fd, socket_handler_func pf_pr, socket_handler_func pf_imm, void *data, unsigned int flags)
172 {
173   struct olsr_socket_entry *new_entry;
174
175   if (fd < 0 || (pf_pr == NULL && pf_imm == NULL)) {
176     OLSR_WARN(LOG_SCHEDULER, "Bogus socket entry - not registering...");
177     return;
178   }
179   OLSR_DEBUG(LOG_SCHEDULER, "Adding OLSR socket entry %d\n", fd);
180
181   new_entry = olsr_malloc(sizeof(*new_entry), "Socket entry");
182
183   new_entry->fd = fd;
184   new_entry->process_immediate = pf_imm;
185   new_entry->process_pollrate = pf_pr;
186   new_entry->data = data;
187   new_entry->flags = flags;
188
189   /* Queue */
190   list_node_init(&new_entry->socket_node);
191   list_add_before(&socket_head, &new_entry->socket_node);
192 }
193
194 /**
195  * Remove a socket and handler to the socketset
196  * beeing used in the main select(2) loop
197  * in listen_loop
198  *
199  *@param fd the socket
200  *@param pf the processing function
201  */
202 int
203 remove_olsr_socket(int fd, socket_handler_func pf_pr, socket_handler_func pf_imm)
204 {
205   struct olsr_socket_entry *entry;
206
207   if (fd < 0 || (pf_pr == NULL && pf_imm == NULL)) {
208     OLSR_WARN(LOG_SCHEDULER, "Bogus socket entry - not processing...");
209     return 0;
210   }
211   OLSR_DEBUG(LOG_SCHEDULER, "Removing OLSR socket entry %d\n", fd);
212
213   OLSR_FOR_ALL_SOCKETS(entry) {
214     if (entry->fd == fd && entry->process_immediate == pf_imm && entry->process_pollrate == pf_pr) {
215       list_remove(&entry->socket_node);
216       free(entry);
217       return 1;
218     }
219   }
220   OLSR_FOR_ALL_SOCKETS_END(entry);
221   return 0;
222 }
223
224 void
225 enable_olsr_socket(int fd, socket_handler_func pf_pr, socket_handler_func pf_imm, unsigned int flags)
226 {
227   struct olsr_socket_entry *entry;
228
229   OLSR_FOR_ALL_SOCKETS(entry) {
230     if (entry->fd == fd && entry->process_immediate == pf_imm && entry->process_pollrate == pf_pr) {
231       entry->flags |= flags;
232     }
233   }
234   OLSR_FOR_ALL_SOCKETS_END(entry);
235 }
236
237 void
238 disable_olsr_socket(int fd, socket_handler_func pf_pr, socket_handler_func pf_imm, unsigned int flags)
239 {
240   struct olsr_socket_entry *entry;
241
242   OLSR_FOR_ALL_SOCKETS(entry) {
243     if (entry->fd == fd && entry->process_immediate == pf_imm && entry->process_pollrate == pf_pr) {
244       entry->flags &= ~flags;
245     }
246   }
247   OLSR_FOR_ALL_SOCKETS_END(entry);
248 }
249
250 /**
251  * Close and free all sockets.
252  */
253 void
254 olsr_flush_sockets(void)
255 {
256   struct olsr_socket_entry *entry;
257
258   OLSR_FOR_ALL_SOCKETS(entry) {
259     CLOSESOCKET(entry->fd);
260     list_remove(&entry->socket_node);
261     free(entry);
262   } OLSR_FOR_ALL_SOCKETS_END(entry);
263 }
264
265 static void
266 poll_sockets(void)
267 {
268   int n;
269   struct olsr_socket_entry *entry;
270   fd_set ibits, obits;
271   struct timeval tvp = { 0, 0 };
272   int hfd = 0, fdsets = 0;
273
274   /* If there are no registered sockets we
275    * do not call select(2)
276    */
277   if (list_is_empty(&socket_head)) {
278     return;
279   }
280
281   FD_ZERO(&ibits);
282   FD_ZERO(&obits);
283
284   /* Adding file-descriptors to FD set */
285   OLSR_FOR_ALL_SOCKETS(entry) {
286     if (entry->process_pollrate == NULL) {
287       continue;
288     }
289     if ((entry->flags & SP_PR_READ) != 0) {
290       fdsets |= SP_PR_READ;
291       FD_SET((unsigned int)entry->fd, &ibits);  /* And we cast here since we get a warning on Win32 */
292     }
293     if ((entry->flags & SP_PR_WRITE) != 0) {
294       fdsets |= SP_PR_WRITE;
295       FD_SET((unsigned int)entry->fd, &obits);  /* And we cast here since we get a warning on Win32 */
296     }
297     if ((entry->flags & (SP_PR_READ | SP_PR_WRITE)) != 0 && entry->fd >= hfd) {
298       hfd = entry->fd + 1;
299     }
300   }
301   OLSR_FOR_ALL_SOCKETS_END(entry);
302
303   /* Running select on the FD set */
304   do {
305     n = olsr_select(hfd, fdsets & SP_PR_READ ? &ibits : NULL, fdsets & SP_PR_WRITE ? &obits : NULL, NULL, &tvp);
306   } while (n == -1 && errno == EINTR);
307
308   if (n == 0) {
309     return;
310   }
311   if (n == -1) {                /* Did something go wrong? */
312     OLSR_WARN(LOG_SCHEDULER, "select error: %s", strerror(errno));
313     return;
314   }
315
316   /* Update time since this is much used by the parsing functions */
317   now_times = olsr_times();
318   OLSR_FOR_ALL_SOCKETS(entry) {
319     int flags;
320     if (entry->process_pollrate == NULL) {
321       continue;
322     }
323     flags = 0;
324     if (FD_ISSET(entry->fd, &ibits)) {
325       flags |= SP_PR_READ;
326     }
327     if (FD_ISSET(entry->fd, &obits)) {
328       flags |= SP_PR_WRITE;
329     }
330     if (flags != 0) {
331       entry->process_pollrate(entry->fd, entry->data, flags);
332     }
333   }
334   OLSR_FOR_ALL_SOCKETS_END(entry);
335 }
336
337 static void
338 handle_fds(uint32_t next_interval)
339 {
340   struct timeval tvp;
341   int32_t remaining;
342
343   /* calculate the first timeout */
344   now_times = olsr_times();
345
346   remaining = TIME_DUE(next_interval);
347   if (remaining <= 0) {
348     /* we are already over the interval */
349     if (list_is_empty(&socket_head)) {
350       /* If there are no registered sockets we do not call select(2) */
351       return;
352     }
353     tvp.tv_sec = 0;
354     tvp.tv_usec = 0;
355   } else {
356     /* we need an absolute time - milliseconds */
357     tvp.tv_sec = remaining / MSEC_PER_SEC;
358     tvp.tv_usec = (remaining % MSEC_PER_SEC) * USEC_PER_MSEC;
359   }
360
361   /* do at least one select */
362   for (;;) {
363     struct olsr_socket_entry *entry;
364     fd_set ibits, obits;
365     int n, hfd = 0, fdsets = 0;
366     FD_ZERO(&ibits);
367     FD_ZERO(&obits);
368
369     /* Adding file-descriptors to FD set */
370     OLSR_FOR_ALL_SOCKETS(entry) {
371       if (entry->process_immediate == NULL) {
372         continue;
373       }
374       if ((entry->flags & SP_IMM_READ) != 0) {
375         fdsets |= SP_IMM_READ;
376         FD_SET((unsigned int)entry->fd, &ibits);        /* And we cast here since we get a warning on Win32 */
377       }
378       if ((entry->flags & SP_IMM_WRITE) != 0) {
379         fdsets |= SP_IMM_WRITE;
380         FD_SET((unsigned int)entry->fd, &obits);        /* And we cast here since we get a warning on Win32 */
381       }
382       if ((entry->flags & (SP_IMM_READ | SP_IMM_WRITE)) != 0 && entry->fd >= hfd) {
383         hfd = entry->fd + 1;
384       }
385     }
386     OLSR_FOR_ALL_SOCKETS_END(entry);
387
388     if (hfd == 0 && (long)remaining <= 0) {
389       /* we are over the interval and we have no fd's. Skip the select() etc. */
390       return;
391     }
392
393     do {
394       n = olsr_select(hfd, fdsets & SP_IMM_READ ? &ibits : NULL, fdsets & SP_IMM_WRITE ? &obits : NULL, NULL, &tvp);
395     } while (n == -1 && errno == EINTR);
396
397     if (n == 0) {               /* timeout! */
398       break;
399     }
400     if (n == -1) {              /* Did something go wrong? */
401       OLSR_WARN(LOG_SCHEDULER, "select error: %s", strerror(errno));
402       break;
403     }
404
405     /* Update time since this is much used by the parsing functions */
406     now_times = olsr_times();
407     OLSR_FOR_ALL_SOCKETS(entry) {
408       int flags;
409       if (entry->process_immediate == NULL) {
410         continue;
411       }
412       flags = 0;
413       if (FD_ISSET(entry->fd, &ibits)) {
414         flags |= SP_IMM_READ;
415       }
416       if (FD_ISSET(entry->fd, &obits)) {
417         flags |= SP_IMM_WRITE;
418       }
419       if (flags != 0) {
420         entry->process_immediate(entry->fd, entry->data, flags);
421       }
422     }
423     OLSR_FOR_ALL_SOCKETS_END(entry);
424
425     /* calculate the next timeout */
426     remaining = TIME_DUE(next_interval);
427     if (remaining <= 0) {
428       /* we are already over the interval */
429       break;
430     }
431     /* we need an absolute time - milliseconds */
432     tvp.tv_sec = remaining / MSEC_PER_SEC;
433     tvp.tv_usec = (remaining % MSEC_PER_SEC) * USEC_PER_MSEC;
434   }
435 }
436
437 /**
438  * Main scheduler event loop. Polls at every
439  * sched_poll_interval and calls all functions
440  * that are timed out or that are triggered.
441  * Also calls the olsr_process_changes()
442  * function at every poll.
443  *
444  * @return nada
445  */
446 void
447 olsr_scheduler(void)
448 {
449   OLSR_INFO(LOG_SCHEDULER, "Scheduler started - polling every %u ms\n", olsr_cnf->pollrate);
450
451   /* Main scheduler loop */
452   while (app_state == STATE_RUNNING) {
453     uint32_t next_interval;
454
455     /*
456      * Update the global timestamp. We are using a non-wallclock timer here
457      * to avoid any undesired side effects if the system clock changes.
458      */
459     now_times = olsr_times();
460     next_interval = GET_TIMESTAMP(olsr_cnf->pollrate);
461
462     /* Read incoming data */
463     poll_sockets();
464
465     /* Process timers */
466     walk_timers(&timer_last_run);
467
468     /* Update */
469     olsr_process_changes();
470
471     /* Check for changes in topology */
472     if (link_changes) {
473       increase_local_ansn_number();
474       OLSR_DEBUG(LOG_SCHEDULER, "ANSN UPDATED %d\n\n", get_local_ansn_number());
475       link_changes = false;
476     }
477
478     /* Read incoming data and handle it immediiately */
479     handle_fds(next_interval);
480   }
481 }
482
483 /**
484  * Decrement a relative timer by a random number range.
485  *
486  * @param the relative timer expressed in units of milliseconds.
487  * @param the jitter in percent
488  * @param cached result of random() at system init.
489  * @return the absolute timer in system clock tick units
490  */
491 static uint32_t
492 calc_jitter(unsigned int rel_time, uint8_t jitter_pct, unsigned int random_val)
493 {
494   unsigned int jitter_time;
495
496   /*
497    * No jitter or, jitter larger than 99% does not make sense.
498    * Also protect against overflows resulting from > 25 bit timers.
499    */
500   if (jitter_pct == 0 || jitter_pct > 99 || rel_time > (1 << 24)) {
501     return GET_TIMESTAMP(rel_time);
502   }
503
504   /*
505    * Play some tricks to avoid overflows with integer arithmetic.
506    */
507   jitter_time = (jitter_pct * rel_time) / 100;
508   jitter_time = random_val / (1 + RAND_MAX / (jitter_time + 1));
509
510   OLSR_DEBUG(LOG_SCHEDULER, "TIMER: jitter %u%% rel_time %ums to %ums\n", jitter_pct, rel_time, rel_time - jitter_time);
511
512   return GET_TIMESTAMP(rel_time - jitter_time);
513 }
514
515 /**
516  * Init datastructures for maintaining timers.
517  */
518 void
519 olsr_init_timers(void)
520 {
521   int idx;
522
523   OLSR_INFO(LOG_SCHEDULER, "Initializing scheduler.\n");
524
525   /* Grab initial timestamp */
526   if (gettimeofday(&first_tv, NULL)) {
527     OLSR_ERROR(LOG_SCHEDULER, "OS clock is not working, have to shut down OLSR (%s)\n", strerror(errno));
528     olsr_exit(1);
529   }
530   last_tv = first_tv;
531   now_times = olsr_times();
532
533   for (idx = 0; idx < TIMER_WHEEL_SLOTS; idx++) {
534     list_head_init(&timer_wheel[idx]);
535   }
536
537   /*
538    * Reset the last timer run.
539    */
540   timer_last_run = now_times;
541
542   /* Allocate a cookie for the block based memeory manager. */
543   timer_mem_cookie = olsr_alloc_cookie("timer_entry", OLSR_COOKIE_TYPE_MEMORY);
544   olsr_cookie_set_memory_size(timer_mem_cookie, sizeof(struct timer_entry));
545   olsr_cookie_set_memory_clear(timer_mem_cookie, false);
546 }
547
548 /**
549  * Walk through the timer list and check if any timer is ready to fire.
550  * Callback the provided function with the context pointer.
551  */
552 static void
553 walk_timers(uint32_t * last_run)
554 {
555   unsigned int total_timers_walked = 0, total_timers_fired = 0;
556   unsigned int wheel_slot_walks = 0;
557
558   /*
559    * Check the required wheel slots since the last time a timer walk was invoked,
560    * or check *all* the wheel slots, whatever is less work.
561    * The latter is meant as a safety belt if the scheduler falls behind.
562    */
563   while ((*last_run <= now_times) && (wheel_slot_walks < TIMER_WHEEL_SLOTS)) {
564     struct list_node tmp_head_node;
565     /* keep some statistics */
566     unsigned int timers_walked = 0, timers_fired = 0;
567
568     /* Get the hash slot for this clocktick */
569     struct list_node *const timer_head_node = &timer_wheel[*last_run & TIMER_WHEEL_MASK];
570
571     /* Walk all entries hanging off this hash bucket. We treat this basically as a stack
572      * so that we always know if and where the next element is.
573      */
574     list_head_init(&tmp_head_node);
575     while (!list_is_empty(timer_head_node)) {
576       /* the top element */
577       struct list_node *const timer_node = timer_head_node->next;
578       struct timer_entry *const timer = list2timer(timer_node);
579
580       /*
581        * Dequeue and insert to a temporary list.
582        * We do this to avoid loosing our walking context when
583        * multiple timers fire.
584        */
585       list_remove(timer_node);
586       list_add_after(&tmp_head_node, timer_node);
587       timers_walked++;
588
589       /* Ready to fire ? */
590       if (TIMED_OUT(timer->timer_clock)) {
591
592         OLSR_DEBUG(LOG_SCHEDULER, "TIMER: fire %s timer %p, ctx %p, "
593                    "at clocktick %u (%s)\n",
594                    timer->timer_cookie->ci_name,
595                    timer, timer->timer_cb_context, (unsigned int)*last_run, olsr_wallclock_string());
596
597         /* This timer is expired, call into the provided callback function */
598         timer->timer_cb(timer->timer_cb_context);
599
600         /* Only act on actually running timers */
601         if (timer->timer_flags & OLSR_TIMER_RUNNING) {
602           /*
603            * Don't restart the periodic timer if the callback function has
604            * stopped the timer.
605            */
606           if (timer->timer_period) {
607             /* For periodical timers, rehash the random number and restart */
608             timer->timer_random = random();
609             olsr_change_timer(timer, timer->timer_period, timer->timer_jitter_pct, OLSR_TIMER_PERIODIC);
610           } else {
611             /* Singleshot timers are stopped */
612             olsr_stop_timer(timer);
613           }
614         }
615
616         timers_fired++;
617       }
618     }
619
620     /*
621      * Now merge the temporary list back to the old bucket.
622      */
623     list_merge(timer_head_node, &tmp_head_node);
624
625     /* keep some statistics */
626     total_timers_walked += timers_walked;
627     total_timers_fired += timers_fired;
628
629     /* Increment the time slot and wheel slot walk iteration */
630     (*last_run)++;
631     wheel_slot_walks++;
632   }
633
634   OLSR_DEBUG(LOG_SCHEDULER, "TIMER: processed %4u/%d clockwheel slots, "
635              "timers walked %4u/%u, timers fired %u\n",
636              wheel_slot_walks, TIMER_WHEEL_SLOTS, total_timers_walked, timer_mem_cookie->ci_usage, total_timers_fired);
637
638   /*
639    * If the scheduler has slipped and we have walked all wheel slots,
640    * reset the last timer run.
641    */
642   *last_run = now_times;
643 }
644
645 /**
646  * Stop and delete all timers.
647  */
648 void
649 olsr_flush_timers(void)
650 {
651   struct list_node *timer_head_node;
652   unsigned int wheel_slot = 0;
653
654   for (wheel_slot = 0; wheel_slot < TIMER_WHEEL_SLOTS; wheel_slot++) {
655     timer_head_node = &timer_wheel[wheel_slot & TIMER_WHEEL_MASK];
656
657     /* Kill all entries hanging off this hash bucket. */
658     while (!list_is_empty(timer_head_node)) {
659       olsr_stop_timer(list2timer(timer_head_node->next));
660     }
661   }
662 }
663
664 /**
665  * Returns the difference between gmt and local time in seconds.
666  * Use gmtime() and localtime() to keep things simple.
667  *
668  * taken and slightly modified from www.tcpdump.org.
669  */
670 static int
671 olsr_get_timezone(void)
672 {
673 #define OLSR_TIMEZONE_UNINITIALIZED -1
674   static int time_diff = OLSR_TIMEZONE_UNINITIALIZED;
675   if (time_diff == OLSR_TIMEZONE_UNINITIALIZED) {
676     int dir;
677     const time_t t = time(NULL);
678     const struct tm gmt = *gmtime(&t);
679     const struct tm *loc = localtime(&t);
680
681     time_diff = (loc->tm_hour - gmt.tm_hour) * 60 * 60 + (loc->tm_min - gmt.tm_min) * 60;
682
683     /*
684      * If the year or julian day is different, we span 00:00 GMT
685      * and must add or subtract a day. Check the year first to
686      * avoid problems when the julian day wraps.
687      */
688     dir = loc->tm_year - gmt.tm_year;
689     if (!dir) {
690       dir = loc->tm_yday - gmt.tm_yday;
691     }
692
693     time_diff += dir * 24 * 60 * 60;
694   }
695   return time_diff;
696 }
697
698 /**
699  * Format an absolute wallclock system time string.
700  * May be called upto 4 times in a single printf() statement.
701  * Displays microsecond resolution.
702  *
703  * @return buffer to a formatted system time string.
704  */
705 const char *
706 olsr_wallclock_string(void)
707 {
708   static char buf[sizeof("00:00:00.000000")];
709   struct timeval now;
710   int sec, usec;
711
712   gettimeofday(&now, NULL);
713
714   sec = (int)now.tv_sec + olsr_get_timezone();
715   usec = (int)now.tv_usec;
716
717   snprintf(buf, sizeof(buf), "%02d:%02d:%02d.%06d", (sec % 86400) / 3600, (sec % 3600) / 60, sec % 60, usec);
718
719   return buf;
720 }
721
722 /**
723  * Format an relative non-wallclock system time string.
724  * May be called upto 4 times in a single printf() statement.
725  * Displays millisecond resolution.
726  *
727  * @param absolute time expressed in clockticks
728  * @return buffer to a formatted system time string.
729  */
730 const char *
731 olsr_clock_string(uint32_t clk)
732 {
733   static char buf[sizeof("00:00:00.000")];
734
735   /* On most systems a clocktick is a 10ms quantity. */
736   unsigned int msec = clk % 1000;
737   unsigned int sec = clk / 1000;
738
739   snprintf(buf, sizeof(buf), "%02u:%02u:%02u.%03u", sec / 3600, (sec % 3600) / 60, (sec % 60), (msec % MSEC_PER_SEC));
740
741   return buf;
742 }
743
744 /**
745  * Start a new timer.
746  *
747  * @param relative time expressed in milliseconds
748  * @param jitter expressed in percent
749  * @param timer callback function
750  * @param context for the callback function
751  * @return a pointer to the created entry
752  */
753 struct timer_entry *
754 olsr_start_timer(unsigned int rel_time,
755                  uint8_t jitter_pct, bool periodical, timer_cb_func cb_func, void *context, struct olsr_cookie_info *ci)
756 {
757   struct timer_entry *timer;
758
759   assert(ci != 0);          /* we want timer cookies everywhere */
760
761   timer = olsr_cookie_malloc(timer_mem_cookie);
762
763   /*
764    * Compute random numbers only once.
765    */
766   if (!timer->timer_random) {
767     timer->timer_random = random();
768   }
769
770   /* Fill entry */
771   timer->timer_clock = calc_jitter(rel_time, jitter_pct, timer->timer_random);
772   timer->timer_cb = cb_func;
773   timer->timer_cb_context = context;
774   timer->timer_jitter_pct = jitter_pct;
775   timer->timer_flags = OLSR_TIMER_RUNNING;
776
777   /* The cookie is used for debugging to traceback the originator */
778   timer->timer_cookie = ci;
779   olsr_cookie_usage_incr(ci);
780
781   /* Singleshot or periodical timer ? */
782   timer->timer_period = periodical ? rel_time : 0;
783
784   /*
785    * Now insert in the respective timer_wheel slot.
786    */
787   list_add_before(&timer_wheel[timer->timer_clock & TIMER_WHEEL_MASK], &timer->timer_list);
788
789   OLSR_DEBUG(LOG_SCHEDULER, "TIMER: start %s timer %p firing in %s, ctx %p\n",
790              ci->ci_name, timer, olsr_clock_string(timer->timer_clock), context);
791
792   return timer;
793 }
794
795 /**
796  * Delete a timer.
797  *
798  * @param the timer_entry that shall be removed
799  * @return nada
800  */
801 void
802 olsr_stop_timer(struct timer_entry *timer)
803 {
804   /* It's okay to get a NULL here */
805   if (!timer) {
806     return;
807   }
808
809   assert(timer->timer_cookie);     /* we want timer cookies everywhere */
810
811   OLSR_DEBUG(LOG_SCHEDULER, "TIMER: stop %s timer %p, ctx %p\n",
812              timer->timer_cookie->ci_name, timer, timer->timer_cb_context);
813
814
815   /*
816    * Carve out of the existing wheel_slot and free.
817    */
818   list_remove(&timer->timer_list);
819   timer->timer_flags &= ~OLSR_TIMER_RUNNING;
820   olsr_cookie_usage_decr(timer->timer_cookie);
821
822   olsr_cookie_free(timer_mem_cookie, timer);
823 }
824
825 /**
826  * Change a timer_entry.
827  *
828  * @param timer_entry to be changed.
829  * @param new relative time expressed in units of milliseconds.
830  * @param new jitter expressed in percent.
831  * @return nada
832  */
833 void
834 olsr_change_timer(struct timer_entry *timer, unsigned int rel_time, uint8_t jitter_pct, bool periodical)
835 {
836   /* Sanity check. */
837   if (!timer) {
838     return;
839   }
840
841   assert(timer->timer_cookie);     /* we want timer cookies everywhere */
842
843   /* Singleshot or periodical timer ? */
844   timer->timer_period = periodical ? rel_time : 0;
845
846   timer->timer_clock = calc_jitter(rel_time, jitter_pct, timer->timer_random);
847   timer->timer_jitter_pct = jitter_pct;
848
849   /*
850    * Changes are easy: Remove timer from the exisiting timer_wheel slot
851    * and reinsert into the new slot.
852    */
853   list_remove(&timer->timer_list);
854   list_add_before(&timer_wheel[timer->timer_clock & TIMER_WHEEL_MASK], &timer->timer_list);
855
856   OLSR_DEBUG(LOG_SCHEDULER, "TIMER: change %s timer %p, firing to %s, ctx %p\n",
857              timer->timer_cookie->ci_name, timer, olsr_clock_string(timer->timer_clock), timer->timer_cb_context);
858 }
859
860 /*
861  * This is the one stop shop for all sort of timer manipulation.
862  * Depending on the paseed in parameters a new timer is started,
863  * or an existing timer is started or an existing timer is
864  * terminated.
865  */
866 void
867 olsr_set_timer(struct timer_entry **timer_ptr,
868                unsigned int rel_time,
869                uint8_t jitter_pct, bool periodical, timer_cb_func cb_func, void *context, struct olsr_cookie_info *cookie)
870 {
871   assert(cookie);          /* we want timer cookies everywhere */
872   if (!*timer_ptr) {
873     /* No timer running, kick it. */
874     *timer_ptr = olsr_start_timer(rel_time, jitter_pct, periodical, cb_func, context, cookie);
875   } else {
876     if (!rel_time) {
877       /* No good future time provided, kill it. */
878       olsr_stop_timer(*timer_ptr);
879       *timer_ptr = NULL;
880     } else {
881       /* Time is ok and timer is running, change it ! */
882       olsr_change_timer(*timer_ptr, rel_time, jitter_pct, periodical);
883     }
884   }
885 }
886
887 /*
888  * Local Variables:
889  * c-basic-offset: 2
890  * indent-tabs-mode: nil
891  * End:
892  */