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