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.24 2006/06/16 10:55:05 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.
33 * This is a compatibility wrapper over Glib's GMainLoop. New code should
34 * use Glib's interface directly.
36 * Each event_handle is associated with a unique GSource, identified by it
43 #include "glib-util.h"
45 /* TODO: use mem chunks to allocate event_handles */
46 /* TODO: lock stuff for threading */
48 /* Write a debugging message if the config variable debug_event
49 * is greater than or equal to i */
50 #define event_debug(i, ...) do { \
51 if ((i) <= debug_event) { \
52 dbprintf(__VA_ARGS__); \
57 * The opaque handle passed back to the caller. This is typedefed to
58 * event_handle_t in our header file.
61 event_fn_t fn; /* function to call when this fires */
62 void *arg; /* argument to pass to previous function */
64 event_type_t type; /* type of event */
65 event_id_t data; /* type data */
67 GSource *source; /* Glib event source, if one exists */
68 guint source_id; /* ID of the glib event source */
70 gboolean has_fired; /* for use by event_wait() */
71 gboolean is_dead; /* should this event be deleted? */
74 /* A list of all extant event_handle objects, used for searching for particular
75 * events and for deleting dead events */
82 static const char *event_type2str(event_type_t type);
84 /* "Fire" an event handle, by calling its callback function */
85 #define fire(eh) do { \
86 event_debug(1, "firing %p: %s/%jd\n", eh, event_type2str((eh)->type), (eh)->data); \
87 (*(eh)->fn)((eh)->arg); \
88 (eh)->has_fired = TRUE; \
91 /* Adapt a Glib callback to an event_handle_t callback; assumes that the
92 * user_ptr for the Glib callback is a pointer to the event_handle_t. */
94 event_handle_callback(
97 event_handle_t *hdl = (event_handle_t *)user_ptr;
99 /* if the handle is dead, then don't fire the callback; this means that
100 * we're in the process of freeing the event */
105 /* don't ever let GMainLoop destroy GSources */
120 event_handle_t *handle;
123 /* sanity-checking */
124 if ((type == EV_READFD) || (type == EV_WRITEFD)) {
125 /* make sure we aren't given a high fd that will overflow a fd_set */
126 if (data >= (int)FD_SETSIZE) {
127 error(_("event_register: Invalid file descriptor %jd"), data);
130 } else if (type == EV_TIME) {
132 error(_("event_register: interval for EV_TIME must be greater than 0; got %jd"), data);
136 handle = g_new0(event_handle_t, 1);
141 handle->is_dead = FALSE;
143 event_debug(1, _("event: register: %p->data=%jd, type=%s\n"),
144 handle, handle->data, event_type2str(handle->type));
146 /* add to the list of events */
147 all_events = g_slist_prepend(all_events, (gpointer)handle);
149 /* and set up the GSource for this event */
153 /* create a new source */
154 if (type == EV_READFD) {
155 cond = G_IO_IN | G_IO_HUP | G_IO_ERR;
157 cond = G_IO_OUT | G_IO_ERR;
160 handle->source = new_fdsource(data, cond);
162 /* attach it to the default GMainLoop */
163 g_source_attach(handle->source, NULL);
164 handle->source_id = g_source_get_id(handle->source);
166 /* And set its callbacks */
167 g_source_set_callback(handle->source, event_handle_callback,
168 (gpointer)handle, NULL);
170 /* drop our reference to it, so when it's detached, it will be
172 g_source_unref(handle->source);
176 /* Glib provides a nice shortcut for timeouts. The *1000 converts
177 * seconds to milliseconds. */
178 handle->source_id = g_timeout_add(data * 1000, event_handle_callback,
181 /* But it doesn't give us the source directly.. */
182 handle->source = g_main_context_find_source_by_id(NULL, handle->source_id);
183 /* EV_TIME must always be handled after EV_READ */
184 g_source_set_priority(handle->source, 10);
188 /* nothing to do -- these are handled independently of GMainLoop */
192 error(_("Unknown event type %s"), event_type2str(type));
199 * Mark an event to be released. Because we may be traversing the queue
200 * when this is called, we must wait until later to actually remove
205 event_handle_t *handle)
207 assert(handle != NULL);
209 event_debug(1, _("event: release (mark): %p data=%jd, type=%s\n"),
210 handle, handle->data,
211 event_type2str(handle->type));
212 assert(!handle->is_dead);
214 /* Mark it as dead and leave it for the event_loop to remove */
215 handle->is_dead = TRUE;
219 * Fire all EV_WAIT events waiting on the specified id.
226 GSList *tofire = NULL;
229 event_debug(1, _("event: wakeup: enter (%jd)\n"), id);
231 /* search for any and all matching events, and record them. This way
232 * we have determined the whole list of events we'll be firing *before*
233 * we fire any of them. */
234 for (iter = all_events; iter != NULL; iter = g_slist_next(iter)) {
235 event_handle_t *eh = (event_handle_t *)iter->data;
236 if (eh->type == EV_WAIT && eh->data == id && !eh->is_dead) {
237 tofire = g_slist_append(tofire, (gpointer)eh);
242 for (iter = tofire; iter != NULL; iter = g_slist_next(iter)) {
243 event_handle_t *eh = (event_handle_t *)iter->data;
244 if (eh->type == EV_WAIT && eh->data == id && !eh->is_dead) {
245 event_debug(1, _("A: event: wakeup triggering: %p id=%jd\n"), eh, id);
251 /* and free the temporary list */
252 g_slist_free(tofire);
262 static void event_loop_wait (event_handle_t *, const int);
268 event_loop_wait(NULL, nonblock);
275 event_loop_wait(eh, 0);
278 /* Flush out any dead events in all_events. Be careful that this
279 * isn't called while someone is iterating over all_events.
281 * @param wait_eh: the event handle we're waiting on, which shouldn't
285 flush_dead_events(event_handle_t *wait_eh)
289 for (iter = all_events; iter != NULL; iter = next) {
290 event_handle_t *hdl = (event_handle_t *)iter->data;
291 next = g_slist_next(iter);
293 /* (handle the case when wait_eh is dead by simply not deleting
294 * it; the next run of event_loop will take care of it) */
295 if (hdl->is_dead && hdl != wait_eh) {
296 all_events = g_slist_delete_link(all_events, iter);
297 if (hdl->source) g_source_destroy(hdl->source);
304 /* Return TRUE if we have any events outstanding that can be dispatched
305 * by GMainLoop. Recall EV_WAIT events appear in all_events, but are
306 * not dispatched by GMainLoop. */
308 any_mainloop_events(void)
312 for (iter = all_events; iter != NULL; iter = g_slist_next(iter)) {
313 event_handle_t *hdl = (event_handle_t *)iter->data;
314 event_debug(2, _("list %p: %s/%jd\n"), hdl, event_type2str((hdl)->type), (hdl)->data);
315 if (hdl->type != EV_WAIT)
324 event_handle_t *wait_eh,
327 event_debug(1, _("event: loop: enter: nonblockg=%d, eh=%p\n"), nonblock, wait_eh);
329 /* If we're waiting for a specific event, then reset its has_fired flag */
331 wait_eh->has_fired = FALSE;
334 /* Keep looping until there are no events, or until wait_eh has fired */
336 /* clean up first, so we don't accidentally check a dead source */
337 flush_dead_events(wait_eh);
339 /* if there's nothing to wait for, then don't block, but run an
340 * iteration so that any other users of GMainLoop will get a chance
342 if (!any_mainloop_events())
345 /* Do an interation */
346 g_main_context_iteration(NULL, !nonblock);
348 /* If the event we've been waiting for has fired or been released, as
349 * appropriate, we're done. See the comments for event_wait in event.h
350 * for the skinny on this weird expression. */
351 if (wait_eh && ((wait_eh->type == EV_WAIT && wait_eh->is_dead)
352 || (wait_eh->type != EV_WAIT && wait_eh->has_fired)))
355 /* Don't loop if we're not blocking */
360 /* extra cleanup, to keep all_events short, and to delete wait_eh if it
361 * has been released. */
362 flush_dead_events(NULL);
367 default_main_loop(void)
369 static GMainLoop *loop = NULL;
371 loop = g_main_loop_new(NULL, TRUE);
376 * Convert an event type into a string
382 static const struct {
386 #define X(s) { s, stringize(s) }
395 for (i = 0; i < (size_t)(sizeof(event_types) / sizeof(event_types[0])); i++)
396 if (type == event_types[i].type)
397 return (event_types[i].name);
398 return (_("BOGUS EVENT TYPE"));
402 * FDSource -- a source for a file descriptor
404 * We could use Glib's GIOChannel for this, but it adds some buffering
405 * and Unicode functionality that we really don't want. The custom GSource
406 * is simple enough anyway, and the Glib documentation describes it in prose.
409 typedef struct FDSource {
410 GSource source; /* must be the first element in the struct */
411 GPollFD pollfd; /* Our file descriptor */
416 GSource *source G_GNUC_UNUSED,
419 *timeout_ = -1; /* block forever, as far as we're concerned */
427 FDSource *fds = (FDSource *)source;
429 /* we need to be dispatched if any interesting events have been received by the FD */
430 return fds->pollfd.events & fds->pollfd.revents;
435 GSource *source G_GNUC_UNUSED,
436 GSourceFunc callback,
440 return callback(user_data);
442 /* Don't automatically detach the event source if there's no callback. */
447 new_fdsource(gint fd, GIOCondition events)
449 static GSourceFuncs *fdsource_funcs = NULL;
453 /* initialize these here to avoid a compiler warning */
454 if (!fdsource_funcs) {
455 fdsource_funcs = g_new0(GSourceFuncs, 1);
456 fdsource_funcs->prepare = fdsource_prepare;
457 fdsource_funcs->check = fdsource_check;
458 fdsource_funcs->dispatch = fdsource_dispatch;
461 src = g_source_new(fdsource_funcs, sizeof(FDSource));
462 fds = (FDSource *)src;
465 fds->pollfd.events = events;
466 g_source_add_poll(src, &fds->pollfd);
472 * ChildWatchSource -- a source for a file descriptor
474 * Newer versions of glib provide equivalent functionality; consider
475 * optionally using that, protected by a GLIB_CHECK_VERSION condition.
478 /* Versions before glib-2.4.0 didn't include a child watch source, and versions
479 * before 2.6.0 used unreliable signals. On these versions, we implement
480 * a "dumb" version of our own invention. This is dumb in the sense that it
481 * doesn't use SIGCHLD to detect a dead child, preferring to just poll at
482 * exponentially increasing interals. Writing a smarter implementation runs into
483 * some tricky race conditions and extra machinery. Since there are few, if any,
484 * users of a glib version this old, such machinery wouldn't get much testing.
486 * FreeBSD users have also reported problems with the glib child watch source,
487 * so we use the dumb version on FreeBSD, too.
490 #if (defined(__FreeBSD__) || GLIB_MAJOR_VERSION < 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 6))
491 typedef struct ChildWatchSource {
492 GSource source; /* must be the first element in the struct */
502 /* this corresponds to rapid checks for about 10 seconds, after which the
503 * waitpid() check occurs every 2 seconds. */
504 #define CWS_BASE_TIMEOUT 20
505 #define CWS_MULT_TIMEOUT 1.1
506 #define CWS_MAX_TIMEOUT 2000
509 child_watch_source_prepare(
513 ChildWatchSource *cws = (ChildWatchSource *)source;
515 *timeout_ = cws->timeout;
517 cws->timeout *= CWS_MULT_TIMEOUT;
518 if (cws->timeout > CWS_MAX_TIMEOUT) cws->timeout = CWS_MAX_TIMEOUT;
524 child_watch_source_check(
527 ChildWatchSource *cws = (ChildWatchSource *)source;
530 if (!cws->dead && waitpid(cws->pid, &cws->status, WNOHANG) > 0) {
538 child_watch_source_dispatch(
539 GSource *source G_GNUC_UNUSED,
540 GSourceFunc callback,
543 ChildWatchSource *cws = (ChildWatchSource *)source;
545 /* this shouldn't happen, but just in case */
548 g_warning("child %jd died before callback was registered", (uintmax_t)cws->pid);
552 ((ChildWatchFunc)callback)(cws->pid, cws->status, user_data);
554 /* Un-queue this source unconditionally -- the child can't die twice */
562 new_child_watch_source(pid_t pid)
564 static GSourceFuncs *child_watch_source_funcs = NULL;
566 ChildWatchSource *cws;
568 /* initialize these here to avoid a compiler warning */
569 if (!child_watch_source_funcs) {
570 child_watch_source_funcs = g_new0(GSourceFuncs, 1);
571 child_watch_source_funcs->prepare = child_watch_source_prepare;
572 child_watch_source_funcs->check = child_watch_source_check;
573 child_watch_source_funcs->dispatch = child_watch_source_dispatch;
576 src = g_source_new(child_watch_source_funcs, sizeof(ChildWatchSource));
577 cws = (ChildWatchSource *)src;
581 cws->timeout = CWS_BASE_TIMEOUT;
586 /* In more recent versions of glib, we just use the built-in glib source */
588 new_child_watch_source(pid_t pid)
590 return g_child_watch_source_new(pid);