xref: /illumos-gate/usr/src/test/libc-tests/tests/c11_threads.c (revision 3665ce8aeee26b1a84fb98951ef011e0779e1ae2)
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
51 cthr_test_sleep_thr(void *arg)
52 {
53 	for (;;) {
54 		sleep(1000);
55 	}
56 
57 	abort();
58 }
59 
60 static 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
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
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
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
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
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
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
172 cthr_test_detach_thr0(void *arg)
173 {
174 	thrd_exit(23);
175 }
176 
177 static int
178 cthr_test_detach_thr1(void *arg)
179 {
180 	return (42);
181 }
182 
183 static 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
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
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
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
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
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
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
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
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
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
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