/*
* 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);
}