[glib/wip/gsubprocess: 2/3] GSubprocess: [rebase] Update for comments



commit c81079855fc94e63933f8257f49615a5ec603749
Author: Colin Walters <walters verbum org>
Date:   Mon May 21 13:00:53 2012 -0400

    GSubprocess: [rebase] Update for comments
    
    https://bugzilla.gnome.org/show_bug.cgi?id=672102

 docs/reference/gio/gio-sections.txt |    8 +-
 gio/gio.symbols                     |    8 +-
 gio/giotypes.h                      |   18 +-
 gio/gsubprocess.c                   |  647 +++++++++++++++++------------------
 gio/gsubprocess.h                   |   42 ++--
 gio/tests/gsubprocess.c             |   23 ++-
 6 files changed, 378 insertions(+), 368 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 546b036..3e3f7be 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -3911,10 +3911,10 @@ g_subprocess_set_child_setup
 g_subprocess_set_detached
 g_subprocess_setenv
 g_subprocess_unsetenv
-g_subprocess_override_environ
+g_subprocess_set_environment
 g_subprocess_set_io_priority
 g_subprocess_set_leave_descriptors_open
-g_subprocess_set_search_path
+g_subprocess_set_use_search_path
 g_subprocess_set_standard_error_file_path
 g_subprocess_set_standard_error_to_devnull
 g_subprocess_set_standard_error_to_stdout
@@ -3937,14 +3937,12 @@ g_subprocess_add_watch_full
 g_subprocess_run_sync
 g_subprocess_wait_sync
 <SUBSECTION Status>
-g_subprocess_get_exit_code
+g_subprocess_get_status_code
 g_subprocess_get_pid
 g_subprocess_query_success
 <SUBSECTION Utilities>
 g_subprocess_run_sync_get_output_bytes
 g_subprocess_run_sync_get_stdout_utf8
-g_subprocess_start_and_splice_async
-g_subprocess_start_and_splice_finish
 <SUBSECTION Standard>
 G_IS_SUBPROCESS
 G_TYPE_SUBPROCESS
diff --git a/gio/gio.symbols b/gio/gio.symbols
index e41ba9c..31fa26c 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1081,12 +1081,12 @@ g_subprocess_add_watch_full
 g_subprocess_append_arg
 g_subprocess_append_args
 g_subprocess_append_args_va
-g_subprocess_get_exit_code
+g_subprocess_get_status_code
 g_subprocess_get_pid
 g_subprocess_get_type
 g_subprocess_new
 g_subprocess_new_with_args
-g_subprocess_override_environ
+g_subprocess_set_environment
 g_subprocess_query_success
 g_subprocess_run_sync
 g_subprocess_run_sync_get_output_bytes
@@ -1098,7 +1098,7 @@ g_subprocess_set_detached
 g_subprocess_setenv
 g_subprocess_set_io_priority
 g_subprocess_set_leave_descriptors_open
-g_subprocess_set_search_path
+g_subprocess_set_use_search_path
 g_subprocess_set_standard_error_file_path
 g_subprocess_set_standard_error_to_devnull
 g_subprocess_set_standard_error_to_stdout
@@ -1114,8 +1114,6 @@ g_subprocess_set_standard_output_to_devnull
 g_subprocess_set_standard_output_unix_fd
 g_subprocess_set_working_directory
 g_subprocess_start
-g_subprocess_start_and_splice_async
-g_subprocess_start_and_splice_finish
 g_subprocess_start_with_pipes
 g_subprocess_unsetenv
 g_subprocess_wait_sync
diff --git a/gio/giotypes.h b/gio/giotypes.h
index e4f38ec..5c841b8 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -137,15 +137,6 @@ typedef struct _GPollableOutputStream         GPollableOutputStream; /* Dummy ty
 typedef struct _GResolver                     GResolver;
 
 /**
- * GSubprocess:
- *
- * A child process.
- *
- * Since: 2.34
- */
-typedef struct _GSubprocess                   GSubprocess;
-
-/**
  * GResource:
  *
  * A resource bundle.
@@ -477,6 +468,15 @@ typedef GType (*GDBusProxyTypeFunc) (GDBusObjectManagerClient   *manager,
 
 typedef struct _GTestDBus GTestDBus;
 
+/**
+ * GSubprocess:
+ *
+ * A child process.
+ *
+ * Since: 2.34
+ */
+typedef struct _GSubprocess                   GSubprocess;
+
 G_END_DECLS
 
 #endif /* __GIO_TYPES_H__ */
diff --git a/gio/gsubprocess.c b/gio/gsubprocess.c
index 8cb825c..b6c839e 100644
--- a/gio/gsubprocess.c
+++ b/gio/gsubprocess.c
@@ -39,6 +39,9 @@
 
 typedef struct _GSubprocessClass GSubprocessClass;
 
+static gpointer
+g_subprocess_waitpid_helper (gpointer data);
+
 struct _GSubprocessClass {
   GObjectClass parent_class;
 };
@@ -58,13 +61,13 @@ struct _GSubprocess
   GPtrArray *child_argv;
   gchar **child_envp;
 
-  gboolean detached : 1;
-  gboolean search_path : 1;
-  gboolean leave_descriptors_open : 1;
-  gboolean stdin_to_devnull : 1;
-  gboolean stdout_to_devnull : 1;
-  gboolean stderr_to_devnull : 1;
-  gboolean stderr_to_stdout : 1;
+  guint detached : 1;
+  guint search_path : 1;
+  guint leave_descriptors_open : 1;
+  guint stdin_to_devnull : 1;
+  guint stdout_to_devnull : 1;
+  guint stderr_to_devnull : 1;
+  guint stderr_to_stdout : 1;
 
   gint io_priority;
 
@@ -74,13 +77,16 @@ struct _GSubprocess
   gpointer             child_setup_user_data;
 
   gint stdin_fd;
+  gint internal_stdin_fd;
   gchar *stdin_path;
   GInputStream *stdin_stream;
 
   gchar *stdout_path;
   gint stdout_fd;
+  gint internal_stdout_fd;
   gchar *stderr_path;
   gint stderr_fd;
+  gint internal_stderr_fd;
 
   GError *internal_error;
 
@@ -89,7 +95,7 @@ struct _GSubprocess
 
   GPid pid;
 
-  int exit_status;
+  gint status_code;
 };
 
 G_DEFINE_TYPE (GSubprocess, g_subprocess, G_TYPE_OBJECT);
@@ -107,8 +113,11 @@ g_subprocess_init (GSubprocess  *self)
   self->child_argv = g_ptr_array_new_with_free_func (g_free);
   self->stdin_to_devnull = TRUE;
   self->stdin_fd = -1;
+  self->internal_stdin_fd = -1;
   self->stdout_fd = -1;
+  self->internal_stdout_fd = -1;
   self->stderr_fd = -1;
+  self->internal_stderr_fd = -1;
   self->io_priority = G_PRIORITY_DEFAULT;
 }
 
@@ -133,16 +142,15 @@ g_subprocess_finalize (GObject *object)
   if (self->state == G_SUBPROCESS_STATE_RUNNING
       && !self->detached)
     {
-      g_warning ("GSubprocess being finalized without child having been reaped; see g_subprocess_set_detached()");
 #ifdef G_OS_UNIX
-      {
-	int status;
-	/* We're forced to block the caller here to ensure we don't
-	 * accumulate zombies.
-	 */
-	waitpid (self->pid, &status, 0);
-      }
+      GThread *reaper_thread;
+
+      reaper_thread = g_thread_new ("GSubprocess wait",
+				    g_subprocess_waitpid_helper,
+				    GINT_TO_POINTER (self->pid));
+      g_thread_unref (reaper_thread);
 #endif
+      g_spawn_close_pid (self->pid);
     }
 
   if (self->child_argv)
@@ -221,7 +229,7 @@ g_subprocess_class_init (GSubprocessClass *class)
 
 /**
  * g_subprocess_new:
- * @executable: (array zero-terminated=1) (element-type guint8): Executable path
+ * @executable: (type filename): Executable path, in GLib filename encoding
  *
  * Creates a new subprocess, with @executable as the initial argument
  * vector.  You can append further arguments via
@@ -238,8 +246,8 @@ g_subprocess_new (const char *executable)
 
 /**
  * g_subprocess_new_with_args:
- * @executable: (array zero-terminated=1) (element-type guint8): Executable path
- * @...: a %NULL-terminated list of arguments
+ * @executable: (type filename): Executable path
+ * @...: a %NULL-terminated list of arguments, in the GLib filename encoding
  *
  * See the documentation of g_subprocess_append_args_va() for details
  * about child process arguments.
@@ -271,23 +279,16 @@ g_subprocess_new_with_args (const gchar     *executable,
 /**
  * g_subprocess_set_argv:
  * @self: a #GSubprocess:
- * @argv: (array zero-terminated=1): Argument array, %NULL-terminated
+ * @argv: (array zero-terminated=1) (element-type filename): Argument array, %NULL-terminated
  *
  * Set the arguments to be used by the child process.  This will
  * overwrite the entire argument vector, including the
- * #GSubprocess::executable property and any previous calls to
- * g_subprocess_append_args().
- *
- * Due to limitations of gobject-introspection, the provided argument
- * is annotated as an array of strings, not an array of bytestrings.
- *
- * For language bindings, you can use calls to
- * g_subprocess_append_arg() indivdually to provide non-UTF8
- * arguments.
+ * #GSubprocess:executable property, any previous calls to
+ * g_subprocess_append_args(), as well as g_subprocess_set_argv0().
  *
  * For more information, see g_subprocess_append_args_va().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -301,6 +302,8 @@ g_subprocess_set_argv (GSubprocess          *self,
   g_return_if_fail (*argv != NULL);
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
 
+  g_subprocess_set_argv0 (self, NULL);
+
   g_ptr_array_set_size (self->child_argv, 0);
   
   for (; *argv; argv++)
@@ -310,7 +313,7 @@ g_subprocess_set_argv (GSubprocess          *self,
 /**
  * g_subprocess_set_argv0:
  * @self: a #GSubprocess:
- * @argv0: (array zero-terminated=1) (element-type guint8): First argument
+ * @argv0: (type filename): First argument
  *
  * On Unix, child processes receive a bytestring provided by the
  * parent process as the first argument (i.e. argv[0]).  By default,
@@ -321,10 +324,7 @@ g_subprocess_set_argv (GSubprocess          *self,
  * executable which behaves differently when executed as 'grep' or
  * 'fgrep'.
  *
- * See the discussion of %G_SPAWN_FILE_AND_ARGV_ZERO in the
- * g_spawn_async_with_pipes() documentation.
- *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -343,13 +343,13 @@ g_subprocess_set_argv0 (GSubprocess         *self,
 /**
  * g_subprocess_append_arg:
  * @self: a #GSubprocess:
- * @arg: (array zero-terminated=1) (element-type guint8): Argument
+ * @arg: (type filename): Argument
  *
  * Append an argument to the child process.  On Unix, this argument is
  * a bytestring.  For more information, see
  * g_subprocess_append_args_va().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -368,13 +368,13 @@ g_subprocess_append_arg (GSubprocess       *self,
 /**
  * g_subprocess_append_args:
  * @self: a #GSubprocess:
- * @first: (array zero-terminated=1) (element-type guint8): First argument to be appended
+ * @first: (type filename): First argument to be appended
  * @...: a %NULL-terminated list of arguments
  *
  * Appends arguments to the child process.  For more information, see
  * g_subprocess_append_args_va().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -400,27 +400,22 @@ g_subprocess_append_args (GSubprocess       *self,
 /**
  * g_subprocess_append_args_va:
  * @self: a #GSubprocess
- * @args: List of %NULL-terminated arguments
+ * @args: %NULL-terminated list of arguments, in GLib filename encoding
  *
- * Append the provided @args to the child argument vector. 
+ * Append the provided @args to the child argument vector.  Each
+ * element should be in the GLib filename encoding.
  *
  * On Unix, the arguments are bytestrings.  Note though that while
  * it's possible to pass arbitrary bytestrings to subprocesses on
  * Unix, not all utility programs are written to handle this
- * correctly.  You should typically only pass strings which are valid
- * in the current locale encoding; see g_locale_to_utf8().
- *
- * On Windows, the arguments are in the GLib filename encoding
- * (i.e. UTF-8).
+ * correctly.
  *
- * Note that you do not need to include %NULL itself into the argument
- * array, as required by g_spawn_async_with_pipes(); it is inserted
- * automatically.
+ * On Windows, the filename encoding is UTF-8, so the arguments here
+ * should be as well.  For more information about how arguments are
+ * processed on Windows, see the documentation of
+ * g_spawn_async_with_pipes().
  *
- * For more information, about how arguments are processed, see the
- * documentation of g_spawn_async_with_pipes().
- *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -447,15 +442,15 @@ g_subprocess_append_args_va (GSubprocess       *self,
  * @self: a #GSubprocess
  * @detached: %TRUE if the child shouldn't be watched
  *
- * Unlike the g_spawn_async_with_pipes(), the default for #GSubprocess
- * is to assume that child exit status should be available; this
+ * Unlike g_spawn_async_with_pipes(), the default for #GSubprocess is
+ * to assume that child exit status should be available; this
  * corresponds to the %G_SPAWN_DO_NOT_REAP_CHILD flag.
  *
  * If you don't plan to monitor the child status via a function like
  * g_subprocess_add_watch() or g_subprocess_wait_sync(), you should
  * set this flag.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -471,21 +466,28 @@ g_subprocess_set_detached (GSubprocess     *self,
 }
 
 /**
- * g_subprocess_set_search_path:
+ * g_subprocess_set_use_search_path:
  * @self: a #GSubprocess
  * @do_search_path: %TRUE if the system path should be searched
  *
- * See the documentation for %G_SPAWN_SEARCH_PATH for details; setting
- * @do_search_path to %FALSE is equivalent to not providing that flag.
+ * By default, the name of the program must be a full path; the
+ * <envar>PATH</envar> shell variable will only be searched if you set
+ * @do_search_path to %TRUE.  If the program name is not a full path
+ * and this flag is not set, then the program will be run from the
+ * current directory (or @working_directory, if specified); this might
+ * be unexpected or even dangerous in some cases when the current
+ * directory is world-writable.
+ *
+ * This option corresponds to the %G_SPAWN_SEARCH_PATH flag.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
  */
 void
-g_subprocess_set_search_path (GSubprocess     *self,
-			      gboolean         do_search_path)
+g_subprocess_set_use_search_path (GSubprocess     *self,
+				  gboolean         do_search_path)
 {
   g_return_if_fail (G_IS_SUBPROCESS (self));
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
@@ -498,10 +500,19 @@ g_subprocess_set_search_path (GSubprocess     *self,
  * @self: a #GSubprocess
  * @do_leave_descriptors_open: %TRUE if file descriptors should be left open
  *
- * See the documentation for %G_SPAWN_LEAVE_DESCRIPTORS_OPEN for
- * details.
+ * Setting @do_leave_descriptors_open to %TRUE means that the parent's
+ * open file descriptors will be inherited by the child; otherwise all
+ * descriptors except stdin/stdout/stderr will be closed before
+ * calling exec() in the child.  The default is %FALSE.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * Note that on Unix, this option is completely independent of file
+ * descriptors which have the close-on-exec flag set.  In other words,
+ * if you set @do_leave_descriptors_open to %FALSE, descriptors which
+ * are close-on-exec will still be closed by the operating system.
+ *
+ * This option corresponds to the %G_SPAWN_LEAVE_DESCRIPTORS_OPEN flag.
+ *
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -516,7 +527,7 @@ g_subprocess_set_leave_descriptors_open (GSubprocess     *self,
   self->leave_descriptors_open = do_leave_descriptors_open;
 }
 
-/**** Envronment control ****/
+/**** Environment control ****/
 
 /**
  * g_subprocess_setenv:
@@ -535,7 +546,7 @@ g_subprocess_set_leave_descriptors_open (GSubprocess     *self,
  * of g_environ_setenv() for more information about environment
  * variables.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -567,7 +578,7 @@ g_subprocess_setenv (GSubprocess       *self,
  * g_environ_unsetenv() for more information about environment
  * variables.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  */
 void
@@ -578,11 +589,14 @@ g_subprocess_unsetenv (GSubprocess       *self,
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
   g_return_if_fail (variable != NULL);
 
+  if (self->child_envp == NULL)
+    self->child_envp = g_get_environ ();
+
   self->child_envp = g_environ_unsetenv (self->child_envp, variable);
 }
 
 /**
- * g_subprocess_override_environ:
+ * g_subprocess_set_environment:
  * @self: a #GSubprocess
  * @envp: (array zero-terminated=1): An environment list
  *
@@ -591,14 +605,14 @@ g_subprocess_unsetenv (GSubprocess       *self,
  * of g_environ_setenv() for more information about environment
  * variables.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
  */
 void
-g_subprocess_override_environ (GSubprocess       *self,
-			       gchar            **envp)
+g_subprocess_set_environment (GSubprocess       *self,
+			      gchar            **envp)
 {
   g_return_if_fail (G_IS_SUBPROCESS (self));
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
@@ -617,7 +631,7 @@ g_subprocess_override_environ (GSubprocess       *self,
  * directory of the current process.  This function allows
  * overriding that default.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  */
 void
@@ -641,7 +655,7 @@ g_subprocess_set_working_directory (GSubprocess       *self,
  * documentation of g_spawn_async_with_pipes() for more information
  * about @child_setup.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -672,7 +686,7 @@ g_subprocess_set_child_setup (GSubprocess           *self,
  * this function allows controlling the priority.  The default is
  * %G_PRIORITY_DEFAULT.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -702,7 +716,7 @@ g_subprocess_set_io_priority (GSubprocess       *self,
  * g_subprocess_set_standard_input_stream(), and
  * g_subprocess_set_standard_input_to_devnull().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -716,8 +730,7 @@ g_subprocess_set_standard_input_file_path (GSubprocess       *self,
   g_return_if_fail (file_path != NULL);
 
   g_clear_object (&self->stdin_stream);
-  g_free (self->stdin_path);
-  self->stdin_path = NULL;
+  g_clear_pointer (&self->stdin_path, g_free);
   self->stdin_to_devnull = FALSE;
   self->stdin_fd = -1;
 
@@ -736,11 +749,12 @@ g_subprocess_set_standard_input_file_path (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_input_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
  */
+#ifdef G_OS_UNIX
 void
 g_subprocess_set_standard_input_unix_fd (GSubprocess       *self,
 					 gint               fd)
@@ -749,13 +763,13 @@ g_subprocess_set_standard_input_unix_fd (GSubprocess       *self,
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
 
   g_clear_object (&self->stdin_stream);
-  g_free (self->stdin_path);
-  self->stdin_path = NULL;
+  g_clear_pointer (&self->stdin_path, g_free);
   self->stdin_to_devnull = FALSE;
   self->stdin_fd = -1;
 
   self->stdin_fd = fd;
 }
+#endif
 
 /**
  * g_subprocess_set_standard_input_to_devnull:
@@ -775,7 +789,7 @@ g_subprocess_set_standard_input_unix_fd (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_input_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -788,8 +802,7 @@ g_subprocess_set_standard_input_to_devnull (GSubprocess       *self,
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
 
   g_clear_object (&self->stdin_stream);
-  g_free (self->stdin_path);
-  self->stdin_path = NULL;
+  g_clear_pointer (&self->stdin_path, g_free);
   self->stdin_to_devnull = FALSE;
   self->stdin_fd = -1;
 
@@ -824,7 +837,7 @@ g_subprocess_set_standard_input_to_devnull (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_input_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -838,8 +851,7 @@ g_subprocess_set_standard_input_stream (GSubprocess       *self,
   g_return_if_fail (stream != NULL);
 
   g_clear_object (&self->stdin_stream);
-  g_free (self->stdin_path);
-  self->stdin_path = NULL;
+  g_clear_pointer (&self->stdin_path, g_free);
   self->stdin_to_devnull = FALSE;
   self->stdin_fd = -1;
 
@@ -859,7 +871,7 @@ g_subprocess_set_standard_input_stream (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_input_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -891,7 +903,7 @@ g_subprocess_set_standard_input_bytes (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_input_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -917,14 +929,14 @@ g_subprocess_set_standard_input_str (GSubprocess       *self,
  * @to_devnull: If %TRUE, redirect process output to null stream
  *
  * The default is for the child process to inherit the standard output
- * of the current process.  Specify %TRUE for @to_devnull to redirect it
- * to the operating system's null stream.
+ * of the current process.  Specify %TRUE for @to_devnull to redirect
+ * it to the operating system's null stream.
  *
  * Calling this function overrides any previous calls, as well as
  * other related functions such as
  * g_subprocess_set_standard_output_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -962,7 +974,7 @@ g_subprocess_set_standard_output_to_devnull (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_output_to_devnull().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -994,11 +1006,12 @@ g_subprocess_set_standard_output_file_path (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_output_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
  */
+#ifdef G_OS_UNIX
 void
 g_subprocess_set_standard_output_unix_fd (GSubprocess       *self,
 					  gint               fd)
@@ -1009,11 +1022,11 @@ g_subprocess_set_standard_output_unix_fd (GSubprocess       *self,
   g_free (self->stdout_path);
   self->stdout_path = NULL;
   self->stdout_to_devnull = FALSE;
-  self->stderr_to_stdout = FALSE;
   self->stdout_fd = -1;
 
   self->stdout_fd = fd;
 }
+#endif
 
 /**
  * g_subprocess_set_standard_error_to_devnull:
@@ -1028,7 +1041,7 @@ g_subprocess_set_standard_output_unix_fd (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_error_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -1040,8 +1053,7 @@ g_subprocess_set_standard_error_to_devnull (GSubprocess       *self,
   g_return_if_fail (G_IS_SUBPROCESS (self));
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
 
-  g_free (self->stderr_path);
-  self->stderr_path = NULL;
+  g_clear_pointer (&self->stderr_path, NULL);
   self->stderr_to_devnull = FALSE;
   self->stderr_to_stdout = FALSE;
   self->stderr_fd = -1;
@@ -1056,13 +1068,17 @@ g_subprocess_set_standard_error_to_devnull (GSubprocess       *self,
  *
  * The default is for the child process to inherit the standard error
  * of the current process.  Specify %TRUE for it to be merged with
- * standard output.
+ * standard output.  In this case, the disposition of standard output
+ * controls the merged stream; for example, if you combine
+ * g_subprocess_set_standard_error_to_stdout() and
+ * g_subprocess_set_standard_output_file_path(), the merged stream
+ * will be sent to the specified file.
  *
  * Calling this function overrides any previous calls, as well as
  * other related functions such as
  * g_subprocess_set_standard_error_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -1074,8 +1090,7 @@ g_subprocess_set_standard_error_to_stdout (GSubprocess       *self,
   g_return_if_fail (G_IS_SUBPROCESS (self));
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
 
-  g_free (self->stderr_path);
-  self->stderr_path = NULL;
+  g_clear_pointer (&self->stderr_path, NULL);
   self->stderr_to_devnull = FALSE;
   self->stderr_to_stdout = FALSE;
   self->stderr_fd = -1;
@@ -1097,7 +1112,7 @@ g_subprocess_set_standard_error_to_stdout (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_error_to_devnull().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -1109,8 +1124,7 @@ g_subprocess_set_standard_error_file_path (GSubprocess       *self,
   g_return_if_fail (G_IS_SUBPROCESS (self));
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
 
-  g_free (self->stderr_path);
-  self->stderr_path = NULL;
+  g_clear_pointer (&self->stderr_path, NULL);
   self->stderr_to_devnull = FALSE;
   self->stderr_to_stdout = FALSE;
   self->stderr_fd = -1;
@@ -1130,7 +1144,7 @@ g_subprocess_set_standard_error_file_path (GSubprocess       *self,
  * other related functions such as
  * g_subprocess_set_standard_error_file_path().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * been called.
  *
  * Since: 2.34
@@ -1142,8 +1156,7 @@ g_subprocess_set_standard_error_unix_fd (GSubprocess       *self,
   g_return_if_fail (G_IS_SUBPROCESS (self));
   g_return_if_fail (self->state == G_SUBPROCESS_STATE_BUILDING);
 
-  g_free (self->stderr_path);
-  self->stderr_path = NULL;
+  g_clear_pointer (&self->stderr_path, NULL);
   self->stderr_to_devnull = FALSE;
   self->stderr_to_stdout = FALSE;
   self->stderr_fd = -1;
@@ -1157,14 +1170,15 @@ g_subprocess_set_standard_error_unix_fd (GSubprocess       *self,
  * @cancellable: a #GCancellable
  * @error: a #GError
  *
- * When called, this runs the child process asynchronously using
- * the current configuration.  You must have at least initialized
- * an argument vector using g_subprocess_append_args() or a related
- * function.
+ * When called, this runs the child process asynchronously using the
+ * current configuration.  This function is equivalent to calling
+ * g_subprocess_start_with_pipes() with %NULL streams.  See the
+ * documentation of that function for more information.
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * already been called.
  *
+ * Returns: %TRUE if subprocess was started, %FALSE on error (and @error will be set)
  * Since: 2.34
  */
 gboolean
@@ -1182,19 +1196,18 @@ g_subprocess_start (GSubprocess       *self,
  * @cancellable: a #GCancellable
  * @error: a #GError
  *
- * When called, this runs the child process synchronously using
- * the current configuration.  You must have at least initialized
- * an argument vector using g_subprocess_append_args() or a related
- * function.
+ * When called, this runs the child process synchronously using the
+ * current configuration.
  *
  * This function simply wraps g_subprocess_start() and
  * g_subprocess_wait_sync().  See the documentation for both of those,
  * as well as g_subprocess_query_success().
  *
- * It invalid to call this function after g_subprocess_start() has
+ * It is invalid to call this function after g_subprocess_start() has
  * already been called.
  *
  * Since: 2.34
+ * Returns: %TRUE if subprocess was executed successfully, %FALSE on error (and @error will be set)
  */
 gboolean
 g_subprocess_run_sync (GSubprocess   *self,
@@ -1218,15 +1231,37 @@ g_subprocess_run_sync (GSubprocess   *self,
 
 #ifdef G_OS_UNIX
 
+/*
+ * This function is called in the case where the caller didn't
+ * explicitly use _set_detached() or watch the child explicitly via
+ * g_subprocess_add_watch().
+ *
+ * We don't want to block the calling thread in waitpid(), so
+ * move it to a thread.
+ *
+ * For more efficiency we could move this to the GLib worker thread,
+ * but callers should really know to use one of _set_detached() or
+ * _add_watch().
+ */
+static gpointer
+g_subprocess_waitpid_helper (gpointer data)
+{
+  GPid pid = GPOINTER_TO_INT (data);
+  gint status;
+
+  waitpid (pid, &status, 0);
+
+  return NULL;
+}
+
 static inline void
-safe_dup2 (int   a,
-	   int   b)
+safe_dup2 (gint   a,
+	   gint   b)
 {
-  int ecode;
+  gint ecode;
   do
-    {
-      ecode = dup2 (a, b);
-    } while (ecode == -1 && errno == EINTR);
+    ecode = dup2 (a, b);
+  while (ecode == -1 && errno == EINTR);
 }
 
 static void
@@ -1236,10 +1271,19 @@ g_subprocess_internal_child_setup (gpointer        user_data)
 
   if (self->stdin_fd >= 0)
     safe_dup2 (self->stdin_fd, 0);
+  else if (self->internal_stdin_fd >= 0)
+    safe_dup2 (self->internal_stdin_fd, 0);
+
   if (self->stdout_fd >= 0)
     safe_dup2 (self->stdout_fd, 1);
+  else if (self->internal_stdout_fd >= 0)
+    safe_dup2 (self->internal_stdout_fd, 1);
+
   if (self->stderr_fd >= 0)
     safe_dup2 (self->stderr_fd, 2);
+  else if (self->internal_stderr_fd >= 0)
+    safe_dup2 (self->internal_stderr_fd, 2);
+
   if (self->stderr_to_stdout)
     safe_dup2 (1, 2);
 
@@ -1294,12 +1338,10 @@ g_subprocess_on_input_splice_finished (GObject      *src,
  *
  * When called, this runs the child process asynchronously using the
  * current configuration, with pipes connected to any of standard
- * input, standard output and standard error.  You must have at least
- * initialized an argument vector using g_subprocess_append_args() or
- * a related function.
+ * input, standard output and standard error.
  *
  * See the documentation for g_spawn_async_with_pipes() for more
- * information about how the child process has been run.
+ * information about how the child process will be run.
  *
  * <warning>If the process is not in detached mode via
  * g_subprocess_set_detached(), you must create a watch for it using
@@ -1308,13 +1350,23 @@ g_subprocess_on_input_splice_finished (GObject      *src,
  * to suboptimal behavior.</warning>
  *
  * If both input and output streams are given, using synchronous I/O
- * on both sides risks deadlock.  Use asynchronous I/O such as
- * g_input_stream_read_async() or higher level wrappers.
+ * from the same thread on both sides risks deadlock.  Use
+ * asynchronous I/O such as g_input_stream_read_async() or higher
+ * level wrappers.
  *
- * It invalid to call this function after
- * g_subprocess_start_with_pipes() has already been called.
+ * The provided @cancellable controls internal input splicing.
+ * Specifically, if an input stream has been provided to the process
+ * via g_subprocess_set_standard_input_stream() or a wrapper such as
+ * g_subprocess_set_standard_input_bytes(), then cancelling
+ * @cancellable will stop I/O.  Because input I/O errors will be
+ * returned from g_subprocess_query_success(), the returned error in
+ * that case will be %G_IO_ERROR_CANCELLED.
+ *
+ * It is invalid to call this function after
+ * g_subprocess_start() has already been called.
  *
  * Since: 2.34
+ * Returns: %TRUE if subprocess was started, %FALSE on error (and @error will be set)
  */
 gboolean
 g_subprocess_start_with_pipes (GSubprocess       *self,
@@ -1366,8 +1418,8 @@ g_subprocess_start_with_pipes (GSubprocess       *self,
 #ifdef G_OS_UNIX
   if (self->stdin_path)
     {
-      self->stdin_fd = g_open (self->stdin_path, O_RDONLY);
-      if (self->stdin_fd < 0)
+      self->internal_stdin_fd = g_open (self->stdin_path, O_RDONLY);
+      if (self->internal_stdin_fd < 0)
 	{
 	  g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
 		       _("Failed to open file '%s'"), self->stdin_path);
@@ -1378,7 +1430,7 @@ g_subprocess_start_with_pipes (GSubprocess       *self,
     }
   if (self->stdout_path)
     {
-      self->stdout_fd = g_open (self->stdout_path, O_CREAT | O_APPEND | O_WRONLY,
+      self->internal_stdout_fd = g_open (self->stdout_path, O_CREAT | O_APPEND | O_WRONLY,
 				0666);
       if (self->stdout_fd < 0)
 	{
@@ -1386,12 +1438,11 @@ g_subprocess_start_with_pipes (GSubprocess       *self,
 		       _("Failed to open file '%s'"), self->stdout_path);
 	  goto out;
 	}
-      g_free (self->stdout_path);
-      self->stdout_path = NULL;
+      g_clear_pointer (&self->stdout_path, NULL);
     }
   if (self->stderr_path)
     {
-      self->stderr_fd = g_open (self->stderr_path, O_CREAT | O_APPEND | O_WRONLY,
+      self->internal_stderr_fd = g_open (self->stderr_path, O_CREAT | O_APPEND | O_WRONLY,
 				0666);
       if (self->stderr_fd < 0)
 	{
@@ -1399,8 +1450,7 @@ g_subprocess_start_with_pipes (GSubprocess       *self,
 		       _("Failed to open file '%s'"), self->stdout_path);
 	  goto out;
 	}
-      g_free (self->stderr_path);
-      self->stderr_path = NULL;
+      g_clear_pointer (&self->stderr_path, NULL);
     }
 #else
   if (self->stdin_path)
@@ -1550,6 +1600,14 @@ g_subprocess_start_with_pipes (GSubprocess       *self,
  out:
   if (tmp_argv)
     g_ptr_array_unref (tmp_argv);
+#ifdef G_OS_UNIX
+  if (self->internal_stdin_fd >= 0)
+    (void) close (self->internal_stdin_fd);
+  if (self->internal_stdout_fd >= 0)
+    (void) close (self->internal_stdout_fd);
+  if (self->internal_stderr_fd >= 0)
+    (void) close (self->internal_stderr_fd);
+#endif
   return ret;
 }
 
@@ -1561,12 +1619,14 @@ g_subprocess_start_with_pipes (GSubprocess       *self,
  * not be used if g_subprocess_set_detached() has been called.
  *
  * <warning>Due to the way GLib presently implements subprocess
- * monitoring works on Unix, attempting to use e.g. kill() to send a
- * signal to the child process has a race condition where the child
- * process may not exist.</warning>
+ * monitoring on Unix, attempting to use e.g. kill() to send a signal
+ * to the child process has a race condition where the child process
+ * may not exist.</warning>
  *
- * This function can only be called when the process has been started
- * via g_subprocess_start() or a related function.
+ * This function can only be called when the process is currently
+ * running.  In particular, do not call this function after a callback
+ * from a child watch such as g_subprocess_add_watch() has been
+ * invoked.
  *
  * Since: 2.34
  */
@@ -1574,8 +1634,7 @@ GPid
 g_subprocess_get_pid (GSubprocess     *self)
 {
   g_return_val_if_fail (G_IS_SUBPROCESS (self), 0);
-  g_return_val_if_fail (self->state == G_SUBPROCESS_STATE_RUNNING
-			|| self->state == G_SUBPROCESS_STATE_TERMINATED, 0);
+  g_return_val_if_fail (self->state == G_SUBPROCESS_STATE_RUNNING, 0);
   g_return_val_if_fail (!self->detached, 0);
   
   return self->pid;
@@ -1587,9 +1646,10 @@ g_subprocess_get_pid (GSubprocess     *self)
  * @function: Callback invoked when child has exited
  * @user_data: Data for @function
  *
- * This function is similar to using g_child_watch_add(), except it
- * operates on the thread default main context.  See
- * g_main_context_get_thread_default().
+ * This function creates a source via g_subprocess_create_source() and
+ * attaches it the to the <link
+ * linkend="g-main-context-push-thread-default">thread-default main
+ * loop</link>.
  *
  * For more information, see the documentation of
  * g_subprocess_add_watch_full().
@@ -1614,12 +1674,12 @@ typedef struct {
 
 static void
 g_subprocess_child_watch_func (GPid       pid,
-			       gint       exit_status,
+			       gint       status_code,
 			       gpointer   user_data)
 {
   GSubprocessWatchTrampolineData *data = user_data;
 
-  data->self->exit_status = exit_status;
+  data->self->status_code = status_code;
   if (data->self->state != G_SUBPROCESS_STATE_TERMINATED)
     {
       g_assert (data->self->state == G_SUBPROCESS_STATE_RUNNING);
@@ -1651,9 +1711,10 @@ g_subprocess_trampoline_data_destroy (gpointer user_data)
  * @user_data: Data for @function
  * @notify: Destroy notify
  *
- * This function is similar to using g_child_watch_add_full(), except
- * it operates on the thread default main context.  See
- * g_main_context_get_thread_default().
+ * This function creates a source via g_subprocess_create_source() and
+ * attaches it the to the <link
+ * linkend="g-main-context-push-thread-default">thread-default main
+ * loop</link>.
  *
  * Inside the callback, you should call either
  * g_subprocess_query_success() or g_subprocess_get_exit_code() to
@@ -1673,6 +1734,39 @@ g_subprocess_add_watch_full (GSubprocess             *self,
 			     GDestroyNotify           notify)
 {
   GSource *source;
+
+  source = g_subprocess_create_source (self, priority, function, user_data, notify);
+  g_source_attach (source, g_main_context_get_thread_default ());
+
+  return source;
+}
+
+/**
+ * g_subprocess_create_source:
+ * @self: a #GSubprocess
+ * @priority: I/O priority
+ * @function: Callback invoked when child has exited
+ * @user_data: Data for @function
+ * @notify: Destroy notify
+ *
+ * This function is similar to g_child_watch_source_new(), except the
+ * callback signature includes the subprocess @self, and status is
+ * accessed via g_subprocess_query_success().
+ *
+ * This function may not be used if g_subprocess_set_detached() has
+ * been called.
+ *
+ * Returns: (transfer full): A newly-created #GSource for this process
+ * Since: 2.34
+ */
+GSource *
+g_subprocess_create_source (GSubprocess             *self,
+			    gint                     priority,
+			    GSubprocessWatchFunc     function,
+			    gpointer                 user_data,
+			    GDestroyNotify           notify)
+{
+  GSource *source;
   GSubprocessWatchTrampolineData *trampoline_data;
 
   g_return_val_if_fail (G_IS_SUBPROCESS (self), 0);
@@ -1688,7 +1782,6 @@ g_subprocess_add_watch_full (GSubprocess             *self,
   trampoline_data->notify = notify;
   g_source_set_callback (source, (GSourceFunc)g_subprocess_child_watch_func, trampoline_data,
 			 g_subprocess_trampoline_data_destroy);
-  g_source_attach (source, g_main_context_get_thread_default ());
 
   return source;
 }
@@ -1698,9 +1791,11 @@ g_subprocess_add_watch_full (GSubprocess             *self,
  * @self: a #GSubprocess
  * @error: a #GError
  *
- * Unlike the g_spawn_async_with_pipes() family of functions, this
- * function by default sets an error if the child exits abnormally
- * (e.g. with a nonzero exit code, or via a fatal signal).
+ * This function sets @error based on the exit code if the child exits
+ * abnormally (e.g. with a nonzero exit code, or via a fatal signal).
+ * This contrasts with the lower-level GLib API of g_child_watch_add()
+ * where callers must use platform-specific macros such as the Unix
+ * WIFEXITED() macro on the exit code.
  *
  * In the case where the child exits abnormally, the resulting @error
  * will have domain %G_IO_ERROR, code %G_IO_ERROR_SUBPROCESS_EXIT_ABNORMAL.
@@ -1734,28 +1829,28 @@ g_subprocess_query_success (GSubprocess   *self,
     }
 
 #ifdef G_OS_UNIX
-  if (WIFEXITED (self->exit_status))
+  if (WIFEXITED (self->status_code))
     {
-      if (WEXITSTATUS (self->exit_status) != 0)
+      if (WEXITSTATUS (self->status_code) != 0)
 	{
 	  g_set_error (error, G_IO_ERROR, G_IO_ERROR_SUBPROCESS_EXIT_ABNORMAL,
 		       _("Child process %ld exited with code %ld"),
-		       (long) self->pid, (long) self->exit_status);
+		       (long) self->pid, (long) self->status_code);
 	  goto out;
 	}
     }
-  else if (WIFSIGNALED (self->exit_status))
+  else if (WIFSIGNALED (self->status_code))
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_SUBPROCESS_EXIT_ABNORMAL,
 		   _("Child process %ld killed by signal %ld"),
-		   (long) self->pid, (long) WTERMSIG (self->exit_status));
+		   (long) self->pid, (long) WTERMSIG (self->status_code));
       goto out;
     }
-  else if (WIFSTOPPED (self->exit_status))
+  else if (WIFSTOPPED (self->status_code))
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_SUBPROCESS_EXIT_ABNORMAL,
 		   _("Child process %ld stopped by signal %ld"),
-		   (long) self->pid, (long) WSTOPSIG (self->exit_status));
+		   (long) self->pid, (long) WSTOPSIG (self->status_code));
       goto out;
     }
   else
@@ -1766,7 +1861,7 @@ g_subprocess_query_success (GSubprocess   *self,
       goto out;
     }
 #else
-  if (self->exit_status != 0)
+  if (self->status_code != 0)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_SUBPROCESS_EXIT_ABNORMAL,
 		   _("Child process %ld exited abnormally"),
@@ -1781,13 +1876,14 @@ g_subprocess_query_success (GSubprocess   *self,
 }
 
 /**
- * g_subprocess_get_exit_code:
+ * g_subprocess_get_status_code:
  * @self: a #GSubprocess
  *
- * Returns the exit code from the process, in the same form as
- * provided by g_spawn_async_with_pipes().  In the typical case where
- * you simply want to print an error message if the child exited
- * abnormally, use g_subprocess_query_success().
+ * Returns an integer with platform-specific semantics representing
+ * the process status, in the same form as provided by
+ * g_spawn_async_with_pipes().  In the typical case where you simply
+ * want an error set if the child exited abnormally, use
+ * g_subprocess_query_success().
  *
  * It is invalid to call this function unless the child has actually
  * terminated.  You can wait for the child to exit via
@@ -1798,13 +1894,13 @@ g_subprocess_query_success (GSubprocess   *self,
  * Since: 2.34
  */
 gint
-g_subprocess_get_exit_code (GSubprocess   *self)
+g_subprocess_get_status_code (GSubprocess   *self)
 {
   g_return_val_if_fail (G_IS_SUBPROCESS (self), FALSE);
   g_return_val_if_fail (!self->detached, FALSE);
   g_return_val_if_fail (self->state == G_SUBPROCESS_STATE_TERMINATED, FALSE);
 
-  return self->exit_status;
+  return self->status_code;
 }
 
 static void
@@ -1824,9 +1920,10 @@ g_subprocess_on_sync_watch (GSubprocess   *self,
  *
  * Synchronously wait for the subprocess to terminate.  This function
  * will also invoke g_subprocess_query_success(), meaning that by
- * default @error will be set if the subprocess exits abornmally.
+ * default @error will be set if the subprocess exits abnormally.
  * 
- * Returns: %TRUE if child exited successfully, %FALSE otherwise
+ * Returns: %TRUE if child exited successfully, %FALSE on
+ *   non-successful status or @cancellable was cancelled
  * Since: 2.34
  */
 gboolean
@@ -1839,17 +1936,24 @@ g_subprocess_wait_sync (GSubprocess        *self,
   GMainContext *context = NULL;
   GMainLoop *loop = NULL;
   GSource *source = NULL;
+  GSource *cancellable_source = NULL;
 
   g_return_val_if_fail (G_IS_SUBPROCESS (self), FALSE);
   g_return_val_if_fail (!self->detached, FALSE);
   g_return_val_if_fail (self->state == G_SUBPROCESS_STATE_RUNNING, FALSE);
 
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+
   context = g_main_context_new ();
   g_main_context_push_thread_default (context);
   pushed_thread_default = TRUE;
   loop = g_main_loop_new (context, TRUE);
   
   source = g_subprocess_add_watch (self, g_subprocess_on_sync_watch, loop);
+  cancellable_source = g_cancellable_source_new (cancellable);
+  g_source_add_child_source (source, cancellable_source);
+  g_source_unref (cancellable_source);
 
   g_main_loop_run (loop);
 
@@ -1874,161 +1978,28 @@ g_subprocess_wait_sync (GSubprocess        *self,
 
 typedef struct {
   GSubprocess *self;
-  GSimpleAsyncResult *res;
   gboolean caught_error;
   GError *error;
+  GMainLoop *loop;
   guint events_needed;
-} GSubprocessSpliceData;
+} GSubprocessRunSyncGetOutputData;
 
 static void
-g_subprocess_on_splice_finished (GObject       *obj,
-				 GAsyncResult  *res,
-				 gpointer       user_data)
+g_subprocess_on_get_output_splice_done (GObject      *obj,
+					GAsyncResult *res,
+					gpointer      user_data)
 {
-  GSubprocessSpliceData *data = user_data;
+  GSubprocessRunSyncGetOutputData *data = user_data;
 
   if (!data->caught_error)
     {
-      if (g_output_stream_splice_finish ((GOutputStream*)obj, res, &data->error) < 0)
+      if (!g_output_stream_splice_finish ((GOutputStream*)obj, res, &data->error))
 	data->caught_error = TRUE;
     }
-
+  
   data->events_needed--;
   if (data->events_needed == 0)
-    {
-      if (data->caught_error)
-	g_simple_async_result_take_error (data->res, data->error);
-      else
-	g_simple_async_result_set_op_res_gboolean (data->res, TRUE);
-      
-      g_simple_async_result_complete (data->res);
-      
-      g_object_unref (data->res);
-
-      g_object_unref (data->self);
-      g_free (data);
-    }
-}
-
-/**
- * g_subprocess_start_and_splice_async:
- * @self: a #GSubprocess
- * @flags: Flags for splicing
- * @stdout_splice: Transfer data from stdout into this stream
- * @stderr_splice: Transfer data from stderr into this stream
- * @cancellable: a #GCancellable
- * @callback: Callback invoked when all data has been transferred
- * @user_data: User data for @callback
- *
- * Start the process, asynchronously transferring data from child
- * standard output and/or standard error into the provided streams.
- * The @callback will be invoked when the child closes the streams,
- * not when it terminates.
- *
- * If you want to query the child exit status, you need to use a
- * function like g_subprocess_add_watch() to be notified when it exits
- * as well.  Or, if you just want the output, use
- * g_subprocess_set_detached().
- *
- * Since: 2.34
- */
-void
-g_subprocess_start_and_splice_async (GSubprocess               *self,
-				     GOutputStreamSpliceFlags   flags,
-				     GOutputStream             *stdout_splice,
-				     GOutputStream             *stderr_splice,
-				     GCancellable              *cancellable,
-				     GAsyncReadyCallback        callback,
-				     gpointer                   user_data)
-{
-  GSubprocessSpliceData *op_data;
-  GSimpleAsyncResult *res;
-  GError *local_error = NULL;
-  GInputStream *subproc_stdout = NULL;
-  GInputStream *subproc_stderr = NULL;
-
-  if (!g_subprocess_start_with_pipes (self, NULL, stdout_splice ? &subproc_stdout : NULL,
-				      stderr_splice ? &subproc_stderr : NULL,
-				      cancellable, &local_error))
-    {
-      res = g_simple_async_result_new_take_error ((GObject*)self, callback, user_data,
-						  local_error);
-      g_simple_async_result_complete_in_idle (res);
-      return;
-    }
-
-  op_data = g_new0 (GSubprocessSpliceData, 1);
-  op_data->self = g_object_ref (self);
-  op_data->res = g_simple_async_result_new ((GObject*)self,
-					    callback, user_data,
-					    g_subprocess_start_and_splice_async);
-
-  if (stdout_splice)
-    {
-      g_output_stream_splice_async (stdout_splice, subproc_stdout, flags,
-				    self->io_priority, cancellable,
-				    g_subprocess_on_splice_finished, op_data);
-      g_object_unref (subproc_stdout);
-      op_data->events_needed++;
-    }
-
-  if (stderr_splice)
-    {
-      g_output_stream_splice_async (stderr_splice, subproc_stderr, flags,
-				    self->io_priority, cancellable,
-				    g_subprocess_on_splice_finished, op_data);
-      g_object_unref (subproc_stderr);
-      op_data->events_needed++;
-    }
-}
-
-/**
- * g_subprocess_start_and_splice_finish:
- * @self: a #GSubprocess
- * @result: a #GAsyncResult
- * @error: a #GError
- *
- * Complete an asynchronous splice operation.  There is at present no
- * way to query the number of bytes written.
- *
- * Returns: %TRUE if the operation was successful, %FALSE otherwise
- * Since: 2.34
- */
-gboolean
-g_subprocess_start_and_splice_finish (GSubprocess               *self,
-				      GAsyncResult              *result,
-				      GError                   **error)
-{
-  GSimpleAsyncResult *simple;
-
-  g_return_val_if_fail (G_IS_SUBPROCESS (self), FALSE);
-  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
-
-  if (G_IS_SIMPLE_ASYNC_RESULT (result))
-    {
-      simple = (GSimpleAsyncResult*) result;
-      if (g_simple_async_result_propagate_error (simple, error))
-	return FALSE;
-    }
-  return TRUE;
-}
-
-typedef struct {
-  GSubprocess *self;
-  GError **error;
-  GMainLoop *loop;
-} GSubprocessRunSyncGetOutputData;
-
-static void
-g_subprocess_on_get_output_splice_done (GObject      *obj,
-					GAsyncResult *res,
-					gpointer      user_data)
-{
-  GSubprocessRunSyncGetOutputData *data = user_data;
-
-  g_subprocess_start_and_splice_finish (data->self, res, data->error);
-
-  g_main_loop_quit (data->loop);
+    g_main_loop_quit (data->loop);
 }
 
 static gboolean
@@ -2043,6 +2014,8 @@ run_sync_get_output_membufs (GSubprocess               *self,
   GSubprocessRunSyncGetOutputData data;
   gboolean pushed_thread_default;
   GMainContext *context = NULL;
+  GInputStream *subproc_stdout = NULL;
+  GInputStream *subproc_stderr = NULL;
   GOutputStream *stdout_membuf = NULL;
   GOutputStream *stderr_membuf = NULL;
 
@@ -2052,7 +2025,7 @@ run_sync_get_output_membufs (GSubprocess               *self,
     stderr_membuf = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
 
   memset (&data, 0, sizeof (data));
-  data.error = error;
+  data.error = NULL;
   data.self = self;
   
   context = g_main_context_new ();
@@ -2060,13 +2033,37 @@ run_sync_get_output_membufs (GSubprocess               *self,
   pushed_thread_default = TRUE;
   data.loop = g_main_loop_new (context, TRUE);
 
-  g_subprocess_start_and_splice_async (self, flags, stdout_membuf,
-				       stderr_membuf, cancellable,
-				       g_subprocess_on_get_output_splice_done,
-				       &data);
+  if (!g_subprocess_start_with_pipes (self, NULL, out_stdout_buf ? &subproc_stdout : NULL,
+				      out_stderr_buf ? &subproc_stderr : NULL,
+				      cancellable, error))
+    goto out;
+
+  if (subproc_stdout)
+    {
+      g_output_stream_splice_async (stdout_membuf, subproc_stdout, flags,
+				    self->io_priority, cancellable,
+				    g_subprocess_on_get_output_splice_done, &data);
+      g_object_unref (subproc_stdout);
+      data.events_needed++;
+    }
+
+  if (subproc_stderr)
+    {
+      g_output_stream_splice_async (stderr_membuf, subproc_stderr, flags,
+				    self->io_priority, cancellable,
+				    g_subprocess_on_get_output_splice_done, &data);
+      g_object_unref (subproc_stderr);
+      data.events_needed++;
+    }
 
   g_main_loop_run (data.loop);
 
+  if (data.caught_error)
+    {
+      g_propagate_error (error, data.error);
+      goto out;
+    }
+
   /* Note minor optimization opportunity: we create two main loops
    * presently */
   if (!g_subprocess_wait_sync (self, cancellable, error))
@@ -2110,9 +2107,9 @@ run_sync_get_output_membufs (GSubprocess               *self,
  *
  * If @error is set for any reason (including the child exiting
  * unsuccessfully), then @out_stdout_bytes and @out_stderr_bytes will
- * be left uninitialized!  If you want to run the child process,
+ * be left uninitialized.  If you want to run the child process while
  * gathering output regardless of exit status, you should use the
- * lower-level function g_subprocess_start_and_splice_async().
+ * lower-level function g_subprocess_start_with_pipes().
  *
  * Returns: %TRUE if child process exited, successfully %FALSE otherwise
  * Since: 2.34
diff --git a/gio/gsubprocess.h b/gio/gsubprocess.h
index 82545cb..f542a95 100644
--- a/gio/gsubprocess.h
+++ b/gio/gsubprocess.h
@@ -35,6 +35,7 @@ G_BEGIN_DECLS
 #define G_SUBPROCESS(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SUBPROCESS, GSubprocess))
 #define G_IS_SUBPROCESS(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SUBPROCESS))
 
+GLIB_AVAILABLE_IN_2_34
 GType            g_subprocess_get_type (void) G_GNUC_CONST;
 
 /**** Creation ****/
@@ -69,15 +70,15 @@ GLIB_AVAILABLE_IN_2_34
 void             g_subprocess_append_args_va (GSubprocess       *self,
 					      va_list            args);
 
-/**** Envronment control ****/
+/**** Environment control ****/
 
 GLIB_AVAILABLE_IN_2_34
 void             g_subprocess_set_detached (GSubprocess     *self,
 					    gboolean         detached);
 
 GLIB_AVAILABLE_IN_2_34
-void             g_subprocess_set_search_path (GSubprocess     *self,
-					       gboolean         do_search_path);
+void             g_subprocess_set_use_search_path (GSubprocess     *self,
+						   gboolean         do_search_path);
 
 GLIB_AVAILABLE_IN_2_34
 void             g_subprocess_set_leave_descriptors_open (GSubprocess     *self,
@@ -94,12 +95,12 @@ void             g_subprocess_unsetenv (GSubprocess       *self,
 					const gchar       *variable);
 
 GLIB_AVAILABLE_IN_2_34
-void             g_subprocess_override_environ (GSubprocess       *self,
-						gchar            **envp);
+void             g_subprocess_set_environment (GSubprocess       *self,
+					       gchar            **new_envp);
 
 GLIB_AVAILABLE_IN_2_34
 void             g_subprocess_set_working_directory (GSubprocess       *self,
-						     const gchar       *working_directory);
+        					     const gchar       *working_directory);
 
 GLIB_AVAILABLE_IN_2_34
 void             g_subprocess_set_child_setup (GSubprocess           *self,
@@ -208,11 +209,24 @@ GSource *        g_subprocess_add_watch_full (GSubprocess                  *self
 					      GDestroyNotify                notify);
 
 GLIB_AVAILABLE_IN_2_34
+GSource *        g_subprocess_create_source (GSubprocess                  *self,
+					     gint                          priority,
+					     GSubprocessWatchFunc          function,
+					     gpointer                      user_data,
+					     GDestroyNotify                notify);
+
+GLIB_AVAILABLE_IN_2_34
 gboolean         g_subprocess_query_success (GSubprocess   *self,
 					     GError       **error);
 
 GLIB_AVAILABLE_IN_2_34
-gint             g_subprocess_get_exit_code (GSubprocess   *self);
+gint             g_subprocess_get_status_code (GSubprocess   *self);
+
+GLIB_AVAILABLE_IN_2_34
+gint             g_subprocess_get_unix_exit_status (GSubprocess   *self,
+						    gboolean      *out_exited,
+						    gboolean      *out_signaled,
+						    gint          *out_code);
 
 GLIB_AVAILABLE_IN_2_34
 gboolean         g_subprocess_wait_sync (GSubprocess   *self,
@@ -222,20 +236,6 @@ gboolean         g_subprocess_wait_sync (GSubprocess   *self,
 /**** High level wrapers ****/
 
 GLIB_AVAILABLE_IN_2_34
-void             g_subprocess_start_and_splice_async (GSubprocess               *self,
-						      GOutputStreamSpliceFlags   flags,
-						      GOutputStream             *stdout_splice,
-						      GOutputStream             *stderr_splice,
-						      GCancellable              *cancellable,
-						      GAsyncReadyCallback        callback,
-						      gpointer                   user_data);
-
-GLIB_AVAILABLE_IN_2_34
-gboolean         g_subprocess_start_and_splice_finish (GSubprocess               *self,
-						       GAsyncResult              *result,
-						       GError                   **error);
-
-GLIB_AVAILABLE_IN_2_34
 gboolean         g_subprocess_run_sync_get_output_bytes (GSubprocess          *self,
 							 GBytes              **out_stdout_bytes,
 							 GBytes              **out_stderr_bytes,
diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c
index 1ee0684..0f8e8d7 100644
--- a/gio/tests/gsubprocess.c
+++ b/gio/tests/gsubprocess.c
@@ -72,6 +72,22 @@ test_noop_detached (void)
   g_object_unref (proc);
 }
 
+static void
+test_noop_non_detached (void)
+{
+  GError *local_error = NULL;
+  GError **error = &local_error;
+  GSubprocess *proc;
+
+  proc = get_test_subprocess ("noop");
+
+  (void)g_subprocess_start (proc, NULL, error);
+  g_assert_no_error (local_error);
+  
+  g_object_unref (proc);
+}
+
+
 #ifdef G_OS_UNIX
 static void
 test_search_path (void)
@@ -82,7 +98,7 @@ test_search_path (void)
 
   proc = g_subprocess_new ("true");
 
-  g_subprocess_set_search_path (proc, TRUE);
+  g_subprocess_set_use_search_path (proc, TRUE);
 
   (void)g_subprocess_run_sync (proc, NULL, error);
   g_assert_no_error (local_error);
@@ -106,8 +122,8 @@ test_exit1 (void)
 
 #ifdef G_OS_UNIX
   {
-    int ecode = g_subprocess_get_exit_code (proc);
-    g_assert (WIFEXITED (ecode) && WEXITSTATUS (ecode) == 1);
+    int scode = g_subprocess_get_status_code (proc);
+    g_assert (WIFEXITED (scode) && WEXITSTATUS (scode) == 1);
   }
 #endif
 
@@ -393,6 +409,7 @@ main (int argc, char **argv)
   g_test_add_func ("/gsubprocess/noop", test_noop);
   g_test_add_func ("/gsubprocess/noop-all-to-null", test_noop_all_to_null);
   g_test_add_func ("/gsubprocess/noop-detached", test_noop_detached);
+  g_test_add_func ("/gsubprocess/noop-nondetached", test_noop_non_detached);
 #ifdef G_OS_UNIX
   g_test_add_func ("/gsubprocess/search-path", test_search_path);
 #endif



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