diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2fadc0e039a..47fc1dcf751 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -650,6 +650,7 @@ set (H5TS_SOURCES ${HDF5_SRC_DIR}/H5TSonce.c ${HDF5_SRC_DIR}/H5TSpool.c ${HDF5_SRC_DIR}/H5TSpthread.c + ${HDF5_SRC_DIR}/H5TSrec_rwlock.c ${HDF5_SRC_DIR}/H5TSrwlock.c ${HDF5_SRC_DIR}/H5TSsemaphore.c ${HDF5_SRC_DIR}/H5TSthread.c diff --git a/src/H5TSatomic.c b/src/H5TSatomic.c index 1e3798cd026..e765a5f491a 100644 --- a/src/H5TSatomic.c +++ b/src/H5TSatomic.c @@ -77,8 +77,7 @@ H5TS_atomic_init_int(H5TS_atomic_int_t *obj, int desired) { FUNC_ENTER_NOAPI_NAMECHECK_ONLY - /* Initialize mutex that protects the "atomic" value */ - (void) + /* Initialize mutex that protects the "atomic" value */ H5TS_mutex_init(&obj->mutex, H5TS_MUTEX_TYPE_PLAIN); /* Set the value */ @@ -104,8 +103,7 @@ H5TS_atomic_destroy_int(H5TS_atomic_int_t *obj) { FUNC_ENTER_NOAPI_NAMECHECK_ONLY - /* Destroy mutex that protects the "atomic" value */ - (void) + /* Destroy mutex that protects the "atomic" value */ H5TS_mutex_destroy(&obj->mutex); FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY @@ -128,8 +126,7 @@ H5TS_atomic_init_uint(H5TS_atomic_uint_t *obj, unsigned desired) { FUNC_ENTER_NOAPI_NAMECHECK_ONLY - /* Initialize mutex that protects the "atomic" value */ - (void) + /* Initialize mutex that protects the "atomic" value */ H5TS_mutex_init(&obj->mutex, H5TS_MUTEX_TYPE_PLAIN); /* Set the value */ @@ -155,13 +152,61 @@ H5TS_atomic_destroy_uint(H5TS_atomic_uint_t *obj) { FUNC_ENTER_NOAPI_NAMECHECK_ONLY - /* Destroy mutex that protects the "atomic" value */ - (void) + /* Destroy mutex that protects the "atomic" value */ H5TS_mutex_destroy(&obj->mutex); FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY } /* end H5TS_atomic_destroy_uint() */ +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_init_voidp + * + * Purpose: Initializes an atomic 'void *' variable object with a value. + * + * Note: Per the C11 standard, this function is not atomic and + * concurrent execution from multiple threads is a data race. + * + * Return: None + * + *-------------------------------------------------------------------------- + */ +void +H5TS_atomic_init_voidp(H5TS_atomic_voidp_t *obj, void *desired) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Initialize mutex that protects the "atomic" value */ + H5TS_mutex_init(&obj->mutex, H5TS_MUTEX_TYPE_PLAIN); + + /* Set the value */ + obj->value = desired; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS_atomic_init_voidp() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_destroy_voidp + * + * Purpose: Destroys / releases resources for an atomic 'void *' variable + * + * Note: No equivalent in the C11 atomics, but needed here, to destroy + * the mutex used to protect the atomic value. + * + * Return: None + * + *-------------------------------------------------------------------------- + */ +void +H5TS_atomic_destroy_voidp(H5TS_atomic_voidp_t *obj) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Destroy mutex that protects the "atomic" value */ + H5TS_mutex_destroy(&obj->mutex); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS_atomic_destroy_voidp() */ + #endif /* H5_HAVE_STDATOMIC_H */ #endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSatomic.h b/src/H5TSatomic.h index dc6e5e4ca0e..478760e8c4d 100644 --- a/src/H5TSatomic.h +++ b/src/H5TSatomic.h @@ -277,4 +277,71 @@ H5TS_atomic_fetch_sub_uint(H5TS_atomic_uint_t *obj, unsigned arg) return ret_value; } /* end H5TS_atomic_fetch_sub_uint() */ -#endif /* H5_HAVE_THREADS */ +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_exchange_voidp + * + * Purpose: Atomically replaces the value of an atomic 'void *' variable + * and returns the value held previously. + * + * Return: Returns the value of the atomic variable held previously + * + *-------------------------------------------------------------------------- + */ +static inline void * +H5TS_atomic_exchange_voidp(H5TS_atomic_voidp_t *obj, void *desired) +{ + void *ret_value; + + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Get the current value */ + ret_value = obj->value; + + /* Set the value */ + obj->value = desired; + + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return ret_value; +} /* end H5TS_atomic_exchange_voidp() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_compare_exchange_strong_voidp + * + * Purpose: Atomically compares the contents of 'obj' with 'expected', and + * if those are bitwise equal, replaces the former with 'desired' + * (performs read-modify-write operation). Otherwise, loads the + * actual contents of 'obj' into '*expected' (performs load + * operation). + * + * Return: The result of the comparison: true if 'obj' was equal to + * 'expected', false otherwise. + * + *-------------------------------------------------------------------------- + */ +static inline bool +H5TS_atomic_compare_exchange_strong_voidp(H5TS_atomic_voidp_t *obj, void **expected, void *desired) +{ + bool ret_value; + + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Compare 'obj' w/'expected' */ + if (obj->value == *expected) { + obj->value = desired; + ret_value = true; + } + else { + *expected = obj->value; + ret_value = false; + } + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return ret_value; +} /* end H5TS_atomic_compare_exchange_strong_voidp() */ + +#endif /* H5_HAVE_STDATOMIC_H */ diff --git a/src/H5TSpkg.h b/src/H5TSpkg.h index c2ff919f2f3..b90f4110d6b 100644 --- a/src/H5TSpkg.h +++ b/src/H5TSpkg.h @@ -34,9 +34,9 @@ /* Enable statistics for recursive R/W lock when H5TS debugging is enabled */ #ifdef H5TS_DEBUG -#define H5TS_ENABLE_REC_RW_LOCK_STATS 1 +#define H5TS_ENABLE_REC_RWLOCK_STATS 1 #else -#define H5TS_ENABLE_REC_RW_LOCK_STATS 0 +#define H5TS_ENABLE_REC_RWLOCK_STATS 0 #endif /****************************/ @@ -57,10 +57,10 @@ typedef struct H5TS_api_info_t { } H5TS_api_info_t; #endif /* H5_HAVE_THREADSAFE */ -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /****************************************************************************** * - * Structure H5TS_rw_lock_stats_t + * Structure H5TS_rec_rwlock_stats_t * * Statistics for the recursive R/W lock. * @@ -118,7 +118,7 @@ typedef struct H5TS_api_info_t { * ******************************************************************************/ -typedef struct H5TS_rw_lock_stats_t { +typedef struct H5TS_rec_rwlock_stats_t { int64_t read_locks_granted; int64_t read_locks_released; int64_t real_read_locks_granted; @@ -134,36 +134,35 @@ typedef struct H5TS_rw_lock_stats_t { int64_t max_write_lock_recursion_depth; int64_t write_locks_delayed; int64_t max_write_locks_pending; -} H5TS_rw_lock_stats_t; +} H5TS_rec_rwlock_stats_t; #endif /****************************************************************************** * - * Structure H5TS_rw_lock_t + * Structure H5TS_rec_rwlock_t * * 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 - * collection fields. + * This structure holds the fields needed to implement a recursive R/W lock that + * allows recursive write locks, and the associated statistics collection fields. * * Note that we can't use the pthreads or Win32 R/W locks: they permit * recursive read locks, but disallow recursive write locks. * * Individual fields are: * - * mutex: Mutex used to maintain mutual exclusion on the fields of - * of this structure. + * mutex: Mutex used to maintain mutual exclusion on the fields of this + * structure. * * lock_type: Whether the lock is unused, a reader, or a writer. * * writers_cv: Condition variable used for waiting writers. * - * write_thread: The thread that owns a write lock, which is recursive - * for that thread. + * write_thread: The thread that owns a write lock, which is recursive for + * that thread. * - * rec_write_lock_count: The # of recursive write locks outstanding - * for the thread that owns the write lock. + * rec_write_lock_count: The # of recursive write locks outstanding for the + * thread that owns the write lock. * * waiting_writers_count: The count of waiting writers. * @@ -171,28 +170,27 @@ typedef struct H5TS_rw_lock_stats_t { * * 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. + * rec_read_lock_count_key: Instance of thread-local key used to maintain a + * 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 + * is_key_registered: Flag to track if the 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. + * stats: Instance of H5TS_rec_rwlock_stats_t used to track statistics + * on the lock. * ******************************************************************************/ typedef enum { - 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; + H5TS_REC_RWLOCK_UNUSED = 0, /* Lock is currently unused */ + H5TS_REC_RWLOCK_WRITE, /* Lock is a recursive write lock */ + H5TS_REC_RWLOCK_READ /* Lock is a recursive read lock */ +} H5TS_rec_rwlock_type_t; -typedef struct H5TS_rw_lock_t { +typedef struct H5TS_rec_rwlock_t { /* General fields */ - H5TS_mutex_t mutex; - H5TS_rw_lock_type_t lock_type; + H5TS_mutex_t mutex; + H5TS_rec_rwlock_type_t lock_type; /* Writer fields */ H5TS_cond_t writers_cv; @@ -206,11 +204,11 @@ typedef struct H5TS_rw_lock_t { H5TS_key_t rec_read_lock_count_key; bool is_key_registered; -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* Stats */ - H5TS_rw_lock_stats_t stats; + H5TS_rec_rwlock_stats_t stats; #endif -} H5TS_rw_lock_t; +} H5TS_rec_rwlock_t; /*****************************/ /* Package Private Variables */ @@ -237,11 +235,12 @@ H5_DLL herr_t H5TS__tinfo_term(void); #endif /* H5_HAVE_THREADSAFE */ /* Recursive R/W lock related function declarations */ -H5_DLL herr_t H5TS__rw_lock_init(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_destroy(H5TS_rw_lock_t *rw_lock); +H5_DLL herr_t H5TS__rec_rwlock_init(H5TS_rec_rwlock_t *lock); +H5_DLL herr_t H5TS__rec_rwlock_rdlock(H5TS_rec_rwlock_t *lock); +H5_DLL herr_t H5TS__rec_rwlock_wrlock(H5TS_rec_rwlock_t *lock); +H5_DLL herr_t H5TS__rec_rwlock_rdunlock(H5TS_rec_rwlock_t *lock); +H5_DLL herr_t H5TS__rec_rwlock_wrunlock(H5TS_rec_rwlock_t *lock); +H5_DLL herr_t H5TS__rec_rwlock_destroy(H5TS_rec_rwlock_t *lock); /* 'once' callbacks */ #ifdef H5_HAVE_THREADSAFE @@ -256,10 +255,10 @@ H5_DLL void H5TS__pthread_first_thread_init(void); #endif #endif /* H5_HAVE_THREADSAFE */ -#if H5TS_ENABLE_REC_RW_LOCK_STATS -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); +#if H5TS_ENABLE_REC_RWLOCK_STATS +H5_DLL herr_t H5TS__rec_rwlock_get_stats(H5TS_rec_rwlock_t *lock, H5TS_rec_rwlock_stats_t *stats); +H5_DLL herr_t H5TS__rec_rwlock_reset_stats(H5TS_rec_rwlock_t *lock); +H5_DLL herr_t H5TS__rec_rwlock_print_stats(const char *header_str, H5TS_rec_rwlock_stats_t *stats); #endif #endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSprivate.h b/src/H5TSprivate.h index ccba5b8c390..d2be12414d6 100644 --- a/src/H5TSprivate.h +++ b/src/H5TSprivate.h @@ -99,7 +99,44 @@ #define H5TS_atomic_fetch_add_uint(obj, arg) atomic_fetch_add((obj), (arg)) #define H5TS_atomic_fetch_sub_uint(obj, arg) atomic_fetch_sub((obj), (arg)) #define H5TS_atomic_destroy_uint(obj) /* void */ -#endif /* H5_HAVE_STDATOMIC_H */ + +/* atomic_voidp */ +#define H5TS_atomic_init_voidp(obj, desired) atomic_init((obj), (desired)) +#define H5TS_atomic_exchange_voidp(obj, desired) atomic_exchange((obj), (desired)) +#define H5TS_atomic_compare_exchange_strong_voidp(obj, expected, desired) \ + atomic_compare_exchange_strong((obj), (expected), (desired)) +#define H5TS_atomic_destroy_voidp(obj) /* void */ +#endif /* H5_HAVE_STDATOMIC_H */ + +#if defined(H5_HAVE_STDATOMIC_H) +/* Spinlock operations, built from C11 atomics. Generally follows the example + * here: http://en.cppreference.com/w/cpp/atomic/atomic_flag with some memory + * order improvements. + * + * Note: Pass a pointer to a H5TS_spinlock_t to all the spinlock macros. + * + */ + +/* Initialize the lock */ +#define H5TS_SPINLOCK_INIT(lock) \ + do { \ + *(lock) = ATOMIC_FLAG_INIT; \ + } while (0) + +/* Acquire the lock */ +#define H5TS_SPINLOCK_LOCK(lock) \ + do { \ + while (atomic_flag_test_and_set_explicit(lock, memory_order_acquire)) \ + ; \ + } while (0) + +/* Release the lock */ +#define H5TS_SPINLOCK_UNLOCK(lock) \ + do { \ + atomic_flag_clear_explicit(lock, memory_order_release); \ + } while (0) + +#endif /****************************/ /* Library Private Typedefs */ @@ -113,6 +150,14 @@ typedef struct H5TS_pool_t H5TS_pool_t; /* Portability aliases */ #ifdef H5_HAVE_C11_THREADS + +/* Non-recursive readers/writer lock */ +typedef struct H5TS_rwlock_t { + mtx_t mutex; + cnd_t read_cv, write_cv; + unsigned readers, writers, read_waiters, write_waiters; +} H5TS_rwlock_t; + typedef thrd_t H5TS_thread_t; typedef int (*H5TS_thread_start_func_t)(void *); typedef int H5TS_thread_ret_t; @@ -128,17 +173,19 @@ typedef LPTHREAD_START_ROUTINE H5TS_thread_start_func_t; typedef DWORD H5TS_thread_ret_t; typedef DWORD H5TS_key_t; typedef CRITICAL_SECTION H5TS_CAPABILITY("mutex") H5TS_mutex_t; +typedef SRWLOCK H5TS_rwlock_t; typedef CONDITION_VARIABLE H5TS_cond_t; typedef INIT_ONCE H5TS_once_t; typedef PINIT_ONCE_FN H5TS_once_init_func_t; #else typedef pthread_t H5TS_thread_t; typedef void *(*H5TS_thread_start_func_t)(void *); -typedef void *H5TS_thread_ret_t; -typedef pthread_key_t H5TS_key_t; -typedef pthread_mutex_t H5TS_CAPABILITY("mutex") H5TS_mutex_t; -typedef pthread_cond_t H5TS_cond_t; -typedef pthread_once_t H5TS_once_t; +typedef void *H5TS_thread_ret_t; +typedef pthread_key_t H5TS_key_t; +typedef pthread_mutex_t H5TS_CAPABILITY("mutex") H5TS_mutex_t; +typedef pthread_rwlock_t H5TS_rwlock_t; +typedef pthread_cond_t H5TS_cond_t; +typedef pthread_once_t H5TS_once_t; typedef void (*H5TS_once_init_func_t)(void); #endif #endif @@ -147,6 +194,12 @@ typedef void (*H5TS_once_init_func_t)(void); #if defined(H5_HAVE_STDATOMIC_H) && !defined(__cplusplus) typedef atomic_int H5TS_atomic_int_t; typedef atomic_uint H5TS_atomic_uint_t; +/* Suppress warning about _Atomic keyword not supported in C99 */ +H5_GCC_DIAG_OFF("c99-c11-compat") +H5_CLANG_DIAG_OFF("c11-extensions") +typedef void *_Atomic H5TS_atomic_voidp_t; +H5_GCC_DIAG_ON("c99-c11-compat") +H5_CLANG_DIAG_ON("c11-extensions") #else typedef struct { H5TS_mutex_t mutex; @@ -156,6 +209,10 @@ typedef struct { H5TS_mutex_t mutex; unsigned value; } H5TS_atomic_uint_t; +typedef struct { + H5TS_mutex_t mutex; + void *value; +} H5TS_atomic_voidp_t; #endif /* Thread Barrier */ @@ -203,6 +260,11 @@ typedef struct H5TS_semaphore_t { } H5TS_semaphore_t; #endif +#if defined(H5_HAVE_STDATOMIC_H) && !defined(__cplusplus) +/* Spinlock, built from C11 atomic_flag */ +typedef atomic_flag H5TS_spinlock_t; +#endif + /*****************************/ /* Library-private Variables */ /*****************************/ @@ -234,6 +296,15 @@ H5_DLL herr_t H5TS_mutex_init(H5TS_mutex_t *mutex, int type); H5_DLL herr_t H5TS_mutex_trylock(H5TS_mutex_t *mutex, bool *acquired) H5TS_TRY_ACQUIRE(SUCCEED, *mutex); H5_DLL herr_t H5TS_mutex_destroy(H5TS_mutex_t *mutex); +/* R/W locks */ +H5_DLL herr_t H5TS_rwlock_init(H5TS_rwlock_t *lock); +/* R/W lock & unlock calls are defined in H5TSrwlock.h */ +static inline herr_t H5TS_rwlock_rdlock(H5TS_rwlock_t *lock); +static inline herr_t H5TS_rwlock_rdunlock(H5TS_rwlock_t *lock); +static inline herr_t H5TS_rwlock_wrlock(H5TS_rwlock_t *lock); +static inline herr_t H5TS_rwlock_wrunlock(H5TS_rwlock_t *lock); +H5_DLL herr_t H5TS_rwlock_destroy(H5TS_rwlock_t *lock); + /* Condition variable operations */ H5_DLL herr_t H5TS_cond_init(H5TS_cond_t *cond); /* Condition variable wait, signal, broadcast calls are defined in H5TScond.h */ @@ -275,6 +346,14 @@ static inline void H5TS_atomic_store_uint(H5TS_atomic_uint_t *obj, unsigned static inline unsigned H5TS_atomic_fetch_add_uint(H5TS_atomic_uint_t *obj, unsigned arg); static inline unsigned H5TS_atomic_fetch_sub_uint(H5TS_atomic_uint_t *obj, unsigned arg); H5_DLL void H5TS_atomic_destroy_uint(H5TS_atomic_uint_t *obj); + +/* void * _Atomic (atomic void pointer) */ +H5_DLL void H5TS_atomic_init_voidp(H5TS_atomic_voidp_t *obj, void *desired); +/* Atomic 'void *' load, store, etc. calls are defined in H5TSatomic.h */ +static inline void *H5TS_atomic_exchange_voidp(H5TS_atomic_voidp_t *obj, void *desired); +static inline bool H5TS_atomic_compare_exchange_strong_voidp(H5TS_atomic_voidp_t *obj, void **expected, + void *desired); +H5_DLL void H5TS_atomic_destroy_voidp(H5TS_atomic_voidp_t *obj); #endif /* H5_HAVE_STDATOMIC_H */ /* Barrier related function declarations */ @@ -297,6 +376,7 @@ H5_DLL herr_t H5TS_semaphore_destroy(H5TS_semaphore_t *sem); #include "H5TSatomic.h" #endif /* H5_HAVE_STDATOMIC_H */ #include "H5TSbarrier.h" +#include "H5TSrwlock.h" #include "H5TSsemaphore.h" #include "H5TSpool.h" diff --git a/src/H5TSrec_rwlock.c b/src/H5TSrec_rwlock.c new file mode 100644 index 00000000000..e191aa3a290 --- /dev/null +++ b/src/H5TSrec_rwlock.c @@ -0,0 +1,745 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://www.hdfgroup.org/licenses. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Purpose: This file contains support for recursive R/W locks, equivalent to + * the pthread 'pthread_rwlock_t' type and capabilities, except that + * threads that hold write access for the lock are allowed to acquire + * write access again (and must match each lock with an unlock operation). + * + * Note: Because this threadsafety framework operates outside the library, + * it does not use the error stack (although it does use error macros + * that don't push errors on a stack) and only uses the "namecheck only" + * FUNC_ENTER_* / FUNC_LEAVE_* macros. + */ + +/****************/ +/* Module Setup */ +/****************/ + +#include "H5TSmodule.h" /* This source code file is part of the H5TS module */ + +/***********/ +/* Headers */ +/***********/ +#include "H5private.h" /* Generic Functions */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5TSpkg.h" /* Threadsafety */ + +#ifdef H5_HAVE_THREADS + +/****************/ +/* Local Macros */ +/****************/ + +/******************/ +/* Local Typedefs */ +/******************/ + +/* + * Count of the number of active [recursive] read lock calls for a given thread. + * The # of readers for the lock in question is decremented when the recursive + * read lock count drops to zero. + */ +typedef int64_t H5TS_rec_entry_count_t; + +/********************/ +/* Local Prototypes */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +#if H5TS_ENABLE_REC_RWLOCK_STATS +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_rdlock + * + * Purpose: Update stats for acquiring a read lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_rdlock(H5TS_rec_rwlock_t *lock, const H5TS_rec_entry_count_t *count) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(lock); + assert(H5TS_REC_RWLOCK_READ == lock->lock_type); + assert(count); + assert(*count >= 1); + + lock->stats.read_locks_granted++; + + if (*count == 1) { + lock->stats.real_read_locks_granted++; + if (lock->reader_thread_count > lock->stats.max_read_locks) + lock->stats.max_read_locks = lock->reader_thread_count; + } + + if (*count > lock->stats.max_read_lock_recursion_depth) + lock->stats.max_read_lock_recursion_depth = *count; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_rdlock() */ + +/*-------------------------------------------------------------------------- + * 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_rec_rwlock_t *lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(lock); + + 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_rec_rwlock_t *lock, const H5TS_rec_entry_count_t *count) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(lock); + assert(H5TS_REC_RWLOCK_READ == lock->lock_type); + assert(count); + assert(*count >= 0); + + lock->stats.read_locks_released++; + + if (*count == 0) + 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_rec_rwlock_t *lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(lock); + assert(H5TS_REC_RWLOCK_WRITE == lock->lock_type); + assert(lock->rec_write_lock_count >= 1); + + lock->stats.write_locks_granted++; + + if (lock->rec_write_lock_count == 1) { + lock->stats.real_write_locks_granted++; + if (lock->rec_write_lock_count > lock->stats.max_write_locks) + lock->stats.max_write_locks = lock->rec_write_lock_count; + } + + if (lock->rec_write_lock_count > lock->stats.max_write_lock_recursion_depth) + lock->stats.max_write_lock_recursion_depth = 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_rec_rwlock_t *lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(lock); + + lock->stats.write_locks_delayed++; + + if (lock->stats.max_write_locks_pending <= lock->waiting_writers_count) + lock->stats.max_write_locks_pending = 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_rec_rwlock_t *lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(lock); + assert(H5TS_REC_RWLOCK_WRITE == lock->lock_type); + assert(lock->rec_write_lock_count >= 0); + + lock->stats.write_locks_released++; + + if (lock->rec_write_lock_count == 0) + lock->stats.real_write_locks_released++; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_wr_unlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rec_rwlock_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__rec_rwlock_get_stats(H5TS_rec_rwlock_t *lock, H5TS_rec_rwlock_stats_t *stats) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == lock || NULL == stats)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Copy R/W lock stats */ + *stats = lock->stats; + +done: + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex) < 0)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rec_rwlock_get_stats() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rec_rwlock_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__rec_rwlock_reset_stats(H5TS_rec_rwlock_t *lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex) < 0)) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Reset stats */ + memset(&lock->stats, 0, sizeof(lock->stats)); + +done: + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex) < 0)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rec_rwlock_reset_stats() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rec_rwlock_print_stats + * + * Purpose: Print statistics for the supplied recursive R/W lock. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rec_rwlock_print_stats(const char *header_str, H5TS_rec_rwlock_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__rec_rwlock_print_stats() */ +#endif /* H5TS_ENABLE_REC_RWLOCK_STATS */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rec_rwlock_init + * + * Purpose: Initialize the supplied instance of H5TS_rec_rwlock_t. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rec_rwlock_init(H5TS_rec_rwlock_t *lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == lock)) + HGOTO_DONE(FAIL); + +#ifdef H5_HAVE_WIN_THREADS + /* The current H5TS_rec_rwlock_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 */ + memset(lock, 0, sizeof(*lock)); + HDcompile_assert(H5TS_REC_RWLOCK_UNUSED == 0); + if (H5_UNLIKELY(H5TS_mutex_init(&lock->mutex, H5TS_MUTEX_TYPE_PLAIN) < 0)) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(H5TS_cond_init(&lock->writers_cv) < 0)) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(H5TS_cond_init(&lock->readers_cv) < 0)) + HGOTO_DONE(FAIL); +#endif + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rec_rwlock_init() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rec_rwlock_destroy + * + * Purpose: Take down an instance of H5TS_rec_rwlock_t. All mutex, condition + * variables, and keys are destroyed. However, the instance of + * H5TS_rec_rwlock_t is not freed. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rec_rwlock_destroy(H5TS_rec_rwlock_t *lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == 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(&lock->mutex) < 0)) + ret_value = FAIL; + if (H5_UNLIKELY(H5TS_cond_destroy(&lock->readers_cv) < 0)) + ret_value = FAIL; + if (H5_UNLIKELY(H5TS_cond_destroy(&lock->writers_cv) < 0)) + ret_value = FAIL; + if (lock->is_key_registered) + if (H5_UNLIKELY(H5TS_key_delete(lock->rec_read_lock_count_key) < 0)) + ret_value = FAIL; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rec_rwlock_destroy() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rec_rwlock_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__rec_rwlock_rdlock(H5TS_rec_rwlock_t *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 == lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex) < 0)) + 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_REC_RWLOCK_WRITE == lock->lock_type && + H5TS_thread_equal(my_thread, lock->write_thread))) + HGOTO_DONE(FAIL); + + /* If there is no thread-specific data for this thread, set it up */ + if (!lock->is_key_registered) { + if (H5_UNLIKELY(H5TS_key_create(&lock->rec_read_lock_count_key, free) < 0)) + HGOTO_DONE(FAIL); + lock->is_key_registered = true; + count = NULL; + } + else if (H5_UNLIKELY(H5TS_key_get_value(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(lock->rec_read_lock_count_key, (void *)count) < 0)) + HGOTO_DONE(FAIL); + } + + if (*count > 0) { /* This is a recursive lock */ + assert(H5TS_REC_RWLOCK_READ == lock->lock_type); + assert(lock->reader_thread_count > 0 && 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_REC_RWLOCK_WRITE == lock->lock_type) { +#if H5TS_ENABLE_REC_RWLOCK_STATS + H5TS__update_stats_rd_lock_delay(lock); +#endif + + do { + if (H5_UNLIKELY(H5TS_cond_wait(&lock->readers_cv, &lock->mutex) < 0)) + HGOTO_DONE(FAIL); + } while (H5TS_REC_RWLOCK_WRITE == lock->lock_type); + } + + /* Set counter's lock type (which might already be set) & increment + * number of reader threads + */ + lock->lock_type = H5TS_REC_RWLOCK_READ; + lock->reader_thread_count++; + } + + /* Increment read lock count for this thread */ + (*count)++; +#if H5TS_ENABLE_REC_RWLOCK_STATS + H5TS__update_stats_rdlock(lock, count); +#endif + +done: + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex) < 0)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rec_rwlock_rdlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rec_rwlock_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__rec_rwlock_wrlock(H5TS_rec_rwlock_t *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 == lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex) < 0)) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Check for initial write lock request on this thread */ + if (H5TS_REC_RWLOCK_WRITE != lock->lock_type || !H5TS_thread_equal(my_thread, lock->write_thread)) { + /* Fail if attempting to acquire a write lock on a thread that holds + * a read lock + */ + if (H5TS_REC_RWLOCK_READ == lock->lock_type) { + H5TS_rec_entry_count_t *count; + + /* Sanity check */ + assert(lock->is_key_registered); + + /* Fail if read lock count for this thread is > 0 */ + if (H5_UNLIKELY(H5TS_key_get_value(lock->rec_read_lock_count_key, (void **)&count) < 0)) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(NULL != count && *count > 0)) + HGOTO_DONE(FAIL); + } + + /* If lock is already held, wait to acquire it */ + if (H5TS_REC_RWLOCK_UNUSED != lock->lock_type) { +#if H5TS_ENABLE_REC_RWLOCK_STATS + H5TS__update_stats_wr_lock_delay(lock); +#endif + + do { + int result; + + lock->waiting_writers_count++; + result = H5TS_cond_wait(&lock->writers_cv, &lock->mutex); + lock->waiting_writers_count--; + if (H5_UNLIKELY(result != 0)) + HGOTO_DONE(FAIL); + } while (H5TS_REC_RWLOCK_UNUSED != lock->lock_type); + } + + /* Set lock type & owner thread */ + lock->lock_type = H5TS_REC_RWLOCK_WRITE; + lock->write_thread = my_thread; + } + + /* Increment write lock count for this thread */ + lock->rec_write_lock_count++; +#if H5TS_ENABLE_REC_RWLOCK_STATS + H5TS__update_stats_wr_lock(lock); +#endif + +done: + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex) < 0)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rec_rwlock_wrlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rec_rwlock_rdunlock + * + * Purpose: Attempt to unlock a read lock. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rec_rwlock_rdunlock(H5TS_rec_rwlock_t *lock) +{ + H5TS_rec_entry_count_t *count; + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex) < 0)) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Error check */ + if (H5_UNLIKELY(H5TS_REC_RWLOCK_READ != lock->lock_type)) + HGOTO_DONE(FAIL); + + /* Sanity and error checks */ + assert(lock->is_key_registered); + assert(lock->reader_thread_count > 0); + assert(0 == lock->rec_write_lock_count); + if (H5_UNLIKELY(H5TS_key_get_value(lock->rec_read_lock_count_key, (void **)&count) < 0)) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(NULL == count)) + HGOTO_DONE(FAIL); + assert(*count > 0); + + /* Decrement recursive lock count for this thread */ + (*count)--; +#if H5TS_ENABLE_REC_RWLOCK_STATS + H5TS__update_stats_rd_unlock(lock, count); +#endif + + /* Check if this thread is releasing its last read lock */ + if (0 == *count) { + /* Decrement the # of threads with a read lock */ + lock->reader_thread_count--; + + /* Check if lock is unused now */ + if (0 == lock->reader_thread_count) { + lock->lock_type = H5TS_REC_RWLOCK_UNUSED; + + /* Indicate that lock is unused now */ + /* Prioritize pending writers if there are any */ + if (lock->waiting_writers_count > 0) { + if (H5_UNLIKELY(H5TS_cond_signal(&lock->writers_cv) < 0)) + HGOTO_DONE(FAIL); + } + else { + if (H5_UNLIKELY(H5TS_cond_broadcast(&lock->readers_cv) < 0)) + HGOTO_DONE(FAIL); + } + } + } + +done: + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex) < 0)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rec_rwlock_rdunlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rec_rwlock_wrunlock + * + * Purpose: Attempt to unlock a write lock + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rec_rwlock_wrunlock(H5TS_rec_rwlock_t *lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex) < 0)) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Error check */ + if (H5_UNLIKELY(H5TS_REC_RWLOCK_WRITE != lock->lock_type)) + HGOTO_DONE(FAIL); + + /* Sanity checks */ + assert(0 == lock->reader_thread_count); + assert(lock->rec_write_lock_count > 0); + + /* Decrement recursive lock count */ + lock->rec_write_lock_count--; +#if H5TS_ENABLE_REC_RWLOCK_STATS + H5TS__update_stats_wr_unlock(lock); +#endif + + /* Check if lock is unused now */ + if (0 == lock->rec_write_lock_count) { + lock->lock_type = H5TS_REC_RWLOCK_UNUSED; + + /* Indicate that lock is unused now */ + /* Prioritize pending writers if there are any */ + if (lock->waiting_writers_count > 0) { + if (H5_UNLIKELY(H5TS_cond_signal(&lock->writers_cv) < 0)) + HGOTO_DONE(FAIL); + } + else { + if (H5_UNLIKELY(H5TS_cond_broadcast(&lock->readers_cv) < 0)) + HGOTO_DONE(FAIL); + } + } + +done: + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex) < 0)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rec_rwlock_wrunlock() */ + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSrwlock.c b/src/H5TSrwlock.c index 43bac524c17..d8f772318a5 100644 --- a/src/H5TSrwlock.c +++ b/src/H5TSrwlock.c @@ -11,10 +11,8 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* - * Purpose: This file contains support for recursive R/W locks, equivalent to - * the pthread 'pthread_rwlock_t' type and capabilities, except that - * threads that hold write access for the lock are allowed to acquire - * write access again (and must match each lock with an unlock operation). + * Purpose: This file contains support for non-recursive R/W locks, equivalent + * to the pthread 'pthread_rwlock_t' type and capabilities. * * Note: Because this threadsafety framework operates outside the library, * it does not use the error stack (although it does use error macros @@ -45,28 +43,6 @@ /* Local Typedefs */ /******************/ -/****************************************************************************** - * - * Structure H5TS_rec_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 decremented when the thread-specific count drops to zero. - * - * Individual fields are: - * - * rec_lock_count: Count of the number of active [recursive] read lock calls - * for a given thread. The # of readers for the lock in question - * is decremented when the recursive read lock count drops to zero. - * - ******************************************************************************/ - -typedef struct H5TS_rec_entry_count { - int64_t rec_lock_count; -} H5TS_rec_entry_count_t; - /********************/ /* Local Prototypes */ /********************/ @@ -83,635 +59,183 @@ typedef struct H5TS_rec_entry_count { /* Local Variables */ /*******************/ -#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 +#ifdef H5_HAVE_C11_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_init * - * Purpose: Update stats for releasing a read lock + * Purpose: Initialize a H5TS_rwlock_t (does not allocate it) * - * Return: none + * Return: Non-negative on success / Negative on failure * - *-------------------------------------------------------------------------- - */ -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) +H5TS_rwlock_init(H5TS_rwlock_t *lock) { - bool have_mutex = false; - herr_t ret_value = SUCCEED; + herr_t ret_value = SUCCEED; - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + FUNC_ENTER_NOAPI_NAMECHECK_ONLY - if (H5_UNLIKELY(NULL == rw_lock || NULL == stats)) + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) HGOTO_DONE(FAIL); - /* Acquire the mutex */ - if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) + /* Initialize synchronization primitives */ + if (H5_UNLIKELY(mtx_init(&lock->mutex, mtx_plain) != thrd_success)) HGOTO_DONE(FAIL); - have_mutex = true; - - /* Copy R/W lock stats */ - *stats = rw_lock->stats; - -done: - if (H5_LIKELY(have_mutex)) - if (H5_UNLIKELY(H5TS_mutex_unlock(&rw_lock->mutex) < 0)) - ret_value = FAIL; - - 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)) + if (H5_UNLIKELY(cnd_init(&lock->read_cv) != thrd_success)) HGOTO_DONE(FAIL); - - /* Acquire the mutex */ - if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex) < 0)) + if (H5_UNLIKELY(cnd_init(&lock->write_cv) != thrd_success)) HGOTO_DONE(FAIL); - have_mutex = true; - /* Reset stats */ - memset(&rw_lock->stats, 0, sizeof(rw_lock->stats)); + /* Initialize scalar fields */ + lock->readers = 0; + lock->writers = 0; + lock->read_waiters = 0; + lock->write_waiters = 0; done: - if (H5_LIKELY(have_mutex)) - if (H5_UNLIKELY(H5TS_mutex_unlock(&rw_lock->mutex) < 0)) - ret_value = FAIL; - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_lock_reset_stats() */ +} /* end H5TS_rwlock_init() */ -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_lock_print_stats +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_destroy * - * Purpose: Print the supplied pthresds recursive R/W lock statistics. + * Purpose: Destroy a H5TS_rwlock_t (does not free it) * - * Return: Non-negative on success / Negative on failure + * 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) +H5TS_rwlock_destroy(H5TS_rwlock_t *lock) { herr_t ret_value = SUCCEED; - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + FUNC_ENTER_NOAPI_NAMECHECK_ONLY - if (H5_UNLIKELY(NULL == header_str || NULL == stats)) + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) 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); + /* Destroy synchronization primitives */ + /* NOTE: mtx_destroy() & cnd_destroy() can't fail */ + mtx_destroy(&lock->mutex); + cnd_destroy(&lock->read_cv); + cnd_destroy(&lock->write_cv); 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 +} /* end H5TS_rwlock_destroy() */ +#else +#ifdef H5_HAVE_WIN_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_init * - * Purpose: Initialize the supplied instance of H5TS_rw_lock_t. + * Purpose: Initialize a H5TS_rwlock_t (does not allocate it) * - * Return: Non-negative on success / Negative on failure + * Return: Non-negative on success / Negative on failure * - *-------------------------------------------------------------------------- + *------------------------------------------------------------------------- */ herr_t -H5TS__rw_lock_init(H5TS_rw_lock_t *rw_lock) +H5TS_rwlock_init(H5TS_rwlock_t *lock) { herr_t ret_value = SUCCEED; - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + FUNC_ENTER_NOAPI_NAMECHECK_ONLY - if (H5_UNLIKELY(NULL == rw_lock)) + /* Check argument */ + if (H5_UNLIKELY(NULL == 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 */ - memset(rw_lock, 0, sizeof(*rw_lock)); - HDcompile_assert(H5TS_RW_LOCK_UNUSED == 0); - if (H5_UNLIKELY(H5TS_mutex_init(&rw_lock->mutex, H5TS_MUTEX_TYPE_PLAIN) < 0)) - HGOTO_DONE(FAIL); - if (H5_UNLIKELY(H5TS_cond_init(&rw_lock->writers_cv) < 0)) - HGOTO_DONE(FAIL); - if (H5_UNLIKELY(H5TS_cond_init(&rw_lock->readers_cv) < 0)) - HGOTO_DONE(FAIL); -#endif + InitializeSRWLock(lock); done: FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_lock_init() */ +} /* end H5TS_rwlock_init() */ -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_lock_destroy +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_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. + * Purpose: Destroy a H5TS_rwlock_t (does not free it) * - * Return: Non-negative on success / Negative on failure + * Return: Non-negative on success / Negative on failure * - *-------------------------------------------------------------------------- + *------------------------------------------------------------------------- */ herr_t -H5TS__rw_lock_destroy(H5TS_rw_lock_t *rw_lock) +H5TS_rwlock_destroy(H5TS_rwlock_t *lock) { herr_t ret_value = SUCCEED; - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + FUNC_ENTER_NOAPI_NAMECHECK_ONLY - if (H5_UNLIKELY(NULL == rw_lock)) + /* Check argument */ + if (H5_UNLIKELY(NULL == 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) < 0)) - ret_value = FAIL; - if (H5_UNLIKELY(H5TS_cond_destroy(&rw_lock->readers_cv) < 0)) - ret_value = FAIL; - if (H5_UNLIKELY(H5TS_cond_destroy(&rw_lock->writers_cv) < 0)) - 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; + /* Destroy synchronization primitives */ + /* SRWLOCKs don't have to be destroyed */ done: FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_lock_destroy() */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_rdlock +} /* end H5TS_rwlock_destroy() */ +#else +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_init * - * Purpose: Attempt to obtain a read lock on the associated recursive - * read / write lock. + * Purpose: Initialize a H5TS_rwlock_t (does not allocate it) * - * Return: Non-negative on success / Negative on failure + * Return: Non-negative on success / Negative on failure * - *-------------------------------------------------------------------------- + *------------------------------------------------------------------------- */ herr_t -H5TS__rw_rdlock(H5TS_rw_lock_t *rw_lock) +H5TS_rwlock_init(H5TS_rwlock_t *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); + herr_t ret_value = SUCCEED; - /* Acquire the mutex */ - if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex) < 0)) - HGOTO_DONE(FAIL); - have_mutex = true; + FUNC_ENTER_NOAPI_NAMECHECK_ONLY - /* 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))) + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) 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, free) < 0)) - 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)) + if (H5_UNLIKELY(pthread_rwlock_init(lock, NULL))) 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) < 0)) - 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) < 0)) - 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 (H5_LIKELY(have_mutex)) - if (H5_UNLIKELY(H5TS_mutex_unlock(&rw_lock->mutex) < 0)) - ret_value = FAIL; - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_rdlock() */ +} /* end H5TS_rwlock_init() */ -/*-------------------------------------------------------------------------- - * Function: H5TS__rw_wrlock +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_destroy * - * Purpose: Attempt to obtain a write lock on the associated recursive - * read / write lock. + * Purpose: Destroy a H5TS_rwlock_t (does not free it) * - * Return: Non-negative on success / Negative on failure + * Return: Non-negative on success / Negative on failure * - *-------------------------------------------------------------------------- + *------------------------------------------------------------------------- */ herr_t -H5TS__rw_wrlock(H5TS_rw_lock_t *rw_lock) +H5TS_rwlock_destroy(H5TS_rwlock_t *lock) { - H5TS_thread_t my_thread = H5TS_thread_self(); - bool have_mutex = false; - herr_t ret_value = SUCCEED; + herr_t ret_value = SUCCEED; FUNC_ENTER_NOAPI_NAMECHECK_ONLY - if (H5_UNLIKELY(NULL == rw_lock)) + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) HGOTO_DONE(FAIL); - /* Acquire the mutex */ - if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex) < 0)) + if (H5_UNLIKELY(pthread_rwlock_destroy(lock))) 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 (H5_LIKELY(have_mutex)) - if (H5_UNLIKELY(H5TS_mutex_unlock(&rw_lock->mutex) < 0)) - ret_value = FAIL; - 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) < 0)) - 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); +} /* end H5TS_rwlock_destroy() */ #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) < 0)) - HGOTO_DONE(FAIL); - } - else { - if (H5_UNLIKELY(H5TS_cond_broadcast(&rw_lock->readers_cv) < 0)) - HGOTO_DONE(FAIL); - } - } - -done: - if (H5_LIKELY(have_mutex)) - if (H5_UNLIKELY(H5TS_mutex_unlock(&rw_lock->mutex) < 0)) - ret_value = FAIL; - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__rw_unlock() */ - #endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSrwlock.h b/src/H5TSrwlock.h new file mode 100644 index 00000000000..16c36d46e5e --- /dev/null +++ b/src/H5TSrwlock.h @@ -0,0 +1,405 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://www.hdfgroup.org/licenses. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Purpose: This file contains support for non-recursive R/W locks, equivalent + * to the pthread 'pthread_rwlock_t' type and capabilities. + * + * Note: Because this threadsafety framework operates outside the library, + * it does not use the error stack (although it does use error macros + * that don't push errors on a stack) and only uses the "namecheck only" + * FUNC_ENTER_* / FUNC_LEAVE_* macros. + */ + +/****************/ +/* Module Setup */ +/****************/ + +/***********/ +/* Headers */ +/***********/ + +/****************/ +/* Local Macros */ +/****************/ + +/******************/ +/* Local Typedefs */ +/******************/ + +/********************/ +/* Local Prototypes */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +#ifdef H5_HAVE_C11_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdlock + * + * Purpose: Acquire a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_rdlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + /* Acquire the lock's mutex */ + if (H5_UNLIKELY(mtx_lock(&lock->mutex) != thrd_success)) + return FAIL; + + /* Check for writers */ + if (lock->writers || lock->write_waiters) { + /* Read waiting */ + lock->read_waiters++; + + /* Wait for writers */ + do { + if (H5_UNLIKELY(thrd_success != cnd_wait(&lock->read_cv, &lock->mutex))) { + mtx_unlock(&lock->mutex); + return FAIL; + } + } while (lock->writers || lock->write_waiters); + + /* Read not waiting any longer */ + lock->read_waiters--; + } + + /* Increment # of readers */ + lock->readers++; + + /* Release mutex */ + if (H5_UNLIKELY(mtx_unlock(&lock->mutex) != thrd_success)) + return FAIL; + + return SUCCEED; +} /* end H5TS_rwlock_rdlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdunlock + * + * Purpose: Release a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_rdunlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + /* Acquire the lock's mutex */ + if (H5_UNLIKELY(mtx_lock(&lock->mutex) != thrd_success)) + return FAIL; + + /* Decrement # of readers */ + lock->readers--; + + /* Check for waiting writers when last readers */ + if (lock->write_waiters && 0 == lock->readers) + if (H5_UNLIKELY(cnd_signal(&lock->write_cv) != thrd_success)) { + mtx_unlock(&lock->mutex); + return FAIL; + } + + /* Release mutex */ + if (H5_UNLIKELY(mtx_unlock(&lock->mutex) != thrd_success)) + return FAIL; + + return SUCCEED; +} /* end H5TS_rwlock_rdunlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrlock + * + * Purpose: Acquire a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_wrlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + /* Acquire the lock's mutex */ + if (H5_UNLIKELY(mtx_lock(&lock->mutex) != thrd_success)) + return FAIL; + + /* Check for readers or other writers */ + if (lock->readers || lock->writers) { + /* Write waiting */ + lock->write_waiters++; + + /* Wait for mutex */ + do { + if (H5_UNLIKELY(thrd_success != cnd_wait(&lock->write_cv, &lock->mutex))) { + mtx_unlock(&lock->mutex); + return FAIL; + } + } while (lock->readers || lock->writers); + + /* Write not waiting any longer */ + lock->write_waiters--; + } + + /* Increment # of writers */ + lock->writers++; + + /* Release mutex */ + if (H5_UNLIKELY(mtx_unlock(&lock->mutex) != thrd_success)) + return FAIL; + + return SUCCEED; +} /* end H5TS_rwlock_wrlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrunlock + * + * Purpose: Release a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_wrunlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + /* Acquire the lock's mutex */ + if (H5_UNLIKELY(mtx_lock(&lock->mutex) != thrd_success)) + return FAIL; + + /* Decrement # of writers */ + lock->writers--; + + /* Check for waiting writers */ + if (lock->write_waiters) { + if (H5_UNLIKELY(cnd_signal(&lock->write_cv) != thrd_success)) { + mtx_unlock(&lock->mutex); + return FAIL; + } + } + else if (lock->read_waiters) + if (H5_UNLIKELY(cnd_broadcast(&lock->read_cv) != thrd_success)) { + mtx_unlock(&lock->mutex); + return FAIL; + } + + /* Release mutex */ + if (H5_UNLIKELY(mtx_unlock(&lock->mutex) != thrd_success)) + return FAIL; + + return SUCCEED; +} /* end H5TS_rwlock_wrunlock() */ + +#else +#ifdef H5_HAVE_WIN_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdlock + * + * Purpose: Acquire a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_rdlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + AcquireSRWLockShared(lock); + + return SUCCEED; +} /* end H5TS_rwlock_rdlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdunlock + * + * Purpose: Release a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_rdunlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + ReleaseSRWLockShared(lock); + + return SUCCEED; +} /* end H5TS_rwlock_rdunlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrlock + * + * Purpose: Acquire a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_wrlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + AcquireSRWLockExclusive(lock); + + return SUCCEED; +} /* end H5TS_rwlock_wrlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrunlock + * + * Purpose: Release a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_wrunlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + ReleaseSRWLockExclusive(lock); + + return SUCCEED; +} /* end H5TS_rwlock_wrunlock() */ + +#else +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdlock + * + * Purpose: Acquire a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_rdlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + if (H5_UNLIKELY(pthread_rwlock_rdlock(lock))) + return FAIL; + + return SUCCEED; +} /* end H5TS_rwlock_rdlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdunlock + * + * Purpose: Release a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_rdunlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + if (H5_UNLIKELY(pthread_rwlock_unlock(lock))) + return FAIL; + + return SUCCEED; +} /* end H5TS_rwlock_rdunlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrlock + * + * Purpose: Acquire a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_wrlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + if (H5_UNLIKELY(pthread_rwlock_wrlock(lock))) + return FAIL; + + return SUCCEED; +} /* end H5TS_rwlock_wrlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdunlock + * + * Purpose: Release a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_rwlock_wrunlock(H5TS_rwlock_t *lock) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == lock)) + return FAIL; + + if (H5_UNLIKELY(pthread_rwlock_unlock(lock))) + return FAIL; + + return SUCCEED; +} /* end H5TS_rwlock_wrunlock() */ +#endif +#endif diff --git a/src/Makefile.am b/src/Makefile.am index c887a9aa73a..87b12d08b06 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -99,7 +99,7 @@ libhdf5_la_SOURCES= H5.c H5build_settings.c H5checksum.c H5dbg.c H5system.c \ H5Tvlen.c \ H5TS.c H5TSatomic.c H5TSbarrier.c H5TSc11.c H5TScond.c \ H5TSint.c H5TSkey.c H5TSmutex.c H5TSonce.c H5TSpool.c H5TSpthread.c \ - H5TSrwlock.c H5TSsemaphore.c H5TSthread.c H5TSwin.c \ + H5TSrec_rwlock.c H5TSrwlock.c H5TSsemaphore.c H5TSthread.c H5TSwin.c \ H5VL.c H5VLcallback.c H5VLdyn_ops.c H5VLint.c H5VLnative.c \ H5VLnative_attr.c H5VLnative_blob.c H5VLnative_dataset.c \ H5VLnative_datatype.c H5VLnative_file.c H5VLnative_group.c \ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5a7e483210a..02a9891d512 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -350,7 +350,8 @@ set (ttsafe_SOURCES ${HDF5_TEST_SOURCE_DIR}/ttsafe_dcreate.c ${HDF5_TEST_SOURCE_DIR}/ttsafe_develop.c ${HDF5_TEST_SOURCE_DIR}/ttsafe_error.c - ${HDF5_TEST_SOURCE_DIR}/ttsafe_rec_rw_lock.c + ${HDF5_TEST_SOURCE_DIR}/ttsafe_rwlock.c + ${HDF5_TEST_SOURCE_DIR}/ttsafe_rec_rwlock.c ${HDF5_TEST_SOURCE_DIR}/ttsafe_semaphore.c ${HDF5_TEST_SOURCE_DIR}/ttsafe_thread_id.c ${HDF5_TEST_SOURCE_DIR}/ttsafe_thread_pool.c diff --git a/test/Makefile.am b/test/Makefile.am index 3625ac3dcce..061bfcf0df5 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -162,7 +162,8 @@ LDADD=libh5test.la $(LIBHDF5) # List the source files for tests that have more than one ttsafe_SOURCES=ttsafe.c ttsafe_acreate.c ttsafe_atomic.c ttsafe_attr_vlen.c \ ttsafe_cancel.c ttsafe_dcreate.c ttsafe_develop.c ttsafe_error.c \ - ttsafe_rec_rw_lock.c ttsafe_semaphore.c ttsafe_thread_id.c ttsafe_thread_pool.c + ttsafe_rwlock.c ttsafe_rec_rwlock.c ttsafe_semaphore.c \ + ttsafe_thread_id.c ttsafe_thread_pool.c cache_image_SOURCES=cache_image.c genall5.c mirror_vfd_SOURCES=mirror_vfd.c genall5.c diff --git a/test/ttsafe.c b/test/ttsafe.c index 76dfa22bcbd..2f5f26db331 100644 --- a/test/ttsafe.c +++ b/test/ttsafe.c @@ -127,15 +127,16 @@ main(int argc, char *argv[]) /* C11 atomics only tested when emulated */ AddTest("atomics", tts_atomics, NULL, "emulation of C11 atomics", NULL); #endif /* H5_HAVE_STDATOMIC_H */ + AddTest("rwlock", tts_rwlock, NULL, "simple R/W locks", NULL); #ifndef H5_HAVE_WIN_THREADS /* Recursive R/W locks */ - AddTest("rec_rwlock_1", tts_rec_rw_lock_smoke_check_1, NULL, "recursive R/W lock smoke check 1 -- basic", + AddTest("rec_rwlock_1", tts_rec_rwlock_smoke_check_1, NULL, "recursive R/W lock smoke check 1 -- basic", NULL); - AddTest("rec_rwlock_2", tts_rec_rw_lock_smoke_check_2, NULL, + AddTest("rec_rwlock_2", tts_rec_rwlock_smoke_check_2, NULL, "recursive R/W lock smoke check 2 -- mob of readers", NULL); - AddTest("rec_rwlock_3", tts_rec_rw_lock_smoke_check_3, NULL, + AddTest("rec_rwlock_3", tts_rec_rwlock_smoke_check_3, NULL, "recursive R/W lock smoke check 3 -- mob of writers", NULL); - AddTest("rec_rwlock_4", tts_rec_rw_lock_smoke_check_4, NULL, + AddTest("rec_rwlock_4", tts_rec_rwlock_smoke_check_4, NULL, "recursive R/W lock smoke check 4 -- mixed mob", NULL); #endif /* !H5_HAVE_WIN_THREADS */ AddTest("semaphore", tts_semaphore, NULL, "lightweight system semaphores", NULL); diff --git a/test/ttsafe.h b/test/ttsafe.h index f551e40462a..7544b3ff32d 100644 --- a/test/ttsafe.h +++ b/test/ttsafe.h @@ -38,11 +38,14 @@ void tts_is_threadsafe(void); #ifdef H5_HAVE_THREADS void tts_thread_pool(void); void tts_atomics(void); +void tts_rwlock(void); void tts_semaphore(void); -void tts_rec_rw_lock_smoke_check_1(void); -void tts_rec_rw_lock_smoke_check_2(void); -void tts_rec_rw_lock_smoke_check_3(void); -void tts_rec_rw_lock_smoke_check_4(void); +#ifndef H5_HAVE_WIN_THREADS +void tts_rec_rwlock_smoke_check_1(void); +void tts_rec_rwlock_smoke_check_2(void); +void tts_rec_rwlock_smoke_check_3(void); +void tts_rec_rwlock_smoke_check_4(void); +#endif /* !H5_HAVE_WIN_THREADS */ #ifdef H5_HAVE_THREADSAFE void tts_dcreate(void); void tts_error(void); diff --git a/test/ttsafe_rec_rw_lock.c b/test/ttsafe_rec_rwlock.c similarity index 77% rename from test/ttsafe_rec_rw_lock.c rename to test/ttsafe_rec_rwlock.c index f7230af3d9d..3e17011e11c 100644 --- a/test/ttsafe_rec_rw_lock.c +++ b/test/ttsafe_rec_rwlock.c @@ -40,9 +40,9 @@ */ /*********************************************************************** * - * Structure rec_rw_lock_test_udata_t + * Structure rec_rwlock_test_udata_t * - * Arrays of instances of rec_rw_lock_test_udata_t are used to configure + * Arrays of instances of rec_rwlock_test_udata_t are used to configure * the threads used to test the recursive R/W lock, and to collect * statistics on their behaviour. These statistics are aggregated and * used to cross-check the statistics collected by the recursive R/W @@ -50,7 +50,7 @@ * * The fields of the structure are discussed below: * - * rw_lock: Pointer to the recursive R/W under test. + * lock: Pointer to the recursive R/W lock under test. * * target_rd_lock_cycles: The number of times the test thread is * required to obtain and drop the read lock. Note @@ -73,17 +73,17 @@ * * The remaining fields are used for statistics collection. They are * thread specific versions of the fields of the same name in - * H5TS_rw_lock_stats_t. See the header comment for that + * H5TS_rec_rwlock_stats_t. See the header comment for that * structure (in H5TSprivate.h) for further details. * ***********************************************************************/ -typedef struct rec_rw_lock_test_udata_t { +typedef struct rec_rwlock_test_udata_t { /* thread control fields */ - H5TS_rw_lock_t *rw_lock; - int32_t target_rd_lock_cycles; - int32_t target_wr_lock_cycles; - int32_t max_recursive_lock_depth; + H5TS_rec_rwlock_t *lock; + int32_t target_rd_lock_cycles; + int32_t target_wr_lock_cycles; + int32_t max_recursive_lock_depth; /* thread stats fields */ int64_t read_locks_granted; @@ -95,11 +95,11 @@ typedef struct rec_rw_lock_test_udata_t { int64_t real_write_locks_granted; int64_t real_write_locks_released; -} rec_rw_lock_test_udata_t; +} rec_rwlock_test_udata_t; /* ********************************************************************** - * tts_rw_lock_smoke_check_test_thread + * tts_rec_rwlock_smoke_check_test_thread * * Perform a sequence of recursive read and/or write locks on the * target recursive R/W lock as directed by the supplied user data. @@ -113,22 +113,22 @@ typedef struct rec_rw_lock_test_udata_t { ********************************************************************** */ static H5TS_THREAD_RETURN_TYPE -tts_rw_lock_smoke_check_test_thread(void *_udata) +tts_rec_rwlock_smoke_check_test_thread(void *_udata) { - hbool_t read; - int32_t rec_lock_depth = 0; - int32_t max_rec_lock_depth; - int32_t rd_locks_remaining; - int32_t wr_locks_remaining; - herr_t result; - H5TS_rw_lock_t *rw_lock; - rec_rw_lock_test_udata_t *udata = (rec_rw_lock_test_udata_t *)_udata; + hbool_t read; + int32_t rec_lock_depth = 0; + int32_t max_rec_lock_depth; + int32_t rd_locks_remaining; + int32_t wr_locks_remaining; + herr_t result; + H5TS_rec_rwlock_t *lock; + rec_rwlock_test_udata_t *udata = (rec_rwlock_test_udata_t *)_udata; assert(_udata); rd_locks_remaining = udata->target_rd_lock_cycles; wr_locks_remaining = udata->target_wr_lock_cycles; max_rec_lock_depth = udata->max_recursive_lock_depth; - rw_lock = udata->rw_lock; + lock = udata->lock; while (rd_locks_remaining > 0 || wr_locks_remaining > 0) { if (wr_locks_remaining == 0) @@ -143,8 +143,8 @@ tts_rw_lock_smoke_check_test_thread(void *_udata) } if (read) { - result = H5TS__rw_rdlock(rw_lock); - CHECK_I(result, "H5TS__rw_rdlock"); + result = H5TS__rec_rwlock_rdlock(lock); + CHECK_I(result, "H5TS__rec_rwlock_rdlock"); udata->read_locks_granted++; udata->real_read_locks_granted++; @@ -153,15 +153,15 @@ tts_rw_lock_smoke_check_test_thread(void *_udata) while (rec_lock_depth > 0) { if (rec_lock_depth >= max_rec_lock_depth || (rand() % 2) == 0) { - result = H5TS__rw_unlock(rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_rdunlock(lock); + CHECK_I(result, "H5TS__rec_rwlock_rdunlock"); rec_lock_depth--; udata->read_locks_released++; } else { - result = H5TS__rw_rdlock(rw_lock); - CHECK_I(result, "H5TS__rw_rdlock"); + result = H5TS__rec_rwlock_rdlock(lock); + CHECK_I(result, "H5TS__rec_rwlock_rdlock"); rec_lock_depth++; udata->read_locks_granted++; @@ -171,8 +171,8 @@ tts_rw_lock_smoke_check_test_thread(void *_udata) udata->real_read_locks_released++; } else { - result = H5TS__rw_wrlock(rw_lock); - CHECK_I(result, "H5TS__rw_wrlock"); + result = H5TS__rec_rwlock_wrlock(lock); + CHECK_I(result, "H5TS__rec_rwlock_wrlock"); udata->write_locks_granted++; udata->real_write_locks_granted++; @@ -181,15 +181,15 @@ tts_rw_lock_smoke_check_test_thread(void *_udata) while (rec_lock_depth > 0) { if (rec_lock_depth >= max_rec_lock_depth || (rand() % 2) == 0) { - result = H5TS__rw_unlock(rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_wrunlock(lock); + CHECK_I(result, "H5TS__rec_rwlock_wrunlock"); rec_lock_depth--; udata->write_locks_released++; } else { - result = H5TS__rw_wrlock(rw_lock); - CHECK_I(result, "H5TS__rw_wrlock"); + result = H5TS__rec_rwlock_wrlock(lock); + CHECK_I(result, "H5TS__rec_rwlock_wrlock"); rec_lock_depth++; udata->write_locks_granted++; @@ -201,11 +201,11 @@ tts_rw_lock_smoke_check_test_thread(void *_udata) } return (H5TS_thread_ret_t)0; -} /* end tts_rw_lock_smoke_check_test_thread() */ +} /* end tts_rec_rwlock_smoke_check_test_thread() */ /* ********************************************************************** - * tts_rec_rw_lock_smoke_check_1 + * tts_rec_rwlock_smoke_check_1 * * Single thread test to verify basic functionality and error * rejection of the recursive R/W lock. @@ -263,33 +263,33 @@ tts_rw_lock_smoke_check_test_thread(void *_udata) ********************************************************************** */ void -tts_rec_rw_lock_smoke_check_1(void) +tts_rec_rwlock_smoke_check_1(void) { herr_t result; -#if H5TS_ENABLE_REC_RW_LOCK_STATS - H5TS_rw_lock_stats_t stats; +#if H5TS_ENABLE_REC_RWLOCK_STATS + H5TS_rec_rwlock_stats_t stats; #endif - H5TS_rw_lock_t rec_rw_lock; + H5TS_rec_rwlock_t lock; /* 1) Initialize an instance of the recursive R/W lock. */ - result = H5TS__rw_lock_init(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_init"); + result = H5TS__rec_rwlock_init(&lock); + CHECK_I(result, "H5TS__rec_rwlock_init"); /* 2) Obtain a read lock. */ - result = H5TS__rw_rdlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_rdlock"); + result = H5TS__rec_rwlock_rdlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_rdlock"); /* 3) Drop the read lock. */ - result = H5TS__rw_unlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_rdunlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_rdunlock"); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* 4) Verify the expected stats, and then reset them. */ - result = H5TS__rw_lock_get_stats(&rec_rw_lock, &stats); - CHECK_I(result, "H5TS__rw_lock_get_stats"); + result = H5TS__rec_rwlock_get_stats(&lock, &stats); + CHECK_I(result, "H5TS__rec_rwlock_get_stats"); - result = H5TS__rw_lock_reset_stats(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_reset_stats"); + result = H5TS__rec_rwlock_reset_stats(&lock); + CHECK_I(result, "H5TS__rec_rwlock_reset_stats"); /* clang-format makes this conditional unreadable, so turn it off. */ /* clang-format off */ @@ -310,34 +310,34 @@ tts_rec_rw_lock_smoke_check_1(void) stats.max_write_locks_pending != 0 ) { TestErrPrintf("Unexpected recursive R/W lock stats -- 1"); - H5TS__rw_lock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); } /* clang-format on */ #endif /* 5) Obtain a read lock. */ - result = H5TS__rw_rdlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_rdlock"); + result = H5TS__rec_rwlock_rdlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_rdlock"); /* 6) Obtain the read lock a second time. */ - result = H5TS__rw_rdlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_rdlock"); + result = H5TS__rec_rwlock_rdlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_rdlock"); /* 7) Drop the read lock. */ - result = H5TS__rw_unlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_rdunlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_rdunlock"); /* 8) Drop the read lock a second time. */ - result = H5TS__rw_unlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_rdunlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_rdunlock"); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* 9) Verify the expected stats, and then reset them. */ - result = H5TS__rw_lock_get_stats(&rec_rw_lock, &stats); - CHECK_I(result, "H5TS__rw_lock_get_stats"); + result = H5TS__rec_rwlock_get_stats(&lock, &stats); + CHECK_I(result, "H5TS__rec_rwlock_get_stats"); - result = H5TS__rw_lock_reset_stats(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_reset_stats"); + result = H5TS__rec_rwlock_reset_stats(&lock); + CHECK_I(result, "H5TS__rec_rwlock_reset_stats"); /* clang-format makes this conditional unreadable, so turn it off. */ /* clang-format off */ @@ -358,26 +358,26 @@ tts_rec_rw_lock_smoke_check_1(void) stats.max_write_locks_pending != 0 ) { TestErrPrintf("Unexpected recursive R/W lock stats -- 2"); - H5TS__rw_lock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); } /* clang-format on */ #endif /* 10) Obtain a write lock. */ - result = H5TS__rw_wrlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_wrlock"); + result = H5TS__rec_rwlock_wrlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_wrlock"); /* 11) Drop the write lock. */ - result = H5TS__rw_unlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_wrunlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_wrunlock"); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* 12) Verify the expected stats, and then reset them. */ - result = H5TS__rw_lock_get_stats(&rec_rw_lock, &stats); - CHECK_I(result, "H5TS__rw_lock_get_stats"); + result = H5TS__rec_rwlock_get_stats(&lock, &stats); + CHECK_I(result, "H5TS__rec_rwlock_get_stats"); - result = H5TS__rw_lock_reset_stats(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_reset_stats"); + result = H5TS__rec_rwlock_reset_stats(&lock); + CHECK_I(result, "H5TS__rec_rwlock_reset_stats"); /* clang-format makes this conditional unreadable, so turn it off. */ /* clang-format off */ @@ -398,34 +398,34 @@ tts_rec_rw_lock_smoke_check_1(void) stats.max_write_locks_pending != 0 ) { TestErrPrintf("Unexpected recursive R/W lock stats -- 3"); - H5TS__rw_lock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); } /* clang-format on */ #endif /* 13) Obtain a write lock. */ - result = H5TS__rw_wrlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_wrlock"); + result = H5TS__rec_rwlock_wrlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_wrlock"); /* 14) Obtain the write lock a second time. */ - result = H5TS__rw_wrlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_wrlock"); + result = H5TS__rec_rwlock_wrlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_wrlock"); /* 15) Drop the write lock. */ - result = H5TS__rw_unlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_wrunlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_wrunlock"); /* 16) Drop the write lock a second time. */ - result = H5TS__rw_unlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_wrunlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_wrunlock"); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* 17) Verify the expected stats, and then reset them. */ - result = H5TS__rw_lock_get_stats(&rec_rw_lock, &stats); - CHECK_I(result, "H5TS__rw_lock_get_stats"); + result = H5TS__rec_rwlock_get_stats(&lock, &stats); + CHECK_I(result, "H5TS__rec_rwlock_get_stats"); - result = H5TS__rw_lock_reset_stats(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_reset_stats"); + result = H5TS__rec_rwlock_reset_stats(&lock); + CHECK_I(result, "H5TS__rec_rwlock_reset_stats"); /* clang-format makes this conditional unreadable, so turn it off. */ /* clang-format off */ @@ -446,42 +446,42 @@ tts_rec_rw_lock_smoke_check_1(void) stats.max_write_locks_pending != 0 ) { TestErrPrintf("Unexpected recursive R/W lock stats -- 4"); - H5TS__rw_lock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); } /* clang-format on */ #endif /* 18) Obtain a write lock. */ - result = H5TS__rw_wrlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_wrlock"); + result = H5TS__rec_rwlock_wrlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_wrlock"); /* 19) Attempt to obtain a read lock -- should fail. */ - result = H5TS__rw_rdlock(&rec_rw_lock); - VERIFY(result, FAIL, "H5TS__rw_rdlock"); + result = H5TS__rec_rwlock_rdlock(&lock); + VERIFY(result, FAIL, "H5TS__rec_rwlock_rdlock"); /* 20) Drop the write lock. */ - result = H5TS__rw_unlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_wrunlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_wrunlock"); /* 21) Obtain a read lock. */ - result = H5TS__rw_rdlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_rdlock"); + result = H5TS__rec_rwlock_rdlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_rdlock"); /* 22) Attempt to obtain a write lock -- should fail. */ - result = H5TS__rw_wrlock(&rec_rw_lock); - VERIFY(result, FAIL, "H5TS__rw_wrlock"); + result = H5TS__rec_rwlock_wrlock(&lock); + VERIFY(result, FAIL, "H5TS__rec_rwlock_wrlock"); /* 23) Drop the read lock. */ - result = H5TS__rw_unlock(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_unlock"); + result = H5TS__rec_rwlock_rdunlock(&lock); + CHECK_I(result, "H5TS__rec_rwlock_rdunlock"); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* 24) Verify the expected stats, and then reset them. */ - result = H5TS__rw_lock_get_stats(&rec_rw_lock, &stats); - CHECK_I(result, "H5TS__rw_lock_get_stats"); + result = H5TS__rec_rwlock_get_stats(&lock, &stats); + CHECK_I(result, "H5TS__rec_rwlock_get_stats"); - result = H5TS__rw_lock_reset_stats(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_reset_stats"); + result = H5TS__rec_rwlock_reset_stats(&lock); + CHECK_I(result, "H5TS__rec_rwlock_reset_stats"); /* clang-format makes this conditional unreadable, so turn it off. */ /* clang-format off */ @@ -502,19 +502,19 @@ tts_rec_rw_lock_smoke_check_1(void) stats.max_write_locks_pending != 0 ) { TestErrPrintf("Unexpected recursive R/W lock stats"); - H5TS__rw_lock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); } /* clang-format on */ #endif /* 25) Shut down the recursive R/W lock. */ - result = H5TS__rw_lock_destroy(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_destroy"); -} /* end tts_rec_rw_lock_smoke_check_1() */ + result = H5TS__rec_rwlock_destroy(&lock); + CHECK_I(result, "H5TS__rec_rwlock_destroy"); +} /* end tts_rec_rwlock_smoke_check_1() */ /* ********************************************************************** - * tts_rec_rw_lock_smoke_check_2 -- mob of readers + * tts_rec_rwlock_smoke_check_2 -- mob of readers * * Multi-threaded test to check management of multiple readers ONLY by * the recursive R/W lock. Test proceeds as follows: @@ -525,7 +525,7 @@ tts_rec_rw_lock_smoke_check_1(void) * * 3) Create the reader threads, each with its own user data. * Activities of the reader threads is discussed in the header - * comment to tts_rw_lock_smoke_check_test_thread(). + * comment to tts_rec_rwlock_smoke_check_test_thread(). * * 4) Wait for all threads to complete. * @@ -546,25 +546,25 @@ tts_rec_rw_lock_smoke_check_1(void) ********************************************************************** */ void -tts_rec_rw_lock_smoke_check_2(void) +tts_rec_rwlock_smoke_check_2(void) { - herr_t result; - int express_test; - int i; - int num_threads = MAX_NUM_THREADS; - int lock_cycles = MAX_LOCK_CYCLES; - H5TS_thread_t threads[MAX_NUM_THREADS]; - rec_rw_lock_test_udata_t *udata = NULL; -#if H5TS_ENABLE_REC_RW_LOCK_STATS - hbool_t verbose = FALSE; - int32_t total_target_rd_lock_cycles = 0; - int32_t total_target_wr_lock_cycles = 0; - H5TS_rw_lock_stats_t stats; - H5TS_rw_lock_stats_t expected; + herr_t result; + int express_test; + int i; + int num_threads = MAX_NUM_THREADS; + int lock_cycles = MAX_LOCK_CYCLES; + H5TS_thread_t threads[MAX_NUM_THREADS]; + rec_rwlock_test_udata_t *udata = NULL; +#if H5TS_ENABLE_REC_RWLOCK_STATS + hbool_t verbose = FALSE; + int32_t total_target_rd_lock_cycles = 0; + int32_t total_target_wr_lock_cycles = 0; + H5TS_rec_rwlock_stats_t stats; + H5TS_rec_rwlock_stats_t expected; #endif - H5TS_rw_lock_t rec_rw_lock; + H5TS_rec_rwlock_t lock; -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* Reset expected stats fields to zero -- we will construct the expected * stats from the thread udata after completion. */ @@ -596,30 +596,30 @@ tts_rec_rw_lock_smoke_check_2(void) } /* 1) Initialize an instance of the recursive R/W lock. */ - result = H5TS__rw_lock_init(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_init"); + result = H5TS__rec_rwlock_init(&lock); + CHECK_I(result, "H5TS__rec_rwlock_init"); /* 2) Setup the user data to be passed to each reader test thread. */ for (i = 0; i < MAX_NUM_THREADS; i++) { memset(&udata[i], 0, sizeof(udata[i])); - udata[i].rw_lock = &rec_rw_lock; + udata[i].lock = &lock; udata[i].target_rd_lock_cycles = lock_cycles; udata[i].max_recursive_lock_depth = 10; } -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS uint64_t start_time = H5_now_usec(); #endif /* 3) Create the reader threads, each with its own user data. */ for (i = 0; i < num_threads; i++) - if (H5TS_thread_create(&threads[i], tts_rw_lock_smoke_check_test_thread, &udata[i]) < 0) + if (H5TS_thread_create(&threads[i], tts_rec_rwlock_smoke_check_test_thread, &udata[i]) < 0) TestErrPrintf("thread # %d did not start", i); /* 4) Wait for all threads to complete. */ for (i = 0; i < num_threads; i++) if (H5TS_thread_join(threads[i], NULL) < 0) TestErrPrintf("thread %d failed to join", i); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS uint64_t end_time = H5_now_usec(); uint64_t elap_time = (unsigned long long)(end_time - start_time); if (verbose) @@ -643,7 +643,7 @@ tts_rec_rw_lock_smoke_check_2(void) assert(udata[i].target_wr_lock_cycles == udata[i].real_write_locks_granted); assert(udata[i].target_wr_lock_cycles == udata[i].real_write_locks_released); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS total_target_rd_lock_cycles += udata[i].target_rd_lock_cycles; total_target_wr_lock_cycles += udata[i].target_wr_lock_cycles; @@ -658,7 +658,7 @@ tts_rec_rw_lock_smoke_check_2(void) #endif } -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* Verify that the threads executed the expected number of read and write * lock cycles. If they didn't, some thread probably encountered an error * and exited early. @@ -677,8 +677,8 @@ tts_rec_rw_lock_smoke_check_2(void) * with the data gathered above. */ - result = H5TS__rw_lock_get_stats(&rec_rw_lock, &stats); - CHECK_I(result, "H5TS__rw_lock_get_stats"); + result = H5TS__rec_rwlock_get_stats(&lock, &stats); + CHECK_I(result, "H5TS__rec_rwlock_get_stats"); /* turn off clang-format for readability */ /* clang-format off */ @@ -700,27 +700,27 @@ tts_rec_rw_lock_smoke_check_2(void) stats.write_locks_delayed != expected.write_locks_delayed || stats.max_write_locks_pending != expected.max_write_locks_pending) { TestErrPrintf("Unexpected recursive R/W lock stats"); - H5TS__rw_lock_print_stats("Actual stats", &stats); - H5TS__rw_lock_print_stats("Expected stats", &expected); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Expected stats", &expected); } /* clang-format on */ if (verbose) - H5TS__rw_lock_print_stats("mob of readers stats", &stats); + H5TS__rec_rwlock_print_stats("mob of readers stats", &stats); #endif /* 7) Shut down the recursive R/W lock. */ - result = H5TS__rw_lock_destroy(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_destroy"); + result = H5TS__rec_rwlock_destroy(&lock); + CHECK_I(result, "H5TS__rec_rwlock_destroy"); /* discard the udata if it exists */ if (udata) free(udata); -} /* end tts_rec_rw_lock_smoke_check_2() */ +} /* end tts_rec_rwlock_smoke_check_2() */ /* ********************************************************************** - * tts_rec_rw_lock_smoke_check_3 -- mob of writers + * tts_rec_rwlock_smoke_check_3 -- mob of writers * * Multi-thread test to check management of multiple writers ONLY by * the recursive R/W lock. Test proceeds as follows: @@ -731,7 +731,7 @@ tts_rec_rw_lock_smoke_check_2(void) * * 3) Create the writer threads, each with its own user data. * Activities of the writer threads is discussed in the header - * comment to tts_rw_lock_smoke_check_test_thread(). + * comment to tts_rec_rwlock_smoke_check_test_thread(). * * 4) Wait for all threads to complete. * @@ -752,25 +752,25 @@ tts_rec_rw_lock_smoke_check_2(void) ********************************************************************** */ void -tts_rec_rw_lock_smoke_check_3(void) +tts_rec_rwlock_smoke_check_3(void) { - herr_t result; - int i; - int express_test; - int num_threads = MAX_NUM_THREADS; - int lock_cycles = MAX_LOCK_CYCLES; - H5TS_thread_t threads[MAX_NUM_THREADS]; - rec_rw_lock_test_udata_t *udata = NULL; -#if H5TS_ENABLE_REC_RW_LOCK_STATS - hbool_t verbose = FALSE; - int32_t total_target_rd_lock_cycles = 0; - int32_t total_target_wr_lock_cycles = 0; - H5TS_rw_lock_stats_t stats; - H5TS_rw_lock_stats_t expected; + herr_t result; + int i; + int express_test; + int num_threads = MAX_NUM_THREADS; + int lock_cycles = MAX_LOCK_CYCLES; + H5TS_thread_t threads[MAX_NUM_THREADS]; + rec_rwlock_test_udata_t *udata = NULL; +#if H5TS_ENABLE_REC_RWLOCK_STATS + hbool_t verbose = FALSE; + int32_t total_target_rd_lock_cycles = 0; + int32_t total_target_wr_lock_cycles = 0; + H5TS_rec_rwlock_stats_t stats; + H5TS_rec_rwlock_stats_t expected; #endif - H5TS_rw_lock_t rec_rw_lock; + H5TS_rec_rwlock_t lock; -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* Reset expected stats fields to zero -- we will construct the expected * stats from the thread udata after completion. */ @@ -802,30 +802,30 @@ tts_rec_rw_lock_smoke_check_3(void) } /* 1) Initialize an instance of the recursive R/W lock. */ - result = H5TS__rw_lock_init(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_init"); + result = H5TS__rec_rwlock_init(&lock); + CHECK_I(result, "H5TS__rec_rwlock_init"); /* 2) Setup the user data to be passed to each writer test thread. */ for (i = 0; i < MAX_NUM_THREADS; i++) { memset(&udata[i], 0, sizeof(udata[i])); - udata[i].rw_lock = &rec_rw_lock; + udata[i].lock = &lock; udata[i].target_wr_lock_cycles = lock_cycles; udata[i].max_recursive_lock_depth = 10; } -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS uint64_t start_time = H5_now_usec(); #endif /* 3) Create the writer threads, each with its own user data. */ for (i = 0; i < num_threads; i++) - if (H5TS_thread_create(&threads[i], tts_rw_lock_smoke_check_test_thread, &udata[i]) < 0) + if (H5TS_thread_create(&threads[i], tts_rec_rwlock_smoke_check_test_thread, &udata[i]) < 0) TestErrPrintf("thread # %d did not start", i); /* 4) Wait for all threads to complete. */ for (i = 0; i < num_threads; i++) if (H5TS_thread_join(threads[i], NULL) < 0) TestErrPrintf("thread %d failed to join", i); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS uint64_t end_time = H5_now_usec(); uint64_t elap_time = (unsigned long long)(end_time - start_time); if (verbose) @@ -849,7 +849,7 @@ tts_rec_rw_lock_smoke_check_3(void) assert(udata[i].target_wr_lock_cycles == udata[i].real_write_locks_granted); assert(udata[i].target_wr_lock_cycles == udata[i].real_write_locks_released); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS total_target_rd_lock_cycles += udata[i].target_rd_lock_cycles; total_target_wr_lock_cycles += udata[i].target_wr_lock_cycles; @@ -864,7 +864,7 @@ tts_rec_rw_lock_smoke_check_3(void) #endif } -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* Verify that the threads executed the expected number of read and write * lock cycles. If they didn't, some thread probably encountered an error * and exited early. @@ -883,8 +883,8 @@ tts_rec_rw_lock_smoke_check_3(void) /* 6) Obtain the stats from the recursive R/W lock, and compare * with the data gathered above. */ - result = H5TS__rw_lock_get_stats(&rec_rw_lock, &stats); - CHECK_I(result, "H5TS__rw_lock_get_stats"); + result = H5TS__rec_rwlock_get_stats(&lock, &stats); + CHECK_I(result, "H5TS__rec_rwlock_get_stats"); /* turn off clang-format for readability */ /* clang-format off */ @@ -905,27 +905,27 @@ tts_rec_rw_lock_smoke_check_3(void) stats.write_locks_delayed < expected.write_locks_delayed || stats.max_write_locks_pending > expected.max_write_locks_pending) { TestErrPrintf("Unexpected recursive R/W lock stats"); - H5TS__rw_lock_print_stats("Actual stats", &stats); - H5TS__rw_lock_print_stats("Expected stats", &expected); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Expected stats", &expected); } /* clang-format on */ if (verbose) - H5TS__rw_lock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); #endif /* 7) Shut down the recursive R/W lock. */ - result = H5TS__rw_lock_destroy(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_destroy"); + result = H5TS__rec_rwlock_destroy(&lock); + CHECK_I(result, "H5TS__rec_rwlock_destroy"); /* discard the udata if it exists */ if (udata) free(udata); -} /* end tts_rec_rw_lock_smoke_check_3() */ +} /* end tts_rec_rwlock_smoke_check_3() */ /* ********************************************************************** - * tts_rec_rw_lock_smoke_check_4 -- mixed mob + * tts_rec_rwlock_smoke_check_4 -- mixed mob * * Multi-thread test to check management of multiple readers and * writers by the recursive R/W lock. Test proceeds as follows: @@ -936,7 +936,7 @@ tts_rec_rw_lock_smoke_check_3(void) * * 3) Create the reader / writer threads, each with its own user data. * Activities of the reader / writer threads is discussed in the - * header comment to tts_rw_lock_smoke_check_test_thread(). + * header comment to tts_rec_rwlock_smoke_check_test_thread(). * * 4) Wait for all threads to complete. * @@ -958,25 +958,25 @@ tts_rec_rw_lock_smoke_check_3(void) ********************************************************************** */ void -tts_rec_rw_lock_smoke_check_4(void) +tts_rec_rwlock_smoke_check_4(void) { - herr_t result; - int i; - int express_test; - int num_threads = MAX_NUM_THREADS; - int lock_cycles = MAX_LOCK_CYCLES; - H5TS_thread_t threads[MAX_NUM_THREADS]; - rec_rw_lock_test_udata_t *udata = NULL; -#if H5TS_ENABLE_REC_RW_LOCK_STATS - hbool_t verbose = FALSE; - int32_t total_target_rd_lock_cycles = 0; - int32_t total_target_wr_lock_cycles = 0; - H5TS_rw_lock_stats_t stats; - H5TS_rw_lock_stats_t expected; + herr_t result; + int i; + int express_test; + int num_threads = MAX_NUM_THREADS; + int lock_cycles = MAX_LOCK_CYCLES; + H5TS_thread_t threads[MAX_NUM_THREADS]; + rec_rwlock_test_udata_t *udata = NULL; +#if H5TS_ENABLE_REC_RWLOCK_STATS + hbool_t verbose = FALSE; + int32_t total_target_rd_lock_cycles = 0; + int32_t total_target_wr_lock_cycles = 0; + H5TS_rec_rwlock_stats_t stats; + H5TS_rec_rwlock_stats_t expected; #endif - H5TS_rw_lock_t rec_rw_lock; + H5TS_rec_rwlock_t lock; -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* Reset expected stats fields to zero -- we will construct the expected * stats from the thread udata after completion. */ @@ -1008,31 +1008,31 @@ tts_rec_rw_lock_smoke_check_4(void) } /* 1) Initialize an instance of the recursive R/W lock. */ - result = H5TS__rw_lock_init(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_init"); + result = H5TS__rec_rwlock_init(&lock); + CHECK_I(result, "H5TS__rec_rwlock_init"); /* 2) Setup the user data to be passed to each writer test thread. */ for (i = 0; i < MAX_NUM_THREADS; i++) { memset(&udata[i], 0, sizeof(udata[i])); - udata[i].rw_lock = &rec_rw_lock; + udata[i].lock = &lock; udata[i].target_rd_lock_cycles = lock_cycles; udata[i].target_wr_lock_cycles = lock_cycles; udata[i].max_recursive_lock_depth = 10; } -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS uint64_t start_time = H5_now_usec(); #endif /* 3) Create the reader threads, each with its own user data. */ for (i = 0; i < num_threads; i++) - if (H5TS_thread_create(&threads[i], tts_rw_lock_smoke_check_test_thread, &udata[i]) < 0) + if (H5TS_thread_create(&threads[i], tts_rec_rwlock_smoke_check_test_thread, &udata[i]) < 0) TestErrPrintf("thread # %d did not start", i); /* 4) Wait for all threads to complete. */ for (i = 0; i < num_threads; i++) if (H5TS_thread_join(threads[i], NULL) < 0) TestErrPrintf("thread %d failed to join", i); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS uint64_t end_time = H5_now_usec(); uint64_t elap_time = (unsigned long long)(end_time - start_time); if (verbose) @@ -1056,7 +1056,7 @@ tts_rec_rw_lock_smoke_check_4(void) assert(udata[i].target_wr_lock_cycles == udata[i].real_write_locks_granted); assert(udata[i].target_wr_lock_cycles == udata[i].real_write_locks_released); -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS total_target_rd_lock_cycles += udata[i].target_rd_lock_cycles; total_target_wr_lock_cycles += udata[i].target_wr_lock_cycles; @@ -1071,7 +1071,7 @@ tts_rec_rw_lock_smoke_check_4(void) #endif } -#if H5TS_ENABLE_REC_RW_LOCK_STATS +#if H5TS_ENABLE_REC_RWLOCK_STATS /* Verify that the threads executed the expected number of read and write * lock cycles. If they didn't, some thread probably encountered an error * and exited early. @@ -1092,8 +1092,8 @@ tts_rec_rw_lock_smoke_check_4(void) /* 6) Obtain the stats from the recursive R/W lock, and compare * with the data gathered above. */ - result = H5TS__rw_lock_get_stats(&rec_rw_lock, &stats); - CHECK_I(result, "H5TS__rw_lock_get_stats"); + result = H5TS__rec_rwlock_get_stats(&lock, &stats); + CHECK_I(result, "H5TS__rec_rwlock_get_stats"); /* turn off clang-format for readability */ /* clang-format off */ @@ -1115,23 +1115,23 @@ tts_rec_rw_lock_smoke_check_4(void) stats.write_locks_delayed < expected.write_locks_delayed || stats.max_write_locks_pending > expected.max_write_locks_pending) { TestErrPrintf("Unexpected recursive R/W lock stats"); - H5TS__rw_lock_print_stats("Actual stats", &stats); - H5TS__rw_lock_print_stats("Expected stats", &expected); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Expected stats", &expected); } /* clang-format on */ if (verbose) - H5TS__rw_lock_print_stats("Actual stats", &stats); + H5TS__rec_rwlock_print_stats("Actual stats", &stats); #endif /* 7) Shut down the recursive R/W lock. */ - result = H5TS__rw_lock_destroy(&rec_rw_lock); - CHECK_I(result, "H5TS__rw_lock_destroy"); + result = H5TS__rec_rwlock_destroy(&lock); + CHECK_I(result, "H5TS__rec_rwlock_destroy"); /* discard the udata if it exists */ if (udata) free(udata); -} /* end tts_rec_rw_lock_smoke_check_4() */ +} /* end tts_rec_rwlock_smoke_check_4() */ #endif /* H5_HAVE_WIN_THREADS */ #endif /* H5_HAVE_THREADS */ diff --git a/test/ttsafe_rwlock.c b/test/ttsafe_rwlock.c new file mode 100644 index 00000000000..c85434288ee --- /dev/null +++ b/test/ttsafe_rwlock.c @@ -0,0 +1,317 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/******************************************************************** + * + * Test the correctness of the non-recursive R/W lock routines + * + ********************************************************************/ + +#include "ttsafe.h" + +#ifdef H5_HAVE_THREADS + +#define NUM_THREADS 16 +#define NUM_WRITERS 4 + +#define NUM_ITERS 32 +#define COUNT_MAX 1024 + +typedef struct { + H5TS_rwlock_t lock; + int val; + H5TS_barrier_t barrier; +} atomic_counter_t; + +static H5TS_THREAD_RETURN_TYPE +incr_task(void *_counter) +{ + atomic_counter_t *counter = (atomic_counter_t *)_counter; + herr_t result; + H5TS_thread_ret_t ret_value = 0; + + result = H5TS_rwlock_wrlock(&counter->lock); + CHECK_I(result, "H5TS_rwlock_wrlock"); + + /* Increment value */ + counter->val++; + + result = H5TS_rwlock_wrunlock(&counter->lock); + CHECK_I(result, "H5TS_rwlock_wrunlock"); + + return ret_value; +} + +static H5TS_THREAD_RETURN_TYPE +many_read(void *_counter) +{ + atomic_counter_t *counter = (atomic_counter_t *)_counter; + herr_t result; + H5TS_thread_ret_t ret_value = 0; + + result = H5TS_rwlock_rdlock(&counter->lock); + CHECK_I(result, "H5TS_rwlock_rdlock"); + + /* Wait at barrier, to confirm that many readers can hold lock */ + result = H5TS_barrier_wait(&counter->barrier); + CHECK_I(result, "H5TS_barrier_wait"); + + result = H5TS_rwlock_rdunlock(&counter->lock); + CHECK_I(result, "H5TS_rdlock_rdunlock"); + + return ret_value; +} + +static H5TS_THREAD_RETURN_TYPE +count_up_and_down(void *_counter) +{ + atomic_counter_t *counter = (atomic_counter_t *)_counter; + herr_t result; + H5TS_thread_ret_t ret_value = 0; + + /* Count up & down a number of times */ + for (unsigned u = 0; u < NUM_ITERS; u++) { + /* Wait at barrier, to ensure all threads are ready to count */ + result = H5TS_barrier_wait(&counter->barrier); + CHECK_I(result, "H5TS_barrier_wait"); + + /* Count up */ + for (unsigned v = 0; v < COUNT_MAX; v++) { + result = H5TS_rwlock_wrlock(&counter->lock); + CHECK_I(result, "H5TS_rwlock_wrlock"); + + /* Increment value */ + counter->val++; + + result = H5TS_rwlock_wrunlock(&counter->lock); + CHECK_I(result, "H5TS_rwlock_wrunlock"); + } + + /* Wait at barrier, to ensure all threads have finishend counting up */ + result = H5TS_barrier_wait(&counter->barrier); + CHECK_I(result, "H5TS_barrier_wait"); + + /* Count down */ + for (unsigned v = 0; v < COUNT_MAX; v++) { + result = H5TS_rwlock_wrlock(&counter->lock); + CHECK_I(result, "H5TS_rwlock_wrlock"); + + /* Decrement value */ + counter->val--; + + result = H5TS_rwlock_wrunlock(&counter->lock); + CHECK_I(result, "H5TS_rwlock_wrunlock"); + } + } + + return ret_value; +} + +static H5TS_THREAD_RETURN_TYPE +verify_counting(void *_counter) +{ + atomic_counter_t *counter = (atomic_counter_t *)_counter; + herr_t result; + int last_val = 0; + H5TS_thread_ret_t ret_value = 0; + + /* Count up & down a number of times */ + for (unsigned u = 0; u < NUM_ITERS; u++) { + /* Wait at barrier, to ensure all threads are ready to count */ + result = H5TS_barrier_wait(&counter->barrier); + CHECK_I(result, "H5TS_barrier_wait"); + + /* Verify that counter goes only up */ + do { + result = H5TS_rwlock_rdlock(&counter->lock); + CHECK_I(result, "H5TS_rwlock_rdlock"); + + /* Check counter value */ + if (counter->val < last_val) + ERROR("incorrect counter value"); + + /* Save value */ + last_val = counter->val; + + result = H5TS_rwlock_rdunlock(&counter->lock); + CHECK_I(result, "H5TS_rdlock_wrunlock"); + + /* Give the writers a chance to make progress */ + H5TS_thread_yield(); + } while (last_val < (NUM_WRITERS * COUNT_MAX)); + + /* Wait at barrier, to ensure all threads have finishend counting up */ + result = H5TS_barrier_wait(&counter->barrier); + CHECK_I(result, "H5TS_barrier_wait"); + + /* Verify that counter goes only down */ + do { + result = H5TS_rwlock_rdlock(&counter->lock); + CHECK_I(result, "H5TS_rwlock_rdlock"); + + /* Check counter value */ + if (counter->val > last_val) + ERROR("incorrect counter value"); + + /* Save value */ + last_val = counter->val; + + result = H5TS_rwlock_rdunlock(&counter->lock); + CHECK_I(result, "H5TS_rdlock_wrunlock"); + + /* Give the writers a chance to make progress */ + H5TS_thread_yield(); + } while (last_val > 0); + } + + return ret_value; +} + +/* + ********************************************************************** + * tts_rwlock + ********************************************************************** + */ +void +tts_rwlock(void) +{ + H5TS_thread_t threads[NUM_THREADS]; + H5TS_pool_t *pool = NULL; + H5TS_rwlock_t lock; + atomic_counter_t counter; + herr_t result; + + /* Sanity checks on bad input */ + result = H5TS_rwlock_init(NULL); + VERIFY(result, FAIL, "H5TS_rwlock_init"); + result = H5TS_rwlock_rdlock(NULL); + VERIFY(result, FAIL, "H5TS_rwlock_rdlock"); + result = H5TS_rwlock_rdunlock(NULL); + VERIFY(result, FAIL, "H5TS_rwlock_rdunlock"); + result = H5TS_rwlock_wrlock(NULL); + VERIFY(result, FAIL, "H5TS_rwlock_wrlock"); + result = H5TS_rwlock_wrunlock(NULL); + VERIFY(result, FAIL, "H5TS_rwlock_wrunlock"); + result = H5TS_rwlock_destroy(NULL); + VERIFY(result, FAIL, "H5TS_rwlock_destroy"); + + /* Create & destroy lock */ + result = H5TS_rwlock_init(&lock); + CHECK_I(result, "H5TS_rwlock_init"); + + result = H5TS_rwlock_destroy(&lock); + CHECK_I(result, "H5TS_rwlock_destroy"); + + /* Read lock & unlock */ + result = H5TS_rwlock_init(&lock); + CHECK_I(result, "H5TS_rwlock_init"); + + result = H5TS_rwlock_rdlock(&lock); + CHECK_I(result, "H5TS_rwlock_rdlock"); + + result = H5TS_rwlock_rdunlock(&lock); + CHECK_I(result, "H5TS_rwlock_rdunlock"); + + result = H5TS_rwlock_destroy(&lock); + CHECK_I(result, "H5TS_rwlock_destroy"); + + /* Write lock & unlock */ + result = H5TS_rwlock_init(&lock); + CHECK_I(result, "H5TS_rwlock_init"); + + result = H5TS_rwlock_wrlock(&lock); + CHECK_I(result, "H5TS_rwlock_wrlock"); + + result = H5TS_rwlock_wrunlock(&lock); + CHECK_I(result, "H5TS_rwlock_wrunlock"); + + result = H5TS_rwlock_destroy(&lock); + CHECK_I(result, "H5TS_rwlock_destroy"); + + /* Hold read lock w/many threads */ + result = H5TS_rwlock_init(&counter.lock); + CHECK_I(result, "H5TS_rwlock_init"); + + result = H5TS_barrier_init(&counter.barrier, NUM_THREADS); + CHECK_I(result, "H5TS_barrier_init"); + + for (unsigned u = 0; u < NUM_THREADS; u++) { + result = H5TS_thread_create(&threads[u], many_read, &counter); + CHECK_I(result, "H5TS_thread_create"); + } + + for (unsigned u = 0; u < NUM_THREADS; u++) { + result = H5TS_thread_join(threads[u], NULL); + CHECK_I(result, "H5TS_thread_join"); + } + + result = H5TS_barrier_destroy(&counter.barrier); + CHECK_I(result, "H5TS_barrier_destroy"); + + result = H5TS_rwlock_destroy(&counter.lock); + CHECK_I(result, "H5TS_rwlock_destroy"); + + /* Increment counter w/many threads */ + result = H5TS_rwlock_init(&counter.lock); + CHECK_I(result, "H5TS_rwlock_init"); + + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + counter.val = 0; + for (unsigned u = 0; u < NUM_THREADS; u++) { + result = H5TS_pool_add_task(pool, incr_task, &counter); + CHECK_I(result, "H5TS_pool_add_task"); + } + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(counter.val, NUM_THREADS, "many incr"); + + result = H5TS_rwlock_destroy(&counter.lock); + CHECK_I(result, "H5TS_rwlock_destroy"); + + /* Increment & decrement counter w/many threads while reading */ + result = H5TS_rwlock_init(&counter.lock); + CHECK_I(result, "H5TS_rwlock_init"); + + result = H5TS_barrier_init(&counter.barrier, NUM_THREADS); + CHECK_I(result, "H5TS_barrier_init"); + + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + counter.val = 0; + for (unsigned u = 0; u < NUM_WRITERS; u++) { + result = H5TS_pool_add_task(pool, count_up_and_down, &counter); + CHECK_I(result, "H5TS_pool_add_task"); + } + for (unsigned u = 0; u < (NUM_THREADS - NUM_WRITERS); u++) { + result = H5TS_pool_add_task(pool, verify_counting, &counter); + CHECK_I(result, "H5TS_pool_add_task"); + } + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(counter.val, 0, "count up & down"); + + result = H5TS_barrier_destroy(&counter.barrier); + CHECK_I(result, "H5TS_barrier_destroy"); + + result = H5TS_rwlock_destroy(&counter.lock); + CHECK_I(result, "H5TS_rwlock_destroy"); + +} /* end tts_rwlock() */ + +#endif /*H5_HAVE_THREADS*/ diff --git a/test/ttsafe_semaphore.c b/test/ttsafe_semaphore.c index b8a3dece723..4d1a6a308cf 100644 --- a/test/ttsafe_semaphore.c +++ b/test/ttsafe_semaphore.c @@ -26,9 +26,9 @@ #define NUM_THREADS 16 typedef struct { - H5TS_semaphore_t ping_sem, pong_sem; - H5TS_atomic_uint_t ping_counter; - H5TS_atomic_uint_t pong_counter; + H5TS_semaphore_t ping_sem, pong_sem; + unsigned ping_counter; + unsigned pong_counter; } pingpong_t; typedef struct { @@ -48,11 +48,11 @@ ping(void *_test_info) result = H5TS_semaphore_wait(&test_info->ping_sem); CHECK_I(result, "H5TS_semaphore_wait"); - H5TS_atomic_fetch_add_uint(&test_info->ping_counter, (unsigned)1); + test_info->ping_counter++; result = H5TS_semaphore_signal(&test_info->pong_sem); CHECK_I(result, "H5TS_semaphore_signal"); - } while (H5TS_atomic_load_uint(&test_info->ping_counter) < NUM_PINGPONG); + } while (test_info->ping_counter < NUM_PINGPONG); return ret_value; } @@ -68,11 +68,11 @@ pong(void *_test_info) result = H5TS_semaphore_wait(&test_info->pong_sem); CHECK_I(result, "H5TS_semaphore_wait"); - H5TS_atomic_fetch_add_uint(&test_info->pong_counter, (unsigned)1); + test_info->pong_counter++; result = H5TS_semaphore_signal(&test_info->ping_sem); CHECK_I(result, "H5TS_semaphore_signal"); - } while (H5TS_atomic_load_uint(&test_info->pong_counter) < NUM_PINGPONG); + } while (test_info->pong_counter < NUM_PINGPONG); return ret_value; } @@ -94,8 +94,8 @@ tts_semaphore_pingpong(void) CHECK_I(result, "H5TS_semaphore_init"); result = H5TS_semaphore_init(&test_info.pong_sem, 0); CHECK_I(result, "H5TS_semaphore_init"); - H5TS_atomic_init_uint(&test_info.ping_counter, (unsigned)0); - H5TS_atomic_init_uint(&test_info.pong_counter, (unsigned)0); + test_info.ping_counter = 0; + test_info.pong_counter = 0; /* Start ping & pong threads */ result = H5TS_thread_create(&ping_thread, ping, &test_info); @@ -113,17 +113,14 @@ tts_semaphore_pingpong(void) result = H5TS_thread_join(pong_thread, NULL); CHECK_I(result, "H5TS_thread_join"); - VERIFY(H5TS_atomic_load_uint(&test_info.ping_counter), NUM_PINGPONG, "ping counter"); - VERIFY(H5TS_atomic_load_uint(&test_info.pong_counter), NUM_PINGPONG, "pong counter"); + VERIFY(test_info.ping_counter, NUM_PINGPONG, "ping counter"); + VERIFY(test_info.pong_counter, NUM_PINGPONG, "pong counter"); - /* Destroy semaphores, etc. */ + /* Destroy semaphores */ result = H5TS_semaphore_destroy(&test_info.ping_sem); CHECK_I(result, "H5TS_semaphore_destroy"); result = H5TS_semaphore_destroy(&test_info.pong_sem); CHECK_I(result, "H5TS_semaphore_destroy"); - - H5TS_atomic_destroy_uint(&test_info.ping_counter); - H5TS_atomic_destroy_uint(&test_info.pong_counter); } /* end tts_semaphore_pingpong() */ static H5TS_THREAD_RETURN_TYPE