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

@ -436,6 +436,7 @@ meta_kms_new (MetaBackend *backend,
NULL, error,
"backend", backend,
"name", "KMS thread",
"thread-type", META_THREAD_TYPE_USER,
NULL);
kms->flags = flags;

View File

@ -23,7 +23,9 @@
#include <glib-object.h>
#include "backends/native/meta-thread.h"
#include "backends/native/meta-thread-private.h"
#define META_THREAD_IMPL_TERMINATE ((gpointer) 1)
enum
{
@ -47,6 +49,8 @@ typedef struct _MetaThreadImplPrivate
{
MetaThread *thread;
GMainLoop *loop;
gboolean in_impl_task;
GMainContext *thread_context;
@ -202,6 +206,7 @@ meta_thread_impl_finalize (GObject *object)
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
g_clear_pointer (&priv->loop, g_main_loop_unref);
g_clear_pointer (&priv->impl_source, g_source_destroy);
g_clear_pointer (&priv->task_queue, g_async_queue_unref);
g_clear_pointer (&priv->thread_context, g_main_context_unref);
@ -444,13 +449,31 @@ meta_thread_impl_register_fd (MetaThreadImpl *thread_impl,
return source;
}
void
meta_thread_impl_terminate (MetaThreadImpl *thread_impl)
{
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
g_async_queue_push (priv->task_queue, META_THREAD_IMPL_TERMINATE);
g_main_context_wakeup (priv->thread_context);
}
gboolean
meta_thread_impl_is_in_impl (MetaThreadImpl *thread_impl)
{
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
return priv->in_impl_task;
switch (meta_thread_get_thread_type (priv->thread))
{
case META_THREAD_TYPE_USER:
return priv->in_impl_task;
case META_THREAD_TYPE_KERNEL:
return meta_thread_get_thread (priv->thread) == g_thread_self ();
}
g_assert_not_reached ();
}
static void
@ -477,6 +500,13 @@ meta_thread_impl_dispatch (MetaThreadImpl *thread_impl)
if (!task)
return 0;
if (task == META_THREAD_IMPL_TERMINATE)
{
if (priv->loop)
g_main_loop_quit (priv->loop);
return 0;
}
priv->in_impl_task = TRUE;
retval = task->func (thread_impl, task->user_data, &error);
@ -505,6 +535,18 @@ meta_thread_impl_dispatch (MetaThreadImpl *thread_impl)
return 1;
}
void
meta_thread_impl_run (MetaThreadImpl *thread_impl)
{
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
meta_assert_in_thread_impl (priv->thread);
priv->loop = g_main_loop_new (priv->thread_context, FALSE);
g_main_loop_run (priv->loop);
}
void
meta_thread_impl_queue_task (MetaThreadImpl *thread_impl,
MetaThreadTask *task)

View File

@ -65,6 +65,10 @@ GSource * meta_thread_impl_register_fd (MetaThreadImpl *thread_impl,
void meta_thread_impl_queue_task (MetaThreadImpl *thread_impl,
MetaThreadTask *task);
void meta_thread_impl_terminate (MetaThreadImpl *thread_impl);
void meta_thread_impl_run (MetaThreadImpl *thread_impl);
int meta_thread_impl_dispatch (MetaThreadImpl *thread_impl);
gboolean meta_thread_impl_is_in_impl (MetaThreadImpl *thread_impl);

View File

@ -28,4 +28,9 @@ META_EXPORT_TEST
void meta_thread_class_register_impl_type (MetaThreadClass *thread_class,
GType impl_type);
META_EXPORT_TEST
MetaThreadType meta_thread_get_thread_type (MetaThread *thread);
GThread * meta_thread_get_thread (MetaThread *thread);
#endif /* META_THREAD_PRIVATE_H */

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)
{

View File

@ -27,6 +27,12 @@
typedef struct _MetaThreadImpl MetaThreadImpl;
typedef enum _MetaThreadType
{
META_THREAD_TYPE_KERNEL,
META_THREAD_TYPE_USER,
} MetaThreadType;
#define META_TYPE_THREAD (meta_thread_get_type ())
META_EXPORT_TEST
G_DECLARE_DERIVABLE_TYPE (MetaThread, meta_thread,

View File

@ -840,6 +840,7 @@ if have_native_backend
'backends/native/meta-stage-native.h',
'backends/native/meta-thread-impl.c',
'backends/native/meta-thread-impl.h',
'backends/native/meta-thread-private.h',
'backends/native/meta-thread.c',
'backends/native/meta-thread.h',
'backends/native/meta-thread-private.h',

View File

@ -23,8 +23,8 @@
#include <glib.h>
#include <glib-unix.h>
#include "backends/native/meta-thread.h"
#include "backends/native/meta-thread-impl.h"
#include "backends/native/meta-thread-private.h"
#include "meta-test/meta-context-test.h"
#include "tests/meta-thread-impl-test.h"
#include "tests/meta-thread-test.h"
@ -156,6 +156,7 @@ register_fd_func (MetaThreadImpl *thread_impl,
typedef struct
{
MetaThread *thread;
GMainLoop *loop;
int state;
@ -186,8 +187,9 @@ idle_data_destroy (gpointer user_data)
{
IdleData *idle_data = user_data;
/* XXX: This can only be checked on kernel type threads. */
/* meta_assert_in_thread_impl (test_thread); */
if (meta_thread_get_thread_type (idle_data->thread) ==
META_THREAD_TYPE_KERNEL)
meta_assert_in_thread_impl (test_thread);
g_assert_cmpint (idle_data->state, ==, 2);
idle_data->state = 3;
@ -471,6 +473,7 @@ run_thread_tests (MetaThread *thread)
/* Test idle source */
g_debug ("Test idle source");
idle_data = (IdleData) { 0 };
idle_data.thread = thread;
idle_data.loop = g_main_loop_new (NULL, FALSE);
meta_thread_run_impl_task_sync (thread, add_idle_func, &idle_data, NULL);
g_main_loop_run (idle_data.loop);
@ -553,6 +556,7 @@ meta_test_thread_user_common (void)
NULL, &error,
"backend", backend,
"name", "test user thread",
"thread-type", META_THREAD_TYPE_USER,
NULL);
g_object_add_weak_pointer (G_OBJECT (thread), (gpointer *) &thread);
g_assert_nonnull (thread);
@ -568,6 +572,33 @@ meta_test_thread_user_common (void)
test_thread = NULL;
}
static void
meta_test_thread_kernel_common (void)
{
MetaBackend *backend = meta_context_get_backend (test_context);
MetaThread *thread;
g_autoptr (GError) error = NULL;
thread = g_initable_new (META_TYPE_THREAD_TEST,
NULL, &error,
"backend", backend,
"name", "test kernel thread",
"thread-type", META_THREAD_TYPE_KERNEL,
NULL);
g_object_add_weak_pointer (G_OBJECT (thread), (gpointer *) &thread);
g_assert_nonnull (thread);
g_assert_null (error);
g_assert (meta_thread_get_backend (thread) == backend);
g_assert_cmpstr (meta_thread_get_name (thread), ==, "test kernel thread");
test_thread = thread;
run_thread_tests (thread);
g_object_unref (thread);
g_assert_null (thread);
test_thread = NULL;
}
static gpointer
late_callback (MetaThreadImpl *thread_impl,
gpointer user_data,
@ -581,7 +612,7 @@ late_callback (MetaThreadImpl *thread_impl,
}
static void
meta_test_thread_user_late_callbacks (void)
meta_test_thread_late_callbacks_common (MetaThreadType thread_type)
{
MetaBackend *backend = meta_context_get_backend (test_context);
MetaThread *thread;
@ -592,6 +623,7 @@ meta_test_thread_user_late_callbacks (void)
NULL, &error,
"backend", backend,
"name", "test late callback",
"thread-type", thread_type,
NULL);
g_object_add_weak_pointer (G_OBJECT (thread), (gpointer *) &thread);
g_assert_nonnull (thread);
@ -604,13 +636,29 @@ meta_test_thread_user_late_callbacks (void)
g_assert_true (done);
}
static void
meta_test_thread_user_late_callbacks (void)
{
meta_test_thread_late_callbacks_common (META_THREAD_TYPE_USER);
}
static void
meta_test_thread_kernel_late_callbacks (void)
{
meta_test_thread_late_callbacks_common (META_THREAD_TYPE_KERNEL);
}
static void
init_tests (void)
{
g_test_add_func ("/backends/native/thread/user/common",
meta_test_thread_user_common);
g_test_add_func ("/backends/native/thread/kernel/common",
meta_test_thread_kernel_common);
g_test_add_func ("/backends/native/thread/user/late-callbacks",
meta_test_thread_user_late_callbacks);
g_test_add_func ("/backends/native/thread/kernel/late-callbacks",
meta_test_thread_kernel_late_callbacks);
}
int