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