1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2016 Joyent, Inc.
14 */
15
16 /*
17 * Validate various C11 threads routines. Specifically we want to cover:
18 *
19 * o threads
20 * o mutexes
21 * o condition variables
22 */
23
24 #include <threads.h>
25 #include <sys/debug.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28
29 #define STRESS_NTHREADS 128
30 #define STRESS_COUNT 1000
31
32 static mtx_t stress_mtx;
33 static int stress_count;
34
35 #define BROADCAST_NTHREADS 128
36
37 static mtx_t broadcast_mtx;
38 static cnd_t broadcast_cnd;
39 static boolean_t broadcast_done;
40
41 #define SIGNAL_NTHREADS 128
42
43 static mtx_t signal_mtx;
44 static cnd_t signal_cnd;
45 static boolean_t signal_done;
46
47 /*
48 * This thread should only ever be used for detach.
49 */
50 static int
cthr_test_sleep_thr(void * arg)51 cthr_test_sleep_thr(void *arg)
52 {
53 for (;;) {
54 sleep(1000);
55 }
56
57 abort();
58 }
59
60 static void
cthr_test_mtx_init(void)61 cthr_test_mtx_init(void)
62 {
63 mtx_t mtx;
64
65 VERIFY3S(mtx_init(&mtx, mtx_plain), ==, thrd_success);
66 mtx_destroy(&mtx);
67 VERIFY3S(mtx_init(&mtx, mtx_timed), ==, thrd_success);
68 mtx_destroy(&mtx);
69 VERIFY3S(mtx_init(&mtx, mtx_plain | mtx_recursive), ==, thrd_success);
70 mtx_destroy(&mtx);
71 VERIFY3S(mtx_init(&mtx, mtx_timed | mtx_recursive), ==, thrd_success);
72 mtx_destroy(&mtx);
73
74 VERIFY3S(mtx_init(&mtx, UINT32_MAX), ==, thrd_error);
75 VERIFY3S(mtx_init(&mtx, 42), ==, thrd_error);
76 }
77
78 static void
cthr_test_mtx_lockrec(void)79 cthr_test_mtx_lockrec(void)
80 {
81 mtx_t mtx;
82
83 VERIFY3S(mtx_init(&mtx, mtx_plain | mtx_recursive), ==, thrd_success);
84 VERIFY3S(mtx_lock(&mtx), ==, thrd_success);
85 VERIFY3S(mtx_lock(&mtx), ==, thrd_success);
86 VERIFY3S(mtx_trylock(&mtx), ==, thrd_success);
87 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
88 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
89 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
90 mtx_destroy(&mtx);
91 }
92
93 static void
cthr_test_mtx_trylock(void)94 cthr_test_mtx_trylock(void)
95 {
96 mtx_t mtx;
97
98 VERIFY3S(mtx_init(&mtx, mtx_plain), ==, thrd_success);
99 VERIFY3S(mtx_trylock(&mtx), ==, thrd_success);
100 VERIFY3S(mtx_trylock(&mtx), ==, thrd_busy);
101 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
102 mtx_destroy(&mtx);
103 }
104
105 static int
cthr_test_stress_thr(void * arg)106 cthr_test_stress_thr(void *arg)
107 {
108 int i;
109 int *ip = arg;
110
111 for (i = 0; i < STRESS_COUNT; i++) {
112 VERIFY3S(mtx_lock(&stress_mtx), ==, thrd_success);
113 *ip = *ip + 1;
114 VERIFY3S(mtx_unlock(&stress_mtx), ==, thrd_success);
115 }
116
117 return (0);
118 }
119
120 static void
cthr_test_stress(void)121 cthr_test_stress(void)
122 {
123 int i;
124 thrd_t threads[STRESS_NTHREADS];
125
126 VERIFY3S(mtx_init(&stress_mtx, mtx_plain), ==, thrd_success);
127 for (i = 0; i < STRESS_NTHREADS; i++) {
128 VERIFY3S(thrd_create(&threads[i], cthr_test_stress_thr,
129 &stress_count), ==, thrd_success);
130 }
131
132 for (i = 0; i < STRESS_NTHREADS; i++) {
133 VERIFY3S(thrd_join(threads[i], NULL), ==, thrd_success);
134 }
135 mtx_destroy(&stress_mtx);
136
137 VERIFY3S(stress_count, ==, STRESS_NTHREADS * STRESS_COUNT);
138 }
139
140 static void
cthr_test_equal(void)141 cthr_test_equal(void)
142 {
143 thrd_t self, other;
144
145 self = thrd_current();
146
147 VERIFY0(thrd_equal(self, self));
148 VERIFY3S(thrd_create(&other, cthr_test_sleep_thr, NULL), ==,
149 thrd_success);
150 VERIFY3S(thrd_equal(self, other), !=, 0);
151 VERIFY3S(thrd_equal(other, other), ==, 0);
152 VERIFY3S(thrd_detach(other), ==, thrd_success);
153 }
154
155 static void
cthr_test_detach_err(void)156 cthr_test_detach_err(void)
157 {
158 thrd_t self, other;
159
160 self = thrd_current();
161
162 VERIFY0(thrd_equal(self, self));
163 VERIFY3S(thrd_create(&other, cthr_test_sleep_thr, NULL), ==,
164 thrd_success);
165 VERIFY3S(thrd_detach(other), ==, thrd_success);
166
167 VERIFY3S(thrd_join(self, NULL), ==, thrd_error);
168 VERIFY3S(thrd_join(other, NULL), ==, thrd_error);
169 }
170
171 static int
cthr_test_detach_thr0(void * arg)172 cthr_test_detach_thr0(void *arg)
173 {
174 thrd_exit(23);
175 abort();
176 }
177
178 static int
cthr_test_detach_thr1(void * arg)179 cthr_test_detach_thr1(void *arg)
180 {
181 return (42);
182 }
183
184 static void
cthr_test_detach(void)185 cthr_test_detach(void)
186 {
187 int status;
188 thrd_t thrd;
189
190 VERIFY3S(thrd_create(&thrd, cthr_test_detach_thr0, NULL), ==,
191 thrd_success);
192 VERIFY3S(thrd_join(thrd, &status), ==, thrd_success);
193 VERIFY3S(status, ==, 23);
194
195 VERIFY3S(thrd_create(&thrd, cthr_test_detach_thr1, NULL), ==,
196 thrd_success);
197 VERIFY3S(thrd_join(thrd, &status), ==, thrd_success);
198 VERIFY3S(status, ==, 42);
199 }
200
201 static void
cthr_test_sleep(void)202 cthr_test_sleep(void)
203 {
204 struct timespec ts;
205 hrtime_t start, end;
206 long stime = 10 * NANOSEC / MILLISEC;
207
208 ts.tv_sec = 1;
209 ts.tv_nsec = -1;
210
211 VERIFY3S(thrd_sleep(&ts, NULL), <, -1);
212
213 ts.tv_sec = 0;
214 ts.tv_nsec = stime;
215 start = gethrtime();
216 VERIFY3S(thrd_sleep(&ts, NULL), ==, 0);
217 end = gethrtime();
218
219 VERIFY3S(end - start, >, stime);
220 }
221
222 static int
cthr_test_broadcast_thr(void * arg)223 cthr_test_broadcast_thr(void *arg)
224 {
225 VERIFY3S(mtx_lock(&broadcast_mtx), ==, thrd_success);
226 while (broadcast_done == B_FALSE)
227 VERIFY3S(cnd_wait(&broadcast_cnd, &broadcast_mtx), ==,
228 thrd_success);
229 VERIFY3S(mtx_unlock(&broadcast_mtx), ==, thrd_success);
230
231 return (0);
232 }
233
234 static void
cthr_test_broadcast(void)235 cthr_test_broadcast(void)
236 {
237 int i;
238 thrd_t threads[BROADCAST_NTHREADS];
239
240 VERIFY3S(mtx_init(&broadcast_mtx, mtx_plain), ==, thrd_success);
241 VERIFY3S(cnd_init(&broadcast_cnd), ==, thrd_success);
242 for (i = 0; i < BROADCAST_NTHREADS; i++) {
243 VERIFY3S(thrd_create(&threads[i], cthr_test_broadcast_thr,
244 NULL), ==, thrd_success);
245 }
246
247 VERIFY3S(mtx_lock(&broadcast_mtx), ==, thrd_success);
248 broadcast_done = B_TRUE;
249 VERIFY3S(mtx_unlock(&broadcast_mtx), ==, thrd_success);
250 VERIFY3S(cnd_broadcast(&broadcast_cnd), ==, thrd_success);
251
252 for (i = 0; i < STRESS_NTHREADS; i++) {
253 VERIFY3S(thrd_join(threads[i], NULL), ==, thrd_success);
254 }
255
256 mtx_destroy(&broadcast_mtx);
257 cnd_destroy(&broadcast_cnd);
258 }
259
260
261 static int
cthr_test_signal_thr(void * arg)262 cthr_test_signal_thr(void *arg)
263 {
264 VERIFY3S(mtx_lock(&signal_mtx), ==, thrd_success);
265 while (signal_done == B_FALSE)
266 VERIFY3S(cnd_wait(&signal_cnd, &signal_mtx), ==,
267 thrd_success);
268 VERIFY3S(mtx_unlock(&signal_mtx), ==, thrd_success);
269 VERIFY3S(cnd_signal(&signal_cnd), ==, thrd_success);
270
271 return (0);
272 }
273
274 static void
cthr_test_signal(void)275 cthr_test_signal(void)
276 {
277 int i;
278 thrd_t threads[SIGNAL_NTHREADS];
279
280 VERIFY3S(mtx_init(&signal_mtx, mtx_plain), ==, thrd_success);
281 VERIFY3S(cnd_init(&signal_cnd), ==, thrd_success);
282 for (i = 0; i < SIGNAL_NTHREADS; i++) {
283 VERIFY3S(thrd_create(&threads[i], cthr_test_signal_thr, NULL),
284 ==, thrd_success);
285 }
286
287 VERIFY3S(mtx_lock(&signal_mtx), ==, thrd_success);
288 signal_done = B_TRUE;
289 VERIFY3S(mtx_unlock(&signal_mtx), ==, thrd_success);
290 VERIFY3S(cnd_signal(&signal_cnd), ==, thrd_success);
291
292 for (i = 0; i < STRESS_NTHREADS; i++) {
293 VERIFY3S(thrd_join(threads[i], NULL), ==, thrd_success);
294 }
295
296 mtx_destroy(&signal_mtx);
297 cnd_destroy(&signal_cnd);
298 }
299
300 static void
cthr_test_cndtime(void)301 cthr_test_cndtime(void)
302 {
303 mtx_t mtx;
304 cnd_t cnd;
305 struct timespec ts;
306
307 ts.tv_sec = 0;
308 ts.tv_nsec = 1 * NANOSEC / MILLISEC;
309 VERIFY3S(mtx_init(&mtx, mtx_plain), ==, thrd_success);
310 VERIFY3S(cnd_init(&cnd), ==, thrd_success);
311
312 VERIFY3S(mtx_lock(&mtx), ==, thrd_success);
313 VERIFY3S(cnd_timedwait(&cnd, &mtx, &ts), ==, thrd_timedout);
314 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
315
316 mtx_destroy(&mtx);
317 cnd_destroy(&cnd);
318 }
319
320 static void
cthr_test_mtx_selftime(void)321 cthr_test_mtx_selftime(void)
322 {
323 mtx_t mtx;
324 struct timespec ts;
325
326 ts.tv_sec = 0;
327 ts.tv_nsec = 1 * NANOSEC / MILLISEC;
328 VERIFY3S(mtx_init(&mtx, mtx_timed), ==, thrd_success);
329 VERIFY3S(mtx_lock(&mtx), ==, thrd_success);
330 VERIFY3S(mtx_timedlock(&mtx, &ts), ==, thrd_timedout);
331 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
332 mtx_destroy(&mtx);
333 }
334
335 static int
cthr_test_mtx_busy_thr(void * arg)336 cthr_test_mtx_busy_thr(void *arg)
337 {
338 mtx_t *mtx = arg;
339 struct timespec ts;
340
341 ts.tv_sec = 0;
342 ts.tv_nsec = 1 * NANOSEC / MILLISEC;
343
344 VERIFY3S(mtx_trylock(mtx), ==, thrd_busy);
345 VERIFY3S(mtx_timedlock(mtx, &ts), ==, thrd_timedout);
346
347 return (0);
348 }
349
350 static void
cthr_test_mtx_busy(void)351 cthr_test_mtx_busy(void)
352 {
353 mtx_t mtx;
354 thrd_t thrd;
355
356 VERIFY3S(mtx_init(&mtx, mtx_timed), ==, thrd_success);
357 VERIFY3S(mtx_lock(&mtx), ==, thrd_success);
358
359 VERIFY3S(thrd_create(&thrd, cthr_test_mtx_busy_thr, &mtx), ==,
360 thrd_success);
361 VERIFY3S(thrd_join(thrd, NULL), ==, thrd_success);
362
363 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
364 mtx_destroy(&mtx);
365 }
366
367 int
main(void)368 main(void)
369 {
370 cthr_test_mtx_init();
371 cthr_test_mtx_lockrec();
372 cthr_test_mtx_trylock();
373 cthr_test_stress();
374 cthr_test_equal();
375 cthr_test_detach_err();
376 cthr_test_detach();
377 cthr_test_sleep();
378 cthr_test_broadcast();
379 cthr_test_signal();
380 cthr_test_cndtime();
381 cthr_test_mtx_selftime();
382 cthr_test_mtx_busy();
383
384 return (0);
385 }
386