home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1023 < prev    next >
Encoding:
Internet Message Format  |  1990-12-28  |  12.1 KB

  1. From: libes@cme.nist.gov (Don Libes)
  2. Newsgroups: alt.sources
  3. Subject: [unix-wizards] Re: Setting multiple timers from a single process.
  4. Message-ID: <11426@stag.math.lsa.umich.edu>
  5. Date: 16 Mar 90 20:45:26 GMT
  6.  
  7. Archive-name: bsd-multiple-timers/16-Mar-90
  8. Original-posting-by: libes@cme.nist.gov (Don Libes)
  9. Original-subject: Re: Setting multiple timers from a single process.
  10. Reposted-by: emv@math.lsa.umich.edu (Edward Vielmetti)
  11.  
  12. [This is an experimental alt.sources re-posting from the newsgroup(s)
  13. comp.unix.wizards. Comments on this service to emv@math.lsa.umich.edu 
  14. (Edward Vielmetti).]
  15.  
  16.  
  17. In article <3412@muffin.cme.nist.gov> libes@cme.nist.gov (Don Libes) writes:
  18. >In article <24200@uhnix1.uh.edu> rr@cs.uh.edu writes:
  19. >>The subj line says it; I want to set multiple timers from a single
  20. >>process. Using alarm() & catching SIGALARM will permit only a single
  21. >>timer. I need to set different values for the different timers 
  22. >>
  23. >>My solution at the moment; [spawn child processes for each timer]
  24. >
  25. >Simulate them by waiting for the shortest one, and keeping track of
  26. >the others yourself.  I've done exactly this.  Anyone who wants code
  27. >(4.2BSD) can mail me.
  28. >
  29. >Just watch out for things like 1) a timer interrupting your timer
  30. >management code, 2) trying to cancel one this is in the process of
  31. >going off (or will be by the time you return), 3) getting a new timer
  32. >which is shorter than the current one you are waiting for, and 4) the
  33. >possibility of several being scheduled for the same time.
  34.  
  35.  
  36. I've had a dozen people ask me for this already, so here is the
  37. source.
  38.  
  39. If you have already received this by mail, toss it as I've fixed one
  40. error that I inserted while I was trying to add documentation.  I've
  41. also added significantly more documentation.
  42.  
  43. /*
  44. Here is code to do multiple timers in 4.2BSD.  It was a bitch to
  45. debug.  However, the code is now very robust - we have been using it
  46. for several years.
  47.  
  48. I never expected to distribute this, so there was no documentation
  49. originally.  I'll give you some right now entered off the top of my
  50. head from what I remember.  You may contact me for nonobvious questions.
  51.  
  52. You want to do as little as possible from a signal handler.  In fact,
  53. all you want to do is figure out what the SIGALRM was for, and mark it
  54. so we can execute later, when it is safe to do so.
  55.  
  56. Thus, we declaring a timer as follows:
  57.  
  58.     _setimr(timeout,event,ast,arg,pname)
  59.  
  60. "timeout" is a relative time after which "ast" will be called with a
  61. single argument "arg".  (Really, it should be a vararg, but is an
  62. int.)  "pname" is a string to associate with this entry for debugging.
  63.  
  64. "ast" is not actually called by the signal handler at interrupt time.
  65. What really happens is that "event" is an address that is set to 1 if
  66. the timer has run down.
  67.  
  68. At your leisure, you can examine all the "event" flags for the
  69. accumulated asts, and execute whichever ones are ready.  In our
  70. implementation, we used a top-level dispatcher which ran continuously
  71. doing this.  Thus, as soon as one ast completed, the next one was
  72. executed, and they never interrupted one another.  (Synchronous
  73. threads were executed this way as well.  The asts were always given
  74. priority to the threads.)
  75.  
  76. There are two calls that you must supply:
  77.  
  78.     _dclast(event, ast, arg, pname);
  79.     _canast(event, ast, arg);
  80.  
  81. _dclast() enters events into your dispatcher, which _canast() removes them.
  82.  
  83.  
  84. Sorry for the lack of better documentation.  Hopefully, the rest of
  85. this should be obvious.
  86.  
  87. You will probably want to cut out some stuff that isn't pertinent to
  88. you.  In particular, this code demanded that the user provide time in
  89. 10 millisecond intervals.  That is totally artificial to BSD.
  90.  
  91. In some ways, the style of these calls are based on VMS.  (Hey, UNIX
  92. never addressed these issues, so shutup.)  To make up for that, the
  93. documentation is in the UNIX style (i.e., the source).  Ha ha!
  94.  
  95. "nip" in the comments below refers to a "network interface processor"
  96. which ran on a standalone 68k.  Hence the reason for our own
  97. dispatcher.  Someone else wrote the original 68k version, and it was
  98. my job to port it to UNIX.  So I had to do creative hacks like this.
  99. (The I/O was much worse, be glad you aren't seeing that.)
  100.  
  101. This is also an apology for the coding style.  It was partly his and
  102. partly mine, both from at least 5 years ago.  Nowadays I would never
  103. distribute such code.  (Translated: I still write code the same way,
  104. but now I would never distribute it.)
  105.  
  106. This code is in the public domain.  Please give credit to the authors
  107. and NIST.
  108.  
  109. Don Libes          libes@cme.nist.gov      ...!uunet!cme-durer!libes
  110. */
  111.  
  112. /* timer.c - This file implements routines for handling timeouts.
  113.  *
  114.  * User calls are:
  115.  *   _clkini() - initialize clock and timers
  116.  *   _setimr() - set a timer
  117.  *   _cantim() - cancel a timer
  118.  *   #disable_interrupts - disable clock and timer interrupts
  119.  *   #enable_interrupts - enable clock and timer interrupts
  120.  *
  121.  *
  122.  * History:
  123.  *     Originally written for standalone MC68000 with MC6840 timer
  124.  *     Ed Barkmeyer, NBS, September 1983
  125.  *
  126.  *     Rewritten for 4.2BSD UNIX with software timer.
  127.  *     Don Libes, NBS, October 1985
  128.  *
  129.  */
  130.  
  131. #include <stdio.h>
  132. #include <signal.h>
  133. #include <sys/time.h>
  134.  
  135. #ifndef TRUE
  136. #define TRUE    1
  137. #define FALSE    0
  138. #endif
  139.  
  140. typedef short int    int2;
  141. typedef int        int4;
  142.  
  143. #define TIMER_SIGNAL_TYPE    SIGALRM
  144. #define TIMER_INTERVAL_TYPE    ITIMER_REAL
  145. struct itimerval timer_value;
  146.  
  147. static unsigned timer_set_timestamp;    /* this holds the value of */
  148.                     /* current_time() whenever an */
  149.                     /* interval timer is started */
  150.  
  151. /* The following definitions support conversions between the basic unit of
  152. time in the nip (which is tens-of-milliseconds) and time in all the 4.2BSD
  153. system calls (which is seconds and microseconds).
  154. */
  155. #define USECS_PER_MSEC        1000
  156. #define MSECS_PER_SEC        1000
  157. #define USECS_PER_SEC        1000000
  158. #define MANY_TENS_OF_MSECS    (~0)        /* unsigned, please */
  159. /* useful for converting from 4.2BSD to nip units */
  160. #define USECS_PER_TEN_MSECS    (USECS_PER_MSEC*10)
  161. #define TENS_OF_MSECS_PER_SEC    (MSECS_PER_SEC/10)
  162. /* useful for converting from nip units to 4.2 units */
  163. #define SEC(x)            (x/TENS_OF_MSECS_PER_SEC)
  164. #define USEC(x)            ((x*USECS_PER_TEN_MSECS)%USECS_PER_SEC)
  165.  
  166. /*
  167.  
  168. A timer wakeup queue should be represented as a data structure that can do
  169. MIN, INSERT, DELETE and TRAVERSE operations in minimal time.  INSERT and
  170. DELETE are necessary to support declaring and cancelling of timers MIN is
  171. necessary to find out who has timed out and to decide what the next interval
  172. to timeout should be.  TRAVERSE is necessary for updating all the timers once
  173. a time period has elapsed.  Unfortunately, I forget what this optimal data
  174. structure is.  Until I remember, we shall use an array.  
  175.  
  176. The array is used as follows:  Each entry in the array has a timeout.  
  177. We will constantly be waiting for the entry with the shortest timeout.
  178. When that times out, we will subtract from each timer's timeout, the time that
  179. has passed since we first started waiting for the current timer.  
  180.  
  181. When a timeout occurs, the event flag is set.  A timer is released from
  182. use when the timeout is 0 AND the event flag points to 0.
  183.  
  184. */
  185.  
  186. #define MAX_TIMERS    24
  187. #define timer_inuse(t)    (t->timeout || t->event)
  188.  
  189. struct timer {
  190.     unsigned int timeout;        /* clock ticks to wait for */
  191.     int2 *event;            /* set to TRUE when time out occurs */
  192. } timerq[MAX_TIMERS];            /* timer queue */
  193. struct timer *next_timer;        /* timer we expect to run down next */
  194. struct timer *shortest_timer();
  195.  
  196. int oldmask;                /* signal mask */
  197. /* actually these just fiddle with the interrupt from the timer */
  198. #define disable_interrupts()    oldmask = sigblock(1<<(TIMER_SIGNAL_TYPE-1))
  199. #define enable_interrupts()    (void) sigsetmask(oldmask)
  200.  
  201. /* used when cancelling timers (if that works) */
  202. struct itimerval cancel_timer = {{ 0,0},{0,0}};
  203.  
  204. void clk_isr();
  205. unsigned int current_time();
  206.  
  207. void _clkini() {
  208.     struct timer *t;
  209.  
  210.     /* disable constant interval timing */
  211.     timerclear(&timer_value.it_interval);
  212.  
  213.     if (signal(TIMER_SIGNAL_TYPE,clk_isr) == BADSIG) {
  214.         perror("_clkini: signal failed\n");
  215.         exit(-1);
  216.     }
  217.     for (t=timerq;t<&timerq[MAX_TIMERS];t++) {
  218.         t->timeout = 0;
  219.         t->event = 0;
  220.     }
  221. }
  222.     
  223. int _setimr(timeout, event, ast, arg, pname)
  224. unsigned int timeout;        /* time to wait in 10msec ticks */
  225. int2 *event;            /* event flag to set on runout */
  226. void (*ast)();            /* routine to be called on runout or 0 */
  227. int4 arg;            /* argument to be provided to timeout ast */
  228. char *pname;
  229. {
  230.     struct timer *free_timer;    /* pointer to unused timer entry */
  231.     struct timer *t;
  232.  
  233.     *event = 0;
  234.     
  235.     /* if it has an associated ast, set it up */
  236.     if (ast) _dclast(event, ast, arg, pname);
  237.  
  238.     if (timeout == 0) { /* no time, so enable it and don't put on queue */
  239.         *event = TRUE;
  240.         return(TRUE);
  241.     }
  242.  
  243.     disable_interrupts();
  244.  
  245.     for (t=timerq;t<&timerq[MAX_TIMERS];t++) {
  246.         if (!timer_inuse(t)) {
  247.             free_timer = t;
  248.             break;
  249.         }
  250.     }
  251.     if (t == &timerq[MAX_TIMERS]) {
  252.         fprintf(stderr,"_setimr: out of timers\n");
  253.         exit(-1);
  254.     }
  255.  
  256.     /* install new timer */
  257.     /* cannot initialize free timer here, because cancel_itimer() will */
  258.     /* munge with it, so push it into the branches of the the if */
  259.     if (!next_timer) {
  260.         /* no timers set at all, so this is shortest */
  261.         free_timer->event = event;
  262.         free_timer->timeout = timeout;
  263.         start_timer(free_timer);
  264.     } else if ((timeout + current_time()) < 
  265.         (next_timer->timeout + timer_set_timestamp)) {
  266.         /* new timer is shorter than current one, so */
  267.         /* cancel current timer and set up new one */
  268.         /* not sure if the next setitimer() is actually necessary */
  269.         /* probably not, but I haven't tested to make sure */
  270.         cancel_itimer();
  271.         free_timer->event = event;
  272.         free_timer->timeout = timeout;
  273.         start_timer(free_timer);
  274.     } else {
  275.         /* new timer is longer, than current one */
  276.         free_timer->event = event;
  277.         free_timer->timeout = timeout;
  278.     }
  279.     enable_interrupts();
  280.     return(TRUE);
  281. }
  282.  
  283. int _cantim(event, rtn, arg)
  284. int2 *event;
  285. void (*rtn)();
  286. int4 arg;
  287. {
  288.     struct timer *t;
  289.  
  290.     if (rtn) _canast(event, rtn, arg);
  291.     disable_interrupts();
  292.  
  293.     for (t=timerq;t<&timerq[MAX_TIMERS];t++) {
  294.         if (t->event == event) {
  295.             t->timeout = 0;
  296.             t->event = 0;
  297.             /* check if we were waiting on this one */
  298.             if (t == next_timer) {
  299.                 cancel_itimer();
  300.                 start_timer(shortest_timer());
  301.             }
  302.             enable_interrupts();
  303.             return(TRUE);
  304.         }
  305.     }
  306.     enable_interrupts();
  307.     return(FALSE);
  308. }
  309.  
  310. /* clock interrupt handler */
  311. /* this routine is executed when an alarm signal occurs */
  312. void clk_isr() {
  313.     /* the following condition could be set up, 
  314.     if the interrupt was delivered while we were in cantim, cancelling
  315.     the last timer */
  316.  
  317.     if (next_timer == 0) return;
  318.  
  319.     update_all_timers_by(current_time() - timer_set_timestamp);
  320.     
  321.     /* start timer for next shortest guy if one exists */
  322.     start_timer(shortest_timer());
  323. }
  324.  
  325. /* subtract time from all timers, enabling any that run out along the way */
  326. update_all_timers_by(time)
  327. unsigned int time;
  328. {
  329.     struct timer *t;
  330.  
  331.     for (t=timerq;t<&timerq[MAX_TIMERS];t++) {
  332.         if (t->timeout) {
  333.             if (time < t->timeout) {
  334.                 t->timeout -= time;
  335.             } else {
  336.                 t->timeout = 0;
  337.                 /* if this has forced a timeout on */
  338.                 /* anyone else, enable it */
  339.                 *t->event = TRUE;
  340.                 t->event = 0;    /* remove timer */
  341.             }
  342.         }
  343.     }
  344. }
  345.  
  346. struct timer *
  347. shortest_timer()
  348. {
  349.     struct timer *t, *s_t;    /* shortest_timer */
  350.     unsigned int old_timer = MANY_TENS_OF_MSECS;
  351.  
  352.     for (s_t = 0,t=timerq;t<&timerq[MAX_TIMERS];t++) {
  353.         if (t->timeout > 0 && t->timeout < old_timer) {
  354.             old_timer = t->timeout;
  355.             s_t = t;
  356.         }
  357.     }
  358.     return(s_t);
  359. }
  360.  
  361. start_timer(t)
  362. struct timer *t;
  363. {
  364.     next_timer = t;        /* remember for _cantim and _setimr */
  365.     if (!t) return;
  366.  
  367.     timer_set_timestamp = current_time();
  368.  
  369.     timer_value.it_value.tv_sec = SEC(next_timer->timeout);
  370.     timer_value.it_value.tv_usec = USEC(next_timer->timeout);
  371.     if (-1 == setitimer(TIMER_INTERVAL_TYPE,&timer_value,
  372.                     (struct itimerval *)0)) {
  373.         perror("start_timer: setitimer");
  374.         exit(-1);
  375.     }
  376. }
  377.  
  378. /* return time in tens of milliseconds */
  379. unsigned int
  380. current_time()
  381. {
  382.     struct timeval tp;
  383.  
  384.     (void) gettimeofday(&tp,(struct timezone *)0);
  385.     /* ignore overflow */
  386.     return(tp.tv_sec*TENS_OF_MSECS_PER_SEC
  387.         + tp.tv_usec/USECS_PER_TEN_MSECS);
  388. }
  389.  
  390. cancel_itimer()
  391. {
  392.     if (-1 == setitimer(TIMER_INTERVAL_TYPE,&cancel_timer,
  393.                        (struct itimerval *)0)) {
  394.         perror("_setimr: setitimer failed");
  395.         exit(-1);
  396.     }
  397.     update_all_timers_by(current_time() - timer_set_timestamp);
  398. }
  399.