Logo Search packages:      
Sourcecode: radvd version File versions  Download package

timer.c

/*
 *   $Id: timer.c,v 1.9 2005/10/18 19:17:29 lutchann Exp $
 *
 *   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;
}

Generated by  Doxygen 1.6.0   Back to index