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

timers.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*                  Copyright (c) 1982-2005 AT&T Corp.                  *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                            by AT&T Corp.                             *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                  David Korn <dgk@research.att.com>                   *
*                                                                      *
***********************************************************************/
#pragma prototyped

#include    <ast.h>
#include    <sig.h>
#include    <error.h>
#include    "fault.h"
#include    "defs.h"
#include    "FEATURE/sigfeatures"
#include    "FEATURE/time"

typedef struct _timer
{
      double            wakeup;
      double            incr;
      struct _timer     *next;
      void        (*action)(void*);
      void        *handle;
} Timer_t;

#define IN_ADDTIMEOUT   1
#define IN_SIGALRM      2
#define DEFER_SIGALRM   4
#define SIGALRM_CALL    8

static Timer_t *tptop, *tpmin, *tpfree;
static char time_state;

static double getnow(void)
{
      register double now;
#ifdef timeofday
      struct timeval tp;
      timeofday(&tp);
      now = tp.tv_sec + 1.e-6*tp.tv_usec;

#else
      now = (double)time((time_t*)0);
#endif /* timeofday */
      return(now+.001);
}

/*
 * set an alarm for <t> seconds
 */
static double setalarm(register double t)
{
#if defined(_lib_setitimer) && defined(ITIMER_REAL)
      struct itimerval tnew, told;
      tnew.it_value.tv_sec = t;
      tnew.it_value.tv_usec = 1.e6*(t- (double)tnew.it_value.tv_sec);
      if(t && tnew.it_value.tv_sec==0 && tnew.it_value.tv_usec<1000)
            tnew.it_value.tv_usec = 1000;
      tnew.it_interval.tv_sec = 0;
      tnew.it_interval.tv_usec = 0;
      if(setitimer(ITIMER_REAL,&tnew,&told) < 0)
            errormsg(SH_DICT,ERROR_system(1),e_alarm);
      t = told.it_value.tv_sec + 1.e-6*told.it_value.tv_usec;
#else
      unsigned seconds = (unsigned)t;
      if(t && seconds<1)
            seconds=1;
      t = (double)alarm(seconds);
#endif
      return(t);
}

/* signal handler for alarm call */
static void sigalrm(int sig)
{
      register Timer_t *tp, *tplast, *tpold, *tpnext;
      double now;
      static double left;
      NOT_USED(sig);
      left = 0;
      if(time_state&SIGALRM_CALL)
            time_state &= ~SIGALRM_CALL;
      else if(alarm(0))
            sh_fault(SIGALRM|SH_TRAP);
      if(time_state)
      {
            if(time_state&IN_ADDTIMEOUT)
                  time_state |= DEFER_SIGALRM;
            return;
      }
      time_state |= IN_SIGALRM;
      sigrelease(SIGALRM);
      while(1)
      {
            now = getnow();
            tpold = tpmin = 0;
            for(tplast=0,tp=tptop; tp; tp=tpnext)
            {
                  tpnext = tp->next;
                  if(tp->action)
                  {
                        if(tp->wakeup <=now)
                        {
                              if(!tpold || tpold->wakeup>tp->wakeup)
                                    tpold = tp;
                        }
                        else
                        {
                              if(!tpmin || tpmin->wakeup>tp->wakeup)
                                    tpmin=tp;
                        }
                        tplast = tp;
                  }
                  else
                  {
                        if(tplast)
                              tplast->next = tp->next;
                        else
                              tptop = tp->next;
                        tp->next = tpfree;
                        tpfree = tp;
                  }
            }
            if((tp=tpold) && tp->incr)
            {
                  while((tp->wakeup += tp->incr) <= now);
                  if(!tpmin || tpmin->wakeup>tp->wakeup)
                        tpmin=tp;
            }
            if(tpmin && (left==0 || (tp && tpmin->wakeup < (now+left))))
            {
                  if(left==0)
                        signal(SIGALRM,sigalrm);
                  left = setalarm(tpmin->wakeup-now);
                  if(left && (now+left) < tpmin->wakeup)
                        setalarm(left);
                  else
                        left=tpmin->wakeup-now;
            }
            if(tp)
            {
                  void  (*action)(void*);
                  action = tp->action;
                  if(!tp->incr)
                        tp->action = 0;
                  errno = EINTR;
                  time_state &= ~IN_SIGALRM;
                  (*action)(tp->handle);
                  time_state |= IN_SIGALRM;
            }
            else
                  break;
      }
      if(!tpmin)
            signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL);
      time_state &= ~IN_SIGALRM;
      errno = EINTR;
}

static void oldalrm(void *handle)
{
      Handler_t fn = *(Handler_t*)handle;
      free(handle);
      (*fn)(SIGALRM);
}
      
void *sh_timeradd(unsigned long msec,int flags,void (*action)(void*),void *handle) 
{
      register Timer_t *tp;
      double t;
      Handler_t fn;
      t = ((double)msec)/1000.;
      if(t<=0 || !action)
            return((void*)0);
      if(tp=tpfree)
            tpfree = tp->next;
      else if(!(tp=(Timer_t*)malloc(sizeof(Timer_t))))
            return((void*)0);
      tp->wakeup = getnow() + t;
      tp->incr = (flags?t:0);
      tp->action = action;
      tp->handle = handle;
      time_state |= IN_ADDTIMEOUT;
      tp->next = tptop;
      tptop = tp;
      if(!tpmin || tp->wakeup < tpmin->wakeup)
      {
            tpmin = tp;
            fn = (Handler_t)signal(SIGALRM,sigalrm);
            if((t= setalarm(t))>0 && fn  && fn!=(Handler_t)sigalrm)
            {
                  Handler_t *hp = (Handler_t*)malloc(sizeof(Handler_t));
                  if(hp)
                  {
                        *hp = fn;
                        sh_timeradd((long)(1000*t), 0, oldalrm, (void*)hp);
                  }
            }
            tp = tptop;
      }
      else if(tpmin && !tpmin->action)
            time_state |= DEFER_SIGALRM;
      time_state &= ~IN_ADDTIMEOUT;
      if(time_state&DEFER_SIGALRM)
      {
            time_state=SIGALRM_CALL;
            sigalrm(SIGALRM);
            if(tp!=tptop)
                  tp=0;
      }
      return((void*)tp);
}

/*
 * delete timer <tp>.  If <tp> is NULL, all timers are deleted
 */
void  timerdel(void *handle)
{
      register Timer_t *tp = (Timer_t*)handle;
      if(tp)
            tp->action = 0;
      else
      {
            for(tp=tptop; tp; tp=tp->next)
                  tp->action = 0;
            if(tpmin)
            {
                  tpmin = 0;
                  setalarm((double)0);
            }
            signal(SIGALRM,(sh.sigflag[SIGALRM]&SH_SIGFAULT)?sh_fault:SIG_DFL);
      }
}


Generated by  Doxygen 1.6.0   Back to index