thread: Introduce kernel thread support

This commit makes it possible to create a MetaThread where the
MetaThreadImpl side runs in a real thread, instead of a artificially
separated impl context.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2777>
This commit is contained in:
Jonas Ådahl
2021-06-11 10:54:02 +02:00
parent 4c317eae07
commit 8b612a6150
8 changed files with 308 additions and 17 deletions

View File

@ -25,12 +25,15 @@
#include "backends/meta-backend-types.h"
#include "backends/native/meta-thread-impl.h"
#include "meta-private-enum-types.h"
enum
{
PROP_0,
PROP_BACKEND,
PROP_NAME,
PROP_THREAD_TYPE,
N_PROPS
};
@ -57,6 +60,13 @@ typedef struct _MetaThreadPrivate
GMutex callbacks_mutex;
GList *pending_callbacks;
guint callbacks_source_id;
MetaThreadType thread_type;
struct {
GThread *thread;
GMutex init_mutex;
} kernel;
} MetaThreadPrivate;
typedef struct _MetaThreadClassPrivate
@ -98,6 +108,9 @@ meta_thread_get_property (GObject *object,
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
case PROP_THREAD_TYPE:
g_value_set_enum (value, priv->thread_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -121,12 +134,29 @@ meta_thread_set_property (GObject *object,
case PROP_NAME:
priv->name = g_value_dup_string (value);
break;
case PROP_THREAD_TYPE:
priv->thread_type = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gpointer
thread_impl_func (gpointer user_data)
{
MetaThread *thread = META_THREAD (user_data);
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
g_mutex_lock (&priv->kernel.init_mutex);
g_mutex_unlock (&priv->kernel.init_mutex);
meta_thread_impl_run (priv->impl);
return GINT_TO_POINTER (TRUE);
}
static gboolean
meta_thread_initable_init (GInitable *initable,
GCancellable *cancellable,
@ -142,7 +172,15 @@ meta_thread_initable_init (GInitable *initable,
priv->main_context = g_main_context_default ();
thread_context = g_main_context_ref (priv->main_context);
switch (priv->thread_type)
{
case META_THREAD_TYPE_USER:
thread_context = g_main_context_ref (priv->main_context);
break;
case META_THREAD_TYPE_KERNEL:
thread_context = g_main_context_new ();
break;
}
g_assert (g_type_is_a (class_priv->impl_type, META_TYPE_THREAD_IMPL));
priv->impl = g_object_new (class_priv->impl_type,
@ -150,6 +188,20 @@ meta_thread_initable_init (GInitable *initable,
"main-context", thread_context,
NULL);
switch (priv->thread_type)
{
case META_THREAD_TYPE_USER:
break;
case META_THREAD_TYPE_KERNEL:
g_mutex_init (&priv->kernel.init_mutex);
g_mutex_lock (&priv->kernel.init_mutex);
priv->kernel.thread = g_thread_new (priv->name,
thread_impl_func,
thread);
g_mutex_unlock (&priv->kernel.init_mutex);
break;
}
return TRUE;
}
@ -159,13 +211,42 @@ initable_iface_init (GInitableIface *initable_iface)
initable_iface->init = meta_thread_initable_init;
}
static void
finalize_thread_user (MetaThread *thread)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
while (meta_thread_impl_dispatch (priv->impl) > 0);
}
static void
finalize_thread_kernel (MetaThread *thread)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
meta_thread_impl_terminate (priv->impl);
g_thread_join (priv->kernel.thread);
priv->kernel.thread = NULL;
g_mutex_clear (&priv->kernel.init_mutex);
}
static void
meta_thread_finalize (GObject *object)
{
MetaThread *thread = META_THREAD (object);
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
while (meta_thread_impl_dispatch (priv->impl) > 0);
switch (priv->thread_type)
{
case META_THREAD_TYPE_USER:
finalize_thread_user (thread);
break;
case META_THREAD_TYPE_KERNEL:
finalize_thread_kernel (thread);
break;
}
meta_thread_flush_callbacks (thread);
g_clear_object (&priv->impl);
@ -203,6 +284,16 @@ meta_thread_class_init (MetaThreadClass *klass)
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_props[PROP_THREAD_TYPE] =
g_param_spec_enum ("thread-type",
"thread-type",
"Type of thread",
META_TYPE_THREAD_TYPE,
META_THREAD_TYPE_KERNEL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, obj_props);
}
@ -317,12 +408,16 @@ typedef struct _MetaSyncTaskData
gboolean done;
GError *error;
gpointer retval;
struct {
GMutex mutex;
GCond cond;
} kernel;
} MetaSyncTaskData;
static void
sync_task_done_in_impl (gpointer retval,
const GError *error,
gpointer user_data)
sync_task_done_user_in_impl (gpointer retval,
const GError *error,
gpointer user_data)
{
MetaSyncTaskData *data = user_data;
@ -331,18 +426,18 @@ sync_task_done_in_impl (gpointer retval,
data->error = error ? g_error_copy (error) : NULL;
}
gpointer
meta_thread_run_impl_task_sync (MetaThread *thread,
MetaThreadTaskFunc func,
gpointer user_data,
GError **error)
static gpointer
run_impl_task_sync_user (MetaThread *thread,
MetaThreadTaskFunc func,
gpointer user_data,
GError **error)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
MetaThreadTask *task;
MetaSyncTaskData data = { 0 };
task = meta_thread_task_new (func, user_data,
sync_task_done_in_impl, &data,
sync_task_done_user_in_impl, &data,
META_THREAD_TASK_FEEDBACK_TYPE_IMPL);
meta_thread_impl_queue_task (priv->impl, task);
@ -359,6 +454,77 @@ meta_thread_run_impl_task_sync (MetaThread *thread,
return data.retval;
}
static void
sync_task_done_kernel_in_impl (gpointer retval,
const GError *error,
gpointer user_data)
{
MetaSyncTaskData *data = user_data;
g_mutex_lock (&data->kernel.mutex);
data->done = TRUE;
data->retval = retval;
data->error = error ? g_error_copy (error) : NULL;
g_cond_signal (&data->kernel.cond);
g_mutex_unlock (&data->kernel.mutex);
}
static gpointer
run_impl_task_sync_kernel (MetaThread *thread,
MetaThreadTaskFunc func,
gpointer user_data,
GError **error)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
MetaThreadTask *task;
MetaSyncTaskData data = { 0 };
g_mutex_init (&data.kernel.mutex);
g_cond_init (&data.kernel.cond);
g_mutex_lock (&data.kernel.mutex);
priv->waiting_for_impl_task = TRUE;
task = meta_thread_task_new (func, user_data,
sync_task_done_kernel_in_impl, &data,
META_THREAD_TASK_FEEDBACK_TYPE_IMPL);
meta_thread_impl_queue_task (priv->impl, task);
while (!data.done)
g_cond_wait (&data.kernel.cond, &data.kernel.mutex);
priv->waiting_for_impl_task = FALSE;
g_mutex_unlock (&data.kernel.mutex);
g_mutex_clear (&data.kernel.mutex);
g_cond_clear (&data.kernel.cond);
if (error)
*error = data.error;
else
g_clear_error (&data.error);
return data.retval;
}
gpointer
meta_thread_run_impl_task_sync (MetaThread *thread,
MetaThreadTaskFunc func,
gpointer user_data,
GError **error)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
switch (priv->thread_type)
{
case META_THREAD_TYPE_USER:
return run_impl_task_sync_user (thread, func, user_data, error);
case META_THREAD_TYPE_KERNEL:
return run_impl_task_sync_kernel (thread, func, user_data, error);
}
g_assert_not_reached ();
}
void
meta_thread_post_impl_task (MetaThread *thread,
MetaThreadTaskFunc func,
@ -391,6 +557,24 @@ meta_thread_get_name (MetaThread *thread)
return priv->name;
}
MetaThreadType
meta_thread_get_thread_type (MetaThread *thread)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
return priv->thread_type;
}
GThread *
meta_thread_get_thread (MetaThread *thread)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
g_assert (priv->thread_type == META_THREAD_TYPE_KERNEL);
return priv->kernel.thread;
}
gboolean
meta_thread_is_in_impl_task (MetaThread *thread)
{