Branch data Line data Source code
1 : : /* GLib testing framework examples and tests
2 : : * Copyright (C) 2008 Imendio AB
3 : : * Authors: Tim Janik
4 : : *
5 : : * SPDX-License-Identifier: LicenseRef-old-glib-tests
6 : : *
7 : : * This work is provided "as is"; redistribution and modification
8 : : * in whole or in part, in any medium, physical or electronic is
9 : : * permitted without restriction.
10 : : *
11 : : * This work is distributed in the hope that it will be useful,
12 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 : : *
15 : : * In no event shall the authors or contributors be liable for any
16 : : * direct, indirect, incidental, special, exemplary, or consequential
17 : : * damages (including, but not limited to, procurement of substitute
18 : : * goods or services; loss of use, data, or profits; or business
19 : : * interruption) however caused and on any theory of liability, whether
20 : : * in contract, strict liability, or tort (including negligence or
21 : : * otherwise) arising in any way out of the use of this software, even
22 : : * if advised of the possibility of such damage.
23 : : */
24 : :
25 : : #ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
26 : : #define GLIB_DISABLE_DEPRECATION_WARNINGS
27 : : #endif
28 : :
29 : : #include <glib.h>
30 : : #include <glib-object.h>
31 : :
32 : : static int mtsafe_call_counter = 0; /* multi thread safe call counter, must be accessed atomically */
33 : : static int unsafe_call_counter = 0; /* single-threaded call counter */
34 : : static GCond sync_cond;
35 : : static GMutex sync_mutex;
36 : :
37 : : #define NUM_COUNTER_INCREMENTS 100000
38 : :
39 : : static void
40 : 6 : call_counter_init (gpointer tclass)
41 : : {
42 : : int i;
43 : 600006 : for (i = 0; i < NUM_COUNTER_INCREMENTS; i++)
44 : : {
45 : 600000 : int saved_unsafe_call_counter = unsafe_call_counter;
46 : 600000 : g_atomic_int_add (&mtsafe_call_counter, 1); /* real call count update */
47 : 600000 : g_thread_yield(); /* let concurrent threads corrupt the unsafe_call_counter state */
48 : 600000 : unsafe_call_counter = 1 + saved_unsafe_call_counter; /* non-atomic counter update */
49 : : }
50 : 6 : }
51 : :
52 : 2 : static void interface_per_class_init (void) { call_counter_init (NULL); }
53 : :
54 : : /* define 3 test interfaces */
55 : : typedef GTypeInterface MyFace0Interface;
56 : : static GType my_face0_get_type (void);
57 : 1 : G_DEFINE_INTERFACE (MyFace0, my_face0, G_TYPE_OBJECT)
58 : 1 : static void my_face0_default_init (MyFace0Interface *iface) { call_counter_init (iface); }
59 : : typedef GTypeInterface MyFace1Interface;
60 : : static GType my_face1_get_type (void);
61 : 1 : G_DEFINE_INTERFACE (MyFace1, my_face1, G_TYPE_OBJECT)
62 : 1 : static void my_face1_default_init (MyFace1Interface *iface) { call_counter_init (iface); }
63 : :
64 : : /* define 3 test objects, adding interfaces 0 & 1, and adding interface 2 after class initialization */
65 : : typedef GObject MyTester0;
66 : : typedef GObjectClass MyTester0Class;
67 : : static GType my_tester0_get_type (void);
68 : 5002 : G_DEFINE_TYPE_WITH_CODE (MyTester0, my_tester0, G_TYPE_OBJECT,
69 : : G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
70 : : G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
71 : 5000 : static void my_tester0_init (MyTester0*t) {}
72 : 1 : static void my_tester0_class_init (MyTester0Class*c) { call_counter_init (c); }
73 : : typedef GObject MyTester1;
74 : : typedef GObjectClass MyTester1Class;
75 : :
76 : : /* Disabled for now (see https://bugzilla.gnome.org/show_bug.cgi?id=687659) */
77 : : #if 0
78 : : typedef GTypeInterface MyFace2Interface;
79 : : static GType my_face2_get_type (void);
80 : : G_DEFINE_INTERFACE (MyFace2, my_face2, G_TYPE_OBJECT)
81 : : static void my_face2_default_init (MyFace2Interface *iface) { call_counter_init (iface); }
82 : :
83 : : static GType my_tester1_get_type (void);
84 : : G_DEFINE_TYPE_WITH_CODE (MyTester1, my_tester1, G_TYPE_OBJECT,
85 : : G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
86 : : G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
87 : : static void my_tester1_init (MyTester1*t) {}
88 : : static void my_tester1_class_init (MyTester1Class*c) { call_counter_init (c); }
89 : : typedef GObject MyTester2;
90 : : typedef GObjectClass MyTester2Class;
91 : : static GType my_tester2_get_type (void);
92 : : G_DEFINE_TYPE_WITH_CODE (MyTester2, my_tester2, G_TYPE_OBJECT,
93 : : G_IMPLEMENT_INTERFACE (my_face0_get_type(), interface_per_class_init)
94 : : G_IMPLEMENT_INTERFACE (my_face1_get_type(), interface_per_class_init))
95 : : static void my_tester2_init (MyTester2*t) {}
96 : : static void my_tester2_class_init (MyTester2Class*c) { call_counter_init (c); }
97 : :
98 : : static gpointer
99 : : tester_init_thread (gpointer data)
100 : : {
101 : : const GInterfaceInfo face2_interface_info = { (GInterfaceInitFunc) interface_per_class_init, NULL, NULL };
102 : : gpointer klass;
103 : : /* first, synchronize with other threads,
104 : : * then run interface and class initializers,
105 : : * using unsafe_call_counter concurrently
106 : : */
107 : : g_mutex_lock (&sync_mutex);
108 : : g_mutex_unlock (&sync_mutex);
109 : : /* test default interface initialization for face0 */
110 : : g_type_default_interface_unref (g_type_default_interface_ref (my_face0_get_type()));
111 : : /* test class initialization, face0 per-class initializer, face1 default and per-class initializer */
112 : : klass = g_type_class_ref ((GType) data);
113 : : /* test face2 default and per-class initializer, after class_init */
114 : : g_type_add_interface_static (G_TYPE_FROM_CLASS (klass), my_face2_get_type(), &face2_interface_info);
115 : : /* cleanups */
116 : : g_type_class_unref (klass);
117 : : return NULL;
118 : : }
119 : :
120 : : static void
121 : : test_threaded_class_init (void)
122 : : {
123 : : GThread *t1, *t2, *t3;
124 : :
125 : : /* pause newly created threads */
126 : : g_mutex_lock (&sync_mutex);
127 : :
128 : : /* create threads */
129 : : t1 = g_thread_create (tester_init_thread, (gpointer) my_tester0_get_type(), TRUE, NULL);
130 : : t2 = g_thread_create (tester_init_thread, (gpointer) my_tester1_get_type(), TRUE, NULL);
131 : : t3 = g_thread_create (tester_init_thread, (gpointer) my_tester2_get_type(), TRUE, NULL);
132 : :
133 : : /* execute threads */
134 : : g_mutex_unlock (&sync_mutex);
135 : : while (g_atomic_int_get (&mtsafe_call_counter) < (3 + 3 + 3 * 3) * NUM_COUNTER_INCREMENTS)
136 : : {
137 : : if (g_test_verbose())
138 : : g_printerr ("Initializers counted: %u\n", g_atomic_int_get (&mtsafe_call_counter));
139 : : g_usleep (50 * 1000); /* wait for threads to complete */
140 : : }
141 : : if (g_test_verbose())
142 : : g_printerr ("Total initializers: %u\n", g_atomic_int_get (&mtsafe_call_counter));
143 : : /* ensure non-corrupted counter updates */
144 : : g_assert_cmpint (g_atomic_int_get (&mtsafe_call_counter), ==, unsafe_call_counter);
145 : :
146 : : g_thread_join (t1);
147 : : g_thread_join (t2);
148 : : g_thread_join (t3);
149 : : }
150 : : #endif
151 : :
152 : : typedef struct {
153 : : GObject parent;
154 : : char *name;
155 : : } PropTester;
156 : : typedef GObjectClass PropTesterClass;
157 : : static GType prop_tester_get_type (void);
158 : 4 : G_DEFINE_TYPE (PropTester, prop_tester, G_TYPE_OBJECT)
159 : : #define PROP_NAME 1
160 : : static void
161 : 2 : prop_tester_init (PropTester* t)
162 : : {
163 : 2 : if (t->name == NULL)
164 : : { } /* needs unit test framework initialization: g_test_bug ("race initializing properties"); */
165 : 2 : }
166 : : static void
167 : 2 : prop_tester_set_property (GObject *object,
168 : : guint property_id,
169 : : const GValue *value,
170 : : GParamSpec *pspec)
171 : 2 : {}
172 : : static void
173 : 1 : prop_tester_class_init (PropTesterClass *c)
174 : : {
175 : : int i;
176 : : GParamSpec *param;
177 : 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (c);
178 : :
179 : 1 : gobject_class->set_property = prop_tester_set_property; /* silence GObject checks */
180 : :
181 : 1 : g_mutex_lock (&sync_mutex);
182 : 1 : g_cond_signal (&sync_cond);
183 : 1 : g_mutex_unlock (&sync_mutex);
184 : :
185 : 101 : for (i = 0; i < 100; i++) /* wait a bit. */
186 : 100 : g_thread_yield();
187 : :
188 : 1 : call_counter_init (c);
189 : 1 : param = g_param_spec_string ("name", "name_i18n",
190 : : "yet-more-wasteful-i18n",
191 : : NULL,
192 : : G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE |
193 : : G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB |
194 : : G_PARAM_STATIC_NICK);
195 : 1 : g_object_class_install_property (gobject_class, PROP_NAME, param);
196 : 1 : }
197 : :
198 : : static gpointer
199 : 2 : object_create (gpointer data)
200 : : {
201 : 2 : GObject *obj = g_object_new (prop_tester_get_type(), "name", "fish", NULL);
202 : 2 : g_object_unref (obj);
203 : 2 : return NULL;
204 : : }
205 : :
206 : : static void
207 : 1 : test_threaded_object_init (void)
208 : : {
209 : : GThread *creator;
210 : 1 : g_mutex_lock (&sync_mutex);
211 : :
212 : 1 : creator = g_thread_create (object_create, NULL, TRUE, NULL);
213 : : /* really provoke the race */
214 : 1 : g_cond_wait (&sync_cond, &sync_mutex);
215 : :
216 : 1 : object_create (NULL);
217 : 1 : g_mutex_unlock (&sync_mutex);
218 : :
219 : 1 : g_thread_join (creator);
220 : 1 : }
221 : :
222 : : typedef struct {
223 : : MyTester0 *strong;
224 : : guint unref_delay;
225 : : } UnrefInThreadData;
226 : :
227 : : static gpointer
228 : 5000 : unref_in_thread (gpointer p)
229 : : {
230 : 5000 : UnrefInThreadData *data = p;
231 : :
232 : 5000 : g_usleep (data->unref_delay);
233 : 5000 : g_object_unref (data->strong);
234 : :
235 : 5000 : return NULL;
236 : : }
237 : :
238 : : /* undefine to see this test fail without GWeakRef */
239 : : #define HAVE_G_WEAK_REF
240 : :
241 : : #define SLEEP_MIN_USEC 1
242 : : #define SLEEP_MAX_USEC 10
243 : :
244 : : static void
245 : 1 : test_threaded_weak_ref (void)
246 : : {
247 : : guint i;
248 : 1 : guint get_wins = 0, unref_wins = 0;
249 : : guint n;
250 : :
251 : 1 : if (g_test_thorough ())
252 : 0 : n = NUM_COUNTER_INCREMENTS;
253 : : else
254 : 1 : n = NUM_COUNTER_INCREMENTS / 20;
255 : :
256 : : #ifdef G_OS_WIN32
257 : : /* On Windows usleep has millisecond resolution and gets rounded up
258 : : * leading to the test running for a long time. */
259 : : n /= 10;
260 : : #endif
261 : :
262 : 5001 : for (i = 0; i < n; i++)
263 : : {
264 : : UnrefInThreadData data;
265 : : #ifdef HAVE_G_WEAK_REF
266 : : /* GWeakRef<MyTester0> in C++ terms */
267 : : GWeakRef weak;
268 : : #else
269 : : gpointer weak;
270 : : #endif
271 : : MyTester0 *strengthened;
272 : : guint get_delay;
273 : : GThread *thread;
274 : 5000 : GError *error = NULL;
275 : :
276 : 5000 : if (g_test_verbose () && (i % (n/20)) == 0)
277 : 0 : g_printerr ("%u%%\n", ((i * 100) / n));
278 : :
279 : : /* Have an object and a weak ref to it */
280 : 5000 : data.strong = g_object_new (my_tester0_get_type (), NULL);
281 : :
282 : : #ifdef HAVE_G_WEAK_REF
283 : 5000 : g_weak_ref_init (&weak, data.strong);
284 : : #else
285 : : weak = data.strong;
286 : : g_object_add_weak_pointer ((GObject *) weak, &weak);
287 : : #endif
288 : :
289 : : /* Delay for a random time on each side of the race, to perturb the
290 : : * timing. Ideally, we want each side to win half the races; on
291 : : * smcv's laptop, these timings are about right.
292 : : */
293 : 5000 : data.unref_delay = g_random_int_range (SLEEP_MIN_USEC / 2, SLEEP_MAX_USEC / 2);
294 : 5000 : get_delay = g_random_int_range (SLEEP_MIN_USEC, SLEEP_MAX_USEC);
295 : :
296 : : /* One half of the race is to unref the shared object */
297 : 5000 : thread = g_thread_create (unref_in_thread, &data, TRUE, &error);
298 : 5000 : g_assert_no_error (error);
299 : :
300 : : /* The other half of the race is to get the object from the "global
301 : : * singleton"
302 : : */
303 : 5000 : g_usleep (get_delay);
304 : :
305 : : #ifdef HAVE_G_WEAK_REF
306 : 5000 : strengthened = g_weak_ref_get (&weak);
307 : : #else
308 : : /* Spot the unsafe pointer access! In GDBusConnection this is rather
309 : : * better-hidden, but ends up with essentially the same thing, albeit
310 : : * cleared in dispose() rather than by a traditional weak pointer
311 : : */
312 : : strengthened = weak;
313 : :
314 : : if (strengthened != NULL)
315 : : g_object_ref (strengthened);
316 : : #endif
317 : :
318 : 5000 : if (strengthened != NULL)
319 : 3837 : g_assert (G_IS_OBJECT (strengthened));
320 : :
321 : : /* Wait for the thread to run */
322 : 5000 : g_thread_join (thread);
323 : :
324 : 5000 : if (strengthened != NULL)
325 : : {
326 : 3837 : get_wins++;
327 : 3837 : g_assert (G_IS_OBJECT (strengthened));
328 : 3837 : g_object_unref (strengthened);
329 : : }
330 : : else
331 : : {
332 : 1163 : unref_wins++;
333 : : }
334 : :
335 : : #ifdef HAVE_G_WEAK_REF
336 : 5000 : g_weak_ref_clear (&weak);
337 : : #else
338 : : if (weak != NULL)
339 : : g_object_remove_weak_pointer (weak, &weak);
340 : : #endif
341 : : }
342 : :
343 : 1 : if (g_test_verbose ())
344 : 0 : g_printerr ("Race won by get %u times, unref %u times\n",
345 : : get_wins, unref_wins);
346 : 1 : }
347 : :
348 : : typedef struct
349 : : {
350 : : GObject *object;
351 : : GWeakRef *weak;
352 : : gint started; /* (atomic) */
353 : : gint finished; /* (atomic) */
354 : : gint disposing; /* (atomic) */
355 : : } ThreadedWeakRefData;
356 : :
357 : : static void
358 : 1 : on_weak_ref_disposed (gpointer data,
359 : : GObject *gobj)
360 : 454521 : {
361 : 1 : ThreadedWeakRefData *thread_data = data;
362 : :
363 : : /* Wait until the thread has started */
364 : 453252 : while (!g_atomic_int_get (&thread_data->started))
365 : 453251 : continue;
366 : :
367 : 1 : g_atomic_int_set (&thread_data->disposing, 1);
368 : :
369 : : /* Wait for the thread to act, so that the object is still valid */
370 : 1271 : while (!g_atomic_int_get (&thread_data->finished))
371 : 1270 : continue;
372 : :
373 : 1 : g_atomic_int_set (&thread_data->disposing, 0);
374 : 1 : }
375 : :
376 : : static gpointer
377 : 1 : on_other_thread_weak_ref (gpointer user_data)
378 : 2 : {
379 : 1 : ThreadedWeakRefData *thread_data = user_data;
380 : 1 : GObject *object = thread_data->object;
381 : :
382 : 1 : g_atomic_int_set (&thread_data->started, 1);
383 : :
384 : : /* Ensure we've started disposal */
385 : 3 : while (!g_atomic_int_get (&thread_data->disposing))
386 : 2 : continue;
387 : :
388 : 1 : g_object_ref (object);
389 : 1 : g_weak_ref_set (thread_data->weak, object);
390 : 1 : g_object_unref (object);
391 : :
392 : 1 : g_assert_cmpint (thread_data->disposing, ==, 1);
393 : 1 : g_atomic_int_set (&thread_data->finished, 1);
394 : :
395 : 1 : return NULL;
396 : : }
397 : :
398 : : static void
399 : 1 : test_threaded_weak_ref_finalization (void)
400 : : {
401 : 1 : GObject *obj = g_object_new (G_TYPE_OBJECT, NULL);
402 : 1 : GWeakRef weak = { { GUINT_TO_POINTER (0xDEADBEEFU) } };
403 : 1 : ThreadedWeakRefData thread_data = {
404 : : .object = obj, .weak = &weak, .started = 0, .finished = 0
405 : : };
406 : :
407 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2390");
408 : 1 : g_test_summary ("Test that a weak ref added by another thread during dispose "
409 : : "of a GObject is cleared during finalisation. "
410 : : "Use on_weak_ref_disposed() to synchronize the other thread "
411 : : "with the dispose vfunc.");
412 : :
413 : 1 : g_weak_ref_init (&weak, NULL);
414 : 1 : g_object_weak_ref (obj, on_weak_ref_disposed, &thread_data);
415 : :
416 : 1 : g_assert_cmpint (obj->ref_count, ==, 1);
417 : 1 : g_thread_unref (g_thread_new ("on_other_thread",
418 : : on_other_thread_weak_ref,
419 : : &thread_data));
420 : 1 : g_object_unref (obj);
421 : :
422 : : /* This is what this test is about: at this point the weak reference
423 : : * should have been unset (and not point to a dead object either). */
424 : 1 : g_assert_null (g_weak_ref_get (&weak));
425 : 1 : }
426 : :
427 : : typedef struct
428 : : {
429 : : GObject *object;
430 : : int done; /* (atomic) */
431 : : int toggles; /* (atomic) */
432 : : } ToggleNotifyThreadData;
433 : :
434 : : static gpointer
435 : 2 : on_reffer_thread (gpointer user_data)
436 : : {
437 : 2 : ToggleNotifyThreadData *thread_data = user_data;
438 : :
439 : 1560871 : while (!g_atomic_int_get (&thread_data->done))
440 : : {
441 : 1560869 : g_object_ref (thread_data->object);
442 : 1560869 : g_object_unref (thread_data->object);
443 : : }
444 : :
445 : 2 : return NULL;
446 : : }
447 : :
448 : : static void
449 : 117507 : on_toggle_notify (gpointer data,
450 : : GObject *object,
451 : : gboolean is_last_ref)
452 : : {
453 : : /* Anything could be put here, but we don't care for this test.
454 : : * Actually having this empty made the bug to happen more frequently (being
455 : : * timing related).
456 : : */
457 : 117507 : }
458 : :
459 : : static gpointer
460 : 1 : on_toggler_thread (gpointer user_data)
461 : : {
462 : 1 : ToggleNotifyThreadData *thread_data = user_data;
463 : :
464 : 100002 : while (!g_atomic_int_get (&thread_data->done))
465 : : {
466 : 100001 : g_object_ref (thread_data->object);
467 : 100001 : g_object_remove_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
468 : 100001 : g_object_add_toggle_ref (thread_data->object, on_toggle_notify, thread_data);
469 : 100001 : g_object_unref (thread_data->object);
470 : 100001 : g_atomic_int_add (&thread_data->toggles, 1);
471 : : }
472 : :
473 : 1 : return NULL;
474 : : }
475 : :
476 : : static void
477 : 1 : test_threaded_toggle_notify (void)
478 : : {
479 : 1 : GObject *object = g_object_new (G_TYPE_OBJECT, NULL);
480 : 1 : ToggleNotifyThreadData data = { object, FALSE, 0 };
481 : : GThread *threads[3];
482 : : gsize i;
483 : 1 : const int n_iterations = g_test_thorough () ? 1000000 : 100000;
484 : :
485 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/2394");
486 : 1 : g_test_summary ("Test that toggle reference notifications can be changed "
487 : : "safely from another (the main) thread without causing the "
488 : : "notifying thread to abort");
489 : :
490 : 1 : g_object_add_toggle_ref (object, on_toggle_notify, &data);
491 : 1 : g_object_unref (object);
492 : :
493 : 1 : g_assert_cmpint (object->ref_count, ==, 1);
494 : 1 : threads[0] = g_thread_new ("on_reffer_thread", on_reffer_thread, &data);
495 : 1 : threads[1] = g_thread_new ("on_another_reffer_thread", on_reffer_thread, &data);
496 : 1 : threads[2] = g_thread_new ("on_main_toggler_thread", on_toggler_thread, &data);
497 : :
498 : : /* We need to wait here for the threads to run for a bit in order to make the
499 : : * race to happen, so we wait for an high number of toggle changes to be met
500 : : * so that we can be consistent on each platform.
501 : : */
502 : 40509226 : while (g_atomic_int_get (&data.toggles) < n_iterations)
503 : : ;
504 : 1 : g_atomic_int_set (&data.done, TRUE);
505 : :
506 : 4 : for (i = 0; i < G_N_ELEMENTS (threads); i++)
507 : 3 : g_thread_join (threads[i]);
508 : :
509 : 1 : g_assert_cmpint (object->ref_count, ==, 1);
510 : 1 : g_clear_object (&object);
511 : 1 : }
512 : :
513 : : static void
514 : 1 : test_threaded_g_pointer_bit_unlock_and_set (void)
515 : : {
516 : : GObject *obj;
517 : : gpointer plock;
518 : : gpointer ptr;
519 : : guintptr ptr2;
520 : : gpointer mangled_obj;
521 : :
522 : : #if defined(__GNUC__)
523 : : /* We should have at least one bit we can use safely for bit-locking */
524 : : G_STATIC_ASSERT (__alignof (GObject) > 1);
525 : : #endif
526 : :
527 : 1 : obj = g_object_new (G_TYPE_OBJECT, NULL);
528 : :
529 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0, NULL) == obj);
530 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, obj) == obj);
531 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 1, 0, NULL) != obj);
532 : :
533 : 1 : mangled_obj = obj;
534 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, mangled_obj) == obj);
535 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x3, mangled_obj) == obj);
536 : 1 : g_atomic_pointer_and (&mangled_obj, ~((gsize) 0x7));
537 : 1 : g_atomic_pointer_or (&mangled_obj, 0x2);
538 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, mangled_obj) != obj);
539 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, mangled_obj) == (gpointer) (((guintptr) obj) | ((guintptr) mangled_obj)));
540 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x3, mangled_obj) == (gpointer) (((guintptr) obj) | ((guintptr) mangled_obj)));
541 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, TRUE, 0x3, mangled_obj) == (gpointer) (((guintptr) obj) | ((guintptr) mangled_obj) | ((guintptr) 1)));
542 : 1 : g_atomic_pointer_and (&mangled_obj, ~((gsize) 0x2));
543 : 1 : g_assert_true (g_pointer_bit_lock_mask_ptr (obj, 0, 0, 0x2, mangled_obj) == obj);
544 : 1 : g_atomic_pointer_or (&mangled_obj, 0x2);
545 : :
546 : 1 : plock = obj;
547 : 1 : g_pointer_bit_lock (&plock, 0);
548 : 1 : g_assert_true (plock != obj);
549 : 1 : g_pointer_bit_unlock_and_set (&plock, 0, obj, 0);
550 : 1 : g_assert_true (plock == obj);
551 : :
552 : 1 : plock = obj;
553 : 1 : g_pointer_bit_lock_and_get (&plock, 0, &ptr2);
554 : 1 : g_assert_true ((gpointer) ptr2 == plock);
555 : 1 : g_assert_true (plock != obj);
556 : 1 : g_atomic_pointer_set (&plock, mangled_obj);
557 : 1 : g_pointer_bit_unlock_and_set (&plock, 0, obj, 0);
558 : 1 : g_assert_true (plock == obj);
559 : :
560 : 1 : plock = obj;
561 : 1 : g_pointer_bit_lock_and_get (&plock, 0, NULL);
562 : 1 : g_assert_true (plock != obj);
563 : 1 : g_atomic_pointer_set (&plock, mangled_obj);
564 : 1 : g_pointer_bit_unlock_and_set (&plock, 0, obj, 0x7);
565 : 1 : g_assert_true (plock != obj);
566 : 1 : g_assert_true (plock == (gpointer) (((guintptr) obj) | ((guintptr) mangled_obj)));
567 : :
568 : 1 : plock = NULL;
569 : 1 : g_pointer_bit_lock (&plock, 0);
570 : 1 : g_assert_true (plock != NULL);
571 : 1 : g_pointer_bit_unlock_and_set (&plock, 0, NULL, 0);
572 : 1 : g_assert_true (plock == NULL);
573 : :
574 : 1 : ptr = ((char *) obj) + 1;
575 : 1 : plock = obj;
576 : 1 : g_pointer_bit_lock (&plock, 0);
577 : 1 : g_assert_true (plock == ptr);
578 : 1 : g_test_expect_message ("GLib", G_LOG_LEVEL_CRITICAL,
579 : : "*assertion 'ptr == pointer_bit_lock_mask_ptr (ptr, lock_bit, FALSE, 0, NULL)' failed*");
580 : 1 : g_pointer_bit_unlock_and_set (&plock, 0, ptr, 0);
581 : 1 : g_test_assert_expected_messages ();
582 : 1 : g_assert_true (plock != ptr);
583 : 1 : g_assert_true (plock == obj);
584 : :
585 : 1 : g_object_unref (obj);
586 : 1 : }
587 : :
588 : : int
589 : 1 : main (int argc,
590 : : char *argv[])
591 : : {
592 : 1 : g_test_init (&argc, &argv, NULL);
593 : :
594 : : /* g_test_add_func ("/GObject/threaded-class-init", test_threaded_class_init); */
595 : 1 : g_test_add_func ("/GObject/threaded-object-init", test_threaded_object_init);
596 : 1 : g_test_add_func ("/GObject/threaded-weak-ref", test_threaded_weak_ref);
597 : 1 : g_test_add_func ("/GObject/threaded-weak-ref/on-finalization",
598 : : test_threaded_weak_ref_finalization);
599 : 1 : g_test_add_func ("/GObject/threaded-toggle-notify",
600 : : test_threaded_toggle_notify);
601 : 1 : g_test_add_func ("/GObject/threaded-g-pointer-bit-unlock-and-set",
602 : : test_threaded_g_pointer_bit_unlock_and_set);
603 : :
604 : 1 : return g_test_run();
605 : : }
|