timer.c revision 32663
1/*
2 *		PPP Timer Processing Module
3 *
4 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5 *
6 *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7 *
8 * Redistribution and use in source and binary forms are permitted
9 * provided that the above copyright notice and this paragraph are
10 * duplicated in all such forms and that any documentation,
11 * advertising materials, and other materials related to such
12 * distribution and use acknowledge that the software was developed
13 * by the Internet Initiative Japan, Inc.  The name of the
14 * IIJ may not be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 *
20 * $Id: timer.c,v 1.26 1997/12/29 22:23:52 brian Exp $
21 *
22 *  TODO:
23 */
24
25#include <signal.h>
26#ifdef SIGALRM
27#include <errno.h>
28#endif
29#include <sys/time.h>
30#include <unistd.h>
31
32#include "command.h"
33#include "mbuf.h"
34#include "log.h"
35#include "sig.h"
36#include "timer.h"
37
38static struct pppTimer *TimerList = NULL;
39
40static void StopTimerNoBlock(struct pppTimer *);
41static void InitTimerService(void);
42
43void
44StopTimer(struct pppTimer * tp)
45{
46#ifdef SIGALRM
47  int omask;
48
49  omask = sigblock(sigmask(SIGALRM));
50#endif
51  StopTimerNoBlock(tp);
52#ifdef SIGALRM
53  sigsetmask(omask);
54#endif
55}
56
57void
58StartTimer(struct pppTimer * tp)
59{
60  struct pppTimer *t, *pt;
61  u_long ticks = 0;
62
63#ifdef SIGALRM
64  int omask;
65
66  omask = sigblock(sigmask(SIGALRM));
67#endif
68
69  if (tp->state != TIMER_STOPPED) {
70    StopTimerNoBlock(tp);
71  }
72  if (tp->load == 0) {
73    LogPrintf(LogDEBUG, "timer %x has 0 load!\n", tp);
74    sigsetmask(omask);
75    return;
76  }
77  pt = NULL;
78  for (t = TimerList; t; t = t->next) {
79    LogPrintf(LogDEBUG, "StartTimer: %x(%d):  ticks: %d, rest: %d\n",
80	      t, t->state, ticks, t->rest);
81    if (ticks + t->rest >= tp->load)
82      break;
83    ticks += t->rest;
84    pt = t;
85  }
86
87  tp->state = TIMER_RUNNING;
88  tp->rest = tp->load - ticks;
89  LogPrintf(LogDEBUG, "StartTimer: Inserting %x before %x, rest = %d\n",
90	    tp, t, tp->rest);
91  /* Insert given *tp just before *t */
92  tp->next = t;
93  if (pt) {
94    pt->next = tp;
95  } else {
96    InitTimerService();
97    TimerList = tp;
98  }
99  if (t)
100    t->rest -= tp->rest;
101
102#ifdef SIGALRM
103  sigsetmask(omask);
104#endif
105}
106
107static void
108StopTimerNoBlock(struct pppTimer * tp)
109{
110  struct pppTimer *t, *pt;
111
112  /*
113   * A Running Timer should be removing TimerList, But STOPPED/EXPIRED is
114   * already removing TimerList. So just marked as TIMER_STOPPED. Do not
115   * change tp->enext!! (Might be Called by expired proc)
116   */
117  LogPrintf(LogDEBUG, "StopTimer: %x, next = %x state=%x\n",
118	    tp, tp->next, tp->state);
119  if (tp->state != TIMER_RUNNING) {
120    tp->next = NULL;
121    tp->state = TIMER_STOPPED;
122    return;
123  }
124  pt = NULL;
125  for (t = TimerList; t != tp && t != NULL; t = t->next)
126    pt = t;
127  if (t) {
128    if (pt) {
129      pt->next = t->next;
130    } else {
131      TimerList = t->next;
132      if (TimerList == NULL)	/* Last one ? */
133	TermTimerService();	/* Terminate Timer Service */
134    }
135    if (t->next)
136      t->next->rest += tp->rest;
137  } else
138    LogPrintf(LogERROR, "Oops, timer not found!!\n");
139
140  tp->next = NULL;
141  tp->state = TIMER_STOPPED;
142}
143
144static void
145TimerService()
146{
147  struct pppTimer *tp, *exp, *wt;
148
149  if (LogIsKept(LogDEBUG))
150    ShowTimers();
151  tp = TimerList;
152  if (tp) {
153    tp->rest--;
154    if (tp->rest == 0) {
155
156      /*
157       * Multiple timers may expires at once. Create list of expired timers.
158       */
159      exp = NULL;
160      do {
161	tp->state = TIMER_EXPIRED;
162	wt = tp->next;
163	tp->enext = exp;
164	exp = tp;
165	LogPrintf(LogDEBUG, "TimerService: Add %x to exp\n", tp);
166	tp = wt;
167      } while (tp && (tp->rest == 0));
168
169      TimerList = tp;
170      if (TimerList == NULL)	/* No timers ? */
171	TermTimerService();	/* Terminate Timer Service */
172      LogPrintf(LogDEBUG, "TimerService: next is %x(%d)\n",
173		TimerList, TimerList ? TimerList->rest : 0);
174
175      /*
176       * Process all expired timers.
177       */
178      while (exp) {
179#ifdef notdef
180	StopTimer(exp);
181#endif
182	if (exp->func)
183	  (*exp->func) (exp->arg);
184
185	/*
186	 * Just Removing each item from expired list And exp->enext will be
187	 * intialized at next expire in this funtion.
188	 */
189	exp = exp->enext;
190      }
191    }
192  }
193}
194
195void
196ShowTimers()
197{
198  struct pppTimer *pt;
199
200  LogPrintf(LogDEBUG, "---- Begin of Timer Service List---\n");
201  for (pt = TimerList; pt; pt = pt->next)
202    LogPrintf(LogDEBUG, "%x: load = %d, rest = %d, state =%x\n",
203	      pt, pt->load, pt->rest, pt->state);
204  LogPrintf(LogDEBUG, "---- End of Timer Service List ---\n");
205}
206
207#ifdef SIGALRM
208
209static void
210nointr_dosleep(u_int sec, u_int usec)
211{
212  struct timeval to, st, et;
213
214  gettimeofday(&st, NULL);
215  et.tv_sec = st.tv_sec + sec;
216  et.tv_usec = st.tv_usec + usec;
217  to.tv_sec = sec;
218  to.tv_usec = usec;
219  for (;;) {
220    if (select(0, NULL, NULL, NULL, &to) == 0 ||
221	errno != EINTR) {
222      break;
223    } else {
224      gettimeofday(&to, NULL);
225      if (to.tv_sec > et.tv_sec + 1 ||
226          (to.tv_sec == et.tv_sec + 1 && to.tv_usec > et.tv_usec) ||
227          to.tv_sec < st.tv_sec ||
228          (to.tv_sec == st.tv_sec && to.tv_usec < st.tv_usec)) {
229        LogPrintf(LogWARN, "Clock adjusted between %d and %d seconds "
230                  "during sleep !\n",
231                  to.tv_sec - st.tv_sec, sec + to.tv_sec - st.tv_sec);
232        st.tv_sec = to.tv_sec;
233        st.tv_usec = to.tv_usec;
234        et.tv_sec = st.tv_sec + sec;
235        et.tv_usec = st.tv_usec + usec;
236        to.tv_sec = sec;
237        to.tv_usec = usec;
238      } else if (to.tv_sec > et.tv_sec ||
239                 (to.tv_sec == et.tv_sec && to.tv_usec >= et.tv_usec)) {
240        break;
241      } else {
242        to.tv_sec = et.tv_sec - to.tv_sec;
243        if (et.tv_usec < to.tv_usec) {
244          to.tv_sec--;
245          to.tv_usec = 1000000 + et.tv_usec - to.tv_usec;
246        } else
247          to.tv_usec = et.tv_usec - to.tv_usec;
248      }
249    }
250  }
251}
252
253void
254nointr_sleep(u_int sec)
255{
256  nointr_dosleep(sec, 0);
257}
258
259void
260nointr_usleep(u_int usec)
261{
262  nointr_dosleep(0, usec);
263}
264
265static void
266InitTimerService()
267{
268  struct itimerval itimer;
269
270  pending_signal(SIGALRM, (void (*) (int)) TimerService);
271  itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
272  itimer.it_interval.tv_usec = itimer.it_value.tv_usec = TICKUNIT;
273  if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
274    LogPrintf(LogERROR, "Unable to set itimer.\n");
275}
276
277void
278TermTimerService(void)
279{
280  struct itimerval itimer;
281
282  itimer.it_interval.tv_usec = itimer.it_interval.tv_sec = 0;
283  itimer.it_value.tv_usec = itimer.it_value.tv_sec = 0;
284  if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
285    LogPrintf(LogERROR, "Unable to set itimer.\n");
286  pending_signal(SIGALRM, SIG_IGN);
287}
288
289#endif
290