|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
|
|
Packit Service |
392537 |
* Copyright (c) 1999 University of Maryland at College Park
|
|
Packit Service |
392537 |
* Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
|
|
Packit Service |
392537 |
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
|
|
Packit Service |
392537 |
* All Rights Reserved.
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* Permission to use, copy, modify, distribute, and sell this software and its
|
|
Packit Service |
392537 |
* documentation for any purpose is hereby granted without fee, provided that
|
|
Packit Service |
392537 |
* the above copyright notice appear in all copies and that both that
|
|
Packit Service |
392537 |
* copyright notice and this permission notice appear in supporting
|
|
Packit Service |
392537 |
* documentation, and that the name of U.M. not be used in advertising or
|
|
Packit Service |
392537 |
* publicity pertaining to distribution of the software without specific,
|
|
Packit Service |
392537 |
* written prior permission. U.M. makes no representations about the
|
|
Packit Service |
392537 |
* suitability of this software for any purpose. It is provided "as is"
|
|
Packit Service |
392537 |
* without express or implied warranty.
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
|
|
Packit Service |
392537 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
|
|
Packit Service |
392537 |
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
Packit Service |
392537 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
Packit Service |
392537 |
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
Packit Service |
392537 |
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* Authors: the Amanda Development Team. Its members are listed in a
|
|
Packit Service |
392537 |
* file named AUTHORS, in the root directory of this distribution.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* $Id: event.c,v 1.24 2006/06/16 10:55:05 martinea Exp $
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* Event handler. Serializes different kinds of events to allow for
|
|
Packit Service |
392537 |
* a uniform interface, central state storage, and centralized
|
|
Packit Service |
392537 |
* interdependency logic.
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* This is a compatibility wrapper over Glib's GMainLoop. New code should
|
|
Packit Service |
392537 |
* use Glib's interface directly.
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* Each event_handle is associated with a unique GSource, identified by it
|
|
Packit Service |
392537 |
* event_source_id.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
#include "amanda.h"
|
|
Packit Service |
392537 |
#include "conffile.h"
|
|
Packit Service |
392537 |
#include "event.h"
|
|
Packit Service |
392537 |
#include "glib-util.h"
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* TODO: use mem chunks to allocate event_handles */
|
|
Packit Service |
392537 |
/* TODO: lock stuff for threading */
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Write a debugging message if the config variable debug_event
|
|
Packit Service |
392537 |
* is greater than or equal to i */
|
|
Packit Service |
392537 |
#define event_debug(i, ...) do { \
|
|
Packit Service |
392537 |
if ((i) <= debug_event) { \
|
|
Packit Service |
392537 |
dbprintf(__VA_ARGS__); \
|
|
Packit Service |
392537 |
} \
|
|
Packit Service |
392537 |
} while (0)
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* The opaque handle passed back to the caller. This is typedefed to
|
|
Packit Service |
392537 |
* event_handle_t in our header file.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
struct event_handle {
|
|
Packit Service |
392537 |
event_fn_t fn; /* function to call when this fires */
|
|
Packit Service |
392537 |
void *arg; /* argument to pass to previous function */
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
event_type_t type; /* type of event */
|
|
Packit Service |
392537 |
event_id_t data; /* type data */
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
GSource *source; /* Glib event source, if one exists */
|
|
Packit Service |
392537 |
guint source_id; /* ID of the glib event source */
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
gboolean has_fired; /* for use by event_wait() */
|
|
Packit Service |
392537 |
gboolean is_dead; /* should this event be deleted? */
|
|
Packit Service |
392537 |
};
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* A list of all extant event_handle objects, used for searching for particular
|
|
Packit Service |
392537 |
* events and for deleting dead events */
|
|
Packit Service |
392537 |
GSList *all_events = NULL;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
#if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31))
|
|
Packit Service |
392537 |
# pragma GCC diagnostic push
|
|
Packit Service |
392537 |
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
|
Packit Service |
392537 |
#endif
|
|
Packit Service |
392537 |
GStaticMutex event_mutex = G_STATIC_MUTEX_INIT;
|
|
Packit Service |
392537 |
#if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31))
|
|
Packit Service |
392537 |
# pragma GCC diagnostic pop
|
|
Packit Service |
392537 |
#endif
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* should event_loop_run stop? */
|
|
Packit Service |
392537 |
gboolean stop = FALSE;
|
|
Packit Service |
392537 |
gboolean global_return_when_empty = TRUE;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* Utility functions
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
static const char *event_type2str(event_type_t type);
|
|
Packit Service |
392537 |
static gboolean any_mainloop_events(void);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* "Fire" an event handle, by calling its callback function */
|
|
Packit Service |
392537 |
#define fire(eh) do { \
|
|
Packit Service |
392537 |
event_debug(1, "firing %p: %s/%jd\n", eh, event_type2str((eh)->type), (eh)->data); \
|
|
Packit Service |
392537 |
if (*(eh)->fn) { (*(eh)->fn)((eh)->arg); } \
|
|
Packit Service |
392537 |
(eh)->has_fired = TRUE; \
|
|
Packit Service |
392537 |
} while(0)
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Adapt a Glib callback to an event_handle_t callback; assumes that the
|
|
Packit Service |
392537 |
* user_ptr for the Glib callback is a pointer to the event_handle_t. */
|
|
Packit Service |
392537 |
static gboolean
|
|
Packit Service |
392537 |
event_handle_callback(
|
|
Packit Service |
392537 |
gpointer user_ptr)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
event_handle_t *hdl = (event_handle_t *)user_ptr;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* if the handle is dead, then don't fire the callback; this means that
|
|
Packit Service |
392537 |
* we're in the process of freeing the event */
|
|
Packit Service |
392537 |
if (!hdl->is_dead) {
|
|
Packit Service |
392537 |
fire(hdl);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* don't ever let GMainLoop destroy GSources */
|
|
Packit Service |
392537 |
return TRUE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* Public functions
|
|
Packit Service |
392537 |
* DEPRECATED because not safe in multi-thread, callback can be called before event_register return
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
event_handle_t *
|
|
Packit Service |
392537 |
event_register(
|
|
Packit Service |
392537 |
event_id_t data,
|
|
Packit Service |
392537 |
event_type_t type,
|
|
Packit Service |
392537 |
event_fn_t fn,
|
|
Packit Service |
392537 |
void *arg)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
event_handle_t *handle;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
handle = event_create(data, type, fn, arg);
|
|
Packit Service |
392537 |
event_activate(handle);
|
|
Packit Service |
392537 |
return handle;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
event_handle_t *
|
|
Packit Service |
392537 |
event_create(
|
|
Packit Service |
392537 |
event_id_t data,
|
|
Packit Service |
392537 |
event_type_t type,
|
|
Packit Service |
392537 |
event_fn_t fn,
|
|
Packit Service |
392537 |
void *arg)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
event_handle_t *handle;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
g_static_mutex_lock(&event_mutex);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* sanity-checking */
|
|
Packit Service |
392537 |
if ((type == EV_READFD) || (type == EV_WRITEFD)) {
|
|
Packit Service |
392537 |
/* make sure we aren't given a high fd that will overflow a fd_set */
|
|
Packit Service |
392537 |
if (data >= (int)FD_SETSIZE) {
|
|
Packit Service |
392537 |
error(_("event_create: Invalid file descriptor %jd"), data);
|
|
Packit Service |
392537 |
/*NOTREACHED*/
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
} else if (type == EV_TIME) {
|
|
Packit Service |
392537 |
if (data < 0) {
|
|
Packit Service |
392537 |
error(_("event_create: interval for EV_TIME must be greater than 0; got %jd"), data);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
handle = g_new0(event_handle_t, 1);
|
|
Packit Service |
392537 |
handle->fn = fn;
|
|
Packit Service |
392537 |
handle->arg = arg;
|
|
Packit Service |
392537 |
handle->type = type;
|
|
Packit Service |
392537 |
handle->data = data;
|
|
Packit Service |
392537 |
handle->is_dead = FALSE;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
event_debug(1, _("event: register: %p->data=%jd, type=%s\n"),
|
|
Packit Service |
392537 |
handle, handle->data, event_type2str(handle->type));
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
g_static_mutex_unlock(&event_mutex);
|
|
Packit Service |
392537 |
return handle;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
void
|
|
Packit Service |
392537 |
event_activate(
|
|
Packit Service |
392537 |
event_handle_t *handle)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
GIOCondition cond;
|
|
Packit Service |
392537 |
assert(handle != NULL);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
g_static_mutex_lock(&event_mutex);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* add to the list of events */
|
|
Packit Service |
392537 |
all_events = g_slist_prepend(all_events, (gpointer)handle);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* and set up the GSource for this event */
|
|
Packit Service |
392537 |
switch (handle->type) {
|
|
Packit Service |
392537 |
case EV_READFD:
|
|
Packit Service |
392537 |
case EV_WRITEFD:
|
|
Packit Service |
392537 |
/* create a new source */
|
|
Packit Service |
392537 |
if (handle->type == EV_READFD) {
|
|
Packit Service |
392537 |
cond = G_IO_IN | G_IO_HUP | G_IO_ERR;
|
|
Packit Service |
392537 |
} else {
|
|
Packit Service |
392537 |
cond = G_IO_OUT | G_IO_ERR;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
handle->source = new_fdsource(handle->data, cond);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* attach it to the default GMainLoop */
|
|
Packit Service |
392537 |
g_source_attach(handle->source, NULL);
|
|
Packit Service |
392537 |
handle->source_id = g_source_get_id(handle->source);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* And set its callbacks */
|
|
Packit Service |
392537 |
g_source_set_callback(handle->source, event_handle_callback,
|
|
Packit Service |
392537 |
(gpointer)handle, NULL);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* drop our reference to it, so when it's detached, it will be
|
|
Packit Service |
392537 |
* destroyed. */
|
|
Packit Service |
392537 |
g_source_unref(handle->source);
|
|
Packit Service |
392537 |
break;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
case EV_TIME:
|
|
Packit Service |
392537 |
/* Glib provides a nice shortcut for timeouts. The *1000 converts
|
|
Packit Service |
392537 |
* seconds to milliseconds. */
|
|
Packit Service |
392537 |
handle->source_id = g_timeout_add(handle->data * 1000, event_handle_callback,
|
|
Packit Service |
392537 |
(gpointer)handle);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* But it doesn't give us the source directly.. */
|
|
Packit Service |
392537 |
handle->source = g_main_context_find_source_by_id(NULL, handle->source_id);
|
|
Packit Service |
392537 |
/* EV_TIME must always be handled after EV_READ */
|
|
Packit Service |
392537 |
g_source_set_priority(handle->source, 10);
|
|
Packit Service |
392537 |
break;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
case EV_WAIT:
|
|
Packit Service |
392537 |
/* nothing to do -- these are handled independently of GMainLoop */
|
|
Packit Service |
392537 |
break;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
default:
|
|
Packit Service |
392537 |
error(_("Unknown event type %s"), event_type2str(handle->type));
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
g_static_mutex_unlock(&event_mutex);
|
|
Packit Service |
392537 |
return;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* Mark an event to be released. Because we may be traversing the queue
|
|
Packit Service |
392537 |
* when this is called, we must wait until later to actually remove
|
|
Packit Service |
392537 |
* the event.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
void
|
|
Packit Service |
392537 |
event_release(
|
|
Packit Service |
392537 |
event_handle_t *handle)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
assert(handle != NULL);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
g_static_mutex_lock(&event_mutex);
|
|
Packit Service |
392537 |
event_debug(1, _("event: release (mark): %p data=%jd, type=%s\n"),
|
|
Packit Service |
392537 |
handle, handle->data,
|
|
Packit Service |
392537 |
event_type2str(handle->type));
|
|
Packit Service |
392537 |
assert(!handle->is_dead);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Mark it as dead and leave it for the event_loop to remove */
|
|
Packit Service |
392537 |
handle->is_dead = TRUE;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
if (global_return_when_empty && !any_mainloop_events()) {
|
|
Packit Service |
392537 |
g_main_loop_quit(default_main_loop());
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
g_static_mutex_unlock(&event_mutex);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* Fire all EV_WAIT events waiting on the specified id.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
int
|
|
Packit Service |
392537 |
event_wakeup(
|
|
Packit Service |
392537 |
event_id_t id)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
GSList *iter;
|
|
Packit Service |
392537 |
GSList *tofire = NULL;
|
|
Packit Service |
392537 |
int nwaken = 0;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
g_static_mutex_lock(&event_mutex);
|
|
Packit Service |
392537 |
event_debug(1, _("event: wakeup: enter (%jd)\n"), id);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* search for any and all matching events, and record them. This way
|
|
Packit Service |
392537 |
* we have determined the whole list of events we'll be firing *before*
|
|
Packit Service |
392537 |
* we fire any of them. */
|
|
Packit Service |
392537 |
for (iter = all_events; iter != NULL; iter = g_slist_next(iter)) {
|
|
Packit Service |
392537 |
event_handle_t *eh = (event_handle_t *)iter->data;
|
|
Packit Service |
392537 |
if (eh->type == EV_WAIT && eh->data == id && !eh->is_dead) {
|
|
Packit Service |
392537 |
tofire = g_slist_append(tofire, (gpointer)eh);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* fire them */
|
|
Packit Service |
392537 |
for (iter = tofire; iter != NULL; iter = g_slist_next(iter)) {
|
|
Packit Service |
392537 |
event_handle_t *eh = (event_handle_t *)iter->data;
|
|
Packit Service |
392537 |
if (eh->type == EV_WAIT && eh->data == id && !eh->is_dead) {
|
|
Packit Service |
392537 |
event_debug(1, _("A: event: wakeup triggering: %p id=%jd\n"), eh, id);
|
|
Packit Service |
392537 |
/* The lcok must be release before running the event */
|
|
Packit Service |
392537 |
g_static_mutex_unlock(&event_mutex);
|
|
Packit Service |
392537 |
fire(eh);
|
|
Packit Service |
392537 |
g_static_mutex_lock(&event_mutex);
|
|
Packit Service |
392537 |
nwaken++;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* and free the temporary list */
|
|
Packit Service |
392537 |
g_slist_free(tofire);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
g_static_mutex_unlock(&event_mutex);
|
|
Packit Service |
392537 |
return (nwaken);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* The event loop.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
static void event_loop_wait (event_handle_t *, const int, gboolean return_when_empty);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
void
|
|
Packit Service |
392537 |
event_loop(
|
|
Packit Service |
392537 |
int nonblock)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
event_loop_wait(NULL, nonblock, TRUE);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
void
|
|
Packit Service |
392537 |
event_loop_run(
|
|
Packit Service |
392537 |
void)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
stop = FALSE;
|
|
Packit Service |
392537 |
event_loop_wait(NULL, 0, FALSE);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
void
|
|
Packit Service |
392537 |
event_loop_quit(
|
|
Packit Service |
392537 |
void)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
stop = TRUE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
void
|
|
Packit Service |
392537 |
event_wait(
|
|
Packit Service |
392537 |
event_handle_t *eh)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
event_loop_wait(eh, 0, TRUE);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Flush out any dead events in all_events. Be careful that this
|
|
Packit Service |
392537 |
* isn't called while someone is iterating over all_events.
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* @param wait_eh: the event handle we're waiting on, which shouldn't
|
|
Packit Service |
392537 |
* be flushed.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
static void
|
|
Packit Service |
392537 |
flush_dead_events(event_handle_t *wait_eh)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
GSList *iter, *next;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
for (iter = all_events; iter != NULL; iter = next) {
|
|
Packit Service |
392537 |
event_handle_t *hdl = (event_handle_t *)iter->data;
|
|
Packit Service |
392537 |
next = g_slist_next(iter);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* (handle the case when wait_eh is dead by simply not deleting
|
|
Packit Service |
392537 |
* it; the next run of event_loop will take care of it) */
|
|
Packit Service |
392537 |
if (hdl->is_dead && hdl != wait_eh) {
|
|
Packit Service |
392537 |
all_events = g_slist_delete_link(all_events, iter);
|
|
Packit Service |
392537 |
if (hdl->source) g_source_destroy(hdl->source);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
amfree(hdl);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Return TRUE if we have any events outstanding that can be dispatched
|
|
Packit Service |
392537 |
* by GMainLoop. Recall EV_WAIT events appear in all_events, but are
|
|
Packit Service |
392537 |
* not dispatched by GMainLoop. */
|
|
Packit Service |
392537 |
static gboolean
|
|
Packit Service |
392537 |
any_mainloop_events(void)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
GSList *iter;
|
|
Packit Service |
392537 |
gboolean ret = FALSE;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
for (iter = all_events; iter != NULL; iter = g_slist_next(iter)) {
|
|
Packit Service |
392537 |
event_handle_t *hdl = (event_handle_t *)iter->data;
|
|
Packit Service |
392537 |
event_debug(2, _("list %p: %s %s/%jd\n"), hdl, hdl->is_dead?"dead":"alive", event_type2str((hdl)->type), (hdl)->data);
|
|
Packit Service |
392537 |
if (hdl->type != EV_WAIT && !hdl->is_dead)
|
|
Packit Service |
392537 |
ret = TRUE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
return ret;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
static void
|
|
Packit Service |
392537 |
event_loop_wait(
|
|
Packit Service |
392537 |
event_handle_t *wait_eh,
|
|
Packit Service |
392537 |
int nonblock,
|
|
Packit Service |
392537 |
gboolean return_when_empty)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
global_return_when_empty = return_when_empty;
|
|
Packit Service |
392537 |
g_static_mutex_lock(&event_mutex);
|
|
Packit Service |
392537 |
event_debug(1, _("event: loop: enter: nonblockg=%d, eh=%p\n"), nonblock, wait_eh);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* If we're waiting for a specific event, then reset its has_fired flag */
|
|
Packit Service |
392537 |
if (wait_eh) {
|
|
Packit Service |
392537 |
wait_eh->has_fired = FALSE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Keep looping until there are no events, or until wait_eh has fired */
|
|
Packit Service |
392537 |
while (1) {
|
|
Packit Service |
392537 |
/* clean up first, so we don't accidentally check a dead source */
|
|
Packit Service |
392537 |
flush_dead_events(wait_eh);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* if there's nothing to wait for, then don't block, but run an
|
|
Packit Service |
392537 |
* iteration so that any other users of GMainLoop will get a chance
|
|
Packit Service |
392537 |
* to run. */
|
|
Packit Service |
392537 |
if (return_when_empty && !any_mainloop_events())
|
|
Packit Service |
392537 |
break;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Do an iteration */
|
|
Packit Service |
392537 |
/* Relese the lock before running an iteration */
|
|
Packit Service |
392537 |
g_static_mutex_unlock(&event_mutex);
|
|
Packit Service |
392537 |
g_main_context_iteration(NULL, !nonblock);
|
|
Packit Service |
392537 |
g_static_mutex_lock(&event_mutex);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* stop if we're told to */
|
|
Packit Service |
392537 |
if (!return_when_empty && stop)
|
|
Packit Service |
392537 |
break;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* If the event we've been waiting for has fired or been released, as
|
|
Packit Service |
392537 |
* appropriate, we're done. See the comments for event_wait in event.h
|
|
Packit Service |
392537 |
* for the skinny on this weird expression. */
|
|
Packit Service |
392537 |
if (wait_eh && ((wait_eh->type == EV_WAIT && wait_eh->is_dead)
|
|
Packit Service |
392537 |
|| (wait_eh->type != EV_WAIT && wait_eh->has_fired)))
|
|
Packit Service |
392537 |
break;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Don't loop if we're not blocking */
|
|
Packit Service |
392537 |
if (nonblock)
|
|
Packit Service |
392537 |
break;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* extra cleanup, to keep all_events short, and to delete wait_eh if it
|
|
Packit Service |
392537 |
* has been released. */
|
|
Packit Service |
392537 |
flush_dead_events(NULL);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
g_static_mutex_unlock(&event_mutex);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
GMainLoop *
|
|
Packit Service |
392537 |
default_main_loop(void)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
static GMainLoop *loop = NULL;
|
|
Packit Service |
392537 |
if (!loop)
|
|
Packit Service |
392537 |
loop = g_main_loop_new(NULL, TRUE);
|
|
Packit Service |
392537 |
return loop;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* Convert an event type into a string
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
static const char *
|
|
Packit Service |
392537 |
event_type2str(
|
|
Packit Service |
392537 |
event_type_t type)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
static const struct {
|
|
Packit Service |
392537 |
event_type_t type;
|
|
Packit Service |
392537 |
const char name[12];
|
|
Packit Service |
392537 |
} event_types[] = {
|
|
Packit Service |
392537 |
#define X(s) { s, stringize(s) }
|
|
Packit Service |
392537 |
X(EV_READFD),
|
|
Packit Service |
392537 |
X(EV_WRITEFD),
|
|
Packit Service |
392537 |
X(EV_TIME),
|
|
Packit Service |
392537 |
X(EV_WAIT),
|
|
Packit Service |
392537 |
#undef X
|
|
Packit Service |
392537 |
};
|
|
Packit Service |
392537 |
size_t i;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
for (i = 0; i < G_N_ELEMENTS(event_types); i++)
|
|
Packit Service |
392537 |
if (type == event_types[i].type)
|
|
Packit Service |
392537 |
return (event_types[i].name);
|
|
Packit Service |
392537 |
return (_("BOGUS EVENT TYPE"));
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* FDSource -- a source for a file descriptor
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* We could use Glib's GIOChannel for this, but it adds some buffering
|
|
Packit Service |
392537 |
* and Unicode functionality that we really don't want. The custom GSource
|
|
Packit Service |
392537 |
* is simple enough anyway, and the Glib documentation describes it in prose.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
typedef struct FDSource {
|
|
Packit Service |
392537 |
GSource source; /* must be the first element in the struct */
|
|
Packit Service |
392537 |
GPollFD pollfd; /* Our file descriptor */
|
|
Packit Service |
392537 |
} FDSource;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
static gboolean
|
|
Packit Service |
392537 |
fdsource_prepare(
|
|
Packit Service |
392537 |
GSource *source G_GNUC_UNUSED,
|
|
Packit Service |
392537 |
gint *timeout_)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
*timeout_ = -1; /* block forever, as far as we're concerned */
|
|
Packit Service |
392537 |
return FALSE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
static gboolean
|
|
Packit Service |
392537 |
fdsource_check(
|
|
Packit Service |
392537 |
GSource *source)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
FDSource *fds = (FDSource *)source;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* we need to be dispatched if any interesting events have been received by the FD */
|
|
Packit Service |
392537 |
return fds->pollfd.events & fds->pollfd.revents;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
static gboolean
|
|
Packit Service |
392537 |
fdsource_dispatch(
|
|
Packit Service |
392537 |
GSource *source G_GNUC_UNUSED,
|
|
Packit Service |
392537 |
GSourceFunc callback,
|
|
Packit Service |
392537 |
gpointer user_data)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
if (callback)
|
|
Packit Service |
392537 |
return callback(user_data);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Don't automatically detach the event source if there's no callback. */
|
|
Packit Service |
392537 |
return TRUE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
GSource *
|
|
Packit Service |
392537 |
new_fdsource(gint fd, GIOCondition events)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
static GSourceFuncs *fdsource_funcs = NULL;
|
|
Packit Service |
392537 |
GSource *src;
|
|
Packit Service |
392537 |
FDSource *fds;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* initialize these here to avoid a compiler warning */
|
|
Packit Service |
392537 |
if (!fdsource_funcs) {
|
|
Packit Service |
392537 |
fdsource_funcs = g_new0(GSourceFuncs, 1);
|
|
Packit Service |
392537 |
fdsource_funcs->prepare = fdsource_prepare;
|
|
Packit Service |
392537 |
fdsource_funcs->check = fdsource_check;
|
|
Packit Service |
392537 |
fdsource_funcs->dispatch = fdsource_dispatch;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
src = g_source_new(fdsource_funcs, sizeof(FDSource));
|
|
Packit Service |
392537 |
fds = (FDSource *)src;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
fds->pollfd.fd = fd;
|
|
Packit Service |
392537 |
fds->pollfd.events = events;
|
|
Packit Service |
392537 |
g_source_add_poll(src, &fds->pollfd);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
return src;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/*
|
|
Packit Service |
392537 |
* ChildWatchSource -- a source for a file descriptor
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* Newer versions of glib provide equivalent functionality; consider
|
|
Packit Service |
392537 |
* optionally using that, protected by a GLIB_CHECK_VERSION condition.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Versions before glib-2.4.0 didn't include a child watch source, and versions
|
|
Packit Service |
392537 |
* before 2.6.0 used unreliable signals. On these versions, we implement
|
|
Packit Service |
392537 |
* a "dumb" version of our own invention. This is dumb in the sense that it
|
|
Packit Service |
392537 |
* doesn't use SIGCHLD to detect a dead child, preferring to just poll at
|
|
Packit Service |
392537 |
* exponentially increasing interals. Writing a smarter implementation runs into
|
|
Packit Service |
392537 |
* some tricky race conditions and extra machinery. Since there are few, if any,
|
|
Packit Service |
392537 |
* users of a glib version this old, such machinery wouldn't get much testing.
|
|
Packit Service |
392537 |
*
|
|
Packit Service |
392537 |
* FreeBSD users have also reported problems with the glib child watch source,
|
|
Packit Service |
392537 |
* so we use the dumb version on FreeBSD, too.
|
|
Packit Service |
392537 |
*/
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
#if (defined(__FreeBSD__) || GLIB_MAJOR_VERSION < 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 6))
|
|
Packit Service |
392537 |
typedef struct ChildWatchSource {
|
|
Packit Service |
392537 |
GSource source; /* must be the first element in the struct */
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
pid_t pid;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
gint dead;
|
|
Packit Service |
392537 |
gint status;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
gint timeout;
|
|
Packit Service |
392537 |
} ChildWatchSource;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* this corresponds to rapid checks for about 10 seconds, after which the
|
|
Packit Service |
392537 |
* waitpid() check occurs every 2 seconds. */
|
|
Packit Service |
392537 |
#define CWS_BASE_TIMEOUT 20
|
|
Packit Service |
392537 |
#define CWS_MULT_TIMEOUT 1.1
|
|
Packit Service |
392537 |
#define CWS_MAX_TIMEOUT 2000
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
static gboolean
|
|
Packit Service |
392537 |
child_watch_source_prepare(
|
|
Packit Service |
392537 |
GSource *source,
|
|
Packit Service |
392537 |
gint *timeout_)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
ChildWatchSource *cws = (ChildWatchSource *)source;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
*timeout_ = cws->timeout;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
cws->timeout *= CWS_MULT_TIMEOUT;
|
|
Packit Service |
392537 |
if (cws->timeout > CWS_MAX_TIMEOUT) cws->timeout = CWS_MAX_TIMEOUT;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
return FALSE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
static gboolean
|
|
Packit Service |
392537 |
child_watch_source_check(
|
|
Packit Service |
392537 |
GSource *source)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
ChildWatchSource *cws = (ChildWatchSource *)source;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* is it dead? */
|
|
Packit Service |
392537 |
if (!cws->dead && waitpid(cws->pid, &cws->status, WNOHANG) > 0) {
|
|
Packit Service |
392537 |
cws->dead = TRUE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
return cws->dead;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
static gboolean
|
|
Packit Service |
392537 |
child_watch_source_dispatch(
|
|
Packit Service |
392537 |
GSource *source G_GNUC_UNUSED,
|
|
Packit Service |
392537 |
GSourceFunc callback,
|
|
Packit Service |
392537 |
gpointer user_data)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
ChildWatchSource *cws = (ChildWatchSource *)source;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* this shouldn't happen, but just in case */
|
|
Packit Service |
392537 |
if (cws->dead) {
|
|
Packit Service |
392537 |
if (!callback) {
|
|
Packit Service |
392537 |
g_warning("child %jd died before callback was registered", (uintmax_t)cws->pid);
|
|
Packit Service |
392537 |
return FALSE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
((ChildWatchFunc)callback)(cws->pid, cws->status, user_data);
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* Un-queue this source unconditionally -- the child can't die twice */
|
|
Packit Service |
392537 |
return FALSE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
return TRUE;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
GSource *
|
|
Packit Service |
392537 |
new_child_watch_source(pid_t pid)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
static GSourceFuncs *child_watch_source_funcs = NULL;
|
|
Packit Service |
392537 |
GSource *src;
|
|
Packit Service |
392537 |
ChildWatchSource *cws;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
/* initialize these here to avoid a compiler warning */
|
|
Packit Service |
392537 |
if (!child_watch_source_funcs) {
|
|
Packit Service |
392537 |
child_watch_source_funcs = g_new0(GSourceFuncs, 1);
|
|
Packit Service |
392537 |
child_watch_source_funcs->prepare = child_watch_source_prepare;
|
|
Packit Service |
392537 |
child_watch_source_funcs->check = child_watch_source_check;
|
|
Packit Service |
392537 |
child_watch_source_funcs->dispatch = child_watch_source_dispatch;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
src = g_source_new(child_watch_source_funcs, sizeof(ChildWatchSource));
|
|
Packit Service |
392537 |
cws = (ChildWatchSource *)src;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
cws->pid = pid;
|
|
Packit Service |
392537 |
cws->dead = FALSE;
|
|
Packit Service |
392537 |
cws->timeout = CWS_BASE_TIMEOUT;
|
|
Packit Service |
392537 |
|
|
Packit Service |
392537 |
return src;
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
#else
|
|
Packit Service |
392537 |
/* In more recent versions of glib, we just use the built-in glib source */
|
|
Packit Service |
392537 |
GSource *
|
|
Packit Service |
392537 |
new_child_watch_source(pid_t pid)
|
|
Packit Service |
392537 |
{
|
|
Packit Service |
392537 |
return g_child_watch_source_new(pid);
|
|
Packit Service |
392537 |
}
|
|
Packit Service |
392537 |
#endif
|