[glib: 6/9] Add tests for GBinding thread-safety
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib: 6/9] Add tests for GBinding thread-safety
- Date: Fri, 4 Dec 2020 14:23:11 +0000 (UTC)
commit cc15c933b34621b102b79b7a9aaafacc69ff7896
Author: Sebastian Dröge <sebastian centricular com>
Date: Tue Nov 24 15:01:02 2020 +0200
Add tests for GBinding thread-safety
gobject/tests/binding.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 178 insertions(+)
---
diff --git a/gobject/tests/binding.c b/gobject/tests/binding.c
index be6d751c1..d79d09ec5 100644
--- a/gobject/tests/binding.c
+++ b/gobject/tests/binding.c
@@ -886,6 +886,182 @@ binding_interface (void)
g_object_unref (target);
}
+typedef struct {
+ GThread *thread;
+ GBinding *binding;
+ GMutex *lock;
+ GCond *cond;
+ gboolean *wait;
+ gint *count; /* (atomic) */
+} ConcurrentUnbindData;
+
+static gpointer
+concurrent_unbind_func (gpointer data)
+{
+ ConcurrentUnbindData *unbind_data = data;
+
+ g_mutex_lock (unbind_data->lock);
+ g_atomic_int_inc (unbind_data->count);
+ while (*unbind_data->wait)
+ g_cond_wait (unbind_data->cond, unbind_data->lock);
+ g_mutex_unlock (unbind_data->lock);
+ g_binding_unbind (unbind_data->binding);
+ g_object_unref (unbind_data->binding);
+
+ return NULL;
+}
+
+static void
+binding_concurrent_unbind (void)
+{
+ guint i, j;
+
+ g_test_summary ("Test that unbinding from multiple threads concurrently works correctly");
+
+ for (i = 0; i < 50; i++)
+ {
+ BindingSource *source = g_object_new (binding_source_get_type (), NULL);
+ BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
+ GBinding *binding;
+ GQueue threads = G_QUEUE_INIT;
+ GMutex lock;
+ GCond cond;
+ gboolean wait = TRUE;
+ gint count = 0; /* (atomic) */
+ ConcurrentUnbindData *data;
+
+ g_mutex_init (&lock);
+ g_cond_init (&cond);
+
+ binding = g_object_bind_property (source, "foo",
+ target, "bar",
+ G_BINDING_BIDIRECTIONAL);
+ g_object_ref (binding);
+
+ for (j = 0; j < 10; j++)
+ {
+ data = g_new0 (ConcurrentUnbindData, 1);
+
+ data->binding = g_object_ref (binding);
+ data->lock = &lock;
+ data->cond = &cond;
+ data->wait = &wait;
+ data->count = &count;
+
+ data->thread = g_thread_new ("binding-concurrent", concurrent_unbind_func, data);
+ g_queue_push_tail (&threads, data);
+ }
+
+ /* wait until all threads are started */
+ while (g_atomic_int_get (&count) < 10)
+ g_thread_yield ();
+
+ g_mutex_lock (&lock);
+ wait = FALSE;
+ g_cond_broadcast (&cond);
+ g_mutex_unlock (&lock);
+
+ while ((data = g_queue_pop_head (&threads)))
+ {
+ g_thread_join (data->thread);
+ g_free (data);
+ }
+
+ g_mutex_clear (&lock);
+ g_cond_clear (&cond);
+
+ g_object_unref (binding);
+ g_object_unref (source);
+ g_object_unref (target);
+ }
+}
+
+typedef struct {
+ GObject *object;
+ GMutex *lock;
+ GCond *cond;
+ gint *count; /* (atomic) */
+ gboolean *wait;
+} ConcurrentFinalizeData;
+
+static gpointer
+concurrent_finalize_func (gpointer data)
+{
+ ConcurrentFinalizeData *finalize_data = data;
+
+ g_mutex_lock (finalize_data->lock);
+ g_atomic_int_inc (finalize_data->count);
+ while (*finalize_data->wait)
+ g_cond_wait (finalize_data->cond, finalize_data->lock);
+ g_mutex_unlock (finalize_data->lock);
+ g_object_unref (finalize_data->object);
+ g_free (finalize_data);
+
+ return NULL;
+}
+
+static void
+binding_concurrent_finalizing (void)
+{
+ guint i;
+
+ g_test_summary ("Test that finalizing source/target from multiple threads concurrently works correctly");
+
+ for (i = 0; i < 50; i++)
+ {
+ BindingSource *source = g_object_new (binding_source_get_type (), NULL);
+ BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
+ GBinding *binding;
+ GMutex lock;
+ GCond cond;
+ gboolean wait = TRUE;
+ ConcurrentFinalizeData *data;
+ GThread *source_thread, *target_thread;
+ gint count = 0; /* (atomic) */
+
+ g_mutex_init (&lock);
+ g_cond_init (&cond);
+
+ binding = g_object_bind_property (source, "foo",
+ target, "bar",
+ G_BINDING_BIDIRECTIONAL);
+ g_object_ref (binding);
+
+ data = g_new0 (ConcurrentFinalizeData, 1);
+ data->object = (GObject *) source;
+ data->wait = &wait;
+ data->lock = &lock;
+ data->cond = &cond;
+ data->count = &count;
+ source_thread = g_thread_new ("binding-concurrent", concurrent_finalize_func, data);
+
+ data = g_new0 (ConcurrentFinalizeData, 1);
+ data->object = (GObject *) target;
+ data->wait = &wait;
+ data->lock = &lock;
+ data->cond = &cond;
+ data->count = &count;
+ target_thread = g_thread_new ("binding-concurrent", concurrent_finalize_func, data);
+
+ /* wait until all threads are started */
+ while (g_atomic_int_get (&count) < 2)
+ g_thread_yield ();
+
+ g_mutex_lock (&lock);
+ wait = FALSE;
+ g_cond_broadcast (&cond);
+ g_mutex_unlock (&lock);
+
+ g_thread_join (source_thread);
+ g_thread_join (target_thread);
+
+ g_mutex_clear (&lock);
+ g_cond_clear (&cond);
+
+ g_object_unref (binding);
+ }
+}
+
int
main (int argc, char *argv[])
{
@@ -908,6 +1084,8 @@ main (int argc, char *argv[])
g_test_add_func ("/binding/unbind-multiple", binding_unbind_multiple);
g_test_add_func ("/binding/fail", binding_fail);
g_test_add_func ("/binding/interface", binding_interface);
+ g_test_add_func ("/binding/concurrent-unbind", binding_concurrent_unbind);
+ g_test_add_func ("/binding/concurrent-finalizing", binding_concurrent_finalizing);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]