summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDana Robinson <43805+derobins@users.noreply.github.com>2021-05-14 13:00:05 (GMT)
committerGitHub <noreply@github.com>2021-05-14 13:00:05 (GMT)
commit8977e43b7766f4f817fa067b2d22e84b775bf5a2 (patch)
treeff2414b18c330b612c202fc2338ee915d31e11d8 /src
parent53cfafff061071dca0d74617629c66350f66d6cc (diff)
downloadhdf5-8977e43b7766f4f817fa067b2d22e84b775bf5a2.zip
hdf5-8977e43b7766f4f817fa067b2d22e84b775bf5a2.tar.gz
hdf5-8977e43b7766f4f817fa067b2d22e84b775bf5a2.tar.bz2
Brings the thread-safety recursive writer locks to 1.12 (#466)
* First cut at replaceing the existing mutex with a recursive R/W lock. This implementation has the following issues: 1) pthreads implementation only -- we still need a windows version. 2) must investigate thread cancelation issues 3) Error reporting is very poor. I followed the error reporting on the existing thread safe code, but this should be re-visited and improved. Code is currently setup to use the new recursive R/W lock instead of the global mutex to control entry to the library in threadsafe builds. To revert to the global mutex, set H5TS__USE_REC_RW_LOCK_FOR_GLOBAL_MUTEX in H5TSprivate.h to FALSE. Added a reasonably robust regression test for the reursive R/W lock in test/ttsafe_rec_rw_lock.c Note that the change to hl/src/H5LTanalyse.c is an artifact of clang-format. Tested serial threadsafe debug and production on jelly, and also regular serial / debug. On Windows builds, the new recursive R/W lock should not be built and we should use the existing global mutex -- however this is not tested at this time. * Updates CMake to build recursive RW lock test * Updates to allow building on Windows * Moves #if statements to better protect non-RW lock code * Adds configure and CMake options for the recursive RW locks * Committing clang-format changes * Updates RELEASE.txt and the build options * Renames H5TS RW lock things * Makes struct members platform independent Also removes _ptr from identifiers * Partial thread-safety RW locks platform independence * Committing clang-format changes * Pthreads side of things is platform-independent now * Formatted source * Added Windows equivalents for some Pthreads calls * Rename H5TS takedown call to destroy * Reorg of RW lock code * Committing clang-format changes * Changes to Pthreads code after development on Visual Studio * Moves stats macros to static inline functions and tidies memory allocs * Converts RW lock print stats call to use C99 formatting * Fixes typos * Formatted source * Updates the RELEASE.txt note to indicate no Win32 threads support Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/H5TS.c1218
-rw-r--r--src/H5TSprivate.h562
-rw-r--r--src/H5private.h18
-rw-r--r--src/libhdf5.settings.in2
4 files changed, 1504 insertions, 296 deletions
diff --git a/src/H5TS.c b/src/H5TS.c
index 65ec4b2..321a83d 100644
--- a/src/H5TS.c
+++ b/src/H5TS.c
@@ -12,14 +12,13 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
- * 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.
+ * 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.
*/
/****************/
@@ -29,9 +28,8 @@
/***********/
/* Headers */
/***********/
-#include "H5private.h" /* Generic Functions */
-#include "H5Eprivate.h" /* Error handling */
-#include "H5MMprivate.h" /* Memory management */
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error handling */
#ifdef H5_HAVE_THREADSAFE
@@ -49,10 +47,27 @@ typedef struct H5TS_cancel_struct {
unsigned int cancel_count;
} H5TS_cancel_t;
+#ifndef H5_HAVE_WIN_THREADS
+/* An H5TS_tid_t is a record of a thread identifier that is
+ * available for reuse.
+ */
+struct _tid;
+typedef struct _tid H5TS_tid_t;
+
+struct _tid {
+ H5TS_tid_t *next;
+ uint64_t id;
+};
+#endif
+
/********************/
/* Local Prototypes */
/********************/
static void H5TS__key_destructor(void *key_val);
+#ifndef H5_HAVE_WIN_THREADS
+static void H5TS_tid_destructor(void *_v);
+static void H5TS_tid_init(void);
+#endif
/*********************/
/* Package Variables */
@@ -65,16 +80,22 @@ static void H5TS__key_destructor(void *key_val);
/* Global variable definitions */
#ifdef H5_HAVE_WIN_THREADS
H5TS_once_t H5TS_first_init_g;
-#else /* H5_HAVE_WIN_THREADS */
+#else
H5TS_once_t H5TS_first_init_g = PTHREAD_ONCE_INIT;
-#endif /* H5_HAVE_WIN_THREADS */
+#endif
/* Thread-local keys, used by other interfaces */
-H5TS_key_t H5TS_errstk_key_g; /* Error stack */
+
+/* Error stack */
+H5TS_key_t H5TS_errstk_key_g;
+
+/* Function 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 */
+H5TS_key_t H5TS_funcstk_key_g;
+#endif
+
+/* API context */
+H5TS_key_t H5TS_apictx_key_g;
/*******************/
/* Local Variables */
@@ -85,17 +106,6 @@ static H5TS_key_t H5TS_cancel_key_s; /* Thread cancellation state */
#ifndef H5_HAVE_WIN_THREADS
-/* An H5TS_tid_t is a record of a thread identifier that is
- * available for reuse.
- */
-struct _tid;
-typedef struct _tid H5TS_tid_t;
-
-struct _tid {
- H5TS_tid_t *next;
- uint64_t id;
-};
-
/* Pointer to first free thread ID record or NULL. */
static H5TS_tid_t *H5TS_tid_next_free = NULL;
static uint64_t H5TS_tid_next_id = 0;
@@ -106,26 +116,20 @@ static pthread_mutex_t H5TS_tid_mtx;
/* Key for thread-local storage of the thread ID. */
static H5TS_key_t H5TS_tid_key;
-#endif /* H5_HAVE_WIN_THREADS */
+#endif
/*--------------------------------------------------------------------------
- * NAME
- * H5TS__key_destructor
- *
- * USAGE
- * H5TS__key_destructor()
+ * Function: H5TS__key_destructor
*
- * RETURNS
- * None
+ * Returns: void
*
- * DESCRIPTION
+ * Description:
* Frees the memory for a key. Called by each thread as it exits.
* Currently all the thread-specific information for all keys are simple
* structures allocated with malloc, so we can free them all uniformly.
*
- * PROGRAMMER: Quincey Koziol
- * February 7, 2003
- *
+ * Programmer: Quincey Koziol
+ * February 7, 2003
*--------------------------------------------------------------------------
*/
static void
@@ -139,17 +143,12 @@ H5TS__key_destructor(void *key_val)
#ifndef H5_HAVE_WIN_THREADS
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_tid_destructor
- *
- * USAGE
- * H5TS_tid_destructor()
+ * Function: H5TS_tid_destructor
*
- * RETURNS
+ * Returns: void
*
- * DESCRIPTION
+ * Description:
* When a thread shuts down, put its ID record on the free list.
- *
*--------------------------------------------------------------------------
*/
static void
@@ -168,17 +167,12 @@ H5TS_tid_destructor(void *_v)
}
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_tid_init
- *
- * USAGE
- * H5TS_tid_init()
- *
- * RETURNS
+ * Function: H5TS_tid_init
*
- * DESCRIPTION
- * Initialize for integer thread identifiers.
+ * Returns: void
*
+ * Description:
+ * Initialization for integer thread identifiers.
*--------------------------------------------------------------------------
*/
static void
@@ -188,17 +182,154 @@ H5TS_tid_init(void)
pthread_key_create(&H5TS_tid_key, H5TS_tid_destructor);
}
+#endif
+
+#ifdef H5_HAVE_WIN_THREADS
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_thread_id
+ * Function: H5TS_win32_process_enter
*
- * USAGE
- * uint64_t id = H5TS_thread_id()
+ * Returns: TRUE on success, FALSE on failure
*
- * RETURNS
- * Return an integer identifier, ID, for the current thread.
+ * Description:
+ * Per-process setup on Windows when using Win32 threads.
*
- * DESCRIPTION
+ *--------------------------------------------------------------------------
+ */
+H5_DLL BOOL CALLBACK
+H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex)
+{
+ BOOL ret_value = TRUE;
+
+ /* initialize global API mutex lock */
+ InitializeCriticalSection(&H5_g.init_lock.CriticalSection);
+
+ /* Set up thread local storage */
+ if (TLS_OUT_OF_INDEXES == (H5TS_errstk_key_g = TlsAlloc()))
+ ret_value = FALSE;
+
+#ifdef H5_HAVE_CODESTACK
+ if (TLS_OUT_OF_INDEXES == (H5TS_funcstk_key_g = TlsAlloc()))
+ ret_value = FALSE;
+#endif
+
+ if (TLS_OUT_OF_INDEXES == (H5TS_apictx_key_g = TlsAlloc()))
+ ret_value = FALSE;
+
+ return ret_value;
+} /* H5TS_win32_process_enter() */
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_win32_thread_enter
+ *
+ * Returns: SUCCEED/FAIL
+ *
+ * Description:
+ * Per-thread setup on Windows when using Win32 threads.
+ *
+ *--------------------------------------------------------------------------
+ */
+herr_t
+H5TS_win32_thread_enter(void)
+{
+ herr_t ret_value = SUCCEED;
+
+ /* Currently a placeholder function. TLS setup is performed
+ * elsewhere in the library.
+ *
+ * WARNING: Do NOT use C standard library functions here.
+ * CRT functions are not allowed in DllMain, which is where this code
+ * is used.
+ */
+
+ return ret_value;
+} /* H5TS_win32_thread_enter() */
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_win32_process_exit
+ *
+ * Returns: void
+ *
+ * Description:
+ * Per-process cleanup on Windows when using Win32 threads.
+ *
+ *--------------------------------------------------------------------------
+ */
+void
+H5TS_win32_process_exit(void)
+{
+
+ /* Windows uses a different thread local storage mechanism which does
+ * not support auto-freeing like pthreads' keys.
+ *
+ * This function is currently registered via atexit() and is called
+ * AFTER H5_term_library().
+ */
+
+ /* Clean up critical section resources (can't fail) */
+ DeleteCriticalSection(&H5_g.init_lock.CriticalSection);
+
+ /* Clean up per-process thread local storage */
+ TlsFree(H5TS_errstk_key_g);
+#ifdef H5_HAVE_CODESTACK
+ TlsFree(H5TS_funcstk_key_g);
+#endif
+ TlsFree(H5TS_apictx_key_g);
+
+ return;
+} /* end H5TS_win32_process_exit() */
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_win32_thread_exit
+ *
+ * Returns: SUCCEED/FAIL
+ *
+ * Description:
+ * Per-thread cleanup on Windows when using Win32 threads.
+ *
+ *--------------------------------------------------------------------------
+ */
+herr_t
+H5TS_win32_thread_exit(void)
+{
+ LPVOID lpvData;
+ herr_t ret_value = SUCCEED;
+
+ /* Windows uses a different thread local storage mechanism which does
+ * not support auto-freeing like pthreads' keys.
+ *
+ * WARNING: Do NOT use C standard library functions here.
+ * CRT functions are not allowed in DllMain, which is where this code
+ * is used.
+ */
+
+ /* Clean up per-thread thread local storage */
+ lpvData = TlsGetValue(H5TS_errstk_key_g);
+ if (lpvData)
+ LocalFree((HLOCAL)lpvData);
+
+#ifdef H5_HAVE_CODESTACK
+ lpvData = TlsGetValue(H5TS_funcstk_key_g);
+ if (lpvData)
+ LocalFree((HLOCAL)lpvData);
+#endif
+
+ lpvData = TlsGetValue(H5TS_apictx_key_g);
+ if (lpvData)
+ LocalFree((HLOCAL)lpvData);
+
+ return ret_value;
+} /* end H5TS_win32_thread_exit() */
+
+#endif /* H5_HAVE_WIN_THREADS */
+
+#ifndef H5_HAVE_WIN_THREADS
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_thread_id
+ *
+ * Returns: An integer identifier for the current thread
+ *
+ * Description:
* The ID satisfies the following properties:
*
* 1 1 <= ID <= UINT64_MAX
@@ -259,20 +390,16 @@ H5TS_thread_id(void)
}
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_pthread_first_thread_init
- *
- * USAGE
- * H5TS_pthread_first_thread_init()
+ * Function: H5TS_pthread_first_thread_init
*
- * RETURNS
+ * Returns: void
*
- * DESCRIPTION
+ * 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
+ * Programmer: Chee Wai LEE
* May 2, 2000
*
*--------------------------------------------------------------------------
@@ -290,9 +417,13 @@ H5TS_pthread_first_thread_init(void)
#endif
/* initialize global API mutex lock */
+#ifdef H5_USE_RECURSIVE_WRITER_LOCKS
+ H5TS_rw_lock_init(&H5_g.init_rw_lock, H5TS_RW_LOCK_POLICY_FAVOR_WRITERS);
+#else
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;
+#endif
/* Initialize integer thread identifiers. */
H5TS_tid_init();
@@ -303,32 +434,30 @@ H5TS_pthread_first_thread_init(void)
#ifdef H5_HAVE_CODESTACK
/* initialize key for thread-specific function stacks */
pthread_key_create(&H5TS_funcstk_key_g, H5TS__key_destructor);
-#endif /* H5_HAVE_CODESTACK */
+#endif
/* initialize key for thread-specific API contexts */
pthread_key_create(&H5TS_apictx_key_g, H5TS__key_destructor);
/* initialize key for thread cancellability mechanism */
pthread_key_create(&H5TS_cancel_key_s, H5TS__key_destructor);
+
} /* end H5TS_pthread_first_thread_init() */
+
#endif /* H5_HAVE_WIN_THREADS */
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_mutex_lock
+ * Function: H5TS_mutex_lock
*
- * USAGE
- * H5TS_mutex_lock(&mutex_var)
*
- * RETURNS
- * 0 on success and non-zero on error.
+ * Returns: SUCCEED/FAIL
*
- * DESCRIPTION
+ * 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
+ * Programmer: Chee Wai LEE
* May 2, 2000
*
*--------------------------------------------------------------------------
@@ -336,15 +465,14 @@ H5TS_pthread_first_thread_init(void)
herr_t
H5TS_mutex_lock(H5TS_mutex_t *mutex)
{
- herr_t ret_value = 0;
+ herr_t ret_value = SUCCEED;
#ifdef H5_HAVE_WIN_THREADS
EnterCriticalSection(&mutex->CriticalSection);
-#else /* H5_HAVE_WIN_THREADS */
+#else
/* Acquire the library lock */
- ret_value = pthread_mutex_lock(&mutex->atomic_lock);
- if (ret_value)
- return ret_value;
+ if (pthread_mutex_lock(&mutex->atomic_lock) != 0)
+ return FAIL;
/* Check if this thread already owns the lock */
if (mutex->lock_count && pthread_equal(pthread_self(), mutex->owner_thread))
@@ -361,28 +489,24 @@ H5TS_mutex_lock(H5TS_mutex_t *mutex)
} /* end else */
/* Release the library lock */
- ret_value = pthread_mutex_unlock(&mutex->atomic_lock);
-#endif /* H5_HAVE_WIN_THREADS */
+ if (pthread_mutex_unlock(&mutex->atomic_lock) != 0)
+ ret_value = FAIL;
+#endif
return ret_value;
} /* end H5TS_mutex_lock() */
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_mutex_unlock
+ * Function: H5TS_mutex_unlock
*
- * USAGE
- * H5TS_mutex_unlock(&mutex_var)
- *
- * RETURNS
- * 0 on success and non-zero on error.
+ * Returns: SUCCEED/FAIL
*
- * DESCRIPTION
+ * 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
+ * Programmer: Chee Wai LEE
* May 2, 2000
*
*--------------------------------------------------------------------------
@@ -390,55 +514,47 @@ H5TS_mutex_lock(H5TS_mutex_t *mutex)
herr_t
H5TS_mutex_unlock(H5TS_mutex_t *mutex)
{
- herr_t ret_value = 0;
+ herr_t ret_value = SUCCEED;
#ifdef H5_HAVE_WIN_THREADS
/* Releases ownership of the specified critical section object. */
LeaveCriticalSection(&mutex->CriticalSection);
-#else /* H5_HAVE_WIN_THREADS */
+#else
/* Decrement the lock count for this thread */
- ret_value = pthread_mutex_lock(&mutex->atomic_lock);
- if (ret_value)
- return ret_value;
+ if (pthread_mutex_lock(&mutex->atomic_lock) != 0)
+ return FAIL;
mutex->lock_count--;
- ret_value = pthread_mutex_unlock(&mutex->atomic_lock);
+ if (pthread_mutex_unlock(&mutex->atomic_lock) != 0)
+ ret_value = FAIL;
/* 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);
- if (err != 0)
- ret_value = err;
- } /* end if */
+ if (mutex->lock_count == 0)
+ if (pthread_cond_signal(&mutex->cond_var) != 0)
+ ret_value = FAIL;
#endif /* H5_HAVE_WIN_THREADS */
return ret_value;
} /* H5TS_mutex_unlock */
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_cancel_count_inc
+ * Function: H5TS_cancel_count_inc
*
- * USAGE
- * H5TS_cancel_count_inc()
+ * Returns: SUCCEED/FAIL
*
- * RETURNS
- * 0 on success non-zero error code on error.
- *
- * DESCRIPTION
+ * Description:
* 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 cancellation counter.
+ *
* Increase the counter value by 1.
*
- * PROGRAMMER: Chee Wai LEE
+ * Programmer: Chee Wai LEE
* May 2, 2000
*
*--------------------------------------------------------------------------
@@ -446,14 +562,11 @@ H5TS_mutex_unlock(H5TS_mutex_t *mutex)
herr_t
H5TS_cancel_count_inc(void)
{
+ 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;
-#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);
@@ -466,24 +579,24 @@ H5TS_cancel_count_inc(void)
* Don't use H5MM calls here since the destructor has to use HDfree in
* order to avoid codestack calls.
*/
- cancel_counter = (H5TS_cancel_t *)HDcalloc(1, sizeof(H5TS_cancel_t));
+ cancel_counter = HDcalloc(1, sizeof(*cancel_counter));
if (NULL == cancel_counter) {
HERROR(H5E_RESOURCE, H5E_NOSPACE, "memory allocation failed");
return FAIL;
- } /* end if */
+ }
/* Set the thread's cancellation counter with the new object */
- ret_value = pthread_setspecific(H5TS_cancel_key_s, (void *)cancel_counter);
- if (ret_value) {
+ if (pthread_setspecific(H5TS_cancel_key_s, (void *)cancel_counter) != 0) {
HDfree(cancel_counter);
return FAIL;
- } /* end if */
- } /* end if */
+ }
+ }
/* Check if thread entering library */
if (cancel_counter->cancel_count == 0)
/* Set cancellation state to 'disable', and remember previous state */
- ret_value = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_counter->previous_state);
+ if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_counter->previous_state) != 0)
+ ret_value = FAIL;
/* Increment # of times the library API was re-entered, to avoid resetting
* previous cancellation state until the final API routine is returning.
@@ -495,23 +608,18 @@ H5TS_cancel_count_inc(void)
} /* end H5TS_cancel_count_inc() */
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_cancel_count_dec
+ * Function: H5TS_cancel_count_dec
*
- * USAGE
- * H5TS_cancel_count_dec()
- *
- * RETURNS
- * 0 on success and a non-zero error code on error.
+ * Returns: SUCCEED/FAIL
*
- * DESCRIPTION
+ * Description:
* If counter value is one, then set cancelability type of the thread
* to the previous cancelability type stored in the cancellation counter.
* (the thread is leaving the library).
*
* Decrement the counter value by 1.
*
- * PROGRAMMER: Chee Wai LEE
+ * Programmer: Chee Wai LEE
* May 2, 2000
*
*--------------------------------------------------------------------------
@@ -519,21 +627,19 @@ H5TS_cancel_count_inc(void)
herr_t
H5TS_cancel_count_dec(void)
{
+ 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;
-#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)
/* Reset to previous thread cancellation state, if last API */
- ret_value = pthread_setcancelstate(cancel_counter->previous_state, NULL);
+ if (pthread_setcancelstate(cancel_counter->previous_state, NULL) != 0)
+ ret_value = FAIL;
/* Decrement cancellation counter */
--cancel_counter->cancel_count;
@@ -542,150 +648,792 @@ H5TS_cancel_count_dec(void)
return ret_value;
} /* end H5TS_cancel_count_dec() */
-#ifdef H5_HAVE_WIN_THREADS
+#ifdef H5_USE_RECURSIVE_WRITER_LOCKS
+
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_win32_process_enter
+ * Function: H5TS_alloc_rec_entry_count
*
- * RETURNS
- * SUCCEED/FAIL
+ * Returns:
+ * Pointer to allocated and initialized instance of
+ * H5TS_rec_entry_count, or NULL on failure.
*
- * DESCRIPTION
- * Per-process setup on Windows when using Win32 threads.
+ * Description:
+ * Allocate and initalize an instance of H5TS_rec_entry_count.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
*
*--------------------------------------------------------------------------
*/
-H5_DLL BOOL CALLBACK
-H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex)
+H5TS_rec_entry_count *
+H5TS_alloc_rec_entry_count(hbool_t write_lock)
{
- BOOL ret_value = TRUE;
+ H5TS_rec_entry_count *ret_value = NULL;
- /* Initialize the critical section (can't fail) */
- InitializeCriticalSection(&H5_g.init_lock.CriticalSection);
+ ret_value = HDmalloc(sizeof(*ret_value));
- /* Set up thread local storage */
- if (TLS_OUT_OF_INDEXES == (H5TS_errstk_key_g = TlsAlloc()))
- ret_value = FALSE;
+ if (ret_value) {
-#ifdef H5_HAVE_CODESTACK
- if (TLS_OUT_OF_INDEXES == (H5TS_funcstk_key_g = TlsAlloc()))
- ret_value = FALSE;
-#endif /* H5_HAVE_CODESTACK */
+ ret_value->magic = H5TS_RW_ENTRY_COUNT_MAGIC;
+ ret_value->write_lock = write_lock;
+ ret_value->rec_lock_count = 1;
+ }
- if (TLS_OUT_OF_INDEXES == (H5TS_apictx_key_g = TlsAlloc()))
- ret_value = FALSE;
+ return ret_value;
+
+} /* end H5TS_alloc_rec_entry_count() */
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_free_rec_entry_count
+ *
+ * Returns: void
+ *
+ * Description:
+ * Frees the supplied instance of H5TS_rec_entry_count.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
+ *
+ *--------------------------------------------------------------------------
+ */
+void
+H5TS_free_rec_entry_count(void *target)
+{
+ H5TS_rec_entry_count *count;
+
+ count = (H5TS_rec_entry_count *)target;
+
+ HDassert(count);
+ HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
+
+ count->magic = 0;
+
+ HDfree(count);
+
+ return;
+
+} /* end H5TS_free_rec_entry_count() */
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_rw_lock_init
+ *
+ * Returns: SUCCEED/FAIL
+ *
+ * Description:
+ * Initialize the supplied instance of H5TS_rw_lock_t.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
+ *
+ *--------------------------------------------------------------------------
+ */
+herr_t
+H5TS_rw_lock_init(H5TS_rw_lock_t *rw_lock, int policy)
+{
+ herr_t ret_value = SUCCEED;
+
+ /* Sanity checks -- until other policies are implemented,
+ * policy must equal H5TS_RW_LOCK_POLICY_FAVOR_WRITERS.
+ */
+ if ((rw_lock == NULL) || (policy != H5TS_RW_LOCK_POLICY_FAVOR_WRITERS)) {
+
+ ret_value = FAIL;
+ }
+
+ /* NOTE: Win32 thread init functions tend to have a return type of void while
+ * Pthreads return an int. We've gone with the lowest common denominator
+ * here, but we're going to have to better abstract this in the future.
+ */
+
+ /* Initialize the mutex */
+ if (ret_value == SUCCEED)
+ if (H5TS_mutex_init(&(rw_lock->mutex)) != 0)
+ ret_value = FAIL;
+
+ /* Initialize the waiting readers cv */
+ if (ret_value == SUCCEED)
+ if (H5TS_cond_init(&(rw_lock->readers_cv)) != 0)
+ ret_value = FAIL;
+
+ /* Initialize the waiting writers cv */
+ if (ret_value == SUCCEED)
+ if (H5TS_cond_init(&(rw_lock->writers_cv)) != 0)
+ ret_value = FAIL;
+
+ /* Initialize the counts key */
+ if (ret_value == SUCCEED)
+ if (pthread_key_create(&(rw_lock->rec_entry_count_key), H5TS_free_rec_entry_count) != 0)
+ ret_value = FAIL;
+
+ if (ret_value == SUCCEED) {
+
+ /* Initialize scalar fields */
+
+ rw_lock->magic = H5TS_RW_LOCK_MAGIC;
+ rw_lock->policy = policy;
+ rw_lock->waiting_readers_count = 0;
+ rw_lock->waiting_writers_count = 0;
+ rw_lock->active_readers = 0;
+ rw_lock->active_writers = 0;
+ rw_lock->stats.read_locks_granted = 0;
+ rw_lock->stats.read_locks_released = 0;
+ rw_lock->stats.real_read_locks_granted = 0;
+ rw_lock->stats.real_read_locks_released = 0;
+ rw_lock->stats.max_read_locks = 0;
+ rw_lock->stats.max_read_lock_recursion_depth = 0;
+ rw_lock->stats.read_locks_delayed = 0;
+ rw_lock->stats.max_read_locks_pending = 0;
+ rw_lock->stats.write_locks_granted = 0;
+ rw_lock->stats.write_locks_released = 0;
+ rw_lock->stats.real_write_locks_granted = 0;
+ rw_lock->stats.real_write_locks_released = 0;
+ rw_lock->stats.max_write_locks = 0;
+ rw_lock->stats.max_write_lock_recursion_depth = 0;
+ rw_lock->stats.write_locks_delayed = 0;
+ rw_lock->stats.max_write_locks_pending = 0;
+ }
return ret_value;
-} /* H5TS_win32_process_enter() */
+
+} /* end H5TS_rw_lock_init() */
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_win32_thread_enter
+ * Function: H5TS_rw_lock_destroy
*
- * RETURNS
- * SUCCEED/FAIL
+ * Returns: SUCCEED/FAIL
*
- * DESCRIPTION
- * Per-thread setup on Windows when using Win32 threads.
+ * Description:
+ * Take down an instance of H5TS_rw_lock_t. All mutex, condition
+ * variables, and keys are destroyed, and magic is set to an invalid
+ * value. However, the instance of H5TS_rw_lock_t is not
+ * freed.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
*
*--------------------------------------------------------------------------
*/
herr_t
-H5TS_win32_thread_enter(void)
+H5TS_rw_lock_destroy(H5TS_rw_lock_t *rw_lock)
{
herr_t ret_value = SUCCEED;
- /* Currently a placeholder function. TLS setup is performed
- * elsewhere in the library.
- *
- * WARNING: Do NOT use C standard library functions here.
- * CRT functions are not allowed in DllMain, which is where this code
- * is used.
+ if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
+
+ ret_value = FAIL;
+ }
+ else {
+
+ /* We are commited to the destroy at this point. Set magic
+ * to an invalid value, and call the appropriate pthread
+ * destroy routines. Call them all, even if one fails along
+ * the way.
+ */
+ rw_lock->magic = 0;
+
+ if (H5TS_mutex_destroy(&(rw_lock->mutex)) < 0)
+ ret_value = FAIL;
+
+ if (H5TS_cond_destroy(&(rw_lock->readers_cv)) < 0)
+ ret_value = FAIL;
+
+ if (H5TS_cond_destroy(&(rw_lock->writers_cv)) < 0)
+ ret_value = FAIL;
+
+ if (pthread_key_delete(rw_lock->rec_entry_count_key) < 0)
+ ret_value = FAIL;
+ }
+
+ return ret_value;
+
+} /* end H5TS_rw_lock_destroy() */
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_rw_rdlock
+ *
+ * Returns: SUCCEED/FAIL
+ *
+ * Description:
+ * Attempt to obtain a read lock on the associated recursive read / write
+ * lock.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
+ *
+ *--------------------------------------------------------------------------
+ */
+herr_t
+H5TS_rw_rdlock(H5TS_rw_lock_t *rw_lock)
+{
+ hbool_t have_mutex = FALSE;
+ int result;
+ H5TS_rec_entry_count *count;
+ herr_t ret_value = SUCCEED;
+
+ if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
+
+ ret_value = FAIL;
+ }
+
+ /* Obtain the mutex */
+ if (ret_value == SUCCEED) {
+ if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
+ ret_value = FAIL;
+ else
+ have_mutex = TRUE;
+ }
+
+ /* If there is no specific data for this thread, this is an
+ * initial read lock request.
*/
+ if (ret_value == SUCCEED) {
+
+ count = (H5TS_rec_entry_count *)H5TS_get_thread_local_value(rw_lock->rec_entry_count_key);
+
+ if (count) { /* This is a recursive lock */
+
+ if ((count->write_lock) || (rw_lock->active_readers == 0) || (rw_lock->active_writers != 0)) {
+
+ ret_value = FAIL;
+ }
+ else {
+
+ count->rec_lock_count++;
+
+ H5TS_update_stats_rd_lock(rw_lock, count);
+ }
+ }
+ else { /* This is an initial read lock request */
+
+ switch (rw_lock->policy) {
+
+ case H5TS_RW_LOCK_POLICY_FAVOR_WRITERS:
+ if ((rw_lock->active_writers != 0) || (rw_lock->waiting_writers_count != 0)) {
+
+ int delayed = rw_lock->waiting_readers_count + 1;
+
+ H5TS_update_stats_rd_lock_delay(rw_lock, delayed);
+ }
+
+ while ((rw_lock->active_writers != 0) || (rw_lock->waiting_writers_count != 0)) {
+
+ rw_lock->waiting_readers_count++;
+
+ result = H5TS_cond_wait(&(rw_lock->readers_cv), &(rw_lock->mutex));
+
+ rw_lock->waiting_readers_count--;
+
+ if (result != 0) {
+
+ ret_value = FAIL;
+ break;
+ }
+ }
+ break;
+
+ default:
+ ret_value = FAIL;
+ break;
+ }
+
+ if ((ret_value == SUCCEED) && (NULL == (count = H5TS_alloc_rec_entry_count(FALSE)))) {
+
+ ret_value = FAIL;
+ }
+
+ if ((ret_value == SUCCEED) &&
+ (H5TS_set_thread_local_value(rw_lock->rec_entry_count_key, (void *)count) != 0)) {
+
+ ret_value = FAIL;
+ }
+
+ if (ret_value == SUCCEED) {
+
+ rw_lock->active_readers++;
+
+ HDassert(count->rec_lock_count == 1);
+
+ H5TS_update_stats_rd_lock(rw_lock, count);
+ }
+ }
+ }
+
+ if (have_mutex) {
+
+ H5TS_mutex_unlock_simple(&(rw_lock->mutex));
+ }
return ret_value;
-} /* H5TS_win32_thread_enter() */
+
+} /* end H5TS_rw_rdlock() */
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_win32_process_exit
+ * Function: H5TS_rw_wrlock
*
- * RETURNS
- * SUCCEED/FAIL
+ * Returns: SUCCEED/FAIL
*
- * DESCRIPTION
- * Per-process cleanup on Windows when using Win32 threads.
+ * Description:
+ * Attempt to obtain a write lock on the associated recursive read / write
+ * lock.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
*
*--------------------------------------------------------------------------
*/
-void
-H5TS_win32_process_exit(void)
+herr_t
+H5TS_rw_wrlock(H5TS_rw_lock_t *rw_lock)
{
+ hbool_t have_mutex = FALSE;
+ int result;
+ H5TS_rec_entry_count *count;
+ herr_t ret_value = SUCCEED;
- /* Windows uses a different thread local storage mechanism which does
- * not support auto-freeing like pthreads' keys.
- *
- * This function is currently registered via atexit() and is called
- * AFTER H5_term_library().
+ if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
+
+ ret_value = FAIL;
+ }
+
+ /* Obtain the mutex */
+ if (ret_value == SUCCEED) {
+ if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
+ ret_value = FAIL;
+ else
+ have_mutex = TRUE;
+ }
+
+ /* If there is no specific data for this thread, this is an
+ * initial write lock request.
*/
+ if (ret_value == SUCCEED) {
- /* Clean up critical section resources (can't fail) */
- DeleteCriticalSection(&H5_g.init_lock.CriticalSection);
+ count = (H5TS_rec_entry_count *)H5TS_get_thread_local_value(rw_lock->rec_entry_count_key);
- /* Clean up per-process thread local storage */
- TlsFree(H5TS_errstk_key_g);
-#ifdef H5_HAVE_CODESTACK
- TlsFree(H5TS_funcstk_key_g);
-#endif /* H5_HAVE_CODESTACK */
- TlsFree(H5TS_apictx_key_g);
+ if (count) { /* this is a recursive lock */
- return;
-} /* H5TS_win32_process_exit() */
+ if ((!(count->write_lock)) || (rw_lock->active_readers != 0) || (rw_lock->active_writers != 1)) {
+
+ ret_value = FAIL;
+ }
+ else {
+
+ count->rec_lock_count++;
+
+ H5TS_update_stats_wr_lock(rw_lock, count);
+ }
+ }
+ else { /* This is an initial write lock request */
+
+ switch (rw_lock->policy) {
+
+ case H5TS_RW_LOCK_POLICY_FAVOR_WRITERS:
+ if ((rw_lock->active_readers > 0) || (rw_lock->active_writers > 0)) {
+
+ int delayed = rw_lock->waiting_writers_count + 1;
+
+ H5TS_update_stats_wr_lock_delay(rw_lock, delayed);
+ }
+
+ while ((rw_lock->active_readers > 0) || (rw_lock->active_writers > 0)) {
+
+ rw_lock->waiting_writers_count++;
+
+ result = H5TS_cond_wait(&(rw_lock->writers_cv), &(rw_lock->mutex));
+
+ rw_lock->waiting_writers_count--;
+
+ if (result != 0) {
+
+ ret_value = FAIL;
+ break;
+ }
+ }
+ break;
+
+ default:
+ ret_value = FAIL;
+ break;
+ }
+
+ if ((ret_value == SUCCEED) && (NULL == (count = H5TS_alloc_rec_entry_count(TRUE)))) {
+
+ ret_value = FAIL;
+ }
+
+ if ((ret_value == SUCCEED) &&
+ (H5TS_set_thread_local_value(rw_lock->rec_entry_count_key, (void *)count) != 0)) {
+
+ ret_value = FAIL;
+ }
+
+ if (ret_value == SUCCEED) {
+
+ rw_lock->active_writers++;
+
+ HDassert(count->rec_lock_count == 1);
+
+ H5TS_update_stats_wr_lock(rw_lock, count);
+ }
+ }
+ }
+
+ if (have_mutex) {
+
+ H5TS_mutex_unlock_simple(&(rw_lock->mutex));
+ }
+
+ return ret_value;
+
+} /* end H5TS_rw_wrlock() */
/*--------------------------------------------------------------------------
- * NAME
- * H5TS_win32_thread_exit
+ * Function: H5TS_rw_unlock
*
- * RETURNS
- * SUCCEED/FAIL
+ * Returns: SUCCEED/FAIL
*
- * DESCRIPTION
- * Per-thread cleanup on Windows when using Win32 threads.
+ * Description:
+ * Attempt to unlock either a read or a write lock on the supplied
+ * recursive read / write lock.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
*
*--------------------------------------------------------------------------
*/
herr_t
-H5TS_win32_thread_exit(void)
+H5TS_rw_unlock(H5TS_rw_lock_t *rw_lock)
{
- LPVOID lpvData;
- herr_t ret_value = SUCCEED;
+ hbool_t have_mutex = FALSE;
+ hbool_t discard_rec_count = FALSE;
+ H5TS_rec_entry_count *count;
+ herr_t ret_value = SUCCEED;
- /* Windows uses a different thread local storage mechanism which does
- * not support auto-freeing like pthreads' keys.
- *
- * WARNING: Do NOT use C standard library functions here.
- * CRT functions are not allowed in DllMain, which is where this code
- * is used.
+ if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
+
+ ret_value = FAIL;
+ }
+
+ /* Obtain the mutex */
+ if (ret_value == SUCCEED) {
+ if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
+ ret_value = FAIL;
+ else
+ have_mutex = TRUE;
+ }
+
+ /* If there is no specific data for this thread, no lock was held,
+ * and thus the unlock call must fail.
*/
+ if (ret_value == SUCCEED) {
- /* Clean up per-thread thread local storage */
- lpvData = TlsGetValue(H5TS_errstk_key_g);
- if (lpvData)
- LocalFree((HLOCAL)lpvData);
+ count = (H5TS_rec_entry_count *)H5TS_get_thread_local_value(rw_lock->rec_entry_count_key);
-#ifdef H5_HAVE_CODESTACK
- lpvData = TlsGetValue(H5TS_funcstk_key_g);
- if (lpvData)
- LocalFree((HLOCAL)lpvData);
-#endif /* H5_HAVE_CODESTACK */
+ HDassert(count);
+ HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
+ HDassert(count->rec_lock_count > 0);
- lpvData = TlsGetValue(H5TS_apictx_key_g);
- if (lpvData)
- LocalFree((HLOCAL)lpvData);
+ if (NULL == count) {
+
+ ret_value = FAIL;
+ }
+ else if (count->magic != H5TS_RW_ENTRY_COUNT_MAGIC) {
+
+ ret_value = FAIL;
+ }
+ else if (count->rec_lock_count <= 0) { /* Corrupt count? */
+
+ ret_value = FAIL;
+ }
+ else if (count->write_lock) { /* Drop a write lock */
+
+ HDassert((rw_lock->active_readers == 0) && (rw_lock->active_writers == 1));
+
+ if ((rw_lock->active_readers != 0) || (rw_lock->active_writers != 1)) {
+
+ ret_value = FAIL;
+ }
+ else {
+
+ count->rec_lock_count--;
+
+ HDassert(count->rec_lock_count >= 0);
+
+ if (count->rec_lock_count == 0) {
+
+ /* Make note that we must discard the
+ * recursive entry counter so it will not
+ * confuse us on the next lock request.
+ */
+ discard_rec_count = TRUE;
+
+ /* Drop the write lock -- will signal later if needed */
+ rw_lock->active_writers--;
+
+ HDassert(rw_lock->active_writers == 0);
+ }
+ }
+
+ H5TS_update_stats_wr_unlock(rw_lock, count);
+ }
+ else { /* drop a read lock */
+
+ HDassert((rw_lock->active_readers > 0) && (rw_lock->active_writers == 0));
+
+ if ((rw_lock->active_readers <= 0) || (rw_lock->active_writers != 0)) {
+
+ ret_value = FAIL;
+ }
+ else {
+
+ count->rec_lock_count--;
+
+ HDassert(count->rec_lock_count >= 0);
+
+ if (count->rec_lock_count == 0) {
+
+ /* Make note that we must discard the
+ * recursive entry counter so it will not
+ * confuse us on the next lock request.
+ */
+ discard_rec_count = TRUE;
+
+ /* Drop the read lock -- will signal later if needed */
+ rw_lock->active_readers--;
+ }
+ }
+
+ H5TS_update_stats_rd_unlock(rw_lock, count);
+ }
+
+ if ((ret_value == SUCCEED) && (rw_lock->active_readers == 0) && (rw_lock->active_writers == 0)) {
+
+ /* No locks held -- signal condition variables if required */
+
+ switch (rw_lock->policy) {
+
+ case H5TS_RW_LOCK_POLICY_FAVOR_WRITERS:
+#ifdef H5_HAVE_WIN_THREADS
+ if (rw_lock->waiting_writers_count > 0)
+ H5TS_cond_signal(&(rw_lock->writers_cv));
+ else if (rw_lock->waiting_readers_count > 0)
+ H5TS_cond_broadcast(&(rw_lock->readers_cv));
+#else
+ if (rw_lock->waiting_writers_count > 0) {
+
+ if (H5TS_cond_signal(&(rw_lock->writers_cv)) != 0)
+ ret_value = FAIL;
+ }
+ else if (rw_lock->waiting_readers_count > 0) {
+
+ if (H5TS_cond_broadcast(&(rw_lock->readers_cv)) != 0)
+ ret_value = FAIL;
+ }
+#endif
+ break;
+ default:
+ ret_value = FAIL;
+ break;
+ }
+ }
+ }
+
+ /* If we are really dropping the lock, must set the value of
+ * rec_entry_count_key for this thread to NULL, so that
+ * when this thread next requests a lock, it will appear
+ * as an initial lock, not a recursive lock.
+ */
+ if (discard_rec_count) {
+
+ HDassert(count);
+
+ if (H5TS_set_thread_local_value(rw_lock->rec_entry_count_key, (void *)NULL) != 0) {
+
+ ret_value = FAIL;
+ }
+
+ H5TS_free_rec_entry_count((void *)count);
+ count = NULL;
+ }
+
+ if (have_mutex) {
+
+ H5TS_mutex_unlock_simple(&(rw_lock->mutex));
+ }
return ret_value;
-} /* H5TS_win32_thread_exit() */
-#endif /* H5_HAVE_WIN_THREADS */
+
+} /* end H5TS_rw_unlock() */
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_rw_lock_get_stats
+ *
+ * Returns: SUCCEED/FAIL
+ *
+ * Description:
+ * Obtain a copy of the current statistics on the supplied
+ * recursive read / write lock. Note that to obtain a consistent
+ * set of statistics, the function must obtain the lock mutex.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
+ *
+ *--------------------------------------------------------------------------
+ */
+herr_t
+H5TS_rw_lock_get_stats(H5TS_rw_lock_t *rw_lock, H5TS_rw_lock_stats_t *stats)
+{
+ hbool_t have_mutex = FALSE;
+ herr_t ret_value = SUCCEED;
+
+ if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC) || (stats == NULL)) {
+
+ ret_value = FAIL;
+ }
+
+ /* Obtain the mutex */
+ if (ret_value == SUCCEED) {
+ if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
+ ret_value = FAIL;
+ else
+ have_mutex = TRUE;
+ }
+
+ if (ret_value == SUCCEED) {
+
+ *stats = rw_lock->stats;
+ }
+
+ if (have_mutex) {
+
+ H5TS_mutex_unlock_simple(&(rw_lock->mutex));
+ }
+
+ return ret_value;
+
+} /* end H5TS_rw_lock_get_stats() */
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_rw_lock_reset_stats
+ *
+ * Returns: SUCCEED/FAIL
+ *
+ * Description:
+ * Reset the statistics for the supplied recursive read / write lock.
+ * Note that to reset the statistics consistently, the function must
+ * obtain the lock mutex.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
+ *
+ *--------------------------------------------------------------------------
+ */
+herr_t
+H5TS_rw_lock_reset_stats(H5TS_rw_lock_t *rw_lock)
+{
+ hbool_t have_mutex = FALSE;
+ /* NOTE: Update this initializer if you modify H5TS_rw_lock_stats_t */
+ static const H5TS_rw_lock_stats_t reset_stats = {/* read_locks_granted = */ 0,
+ /* read_locks_released = */ 0,
+ /* real_read_locks_granted = */ 0,
+ /* real_read_locks_released = */ 0,
+ /* max_read_locks = */ 0,
+ /* max_read_lock_recursion_depth = */ 0,
+ /* read_locks_delayed = */ 0,
+ /* max_read_locks_pending = */ 0,
+ /* write_locks_granted = */ 0,
+ /* write_locks_released = */ 0,
+ /* real_write_locks_granted = */ 0,
+ /* real_write_locks_released = */ 0,
+ /* max_write_locks = */ 0,
+ /* max_write_lock_recursion_depth = */ 0,
+ /* write_locks_delayed = */ 0,
+ /* max_write_locks_pending = */ 0};
+ herr_t ret_value = SUCCEED;
+
+ if ((rw_lock == NULL) || (rw_lock->magic != H5TS_RW_LOCK_MAGIC)) {
+
+ ret_value = FAIL;
+ }
+
+ /* Obtain the mutex */
+ if (ret_value == SUCCEED) {
+ if (H5TS_mutex_lock_simple(&(rw_lock->mutex)) != 0)
+ ret_value = FAIL;
+ else
+ have_mutex = TRUE;
+ }
+
+ if (ret_value == SUCCEED) {
+
+ rw_lock->stats = reset_stats;
+ }
+
+ if (have_mutex) {
+
+ H5TS_mutex_unlock_simple(&(rw_lock->mutex));
+ }
+
+ return ret_value;
+
+} /* end H5TS_rw_lock_reset_stats() */
+
+/*--------------------------------------------------------------------------
+ * Function: H5TS_rw_lock_print_stats
+ *
+ * Returns: SUCCEED/FAIL
+ *
+ * Description:
+ * Print the supplied pthresds recursive R/W lock statistics to
+ * standard out.
+ *
+ * UPDATE THIS FUNCTION IF YOU MODIFY H5TS_rw_lock_stats_t.
+ *
+ * Programmer: John Mainzer
+ * August 28, 2020
+ *
+ *--------------------------------------------------------------------------
+ */
+herr_t
+H5TS_rw_lock_print_stats(const char *header_str, H5TS_rw_lock_stats_t *stats)
+{
+ herr_t ret_value = SUCCEED;
+
+ if ((header_str == NULL) || (stats == NULL)) {
+
+ ret_value = FAIL;
+ }
+ else {
+
+ HDfprintf(stdout, "\n\n%s\n\n", header_str);
+ HDfprintf(stdout, " read_locks_granted = %" PRId64 "\n", stats->read_locks_granted);
+ HDfprintf(stdout, " read_locks_released = %" PRId64 "\n", stats->read_locks_released);
+ HDfprintf(stdout, " real_read_locks_granted = %" PRId64 "\n", stats->real_read_locks_granted);
+ HDfprintf(stdout, " real_read_locks_released = %" PRId64 "\n",
+ stats->real_read_locks_released);
+ HDfprintf(stdout, " max_read_locks = %" PRId64 "\n", stats->max_read_locks);
+ HDfprintf(stdout, " max_read_lock_recursion_depth = %" PRId64 "\n",
+ stats->max_read_lock_recursion_depth);
+ HDfprintf(stdout, " read_locks_delayed = %" PRId64 "\n", stats->read_locks_delayed);
+ HDfprintf(stdout, " max_read_locks_pending = %" PRId64 "\n", stats->max_read_locks_pending);
+ HDfprintf(stdout, " write_locks_granted = %" PRId64 "\n", stats->write_locks_granted);
+ HDfprintf(stdout, " write_locks_released = %" PRId64 "\n", stats->write_locks_released);
+ HDfprintf(stdout, " real_write_locks_granted = %" PRId64 "\n",
+ stats->real_write_locks_granted);
+ HDfprintf(stdout, " real_write_locks_released = %" PRId64 "\n",
+ stats->real_write_locks_released);
+ HDfprintf(stdout, " max_write_locks = %" PRId64 "\n", stats->max_write_locks);
+ HDfprintf(stdout, " max_write_lock_recursion_depth = %" PRId64 "\n",
+ stats->max_write_lock_recursion_depth);
+ HDfprintf(stdout, " write_locks_delayed = %" PRId64 "\n", stats->write_locks_delayed);
+ HDfprintf(stdout, " max_write_locks_pending = %" PRId64 "\n\n",
+ stats->max_write_locks_pending);
+ }
+
+ return ret_value;
+
+} /* end H5TS_rw_lock_print_stats() */
+
+#endif /* H5_USE_RECURSIVE_WRITER_LOCKS */
/*--------------------------------------------------------------------------
* NAME
@@ -727,6 +1475,6 @@ H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata)
#endif /* H5_HAVE_WIN_THREADS */
return ret_value;
-} /* H5TS_create_thread */
+} /* end H5TS_create_thread() */
#endif /* H5_HAVE_THREADSAFE */
diff --git a/src/H5TSprivate.h b/src/H5TSprivate.h
index e0cbbc2..e324b38 100644
--- a/src/H5TSprivate.h
+++ b/src/H5TSprivate.h
@@ -13,11 +13,9 @@
/*-------------------------------------------------------------------------
*
- * Created: H5TSprivate.h
- * May 2 2000
- * Chee Wai LEE
+ * Created: H5TSprivate.h
*
- * Purpose: Private non-prototype header.
+ * Purpose: Thread-safety abstractions used by the library
*
*-------------------------------------------------------------------------
*/
@@ -25,57 +23,107 @@
#define H5TSprivate_H_
#ifdef H5_HAVE_THREADSAFE
+
/* Public headers needed by this file */
#ifdef LATER
#include "H5TSpublic.h" /* Public API prototypes */
#endif /* LATER */
-#ifdef H5_HAVE_WIN_THREADS
-
-/* Library level data structures */
+/**************************/
+/* Library Private Macros */
+/**************************/
-/* Mutexes, Threads, and Attributes */
-typedef struct H5TS_mutex_struct {
- CRITICAL_SECTION CriticalSection;
-} H5TS_mutex_t;
+/* Defines */
-/* Portability wrappers around Windows Threads types */
-typedef CRITICAL_SECTION H5TS_mutex_simple_t;
-typedef HANDLE H5TS_thread_t;
-typedef HANDLE H5TS_attr_t;
-typedef DWORD H5TS_key_t;
-typedef INIT_ONCE H5TS_once_t;
+#ifdef H5_HAVE_WIN_THREADS
-/* Defines */
-/* not used on windows side, but need to be defined to something */
+/* Scope Definitions (Pthreads only) */
#define H5TS_SCOPE_SYSTEM 0
#define H5TS_SCOPE_PROCESS 0
-#define H5TS_CALL_CONV WINAPI
+
+/* Calling convention (Windows only) */
+#define H5TS_CALL_CONV WINAPI
/* Portability function aliases */
#define H5TS_get_thread_local_value(key) TlsGetValue(key)
#define H5TS_set_thread_local_value(key, value) TlsSetValue(key, value)
-#define H5TS_attr_init(attr_ptr) 0
-#define H5TS_attr_setscope(attr_ptr, scope) 0
-#define H5TS_attr_destroy(attr_ptr) 0
+#define H5TS_attr_init(attr) 0
+#define H5TS_attr_setscope(attr, scope) 0
+#define H5TS_attr_destroy(attr) 0
#define H5TS_wait_for_thread(thread) WaitForSingleObject(thread, INFINITE)
#define H5TS_mutex_init(mutex) InitializeCriticalSection(mutex)
#define H5TS_mutex_lock_simple(mutex) EnterCriticalSection(mutex)
#define H5TS_mutex_unlock_simple(mutex) LeaveCriticalSection(mutex)
-/* Functions called from DllMain */
-H5_DLL BOOL CALLBACK H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex);
-H5_DLL void H5TS_win32_process_exit(void);
-H5_DLL herr_t H5TS_win32_thread_enter(void);
-H5_DLL herr_t H5TS_win32_thread_exit(void);
-
+/* No Pthreads equivalent - we use a custom H5TS call with that thread library */
#define H5TS_thread_id() ((uint64_t)GetCurrentThreadId())
-#else /* H5_HAVE_WIN_THREADS */
+#else
+
+/* Scope Definitions (Pthreads only) */
+#define H5TS_SCOPE_SYSTEM PTHREAD_SCOPE_SYSTEM
+#define H5TS_SCOPE_PROCESS PTHREAD_SCOPE_PROCESS
-/* Library level data structures */
+/* Calling convention (Windows only) */
+#define H5TS_CALL_CONV /* N/A */
+
+/* Portability function aliases */
+#define H5TS_get_thread_local_value(key) pthread_getspecific(key)
+#define H5TS_set_thread_local_value(key, value) pthread_setspecific(key, value)
+#define H5TS_attr_init(attr) pthread_attr_init((attr))
+#define H5TS_attr_setscope(attr, scope) pthread_attr_setscope(attr, scope)
+#define H5TS_attr_destroy(attr) pthread_attr_destroy(attr)
+#define H5TS_wait_for_thread(thread) pthread_join(thread, NULL)
+#define H5TS_mutex_init(mutex) pthread_mutex_init(mutex, NULL)
+#define H5TS_mutex_lock_simple(mutex) pthread_mutex_lock(mutex)
+#define H5TS_mutex_unlock_simple(mutex) pthread_mutex_unlock(mutex)
+
+/* No Win32 thread equivalent - only needed for RW locks which are not supported
+ * under Windows threads.
+ */
+#define H5TS_mutex_destroy(mutex) pthread_mutex_destroy(mutex)
+#define H5TS_cond_init(cond) pthread_cond_init(cond, NULL)
+#define H5TS_cond_destroy(cond) pthread_cond_destroy(cond)
+#define H5TS_cond_wait(cond, mutex) pthread_cond_wait(cond, mutex)
+#define H5TS_cond_signal(cond) pthread_cond_signal(cond)
+#define H5TS_cond_broadcast(cond) pthread_cond_broadcast(cond)
+
+#endif /* H5_HAVE_WIN_THREADS */
+
+/******************************************************************************
+ * Macros to maintain statistics on the Pthreads recursive R/W lock.
+ ******************************************************************************/
+
+#ifdef H5_USE_RECURSIVE_WRITER_LOCKS
+
+/* Magic values for struct sanity checking */
+
+/* RW lock */
+#define H5TS_RW_LOCK_MAGIC 0XABCD
+
+/* RW lock entry counts */
+#define H5TS_RW_ENTRY_COUNT_MAGIC 0XABBA
+
+/* Flag for favoring writers */
+/* THIS SHOULD BE AN ENUM */
+#define H5TS_RW_LOCK_POLICY_FAVOR_WRITERS 0
+
+#endif /* H5_USE_RECURSIVE_WRITER_LOCKS */
+
+/****************************/
+/* Library Private Typedefs */
+/****************************/
/* Mutexes, Threads, and Attributes */
+
+#ifdef H5_HAVE_WIN_THREADS
+
+typedef struct H5TS_mutex_struct {
+ CRITICAL_SECTION CriticalSection;
+} H5TS_mutex_t;
+
+#else
+
typedef struct H5TS_mutex_struct {
pthread_t owner_thread; /* current lock owner */
pthread_mutex_t atomic_lock; /* lock for atomicity of new mechanism */
@@ -83,42 +131,431 @@ typedef struct H5TS_mutex_struct {
unsigned int lock_count;
} H5TS_mutex_t;
-/* Portability wrappers around pthread types */
+#endif /* H5_HAVE_WIN_THREADS */
+
+/* Portability wrappers */
+
+#ifdef H5_HAVE_WIN_THREADS
+
+typedef HANDLE H5TS_thread_t;
+typedef HANDLE H5TS_attr_t;
+typedef CRITICAL_SECTION H5TS_mutex_simple_t;
+typedef DWORD H5TS_key_t;
+typedef INIT_ONCE H5TS_once_t;
+typedef CONDITION_VARIABLE H5TS_cond_t;
+
+#else
+
typedef pthread_t H5TS_thread_t;
typedef pthread_attr_t H5TS_attr_t;
typedef pthread_mutex_t H5TS_mutex_simple_t;
typedef pthread_key_t H5TS_key_t;
typedef pthread_once_t H5TS_once_t;
+typedef pthread_cond_t H5TS_cond_t;
-/* Scope Definitions */
-#define H5TS_SCOPE_SYSTEM PTHREAD_SCOPE_SYSTEM
-#define H5TS_SCOPE_PROCESS PTHREAD_SCOPE_PROCESS
-#define H5TS_CALL_CONV /* unused - Windows only */
+#endif /* H5_HAVE_WIN_THREADS */
-/* Portability function aliases */
-#define H5TS_get_thread_local_value(key) pthread_getspecific(key)
-#define H5TS_set_thread_local_value(key, value) pthread_setspecific(key, value)
-#define H5TS_attr_init(attr_ptr) pthread_attr_init((attr_ptr))
-#define H5TS_attr_setscope(attr_ptr, scope) pthread_attr_setscope(attr_ptr, scope)
-#define H5TS_attr_destroy(attr_ptr) pthread_attr_destroy(attr_ptr)
-#define H5TS_wait_for_thread(thread) pthread_join(thread, NULL)
-#define H5TS_mutex_init(mutex) pthread_mutex_init(mutex, NULL)
-#define H5TS_mutex_lock_simple(mutex) pthread_mutex_lock(mutex)
-#define H5TS_mutex_unlock_simple(mutex) pthread_mutex_unlock(mutex)
+#ifdef H5_USE_RECURSIVE_WRITER_LOCKS
-/* Pthread-only routines */
-H5_DLL uint64_t H5TS_thread_id(void);
-H5_DLL void H5TS_pthread_first_thread_init(void);
+/******************************************************************************
+ *
+ * Structure H5TS_rw_lock_stats_t
+ *
+ * Catchall structure for statistics on the recursive p-threads based
+ * recursive R/W lock (see declaration of H5TS_rw_lock_t below).
+ *
+ * Since the mutex must be held when reading a consistent set of statistics
+ * from the recursibe R/W lock, it simplifies matters to bundle them into
+ * a single structure. This structure exists for that purpose.
+ *
+ * If you modify this structure, be sure to make equivalent changes to
+ * the reset_stats initializer in H5TS_rw_lock_reset_stats().
+ *
+ * Individual fields are discussed below.
+ *
+ * JRM -- 8/28/20
+ *
+ * Read lock stats:
+ *
+ * read_locks_granted: 64 bit integer used to count the total number of read
+ * locks granted. Note that this includes recursive lock
+ * requests.
+ *
+ * read_locks_released: 64 bit integer used to count the total number of read
+ * locks released. Note that this includes recursive lock
+ * release requests.
+ *
+ * real_read_locks_granted: 64 bit integer used to count the total number of
+ * read locks granted, less any recursive lock requests.
+ *
+ * real_read_locks_released: 64 bit integer used to count the total number of
+ * read locks released, less any recursive lock releases.
+ *
+ * max_read_locks; 64 bit integer used to track the maximum number of read
+ * locks active at any point in time.
+ *
+ * max_read_lock_recursion_depth; 64 bit integer used to track the maximum
+ * recursion depth observed for any read lock.
+ *
+ * read_locks_delayed: 64 bit integer used to track the number of read locks
+ * that were not granted immediately.
+ *
+ * max_read_locks_delayed; 64 bit integer used to track the maximum number of
+ * pending read locks at any point in time.
+ *
+ *
+ * Write lock stats:
+ *
+ * write_locks_granted: 64 bit integer used to count the total number of write
+ * locks granted. Note that this includes recursive lock
+ * requests.
+ *
+ * write_locks_released: 64 bit integer used to count the total number of write
+ * locks released. Note that this includes recursive lock
+ * release requests.
+ *
+ * real_write_locks_granted: 64 bit integer used to count the total number of
+ * write locks granted, less any recursive lock requests.
+ *
+ * real_write_locks_released: 64 bit integer used to count the total number of
+ * write locks released, less any recursive lock releases.
+ *
+ * max_write_locks; 64 bit integer used to track the maximum number of write
+ * locks active at any point in time. Must be either zero or one.
+ *
+ * max_write_lock_recursion_depth; 64 bit integer used to track the maximum
+ * recursion depth observed for any write lock.
+ *
+ * write_locks_delayed: 64 bit integer used to track the number of write locks
+ * that were not granted immediately.
+ *
+ * max_write_locks_delayed; 64 bit integer used to track the maximum number of
+ * pending write locks at any point in time.
+ *
+ ******************************************************************************/
-#endif /* H5_HAVE_WIN_THREADS */
+typedef struct H5TS_rw_lock_stats_t {
+
+ int64_t read_locks_granted;
+ int64_t read_locks_released;
+ int64_t real_read_locks_granted;
+ int64_t real_read_locks_released;
+ int64_t max_read_locks;
+ int64_t max_read_lock_recursion_depth;
+ int64_t read_locks_delayed;
+ int64_t max_read_locks_pending;
+ int64_t write_locks_granted;
+ int64_t write_locks_released;
+ int64_t real_write_locks_granted;
+ int64_t real_write_locks_released;
+ int64_t max_write_locks;
+ int64_t max_write_lock_recursion_depth;
+ int64_t write_locks_delayed;
+ int64_t max_write_locks_pending;
+
+} H5TS_rw_lock_stats_t;
+
+/******************************************************************************
+ *
+ * Structure H5TS_rw_lock_t
+ *
+ * A read / write lock, is a lock that allows either an arbitrary number
+ * of readers, or a single writer into a critical region. A recurssive
+ * lock is one that allows a thread that already has a lock (be it read or
+ * write) to successfully request the lock again, only dropping the lock
+ * when the number of un-lock calls equals the number of lock calls.
+ *
+ * Note that we can't use the Pthreads or Win32 R/W locks, as while they
+ * permit recursive read locks, they disallow recursive write locks.
+ *
+ * This structure is a catchall for the fields needed to implement a
+ * recursive R/W lock that allows recursive write locks, and for the
+ * associate statistics collection fields.
+ *
+ * This recursive R/W lock implementation is an extension of the R/W lock
+ * implementation given in "UNIX network programming" Volume 2, Chapter 8
+ * by w. Richard Stevens, 2nd edition.
+ *
+ * Individual fields are discussed below.
+ *
+ * JRM -- 8/28/20
+ *
+ * magic: Unsigned 32 bit integer field used for sanity checking. This
+ * fields must always be set to H5TS_RW_LOCK_MAGIC.
+ * If this structure is allocated dynamically, remember to set
+ * it to some invalid value before discarding the structure.
+ *
+ * policy Integer containing a code indicating the precidence policy
+ * used by the R/W lock. The supported policies are listed
+ * below:
+ *
+ * H5TS__RW_LOCK_POLICY__FAVOR_WRITERS:
+ *
+ * If selected, the R/W lock will grant access to a pending
+ * writer if there are both pending readers and writers.
+ *
+ *
+ * --- Define other policies here ---
+ *
+ *
+ * mutex: Mutex used to maintain mutual exclusion on the fields of
+ * of this structure.
+ *
+ * readers_cv: Condition variable used for waiting readers.
+ *
+ * writers_cv: Condition variable used for waiting writers.
+ *
+ * waiting_readers_count: 32 bit integer used to maintain a count of
+ * waiting readers. This value should always be non-negative.
+ *
+ * waiting_writers_count: 32 bit integer used to maintain a count of
+ * waiting writers. This value should always be non-negative.
+ *
+ * The following two fields could be combined into a single field, with
+ * the count of active readers being represented by a positive value, and
+ * the number of writers by a negative value. Two fields are used to
+ * facilitate sanity checking.
+ *
+ * active_readers: 32 bit integer used to maintain a count of
+ * readers that currently hold a read lock. This value
+ * must be zero if active_writers is positive. It should
+ * never be negative.
+ *
+ * active_writers: 32 bit integer used to maintain a count of
+ * writers that currently hold a write lock. This value
+ * must always be either 0 or 1, and must be zero if
+ * active_readers is positive. It should never be negative.
+ *
+ * rec_entry_count_key: Instance of thread-local key used to maintain
+ * a thread specific lock type and recursive entry count
+ * for all threads holding a lock.
+ *
+ * stats: Instance of H5TS_rw_lock_stats_t used to track
+ * statistics on the recursive R/W lock. See the declaration
+ * of the structure for discussion of its fields.
+ *
+ * Note that the stats are gathered into a structure because
+ * we must obtain the mutex when reading the statistics to
+ * avoid changes while the statistics are being read. Collecting
+ * them into a structure facilitates this.
+ *
+ ******************************************************************************/
+
+typedef struct H5TS_rw_lock_t {
+
+ uint32_t magic;
+ int32_t policy;
+ H5TS_mutex_simple_t mutex;
+ H5TS_cond_t readers_cv;
+ H5TS_cond_t writers_cv;
+ int32_t waiting_readers_count;
+ int32_t waiting_writers_count;
+ int32_t active_readers;
+ int32_t active_writers;
+ H5TS_key_t rec_entry_count_key;
+ int32_t writer_rec_entry_count;
+ struct H5TS_rw_lock_stats_t stats;
+
+} H5TS_rw_lock_t;
+
+/******************************************************************************
+ *
+ * Structure H5TS_rec_entry_count
+ *
+ * Strucure associated with the reader_rec_entry_count_key defined in
+ * H5TS_rw_lock_t.
+ *
+ * The primary purpose of this structure is to maintain a count of recursive
+ * locks so that the lock can be dropped when the count drops to zero.
+ *
+ * Aditional fields are included for purposes of sanity checking.
+ *
+ * Individual fields are discussed below.
+ *
+ * JRM -- 8/28/20
+ *
+ * magic: Unsigned 32 bit integer field used for sanity checking. This
+ * field must always be set to H5TS_RW_ENTRY_COUNT_MAGIC, and
+ * should be set to some invalid value just before the structure
+ * is freed.
+ *
+ * write_lock: Boolean field that is set to TRUE if the count is for a write
+ * lock, and to FALSE if it is for a read lock.
+ *
+ * rec_lock_count: Count of the number of recursive lock calls, less
+ * the number of recursive unlock calls. The lock in question
+ * is dropped when the count drops to zero.
+ *
+ ******************************************************************************/
+
+typedef struct H5TS_rec_entry_count {
+
+ uint32_t magic;
+ hbool_t write_lock;
+ int64_t rec_lock_count;
+
+} H5TS_rec_entry_count;
+
+#endif /* H5_USE_RECURSIVE_WRITER_LOCKS */
+
+/*****************************/
+/* Library-private Variables */
+/*****************************/
/* Library-scope global variables */
-extern H5TS_once_t H5TS_first_init_g; /* Library initialization */
-extern H5TS_key_t H5TS_errstk_key_g; /* Error stacks */
+
+/* Library initialization */
+extern H5TS_once_t H5TS_first_init_g;
+
+/* Error stacks */
+extern H5TS_key_t H5TS_errstk_key_g;
+
+/* Function stacks */
#ifdef H5_HAVE_CODESTACK
-extern H5TS_key_t H5TS_funcstk_key_g; /* Function stacks */
-#endif /* H5_HAVE_CODESTACK */
-extern H5TS_key_t H5TS_apictx_key_g; /* API contexts */
+extern H5TS_key_t H5TS_funcstk_key_g;
+#endif
+
+/* API contexts */
+extern H5TS_key_t H5TS_apictx_key_g;
+
+/***********************************/
+/* Private static inline functions */
+/***********************************/
+
+#ifdef H5_USE_RECURSIVE_WRITER_LOCKS
+
+static inline void
+H5TS_update_stats_rd_lock(H5TS_rw_lock_t *rw_lock, H5TS_rec_entry_count *count)
+{
+ HDassert(rw_lock);
+ HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
+ HDassert(count);
+ HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
+ HDassert(count->rec_lock_count >= 1);
+ HDassert(!count->write_lock);
+
+ rw_lock->stats.read_locks_granted++;
+
+ if (count->rec_lock_count == 1) {
+
+ rw_lock->stats.real_read_locks_granted++;
+
+ if (rw_lock->active_readers > rw_lock->stats.max_read_locks)
+ rw_lock->stats.max_read_locks = rw_lock->active_readers;
+ }
+
+ if (count->rec_lock_count > rw_lock->stats.max_read_lock_recursion_depth)
+ rw_lock->stats.max_read_lock_recursion_depth = count->rec_lock_count;
+
+} /* end H5TS_update_stats_rd_lock() */
+
+static inline void
+H5TS_update_stats_rd_lock_delay(H5TS_rw_lock_t *rw_lock, int waiting_count)
+{
+ HDassert(rw_lock);
+ HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
+ HDassert((waiting_count) > 0);
+
+ rw_lock->stats.read_locks_delayed++;
+
+ if (rw_lock->stats.max_read_locks_pending < waiting_count)
+ rw_lock->stats.max_read_locks_pending = (waiting_count);
+
+} /* end H5TS_update_stats_rd_lock_delay() */
+
+static inline void
+H5TS_update_stats_rd_unlock(H5TS_rw_lock_t *rw_lock, H5TS_rec_entry_count *count)
+{
+ HDassert(rw_lock);
+ HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
+ HDassert(count);
+ HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
+ HDassert(count->rec_lock_count >= 0);
+ HDassert(!count->write_lock);
+
+ rw_lock->stats.read_locks_released++;
+
+ if (count->rec_lock_count == 0)
+ rw_lock->stats.real_read_locks_released++;
+
+} /* end H5TS_update_stats_rd_unlock() */
+
+static inline void
+H5TS_update_stats_wr_lock(H5TS_rw_lock_t *rw_lock, H5TS_rec_entry_count *count)
+{
+ HDassert(rw_lock);
+ HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
+ HDassert(count);
+ HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
+ HDassert(count->rec_lock_count >= 1);
+ HDassert(count->write_lock);
+
+ rw_lock->stats.write_locks_granted++;
+
+ if (count->rec_lock_count == 1) {
+
+ rw_lock->stats.real_write_locks_granted++;
+
+ if (rw_lock->active_writers > rw_lock->stats.max_write_locks)
+ rw_lock->stats.max_write_locks = rw_lock->active_writers;
+ }
+
+ if (count->rec_lock_count > rw_lock->stats.max_write_lock_recursion_depth)
+ rw_lock->stats.max_write_lock_recursion_depth = count->rec_lock_count;
+
+} /* end H5TS_update_stats_wr_lock() */
+
+static inline void
+H5TS_update_stats_wr_lock_delay(H5TS_rw_lock_t *rw_lock, int waiting_count)
+{
+ HDassert(rw_lock);
+ HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
+ HDassert(waiting_count > 0);
+
+ rw_lock->stats.write_locks_delayed++;
+
+ if (rw_lock->stats.max_write_locks_pending < waiting_count)
+ rw_lock->stats.max_write_locks_pending = waiting_count;
+
+} /* end H5TS_update_stats_wr_lock_delay() */
+
+static inline void
+H5TS_update_stats_wr_unlock(H5TS_rw_lock_t *rw_lock, H5TS_rec_entry_count *count)
+{
+ HDassert(rw_lock);
+ HDassert(rw_lock->magic == H5TS_RW_LOCK_MAGIC);
+ HDassert(count);
+ HDassert(count->magic == H5TS_RW_ENTRY_COUNT_MAGIC);
+ HDassert(count->rec_lock_count >= 0);
+ HDassert(count->write_lock);
+
+ rw_lock->stats.write_locks_released++;
+
+ if (count->rec_lock_count == 0)
+ rw_lock->stats.real_write_locks_released++;
+
+} /* end H5TS_update_stats_wr_unlock() */
+#endif
+
+/***************************************/
+/* Library-private Function Prototypes */
+/***************************************/
+
+/* Platform-specific functions */
+#ifdef H5_HAVE_WIN_THREADS
+
+/* Functions called from DllMain */
+H5_DLL BOOL CALLBACK H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex);
+H5_DLL void H5TS_win32_process_exit(void);
+H5_DLL herr_t H5TS_win32_thread_enter(void);
+H5_DLL herr_t H5TS_win32_thread_exit(void);
+
+#else
+
+H5_DLL uint64_t H5TS_thread_id(void);
+H5_DLL void H5TS_pthread_first_thread_init(void);
+
+#endif /* H5_HAVE_WIN_THREADS */
/* Library-scope routines */
/* (Only used within H5private.h macros) */
@@ -127,11 +564,26 @@ H5_DLL herr_t H5TS_mutex_unlock(H5TS_mutex_t *mutex);
H5_DLL herr_t H5TS_cancel_count_inc(void);
H5_DLL herr_t H5TS_cancel_count_dec(void);
+/* Fully recursive R/W lock related function declarations */
+#ifdef H5_USE_RECURSIVE_WRITER_LOCKS
+H5_DLL H5TS_rec_entry_count *H5TS_alloc_rec_entry_count(hbool_t write_lock);
+H5_DLL void H5TS_free_rec_entry_count(void *target);
+H5_DLL herr_t H5TS_rw_lock_init(H5TS_rw_lock_t *rw_lock, int policy);
+H5_DLL herr_t H5TS_rw_lock_destroy(H5TS_rw_lock_t *rw_lock);
+H5_DLL herr_t H5TS_rw_rdlock(H5TS_rw_lock_t *rw_lock);
+H5_DLL herr_t H5TS_rw_wrlock(H5TS_rw_lock_t *rw_lock);
+H5_DLL herr_t H5TS_rw_unlock(H5TS_rw_lock_t *rw_lock);
+H5_DLL herr_t H5TS_rw_lock_get_stats(H5TS_rw_lock_t *rw_lock, H5TS_rw_lock_stats_t *stats);
+H5_DLL herr_t H5TS_rw_lock_reset_stats(H5TS_rw_lock_t *rw_lock);
+H5_DLL herr_t H5TS_rw_lock_print_stats(const char *header_str, H5TS_rw_lock_stats_t *stats);
+#endif
+
/* Testing routines */
H5_DLL H5TS_thread_t H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata);
#else /* H5_HAVE_THREADSAFE */
+/* Non-threadsafe code needs this */
#define H5TS_thread_id() ((uint64_t)0)
#endif /* H5_HAVE_THREADSAFE */
diff --git a/src/H5private.h b/src/H5private.h
index 7f77c9a..6b29a44 100644
--- a/src/H5private.h
+++ b/src/H5private.h
@@ -1976,9 +1976,13 @@ extern char H5_lib_vers_info_g[];
/* replacement structure for original global variable */
typedef struct H5_api_struct {
- H5TS_mutex_t init_lock; /* API entrance mutex */
- hbool_t H5_libinit_g; /* Has the library been initialized? */
- hbool_t H5_libterm_g; /* Is the library being shutdown? */
+#ifdef H5_USE_RECURSIVE_WRITER_LOCKS
+ H5TS_rw_lock_t init_rw_lock; /* API entrance RW lock */
+#else
+ H5TS_mutex_t init_lock; /* API entrance mutex */
+#endif
+ hbool_t H5_libinit_g; /* Has the library been initialized? */
+ hbool_t H5_libterm_g; /* Is the library being shutdown? */
} H5_api_t;
/* Macros for accessing the global variables */
@@ -1993,13 +1997,17 @@ typedef struct H5_api_struct {
#endif
/* Macros for threadsafe HDF-5 Phase I locks */
+#ifdef H5_USE_RECURSIVE_WRITER_LOCKS
+#define H5_API_LOCK H5TS_rw_wrlock(&H5_g.init_rw_lock);
+#define H5_API_UNLOCK H5TS_rw_unlock(&H5_g.init_rw_lock);
+#else
#define H5_API_LOCK H5TS_mutex_lock(&H5_g.init_lock);
#define H5_API_UNLOCK H5TS_mutex_unlock(&H5_g.init_lock);
+#endif
/* Macros for thread cancellation-safe mechanism */
#define H5_API_UNSET_CANCEL H5TS_cancel_count_inc();
-
-#define H5_API_SET_CANCEL H5TS_cancel_count_dec();
+#define H5_API_SET_CANCEL H5TS_cancel_count_dec();
extern H5_api_t H5_g;
diff --git a/src/libhdf5.settings.in b/src/libhdf5.settings.in
index 44c1540..a4d4af6 100644
--- a/src/libhdf5.settings.in
+++ b/src/libhdf5.settings.in
@@ -73,7 +73,7 @@ Parallel Filtered Dataset Writes: @PARALLEL_FILTERED_WRITES@
High-level library: @HDF5_HL@
Build HDF5 Tests: @HDF5_TESTS@
Build HDF5 Tools: @HDF5_TOOLS@
- Threadsafety: @THREADSAFE@
+ Threadsafety: @THREADSAFE@ (recursive RW locks: @RECURSIVE_RW_LOCKS@)
Default API mapping: @DEFAULT_API_VERSION@
With deprecated public symbols: @DEPRECATED_SYMBOLS@
I/O filters (external): @EXTERNAL_FILTERS@