3 #include <glib-object.h>
5 #define assert_cmpsource(binding, op, expected_source) G_STMT_START { \
7 tmp = g_binding_dup_source ((binding)); \
8 G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
9 tmp2 = g_binding_get_source ((binding)); \
10 G_GNUC_END_IGNORE_DEPRECATIONS \
11 g_assert_nonnull (tmp); \
12 g_assert_true ((gpointer) tmp op (gpointer) (expected_source)); \
13 g_assert_true (tmp == tmp2); \
14 g_object_unref (tmp); \
17 #define assert_cmptarget(binding, op, expected_target) G_STMT_START { \
18 GObject *tmp, *tmp2; \
19 tmp = g_binding_dup_target ((binding)); \
20 G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
21 tmp2 = g_binding_get_target ((binding)); \
22 G_GNUC_END_IGNORE_DEPRECATIONS \
23 g_assert_nonnull (tmp); \
24 g_assert_true ((gpointer) tmp op (gpointer) (expected_target)); \
25 g_assert_true (tmp == tmp2); \
26 g_object_unref (tmp); \
30 GTypeInterface g_iface;
33 GType foo_get_type (void);
35 G_DEFINE_INTERFACE (Foo, foo, G_TYPE_OBJECT)
38 foo_default_init (FooInterface *iface)
47 GObjectClass parent_class;
51 baa_init_foo (FooInterface *iface)
55 GType baa_get_type (void);
57 G_DEFINE_TYPE_WITH_CODE (Baa, baa, G_TYPE_OBJECT,
58 G_IMPLEMENT_INTERFACE (foo_get_type (), baa_init_foo))
66 baa_class_init (BaaClass *class)
70 typedef struct _BindingSource
72 GObject parent_instance;
81 typedef struct _BindingSourceClass
83 GObjectClass parent_class;
92 PROP_SOURCE_DOUBLE_VALUE,
97 static GType binding_source_get_type (void);
98 G_DEFINE_TYPE (BindingSource, binding_source, G_TYPE_OBJECT)
101 binding_source_set_property (GObject *gobject,
106 BindingSource *source = (BindingSource *) gobject;
110 case PROP_SOURCE_FOO:
111 source->foo = g_value_get_int (value);
114 case PROP_SOURCE_BAR:
115 source->bar = g_value_get_int (value);
118 case PROP_SOURCE_DOUBLE_VALUE:
119 source->double_value = g_value_get_double (value);
122 case PROP_SOURCE_TOGGLE:
123 source->toggle = g_value_get_boolean (value);
126 case PROP_SOURCE_OBJECT:
127 source->item = g_value_get_object (value);
131 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
136 binding_source_get_property (GObject *gobject,
141 BindingSource *source = (BindingSource *) gobject;
145 case PROP_SOURCE_FOO:
146 g_value_set_int (value, source->foo);
149 case PROP_SOURCE_BAR:
150 g_value_set_int (value, source->bar);
153 case PROP_SOURCE_DOUBLE_VALUE:
154 g_value_set_double (value, source->double_value);
157 case PROP_SOURCE_TOGGLE:
158 g_value_set_boolean (value, source->toggle);
161 case PROP_SOURCE_OBJECT:
162 g_value_set_object (value, source->item);
166 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
171 binding_source_class_init (BindingSourceClass *klass)
173 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
175 gobject_class->set_property = binding_source_set_property;
176 gobject_class->get_property = binding_source_get_property;
178 g_object_class_install_property (gobject_class, PROP_SOURCE_FOO,
179 g_param_spec_int ("foo", "Foo", "Foo",
183 g_object_class_install_property (gobject_class, PROP_SOURCE_BAR,
184 g_param_spec_int ("bar", "Bar", "Bar",
188 g_object_class_install_property (gobject_class, PROP_SOURCE_DOUBLE_VALUE,
189 g_param_spec_double ("double-value", "Value", "Value",
193 g_object_class_install_property (gobject_class, PROP_SOURCE_TOGGLE,
194 g_param_spec_boolean ("toggle", "Toggle", "Toggle",
197 g_object_class_install_property (gobject_class, PROP_SOURCE_OBJECT,
198 g_param_spec_object ("object", "Object", "Object",
204 binding_source_init (BindingSource *self)
208 typedef struct _BindingTarget
210 GObject parent_instance;
213 gdouble double_value;
218 typedef struct _BindingTargetClass
220 GObjectClass parent_class;
221 } BindingTargetClass;
228 PROP_TARGET_DOUBLE_VALUE,
233 static GType binding_target_get_type (void);
234 G_DEFINE_TYPE (BindingTarget, binding_target, G_TYPE_OBJECT)
237 binding_target_set_property (GObject *gobject,
242 BindingTarget *target = (BindingTarget *) gobject;
246 case PROP_TARGET_BAR:
247 target->bar = g_value_get_int (value);
250 case PROP_TARGET_DOUBLE_VALUE:
251 target->double_value = g_value_get_double (value);
254 case PROP_TARGET_TOGGLE:
255 target->toggle = g_value_get_boolean (value);
258 case PROP_TARGET_FOO:
259 target->foo = g_value_get_object (value);
263 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
268 binding_target_get_property (GObject *gobject,
273 BindingTarget *target = (BindingTarget *) gobject;
277 case PROP_TARGET_BAR:
278 g_value_set_int (value, target->bar);
281 case PROP_TARGET_DOUBLE_VALUE:
282 g_value_set_double (value, target->double_value);
285 case PROP_TARGET_TOGGLE:
286 g_value_set_boolean (value, target->toggle);
289 case PROP_TARGET_FOO:
290 g_value_set_object (value, target->foo);
294 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
299 binding_target_class_init (BindingTargetClass *klass)
301 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
303 gobject_class->set_property = binding_target_set_property;
304 gobject_class->get_property = binding_target_get_property;
306 g_object_class_install_property (gobject_class, PROP_TARGET_BAR,
307 g_param_spec_int ("bar", "Bar", "Bar",
311 g_object_class_install_property (gobject_class, PROP_TARGET_DOUBLE_VALUE,
312 g_param_spec_double ("double-value", "Value", "Value",
316 g_object_class_install_property (gobject_class, PROP_TARGET_TOGGLE,
317 g_param_spec_boolean ("toggle", "Toggle", "Toggle",
320 g_object_class_install_property (gobject_class, PROP_TARGET_FOO,
321 g_param_spec_object ("foo", "Foo", "Foo",
327 binding_target_init (BindingTarget *self)
332 celsius_to_fahrenheit (GBinding *binding,
333 const GValue *from_value,
335 gpointer user_data G_GNUC_UNUSED)
337 gdouble celsius, fahrenheit;
339 g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
340 g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
342 celsius = g_value_get_double (from_value);
343 fahrenheit = (9 * celsius / 5) + 32.0;
345 if (g_test_verbose ())
346 g_printerr ("Converting %.2fC to %.2fF\n", celsius, fahrenheit);
348 g_value_set_double (to_value, fahrenheit);
354 fahrenheit_to_celsius (GBinding *binding,
355 const GValue *from_value,
357 gpointer user_data G_GNUC_UNUSED)
359 gdouble celsius, fahrenheit;
361 g_assert_true (G_VALUE_HOLDS (from_value, G_TYPE_DOUBLE));
362 g_assert_true (G_VALUE_HOLDS (to_value, G_TYPE_DOUBLE));
364 fahrenheit = g_value_get_double (from_value);
365 celsius = 5 * (fahrenheit - 32.0) / 9;
367 if (g_test_verbose ())
368 g_printerr ("Converting %.2fF to %.2fC\n", fahrenheit, celsius);
370 g_value_set_double (to_value, celsius);
376 binding_default (void)
378 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
379 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
382 binding = g_object_bind_property (source, "foo",
386 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
388 assert_cmpsource (binding, ==, source);
389 assert_cmptarget (binding, ==, target);
391 g_assert_cmpstr (g_binding_get_source_property (binding), ==, "foo");
392 g_assert_cmpstr (g_binding_get_target_property (binding), ==, "bar");
393 g_assert_cmpint (g_binding_get_flags (binding), ==, G_BINDING_DEFAULT);
395 g_object_set (source, "foo", 42, NULL);
396 g_assert_cmpint (source->foo, ==, target->bar);
398 g_object_set (target, "bar", 47, NULL);
399 g_assert_cmpint (source->foo, !=, target->bar);
401 g_object_unref (binding);
403 g_object_set (source, "foo", 0, NULL);
404 g_assert_cmpint (source->foo, !=, target->bar);
406 g_object_unref (source);
407 g_object_unref (target);
408 g_assert_null (binding);
412 binding_canonicalisation (void)
414 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
415 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
418 g_test_summary ("Test that bindings set up with non-canonical property names work");
420 binding = g_object_bind_property (source, "double_value",
421 target, "double_value",
424 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
426 assert_cmpsource (binding, ==, source);
427 assert_cmptarget (binding, ==, target);
429 g_assert_cmpstr (g_binding_get_source_property (binding), ==, "double-value");
430 g_assert_cmpstr (g_binding_get_target_property (binding), ==, "double-value");
431 g_assert_cmpint (g_binding_get_flags (binding), ==, G_BINDING_DEFAULT);
433 g_object_set (source, "double-value", 24.0, NULL);
434 g_assert_cmpfloat (target->double_value, ==, source->double_value);
436 g_object_set (target, "double-value", 69.0, NULL);
437 g_assert_cmpfloat (source->double_value, !=, target->double_value);
439 g_object_unref (target);
440 g_object_unref (source);
441 g_assert_null (binding);
445 binding_bidirectional (void)
447 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
448 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
451 binding = g_object_bind_property (source, "foo",
453 G_BINDING_BIDIRECTIONAL);
454 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
456 g_object_set (source, "foo", 42, NULL);
457 g_assert_cmpint (source->foo, ==, target->bar);
459 g_object_set (target, "bar", 47, NULL);
460 g_assert_cmpint (source->foo, ==, target->bar);
462 g_object_unref (binding);
464 g_object_set (source, "foo", 0, NULL);
465 g_assert_cmpint (source->foo, !=, target->bar);
467 g_object_unref (source);
468 g_object_unref (target);
469 g_assert_null (binding);
473 data_free (gpointer data)
481 binding_transform_default (void)
483 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
484 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
487 gchar *src_prop, *trg_prop;
490 binding = g_object_bind_property (source, "foo",
491 target, "double-value",
492 G_BINDING_BIDIRECTIONAL);
494 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
496 g_object_get (binding,
498 "source-property", &src_prop,
500 "target-property", &trg_prop,
503 g_assert_true (src == source);
504 g_assert_true (trg == target);
505 g_assert_cmpstr (src_prop, ==, "foo");
506 g_assert_cmpstr (trg_prop, ==, "double-value");
507 g_assert_cmpint (flags, ==, G_BINDING_BIDIRECTIONAL);
508 g_object_unref (src);
509 g_object_unref (trg);
513 g_object_set (source, "foo", 24, NULL);
514 g_assert_cmpfloat (target->double_value, ==, 24.0);
516 g_object_set (target, "double-value", 69.0, NULL);
517 g_assert_cmpint (source->foo, ==, 69);
519 g_object_unref (target);
520 g_object_unref (source);
521 g_assert_null (binding);
525 binding_transform (void)
527 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
528 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
529 GBinding *binding G_GNUC_UNUSED;
530 gboolean unused_data = FALSE;
532 binding = g_object_bind_property_full (source, "double-value",
533 target, "double-value",
534 G_BINDING_BIDIRECTIONAL,
535 celsius_to_fahrenheit,
536 fahrenheit_to_celsius,
537 &unused_data, data_free);
539 g_object_set (source, "double-value", 24.0, NULL);
540 g_assert_cmpfloat (target->double_value, ==, ((9 * 24.0 / 5) + 32.0));
542 g_object_set (target, "double-value", 69.0, NULL);
543 g_assert_cmpfloat (source->double_value, ==, (5 * (69.0 - 32.0) / 9));
545 g_object_unref (source);
546 g_object_unref (target);
548 g_assert_true (unused_data);
552 binding_transform_closure (void)
554 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
555 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
556 GBinding *binding G_GNUC_UNUSED;
557 gboolean unused_data_1 = FALSE, unused_data_2 = FALSE;
558 GClosure *c2f_clos, *f2c_clos;
560 c2f_clos = g_cclosure_new (G_CALLBACK (celsius_to_fahrenheit), &unused_data_1, (GClosureNotify) data_free);
562 f2c_clos = g_cclosure_new (G_CALLBACK (fahrenheit_to_celsius), &unused_data_2, (GClosureNotify) data_free);
564 binding = g_object_bind_property_with_closures (source, "double-value",
565 target, "double-value",
566 G_BINDING_BIDIRECTIONAL,
570 g_object_set (source, "double-value", 24.0, NULL);
571 g_assert_cmpfloat (target->double_value, ==, ((9 * 24.0 / 5) + 32.0));
573 g_object_set (target, "double-value", 69.0, NULL);
574 g_assert_cmpfloat (source->double_value, ==, (5 * (69.0 - 32.0) / 9));
576 g_object_unref (source);
577 g_object_unref (target);
579 g_assert_true (unused_data_1);
580 g_assert_true (unused_data_2);
586 BindingSource *a = g_object_new (binding_source_get_type (), NULL);
587 BindingSource *b = g_object_new (binding_source_get_type (), NULL);
588 BindingSource *c = g_object_new (binding_source_get_type (), NULL);
589 GBinding *binding_1, *binding_2;
591 g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=621782");
594 binding_1 = g_object_bind_property (a, "foo", b, "foo", G_BINDING_BIDIRECTIONAL);
595 g_object_add_weak_pointer (G_OBJECT (binding_1), (gpointer *) &binding_1);
597 binding_2 = g_object_bind_property (b, "foo", c, "foo", G_BINDING_BIDIRECTIONAL);
598 g_object_add_weak_pointer (G_OBJECT (binding_2), (gpointer *) &binding_2);
600 /* verify the chain */
601 g_object_set (a, "foo", 42, NULL);
602 g_assert_cmpint (a->foo, ==, b->foo);
603 g_assert_cmpint (b->foo, ==, c->foo);
605 /* unbind A -> B and B -> C */
606 g_object_unref (binding_1);
607 g_assert_null (binding_1);
608 g_object_unref (binding_2);
609 g_assert_null (binding_2);
611 /* bind A -> C directly */
612 binding_2 = g_object_bind_property (a, "foo", c, "foo", G_BINDING_BIDIRECTIONAL);
614 /* verify the chain is broken */
615 g_object_set (a, "foo", 47, NULL);
616 g_assert_cmpint (a->foo, !=, b->foo);
617 g_assert_cmpint (a->foo, ==, c->foo);
625 binding_sync_create (void)
627 BindingSource *source = g_object_new (binding_source_get_type (),
630 BindingTarget *target = g_object_new (binding_target_get_type (),
635 binding = g_object_bind_property (source, "foo",
637 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
639 g_assert_cmpint (source->foo, ==, 42);
640 g_assert_cmpint (target->bar, ==, 42);
642 g_object_set (source, "foo", 47, NULL);
643 g_assert_cmpint (source->foo, ==, target->bar);
645 g_object_unref (binding);
647 g_object_set (target, "bar", 49, NULL);
649 binding = g_object_bind_property (source, "foo",
651 G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
652 g_assert_cmpint (source->foo, ==, 47);
653 g_assert_cmpint (target->bar, ==, 47);
655 g_object_unref (source);
656 g_object_unref (target);
660 binding_invert_boolean (void)
662 BindingSource *source = g_object_new (binding_source_get_type (),
665 BindingTarget *target = g_object_new (binding_target_get_type (),
670 binding = g_object_bind_property (source, "toggle",
672 G_BINDING_BIDIRECTIONAL | G_BINDING_INVERT_BOOLEAN);
674 g_assert_true (source->toggle);
675 g_assert_false (target->toggle);
677 g_object_set (source, "toggle", FALSE, NULL);
678 g_assert_false (source->toggle);
679 g_assert_true (target->toggle);
681 g_object_set (target, "toggle", FALSE, NULL);
682 g_assert_true (source->toggle);
683 g_assert_false (target->toggle);
685 g_object_unref (binding);
686 g_object_unref (source);
687 g_object_unref (target);
691 binding_same_object (void)
693 BindingSource *source = g_object_new (binding_source_get_type (),
699 binding = g_object_bind_property (source, "foo",
701 G_BINDING_BIDIRECTIONAL);
702 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
704 g_object_set (source, "foo", 10, NULL);
705 g_assert_cmpint (source->foo, ==, 10);
706 g_assert_cmpint (source->bar, ==, 10);
707 g_object_set (source, "bar", 30, NULL);
708 g_assert_cmpint (source->foo, ==, 30);
709 g_assert_cmpint (source->bar, ==, 30);
711 g_object_unref (source);
712 g_assert_null (binding);
716 binding_unbind (void)
718 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
719 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
722 binding = g_object_bind_property (source, "foo",
725 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
727 g_object_set (source, "foo", 42, NULL);
728 g_assert_cmpint (source->foo, ==, target->bar);
730 g_object_set (target, "bar", 47, NULL);
731 g_assert_cmpint (source->foo, !=, target->bar);
733 g_binding_unbind (binding);
734 g_assert_null (binding);
736 g_object_set (source, "foo", 0, NULL);
737 g_assert_cmpint (source->foo, !=, target->bar);
739 g_object_unref (source);
740 g_object_unref (target);
743 /* g_binding_unbind() has a special case for this */
744 source = g_object_new (binding_source_get_type (), NULL);
745 binding = g_object_bind_property (source, "foo",
748 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
750 g_binding_unbind (binding);
751 g_assert_null (binding);
753 g_object_unref (source);
756 /* When source or target die, so does the binding if there is no other ref */
758 binding_unbind_weak (void)
761 BindingSource *source;
762 BindingTarget *target;
764 /* first source, then target */
765 source = g_object_new (binding_source_get_type (), NULL);
766 target = g_object_new (binding_target_get_type (), NULL);
767 binding = g_object_bind_property (source, "foo",
770 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
771 g_assert_nonnull (binding);
772 g_object_unref (source);
773 g_assert_null (binding);
774 g_object_unref (target);
775 g_assert_null (binding);
777 /* first target, then source */
778 source = g_object_new (binding_source_get_type (), NULL);
779 target = g_object_new (binding_target_get_type (), NULL);
780 binding = g_object_bind_property (source, "foo",
783 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
784 g_assert_nonnull (binding);
785 g_object_unref (target);
786 g_assert_null (binding);
787 g_object_unref (source);
788 g_assert_null (binding);
790 /* target and source are the same */
791 source = g_object_new (binding_source_get_type (), NULL);
792 binding = g_object_bind_property (source, "foo",
795 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
796 g_assert_nonnull (binding);
797 g_object_unref (source);
798 g_assert_null (binding);
801 /* Test that every call to unbind() after the first is a noop */
803 binding_unbind_multiple (void)
805 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
806 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
810 g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1373");
812 binding = g_object_bind_property (source, "foo",
815 g_object_ref (binding);
816 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
817 g_assert_nonnull (binding);
819 /* this shouldn't crash */
820 for (i = 0; i < 50; i++)
822 g_binding_unbind (binding);
823 g_assert_nonnull (binding);
826 g_object_unref (binding);
827 g_assert_null (binding);
829 g_object_unref (source);
830 g_object_unref (target);
836 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
837 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
840 /* double -> boolean is not supported */
841 binding = g_object_bind_property (source, "double-value",
844 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
846 g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_CRITICAL,
847 "*Unable to convert*double*boolean*");
848 g_object_set (source, "double-value", 1.0, NULL);
849 g_test_assert_expected_messages ();
851 g_object_unref (source);
852 g_object_unref (target);
853 g_assert_null (binding);
857 transform_to_func (GBinding *binding,
858 const GValue *value_a,
862 if (g_value_type_compatible (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b)))
864 g_value_copy (value_a, value_b);
868 if (g_value_type_transformable (G_VALUE_TYPE (value_a), G_VALUE_TYPE (value_b)))
870 if (g_value_transform (value_a, value_b))
878 binding_interface (void)
880 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
881 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
884 GClosure *transform_to;
886 /* binding a generic object property to an interface-valued one */
887 binding = g_object_bind_property (source, "object",
891 baa = g_object_new (baa_get_type (), NULL);
892 g_object_set (source, "object", baa, NULL);
893 g_object_unref (baa);
895 g_binding_unbind (binding);
897 /* the same, with a generic marshaller */
898 transform_to = g_cclosure_new (G_CALLBACK (transform_to_func), NULL, NULL);
899 g_closure_set_marshal (transform_to, g_cclosure_marshal_generic);
900 binding = g_object_bind_property_with_closures (source, "object",
906 baa = g_object_new (baa_get_type (), NULL);
907 g_object_set (source, "object", baa, NULL);
908 g_object_unref (baa);
910 g_binding_unbind (binding);
912 g_object_unref (source);
913 g_object_unref (target);
922 gint *count; /* (atomic) */
923 } ConcurrentUnbindData;
926 concurrent_unbind_func (gpointer data)
928 ConcurrentUnbindData *unbind_data = data;
930 g_mutex_lock (unbind_data->lock);
931 g_atomic_int_inc (unbind_data->count);
932 while (*unbind_data->wait)
933 g_cond_wait (unbind_data->cond, unbind_data->lock);
934 g_mutex_unlock (unbind_data->lock);
935 g_binding_unbind (unbind_data->binding);
936 g_object_unref (unbind_data->binding);
942 binding_concurrent_unbind (void)
946 g_test_summary ("Test that unbinding from multiple threads concurrently works correctly");
948 for (i = 0; i < 50; i++)
950 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
951 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
953 GQueue threads = G_QUEUE_INIT;
956 gboolean wait = TRUE;
957 gint count = 0; /* (atomic) */
958 ConcurrentUnbindData *data;
960 g_mutex_init (&lock);
963 binding = g_object_bind_property (source, "foo",
965 G_BINDING_BIDIRECTIONAL);
966 g_object_ref (binding);
968 for (j = 0; j < 10; j++)
970 data = g_new0 (ConcurrentUnbindData, 1);
972 data->binding = g_object_ref (binding);
976 data->count = &count;
978 data->thread = g_thread_new ("binding-concurrent", concurrent_unbind_func, data);
979 g_queue_push_tail (&threads, data);
982 /* wait until all threads are started */
983 while (g_atomic_int_get (&count) < 10)
986 g_mutex_lock (&lock);
988 g_cond_broadcast (&cond);
989 g_mutex_unlock (&lock);
991 while ((data = g_queue_pop_head (&threads)))
993 g_thread_join (data->thread);
997 g_mutex_clear (&lock);
998 g_cond_clear (&cond);
1000 g_object_unref (binding);
1001 g_object_unref (source);
1002 g_object_unref (target);
1010 gint *count; /* (atomic) */
1012 } ConcurrentFinalizeData;
1015 concurrent_finalize_func (gpointer data)
1017 ConcurrentFinalizeData *finalize_data = data;
1019 g_mutex_lock (finalize_data->lock);
1020 g_atomic_int_inc (finalize_data->count);
1021 while (*finalize_data->wait)
1022 g_cond_wait (finalize_data->cond, finalize_data->lock);
1023 g_mutex_unlock (finalize_data->lock);
1024 g_object_unref (finalize_data->object);
1025 g_free (finalize_data);
1031 binding_concurrent_finalizing (void)
1035 g_test_summary ("Test that finalizing source/target from multiple threads concurrently works correctly");
1037 for (i = 0; i < 50; i++)
1039 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
1040 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
1044 gboolean wait = TRUE;
1045 ConcurrentFinalizeData *data;
1046 GThread *source_thread, *target_thread;
1047 gint count = 0; /* (atomic) */
1049 g_mutex_init (&lock);
1050 g_cond_init (&cond);
1052 binding = g_object_bind_property (source, "foo",
1054 G_BINDING_BIDIRECTIONAL);
1055 g_object_ref (binding);
1057 data = g_new0 (ConcurrentFinalizeData, 1);
1058 data->object = (GObject *) source;
1062 data->count = &count;
1063 source_thread = g_thread_new ("binding-concurrent", concurrent_finalize_func, data);
1065 data = g_new0 (ConcurrentFinalizeData, 1);
1066 data->object = (GObject *) target;
1070 data->count = &count;
1071 target_thread = g_thread_new ("binding-concurrent", concurrent_finalize_func, data);
1073 /* wait until all threads are started */
1074 while (g_atomic_int_get (&count) < 2)
1077 g_mutex_lock (&lock);
1079 g_cond_broadcast (&cond);
1080 g_mutex_unlock (&lock);
1082 g_thread_join (source_thread);
1083 g_thread_join (target_thread);
1085 g_mutex_clear (&lock);
1086 g_cond_clear (&cond);
1088 g_object_unref (binding);
1093 binding_dispose_source (void)
1095 /* Test that the source can be disposed */
1096 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
1097 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
1100 g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2676");
1102 binding = g_object_bind_property (source, "foo",
1106 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
1108 g_object_run_dispose (G_OBJECT (source));
1109 g_assert_null (binding);
1111 g_object_unref (target);
1112 g_object_unref (source);
1116 binding_dispose_target (void)
1118 /* Test that the target can be disposed */
1119 BindingSource *source = g_object_new (binding_source_get_type (), NULL);
1120 BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
1123 g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2676");
1125 binding = g_object_bind_property (source, "foo",
1129 g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
1131 g_object_run_dispose (G_OBJECT (target));
1132 g_assert_null (binding);
1134 g_object_unref (target);
1135 g_object_unref (source);
1139 main (int argc, char *argv[])
1141 g_test_init (&argc, &argv, NULL);
1143 g_test_add_func ("/binding/default", binding_default);
1144 g_test_add_func ("/binding/canonicalisation", binding_canonicalisation);
1145 g_test_add_func ("/binding/bidirectional", binding_bidirectional);
1146 g_test_add_func ("/binding/transform", binding_transform);
1147 g_test_add_func ("/binding/transform-default", binding_transform_default);
1148 g_test_add_func ("/binding/transform-closure", binding_transform_closure);
1149 g_test_add_func ("/binding/chain", binding_chain);
1150 g_test_add_func ("/binding/sync-create", binding_sync_create);
1151 g_test_add_func ("/binding/invert-boolean", binding_invert_boolean);
1152 g_test_add_func ("/binding/same-object", binding_same_object);
1153 g_test_add_func ("/binding/unbind", binding_unbind);
1154 g_test_add_func ("/binding/unbind-weak", binding_unbind_weak);
1155 g_test_add_func ("/binding/unbind-multiple", binding_unbind_multiple);
1156 g_test_add_func ("/binding/fail", binding_fail);
1157 g_test_add_func ("/binding/interface", binding_interface);
1158 g_test_add_func ("/binding/concurrent-unbind", binding_concurrent_unbind);
1159 g_test_add_func ("/binding/concurrent-finalizing", binding_concurrent_finalizing);
1160 g_test_add_func ("/binding/dispose-source", binding_dispose_source);
1161 g_test_add_func ("/binding/dispose-target", binding_dispose_target);
1163 return g_test_run ();