From 67089c41b4bf6a58e97d5b670a2992ddb9b89f5a Mon Sep 17 00:00:00 2001 From: Quincey Koziol Date: Mon, 13 May 2024 11:29:49 -0500 Subject: [PATCH] Add simple non-recursive readrs/writer lock Signed-off-by: Quincey Koziol --- src/CMakeLists.txt | 1 + src/H5TSprivate.h | 18 ++ src/H5TSrwlock.c | 555 +++++++++++++++++++++++++++++++++++++++++++++ src/Makefile.am | 2 +- 4 files changed, 575 insertions(+), 1 deletion(-) create mode 100644 src/H5TSrwlock.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0786922f348..6aef96e10cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -651,6 +651,7 @@ set (H5TS_SOURCES ${HDF5_SRC_DIR}/H5TSpool.c ${HDF5_SRC_DIR}/H5TSpthread.c ${HDF5_SRC_DIR}/H5TSrec_rwlock.c + ${HDF5_SRC_DIR}/H5TSrwlock.c ${HDF5_SRC_DIR}/H5TSthread.c ${HDF5_SRC_DIR}/H5TSwin.c ) diff --git a/src/H5TSprivate.h b/src/H5TSprivate.h index f4e5503ce47..d5c78738750 100644 --- a/src/H5TSprivate.h +++ b/src/H5TSprivate.h @@ -116,6 +116,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; @@ -131,6 +139,7 @@ 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; @@ -140,6 +149,7 @@ 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_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); @@ -206,6 +216,14 @@ H5_DLL herr_t H5TS_mutex_trylock(H5TS_mutex_t *mutex, bool *acquired) H5TS_TRY_A H5_DLL herr_t H5TS_mutex_unlock(H5TS_mutex_t *mutex) H5TS_RELEASE(*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); +H5_DLL herr_t H5TS_rwlock_rdlock(H5TS_rwlock_t *lock); +H5_DLL herr_t H5TS_rwlock_rdunlock(H5TS_rwlock_t *lock); +H5_DLL herr_t H5TS_rwlock_wrlock(H5TS_rwlock_t *lock); +H5_DLL 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); H5_DLL herr_t H5TS_cond_wait(H5TS_cond_t *cond, H5TS_mutex_t *mutex); diff --git a/src/H5TSrwlock.c b/src/H5TSrwlock.c new file mode 100644 index 00000000000..d3ec1cdab08 --- /dev/null +++ b/src/H5TSrwlock.c @@ -0,0 +1,555 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. 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 */ +/****************/ + +#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 */ +/******************/ + +/********************/ +/* Local Prototypes */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +#ifdef H5_HAVE_C11_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_init + * + * Purpose: Initialize a H5TS_rwlock_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_init(H5TS_rwlock_t *lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Initialize synchronization primitives */ + if (H5_UNLIKELY(mtx_init(&lock->mutex, mtx_plain) != thrd_success)) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(cnd_init(&lock->read_cv) != thrd_success)) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(cnd_init(&lock->write_cv) != thrd_success)) + HGOTO_DONE(FAIL); + + /* Initialize scalar fields */ + lock->readers = 0; + lock->writers = 0; + lock->read_waiters = 0; + lock->write_waiters = 0; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdlock + * + * Purpose: Acquire a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_rdlock(H5TS_rwlock_t *lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Acquire the lock's mutex */ + if (H5_UNLIKELY(mtx_lock(&lock->mutex) != thrd_success)) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* 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))) + HGOTO_DONE(FAIL); + } while (lock->writers || lock->write_waiters); + + /* Read not waiting any longer */ + lock->read_waiters--; + } + + /* Increment # of readers */ + lock->readers++; + +done: + /* Release mutex, if we're holding it */ + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(mtx_unlock(&lock->mutex) != thrd_success)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_rdlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdunlock + * + * Purpose: Release a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_rdunlock(H5TS_rwlock_t *lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Acquire the lock's mutex */ + if (H5_UNLIKELY(mtx_lock(&lock->mutex) != thrd_success)) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* 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)) + HGOTO_DONE(FAIL); + +done: + /* Release mutex, if we're holding it */ + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(mtx_unlock(&lock->mutex) != thrd_success)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_rdunlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrlock + * + * Purpose: Acquire a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_wrlock(H5TS_rwlock_t *lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Acquire the lock's mutex */ + if (H5_UNLIKELY(mtx_lock(&lock->mutex) != thrd_success)) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* 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))) + HGOTO_DONE(FAIL); + } while (lock->readers || lock->writers); + + /* Write not waiting any longer */ + lock->write_waiters--; + } + + /* Increment # of writers */ + lock->writers++; + +done: + /* Release mutex, if we're holding it */ + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(mtx_unlock(&lock->mutex) != thrd_success)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_wrlock() */ + +------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrunlock + * + * Purpose: Release a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_wrunlock(H5TS_rwlock_t *lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Acquire the lock's mutex */ + if (H5_UNLIKELY(mtx_lock(&lock->mutex) != thrd_success)) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Decrement # of writers */ + lock->writers--; + + /* Check for waiting writers */ + if (lock->write_waiters) { + if (H5_UNLIKELY(cnd_signal(&lock->write_cv) != thrd_success)) + HGOTO_DONE(FAIL); + } else if (lock->read_waiters) + if (H5_UNLIKELY(cnd_broadcast(&lock->read_cv) != thrd_success)) + HGOTO_DONE(FAIL); + +done: + /* Release mutex, if we're holding it */ + if (H5_LIKELY(have_mutex)) + if (H5_UNLIKELY(mtx_unlock(&lock->mutex) != thrd_success)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_wrunlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_destroy + * + * Purpose: Destroy a H5TS_rwlock_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_destroy(H5TS_rwlock_t *lock) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* 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); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_rwlock_destroy() */ +#else +#ifdef H5_HAVE_WIN_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_init + * + * Purpose: Initialize a H5TS_rwlock_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_init(H5TS_rwlock_t *lock) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + InitializeSRWLock(lock); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_rwlock_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdlock + * + * Purpose: Acquire a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_rdlock(H5TS_rwlock_t *lock) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + AcquireSRWLockShared(lock); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_rwlock_rdlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdunlock + * + * Purpose: Release a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_rdunlock(H5TS_rwlock_t *lock) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + ReleaseSRWLockShared(lock); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_rwlock_rdunlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrlock + * + * Purpose: Acquire a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_wrlock(H5TS_rwlock_t *lock) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + AcquireSRWLockExclusive(lock); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_rwlock_wrlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrunlock + * + * Purpose: Release a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_wrunlock(H5TS_rwlock_t *lock) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + ReleaseSRWLockExclusive(lock); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_rwlock_wrunlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_destroy + * + * Purpose: Destroy a H5TS_rwlock_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_destroy(H5TS_rwlock_t *lock) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Destroy synchronization primitives */ + /* SRWLOCKs don't have to be destroyed */ + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_rwlock_destroy() */ +#else +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_init + * + * Purpose: Initialize a H5TS_rwlock_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_init(H5TS_rwlock_t *lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_rwlock_init(lock, NULL))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdlock + * + * Purpose: Acquire a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_rdlock(H5TS_rwlock_t *lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_rwlock_rdlock(lock))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_rdlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdunlock + * + * Purpose: Release a read lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_rdunlock(H5TS_rwlock_t *lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_rwlock_unlock(lock))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_rdunlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_wrlock + * + * Purpose: Acquire a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_wrlock(H5TS_rwlock_t *lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_rwlock_wrlock(lock))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_wrlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_rdunlock + * + * Purpose: Release a write lock + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_wrunlock(H5TS_rwlock_t *lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_rwlock_unlock(lock))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_wrunlock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_rwlock_destroy + * + * Purpose: Destroy a H5TS_rwlock_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_rwlock_destroy(H5TS_rwlock_t *lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_rwlock_destroy(lock))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_rwlock_destroy() */ +#endif +#endif + +#endif /* H5_HAVE_THREADS */ + diff --git a/src/Makefile.am b/src/Makefile.am index 974daf48aed..d53f9c8afdc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -100,7 +100,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 \ - H5TSrec_rwlock.c H5TSthread.c H5TSwin.c \ + H5TSrec_rwlock.c H5TSrwlock.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 \