1 /* GLib testing framework examples and tests
2 * Copyright (C) 2008 Imendio AB
5 * This work is provided "as is"; redistribution and modification
6 * in whole or in part, in any medium, physical or electronic is
7 * permitted without restriction.
9 * This work is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * In no event shall the authors or contributors be liable for any
14 * direct, indirect, incidental, special, exemplary, or consequential
15 * damages (including, but not limited to, procurement of substitute
16 * goods or services; loss of use, data, or profits; or business
17 * interruption) however caused and on any theory of liability, whether
18 * in contract, strict liability, or tort (including negligence or
19 * otherwise) arising in any way out of the use of this software, even
20 * if advised of the possibility of such damage.
22 #define GLIB_DISABLE_DEPRECATION_WARNINGS
24 #include <glib-object.h>
26 static volatile int mtsafe_call_counter
= 0; /* multi thread safe call counter */
27 static int unsafe_call_counter
= 0; /* single-threaded call counter */
28 static GCond sync_cond
;
29 static GMutex sync_mutex
;
31 #define NUM_COUNTER_INCREMENTS 100000
34 call_counter_init (gpointer tclass
)
37 for (i
= 0; i
< NUM_COUNTER_INCREMENTS
; i
++)
39 int saved_unsafe_call_counter
= unsafe_call_counter
;
40 g_atomic_int_add (&mtsafe_call_counter
, 1); /* real call count update */
41 g_thread_yield(); /* let concurrent threads corrupt the unsafe_call_counter state */
42 unsafe_call_counter
= 1 + saved_unsafe_call_counter
; /* non-atomic counter update */
46 static void interface_per_class_init (void) { call_counter_init (NULL
); }
48 /* define 3 test interfaces */
49 typedef GTypeInterface MyFace0Interface
;
50 static GType
my_face0_get_type (void);
51 G_DEFINE_INTERFACE (MyFace0
, my_face0
, G_TYPE_OBJECT
)
52 static void my_face0_default_init (MyFace0Interface
*iface
) { call_counter_init (iface
); }
53 typedef GTypeInterface MyFace1Interface
;
54 static GType
my_face1_get_type (void);
55 G_DEFINE_INTERFACE (MyFace1
, my_face1
, G_TYPE_OBJECT
)
56 static void my_face1_default_init (MyFace1Interface
*iface
) { call_counter_init (iface
); }
58 /* define 3 test objects, adding interfaces 0 & 1, and adding interface 2 after class initialization */
59 typedef GObject MyTester0
;
60 typedef GObjectClass MyTester0Class
;
61 static GType
my_tester0_get_type (void);
62 G_DEFINE_TYPE_WITH_CODE (MyTester0
, my_tester0
, G_TYPE_OBJECT
,
63 G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init
)
64 G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init
))
65 static void my_tester0_init (MyTester0
*t
) {}
66 static void my_tester0_class_init (MyTester0Class
*c
) { call_counter_init (c
); }
67 typedef GObject MyTester1
;
68 typedef GObjectClass MyTester1Class
;
70 /* Disabled for now (see https://bugzilla.gnome.org/show_bug.cgi?id=687659) */
72 typedef GTypeInterface MyFace2Interface
;
73 static GType
my_face2_get_type (void);
74 G_DEFINE_INTERFACE (MyFace2
, my_face2
, G_TYPE_OBJECT
)
75 static void my_face2_default_init (MyFace2Interface
*iface
) { call_counter_init (iface
); }
77 static GType
my_tester1_get_type (void);
78 G_DEFINE_TYPE_WITH_CODE (MyTester1
, my_tester1
, G_TYPE_OBJECT
,
79 G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init
)
80 G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init
))
81 static void my_tester1_init (MyTester1
*t
) {}
82 static void my_tester1_class_init (MyTester1Class
*c
) { call_counter_init (c
); }
83 typedef GObject MyTester2
;
84 typedef GObjectClass MyTester2Class
;
85 static GType
my_tester2_get_type (void);
86 G_DEFINE_TYPE_WITH_CODE (MyTester2
, my_tester2
, G_TYPE_OBJECT
,
87 G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init
)
88 G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init
))
89 static void my_tester2_init (MyTester2
*t
) {}
90 static void my_tester2_class_init (MyTester2Class
*c
) { call_counter_init (c
); }
93 tester_init_thread (gpointer data
)
95 const GInterfaceInfo face2_interface_info
= { (GInterfaceInitFunc
) interface_per_class_init
, NULL
, NULL
};
97 /* first, syncronize with other threads,
98 * then run interface and class initializers,
99 * using unsafe_call_counter concurrently
101 g_mutex_lock (&sync_mutex
);
102 g_mutex_unlock (&sync_mutex
);
103 /* test default interface initialization for face0 */
104 g_type_default_interface_unref (g_type_default_interface_ref (my_face0_get_type()));
105 /* test class initialization, face0 per-class initializer, face1 default and per-class initializer */
106 klass
= g_type_class_ref ((GType
) data
);
107 /* test face2 default and per-class initializer, after class_init */
108 g_type_add_interface_static (G_TYPE_FROM_CLASS (klass
), my_face2_get_type(), &face2_interface_info
);
110 g_type_class_unref (klass
);
115 test_threaded_class_init (void)
117 GThread
*t1
, *t2
, *t3
;
119 /* pause newly created threads */
120 g_mutex_lock (&sync_mutex
);
123 t1
= g_thread_create (tester_init_thread
, (gpointer
) my_tester0_get_type(), TRUE
, NULL
);
124 t2
= g_thread_create (tester_init_thread
, (gpointer
) my_tester1_get_type(), TRUE
, NULL
);
125 t3
= g_thread_create (tester_init_thread
, (gpointer
) my_tester2_get_type(), TRUE
, NULL
);
127 /* execute threads */
128 g_mutex_unlock (&sync_mutex
);
129 while (g_atomic_int_get (&mtsafe_call_counter
) < (3 + 3 + 3 * 3) * NUM_COUNTER_INCREMENTS
)
131 if (g_test_verbose())
132 g_printerr ("Initializers counted: %u\n", g_atomic_int_get (&mtsafe_call_counter
));
133 g_usleep (50 * 1000); /* wait for threads to complete */
135 if (g_test_verbose())
136 g_printerr ("Total initializers: %u\n", g_atomic_int_get (&mtsafe_call_counter
));
137 /* ensure non-corrupted counter updates */
138 g_assert_cmpint (g_atomic_int_get (&mtsafe_call_counter
), ==, unsafe_call_counter
);
150 typedef GObjectClass PropTesterClass
;
151 static GType
prop_tester_get_type (void);
152 G_DEFINE_TYPE (PropTester
, prop_tester
, G_TYPE_OBJECT
)
155 prop_tester_init (PropTester
* t
)
158 ; /* neds unit test framework initialization: g_test_bug ("race initializing properties"); */
161 prop_tester_set_property (GObject
*object
,
167 prop_tester_class_init (PropTesterClass
*c
)
171 GObjectClass
*gobject_class
= G_OBJECT_CLASS (c
);
173 gobject_class
->set_property
= prop_tester_set_property
; /* silence GObject checks */
175 g_mutex_lock (&sync_mutex
);
176 g_cond_signal (&sync_cond
);
177 g_mutex_unlock (&sync_mutex
);
179 for (i
= 0; i
< 100; i
++) /* wait a bit. */
182 call_counter_init (c
);
183 param
= g_param_spec_string ("name", "name_i18n",
184 "yet-more-wasteful-i18n",
186 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_WRITABLE
|
187 G_PARAM_STATIC_NAME
| G_PARAM_STATIC_BLURB
|
188 G_PARAM_STATIC_NICK
);
189 g_object_class_install_property (gobject_class
, PROP_NAME
, param
);
193 object_create (gpointer data
)
195 GObject
*obj
= g_object_new (prop_tester_get_type(), "name", "fish", NULL
);
196 g_object_unref (obj
);
201 test_threaded_object_init (void)
204 g_mutex_lock (&sync_mutex
);
206 creator
= g_thread_create (object_create
, NULL
, TRUE
, NULL
);
207 /* really provoke the race */
208 g_cond_wait (&sync_cond
, &sync_mutex
);
210 object_create (NULL
);
211 g_mutex_unlock (&sync_mutex
);
213 g_thread_join (creator
);
222 unref_in_thread (gpointer p
)
224 UnrefInThreadData
*data
= p
;
226 g_usleep (data
->unref_delay
);
227 g_object_unref (data
->strong
);
232 /* undefine to see this test fail without GWeakRef */
233 #define HAVE_G_WEAK_REF
235 #define SLEEP_MIN_USEC 1
236 #define SLEEP_MAX_USEC 10
239 test_threaded_weak_ref (void)
242 guint get_wins
= 0, unref_wins
= 0;
245 if (g_test_thorough ())
246 n
= NUM_COUNTER_INCREMENTS
;
248 n
= NUM_COUNTER_INCREMENTS
/ 20;
250 for (i
= 0; i
< n
; i
++)
252 UnrefInThreadData data
;
253 #ifdef HAVE_G_WEAK_REF
254 /* GWeakRef<MyTester0> in C++ terms */
259 MyTester0
*strengthened
;
262 GError
*error
= NULL
;
264 if (g_test_verbose () && (i
% (n
/20)) == 0)
265 g_printerr ("%u%%\n", ((i
* 100) / n
));
267 /* Have an object and a weak ref to it */
268 data
.strong
= g_object_new (my_tester0_get_type (), NULL
);
270 #ifdef HAVE_G_WEAK_REF
271 g_weak_ref_init (&weak
, data
.strong
);
274 g_object_add_weak_pointer ((GObject
*) weak
, &weak
);
277 /* Delay for a random time on each side of the race, to perturb the
278 * timing. Ideally, we want each side to win half the races; on
279 * smcv's laptop, these timings are about right.
281 data
.unref_delay
= g_random_int_range (SLEEP_MIN_USEC
/ 2, SLEEP_MAX_USEC
/ 2);
282 get_delay
= g_random_int_range (SLEEP_MIN_USEC
, SLEEP_MAX_USEC
);
284 /* One half of the race is to unref the shared object */
285 thread
= g_thread_create (unref_in_thread
, &data
, TRUE
, &error
);
286 g_assert_no_error (error
);
288 /* The other half of the race is to get the object from the "global
291 g_usleep (get_delay
);
293 #ifdef HAVE_G_WEAK_REF
294 strengthened
= g_weak_ref_get (&weak
);
296 /* Spot the unsafe pointer access! In GDBusConnection this is rather
297 * better-hidden, but ends up with essentially the same thing, albeit
298 * cleared in dispose() rather than by a traditional weak pointer
302 if (strengthened
!= NULL
)
303 g_object_ref (strengthened
);
306 if (strengthened
!= NULL
)
307 g_assert (G_IS_OBJECT (strengthened
));
309 /* Wait for the thread to run */
310 g_thread_join (thread
);
312 if (strengthened
!= NULL
)
315 g_assert (G_IS_OBJECT (strengthened
));
316 g_object_unref (strengthened
);
323 #ifdef HAVE_G_WEAK_REF
324 g_weak_ref_clear (&weak
);
327 g_object_remove_weak_pointer (weak
, &weak
);
331 if (g_test_verbose ())
332 g_printerr ("Race won by get %u times, unref %u times\n",
333 get_wins
, unref_wins
);
340 g_test_init (&argc
, &argv
, NULL
);
342 /* g_test_add_func ("/GObject/threaded-class-init", test_threaded_class_init); */
343 g_test_add_func ("/GObject/threaded-object-init", test_threaded_object_init
);
344 g_test_add_func ("/GObject/threaded-weak-ref", test_threaded_weak_ref
);