mirror of
https://github.com/HDFGroup/hdf5.git
synced 2024-11-21 01:04:10 +08:00
[svn-r2264] Added Thread-safe feature. This is the phase 1 implementation
that all HDF5 API functions are protected by a mutex lock. Basically, serialized all API calls. To use it, use configure --enable-threadsafe --with-pthread
This commit is contained in:
parent
bc520e88b4
commit
e26f4e5eed
71
src/H5.c
71
src/H5.c
@ -40,7 +40,19 @@ FILE *fdopen(int fd, const char *mode);
|
||||
|
||||
#define PABLO_MASK H5_mask
|
||||
|
||||
/* statically initialize block for pthread_once call used in initializing */
|
||||
/* the first global mutex */
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
pthread_once_t H5_first_init_g = PTHREAD_ONCE_INIT;
|
||||
pthread_key_t H5_errstk_key_g;
|
||||
pthread_key_t H5_cancel_key_g;
|
||||
hbool_t H5_allow_concurrent_g = FALSE; /* concurrent APIs override this */
|
||||
|
||||
H5_api_t H5_g;
|
||||
#else
|
||||
hbool_t H5_libinit_g = FALSE;
|
||||
#endif
|
||||
|
||||
hbool_t dont_atexit_g = FALSE;
|
||||
H5_debug_t H5_debug_g; /*debugging info */
|
||||
static void H5_debug_mask(const char*);
|
||||
@ -50,17 +62,19 @@ static intn interface_initialize_g = 0;
|
||||
#define INTERFACE_INIT NULL
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
NAME
|
||||
H5_init_library -- Initialize library-global information
|
||||
USAGE
|
||||
herr_t H5_init_library()
|
||||
|
||||
RETURNS
|
||||
Non-negative on success/Negative on failure
|
||||
DESCRIPTION
|
||||
Initializes any library-global data or routines.
|
||||
|
||||
--------------------------------------------------------------------------*/
|
||||
* NAME
|
||||
* H5_init_library -- Initialize library-global information
|
||||
* USAGE
|
||||
* herr_t H5_init_library()
|
||||
*
|
||||
* RETURNS
|
||||
* Non-negative on success/Negative on failure
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initializes any library-global data or routines.
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
herr_t
|
||||
H5_init_library(void)
|
||||
{
|
||||
@ -147,7 +161,17 @@ H5_term_library(void)
|
||||
H5E_auto_t func;
|
||||
|
||||
/* Don't do anything if the library is already closed */
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
|
||||
/* explicit locking of the API */
|
||||
pthread_once(&H5_first_init_g, H5_first_thread_init);
|
||||
|
||||
H5_mutex_lock(&H5_g.init_lock);
|
||||
|
||||
if (!H5_g.H5_libinit_g) return;
|
||||
#else
|
||||
if (!H5_libinit_g) return;
|
||||
#endif
|
||||
|
||||
/* Check if we should display error output */
|
||||
H5Eget_auto(&func,NULL);
|
||||
@ -190,7 +214,13 @@ H5_term_library(void)
|
||||
}
|
||||
|
||||
/* Mark library as closed */
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
H5_g.H5_libinit_g = FALSE;
|
||||
|
||||
H5_mutex_unlock(&H5_g.init_lock);
|
||||
#else
|
||||
H5_libinit_g = FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -223,10 +253,20 @@ herr_t
|
||||
H5dont_atexit(void)
|
||||
{
|
||||
/* FUNC_ENTER_INIT() should not be called */
|
||||
|
||||
/* locking code explicitly since FUNC_ENTER is not called */
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
pthread_once(&H5_first_init_g, H5_first_thread_init);
|
||||
|
||||
H5_mutex_lock(&H5_g.init_lock);
|
||||
#endif
|
||||
H5_trace(FALSE, "H5dont_atexit", "");
|
||||
if (dont_atexit_g) return FAIL;
|
||||
dont_atexit_g = TRUE;
|
||||
H5_trace(TRUE, NULL, "e", SUCCEED);
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
H5_mutex_unlock(&H5_g.init_lock);
|
||||
#endif
|
||||
return(SUCCEED);
|
||||
}
|
||||
|
||||
@ -479,7 +519,16 @@ H5close (void)
|
||||
* thing just to release it all right away. It is safe to call this
|
||||
* function for an uninitialized library.
|
||||
*/
|
||||
/* Explicitly lock the call since FUNC_ENTER is not called */
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
pthread_once(&H5_first_init_g, H5_first_thread_init);
|
||||
|
||||
H5_mutex_lock(&H5_g.init_lock);
|
||||
#endif
|
||||
H5_term_library();
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
H5_mutex_unlock(&H5_g.init_lock);
|
||||
#endif
|
||||
return SUCCEED;
|
||||
}
|
||||
|
||||
|
55
src/H5E.c
55
src/H5E.c
@ -146,13 +146,26 @@ static intn interface_initialize_g = 0;
|
||||
static herr_t H5E_init_interface (void);
|
||||
const hbool_t H5E_clearable_g = TRUE; /* DO NOT CHANGE */
|
||||
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
/*
|
||||
* The per-thread error stack. pthread_once() initializes a special
|
||||
* key that will be used by all threads to create a stack specific to
|
||||
* each thread individually. The association of stacks to threads will
|
||||
* be handled by the pthread library.
|
||||
*
|
||||
* In order for this macro to work, H5E_get_my_stack() must be preceeded
|
||||
* by "H5E_t *estack =".
|
||||
*/
|
||||
H5E_t *H5E_get_stack(void);
|
||||
#define H5E_get_my_stack() H5E_get_stack()
|
||||
#else
|
||||
/*
|
||||
* The error stack. Eventually we'll have some sort of global table so each
|
||||
* thread has it's own stack. The stacks will be created on demand when the
|
||||
* thread first calls H5E_push().
|
||||
*/
|
||||
* thread first calls H5E_push(). */
|
||||
H5E_t H5E_stack_g[1];
|
||||
#define H5E_get_my_stack() (H5E_stack_g+0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Automatic error stack traversal occurs if the traversal callback function
|
||||
@ -162,6 +175,39 @@ H5E_t H5E_stack_g[1];
|
||||
herr_t (*H5E_auto_g)(void*) = (herr_t(*)(void*))H5Eprint;
|
||||
void *H5E_auto_data_g = NULL;
|
||||
|
||||
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
/*-------------------------------------------------------------------------
|
||||
* Function: H5E_get_stack
|
||||
*
|
||||
* Purpose: Support function for H5E_get_my_stack() to initialize and
|
||||
* acquire per-thread error stack.
|
||||
*
|
||||
* Return: Success: error stack (H5E_t *)
|
||||
*
|
||||
* Failure: NULL
|
||||
*
|
||||
* Programmer: Chee Wai LEE
|
||||
* April 24, 2000
|
||||
*
|
||||
* Modifications:
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
H5E_t *H5E_get_stack() {
|
||||
H5E_t *estack;
|
||||
|
||||
if (estack = pthread_getspecific(H5_errstk_key_g)) {
|
||||
return estack;
|
||||
} else {
|
||||
/* no associated value with current thread - create one */
|
||||
estack = (H5E_t *)malloc(sizeof(H5E_t));
|
||||
pthread_setspecific(H5_errstk_key_g, (void *)estack);
|
||||
return estack;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Function: H5E_init_interface
|
||||
@ -311,7 +357,12 @@ H5Eprint(FILE *stream)
|
||||
/*NO TRACE*/
|
||||
|
||||
if (!stream) stream = stderr;
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
fprintf (stream, "HDF5-DIAG: Error detected in thread %d."
|
||||
,pthread_self());
|
||||
#else
|
||||
fprintf (stream, "HDF5-DIAG: Error detected in thread 0.");
|
||||
#endif
|
||||
if (estack && estack->nused>0) fprintf (stream, " Back trace follows.");
|
||||
HDfputc ('\n', stream);
|
||||
status = H5E_walk (H5E_WALK_DOWNWARD, H5Ewalk_cb, (void*)stream);
|
||||
|
@ -43,6 +43,9 @@
|
||||
if (H5_IS_API(FUNC) && H5E_auto_g) { \
|
||||
(H5E_auto_g)(H5E_auto_data_g); \
|
||||
} \
|
||||
H5_API_UNLOCK_BEGIN \
|
||||
H5_API_UNLOCK_END \
|
||||
H5_API_SET_CANCEL \
|
||||
return (ret_val); \
|
||||
}
|
||||
|
||||
@ -54,6 +57,9 @@
|
||||
#define HRETURN(ret_val) { \
|
||||
PABLO_TRACE_OFF (PABLO_MASK, pablo_func_id); \
|
||||
H5TRACE_RETURN(ret_val); \
|
||||
H5_API_UNLOCK_BEGIN \
|
||||
H5_API_UNLOCK_END \
|
||||
H5_API_SET_CANCEL \
|
||||
return (ret_val); \
|
||||
}
|
||||
|
||||
|
243
src/H5TS.c
Normal file
243
src/H5TS.c
Normal file
@ -0,0 +1,243 @@
|
||||
/****************************************************************************
|
||||
* NCSA HDF *
|
||||
* Software Development Group *
|
||||
* National Center for Supercomputing Applications *
|
||||
* University of Illinois at Urbana-Champaign *
|
||||
* 605 E. Springfield, Champaign IL 61820 *
|
||||
* *
|
||||
* For conditions of distribution and use, see the accompanying *
|
||||
* hdf/COPYING file. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef RCSID
|
||||
static char RcsId[] = "@(#)$Revision$";
|
||||
#endif
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/* private headers */
|
||||
#include <H5private.h> /*library */
|
||||
#include <H5Eprivate.h> /*error handling */
|
||||
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
* NAME
|
||||
* H5_first_thread_init
|
||||
* USAGE
|
||||
* H5_first_thread_init()
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Initialization of global API lock, keys for per-thread error stacks
|
||||
* and cancallability information. Called by the first thread that enters
|
||||
* the library.
|
||||
*
|
||||
* PROGRAMMER: Chee Wai LEE
|
||||
* May 2, 2000
|
||||
*
|
||||
* MODIFICATIONS:
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
void H5_first_thread_init() {
|
||||
/* initialize global API mutex lock */
|
||||
H5_g.H5_libinit_g = FALSE;
|
||||
H5_g.init_lock.owner_thread = NULL;
|
||||
pthread_mutex_init(&H5_g.init_lock.atomic_lock, NULL);
|
||||
pthread_cond_init(&H5_g.init_lock.cond_var, NULL);
|
||||
H5_g.init_lock.lock_count = 0;
|
||||
|
||||
/* initialize key for thread-specific error stacks */
|
||||
pthread_key_create(&H5_errstk_key_g, NULL);
|
||||
|
||||
/* initialize key for thread cancellability mechanism */
|
||||
pthread_key_create(&H5_cancel_key_g, NULL);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
* NAME
|
||||
* H5_mutex_init
|
||||
* USAGE
|
||||
* H5_mutex_init(&mutex_var)
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Recursive lock semantics for HDF5 (lock initialization) -
|
||||
* Multiple acquisition of a lock by a thread is permitted with a
|
||||
* corresponding unlock operation required.
|
||||
*
|
||||
* PROGRAMMER: Chee Wai LEE
|
||||
* May 2, 2000
|
||||
*
|
||||
* MODIFICATIONS:
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
void H5_mutex_init(H5_mutex_t *H5_mutex) {
|
||||
(*H5_mutex).owner_thread = NULL;
|
||||
pthread_mutex_init(&(*H5_mutex).atomic_lock, NULL);
|
||||
pthread_cond_init(&(*H5_mutex).cond_var, NULL);
|
||||
(*H5_mutex).lock_count = 0;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
* NAME
|
||||
* H5_mutex_lock
|
||||
* USAGE
|
||||
* H5_mutex_lock(&mutex_var)
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Recursive lock semantics for HDF5 (locking) -
|
||||
* Multiple acquisition of a lock by a thread is permitted with a
|
||||
* corresponding unlock operation required.
|
||||
*
|
||||
* PROGRAMMER: Chee Wai LEE
|
||||
* May 2, 2000
|
||||
*
|
||||
* MODIFICATIONS:
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
void H5_mutex_lock(H5_mutex_t *H5_mutex) {
|
||||
pthread_mutex_lock(&(*H5_mutex).atomic_lock);
|
||||
if (pthread_equal(pthread_self(), (*H5_mutex).owner_thread)) {
|
||||
/* already owned by self - increment count */
|
||||
(*H5_mutex).lock_count++;
|
||||
} else {
|
||||
if ((*H5_mutex).owner_thread == NULL) {
|
||||
/* no one else has locked it - set owner and grab lock */
|
||||
(*H5_mutex).owner_thread = pthread_self();
|
||||
(*H5_mutex).lock_count = 1;
|
||||
} else {
|
||||
/* if already locked by someone else */
|
||||
while (1) {
|
||||
pthread_cond_wait(&(*H5_mutex).cond_var, &(*H5_mutex).atomic_lock);
|
||||
if ((*H5_mutex).owner_thread == NULL) {
|
||||
(*H5_mutex).owner_thread = pthread_self();
|
||||
(*H5_mutex).lock_count = 1;
|
||||
break;
|
||||
} /* else do nothing and loop back to wait on condition*/
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&(*H5_mutex).atomic_lock);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
* NAME
|
||||
* H5_mutex_unlock
|
||||
* USAGE
|
||||
* H5_mutex_unlock(&mutex_var)
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Recursive lock semantics for HDF5 (unlocking) -
|
||||
* Multiple acquisition of a lock by a thread is permitted with a
|
||||
* corresponding unlock operation required.
|
||||
*
|
||||
* PROGRAMMER: Chee Wai LEE
|
||||
* May 2, 2000
|
||||
*
|
||||
* MODIFICATIONS:
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
void H5_mutex_unlock(H5_mutex_t *H5_mutex) {
|
||||
pthread_mutex_lock(&(*H5_mutex).atomic_lock);
|
||||
(*H5_mutex).lock_count--;
|
||||
if ((*H5_mutex).lock_count == 0) {
|
||||
(*H5_mutex).owner_thread = NULL;
|
||||
pthread_cond_signal(&(*H5_mutex).cond_var);
|
||||
}
|
||||
pthread_mutex_unlock(&(*H5_mutex).atomic_lock);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
* NAME
|
||||
* H5_cancel_count_inc
|
||||
* USAGE
|
||||
* H5_cancel_count_inc()
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Creates a cancelation counter for a thread if it is the first time
|
||||
* the thread is entering the library.
|
||||
*
|
||||
* if counter value is zero, then set cancelability type of the thread
|
||||
* to PTHREAD_CANCEL_DISABLE as thread is entering the library and store
|
||||
* the previous cancelability type into cancelation counter.
|
||||
* Increase the counter value by 1.
|
||||
*
|
||||
* PROGRAMMER: Chee Wai LEE
|
||||
* May 2, 2000
|
||||
*
|
||||
* MODIFICATIONS:
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
H5_cancel_count_inc(void)
|
||||
{
|
||||
H5_cancel_t *cancel_counter;
|
||||
|
||||
if (cancel_counter = pthread_getspecific(H5_cancel_key_g)) {
|
||||
/* do nothing here */
|
||||
} else {
|
||||
/* first time thread calls library - create new counter and associate
|
||||
with key
|
||||
*/
|
||||
cancel_counter = (H5_cancel_t *)malloc(sizeof(H5_cancel_t));
|
||||
cancel_counter->cancel_count = 0;
|
||||
pthread_setspecific(H5_cancel_key_g, (void *)cancel_counter);
|
||||
}
|
||||
|
||||
if (cancel_counter->cancel_count == 0) {
|
||||
/* thread entering library */
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
|
||||
&(cancel_counter->previous_state));
|
||||
}
|
||||
cancel_counter->cancel_count++;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
* NAME
|
||||
* H5_cancel_count_dec
|
||||
* USAGE
|
||||
* H5_cancel_count_dec()
|
||||
*
|
||||
* RETURNS
|
||||
*
|
||||
* DESCRIPTION
|
||||
* if counter value is one, then set cancelability type of the thread
|
||||
* to the previous cancelability type stored in the cancelation counter.
|
||||
* (the thread is leaving the library).
|
||||
*
|
||||
* Decrement the counter value by 1.
|
||||
*
|
||||
* PROGRAMMER: Chee Wai LEE
|
||||
* May 2, 2000
|
||||
*
|
||||
* MODIFICATIONS:
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
H5_cancel_count_dec(void)
|
||||
{
|
||||
H5_cancel_t *cancel_counter = pthread_getspecific(H5_cancel_key_g);
|
||||
|
||||
if (cancel_counter->cancel_count == 1) {
|
||||
pthread_setcancelstate(cancel_counter->previous_state, NULL);
|
||||
}
|
||||
cancel_counter->cancel_count--;
|
||||
}
|
||||
|
||||
#endif
|
104
src/H5private.h
104
src/H5private.h
@ -15,6 +15,11 @@
|
||||
#include <H5public.h> /* Include Public Definitions */
|
||||
#include <H5config.h> /* Include all configuration info */
|
||||
|
||||
/* include the pthread library */
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Include ANSI-C header files.
|
||||
*/
|
||||
@ -868,11 +873,89 @@ __DLL__ void H5_trace(hbool_t returning, const char *func, const char *type,
|
||||
* Added auto variable RTYPE which is initialized by the tracing macros.
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
extern hbool_t H5_libinit_g; /*good thing C's lazy about extern! */
|
||||
|
||||
/* Is `S' the name of an API function? */
|
||||
#define H5_IS_API(S) ('_'!=S[2] && '_'!=S[3] && (!S[4] || '_'!=S[4]))
|
||||
|
||||
/* Lock headers */
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
typedef struct H5_mutex_struct {
|
||||
pthread_t owner_thread; /* current lock owner */
|
||||
pthread_mutex_t atomic_lock; /* lock for atomicity of new mechanism */
|
||||
pthread_cond_t cond_var; /* condition variable */
|
||||
unsigned int lock_count;
|
||||
} H5_mutex_t;
|
||||
|
||||
/* cancelability structure */
|
||||
typedef struct H5_cancel_struct {
|
||||
int previous_state;
|
||||
unsigned int cancel_count;
|
||||
} H5_cancel_t;
|
||||
|
||||
/* replacement structure for original global variable */
|
||||
typedef struct H5_api_struct {
|
||||
H5_mutex_t init_lock; /* API entrance mutex */
|
||||
hbool_t H5_libinit_g;
|
||||
} H5_api_t;
|
||||
|
||||
|
||||
/* Macro for first thread initialization */
|
||||
#define H5_FIRST_THREAD_INIT \
|
||||
pthread_once(&H5_first_init_g, H5_first_thread_init);
|
||||
|
||||
/* Macros for threadsafe HDF-5 Phase I locks */
|
||||
#define H5_INIT_GLOBAL H5_g.H5_libinit_g
|
||||
#define H5_API_LOCK_BEGIN \
|
||||
if (H5_IS_API(FUNC)) { \
|
||||
H5_mutex_lock(&H5_g.init_lock);
|
||||
#define H5_API_LOCK_END }
|
||||
#define H5_API_UNLOCK_BEGIN \
|
||||
if (H5_IS_API(FUNC)) { \
|
||||
H5_mutex_unlock(&H5_g.init_lock);
|
||||
#define H5_API_UNLOCK_END }
|
||||
|
||||
/* Macros for thread cancellation-safe mechanism */
|
||||
#define H5_API_UNSET_CANCEL \
|
||||
if (H5_IS_API(FUNC)) { \
|
||||
H5_cancel_count_inc(); \
|
||||
}
|
||||
|
||||
#define H5_API_SET_CANCEL \
|
||||
if (H5_IS_API(FUNC)) { \
|
||||
H5_cancel_count_dec(); \
|
||||
}
|
||||
|
||||
/* Extern global variables */
|
||||
extern pthread_once_t H5_first_init_g;
|
||||
extern pthread_key_t H5_errstk_key_g;
|
||||
extern pthread_key_t H5_cancel_key_g;
|
||||
extern hbool_t H5_allow_concurrent_g;
|
||||
extern H5_api_t H5_g;
|
||||
|
||||
void H5_first_thread_init(void);
|
||||
|
||||
#else
|
||||
|
||||
/* disable any first thread init mechanism */
|
||||
#define H5_FIRST_THREAD_INIT
|
||||
|
||||
#define H5_INIT_GLOBAL H5_libinit_g
|
||||
|
||||
/* disable locks (sequential version) */
|
||||
#define H5_API_LOCK_BEGIN
|
||||
#define H5_API_LOCK_END
|
||||
#define H5_API_UNLOCK_BEGIN
|
||||
#define H5_API_UNLOCK_END
|
||||
|
||||
/* disable cancelability (sequential version) */
|
||||
#define H5_API_UNSET_CANCEL
|
||||
#define H5_API_SET_CANCEL
|
||||
|
||||
/* extern global variables */
|
||||
|
||||
extern hbool_t H5_libinit_g; /*good thing C's lazy about extern! */
|
||||
#endif
|
||||
|
||||
#define FUNC_ENTER(func_name,err) FUNC_ENTER_INIT(func_name,INTERFACE_INIT,err)
|
||||
|
||||
#define FUNC_ENTER_INIT(func_name,interface_init_func,err) { \
|
||||
@ -883,13 +966,17 @@ extern hbool_t H5_libinit_g; /*good thing C's lazy about extern! */
|
||||
PABLO_TRACE_ON (PABLO_MASK, pablo_func_id); \
|
||||
\
|
||||
/* Initialize the library */ \
|
||||
if (!H5_libinit_g) { \
|
||||
H5_libinit_g = TRUE; \
|
||||
if (H5_init_library()<0) { \
|
||||
HRETURN_ERROR (H5E_FUNC, H5E_CANTINIT, err, \
|
||||
"library initialization failed"); \
|
||||
} \
|
||||
} \
|
||||
H5_FIRST_THREAD_INIT \
|
||||
H5_API_UNSET_CANCEL \
|
||||
H5_API_LOCK_BEGIN \
|
||||
if (!(H5_INIT_GLOBAL)) { \
|
||||
H5_INIT_GLOBAL = TRUE; \
|
||||
if (H5_init_library()<0) { \
|
||||
HRETURN_ERROR (H5E_FUNC, H5E_CANTINIT, err, \
|
||||
"library initialization failed"); \
|
||||
} \
|
||||
} \
|
||||
H5_API_LOCK_END \
|
||||
\
|
||||
/* Initialize this interface or bust */ \
|
||||
if (!interface_initialize_g) { \
|
||||
@ -925,7 +1012,6 @@ extern hbool_t H5_libinit_g; /*good thing C's lazy about extern! */
|
||||
*/
|
||||
#define FUNC_LEAVE(return_value) HRETURN(return_value)}}
|
||||
|
||||
|
||||
/*
|
||||
* The FUNC_ENTER() and FUNC_LEAVE() macros make calls to Pablo functions
|
||||
* through one of these two sets of macros.
|
||||
|
@ -26,7 +26,8 @@ LIB_SRC=H5.c H5A.c H5AC.c H5B.c H5D.c H5E.c H5F.c H5Farray.c H5Fistore.c \
|
||||
H5Ocont.c H5Odtype.c H5Oefl.c H5Ofill.c H5Olayout.c H5Omtime.c H5Oname.c \
|
||||
H5Onull.c H5Osdspace.c H5Oshared.c H5Ostab.c H5P.c H5R.c H5RA.c H5S.c \
|
||||
H5Sall.c H5Shyper.c H5Smpio.c H5Snone.c H5Spoint.c H5Sselect.c H5T.c \
|
||||
H5Tbit.c H5Tconv.c H5Tinit.c H5Tvlen.c H5TB.c H5V.c H5Z.c H5Zdeflate.c
|
||||
H5Tbit.c H5Tconv.c H5Tinit.c H5Tvlen.c H5TB.c H5TS.c H5V.c H5Z.c \
|
||||
H5Zdeflate.c
|
||||
|
||||
|
||||
LIB_OBJ=$(LIB_SRC:.c=.lo)
|
||||
|
@ -16,10 +16,12 @@ CPPFLAGS=-I. -I$(srcdir) -I../src -I$(top_srcdir)/src @CPPFLAGS@
|
||||
## These are our main targets. They should be listed in the order to be
|
||||
## executed, generally most specific tests to least specific tests.
|
||||
RUNTEST=$(LT_RUN)
|
||||
|
||||
TEST_PROGS=testhdf5 lheap ohdr stab gheap hyperslab istore bittests dtypes \
|
||||
dsets cmpd_dset extend external links unlink big mtime fillval mount \
|
||||
flush1 flush2 enum gass_write gass_read gass_append dpss_write \
|
||||
dpss_read srb_write srb_append srb_read
|
||||
dpss_read srb_write srb_append srb_read ttsafe
|
||||
|
||||
TIMINGS=iopipe chunk ragged overhead
|
||||
|
||||
## The libh5test.a library provides common support code for the tests. We link
|
||||
@ -46,25 +48,28 @@ MOSTLYCLEAN=cmpd_dset.h5 dataset.h5 extend.h5 istore.h5 tfile1.h5 tfile2.h5 \
|
||||
big.data big[0-9][0-9][0-9][0-9][0-9].h5 dtypes1.h5 dtypes2.h5 \
|
||||
tattr.h5 tselect.h5 mtime.h5 ragged.h5 unlink.h5 overhead.h5 \
|
||||
fillval_[0-9].h5 fillval.raw mount_[0-9].h5 trefer[12].h5 \
|
||||
tvltypes.h5 tvlstr.h5 flush.h5 enum1.h5 titerate.h5
|
||||
tvltypes.h5 tvlstr.h5 flush.h5 enum1.h5 titerate.h5 ttsafe.h5
|
||||
CLEAN=$(TIMINGS)
|
||||
|
||||
## Source and object files for programs... The TEST_SRC list contains all the
|
||||
## source files and is used for things like dependencies, archiving, etc. The
|
||||
## other source lists are for the individual tests, the files of which may
|
||||
## overlap with other tests.
|
||||
|
||||
TEST_SRC=big.c bittests.c chunk.c cmpd_dset.c dsets.c dtypes.c extend.c \
|
||||
external.c fillval.c flush1.c flush2.c gheap.c h5test.c hyperslab.c \
|
||||
iopipe.c istore.c lheap.c links.c mount.c mtime.c ohdr.c overhead.c \
|
||||
ragged.c stab.c tattr.c testhdf5.c tfile.c th5s.c titerate.c tmeta.c \
|
||||
trefer.c tselect.c ttbbt.c tvltypes.c tvlstr.c unlink.c enum.c \
|
||||
ttsafe.c ttsafe_dcreate.c ttsafe_error.c ttsafe_cancel.c \
|
||||
ttsafe_acreate.c \
|
||||
gass_write.c gass_read.c gass_append.c dpss_read.c dpss_write.c \
|
||||
srb_read.c srb_write.c srb_append.c
|
||||
|
||||
TEST_OBJ=$(TEST_SRC:.c=.lo)
|
||||
|
||||
## Private header files (not to be installed)...
|
||||
PRIVATE_HDR=testhdf5.h
|
||||
PRIVATE_HDR=testhdf5.h ttsafe.h
|
||||
|
||||
## Additional targets
|
||||
.PHONY: timings _timings
|
||||
@ -78,7 +83,12 @@ timings _timings: $(TIMINGS)
|
||||
|
||||
## How to build the tests... They all depend on the test and hdf5 libraries.
|
||||
$(TEST_PROGS): $(LIB) $(LIBHDF5)
|
||||
|
||||
TESTHDF5_OBJ=testhdf5.lo tattr.lo tfile.lo titerate.lo tmeta.lo trefer.lo tselect.lo ttbbt.lo tvltypes.lo tvlstr.lo th5s.lo
|
||||
|
||||
TTS_OBJ=ttsafe.lo ttsafe_dcreate.lo ttsafe_error.lo ttsafe_cancel.lo \
|
||||
ttsafe_acreate.lo
|
||||
|
||||
testhdf5: $(TESTHDF5_OBJ)
|
||||
@$(LT_LINK_EXE) $(CFLAGS) -o $@ $(TESTHDF5_OBJ) $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
|
||||
|
||||
@ -157,6 +167,9 @@ flush2: flush2.lo
|
||||
enum: enum.lo
|
||||
@$(LT_LINK_EXE) $(CFLAGS) -o $@ enum.lo $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
|
||||
|
||||
ttsafe: $(TTS_OBJ)
|
||||
@$(LT_LINK_EXE) $(CFLAGS) -o $@ $(TTS_OBJ) $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
|
||||
|
||||
gass_write: gass_write.lo
|
||||
@$(LT_LINK_EXE) $(CFLAGS) -o $@ gass_write.lo $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
|
||||
|
||||
@ -182,3 +195,4 @@ srb_append: srb_append.lo
|
||||
@$(LT_LINK_EXE) $(CFLAGS) -o $@ srb_append.lo $(LIB) $(LIBHDF5) $(LDFLAGS) $(LIBS)
|
||||
|
||||
@CONCLUDE@
|
||||
|
||||
|
334
test/ttsafe.c
Normal file
334
test/ttsafe.c
Normal file
@ -0,0 +1,334 @@
|
||||
/****************************************************************************
|
||||
* NCSA HDF *
|
||||
* Software Development Group *
|
||||
* National Center for Supercomputing Applications *
|
||||
* University of Illinois at Urbana-Champaign *
|
||||
* 605 E. Springfield, Champaign IL 61820 *
|
||||
* *
|
||||
* For conditions of distribution and use, see the accompanying *
|
||||
* hdf/COPYING file. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef RCSID
|
||||
static char RcsId[] = "@(#)$Revision$";
|
||||
#endif
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
FILE
|
||||
ttsafe.c - HDF5 threadsafe testing framework main file.
|
||||
|
||||
REMARKS
|
||||
General test wrapper for HDF5 library thread safety test programs
|
||||
|
||||
DESIGN
|
||||
Each test function should be implemented as function having no
|
||||
parameters and returning void (i.e. no return value). They should be put
|
||||
into the list of InitTest() calls in main() below. Functions which depend
|
||||
on other functionality should be placed below the InitTest() call for the
|
||||
base functionality testing.
|
||||
Each test module should include ttsafe.h and define a unique set of
|
||||
names for test files they create.
|
||||
|
||||
BUGS/LIMITATIONS
|
||||
|
||||
EXPORTED ROUTINES/VARIABLES:
|
||||
Two variables are exported: num_errs, and Verbosity.
|
||||
|
||||
*/
|
||||
|
||||
#if defined __MWERKS__
|
||||
#include <console.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#define MAXNUMOFTESTS 50
|
||||
#define HDF5_TEST_MASTER
|
||||
|
||||
#define MAX_NUM_NAME 1000
|
||||
#define NAME_OFFSET 6 /* offset for "name<num>" */
|
||||
|
||||
/* Internal Variables */
|
||||
static int Index = 0;
|
||||
|
||||
/* Global variables */
|
||||
int num_errs = 0;
|
||||
int Verbosity;
|
||||
|
||||
#include <ttsafe.h>
|
||||
|
||||
#ifndef H5_HAVE_THREADSAFE
|
||||
int main(void)
|
||||
{
|
||||
printf("Test skipped because THREADSAFE not enabled\n");
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/* ANY new test needs to have a prototype in tproto.h */
|
||||
struct TestStruct {
|
||||
int NumErrors;
|
||||
char Description[64];
|
||||
int SkipFlag;
|
||||
char Name[16];
|
||||
void (*Call) (void);
|
||||
void (*Cleanup) (void);
|
||||
} Test[MAXNUMOFTESTS];
|
||||
|
||||
static void InitTest(const char *TheName, void (*TheCall) (void), void (*Cleanup) (void), const char *TheDescr);
|
||||
static void usage(void);
|
||||
|
||||
static void
|
||||
InitTest(const char *TheName, void (*TheCall) (void), void (*Cleanup) (void), const char *TheDescr)
|
||||
{
|
||||
if (Index >= MAXNUMOFTESTS) {
|
||||
print_func("Uh-oh, too many tests added, increase MAXNUMOFTEST!\n");
|
||||
exit(-1);
|
||||
} /* end if */
|
||||
HDstrcpy(Test[Index].Description, TheDescr);
|
||||
HDstrcpy(Test[Index].Name, TheName);
|
||||
Test[Index].Call = TheCall;
|
||||
Test[Index].Cleanup = Cleanup;
|
||||
Test[Index].NumErrors = -1;
|
||||
Test[Index].SkipFlag = 0;
|
||||
Index++;
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
intn i;
|
||||
|
||||
print_func("Usage: testhdf5 [-v[erbose] (l[ow]|m[edium]|h[igh]|0-10)] \n");
|
||||
print_func(" [-[e]x[clude] name+] \n");
|
||||
print_func(" [-o[nly] name+] \n");
|
||||
print_func(" [-b[egin] name] \n");
|
||||
print_func(" [-s[ummary]] \n");
|
||||
print_func(" [-c[leanoff]] \n");
|
||||
print_func(" [-n[ocaching]] \n");
|
||||
print_func(" [-h[elp]] \n");
|
||||
print_func("\n\n");
|
||||
print_func("verbose controls the amount of information displayed\n");
|
||||
print_func("exclude to exclude tests by name\n");
|
||||
print_func("only to name tests which should be run\n");
|
||||
print_func("begin start at the name of the test givin\n");
|
||||
print_func("summary prints a summary of test results at the end\n");
|
||||
print_func("cleanoff does not delete *.hdf files after execution of tests\n");
|
||||
print_func("nocaching do not turn on low-level DD caching\n");
|
||||
print_func("help print out this information\n");
|
||||
print_func("\n\n");
|
||||
print_func("This program currently tests the following: \n\n");
|
||||
print_func("%16s %s\n", "Name", "Description");
|
||||
print_func("%16s %s\n", "----", "-----------");
|
||||
for (i = 0; i < Index; i++)
|
||||
print_func("%16s %s\n", Test[i].Name, Test[i].Description);
|
||||
print_func("\n\n");
|
||||
} /* end usage() */
|
||||
|
||||
/*
|
||||
* This routine is designed to provide equivalent functionality to 'printf'
|
||||
* and allow easy replacement for environments which don't have stdin/stdout
|
||||
* available. (i.e. Windows & the Mac)
|
||||
*/
|
||||
int
|
||||
print_func(const char *format,...)
|
||||
{
|
||||
va_list arglist;
|
||||
int ret_value;
|
||||
|
||||
va_start(arglist, format);
|
||||
ret_value = vprintf(format, arglist);
|
||||
va_end(arglist);
|
||||
return (ret_value);
|
||||
}
|
||||
|
||||
char* gen_name(int value) {
|
||||
char* temp;
|
||||
int i, length;
|
||||
|
||||
length = num_digits(MAX_NUM_NAME-1);
|
||||
temp = (char *)malloc((NAME_OFFSET+length+1)*sizeof(char));
|
||||
temp = strcpy(temp, "attrib");
|
||||
temp[NAME_OFFSET+length] = '\0';
|
||||
|
||||
for (i=length-1;i>=0;i--) {
|
||||
temp[NAME_OFFSET+i] = (char)((int)'0' + value%10);
|
||||
value = value/10;
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/* pre-condition: num must be a non-negative number */
|
||||
int num_digits(int num) {
|
||||
int i=0;
|
||||
|
||||
if (num == 0)
|
||||
return 1;
|
||||
while (num > 0) {
|
||||
num = num/10;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int CLLoop; /* Command Line Loop */
|
||||
int Loop, Loop1;
|
||||
int Summary = 0;
|
||||
int CleanUp = 1;
|
||||
int Cache = 1;
|
||||
uintn major, minor, release;
|
||||
|
||||
#if defined __MWERKS__
|
||||
argc = ccommand(&argv);
|
||||
#endif
|
||||
|
||||
#if !(defined MAC || defined __MWERKS__ || defined SYMANTEC_C)
|
||||
/* Un-buffer the stdout and stderr */
|
||||
setbuf(stderr, NULL);
|
||||
setbuf(stdout, NULL);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Turn off automatic error reporting since we do it ourselves. Besides,
|
||||
* half the functions this test calls are private, so automatic error
|
||||
* reporting wouldn't do much good since it's triggered at the API layer.
|
||||
*/
|
||||
H5Eset_auto (NULL, NULL);
|
||||
|
||||
/* Tests are generally arranged from least to most complexity... */
|
||||
InitTest("dcreate", tts_dcreate, cleanup_dcreate, "multi-dataset creation");
|
||||
InitTest("error", tts_error, cleanup_error, "per-thread error stacks");
|
||||
InitTest("cancel", tts_cancel, cleanup_cancel, "Thread cancellation safety test");
|
||||
InitTest("acreate", tts_acreate, cleanup_acreate, "multi-attribute creation");
|
||||
|
||||
Verbosity = 4; /* Default Verbosity is Low */
|
||||
H5get_libversion(&major, &minor, &release);
|
||||
|
||||
print_func("\nFor help use: testhdf5 -help\n");
|
||||
print_func("Linked with hdf5 version %u.%u release %u\n",
|
||||
(unsigned)major, (unsigned)minor, (unsigned)release);
|
||||
for (CLLoop = 1; CLLoop < argc; CLLoop++) {
|
||||
if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-verbose") == 0) ||
|
||||
(HDstrcmp(argv[CLLoop], "-v") == 0))) {
|
||||
if (argv[CLLoop + 1][0] == 'l')
|
||||
Verbosity = 4;
|
||||
else if (argv[CLLoop + 1][0] == 'm')
|
||||
Verbosity = 6;
|
||||
else if (argv[CLLoop + 1][0] == 'h')
|
||||
Verbosity = 10;
|
||||
else
|
||||
Verbosity = atoi(argv[CLLoop + 1]);
|
||||
} /* end if */
|
||||
if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-summary") == 0) ||
|
||||
(HDstrcmp(argv[CLLoop], "-s") == 0)))
|
||||
Summary = 1;
|
||||
|
||||
if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-help") == 0) ||
|
||||
(HDstrcmp(argv[CLLoop], "-h") == 0))) {
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-cleanoff") == 0) ||
|
||||
(HDstrcmp(argv[CLLoop], "-c") == 0)))
|
||||
CleanUp = 0;
|
||||
|
||||
if ((argc > CLLoop) && ((HDstrcmp(argv[CLLoop], "-nocache") == 0) ||
|
||||
(HDstrcmp(argv[CLLoop], "-n") == 0))) {
|
||||
Cache = 0;
|
||||
printf ("Cache = %d\n", Cache);
|
||||
}
|
||||
|
||||
if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-exclude") == 0) ||
|
||||
(HDstrcmp(argv[CLLoop], "-x") == 0))) {
|
||||
Loop = CLLoop + 1;
|
||||
while ((Loop < argc) && (argv[Loop][0] != '-')) {
|
||||
for (Loop1 = 0; Loop1 < Index; Loop1++)
|
||||
if (HDstrcmp(argv[Loop], Test[Loop1].Name) == 0)
|
||||
Test[Loop1].SkipFlag = 1;
|
||||
Loop++;
|
||||
} /* end while */
|
||||
} /* end if */
|
||||
if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-begin") == 0) ||
|
||||
(HDstrcmp(argv[CLLoop], "-b") == 0))) {
|
||||
Loop = CLLoop + 1;
|
||||
while ((Loop < argc) && (argv[Loop][0] != '-')) {
|
||||
for (Loop1 = 0; Loop1 < Index; Loop1++) {
|
||||
if (HDstrcmp(argv[Loop], Test[Loop1].Name) != 0)
|
||||
Test[Loop1].SkipFlag = 1;
|
||||
if (HDstrcmp(argv[Loop], Test[Loop1].Name) == 0)
|
||||
Loop1 = Index;
|
||||
} /* end for */
|
||||
Loop++;
|
||||
} /* end while */
|
||||
} /* end if */
|
||||
if ((argc > CLLoop + 1) && ((HDstrcmp(argv[CLLoop], "-only") == 0) ||
|
||||
(HDstrcmp(argv[CLLoop], "-o") == 0))) {
|
||||
for (Loop = 0; Loop < Index; Loop++)
|
||||
Test[Loop].SkipFlag = 1;
|
||||
Loop = CLLoop + 1;
|
||||
while ((Loop < argc) && (argv[Loop][0] != '-')) {
|
||||
for (Loop1 = 0; Loop1 < Index; Loop1++)
|
||||
if (HDstrcmp(argv[Loop], Test[Loop1].Name) == 0)
|
||||
Test[Loop1].SkipFlag = 0;
|
||||
Loop++;
|
||||
} /* end while */
|
||||
} /* end if */
|
||||
} /* end for */
|
||||
|
||||
#ifdef NOT_YET
|
||||
if (Cache) /* turn on caching, unless we were instucted not to */
|
||||
Hcache(CACHE_ALL_FILES, TRUE);
|
||||
#endif /* NOT_YET */
|
||||
|
||||
for (Loop = 0; Loop < Index; Loop++) {
|
||||
if (Test[Loop].SkipFlag) {
|
||||
MESSAGE(2, ("Skipping -- %s \n", Test[Loop].Description));
|
||||
} else {
|
||||
MESSAGE(2, ("Testing -- %s (%s) \n", Test[Loop].Description,
|
||||
Test[Loop].Name));
|
||||
MESSAGE(5, ("===============================================\n"));
|
||||
Test[Loop].NumErrors = num_errs;
|
||||
(*Test[Loop].Call) ();
|
||||
Test[Loop].NumErrors = num_errs - Test[Loop].NumErrors;
|
||||
MESSAGE(5, ("===============================================\n"));
|
||||
MESSAGE(5, ("There were %d errors detected.\n\n", (int) Test[Loop].NumErrors));
|
||||
} /* end else */
|
||||
} /* end for */
|
||||
|
||||
MESSAGE(2, ("\n\n"))
|
||||
if (num_errs)
|
||||
print_func("!!! %d Error(s) were detected !!!\n\n", (int) num_errs);
|
||||
else
|
||||
print_func("All tests were successful. \n\n");
|
||||
|
||||
if (Summary) {
|
||||
print_func("Summary of Test Results:\n");
|
||||
print_func("Name of Test Errors Description of Test\n");
|
||||
print_func("---------------- ------ --------------------------------------\n");
|
||||
|
||||
for (Loop = 0; Loop < Index; Loop++) {
|
||||
if (Test[Loop].NumErrors == -1)
|
||||
print_func("%16s %6s %s\n", Test[Loop].Name, "N/A", Test[Loop].Description);
|
||||
else
|
||||
print_func("%16s %6d %s\n", Test[Loop].Name, (int) Test[Loop].NumErrors,
|
||||
Test[Loop].Description);
|
||||
} /* end for */
|
||||
print_func("\n\n");
|
||||
} /* end if */
|
||||
if (CleanUp && !getenv("HDF5_NOCLEANUP")) {
|
||||
MESSAGE(2, ("\nCleaning Up temp files...\n\n"));
|
||||
|
||||
/* call individual cleanup routines in each source module */
|
||||
for (Loop = 0; Loop < Index; Loop++)
|
||||
if (!Test[Loop].SkipFlag && Test[Loop].Cleanup!=NULL)
|
||||
(*Test[Loop].Cleanup) ();
|
||||
}
|
||||
return (num_errs);
|
||||
} /* end main() */
|
||||
#endif /*H5_HAVE_THREADSAFE*/
|
141
test/ttsafe.h
Normal file
141
test/ttsafe.h
Normal file
@ -0,0 +1,141 @@
|
||||
/****************************************************************************
|
||||
* NCSA HDF *
|
||||
* Software Development Group *
|
||||
* National Center for Supercomputing Applications *
|
||||
* University of Illinois at Urbana-Champaign *
|
||||
* 605 E. Springfield, Champaign IL 61820 *
|
||||
* *
|
||||
* For conditions of distribution and use, see the accompanying *
|
||||
* hdf/COPYING file. *
|
||||
* *
|
||||
****************************************************************************/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* This header file contains information required for testing the HDF5 library.
|
||||
*/
|
||||
|
||||
#ifndef HDF5TEST_H
|
||||
#define HDF5TEST_H
|
||||
|
||||
#include <hdf5.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Include required headers. This file tests internal library functions,
|
||||
* so we include the private headers here.
|
||||
*/
|
||||
#include <H5private.h>
|
||||
#include <H5Eprivate.h>
|
||||
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
/* Include pthread library for threadsafe tests */
|
||||
#include <pthread.h>
|
||||
|
||||
extern int num_errs;
|
||||
extern int Verbosity;
|
||||
|
||||
/* Use %ld to print the value because long should cover most cases. */
|
||||
/* Used to make certain a return value _is_not_ a value */
|
||||
#define CHECK(ret, val, where) do { \
|
||||
if (Verbosity>9) print_func(" Call to routine: %15s at line %4d " \
|
||||
"in %s returned %ld \n", \
|
||||
where, (int)__LINE__, __FILE__, \
|
||||
(long)ret); \
|
||||
if (ret == val) { \
|
||||
print_func("*** UNEXPECTED RETURN from %s is %ld at line %4d " \
|
||||
"in %s\n", where, (long)ret, (int)__LINE__, __FILE__); \
|
||||
num_errs++; \
|
||||
H5Eprint (stdout); \
|
||||
} \
|
||||
H5Eclear(); \
|
||||
} while(0)
|
||||
|
||||
#define CHECK_I(ret,where) { \
|
||||
if (Verbosity>9) { \
|
||||
print_func(" Call to routine: %15s at line %4d in %s returned %ld\n", \
|
||||
(where), (int)__LINE__, __FILE__, (long)(ret)); \
|
||||
} \
|
||||
if ((ret)<0) { \
|
||||
print_func ("*** UNEXPECTED RETURN from %s is %ld line %4d in %s\n", \
|
||||
(where), (long)(ret), (int)__LINE__, __FILE__); \
|
||||
H5Eprint (stdout); \
|
||||
num_errs++; \
|
||||
} \
|
||||
H5Eclear (); \
|
||||
}
|
||||
|
||||
#define CHECK_PTR(ret,where) { \
|
||||
if (Verbosity>9) { \
|
||||
print_func(" Call to routine: %15s at line %4d in %s returned %p\n", \
|
||||
(where), (int)__LINE__, __FILE__, (ret)); \
|
||||
} \
|
||||
if (!(ret)) { \
|
||||
print_func ("*** UNEXPECTED RETURN from %s is NULL line %4d in %s\n", \
|
||||
(where), (int)__LINE__, __FILE__); \
|
||||
H5Eprint (stdout); \
|
||||
num_errs++; \
|
||||
} \
|
||||
H5Eclear (); \
|
||||
}
|
||||
|
||||
/* Used to make certain a return value _is_ a value */
|
||||
#define VERIFY(x, val, where) do { \
|
||||
if (Verbosity>9) { \
|
||||
print_func(" Call to routine: %15s at line %4d in %s had value " \
|
||||
"%ld \n", where, (int)__LINE__, __FILE__, (long)x); \
|
||||
} \
|
||||
if (x != val) { \
|
||||
print_func("*** UNEXPECTED VALUE from %s is %ld at line %4d " \
|
||||
"in %s\n", where, (long)x, (int)__LINE__, __FILE__); \
|
||||
H5Eprint (stdout); \
|
||||
num_errs++; \
|
||||
} \
|
||||
H5Eclear(); \
|
||||
} while(0)
|
||||
|
||||
/* Used to document process through a test and to check for errors */
|
||||
#define RESULT(ret,func) do { \
|
||||
if (Verbosity>8) { \
|
||||
print_func(" Call to routine: %15s at line %4d in %s returned " \
|
||||
"%ld\n", func, (int)__LINE__, __FILE__, (long)ret); \
|
||||
} \
|
||||
if (Verbosity>9) HEprint(stdout, 0); \
|
||||
if (ret == FAIL) { \
|
||||
print_func("*** UNEXPECTED RETURN from %s is %ld at line %4d " \
|
||||
"in %s\n", func, (long)ret, (int)__LINE__, __FILE__); \
|
||||
H5Eprint (stdout); \
|
||||
num_errs++; \
|
||||
} \
|
||||
H5Eclear(); \
|
||||
} while(0)
|
||||
|
||||
/* Used to document process through a test */
|
||||
#define MESSAGE(V,A) {if (Verbosity>(V)) print_func A;}
|
||||
|
||||
/* definitions for command strings */
|
||||
#define VERBOSITY_STR "Verbosity"
|
||||
#define SKIP_STR "Skip"
|
||||
#define TEST_STR "Test"
|
||||
#define CLEAN_STR "Cleanup"
|
||||
|
||||
/* Prototypes for the support routines */
|
||||
int print_func(const char *,...);
|
||||
extern char* gen_name(int);
|
||||
extern int num_digits(int);
|
||||
|
||||
/* Prototypes for the test routines */
|
||||
void tts_dcreate(void);
|
||||
void tts_error(void);
|
||||
void tts_cancel(void);
|
||||
void tts_acreate(void);
|
||||
|
||||
/* Prototypes for the cleanup routines */
|
||||
void cleanup_dcreate(void);
|
||||
void cleanup_error(void);
|
||||
void cleanup_cancel(void);
|
||||
void cleanup_acreate(void);
|
||||
|
||||
#endif /* H5_HAVE_THREADSAFE */
|
||||
#endif /* HDF5_TESTH */
|
166
test/ttsafe_acreate.c
Normal file
166
test/ttsafe_acreate.c
Normal file
@ -0,0 +1,166 @@
|
||||
/********************************************************************
|
||||
*
|
||||
* Testing for thread safety in H5A (dataset attribute) library
|
||||
* operations. -- Threaded program --
|
||||
* ------------------------------------------------------------------
|
||||
*
|
||||
* Plan: Attempt to break H5Acreate by making many simultaneous create
|
||||
* calls.
|
||||
*
|
||||
* Claim: N calls to H5Acreate should create N attributes for a dataset
|
||||
* if threadsafe. If some unprotected shared data exists for the
|
||||
* dataset (eg, a count of the number of attributes in the
|
||||
* dataset), there is a small chance that consecutive reads occur
|
||||
* before a write to that shared variable.
|
||||
*
|
||||
* Created: Oct 5 1999
|
||||
* Programmer: Chee Wai LEE
|
||||
*
|
||||
* Modification History
|
||||
* --------------------
|
||||
* May 15 2000 - incorporated into library tests (Chee Wai LEE)
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include "ttsafe.h"
|
||||
|
||||
#ifndef H5_HAVE_THREADSAFE
|
||||
static int dummy; /* just to create a non-empty object file */
|
||||
#else
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define FILE "ttsafe.h5"
|
||||
#define DATASETNAME "IntData"
|
||||
#define NUM_THREADS 16
|
||||
|
||||
void *tts_acreate_thread(void *);
|
||||
|
||||
typedef struct acreate_data_struct {
|
||||
hid_t dataset;
|
||||
hid_t datatype;
|
||||
hid_t dataspace;
|
||||
int current_index;
|
||||
} ttsafe_name_data_t;
|
||||
|
||||
void tts_acreate(void) {
|
||||
|
||||
/* Pthread definitions
|
||||
*/
|
||||
pthread_t threads[NUM_THREADS];
|
||||
|
||||
/* HDF5 data definitions
|
||||
*/
|
||||
hid_t file, dataset;
|
||||
hid_t dataspace, datatype;
|
||||
hid_t attribute;
|
||||
hsize_t dimsf[1]; /* dataset dimensions */
|
||||
|
||||
int data; /* data to write */
|
||||
int buffer, ret;
|
||||
|
||||
int i;
|
||||
ttsafe_name_data_t *attrib_data;
|
||||
|
||||
/* create a hdf5 file using H5F_ACC_TRUNC access,
|
||||
* default file creation plist and default file
|
||||
* access plist
|
||||
*/
|
||||
file = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
|
||||
|
||||
/* create a simple dataspace for the dataset
|
||||
*/
|
||||
dimsf[0] = 1;
|
||||
dataspace = H5Screate_simple(1,dimsf,NULL);
|
||||
|
||||
/* define datatype for the data using native little endian integers
|
||||
*/
|
||||
datatype = H5Tcopy(H5T_NATIVE_INT);
|
||||
H5Tset_order(datatype, H5T_ORDER_LE);
|
||||
|
||||
/* create a new dataset within the file
|
||||
*/
|
||||
dataset = H5Dcreate(file, DATASETNAME, datatype, dataspace,
|
||||
H5P_DEFAULT);
|
||||
|
||||
/* initialize data for dataset and write value to dataset
|
||||
*/
|
||||
data = NUM_THREADS;
|
||||
H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
|
||||
H5P_DEFAULT, &data);
|
||||
|
||||
/* simultaneously create a large number of attributes to be
|
||||
associated with the dataset
|
||||
*/
|
||||
|
||||
for (i=0;i<NUM_THREADS;i++) {
|
||||
attrib_data = malloc(sizeof(ttsafe_name_data_t));
|
||||
attrib_data->dataset = dataset;
|
||||
attrib_data->datatype = datatype;
|
||||
attrib_data->dataspace = dataspace;
|
||||
attrib_data->current_index = i;
|
||||
pthread_create(&threads[i],NULL,tts_acreate_thread,attrib_data);
|
||||
}
|
||||
|
||||
for (i=0;i<NUM_THREADS;i++) {
|
||||
pthread_join(threads[i],NULL);
|
||||
}
|
||||
|
||||
/* verify the correctness of the test */
|
||||
for (i=0; i<NUM_THREADS; i++) {
|
||||
attribute = H5Aopen_name(dataset,gen_name(i));
|
||||
if (attribute < 0) {
|
||||
fprintf(stderr,"unable to open appropriate attribute. Test failed!\n");
|
||||
} else {
|
||||
ret = H5Aread(attribute, H5T_NATIVE_INT, &buffer);
|
||||
if ((ret < 0) || (buffer != i)) {
|
||||
fprintf(stderr,"wrong data values. Test failed!\n");
|
||||
}
|
||||
H5Aclose(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
/* close remaining resources
|
||||
*/
|
||||
H5Sclose(dataspace);
|
||||
H5Tclose(datatype);
|
||||
H5Dclose(dataset);
|
||||
H5Fclose(file);
|
||||
}
|
||||
|
||||
void *tts_acreate_thread(void *client_data) {
|
||||
|
||||
hid_t attribute;
|
||||
hsize_t dimsf[1]; /* dataset dimensions */
|
||||
|
||||
char *attribute_name;
|
||||
int *attribute_data; /* data for attributes */
|
||||
int i;
|
||||
|
||||
ttsafe_name_data_t *attrib_data = (ttsafe_name_data_t *)client_data;
|
||||
|
||||
/* create attribute
|
||||
*/
|
||||
attribute_name = gen_name(attrib_data->current_index);
|
||||
attribute = H5Acreate(attrib_data->dataset,
|
||||
attribute_name,
|
||||
attrib_data->datatype,
|
||||
attrib_data->dataspace,
|
||||
H5P_DEFAULT);
|
||||
|
||||
/* Write data to the attribute
|
||||
*/
|
||||
attribute_data = malloc(sizeof(int));
|
||||
*attribute_data = attrib_data->current_index;
|
||||
H5Awrite(attribute,H5T_NATIVE_INT,attribute_data);
|
||||
H5Aclose(attribute);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cleanup_acreate(void) {
|
||||
}
|
||||
|
||||
#endif /*H5_HAVE_THREADSAFE*/
|
||||
|
203
test/ttsafe_cancel.c
Normal file
203
test/ttsafe_cancel.c
Normal file
@ -0,0 +1,203 @@
|
||||
/********************************************************************
|
||||
*
|
||||
* Testing thread safety. Thread Cancellation safety
|
||||
* -------------------------------------------------
|
||||
*
|
||||
* The main thread spawns a child to perform a series of dataset writes
|
||||
* to a hdf5 file. The main thread and child thread synchronizes within
|
||||
* a callback function called during a H5Diterate call afterwhich the
|
||||
* main thread attempts to cancel the child thread.
|
||||
*
|
||||
* The cancellation should only work after the child thread has safely
|
||||
* left the H5Diterate call.
|
||||
*
|
||||
* Temporary files generated:
|
||||
* ttsafe.h5
|
||||
*
|
||||
* HDF5 APIs exercised in thread:
|
||||
* H5Screate_simple, H5Tcopy, H5Tset_order, H5Dcreate, H5Dclose,
|
||||
* H5Dwrite, H5Dread, H5Diterate, H5Tclose, H5Sclose.
|
||||
*
|
||||
* Created: May 15 2000
|
||||
* Programmer: Chee Wai LEE
|
||||
*
|
||||
* Modification History
|
||||
* --------------------
|
||||
*
|
||||
********************************************************************/
|
||||
#include "ttsafe.h"
|
||||
|
||||
#ifndef H5_HAVE_THREADSAFE
|
||||
static int dummy; /* just to create a non-empty object file */
|
||||
#else
|
||||
|
||||
#define FILE "ttsafe.h5"
|
||||
#define DATASETNAME "commonname"
|
||||
|
||||
void *tts_cancel_thread(void *);
|
||||
void tts_cancel_barrier(void);
|
||||
herr_t tts_cancel_callback(void *, hid_t, hsize_t, hssize_t *, void *);
|
||||
void cancellation_cleanup(void *);
|
||||
|
||||
hid_t cancel_file;
|
||||
typedef struct cleanup_struct {
|
||||
hid_t dataset;
|
||||
hid_t datatype;
|
||||
hid_t dataspace;
|
||||
} cancel_cleanup_t;
|
||||
|
||||
pthread_t childthread;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
|
||||
void tts_cancel(void) {
|
||||
|
||||
pthread_attr_t attribute;
|
||||
hid_t dataset;
|
||||
|
||||
int buffer;
|
||||
|
||||
/* make thread scheduling global */
|
||||
pthread_attr_init(&attribute);
|
||||
pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM);
|
||||
|
||||
/* create a hdf5 file using H5F_ACC_TRUNC access,
|
||||
* default file creation plist and default file
|
||||
* access plist
|
||||
*/
|
||||
cancel_file = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
|
||||
|
||||
pthread_create(&childthread, &attribute, tts_cancel_thread, NULL);
|
||||
|
||||
tts_cancel_barrier();
|
||||
pthread_cancel(childthread);
|
||||
|
||||
dataset = H5Dopen(cancel_file, DATASETNAME);
|
||||
H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
|
||||
&buffer);
|
||||
|
||||
if (buffer == 11) {
|
||||
/* do nothing */
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"operation unsuccessful with value at %d instead of 11\n",
|
||||
buffer);
|
||||
}
|
||||
|
||||
H5Dclose(dataset);
|
||||
H5Fclose(cancel_file);
|
||||
}
|
||||
|
||||
void *tts_cancel_thread(void *arg) {
|
||||
|
||||
int datavalue;
|
||||
int *buffer;
|
||||
hid_t dataspace, datatype, dataset;
|
||||
hsize_t dimsf[1]; /* dataset dimensions */
|
||||
|
||||
cancel_cleanup_t *cleanup_structure;
|
||||
|
||||
/* define dataspace for dataset
|
||||
*/
|
||||
dimsf[0] = 1;
|
||||
dataspace = H5Screate_simple(1,dimsf,NULL);
|
||||
|
||||
/* define datatype for the data using native little endian integers
|
||||
*/
|
||||
datatype = H5Tcopy(H5T_NATIVE_INT);
|
||||
H5Tset_order(datatype, H5T_ORDER_LE);
|
||||
|
||||
/* create a new dataset within the file
|
||||
*/
|
||||
dataset = H5Dcreate(cancel_file, DATASETNAME, datatype, dataspace,
|
||||
H5P_DEFAULT);
|
||||
|
||||
/* If thread is cancelled, make cleanup call */
|
||||
cleanup_structure = (cancel_cleanup_t*)malloc(sizeof(cancel_cleanup_t));
|
||||
cleanup_structure->dataset = dataset;
|
||||
cleanup_structure->datatype = datatype;
|
||||
cleanup_structure->dataspace = dataspace;
|
||||
pthread_cleanup_push(cancellation_cleanup, cleanup_structure);
|
||||
|
||||
datavalue = 1;
|
||||
H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
|
||||
&datavalue);
|
||||
|
||||
buffer = malloc(sizeof(int));
|
||||
H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
|
||||
buffer);
|
||||
H5Diterate(buffer, H5T_NATIVE_INT, dataspace, tts_cancel_callback,
|
||||
&dataset);
|
||||
|
||||
sleep(3);
|
||||
|
||||
datavalue = 100;
|
||||
H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
|
||||
&datavalue);
|
||||
H5Dclose(dataset);
|
||||
H5Tclose(datatype);
|
||||
H5Sclose(dataspace);
|
||||
|
||||
/* required by pthreads. the argument 0 pops the stack but does not
|
||||
execute the cleanup routine.
|
||||
*/
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
herr_t tts_cancel_callback(void *elem, hid_t type_id, hsize_t ndim,
|
||||
hssize_t *point, void *operator_data) {
|
||||
int value = *(int *)elem;
|
||||
hid_t dataset = *(hid_t *)operator_data;
|
||||
|
||||
tts_cancel_barrier();
|
||||
sleep(3);
|
||||
|
||||
if (value != 1) {
|
||||
fprintf(stderr,"Error! Element value should be 1 and not %d\n", value);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
value += 10;
|
||||
H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
|
||||
&value);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* need to perform the dataset, datatype and dataspace close that was
|
||||
never performed because of thread cancellation
|
||||
*/
|
||||
void cancellation_cleanup(void *arg) {
|
||||
cancel_cleanup_t *cleanup_structure = (cancel_cleanup_t *)arg;
|
||||
H5Dclose(cleanup_structure->dataset);
|
||||
H5Tclose(cleanup_structure->datatype);
|
||||
H5Sclose(cleanup_structure->dataspace);
|
||||
/* retained for debugging */
|
||||
/* printf("cancellation noted, cleaning up ... \n"); */
|
||||
}
|
||||
|
||||
/*
|
||||
artificial (and specific to this test) barrier to keep track of whether
|
||||
both the main and child threads have reached a point in the program.
|
||||
*/
|
||||
void tts_cancel_barrier() {
|
||||
|
||||
static int count = 2;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (count != 1) {
|
||||
count--;
|
||||
pthread_cond_wait(&cond, &mutex);
|
||||
} else {
|
||||
pthread_cond_signal(&cond);
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
}
|
||||
|
||||
void cleanup_cancel() {
|
||||
H5close();
|
||||
}
|
||||
#endif /*H5_HAVE_THREADSAFE*/
|
191
test/ttsafe_dcreate.c
Normal file
191
test/ttsafe_dcreate.c
Normal file
@ -0,0 +1,191 @@
|
||||
/********************************************************************
|
||||
*
|
||||
* Testing thread safety in dataset creation in the HDF5 library
|
||||
* -------------------------------------------------------------
|
||||
*
|
||||
* Set of tests to run multiple threads so that each creates a different
|
||||
* dataset. This is likely to cause race-conditions if run in a non
|
||||
* threadsafe environment.
|
||||
*
|
||||
* Temporary files generated:
|
||||
* ttsafe.h5
|
||||
*
|
||||
* HDF5 APIs exercised in thread:
|
||||
* H5Screate_simple, H5Tcopy, H5Tset_order, H5Dcreate, H5Dwrite, H5Dclose,
|
||||
* H5Tclose, H5Sclose.
|
||||
*
|
||||
* Created: Apr 28 2000
|
||||
* Programmer: Chee Wai LEE
|
||||
*
|
||||
* Modification History
|
||||
* --------------------
|
||||
*
|
||||
********************************************************************/
|
||||
#include "ttsafe.h"
|
||||
|
||||
#ifndef H5_HAVE_THREADSAFE
|
||||
static int dummy; /* just to create a non-empty object file */
|
||||
#else
|
||||
|
||||
#define FILE "ttsafe.h5"
|
||||
#define DATASETNAME_LENGTH 10
|
||||
#define NUM_THREAD 16
|
||||
|
||||
void *tts_dcreate_creator(void *);
|
||||
|
||||
/*
|
||||
**********************************************************************
|
||||
* Thread safe test - multiple dataset creation
|
||||
**********************************************************************
|
||||
*/
|
||||
void tts_dcreate(void) {
|
||||
|
||||
/* Pthread definitions
|
||||
*/
|
||||
pthread_t threads[NUM_THREAD];
|
||||
|
||||
/* HDF5 data definitions
|
||||
*/
|
||||
hid_t file, dataset, datatype;
|
||||
|
||||
int datavalue;
|
||||
int i;
|
||||
|
||||
typedef struct thread_info {
|
||||
int id;
|
||||
hid_t file;
|
||||
char *dsetname;
|
||||
} thread_info;
|
||||
|
||||
thread_info *thread_out;
|
||||
|
||||
char *dsetname[NUM_THREAD];
|
||||
pthread_attr_t attribute;
|
||||
|
||||
/* set pthread attribute to perform global scheduling */
|
||||
pthread_attr_init(&attribute);
|
||||
pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM);
|
||||
|
||||
/* set individual dataset names (rather than generated the names
|
||||
automatically)
|
||||
*/
|
||||
|
||||
for (i=0;i<NUM_THREAD;i++) {
|
||||
dsetname[i] = (char *)malloc(sizeof(char)*DATASETNAME_LENGTH);
|
||||
}
|
||||
dsetname[0] = "zero";
|
||||
dsetname[1] = "one";
|
||||
dsetname[2] = "two";
|
||||
dsetname[3] = "three";
|
||||
dsetname[4] = "four";
|
||||
dsetname[5] = "five";
|
||||
dsetname[6] = "six";
|
||||
dsetname[7] = "seven";
|
||||
dsetname[8] = "eight";
|
||||
dsetname[9] = "nine";
|
||||
dsetname[10] = "ten";
|
||||
dsetname[11] = "eleven";
|
||||
dsetname[12] = "twelve";
|
||||
dsetname[13] = "thirteen";
|
||||
dsetname[14] = "fourteen";
|
||||
dsetname[15] = "fifteen";
|
||||
|
||||
/* create a hdf5 file using H5F_ACC_TRUNC access,
|
||||
* default file creation plist and default file
|
||||
* access plist
|
||||
*/
|
||||
file = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
|
||||
|
||||
/* simultaneously create a large number of datasets within the file
|
||||
*/
|
||||
for (i=0;i<NUM_THREAD;i++) {
|
||||
thread_out = (thread_info *)malloc(sizeof(thread_info));
|
||||
thread_out->id = i;
|
||||
thread_out->file = file;
|
||||
thread_out->dsetname = dsetname[i];
|
||||
pthread_create(&threads[i],NULL,tts_dcreate_creator,thread_out);
|
||||
}
|
||||
|
||||
for (i=0;i<NUM_THREAD;i++) {
|
||||
pthread_join(threads[i],NULL);
|
||||
}
|
||||
|
||||
/* compare data to see if it is written correctly
|
||||
*/
|
||||
|
||||
/* define datatype for the data using native little endian integers
|
||||
*/
|
||||
datatype = H5Tcopy(H5T_NATIVE_INT);
|
||||
|
||||
for (i=0;i<NUM_THREAD;i++) {
|
||||
if ((dataset = H5Dopen(file,dsetname[i])) < 0) {
|
||||
fprintf(stderr,"Dataset name not found - test failed\n");
|
||||
H5Fclose(file);
|
||||
return;
|
||||
} else {
|
||||
H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &datavalue);
|
||||
if (datavalue != i) {
|
||||
fprintf(stderr,
|
||||
"Wrong value read %d for dataset name %s - test failed\n",
|
||||
datavalue, dsetname[i]);
|
||||
H5Dclose(dataset);
|
||||
H5Fclose(file);
|
||||
return;
|
||||
}
|
||||
H5Dclose(dataset);
|
||||
}
|
||||
}
|
||||
/* close remaining resources
|
||||
*/
|
||||
H5Fclose(file);
|
||||
|
||||
}
|
||||
|
||||
void *tts_dcreate_creator(void *thread_data) {
|
||||
|
||||
hid_t dataspace, datatype, dataset;
|
||||
hsize_t dimsf[1]; /* dataset dimensions */
|
||||
|
||||
struct thread_info {
|
||||
int id;
|
||||
hid_t file;
|
||||
char *dsetname;
|
||||
} thread_in;
|
||||
|
||||
thread_in.dsetname = (char *)malloc(sizeof(char)*DATASETNAME_LENGTH);
|
||||
thread_in = *((struct thread_info *)thread_data);
|
||||
|
||||
/* define dataspace for dataset
|
||||
*/
|
||||
dimsf[0] = 1;
|
||||
dataspace = H5Screate_simple(1,dimsf,NULL);
|
||||
|
||||
/* define datatype for the data using native little endian integers
|
||||
*/
|
||||
datatype = H5Tcopy(H5T_NATIVE_INT);
|
||||
H5Tset_order(datatype, H5T_ORDER_LE);
|
||||
|
||||
/* create a new dataset within the file
|
||||
*/
|
||||
dataset = H5Dcreate(thread_in.file, thread_in.dsetname,
|
||||
datatype, dataspace,
|
||||
H5P_DEFAULT);
|
||||
|
||||
/* initialize data for dataset and write value to dataset
|
||||
*/
|
||||
H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
|
||||
H5P_DEFAULT, &thread_in.id);
|
||||
|
||||
/* close dataset, datatype and dataspace resources
|
||||
*/
|
||||
H5Dclose(dataset);
|
||||
H5Tclose(datatype);
|
||||
H5Sclose(dataspace);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cleanup_dcreate(void) {
|
||||
H5close();
|
||||
}
|
||||
#endif /*H5_HAVE_THREADSAFE*/
|
179
test/ttsafe_error.c
Normal file
179
test/ttsafe_error.c
Normal file
@ -0,0 +1,179 @@
|
||||
/********************************************************************
|
||||
*
|
||||
* Testing thread safety. Deliberate per-thread errors to test error stack
|
||||
* -----------------------------------------------------------------------
|
||||
*
|
||||
* Create 16 multiple threads to create datasets with the same name. The
|
||||
* library should respond with 15 equivalent error stack printouts (one for
|
||||
* each bad thread). The final hdf5 file should be a valid file with one
|
||||
* entry.
|
||||
*
|
||||
* Temporary files generated:
|
||||
* ttsafe.h5
|
||||
*
|
||||
* HDF5 APIs exercised in thread:
|
||||
* H5Screate_simple, H5Tcopy, H5Tset_order, H5Dcreate, H5Dclose,
|
||||
* H5Tclose, H5Sclose.
|
||||
*
|
||||
* Created: Apr 28 2000
|
||||
* Programmer: Chee Wai LEE
|
||||
*
|
||||
* Modification History
|
||||
* --------------------
|
||||
*
|
||||
********************************************************************/
|
||||
#include "ttsafe.h"
|
||||
|
||||
#ifndef H5_HAVE_THREADSAFE
|
||||
static int dummy; /* just to create a non-empty object file */
|
||||
#else
|
||||
|
||||
#define NUM_THREAD 16
|
||||
#define FILE "ttsafe.h5"
|
||||
/* Having a common dataset name is an error */
|
||||
#define DATASETNAME "commonname"
|
||||
#define EXPECTED_ERROR_DEPTH 3
|
||||
#define WRITE_NUMBER 37
|
||||
|
||||
herr_t error_callback(void *);
|
||||
herr_t walk_error_callback(int, H5E_error_t *, void *);
|
||||
void *tts_error_thread(void *);
|
||||
hid_t error_file;
|
||||
|
||||
typedef struct err_num_struct {
|
||||
int maj_num;
|
||||
int min_num;
|
||||
} err_num_t;
|
||||
|
||||
err_num_t expected[] = {{15, 23}, {15, 23}, {10, 32}};
|
||||
int error_flag = 0;
|
||||
int error_count = 0;
|
||||
|
||||
pthread_mutex_t error_mutex;
|
||||
|
||||
void tts_error(void) {
|
||||
|
||||
int i;
|
||||
|
||||
pthread_t threads[NUM_THREAD];
|
||||
pthread_attr_t attribute;
|
||||
|
||||
hid_t dataset;
|
||||
int value;
|
||||
|
||||
H5E_auto_t old_error_cb;
|
||||
void *old_error_client_data;
|
||||
|
||||
/* set up mutex for global count of errors */
|
||||
pthread_mutex_init(&error_mutex, NULL);
|
||||
|
||||
/* preserve previous error stack handler */
|
||||
H5Eget_auto(&old_error_cb, &old_error_client_data);
|
||||
|
||||
/* set our own auto error stack handler */
|
||||
H5Eset_auto(error_callback, NULL);
|
||||
|
||||
/* make thread scheduling global */
|
||||
pthread_attr_init(&attribute);
|
||||
pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM);
|
||||
|
||||
/* create a hdf5 file using H5F_ACC_TRUNC access,
|
||||
* default file creation plist and default file
|
||||
* access plist
|
||||
*/
|
||||
error_file = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
|
||||
|
||||
for (i=0;i<NUM_THREAD;i++) {
|
||||
pthread_create(&threads[i], &attribute, tts_error_thread, NULL);
|
||||
}
|
||||
|
||||
for (i=0;i<NUM_THREAD;i++) {
|
||||
pthread_join(threads[i],NULL);
|
||||
}
|
||||
|
||||
if (error_flag) {
|
||||
fprintf(stderr, "Threads reporting different error values!\n");
|
||||
}
|
||||
|
||||
if (error_count != NUM_THREAD - 1) {
|
||||
fprintf(stderr, "Error: %d threads failed instead of %d\n",
|
||||
error_count, NUM_THREAD-1);
|
||||
}
|
||||
|
||||
dataset = H5Dopen(error_file, DATASETNAME);
|
||||
H5Dread(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
|
||||
|
||||
if (value != WRITE_NUMBER) {
|
||||
fprintf(stderr,
|
||||
"Error: Successful thread wrote value %d instead of %d\n",
|
||||
value, WRITE_NUMBER);
|
||||
}
|
||||
|
||||
H5Dclose(dataset);
|
||||
H5Fclose(error_file);
|
||||
|
||||
/* turn our error stack handler off */
|
||||
H5Eset_auto(old_error_cb, old_error_client_data);
|
||||
}
|
||||
|
||||
void *tts_error_thread(void *arg) {
|
||||
|
||||
hid_t dataspace, datatype, dataset;
|
||||
hsize_t dimsf[1]; /* dataset dimensions */
|
||||
int value;
|
||||
|
||||
/* define dataspace for dataset
|
||||
*/
|
||||
dimsf[0] = 1;
|
||||
dataspace = H5Screate_simple(1,dimsf,NULL);
|
||||
|
||||
/* define datatype for the data using native little endian integers
|
||||
*/
|
||||
datatype = H5Tcopy(H5T_NATIVE_INT);
|
||||
H5Tset_order(datatype, H5T_ORDER_LE);
|
||||
|
||||
/* create a new dataset within the file
|
||||
*/
|
||||
dataset = H5Dcreate(error_file, DATASETNAME, datatype, dataspace,
|
||||
H5P_DEFAULT);
|
||||
if (dataset >= 0) { /* not an error */
|
||||
value = WRITE_NUMBER;
|
||||
H5Dwrite(dataset, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
|
||||
|
||||
H5Dclose(dataset);
|
||||
}
|
||||
H5Tclose(datatype);
|
||||
H5Sclose(dataspace);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void cleanup_error() {
|
||||
}
|
||||
|
||||
herr_t error_callback(void *client_data) {
|
||||
pthread_mutex_lock(&error_mutex);
|
||||
error_count++;
|
||||
pthread_mutex_unlock(&error_mutex);
|
||||
return (H5Ewalk(H5E_WALK_DOWNWARD, walk_error_callback, NULL));
|
||||
}
|
||||
|
||||
herr_t walk_error_callback(int n, H5E_error_t *err_desc,
|
||||
void *client_data) {
|
||||
int maj_num, min_num;
|
||||
|
||||
if (err_desc) {
|
||||
maj_num = err_desc->maj_num;
|
||||
min_num = err_desc->min_num;
|
||||
|
||||
if (n < EXPECTED_ERROR_DEPTH) {
|
||||
if (maj_num == expected[n].maj_num &&
|
||||
min_num == expected[n].min_num) {
|
||||
return SUCCEED;
|
||||
}
|
||||
}
|
||||
}
|
||||
error_flag = -1;
|
||||
return SUCCEED;
|
||||
}
|
||||
|
||||
#endif /*H5_HAVE_THREADSAFE*/
|
Loading…
Reference in New Issue
Block a user