]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - support/sdk/c/blip/driver/radvd-1.0/timer.c
Merge TinyOS 2.1.1 into master.
[tinyos-2.x.git] / support / sdk / c / blip / driver / radvd-1.0 / timer.c
diff --git a/support/sdk/c/blip/driver/radvd-1.0/timer.c b/support/sdk/c/blip/driver/radvd-1.0/timer.c
new file mode 100644 (file)
index 0000000..4879c66
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ *   $Id$
+ *
+ *   Authors:
+ *    Pedro Roque              <roque@di.fc.ul.pt>
+ *    Lars Fenneberg           <lf@elemental.net>
+ *
+ *   This software is Copyright 1996-2000 by the above mentioned author(s), 
+ *   All Rights Reserved.
+ *
+ *   The license which is distributed with this software in the file COPYRIGHT
+ *   applies to this software. If your distribution is missing this file, you
+ *   may request it from <pekkas@netcore.fi>.
+ *
+ */
+
+#include "config.h"
+#include "includes.h"
+#include "radvd.h"
+
+static struct timer_lst timers_head = {
+       {LONG_MAX, LONG_MAX},
+       NULL, NULL,
+       &timers_head, &timers_head
+};
+
+static void alarm_handler(int sig);
+int inline check_time_diff(struct timer_lst *tm, struct timeval tv);
+
+static void
+schedule_timer(void)
+{
+       struct timer_lst *tm = timers_head.next;
+       struct timeval tv;
+
+       gettimeofday(&tv, NULL);
+
+       if (tm != &timers_head)
+       {
+               struct itimerval next;
+              
+               memset(&next, 0, sizeof(next));
+              
+               timersub(&tm->expires, &tv, &next.it_value);
+
+               signal(SIGALRM, alarm_handler);
+
+               if ((next.it_value.tv_sec > 0) || 
+                               ((next.it_value.tv_sec == 0) && (next.it_value.tv_usec > 0)))
+               {
+                       dlog(LOG_DEBUG, 4, "calling alarm: %ld secs, %ld usecs", 
+                                       next.it_value.tv_sec, next.it_value.tv_usec);
+
+                       if(setitimer(ITIMER_REAL, &next,  NULL))
+                               flog(LOG_WARNING, "schedule_timer setitimer for %ld.%ld failed: %s",
+                                       next.it_value.tv_sec, next.it_value.tv_usec, strerror(errno));
+               }
+               else
+               {
+                       dlog(LOG_DEBUG, 4, "next timer has already expired, queueing signal");  
+                       kill(getpid(), SIGALRM);
+               }
+       }
+}
+
+void
+set_timer(struct timer_lst *tm, double secs)
+{
+       struct timeval tv;
+       struct timer_lst *lst;
+       sigset_t bmask, oldmask;
+       struct timeval firein;
+
+       dlog(LOG_DEBUG, 3, "setting timer: %.2f secs", secs);
+
+       firein.tv_sec = (long)secs;
+       firein.tv_usec = (long)((secs - (double)firein.tv_sec) * 1000000);
+
+       dlog(LOG_DEBUG, 5, "setting timer: %ld secs %ld usecs", firein.tv_sec, firein.tv_usec);
+
+       gettimeofday(&tv, NULL);
+       timeradd(&tv, &firein, &tm->expires);
+
+       sigemptyset(&bmask);
+       sigaddset(&bmask, SIGALRM);
+       sigprocmask(SIG_BLOCK, &bmask, &oldmask);
+
+       lst = &timers_head;
+
+       /* the timers are in the list in the order they expire, the soonest first */
+       do {
+               lst = lst->next;
+       } while ((tm->expires.tv_sec > lst->expires.tv_sec) ||
+                ((tm->expires.tv_sec == lst->expires.tv_sec) && 
+                 (tm->expires.tv_usec > lst->expires.tv_usec)));
+
+       tm->next = lst;
+       tm->prev = lst->prev;
+       lst->prev = tm;
+       tm->prev->next = tm;
+
+       dlog(LOG_DEBUG, 5, "calling schedule_timer from set_timer context");
+       schedule_timer();
+
+       sigprocmask(SIG_SETMASK, &oldmask, NULL);
+}
+
+void
+clear_timer(struct timer_lst *tm)
+{
+       sigset_t bmask, oldmask;
+
+       sigemptyset(&bmask);
+       sigaddset(&bmask, SIGALRM);
+       sigprocmask(SIG_BLOCK, &bmask, &oldmask);
+       
+       tm->prev->next = tm->next;
+       tm->next->prev = tm->prev;
+       
+       tm->prev = tm->next = NULL;
+       
+       dlog(LOG_DEBUG, 5, "calling schedule_timer from clear_timer context");
+       schedule_timer();
+
+       sigprocmask(SIG_SETMASK, &oldmask, NULL);
+}
+
+static void
+alarm_handler(int sig)
+{
+       struct timer_lst *tm, *back;
+       struct timeval tv;
+       gettimeofday(&tv, NULL);
+       tm = timers_head.next;
+
+       /*
+        * This handler is called when the alarm goes off, so at least one of
+        * the interfaces' timers should satisfy the while condition.
+        *
+        * Sadly, this is not always the case, at least on Linux kernels:
+        * see http://lkml.org/lkml/2005/4/29/163. :-(.  It seems some
+        * versions of timers are not accurate and get called up to a couple of
+        * hundred microseconds before they expire.
+        *
+        * Therefore we allow some inaccuracy here; it's sufficient for us
+        * that a timer should go off in a millisecond.
+        */
+
+       /* unused timers are initialized to LONG_MAX so we skip them */
+       while (tm->expires.tv_sec != LONG_MAX && check_time_diff(tm, tv))
+       {               
+               tm->prev->next = tm->next;
+               tm->next->prev = tm->prev;
+
+               back = tm;
+               tm = tm->next;
+               back->prev = back->next = NULL;
+
+               (*back->handler)(back->data);
+       }
+
+       dlog(LOG_DEBUG, 5, "calling schedule_timer from alarm_handler context");
+       schedule_timer();
+}
+
+
+void
+init_timer(struct timer_lst *tm, void (*handler)(void *), void *data)
+{
+       memset(tm, 0, sizeof(struct timer_lst));
+       tm->handler = handler;
+       tm->data = data;
+}
+
+int inline
+check_time_diff(struct timer_lst *tm, struct timeval tv)
+{
+       struct itimerval diff;
+       memset(&diff, 0, sizeof(diff));
+
+       #define ALLOW_CLOCK_USEC 1000
+
+       timersub(&tm->expires, &tv, &diff.it_value);
+       dlog(LOG_DEBUG, 5, "check_time_diff, difference: %ld sec + %ld usec",
+               diff.it_value.tv_sec, diff.it_value.tv_usec);
+
+       if (diff.it_value.tv_sec <= 0) {
+               /* already gone, this is the "good" case */
+               if (diff.it_value.tv_sec < 0)
+                       return 1;
+#ifdef __linux__ /* we haven't seen this on other OSes */
+               /* still OK if the expiry time is not too much in the future */
+               else if (diff.it_value.tv_usec > 0 &&
+                           diff.it_value.tv_usec <= ALLOW_CLOCK_USEC) {
+                       dlog(LOG_DEBUG, 4, "alarm_handler clock was probably off by %ld usec, allowing %u",
+                            tm->expires.tv_usec-tv.tv_usec, ALLOW_CLOCK_USEC);
+                       return 2;
+               }
+#endif /* __linux__ */
+               else /* scheduled intentionally in the future? */
+                       return 0;
+       }
+       return 0;
+}