164 lines
5.1 KiB
C
164 lines
5.1 KiB
C
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Copyright by The HDF Group. *
|
|
* All rights reserved. *
|
|
* *
|
|
* This file is part of HDF5. The full HDF5 copyright notice, including *
|
|
* terms governing use, modification, and redistribution, is contained in *
|
|
* the COPYING file, which can be found at the root of the source code *
|
|
* distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. *
|
|
* If you do not have access to either file, you may request a copy from *
|
|
* help@hdfgroup.org. *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
/* Check that a thread ID returned by H5TS_thread_id() possesses the
|
|
* following properties:
|
|
*
|
|
* 1 ID >= 1.
|
|
* 2 The ID is constant over the thread's lifetime.
|
|
* 3 No two threads share an ID during their lifetimes.
|
|
* 4 A thread's ID is available for reuse as soon as it is joined.
|
|
*/
|
|
#include <err.h>
|
|
|
|
/*
|
|
* Include required headers. This file tests internal library functions,
|
|
* so we include the private headers here.
|
|
*/
|
|
#include "testhdf5.h"
|
|
|
|
#if defined(H5_HAVE_THREADSAFE) && !defined(H5_HAVE_WIN_THREADS)
|
|
|
|
#define threads_failure(_call, _result) do { \
|
|
errx(EXIT_FAILURE, "%s.%d: " #_call ": %s", __func__, \
|
|
__LINE__, strerror(_result)); \
|
|
} while (false)
|
|
|
|
#define NTHREADS 5
|
|
|
|
static volatile bool failed = false;
|
|
static pthread_barrier_t barrier;
|
|
static bool used[NTHREADS];
|
|
static pthread_mutex_t used_lock;
|
|
|
|
static void
|
|
atomic_printf(const char *fmt, ...)
|
|
{
|
|
char buf[80];
|
|
va_list ap;
|
|
ssize_t nprinted, nwritten;
|
|
|
|
va_start(ap, fmt);
|
|
nprinted = vsnprintf(buf, sizeof(buf), fmt, ap);
|
|
va_end(ap);
|
|
|
|
if (nprinted == -1)
|
|
err(EXIT_FAILURE, "%s.%d: vsnprintf", __func__, __LINE__);
|
|
else if (nprinted >= (ssize_t)sizeof(buf))
|
|
errx(EXIT_FAILURE, "%s.%d: vsnprintf overflowed", __func__, __LINE__);
|
|
|
|
nwritten = write(STDOUT_FILENO, buf, (size_t)nprinted);
|
|
if (nwritten < nprinted) {
|
|
errx(EXIT_FAILURE, "%s.%d: write error or short write",
|
|
__func__, __LINE__);
|
|
}
|
|
}
|
|
|
|
/* Each thread runs this routine. The routine fetches the current
|
|
* thread's ID, makes sure that it is in the expected range, makes
|
|
* sure that in this round of testing, no two threads shared the
|
|
* same ID,
|
|
*/
|
|
static void *
|
|
thread_main(void H5_ATTR_UNUSED *arg)
|
|
{
|
|
uint64_t ntid, tid;
|
|
|
|
tid = H5TS_thread_id();
|
|
|
|
if (tid < 1 || NTHREADS < tid) {
|
|
atomic_printf("unexpected tid %" PRIu64 " FAIL\n", tid);
|
|
goto pre_barrier_error;
|
|
}
|
|
pthread_mutex_lock(&used_lock);
|
|
if (used[tid - 1]) {
|
|
atomic_printf("reused tid %" PRIu64 " FAIL\n", tid);
|
|
pthread_mutex_unlock(&used_lock);
|
|
goto pre_barrier_error;
|
|
}
|
|
used[tid - 1] = true;
|
|
pthread_mutex_unlock(&used_lock);
|
|
|
|
atomic_printf("tid %" PRIu64 " in [1, %d] PASS\n", tid, NTHREADS);
|
|
pthread_barrier_wait(&barrier);
|
|
|
|
ntid = H5TS_thread_id();
|
|
if (ntid != tid) {
|
|
atomic_printf("tid changed from %" PRIu64 " to %" PRIu64 " FAIL\n",
|
|
tid, ntid);
|
|
failed = true;
|
|
}
|
|
return NULL;
|
|
pre_barrier_error:
|
|
pthread_barrier_wait(&barrier);
|
|
failed = true;
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
int i, rc, times;
|
|
pthread_t threads[NTHREADS];
|
|
|
|
/* Run H5open() to initialize the library's thread-ID freelist,
|
|
* mutex, etc.
|
|
*/
|
|
if (H5open() != SUCCEED)
|
|
errx(EXIT_FAILURE, "%s.%d: H5open failed", __func__, __LINE__);
|
|
|
|
if ((rc = pthread_mutex_init(&used_lock, NULL)) == -1)
|
|
threads_failure(pthread_mutex_init, rc);
|
|
|
|
if ((rc = pthread_barrier_init(&barrier, NULL, NTHREADS)) != 0)
|
|
threads_failure(pthread_barrier_init, rc);
|
|
|
|
/* Start the test threads and join them twice to make sure that
|
|
* the thread IDs are recycled in the second round.
|
|
*/
|
|
for (times = 0; times < 2; times++) {
|
|
|
|
for (i = 0; i < NTHREADS; i++)
|
|
used[i] = false; // access synchronized by thread create/join
|
|
|
|
for (i = 0; i < NTHREADS; i++) {
|
|
rc = pthread_create(&threads[i], NULL, thread_main, NULL);
|
|
if (rc != 0)
|
|
threads_failure(pthread_create, rc);
|
|
}
|
|
|
|
for (i = 0; i < NTHREADS; i++) {
|
|
rc = pthread_join(threads[i], NULL);
|
|
if (rc != 0)
|
|
threads_failure(pthread_join, rc);
|
|
}
|
|
|
|
for (i = 0; i < NTHREADS; i++) {
|
|
if (!used[i]) // access synchronized by thread create/join
|
|
errx(EXIT_FAILURE, "thread ID %d did not run.", i + 1);
|
|
}
|
|
}
|
|
if ((rc = pthread_barrier_destroy(&barrier)) != 0)
|
|
threads_failure(pthread_barrier_destroy, rc);
|
|
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
}
|
|
|
|
#else /*H5_HAVE_THREADSAFE && !H5_HAVE_WIN_THREADS*/
|
|
int
|
|
main(void)
|
|
{
|
|
errx(EXIT_FAILURE, "not implemented in this configuration.");
|
|
}
|
|
|
|
#endif /*H5_HAVE_THREADSAFE && !H5_HAVE_WIN_THREADS*/
|
|
|