thread: Move context and task management to impl side

It's the impl side that wants to add impl side idle sources, or fd
sources, etc, so make it part of MetaThreadImpl.

This changes things to be GAsyncQueue based. While things are still
technically single threaded, the GAsyncQueue type is used as later we'll
introduce queuing tasks asynchronously, then eventually queuing across
thread barriers.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2777>
This commit is contained in:
Jonas Ådahl 2021-05-27 11:36:35 +02:00
parent 229904cb4b
commit fda883e859
8 changed files with 370 additions and 184 deletions

View File

@ -31,6 +31,7 @@
#include "backends/native/meta-kms-private.h"
#include "backends/native/meta-kms-update-private.h"
#include "backends/native/meta-kms-utils.h"
#include "backends/native/meta-thread-impl.h"
typedef gboolean (* MetaKmsSimpleProcessFunc) (MetaKmsImplDevice *impl_device,
MetaKmsUpdate *update,
@ -757,11 +758,11 @@ schedule_retry_page_flip (MetaKmsImplDeviceSimple *impl_device_simple,
{
MetaKmsImplDevice *impl_device =
META_KMS_IMPL_DEVICE (impl_device_simple);
MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device);
MetaKms *kms = meta_kms_device_get_kms (device);
MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device);
MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl);
GSource *source;
source = meta_thread_add_source_in_impl (META_THREAD (kms), retry_page_flips,
source = meta_thread_impl_add_source (thread_impl, retry_page_flips,
impl_device_simple, NULL);
g_source_set_ready_time (source, retry_time_us);
@ -831,8 +832,6 @@ mode_set_fallback (MetaKmsImplDeviceSimple *impl_device_simple,
GError **error)
{
MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (impl_device_simple);
MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device);
MetaKms *kms = meta_kms_device_get_kms (device);
MetaKmsCrtc *crtc = meta_kms_page_flip_data_get_crtc (page_flip_data);
CachedModeSet *cached_mode_set;
g_autofree uint32_t *connectors = NULL;
@ -882,9 +881,11 @@ mode_set_fallback (MetaKmsImplDeviceSimple *impl_device_simple,
if (!impl_device_simple->mode_set_fallback_feedback_source)
{
MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device);
MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl);
GSource *source;
source = meta_thread_add_source_in_impl (META_THREAD (kms),
source = meta_thread_impl_add_source (thread_impl,
mode_set_fallback_feedback_idle,
impl_device_simple,
NULL);
@ -941,13 +942,13 @@ dispatch_page_flip (MetaKmsImplDevice *impl_device,
if (!plane_assignment && !custom_page_flip)
{
MetaKmsDevice *device = meta_kms_impl_device_get_device (impl_device);
MetaKms *kms = meta_kms_device_get_kms (device);
MetaKmsImpl *impl = meta_kms_impl_device_get_impl (impl_device);
MetaThreadImpl *thread_impl = META_THREAD_IMPL (impl);
GSource *source;
meta_kms_page_flip_data_make_symbolic (page_flip_data);
source = meta_thread_add_source_in_impl (META_THREAD (kms),
source = meta_thread_impl_add_source (thread_impl,
symbolic_page_flip_idle,
page_flip_data,
NULL);

View File

@ -89,6 +89,15 @@ G_DEFINE_TYPE_WITH_CODE (MetaKmsImplDevice, meta_kms_impl_device,
G_DEFINE_QUARK (-meta-kms-error-quark, meta_kms_error)
MetaKmsImpl *
meta_kms_impl_device_get_impl (MetaKmsImplDevice *impl_device)
{
MetaKmsImplDevicePrivate *priv =
meta_kms_impl_device_get_instance_private (impl_device);
return priv->impl;
}
MetaKmsDevice *
meta_kms_impl_device_get_device (MetaKmsImplDevice *impl_device)
{
@ -856,10 +865,8 @@ ensure_device_file (MetaKmsImplDevice *impl_device,
if (!(priv->flags & META_KMS_DEVICE_FLAG_NO_MODE_SETTING))
{
MetaKms *kms = meta_kms_impl_get_kms (priv->impl);
priv->fd_source =
meta_thread_register_fd_in_impl (META_THREAD (kms),
meta_thread_impl_register_fd (META_THREAD_IMPL (priv->impl),
meta_device_file_get_fd (device_file),
kms_event_dispatch_in_impl,
impl_device);

View File

@ -116,6 +116,8 @@ enum
#define META_KMS_ERROR meta_kms_error_quark ()
GQuark meta_kms_error_quark (void);
MetaKmsImpl * meta_kms_impl_device_get_impl (MetaKmsImplDevice *impl_device);
MetaKmsDevice * meta_kms_impl_device_get_device (MetaKmsImplDevice *impl_device);
GList * meta_kms_impl_device_copy_connectors (MetaKmsImplDevice *impl_device);

View File

@ -30,6 +30,7 @@ enum
PROP_0,
PROP_THREAD,
PROP_MAIN_CONTEXT,
N_PROPS
};
@ -39,9 +40,21 @@ static GParamSpec *obj_props[N_PROPS];
typedef struct _MetaThreadImplPrivate
{
MetaThread *thread;
gboolean in_impl_task;
GMainContext *thread_context;
GAsyncQueue *task_queue;
} MetaThreadImplPrivate;
struct _MetaThreadTask
{
MetaThreadTaskFunc func;
gpointer user_data;
MetaThreadTaskFeedbackFunc feedback_func;
gpointer feedback_user_data;
};
G_DEFINE_TYPE_WITH_PRIVATE (MetaThreadImpl, meta_thread_impl, G_TYPE_OBJECT)
static void
@ -59,6 +72,9 @@ meta_thread_impl_get_property (GObject *object,
case PROP_THREAD:
g_value_set_object (value, priv->thread);
break;
case PROP_MAIN_CONTEXT:
g_value_set_boxed (value, priv->thread_context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -80,6 +96,9 @@ meta_thread_impl_set_property (GObject *object,
case PROP_THREAD:
priv->thread = g_value_get_object (value);
break;
case PROP_MAIN_CONTEXT:
priv->thread_context = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -93,9 +112,22 @@ meta_thread_impl_constructed (GObject *object)
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
G_OBJECT_CLASS (meta_thread_impl_parent_class)->constructed (object);
priv->task_queue = g_async_queue_new ();
priv->thread_context = g_main_context_get_thread_default ();
G_OBJECT_CLASS (meta_thread_impl_parent_class)->constructed (object);
}
static void
meta_thread_impl_finalize (GObject *object)
{
MetaThreadImpl *thread_impl = META_THREAD_IMPL (object);
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
g_clear_pointer (&priv->task_queue, g_async_queue_unref);
g_clear_pointer (&priv->thread_context, g_main_context_unref);
G_OBJECT_CLASS (meta_thread_impl_parent_class)->finalize (object);
}
static void
@ -106,6 +138,7 @@ meta_thread_impl_class_init (MetaThreadImplClass *klass)
object_class->get_property = meta_thread_impl_get_property;
object_class->set_property = meta_thread_impl_set_property;
object_class->constructed = meta_thread_impl_constructed;
object_class->finalize = meta_thread_impl_finalize;
obj_props[PROP_THREAD] =
g_param_spec_object ("thread",
@ -115,6 +148,14 @@ meta_thread_impl_class_init (MetaThreadImplClass *klass)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
obj_props[PROP_MAIN_CONTEXT] =
g_param_spec_boxed ("main-context",
"main-context",
"GMainContext",
G_TYPE_MAIN_CONTEXT,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPS, obj_props);
}
@ -140,3 +181,219 @@ meta_thread_impl_get_main_context (MetaThreadImpl *thread_impl)
return priv->thread_context;
}
MetaThreadTask *
meta_thread_task_new (MetaThreadTaskFunc func,
gpointer user_data,
MetaThreadTaskFeedbackFunc feedback_func,
gpointer feedback_user_data)
{
MetaThreadTask *task;
task = g_new0 (MetaThreadTask, 1);
*task = (MetaThreadTask) {
.func = func,
.user_data = user_data,
.feedback_func = feedback_func,
.feedback_user_data = feedback_user_data,
};
return task;
}
void
meta_thread_task_free (MetaThreadTask *task)
{
g_free (task);
}
typedef struct _MetaThreadImplIdleSource
{
GSource source;
MetaThreadImpl *thread_impl;
} MetaThreadImplIdleSource;
static gboolean
impl_idle_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
MetaThreadImplIdleSource *impl_idle_source =
(MetaThreadImplIdleSource *) source;
MetaThreadImpl *thread_impl = impl_idle_source->thread_impl;
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
gboolean ret;
priv->in_impl_task = TRUE;
ret = callback (user_data);
priv->in_impl_task = FALSE;
return ret;
}
static GSourceFuncs impl_idle_source_funcs = {
.dispatch = impl_idle_source_dispatch,
};
GSource *
meta_thread_impl_add_source (MetaThreadImpl *thread_impl,
GSourceFunc func,
gpointer user_data,
GDestroyNotify user_data_destroy)
{
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
GSource *source;
MetaThreadImplIdleSource *impl_idle_source;
meta_assert_in_thread_impl (priv->thread);
source = g_source_new (&impl_idle_source_funcs,
sizeof (MetaThreadImplIdleSource));
g_source_set_name (source, "[mutter] MetaThreadImpl idle source");
impl_idle_source = (MetaThreadImplIdleSource *) source;
impl_idle_source->thread_impl = thread_impl;
g_source_set_callback (source, func, user_data, user_data_destroy);
g_source_set_ready_time (source, 0);
g_source_attach (source, priv->thread_context);
return source;
}
typedef struct _MetaThreadImplFdSource
{
GSource source;
gpointer fd_tag;
MetaThreadImpl *thread_impl;
MetaThreadTaskFunc dispatch;
gpointer user_data;
} MetaThreadImplFdSource;
static gboolean
meta_thread_impl_fd_source_check (GSource *source)
{
MetaThreadImplFdSource *impl_fd_source = (MetaThreadImplFdSource *) source;
return g_source_query_unix_fd (source, impl_fd_source->fd_tag) & G_IO_IN;
}
static gpointer
dispatch_task_func (MetaThreadImpl *thread_impl,
MetaThreadTaskFunc dispatch,
gpointer user_data,
GError **error)
{
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
gpointer retval = NULL;
priv->in_impl_task = TRUE;
retval = dispatch (thread_impl, user_data, error);
priv->in_impl_task = FALSE;
return retval;
}
static gboolean
meta_thread_impl_fd_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
MetaThreadImplFdSource *impl_fd_source = (MetaThreadImplFdSource *) source;
MetaThreadImpl *thread_impl = impl_fd_source->thread_impl;
gpointer retval;
GError *error = NULL;
retval = dispatch_task_func (thread_impl,
impl_fd_source->dispatch,
impl_fd_source->user_data,
&error);
if (!GPOINTER_TO_INT (retval))
{
g_warning ("Failed to dispatch fd source: %s", error->message);
g_error_free (error);
}
return G_SOURCE_CONTINUE;
}
static GSourceFuncs impl_fd_source_funcs = {
NULL,
meta_thread_impl_fd_source_check,
meta_thread_impl_fd_source_dispatch
};
GSource *
meta_thread_impl_register_fd (MetaThreadImpl *thread_impl,
int fd,
MetaThreadTaskFunc dispatch,
gpointer user_data)
{
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
GSource *source;
MetaThreadImplFdSource *impl_fd_source;
meta_assert_in_thread_impl (priv->thread);
source = g_source_new (&impl_fd_source_funcs,
sizeof (MetaThreadImplFdSource));
g_source_set_name (source, "[mutter] MetaThreadImpl fd source");
impl_fd_source = (MetaThreadImplFdSource *) source;
impl_fd_source->dispatch = dispatch;
impl_fd_source->user_data = user_data;
impl_fd_source->thread_impl = thread_impl;
impl_fd_source->fd_tag = g_source_add_unix_fd (source, fd,
G_IO_IN | G_IO_ERR);
g_source_attach (source, priv->thread_context);
return source;
}
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;
}
void
meta_thread_impl_dispatch (MetaThreadImpl *thread_impl)
{
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
MetaThreadTask *task;
gpointer retval;
g_autoptr (GError) error = NULL;
task = g_async_queue_pop (priv->task_queue);
priv->in_impl_task = TRUE;
retval = task->func (thread_impl, task->user_data, &error);
if (task->feedback_func)
task->feedback_func (retval, error, task->feedback_user_data);
meta_thread_task_free (task);
priv->in_impl_task = FALSE;
}
void
meta_thread_impl_queue_task (MetaThreadImpl *thread_impl,
MetaThreadTask *task)
{
MetaThreadImplPrivate *priv =
meta_thread_impl_get_instance_private (thread_impl);
g_async_queue_push (priv->task_queue, task);
g_main_context_wakeup (priv->thread_context);
}

View File

@ -22,6 +22,7 @@
#include <glib-object.h>
#include "backends/native/meta-thread.h"
#include "core/util-private.h"
typedef struct _MetaThread MetaThread;
@ -36,9 +37,41 @@ struct _MetaThreadImplClass
GObjectClass parent_class;
};
typedef struct _MetaThreadTask MetaThreadTask;
typedef void (* MetaThreadTaskFeedbackFunc) (gpointer retval,
const GError *error,
gpointer user_data);
META_EXPORT_TEST
MetaThread * meta_thread_impl_get_thread (MetaThreadImpl *thread_impl);
GMainContext * meta_thread_impl_get_main_context (MetaThreadImpl *thread_impl);
META_EXPORT_TEST
GSource * meta_thread_impl_add_source (MetaThreadImpl *thread_impl,
GSourceFunc func,
gpointer user_data,
GDestroyNotify user_data_destroy);
META_EXPORT_TEST
GSource * meta_thread_impl_register_fd (MetaThreadImpl *thread_impl,
int fd,
MetaThreadTaskFunc dispatch,
gpointer user_data);
void meta_thread_impl_queue_task (MetaThreadImpl *thread_impl,
MetaThreadTask *task);
void meta_thread_impl_dispatch (MetaThreadImpl *thread_impl);
gboolean meta_thread_impl_is_in_impl (MetaThreadImpl *thread_impl);
MetaThreadTask * meta_thread_task_new (MetaThreadTaskFunc func,
gpointer user_data,
MetaThreadTaskFeedbackFunc feedback_func,
gpointer feedback_user_data);
void meta_thread_task_free (MetaThreadTask *task);
#endif /* META_THREAD_IMPL_H */

View File

@ -43,23 +43,6 @@ typedef struct _MetaThreadCallbackData
GDestroyNotify user_data_destroy;
} MetaThreadCallbackData;
typedef struct _MetaThreadSimpleImplSource
{
GSource source;
MetaThread *thread;
} MetaThreadSimpleImplSource;
typedef struct _MetaThreadFdImplSource
{
GSource source;
gpointer fd_tag;
MetaThread *thread;
MetaThreadTaskFunc dispatch;
gpointer user_data;
} MetaThreadFdImplSource;
typedef struct _MetaThreadPrivate
{
MetaBackend *backend;
@ -67,7 +50,6 @@ typedef struct _MetaThreadPrivate
GMainContext *main_context;
MetaThreadImpl *impl;
gboolean in_impl_task;
gboolean waiting_for_impl_task;
GList *pending_callbacks;
@ -147,12 +129,16 @@ meta_thread_initable_init (GInitable *initable,
MetaThreadClassPrivate *class_priv =
G_TYPE_CLASS_GET_PRIVATE (thread_class, META_TYPE_THREAD,
MetaThreadClassPrivate);
g_autoptr (GMainContext) thread_context = NULL;
priv->main_context = g_main_context_get_thread_default ();
priv->main_context = g_main_context_default ();
thread_context = g_main_context_ref (priv->main_context);
g_assert (g_type_is_a (class_priv->impl_type, META_TYPE_THREAD_IMPL));
priv->impl = g_object_new (class_priv->impl_type,
"thread", thread,
"main-context", thread_context,
NULL);
return TRUE;
@ -282,6 +268,25 @@ meta_thread_queue_callback (MetaThread *thread,
}
}
typedef struct _MetaSyncTaskData
{
gboolean done;
GError *error;
gpointer retval;
} MetaSyncTaskData;
static void
sync_task_done_in_impl (gpointer retval,
const GError *error,
gpointer user_data)
{
MetaSyncTaskData *data = user_data;
data->done = TRUE;
data->retval = retval;
data->error = error ? g_error_copy (error) : NULL;
}
gpointer
meta_thread_run_impl_task_sync (MetaThread *thread,
MetaThreadTaskFunc func,
@ -289,129 +294,24 @@ meta_thread_run_impl_task_sync (MetaThread *thread,
GError **error)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
gpointer ret;
MetaThreadTask *task;
MetaSyncTaskData data = { 0 };
task = meta_thread_task_new (func, user_data,
sync_task_done_in_impl, &data);
meta_thread_impl_queue_task (priv->impl, task);
priv->in_impl_task = TRUE;
priv->waiting_for_impl_task = TRUE;
ret = func (priv->impl, user_data, error);
while (!data.done)
meta_thread_impl_dispatch (priv->impl);
priv->waiting_for_impl_task = FALSE;
priv->in_impl_task = FALSE;
return ret;
}
if (error)
*error = data.error;
else
g_clear_error (&data.error);
static gboolean
simple_impl_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
MetaThreadSimpleImplSource *simple_impl_source =
(MetaThreadSimpleImplSource *) source;
MetaThread *thread = simple_impl_source->thread;
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
gboolean ret;
priv->in_impl_task = TRUE;
ret = callback (user_data);
priv->in_impl_task = FALSE;
return ret;
}
static GSourceFuncs simple_impl_source_funcs = {
.dispatch = simple_impl_source_dispatch,
};
GSource *
meta_thread_add_source_in_impl (MetaThread *thread,
GSourceFunc func,
gpointer user_data,
GDestroyNotify user_data_destroy)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
GSource *source;
MetaThreadSimpleImplSource *simple_impl_source;
meta_assert_in_thread_impl (thread);
source = g_source_new (&simple_impl_source_funcs,
sizeof (MetaThreadSimpleImplSource));
g_source_set_name (source, "[mutter] MetaThread simple impl");
simple_impl_source = (MetaThreadSimpleImplSource *) source;
simple_impl_source->thread = thread;
g_source_set_callback (source, func, user_data, user_data_destroy);
g_source_set_ready_time (source, 0);
g_source_attach (source, meta_thread_impl_get_main_context (priv->impl));
return source;
}
static gboolean
meta_thread_fd_impl_source_check (GSource *source)
{
MetaThreadFdImplSource *fd_impl_source = (MetaThreadFdImplSource *) source;
return g_source_query_unix_fd (source, fd_impl_source->fd_tag) & G_IO_IN;
}
static gboolean
meta_thread_fd_impl_source_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data)
{
MetaThreadFdImplSource *fd_impl_source = (MetaThreadFdImplSource *) source;
MetaThread *thread = fd_impl_source->thread;
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
gpointer ret;
GError *error = NULL;
priv->in_impl_task = TRUE;
ret = fd_impl_source->dispatch (priv->impl,
fd_impl_source->user_data,
&error);
priv->in_impl_task = FALSE;
if (!GPOINTER_TO_INT (ret))
{
g_warning ("Failed to dispatch fd source: %s", error->message);
g_error_free (error);
}
return G_SOURCE_CONTINUE;
}
static GSourceFuncs fd_impl_source_funcs = {
NULL,
meta_thread_fd_impl_source_check,
meta_thread_fd_impl_source_dispatch
};
GSource *
meta_thread_register_fd_in_impl (MetaThread *thread,
int fd,
MetaThreadTaskFunc dispatch,
gpointer user_data)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
GSource *source;
MetaThreadFdImplSource *fd_impl_source;
meta_assert_in_thread_impl (thread);
source = g_source_new (&fd_impl_source_funcs,
sizeof (MetaThreadFdImplSource));
g_source_set_name (source, "[mutter] MetaThread fd impl");
fd_impl_source = (MetaThreadFdImplSource *) source;
fd_impl_source->dispatch = dispatch;
fd_impl_source->user_data = user_data;
fd_impl_source->thread = thread;
fd_impl_source->fd_tag = g_source_add_unix_fd (source, fd,
G_IO_IN | G_IO_ERR);
g_source_attach (source, meta_thread_impl_get_main_context (priv->impl));
return source;
return data.retval;
}
MetaBackend *
@ -427,7 +327,7 @@ meta_thread_is_in_impl_task (MetaThread *thread)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
return priv->in_impl_task;
return meta_thread_impl_is_in_impl (priv->impl);
}
gboolean

View File

@ -60,18 +60,6 @@ gpointer meta_thread_run_impl_task_sync (MetaThread *thread,
gpointer user_data,
GError **error);
META_EXPORT_TEST
GSource * meta_thread_add_source_in_impl (MetaThread *thread,
GSourceFunc func,
gpointer user_data,
GDestroyNotify user_data_destroy);
META_EXPORT_TEST
GSource * meta_thread_register_fd_in_impl (MetaThread *thread,
int fd,
MetaThreadTaskFunc dispatch,
gpointer user_data);
META_EXPORT_TEST
MetaBackend * meta_thread_get_backend (MetaThread *thread);

View File

@ -125,8 +125,7 @@ register_fd_func (MetaThreadImpl *thread_impl,
{
PipeData *pipe_data = user_data;
pipe_data->source =
meta_thread_register_fd_in_impl (meta_thread_impl_get_thread (thread_impl),
pipe_data->source = meta_thread_impl_register_fd (thread_impl,
pipe_data->fd,
dispatch_pipe,
pipe_data);
@ -185,8 +184,7 @@ add_idle_func (MetaThreadImpl *thread_impl,
meta_assert_in_thread_impl (meta_thread_impl_get_thread (thread_impl));
source =
meta_thread_add_source_in_impl (meta_thread_impl_get_thread (thread_impl),
source = meta_thread_impl_add_source (thread_impl,
idle_cb,
idle_data,
idle_data_destroy);