diff options
author | Chee-Wai Lee <cwlee@ncsa.uiuc.edu> | 2000-05-18 19:13:33 (GMT) |
---|---|---|
committer | Chee-Wai Lee <cwlee@ncsa.uiuc.edu> | 2000-05-18 19:13:33 (GMT) |
commit | e26f4e5eed2e219597d4bfa85925840c84dd64db (patch) | |
tree | 281871beb7e12d1a2dd90e3bdd49eaa2bdf19fd8 /src | |
parent | bc520e88b4ad3b175c0aeca2c90e021956676533 (diff) | |
download | hdf5-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.c | 71 | ||||
-rw-r--r-- | src/H5E.c | 55 | ||||
-rw-r--r-- | src/H5Eprivate.h | 6 | ||||
-rw-r--r-- | src/H5TS.c | 243 | ||||
-rw-r--r-- | src/H5private.h | 104 | ||||
-rw-r--r-- | src/Makefile.in | 3 |
6 files changed, 459 insertions, 23 deletions
@@ -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; } @@ -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) |