summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChee-Wai Lee <cwlee@ncsa.uiuc.edu>2000-05-18 19:13:33 (GMT)
committerChee-Wai Lee <cwlee@ncsa.uiuc.edu>2000-05-18 19:13:33 (GMT)
commite26f4e5eed2e219597d4bfa85925840c84dd64db (patch)
tree281871beb7e12d1a2dd90e3bdd49eaa2bdf19fd8
parentbc520e88b4ad3b175c0aeca2c90e021956676533 (diff)
downloadhdf5-e26f4e5eed2e219597d4bfa85925840c84dd64db.zip
hdf5-e26f4e5eed2e219597d4bfa85925840c84dd64db.tar.gz
hdf5-e26f4e5eed2e219597d4bfa85925840c84dd64db.tar.bz2
[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
-rw-r--r--src/H5.c71
-rw-r--r--src/H5E.c55
-rw-r--r--src/H5Eprivate.h6
-rw-r--r--src/H5TS.c243
-rw-r--r--src/H5private.h104
-rw-r--r--src/Makefile.in3
-rw-r--r--test/Makefile.in20
-rw-r--r--test/ttsafe.c334
-rw-r--r--test/ttsafe.h141
-rw-r--r--test/ttsafe_acreate.c166
-rw-r--r--test/ttsafe_cancel.c203
-rw-r--r--test/ttsafe_dcreate.c191
-rw-r--r--test/ttsafe_error.c179
13 files changed, 1690 insertions, 26 deletions
diff --git a/src/H5.c b/src/H5.c
index e4a6a88..ee0da0c 100644
--- a/src/H5.c
+++ b/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;
}
diff --git a/src/H5E.c b/src/H5E.c
index 44f35ab..cc7b482 100644
--- a/src/H5E.c
+++ b/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
@@ -163,6 +176,39 @@ 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);
diff --git a/src/H5Eprivate.h b/src/H5Eprivate.h
index 89f9c5c..49fbc96 100644
--- a/src/H5Eprivate.h
+++ b/src/H5Eprivate.h
@@ -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); \
}
diff --git a/src/H5TS.c b/src/H5TS.c
new file mode 100644
index 0000000..e7317bc
--- /dev/null
+++ b/src/H5TS.c
@@ -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
diff --git a/src/H5private.h b/src/H5private.h
index bdb59a9..60e234d 100644
--- a/src/H5private.h
+++ b/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.
diff --git a/src/Makefile.in b/src/Makefile.in
index 15650bf..f5e7643 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -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)
diff --git a/test/Makefile.in b/test/Makefile.in
index 2f0912d..eba7699 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -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@
+
diff --git a/test/ttsafe.c b/test/ttsafe.c
new file mode 100644
index 0000000..cf87752
--- /dev/null
+++ b/test/ttsafe.c
@@ -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*/
diff --git a/test/ttsafe.h b/test/ttsafe.h
new file mode 100644
index 0000000..2359764
--- /dev/null
+++ b/test/ttsafe.h
@@ -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 */
diff --git a/test/ttsafe_acreate.c b/test/ttsafe_acreate.c
new file mode 100644
index 0000000..ca94470
--- /dev/null
+++ b/test/ttsafe_acreate.c
@@ -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*/
+
diff --git a/test/ttsafe_cancel.c b/test/ttsafe_cancel.c
new file mode 100644
index 0000000..4e893e9
--- /dev/null
+++ b/test/ttsafe_cancel.c
@@ -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*/
diff --git a/test/ttsafe_dcreate.c b/test/ttsafe_dcreate.c
new file mode 100644
index 0000000..cc7d6e6
--- /dev/null
+++ b/test/ttsafe_dcreate.c
@@ -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*/
diff --git a/test/ttsafe_error.c b/test/ttsafe_error.c
new file mode 100644
index 0000000..1dafd23
--- /dev/null
+++ b/test/ttsafe_error.c
@@ -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*/