[glib: 3/7] gdesktopappinfo: enable fast posix_spawn gspawn codepath



commit 742efe6232aba81c2c52c229c900a57ec2afedfd
Author: Daniel Drake <drake endlessm com>
Date:   Mon Jun 4 19:07:11 2018 -0600

    gdesktopappinfo: enable fast posix_spawn gspawn codepath
    
    In order to use the new posix_spawn gspawn codepath, for more robust
    app launching when available memory is low, we need to meet some
    conditions.
    
    child_setup needs to be NULL for this optimization to work, so drop
    the internal child_setup that is used here. Replace it with a lightweight
    wrapper binary (gio-launch-desktop) that sets GIO_LAUNCHED_DESKTOP_FILE_PID
    before executing the app.
    
    Adjust PATH for gio tests so that it can execute the new binary from the
    build directory.

 gio/Makefile.am          |   4 +-
 gio/gdesktopappinfo.c    | 105 ++++++++++++++++++-----------------------------
 gio/gio-launch-desktop.c |  52 +++++++++++++++++++++++
 gio/meson.build          |   6 +++
 gio/tests/Makefile.am    |   4 +-
 gio/tests/meson.build    |   1 +
 6 files changed, 106 insertions(+), 66 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 7c7f247c0..13a85edb1 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -819,7 +819,7 @@ gio.def: libgio-2.0.la
 gio-2.0.lib: libgio-2.0.la gio.def
        $(AM_V_GEN) lib.exe -machine:@LIB_EXE_MACHINE_FLAG@ -name:libgio-2.0-$(LT_CURRENT_MINUS_AGE).dll 
-def:$(builddir)/gio.def -out:$@
 
-bin_PROGRAMS = gio-querymodules glib-compile-schemas glib-compile-resources gsettings
+bin_PROGRAMS = gio-querymodules glib-compile-schemas glib-compile-resources gsettings gio-launch-desktop
 
 glib_compile_resources_LDADD = libgio-2.0.la           \
        $(top_builddir)/gobject/libgobject-2.0.la       \
@@ -840,6 +840,8 @@ gio_querymodules_LDADD       = libgio-2.0.la                \
        $(top_builddir)/glib/libglib-2.0.la             \
        $(NULL)
 
+gio_launch_desktop_SOURCES = gio-launch-desktop.c
+
 gconstructor_as_data.h: $(top_srcdir)/glib/gconstructor.h data-to-c.py
        $(AM_V_GEN) $(srcdir)/data-to-c.py $(top_srcdir)/glib/gconstructor.h gconstructor_code $@
 
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
index a2aa760c7..4eac5ada2 100644
--- a/gio/gdesktopappinfo.c
+++ b/gio/gdesktopappinfo.c
@@ -155,6 +155,7 @@ static guint           n_desktop_file_dirs;
 static const guint     desktop_file_dir_user_config_index = 0;
 static guint           desktop_file_dir_user_data_index;
 static GMutex          desktop_file_dir_lock;
+static const gchar    *gio_launch_desktop_path = NULL;
 
 /* Monitor 'changed' signal handler {{{2 */
 static void desktop_file_dir_reset (DesktopFileDir *dir);
@@ -2562,41 +2563,6 @@ create_files_for_uris (GList *uris)
   return g_list_reverse (res);
 }
 
-typedef struct
-{
-  GSpawnChildSetupFunc user_setup;
-  gpointer user_setup_data;
-
-  char *pid_envvar;
-} ChildSetupData;
-
-static void
-child_setup (gpointer user_data)
-{
-  ChildSetupData *data = user_data;
-
-  if (data->pid_envvar)
-    {
-      pid_t pid = getpid ();
-      char buf[20];
-      int i;
-
-      /* Write the pid into the space already reserved for it in the
-       * environment array. We can't use sprintf because it might
-       * malloc, so we do it by hand. It's simplest to write the pid
-       * out backwards first, then copy it over.
-       */
-      for (i = 0; pid; i++, pid /= 10)
-        buf[i] = (pid % 10) + '0';
-      for (i--; i >= 0; i--)
-        *(data->pid_envvar++) = buf[i];
-      *data->pid_envvar = '\0';
-    }
-
-  if (data->user_setup)
-    data->user_setup (data->user_setup_data);
-}
-
 static void
 notify_desktop_launch (GDBusConnection  *session_bus,
                        GDesktopAppInfo  *info,
@@ -2683,7 +2649,6 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
 
   char **argv, **envp;
   int argc;
-  ChildSetupData data;
 
   g_return_val_if_fail (info != NULL, FALSE);
 
@@ -2705,6 +2670,8 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
       GList *launched_uris;
       GList *iter;
       char *sn_id = NULL;
+      char **wrapped_argv;
+      int i;
 
       old_uris = dup_uris;
       if (!expand_application_parameters (info, exec_line, &dup_uris, &argc, &argv, error))
@@ -2723,25 +2690,11 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
           goto out;
         }
 
-      data.user_setup = user_setup;
-      data.user_setup_data = user_setup_data;
-
       if (info->filename)
-        {
-          envp = g_environ_setenv (envp,
-                                   "GIO_LAUNCHED_DESKTOP_FILE",
-                                   info->filename,
-                                   TRUE);
-          envp = g_environ_setenv (envp,
-                                   "GIO_LAUNCHED_DESKTOP_FILE_PID",
-                                   "XXXXXXXXXXXXXXXXXXXX", /* filled in child_setup */
-                                   TRUE);
-          data.pid_envvar = (char *)g_environ_getenv (envp, "GIO_LAUNCHED_DESKTOP_FILE_PID");
-        }
-      else
-        {
-          data.pid_envvar = NULL;
-        }
+        envp = g_environ_setenv (envp,
+                                 "GIO_LAUNCHED_DESKTOP_FILE",
+                                 info->filename,
+                                 TRUE);
 
       sn_id = NULL;
       if (launch_context)
@@ -2760,12 +2713,35 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
           g_list_free_full (launched_files, g_object_unref);
         }
 
+      if (g_once_init_enter (&gio_launch_desktop_path))
+        {
+          const gchar *tmp;
+
+          /* Allow test suite to specify path to gio-launch-desktop */
+          tmp = g_getenv ("GIO_LAUNCH_DESKTOP");
+
+          /* Fall back on usual searching in $PATH */
+          if (tmp == NULL)
+            tmp = "gio-launch-desktop";
+          g_once_init_leave (&gio_launch_desktop_path, tmp);
+        }
+
+      wrapped_argv = g_new (char *, argc + 2);
+      wrapped_argv[0] = g_strdup (gio_launch_desktop_path);
+
+      for (i = 0; i < argc; i++)
+        wrapped_argv[i + 1] = g_steal_pointer (&argv[i]);
+
+      wrapped_argv[i + 1] = NULL;
+      g_free (argv);
+      argv = NULL;
+
       if (!g_spawn_async (info->path,
-                          argv,
+                          wrapped_argv,
                           envp,
                           spawn_flags,
-                          child_setup,
-                          &data,
+                          user_setup,
+                          user_setup_data,
                           &pid,
                           error))
         {
@@ -2805,8 +2781,8 @@ g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo            *info,
       g_free (sn_id);
       g_list_free (launched_uris);
 
-      g_strfreev (argv);
-      argv = NULL;
+      g_strfreev (wrapped_argv);
+      wrapped_argv = NULL;
     }
   while (dup_uris != NULL);
 
@@ -3046,11 +3022,12 @@ g_desktop_app_info_launch (GAppInfo           *appinfo,
  * launch applications.  Ordinary applications should use
  * g_app_info_launch_uris().
  *
- * If the application is launched via traditional UNIX fork()/exec()
- * then @spawn_flags, @user_setup and @user_setup_data are used for the
- * call to g_spawn_async().  Additionally, @pid_callback (with
- * @pid_callback_data) will be called to inform about the PID of the
- * created process.
+ * If the application is launched via GSpawn, then @spawn_flags, @user_setup
+ * and @user_setup_data are used for the call to g_spawn_async().
+ * Additionally, @pid_callback (with @pid_callback_data) will be called to
+ * inform about the PID of the created process. See g_spawn_async_with_pipes()
+ * for information on certain parameter conditions that can enable an
+ * optimized posix_spawn() codepath to be used.
  *
  * If application launching occurs via some other mechanism (eg: D-Bus
  * activation) then @spawn_flags, @user_setup, @user_setup_data,
diff --git a/gio/gio-launch-desktop.c b/gio/gio-launch-desktop.c
new file mode 100644
index 000000000..03845df28
--- /dev/null
+++ b/gio/gio-launch-desktop.c
@@ -0,0 +1,52 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2018 Endless Mobile, Inc.
+ *
+ * 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.1 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel Drake <drake endlessm com>
+ */
+
+/*
+ * gio-launch-desktop: GDesktopAppInfo helper
+ * Executable wrapper to set GIO_LAUNCHED_DESKTOP_FILE_PID
+ * There are complications when doing this in a fork()/exec() codepath,
+ * and it cannot otherwise be done with posix_spawn().
+ * This wrapper is designed to be minimal and lightweight.
+ * It does not even link against glib.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+int
+main (int argc, char *argv[])
+{
+  pid_t pid = getpid ();
+  char buf[50];
+  int r;
+
+  if (argc < 2)
+    return -1;
+
+  r = snprintf (buf, sizeof (buf), "GIO_LAUNCHED_DESKTOP_FILE_PID=%ld", (long) pid);
+  if (r >= sizeof (buf))
+    return -1;
+
+  putenv (buf);
+
+  return execvp (argv[1], argv + 1);
+}
diff --git a/gio/meson.build b/gio/meson.build
index 9544ab395..89246fea3 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -411,6 +411,12 @@ if host_system != 'windows'
     contenttype_sources += files('gcontenttype.c')
     appinfo_sources += files('gdesktopappinfo.c')
     gio_unix_include_headers += files('gdesktopappinfo.h')
+
+    executable('gio-launch-desktop', 'gio-launch-desktop.c',
+      install : true,
+      c_args : gio_c_args,
+      # intl.lib is not compatible with SAFESEH
+      link_args : noseh_link_args)
   endif
 
   subdir('xdgmime')
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 599823893..e0bc6c7c2 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -15,7 +15,9 @@ LDADD = \
 AM_CPPFLAGS = $(gio_INCLUDES) $(GLIB_DEBUG_FLAGS) -I$(top_builddir)/gio -I$(top_srcdir)/gio
 DEFS = -DG_LOG_DOMAIN=\"GLib-GIO\" -DTEST_SERVICES=\""$(abs_top_builddir)/gio/tests/services"\"
 AM_CFLAGS = $(GLIB_WARN_CFLAGS)
-AM_TESTS_ENVIRONMENT += GIO_MODULE_DIR=
+AM_TESTS_ENVIRONMENT += \
+       GIO_MODULE_DIR= \
+       GIO_LAUNCH_DESKTOP="$(top_builddir)/gio/gio-launch-desktop"
 
 # -----------------------------------------------------------------------------
 #  Test programs buildable on all platforms
diff --git a/gio/tests/meson.build b/gio/tests/meson.build
index 558f3d721..cde1372ad 100644
--- a/gio/tests/meson.build
+++ b/gio/tests/meson.build
@@ -74,6 +74,7 @@ test_env = [
   'G_TEST_SRCDIR=' + meson.current_source_dir(),
   'G_TEST_BUILDDIR=' + meson.current_build_dir(),
   'GIO_MODULE_DIR=',
+  'GIO_LAUNCH_DESKTOP=' + meson.build_root() + '/gio/gio-launch-desktop',
 ]
 
 test_c_args = [


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