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 }