source: trunk/athena/etc/larvnetd/timer.c @ 11904

Revision 11904, 6.3 KB checked in by ghudson, 26 years ago (diff)
Larvnet is monitoring you.
Line 
1/* Copyright 1998 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16/* This file implements a mini-library of functions for setting up
17 * and processing timers.
18 */
19
20static const char rcsid[] = "$Id: timer.c,v 1.1 1998-09-01 20:57:47 ghudson Exp $";
21
22#include <sys/types.h>
23#include <stdlib.h>
24#include <time.h>
25#include <syslog.h>
26#include "larvnetd.h"
27#include "timer.h"
28
29/* DELTA is just an offset to keep the size a bit less than a power of
30 * two.  It's measured in pointers, so it's 32 bytes on most systems.
31 */
32#define DELTA 8
33#define INITIAL_HEAP_SIZE (1024 - DELTA)
34
35/* We have three operations which we need to be able to perform
36 * quickly: adding a timer, deleting a timer given a pointer to
37 * it, and determining which timer will be the next to go off.  A
38 * heap is an ideal data structure for these purposes, so we use
39 * one.  The heap is an array of pointers to timers, and each timer
40 * knows the position of its pointer in the heap.
41 *
42 * Okay, what is the heap, exactly?  It's a data structure,
43 * represented as an array, with the invariant condition that
44 * the timeout of heap[i] is less than or equal to the timeout of
45 * heap[i * 2 + 1] and heap[i * 2 + 2] (assuming i * 2 + 1 and
46 * i * 2 + 2 are valid * indices).  An obvious consequence of this
47 * is that heap[0] has the lowest timer value, so finding the first
48 * timer to go off is easy.  We say that an index i has "children"
49 * i * 2 + 1 and i * 2 + 1, and the "parent" (i - 1) / 2.
50 *
51 * To add a timer to the heap, we start by adding it to the end, and
52 * then keep swapping it with its parent until it has a parent with
53 * a timer value less than its value.  With a little bit of thought,
54 * you can see that this preserves the heap property on all indices
55 * of the array.
56 *
57 * To delete a timer at position i from the heap, we discard it and
58 * fill in its position with the last timer in the heap.  In order
59 * to restore the heap, we have to consider two cases: the timer
60 * value at i is less than that of its parent, or the timer value at
61 * i is greater than that of one of its children.  In the first case,
62 * we propagate the timer at i up the tree, swapping it with its
63 * parent, until the heap is restored; in the second case, we
64 * propagate the timer down the tree, swapping it with its least
65 * child, until the heap is restored.
66 */
67
68/* In order to ensure that the back pointers from timers are consistent
69 * with the heap pointers, all heap assignments should be done with the
70 * HEAP_ASSIGN() macro, which sets the back pointer and updates the
71 * heap at the same time.
72 */
73#define PARENT(i) (((i) - 1) / 2)
74#define CHILD1(i) ((i) * 2 + 1)
75#define CHILD2(i) ((i) * 2 + 2)
76#define TIME(i) (heap[i]->abstime)
77#define HEAP_ASSIGN(pos, tmr) ((heap[pos] = (tmr))->heap_pos = (pos))
78
79static Timer **heap;
80static int num_timers = 0;
81static int heap_size = 0;
82
83static void timer_botch(void *);
84static Timer *add_timer(Timer *);
85
86Timer *timer_set_rel(int reltime, Timer_proc proc, void *arg)
87{
88  return timer_set_abs(time(NULL) + reltime, proc, arg);
89}
90
91Timer *timer_set_abs(time_t abstime, Timer_proc proc, void *arg)
92{
93  Timer *timer;
94
95  timer = (Timer *) emalloc(sizeof(Timer));
96  timer->abstime = abstime;
97  timer->func = proc;
98  timer->arg = arg;
99  return add_timer(timer);
100}
101
102void *timer_reset(Timer *timer)
103{
104  int pos, min;
105  void *arg;
106
107  /* Free the timer, saving its heap position and argument. */
108  pos = timer->heap_pos;
109  arg = timer->arg;
110  free(timer);
111
112  if (pos != num_timers - 1)
113    {
114      /* Replace the timer with the last timer in the heap and
115       * restore the heap, propagating the timer either up or
116       * down, depending on which way it violates the heap
117       * property to insert the last timer in place of the
118       * deleted timer.
119       */
120      if (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos)))
121        {
122          do
123            {
124              HEAP_ASSIGN(pos, heap[PARENT(pos)]);
125              pos = PARENT(pos);
126            }
127          while (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos)));
128          HEAP_ASSIGN(pos, heap[num_timers - 1]);
129        }
130      else
131        {
132          while (CHILD2(pos) < num_timers)
133            {
134              min = num_timers - 1;
135              if (TIME(CHILD1(pos)) < TIME(min))
136                min = CHILD1(pos);
137              if (TIME(CHILD2(pos)) < TIME(min))
138                min = CHILD2(pos);
139              HEAP_ASSIGN(pos, heap[min]);
140              pos = min;
141            }
142          if (pos != num_timers - 1)
143            HEAP_ASSIGN(pos, heap[num_timers - 1]);
144        }
145    }
146  num_timers--;
147  return arg;
148}
149
150static Timer *add_timer(Timer *new)
151{
152  int pos;
153
154  /* Create or resize the heap as necessary. */
155  if (heap_size == 0)
156    {
157      heap_size = INITIAL_HEAP_SIZE;
158      heap = (Timer **) emalloc(heap_size * sizeof(Timer *));
159    }
160  else if (num_timers >= heap_size)
161    {
162      heap_size = heap_size * 2 + DELTA;
163      heap = (Timer **) erealloc(heap, heap_size * sizeof(Timer *));
164    }
165
166  /* Insert the Timer *into the heap. */
167  pos = num_timers;
168  while (pos > 0 && new->abstime < TIME(PARENT(pos)))
169    {
170      HEAP_ASSIGN(pos, heap[PARENT(pos)]);
171      pos = PARENT(pos);
172    }
173  HEAP_ASSIGN(pos, new);
174  num_timers++;
175
176  return new;
177}
178
179void timer_process(void)
180{
181  Timer *t;
182  Timer_proc func;
183  void *arg;
184
185  if (num_timers == 0 || heap[0]->abstime > time(NULL))
186    return;
187
188  /* Remove the first timer from the heap, remembering its
189   * function and argument.
190   */
191  t = heap[0];
192  func = t->func;
193  arg = t->arg;
194  t->func = timer_botch;
195  t->arg = NULL;
196  timer_reset(t);
197
198  /* Run the function. */
199  func(arg);
200}
201
202struct timeval *timer_timeout(struct timeval *tvbuf)
203{
204  if (num_timers > 0)
205    {
206      tvbuf->tv_sec = heap[0]->abstime - time(NULL);
207      if (tvbuf->tv_sec < 0)
208        tvbuf->tv_sec = 0;
209      tvbuf->tv_usec = 0;
210      return tvbuf;
211    }
212  else
213    return NULL;
214}
215
216static void timer_botch(void *arg)
217{
218  syslog(LOG_ALERT, "timer botch");
219  abort();
220}
Note: See TracBrowser for help on using the repository browser.