1/* 2 * $Id: timer.c 241182 2011-02-17 21:50:03Z $ 3 * 4 * Authors: 5 * Pedro Roque <roque@di.fc.ul.pt> 6 * Lars Fenneberg <lf@elemental.net> 7 * 8 * This software is Copyright 1996-2000 by the above mentioned author(s), 9 * All Rights Reserved. 10 * 11 * The license which is distributed with this software in the file COPYRIGHT 12 * applies to this software. If your distribution is missing this file, you 13 * may request it from <pekkas@netcore.fi>. 14 * 15 */ 16 17#include <config.h> 18#include <includes.h> 19#include <radvd.h> 20 21static struct timer_lst timers_head = { 22 {LONG_MAX, LONG_MAX}, 23 NULL, NULL, 24 &timers_head, &timers_head 25}; 26 27static void alarm_handler(int sig); 28int inline check_time_diff(struct timer_lst *tm, struct timeval tv); 29 30static void 31schedule_timer(void) 32{ 33 struct timer_lst *tm = timers_head.next; 34 struct timeval tv; 35 36 gettimeofday(&tv, NULL); 37 38 if (tm != &timers_head) 39 { 40 struct itimerval next; 41 42 memset(&next, 0, sizeof(next)); 43 44 timersub(&tm->expires, &tv, &next.it_value); 45 46 signal(SIGALRM, alarm_handler); 47 48 if ((next.it_value.tv_sec > 0) || 49 ((next.it_value.tv_sec == 0) && (next.it_value.tv_usec > 0))) 50 { 51 dlog(LOG_DEBUG, 4, "calling alarm: %ld secs, %ld usecs", 52 next.it_value.tv_sec, next.it_value.tv_usec); 53 54 if(setitimer(ITIMER_REAL, &next, NULL)) 55 flog(LOG_WARNING, "schedule_timer setitimer for %ld.%ld failed: %s", 56 next.it_value.tv_sec, next.it_value.tv_usec, strerror(errno)); 57 } 58 else 59 { 60 dlog(LOG_DEBUG, 4, "next timer has already expired, queueing signal"); 61 kill(getpid(), SIGALRM); 62 } 63 } 64} 65 66void 67set_timer(struct timer_lst *tm, double secs) 68{ 69 struct timeval tv; 70 struct timer_lst *lst; 71 sigset_t bmask, oldmask; 72 struct timeval firein; 73 74 dlog(LOG_DEBUG, 3, "setting timer: %.2f secs", secs); 75 76 firein.tv_sec = (long)secs; 77 firein.tv_usec = (long)((secs - (double)firein.tv_sec) * 1000000); 78 79 dlog(LOG_DEBUG, 5, "setting timer: %ld secs %ld usecs", firein.tv_sec, firein.tv_usec); 80 81 gettimeofday(&tv, NULL); 82 timeradd(&tv, &firein, &tm->expires); 83 84 sigemptyset(&bmask); 85 sigaddset(&bmask, SIGALRM); 86 sigprocmask(SIG_BLOCK, &bmask, &oldmask); 87 88 lst = &timers_head; 89 90 /* the timers are in the list in the order they expire, the soonest first */ 91 do { 92 lst = lst->next; 93 } while ((tm->expires.tv_sec > lst->expires.tv_sec) || 94 ((tm->expires.tv_sec == lst->expires.tv_sec) && 95 (tm->expires.tv_usec > lst->expires.tv_usec))); 96 97 tm->next = lst; 98 tm->prev = lst->prev; 99 lst->prev = tm; 100 tm->prev->next = tm; 101 102 dlog(LOG_DEBUG, 5, "calling schedule_timer from set_timer context"); 103 schedule_timer(); 104 105 sigprocmask(SIG_SETMASK, &oldmask, NULL); 106} 107 108void 109clear_timer(struct timer_lst *tm) 110{ 111 sigset_t bmask, oldmask; 112 113 sigemptyset(&bmask); 114 sigaddset(&bmask, SIGALRM); 115 sigprocmask(SIG_BLOCK, &bmask, &oldmask); 116 117 tm->prev->next = tm->next; 118 tm->next->prev = tm->prev; 119 120 tm->prev = tm->next = NULL; 121 122 dlog(LOG_DEBUG, 5, "calling schedule_timer from clear_timer context"); 123 schedule_timer(); 124 125 sigprocmask(SIG_SETMASK, &oldmask, NULL); 126} 127 128static void 129alarm_handler(int sig) 130{ 131 struct timer_lst *tm, *back; 132 struct timeval tv; 133 gettimeofday(&tv, NULL); 134 tm = timers_head.next; 135 136 /* 137 * This handler is called when the alarm goes off, so at least one of 138 * the interfaces' timers should satisfy the while condition. 139 * 140 * Sadly, this is not always the case, at least on Linux kernels: 141 * see http://lkml.org/lkml/2005/4/29/163. :-(. It seems some 142 * versions of timers are not accurate and get called up to a couple of 143 * hundred microseconds before they expire. 144 * 145 * Therefore we allow some inaccuracy here; it's sufficient for us 146 * that a timer should go off in a millisecond. 147 */ 148 149 /* unused timers are initialized to LONG_MAX so we skip them */ 150 while (tm->expires.tv_sec != LONG_MAX && check_time_diff(tm, tv)) 151 { 152 tm->prev->next = tm->next; 153 tm->next->prev = tm->prev; 154 155 back = tm; 156 tm = tm->next; 157 back->prev = back->next = NULL; 158 159 (*back->handler)(back->data); 160 } 161 162 dlog(LOG_DEBUG, 5, "calling schedule_timer from alarm_handler context"); 163 schedule_timer(); 164} 165 166 167void 168init_timer(struct timer_lst *tm, void (*handler)(void *), void *data) 169{ 170 memset(tm, 0, sizeof(struct timer_lst)); 171 tm->handler = handler; 172 tm->data = data; 173} 174 175int inline 176check_time_diff(struct timer_lst *tm, struct timeval tv) 177{ 178 struct itimerval diff; 179 memset(&diff, 0, sizeof(diff)); 180 181 #define ALLOW_CLOCK_USEC 1000 182 183 timersub(&tm->expires, &tv, &diff.it_value); 184 dlog(LOG_DEBUG, 5, "check_time_diff, difference: %ld sec + %ld usec", 185 diff.it_value.tv_sec, diff.it_value.tv_usec); 186 187 if (diff.it_value.tv_sec <= 0) { 188 /* already gone, this is the "good" case */ 189 if (diff.it_value.tv_sec < 0) 190 return 1; 191#ifdef __linux__ /* we haven't seen this on other OSes */ 192 /* still OK if the expiry time is not too much in the future */ 193 else if (diff.it_value.tv_usec > 0 && 194 diff.it_value.tv_usec <= ALLOW_CLOCK_USEC) { 195 dlog(LOG_DEBUG, 4, "alarm_handler clock was probably off by %ld usec, allowing %u", 196 tm->expires.tv_usec-tv.tv_usec, ALLOW_CLOCK_USEC); 197 return 2; 198 } 199#endif /* __linux__ */ 200 else /* scheduled intentionally in the future? */ 201 return 0; 202 } 203 return 0; 204} 205