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 }