[glib/wip/child-catchall: 3/3] GChildWatchSource: Allow passing -1 for pid



commit d3300725d94b79f57c2fc8db90a09b35ade456b7
Author: Colin Walters <walters verbum org>
Date:   Thu Oct 25 02:54:05 2012 -0400

    GChildWatchSource: Allow passing -1 for pid
    
    With the goal of modifying gnome-session to use the new Linux
    PR_SET_CHILD_SUBREAPER, it needs to be able to reap arbitary children
    that may be reparented to it.
    
    But if gnome-session is going to continue to work with GLib and GIO,
    even if we converted all child watches in gnome-session to use this
    new API, we'd still have to contend with the possibility of it having
    a process spawned indirectly (stuff like the GDBus dbus-launch
    invocation).  Thus, we don't give the -1 child watch *every* waitpid,
    only the ones which don't have a specific watcher.
    
    Also, fix up g_spawn_sync() to hold the unix signal lock when forking.
    This way we ensure that GLib itself gets the waitpid() result for the
    synchronously spawned child, instead of racing with the worker thread.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=687061

 glib/gmain-internal.h |    6 +
 glib/gmain.c          |  371 +++++++++++++++++++++++++++++++++++++++++--------
 glib/gspawn.c         |   31 ++++-
 glib/tests/unix.c     |  109 +++++++++++++++
 4 files changed, 457 insertions(+), 60 deletions(-)
---
diff --git a/glib/gmain-internal.h b/glib/gmain-internal.h
index 648aff3..0877718 100644
--- a/glib/gmain-internal.h
+++ b/glib/gmain-internal.h
@@ -30,6 +30,12 @@ G_BEGIN_DECLS
 
 GSource *_g_main_create_unix_signal_watch (int signum);
 
+#ifdef G_OS_UNIX
+void _g_main_unix_signal_lock (void);
+void _g_main_unix_signal_unlock (void);
+GSource * _g_main_child_watch_source_new_unix_unlocked (GPid  pid);
+#endif
+
 G_END_DECLS
 
 #endif /* __G_MAIN_H__ */
diff --git a/glib/gmain.c b/glib/gmain.c
index af1092d..7db6400 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -185,6 +185,7 @@
 typedef struct _GTimeoutSource GTimeoutSource;
 typedef struct _GChildWatchSource GChildWatchSource;
 typedef struct _GUnixSignalWatchSource GUnixSignalWatchSource;
+typedef struct _GUnixCatchallChildWatchSource GUnixCatchallChildWatchSource;
 typedef struct _GPollRec GPollRec;
 typedef struct _GSourceCallback GSourceCallback;
 
@@ -303,6 +304,12 @@ struct _GUnixSignalWatchSource
   gboolean    pending;
 };
 
+struct _GUnixCatchallChildWatchSource
+{
+  GSource     source;
+  GSList     *pending_waits;
+};
+
 struct _GPollRec
 {
   GPollFD *fd;
@@ -395,6 +402,13 @@ static gboolean g_unix_signal_watch_dispatch (GSource     *source,
 					      GSourceFunc  callback,
 					      gpointer     user_data);
 static void     g_unix_signal_watch_finalize  (GSource     *source);
+static gboolean g_unix_catchall_watch_prepare  (GSource     *source,
+					      gint        *timeout);
+static gboolean g_unix_catchall_watch_check    (GSource     *source);
+static gboolean g_unix_catchall_watch_dispatch (GSource     *source,
+					      GSourceFunc  callback,
+					      gpointer     user_data);
+static void     g_unix_catchall_watch_finalize  (GSource     *source);
 #endif
 static gboolean g_idle_prepare     (GSource     *source,
 				    gint        *timeout);
@@ -428,6 +442,12 @@ static volatile int any_unix_signal_pending;
 G_LOCK_DEFINE_STATIC (unix_signal_lock);
 static GSList *unix_signal_watches;
 static GSList *unix_child_watches;
+static GUnixCatchallChildWatchSource *unix_catchall_child_watch;
+
+typedef struct {
+  pid_t pid;
+  gint estatus;
+} UnixWaitStatus;
 
 static GSourceFuncs g_unix_signal_funcs =
 {
@@ -436,6 +456,14 @@ static GSourceFuncs g_unix_signal_funcs =
   g_unix_signal_watch_dispatch,
   g_unix_signal_watch_finalize
 };
+
+static GSourceFuncs g_unix_catchall_funcs =
+{
+  g_unix_catchall_watch_prepare,
+  g_unix_catchall_watch_check,
+  g_unix_catchall_watch_dispatch,
+  g_unix_catchall_watch_finalize
+};
 #endif /* !G_OS_WIN32 */
 G_LOCK_DEFINE_STATIC (main_context_list);
 static GSList *main_context_list = NULL;
@@ -4392,6 +4420,115 @@ wake_source (GSource *source)
   G_UNLOCK(main_context_list);
 }
 
+/* Here the application has explicitly requested use of waitpid(-1,
+ * ...).  The contortions below are to be sure we dispatch normal
+ * GChildWatchSource for their requested pid, only passing other pids
+ * to the catchall source.
+ */
+static void
+dispatch_sigchld_with_catchall (void)
+{
+  gboolean caught_one_catchall = FALSE;
+
+  while (TRUE)
+    {
+      gboolean give_to_catchall = TRUE;
+      GSList *node;
+      int estatus;
+      pid_t pid;
+
+      /* Normal EINTR loop */
+      do
+        pid = waitpid (-1, &estatus, WNOHANG);
+      while (pid == -1 && errno == EINTR);
+      
+      /* We're done if we have no child processes, or on an error.
+       * Ideally we'd log an error, if one somehow occurred, but we
+       * can't get ECHILD, we're handling EINTR, and the only way we'd
+       * get EINVAL is if the system didn't support WNOHANG, in which
+       * case we're screwed anyways.  Those are all of the errnos
+       * listed in the glibc manual.
+       */
+      if (pid <= 0)
+        break;
+
+      for (node = unix_child_watches; node; node = node->next)
+        {
+          GChildWatchSource *source = node->data;
+      
+          if (source->child_exited
+              || source->pid != pid)
+            continue;
+
+          source->child_status = estatus;
+          source->child_exited = TRUE;
+          give_to_catchall = FALSE;
+          wake_source ((GSource*)source);
+        }
+
+      /* The catchall watch only gets processes that don't already
+       * have a specific child watch.
+       */
+      if (!give_to_catchall)
+        continue;
+
+      if (unix_catchall_child_watch != NULL)
+        {
+          UnixWaitStatus *waitstatus = g_slice_new (UnixWaitStatus);
+          waitstatus->pid = pid;
+          waitstatus->estatus = estatus;
+                  
+          unix_catchall_child_watch->pending_waits = g_slist_prepend (unix_catchall_child_watch->pending_waits, waitstatus);
+          caught_one_catchall = TRUE;
+        }
+    }
+
+  if (caught_one_catchall)
+    wake_source ((GSource *) unix_catchall_child_watch);
+}
+
+/* In this case, we have to individually scan all of the children.
+ *
+ * The docs promise that we will not reap children that we are
+ * not explicitly watching, so that ties our hands from calling
+ * waitpid(-1) - that is a separate _catchall API.  We also
+ * can't use siginfo's si_pid field since if multiple SIGCHLD
+ * arrive at the same time, one of them can be dropped (since a
+ * given UNIX signal can only be pending once).
+ */
+static void
+dispatch_sigchld_individually (void)
+{
+  GSList *node;
+
+  for (node = unix_child_watches; node; node = node->next)
+    {
+      GChildWatchSource *source = node->data;
+      pid_t pid;
+
+      if (source->child_exited)
+        continue;
+      
+      do
+        {
+          pid = waitpid (source->pid, &source->child_status, WNOHANG);
+          if (pid > 0)
+            {
+              source->child_exited = TRUE;
+              wake_source ((GSource *) source);
+            }
+          else if (pid == -1 && errno == ECHILD)
+            {
+              g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). Most likely the process is ignoring SIGCHLD, or some other thread is invoking waitpid() with a nonpositive first argument; either behavior can break applications that use g_child_watch_add()/g_spawn_sync() either directly or indirectly.");
+              source->child_exited = TRUE;
+              source->child_status = 0;
+              wake_source ((GSource *) source);
+            }
+        }
+      while (pid == -1 && errno == EINTR);
+    }
+}
+
 static void
 dispatch_unix_signals (void)
 {
@@ -4407,39 +4544,13 @@ dispatch_unix_signals (void)
     {
       unix_signal_pending[SIGCHLD] = FALSE;
 
-      /* The only way we can do this is to scan all of the children.
-       *
-       * The docs promise that we will not reap children that we are not
-       * explicitly watching, so that ties our hands from calling
-       * waitpid(-1).  We also can't use siginfo's si_pid field since if
-       * multiple SIGCHLD arrive at the same time, one of them can be
-       * dropped (since a given UNIX signal can only be pending once).
-       */
-      for (node = unix_child_watches; node; node = node->next)
+      if (unix_catchall_child_watch != NULL)
         {
-          GChildWatchSource *source = node->data;
-
-          if (!source->child_exited)
-            {
-              pid_t pid;
-              do
-                {
-                  pid = waitpid (source->pid, &source->child_status, WNOHANG);
-                  if (pid > 0)
-                    {
-                      source->child_exited = TRUE;
-                      wake_source ((GSource *) source);
-                    }
-                  else if (pid == -1 && errno == ECHILD)
-                    {
-                      g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). Most likely the process is ignoring SIGCHLD, or some other thread is invoking waitpid() with a nonpositive first argument; either behavior can break applications that use g_child_watch_add()/g_spawn_sync() either directly or indirectly.");
-                      source->child_exited = TRUE;
-                      source->child_status = 0;
-                      wake_source ((GSource *) source);
-                    }
-                }
-              while (pid == -1 && errno == EINTR);
-            }
+          dispatch_sigchld_with_catchall ();
+        }
+      else
+        {
+          dispatch_sigchld_individually ();
         }
     }
 
@@ -4484,6 +4595,14 @@ g_child_watch_check (GSource *source)
   return child_watch_source->child_exited;
 }
 
+static void
+g_child_watch_finalize (GSource *source)
+{
+  G_LOCK (unix_signal_lock);
+  unix_child_watches = g_slist_remove (unix_child_watches, source);
+  G_UNLOCK (unix_signal_lock);
+}
+
 static gboolean
 g_unix_signal_watch_prepare (GSource *source,
 			     gint    *timeout)
@@ -4530,6 +4649,14 @@ g_unix_signal_watch_dispatch (GSource    *source,
 }
 
 static void
+g_unix_signal_watch_finalize (GSource    *source)
+{
+  G_LOCK (unix_signal_lock);
+  unix_signal_watches = g_slist_remove (unix_signal_watches, source);
+  G_UNLOCK (unix_signal_lock);
+}
+
+static void
 ensure_unix_signal_handler_installed_unlocked (int signum)
 {
   static sigset_t installed_signal_mask;
@@ -4577,23 +4704,114 @@ _g_main_create_unix_signal_watch (int signum)
   return source;
 }
 
-static void
-g_unix_signal_watch_finalize (GSource    *source)
+static gboolean
+g_unix_catchall_watch_prepare (GSource *source,
+                               gint    *timeout)
 {
+  gboolean res;
+  GUnixCatchallChildWatchSource *catchall_source;
+
+  catchall_source = (GUnixCatchallChildWatchSource *) source;
+
   G_LOCK (unix_signal_lock);
-  unix_signal_watches = g_slist_remove (unix_signal_watches, source);
+  res = catchall_source->pending_waits != NULL;
   G_UNLOCK (unix_signal_lock);
+  return res;
+}
+
+static gboolean
+g_unix_catchall_watch_check (GSource  *source)
+{
+  return g_unix_catchall_watch_prepare (source, NULL);
+}
+
+static gboolean
+g_unix_catchall_watch_dispatch (GSource    *source, 
+                                GSourceFunc callback,
+                                gpointer    user_data)
+{
+  GUnixCatchallChildWatchSource *catchall_source;
+  GChildWatchFunc child_callback = (GChildWatchFunc) callback;
+  GSList *iter;
+  GSList *pending;
+
+  catchall_source = (GUnixCatchallChildWatchSource *) source;
+
+  if (!callback)
+    {
+      g_warning ("Unix catchall source dispatched without callback\n"
+		 "You must call g_source_set_callback().");
+      return FALSE;
+    }
+
+  G_LOCK (unix_signal_lock);
+  pending = catchall_source->pending_waits;
+  catchall_source->pending_waits = NULL;
+  G_UNLOCK (unix_signal_lock);
+
+  for (iter = pending; iter; iter = iter->next)
+    {
+      UnixWaitStatus *status = iter->data;
+
+      (child_callback) (status->pid, status->estatus, user_data);
+      
+      g_slice_free (UnixWaitStatus, status);
+    }
+
+  g_slist_free (pending);
+
+  return TRUE;
 }
 
 static void
-g_child_watch_finalize (GSource *source)
+g_unix_catchall_watch_finalize (GSource *source)
 {
   G_LOCK (unix_signal_lock);
-  unix_child_watches = g_slist_remove (unix_child_watches, source);
+  g_assert (source == (GSource*)unix_catchall_child_watch);
+  unix_catchall_child_watch = NULL;
   G_UNLOCK (unix_signal_lock);
 }
 
-#endif /* G_OS_WIN32 */
+/**
+ * g_unix_catchall_child_source_new:
+ *
+ *
+ * Returns: A new #GSource
+ *
+ * Since: 2.36
+ */
+GSource *
+g_unix_catchall_child_source_new (void)
+{
+  GSource *source;
+
+  g_return_val_if_fail (unix_catchall_child_watch == NULL, NULL);
+
+  source = g_source_new (&g_unix_catchall_funcs, sizeof (GUnixCatchallChildWatchSource));
+
+  G_LOCK (unix_signal_lock);
+  ensure_unix_signal_handler_installed_unlocked (SIGCHLD);
+  unix_catchall_child_watch = (GUnixCatchallChildWatchSource*)source;
+  G_UNLOCK (unix_signal_lock);
+
+  return source;
+}
+
+/* Used by gspawn.c */
+void
+_g_main_unix_signal_lock (void)
+{
+  G_LOCK (unix_signal_lock);
+}
+
+/* Used by gspawn.c */
+void
+_g_main_unix_signal_unlock (void)
+{
+  G_UNLOCK (unix_signal_lock);
+}
+
+#endif /* !G_OS_WIN32 */
 
 static gboolean
 g_child_watch_dispatch (GSource    *source, 
@@ -4629,7 +4847,52 @@ g_unix_signal_handler (int signum)
   g_wakeup_signal (glib_worker_context->wakeup);
 }
 
-#endif /* !G_OS_WIN32 */
+/* must be holding unix_signal_lock */
+GSource *
+_g_main_child_watch_source_new_unix_unlocked (GPid  pid)
+{
+  GSource *source;
+
+  ensure_unix_signal_handler_installed_unlocked (SIGCHLD);
+
+  if (pid == -1)
+    {
+      g_assert (unix_catchall_child_watch == NULL);
+      source = g_source_new (&g_unix_catchall_funcs, sizeof (GUnixCatchallChildWatchSource));
+      unix_catchall_child_watch = (GUnixCatchallChildWatchSource*)source;
+    }
+  else
+    {
+      GChildWatchSource *child_watch_source;
+
+      source = g_source_new (&g_child_watch_funcs, sizeof (GChildWatchSource));
+      child_watch_source = (GChildWatchSource *)source;
+      child_watch_source->pid = pid;
+      unix_child_watches = g_slist_prepend (unix_child_watches, child_watch_source);
+      if (waitpid (pid, &child_watch_source->child_status, WNOHANG) > 0)
+        child_watch_source->child_exited = TRUE;
+    }
+  
+  return source;
+}
+
+#else
+
+static GSource *
+child_watch_source_new_win32 (GPid  pid)
+{
+  GSource *source;
+  GChildWatchSource *child_watch_source;
+
+  source = g_source_new (&g_child_watch_funcs, sizeof (GChildWatchSource));
+  child_watch_source = (GChildWatchSource*) source;
+  child_watch_source->poll.fd = (gintptr) pid;
+  child_watch_source->poll.events = G_IO_IN;
+
+  g_source_add_poll (source, &child_watch_source->poll);
+}
+
+#endif
 
 /**
  * g_child_watch_source_new:
@@ -4655,6 +4918,15 @@ g_unix_signal_handler (int signum)
  * compatible with calling <literal>waitpid</literal> with a
  * nonpositive first argument in the application. Calling waitpid()
  * for individual pids will still work fine.
+ *
+ * Since GLib 2.36, -1 may be given as @pid.  In this mode, the source
+ * will watch child processes of the current process which don't
+ * already have another dedicated child watch.  This allows a
+ * GLib-using process to perform a function equivalent to calling
+ * <literal>waitpid(-1, ...)</literal>.  There can at present be at
+ * most one instance of a source in this mode.  On Linux, it makes
+ * sense to use this together with
+ * <literal>prctl(PR_SET_CHILD_SUBREAPER)</literal>.
  * 
  * Return value: the newly-created child watch source
  *
@@ -4663,26 +4935,15 @@ g_unix_signal_handler (int signum)
 GSource *
 g_child_watch_source_new (GPid pid)
 {
-  GSource *source = g_source_new (&g_child_watch_funcs, sizeof (GChildWatchSource));
-  GChildWatchSource *child_watch_source = (GChildWatchSource *)source;
-
-  child_watch_source->pid = pid;
-
 #ifdef G_OS_WIN32
-  child_watch_source->poll.fd = (gintptr) pid;
-  child_watch_source->poll.events = G_IO_IN;
-
-  g_source_add_poll (source, &child_watch_source->poll);
-#else /* G_OS_WIN32 */
+  return child_watch_source_new_win32 (pid);
+#else
+  GSource *source;
   G_LOCK (unix_signal_lock);
-  ensure_unix_signal_handler_installed_unlocked (SIGCHLD);
-  unix_child_watches = g_slist_prepend (unix_child_watches, child_watch_source);
-  if (waitpid (pid, &child_watch_source->child_status, WNOHANG) > 0)
-    child_watch_source->child_exited = TRUE;
+  source = _g_main_child_watch_source_new_unix_unlocked (pid);
   G_UNLOCK (unix_signal_lock);
-#endif /* G_OS_WIN32 */
-
   return source;
+#endif
 }
 
 /**
diff --git a/glib/gspawn.c b/glib/gspawn.c
index 3545a78..4317e6a 100644
--- a/glib/gspawn.c
+++ b/glib/gspawn.c
@@ -47,6 +47,7 @@
 #include "genviron.h"
 #include "gmem.h"
 #include "gmain.h"
+#include "gmain-internal.h"
 #include "gshell.h"
 #include "gstring.h"
 #include "gstrfuncs.h"
@@ -281,9 +282,10 @@ g_spawn_sync (const gchar          *working_directory,
   gint ret;
   GString *outstr = NULL;
   GString *errstr = NULL;
-  gboolean failed;
+  gboolean failed = FALSE;
   gint status;
   SyncWaitpidData waitpid_data;
+  gboolean have_unix_signal_lock = FALSE;
   
   g_return_val_if_fail (argv != NULL, FALSE);
   g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE);
@@ -300,6 +302,12 @@ g_spawn_sync (const gchar          *working_directory,
 
   if (standard_error)
     *standard_error = NULL;
+
+  /* Hold a lock on any potential waitpid(-1, ...) calls until we have
+   * a child watch source set up.
+   */
+  _g_main_unix_signal_lock ();
+  have_unix_signal_lock = TRUE;
   
   if (!fork_exec_with_pipes (FALSE,
                              working_directory,
@@ -319,11 +327,12 @@ g_spawn_sync (const gchar          *working_directory,
                              standard_output ? &outpipe : NULL,
                              standard_error ? &errpipe : NULL,
                              error))
-    return FALSE;
+    {
+      failed = TRUE;
+      goto out;
+    }
 
   /* Read data from child. */
-  
-  failed = FALSE;
 
   if (outpipe >= 0)
     {
@@ -432,10 +441,18 @@ g_spawn_sync (const gchar          *working_directory,
     waitpid_data.loop = loop;
     waitpid_data.status_p = &status;
     
-    source = g_child_watch_source_new (pid);
+    /* Assumes we have the unix signal lock */
+    source = _g_main_child_watch_source_new_unix_unlocked (pid);
     g_source_set_callback (source, (GSourceFunc)on_sync_waitpid, &waitpid_data, NULL);
     g_source_attach (source, context);
     g_source_unref (source);
+
+    /* Now we safely have our waitpid() queued up for the GLib worker
+     * thread to process.  Unlock the mutex so it can continue.
+     */
+    g_assert (have_unix_signal_lock);
+    _g_main_unix_signal_unlock ();
+    have_unix_signal_lock = FALSE;
     
     g_main_loop_run (loop);
 
@@ -443,6 +460,10 @@ g_spawn_sync (const gchar          *working_directory,
     g_main_loop_unref (loop);
   }
   
+ out:
+  if (have_unix_signal_lock)
+    _g_main_unix_signal_unlock ();
+  
   if (failed)
     {
       if (outstr)
diff --git a/glib/tests/unix.c b/glib/tests/unix.c
index 329e19a..f649eec 100644
--- a/glib/tests/unix.c
+++ b/glib/tests/unix.c
@@ -147,6 +147,113 @@ test_sighup_add_remove (void)
 
 }
 
+typedef struct {
+  GPid pid;
+  GMainLoop *loop;
+  gboolean regular_exited;
+  gboolean catchall_exited;
+} CatchAllData;
+
+static void
+on_catchall_child (GPid   pid,
+                   gint   estatus,
+                   gpointer user_data)
+{
+  CatchAllData *data = user_data;
+  GError *local_error = NULL;
+  GError **error = &local_error;
+
+  g_spawn_check_exit_status (estatus, error);
+  g_assert_no_error (local_error);
+
+  if (pid == data->pid)
+    {
+      data->catchall_exited = TRUE;
+      if (data->regular_exited)
+        g_main_loop_quit (data->loop);
+    }
+}
+
+static void
+test_catchall_source (void)
+{
+  GMainLoop *mainloop;
+  CatchAllData data;
+  GSource *source;
+  GPid pid;
+  GError *local_error = NULL;
+  GError **error = &local_error;
+  char *child_args[] = { "/bin/true", "/bin/true", NULL };
+
+  memset (&data, 0, sizeof (data));
+  data.regular_exited = TRUE;  /* For the next test */
+  mainloop = g_main_loop_new (NULL, FALSE);
+  data.loop = mainloop;
+
+  source = g_unix_catchall_child_source_new ();
+  g_source_set_callback (source, (GSourceFunc)on_catchall_child, &data, NULL);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+
+  g_spawn_async (NULL, (char**)child_args, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+                 NULL, NULL, &pid, error);
+  g_assert_no_error (local_error);
+  data.pid = pid;
+
+  g_main_loop_run (mainloop);
+  g_source_destroy (source);
+  g_main_loop_unref (mainloop);
+}
+
+static void
+on_regular_child (GPid  pid,
+                  gint  estatus,
+                  gpointer user_data)
+{
+  CatchAllData *data = user_data;
+  data->regular_exited = TRUE;
+
+  if (data->catchall_exited)
+    g_main_loop_quit (data->loop);
+}
+
+static void
+test_catchall_and_regular_child (void)
+{
+  GMainLoop *mainloop;
+  CatchAllData data;
+  GSource *source;
+  GPid pid;
+  GError *local_error = NULL;
+  GError **error = &local_error;
+  char *child_args[] = { "/bin/true", "/bin/true", NULL };
+
+  memset (&data, 0, sizeof (data));
+  mainloop = g_main_loop_new (NULL, FALSE);
+  data.loop = mainloop;
+
+  g_spawn_async (NULL, (char**)child_args, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+                 NULL, NULL, &pid, error);
+  g_child_watch_add (pid, on_regular_child, &data);
+
+  source = g_child_watch_source_new (-1);
+  g_source_set_callback (source, (GSourceFunc)on_catchall_child, &data, NULL);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+
+  g_spawn_async (NULL, (char**)child_args, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+                 NULL, NULL, &pid, error);
+  g_assert_no_error (local_error);
+  data.pid = pid;
+
+  g_main_loop_run (mainloop);
+
+  g_source_destroy (source);
+  g_main_loop_unref (mainloop);
+
+  g_assert (data.regular_exited && data.catchall_exited);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -159,6 +266,8 @@ main (int   argc,
   g_test_add_func ("/glib-unix/sigterm", test_sigterm);
   g_test_add_func ("/glib-unix/sighup_again", test_sighup);
   g_test_add_func ("/glib-unix/sighup_add_remove", test_sighup_add_remove);
+  g_test_add_func ("/glib-unix/catchall-source", test_catchall_source);
+  g_test_add_func ("/glib-unix/catchall-and-regular-child", test_catchall_and_regular_child);
 
   return g_test_run();
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]