Skip to content

Commit

Permalink
Rename and add more locking infrastructure (HDFGroup#4729)
Browse files Browse the repository at this point in the history
Rename existing recursive R/W locks from H5TS_rw_lock_t to H5TS_rec_rwlock_t, so it's more obvious that they are recursive.

Add non-recursive R/W lock, H5TS_rwlock_t

Add spinlock, H5TS_spinlock_t

Add an atomic void pointer, H5TS_atomic_voidp_t
  • Loading branch information
qkoziol authored Sep 7, 2024
1 parent baa0439 commit 92532bb
Show file tree
Hide file tree
Showing 16 changed files with 2,043 additions and 857 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 53 additions & 8 deletions src/H5TSatomic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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
Expand All @@ -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 */
Expand All @@ -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 */
69 changes: 68 additions & 1 deletion src/H5TSatomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
81 changes: 40 additions & 41 deletions src/H5TSpkg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

/****************************/
Expand All @@ -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.
*
Expand Down Expand Up @@ -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;
Expand All @@ -134,65 +134,63 @@ 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.
*
* readers_cv: Condition variable used for waiting readers.
*
* 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;
Expand All @@ -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 */
Expand All @@ -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
Expand All @@ -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 */
Expand Down
Loading

0 comments on commit 92532bb

Please sign in to comment.