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 (); }