1 | /* Unit tests for GRecMutex |
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_rec_mutex1 (void) |
34 | { |
35 | GRecMutex mutex; |
36 | |
37 | g_rec_mutex_init (rec_mutex: &mutex); |
38 | g_rec_mutex_lock (rec_mutex: &mutex); |
39 | g_rec_mutex_unlock (rec_mutex: &mutex); |
40 | g_rec_mutex_lock (rec_mutex: &mutex); |
41 | g_rec_mutex_unlock (rec_mutex: &mutex); |
42 | g_rec_mutex_clear (rec_mutex: &mutex); |
43 | } |
44 | |
45 | static void |
46 | test_rec_mutex2 (void) |
47 | { |
48 | static GRecMutex mutex; |
49 | |
50 | g_rec_mutex_lock (rec_mutex: &mutex); |
51 | g_rec_mutex_unlock (rec_mutex: &mutex); |
52 | g_rec_mutex_lock (rec_mutex: &mutex); |
53 | g_rec_mutex_unlock (rec_mutex: &mutex); |
54 | } |
55 | |
56 | static void |
57 | test_rec_mutex3 (void) |
58 | { |
59 | static GRecMutex mutex; |
60 | gboolean ret; |
61 | |
62 | ret = g_rec_mutex_trylock (rec_mutex: &mutex); |
63 | g_assert (ret); |
64 | |
65 | ret = g_rec_mutex_trylock (rec_mutex: &mutex); |
66 | g_assert (ret); |
67 | |
68 | g_rec_mutex_unlock (rec_mutex: &mutex); |
69 | g_rec_mutex_unlock (rec_mutex: &mutex); |
70 | } |
71 | |
72 | #define LOCKS 48 |
73 | #define ITERATIONS 10000 |
74 | #define THREADS 100 |
75 | |
76 | |
77 | GThread *owners[LOCKS]; |
78 | GRecMutex locks[LOCKS]; |
79 | |
80 | static void |
81 | acquire (gint nr) |
82 | { |
83 | GThread *self; |
84 | |
85 | self = g_thread_self (); |
86 | |
87 | if (!g_rec_mutex_trylock (rec_mutex: &locks[nr])) |
88 | { |
89 | if (g_test_verbose ()) |
90 | g_printerr (format: "thread %p going to block on lock %d\n" , self, nr); |
91 | |
92 | g_rec_mutex_lock (rec_mutex: &locks[nr]); |
93 | } |
94 | |
95 | g_assert (owners[nr] == NULL); /* hopefully nobody else is here */ |
96 | owners[nr] = self; |
97 | |
98 | /* let some other threads try to ruin our day */ |
99 | g_thread_yield (); |
100 | g_thread_yield (); |
101 | |
102 | g_assert (owners[nr] == self); /* hopefully this is still us... */ |
103 | |
104 | if (g_test_verbose ()) |
105 | g_printerr (format: "thread %p recursively taking lock %d\n" , self, nr); |
106 | |
107 | g_rec_mutex_lock (rec_mutex: &locks[nr]); /* we're recursive, after all */ |
108 | |
109 | g_assert (owners[nr] == self); /* hopefully this is still us... */ |
110 | |
111 | g_rec_mutex_unlock (rec_mutex: &locks[nr]); |
112 | |
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_rec_mutex_unlock (rec_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_rec_mutex4 (void) |
140 | { |
141 | gint i; |
142 | GThread *threads[THREADS]; |
143 | |
144 | for (i = 0; i < LOCKS; i++) |
145 | g_rec_mutex_init (rec_mutex: &locks[i]); |
146 | |
147 | for (i = 0; i < THREADS; i++) |
148 | threads[i] = g_thread_new (name: "test" , func: thread_func, 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_rec_mutex_clear (rec_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 gint depth; |
163 | |
164 | static gboolean |
165 | do_addition (gint *value) |
166 | { |
167 | static GRecMutex lock; |
168 | gboolean more; |
169 | gint i; |
170 | |
171 | /* test performance of "good" cases (ie: short critical sections) */ |
172 | for (i = 0; i < depth; i++) |
173 | g_rec_mutex_lock (rec_mutex: &lock); |
174 | |
175 | if ((more = *value != COUNT_TO)) |
176 | if (*value != -1) |
177 | (*value)++; |
178 | |
179 | for (i = 0; i < depth; i++) |
180 | g_rec_mutex_unlock (rec_mutex: &lock); |
181 | |
182 | return more; |
183 | } |
184 | |
185 | static gpointer |
186 | addition_thread (gpointer value) |
187 | { |
188 | while (do_addition (value)); |
189 | |
190 | return NULL; |
191 | } |
192 | |
193 | static void |
194 | test_mutex_perf (gconstpointer data) |
195 | { |
196 | gint c = GPOINTER_TO_INT (data); |
197 | GThread *threads[THREADS]; |
198 | gint64 start_time; |
199 | gint n_threads; |
200 | gdouble rate; |
201 | gint x = -1; |
202 | gint i; |
203 | |
204 | n_threads = c / 256; |
205 | depth = c % 256; |
206 | |
207 | for (i = 0; i < n_threads - 1; i++) |
208 | threads[i] = g_thread_new (name: "test" , func: addition_thread, data: &x); |
209 | |
210 | /* avoid measuring thread setup/teardown time */ |
211 | start_time = g_get_monotonic_time (); |
212 | g_atomic_int_set (&x, 0); |
213 | addition_thread (value: &x); |
214 | g_assert_cmpint (g_atomic_int_get (&x), ==, COUNT_TO); |
215 | rate = g_get_monotonic_time () - start_time; |
216 | rate = x / rate; |
217 | |
218 | for (i = 0; i < n_threads - 1; i++) |
219 | g_thread_join (thread: threads[i]); |
220 | |
221 | g_test_maximized_result (maximized_quantity: rate, format: "%f mips" , rate); |
222 | } |
223 | |
224 | |
225 | int |
226 | main (int argc, char *argv[]) |
227 | { |
228 | g_test_init (argc: &argc, argv: &argv, NULL); |
229 | |
230 | g_test_add_func (testpath: "/thread/rec-mutex1" , test_func: test_rec_mutex1); |
231 | g_test_add_func (testpath: "/thread/rec-mutex2" , test_func: test_rec_mutex2); |
232 | g_test_add_func (testpath: "/thread/rec-mutex3" , test_func: test_rec_mutex3); |
233 | g_test_add_func (testpath: "/thread/rec-mutex4" , test_func: test_rec_mutex4); |
234 | |
235 | if (g_test_perf ()) |
236 | { |
237 | gint i, j; |
238 | |
239 | for (i = 0; i < 5; i++) |
240 | for (j = 1; j <= 5; j++) |
241 | { |
242 | gchar name[80]; |
243 | guint c; |
244 | |
245 | c = i * 256 + j; |
246 | |
247 | if (i) |
248 | sprintf (s: name, format: "/thread/rec-mutex/perf/contended%d/depth%d" , i, j); |
249 | else |
250 | sprintf (s: name, format: "/thread/rec-mutex/perf/uncontended/depth%d" , j); |
251 | |
252 | g_test_add_data_func (testpath: name, GINT_TO_POINTER (c), test_func: test_mutex_perf); |
253 | } |
254 | } |
255 | |
256 | return g_test_run (); |
257 | } |
258 | |