summaryrefslogtreecommitdiffstats
path: root/src
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 /src
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
Diffstat (limited to 'src')
-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
6 files changed, 459 insertions, 23 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)