1 | /* Unit tests for GMutex |
2 | * Copyright (C) 2011 Red Hat, Inc |
3 | * Author: Matthias Clasen |
4 | * |
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. |
8 | * |
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. |
12 | * |
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. |
21 | */ |
22 | |
23 | /* We are testing some deprecated APIs here */ |
24 | #ifndef GLIB_DISABLE_DEPRECATION_WARNINGS |
25 | #define GLIB_DISABLE_DEPRECATION_WARNINGS |
26 | #endif |
27 | |
28 | #include <glib.h> |
29 | |
30 | #include <stdio.h> |
31 | |
32 | static void |
33 | test_mutex1 (void) |
34 | { |
35 | GMutex mutex; |
36 | |
37 | g_mutex_init (mutex: &mutex); |
38 | g_mutex_lock (mutex: &mutex); |
39 | g_mutex_unlock (mutex: &mutex); |
40 | g_mutex_lock (mutex: &mutex); |
41 | g_mutex_unlock (mutex: &mutex); |
42 | g_mutex_clear (mutex: &mutex); |
43 | } |
44 | |
45 | static void |
46 | test_mutex2 (void) |
47 | { |
48 | static GMutex mutex; |
49 | |
50 | g_mutex_lock (mutex: &mutex); |
51 | g_mutex_unlock (mutex: &mutex); |
52 | g_mutex_lock (mutex: &mutex); |
53 | g_mutex_unlock (mutex: &mutex); |
54 | } |
55 | |
56 | static void |
57 | test_mutex3 (void) |
58 | { |
59 | GMutex *mutex; |
60 | |
61 | mutex = g_mutex_new (); |
62 | g_mutex_lock (mutex); |
63 | g_mutex_unlock (mutex); |
64 | g_mutex_lock (mutex); |
65 | g_mutex_unlock (mutex); |
66 | g_mutex_free (mutex); |
67 | } |
68 | |
69 | static void |
70 | test_mutex4 (void) |
71 | { |
72 | static GMutex mutex; |
73 | gboolean ret; |
74 | |
75 | ret = g_mutex_trylock (mutex: &mutex); |
76 | g_assert (ret); |
77 | |
78 | /* no guarantees that mutex is recursive, so could return 0 or 1 */ |
79 | if (g_mutex_trylock (mutex: &mutex)) |
80 | g_mutex_unlock (mutex: &mutex); |
81 | |
82 | g_mutex_unlock (mutex: &mutex); |
83 | } |
84 | |
85 | #define LOCKS 48 |
86 | #define ITERATIONS 10000 |
87 | #define THREADS 100 |
88 | |
89 | |
90 | GThread *owners[LOCKS]; |
91 | GMutex locks[LOCKS]; |
92 | |
93 | static void |
94 | acquire (gint nr) |
95 | { |
96 | GThread *self; |
97 | |
98 | self = g_thread_self (); |
99 | |
100 | if (!g_mutex_trylock (mutex: &locks[nr])) |
101 | { |
102 | if (g_test_verbose ()) |
103 | g_printerr (format: "thread %p going to block on lock %d\n" , self, nr); |
104 | |
105 | g_mutex_lock (mutex: &locks[nr]); |
106 | } |
107 | |
108 | g_assert (owners[nr] == NULL); /* hopefully nobody else is here */ |
109 | owners[nr] = self; |
110 | |
111 | /* let some other threads try to ruin our day */ |
112 | g_thread_yield (); |
113 | g_thread_yield (); |
114 | g_thread_yield (); |
115 | |
116 | g_assert (owners[nr] == self); /* hopefully this is still us... */ |
117 | owners[nr] = NULL; /* make way for the next guy */ |
118 | |
119 | g_mutex_unlock (mutex: &locks[nr]); |
120 | } |
121 | |
122 | static gpointer |
123 | thread_func (gpointer data) |
124 | { |
125 | gint i; |
126 | GRand *rand; |
127 | |
128 | rand = g_rand_new (); |
129 | |
130 | for (i = 0; i < ITERATIONS; i++) |
131 | acquire (nr: g_rand_int_range (rand_: rand, begin: 0, LOCKS)); |
132 | |
133 | g_rand_free (rand_: rand); |
134 | |
135 | return NULL; |
136 | } |
137 | |
138 | static void |
139 | test_mutex5 (void) |
140 | { |
141 | gint i; |
142 | GThread *threads[THREADS]; |
143 | |
144 | for (i = 0; i < LOCKS; i++) |
145 | g_mutex_init (mutex: &locks[i]); |
146 | |
147 | for (i = 0; i < THREADS; i++) |
148 | threads[i] = g_thread_create (func: thread_func, NULL, TRUE, NULL); |
149 | |
150 | for (i = 0; i < THREADS; i++) |
151 | g_thread_join (thread: threads[i]); |
152 | |
153 | for (i = 0; i < LOCKS; i++) |
154 | g_mutex_clear (mutex: &locks[i]); |
155 | |
156 | for (i = 0; i < LOCKS; i++) |
157 | g_assert (owners[i] == NULL); |
158 | } |
159 | |
160 | #define COUNT_TO 100000000 |
161 | |
162 | static gboolean |
163 | do_addition (gint *value) |
164 | { |
165 | static GMutex lock; |
166 | gboolean more; |
167 | |
168 | /* test performance of "good" cases (ie: short critical sections) */ |
169 | g_mutex_lock (mutex: &lock); |
170 | if ((more = *value != COUNT_TO)) |
171 | if (*value != -1) |
172 | (*value)++; |
173 | g_mutex_unlock (mutex: &lock); |
174 | |
175 | return more; |
176 | } |
177 | |
178 | static gpointer |
179 | addition_thread (gpointer value) |
180 | { |
181 | while (do_addition (value)); |
182 | |
183 | return NULL; |
184 | } |
185 | |
186 | static void |
187 | test_mutex_perf (gconstpointer data) |
188 | { |
189 | guint n_threads = GPOINTER_TO_UINT (data); |
190 | GThread *threads[THREADS]; |
191 | gint64 start_time; |
192 | gdouble rate; |
193 | gint x = -1; |
194 | guint i; |
195 | |
196 | g_assert (n_threads <= G_N_ELEMENTS (threads)); |
197 | |
198 | for (i = 0; n_threads > 0 && i < n_threads - 1; i++) |
199 | threads[i] = g_thread_create (func: addition_thread, data: &x, TRUE, NULL); |
200 | |
201 | /* avoid measuring thread setup/teardown time */ |
202 | start_time = g_get_monotonic_time (); |
203 | g_atomic_int_set (&x, 0); |
204 | addition_thread (value: &x); |
205 | g_assert_cmpint (g_atomic_int_get (&x), ==, COUNT_TO); |
206 | rate = g_get_monotonic_time () - start_time; |
207 | rate = x / rate; |
208 | |
209 | for (i = 0; n_threads > 0 && i < n_threads - 1; i++) |
210 | g_thread_join (thread: threads[i]); |
211 | |
212 | g_test_maximized_result (maximized_quantity: rate, format: "%f mips" , rate); |
213 | } |
214 | |
215 | int |
216 | main (int argc, char *argv[]) |
217 | { |
218 | g_test_init (argc: &argc, argv: &argv, NULL); |
219 | |
220 | g_test_add_func (testpath: "/thread/mutex1" , test_func: test_mutex1); |
221 | g_test_add_func (testpath: "/thread/mutex2" , test_func: test_mutex2); |
222 | g_test_add_func (testpath: "/thread/mutex3" , test_func: test_mutex3); |
223 | g_test_add_func (testpath: "/thread/mutex4" , test_func: test_mutex4); |
224 | g_test_add_func (testpath: "/thread/mutex5" , test_func: test_mutex5); |
225 | |
226 | if (g_test_perf ()) |
227 | { |
228 | guint i; |
229 | |
230 | g_test_add_data_func (testpath: "/thread/mutex/perf/uncontended" , GUINT_TO_POINTER (0), test_func: test_mutex_perf); |
231 | |
232 | for (i = 1; i <= 10; i++) |
233 | { |
234 | gchar name[80]; |
235 | sprintf (s: name, format: "/thread/mutex/perf/contended/%u" , i); |
236 | g_test_add_data_func (testpath: name, GUINT_TO_POINTER (i), test_func: test_mutex_perf); |
237 | } |
238 | } |
239 | |
240 | return g_test_run (); |
241 | } |
242 | |