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 2019 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 (void) sleep(1000);
55 }
56
57 return (0);
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 VERIFY3S(thrd_equal(self, self), !=, 0);
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 VERIFY3S(thrd_equal(self, self), !=, 0);
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 }
176
177 static int
cthr_test_detach_thr1(void * arg)178 cthr_test_detach_thr1(void *arg)
179 {
180 return (42);
181 }
182
183 static void
cthr_test_detach(void)184 cthr_test_detach(void)
185 {
186 int status;
187 thrd_t thrd;
188
189 VERIFY3S(thrd_create(&thrd, cthr_test_detach_thr0, NULL), ==,
190 thrd_success);
191 VERIFY3S(thrd_join(thrd, &status), ==, thrd_success);
192 VERIFY3S(status, ==, 23);
193
194 VERIFY3S(thrd_create(&thrd, cthr_test_detach_thr1, NULL), ==,
195 thrd_success);
196 VERIFY3S(thrd_join(thrd, &status), ==, thrd_success);
197 VERIFY3S(status, ==, 42);
198 }
199
200 static void
cthr_test_sleep(void)201 cthr_test_sleep(void)
202 {
203 struct timespec ts;
204 hrtime_t start, end;
205 long stime = 10 * NANOSEC / MILLISEC;
206
207 ts.tv_sec = 1;
208 ts.tv_nsec = -1;
209
210 VERIFY3S(thrd_sleep(&ts, NULL), <, -1);
211
212 ts.tv_sec = 0;
213 ts.tv_nsec = stime;
214 start = gethrtime();
215 VERIFY3S(thrd_sleep(&ts, NULL), ==, 0);
216 end = gethrtime();
217
218 VERIFY3S(end - start, >, stime);
219 }
220
221 static int
cthr_test_broadcast_thr(void * arg)222 cthr_test_broadcast_thr(void *arg)
223 {
224 VERIFY3S(mtx_lock(&broadcast_mtx), ==, thrd_success);
225 while (broadcast_done == B_FALSE)
226 VERIFY3S(cnd_wait(&broadcast_cnd, &broadcast_mtx), ==,
227 thrd_success);
228 VERIFY3S(mtx_unlock(&broadcast_mtx), ==, thrd_success);
229
230 return (0);
231 }
232
233 static void
cthr_test_broadcast(void)234 cthr_test_broadcast(void)
235 {
236 int i;
237 thrd_t threads[BROADCAST_NTHREADS];
238
239 VERIFY3S(mtx_init(&broadcast_mtx, mtx_plain), ==, thrd_success);
240 VERIFY3S(cnd_init(&broadcast_cnd), ==, thrd_success);
241 for (i = 0; i < BROADCAST_NTHREADS; i++) {
242 VERIFY3S(thrd_create(&threads[i], cthr_test_broadcast_thr,
243 NULL), ==, thrd_success);
244 }
245
246 VERIFY3S(mtx_lock(&broadcast_mtx), ==, thrd_success);
247 broadcast_done = B_TRUE;
248 VERIFY3S(mtx_unlock(&broadcast_mtx), ==, thrd_success);
249 VERIFY3S(cnd_broadcast(&broadcast_cnd), ==, thrd_success);
250
251 for (i = 0; i < STRESS_NTHREADS; i++) {
252 VERIFY3S(thrd_join(threads[i], NULL), ==, thrd_success);
253 }
254
255 mtx_destroy(&broadcast_mtx);
256 cnd_destroy(&broadcast_cnd);
257 }
258
259
260 static int
cthr_test_signal_thr(void * arg)261 cthr_test_signal_thr(void *arg)
262 {
263 VERIFY3S(mtx_lock(&signal_mtx), ==, thrd_success);
264 while (signal_done == B_FALSE)
265 VERIFY3S(cnd_wait(&signal_cnd, &signal_mtx), ==,
266 thrd_success);
267 VERIFY3S(mtx_unlock(&signal_mtx), ==, thrd_success);
268 VERIFY3S(cnd_signal(&signal_cnd), ==, thrd_success);
269
270 return (0);
271 }
272
273 static void
cthr_test_signal(void)274 cthr_test_signal(void)
275 {
276 int i;
277 thrd_t threads[SIGNAL_NTHREADS];
278
279 VERIFY3S(mtx_init(&signal_mtx, mtx_plain), ==, thrd_success);
280 VERIFY3S(cnd_init(&signal_cnd), ==, thrd_success);
281 for (i = 0; i < SIGNAL_NTHREADS; i++) {
282 VERIFY3S(thrd_create(&threads[i], cthr_test_signal_thr, NULL),
283 ==, thrd_success);
284 }
285
286 VERIFY3S(mtx_lock(&signal_mtx), ==, thrd_success);
287 signal_done = B_TRUE;
288 VERIFY3S(mtx_unlock(&signal_mtx), ==, thrd_success);
289 VERIFY3S(cnd_signal(&signal_cnd), ==, thrd_success);
290
291 for (i = 0; i < STRESS_NTHREADS; i++) {
292 VERIFY3S(thrd_join(threads[i], NULL), ==, thrd_success);
293 }
294
295 mtx_destroy(&signal_mtx);
296 cnd_destroy(&signal_cnd);
297 }
298
299 static void
cthr_test_cndtime(void)300 cthr_test_cndtime(void)
301 {
302 mtx_t mtx;
303 cnd_t cnd;
304 struct timespec ts;
305
306 ts.tv_sec = 0;
307 ts.tv_nsec = 1 * NANOSEC / MILLISEC;
308 VERIFY3S(mtx_init(&mtx, mtx_plain), ==, thrd_success);
309 VERIFY3S(cnd_init(&cnd), ==, thrd_success);
310
311 VERIFY3S(mtx_lock(&mtx), ==, thrd_success);
312 VERIFY3S(cnd_timedwait(&cnd, &mtx, &ts), ==, thrd_timedout);
313 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
314
315 mtx_destroy(&mtx);
316 cnd_destroy(&cnd);
317 }
318
319 static void
cthr_test_mtx_selftime(void)320 cthr_test_mtx_selftime(void)
321 {
322 mtx_t mtx;
323 struct timespec ts;
324
325 ts.tv_sec = 0;
326 ts.tv_nsec = 1 * NANOSEC / MILLISEC;
327 VERIFY3S(mtx_init(&mtx, mtx_timed), ==, thrd_success);
328 VERIFY3S(mtx_lock(&mtx), ==, thrd_success);
329 VERIFY3S(mtx_timedlock(&mtx, &ts), ==, thrd_timedout);
330 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
331 mtx_destroy(&mtx);
332 }
333
334 static int
cthr_test_mtx_busy_thr(void * arg)335 cthr_test_mtx_busy_thr(void *arg)
336 {
337 mtx_t *mtx = arg;
338 struct timespec ts;
339
340 ts.tv_sec = 0;
341 ts.tv_nsec = 1 * NANOSEC / MILLISEC;
342
343 VERIFY3S(mtx_trylock(mtx), ==, thrd_busy);
344 VERIFY3S(mtx_timedlock(mtx, &ts), ==, thrd_timedout);
345
346 return (0);
347 }
348
349 static void
cthr_test_mtx_busy(void)350 cthr_test_mtx_busy(void)
351 {
352 mtx_t mtx;
353 thrd_t thrd;
354
355 VERIFY3S(mtx_init(&mtx, mtx_timed), ==, thrd_success);
356 VERIFY3S(mtx_lock(&mtx), ==, thrd_success);
357
358 VERIFY3S(thrd_create(&thrd, cthr_test_mtx_busy_thr, &mtx), ==,
359 thrd_success);
360 VERIFY3S(thrd_join(thrd, NULL), ==, thrd_success);
361
362 VERIFY3S(mtx_unlock(&mtx), ==, thrd_success);
363 mtx_destroy(&mtx);
364 }
365
366 int
main(void)367 main(void)
368 {
369 cthr_test_mtx_init();
370 cthr_test_mtx_lockrec();
371 cthr_test_mtx_trylock();
372 cthr_test_stress();
373 cthr_test_equal();
374 cthr_test_detach_err();
375 cthr_test_detach();
376 cthr_test_sleep();
377 cthr_test_broadcast();
378 cthr_test_signal();
379 cthr_test_cndtime();
380 cthr_test_mtx_selftime();
381 cthr_test_mtx_busy();
382
383 return (0);
384 }
385