diff options
Diffstat (limited to 'usr/src/test/libc-tests/tests/c11_threads.c')
-rw-r--r-- | usr/src/test/libc-tests/tests/c11_threads.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/usr/src/test/libc-tests/tests/c11_threads.c b/usr/src/test/libc-tests/tests/c11_threads.c new file mode 100644 index 0000000000..dffadcc2af --- /dev/null +++ b/usr/src/test/libc-tests/tests/c11_threads.c @@ -0,0 +1,385 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2016 Joyent, Inc. + */ + +/* + * Validate various C11 threads routines. Specifically we want to cover: + * + * o threads + * o mutexes + * o condition variables + */ + +#include <threads.h> +#include <sys/debug.h> +#include <stdlib.h> +#include <unistd.h> + +#define STRESS_NTHREADS 128 +#define STRESS_COUNT 1000 + +static mtx_t stress_mtx; +static int stress_count; + +#define BROADCAST_NTHREADS 128 + +static mtx_t broadcast_mtx; +static cnd_t broadcast_cnd; +static boolean_t broadcast_done; + +#define SIGNAL_NTHREADS 128 + +static mtx_t signal_mtx; +static cnd_t signal_cnd; +static boolean_t signal_done; + +/* + * This thread should only ever be used for detach. + */ +static int +cthr_test_sleep_thr(void *arg) +{ + for (;;) { + sleep(1000); + } + + abort(); +} + +static void +cthr_test_mtx_init(void) +{ + mtx_t mtx; + + VERIFY3S(mtx_init(&mtx, mtx_plain), ==, thrd_success); + mtx_destroy(&mtx); + VERIFY3S(mtx_init(&mtx, mtx_timed), ==, thrd_success); + mtx_destroy(&mtx); + VERIFY3S(mtx_init(&mtx, mtx_plain | mtx_recursive), ==, thrd_success); + mtx_destroy(&mtx); + VERIFY3S(mtx_init(&mtx, mtx_timed | mtx_recursive), ==, thrd_success); + mtx_destroy(&mtx); + + VERIFY3S(mtx_init(&mtx, UINT32_MAX), ==, thrd_error); + VERIFY3S(mtx_init(&mtx, 42), ==, thrd_error); +} + +static void +cthr_test_mtx_lockrec(void) +{ + mtx_t mtx; + + VERIFY3S(mtx_init(&mtx, mtx_plain | mtx_recursive), ==, thrd_success); + VERIFY3S(mtx_lock(&mtx), ==, thrd_success); + VERIFY3S(mtx_lock(&mtx), ==, thrd_success); + VERIFY3S(mtx_trylock(&mtx), ==, thrd_success); + VERIFY3S(mtx_unlock(&mtx), ==, thrd_success); + VERIFY3S(mtx_unlock(&mtx), ==, thrd_success); + VERIFY3S(mtx_unlock(&mtx), ==, thrd_success); + mtx_destroy(&mtx); +} + +static void +cthr_test_mtx_trylock(void) +{ + mtx_t mtx; + + VERIFY3S(mtx_init(&mtx, mtx_plain), ==, thrd_success); + VERIFY3S(mtx_trylock(&mtx), ==, thrd_success); + VERIFY3S(mtx_trylock(&mtx), ==, thrd_busy); + VERIFY3S(mtx_unlock(&mtx), ==, thrd_success); + mtx_destroy(&mtx); +} + +static int +cthr_test_stress_thr(void *arg) +{ + int i; + int *ip = arg; + + for (i = 0; i < STRESS_COUNT; i++) { + VERIFY3S(mtx_lock(&stress_mtx), ==, thrd_success); + *ip = *ip + 1; + VERIFY3S(mtx_unlock(&stress_mtx), ==, thrd_success); + } + + return (0); +} + +static void +cthr_test_stress(void) +{ + int i; + thrd_t threads[STRESS_NTHREADS]; + + VERIFY3S(mtx_init(&stress_mtx, mtx_plain), ==, thrd_success); + for (i = 0; i < STRESS_NTHREADS; i++) { + VERIFY3S(thrd_create(&threads[i], cthr_test_stress_thr, + &stress_count), ==, thrd_success); + } + + for (i = 0; i < STRESS_NTHREADS; i++) { + VERIFY3S(thrd_join(threads[i], NULL), ==, thrd_success); + } + mtx_destroy(&stress_mtx); + + VERIFY3S(stress_count, ==, STRESS_NTHREADS * STRESS_COUNT); +} + +static void +cthr_test_equal(void) +{ + thrd_t self, other; + + self = thrd_current(); + + VERIFY0(thrd_equal(self, self)); + VERIFY3S(thrd_create(&other, cthr_test_sleep_thr, NULL), ==, + thrd_success); + VERIFY3S(thrd_equal(self, other), !=, 0); + VERIFY3S(thrd_equal(other, other), ==, 0); + VERIFY3S(thrd_detach(other), ==, thrd_success); +} + +static void +cthr_test_detach_err(void) +{ + thrd_t self, other; + + self = thrd_current(); + + VERIFY0(thrd_equal(self, self)); + VERIFY3S(thrd_create(&other, cthr_test_sleep_thr, NULL), ==, + thrd_success); + VERIFY3S(thrd_detach(other), ==, thrd_success); + + VERIFY3S(thrd_join(self, NULL), ==, thrd_error); + VERIFY3S(thrd_join(other, NULL), ==, thrd_error); +} + +static int +cthr_test_detach_thr0(void *arg) +{ + thrd_exit(23); + abort(); +} + +static int +cthr_test_detach_thr1(void *arg) +{ + return (42); +} + +static void +cthr_test_detach(void) +{ + int status; + thrd_t thrd; + + VERIFY3S(thrd_create(&thrd, cthr_test_detach_thr0, NULL), ==, + thrd_success); + VERIFY3S(thrd_join(thrd, &status), ==, thrd_success); + VERIFY3S(status, ==, 23); + + VERIFY3S(thrd_create(&thrd, cthr_test_detach_thr1, NULL), ==, + thrd_success); + VERIFY3S(thrd_join(thrd, &status), ==, thrd_success); + VERIFY3S(status, ==, 42); +} + +static void +cthr_test_sleep(void) +{ + struct timespec ts; + hrtime_t start, end; + long stime = 10 * NANOSEC / MILLISEC; + + ts.tv_sec = 1; + ts.tv_nsec = -1; + + VERIFY3S(thrd_sleep(&ts, NULL), <, -1); + + ts.tv_sec = 0; + ts.tv_nsec = stime; + start = gethrtime(); + VERIFY3S(thrd_sleep(&ts, NULL), ==, 0); + end = gethrtime(); + + VERIFY3S(end - start, >, stime); +} + +static int +cthr_test_broadcast_thr(void *arg) +{ + VERIFY3S(mtx_lock(&broadcast_mtx), ==, thrd_success); + while (broadcast_done == B_FALSE) + VERIFY3S(cnd_wait(&broadcast_cnd, &broadcast_mtx), ==, + thrd_success); + VERIFY3S(mtx_unlock(&broadcast_mtx), ==, thrd_success); + + return (0); +} + +static void +cthr_test_broadcast(void) +{ + int i; + thrd_t threads[BROADCAST_NTHREADS]; + + VERIFY3S(mtx_init(&broadcast_mtx, mtx_plain), ==, thrd_success); + VERIFY3S(cnd_init(&broadcast_cnd), ==, thrd_success); + for (i = 0; i < BROADCAST_NTHREADS; i++) { + VERIFY3S(thrd_create(&threads[i], cthr_test_broadcast_thr, + NULL), ==, thrd_success); + } + + VERIFY3S(mtx_lock(&broadcast_mtx), ==, thrd_success); + broadcast_done = B_TRUE; + VERIFY3S(mtx_unlock(&broadcast_mtx), ==, thrd_success); + VERIFY3S(cnd_broadcast(&broadcast_cnd), ==, thrd_success); + + for (i = 0; i < STRESS_NTHREADS; i++) { + VERIFY3S(thrd_join(threads[i], NULL), ==, thrd_success); + } + + mtx_destroy(&broadcast_mtx); + cnd_destroy(&broadcast_cnd); +} + + +static int +cthr_test_signal_thr(void *arg) +{ + VERIFY3S(mtx_lock(&signal_mtx), ==, thrd_success); + while (signal_done == B_FALSE) + VERIFY3S(cnd_wait(&signal_cnd, &signal_mtx), ==, + thrd_success); + VERIFY3S(mtx_unlock(&signal_mtx), ==, thrd_success); + VERIFY3S(cnd_signal(&signal_cnd), ==, thrd_success); + + return (0); +} + +static void +cthr_test_signal(void) +{ + int i; + thrd_t threads[SIGNAL_NTHREADS]; + + VERIFY3S(mtx_init(&signal_mtx, mtx_plain), ==, thrd_success); + VERIFY3S(cnd_init(&signal_cnd), ==, thrd_success); + for (i = 0; i < SIGNAL_NTHREADS; i++) { + VERIFY3S(thrd_create(&threads[i], cthr_test_signal_thr, NULL), + ==, thrd_success); + } + + VERIFY3S(mtx_lock(&signal_mtx), ==, thrd_success); + signal_done = B_TRUE; + VERIFY3S(mtx_unlock(&signal_mtx), ==, thrd_success); + VERIFY3S(cnd_signal(&signal_cnd), ==, thrd_success); + + for (i = 0; i < STRESS_NTHREADS; i++) { + VERIFY3S(thrd_join(threads[i], NULL), ==, thrd_success); + } + + mtx_destroy(&signal_mtx); + cnd_destroy(&signal_cnd); +} + +static void +cthr_test_cndtime(void) +{ + mtx_t mtx; + cnd_t cnd; + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 1 * NANOSEC / MILLISEC; + VERIFY3S(mtx_init(&mtx, mtx_plain), ==, thrd_success); + VERIFY3S(cnd_init(&cnd), ==, thrd_success); + + VERIFY3S(mtx_lock(&mtx), ==, thrd_success); + VERIFY3S(cnd_timedwait(&cnd, &mtx, &ts), ==, thrd_timedout); + VERIFY3S(mtx_unlock(&mtx), ==, thrd_success); + + mtx_destroy(&mtx); + cnd_destroy(&cnd); +} + +static void +cthr_test_mtx_selftime(void) +{ + mtx_t mtx; + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 1 * NANOSEC / MILLISEC; + VERIFY3S(mtx_init(&mtx, mtx_timed), ==, thrd_success); + VERIFY3S(mtx_lock(&mtx), ==, thrd_success); + VERIFY3S(mtx_timedlock(&mtx, &ts), ==, thrd_timedout); + VERIFY3S(mtx_unlock(&mtx), ==, thrd_success); + mtx_destroy(&mtx); +} + +static int +cthr_test_mtx_busy_thr(void *arg) +{ + mtx_t *mtx = arg; + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 1 * NANOSEC / MILLISEC; + + VERIFY3S(mtx_trylock(mtx), ==, thrd_busy); + VERIFY3S(mtx_timedlock(mtx, &ts), ==, thrd_timedout); + + return (0); +} + +static void +cthr_test_mtx_busy(void) +{ + mtx_t mtx; + thrd_t thrd; + + VERIFY3S(mtx_init(&mtx, mtx_timed), ==, thrd_success); + VERIFY3S(mtx_lock(&mtx), ==, thrd_success); + + VERIFY3S(thrd_create(&thrd, cthr_test_mtx_busy_thr, &mtx), ==, + thrd_success); + VERIFY3S(thrd_join(thrd, NULL), ==, thrd_success); + + VERIFY3S(mtx_unlock(&mtx), ==, thrd_success); + mtx_destroy(&mtx); +} + +int +main(void) +{ + cthr_test_mtx_init(); + cthr_test_mtx_lockrec(); + cthr_test_mtx_trylock(); + cthr_test_stress(); + cthr_test_equal(); + cthr_test_detach_err(); + cthr_test_detach(); + cthr_test_sleep(); + cthr_test_broadcast(); + cthr_test_signal(); + cthr_test_cndtime(); + cthr_test_mtx_selftime(); + cthr_test_mtx_busy(); + + return (0); +} |