Wrap waitpid() as a GSource. This is a partial implementation of the "Unix
authorMatthias Clasen <maclas@gmx.de>
Sat, 14 Feb 2004 00:23:36 +0000 (00:23 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Sat, 14 Feb 2004 00:23:36 +0000 (00:23 +0000)
Sat Feb 14 01:21:34 2004  Matthias Clasen  <maclas@gmx.de>

* glib/gmain.h:
* glib/gmain.c (g_child_watch_source_new):
* glib/gmain.c (g_child_watch_add):
* glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a
GSource. This is a partial implementation of the "Unix signal
source".  (#50296, Jonathan R. Blandford)

* configure.in: Add the necessary configury to typedef GPid
appropriately.

* tests/Makefile.am:
* tests/child-test.c: Test child_watch sources.

13 files changed:
ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
configure.in
docs/reference/ChangeLog
docs/reference/glib/glib-sections.txt
glib/gmain.c
glib/gmain.h
tests/Makefile.am
tests/child-test.c [new file with mode: 0644]

index 7e779d5..cc1641a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+Sat Feb 14 01:21:34 2004  Matthias Clasen  <maclas@gmx.de>
+
+       * glib/gmain.h: 
+       * glib/gmain.c (g_child_watch_source_new): 
+       * glib/gmain.c (g_child_watch_add): 
+       * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a
+       GSource. This is a partial implementation of the "Unix signal 
+       source".  (#50296, Jonathan R. Blandford)
+
+       * configure.in: Add the necessary configury to typedef GPid
+       appropriately.
+
+       * tests/Makefile.am:
+       * tests/child-test.c: Test child_watch sources.
+
 Sat Feb  7 15:02:01 2004  Manish Singh  <yosh@gimp.org>
 
        * tests/type-test.c: Fix broken test for gsize formats.
index 7e779d5..cc1641a 100644 (file)
@@ -1,3 +1,18 @@
+Sat Feb 14 01:21:34 2004  Matthias Clasen  <maclas@gmx.de>
+
+       * glib/gmain.h: 
+       * glib/gmain.c (g_child_watch_source_new): 
+       * glib/gmain.c (g_child_watch_add): 
+       * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a
+       GSource. This is a partial implementation of the "Unix signal 
+       source".  (#50296, Jonathan R. Blandford)
+
+       * configure.in: Add the necessary configury to typedef GPid
+       appropriately.
+
+       * tests/Makefile.am:
+       * tests/child-test.c: Test child_watch sources.
+
 Sat Feb  7 15:02:01 2004  Manish Singh  <yosh@gimp.org>
 
        * tests/type-test.c: Fix broken test for gsize formats.
index 7e779d5..cc1641a 100644 (file)
@@ -1,3 +1,18 @@
+Sat Feb 14 01:21:34 2004  Matthias Clasen  <maclas@gmx.de>
+
+       * glib/gmain.h: 
+       * glib/gmain.c (g_child_watch_source_new): 
+       * glib/gmain.c (g_child_watch_add): 
+       * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a
+       GSource. This is a partial implementation of the "Unix signal 
+       source".  (#50296, Jonathan R. Blandford)
+
+       * configure.in: Add the necessary configury to typedef GPid
+       appropriately.
+
+       * tests/Makefile.am:
+       * tests/child-test.c: Test child_watch sources.
+
 Sat Feb  7 15:02:01 2004  Manish Singh  <yosh@gimp.org>
 
        * tests/type-test.c: Fix broken test for gsize formats.
index 7e779d5..cc1641a 100644 (file)
@@ -1,3 +1,18 @@
+Sat Feb 14 01:21:34 2004  Matthias Clasen  <maclas@gmx.de>
+
+       * glib/gmain.h: 
+       * glib/gmain.c (g_child_watch_source_new): 
+       * glib/gmain.c (g_child_watch_add): 
+       * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a
+       GSource. This is a partial implementation of the "Unix signal 
+       source".  (#50296, Jonathan R. Blandford)
+
+       * configure.in: Add the necessary configury to typedef GPid
+       appropriately.
+
+       * tests/Makefile.am:
+       * tests/child-test.c: Test child_watch sources.
+
 Sat Feb  7 15:02:01 2004  Manish Singh  <yosh@gimp.org>
 
        * tests/type-test.c: Fix broken test for gsize formats.
index 7e779d5..cc1641a 100644 (file)
@@ -1,3 +1,18 @@
+Sat Feb 14 01:21:34 2004  Matthias Clasen  <maclas@gmx.de>
+
+       * glib/gmain.h: 
+       * glib/gmain.c (g_child_watch_source_new): 
+       * glib/gmain.c (g_child_watch_add): 
+       * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a
+       GSource. This is a partial implementation of the "Unix signal 
+       source".  (#50296, Jonathan R. Blandford)
+
+       * configure.in: Add the necessary configury to typedef GPid
+       appropriately.
+
+       * tests/Makefile.am:
+       * tests/child-test.c: Test child_watch sources.
+
 Sat Feb  7 15:02:01 2004  Manish Singh  <yosh@gimp.org>
 
        * tests/type-test.c: Fix broken test for gsize formats.
index 7e779d5..cc1641a 100644 (file)
@@ -1,3 +1,18 @@
+Sat Feb 14 01:21:34 2004  Matthias Clasen  <maclas@gmx.de>
+
+       * glib/gmain.h: 
+       * glib/gmain.c (g_child_watch_source_new): 
+       * glib/gmain.c (g_child_watch_add): 
+       * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a
+       GSource. This is a partial implementation of the "Unix signal 
+       source".  (#50296, Jonathan R. Blandford)
+
+       * configure.in: Add the necessary configury to typedef GPid
+       appropriately.
+
+       * tests/Makefile.am:
+       * tests/child-test.c: Test child_watch sources.
+
 Sat Feb  7 15:02:01 2004  Manish Singh  <yosh@gimp.org>
 
        * tests/type-test.c: Fix broken test for gsize formats.
index c6eb370..d6bcbaf 100644 (file)
@@ -131,6 +131,7 @@ case "$host" in
     GOBJECT_DEF=gobject.def
     GTHREAD_DEF=gthread.def
     TESTGMODULE_EXP=testgmodule.exp
+    glib_pid_type=HANDLE
     ;;
   *)
     glib_native_win32=no
@@ -143,6 +144,7 @@ case "$host" in
     GOBJECT_DEF=
     GTHREAD_DEF=
     TESTGMODULE_EXP=
+    glib_pid_type=int
     ;;
 esac
 AC_MSG_RESULT([$glib_native_win32])
@@ -2265,6 +2267,8 @@ _______EOF
 
 #define G_MODULE_SUFFIX "$g_module_suffix"
 
+typedef $g_pid_type GPid;
+
 G_END_DECLS
 
 #endif /* GLIBCONFIG_H */
@@ -2536,6 +2540,7 @@ g_mutex_contents="$glib_cv_byte_contents_gmutex"
 
 g_module_suffix="$glib_gmodule_suffix"
 
+g_pid_type="$glib_pid_type"
 case $host in
   *-*-beos*)
     glib_os="#define G_OS_BEOS"
index 5c26742..9ef02c2 100644 (file)
@@ -1,3 +1,9 @@
+Sat Feb 14 01:25:23 2004  Matthias Clasen  <maclas@gmx.de>
+
+       * glib/glib-sections.txt: Add GPid, GChildWatchFunc,
+       g_child_watch_source_new, g_child_watch_add,
+       g_child_watch_add_full.
+
 Fri Feb 13 23:16:25 2004  Matthias Clasen  <maclas@gmx.de>
 
        * glib/tmpl/macros_misc.sgml: Fix a typo. 
index 4231b1a..3fb0e5b 100644 (file)
@@ -427,6 +427,12 @@ g_idle_add_full
 g_idle_remove_by_data
 
 <SUBSECTION>
+GPid
+GChildWatchFunc
+g_child_watch_source_new
+g_child_watch_add
+g_child_watch_add_full
+<SUBSECTION>
 GPollFD
 
 <SUBSECTION>
@@ -468,6 +474,7 @@ GLIB_SYSDEF_POLLPRI
 G_WIN32_MSG_HANDLE
 g_idle_funcs
 g_timeout_funcs
+g_child_watch_funcs
 </SECTION>
 
 <SECTION>
index 603eec0..18e503b 100644 (file)
 #include <net/socket.h>
 #endif /* G_OS_BEOS */
 
+#ifdef G_OS_UNIX
+#include <fcntl.h>
+#include <sys/wait.h>
+#endif
 /* Types */
 
 typedef struct _GTimeoutSource GTimeoutSource;
+typedef struct _GChildWatchSource GChildWatchSource;
 typedef struct _GPollRec GPollRec;
 typedef struct _GSourceCallback GSourceCallback;
 
@@ -157,6 +162,15 @@ struct _GTimeoutSource
   guint       interval;
 };
 
+struct _GChildWatchSource
+{
+  GSource     source;
+  GPid        pid;
+  gint        child_status;
+  gint        count;
+  gboolean    child_exited;
+};
+
 struct _GPollRec
 {
   gint priority;
@@ -175,8 +189,6 @@ struct _GPollRec
 #endif
 
 #define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0)
-#define SOURCE_BLOCKED(source) (((source)->flags & G_HOOK_FLAG_IN_CALL) != 0 && \
-                               ((source)->flags & G_SOURCE_CAN_RECURSE) == 0)
 
 #define SOURCE_UNREF(source, context)                       \
    G_STMT_START {                                           \
@@ -213,6 +225,12 @@ static gboolean g_timeout_check    (GSource     *source);
 static gboolean g_timeout_dispatch (GSource     *source,
                                    GSourceFunc  callback,
                                    gpointer     user_data);
+static gboolean g_child_watch_prepare  (GSource     *source,
+                                       gint        *timeout);
+static gboolean g_child_watch_check    (GSource     *source);
+static gboolean g_child_watch_dispatch (GSource     *source,
+                                       GSourceFunc  callback,
+                                       gpointer     user_data);
 static gboolean g_idle_prepare     (GSource     *source,
                                    gint        *timeout);
 static gboolean g_idle_check       (GSource     *source);
@@ -224,6 +242,18 @@ G_LOCK_DEFINE_STATIC (main_loop);
 static GMainContext *default_main_context;
 static GSList *main_contexts_without_pipe = NULL;
 
+/* Child status monitoring code */
+enum {
+  CHILD_WATCH_UNINITIALIZED,
+  CHILD_WATCH_INITIALIZED_SINGLE,
+  CHILD_WATCH_INITIALIZED_THREADED
+};
+static gint child_watch_init_state = CHILD_WATCH_UNINITIALIZED;
+static gint child_watch_count = 0;
+static gint child_watch_wake_up_pipe[2] = {0, 0};
+G_LOCK_DEFINE_STATIC (main_context_list);
+static GSList *main_context_list = NULL;
+
 #if defined(G_PLATFORM_WIN32) && defined(__GNUC__)
 __declspec(dllexport)
 #endif
@@ -235,6 +265,14 @@ GSourceFuncs g_timeout_funcs =
   NULL
 };
 
+GSourceFuncs g_child_watch_funcs =
+{
+  g_child_watch_prepare,
+  g_child_watch_check,
+  g_child_watch_dispatch,
+  NULL
+};
+
 #if defined(G_PLATFORM_WIN32) && defined(__GNUC__)
 __declspec(dllexport)
 #endif
@@ -586,6 +624,10 @@ g_main_context_unref_and_unlock (GMainContext *context)
       return;
     }
 
+  G_LOCK (main_context_list);
+  main_context_list = g_slist_remove (main_context_list, context);
+  G_UNLOCK (main_context_list);
+
   source = context->source_list;
   while (source)
     {
@@ -649,6 +691,8 @@ g_main_context_unref (GMainContext *context)
 static void 
 g_main_context_init_pipe (GMainContext *context)
 {
+  if (context->wake_up_pipe[0] != -1)
+    return;
 # ifndef G_OS_WIN32
   if (pipe (context->wake_up_pipe) < 0)
     g_error ("Cannot create pipe main loop wake-up: %s\n",
@@ -702,7 +746,10 @@ g_main_context_new ()
   context->owner = NULL;
   context->waiters = NULL;
 #endif
-      
+
+  context->wake_up_pipe[0] = -1;
+  context->wake_up_pipe[1] = -1;
+
   context->ref_count = 1;
 
   context->next_id = 1;
@@ -730,6 +777,10 @@ g_main_context_new ()
                                                  context);
 #endif
 
+  G_LOCK (main_context_list);
+  main_context_list = g_slist_append (main_context_list, context);
+  G_UNLOCK (main_context_list);
+
   return context;
 }
 
@@ -920,17 +971,14 @@ g_source_destroy_internal (GSource      *source,
          old_cb_funcs->unref (old_cb_data);
          LOCK_CONTEXT (context);
        }
-
-      if (!SOURCE_BLOCKED (source))
+      
+      tmp_list = source->poll_fds;
+      while (tmp_list)
        {
-         tmp_list = source->poll_fds;
-         while (tmp_list)
-           {
-             g_main_context_remove_poll_unlocked (context, tmp_list->data);
-             tmp_list = tmp_list->next;
-           }
+         g_main_context_remove_poll_unlocked (context, tmp_list->data);
+         tmp_list = tmp_list->next;
        }
-         
+      
       g_source_unref_internal (source, context, TRUE);
     }
 
@@ -1036,8 +1084,7 @@ g_source_add_poll (GSource *source,
 
   if (context)
     {
-      if (!SOURCE_BLOCKED (source))
-       g_main_context_add_poll_unlocked (context, source->priority, fd);
+      g_main_context_add_poll_unlocked (context, source->priority, fd);
       UNLOCK_CONTEXT (context);
     }
 }
@@ -1069,8 +1116,7 @@ g_source_remove_poll (GSource *source,
 
   if (context)
     {
-      if (!SOURCE_BLOCKED (source))
-       g_main_context_remove_poll_unlocked (context, fd);
+      g_main_context_remove_poll_unlocked (context, fd);
       UNLOCK_CONTEXT (context);
     }
 }
@@ -1224,22 +1270,16 @@ g_source_set_priority (GSource  *source,
 
   if (context)
     {
-      /* Remove the source from the context's source and then
-       * add it back so it is sorted in the correct plcae
-       */
-      g_source_list_remove (source, source->context);
-      g_source_list_add (source, source->context);
-
-      if (!SOURCE_BLOCKED (source))
+      source->next = NULL;
+      source->prev = NULL;
+      
+      tmp_list = source->poll_fds;
+      while (tmp_list)
        {
-         tmp_list = source->poll_fds;
-         while (tmp_list)
-           {
-             g_main_context_remove_poll_unlocked (context, tmp_list->data);
-             g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
-             
-             tmp_list = tmp_list->next;
-           }
+         g_main_context_remove_poll_unlocked (context, tmp_list->data);
+         g_main_context_add_poll_unlocked (context, priority, tmp_list->data);
+      
+         tmp_list = tmp_list->next;
        }
       
       UNLOCK_CONTEXT (source->context);
@@ -1543,11 +1583,11 @@ g_main_context_find_source_by_user_data (GMainContext *context,
  * g_source_remove:
  * @tag: the id of the source to remove.
  * 
- * Removes the source with the given id from the default main
- * context. The id of a #GSource is given by g_source_get_id(),
- * or will be returned by the functions g_source_attach(),
- * g_idle_add(), g_idle_add_full(), g_timeout_add(),
- * g_timeout_add_full(), g_io_add_watch, and g_io_add_watch_full().
+ * Removes the source with the given id from the default main context. The id of
+ * a #GSource is given by g_source_get_id(), or will be returned by the
+ * functions g_source_attach(), g_idle_add(), g_idle_add_full(),
+ * g_timeout_add(), g_timeout_add_full(), g_child_watch_add(),
+ * g_child_watch_add_full(), g_io_add_watch(), and g_io_add_watch_full().
  *
  * See also g_source_destroy().
  *
@@ -1666,43 +1706,6 @@ g_get_current_time (GTimeVal *result)
 
 /* Running the main loop */
 
-/* Temporarily remove all this source's file descriptors from the
- * poll(), so that if data comes available for one of the file descriptors
- * we don't continually spin in the poll()
- */
-/* HOLDS: source->context's lock */
-void
-block_source (GSource *source)
-{
-  GSList *tmp_list;
-
-  g_return_if_fail (!SOURCE_BLOCKED (source));
-
-  tmp_list = source->poll_fds;
-  while (tmp_list)
-    {
-      g_main_context_remove_poll_unlocked (source->context, tmp_list->data);
-      tmp_list = tmp_list->next;
-    }
-}
-
-/* HOLDS: source->context's lock */
-void
-unblock_source (GSource *source)
-{
-  GSList *tmp_list;
-  
-  g_return_if_fail (!SOURCE_BLOCKED (source)); /* Source already unblocked */
-  g_return_if_fail (!SOURCE_DESTROYED (source));
-  
-  tmp_list = source->poll_fds;
-  while (tmp_list)
-    {
-      g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data);
-      tmp_list = tmp_list->next;
-    }
-}
-
 /* HOLDS: context's lock */
 static void
 g_main_dispatch (GMainContext *context)
@@ -1738,9 +1741,6 @@ g_main_dispatch (GMainContext *context)
          if (cb_funcs)
            cb_funcs->ref (cb_data);
          
-         if ((source->flags & G_SOURCE_CAN_RECURSE) == 0)
-           block_source (source);
-         
          was_in_call = source->flags & G_HOOK_FLAG_IN_CALL;
          source->flags |= G_HOOK_FLAG_IN_CALL;
 
@@ -1760,10 +1760,6 @@ g_main_dispatch (GMainContext *context)
         if (!was_in_call)
            source->flags &= ~G_HOOK_FLAG_IN_CALL;
 
-         if ((source->flags & G_SOURCE_CAN_RECURSE) == 0 &&
-             !SOURCE_DESTROYED (source))
-           unblock_source (source);
-         
          /* Note: this depends on the fact that we can't switch
           * sources from one main context to another
           */
@@ -2052,7 +2048,7 @@ g_main_context_prepare (GMainContext *context,
          SOURCE_UNREF (source, context);
          break;
        }
-      if (SOURCE_BLOCKED (source))
+      if ((source->flags & G_HOOK_FLAG_IN_CALL) && !(source->flags & G_SOURCE_CAN_RECURSE))
        goto next;
 
       if (!(source->flags & G_SOURCE_READY))
@@ -2203,8 +2199,8 @@ g_main_context_check (GMainContext *context,
   if (!context->poll_waiting)
     {
 #ifndef G_OS_WIN32
-      gchar c;
-      read (context->wake_up_pipe[0], &c, 1);
+      gchar a;
+      read (context->wake_up_pipe[0], &a, 1);
 #endif
     }
   else
@@ -2240,7 +2236,7 @@ g_main_context_check (GMainContext *context,
          SOURCE_UNREF (source, context);
          break;
        }
-      if (SOURCE_BLOCKED (source))
+      if ((source->flags & G_HOOK_FLAG_IN_CALL) && !(source->flags & G_SOURCE_CAN_RECURSE))
        goto next;
 
       if (!(source->flags & G_SOURCE_READY))
@@ -3241,6 +3237,274 @@ g_timeout_add (guint32        interval,
                             interval, function, data, NULL);
 }
 
+/* Child watch functions */
+
+static void
+check_for_child_exited (GSource *source)
+{
+  GChildWatchSource *child_watch_source;
+  gint count;
+
+  /* protect against another SIGCHLD in the middle of this call */
+  count = child_watch_count;
+
+  child_watch_source = (GChildWatchSource *) source;
+
+  if (child_watch_source->count < count)
+    {
+      gint child_status;
+
+      if (waitpid (child_watch_source->pid, &child_status, WNOHANG) > 0)
+       {
+         child_watch_source->child_status = child_status;
+         child_watch_source->child_exited = TRUE;
+       }
+      child_watch_source->count = count;
+    }
+}
+
+static gboolean
+g_child_watch_prepare (GSource *source,
+                      gint    *timeout)
+{
+  GChildWatchSource *child_watch_source;
+  *timeout = -1;
+
+  child_watch_source = (GChildWatchSource *) source;
+
+  check_for_child_exited (source);
+
+  return child_watch_source->child_exited;
+}
+
+
+static gboolean 
+g_child_watch_check (GSource  *source)
+{
+  GChildWatchSource *child_watch_source;
+
+  child_watch_source = (GChildWatchSource *) source;
+
+  return (child_watch_source->count < child_watch_count);
+}
+
+static gboolean
+g_child_watch_dispatch (GSource    *source, 
+                       GSourceFunc callback,
+                       gpointer    user_data)
+{
+  GChildWatchSource *child_watch_source;
+  GChildWatchFunc child_watch_callback = (GChildWatchFunc) callback;
+
+  child_watch_source = (GChildWatchSource *) source;
+
+  if (!callback)
+    {
+      g_warning ("Child watch source dispatched without callback\n"
+                "You must call g_source_set_callback().");
+      return FALSE;
+    }
+
+  (child_watch_callback) (child_watch_source->pid, child_watch_source->child_status, user_data);
+
+  /* We never keep a child watch source around as the child is gone */
+  return FALSE;
+}
+
+static void
+g_child_watch_signal_handler (int signum)
+{
+  child_watch_count ++;
+
+  if (child_watch_init_state == CHILD_WATCH_INITIALIZED_THREADED)
+    {
+      write (child_watch_wake_up_pipe[1], "B", 1);
+    }
+  else
+    {
+      /* We count on the signal interrupting the poll in the same thread.
+       */
+    }
+}
+static void
+g_child_watch_source_init_single (void)
+{
+  g_assert (! g_thread_supported());
+  g_assert (child_watch_init_state == CHILD_WATCH_UNINITIALIZED);
+
+  child_watch_init_state = CHILD_WATCH_INITIALIZED_SINGLE;
+
+  signal (SIGCHLD, g_child_watch_signal_handler);
+}
+
+static gpointer
+child_watch_helper_thread (gpointer data)
+{
+  GPollFD fds;
+  GPollFunc poll_func;
+
+#ifdef HAVE_POLL
+      poll_func = (GPollFunc)poll;
+#else
+      poll_func = g_poll;
+#endif
+
+  fds.fd = child_watch_wake_up_pipe[0];
+  fds.events = G_IO_IN;
+
+  while (1)
+    {
+      gchar b[20];
+      GSList *list;
+
+      read (child_watch_wake_up_pipe[0], b, 20);
+
+      /* We were woken up.  Wake up all other contexts in all other threads */
+      G_UNLOCK (main_context_list);
+      for (list = main_context_list; list; list = list->next)
+       {
+         GMainContext *context;
+
+         context = list->data;
+         g_main_context_wakeup (context);
+       }
+      G_LOCK (main_context_list);
+    }
+  return NULL;
+}
+
+static void
+g_child_watch_source_init_multi_threaded (void)
+{
+  GError *error = NULL;
+
+  g_assert (g_thread_supported());
+
+  if (pipe (child_watch_wake_up_pipe) < 0)
+    g_error ("Cannot create wake up pipe: %s\n", g_strerror (errno));
+  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;
+  signal (SIGCHLD, g_child_watch_signal_handler);
+}
+
+static void
+g_child_watch_source_init_promote_single_to_threaded (void)
+{
+  g_child_watch_source_init_multi_threaded ();
+}
+
+static void
+g_child_watch_source_init (void)
+{
+  if (g_thread_supported())
+    {
+      if (child_watch_init_state == CHILD_WATCH_UNINITIALIZED)
+       g_child_watch_source_init_multi_threaded ();
+      else if (child_watch_init_state == CHILD_WATCH_INITIALIZED_SINGLE)
+       g_child_watch_source_init_promote_single_to_threaded ();
+    }
+  else
+    {
+      if (child_watch_init_state == CHILD_WATCH_UNINITIALIZED)
+       g_child_watch_source_init_single ();
+    }
+}
+
+/**
+ * g_child_watch_source_new:
+ * @pid: process id of a child process to watch
+ * 
+ * Creates a new child_watch source.
+ *
+ * The source will not initially be associated with any #GMainContext
+ * and must be added to one with g_source_attach() before it will be
+ * executed.
+ * 
+ * Return value: the newly-created child watch source
+ *
+ * Since: 2.4
+ **/
+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;
+
+  g_child_watch_source_init ();
+
+  child_watch_source->pid = pid;
+
+  return source;
+}
+
+/**
+ * g_child_watch_add_full:
+ * @priority: the priority of the idle source. Typically this will be in the
+ *            range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE.
+ * @pid:      process id of a child process to watch
+ * @function: function to call
+ * @data:     data to pass to @function
+ * @notify:   function to call when the idle is removed, or %NULL
+ * 
+ * Sets a function to be called when the child indicated by @pid exits, at a
+ * default priority, #G_PRIORITY_DEFAULT.
+ * 
+ * Return value: the id of event source.
+ *
+ * Since: 2.4
+ **/
+guint
+g_child_watch_add_full (gint            priority,
+                       GPid            pid,
+                       GChildWatchFunc function,
+                       gpointer        data,
+                       GDestroyNotify  notify)
+{
+  GSource *source;
+  guint id;
+  
+  g_return_val_if_fail (function != NULL, 0);
+
+  source = g_child_watch_source_new (pid);
+
+  if (priority != G_PRIORITY_DEFAULT)
+    g_source_set_priority (source, priority);
+
+  g_source_set_callback (source, (GSourceFunc) function, data, notify);
+  id = g_source_attach (source, NULL);
+  g_source_unref (source);
+
+  return id;
+}
+
+/**
+ * g_child_watch_add:
+ * @pid:      process id of a child process to watch
+ * @function: function to call
+ * @data:     data to pass to @function
+ * 
+ * Sets a function to be called when the child indicated by @pid exits, at a
+ * default priority, #G_PRIORITY_DEFAULT.
+ * 
+ * Return value: the id of event source.
+ *
+ * Since: 2.4
+ **/
+guint 
+g_child_watch_add (GPid            pid,
+                  GChildWatchFunc function,
+                  gpointer        data)
+{
+  return g_child_watch_add_full (G_PRIORITY_DEFAULT, pid, function, data, NULL);
+}
+
+
 /* Idle functions */
 
 static gboolean 
index 5a224bc..fb7ac3e 100644 (file)
@@ -32,7 +32,9 @@ typedef struct _GSourceCallbackFuncs  GSourceCallbackFuncs;
 typedef struct _GSourceFuncs           GSourceFuncs;
 
 typedef gboolean (*GSourceFunc)       (gpointer data);
-
+typedef void     (*GChildWatchFunc)   (GPid     pid,
+                                      gint     status,
+                                      gpointer data);
 struct _GSource
 {
   /*< private >*/
@@ -243,8 +245,9 @@ void     g_source_get_current_time (GSource        *source,
 
 /* Specific source types
  */
-GSource *g_idle_source_new    (void);
-GSource *g_timeout_source_new (guint         interval);
+GSource *g_idle_source_new        (void);
+GSource *g_child_watch_source_new (GPid pid);
+GSource *g_timeout_source_new     (guint interval);
 
 /* Miscellaneous functions
  */
@@ -278,25 +281,34 @@ gboolean g_source_remove_by_user_data        (gpointer       user_data);
 gboolean g_source_remove_by_funcs_user_data  (GSourceFuncs  *funcs,
                                              gpointer       user_data);
 
-/* Idles and timeouts */
-guint          g_timeout_add_full      (gint           priority,
-                                        guint          interval, 
-                                        GSourceFunc    function,
-                                        gpointer       data,
-                                        GDestroyNotify notify);
-guint          g_timeout_add           (guint          interval,
-                                        GSourceFunc    function,
-                                        gpointer       data);
-guint          g_idle_add              (GSourceFunc    function,
-                                        gpointer       data);
-guint          g_idle_add_full         (gint           priority,
-                                        GSourceFunc    function,
-                                        gpointer       data,
-                                        GDestroyNotify notify);
-gboolean       g_idle_remove_by_data   (gpointer       data);
+/* Idles, child watchers and timeouts */
+guint    g_timeout_add_full     (gint            priority,
+                                guint           interval,
+                                GSourceFunc     function,
+                                gpointer        data,
+                                GDestroyNotify  notify);
+guint    g_timeout_add          (guint           interval,
+                                GSourceFunc     function,
+                                gpointer        data);
+guint    g_child_watch_add_full (gint            priority,
+                                GPid            pid,
+                                GChildWatchFunc function,
+                                gpointer        data,
+                                GDestroyNotify  notify);
+guint    g_child_watch_add      (GPid            pid,
+                                GChildWatchFunc function,
+                                gpointer        data);
+guint    g_idle_add             (GSourceFunc     function,
+                                gpointer        data);
+guint    g_idle_add_full        (gint            priority,
+                                GSourceFunc     function,
+                                gpointer        data,
+                                GDestroyNotify  notify);
+gboolean g_idle_remove_by_data  (gpointer        data);
 
 /* Hook for GClosure / GSource integration. Don't touch */
 GLIB_VAR GSourceFuncs g_timeout_funcs;
+GLIB_VAR GSourceFuncs g_child_watch_funcs;
 GLIB_VAR GSourceFuncs g_idle_funcs;
 
 G_END_DECLS
index 2357d90..69c4764 100644 (file)
@@ -66,6 +66,7 @@ endif
 test_programs =                                        \
        array-test                              \
        $(CXX_TEST)                             \
+       child-test                              \
        completion-test                         \
        date-test                               \
        dirname-test                            \
@@ -115,6 +116,7 @@ thread_ldadd = $(libgthread) $(G_THREAD_LIBS) $(progs_ldadd)
 module_ldadd = $(libgmodule) $(G_MODULE_LIBS) $(progs_ldadd)
 
 array_test_LDADD = $(progs_ldadd)
+child_test_LDADD = $(thread_ldadd)
 completion_test_LDADD = $(progs_ldadd)
 date_test_LDADD = $(progs_ldadd)
 dirname_test_LDADD = $(progs_ldadd)
diff --git a/tests/child-test.c b/tests/child-test.c
new file mode 100644 (file)
index 0000000..d910b8c
--- /dev/null
@@ -0,0 +1,101 @@
+/* GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
+ * file for a list of people on the GLib Team.  See the ChangeLog
+ * files for a list of changes.  These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/. 
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+GMainLoop *main_loop;
+gint alive;
+
+gint
+get_a_child (gint ttl)
+{
+  GPid pid;
+
+  pid = fork ();
+  if (pid < 0)
+    exit (1);
+
+  if (pid > 0)
+    return pid;
+
+  sleep (ttl);
+  _exit (0);
+}
+
+gboolean
+child_watch_callback (GPid pid, gint status, gpointer data)
+{
+  g_print ("child %d exited, status %d\n", pid, status);
+
+  if (--alive == 0)
+    g_main_loop_quit (main_loop);
+
+  return TRUE;
+}
+
+static gpointer
+test_thread (gpointer data)
+{
+  GMainLoop *new_main_loop;
+  GSource *source;
+  GPid pid;
+  gint ttl = GPOINTER_TO_INT (data);
+
+  new_main_loop = g_main_loop_new (NULL, FALSE);
+
+  pid = get_a_child (ttl);
+  source = g_child_watch_source_new (pid);
+  g_source_set_callback (source, (GSourceFunc) child_watch_callback, NULL, NULL);
+  g_source_attach (source, g_main_loop_get_context (new_main_loop));
+  g_source_unref (source);
+
+  g_print ("whee! created pid: %d\n", pid);
+  g_main_loop_run (new_main_loop);
+
+}
+
+int
+main (int argc, char *argv[])
+{
+  g_thread_init (NULL);
+  main_loop = g_main_loop_new (NULL, FALSE);
+
+  system ("/bin/true");
+
+  alive = 2;
+  g_thread_create (test_thread, GINT_TO_POINTER (10), FALSE, NULL);
+  g_thread_create (test_thread, GINT_TO_POINTER (20), FALSE, NULL);
+  
+  g_main_loop_run (main_loop);
+
+  return 0;
+}