Skip to content

Commit

Permalink
gdesktopappinfo: enable fast posix_spawn gspawn codepath
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dsd committed Jun 21, 2018
1 parent 61f5459 commit 742efe6
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 66 deletions.
4 changes: 3 additions & 1 deletion gio/Makefile.am
Expand Up @@ -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 \
Expand All @@ -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 $@

Expand Down
105 changes: 41 additions & 64 deletions gio/gdesktopappinfo.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand All @@ -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))
Expand All @@ -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)
Expand All @@ -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))
{
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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,
Expand Down
52 changes: 52 additions & 0 deletions 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);
}
6 changes: 6 additions & 0 deletions gio/meson.build
Expand Up @@ -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')
Expand Down
4 changes: 3 additions & 1 deletion gio/tests/Makefile.am
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions gio/tests/meson.build
Expand Up @@ -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 = [
Expand Down

0 comments on commit 742efe6

Please sign in to comment.