Commit 884baf73 authored by Christian Hergert's avatar Christian Hergert
Browse files

libide-threading: add new threading static library

The new threading static library moves subprocesses and tasks into a
space to help plugins and Builder-core to manage multi-processing
features.
parent 98f1f925
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright 2012, 2013, 2016 Red Hat, Inc.
* Copyright 2012, 2013 Canonical Limited
*
* This program 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 licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Authors: Christian Hergert <chergert@redhat.com>
*/
#pragma once
#include "subprocess/ide-subprocess.h"
G_BEGIN_DECLS
#define IDE_TYPE_BREAKOUT_SUBPROCESS (ide_breakout_subprocess_get_type())
G_DECLARE_FINAL_TYPE (IdeBreakoutSubprocess, ide_breakout_subprocess, IDE, BREAKOUT_SUBPROCESS, GObject)
G_END_DECLS
subprocess_headers = [
'ide-subprocess.h',
'ide-subprocess-launcher.h',
'ide-subprocess-supervisor.h',
]
subprocess_sources = [
'ide-subprocess.c',
'ide-subprocess-launcher.c',
'ide-subprocess-supervisor.c',
]
subprocess_private_sources = [
'ide-breakout-subprocess.c',
'ide-breakout-subprocess.h',
'ide-breakout-subprocess-private.h',
'ide-simple-subprocess.c',
'ide-simple-subprocess.h',
]
libide_public_headers += files(subprocess_headers)
libide_public_sources += files(subprocess_sources)
libide_private_sources += files(subprocess_private_sources)
install_headers(subprocess_headers, subdir: join_paths(libide_header_subdir, 'subprocess'))
/* ide-breakout-subprocess-private.h
/* ide-flatpak-subprocess-private.h
*
* Copyright 2016-2019 Christian Hergert <chergert@redhat.com>
*
......@@ -20,17 +20,21 @@
#pragma once
#include "subprocess/ide-breakout-subprocess.h"
#include "ide-subprocess.h"
G_BEGIN_DECLS
#define IDE_TYPE_FLATPAK_SUBPROCESS (ide_flatpak_subprocess_get_type())
G_DECLARE_FINAL_TYPE (IdeFlatpakSubprocess, ide_flatpak_subprocess, IDE, FLATPAK_SUBPROCESS, GObject)
typedef struct
{
gint source_fd;
gint dest_fd;
} IdeBreakoutFdMapping;
IdeSubprocess *_ide_breakout_subprocess_new (const gchar *cwd,
IdeSubprocess *_ide_flatpak_subprocess_new (const gchar *cwd,
const gchar * const *argv,
const gchar * const *env,
GSubprocessFlags flags,
......
/* ide-gtask-private.h
*
* Copyright 2018-2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gio/gio.h>
G_BEGIN_DECLS
void ide_g_task_return_boolean_from_main (GTask *task,
gboolean value);
void ide_g_task_return_int_from_main (GTask *task,
gint value);
void ide_g_task_return_pointer_from_main (GTask *task,
gpointer value,
GDestroyNotify notify);
void ide_g_task_return_error_from_main (GTask *task,
GError *error);
G_END_DECLS
/* ide-gtask.c
*
* Copyright 2016-2019 Christian Hergert <chergert@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#define G_LOG_DOMAIN "ide-gtask"
#include "config.h"
#include "ide-gtask-private.h"
typedef struct
{
GType type;
GTask *task;
union {
gboolean v_bool;
gint v_int;
GError *v_error;
struct {
gpointer pointer;
GDestroyNotify destroy;
} v_ptr;
} u;
} TaskState;
static gboolean
do_return (gpointer user_data)
{
TaskState *state = user_data;
switch (state->type)
{
case G_TYPE_INT:
g_task_return_int (state->task, state->u.v_int);
break;
case G_TYPE_BOOLEAN:
g_task_return_boolean (state->task, state->u.v_bool);
break;
case G_TYPE_POINTER:
g_task_return_pointer (state->task, state->u.v_ptr.pointer, state->u.v_ptr.destroy);
state->u.v_ptr.pointer = NULL;
state->u.v_ptr.destroy = NULL;
break;
default:
if (state->type == G_TYPE_ERROR)
{
g_task_return_error (state->task, g_steal_pointer (&state->u.v_error));
break;
}
g_assert_not_reached ();
}
g_clear_object (&state->task);
g_slice_free (TaskState, state);
return G_SOURCE_REMOVE;
}
static void
task_state_attach (TaskState *state)
{
GMainContext *main_context;
GSource *source;
g_assert (state != NULL);
g_assert (G_IS_TASK (state->task));
main_context = g_task_get_context (state->task);
source = g_timeout_source_new (0);
g_source_set_callback (source, do_return, state, NULL);
g_source_set_name (source, "[ide] ide_g_task_return_from_main");
g_source_attach (source, main_context);
g_source_unref (source);
}
/**
* ide_g_task_return_boolean_from_main:
*
* This is just like g_task_return_boolean() except that it enforces
* that the current stack return to the main context before dispatching
* the callback.
*
* Since: 3.32
*/
void
ide_g_task_return_boolean_from_main (GTask *task,
gboolean value)
{
TaskState *state;
g_return_if_fail (G_IS_TASK (task));
state = g_slice_new0 (TaskState);
state->type = G_TYPE_BOOLEAN;
state->task = g_object_ref (task);
state->u.v_bool = !!value;
task_state_attach (state);
}
void
ide_g_task_return_int_from_main (GTask *task,
gint value)
{
TaskState *state;
g_return_if_fail (G_IS_TASK (task));
state = g_slice_new0 (TaskState);
state->type = G_TYPE_INT;
state->task = g_object_ref (task);
state->u.v_int = value;
task_state_attach (state);
}
void
ide_g_task_return_pointer_from_main (GTask *task,
gpointer value,
GDestroyNotify notify)
{
TaskState *state;
g_return_if_fail (G_IS_TASK (task));
state = g_slice_new0 (TaskState);
state->type = G_TYPE_POINTER;
state->task = g_object_ref (task);
state->u.v_ptr.pointer = value;
state->u.v_ptr.destroy = notify;
task_state_attach (state);
}
/**
* ide_g_task_return_error_from_main:
* @task: a #GTask
* @error: (transfer full): a #GError.
*
* Like g_task_return_error() but ensures we return to the main loop before
* dispatching the result.
*
* Since: 3.32
*/
void
ide_g_task_return_error_from_main (GTask *task,
GError *error)
{
TaskState *state;
g_return_if_fail (G_IS_TASK (task));
state = g_slice_new0 (TaskState);
state->type = G_TYPE_ERROR;
state->task = g_object_ref (task);
state->u.v_error = error;
task_state_attach (state);
}
/* ide-simple-subprocess.h
/* ide-simple-subprocess-private.h
*
* Copyright 2016-2019 Christian Hergert <chergert@redhat.com>
*
......@@ -20,7 +20,7 @@
#pragma once
#include "subprocess/ide-subprocess.h"
#include "ide-subprocess.h"
G_BEGIN_DECLS
......@@ -28,6 +28,12 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (IdeSimpleSubprocess, ide_simple_subprocess, IDE, SIMPLE_SUBPROCESS, GObject)
struct _IdeSimpleSubprocess
{
GObject parent_instance;
GSubprocess *subprocess;
};
IdeSubprocess *ide_simple_subprocess_new (GSubprocess *subprocess);
G_END_DECLS
......@@ -22,15 +22,9 @@
#include "config.h"
#include "ide-debug.h"
#include <libide-core.h>
#include "subprocess/ide-simple-subprocess.h"
struct _IdeSimpleSubprocess
{
GObject parent_instance;
GSubprocess *subprocess;
};
#include "ide-simple-subprocess-private.h"
static void subprocess_iface_init (IdeSubprocessInterface *iface);
......
......@@ -22,9 +22,9 @@
#include "config.h"
#include <dazzle.h>
#include <fcntl.h>
#include <errno.h>
#include <fcntl.h>
#include <libide-core.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
......@@ -33,15 +33,13 @@
#include <sys/types.h>
#include <unistd.h>
#include "ide-debug.h"
#include "ide-environment.h"
#include "ide-environment-variable.h"
#include "ide-flatpak-subprocess-private.h"
#include "ide-simple-subprocess-private.h"
#include "ide-subprocess-launcher.h"
#include "buildsystem/ide-environment-variable.h"
#include "buildsystem/ide-environment.h"
#include "subprocess/ide-breakout-subprocess.h"
#include "subprocess/ide-breakout-subprocess-private.h"
#include "subprocess/ide-simple-subprocess.h"
#include "subprocess/ide-subprocess-launcher.h"
#include "util/ide-flatpak.h"
#define is_flatpak() (ide_get_process_kind() == IDE_PROCESS_KIND_FLATPAK)
typedef struct
{
......@@ -145,7 +143,7 @@ ide_subprocess_launcher_kill_host_process (GCancellable *cancellable,
IDE_ENTRY;
g_assert (G_IS_CANCELLABLE (cancellable));
g_assert (IDE_IS_BREAKOUT_SUBPROCESS (subprocess));
g_assert (IDE_IS_FLATPAK_SUBPROCESS (subprocess));
g_signal_handlers_disconnect_by_func (cancellable,
G_CALLBACK (ide_subprocess_launcher_kill_host_process),
......@@ -165,19 +163,19 @@ ide_subprocess_launcher_new (GSubprocessFlags flags)
}
static gboolean
should_use_breakout_process (IdeSubprocessLauncher *self)
should_use_flatpak_process (IdeSubprocessLauncher *self)
{
IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
g_assert (IDE_IS_SUBPROCESS_LAUNCHER (self));
if (g_getenv ("IDE_USE_BREAKOUT_SUBPROCESS") != NULL)
if (g_getenv ("IDE_USE_FLATPAK_SUBPROCESS") != NULL)
return TRUE;
if (!priv->run_on_host)
return FALSE;
return ide_is_flatpak ();
return is_flatpak ();
}
static void
......@@ -223,7 +221,7 @@ ide_subprocess_launcher_spawn_host_worker (GTask *task,
if (priv->stderr_fd != -1)
stderr_fd = dup (priv->stderr_fd);
process = _ide_breakout_subprocess_new (priv->cwd,
process = _ide_flatpak_subprocess_new (priv->cwd,
(const gchar * const *)priv->argv->pdata,
(const gchar * const *)priv->environ,
priv->flags,
......@@ -383,6 +381,8 @@ ide_subprocess_launcher_real_spawn (IdeSubprocessLauncher *self,
{
IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
g_autoptr(GTask) task = NULL;
IdeSubprocess *ret;
GError *local_error = NULL;
g_assert (IDE_IS_SUBPROCESS_LAUNCHER (self));
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
......@@ -390,7 +390,7 @@ ide_subprocess_launcher_real_spawn (IdeSubprocessLauncher *self,
task = g_task_new (self, cancellable, NULL, NULL);
g_task_set_source_tag (task, ide_subprocess_launcher_real_spawn);
if (priv->clear_env || (ide_is_flatpak () && priv->run_on_host))
if (priv->clear_env || (is_flatpak () && priv->run_on_host))
{
/*
* Many things break without at least PATH, HOME, etc. being set.
......@@ -404,12 +404,22 @@ ide_subprocess_launcher_real_spawn (IdeSubprocessLauncher *self,
ide_subprocess_launcher_setenv (self, "LANG", g_getenv ("LANG"), FALSE);
}
if (should_use_breakout_process (self))
if (should_use_flatpak_process (self))
ide_subprocess_launcher_spawn_host_worker (task, self, NULL, cancellable);
else
ide_subprocess_launcher_spawn_worker (task, self, NULL, cancellable);
return g_task_propagate_pointer (task, error);
ret = g_task_propagate_pointer (task, &local_error);
if (!ret && !local_error)
local_error = g_error_new (G_IO_ERROR,
G_IO_ERROR_FAILED,
"An unkonwn error occurred while spawning");
if (local_error != NULL)
g_propagate_error (error, g_steal_pointer (&local_error));
return g_steal_pointer (&ret);
}
static void
......@@ -724,10 +734,10 @@ ide_subprocess_launcher_set_cwd (IdeSubprocessLauncher *self,
g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
if (dzl_str_empty0 (cwd))
if (ide_str_empty0 (cwd))
cwd = ".";
if (!dzl_str_equal0 (priv->cwd, cwd))
if (!ide_str_equal0 (priv->cwd, cwd))
{
g_free (priv->cwd);
priv->cwd = g_strdup (cwd);
......@@ -766,7 +776,7 @@ ide_subprocess_launcher_overlay_environment (IdeSubprocessLauncher *self,
key = ide_environment_variable_get_key (var);
value = ide_environment_variable_get_value (var);
if (!dzl_str_empty0 (key))
if (!ide_str_empty0 (key))
ide_subprocess_launcher_setenv (self, key, value ?: "", TRUE);
}
}
......
......@@ -20,11 +20,15 @@
#pragma once
#include <gio/gio.h>
#if !defined (IDE_THREADING_INSIDE) && !defined (IDE_THREADING_COMPILATION)
# error "Only <libide-threading.h> can be included directly."
#endif
#include "ide-version-macros.h"
#include <gio/gio.h>
#include <libide-core.h>
#include "ide-types.h"
#include "ide-subprocess.h"
#include "ide-environment.h"
G_BEGIN_DECLS
......@@ -42,14 +46,7 @@ struct _IdeSubprocessLauncherClass
GError **error);
/*< private >*/
gpointer _reserved1;
gpointer _reserved2;
gpointer _reserved3;
gpointer _reserved4;
gpointer _reserved5;
gpointer _reserved6;
gpointer _reserved7;
gpointer _reserved8;
gpointer _reserved[8];
};
IDE_AVAILABLE_IN_3_32
......
......@@ -22,10 +22,10 @@
#include "config.h"
#include "ide-debug.h"
#include <libide-core.h>
#include "subprocess/ide-subprocess.h"
#include "subprocess/ide-subprocess-supervisor.h"
#include "ide-subprocess.h"
#include "ide-subprocess-supervisor.h"
/*
* We will rate limit supervision to once per RATE_LIMIT_THRESHOLD_SECONDS
......
......@@ -20,10 +20,14 @@
#pragma once
#include "ide-version-macros.h"
#if !defined (IDE_THREADING_INSIDE) && !defined (IDE_THREADING_COMPILATION)
# error "Only <libide-threading.h> can be included directly."
#endif
#include "subprocess/ide-subprocess.h"
#include "subprocess/ide-subprocess-launcher.h"
#include <libide-core.h>
#include "ide-subprocess.h"
#include "ide-subprocess-launcher.h"
G_BEGIN_DECLS
......
......@@ -23,10 +23,9 @@
#include "config.h"
#include <string.h>
#include <libide-core.h>
#include "ide-debug.h"
#include "subprocess/ide-subprocess.h"
#include "ide-subprocess.h"
G_DEFINE_INTERFACE (IdeSubprocess, ide_subprocess, G_TYPE_OBJECT)
......
......@@ -20,9 +20,12 @@
#pragma once
#include <gio/gio.h>
#if !defined (IDE_THREADING_INSIDE) && !defined (IDE_THREADING_COMPILATION)
# error "Only <libide-threading.h> can be included directly."
#endif
#include "ide-version-macros.h"
#include <gio/gio.h>
#include <libide-core.h>
G_BEGIN_DECLS
......@@ -93,85 +96,85 @@ struct _IdeSubprocessInterface
};
IDE_AVAILABLE_IN_3_32
const gchar *ide_subprocess_get_identifier (IdeSubprocess *self);
const gchar *ide_subprocess_get_identifier (IdeSubprocess *self);
IDE_AVAILABLE_IN_3_32
GInputStream *ide_subprocess_get_stdout_pipe (IdeSubprocess *self);
GInputStream *ide_subprocess_get_stdout_pipe (IdeSubprocess *self);
IDE_AVAILABLE_IN_3_32
GInputStream *ide_subprocess_get_stderr_pipe (IdeSubprocess *self);
GInputStream *ide_subprocess_get_stderr_pipe (IdeSubprocess *self);
IDE_AVAILABLE_IN_3_32
GOutputStream *ide_subprocess_get_stdin_pipe (IdeSubprocess *self);
GOutputStream *ide_subprocess_get_stdin_pipe (IdeSubprocess *self);
IDE_AVAILABLE_IN_3_32
gboolean ide_subprocess_wait (IdeSubprocess *self,
GCancellable *cancellable,
GError **error);
gboolean ide_subprocess_wait (IdeSubprocess *self,
GCancellable *cancellable,
GError **error);
IDE_AVAILABLE_IN_3_32
gboolean ide_subprocess_wait_check (IdeSubprocess