2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1999 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: event.c,v 1.20 2005/10/02 15:31:07 martinea Exp $
29 * Event handler. Serializes different kinds of events to allow for
30 * a uniform interface, central state storage, and centralized
31 * interdependency logic.
34 /*#define EVENT_DEBUG*/
37 #define eventprintf(x) dbprintf(x)
39 #define eventprintf(x)
47 * The opaque handle passed back to the caller. This is typedefed to
48 * event_handle_t in our header file.
51 event_fn_t fn; /* function to call when this fires */
52 void *arg; /* argument to pass to previous function */
53 event_type_t type; /* type of event */
54 event_id_t data; /* type data */
55 time_t lastfired; /* timestamp of last fired (EV_TIME only) */
56 LIST_ENTRY(event_handle) le; /* queue handle */
60 * eventq is a queue of currently active events.
61 * cache is a queue of unused handles. We keep a few around to avoid
62 * malloc overhead when doing a lot of register/releases.
65 LIST_HEAD(, event_handle) listhead;
68 LIST_HEAD_INITIALIZER(eventq.listhead), 0
70 LIST_HEAD_INITIALIZER(eventq.listhead), 0
72 #define eventq_first(q) LIST_FIRST(&q.listhead)
73 #define eventq_next(eh) LIST_NEXT(eh, le)
74 #define eventq_add(q, eh) LIST_INSERT_HEAD(&q.listhead, eh, le);
75 #define eventq_remove(eh) LIST_REMOVE(eh, le);
78 * How many items we can have in the handle cache before we start
84 * A table of currently set signal handlers.
86 static struct sigtabent {
87 event_handle_t *handle; /* handle for this signal */
88 int score; /* number of signals recvd since last checked */
89 void (*oldhandler) P((int));/* old handler (for unsetting) */
93 static const char *event_type2str P((event_type_t));
95 #define fire(eh) (*(eh)->fn)((eh)->arg)
96 static void signal_handler P((int));
97 static event_handle_t *gethandle P((void));
98 static void puthandle P((event_handle_t *));
101 * Add a new event. See the comment in event.h for what the arguments
105 event_register(data, type, fn, arg)
111 event_handle_t *handle;
116 /* make sure we aren't given a high fd that will overflow a fd_set */
117 assert(data < FD_SETSIZE);
121 /* make sure signals are within range */
123 /* make sure we don't double-register a signal */
124 assert(sigtable[data].handle == NULL);
133 /* callers can't register EV_DEAD */
138 handle = gethandle();
143 handle->lastfired = -1;
144 eventq_add(eventq, handle);
147 eventprintf(("%s: event: register: %X data=%lu, type=%s\n", debug_prefix_time(NULL), (int)handle,
148 handle->data, event_type2str(handle->type)));
153 * Mark an event to be released. Because we may be traversing the queue
154 * when this is called, we must wait until later to actually remove
158 event_release(handle)
159 event_handle_t *handle;
162 assert(handle != NULL);
164 eventprintf(("%s: event: release (mark): %X data=%lu, type=%s\n", debug_prefix_time(NULL),
165 (int)handle, handle->data, event_type2str(handle->type)));
166 assert(handle->type != EV_DEAD);
169 * For signal events, we need to specially remove then from the
170 * signal event table.
172 if (handle->type == EV_SIG) {
173 struct sigtabent *se = &sigtable[handle->data];
175 assert(se->handle == handle);
176 signal(handle->data, se->oldhandler);
182 * Decrement the qlength now since this is no longer a real
188 * Mark it as dead and leave it for the loop to remove.
190 handle->type = EV_DEAD;
194 * Fire all EV_WAIT events waiting on the specified id.
203 eventprintf(("%s: event: wakeup: enter (%lu)\n", debug_prefix_time(NULL), id));
207 for (eh = eventq_first(eventq); eh != NULL; eh = eventq_next(eh)) {
209 if (eh->type == EV_WAIT && eh->data == id) {
210 eventprintf(("%s: event: wakeup: %X id=%lu\n", debug_prefix_time(NULL), (int)eh, id));
220 * The event loop. We need to be specially careful here with adds and
221 * deletes. Since adds and deletes will often happen while this is running,
222 * we need to make sure we don't end up referencing a dead event handle.
225 event_loop(dontblock)
229 static int entry = 0;
231 fd_set readfds, writefds, errfds, werrfds;
232 struct timeval timeout, *tvptr;
233 int ntries, maxfd, rc, interval;
235 event_handle_t *eh, *nexteh;
236 struct sigtabent *se;
238 eventprintf(("%s: event: loop: enter: dontblock=%d, qlength=%d\n", debug_prefix_time(NULL),
239 dontblock, eventq.qlength));
242 * If we have no events, we have nothing to do
244 if (eventq.qlength == 0)
248 * We must not be entered twice
250 assert(++entry == 1);
255 * Save a copy of the current time once, to reduce syscall load
258 curtime = time(NULL);
262 eventprintf(("%s: event: loop: dontblock=%d, qlength=%d\n", debug_prefix_time(NULL), dontblock,
264 for (eh = eventq_first(eventq); eh != NULL; eh = eventq_next(eh)) {
265 eventprintf(("%s: %X: %s data=%lu fn=0x%x arg=0x%x\n", debug_prefix_time(NULL), (int)eh,
266 event_type2str(eh->type), eh->data, (int)eh->fn, (int)eh->arg));
270 * Set ourselves up with no timeout initially.
276 * If we can block, initially set the tvptr to NULL. If
277 * we come across timeout events in the loop below, they
278 * will set it to an appropriate buffer. If we don't
279 * see any timeout events, then tvptr will remain NULL
280 * and the select will properly block indefinately.
282 * If we can't block, set it to point to the timeout buf above.
290 * Rebuild the select bitmasks each time.
298 * Run through each event handle and setup the events.
299 * We save our next pointer early in case we GC some dead
302 for (eh = eventq_first(eventq); eh != NULL; eh = nexteh) {
303 nexteh = eventq_next(eh);
308 * Read fds just get set into the select bitmask
311 FD_SET(eh->data, &readfds);
312 FD_SET(eh->data, &errfds);
313 maxfd = max(maxfd, eh->data);
317 * Likewise with write fds
320 FD_SET(eh->data, &writefds);
321 FD_SET(eh->data, &errfds);
322 maxfd = max(maxfd, eh->data);
326 * Only set signals that aren't already set to avoid unnecessary
330 se = &sigtable[eh->data];
332 if (se->handle == eh)
335 /* no previous handle */
336 assert(se->handle == NULL);
339 se->oldhandler = signal(eh->data, signal_handler);
343 * Compute the timeout for this select
346 /* if we're not supposed to block, then leave it at 0 */
350 if (eh->lastfired == -1)
351 eh->lastfired = curtime;
353 interval = eh->data - (curtime - eh->lastfired);
358 timeout.tv_sec = min(timeout.tv_sec, interval);
360 /* this is the first timeout */
362 timeout.tv_sec = interval;
367 * Wait events are processed immediately by event_wakeup()
389 eventprintf(("%s: event: select: dontblock=%d, maxfd=%d, timeout=%ld\n", debug_prefix_time(NULL),
390 dontblock, maxfd, tvptr != NULL ? timeout.tv_sec : -1));
391 rc = select(maxfd + 1, &readfds, &writefds, &errfds, tvptr);
392 eventprintf(("%s: event: select returns %d\n", debug_prefix_time(NULL), rc));
395 * Select errors can mean many things. Interrupted events should
396 * not be fatal, since they could be delivered signals which still
397 * need to have their events fired.
400 if (errno != EINTR) {
402 error("select failed: %s", strerror(errno));
405 /* proceed if errno == EINTR, we may have caught a signal */
407 /* contents cannot be trusted */
414 * Grab the current time again for use in timed events.
416 curtime = time(NULL);
419 * We need to copy the errfds into werrfds, so file descriptors
420 * that are being polled for both reading and writing have
421 * both of their poll events 'see' the error.
423 memcpy(&werrfds, &errfds, sizeof(werrfds));
426 * Now run through the events and fire the ones that are ready.
427 * Don't handle file descriptor events if the select failed.
429 for (eh = eventq_first(eventq); eh != NULL; eh = eventq_next(eh)) {
433 * Read fds: just fire the event if set in the bitmask
436 if (FD_ISSET(eh->data, &readfds) ||
437 FD_ISSET(eh->data, &errfds)) {
438 FD_CLR(eh->data, &readfds);
439 FD_CLR(eh->data, &errfds);
445 * Write fds: same as Read fds
448 if (FD_ISSET(eh->data, &writefds) ||
449 FD_ISSET(eh->data, &werrfds)) {
450 FD_CLR(eh->data, &writefds);
451 FD_CLR(eh->data, &werrfds);
457 * Signal events: check the score for fires, and run the
458 * event if we got one.
461 se = &sigtable[eh->data];
463 assert(se->handle == eh);
470 * Timed events: check the interval elapsed since last fired,
471 * and set it off if greater or equal to requested interval.
474 if (eh->lastfired == -1)
475 eh->lastfired = curtime;
476 if (curtime - eh->lastfired >= eh->data) {
477 eh->lastfired = curtime;
483 * Wait events are handled immediately by event_wakeup()
484 * Dead events are handled by the pre-select loop.
495 } while (!dontblock && eventq.qlength > 0);
497 assert(--entry == 0);
501 * Generic signal handler. Used to count caught signals for the event
505 signal_handler(signo)
509 assert(signo >= 0 && signo < sizeof(sigtable) / sizeof(sigtable[0]));
510 sigtable[signo].score++;
514 * Return a new handle. Take from the handle cache if not empty. Otherwise,
517 static event_handle_t *
522 if ((eh = eventq_first(cache)) != NULL) {
523 assert(cache.qlength > 0);
528 assert(cache.qlength == 0);
529 return (alloc(sizeof(*eh)));
533 * Free a handle. If there's space in the handle cache, put it there.
534 * Otherwise, free it.
541 if (cache.qlength > CACHEDEPTH) {
545 eventq_add(cache, eh);
551 * Convert an event type into a string
557 static const struct {
561 #define X(s) { s, stringize(s) }
572 for (i = 0; i < sizeof(event_types) / sizeof(event_types[0]); i++)
573 if (type == event_types[i].type)
574 return (event_types[i].name);
575 return ("BOGUS EVENT TYPE");
577 #endif /* EVENT_DEBUG */