From 730e7c4642074e9fff71e5cf9598ec644837bc4c Mon Sep 17 00:00:00 2001 From: Quincey Koziol Date: Tue, 19 Mar 2024 19:41:25 -0700 Subject: [PATCH] Move H5TS_rw_lock_t implementation into its own source file. Now that the H5TS_rw_lock_t code is fully using only H5TS package objects and operations, and no pthread-specific code, it can be moved into its own source file. It still doesn't actually work with Windows threads, but the H5TS__rw_lock_init() routine will throw an error for that case. Signed-off-by: Quincey Koziol --- src/H5TSpkg.h | 43 +-- src/H5TSprivate.h | 17 +- src/H5TSpthread.c | 697 --------------------------------------------- src/H5TSrwlock.c | 703 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 725 insertions(+), 735 deletions(-) diff --git a/src/H5TSpkg.h b/src/H5TSpkg.h index 75e75261910..d866d669fc2 100644 --- a/src/H5TSpkg.h +++ b/src/H5TSpkg.h @@ -74,10 +74,6 @@ typedef struct H5TS_api_info_t { unsigned attempt_lock_count; } H5TS_api_info_t; -#ifdef H5_HAVE_WIN_THREADS - -#else - /* Enable statistics when H5TS debugging is enabled */ #ifdef H5TS_DEBUG #define H5TS_ENABLE_REC_RW_LOCK_STATS 1 @@ -90,15 +86,11 @@ typedef struct H5TS_api_info_t { * * 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. + * Statistics for the recursive R/W lock. * - * If you modify this structure, be sure to make equivalent changes to - * the reset_stats code in H5TS__rw_lock_reset_stats(). + * Since a mutex must be held to read a consistent set of statistics from a + * recursive R/W lock, it simplifies matters to bundle them into a single + * structure. * * Individual fields are: * @@ -173,11 +165,7 @@ typedef struct H5TS_rw_lock_stats_t { * * Structure H5TS_rw_lock_t * - * A readers / writer (R/W) lock is a lock that allows either an arbitrary - * number of readers, or a single writer into a critical region. A recursive - * lock is one that allows a thread that already holds a lock (read or - * write) to successfully request the lock again, only dropping the lock - * when the number of unlock calls equals the number of lock calls. + * A recursive readers / writer (R/W) lock. * * This structure holds the fields needed to implement a recursive R/W lock * that allows recursive write locks, and for the associated statistics @@ -186,10 +174,6 @@ typedef struct H5TS_rw_lock_stats_t { * Note that we can't use the pthreads or Win32 R/W locks: they permit * recursive read locks, but disallow recursive write locks. * - * 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: * * mutex: Mutex used to maintain mutual exclusion on the fields of @@ -209,21 +193,24 @@ typedef struct H5TS_rw_lock_stats_t { * * readers_cv: Condition variable used for waiting readers. * - * active_reader_threads: The # of threads holding a read lock. + * reader_thread_count: The # of threads holding a read lock. * * rec_read_lock_count_key: Instance of thread-local key used to maintain * a thread-specific recursive lock count for each thread * holding a read lock. * + * is_key_registered: Flag to track if the rec_read_lock_count_key has been + * registered yet for a lock. + * * stats: Instance of H5TS_rw_lock_stats_t used to track * statistics on the recursive R/W lock. * ******************************************************************************/ typedef enum { - UNUSED = 0, /* Lock is currently unused */ - WRITE, /* Lock is a recursive write lock */ - READ /* Lock is a recursive read lock */ + H5TS_RW_LOCK_UNUSED = 0, /* Lock is currently unused */ + H5TS_RW_LOCK_WRITE, /* Lock is a recursive write lock */ + H5TS_RW_LOCK_READ /* Lock is a recursive read lock */ } H5TS_rw_lock_type_t; typedef struct H5TS_rw_lock_t { @@ -238,10 +225,10 @@ typedef struct H5TS_rw_lock_t { int32_t waiting_writers_count; /* Reader fields */ - bool is_key_registered; H5TS_cond_t readers_cv; - int32_t active_reader_threads; + int32_t reader_thread_count; H5TS_key_t rec_read_lock_count_key; + bool is_key_registered; #if H5TS_ENABLE_REC_RW_LOCK_STATS /* Stats */ @@ -249,8 +236,6 @@ typedef struct H5TS_rw_lock_t { #endif } H5TS_rw_lock_t; -#endif /* H5_HAVE_WIN_THREADS */ - /*****************************/ /* Package Private Variables */ /*****************************/ diff --git a/src/H5TSprivate.h b/src/H5TSprivate.h index 79234bcfa90..d15fa92e80e 100644 --- a/src/H5TSprivate.h +++ b/src/H5TSprivate.h @@ -92,17 +92,16 @@ typedef pthread_once_t H5TS_once_t; typedef void (*H5TS_once_init_func_t)(void); #endif - /*****************************/ - /* Library-private Variables */ - /*****************************/ +/*****************************/ +/* Library-private Variables */ +/*****************************/ - /***************************************/ - /* Library-private Function Prototypes */ - /***************************************/ +/***************************************/ +/* Library-private Function Prototypes */ +/***************************************/ - /* Library/thread init/term operations */ - H5_DLL void - H5TS_term_package(void); +/* Library/thread init/term operations */ +H5_DLL void H5TS_term_package(void); /* API locking */ H5_DLL herr_t H5TS_api_lock(void); diff --git a/src/H5TSpthread.c b/src/H5TSpthread.c index 3c492fbc502..ae3cff2b0bd 100644 --- a/src/H5TSpthread.c +++ b/src/H5TSpthread.c @@ -41,72 +41,14 @@ /* Local Macros */ /****************/ -/* R/W lock initialization macro */ -#if H5TS_ENABLE_REC_RW_LOCK_STATS -#define H5TS_RW_LOCK_INIT \ - { \ - PTHREAD_MUTEX_INITIALIZER, /* mutex */ \ - (H5TS_rw_lock_type_t)UNUSED, /* lock_type */ \ - PTHREAD_COND_INITIALIZER, /* writers_cv */ \ - 0, /* write_thread */ \ - 0, /* rec_write_lock_count */ \ - 0, /* waiting_writers_count */ \ - false, /* is_key_registered */ \ - PTHREAD_COND_INITIALIZER, /* readers_cv */ \ - 0, /* active_reader_threads */ \ - (H5TS_key_t)0, /* rec_read_lock_count_key */ \ - { \ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ - } /* stats */ \ - } -#else -#define H5TS_RW_LOCK_INIT \ - { \ - PTHREAD_MUTEX_INITIALIZER, /* mutex */ \ - (H5TS_rw_lock_type_t)UNUSED, /* lock_type */ \ - PTHREAD_COND_INITIALIZER, /* writers_cv */ \ - 0, /* write_thread */ \ - 0, /* rec_write_lock_count */ \ - 0, /* waiting_writers_count */ \ - false, /* is_key_registered */ \ - PTHREAD_COND_INITIALIZER, /* readers_cv */ \ - 0, /* active_reader_threads */ \ - (H5TS_key_t)0 /* rec_read_lock_count_key */ \ - } -#endif - /******************/ /* Local Typedefs */ /******************/ -/****************************************************************************** - * - * Structure H5TS_rec_read_entry_count_t - * - * Structure associated with the rec_read_lock_count_key defined in - * H5TS_rw_lock_t. - * - * This structure maintains a count of recursive read locks so that the lock can - * be decrements when the thread-specific count drops to zero. - * - * Individual fields are: - * - * rec_lock_count: Count of the number of recursive lock calls, less - * the number of recursive unlock calls. The lock in question - * is decremented when the count drops to zero. - * - ******************************************************************************/ - -typedef struct H5TS_rec_entry_count { - int64_t rec_lock_count; -} H5TS_rec_entry_count_t; - /********************/ /* Local Prototypes */ /********************/ -static void H5TS__key_destructor(void *key_val); - /*********************/ /* Package Variables */ /*********************/ @@ -119,33 +61,6 @@ static void H5TS__key_destructor(void *key_val); /* Local Variables */ /*******************/ -/* Default value to initialize R/W locks */ -static const H5TS_rw_lock_t H5TS_rw_lock_def = H5TS_RW_LOCK_INIT; - -/*-------------------------------------------------------------------------- - * Function: H5TS__key_destructor - * - * Purpose: 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. - * - * Return: None - * - *-------------------------------------------------------------------------- - */ -static void -H5TS__key_destructor(void *key_val) -{ - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - /* Use free here instead of H5MM_xfree(), to avoid calling the H5CS routines */ - if (NULL != key_val) - free(key_val); - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* end H5TS__key_destructor() */ - /*-------------------------------------------------------------------------- * Function: H5TS__pthread_first_thread_init * @@ -175,618 +90,6 @@ H5TS__pthread_first_thread_init(void) FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY } /* end H5TS__pthread_first_thread_init() */ -#if H5TS_ENABLE_REC_RW_LOCK_STATS -/*-------------------------------------------------------------------------- - * Function: H5TS__update_stats_rd_lock - * - * Purpose: Update stats for acquiring a read lock - * - * Return: none - * - *-------------------------------------------------------------------------- - */ -static void -H5TS__update_stats_rd_lock(H5TS_rw_lock_t *rw_lock, const H5TS_rec_entry_count_t *count) -{ - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - assert(rw_lock); - assert(READ == rw_lock->lock_type); - assert(count); - assert(count->rec_lock_count >= 1); - - rw_lock->stats.read_locks_granted++; - - if (count->rec_lock_count == 1) { - rw_lock->stats.real_read_locks_granted++; - if (rw_lock->active_reader_threads > rw_lock->stats.max_read_locks) - rw_lock->stats.max_read_locks = rw_lock->active_reader_threads; - } - - 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; - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* end H5TS__update_stats_rd_lock() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__update_stats_rd_lock_delay - * - * Purpose: Update stats for delay in acquiring a read lock - * - * Return: none - * - *-------------------------------------------------------------------------- - */ -static void -H5TS__update_stats_rd_lock_delay(H5TS_rw_lock_t *rw_lock) -{ - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - assert(rw_lock); - - rw_lock->stats.read_locks_delayed++; - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* end H5TS__update_stats_rd_lock_delay() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__update_stats_rd_unlock - * - * Purpose: Update stats for releasing a read lock - * - * Return: none - * - *-------------------------------------------------------------------------- - */ -static void -H5TS__update_stats_rd_unlock(H5TS_rw_lock_t *rw_lock, const H5TS_rec_entry_count_t *count) -{ - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - assert(rw_lock); - assert(READ == rw_lock->lock_type); - assert(count); - assert(count->rec_lock_count >= 0); - - rw_lock->stats.read_locks_released++; - - if (count->rec_lock_count == 0) - rw_lock->stats.real_read_locks_released++; - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* end H5TS__update_stats_rd_unlock() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__update_stats_wr_lock - * - * Purpose: Update stats for acquiring a write lock - * - * Return: none - * - *-------------------------------------------------------------------------- - */ -static void -H5TS__update_stats_wr_lock(H5TS_rw_lock_t *rw_lock) -{ - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - assert(rw_lock); - assert(WRITE == rw_lock->lock_type); - assert(rw_lock->rec_write_lock_count >= 1); - - rw_lock->stats.write_locks_granted++; - - if (rw_lock->rec_write_lock_count == 1) { - rw_lock->stats.real_write_locks_granted++; - if (rw_lock->rec_write_lock_count > rw_lock->stats.max_write_locks) - rw_lock->stats.max_write_locks = rw_lock->rec_write_lock_count; - } - - if (rw_lock->rec_write_lock_count > rw_lock->stats.max_write_lock_recursion_depth) - rw_lock->stats.max_write_lock_recursion_depth = rw_lock->rec_write_lock_count; - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* end H5TS__update_stats_wr_lock() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__update_stats_wr_lock_delay - * - * Purpose: Update stats for delay in acquiring a write lock - * - * Return: none - * - *-------------------------------------------------------------------------- - */ -static void -H5TS__update_stats_wr_lock_delay(H5TS_rw_lock_t *rw_lock) -{ - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - assert(rw_lock); - - rw_lock->stats.write_locks_delayed++; - - if (rw_lock->stats.max_write_locks_pending <= rw_lock->waiting_writers_count) - rw_lock->stats.max_write_locks_pending = rw_lock->waiting_writers_count + 1; - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* end H5TS__update_stats_wr_lock_delay() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__update_stats_wr_unlock - * - * Purpose: Update stats for releasing a write lock - * - * Return: none - * - *-------------------------------------------------------------------------- - */ -static void -H5TS__update_stats_wr_unlock(H5TS_rw_lock_t *rw_lock) -{ - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - assert(rw_lock); - assert(WRITE == rw_lock->lock_type); - assert(rw_lock->rec_write_lock_count >= 0); - - rw_lock->stats.write_locks_released++; - - if (rw_lock->rec_write_lock_count == 0) - rw_lock->stats.real_write_locks_released++; - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* end H5TS__update_stats_wr_unlock() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_lock_get_stats - * - * Purpose: Obtain a copy of the current statistics for a recursive - * read / write lock. - * - * Note: To obtain a consistent set of statistics, the function must - * obtain the lock mutex. - * - * Return: Non-negative on success / Negative on failure - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS__rw_lock_get_stats(H5TS_rw_lock_t *rw_lock, H5TS_rw_lock_stats_t *stats) -{ - bool have_mutex = false; - herr_t ret_value = SUCCEED; - - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - if (H5_UNLIKELY(NULL == rw_lock || NULL == stats)) - HGOTO_DONE(FAIL); - - /* Acquire the mutex */ - if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) - HGOTO_DONE(FAIL); - have_mutex = true; - - /* Copy R/W lock stats */ - *stats = rw_lock->stats; - -done: - if (have_mutex) - H5TS_mutex_unlock(&rw_lock->mutex); - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_lock_get_stats() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_lock_reset_stats - * - * Purpose: Reset the statistics for the supplied recursive read / write - * lock. - * - * Note: To obtain a consistent set of statistics, the function must - * obtain the lock mutex. - * - * Return: Non-negative on success / Negative on failure - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS__rw_lock_reset_stats(H5TS_rw_lock_t *rw_lock) -{ - bool have_mutex = false; - herr_t ret_value = SUCCEED; - - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - if (H5_UNLIKELY(NULL == rw_lock)) - HGOTO_DONE(FAIL); - - /* Acquire the mutex */ - if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) - HGOTO_DONE(FAIL); - have_mutex = true; - - /* Reset stats */ - memset(&rw_lock->stats, 0, sizeof(rw_lock->stats)); - -done: - if (have_mutex) - H5TS_mutex_unlock(&rw_lock->mutex); - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_lock_reset_stats() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_lock_print_stats - * - * Purpose: Print the supplied pthresds recursive R/W lock statistics. - * - * Return: Non-negative on success / Negative on failure - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS__rw_lock_print_stats(const char *header_str, H5TS_rw_lock_stats_t *stats) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - if (H5_UNLIKELY(NULL == header_str || NULL == stats)) - HGOTO_DONE(FAIL); - - fprintf(stdout, "\n\n%s\n\n", header_str); - fprintf(stdout, " read_locks_granted = %" PRId64 "\n", stats->read_locks_granted); - fprintf(stdout, " read_locks_released = %" PRId64 "\n", stats->read_locks_released); - fprintf(stdout, " real_read_locks_granted = %" PRId64 "\n", stats->real_read_locks_granted); - fprintf(stdout, " real_read_locks_released = %" PRId64 "\n", stats->real_read_locks_released); - fprintf(stdout, " max_read_locks = %" PRId64 "\n", stats->max_read_locks); - fprintf(stdout, " max_read_lock_recursion_depth = %" PRId64 "\n", stats->max_read_lock_recursion_depth); - fprintf(stdout, " read_locks_delayed = %" PRId64 "\n", stats->read_locks_delayed); - fprintf(stdout, " write_locks_granted = %" PRId64 "\n", stats->write_locks_granted); - fprintf(stdout, " write_locks_released = %" PRId64 "\n", stats->write_locks_released); - fprintf(stdout, " real_write_locks_granted = %" PRId64 "\n", stats->real_write_locks_granted); - fprintf(stdout, " real_write_locks_released = %" PRId64 "\n", stats->real_write_locks_released); - fprintf(stdout, " max_write_locks = %" PRId64 "\n", stats->max_write_locks); - fprintf(stdout, " max_write_lock_recursion_depth = %" PRId64 "\n", - stats->max_write_lock_recursion_depth); - fprintf(stdout, " write_locks_delayed = %" PRId64 "\n", stats->write_locks_delayed); - fprintf(stdout, " max_write_locks_pending = %" PRId64 "\n\n", stats->max_write_locks_pending); - -done: - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_lock_print_stats() */ -#endif /* H5TS_ENABLE_REC_RW_LOCK_STATS */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_lock_init - * - * Purpose: Initialize the supplied instance of H5TS_rw_lock_t. - * - * Return: Non-negative on success / Negative on failure - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS__rw_lock_init(H5TS_rw_lock_t *rw_lock) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - if (H5_UNLIKELY(NULL == rw_lock)) - HGOTO_DONE(FAIL); - - /* Initialize the lock */ - memcpy(rw_lock, &H5TS_rw_lock_def, sizeof(H5TS_rw_lock_def)); - -done: - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_lock_init() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_lock_destroy - * - * Purpose: Take down an instance of H5TS_rw_lock_t. All mutex, condition - * variables, and keys are destroyed. However, the instance of - * H5TS_rw_lock_t is not freed. - * - * Return: Non-negative on success / Negative on failure - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS__rw_lock_destroy(H5TS_rw_lock_t *rw_lock) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - if (H5_UNLIKELY(NULL == rw_lock)) - HGOTO_DONE(FAIL); - - /* Call the appropriate destroy routines. We are committed - * to the destroy at this point, so call them all, even if one fails - * along the way. - */ - if (H5_UNLIKELY(H5TS_mutex_destroy(&rw_lock->mutex))) - ret_value = FAIL; - if (H5_UNLIKELY(H5TS_cond_destroy(&rw_lock->readers_cv))) - ret_value = FAIL; - if (H5_UNLIKELY(H5TS_cond_destroy(&rw_lock->writers_cv))) - ret_value = FAIL; - if (rw_lock->is_key_registered) - if (H5_UNLIKELY(H5TS_key_delete(rw_lock->rec_read_lock_count_key) < 0)) - ret_value = FAIL; - -done: - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_lock_destroy() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_rdlock - * - * Purpose: Attempt to obtain a read lock on the associated recursive - * read / write lock. - * - * Return: Non-negative on success / Negative on failure - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS__rw_rdlock(H5TS_rw_lock_t *rw_lock) -{ - H5TS_rec_entry_count_t *count; - H5TS_thread_t my_thread = H5TS_thread_self(); - bool have_mutex = false; - herr_t ret_value = SUCCEED; - - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - if (H5_UNLIKELY(NULL == rw_lock)) - HGOTO_DONE(FAIL); - - /* Acquire the mutex */ - if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) - HGOTO_DONE(FAIL); - have_mutex = true; - - /* Fail if attempting to acquire a read lock on a thread that holds - * a write lock - */ - if (H5_UNLIKELY(WRITE == rw_lock->lock_type && H5TS_thread_equal(my_thread, rw_lock->write_thread))) - HGOTO_DONE(FAIL); - - /* If there is no thread-specific data for this thread, set it up */ - if (!rw_lock->is_key_registered) { - if (H5_UNLIKELY(H5TS_key_create(&rw_lock->rec_read_lock_count_key, H5TS__key_destructor))) - HGOTO_DONE(FAIL); - rw_lock->is_key_registered = true; - count = NULL; - } - else if (H5_UNLIKELY(H5TS_key_get_value(rw_lock->rec_read_lock_count_key, (void **)&count) < 0)) - HGOTO_DONE(FAIL); - if (NULL == count) { - if (H5_UNLIKELY(NULL == (count = calloc(1, sizeof(*count))))) - HGOTO_DONE(FAIL); - - if (H5_UNLIKELY(H5TS_key_set_value(rw_lock->rec_read_lock_count_key, (void *)count))) - HGOTO_DONE(FAIL); - } - - if (count->rec_lock_count > 0) { /* This is a recursive lock */ - assert(READ == rw_lock->lock_type); - assert(rw_lock->active_reader_threads > 0 && rw_lock->rec_write_lock_count == 0); - } - else { /* This is an initial read lock request, on this thread */ - /* Readers defer to current or pending writers */ - if (WRITE == rw_lock->lock_type) { -#if H5TS_ENABLE_REC_RW_LOCK_STATS - H5TS__update_stats_rd_lock_delay(rw_lock); -#endif - - do { - if (H5_UNLIKELY(H5TS_cond_wait(&rw_lock->readers_cv, &rw_lock->mutex))) - HGOTO_DONE(FAIL); - } while (WRITE == rw_lock->lock_type); - } - - /* Set counter's lock type (which might already be set) & increment - * number of reader threads - */ - rw_lock->lock_type = READ; - rw_lock->active_reader_threads++; - } - - /* Increment read lock count for this thread */ - count->rec_lock_count++; -#if H5TS_ENABLE_REC_RW_LOCK_STATS - H5TS__update_stats_rd_lock(rw_lock, count); -#endif - -done: - if (have_mutex) - H5TS_mutex_unlock(&rw_lock->mutex); - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_rdlock() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_wrlock - * - * Purpose: Attempt to obtain a write lock on the associated recursive - * read / write lock. - * - * Return: Non-negative on success / Negative on failure - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS__rw_wrlock(H5TS_rw_lock_t *rw_lock) -{ - H5TS_thread_t my_thread = H5TS_thread_self(); - bool have_mutex = false; - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - if (H5_UNLIKELY(NULL == rw_lock)) - HGOTO_DONE(FAIL); - - /* Acquire the mutex */ - if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) - HGOTO_DONE(FAIL); - have_mutex = true; - - /* Check for initial write lock request on this thread */ - if (WRITE != rw_lock->lock_type || !H5TS_thread_equal(my_thread, rw_lock->write_thread)) { - /* Fail if attempting to acquire a write lock on a thread that holds - * a read lock - */ - if (READ == rw_lock->lock_type) { - H5TS_rec_entry_count_t *count; - - /* Sanity check */ - assert(rw_lock->is_key_registered); - - /* Fail if read lock count for this thread is > 0 */ - if (H5_UNLIKELY(H5TS_key_get_value(rw_lock->rec_read_lock_count_key, (void **)&count) < 0)) - HGOTO_DONE(FAIL); - if (H5_UNLIKELY(NULL != count && count->rec_lock_count > 0)) - HGOTO_DONE(FAIL); - } - - /* If lock is already held, wait to acquire it */ - if (UNUSED != rw_lock->lock_type) { -#if H5TS_ENABLE_REC_RW_LOCK_STATS - H5TS__update_stats_wr_lock_delay(rw_lock); -#endif - - do { - int result; - - rw_lock->waiting_writers_count++; - result = H5TS_cond_wait(&rw_lock->writers_cv, &rw_lock->mutex); - rw_lock->waiting_writers_count--; - if (H5_UNLIKELY(result != 0)) - HGOTO_DONE(FAIL); - } while (UNUSED != rw_lock->lock_type); - } - - /* Set lock type & owner thread */ - rw_lock->lock_type = WRITE; - rw_lock->write_thread = my_thread; - } - - /* Increment write lock count for this thread */ - rw_lock->rec_write_lock_count++; -#if H5TS_ENABLE_REC_RW_LOCK_STATS - H5TS__update_stats_wr_lock(rw_lock); -#endif - -done: - if (have_mutex) - H5TS_mutex_unlock(&rw_lock->mutex); - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_wrlock() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_unlock - * - * Purpose: Attempt to unlock either a read or a write lock on a - * recursive read / write lock. - * - * Return: Non-negative on success / Negative on failure - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS__rw_unlock(H5TS_rw_lock_t *rw_lock) -{ - bool have_mutex = false; - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - if (H5_UNLIKELY(NULL == rw_lock)) - HGOTO_DONE(FAIL); - - /* Acquire the mutex */ - if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) - HGOTO_DONE(FAIL); - have_mutex = true; - - /* Error check */ - if (H5_UNLIKELY(UNUSED == rw_lock->lock_type)) /* Unlocking an unused lock? */ - HGOTO_DONE(FAIL); - - if (WRITE == rw_lock->lock_type) { /* Drop a write lock */ - /* Sanity checks */ - assert(0 == rw_lock->active_reader_threads); - assert(rw_lock->rec_write_lock_count > 0); - - /* Decrement recursive lock count */ - rw_lock->rec_write_lock_count--; -#if H5TS_ENABLE_REC_RW_LOCK_STATS - H5TS__update_stats_wr_unlock(rw_lock); -#endif - - /* Check if lock is unused now */ - if (0 == rw_lock->rec_write_lock_count) - rw_lock->lock_type = UNUSED; - } - else { /* Drop a read lock */ - H5TS_rec_entry_count_t *count; - - /* Sanity and error checks */ - assert(rw_lock->is_key_registered); - assert(rw_lock->active_reader_threads > 0); - assert(0 == rw_lock->rec_write_lock_count); - if (H5_UNLIKELY(H5TS_key_get_value(rw_lock->rec_read_lock_count_key, (void **)&count) < 0)) - HGOTO_DONE(FAIL); - if (H5_UNLIKELY(NULL == count)) - HGOTO_DONE(FAIL); - assert(count->rec_lock_count > 0); - - /* Decrement recursive lock count for this thread */ - count->rec_lock_count--; -#if H5TS_ENABLE_REC_RW_LOCK_STATS - H5TS__update_stats_rd_unlock(rw_lock, count); -#endif - - /* Check if this thread is releasing its last read lock */ - if (0 == count->rec_lock_count) { - /* Decrement the # of threads with a read lock */ - rw_lock->active_reader_threads--; - - /* Check if lock is unused now */ - if (0 == rw_lock->active_reader_threads) - rw_lock->lock_type = UNUSED; - } - } - - /* Signal condition variable if lock is unused now */ - if (UNUSED == rw_lock->lock_type) { - /* Prioritize pending writers if there are any */ - if (rw_lock->waiting_writers_count > 0) { - if (H5_UNLIKELY(H5TS_cond_signal(&rw_lock->writers_cv))) - HGOTO_DONE(FAIL); - } - else { - if (H5_UNLIKELY(H5TS_cond_broadcast(&rw_lock->readers_cv))) - HGOTO_DONE(FAIL); - } - } - -done: - if (have_mutex) - H5TS_mutex_unlock(&rw_lock->mutex); - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_unlock() */ - #endif /* H5_HAVE_WIN_THREADS */ #endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSrwlock.c b/src/H5TSrwlock.c index f61cdc68fa5..75619dfe61b 100644 --- a/src/H5TSrwlock.c +++ b/src/H5TSrwlock.c @@ -41,13 +41,70 @@ /* Local Macros */ /****************/ +/* R/W lock initialization macro */ +#if H5TS_ENABLE_REC_RW_LOCK_STATS +#define H5TS_RW_LOCK_INIT \ + { \ + H5TS_MUTEX_INITIALIZER, /* mutex */ \ + H5TS_RW_LOCK_UNUSED, /* lock_type */ \ + H5TS_COND_INITIALIZER, /* writers_cv */ \ + 0, /* write_thread */ \ + 0, /* rec_write_lock_count */ \ + 0, /* waiting_writers_count */ \ + H5TS_COND_INITIALIZER, /* readers_cv */ \ + 0, /* reader_thread_count */ \ + (H5TS_key_t)0, /* rec_read_lock_count_key */ \ + false, /* is_key_registered */ \ + { \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ + } /* stats */ \ + } +#else +#define H5TS_RW_LOCK_INIT \ + { \ + H5TS_MUTEX_INITIALIZER, /* mutex */ \ + H5TS_RW_LOCK_UNUSED, /* lock_type */ \ + H5TS_COND_INITIALIZER, /* writers_cv */ \ + 0, /* write_thread */ \ + 0, /* rec_write_lock_count */ \ + 0, /* waiting_writers_count */ \ + H5TS_COND_INITIALIZER, /* readers_cv */ \ + 0, /* reader_thread_count */ \ + (H5TS_key_t)0 /* rec_read_lock_count_key */ \ + false, /* is_key_registered */ \ + } +#endif + /******************/ /* Local Typedefs */ /******************/ +/****************************************************************************** + * + * Structure H5TS_rec_read_entry_count_t + * + * Structure associated with the rec_read_lock_count_key defined in + * H5TS_rw_lock_t. + * + * This structure maintains a count of recursive read locks so that the lock can + * be decrements when the thread-specific count drops to zero. + * + * Individual fields are: + * + * rec_lock_count: Count of the number of recursive lock calls, less + * the number of recursive unlock calls. The lock in question + * is decremented when the count drops to zero. + * + ******************************************************************************/ + +typedef struct H5TS_rec_entry_count { + int64_t rec_lock_count; +} H5TS_rec_entry_count_t; + /********************/ /* Local Prototypes */ /********************/ +static void H5TS__key_destructor(void *key_val); /*********************/ /* Package Variables */ @@ -61,4 +118,650 @@ /* Local Variables */ /*******************/ +/* Default value to initialize R/W locks */ +static const H5TS_rw_lock_t H5TS_rw_lock_def = H5TS_RW_LOCK_INIT; + +/*-------------------------------------------------------------------------- + * Function: H5TS__key_destructor + * + * Purpose: 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. + * + * Return: None + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__key_destructor(void *key_val) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Use free here instead of H5MM_xfree(), to avoid calling the H5CS routines */ + if (NULL != key_val) + free(key_val); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__key_destructor() */ + +#if H5TS_ENABLE_REC_RW_LOCK_STATS +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_rd_lock + * + * Purpose: Update stats for acquiring a read lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_rd_lock(H5TS_rw_lock_t *rw_lock, const H5TS_rec_entry_count_t *count) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + assert(H5TS_RW_LOCK_READ == rw_lock->lock_type); + assert(count); + assert(count->rec_lock_count >= 1); + + rw_lock->stats.read_locks_granted++; + + if (count->rec_lock_count == 1) { + rw_lock->stats.real_read_locks_granted++; + if (rw_lock->reader_thread_count > rw_lock->stats.max_read_locks) + rw_lock->stats.max_read_locks = rw_lock->reader_thread_count; + } + + 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; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_rd_lock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_rd_lock_delay + * + * Purpose: Update stats for delay in acquiring a read lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_rd_lock_delay(H5TS_rw_lock_t *rw_lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + + rw_lock->stats.read_locks_delayed++; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_rd_lock_delay() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_rd_unlock + * + * Purpose: Update stats for releasing a read lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_rd_unlock(H5TS_rw_lock_t *rw_lock, const H5TS_rec_entry_count_t *count) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + assert(H5TS_RW_LOCK_READ == rw_lock->lock_type); + assert(count); + assert(count->rec_lock_count >= 0); + + rw_lock->stats.read_locks_released++; + + if (count->rec_lock_count == 0) + rw_lock->stats.real_read_locks_released++; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_rd_unlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_wr_lock + * + * Purpose: Update stats for acquiring a write lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_wr_lock(H5TS_rw_lock_t *rw_lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + assert(H5TS_RW_LOCK_WRITE == rw_lock->lock_type); + assert(rw_lock->rec_write_lock_count >= 1); + + rw_lock->stats.write_locks_granted++; + + if (rw_lock->rec_write_lock_count == 1) { + rw_lock->stats.real_write_locks_granted++; + if (rw_lock->rec_write_lock_count > rw_lock->stats.max_write_locks) + rw_lock->stats.max_write_locks = rw_lock->rec_write_lock_count; + } + + if (rw_lock->rec_write_lock_count > rw_lock->stats.max_write_lock_recursion_depth) + rw_lock->stats.max_write_lock_recursion_depth = rw_lock->rec_write_lock_count; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_wr_lock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_wr_lock_delay + * + * Purpose: Update stats for delay in acquiring a write lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_wr_lock_delay(H5TS_rw_lock_t *rw_lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + + rw_lock->stats.write_locks_delayed++; + + if (rw_lock->stats.max_write_locks_pending <= rw_lock->waiting_writers_count) + rw_lock->stats.max_write_locks_pending = rw_lock->waiting_writers_count + 1; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_wr_lock_delay() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_wr_unlock + * + * Purpose: Update stats for releasing a write lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_wr_unlock(H5TS_rw_lock_t *rw_lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + assert(H5TS_RW_LOCK_WRITE == rw_lock->lock_type); + assert(rw_lock->rec_write_lock_count >= 0); + + rw_lock->stats.write_locks_released++; + + if (rw_lock->rec_write_lock_count == 0) + rw_lock->stats.real_write_locks_released++; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_wr_unlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_lock_get_stats + * + * Purpose: Obtain a copy of the current statistics for a recursive + * read / write lock. + * + * Note: To obtain a consistent set of statistics, the function must + * obtain the lock mutex. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_lock_get_stats(H5TS_rw_lock_t *rw_lock, H5TS_rw_lock_stats_t *stats) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock || NULL == stats)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Copy R/W lock stats */ + *stats = rw_lock->stats; + +done: + if (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_lock_get_stats() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_lock_reset_stats + * + * Purpose: Reset the statistics for the supplied recursive read / write + * lock. + * + * Note: To obtain a consistent set of statistics, the function must + * obtain the lock mutex. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_lock_reset_stats(H5TS_rw_lock_t *rw_lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Reset stats */ + memset(&rw_lock->stats, 0, sizeof(rw_lock->stats)); + +done: + if (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_lock_reset_stats() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_lock_print_stats + * + * Purpose: Print the supplied pthresds recursive R/W lock statistics. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_lock_print_stats(const char *header_str, H5TS_rw_lock_stats_t *stats) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == header_str || NULL == stats)) + HGOTO_DONE(FAIL); + + fprintf(stdout, "\n\n%s\n\n", header_str); + fprintf(stdout, " read_locks_granted = %" PRId64 "\n", stats->read_locks_granted); + fprintf(stdout, " read_locks_released = %" PRId64 "\n", stats->read_locks_released); + fprintf(stdout, " real_read_locks_granted = %" PRId64 "\n", stats->real_read_locks_granted); + fprintf(stdout, " real_read_locks_released = %" PRId64 "\n", stats->real_read_locks_released); + fprintf(stdout, " max_read_locks = %" PRId64 "\n", stats->max_read_locks); + fprintf(stdout, " max_read_lock_recursion_depth = %" PRId64 "\n", stats->max_read_lock_recursion_depth); + fprintf(stdout, " read_locks_delayed = %" PRId64 "\n", stats->read_locks_delayed); + fprintf(stdout, " write_locks_granted = %" PRId64 "\n", stats->write_locks_granted); + fprintf(stdout, " write_locks_released = %" PRId64 "\n", stats->write_locks_released); + fprintf(stdout, " real_write_locks_granted = %" PRId64 "\n", stats->real_write_locks_granted); + fprintf(stdout, " real_write_locks_released = %" PRId64 "\n", stats->real_write_locks_released); + fprintf(stdout, " max_write_locks = %" PRId64 "\n", stats->max_write_locks); + fprintf(stdout, " max_write_lock_recursion_depth = %" PRId64 "\n", + stats->max_write_lock_recursion_depth); + fprintf(stdout, " write_locks_delayed = %" PRId64 "\n", stats->write_locks_delayed); + fprintf(stdout, " max_write_locks_pending = %" PRId64 "\n\n", stats->max_write_locks_pending); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_lock_print_stats() */ +#endif /* H5TS_ENABLE_REC_RW_LOCK_STATS */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_lock_init + * + * Purpose: Initialize the supplied instance of H5TS_rw_lock_t. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_lock_init(H5TS_rw_lock_t *rw_lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + +#ifdef H5_HAVE_WIN_THREADS + /* The current H5TS_rw_lock_t implementation uses H5TS_key_create() with a + * key destructor callback , which is not [currently] supported by Windows. + */ + HGOTO_DONE(FAIL); +#else + /* Initialize the lock */ + memcpy(rw_lock, &H5TS_rw_lock_def, sizeof(H5TS_rw_lock_def)); +#endif + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_lock_init() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_lock_destroy + * + * Purpose: Take down an instance of H5TS_rw_lock_t. All mutex, condition + * variables, and keys are destroyed. However, the instance of + * H5TS_rw_lock_t is not freed. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_lock_destroy(H5TS_rw_lock_t *rw_lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + + /* Call the appropriate destroy routines. We are committed + * to the destroy at this point, so call them all, even if one fails + * along the way. + */ + if (H5_UNLIKELY(H5TS_mutex_destroy(&rw_lock->mutex))) + ret_value = FAIL; + if (H5_UNLIKELY(H5TS_cond_destroy(&rw_lock->readers_cv))) + ret_value = FAIL; + if (H5_UNLIKELY(H5TS_cond_destroy(&rw_lock->writers_cv))) + ret_value = FAIL; + if (rw_lock->is_key_registered) + if (H5_UNLIKELY(H5TS_key_delete(rw_lock->rec_read_lock_count_key) < 0)) + ret_value = FAIL; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_lock_destroy() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_rdlock + * + * Purpose: Attempt to obtain a read lock on the associated recursive + * read / write lock. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_rdlock(H5TS_rw_lock_t *rw_lock) +{ + H5TS_rec_entry_count_t *count; + H5TS_thread_t my_thread = H5TS_thread_self(); + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Fail if attempting to acquire a read lock on a thread that holds + * a write lock + */ + if (H5_UNLIKELY(H5TS_RW_LOCK_WRITE == rw_lock->lock_type && H5TS_thread_equal(my_thread, rw_lock->write_thread))) + HGOTO_DONE(FAIL); + + /* If there is no thread-specific data for this thread, set it up */ + if (!rw_lock->is_key_registered) { + if (H5_UNLIKELY(H5TS_key_create(&rw_lock->rec_read_lock_count_key, H5TS__key_destructor))) + HGOTO_DONE(FAIL); + rw_lock->is_key_registered = true; + count = NULL; + } + else if (H5_UNLIKELY(H5TS_key_get_value(rw_lock->rec_read_lock_count_key, (void **)&count) < 0)) + HGOTO_DONE(FAIL); + if (NULL == count) { + if (H5_UNLIKELY(NULL == (count = calloc(1, sizeof(*count))))) + HGOTO_DONE(FAIL); + + if (H5_UNLIKELY(H5TS_key_set_value(rw_lock->rec_read_lock_count_key, (void *)count))) + HGOTO_DONE(FAIL); + } + + if (count->rec_lock_count > 0) { /* This is a recursive lock */ + assert(H5TS_RW_LOCK_READ == rw_lock->lock_type); + assert(rw_lock->reader_thread_count > 0 && rw_lock->rec_write_lock_count == 0); + } + else { /* This is an initial read lock request, on this thread */ + /* Readers defer to current or pending writers */ + if (H5TS_RW_LOCK_WRITE == rw_lock->lock_type) { +#if H5TS_ENABLE_REC_RW_LOCK_STATS + H5TS__update_stats_rd_lock_delay(rw_lock); +#endif + + do { + if (H5_UNLIKELY(H5TS_cond_wait(&rw_lock->readers_cv, &rw_lock->mutex))) + HGOTO_DONE(FAIL); + } while (H5TS_RW_LOCK_WRITE == rw_lock->lock_type); + } + + /* Set counter's lock type (which might already be set) & increment + * number of reader threads + */ + rw_lock->lock_type = H5TS_RW_LOCK_READ; + rw_lock->reader_thread_count++; + } + + /* Increment read lock count for this thread */ + count->rec_lock_count++; +#if H5TS_ENABLE_REC_RW_LOCK_STATS + H5TS__update_stats_rd_lock(rw_lock, count); +#endif + +done: + if (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_rdlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_wrlock + * + * Purpose: Attempt to obtain a write lock on the associated recursive + * read / write lock. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_wrlock(H5TS_rw_lock_t *rw_lock) +{ + H5TS_thread_t my_thread = H5TS_thread_self(); + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Check for initial write lock request on this thread */ + if (H5TS_RW_LOCK_WRITE != rw_lock->lock_type || !H5TS_thread_equal(my_thread, rw_lock->write_thread)) { + /* Fail if attempting to acquire a write lock on a thread that holds + * a read lock + */ + if (H5TS_RW_LOCK_READ == rw_lock->lock_type) { + H5TS_rec_entry_count_t *count; + + /* Sanity check */ + assert(rw_lock->is_key_registered); + + /* Fail if read lock count for this thread is > 0 */ + if (H5_UNLIKELY(H5TS_key_get_value(rw_lock->rec_read_lock_count_key, (void **)&count) < 0)) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(NULL != count && count->rec_lock_count > 0)) + HGOTO_DONE(FAIL); + } + + /* If lock is already held, wait to acquire it */ + if (H5TS_RW_LOCK_UNUSED != rw_lock->lock_type) { +#if H5TS_ENABLE_REC_RW_LOCK_STATS + H5TS__update_stats_wr_lock_delay(rw_lock); +#endif + + do { + int result; + + rw_lock->waiting_writers_count++; + result = H5TS_cond_wait(&rw_lock->writers_cv, &rw_lock->mutex); + rw_lock->waiting_writers_count--; + if (H5_UNLIKELY(result != 0)) + HGOTO_DONE(FAIL); + } while (H5TS_RW_LOCK_UNUSED != rw_lock->lock_type); + } + + /* Set lock type & owner thread */ + rw_lock->lock_type = H5TS_RW_LOCK_WRITE; + rw_lock->write_thread = my_thread; + } + + /* Increment write lock count for this thread */ + rw_lock->rec_write_lock_count++; +#if H5TS_ENABLE_REC_RW_LOCK_STATS + H5TS__update_stats_wr_lock(rw_lock); +#endif + +done: + if (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_wrlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_unlock + * + * Purpose: Attempt to unlock either a read or a write lock on a + * recursive read / write lock. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_unlock(H5TS_rw_lock_t *rw_lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Error check */ + if (H5_UNLIKELY(H5TS_RW_LOCK_UNUSED == rw_lock->lock_type)) /* Unlocking an unused lock? */ + HGOTO_DONE(FAIL); + + if (H5TS_RW_LOCK_WRITE == rw_lock->lock_type) { /* Drop a write lock */ + /* Sanity checks */ + assert(0 == rw_lock->reader_thread_count); + assert(rw_lock->rec_write_lock_count > 0); + + /* Decrement recursive lock count */ + rw_lock->rec_write_lock_count--; +#if H5TS_ENABLE_REC_RW_LOCK_STATS + H5TS__update_stats_wr_unlock(rw_lock); +#endif + + /* Check if lock is unused now */ + if (0 == rw_lock->rec_write_lock_count) + rw_lock->lock_type = H5TS_RW_LOCK_UNUSED; + } + else { /* Drop a read lock */ + H5TS_rec_entry_count_t *count; + + /* Sanity and error checks */ + assert(rw_lock->is_key_registered); + assert(rw_lock->reader_thread_count > 0); + assert(0 == rw_lock->rec_write_lock_count); + if (H5_UNLIKELY(H5TS_key_get_value(rw_lock->rec_read_lock_count_key, (void **)&count) < 0)) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(NULL == count)) + HGOTO_DONE(FAIL); + assert(count->rec_lock_count > 0); + + /* Decrement recursive lock count for this thread */ + count->rec_lock_count--; +#if H5TS_ENABLE_REC_RW_LOCK_STATS + H5TS__update_stats_rd_unlock(rw_lock, count); +#endif + + /* Check if this thread is releasing its last read lock */ + if (0 == count->rec_lock_count) { + /* Decrement the # of threads with a read lock */ + rw_lock->reader_thread_count--; + + /* Check if lock is unused now */ + if (0 == rw_lock->reader_thread_count) + rw_lock->lock_type = H5TS_RW_LOCK_UNUSED; + } + } + + /* Signal condition variable if lock is unused now */ + if (H5TS_RW_LOCK_UNUSED == rw_lock->lock_type) { + /* Prioritize pending writers if there are any */ + if (rw_lock->waiting_writers_count > 0) { + if (H5_UNLIKELY(H5TS_cond_signal(&rw_lock->writers_cv))) + HGOTO_DONE(FAIL); + } + else { + if (H5_UNLIKELY(H5TS_cond_broadcast(&rw_lock->readers_cv))) + HGOTO_DONE(FAIL); + } + } + +done: + if (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_unlock() */ + #endif /* H5_HAVE_THREADSAFE */