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