source: trunk/third/sendmail/libsm/clock.c @ 19204

Revision 19204, 11.6 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998-2003 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.
7 *
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.
11 *
12 */
13
14#include <sm/gen.h>
15SM_RCSID("@(#)$Id: clock.c,v 1.1.1.1 2003-04-08 15:08:55 zacheiss Exp $")
16#include <unistd.h>
17#include <time.h>
18#include <errno.h>
19#if SM_CONF_SETITIMER
20# include <sys/time.h>
21#endif /* SM_CONF_SETITIMER */
22#include <sm/heap.h>
23#include <sm/debug.h>
24#include <sm/bitops.h>
25#include <sm/clock.h>
26#include "local.h"
27
28#ifndef sigmask
29# define sigmask(s)     (1 << ((s) - 1))
30#endif /* ! sigmask */
31
32static void     sm_endsleep __P((void));
33
34
35/*
36**  SM_SETEVENTM -- set an event to happen at a specific time in milliseconds.
37**
38**      Events are stored in a sorted list for fast processing.
39**      An event only applies to the process that set it.
40**      Source is #ifdef'd to work with older OS's that don't have setitimer()
41**      (that is, don't have a timer granularity less than 1 second).
42**
43**      Parameters:
44**              intvl -- interval until next event occurs (milliseconds).
45**              func -- function to call on event.
46**              arg -- argument to func on event.
47**
48**      Returns:
49**              On success returns the SM_EVENT entry created.
50**              On failure returns NULL.
51**
52**      Side Effects:
53**              none.
54*/
55
56static SM_EVENT *volatile SmEventQueue;         /* head of event queue */
57static SM_EVENT *volatile SmFreeEventList;      /* list of free events */
58
59SM_EVENT *
60sm_seteventm(intvl, func, arg)
61        int intvl;
62        void (*func)();
63        int arg;
64{
65        ENTER_CRITICAL();
66        if (SmFreeEventList == NULL)
67        {
68                SmFreeEventList = (SM_EVENT *) sm_pmalloc_x(sizeof *SmFreeEventList);
69                SmFreeEventList->ev_link = NULL;
70        }
71        LEAVE_CRITICAL();
72
73        return sm_sigsafe_seteventm(intvl, func, arg);
74}
75
76/*
77**      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
78**              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
79**              DOING.
80*/
81
82SM_EVENT *
83sm_sigsafe_seteventm(intvl, func, arg)
84        int intvl;
85        void (*func)();
86        int arg;
87{
88        register SM_EVENT **evp;
89        register SM_EVENT *ev;
90#if SM_CONF_SETITIMER
91        auto struct timeval now, nowi, ival;
92        auto struct itimerval itime;
93#else /*  SM_CONF_SETITIMER */
94        auto time_t now, nowi;
95#endif /*  SM_CONF_SETITIMER */
96        int wasblocked;
97
98        /* negative times are not allowed */
99        if (intvl <= 0)
100                return NULL;
101
102        wasblocked = sm_blocksignal(SIGALRM);
103#if SM_CONF_SETITIMER
104        ival.tv_sec = intvl / 1000;
105        ival.tv_usec = (intvl - ival.tv_sec * 1000) * 10;
106        (void) gettimeofday(&now, NULL);
107        nowi = now;
108        timeradd(&now, &ival, &nowi);
109#else /*  SM_CONF_SETITIMER */
110        now = time(NULL);
111        nowi = now + (time_t)(intvl / 1000);
112#endif /*  SM_CONF_SETITIMER */
113
114        /* search event queue for correct position */
115        for (evp = (SM_EVENT **) (&SmEventQueue);
116             (ev = *evp) != NULL;
117             evp = &ev->ev_link)
118        {
119#if SM_CONF_SETITIMER
120                if (timercmp(&(ev->ev_time), &nowi, >=))
121#else /* SM_CONF_SETITIMER */
122                if (ev->ev_time >= nowi)
123#endif /* SM_CONF_SETITIMER */
124                        break;
125        }
126
127        ENTER_CRITICAL();
128        if (SmFreeEventList == NULL)
129        {
130                /*
131                **  This shouldn't happen.  If called from sm_seteventm(),
132                **  we have just malloced a SmFreeEventList entry.  If
133                **  called from a signal handler, it should have been
134                **  from an existing event which sm_tick() just added to
135                **  SmFreeEventList.
136                */
137
138                LEAVE_CRITICAL();
139                return NULL;
140        }
141        else
142        {
143                ev = SmFreeEventList;
144                SmFreeEventList = ev->ev_link;
145        }
146        LEAVE_CRITICAL();
147
148        /* insert new event */
149        ev->ev_time = nowi;
150        ev->ev_func = func;
151        ev->ev_arg = arg;
152        ev->ev_pid = getpid();
153        ENTER_CRITICAL();
154        ev->ev_link = *evp;
155        *evp = ev;
156        LEAVE_CRITICAL();
157
158        (void) sm_signal(SIGALRM, sm_tick);
159# if SM_CONF_SETITIMER
160        timersub(&SmEventQueue->ev_time, &now, &itime.it_value);
161        itime.it_interval.tv_sec = 0;
162        itime.it_interval.tv_usec = 0;
163        if (itime.it_value.tv_sec < 0)
164                itime.it_value.tv_sec = 0;
165        if (itime.it_value.tv_sec == 0 && itime.it_value.tv_usec == 0)
166                itime.it_value.tv_usec = 1000;
167        (void) setitimer(ITIMER_REAL, &itime, NULL);
168# else /* SM_CONF_SETITIMER */
169        intvl = SmEventQueue->ev_time - now;
170        (void) alarm((unsigned) (intvl < 1 ? 1 : intvl));
171# endif /* SM_CONF_SETITIMER */
172        if (wasblocked == 0)
173                (void) sm_releasesignal(SIGALRM);
174        return ev;
175}
176/*
177**  SM_CLREVENT -- remove an event from the event queue.
178**
179**      Parameters:
180**              ev -- pointer to event to remove.
181**
182**      Returns:
183**              none.
184**
185**      Side Effects:
186**              arranges for event ev to not happen.
187*/
188
189void
190sm_clrevent(ev)
191        register SM_EVENT *ev;
192{
193        register SM_EVENT **evp;
194        int wasblocked;
195# if SM_CONF_SETITIMER
196        struct itimerval clr;
197# endif /* SM_CONF_SETITIMER */
198
199        if (ev == NULL)
200                return;
201
202        /* find the parent event */
203        wasblocked = sm_blocksignal(SIGALRM);
204        for (evp = (SM_EVENT **) (&SmEventQueue);
205             *evp != NULL;
206             evp = &(*evp)->ev_link)
207        {
208                if (*evp == ev)
209                        break;
210        }
211
212        /* now remove it */
213        if (*evp != NULL)
214        {
215                ENTER_CRITICAL();
216                *evp = ev->ev_link;
217                ev->ev_link = SmFreeEventList;
218                SmFreeEventList = ev;
219                LEAVE_CRITICAL();
220        }
221
222        /* restore clocks and pick up anything spare */
223        if (wasblocked == 0)
224                (void) sm_releasesignal(SIGALRM);
225        if (SmEventQueue != NULL)
226                (void) kill(getpid(), SIGALRM);
227        else
228        {
229                /* nothing left in event queue, no need for an alarm */
230# if SM_CONF_SETITIMER
231                clr.it_interval.tv_sec = 0;
232                clr.it_interval.tv_usec = 0;
233                clr.it_value.tv_sec = 0;
234                clr.it_value.tv_usec = 0;
235                (void) setitimer(ITIMER_REAL, &clr, NULL);
236# else /* SM_CONF_SETITIMER */
237                (void) alarm(0);
238# endif /* SM_CONF_SETITIMER */
239        }
240}
241/*
242**  SM_CLEAR_EVENTS -- remove all events from the event queue.
243**
244**      Parameters:
245**              none.
246**
247**      Returns:
248**              none.
249*/
250
251void
252sm_clear_events()
253{
254        register SM_EVENT *ev;
255#if SM_CONF_SETITIMER
256        struct itimerval clr;
257#endif /* SM_CONF_SETITIMER */
258        int wasblocked;
259
260        /* nothing will be left in event queue, no need for an alarm */
261#if SM_CONF_SETITIMER
262        clr.it_interval.tv_sec = 0;
263        clr.it_interval.tv_usec = 0;
264        clr.it_value.tv_sec = 0;
265        clr.it_value.tv_usec = 0;
266        (void) setitimer(ITIMER_REAL, &clr, NULL);
267#else /* SM_CONF_SETITIMER */
268        (void) alarm(0);
269#endif /* SM_CONF_SETITIMER */
270
271        if (SmEventQueue == NULL)
272                return;
273
274        wasblocked = sm_blocksignal(SIGALRM);
275
276        /* find the end of the EventQueue */
277        for (ev = SmEventQueue; ev->ev_link != NULL; ev = ev->ev_link)
278                continue;
279
280        ENTER_CRITICAL();
281        ev->ev_link = SmFreeEventList;
282        SmFreeEventList = SmEventQueue;
283        SmEventQueue = NULL;
284        LEAVE_CRITICAL();
285
286        /* restore clocks and pick up anything spare */
287        if (wasblocked == 0)
288                (void) sm_releasesignal(SIGALRM);
289}
290/*
291**  SM_TICK -- take a clock tick
292**
293**      Called by the alarm clock.  This routine runs events as needed.
294**      Always called as a signal handler, so we assume that SIGALRM
295**      has been blocked.
296**
297**      Parameters:
298**              One that is ignored; for compatibility with signal handlers.
299**
300**      Returns:
301**              none.
302**
303**      Side Effects:
304**              calls the next function in EventQueue.
305**
306**      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
307**              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
308**              DOING.
309*/
310
311/* ARGSUSED */
312SIGFUNC_DECL
313sm_tick(sig)
314        int sig;
315{
316        register SM_EVENT *ev;
317        pid_t mypid;
318        int save_errno = errno;
319#if SM_CONF_SETITIMER
320        struct itimerval clr;
321        struct timeval now;
322#else /* SM_CONF_SETITIMER */
323        register time_t now;
324#endif /* SM_CONF_SETITIMER */
325
326#if SM_CONF_SETITIMER
327        clr.it_interval.tv_sec = 0;
328        clr.it_interval.tv_usec = 0;
329        clr.it_value.tv_sec = 0;
330        clr.it_value.tv_usec = 0;
331        (void) setitimer(ITIMER_REAL, &clr, NULL);
332        gettimeofday(&now, NULL);
333#else /* SM_CONF_SETITIMER */
334        (void) alarm(0);
335        now = time(NULL);
336#endif /* SM_CONF_SETITIMER */
337
338        FIX_SYSV_SIGNAL(sig, sm_tick);
339        errno = save_errno;
340        CHECK_CRITICAL(sig);
341
342        mypid = getpid();
343        while (PendingSignal != 0)
344        {
345                int sigbit = 0;
346                int sig = 0;
347
348                if (bitset(PEND_SIGHUP, PendingSignal))
349                {
350                        sigbit = PEND_SIGHUP;
351                        sig = SIGHUP;
352                }
353                else if (bitset(PEND_SIGINT, PendingSignal))
354                {
355                        sigbit = PEND_SIGINT;
356                        sig = SIGINT;
357                }
358                else if (bitset(PEND_SIGTERM, PendingSignal))
359                {
360                        sigbit = PEND_SIGTERM;
361                        sig = SIGTERM;
362                }
363                else if (bitset(PEND_SIGUSR1, PendingSignal))
364                {
365                        sigbit = PEND_SIGUSR1;
366                        sig = SIGUSR1;
367                }
368                else
369                {
370                        /* If we get here, we are in trouble */
371                        abort();
372                }
373                PendingSignal &= ~sigbit;
374                kill(mypid, sig);
375        }
376
377#if SM_CONF_SETITIMER
378        gettimeofday(&now, NULL);
379#else /* SM_CONF_SETITIMER */
380        now = time(NULL);
381#endif /* SM_CONF_SETITIMER */
382        while ((ev = SmEventQueue) != NULL &&
383               (ev->ev_pid != mypid ||
384#if SM_CONF_SETITIMER
385                timercmp(&ev->ev_time, &now, <=)
386#else /* SM_CONF_SETITIMER */
387                ev->ev_time <= now
388#endif /* SM_CONF_SETITIMER */
389                ))
390        {
391                void (*f)();
392                int arg;
393                pid_t pid;
394
395                /* process the event on the top of the queue */
396                ev = SmEventQueue;
397                SmEventQueue = SmEventQueue->ev_link;
398
399                /* we must be careful in here because ev_func may not return */
400                f = ev->ev_func;
401                arg = ev->ev_arg;
402                pid = ev->ev_pid;
403                ENTER_CRITICAL();
404                ev->ev_link = SmFreeEventList;
405                SmFreeEventList = ev;
406                LEAVE_CRITICAL();
407                if (pid != getpid())
408                        continue;
409                if (SmEventQueue != NULL)
410                {
411#if SM_CONF_SETITIMER
412                        if (timercmp(&SmEventQueue->ev_time, &now, >))
413                        {
414                                timersub(&SmEventQueue->ev_time, &now,
415                                         &clr.it_value);
416                                clr.it_interval.tv_sec = 0;
417                                clr.it_interval.tv_usec = 0;
418                                if (clr.it_value.tv_sec < 0)
419                                        clr.it_value.tv_sec = 0;
420                                if (clr.it_value.tv_sec == 0 &&
421                                    clr.it_value.tv_usec == 0)
422                                        clr.it_value.tv_usec = 1000;
423                                (void) setitimer(ITIMER_REAL, &clr, NULL);
424                        }
425                        else
426                        {
427                                clr.it_interval.tv_sec = 0;
428                                clr.it_interval.tv_usec = 0;
429                                clr.it_value.tv_sec = 3;
430                                clr.it_value.tv_usec = 0;
431                                (void) setitimer(ITIMER_REAL, &clr, NULL);
432                        }
433#else /* SM_CONF_SETITIMER */
434                        if (SmEventQueue->ev_time > now)
435                                (void) alarm((unsigned) (SmEventQueue->ev_time
436                                                         - now));
437                        else
438                                (void) alarm(3);
439#endif /* SM_CONF_SETITIMER */
440                }
441
442                /* call ev_func */
443                errno = save_errno;
444                (*f)(arg);
445#if SM_CONF_SETITIMER
446                clr.it_interval.tv_sec = 0;
447                clr.it_interval.tv_usec = 0;
448                clr.it_value.tv_sec = 0;
449                clr.it_value.tv_usec = 0;
450                (void) setitimer(ITIMER_REAL, &clr, NULL);
451                gettimeofday(&now, NULL);
452#else /* SM_CONF_SETITIMER */
453                (void) alarm(0);
454                now = time(NULL);
455#endif /* SM_CONF_SETITIMER */
456        }
457        if (SmEventQueue != NULL)
458        {
459#if SM_CONF_SETITIMER
460                timersub(&SmEventQueue->ev_time, &now, &clr.it_value);
461                clr.it_interval.tv_sec = 0;
462                clr.it_interval.tv_usec = 0;
463                if (clr.it_value.tv_sec < 0)
464                        clr.it_value.tv_sec = 0;
465                if (clr.it_value.tv_sec == 0 && clr.it_value.tv_usec == 0)
466                        clr.it_value.tv_usec = 1000;
467                (void) setitimer(ITIMER_REAL, &clr, NULL);
468#else /* SM_CONF_SETITIMER */
469                (void) alarm((unsigned) (SmEventQueue->ev_time - now));
470#endif /* SM_CONF_SETITIMER */
471        }
472        errno = save_errno;
473        return SIGFUNC_RETURN;
474}
475/*
476**  SLEEP -- a version of sleep that works with this stuff
477**
478**      Because Unix sleep uses the alarm facility, I must reimplement
479**      it here.
480**
481**      Parameters:
482**              intvl -- time to sleep.
483**
484**      Returns:
485**              zero.
486**
487**      Side Effects:
488**              waits for intvl time.  However, other events can
489**              be run during that interval.
490*/
491
492
493static bool     volatile SmSleepDone;
494
495#ifndef SLEEP_T
496# define SLEEP_T        unsigned int
497#endif /* ! SLEEP_T */
498
499SLEEP_T
500sleep(intvl)
501        unsigned int intvl;
502{
503        int was_held;
504
505        if (intvl == 0)
506                return (SLEEP_T) 0;
507        SmSleepDone = false;
508        (void) sm_setevent((time_t) intvl, sm_endsleep, 0);
509        was_held = sm_releasesignal(SIGALRM);
510        while (!SmSleepDone)
511                (void) pause();
512        if (was_held > 0)
513                (void) sm_blocksignal(SIGALRM);
514        return (SLEEP_T) 0;
515}
516
517static void
518sm_endsleep()
519{
520        /*
521        **  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
522        **      ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
523        **      DOING.
524        */
525
526        SmSleepDone = true;
527}
528
Note: See TracBrowser for help on using the repository browser.