[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:
Chee-Wai Lee 2000-05-18 14:13:33 -05:00
parent bc520e88b4
commit e26f4e5eed
13 changed files with 1690 additions and 26 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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
View 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

View File

@ -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.

View File

@ -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)

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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*/