2293af1bc0b853ef667a551e8a88b7bcea53f5d6
[strongswan.git] / src / libstrongswan / tests / suites / test_threading.c
1 /*
2 * Copyright (C) 2013 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "test_suite.h"
18
19 #include <sched.h>
20
21 #include <threading/thread.h>
22 #include <threading/mutex.h>
23 #include <threading/condvar.h>
24
25 /*******************************************************************************
26 * recursive mutex test
27 */
28
29 #define THREADS 20
30
31 /**
32 * Thread barrier data
33 */
34 typedef struct {
35 mutex_t *mutex;
36 condvar_t *cond;
37 int count;
38 int current;
39 bool active;
40 } barrier_t;
41
42 /**
43 * Create a thread barrier for count threads
44 */
45 static barrier_t* barrier_create(int count)
46 {
47 barrier_t *this;
48
49 INIT(this,
50 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
51 .cond = condvar_create(CONDVAR_TYPE_DEFAULT),
52 .count = count,
53 );
54
55 return this;
56 }
57
58 /**
59 * Destroy a thread barrier
60 */
61 static void barrier_destroy(barrier_t *this)
62 {
63 this->mutex->destroy(this->mutex);
64 this->cond->destroy(this->cond);
65 free(this);
66 }
67
68 /**
69 * Wait to have configured number of threads in barrier
70 */
71 static bool barrier_wait(barrier_t *this)
72 {
73 bool winner = FALSE;
74
75 this->mutex->lock(this->mutex);
76 if (!this->active)
77 { /* first, reset */
78 this->active = TRUE;
79 this->current = 0;
80 }
81
82 this->current++;
83 while (this->current < this->count)
84 {
85 this->cond->wait(this->cond, this->mutex);
86 }
87 if (this->active)
88 { /* first, win */
89 winner = TRUE;
90 this->active = FALSE;
91 }
92 this->mutex->unlock(this->mutex);
93 this->cond->broadcast(this->cond);
94 sched_yield();
95
96 return winner;
97 }
98
99 /**
100 * Barrier for some tests
101 */
102 static barrier_t *barrier;
103
104 static void *mutex_run(void *data)
105 {
106 mutex_t *mutex = (mutex_t*)data;
107 static int locked = 0;
108 int i;
109
110 /* wait for all threads before getting in action */
111 barrier_wait(barrier);
112
113 for (i = 0; i < 100; i++)
114 {
115 mutex->lock(mutex);
116 mutex->lock(mutex);
117 mutex->lock(mutex);
118 locked++;
119 sched_yield();
120 if (locked > 1)
121 {
122 fail("two threads locked the mutex concurrently");
123 }
124 locked--;
125 mutex->unlock(mutex);
126 mutex->unlock(mutex);
127 mutex->unlock(mutex);
128 }
129 return NULL;
130 }
131
132 START_TEST(test_mutex)
133 {
134 thread_t *threads[THREADS];
135 mutex_t *mutex;
136 int i;
137
138 barrier = barrier_create(THREADS);
139 mutex = mutex_create(MUTEX_TYPE_RECURSIVE);
140
141 for (i = 0; i < 10; i++)
142 {
143 mutex->lock(mutex);
144 mutex->unlock(mutex);
145 }
146 for (i = 0; i < 10; i++)
147 {
148 mutex->lock(mutex);
149 }
150 for (i = 0; i < 10; i++)
151 {
152 mutex->unlock(mutex);
153 }
154
155 for (i = 0; i < THREADS; i++)
156 {
157 threads[i] = thread_create(mutex_run, mutex);
158 }
159 for (i = 0; i < THREADS; i++)
160 {
161 threads[i]->join(threads[i]);
162 }
163
164 mutex->destroy(mutex);
165 barrier_destroy(barrier);
166 }
167 END_TEST
168
169 static void *join_run(void *data)
170 {
171 /* force some context switches */
172 sched_yield();
173 return (void*)((uintptr_t)data + THREADS);
174 }
175
176 START_TEST(test_join)
177 {
178 thread_t *threads[THREADS];
179 int i;
180
181 for (i = 0; i < THREADS; i++)
182 {
183 threads[i] = thread_create(join_run, (void*)(uintptr_t)i);
184 }
185 for (i = 0; i < THREADS; i++)
186 {
187 ck_assert_int_eq((uintptr_t)threads[i]->join(threads[i]), i + THREADS);
188 }
189 }
190 END_TEST
191
192 static void *exit_join_run(void *data)
193 {
194 sched_yield();
195 thread_exit((void*)((uintptr_t)data + THREADS));
196 /* not reached */
197 ck_assert(FALSE);
198 return NULL;
199 }
200
201 START_TEST(test_join_exit)
202 {
203 thread_t *threads[THREADS];
204 int i;
205
206 for (i = 0; i < THREADS; i++)
207 {
208 threads[i] = thread_create(exit_join_run, (void*)(uintptr_t)i);
209 }
210 for (i = 0; i < THREADS; i++)
211 {
212 ck_assert_int_eq((uintptr_t)threads[i]->join(threads[i]), i + THREADS);
213 }
214 }
215 END_TEST
216
217 static void *detach_run(void *data)
218 {
219 refcount_t *running = (refcount_t*)data;
220
221 ignore_result(ref_put(running));
222 return NULL;
223 }
224
225 START_TEST(test_detach)
226 {
227 thread_t *threads[THREADS];
228 int i;
229 refcount_t running = 0;
230
231 for (i = 0; i < THREADS; i++)
232 {
233 ref_get(&running);
234 threads[i] = thread_create(detach_run, &running);
235 }
236 for (i = 0; i < THREADS; i++)
237 {
238 threads[i]->detach(threads[i]);
239 }
240 while (running > 0)
241 {
242 sched_yield();
243 }
244 /* no checks done here, but we check that thread state gets cleaned
245 * up with leak detective. */
246 }
247 END_TEST
248
249 static void *detach_exit_run(void *data)
250 {
251 refcount_t *running = (refcount_t*)data;
252
253 ignore_result(ref_put(running));
254 thread_exit(NULL);
255 /* not reached */
256 ck_assert(FALSE);
257 return NULL;
258 }
259
260 START_TEST(test_detach_exit)
261 {
262 thread_t *threads[THREADS];
263 int i;
264 refcount_t running = 0;
265
266 for (i = 0; i < THREADS; i++)
267 {
268 ref_get(&running);
269 threads[i] = thread_create(detach_exit_run, &running);
270 }
271 for (i = 0; i < THREADS; i++)
272 {
273 threads[i]->detach(threads[i]);
274 }
275 while (running > 0)
276 {
277 sched_yield();
278 }
279 /* no checks done here, but we check that thread state gets cleaned
280 * up with leak detective. */
281 }
282 END_TEST
283
284 Suite *threading_suite_create()
285 {
286 Suite *s;
287 TCase *tc;
288
289 s = suite_create("threading");
290
291 tc = tcase_create("recursive mutex");
292 tcase_add_test(tc, test_mutex);
293 suite_add_tcase(s, tc);
294
295 tc = tcase_create("thread joining");
296 tcase_add_test(tc, test_join);
297 tcase_add_test(tc, test_join_exit);
298 suite_add_tcase(s, tc);
299
300 tc = tcase_create("thread detaching");
301 tcase_add_test(tc, test_detach);
302 tcase_add_test(tc, test_detach_exit);
303 suite_add_tcase(s, tc);
304
305 return s;
306 }