timer.c revision 32063
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.24 1997/11/22 03:37: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 <stdio.h>
31#include <unistd.h>
32
33#include "command.h"
34#include "mbuf.h"
35#include "log.h"
36#include "defs.h"
37#include "sig.h"
38#include "timer.h"
39
40struct pppTimer *TimerList = NULL;
41
42static void StopTimerNoBlock(struct pppTimer *);
43static void InitTimerService(void);
44
45void
46StopTimer(struct pppTimer * tp)
47{
48#ifdef SIGALRM
49  int omask;
50
51  omask = sigblock(sigmask(SIGALRM));
52#endif
53  StopTimerNoBlock(tp);
54#ifdef SIGALRM
55  sigsetmask(omask);
56#endif
57}
58
59void
60StartTimer(struct pppTimer * tp)
61{
62  struct pppTimer *t, *pt;
63  u_long ticks = 0;
64
65#ifdef SIGALRM
66  int omask;
67
68  omask = sigblock(sigmask(SIGALRM));
69#endif
70
71  if (tp->state != TIMER_STOPPED) {
72    StopTimerNoBlock(tp);
73  }
74  if (tp->load == 0) {
75    LogPrintf(LogDEBUG, "timer %x has 0 load!\n", tp);
76    sigsetmask(omask);
77    return;
78  }
79  pt = NULL;
80  for (t = TimerList; t; t = t->next) {
81    LogPrintf(LogDEBUG, "StartTimer: %x(%d):  ticks: %d, rest: %d\n",
82	      t, t->state, ticks, t->rest);
83    if (ticks + t->rest >= tp->load)
84      break;
85    ticks += t->rest;
86    pt = t;
87  }
88
89  tp->state = TIMER_RUNNING;
90  tp->rest = tp->load - ticks;
91  LogPrintf(LogDEBUG, "StartTimer: Inserting %x before %x, rest = %d\n",
92	    tp, t, tp->rest);
93  /* Insert given *tp just before *t */
94  tp->next = t;
95  if (pt) {
96    pt->next = tp;
97  } else {
98    InitTimerService();
99    TimerList = tp;
100  }
101  if (t)
102    t->rest -= tp->rest;
103
104#ifdef SIGALRM
105  sigsetmask(omask);
106#endif
107}
108
109static void
110StopTimerNoBlock(struct pppTimer * tp)
111{
112  struct pppTimer *t, *pt;
113
114  /*
115   * A Running Timer should be removing TimerList, But STOPPED/EXPIRED is
116   * already removing TimerList. So just marked as TIMER_STOPPED. Do not
117   * change tp->enext!! (Might be Called by expired proc)
118   */
119  LogPrintf(LogDEBUG, "StopTimer: %x, next = %x state=%x\n",
120	    tp, tp->next, tp->state);
121  if (tp->state != TIMER_RUNNING) {
122    tp->next = NULL;
123    tp->state = TIMER_STOPPED;
124    return;
125  }
126  pt = NULL;
127  for (t = TimerList; t != tp && t != NULL; t = t->next)
128    pt = t;
129  if (t) {
130    if (pt) {
131      pt->next = t->next;
132    } else {
133      TimerList = t->next;
134      if (TimerList == NULL)	/* Last one ? */
135	TermTimerService();	/* Terminate Timer Service */
136    }
137    if (t->next)
138      t->next->rest += tp->rest;
139  } else
140    LogPrintf(LogERROR, "Oops, timer not found!!\n");
141
142  tp->next = NULL;
143  tp->state = TIMER_STOPPED;
144}
145
146void
147TimerService()
148{
149  struct pppTimer *tp, *exp, *wt;
150
151  if (LogIsKept(LogDEBUG))
152    ShowTimers();
153  tp = TimerList;
154  if (tp) {
155    tp->rest--;
156    if (tp->rest == 0) {
157
158      /*
159       * Multiple timers may expires at once. Create list of expired timers.
160       */
161      exp = NULL;
162      do {
163	tp->state = TIMER_EXPIRED;
164	wt = tp->next;
165	tp->enext = exp;
166	exp = tp;
167	LogPrintf(LogDEBUG, "TimerService: Add %x to exp\n", tp);
168	tp = wt;
169      } while (tp && (tp->rest == 0));
170
171      TimerList = tp;
172      if (TimerList == NULL)	/* No timers ? */
173	TermTimerService();	/* Terminate Timer Service */
174      LogPrintf(LogDEBUG, "TimerService: next is %x(%d)\n",
175		TimerList, TimerList ? TimerList->rest : 0);
176
177      /*
178       * Process all expired timers.
179       */
180      while (exp) {
181#ifdef notdef
182	StopTimer(exp);
183#endif
184	if (exp->func)
185	  (*exp->func) (exp->arg);
186
187	/*
188	 * Just Removing each item from expired list And exp->enext will be
189	 * intialized at next expire in this funtion.
190	 */
191	exp = exp->enext;
192      }
193    }
194  }
195}
196
197void
198ShowTimers()
199{
200  struct pppTimer *pt;
201
202  LogPrintf(LogDEBUG, "---- Begin of Timer Service List---\n");
203  for (pt = TimerList; pt; pt = pt->next)
204    LogPrintf(LogDEBUG, "%x: load = %d, rest = %d, state =%x\n",
205	      pt, pt->load, pt->rest, pt->state);
206  LogPrintf(LogDEBUG, "---- End of Timer Service List ---\n");
207}
208
209#ifdef SIGALRM
210
211static void
212nointr_dosleep(u_int sec, u_int usec)
213{
214  struct timeval to, st, et;
215
216  gettimeofday(&st, NULL);
217  et.tv_sec = st.tv_sec + sec;
218  et.tv_usec = st.tv_usec + usec;
219  to.tv_sec = sec;
220  to.tv_usec = usec;
221  for (;;) {
222    if (select(0, NULL, NULL, NULL, &to) == 0 ||
223	errno != EINTR) {
224      break;
225    } else {
226      gettimeofday(&to, NULL);
227      if (to.tv_sec > et.tv_sec ||
228          (to.tv_sec == et.tv_sec && to.tv_usec > et.tv_usec) ||
229          to.tv_sec < st.tv_sec ||
230          (to.tv_sec == st.tv_sec && to.tv_usec < st.tv_usec)) {
231        LogPrintf(LogWARN, "Clock adjusted between %d and %d seconds "
232                  "during sleep !\n",
233                  to.tv_sec - st.tv_sec, sec + to.tv_sec - st.tv_sec);
234        st.tv_sec = to.tv_sec;
235        st.tv_usec = to.tv_usec;
236        et.tv_sec = st.tv_sec + sec;
237        et.tv_usec = st.tv_usec + usec;
238        to.tv_sec = sec;
239        to.tv_usec = usec;
240      } else if (to.tv_sec == et.tv_sec && to.tv_usec == et.tv_usec) {
241        break;
242      } else {
243        to.tv_sec = et.tv_sec - to.tv_sec;
244        if (et.tv_usec < to.tv_usec) {
245          to.tv_sec--;
246          to.tv_usec = 1000000 + et.tv_usec - to.tv_usec;
247        } else
248          to.tv_usec = et.tv_usec - to.tv_usec;
249      }
250    }
251  }
252}
253
254void
255nointr_sleep(u_int sec)
256{
257  nointr_dosleep(sec, 0);
258}
259
260void
261nointr_usleep(u_int usec)
262{
263  nointr_dosleep(0, usec);
264}
265
266static void
267InitTimerService()
268{
269  struct itimerval itimer;
270
271  pending_signal(SIGALRM, (void (*) (int)) TimerService);
272  itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
273  itimer.it_interval.tv_usec = itimer.it_value.tv_usec = TICKUNIT;
274  if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
275    LogPrintf(LogERROR, "Unable to set itimer.\n");
276}
277
278void
279TermTimerService(void)
280{
281  struct itimerval itimer;
282
283  itimer.it_interval.tv_usec = itimer.it_interval.tv_sec = 0;
284  itimer.it_value.tv_usec = itimer.it_value.tv_sec = 0;
285  if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
286    LogPrintf(LogERROR, "Unable to set itimer.\n");
287  pending_signal(SIGALRM, SIG_IGN);
288}
289
290#endif
291