summaryrefslogtreecommitdiff
path: root/usr/src/test/libc-tests/tests/c11_threads.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/test/libc-tests/tests/c11_threads.c')
-rw-r--r--usr/src/test/libc-tests/tests/c11_threads.c385
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);
+}