timer.c revision 31343
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.23 1997/11/09 06:22:49 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
210u_int
211nointr_sleep(u_int sec)
212{
213  struct timeval to, st, et;
214  long sld, nwd, std;
215
216  gettimeofday(&st, NULL);
217  to.tv_sec = sec;
218  to.tv_usec = 0;
219  std = st.tv_sec * 1000000 + st.tv_usec;
220  for (;;) {
221    if (select(0, NULL, NULL, NULL, &to) == 0 ||
222	errno != EINTR) {
223      break;
224    } else {
225      gettimeofday(&et, NULL);
226      sld = to.tv_sec * 1000000 + to.tv_sec;
227      nwd = et.tv_sec * 1000000 + et.tv_usec - std;
228      if (sld > nwd)
229	sld -= nwd;
230      else
231	sld = 1;		/* Avoid both tv_sec/usec is 0 */
232
233      /* Calculate timeout value for select */
234      to.tv_sec = sld / 1000000;
235      to.tv_usec = sld % 1000000;
236    }
237  }
238  return (0L);
239}
240
241void
242nointr_usleep(u_int usec)
243{
244  struct timeval to, st, et;
245  long sld, nwd, std;
246
247  gettimeofday(&st, NULL);
248  to.tv_sec = 0;
249  to.tv_usec = usec;
250  std = st.tv_sec * 1000000 + st.tv_usec;
251  for (;;) {
252    if (select(0, NULL, NULL, NULL, &to) == 0 ||
253	errno != EINTR) {
254      break;
255    } else {
256      gettimeofday(&et, NULL);
257      sld = to.tv_sec * 1000000 + to.tv_sec;
258      nwd = et.tv_sec * 1000000 + et.tv_usec - std;
259      if (sld > nwd)
260	sld -= nwd;
261      else
262	sld = 1;		/* Avoid both tv_sec/usec is 0 */
263
264      /* Calculate timeout value for select */
265      to.tv_sec = sld / 1000000;
266      to.tv_usec = sld % 1000000;
267
268    }
269  }
270}
271
272static void
273InitTimerService()
274{
275  struct itimerval itimer;
276
277  pending_signal(SIGALRM, (void (*) (int)) TimerService);
278  itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
279  itimer.it_interval.tv_usec = itimer.it_value.tv_usec = TICKUNIT;
280  if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
281    LogPrintf(LogERROR, "Unable to set itimer.\n");
282}
283
284void
285TermTimerService(void)
286{
287  struct itimerval itimer;
288
289  itimer.it_interval.tv_usec = itimer.it_interval.tv_sec = 0;
290  itimer.it_value.tv_usec = itimer.it_value.tv_sec = 0;
291  if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
292    LogPrintf(LogERROR, "Unable to set itimer.\n");
293  pending_signal(SIGALRM, SIG_IGN);
294}
295
296#endif
297