8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / sendmail / libsm / clock.c
blobf16115ba3015fc9eae328c1bd313da7360b0ee8a
1 /*
2 * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
14 #pragma ident "%Z%%M% %I% %E% SMI"
16 #include <sm/gen.h>
17 SM_RCSID("@(#)$Id: clock.c,v 1.47 2005/06/14 23:07:20 ca Exp $")
18 #include <unistd.h>
19 #include <time.h>
20 #include <errno.h>
21 #if SM_CONF_SETITIMER
22 # include <sm/time.h>
23 #endif /* SM_CONF_SETITIMER */
24 #include <sm/heap.h>
25 #include <sm/debug.h>
26 #include <sm/bitops.h>
27 #include <sm/clock.h>
28 #include "local.h"
29 #if _FFR_SLEEP_USE_SELECT > 0
30 # include <sys/types.h>
31 #endif /* _FFR_SLEEP_USE_SELECT > 0 */
32 #if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
33 # include <syslog.h>
34 #endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
36 #ifndef sigmask
37 # define sigmask(s) (1 << ((s) - 1))
38 #endif /* ! sigmask */
42 ** SM_SETEVENTM -- set an event to happen at a specific time in milliseconds.
44 ** Events are stored in a sorted list for fast processing.
45 ** An event only applies to the process that set it.
46 ** Source is #ifdef'd to work with older OS's that don't have setitimer()
47 ** (that is, don't have a timer granularity less than 1 second).
49 ** Parameters:
50 ** intvl -- interval until next event occurs (milliseconds).
51 ** func -- function to call on event.
52 ** arg -- argument to func on event.
54 ** Returns:
55 ** On success returns the SM_EVENT entry created.
56 ** On failure returns NULL.
58 ** Side Effects:
59 ** none.
62 static SM_EVENT *volatile SmEventQueue; /* head of event queue */
63 static SM_EVENT *volatile SmFreeEventList; /* list of free events */
65 SM_EVENT *
66 sm_seteventm(intvl, func, arg)
67 int intvl;
68 void (*func)__P((int));
69 int arg;
71 ENTER_CRITICAL();
72 if (SmFreeEventList == NULL)
74 SmFreeEventList = (SM_EVENT *) sm_pmalloc_x(sizeof *SmFreeEventList);
75 SmFreeEventList->ev_link = NULL;
77 LEAVE_CRITICAL();
79 return sm_sigsafe_seteventm(intvl, func, arg);
83 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
84 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
85 ** DOING.
88 SM_EVENT *
89 sm_sigsafe_seteventm(intvl, func, arg)
90 int intvl;
91 void (*func)__P((int));
92 int arg;
94 register SM_EVENT **evp;
95 register SM_EVENT *ev;
96 #if SM_CONF_SETITIMER
97 auto struct timeval now, nowi, ival;
98 auto struct itimerval itime;
99 #else /* SM_CONF_SETITIMER */
100 auto time_t now, nowi;
101 #endif /* SM_CONF_SETITIMER */
102 int wasblocked;
104 /* negative times are not allowed */
105 if (intvl <= 0)
106 return NULL;
108 wasblocked = sm_blocksignal(SIGALRM);
109 #if SM_CONF_SETITIMER
110 ival.tv_sec = intvl / 1000;
111 ival.tv_usec = (intvl - ival.tv_sec * 1000) * 10;
112 (void) gettimeofday(&now, NULL);
113 nowi = now;
114 timeradd(&now, &ival, &nowi);
115 #else /* SM_CONF_SETITIMER */
116 now = time(NULL);
117 nowi = now + (time_t)(intvl / 1000);
118 #endif /* SM_CONF_SETITIMER */
120 /* search event queue for correct position */
121 for (evp = (SM_EVENT **) (&SmEventQueue);
122 (ev = *evp) != NULL;
123 evp = &ev->ev_link)
125 #if SM_CONF_SETITIMER
126 if (timercmp(&(ev->ev_time), &nowi, >=))
127 #else /* SM_CONF_SETITIMER */
128 if (ev->ev_time >= nowi)
129 #endif /* SM_CONF_SETITIMER */
130 break;
133 ENTER_CRITICAL();
134 if (SmFreeEventList == NULL)
137 ** This shouldn't happen. If called from sm_seteventm(),
138 ** we have just malloced a SmFreeEventList entry. If
139 ** called from a signal handler, it should have been
140 ** from an existing event which sm_tick() just added to
141 ** SmFreeEventList.
144 LEAVE_CRITICAL();
145 if (wasblocked == 0)
146 (void) sm_releasesignal(SIGALRM);
147 return NULL;
149 else
151 ev = SmFreeEventList;
152 SmFreeEventList = ev->ev_link;
154 LEAVE_CRITICAL();
156 /* insert new event */
157 ev->ev_time = nowi;
158 ev->ev_func = func;
159 ev->ev_arg = arg;
160 ev->ev_pid = getpid();
161 ENTER_CRITICAL();
162 ev->ev_link = *evp;
163 *evp = ev;
164 LEAVE_CRITICAL();
166 (void) sm_signal(SIGALRM, sm_tick);
167 # if SM_CONF_SETITIMER
168 timersub(&SmEventQueue->ev_time, &now, &itime.it_value);
169 itime.it_interval.tv_sec = 0;
170 itime.it_interval.tv_usec = 0;
171 if (itime.it_value.tv_sec < 0)
172 itime.it_value.tv_sec = 0;
173 if (itime.it_value.tv_sec == 0 && itime.it_value.tv_usec == 0)
174 itime.it_value.tv_usec = 1000;
175 (void) setitimer(ITIMER_REAL, &itime, NULL);
176 # else /* SM_CONF_SETITIMER */
177 intvl = SmEventQueue->ev_time - now;
178 (void) alarm((unsigned) (intvl < 1 ? 1 : intvl));
179 # endif /* SM_CONF_SETITIMER */
180 if (wasblocked == 0)
181 (void) sm_releasesignal(SIGALRM);
182 return ev;
185 ** SM_CLREVENT -- remove an event from the event queue.
187 ** Parameters:
188 ** ev -- pointer to event to remove.
190 ** Returns:
191 ** none.
193 ** Side Effects:
194 ** arranges for event ev to not happen.
197 void
198 sm_clrevent(ev)
199 register SM_EVENT *ev;
201 register SM_EVENT **evp;
202 int wasblocked;
203 # if SM_CONF_SETITIMER
204 struct itimerval clr;
205 # endif /* SM_CONF_SETITIMER */
207 if (ev == NULL)
208 return;
210 /* find the parent event */
211 wasblocked = sm_blocksignal(SIGALRM);
212 for (evp = (SM_EVENT **) (&SmEventQueue);
213 *evp != NULL;
214 evp = &(*evp)->ev_link)
216 if (*evp == ev)
217 break;
220 /* now remove it */
221 if (*evp != NULL)
223 ENTER_CRITICAL();
224 *evp = ev->ev_link;
225 ev->ev_link = SmFreeEventList;
226 SmFreeEventList = ev;
227 LEAVE_CRITICAL();
230 /* restore clocks and pick up anything spare */
231 if (wasblocked == 0)
232 (void) sm_releasesignal(SIGALRM);
233 if (SmEventQueue != NULL)
234 (void) kill(getpid(), SIGALRM);
235 else
237 /* nothing left in event queue, no need for an alarm */
238 # if SM_CONF_SETITIMER
239 clr.it_interval.tv_sec = 0;
240 clr.it_interval.tv_usec = 0;
241 clr.it_value.tv_sec = 0;
242 clr.it_value.tv_usec = 0;
243 (void) setitimer(ITIMER_REAL, &clr, NULL);
244 # else /* SM_CONF_SETITIMER */
245 (void) alarm(0);
246 # endif /* SM_CONF_SETITIMER */
250 ** SM_CLEAR_EVENTS -- remove all events from the event queue.
252 ** Parameters:
253 ** none.
255 ** Returns:
256 ** none.
259 void
260 sm_clear_events()
262 register SM_EVENT *ev;
263 #if SM_CONF_SETITIMER
264 struct itimerval clr;
265 #endif /* SM_CONF_SETITIMER */
266 int wasblocked;
268 /* nothing will be left in event queue, no need for an alarm */
269 #if SM_CONF_SETITIMER
270 clr.it_interval.tv_sec = 0;
271 clr.it_interval.tv_usec = 0;
272 clr.it_value.tv_sec = 0;
273 clr.it_value.tv_usec = 0;
274 (void) setitimer(ITIMER_REAL, &clr, NULL);
275 #else /* SM_CONF_SETITIMER */
276 (void) alarm(0);
277 #endif /* SM_CONF_SETITIMER */
279 if (SmEventQueue == NULL)
280 return;
282 wasblocked = sm_blocksignal(SIGALRM);
284 /* find the end of the EventQueue */
285 for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link)
286 continue;
288 ENTER_CRITICAL();
289 ev->ev_link = SmFreeEventList;
290 SmFreeEventList = SmEventQueue;
291 SmEventQueue = NULL;
292 LEAVE_CRITICAL();
294 /* restore clocks and pick up anything spare */
295 if (wasblocked == 0)
296 (void) sm_releasesignal(SIGALRM);
299 ** SM_TICK -- take a clock tick
301 ** Called by the alarm clock. This routine runs events as needed.
302 ** Always called as a signal handler, so we assume that SIGALRM
303 ** has been blocked.
305 ** Parameters:
306 ** One that is ignored; for compatibility with signal handlers.
308 ** Returns:
309 ** none.
311 ** Side Effects:
312 ** calls the next function in EventQueue.
314 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
315 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
316 ** DOING.
319 /* ARGSUSED */
320 SIGFUNC_DECL
321 sm_tick(sig)
322 int sig;
324 register SM_EVENT *ev;
325 pid_t mypid;
326 int save_errno = errno;
327 #if SM_CONF_SETITIMER
328 struct itimerval clr;
329 struct timeval now;
330 #else /* SM_CONF_SETITIMER */
331 register time_t now;
332 #endif /* SM_CONF_SETITIMER */
334 #if SM_CONF_SETITIMER
335 clr.it_interval.tv_sec = 0;
336 clr.it_interval.tv_usec = 0;
337 clr.it_value.tv_sec = 0;
338 clr.it_value.tv_usec = 0;
339 (void) setitimer(ITIMER_REAL, &clr, NULL);
340 gettimeofday(&now, NULL);
341 #else /* SM_CONF_SETITIMER */
342 (void) alarm(0);
343 now = time(NULL);
344 #endif /* SM_CONF_SETITIMER */
346 FIX_SYSV_SIGNAL(sig, sm_tick);
347 errno = save_errno;
348 CHECK_CRITICAL(sig);
350 mypid = getpid();
351 while (PendingSignal != 0)
353 int sigbit = 0;
354 int sig = 0;
356 if (bitset(PEND_SIGHUP, PendingSignal))
358 sigbit = PEND_SIGHUP;
359 sig = SIGHUP;
361 else if (bitset(PEND_SIGINT, PendingSignal))
363 sigbit = PEND_SIGINT;
364 sig = SIGINT;
366 else if (bitset(PEND_SIGTERM, PendingSignal))
368 sigbit = PEND_SIGTERM;
369 sig = SIGTERM;
371 else if (bitset(PEND_SIGUSR1, PendingSignal))
373 sigbit = PEND_SIGUSR1;
374 sig = SIGUSR1;
376 else
378 /* If we get here, we are in trouble */
379 abort();
381 PendingSignal &= ~sigbit;
382 kill(mypid, sig);
385 #if SM_CONF_SETITIMER
386 gettimeofday(&now, NULL);
387 #else /* SM_CONF_SETITIMER */
388 now = time(NULL);
389 #endif /* SM_CONF_SETITIMER */
390 while ((ev = SmEventQueue) != NULL &&
391 (ev->ev_pid != mypid ||
392 #if SM_CONF_SETITIMER
393 timercmp(&ev->ev_time, &now, <=)
394 #else /* SM_CONF_SETITIMER */
395 ev->ev_time <= now
396 #endif /* SM_CONF_SETITIMER */
399 void (*f)__P((int));
400 int arg;
401 pid_t pid;
403 /* process the event on the top of the queue */
404 ev = SmEventQueue;
405 SmEventQueue = SmEventQueue->ev_link;
407 /* we must be careful in here because ev_func may not return */
408 f = ev->ev_func;
409 arg = ev->ev_arg;
410 pid = ev->ev_pid;
411 ENTER_CRITICAL();
412 ev->ev_link = SmFreeEventList;
413 SmFreeEventList = ev;
414 LEAVE_CRITICAL();
415 if (pid != getpid())
416 continue;
417 if (SmEventQueue != NULL)
419 #if SM_CONF_SETITIMER
420 if (timercmp(&SmEventQueue->ev_time, &now, >))
422 timersub(&SmEventQueue->ev_time, &now,
423 &clr.it_value);
424 clr.it_interval.tv_sec = 0;
425 clr.it_interval.tv_usec = 0;
426 if (clr.it_value.tv_sec < 0)
427 clr.it_value.tv_sec = 0;
428 if (clr.it_value.tv_sec == 0 &&
429 clr.it_value.tv_usec == 0)
430 clr.it_value.tv_usec = 1000;
431 (void) setitimer(ITIMER_REAL, &clr, NULL);
433 else
435 clr.it_interval.tv_sec = 0;
436 clr.it_interval.tv_usec = 0;
437 clr.it_value.tv_sec = 3;
438 clr.it_value.tv_usec = 0;
439 (void) setitimer(ITIMER_REAL, &clr, NULL);
441 #else /* SM_CONF_SETITIMER */
442 if (SmEventQueue->ev_time > now)
443 (void) alarm((unsigned) (SmEventQueue->ev_time
444 - now));
445 else
446 (void) alarm(3);
447 #endif /* SM_CONF_SETITIMER */
450 /* call ev_func */
451 errno = save_errno;
452 (*f)(arg);
453 #if SM_CONF_SETITIMER
454 clr.it_interval.tv_sec = 0;
455 clr.it_interval.tv_usec = 0;
456 clr.it_value.tv_sec = 0;
457 clr.it_value.tv_usec = 0;
458 (void) setitimer(ITIMER_REAL, &clr, NULL);
459 gettimeofday(&now, NULL);
460 #else /* SM_CONF_SETITIMER */
461 (void) alarm(0);
462 now = time(NULL);
463 #endif /* SM_CONF_SETITIMER */
465 if (SmEventQueue != NULL)
467 #if SM_CONF_SETITIMER
468 timersub(&SmEventQueue->ev_time, &now, &clr.it_value);
469 clr.it_interval.tv_sec = 0;
470 clr.it_interval.tv_usec = 0;
471 if (clr.it_value.tv_sec < 0)
472 clr.it_value.tv_sec = 0;
473 if (clr.it_value.tv_sec == 0 && clr.it_value.tv_usec == 0)
474 clr.it_value.tv_usec = 1000;
475 (void) setitimer(ITIMER_REAL, &clr, NULL);
476 #else /* SM_CONF_SETITIMER */
477 (void) alarm((unsigned) (SmEventQueue->ev_time - now));
478 #endif /* SM_CONF_SETITIMER */
480 errno = save_errno;
481 return SIGFUNC_RETURN;
484 ** SLEEP -- a version of sleep that works with this stuff
486 ** Because Unix sleep uses the alarm facility, I must reimplement
487 ** it here.
489 ** Parameters:
490 ** intvl -- time to sleep.
492 ** Returns:
493 ** zero.
495 ** Side Effects:
496 ** waits for intvl time. However, other events can
497 ** be run during that interval.
501 # if !HAVE_NANOSLEEP
502 static void sm_endsleep __P((int));
503 static bool volatile SmSleepDone;
504 # endif /* !HAVE_NANOSLEEP */
506 #ifndef SLEEP_T
507 # define SLEEP_T unsigned int
508 #endif /* ! SLEEP_T */
510 SLEEP_T
511 sleep(intvl)
512 unsigned int intvl;
514 #if HAVE_NANOSLEEP
515 struct timespec rqtp;
517 if (intvl == 0)
518 return (SLEEP_T) 0;
519 rqtp.tv_sec = intvl;
520 rqtp.tv_nsec = 0;
521 nanosleep(&rqtp, NULL);
522 return (SLEEP_T) 0;
523 #else /* HAVE_NANOSLEEP */
524 int was_held;
525 SM_EVENT *ev;
526 #if _FFR_SLEEP_USE_SELECT > 0
527 int r;
528 # if _FFR_SLEEP_USE_SELECT > 0
529 struct timeval sm_io_to;
530 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
531 #endif /* _FFR_SLEEP_USE_SELECT > 0 */
532 #if SM_CONF_SETITIMER
533 struct timeval now, begin, diff;
534 # if _FFR_SLEEP_USE_SELECT > 0
535 struct timeval slpv;
536 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
537 #else /* SM_CONF_SETITIMER */
538 time_t begin, now;
539 #endif /* SM_CONF_SETITIMER */
541 if (intvl == 0)
542 return (SLEEP_T) 0;
543 #if defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2
544 if (intvl > _FFR_MAX_SLEEP_TIME)
546 syslog(LOG_ERR, "sleep: interval=%u exceeds max value %d",
547 intvl, _FFR_MAX_SLEEP_TIME);
548 # if 0
549 SM_ASSERT(intvl < (unsigned int) INT_MAX);
550 # endif /* 0 */
551 intvl = _FFR_MAX_SLEEP_TIME;
553 #endif /* defined(_FFR_MAX_SLEEP_TIME) && _FFR_MAX_SLEEP_TIME > 2 */
554 SmSleepDone = false;
556 #if SM_CONF_SETITIMER
557 # if _FFR_SLEEP_USE_SELECT > 0
558 slpv.tv_sec = intvl;
559 slpv.tv_usec = 0;
560 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
561 (void) gettimeofday(&now, NULL);
562 begin = now;
563 #else /* SM_CONF_SETITIMER */
564 now = begin = time(NULL);
565 #endif /* SM_CONF_SETITIMER */
567 ev = sm_setevent((time_t) intvl, sm_endsleep, 0);
568 if (ev == NULL)
570 /* COMPLAIN */
571 #if 0
572 syslog(LOG_ERR, "sleep: sm_setevent(%u) failed", intvl);
573 #endif /* 0 */
574 SmSleepDone = true;
576 was_held = sm_releasesignal(SIGALRM);
578 while (!SmSleepDone)
580 #if SM_CONF_SETITIMER
581 (void) gettimeofday(&now, NULL);
582 timersub(&now, &begin, &diff);
583 if (diff.tv_sec < 0 ||
584 (diff.tv_sec == 0 && diff.tv_usec == 0))
585 break;
586 # if _FFR_SLEEP_USE_SELECT > 0
587 timersub(&slpv, &diff, &sm_io_to);
588 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
589 #else /* SM_CONF_SETITIMER */
590 now = time(NULL);
593 ** Check whether time expired before signal is released.
594 ** Due to the granularity of time() add 1 to be on the
595 ** safe side.
598 if (!(begin + (time_t) intvl + 1 > now))
599 break;
600 # if _FFR_SLEEP_USE_SELECT > 0
601 sm_io_to.tv_sec = intvl - (now - begin);
602 if (sm_io_to.tv_sec <= 0)
603 sm_io_to.tv_sec = 1;
604 sm_io_to.tv_usec = 0;
605 # endif /* _FFR_SLEEP_USE_SELECT > 0 */
606 #endif /* SM_CONF_SETITIMER */
607 #if _FFR_SLEEP_USE_SELECT > 0
608 if (intvl <= _FFR_SLEEP_USE_SELECT)
610 r = select(0, NULL, NULL, NULL, &sm_io_to);
611 if (r == 0)
612 break;
614 else
615 #endif /* _FFR_SLEEP_USE_SELECT > 0 */
616 (void) pause();
619 /* if out of the loop without the event being triggered remove it */
620 if (!SmSleepDone)
621 sm_clrevent(ev);
622 if (was_held > 0)
623 (void) sm_blocksignal(SIGALRM);
624 return (SLEEP_T) 0;
625 #endif /* HAVE_NANOSLEEP */
628 #if !HAVE_NANOSLEEP
629 static void
630 sm_endsleep(ignore)
631 int ignore;
634 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
635 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
636 ** DOING.
639 SmSleepDone = true;
641 #endif /* !HAVE_NANOSLEEP */