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