1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
|
/* This file is part of the Project Athena Zephyr Notification System.
* It contains functions for managing multiple timeouts.
*
* Created by: John T. Kohl
* Derived from timer_manager_ by Ken Raeburn
*
* $Source: /mit/zephyr/repository/zephyr/server/timer.c,v $
* $Author: ghudson $
*
*/
#include "zserver.h"
#ifndef SABER
#ifndef lint
static const char rcsid[] =
"$Id: timer.c,v 1.18 1995/07/07 22:12:27 ghudson Exp $";
#endif /* lint */
#endif /* SABER */
/*
* timer_manager_ -- routines for handling timers in login_shell
* (and elsewhere)
*
* Copyright 1986 Student Information Processing Board,
* Massachusetts Institute of Technology
*
* written by Ken Raeburn
Permission to use, copy, modify, and distribute this
software and its documentation for any purpose and without
fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright
notice and this permission notice appear in supporting
documentation, and that the name of M.I.T. and the Student
Information Processing Board not be used in
advertising or publicity pertaining to distribution of the
software without specific, written prior permission.
M.I.T. and the Student Information Processing Board
make no representations about the suitability of
this software for any purpose. It is provided "as is"
without express or implied warranty.
*/
/*
* External functions:
*
* Timer *timer_set_rel (time_rel, proc, arg)
* long time_rel;
* void (*proc)();
* caddr_t arg;
* Timer *timer_set_abs (time_abs, proc, arg)
* long time_abs;
* void (*proc)();
* caddr_t arg;
*
* void timer_reset(tmr)
* Timer *tmr;
*
* void timer_process()
*
*/
/* DELTA is just an offset to keep the size a bit less than a power
* of two. It's measured in pointers, so it's 32 bytes on most
* systems. */
#define DELTA 8
#define INITIAL_HEAP_SIZE (1024 - DELTA)
/* We have three operations which we need to be able to perform
* quickly: adding a timer, deleting a timer given a pointer to
* it, and determining which timer will be the next to go off. A
* heap is an ideal data structure for these purposes, so we use
* one. The heap is an array of pointers to timers, and each timer
* knows the position of its pointer in the heap.
*
* Okay, what is the heap, exactly? It's a data structure,
* represented as an array, with the invariant condition that
* the timeout of heap[i] is less than or equal to the timeout of
* heap[i * 2 + 1] and heap[i * 2 + 2] (assuming i * 2 + 1 and
* i * 2 + 2 are valid * indices). An obvious consequence of this
* is that heap[0] has the lowest timer value, so finding the first
* timer to go off is easy. We say that an index i has "children"
* i * 2 + 1 and i * 2 + 1, and the "parent" (i - 1) / 2.
*
* To add a timer to the heap, we start by adding it to the end, and
* then keep swapping it with its parent until it has a parent with
* a timer value less than its value. With a little bit of thought,
* you can see that this preserves the heap property on all indices
* of the array.
*
* To delete a timer at position i from the heap, we discard it and
* fill in its position with the last timer in the heap. In order
* to restore the heap, we have to consider two cases: the timer
* value at i is less than that of its parent, or the timer value at
* i is greater than that of one of its children. In the first case,
* we propagate the timer at i up the tree, swapping it with its
* parent, until the heap is restored; in the second case, we
* propagate the timer down the tree, swapping it with its least
* child, until the heap is restored. */
/* In order to ensure that the back pointers from timers are consistent
* with the heap pointers, all heap assignments should be done with the
* HEAP_ASSIGN() macro, which sets the back pointer and updates the
* heap at the same time. */
#define PARENT(i) (((i) - 1) / 2)
#define CHILD1(i) ((i) * 2 + 1)
#define CHILD2(i) ((i) * 2 + 2)
#define TIME(i) (heap[i]->abstime)
#define HEAP_ASSIGN(pos, tmr) ((heap[pos] = (tmr))->heap_pos = (pos))
time_t nexttimo = 0L; /* the Unix time of the next alarm */
static Timer **heap;
static int num_timers = 0;
static int heap_size = 0;
static void timer_botch __P((void*));
static Timer *add_timer __P((Timer *));
/*
* timer_set_rel(time_rel, proc)
* time_rel: alarm time relative to now, in seconds
* proc: subroutine to be called (no args, returns void)
*
* creates a "timer" and adds it to the current list, returns "timer"
*/
Timer *timer_set_rel(time_rel, proc, arg)
long time_rel;
void (*proc) __P((void *));
void *arg;
{
Timer *new_t;
new_t = (Timer *) malloc(sizeof(*new_t));
if (new_t == NULL)
return(NULL);
new_t->abstime = time_rel + NOW;
new_t->func = proc;
new_t->arg = arg;
return add_timer(new_t);
}
/*
* timer_reset
*
* args:
* tmr: timer to be removed from the list
*
* removes any timers matching tmr and reallocates list
*
*/
void
timer_reset(tmr)
Timer *tmr;
{
int pos, min;
/* Free the timer, saving its heap position. */
pos = tmr->heap_pos;
free(tmr);
if (pos != num_timers - 1) {
/* Replace the timer with the last timer in the heap and
* restore the heap, propagating the timer either up or
* down, depending on which way it violates the heap
* property to insert the last timer in place of the
* deleted timer. */
if (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos))) {
do {
HEAP_ASSIGN(pos, heap[PARENT(pos)]);
pos = PARENT(pos);
} while (pos > 0 && TIME(num_timers - 1) < TIME(PARENT(pos)));
HEAP_ASSIGN(pos, heap[num_timers - 1]);
} else {
while (CHILD2(pos) < num_timers) {
min = num_timers - 1;
if (TIME(CHILD1(pos)) < TIME(min))
min = CHILD1(pos);
if (TIME(CHILD2(pos)) < TIME(min))
min = CHILD2(pos);
HEAP_ASSIGN(pos, heap[min]);
pos = min;
}
if (pos != num_timers - 1)
HEAP_ASSIGN(pos, heap[num_timers - 1]);
}
}
num_timers--;
/* Fix up the next timeout. */
nexttimo = (num_timers == 0) ? 0 : heap[0]->abstime;
}
#define set_timeval(t,s) ((t).tv_sec=(s),(t).tv_usec=0,(t))
/* add_timer(t:timer)
*
* args:
* t: new "timer" to be added
*
* returns:
* 0 if successful
* -1 if error (errno set) -- old time table may have been destroyed
*
*/
static Timer *
add_timer(new)
Timer *new;
{
int pos;
/* Create or resize the heap as necessary. */
if (heap_size == 0) {
heap_size = INITIAL_HEAP_SIZE;
heap = (Timer **) malloc(heap_size * sizeof(Timer *));
} else if (num_timers >= heap_size) {
heap_size = heap_size * 2 + DELTA;
heap = (Timer **) realloc(heap, heap_size * sizeof(Timer *));
}
if (!heap) {
free(new);
return NULL;
}
/* Insert the Timer *into the heap. */
pos = num_timers;
while (pos > 0 && new->abstime < TIME(PARENT(pos))) {
HEAP_ASSIGN(pos, heap[PARENT(pos)]);
pos = PARENT(pos);
}
HEAP_ASSIGN(pos, new);
num_timers++;
/* Fix up the next timeout. */
nexttimo = heap[0]->abstime;
return new;
}
/*
* timer_process -- checks for next timer execution time
* and execute
*
*/
void
timer_process()
{
Timer *t;
timer_proc func;
void *arg;
int valid = 0;
if (num_timers == 0 || heap[0]->abstime > NOW)
return;
/* Remove the first timer from the heap, remembering it's
* function and argument. timer_reset() updates nexttimo. */
t = heap[0];
func = t->func;
arg = t->arg;
t->func = timer_botch;
t->arg = NULL;
timer_reset(t);
/* Run the function. */
func(arg);
}
static void
timer_botch(arg)
void *arg;
{
syslog(LOG_CRIT, "timer botch\n");
abort();
}
|