📄 event.c
字号:
/* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1999 University of Maryland at College Park * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, 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 U.M. not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. U.M. makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Authors: the Amanda Development Team. Its members are listed in a * file named AUTHORS, in the root directory of this distribution. *//* * $Id: event.c,v 1.24 2006/06/16 10:55:05 martinea Exp $ * * Event handler. Serializes different kinds of events to allow for * a uniform interface, central state storage, and centralized * interdependency logic. */#include "amanda.h"#include "event.h"#include "queue.h"#include "conffile.h"#define event_debug(i, ...) do { \ if ((i) <= debug_event) { \ dbprintf(__VA_ARGS__); \ } \} while (0)/* * The opaque handle passed back to the caller. This is typedefed to * event_handle_t in our header file. */struct event_handle { event_fn_t fn; /* function to call when this fires */ void *arg; /* argument to pass to previous function */ event_type_t type; /* type of event */ event_id_t data; /* type data */ time_t lastfired; /* timestamp of last fired (EV_TIME only) */ LIST_ENTRY(event_handle) le; /* queue handle */};/* * eventq is a queue of currently active events. * cache is a queue of unused handles. We keep a few around to avoid * malloc overhead when doing a lot of register/releases. */static struct { LIST_HEAD(, event_handle) listhead; int qlength;} eventq = { LIST_HEAD_INITIALIZER(eventq.listhead), 0}, cache = { LIST_HEAD_INITIALIZER(eventq.listhead), 0};#define eventq_first(q) LIST_FIRST(&q.listhead)#define eventq_next(eh) LIST_NEXT(eh, le)#define eventq_add(q, eh) LIST_INSERT_HEAD(&q.listhead, eh, le);#define eventq_remove(eh) LIST_REMOVE(eh, le);/* * How many items we can have in the handle cache before we start * freeing. */#define CACHEDEPTH 10/* * A table of currently set signal handlers. */static struct sigtabent { event_handle_t *handle; /* handle for this signal */ int score; /* number of signals recvd since last checked */ void (*oldhandler)(int);/* old handler (for unsetting) */} sigtable[NSIG];static const char *event_type2str(event_type_t);#define fire(eh) (*(eh)->fn)((eh)->arg)static void signal_handler(int);static event_handle_t *gethandle(void);static void puthandle(event_handle_t *);static int event_loop_wait (event_handle_t *, const int);/* * Add a new event. See the comment in event.h for what the arguments * mean. */event_handle_t *event_register( event_id_t data, event_type_t type, event_fn_t fn, void *arg){ event_handle_t *handle; if ((type == EV_READFD) || (type == EV_WRITEFD)) { /* make sure we aren't given a high fd that will overflow a fd_set */ if (data >= (int)FD_SETSIZE) { error(_("event_register: Invalid file descriptor %lu"), data); /*NOTREACHED*/ }#if !defined(__lint) /* Global checking knows that these are never called */ } else if (type == EV_SIG) { /* make sure signals are within range */ if (data >= NSIG) { error(_("event_register: Invalid signal %lu"), data); /*NOTREACHED*/ } if (sigtable[data].handle != NULL) { error(_("event_register: signal %lu already registered"), data); /*NOTREACHED*/ } } else if (type >= EV_DEAD) { error(_("event_register: Invalid event type %d"), type); /*NOTREACHED*/#endif } handle = gethandle(); handle->fn = fn; handle->arg = arg; handle->type = type; handle->data = data; handle->lastfired = -1; eventq_add(eventq, handle); eventq.qlength++; event_debug(1, _("event: register: %p->data=%lu, type=%s\n"), handle, handle->data, event_type2str(handle->type)); return (handle);}/* * Mark an event to be released. Because we may be traversing the queue * when this is called, we must wait until later to actually remove * the event. */voidevent_release( event_handle_t *handle){ assert(handle != NULL); event_debug(1, _("event: release (mark): %p data=%lu, type=%s\n"), handle, handle->data, event_type2str(handle->type)); assert(handle->type != EV_DEAD); /* * For signal events, we need to specially remove then from the * signal event table. */ if (handle->type == EV_SIG) { struct sigtabent *se = &sigtable[handle->data]; assert(se->handle == handle); signal((int)handle->data, se->oldhandler); se->handle = NULL; se->score = 0; } /* * Decrement the qlength now since this is no longer a real * event. */ eventq.qlength--; /* * Mark it as dead and leave it for the loop to remove. */ handle->type = EV_DEAD;}/* * Fire all EV_WAIT events waiting on the specified id. */intevent_wakeup( event_id_t id){ event_handle_t *eh; int nwaken = 0; event_debug(1, _("event: wakeup: enter (%lu)\n"), id); for (eh = eventq_first(eventq); eh != NULL; eh = eventq_next(eh)) { if (eh->type == EV_WAIT && eh->data == id) { event_debug(1, _("event: wakeup: %p id=%lu\n"), eh, id); fire(eh); nwaken++; } } return (nwaken);}/* * The event loop. We need to be specially careful here with adds and * deletes. Since adds and deletes will often happen while this is running, * we need to make sure we don't end up referencing a dead event handle. */voidevent_loop( const int dontblock){ event_loop_wait((event_handle_t *)NULL, dontblock);}intevent_wait( event_handle_t *eh){ return event_loop_wait(eh, 0);}/* * The event loop. We need to be specially careful here with adds and * deletes. Since adds and deletes will often happen while this is running, * we need to make sure we don't end up referencing a dead event handle. */static intevent_loop_wait( event_handle_t *wait_eh, const int dontblock){#ifdef ASSERTIONS static int entry = 0;#endif SELECT_ARG_TYPE readfds, writefds, errfds, werrfds; struct timeval timeout, *tvptr; int ntries, maxfd, rc; long interval; time_t curtime; event_handle_t *eh, *nexteh; struct sigtabent *se; int event_wait_fired = 0; int see_event; event_debug(1, _("event: loop: enter: dontblock=%d, qlength=%d, eh=%p\n"), dontblock, eventq.qlength, wait_eh); /* * If we have no events, we have nothing to do */ if (eventq.qlength == 0) return 0; /* * We must not be entered twice */ assert(++entry == 1); ntries = 0; /* * Save a copy of the current time once, to reduce syscall load * slightly. */ curtime = time(NULL); do { if (debug_event >= 1) { event_debug(1, _("event: loop: dontblock=%d, qlength=%d eh=%p\n"), dontblock, eventq.qlength, wait_eh); for (eh = eventq_first(eventq); eh != NULL; eh = eventq_next(eh)) { event_debug(1, _("%p): %s data=%lu fn=%p arg=%p\n"), eh, event_type2str(eh->type), eh->data, eh->fn, eh->arg); } } /* * Set ourselves up with no timeout initially. */ timeout.tv_sec = 0; timeout.tv_usec = 0; /* * If we can block, initially set the tvptr to NULL. If * we come across timeout events in the loop below, they * will set it to an appropriate buffer. If we don't * see any timeout events, then tvptr will remain NULL * and the select will properly block indefinately. * * If we can't block, set it to point to the timeout buf above. */ if (dontblock) tvptr = &timeout; else tvptr = NULL; /* * Rebuild the select bitmasks each time.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -