xref: /illumos-gate/usr/src/test/libc-tests/tests/c11_threads.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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 2017 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 	abort();
176 }
177 
178 static int
179 cthr_test_detach_thr1(void *arg)
180 {
181 	return (42);
182 }
183 
184 static 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
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
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
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
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
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
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
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
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
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
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