1/*
2 * Copyright © 2008 Ryan Lortie
3 * Copyright © 2010 Codethink Limited
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * See the included COPYING file for more information.
11 */
12
13#include "config.h"
14
15/* LOCKS should be more than the number of contention
16 * counters in gthread.c in order to ensure we exercise
17 * the case where they overlap.
18 */
19#define LOCKS 48
20#define ITERATIONS 10000
21#define THREADS 100
22
23#include <glib.h>
24
25#if TEST_EMULATED_FUTEX
26
27#pragma GCC diagnostic push
28#pragma GCC diagnostic ignored "-Wmissing-prototypes"
29
30 /* this is defined for the 1bit-mutex-emufutex test.
31 *
32 * we want to test the emulated futex even if futex(2) is available.
33 */
34
35 /* side-step some glib build stuff */
36 #define GLIB_COMPILATION
37
38 /* rebuild gbitlock.c without futex support,
39 defining our own version of the g_bit_*lock symbols
40 */
41 #undef g_pointer_bit_lock
42 #undef g_pointer_bit_trylock
43 #undef g_pointer_bit_unlock
44
45 #define g_bit_lock _emufutex_g_bit_lock
46 #define g_bit_trylock _emufutex_g_bit_trylock
47 #define g_bit_unlock _emufutex_g_bit_unlock
48 #define g_pointer_bit_lock _emufutex_g_pointer_bit_lock
49 #define g_pointer_bit_trylock _emufutex_g_pointer_bit_trylock
50 #define g_pointer_bit_unlock _emufutex_g_pointer_bit_unlock
51
52 #define G_BIT_LOCK_FORCE_FUTEX_EMULATION
53
54 #include <glib/gbitlock.c>
55
56#pragma GCC diagnostic pop
57#endif
58
59volatile GThread *owners[LOCKS];
60volatile gint locks[LOCKS];
61volatile gpointer ptrs[LOCKS];
62volatile gint bits[LOCKS];
63
64static void
65acquire (int nr,
66 gboolean use_pointers)
67{
68 GThread *self;
69
70 self = g_thread_self ();
71
72 g_assert_cmpint (((gsize) ptrs) % sizeof(gint), ==, 0);
73
74 if (!(use_pointers ?
75 g_pointer_bit_trylock (&ptrs[nr], bits[nr])
76 : g_bit_trylock (address: &locks[nr], lock_bit: bits[nr])))
77 {
78 if (g_test_verbose ())
79 g_printerr (format: "thread %p going to block on lock %d\n", self, nr);
80
81 if (use_pointers)
82 g_pointer_bit_lock (&ptrs[nr], bits[nr]);
83 else
84 g_bit_lock (address: &locks[nr], lock_bit: bits[nr]);
85 }
86
87 g_assert (owners[nr] == NULL); /* hopefully nobody else is here */
88 owners[nr] = self;
89
90 /* let some other threads try to ruin our day */
91 g_thread_yield ();
92 g_thread_yield ();
93 g_thread_yield ();
94
95 g_assert (owners[nr] == self); /* hopefully this is still us... */
96 owners[nr] = NULL; /* make way for the next guy */
97
98 if (use_pointers)
99 g_pointer_bit_unlock (&ptrs[nr], bits[nr]);
100 else
101 g_bit_unlock (address: &locks[nr], lock_bit: bits[nr]);
102}
103
104static gpointer
105thread_func (gpointer data)
106{
107 gboolean use_pointers = GPOINTER_TO_INT (data);
108 gint i;
109 GRand *rand;
110
111 rand = g_rand_new ();
112
113 for (i = 0; i < ITERATIONS; i++)
114 acquire (nr: g_rand_int_range (rand_: rand, begin: 0, LOCKS), use_pointers);
115
116 g_rand_free (rand_: rand);
117
118 return NULL;
119}
120
121static void
122testcase (gconstpointer data)
123{
124 gboolean use_pointers = GPOINTER_TO_INT (data);
125 GThread *threads[THREADS];
126 int i;
127
128#ifdef TEST_EMULATED_FUTEX
129 #define SUFFIX "-emufutex"
130
131 /* ensure that we are using the emulated futex by checking
132 * (at compile-time) for the existence of 'g_futex_address_list'
133 */
134 g_assert (g_futex_address_list == NULL);
135#else
136 #define SUFFIX ""
137#endif
138
139 for (i = 0; i < LOCKS; i++)
140 bits[i] = g_random_int () % 32;
141
142 for (i = 0; i < THREADS; i++)
143 threads[i] = g_thread_new (name: "foo", func: thread_func,
144 GINT_TO_POINTER (use_pointers));
145
146 for (i = 0; i < THREADS; i++)
147 g_thread_join (thread: threads[i]);
148
149 for (i = 0; i < LOCKS; i++)
150 {
151 g_assert (owners[i] == NULL);
152 g_assert (locks[i] == 0);
153 }
154}
155
156int
157main (int argc, char **argv)
158{
159 g_test_init (argc: &argc, argv: &argv, NULL);
160
161 g_test_add_data_func (testpath: "/glib/1bit-mutex" SUFFIX "/int", test_data: (gpointer) 0, test_func: testcase);
162 g_test_add_data_func (testpath: "/glib/1bit-mutex" SUFFIX "/pointer", test_data: (gpointer) 1, test_func: testcase);
163
164 return g_test_run ();
165}
166

source code of gtk/subprojects/glib/glib/tests/1bit-mutex.c