thread: Make callback handling thread safe

This is in preparation for introducing kernel threads, where proper
synchronization becomes necessary.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2777>
This commit is contained in:
Jonas Ådahl 2021-06-11 10:27:36 +02:00
parent 915bceb5a0
commit 4c317eae07

View File

@ -54,6 +54,7 @@ typedef struct _MetaThreadPrivate
MetaThreadImpl *impl;
gboolean waiting_for_impl_task;
GMutex callbacks_mutex;
GList *pending_callbacks;
guint callbacks_source_id;
} MetaThreadPrivate;
@ -166,11 +167,12 @@ meta_thread_finalize (GObject *object)
while (meta_thread_impl_dispatch (priv->impl) > 0);
meta_thread_flush_callbacks (thread);
g_clear_handle_id (&priv->callbacks_source_id, g_source_remove);
g_clear_object (&priv->impl);
g_clear_pointer (&priv->name, g_free);
g_mutex_clear (&priv->callbacks_mutex);
G_OBJECT_CLASS (meta_thread_parent_class)->finalize (object);
}
@ -207,6 +209,9 @@ meta_thread_class_init (MetaThreadClass *klass)
static void
meta_thread_init (MetaThread *thread)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
g_mutex_init (&priv->callbacks_mutex);
}
void
@ -221,16 +226,14 @@ meta_thread_class_register_impl_type (MetaThreadClass *thread_class,
class_priv->impl_type = impl_type;
}
int
meta_thread_flush_callbacks (MetaThread *thread)
static int
dispatch_callbacks (MetaThread *thread,
GList *pending_callbacks)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
GList *l;
int callback_count = 0;
GList *l;
meta_assert_not_in_thread_impl (thread);
for (l = priv->pending_callbacks; l; l = l->next)
for (l = pending_callbacks; l; l = l->next)
{
MetaThreadCallbackData *callback_data = l->data;
@ -239,21 +242,41 @@ meta_thread_flush_callbacks (MetaThread *thread)
callback_count++;
}
g_list_free (priv->pending_callbacks);
priv->pending_callbacks = NULL;
return callback_count;
}
int
meta_thread_flush_callbacks (MetaThread *thread)
{
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
g_autoptr (GList) pending_callbacks = NULL;
meta_assert_not_in_thread_impl (thread);
g_mutex_lock (&priv->callbacks_mutex);
pending_callbacks = g_steal_pointer (&priv->pending_callbacks);
g_clear_handle_id (&priv->callbacks_source_id, g_source_remove);
g_mutex_unlock (&priv->callbacks_mutex);
return dispatch_callbacks (thread, pending_callbacks);
}
static gboolean
callback_idle (gpointer user_data)
{
MetaThread *thread = user_data;
MetaThreadPrivate *priv = meta_thread_get_instance_private (thread);
g_autoptr (GList) pending_callbacks = NULL;
meta_thread_flush_callbacks (thread);
meta_assert_not_in_thread_impl (thread);
g_mutex_lock (&priv->callbacks_mutex);
pending_callbacks = g_steal_pointer (&priv->pending_callbacks);
priv->callbacks_source_id = 0;
g_mutex_unlock (&priv->callbacks_mutex);
dispatch_callbacks (thread, pending_callbacks);
return G_SOURCE_REMOVE;
}
@ -272,6 +295,8 @@ meta_thread_queue_callback (MetaThread *thread,
.user_data = user_data,
.user_data_destroy = user_data_destroy,
};
g_mutex_lock (&priv->callbacks_mutex);
priv->pending_callbacks = g_list_append (priv->pending_callbacks,
callback_data);
if (!priv->callbacks_source_id)
@ -284,6 +309,7 @@ meta_thread_queue_callback (MetaThread *thread,
priv->main_context);
g_source_unref (idle_source);
}
g_mutex_unlock (&priv->callbacks_mutex);
}
typedef struct _MetaSyncTaskData