From 8b612a61502da778226db2455f8fff22cb0dd44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 11 Jun 2021 10:54:02 +0200 Subject: [PATCH] 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: --- src/backends/native/meta-kms.c | 1 + src/backends/native/meta-thread-impl.c | 46 ++++- src/backends/native/meta-thread-impl.h | 4 + src/backends/native/meta-thread-private.h | 5 + src/backends/native/meta-thread.c | 206 ++++++++++++++++++++-- src/backends/native/meta-thread.h | 6 + src/meson.build | 1 + src/tests/native-thread.c | 56 +++++- 8 files changed, 308 insertions(+), 17 deletions(-) diff --git a/src/backends/native/meta-kms.c b/src/backends/native/meta-kms.c index 463f92ef7..837af2492 100644 --- a/src/backends/native/meta-kms.c +++ b/src/backends/native/meta-kms.c @@ -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; diff --git a/src/backends/native/meta-thread-impl.c b/src/backends/native/meta-thread-impl.c index 0e64e0d4d..086e9268b 100644 --- a/src/backends/native/meta-thread-impl.c +++ b/src/backends/native/meta-thread-impl.c @@ -23,7 +23,9 @@ #include -#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) diff --git a/src/backends/native/meta-thread-impl.h b/src/backends/native/meta-thread-impl.h index c3b8c6449..2dd880210 100644 --- a/src/backends/native/meta-thread-impl.h +++ b/src/backends/native/meta-thread-impl.h @@ -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); diff --git a/src/backends/native/meta-thread-private.h b/src/backends/native/meta-thread-private.h index 4387fdf06..56f6e45c6 100644 --- a/src/backends/native/meta-thread-private.h +++ b/src/backends/native/meta-thread-private.h @@ -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 */ diff --git a/src/backends/native/meta-thread.c b/src/backends/native/meta-thread.c index b6bfd0806..f0de9d18f 100644 --- a/src/backends/native/meta-thread.c +++ b/src/backends/native/meta-thread.c @@ -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) { diff --git a/src/backends/native/meta-thread.h b/src/backends/native/meta-thread.h index 4eb6741d9..a754e891f 100644 --- a/src/backends/native/meta-thread.h +++ b/src/backends/native/meta-thread.h @@ -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, diff --git a/src/meson.build b/src/meson.build index b4025d5c6..9b36de52a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -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', diff --git a/src/tests/native-thread.c b/src/tests/native-thread.c index ee26ea43e..41f51ce6d 100644 --- a/src/tests/native-thread.c +++ b/src/tests/native-thread.c @@ -23,8 +23,8 @@ #include #include -#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