mirror of
https://github.com/HDFGroup/hdf5.git
synced 2025-04-12 17:31:09 +08:00
Replace pthread_self_ulong() with H5TS_thread_id(). The POSIX Threads
implementation ought to be portable to any system that has POSIX Threads. On Windows, I use the same API call as before.
This commit is contained in:
parent
0147d2493e
commit
986c7451a0
@ -145,11 +145,7 @@ H5CS_print_stack(const H5CS_t *fstack, FILE *stream)
|
||||
|
||||
HDfprintf(stream, "HDF5-DIAG: Function stack from %s ", H5_lib_vers_info_g);
|
||||
/* try show the process or thread id in multiple processes cases*/
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
HDfprintf(stream, "thread %lu.", HDpthread_self_ulong());
|
||||
#else /* H5_HAVE_THREADSAFE */
|
||||
HDfprintf(stream, "thread 0.");
|
||||
#endif /* H5_HAVE_THREADSAFE */
|
||||
HDfprintf(stream, "thread %lu.", H5TS_thread_id());
|
||||
if(fstack && fstack->nused>0)
|
||||
HDfprintf(stream, " Back trace follows.");
|
||||
HDfputc('\n', stream);
|
||||
|
@ -259,10 +259,8 @@ H5E__walk1_cb(int n, H5E_error1_t *err_desc, void *client_data)
|
||||
else
|
||||
HDfprintf(stream, "thread 0");
|
||||
} /* end block */
|
||||
#elif defined(H5_HAVE_THREADSAFE)
|
||||
HDfprintf(stream, "thread %lu", (unsigned long)HDpthread_self_ulong());
|
||||
#else
|
||||
HDfprintf(stream, "thread 0");
|
||||
HDfprintf(stream, "thread %lu", H5TS_thread_id());
|
||||
#endif
|
||||
HDfprintf(stream, ":\n");
|
||||
} /* end if */
|
||||
@ -391,10 +389,8 @@ H5E__walk2_cb(unsigned n, const H5E_error2_t *err_desc, void *client_data)
|
||||
else
|
||||
HDfprintf(stream, "thread 0");
|
||||
} /* end block */
|
||||
#elif defined(H5_HAVE_THREADSAFE)
|
||||
HDfprintf(stream, "thread %lu", (unsigned long)HDpthread_self_ulong());
|
||||
#else
|
||||
HDfprintf(stream, "thread 0");
|
||||
HDfprintf(stream, "thread %lu", H5TS_thread_id());
|
||||
#endif
|
||||
HDfprintf(stream, ":\n");
|
||||
} /* end if */
|
||||
|
119
src/H5TS.c
119
src/H5TS.c
@ -37,6 +37,31 @@ H5TS_key_t H5TS_funcstk_key_g;
|
||||
H5TS_key_t H5TS_apictx_key_g;
|
||||
H5TS_key_t H5TS_cancel_key_g;
|
||||
|
||||
#ifndef H5_HAVE_WIN_THREADS
|
||||
|
||||
/* An h5_tid_t is a record of a thread identifier that is
|
||||
* available for reuse.
|
||||
*/
|
||||
struct _tid;
|
||||
typedef struct _tid h5_tid_t;
|
||||
|
||||
struct _tid {
|
||||
h5_tid_t *next;
|
||||
unsigned long id;
|
||||
};
|
||||
|
||||
/* Pointer to first free thread ID record or NULL. */
|
||||
static h5_tid_t *tid_next_free = NULL;
|
||||
static unsigned long tid_next_id = 0;
|
||||
|
||||
/* Mutual exclusion for access to tid_next_free and tid_next_id. */
|
||||
static pthread_mutex_t tid_mtx;
|
||||
|
||||
/* Key for thread-local storage of the thread ID. */
|
||||
static H5TS_key_t tid_key;
|
||||
|
||||
#endif /* H5_HAVE_WIN_THREADS */
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
* NAME
|
||||
@ -68,6 +93,89 @@ H5TS_key_destructor(void *key_val)
|
||||
|
||||
#ifndef H5_HAVE_WIN_THREADS
|
||||
|
||||
/* When a thread shuts down, put its ID record on the free list. */
|
||||
static void
|
||||
tid_destructor(void *_v)
|
||||
{
|
||||
h5_tid_t *tid = _v;
|
||||
|
||||
if (tid == NULL)
|
||||
return;
|
||||
|
||||
/* XXX I can use mutexes in destructors, right? */
|
||||
/* TBD use an atomic CAS */
|
||||
pthread_mutex_lock(&tid_mtx);
|
||||
tid->next = tid_next_free;
|
||||
tid_next_free = tid;
|
||||
pthread_mutex_unlock(&tid_mtx);
|
||||
}
|
||||
|
||||
/* Initialize for integer thread identifiers. */
|
||||
static void
|
||||
tid_init(void)
|
||||
{
|
||||
pthread_mutex_init(&tid_mtx, NULL);
|
||||
pthread_key_create(&tid_key, tid_destructor);
|
||||
}
|
||||
|
||||
/* Return an integer identifier, ID, for the current thread satisfies the
|
||||
* following properties:
|
||||
*
|
||||
* 1 1 <= ID <= ULONG_MAX
|
||||
* 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.
|
||||
*
|
||||
* ID 0 is reserved. H5TS_thread_id() returns 0 if the library was not built
|
||||
* with thread safety or if an error prevents it from assigning an ID.
|
||||
*/
|
||||
unsigned long
|
||||
H5TS_thread_id(void)
|
||||
{
|
||||
h5_tid_t *tid = pthread_getspecific(tid_key);
|
||||
h5_tid_t proto_tid;
|
||||
|
||||
/* An ID is already assigned. */
|
||||
if (tid != NULL)
|
||||
return tid->id;
|
||||
|
||||
/* An ID is *not* already assigned: reuse an ID that's on the
|
||||
* free list, or else generate a new ID.
|
||||
*
|
||||
* Allocating memory while holding a mutex is bad form, so
|
||||
* point `tid` at `proto_tid` if we need to allocate some
|
||||
* memory.
|
||||
*/
|
||||
pthread_mutex_lock(&tid_mtx);
|
||||
if ((tid = tid_next_free) != NULL)
|
||||
tid_next_free = tid->next;
|
||||
else if (tid_next_id != ULONG_MAX) {
|
||||
tid = &proto_tid;
|
||||
tid->id = ++tid_next_id;
|
||||
}
|
||||
pthread_mutex_unlock(&tid_mtx);
|
||||
|
||||
/* If a prototype ID record was established, copy it to the heap. */
|
||||
if (tid == &proto_tid) {
|
||||
if ((tid = HDmalloc(sizeof(*tid))) != NULL)
|
||||
*tid = proto_tid;
|
||||
}
|
||||
|
||||
if (tid == NULL)
|
||||
return 0;
|
||||
|
||||
/* Finish initializing the ID record and set a thread-local pointer
|
||||
* to it.
|
||||
*/
|
||||
tid->next = NULL;
|
||||
if (pthread_setspecific(tid_key, tid) != 0) {
|
||||
tid_destructor(tid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tid->id;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
* NAME
|
||||
* H5TS_pthread_first_thread_init
|
||||
@ -104,6 +212,9 @@ H5TS_pthread_first_thread_init(void)
|
||||
pthread_cond_init(&H5_g.init_lock.cond_var, NULL);
|
||||
H5_g.init_lock.lock_count = 0;
|
||||
|
||||
/* Initialize integer thread identifiers. */
|
||||
tid_init();
|
||||
|
||||
/* initialize key for thread-specific error stacks */
|
||||
pthread_key_create(&H5TS_errstk_key_g, H5TS_key_destructor);
|
||||
|
||||
@ -528,5 +639,13 @@ H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata)
|
||||
|
||||
} /* H5TS_create_thread */
|
||||
|
||||
#else /* H5_HAVE_THREADSAFE */
|
||||
|
||||
unsigned long
|
||||
H5TS_thread_id(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* H5_HAVE_THREADSAFE */
|
||||
|
||||
|
@ -68,7 +68,7 @@ H5_DLL void H5TS_win32_process_exit(void);
|
||||
H5_DLL herr_t H5TS_win32_thread_enter(void);
|
||||
H5_DLL herr_t H5TS_win32_thread_exit(void);
|
||||
|
||||
|
||||
#define H5TS_thread_id() ((unsigned long)GetCurrentThreadId())
|
||||
|
||||
#else /* H5_HAVE_WIN_THREADS */
|
||||
|
||||
@ -102,6 +102,7 @@ typedef pthread_once_t H5TS_once_t;
|
||||
#define H5TS_mutex_init(mutex) pthread_mutex_init(mutex, NULL)
|
||||
#define H5TS_mutex_lock_simple(mutex) pthread_mutex_lock(mutex)
|
||||
#define H5TS_mutex_unlock_simple(mutex) pthread_mutex_unlock(mutex)
|
||||
H5_DLL unsigned long H5TS_thread_id(void);
|
||||
|
||||
#endif /* H5_HAVE_WIN_THREADS */
|
||||
|
||||
|
@ -1549,11 +1549,6 @@ extern char *strdup(const char *s);
|
||||
#define HDpthread_self() pthread_self()
|
||||
#endif /* HDpthread_self */
|
||||
|
||||
/* Use this version of pthread_self for printing the thread ID */
|
||||
#ifndef HDpthread_self_ulong
|
||||
#define HDpthread_self_ulong() ((unsigned long)pthread_self())
|
||||
#endif /* HDpthread_self_ulong */
|
||||
|
||||
/* Macro for "stringizing" an integer in the C preprocessor (use H5_TOSTRING) */
|
||||
/* (use H5_TOSTRING, H5_STRINGIZE is just part of the implementation) */
|
||||
#define H5_STRINGIZE(x) #x
|
||||
|
@ -163,10 +163,6 @@ extern "C" {
|
||||
|
||||
/* Non-POSIX functions */
|
||||
|
||||
/* Don't use actual pthread_self on Windows because the return
|
||||
* type cannot be cast as a ulong like other systems. */
|
||||
#define HDpthread_self_ulong() ((unsigned long)GetCurrentThreadId())
|
||||
|
||||
#ifndef H5_HAVE_MINGW
|
||||
#define HDftruncate(F,L) _chsize_s(F,L)
|
||||
#define HDfseek(F,O,W) _fseeki64(F,O,W)
|
||||
|
@ -63,7 +63,7 @@ TEST_PROG= testhdf5 \
|
||||
flush1 flush2 app_ref enum set_extent ttsafe enc_dec_plist \
|
||||
enc_dec_plist_cross_platform getname vfd ros3 s3comms hdfs ntypes \
|
||||
dangle dtransform reserved cross_read freespace mf vds file_image \
|
||||
unregister cache_logging cork swmr vol
|
||||
unregister cache_logging cork swmr thread_id vol
|
||||
|
||||
# List programs to be built when testing here.
|
||||
# error_test and err_compat are built at the same time as the other tests, but executed by testerror.sh.
|
||||
|
@ -1172,10 +1172,8 @@ h5_show_hostname(void)
|
||||
else
|
||||
HDprintf("thread 0.");
|
||||
}
|
||||
#elif defined(H5_HAVE_THREADSAFE)
|
||||
HDprintf("thread %lu.", HDpthread_self_ulong());
|
||||
#else
|
||||
HDprintf("thread 0.");
|
||||
HDprintf("thread %lu.", H5TS_thread_id());
|
||||
#endif
|
||||
#ifdef H5_HAVE_WIN32_API
|
||||
|
||||
|
162
test/thread_id.c
Normal file
162
test/thread_id.c
Normal file
@ -0,0 +1,162 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* 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"
|
||||
|
||||
#define threads_failure(_call, _result) do { \
|
||||
errx(EXIT_FAILURE, "%s.%d: " #_call ": %s", __func__, \
|
||||
__LINE__, strerror(_result)); \
|
||||
} while (false)
|
||||
|
||||
#if defined(H5_HAVE_THREADSAFE) && !defined(H5_HAVE_WIN_THREADS)
|
||||
|
||||
#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)
|
||||
{
|
||||
unsigned long ntid, tid;
|
||||
|
||||
tid = H5TS_thread_id();
|
||||
|
||||
if (tid < 1 || NTHREADS < tid) {
|
||||
atomic_printf("unexpected tid %lu FAIL\n", tid);
|
||||
goto pre_barrier_error;
|
||||
}
|
||||
pthread_mutex_lock(&used_lock);
|
||||
if (used[tid - 1]) {
|
||||
atomic_printf("reused tid %lu FAIL\n", tid);
|
||||
pthread_mutex_unlock(&used_lock);
|
||||
goto pre_barrier_error;
|
||||
}
|
||||
used[tid - 1] = true;
|
||||
pthread_mutex_unlock(&used_lock);
|
||||
|
||||
atomic_printf("tid %lu in [1, %d] PASS\n", tid, NTHREADS);
|
||||
pthread_barrier_wait(&barrier);
|
||||
|
||||
ntid = H5TS_thread_id();
|
||||
if (ntid != tid) {
|
||||
atomic_printf("tid changed from %lu to %lu 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*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user