summaryrefslogtreecommitdiffstats
path: root/src/H5TS.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/H5TS.c')
-rw-r--r--src/H5TS.c298
1 files changed, 195 insertions, 103 deletions
diff --git a/src/H5TS.c b/src/H5TS.c
index bd1b748..29be128 100644
--- a/src/H5TS.c
+++ b/src/H5TS.c
@@ -11,31 +11,80 @@
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-/* private headers */
-#include "H5private.h" /*library */
-#include "H5Eprivate.h" /*error handling */
-#include "H5MMprivate.h" /*memory management functions */
+/*
+ * Purpose: This file contains the framework for ensuring that the global
+ * library lock is held when an API routine is called. This
+ * framework works in concert with the FUNC_ENTER_API / FUNC_LEAVE_API
+ * macros defined in H5private.h.
+ *
+ * Note: Because this threadsafety framework operates outside the library,
+ * it does not use the error stack and only uses the "namecheck only"
+ * FUNC_ENTER_* / FUNC_LEAVE_* macros.
+ */
+
+/****************/
+/* Module Setup */
+/****************/
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5MMprivate.h" /* Memory management */
#ifdef H5_HAVE_THREADSAFE
-/* Module specific data structures */
+/****************/
+/* Local Macros */
+/****************/
-/* cancelability structure */
+/******************/
+/* Local Typedefs */
+/******************/
+
+/* Cancelability structure */
typedef struct H5TS_cancel_struct {
int previous_state;
unsigned int cancel_count;
} H5TS_cancel_t;
+/* Function pointer typedef for thread callback function */
+typedef void *(*H5TS_thread_cb_t)(void *);
+
+/********************/
+/* Local Prototypes */
+/********************/
+static void H5TS__key_destructor(void *key_val);
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
/* Global variable definitions */
#ifdef H5_HAVE_WIN_THREADS
H5TS_once_t H5TS_first_init_g;
-#else /* H5_HAVE_WIN_THREADS */
+#else /* H5_HAVE_WIN_THREADS */
H5TS_once_t H5TS_first_init_g = PTHREAD_ONCE_INIT;
#endif /* H5_HAVE_WIN_THREADS */
-H5TS_key_t H5TS_errstk_key_g;
-H5TS_key_t H5TS_funcstk_key_g;
-H5TS_key_t H5TS_apictx_key_g;
-H5TS_key_t H5TS_cancel_key_g;
+
+/* Thread-local keys, used by other interfaces */
+H5TS_key_t H5TS_errstk_key_g; /* Error stack */
+#ifdef H5_HAVE_CODESTACK
+H5TS_key_t H5TS_funcstk_key_g; /* Function stack */
+#endif /* H5_HAVE_CODESTACK */
+H5TS_key_t H5TS_apictx_key_g; /* API context */
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+/* Thread-local keys, used in this module */
+static H5TS_key_t H5TS_cancel_key_s; /* Thread cancellation state */
#ifndef H5_HAVE_WIN_THREADS
@@ -64,12 +113,13 @@ static H5TS_key_t H5TS_tid_key;
/*--------------------------------------------------------------------------
* NAME
- * H5TS_key_destructor
+ * H5TS__key_destructor
*
* USAGE
- * H5TS_key_destructor()
+ * H5TS__key_destructor()
*
* RETURNS
+ * None
*
* DESCRIPTION
* Frees the memory for a key. Called by each thread as it exits.
@@ -82,12 +132,16 @@ static H5TS_key_t H5TS_tid_key;
*--------------------------------------------------------------------------
*/
static void
-H5TS_key_destructor(void *key_val)
+H5TS__key_destructor(void *key_val)
{
+ FUNC_ENTER_STATIC_NAMECHECK_ONLY
+
/* Use HDfree here instead of H5MM_xfree(), to avoid calling the H5CS routines */
if (key_val != NULL)
HDfree(key_val);
-}
+
+ FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY
+} /* end H5TS__key_destructor() */
#ifndef H5_HAVE_WIN_THREADS
@@ -114,10 +168,10 @@ H5TS_tid_destructor(void *_v)
return;
/* TBD use an atomic CAS */
- pthread_mutex_lock(&H5TS_tid_mtx);
+ HDpthread_mutex_lock(&H5TS_tid_mtx);
tid->next = H5TS_tid_next_free;
H5TS_tid_next_free = tid;
- pthread_mutex_unlock(&H5TS_tid_mtx);
+ HDpthread_mutex_unlock(&H5TS_tid_mtx);
}
/*--------------------------------------------------------------------------
@@ -137,8 +191,8 @@ H5TS_tid_destructor(void *_v)
static void
H5TS_tid_init(void)
{
- pthread_mutex_init(&H5TS_tid_mtx, NULL);
- pthread_key_create(&H5TS_tid_key, H5TS_tid_destructor);
+ HDpthread_mutex_init(&H5TS_tid_mtx, NULL);
+ HDpthread_key_create(&H5TS_tid_key, H5TS_tid_destructor);
}
/*--------------------------------------------------------------------------
@@ -168,7 +222,7 @@ H5TS_tid_init(void)
uint64_t
H5TS_thread_id(void)
{
- H5TS_tid_t *tid = pthread_getspecific(H5TS_tid_key);
+ H5TS_tid_t *tid = HDpthread_getspecific(H5TS_tid_key);
H5TS_tid_t proto_tid;
/* An ID is already assigned. */
@@ -182,20 +236,19 @@ H5TS_thread_id(void)
* point `tid` at `proto_tid` if we need to allocate some
* memory.
*/
- pthread_mutex_lock(&H5TS_tid_mtx);
+ HDpthread_mutex_lock(&H5TS_tid_mtx);
if ((tid = H5TS_tid_next_free) != NULL)
H5TS_tid_next_free = tid->next;
else if (H5TS_tid_next_id != UINT64_MAX) {
tid = &proto_tid;
tid->id = ++H5TS_tid_next_id;
}
- pthread_mutex_unlock(&H5TS_tid_mtx);
+ HDpthread_mutex_unlock(&H5TS_tid_mtx);
/* If a prototype ID record was established, copy it to the heap. */
- if (tid == &proto_tid) {
+ if (tid == &proto_tid)
if ((tid = HDmalloc(sizeof(*tid))) != NULL)
*tid = proto_tid;
- }
if (tid == NULL)
return 0;
@@ -204,7 +257,7 @@ H5TS_thread_id(void)
* to it.
*/
tid->next = NULL;
- if (pthread_setspecific(H5TS_tid_key, tid) != 0) {
+ if (HDpthread_setspecific(H5TS_tid_key, tid) != 0) {
H5TS_tid_destructor(tid);
return 0;
}
@@ -237,6 +290,8 @@ H5TS_pthread_first_thread_init(void)
H5_g.H5_libinit_g = FALSE; /* Library hasn't been initialized */
H5_g.H5_libterm_g = FALSE; /* Library isn't being shutdown */
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
+
#ifdef H5_HAVE_WIN32_API
#ifdef PTW32_STATIC_LIB
pthread_win32_process_attach_np();
@@ -244,25 +299,29 @@ H5TS_pthread_first_thread_init(void)
#endif
/* initialize global API mutex lock */
- pthread_mutex_init(&H5_g.init_lock.atomic_lock, NULL);
- pthread_cond_init(&H5_g.init_lock.cond_var, NULL);
+ HDpthread_mutex_init(&H5_g.init_lock.atomic_lock, NULL);
+ HDpthread_cond_init(&H5_g.init_lock.cond_var, NULL);
H5_g.init_lock.lock_count = 0;
/* Initialize integer thread identifiers. */
H5TS_tid_init();
/* initialize key for thread-specific error stacks */
- pthread_key_create(&H5TS_errstk_key_g, H5TS_key_destructor);
+ HDpthread_key_create(&H5TS_errstk_key_g, H5TS__key_destructor);
+#ifdef H5_HAVE_CODESTACK
/* initialize key for thread-specific function stacks */
- pthread_key_create(&H5TS_funcstk_key_g, H5TS_key_destructor);
+ HDpthread_key_create(&H5TS_funcstk_key_g, H5TS__key_destructor);
+#endif /* H5_HAVE_CODESTACK */
/* initialize key for thread-specific API contexts */
- pthread_key_create(&H5TS_apictx_key_g, H5TS_key_destructor);
+ HDpthread_key_create(&H5TS_apictx_key_g, H5TS__key_destructor);
/* initialize key for thread cancellability mechanism */
- pthread_key_create(&H5TS_cancel_key_g, H5TS_key_destructor);
-}
+ HDpthread_key_create(&H5TS_cancel_key_s, H5TS__key_destructor);
+
+ FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY
+} /* end H5TS_pthread_first_thread_init() */
#endif /* H5_HAVE_WIN_THREADS */
/*--------------------------------------------------------------------------
@@ -288,32 +347,39 @@ H5TS_pthread_first_thread_init(void)
herr_t
H5TS_mutex_lock(H5TS_mutex_t *mutex)
{
+ herr_t ret_value = 0;
+
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
+
#ifdef H5_HAVE_WIN_THREADS
EnterCriticalSection(&mutex->CriticalSection);
- return 0;
-#else /* H5_HAVE_WIN_THREADS */
- herr_t ret_value = pthread_mutex_lock(&mutex->atomic_lock);
-
+#else /* H5_HAVE_WIN_THREADS */
+ /* Acquire the library lock */
+ ret_value = HDpthread_mutex_lock(&mutex->atomic_lock);
if (ret_value)
- return ret_value;
+ HGOTO_DONE(ret_value);
- if (mutex->lock_count && pthread_equal(pthread_self(), mutex->owner_thread)) {
+ /* Check if this thread already owns the lock */
+ if (mutex->lock_count && HDpthread_equal(HDpthread_self(), mutex->owner_thread))
/* already owned by self - increment count */
mutex->lock_count++;
- }
else {
- /* if owned by other thread, wait for condition signal */
+ /* Wait until the lock is released by current owner thread */
while (mutex->lock_count)
- pthread_cond_wait(&mutex->cond_var, &mutex->atomic_lock);
+ HDpthread_cond_wait(&mutex->cond_var, &mutex->atomic_lock);
/* After we've received the signal, take ownership of the mutex */
- mutex->owner_thread = pthread_self();
+ mutex->owner_thread = HDpthread_self();
mutex->lock_count = 1;
- }
+ } /* end else */
- return pthread_mutex_unlock(&mutex->atomic_lock);
+ /* Release the library lock */
+ ret_value = HDpthread_mutex_unlock(&mutex->atomic_lock);
#endif /* H5_HAVE_WIN_THREADS */
-}
+
+done:
+ FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value)
+} /* end H5TS_mutex_lock() */
/*--------------------------------------------------------------------------
* NAME
@@ -338,30 +404,36 @@ H5TS_mutex_lock(H5TS_mutex_t *mutex)
herr_t
H5TS_mutex_unlock(H5TS_mutex_t *mutex)
{
+ herr_t ret_value = 0;
+
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
+
#ifdef H5_HAVE_WIN_THREADS
/* Releases ownership of the specified critical section object. */
LeaveCriticalSection(&mutex->CriticalSection);
- return 0;
-#else /* H5_HAVE_WIN_THREADS */
- herr_t ret_value = pthread_mutex_lock(&mutex->atomic_lock);
+#else /* H5_HAVE_WIN_THREADS */
+ /* Decrement the lock count for this thread */
+ ret_value = HDpthread_mutex_lock(&mutex->atomic_lock);
if (ret_value)
- return ret_value;
-
+ HGOTO_DONE(ret_value);
mutex->lock_count--;
+ ret_value = HDpthread_mutex_unlock(&mutex->atomic_lock);
- ret_value = pthread_mutex_unlock(&mutex->atomic_lock);
-
+ /* If the lock count drops to zero, signal the condition variable, to
+ * wake another thread.
+ */
if (mutex->lock_count == 0) {
int err;
- err = pthread_cond_signal(&mutex->cond_var);
+ err = HDpthread_cond_signal(&mutex->cond_var);
if (err != 0)
ret_value = err;
} /* end if */
-
- return ret_value;
#endif /* H5_HAVE_WIN_THREADS */
+
+done:
+ FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value)
} /* H5TS_mutex_unlock */
/*--------------------------------------------------------------------------
@@ -375,12 +447,12 @@ H5TS_mutex_unlock(H5TS_mutex_t *mutex)
* 0 on success non-zero error code on error.
*
* DESCRIPTION
- * Creates a cancelation counter for a thread if it is the first time
+ * Creates a cancellation 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.
+ * the previous cancelability type into cancellation counter.
* Increase the counter value by 1.
*
* PROGRAMMER: Chee Wai LEE
@@ -391,15 +463,20 @@ H5TS_mutex_unlock(H5TS_mutex_t *mutex)
herr_t
H5TS_cancel_count_inc(void)
{
-#ifdef H5_HAVE_WIN_THREADS
- /* unsupported; just return 0 */
- return SUCCEED;
-#else /* H5_HAVE_WIN_THREADS */
+#ifndef H5_HAVE_WIN_THREADS
H5TS_cancel_t *cancel_counter;
- herr_t ret_value = SUCCEED;
+#endif /* H5_HAVE_WIN_THREADS */
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
- cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_g);
+#ifdef H5_HAVE_WIN_THREADS
+ /* unsupported */
+#else /* H5_HAVE_WIN_THREADS */
+ /* Acquire the thread's cancellation counter */
+ cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_s);
+ /* Check if it's created yet */
if (!cancel_counter) {
/*
* First time thread calls library - create new counter and associate
@@ -409,24 +486,31 @@ H5TS_cancel_count_inc(void)
* order to avoid codestack calls.
*/
cancel_counter = (H5TS_cancel_t *)HDcalloc(1, sizeof(H5TS_cancel_t));
+ if (NULL == cancel_counter)
+ HGOTO_DONE(FAIL);
+
+ /* Set the thread's cancellation counter with the new object */
+ ret_value = HDpthread_setspecific(H5TS_cancel_key_s, (void *)cancel_counter);
+ if (ret_value) {
+ HDfree(cancel_counter);
+ HGOTO_DONE(FAIL);
+ } /* end if */
+ } /* end if */
- if (!cancel_counter) {
- HERROR(H5E_RESOURCE, H5E_NOSPACE, "memory allocation failed");
- return FAIL;
- }
-
- ret_value = pthread_setspecific(H5TS_cancel_key_g, (void *)cancel_counter);
- }
-
+ /* Check if thread entering library */
if (cancel_counter->cancel_count == 0)
- /* thread entering library */
- ret_value = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_counter->previous_state);
+ /* Set cancellation state to 'disable', and remember previous state */
+ ret_value = HDpthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_counter->previous_state);
+ /* Increment # of times the library API was re-entered, to avoid resetting
+ * previous cancellation state until the final API routine is returning.
+ */
++cancel_counter->cancel_count;
-
- return ret_value;
#endif /* H5_HAVE_WIN_THREADS */
-}
+
+done:
+ FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value)
+} /* end H5TS_cancel_count_inc() */
/*--------------------------------------------------------------------------
* NAME
@@ -440,7 +524,7 @@ H5TS_cancel_count_inc(void)
*
* DESCRIPTION
* If counter value is one, then set cancelability type of the thread
- * to the previous cancelability type stored in the cancelation counter.
+ * to the previous cancelability type stored in the cancellation counter.
* (the thread is leaving the library).
*
* Decrement the counter value by 1.
@@ -453,23 +537,30 @@ H5TS_cancel_count_inc(void)
herr_t
H5TS_cancel_count_dec(void)
{
-#ifdef H5_HAVE_WIN_THREADS
- /* unsupported; will just return 0 */
- return SUCCEED;
-#else /* H5_HAVE_WIN_THREADS */
- register H5TS_cancel_t *cancel_counter;
- herr_t ret_value = SUCCEED;
+#ifndef H5_HAVE_WIN_THREADS
+ H5TS_cancel_t *cancel_counter;
+#endif /* H5_HAVE_WIN_THREADS */
+ herr_t ret_value = SUCCEED;
- cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_g);
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
+#ifdef H5_HAVE_WIN_THREADS
+ /* unsupported */
+#else /* H5_HAVE_WIN_THREADS */
+ /* Acquire the thread's cancellation counter */
+ cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_s);
+
+ /* Check for leaving last API routine */
if (cancel_counter->cancel_count == 1)
- ret_value = pthread_setcancelstate(cancel_counter->previous_state, NULL);
+ /* Reset to previous thread cancellation state, if last API */
+ ret_value = HDpthread_setcancelstate(cancel_counter->previous_state, NULL);
+ /* Decrement cancellation counter */
--cancel_counter->cancel_count;
-
- return ret_value;
#endif /* H5_HAVE_WIN_THREADS */
-}
+
+ FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value)
+} /* end H5TS_cancel_count_dec() */
#ifdef H5_HAVE_WIN_THREADS
/*--------------------------------------------------------------------------
@@ -489,6 +580,8 @@ H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex)
{
BOOL ret_value = TRUE;
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
+
/* Initialize the critical section (can't fail) */
InitializeCriticalSection(&H5_g.init_lock.CriticalSection);
@@ -504,11 +597,9 @@ H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex)
if (TLS_OUT_OF_INDEXES == (H5TS_apictx_key_g = TlsAlloc()))
ret_value = FALSE;
- return ret_value;
+ FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value)
} /* H5TS_win32_process_enter() */
-#endif /* H5_HAVE_WIN_THREADS */
-#ifdef H5_HAVE_WIN_THREADS
/*--------------------------------------------------------------------------
* NAME
* H5TS_win32_thread_enter
@@ -526,6 +617,8 @@ H5TS_win32_thread_enter(void)
{
herr_t ret_value = SUCCEED;
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
+
/* Currently a placeholder function. TLS setup is performed
* elsewhere in the library.
*
@@ -534,11 +627,9 @@ H5TS_win32_thread_enter(void)
* is used.
*/
- return ret_value;
+ FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value)
} /* H5TS_win32_thread_enter() */
-#endif /* H5_HAVE_WIN_THREADS */
-#ifdef H5_HAVE_WIN_THREADS
/*--------------------------------------------------------------------------
* NAME
* H5TS_win32_process_exit
@@ -554,6 +645,7 @@ H5TS_win32_thread_enter(void)
void
H5TS_win32_process_exit(void)
{
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
/* Windows uses a different thread local storage mechanism which does
* not support auto-freeing like pthreads' keys.
@@ -572,11 +664,9 @@ H5TS_win32_process_exit(void)
#endif /* H5_HAVE_CODESTACK */
TlsFree(H5TS_apictx_key_g);
- return;
+ FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY
} /* H5TS_win32_process_exit() */
-#endif /* H5_HAVE_WIN_THREADS */
-#ifdef H5_HAVE_WIN_THREADS
/*--------------------------------------------------------------------------
* NAME
* H5TS_win32_thread_exit
@@ -595,6 +685,8 @@ H5TS_win32_thread_exit(void)
LPVOID lpvData;
herr_t ret_value = SUCCEED;
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
+
/* Windows uses a different thread local storage mechanism which does
* not support auto-freeing like pthreads' keys.
*
@@ -618,7 +710,7 @@ H5TS_win32_thread_exit(void)
if (lpvData)
LocalFree((HLOCAL)lpvData);
- return ret_value;
+ FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value)
} /* H5TS_win32_thread_exit() */
#endif /* H5_HAVE_WIN_THREADS */
@@ -638,12 +730,13 @@ H5TS_win32_thread_exit(void)
*--------------------------------------------------------------------------
*/
H5TS_thread_t
-H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata)
+H5TS_create_thread(H5TS_thread_cb_t func, H5TS_attr_t *attr, void *udata)
{
H5TS_thread_t ret_value;
-#ifdef H5_HAVE_WIN_THREADS
+ FUNC_ENTER_NOAPI_NAMECHECK_ONLY
+#ifdef H5_HAVE_WIN_THREADS
/* When calling C runtime functions, you should use _beginthread or
* _beginthreadex instead of CreateThread. Threads created with
* CreateThread risk being killed in low-memory situations. Since we
@@ -658,12 +751,11 @@ H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata)
#else /* H5_HAVE_WIN_THREADS */
- pthread_create(&ret_value, attr, (void *(*)(void *))func, udata);
+ HDpthread_create(&ret_value, attr, (void *(*)(void *))func, udata);
#endif /* H5_HAVE_WIN_THREADS */
- return ret_value;
-
+ FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value)
} /* H5TS_create_thread */
#endif /* H5_HAVE_THREADSAFE */