mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-27 06:26:15 +01:00
gmain: Prepare child watch handling for more generic signal handling
In preparation for supporting more Unix signals such as SIGHUP, SIGTERM etc., https://bugzilla.gnome.org/show_bug.cgi?id=644941
This commit is contained in:
parent
0ff211f520
commit
920899d78f
@ -1,7 +1,7 @@
|
|||||||
/* GLIB - Library of useful routines for C programming
|
/* GLIB - Library of useful routines for C programming
|
||||||
* Copyright (C) 2011 Red Hat, Inc.
|
* Copyright (C) 2011 Red Hat, Inc.
|
||||||
*
|
*
|
||||||
* glib-unix.c: Unix specific API wrappers and convenience functions
|
* glib-unix.c: UNIX specific API wrappers and convenience functions
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:gunix
|
* SECTION:gunix
|
||||||
* @short_description: Unix-specific utilities and integration
|
* @short_description: UNIX-specific utilities and integration
|
||||||
* @include: glib-unix.h
|
* @include: glib-unix.h
|
||||||
*
|
*
|
||||||
* Most of GLib is intended to be portable; in constrast, this set of
|
* Most of GLib is intended to be portable; in constrast, this set of
|
||||||
@ -77,15 +77,14 @@ g_unix_set_error_from_errno_saved (GError **error,
|
|||||||
* @flags: Bitfield of file descriptor flags, see "man 2 fcntl"
|
* @flags: Bitfield of file descriptor flags, see "man 2 fcntl"
|
||||||
* @error: a #GError
|
* @error: a #GError
|
||||||
*
|
*
|
||||||
* Similar to the Unix pipe() call, but on modern systems like
|
* Similar to the UNIX pipe() call, but on modern systems like Linux
|
||||||
* Linux uses the pipe2 system call, which atomically creates
|
* uses the pipe2 system call, which atomically creates a pipe with
|
||||||
* a pipe with the configured flags. The only supported flag
|
* the configured flags. The only supported flag currently is
|
||||||
* currently is FD_CLOEXEC. If for example you want to configure
|
* FD_CLOEXEC. If for example you want to configure O_NONBLOCK, that
|
||||||
* O_NONBLOCK, that must still be done separately with fcntl().
|
* must still be done separately with fcntl().
|
||||||
*
|
*
|
||||||
* Note in particular this function does *not* take O_CLOEXEC, it
|
* <note>This function does *not* take O_CLOEXEC, it takes FD_CLOEXEC as if
|
||||||
* takes FD_CLOEXEC as if for fcntl(); these are different on
|
* for fcntl(); these are different on Linux/glibc.</note>
|
||||||
* Linux/glibc.
|
|
||||||
*
|
*
|
||||||
* Returns: %TRUE on success, %FALSE if not (and errno will be set).
|
* Returns: %TRUE on success, %FALSE if not (and errno will be set).
|
||||||
*
|
*
|
||||||
|
142
glib/gmain.c
142
glib/gmain.c
@ -77,6 +77,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif /* HAVE_UNISTD_H */
|
#endif /* HAVE_UNISTD_H */
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
#define STRICT
|
#define STRICT
|
||||||
@ -364,6 +365,8 @@ static void g_main_context_remove_poll_unlocked (GMainContext *context,
|
|||||||
GPollFD *fd);
|
GPollFD *fd);
|
||||||
static void g_main_context_wakeup_unlocked (GMainContext *context);
|
static void g_main_context_wakeup_unlocked (GMainContext *context);
|
||||||
|
|
||||||
|
static void _g_main_wake_up_all_contexts (void);
|
||||||
|
|
||||||
static gboolean g_timeout_prepare (GSource *source,
|
static gboolean g_timeout_prepare (GSource *source,
|
||||||
gint *timeout);
|
gint *timeout);
|
||||||
static gboolean g_timeout_check (GSource *source);
|
static gboolean g_timeout_check (GSource *source);
|
||||||
@ -388,6 +391,15 @@ static GMainContext *default_main_context;
|
|||||||
static GSList *main_contexts_without_pipe = NULL;
|
static GSList *main_contexts_without_pipe = NULL;
|
||||||
|
|
||||||
#ifndef G_OS_WIN32
|
#ifndef G_OS_WIN32
|
||||||
|
|
||||||
|
/* The UNIX signal pipe contains a single byte specifying which
|
||||||
|
* signal was received.
|
||||||
|
*/
|
||||||
|
#define _UNIX_SIGNAL_PIPE_SIGCHLD_CHAR 'C'
|
||||||
|
/* Guards unix_signal_wake_up_pipe */
|
||||||
|
G_LOCK_DEFINE_STATIC (unix_signal_lock);
|
||||||
|
static gint unix_signal_wake_up_pipe[2] = {-1, -1};
|
||||||
|
|
||||||
/* Child status monitoring code */
|
/* Child status monitoring code */
|
||||||
enum {
|
enum {
|
||||||
CHILD_WATCH_UNINITIALIZED,
|
CHILD_WATCH_UNINITIALIZED,
|
||||||
@ -396,7 +408,6 @@ enum {
|
|||||||
};
|
};
|
||||||
static gint child_watch_init_state = CHILD_WATCH_UNINITIALIZED;
|
static gint child_watch_init_state = CHILD_WATCH_UNINITIALIZED;
|
||||||
static gint child_watch_count = 1;
|
static gint child_watch_count = 1;
|
||||||
static gint child_watch_wake_up_pipe[2] = {0, 0};
|
|
||||||
#endif /* !G_OS_WIN32 */
|
#endif /* !G_OS_WIN32 */
|
||||||
G_LOCK_DEFINE_STATIC (main_context_list);
|
G_LOCK_DEFINE_STATIC (main_context_list);
|
||||||
static GSList *main_context_list = NULL;
|
static GSList *main_context_list = NULL;
|
||||||
@ -3717,6 +3728,30 @@ g_main_context_get_poll_func (GMainContext *context)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_g_main_wake_up_all_contexts (void)
|
||||||
|
{
|
||||||
|
GSList *list;
|
||||||
|
|
||||||
|
/* We were woken up. Wake up all other contexts in all other threads */
|
||||||
|
G_LOCK (main_context_list);
|
||||||
|
for (list = main_context_list; list; list = list->next)
|
||||||
|
{
|
||||||
|
GMainContext *context;
|
||||||
|
|
||||||
|
context = list->data;
|
||||||
|
if (g_atomic_int_get (&context->ref_count) > 0)
|
||||||
|
/* Due to racing conditions we can find ref_count == 0, in
|
||||||
|
* that case, however, the context is still not destroyed
|
||||||
|
* and no poll can be active, otherwise the ref_count
|
||||||
|
* wouldn't be 0
|
||||||
|
*/
|
||||||
|
g_main_context_wakeup (context);
|
||||||
|
}
|
||||||
|
G_UNLOCK (main_context_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* HOLDS: context's lock */
|
/* HOLDS: context's lock */
|
||||||
/* Wake the main loop up from a poll() */
|
/* Wake the main loop up from a poll() */
|
||||||
static void
|
static void
|
||||||
@ -4263,7 +4298,8 @@ g_child_watch_signal_handler (int signum)
|
|||||||
|
|
||||||
if (child_watch_init_state == CHILD_WATCH_INITIALIZED_THREADED)
|
if (child_watch_init_state == CHILD_WATCH_INITIALIZED_THREADED)
|
||||||
{
|
{
|
||||||
write (child_watch_wake_up_pipe[1], "B", 1);
|
char buf[1] = { _UNIX_SIGNAL_PIPE_SIGCHLD_CHAR };
|
||||||
|
write (unix_signal_wake_up_pipe[1], buf, 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -4288,52 +4324,92 @@ g_child_watch_source_init_single (void)
|
|||||||
sigaction (SIGCHLD, &action, NULL);
|
sigaction (SIGCHLD, &action, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
G_GNUC_NORETURN static gpointer
|
static gpointer unix_signal_helper_thread (gpointer data) G_GNUC_NORETURN;
|
||||||
child_watch_helper_thread (gpointer data)
|
|
||||||
|
/*
|
||||||
|
* This thread is created whenever anything in GLib needs
|
||||||
|
* to deal with UNIX signals; at present, just SIGCHLD
|
||||||
|
* from g_child_watch_source_new().
|
||||||
|
*
|
||||||
|
* Note: We could eventually make this thread a more public interface
|
||||||
|
* and allow e.g. GDBus to use it instead of its own worker thread.
|
||||||
|
*/
|
||||||
|
static gpointer
|
||||||
|
unix_signal_helper_thread (gpointer data)
|
||||||
{
|
{
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
gchar b[20];
|
gchar b[128];
|
||||||
GSList *list;
|
ssize_t i, bytes_read;
|
||||||
|
|
||||||
read (child_watch_wake_up_pipe[0], b, 20);
|
bytes_read = read (unix_signal_wake_up_pipe[0], b, sizeof (b));
|
||||||
|
if (bytes_read < 0)
|
||||||
/* We were woken up. Wake up all other contexts in all other threads */
|
|
||||||
G_LOCK (main_context_list);
|
|
||||||
for (list = main_context_list; list; list = list->next)
|
|
||||||
{
|
{
|
||||||
GMainContext *context;
|
g_warning ("Failed to read from child watch wake up pipe: %s",
|
||||||
|
strerror (errno));
|
||||||
context = list->data;
|
/* Not much we can do here sanely; just wait a second and hope
|
||||||
if (g_atomic_int_get (&context->ref_count) > 0)
|
* it was transient.
|
||||||
/* Due to racing conditions we can find ref_count == 0, in
|
*/
|
||||||
* that case, however, the context is still not destroyed
|
g_usleep (G_USEC_PER_SEC);
|
||||||
* and no poll can be active, otherwise the ref_count
|
continue;
|
||||||
* wouldn't be 0 */
|
}
|
||||||
g_main_context_wakeup (context);
|
for (i = 0; i < bytes_read; i++)
|
||||||
|
{
|
||||||
|
switch (b[i])
|
||||||
|
{
|
||||||
|
case _UNIX_SIGNAL_PIPE_SIGCHLD_CHAR:
|
||||||
|
/* The child watch source will call waitpid() in its
|
||||||
|
* prepare() and check() methods; however, we don't
|
||||||
|
* know which pid exited, so we need to wake up
|
||||||
|
* all contexts. Note: actually we could get the pid
|
||||||
|
* from the "siginfo_t" via the handler, but to pass
|
||||||
|
* that info down the pipe would require a more structured
|
||||||
|
* data stream (as opposed to a single byte).
|
||||||
|
*/
|
||||||
|
_g_main_wake_up_all_contexts ();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_warning ("Invalid char '%c' read from child watch pipe", b[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
G_UNLOCK (main_context_list);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_g_main_init_unix_signal_wakeup_pipe (void)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
G_LOCK (unix_signal_lock);
|
||||||
|
|
||||||
|
if (unix_signal_wake_up_pipe[0] >= 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
g_assert (g_thread_supported());
|
||||||
|
|
||||||
|
if (!g_unix_pipe_flags (unix_signal_wake_up_pipe, FD_CLOEXEC, &error))
|
||||||
|
g_error ("Cannot create UNIX signal wake up pipe: %s\n", error->message);
|
||||||
|
fcntl (unix_signal_wake_up_pipe[1], F_SETFL, O_NONBLOCK | fcntl (unix_signal_wake_up_pipe[1], F_GETFL));
|
||||||
|
|
||||||
|
/* We create a helper thread that polls on the wakeup pipe indefinitely */
|
||||||
|
/* FIXME: Think this through for races */
|
||||||
|
if (g_thread_create (unix_signal_helper_thread, NULL, FALSE, &error) == NULL)
|
||||||
|
g_error ("Cannot create a thread to monitor UNIX signals: %s\n", error->message);
|
||||||
|
|
||||||
|
out:
|
||||||
|
G_UNLOCK (unix_signal_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
g_child_watch_source_init_multi_threaded (void)
|
g_child_watch_source_init_multi_threaded (void)
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
|
||||||
struct sigaction action;
|
struct sigaction action;
|
||||||
|
|
||||||
|
_g_main_init_unix_signal_wakeup_pipe ();
|
||||||
|
|
||||||
g_assert (g_thread_supported());
|
|
||||||
|
|
||||||
if (!g_unix_pipe_flags (child_watch_wake_up_pipe, FD_CLOEXEC, &error))
|
|
||||||
g_error ("Cannot create wake up pipe: %s\n", error->message);
|
|
||||||
fcntl (child_watch_wake_up_pipe[1], F_SETFL, O_NONBLOCK | fcntl (child_watch_wake_up_pipe[1], F_GETFL));
|
|
||||||
|
|
||||||
/* We create a helper thread that polls on the wakeup pipe indefinitely */
|
|
||||||
/* FIXME: Think this through for races */
|
|
||||||
if (g_thread_create (child_watch_helper_thread, NULL, FALSE, &error) == NULL)
|
|
||||||
g_error ("Cannot create a thread to monitor child exit status: %s\n", error->message);
|
|
||||||
child_watch_init_state = CHILD_WATCH_INITIALIZED_THREADED;
|
child_watch_init_state = CHILD_WATCH_INITIALIZED_THREADED;
|
||||||
|
|
||||||
action.sa_handler = g_child_watch_signal_handler;
|
action.sa_handler = g_child_watch_signal_handler;
|
||||||
sigemptyset (&action.sa_mask);
|
sigemptyset (&action.sa_mask);
|
||||||
action.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
action.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
||||||
|
Loading…
Reference in New Issue
Block a user