source: trunk/third/xntp/xntpd/ntp_timer.c @ 10832

Revision 10832, 10.6 KB checked in by brlewis, 27 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r10831, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * ntp_event.c - event timer support routines
3 */
4#ifdef HAVE_CONFIG_H
5#include <config.h>
6#endif
7
8#include <stdio.h>
9#include <errno.h>
10#include <sys/types.h>
11#include <sys/time.h>
12#include <signal.h>
13#include <sys/signal.h>
14
15#include "ntpd.h"
16#include "ntp_stdlib.h"
17
18/*
19 * These routines provide support for the event timer.  The timer is
20 * implemented by an interrupt routine which sets a flag once every
21 * 2**EVENT_TIMEOUT seconds (currently 4), and a timer routine which
22 * is called when the mainline code gets around to seeing the flag.
23 * The timer routine dispatches the clock adjustment code if its time
24 * has come, then searches the timer queue for expiries which are
25 * dispatched to the transmit procedure.  Finally, we call the hourly
26 * procedure to do cleanup and print a message.
27 */
28
29/*
30 * Alarm flag.  The mainline code imports this.
31 */
32volatile int alarm_flag;
33
34/*
35 * adjust and hourly counters
36 */
37static  u_long adjust_timer;
38static  u_long hourly_timer;
39
40/*
41 * Imported from the leap module.  The leap timer.
42 */
43extern u_long leap_timer;
44
45/*
46 * Statistics counter for the interested.
47 */
48volatile u_long alarm_overflow;
49
50#define HOUR    (60*60)
51
52/*
53 * Current_time holds the number of seconds since we started, in
54 * increments of 2**EVENT_TIMEOUT seconds.  The timer queue is the
55 * hash into which we sort timer entries.
56 */
57u_long current_time;
58struct event timerqueue[TIMER_NSLOTS];
59
60/*
61 * Stats.  Number of overflows and number of calls to transmit().
62 */
63u_long timer_timereset;
64u_long timer_overflows;
65u_long timer_xmtcalls;
66
67#ifndef SYS_WINNT
68static  RETSIGTYPE alarming     P((int));
69#else
70void PASCAL alarming P((UINT,UINT,DWORD,DWORD,DWORD));
71#endif /* SYS_WINNT */
72
73#if defined(VMS)
74static int vmstimer[2];         /* time for next timer AST */
75static int vmsinc[2];           /* timer increment */
76#endif /* VMS */
77
78/*
79 * init_timer - initialize the timer data structures
80 */
81void
82init_timer()
83{
84  register int i;
85#if !defined(VMS)
86# ifndef SYS_WINNT
87#ifndef HAVE_TIMER_SETTIME
88  struct itimerval itimer;
89#else
90static timer_t xntpd_timerid;   /* should be global if we ever want to kill
91                                   timer without rebooting ... */
92       struct itimerspec itimer;
93#endif
94
95# else /* SYS_WINNT */
96  TIMECAPS tc;
97  HANDLE hToken;
98  TOKEN_PRIVILEGES tkp;
99  UINT wTimerRes, wTimerID;
100  extern HANDLE hMutex;
101# endif /* SYS_WINNT */
102#endif /* VMS */
103
104  /*
105   * Initialize...
106   */
107  alarm_flag = 0;
108  alarm_overflow = 0;
109  adjust_timer = 1;
110  hourly_timer = HOUR;
111  current_time = 0;
112  timer_overflows = 0;
113  timer_xmtcalls = 0;
114  timer_timereset = 0;
115
116  for (i = 0; i < TIMER_NSLOTS; i++) {
117    /*
118     * Queue pointers should point at themselves.  Event
119     * times must be set to 0 since this is used to
120     * detect the queue end.
121     */
122    timerqueue[i].next = &timerqueue[i];
123    timerqueue[i].prev = &timerqueue[i];
124    timerqueue[i].event_time = 0;
125  }
126
127#ifndef SYS_WINNT
128  /*
129   * Set up the alarm interrupt.  The first comes 2**EVENT_TIMEOUT
130   * seconds from now and they continue on every 2**EVENT_TIMEOUT
131   * seconds.
132   */
133# if !defined(VMS)
134#if defined(HAVE_TIMER_CREATE) && defined(HAVE_TIMER_SETTIME)
135  if (timer_create (CLOCK_REALTIME, NULL, &xntpd_timerid) ==
136#ifdef SYS_VXWORKS
137      ERROR
138#else
139      -1
140#endif
141      )
142    {
143      fprintf (stderr, "timer create FAILED\n");
144      exit (0);
145    }
146  (void) signal_no_reset(SIGALRM, alarming);
147  itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
148  itimer.it_interval.tv_nsec = itimer.it_value.tv_nsec = 0;
149  timer_settime(xntpd_timerid, 0 /*!TIMER_ABSTIME*/, &itimer, NULL);
150#else
151  (void) signal_no_reset(SIGALRM, alarming);
152  itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
153  itimer.it_interval.tv_usec = itimer.it_value.tv_usec = 0;
154  setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
155#endif
156
157# else /* VMS */
158  vmsinc[0] = 10000000;         /* 1 sec */
159  vmsinc[1] = 0;
160  lib$emul(&(1<<EVENT_TIMEOUT), &vmsinc, &0, &vmsinc);
161
162  sys$gettim(&vmstimer);        /* that's "now" as abstime */
163
164  lib$addx(&vmsinc, &vmstimer, &vmstimer);
165  sys$setimr(0, &vmstimer, alarming, alarming, 0);
166# endif /* VMS */
167#else /* SYS_WINNT */
168  _tzset();
169
170  /*
171   * Get privileges needed for fiddling with the clock
172   */
173
174  /* get the current process token handle */
175  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
176    msyslog(LOG_ERR, "OpenProcessToken failed: %m");
177    exit(1);
178  }
179  /* get the LUID for system-time privilege. */
180  LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
181  tkp.PrivilegeCount = 1;  /* one privilege to set */
182  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
183  /* get set-time privilege for this process. */
184  AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
185  /* cannot test return value of AdjustTokenPrivileges. */
186  if (GetLastError() != ERROR_SUCCESS)
187    msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");
188
189  /*
190   * Set up timer interrupts for every 2**EVENT_TIMEOUT seconds
191   * Under Windows/NT, expiry of timer interval leads to invocation
192   * of a callback function (on a different thread) rather than
193   * generating an alarm signal
194   */
195
196  /* determine max and min resolution supported */
197  if(timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) {
198    msyslog(LOG_ERR, "timeGetDevCaps failed: %m");
199    exit(1);
200  }
201  wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax);
202  /* establish the minimum timer resolution that we'll use */
203  timeBeginPeriod(wTimerRes);
204
205  hMutex = CreateMutex(
206                       NULL,            /* no security attributes */
207                       FALSE,           /* initially not owned */
208                       "MutexForNTP");  /* name of mutex */
209  if (hMutex == NULL) {
210    msyslog(LOG_ERR, "cannot create a mutex: %m\n");
211    exit(1);
212  }
213
214  /* start the timer event */
215  wTimerID = timeSetEvent(
216                          (1<<EVENT_TIMEOUT) * 1000,   /* Delay in ms */
217                          wTimerRes,                   /* Resolution */
218                          (LPTIMECALLBACK) alarming,   /* Callback function */
219                          (DWORD) 0,                   /* User data */
220                          TIME_PERIODIC);           /* Event type (periodic) */
221  if (wTimerID == 0) {
222    msyslog(LOG_ERR, "timeSetEvent failed: %m");
223    exit(1);
224  }
225#endif /* SYS_WINNT */
226}
227
228
229/*
230 * timer - dispatch anyone who needs to be
231 */
232void
233timer()
234{
235  register struct event *ev;
236  register struct event *tq;
237#ifdef SYS_WINNT
238  extern HANDLE hMutex;
239#endif
240
241  current_time += (1<<EVENT_TIMEOUT);
242
243  /*
244   * Adjustment timeout first
245   */
246  if (adjust_timer <= current_time) {
247    adjust_timer += 1;
248    adj_host_clock();
249  }
250
251#ifdef SYS_WINNT
252  if (!ReleaseMutex(hMutex)) {
253    msyslog(LOG_ERR, "alarming cannot release mutex: %m\n");
254    exit(1);
255  }
256#endif /* SYS_WINNT */
257
258  /*
259   * Leap timer next.
260   */
261  if (leap_timer != 0 && leap_timer <= current_time)
262    leap_process();
263
264  /*
265   * Now dispatch any peers whose event timer has expired.
266   */
267
268  /* Added mutex to prevent race condition among threads under Windows NT */
269#ifdef SYS_WINNT
270  WaitForSingleObject(m_hListMutex,INFINITE);
271#endif /* SYS_WINNT */
272
273#ifdef TIMERQUEUE_DEBUG
274  {
275    int i;
276    int j;
277
278    for (i = 0; i < TIMER_NSLOTS; ++i)
279      {
280        struct event *qh;
281
282        qh = ev = &timerqueue[i];
283        if (qh->event_time != 0)
284          msyslog(LOG_ERR, "timerqueue[%d].event_time is %d instead of 0!",
285                  i, timerqueue[i].event_time);
286        j = 0;
287        do
288          {
289            if (ev->prev->next != ev)
290              msyslog(LOG_ERR, "timerqueue[%d]: #%d: ev->prev->next != ev",
291                      i, j);
292            if (ev->next->prev != ev)
293              msyslog(LOG_ERR, "timerqueue[%d]: #%d: ev->next->prev != ev",
294                      i, j);
295            ++j;
296            ev = ev->next;
297          }
298        while (qh != ev);
299      }
300  }
301#endif /* TIMERQUEUE_DEBUG */
302
303  tq = &timerqueue[TIMER_SLOT(current_time)];
304  if (tq) {
305    ev = tq->next;
306    while (ev
307           && ev->event_time != 0
308           && ev->event_time < (current_time + (1<<EVENT_TIMEOUT))) {
309      tq->next = ev->next;
310      tq->next->prev = tq;
311      ev->prev = ev->next = 0;
312      timer_xmtcalls++;
313      ev->event_handler(ev->peer);
314      ev = tq->next;
315    }
316    if (!ev)
317      msyslog(LOG_ERR, "timer: ev was NIL!");
318  } else {
319    msyslog(LOG_ERR, "timer: tq was NIL!");
320  }
321
322  /* Added mutex to prevent race condition among threads under Windows NT */
323#ifdef SYS_WINNT
324  ReleaseMutex(m_hListMutex);
325#endif /* SYS_WINNT */
326
327  /*
328   * Finally, call the hourly routine
329   */
330  if (hourly_timer <= current_time) {
331    hourly_timer += HOUR;
332    hourly_stats();
333  }
334}
335
336
337#ifndef SYS_WINNT
338/*
339 * alarming - tell the world we've been alarmed
340 */
341static RETSIGTYPE
342alarming(sig)
343     int sig;
344{
345  extern int initializing;      /* from main line code */
346
347#if !defined(VMS)
348  if (initializing)
349    return;
350  if (alarm_flag)
351    alarm_overflow++;
352  else
353    alarm_flag++;
354#else /* VMS AST routine */
355  if (!initializing) {
356    if (alarm_flag) alarm_overflow++;
357    else alarm_flag = 1;        /* increment is no good */
358  }
359  lib$addx(&vmsinc,&vmstimer,&vmstimer);
360  sys$setimr(0,&vmstimer,alarming,alarming,0);
361#endif /* VMS */
362}
363#else /* SYS_WINNT */
364/*
365 * alarming for WinNT - invoke the timer() routine after grabbing the mutex
366 */
367void PASCAL alarming (UINT wTimerID, UINT msg,
368                      DWORD dwUser, DWORD dw1, DWORD dw2)
369{
370  extern int debug;
371  static int initializing2 = 1;
372  extern HANDLE hMutex;
373  DWORD dwWaitResult;
374  extern HANDLE TimerThreadHandle;
375  static DWORD threadID;
376#ifdef DEBUG
377  SYSTEMTIME st;
378#endif
379
380  /*
381   * set the priority for timer() thread to be higher than the main thread
382   */
383  if (initializing2) {
384    TimerThreadHandle = GetCurrentThread();
385    if (!SetThreadPriority(TimerThreadHandle, (DWORD) THREAD_PRIORITY_HIGHEST))
386      msyslog(LOG_ERR, "SetThreadPriority failed: %m");
387    threadID = GetCurrentThreadId();
388    initializing2 = 0;
389  }
390
391#ifdef DEBUG
392  if (debug > 9) {
393    GetSystemTime(&st);
394    printf("thread %u (timer callback): time %02u:%02u:%02u:%03u\n",
395           threadID, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
396    fflush(stdout);
397  }
398#endif
399
400  dwWaitResult = WaitForSingleObject(
401                                     hMutex,    /* handle of mutex */
402                                     5000L);    /* five-second time-out interval */
403
404  switch (dwWaitResult) {
405  case WAIT_OBJECT_0:
406    /* The thread got mutex ownership. */
407    /* the mutex is released in the timer() routine */
408    timer();
409    break;
410  default:
411    /* Cannot get mutex ownership due to time-out. */
412    msyslog(LOG_ERR, "alarming error with mutex: %m\n");
413    exit(1);
414  }
415
416  UNREFERENCED_PARAMETER(dw1);
417  UNREFERENCED_PARAMETER(dw2);
418  UNREFERENCED_PARAMETER(dwUser);
419  UNREFERENCED_PARAMETER(msg);
420  UNREFERENCED_PARAMETER(wTimerID);
421}
422#endif /* SYS_WINNT */
423
424
425/*
426 * timer_clr_stats - clear timer module stat counters
427 */
428void
429timer_clr_stats()
430{
431  timer_overflows = 0;
432  timer_xmtcalls = 0;
433  timer_timereset = current_time;
434}
Note: See TracBrowser for help on using the repository browser.