diff --git a/.clang-format b/.clang-format index 59a9bd479ae..3c2dece7082 100644 --- a/.clang-format +++ b/.clang-format @@ -70,6 +70,7 @@ StatementMacros: - FUNC_ENTER_API - FUNC_ENTER_API_NAMECHECK_ONLY - FUNC_ENTER_NOAPI + - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - FUNC_ENTER_NOAPI_NOERR - FUNC_ENTER_NOAPI_NOINIT - FUNC_ENTER_NOAPI_NOINIT_NOERR @@ -77,6 +78,8 @@ StatementMacros: - FUNC_LEAVE_API - FUNC_LEAVE_API_NAMECHECK_ONLY - FUNC_LEAVE_NOAPI + - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY + - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY - FUNC_LEAVE_NOAPI_NOFS - H5E_END_TRY - H5E_PRINTF diff --git a/ACKNOWLEDGMENTS b/ACKNOWLEDGMENTS index f1b5b45b94a..c38e60c8d41 100644 --- a/ACKNOWLEDGMENTS +++ b/ACKNOWLEDGMENTS @@ -1,18 +1,20 @@ -Acknowledgments - November 2010 -------------------------------- +Acknowledgments +--------------- We would like to thank the following people who have contributed directly or indirectly to HDF5: -Werner Benger, for contributing code used to add support for the Windows +Werner Benger, for contributing code used to add support for the Windows Threading library included in the 1.8.6 release. John A. Biddiscombe, Mike Jackson, and Sean McBride for contributing and testing CMake code included in the HDF5 1.8.5 distribution. +NVIDIA, for contributing multithreaded concurrency support. + The HDF5 community for helping shape the development of HDF5 by contributing bug reports and patches, joining in on forum discussions, and continually supporting our software. -Finally, we'd like to thank all organizations whose funding has made HDF5 +Finally, we'd like to thank all organizations whose funding has made HDF5 possible. diff --git a/CMakeLists.txt b/CMakeLists.txt index fdc2d48b1b6..5382a8ff8b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -768,6 +768,9 @@ if (HDF5_ENABLE_PARALLEL) Reading/Writing >2GB of data in a single parallel I/O operation will be disabled.") set (LARGE_PARALLEL_IO OFF) endif () + + # Used by Subfiling VFD feature + CHECK_SYMBOL_EXISTS (MPI_Comm_split_type "mpi.h" H5_HAVE_MPI_Comm_split_type) else () message (FATAL_ERROR "Parallel libraries not found") endif () @@ -781,6 +784,38 @@ if (H5_HAVE_PARALLEL) endif () endif () +# Determine if a threading package is available on this system +option (HDF5_ENABLE_THREADS "Enable thread support" ON) +set (THREADS_PREFER_PTHREAD_FLAG ON) +find_package (Threads) +if (Threads_FOUND) + set (H5_HAVE_THREADS 1) + set (CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + + # Determine which threading package to use + # Comment out check for C11 threads for now, since it conflicts with the + # current --std=c99 compile flags at configuration time. When we switch to + # --std=c11, this can be uncommented. + #CHECK_INCLUDE_FILE("threads.h" HAVE_THREADS_H) + if (WIN32) + # When Win32 is available, we use those threads + set (H5_HAVE_WIN_THREADS 1) + elseif (HAVE_THREADS_H) + # When C11 threads are available, those are the top choice + set (H5_HAVE_C11_THREADS 1) + elseif (CMAKE_USE_PTHREADS_INIT) + set (H5_HAVE_PTHREAD_H 1) + else () + message (FATAL_ERROR " **** thread support requires C11 threads, Win32 threads or Pthreads **** ") + endif () + + # Check for compiler support for atomic variables + CHECK_INCLUDE_FILE("stdatomic.h" HAVE_STDATOMIC_H) + if (HAVE_STDATOMIC_H) + set (H5_HAVE_STDATOMIC_H 1) + endif() +endif () + # Determine whether to build the HDF5 Subfiling VFD set (H5FD_SUBFILING_DIR ${HDF5_SRC_DIR}/H5FDsubfiling) set (HDF5_SRC_INCLUDE_DIRS @@ -795,57 +830,21 @@ if (HDF5_ENABLE_SUBFILING_VFD) if (NOT HDF5_ENABLE_PARALLEL) message (FATAL_ERROR "Subfiling VFD requires a parallel HDF5 build") else () - # Check for MPI_Comm_split_type - CHECK_SYMBOL_EXISTS (MPI_Comm_split_type "mpi.h" H5_HAVE_MPI_Comm_split_type) + # Make sure we found MPI_Comm_split_type previously if (NOT H5_HAVE_MPI_Comm_split_type) message (FATAL_ERROR "Subfiling VFD requires MPI-3 support for MPI_Comm_split_type") endif () endif () - if (NOT DEFINED Threads_FOUND) - set (THREADS_PREFER_PTHREAD_FLAG ON) - find_package (Threads REQUIRED) - endif () - - # For now, make sure we're using pthreads. Once Subfiling can be - # supported on Windows, we should allow Win32 threads as well - if (NOT ${Threads_FOUND} OR NOT ${CMAKE_USE_PTHREADS_INIT}) - message (FATAL_ERROR "Subfiling requires pthreads for system thread library") + # Subfiling requires thread operations + if (NOT Threads_FOUND) + message (FATAL_ERROR "Subfiling requires thread operations support") endif () - CHECK_INCLUDE_FILE("stdatomic.h" HAVE_STDATOMIC_H) - if (NOT HAVE_STDATOMIC_H) - message (FATAL_ERROR "Subfiling VFD requires atomic operations support. C11 stdatomic.h NOT available.") - else() - set (H5_HAVE_STDATOMIC_H 1) - endif() - set (H5_HAVE_SUBFILING_VFD 1) # IOC VFD is currently only built when subfiling is enabled set (H5_HAVE_IOC_VFD 1) - message (STATUS "Setting up to use Mercury components") - set (H5FD_SUBFILING_MERCURY_DIR ${H5FD_SUBFILING_DIR}/mercury/src/util) - set (HDF5_SRC_INCLUDE_DIRS - ${HDF5_SRC_INCLUDE_DIRS} - ${H5FD_SUBFILING_MERCURY_DIR} - ) - set (CMAKE_REQUIRED_INCLUDES "${H5FD_SUBFILING_MERCURY_DIR}") - - # Run some configure checks for the Mercury util files - set (CMAKE_EXTRA_INCLUDE_FILES pthread.h) - set (CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) - - check_type_size(PTHREAD_MUTEX_ADAPTIVE_NP PTHREAD_MUTEX_ADAPTIVE_NP_SIZE) - if (HAVE_PTHREAD_MUTEX_ADAPTIVE_NP_SIZE) - set (${HDF_PREFIX}_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP 1) - endif () - - check_symbol_exists(pthread_condattr_setclock pthread.h - ${HDF_PREFIX}_HAVE_PTHREAD_CONDATTR_SETCLOCK) - - unset (CMAKE_EXTRA_INCLUDE_FILES) - unset (CMAKE_REQUIRED_LIBRARIES) if(NOT H5_HAVE_SUBFILING_VFD EQUAL 1) set (HDF5_ENABLE_SUBFILING_VFD OFF CACHE BOOL "Build Parallel HDF5 Subfiling VFD" FORCE) endif() @@ -942,6 +941,13 @@ if (HDF5_ENABLE_THREADSAFE) message (FATAL_ERROR " **** thread-safety option not supported with static library **** ") endif () endif () + if (HDF_ENABLE_PARALLEL) + if (NOT ALLOW_UNSUPPORTED) + message (FATAL_ERROR " **** Parallel and thread-safety options are not supported, override with ALLOW_UNSUPPORTED option **** ") + else () + message (VERBOSE " **** Allowing unsupported parallel and thread-safety options **** ") + endif () + endif () if (HDF5_BUILD_FORTRAN) if (NOT ALLOW_UNSUPPORTED) message (FATAL_ERROR " **** Fortran and thread-safety options are not supported, override with ALLOW_UNSUPPORTED option **** ") @@ -963,19 +969,13 @@ if (HDF5_ENABLE_THREADSAFE) message (VERBOSE " **** Allowing unsupported HL and thread-safety options **** ") endif () endif () - if (WIN32) - # When Win32 is available, we use those threads - set (H5_HAVE_WIN_THREADS 1) - else () - if (NOT H5_HAVE_PTHREAD_H) - message (FATAL_ERROR " **** thread-safe option requires Win32 threads or Pthreads **** ") - endif () - endif () - set (THREADS_PREFER_PTHREAD_FLAG ON) - find_package (Threads REQUIRED) - if (Threads_FOUND) - set (H5_HAVE_THREADSAFE 1) + + # Check for threading package + if (NOT Threads_FOUND) + message (FATAL_ERROR " **** thread-safety option requires a threading package and none was found **** ") endif () + + set (H5_HAVE_THREADSAFE 1) endif () #----------------------------------------------------------------------------- diff --git a/HDF5Examples/CMakeLists.txt b/HDF5Examples/CMakeLists.txt index 7f823abb1e7..b155fb4025d 100644 --- a/HDF5Examples/CMakeLists.txt +++ b/HDF5Examples/CMakeLists.txt @@ -99,9 +99,40 @@ if (H5_HAVE_PARALLEL) INCLUDE_DIRECTORIES (${MPI_C_INCLUDE_DIRS}) endif () +# Determine if a threading package is available on this system +option (HDF5_ENABLE_THREADS "Enable thread support" ON) +set (THREADS_PREFER_PTHREAD_FLAG ON) +find_package (Threads) +if (Threads_FOUND) + set (H5_HAVE_THREADS 1) + set (CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + + # Determine which threading package to use + # Comment out check for C11 threads for now, since it conflicts with the + # current --std=c99 compile flags at configuration time. When we switch to + # --std=c11, this can be uncommented. + #CHECK_INCLUDE_FILE("threads.h" HAVE_THREADS_H) + if (WIN32) + # When Win32 is available, we use those threads + set (H5_HAVE_WIN_THREADS 1) + elseif (HAVE_THREADS_H) + # When C11 threads are available, those are the top choice + set (H5_HAVE_C11_THREADS 1) + elseif (CMAKE_USE_PTHREADS_INIT) + set (H5_HAVE_PTHREAD_H 1) + else () + message (FATAL_ERROR " **** thread support requires C11 threads, Win32 threads or Pthreads **** ") + endif () + + # Check for compiler support for atomic variables + CHECK_INCLUDE_FILE("stdatomic.h" HAVE_STDATOMIC_H) + if (HAVE_STDATOMIC_H) + set (H5_HAVE_STDATOMIC_H 1) + endif() +endif () + #----------------------------------------------------------------------------- # Option to use threadsafe -# Note: Currently CMake only allows configuring of threadsafe on WINDOWS. #----------------------------------------------------------------------------- option (HDF_ENABLE_THREADSAFE "Enable Threadsafety" OFF) # Note that HDF_ENABLE_THREADSAFE is the CMake option for determining @@ -109,17 +140,12 @@ option (HDF_ENABLE_THREADSAFE "Enable Threadsafety" OFF) # is the CMake option determining whether HDF5 was configured with # thread-safety enabled. if (HDF_ENABLE_THREADSAFE AND HDF5_ENABLE_THREADSAFE) - if (WIN32) - set (H5_HAVE_WIN_THREADS 1) - set (H5_HAVE_THREADSAFE 1) - endif () - set(CMAKE_THREAD_PREFER_PTHREAD TRUE) - set(THREADS_PREFER_PTHREAD_FLAG TRUE) - find_package(Threads REQUIRED) + # Check for threading package if (NOT Threads_FOUND) - message (STATUS " **** thread-safe package not found - threads still might work **** ") + message (FATAL_ERROR " **** thread-safety option requires a threading package and none was found **** ") endif () - set (H5EX_HDF5_LINK_LIBS ${H5EX_HDF5_LINK_LIBS} Threads::Threads) + + set (H5_HAVE_THREADSAFE 1) endif () set_directory_properties(PROPERTIES INCLUDE_DIRECTORIES diff --git a/config/clang-warnings/error-general b/config/clang-warnings/error-general index 883dff76f7a..52f8ac865e3 100644 --- a/config/clang-warnings/error-general +++ b/config/clang-warnings/error-general @@ -57,8 +57,6 @@ # Here is a list of tests and examples that have issues with the stricter warnings as error # # NOTE: Test files are not compatible with these warnings as errors -# thread_id.c, -# -Werror=unused-function # dsets.c # -Werror=unused-parameter # diff --git a/config/cmake/ConfigureChecks.cmake b/config/cmake/ConfigureChecks.cmake index e7e3c27ff75..589cf83c746 100644 --- a/config/cmake/ConfigureChecks.cmake +++ b/config/cmake/ConfigureChecks.cmake @@ -439,7 +439,7 @@ if (MINGW OR NOT WINDOWS) foreach (other_test HAVE_ATTRIBUTE HAVE_BUILTIN_EXPECT - SYSTEM_SCOPE_THREADS + PTHREAD_BARRIER HAVE_SOCKLEN_T ) HDF_FUNCTION_TEST (${other_test}) @@ -582,12 +582,6 @@ if (MINGW OR NOT WINDOWS) endif () endif () -# Check for clock_gettime() CLOCK_MONOTONIC_COARSE -set (CMAKE_EXTRA_INCLUDE_FILES time.h) -check_type_size(CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_COARSE_SIZE) -if (HAVE_CLOCK_MONOTONIC_COARSE_SIZE) - set (${HDF_PREFIX}_HAVE_CLOCK_MONOTONIC_COARSE 1) -endif () unset (CMAKE_EXTRA_INCLUDE_FILES) #----------------------------------------------------------------------------- diff --git a/config/cmake/H5pubconf.h.in b/config/cmake/H5pubconf.h.in index 2530a95830d..c3e92eba66b 100644 --- a/config/cmake/H5pubconf.h.in +++ b/config/cmake/H5pubconf.h.in @@ -107,9 +107,6 @@ /* Define to 1 if you have the `clock_gettime' function. */ #cmakedefine H5_HAVE_CLOCK_GETTIME @H5_HAVE_CLOCK_GETTIME@ -/* Define to 1 if CLOCK_MONOTONIC_COARSE is available */ -#cmakedefine H5_HAVE_CLOCK_MONOTONIC_COARSE @H5_HAVE_CLOCK_MONOTONIC_COARSE@ - /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_CURL_CURL_H @H5_HAVE_CURL_H@ @@ -261,11 +258,8 @@ /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_PTHREAD_H @H5_HAVE_PTHREAD_H@ -/* Define to 1 if 'pthread_condattr_setclock()' is available */ -#cmakedefine H5_HAVE_PTHREAD_CONDATTR_SETCLOCK @H5_HAVE_PTHREAD_CONDATTR_SETCLOCK@ - -/* Define to 1 if PTHREAD_MUTEX_ADAPTIVE_NP is available */ -#cmakedefine H5_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP @H5_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP@ +/* Define to 1 if the compiler supports the pthread_barrier_*() routines */ +#cmakedefine H5_HAVE_PTHREAD_BARRIER @H5_HAVE_PTHREAD_BARRIER@ /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_PWD_H @H5_HAVE_PWD_H@ @@ -325,6 +319,9 @@ /* Define to 1 if the compiler supports the __builtin_expect() extension */ #cmakedefine H5_HAVE_BUILTIN_EXPECT @H5_HAVE_BUILTIN_EXPECT@ +/* Define if we have thread support */ +# cmakedefine H5_HAVE_THREADS @H5_HAVE_THREADS@ + #if defined(_WIN32) && !defined(H5_BUILT_AS_DYNAMIC_LIB) /* Not supported on WIN32 platforms with static linking */ /* #undef H5_HAVE_THREADSAFE */ @@ -360,6 +357,9 @@ /* Define to 1 if you have Win32 threads */ #cmakedefine H5_HAVE_WIN_THREADS @H5_HAVE_WIN_THREADS@ +/* Define to 1 if you have C11 threads */ +#cmakedefine H5_HAVE_C11_THREADS @H5_HAVE_C11_THREADS@ + /* Define if your system has Windows-style path name. */ #cmakedefine H5_HAVE_WINDOW_PATH @H5_HAVE_WINDOW_PATH@ @@ -589,10 +589,6 @@ /* Define if strict file format checks are enabled */ #cmakedefine H5_STRICT_FORMAT_CHECKS @H5_STRICT_FORMAT_CHECKS@ -/* Define if your system supports pthread_attr_setscope(&attribute, - PTHREAD_SCOPE_SYSTEM) call. */ -#cmakedefine H5_SYSTEM_SCOPE_THREADS @H5_SYSTEM_SCOPE_THREADS@ - /* Define using v1.6 public API symbols by default */ #cmakedefine H5_USE_16_API_DEFAULT @H5_USE_16_API_DEFAULT@ diff --git a/config/cmake/HDF5DeveloperBuild.cmake b/config/cmake/HDF5DeveloperBuild.cmake index a8c128d7e70..07c9acab301 100644 --- a/config/cmake/HDF5DeveloperBuild.cmake +++ b/config/cmake/HDF5DeveloperBuild.cmake @@ -163,6 +163,12 @@ if (HDF5_ENABLE_DEBUG_H5FS_ASSERT) list (APPEND HDF5_DEBUG_APIS H5FS_DEBUG_ASSERT) endif () +option (HDF5_ENABLE_DEBUG_H5TS "Enable debugging of H5TS module" OFF) +mark_as_advanced (HDF5_ENABLE_DEBUG_H5TS) +if (HDF5_ENABLE_DEBUG_H5TS) + list (APPEND HDF5_DEBUG_APIS H5TS_DEBUG) +endif () + # If HDF5 free list debugging wasn't specifically enabled, disable # free lists entirely for developer build modes, as they can # make certain types of issues (like references to stale pointers) diff --git a/config/cmake/HDFTests.c b/config/cmake/HDFTests.c index 042a34dfd51..ae646ee051b 100644 --- a/config/cmake/HDFTests.c +++ b/config/cmake/HDFTests.c @@ -64,23 +64,21 @@ SIMPLE_TEST(timezone = 0); #endif /* HAVE_TIMEZONE */ -#ifdef SYSTEM_SCOPE_THREADS -#include +#ifdef PTHREAD_BARRIER #include int main(void) { - pthread_attr_t attribute; + pthread_barrier_t barr; int ret; - pthread_attr_init(&attribute); - ret = pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM); + ret = pthread_barrier_init(&barr, NULL, 1); if (ret == 0) return 0; return 1; } -#endif /* SYSTEM_SCOPE_THREADS */ +#endif /* PTHREAD_BARRIER */ #ifdef HAVE_SOCKLEN_T diff --git a/config/cmake/hdf5-config.cmake.in b/config/cmake/hdf5-config.cmake.in index c052762a195..5ee4d85e4d3 100644 --- a/config/cmake/hdf5-config.cmake.in +++ b/config/cmake/hdf5-config.cmake.in @@ -46,6 +46,7 @@ set (${HDF5_PACKAGE_NAME}_INSTALL_MOD_FORTRAN "@HDF5_INSTALL_MOD_FORTRAN@") set (${HDF5_PACKAGE_NAME}_BUILD_HL_LIB @HDF5_BUILD_HL_LIB@) set (${HDF5_PACKAGE_NAME}_BUILD_SHARED_LIBS @H5_ENABLE_SHARED_LIB@) set (${HDF5_PACKAGE_NAME}_BUILD_STATIC_LIBS @H5_ENABLE_STATIC_LIB@) +set (${HDF5_PACKAGE_NAME}_ENABLE_THREADS @HDF5_ENABLE_THREADS@) set (${HDF5_PACKAGE_NAME}_ENABLE_THREADSAFE @HDF5_ENABLE_THREADSAFE@) set (${HDF5_PACKAGE_NAME}_ENABLE_PARALLEL @HDF5_ENABLE_PARALLEL@) set (${HDF5_PACKAGE_NAME}_DEFAULT_API_VERSION "@DEFAULT_API_VERSION@") diff --git a/config/gnu-warnings/error-general b/config/gnu-warnings/error-general index 73d1dd564cb..406bfd6e6c1 100644 --- a/config/gnu-warnings/error-general +++ b/config/gnu-warnings/error-general @@ -38,8 +38,6 @@ # Here is a list of tests and examples that have issues with the stricter warnings as error # # NOTE: Test files are not compatible with these warnings as errors -# thread_id.c, -# -Werror=unused-function # dsets.c # -Werror=unused-parameter # external.c,perform/sio_engine.c diff --git a/configure.ac b/configure.ac index a150c4930f2..d2406ed81d2 100644 --- a/configure.ac +++ b/configure.ac @@ -2140,78 +2140,42 @@ AM_CONDITIONAL([BUILD_SHARED_SZIP_CONDITIONAL], [test "X$USE_FILTER_SZIP" = "Xye AC_CACHE_SAVE ## ---------------------------------------------------------------------- -## Enable thread-safe version of library. It requires Pthreads support -## on POSIX systems. +## Enable library-internal concurrency capabilities (threads, atomic operations, +## mutual exclusion, condition variables, and thread-specific storage). ## -AC_SUBST([THREADSAFE]) - -## Default is no thread-safety -THREADSAFE=no - -AC_MSG_CHECKING([for thread safe support]) -AC_ARG_ENABLE([threadsafe], - [AS_HELP_STRING([--enable-threadsafe], - [Enable thread-safe capability. Not compatible with the high-level library, Fortran, or C++ wrappers. - [default=no]])], - [THREADSAFE=$enableval]) - -## The high-level, C++, Fortran and Java interfaces are not compatible -## with the thread-safety option because the lock is not hoisted -## into the higher-level API calls. - -## --enable-threadsafe is incompatible with --enable-hl unless -## --enable-unsupported has been specified on the configure line. +## Note, this is required for enabling the threadsafe API feature, but does +## _not_ enable it, or provide threadsafety to applications calling HDF5 API +## routines. *Both* --enable-threads _and_ --enable-threadsafe must be provided +## to enable threadsafety to applications. ## -## Note that the high-level library is enabled by default so most -## users will have to add --disable-hl to the configure options. -if test "X${ALLOW_UNSUPPORTED}" != "Xyes"; then - if test "X${HDF5_HL}" = "Xyes" -a "X${enable_threadsafe}" = "Xyes"; then - AC_MSG_ERROR([The thread-safe library is incompatible with the high-level library. --disable-hl can be used to prevent building the high-level library (recommended). Alternatively, --enable-unsupported will allow building the high-level library, though this configuration is not supported by The HDF Group.]) - fi -fi - -## The --enable-threadsafe flag is not compatible with --enable-cxx. -## If the user tried to specify both flags, throw an error, unless -## they also provided the --enable-unsupported flag. -if test "X${ALLOW_UNSUPPORTED}" != "Xyes"; then - if test "X${HDF_CXX}" = "Xyes" -a "X${enable_threadsafe}" = "Xyes"; then - AC_MSG_ERROR([--enable-cxx and --enable-threadsafe flags are incompatible. Use --enable-unsupported to override this error.]) - fi -fi - -## --enable-threadsafe is also incompatible with --enable-fortran unless -## --enable-unsupported has been specified on the configure line. -if test "X${ALLOW_UNSUPPORTED}" != "Xyes"; then - if test "X${HDF_FORTRAN}" = "Xyes" -a "X${enable_threadsafe}" = "Xyes"; then - AC_MSG_ERROR([--enable-fortran and --enable-threadsafe flags are incompatible. Use --enable-unsupported to override this error.]) - fi -fi +## C11 threads (and atomics) are preferred and if available, will be chosen +## over pthreads or Windows threading. +## +## We usually pick up the system Pthreads library, so --with-pthread +## is only necessary if you are using a custom Pthreads library or if +## your OS hides its implementation in an unusual location. +## +## On Windows, we use Win32 threads and no special configuration should be +## required to use them. +## +AC_SUBST([THREADS]) -## --enable-threadsafe is also incompatible with --enable-java unless -## --enable-unsupported has been specified on the configure line. -if test "X${ALLOW_UNSUPPORTED}" != "Xyes"; then - if test "X${HDF_JAVA}" = "Xyes" -a "X${enable_threadsafe}" = "Xyes"; then - AC_MSG_ERROR([--enable-java and --enable-threadsafe flags are incompatible. Use --enable-unsupported to override this error.]) - fi +## Default is to attempt to enable thread support +if test "X-$THREADS" = X- ; then + THREADS=yes fi -case "X-$THREADSAFE" in - X-|X-no) - AC_MSG_RESULT([no]) - ;; - X-yes) - THREADSAFE=yes - AC_MSG_RESULT([yes]) - ;; - *) - AC_MSG_RESULT([error]) - AC_MSG_ERROR([\'$enableval\' is not a valid threadsafe type]) - ;; -esac +AC_ARG_ENABLE([threads], + [AS_HELP_STRING([--enable-threads], + [Enable threads capability. A prerequisite for enabling threadsafe API calls. + [default=yes] + ])], + [THREADS=$enableval]) -if test "X$THREADSAFE" = "Xyes"; then - AC_DEFINE([HAVE_THREADSAFE], [1], [Define if we have thread safe support]) +if test "X$THREADS" = "Xyes"; then + ## Allow other thread packages instead of Pthreads + allow_pthreads="yes" ## ---------------------------------------------------------------------- ## Is the Pthreads library present? It has a header file `pthread.h' and @@ -2221,30 +2185,50 @@ if test "X$THREADSAFE" = "Xyes"; then ## be preceded by a comma. ## ## The default is to use Pthreads when building with the Autotools, unless - ## we're building w/ MinGW. + ## the compiler has C11 threads or we're building w/ MinGW. + ## + ## Currently C11 threads will take priority over Pthreads and Win32 threads + ## AC_SUBST([HAVE_PTHREAD]) HAVE_PTHREAD=yes AC_ARG_WITH([pthread], [AS_HELP_STRING([--with-pthread=DIR], [Specify alternative path to Pthreads library when thread-safe capability is built. Set this to 'yes' or the location of Pthreads when building + with a compiler that has C11 threads or with MinGW and you would rather use Pthreads - than Win32 threads.])],, + than C11 or Win32 threads.])],, [withval=default]) + ## Check for C11 threads, we want to use C11 threads unless the builder + ## explicitly specifies --with-pthreads=(yes | path(s)) + AC_SUBST([HAVE_C11_THREADS]) HAVE_C11_THREADS=no + # Comment out check for C11 threads for now, since it conflicts with the + # current --std=c99 compile flags at configuration time. When we switch to + # --std=c11, this can be uncommented. + #AC_CHECK_HEADERS([threads.h],[HAVE_THREADS_H="yes"],[HAVE_THREADS_H="no"]) + if test "x$HAVE_THREADS_H" = "xyes"; then + # Default or no --> C11 threads + if test "$withval" = "default" -o "$withval" = "no" ; then + allow_pthreads="no" + unset HAVE_PTHREAD + AC_DEFINE([HAVE_C11_THREADS], [yes], [Define to yes if you have C11 threads]) + fi + fi + ## If we're on MinGW, we want to use Win32 threads unless the builder ## explicitly specifies --with-pthreads=(yes | path(s)) - mingw_use_win32_threads="no" + AC_SUBST([HAVE_WIN_THREADS]) HAVE_WIN_THREADS=no if test -n "$HDF_MINGW" -a "$HDF_MINGW" = "yes" ; then # Default or no --> Win32 threads if test "$withval" = "default" -o "$withval" = "no" ; then - mingw_use_win32_threads="yes" + allow_pthreads="no" unset HAVE_PTHREAD - AC_DEFINE([HAVE_WIN_THREADS], [1], [Define to 1 if you have win32 threads]) + AC_DEFINE([HAVE_WIN_THREADS], [yes], [Define to yes if you have win32 threads]) fi fi - if test "$mingw_use_win32_threads" = "no" ; then + if test "$allow_pthreads" = "yes" ; then case "$withval" in default | yes) AC_CHECK_HEADERS([pthread.h],, [unset HAVE_PTHREAD]) @@ -2294,44 +2278,115 @@ if test "X$THREADSAFE" = "Xyes"; then ;; esac - ## ---------------------------------------------------------------------- - ## Check if pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM) - ## is supported on this system - ## - ## Unfortunately, this probably needs to be an AC_RUN_IFELSE since - ## it's impossible to determine if PTHREAD_SCOPE_SYSTEM is - ## supported a priori. POSIX.1-2001 requires that a conformant - ## system need only support one of SYSTEM or PROCESS scopes. - ## - ## CROSS-COMPILING: Use a pessimistic 'no'. You can hand-hack the config - ## file if you know otherwise. - AC_MSG_CHECKING([Pthreads supports system scope]) - AC_CACHE_VAL([hdf5_cv_system_scope_threads], - [AC_RUN_IFELSE( - [AC_LANG_PROGRAM([ - #include - #include - ],[ - pthread_attr_t attribute; - int ret; - - pthread_attr_init(&attribute); - ret=pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM); - exit(ret==0 ? 0 : 1); - ])] - , [hdf5_cv_system_scope_threads=yes], [hdf5_cv_system_scope_threads=no], [hdf5_cv_system_scope_threads=no])]) - - if test ${hdf5_cv_system_scope_threads} = "yes"; then - AC_DEFINE([SYSTEM_SCOPE_THREADS], [1], - [Define if your system supports pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM) call.]) - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) - AC_MSG_NOTICE([Always 'no' if cross-compiling. Edit the config file if your platform supports pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM).]) + ## If using Pthreads, check for barrier routines + if test "x$HAVE_PTHREAD" = "xyes"; then + AC_CHECK_DECL([pthread_barrier_init], + [AC_DEFINE([HAVE_PTHREAD_BARRIER], [1], + [Define if has pthread_barrier_*() routines])], + [], + [[#include ]]) fi fi # end of Pthreads checks -fi # end of threadsafe processing + + ## Check for C11 atomics + AC_CHECK_HEADERS([stdatomic.h],[HAVE_STDATOMIC_H="yes"],[HAVE_STDATOMIC_H="no"]) + +fi # end of threads processing + +# Check to verify that a threading package was found +AC_MSG_CHECKING([for threads support]) +if test "x$HAVE_PTHREAD" = "xno" -a "x$HAVE_WIN_THREADS" = "xno" -a "x$HAVE_C11_THREADS" = "xno"; then + THREADS=no +fi + +case "X-$THREADS" in + X-no) + AC_MSG_RESULT([no]) + ;; + X-yes) + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_THREADS], [1], [Define if we have internal threads support]) + ;; + *) + AC_MSG_RESULT([error]) + AC_MSG_ERROR([\'$enableval\' is not a valid threads setting]) + ;; +esac + +## ---------------------------------------------------------------------- +## Enable thread-safe version of library (requires threads support) +## +AC_SUBST([THREADSAFE]) + +## Default is no thread-safety +THREADSAFE=no + +AC_MSG_CHECKING([for thread safe support]) +AC_ARG_ENABLE([threadsafe], + [AS_HELP_STRING([--enable-threadsafe], + [Enable thread-safe capability. Not compatible with the high-level library, Fortran, or C++ wrappers. + [default=no]])], + [THREADSAFE=$enableval]) + +## The high-level, C++, Fortran and Java interfaces are not compatible +## with the thread-safety option because the lock is not hoisted +## into the higher-level API calls. + +## --enable-threadsafe is incompatible with --enable-hl unless +## --enable-unsupported has been specified on the configure line. +## +## Note that the high-level library is enabled by default so most +## users will have to add --disable-hl to the configure options. +if test "X${ALLOW_UNSUPPORTED}" != "Xyes"; then + if test "X${HDF5_HL}" = "Xyes" -a "X${enable_threadsafe}" = "Xyes"; then + AC_MSG_ERROR([The thread-safe library is incompatible with the high-level library. --disable-hl can be used to prevent building the high-level library (recommended). Alternatively, --enable-unsupported will allow building the high-level library, though this configuration is not supported by The HDF Group.]) + fi +fi + +## The --enable-threadsafe flag is not compatible with --enable-cxx. +## If the user tried to specify both flags, throw an error, unless +## they also provided the --enable-unsupported flag. +if test "X${ALLOW_UNSUPPORTED}" != "Xyes"; then + if test "X${HDF_CXX}" = "Xyes" -a "X${enable_threadsafe}" = "Xyes"; then + AC_MSG_ERROR([--enable-cxx and --enable-threadsafe flags are incompatible. Use --enable-unsupported to override this error.]) + fi +fi + +## --enable-threadsafe is also incompatible with --enable-fortran unless +## --enable-unsupported has been specified on the configure line. +if test "X${ALLOW_UNSUPPORTED}" != "Xyes"; then + if test "X${HDF_FORTRAN}" = "Xyes" -a "X${enable_threadsafe}" = "Xyes"; then + AC_MSG_ERROR([--enable-fortran and --enable-threadsafe flags are incompatible. Use --enable-unsupported to override this error.]) + fi +fi + +## --enable-threadsafe is also incompatible with --enable-java unless +## --enable-unsupported has been specified on the configure line. +if test "X${ALLOW_UNSUPPORTED}" != "Xyes"; then + if test "X${HDF_JAVA}" = "Xyes" -a "X${enable_threadsafe}" = "Xyes"; then + AC_MSG_ERROR([--enable-java and --enable-threadsafe flags are incompatible. Use --enable-unsupported to override this error.]) + fi +fi + +if test "X${enable_threadsafe}" = "Xyes" -a "X$THREADS" = "Xno"; then + AC_MSG_ERROR([--enable-threadsafe given, but no threading package found.]) +fi + +case "X-$THREADSAFE" in + X-|X-no) + AC_MSG_RESULT([no]) + ;; + X-yes) + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_THREADSAFE], [1], [Define if we have thread safe support]) + ;; + *) + AC_MSG_RESULT([error]) + AC_MSG_ERROR([\'$enableval\' is not a valid threadsafe type]) + ;; +esac + ## ---------------------------------------------------------------------- ## Check for MONOTONIC_TIMER support (used in clock_gettime). This has @@ -2904,8 +2959,8 @@ AC_SUBST([INTERNAL_DEBUG_OUTPUT]) ## too specialized or have huge performance hits. These ## are not listed in the "all" packages list. ## -## all_packages="AC,B2,CX,D,F,FA,FL,FS,MM,O,T,Z" -all_packages="AC,B2,CX,D,F,MM,O,T,Z" +## all_packages="AC,B2,CX,D,F,FA,FL,FS,MM,O,T,TS,Z" +all_packages="AC,B2,CX,D,F,MM,O,T,TS,Z" case "X-$INTERNAL_DEBUG_OUTPUT" in X-yes|X-all) @@ -3447,11 +3502,9 @@ fi ## Check if Subfiling I/O driver is enabled by --enable-subfiling-vfd ## AC_SUBST([SUBFILING_VFD]) -AC_SUBST([HAVE_MERCURY]) ## Default is no subfiling VFD SUBFILING_VFD=no -HAVE_MERCURY="no" ## Always include subfiling directory so public header files are available H5_CPPFLAGS="$H5_CPPFLAGS -I$ac_abs_confdir/src/H5FDsubfiling" @@ -3497,40 +3550,11 @@ if test "X$SUBFILING_VFD" = "Xyes"; then ] ) - # Set-up mercury - HAVE_MERCURY="yes" - mercury_dir="$ac_abs_confdir/src/H5FDsubfiling/mercury" - mercury_inc="$mercury_dir/src/util" - - H5_CPPFLAGS="$H5_CPPFLAGS -I$mercury_inc" - - HAVE_STDATOMIC_H="yes" - AC_CHECK_HEADERS([stdatomic.h],,[HAVE_STDATOMIC_H="no"]) - if test "x$HAVE_STDATOMIC_H" = "xno"; then - AC_MSG_ERROR([Subfiling VFD requires atomic operations support. C11 stdatomic.h NOT available.]) + # Check for threads availability + if test "x$THREADS" = "xno"; then + AC_MSG_ERROR([Subfiling VFD requires thread operations support.]) fi - # Checks for libraries. - AC_SEARCH_LIBS([shm_open], [rt]) - AC_CHECK_LIB([pthread], [pthread_self],[], [echo "Error: Required library pthread not found." && exit 1]) - - # Perform various checks for Mercury util code - AC_CHECK_DECL([PTHREAD_MUTEX_ADAPTIVE_NP], - [AC_DEFINE([HAVE_PTHREAD_MUTEX_ADAPTIVE_NP], [1], - [Define if has PTHREAD_MUTEX_ADAPTIVE_NP])], - [], - [[#include ]]) - AC_CHECK_DECL([pthread_condattr_setclock], - [AC_DEFINE([HAVE_PTHREAD_CONDATTR_SETCLOCK], [1], - [Define if has pthread_condattr_setclock()])], - [], - [[#include ]]) - AC_CHECK_DECL([CLOCK_MONOTONIC_COARSE], - [AC_DEFINE([HAVE_CLOCK_MONOTONIC_COARSE], [1], - [Define if has CLOCK_MONOTONIC_COARSE])], - [], - [[#include ]]) - else AC_MSG_RESULT([no]) fi @@ -3539,8 +3563,6 @@ fi AM_CONDITIONAL([SUBFILING_VFD_CONDITIONAL], [test "X$SUBFILING_VFD" = "Xyes"]) # IOC VFD is currently only built when subfiling is enabled AM_CONDITIONAL([IOC_VFD_CONDITIONAL], [test "X$SUBFILING_VFD" = "Xyes"]) -# Mercury is currently only needed if subfiling is enabled -AM_CONDITIONAL([HAVE_MERCURY_CONDITIONAL], [test "X$HAVE_MERCURY" = "Xyes"]) ## ---------------------------------------------------------------------- diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt index 22e9eead0b6..d70881df143 100644 --- a/release_docs/RELEASE.txt +++ b/release_docs/RELEASE.txt @@ -47,6 +47,18 @@ New Features Configuration: ------------- + - Added configuration option for internal threading/concurrency support: + + CMake: HDF5_ENABLE_THREADS (ON/OFF) (Default: ON) + Autotools: --enable-threads (yes/no) (Default: yes) + + This option enables support for threading and concurrency algorithms + within the HDF5 library. It is required for, but separate from, the + 'threadsafe' configure option, which makes the HDF5 API safe to call from + multiple threads. It is possible to enable the 'threads' option and + disable the 'threadsafe' option, but not vice versa. The 'threads' option + must be on to enable the subfiling VFD. + - Moved examples to the HDF5Examples folder in the source tree. Moved the C++ and Fortran examples from the examples folder to the HDF5Examples diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d474b877f0..3fb87c63862 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -272,9 +272,8 @@ set (H5FD_HDRS ${HDF5_SRC_DIR}/H5FDwindows.h ) -# Append Subfiling VFD and Mercury sources to H5FD -# interface if Subfiling VFD is built. Append Subfiling -# VFD public headers to H5FD_HDRS regardless. +# Append Subfiling VFD sources to H5FD interface if it is built. +# Append Subfiling VFD public headers to H5FD_HDRS regardless. add_subdirectory (${H5FD_SUBFILING_DIR}) IDE_GENERATED_PROPERTIES ("H5FD" "${H5FD_HDRS}" "${H5FD_SOURCES}" ) @@ -641,6 +640,20 @@ IDE_GENERATED_PROPERTIES ("H5T" "${H5T_HDRS}" "${H5T_SOURCES}" ) set (H5TS_SOURCES ${HDF5_SRC_DIR}/H5TS.c + ${HDF5_SRC_DIR}/H5TSatomic.c + ${HDF5_SRC_DIR}/H5TSbarrier.c + ${HDF5_SRC_DIR}/H5TSc11.c + ${HDF5_SRC_DIR}/H5TScond.c + ${HDF5_SRC_DIR}/H5TSint.c + ${HDF5_SRC_DIR}/H5TSkey.c + ${HDF5_SRC_DIR}/H5TSmutex.c + ${HDF5_SRC_DIR}/H5TSonce.c + ${HDF5_SRC_DIR}/H5TSpool.c + ${HDF5_SRC_DIR}/H5TSpthread.c + ${HDF5_SRC_DIR}/H5TSrwlock.c + ${HDF5_SRC_DIR}/H5TSsemaphore.c + ${HDF5_SRC_DIR}/H5TSthread.c + ${HDF5_SRC_DIR}/H5TSwin.c ) set (H5TS_HDRS ${HDF5_SRC_DIR}/H5TSdevelop.h @@ -758,6 +771,7 @@ set (H5_MODULE_HEADERS ${HDF5_SRC_DIR}/H5SLmodule.h ${HDF5_SRC_DIR}/H5SMmodule.h ${HDF5_SRC_DIR}/H5Tmodule.h + ${HDF5_SRC_DIR}/H5TSmodule.h ${HDF5_SRC_DIR}/H5VLmodule.h ${HDF5_SRC_DIR}/H5Zmodule.h ) @@ -969,6 +983,7 @@ set (H5_PRIVATE_HEADERS ${HDF5_SRC_DIR}/H5Tpkg.h ${HDF5_SRC_DIR}/H5Tprivate.h + ${HDF5_SRC_DIR}/H5TSpkg.h ${HDF5_SRC_DIR}/H5TSprivate.h ${HDF5_SRC_DIR}/H5UCprivate.h @@ -1088,7 +1103,7 @@ if (BUILD_STATIC_LIBS) ) if (NOT WIN32) target_link_libraries (${HDF5_LIB_TARGET} - PRIVATE "$<$,$>:Threads::Threads>" + PRIVATE "$<$,$>:Threads::Threads>" ) endif () set_global_variable (HDF5_LIBRARIES_TO_EXPORT ${HDF5_LIB_TARGET}) @@ -1111,6 +1126,7 @@ if (BUILD_SHARED_LIBS) "H5_BUILT_AS_DYNAMIC_LIB" ${HDF_EXTRA_C_FLAGS} PRIVATE + "$<$:H5_HAVE_THREADS>" "$<$:H5_HAVE_THREADSAFE>" "$<$:H5_DEBUG_API>" # Enable tracing of the API "$<$:${HDF5_DEBUG_APIS}>" diff --git a/src/H5.c b/src/H5.c index d4521bd4c4a..9dc9fca1022 100644 --- a/src/H5.c +++ b/src/H5.c @@ -73,14 +73,9 @@ static int H5__mpi_delete_cb(MPI_Comm comm, int keyval, void *attr_val, int *fla static const unsigned VERS_RELEASE_EXCEPTIONS[] = {0}; static const unsigned VERS_RELEASE_EXCEPTIONS_SIZE = 1; -/* statically initialize block for pthread_once call used in initializing */ -/* the first global mutex */ -#ifdef H5_HAVE_THREADSAFE -H5_api_t H5_g; -#else +/* Library init / term status (global) */ bool H5_libinit_g = false; /* Library hasn't been initialized */ bool H5_libterm_g = false; /* Library isn't being shutdown */ -#endif char H5_lib_vers_info_g[] = H5_VERS_INFO; static bool H5_dont_atexit_g = false; @@ -213,13 +208,14 @@ H5_init_library(void) */ if (!H5_dont_atexit_g) { -#if defined(H5_HAVE_THREADSAFE) && defined(H5_HAVE_WIN_THREADS) - /* Clean up Win32 thread resources. Pthreads automatically cleans up. - * This must be entered before the library cleanup code so it's +#if defined(H5_HAVE_THREADSAFE) + /* Clean up thread resources. + * + * This must be pushed before the library cleanup code so it's * executed in LIFO order (i.e., last). */ - (void)atexit(H5TS_win32_process_exit); -#endif /* H5_HAVE_THREADSAFE && H5_HAVE_WIN_THREADS */ + (void)atexit(H5TS_term_package); +#endif /* H5_HAVE_THREADSAFE */ /* Normal library termination code */ (void)atexit(H5_term_library); @@ -302,14 +298,12 @@ H5_term_library(void) int nprinted; H5E_auto2_t func; -#ifdef H5_HAVE_THREADSAFE - /* explicit locking of the API */ - H5_FIRST_THREAD_INIT + /* Acquire the API lock */ + H5CANCEL_DECL H5_API_LOCK -#endif /* Don't do anything if the library is already closed */ - if (!(H5_INIT_GLOBAL)) + if (!H5_INIT_GLOBAL) goto done; /* Indicate that the library is being shut down */ @@ -506,9 +500,8 @@ H5_term_library(void) /* Don't pop the API context (i.e. H5CX_pop), since it's been shut down already */ done: -#ifdef H5_HAVE_THREADSAFE + /* Release API lock */ H5_API_UNLOCK -#endif /* H5_HAVE_THREADSAFE */ return; } /* end H5_term_library() */ @@ -1247,63 +1240,3 @@ H5is_library_terminating(bool *is_terminating /*out*/) FUNC_LEAVE_API_NOINIT(ret_value) } /* end H5is_library_terminating() */ - -#if defined(H5_HAVE_THREADSAFE) && defined(H5_BUILT_AS_DYNAMIC_LIB) && defined(H5_HAVE_WIN32_API) && \ - defined(H5_HAVE_WIN_THREADS) -/*------------------------------------------------------------------------- - * Function: DllMain - * - * Purpose: Handles various conditions in the library on Windows. - * - * NOTE: The main purpose of this is for handling Win32 thread cleanup - * on thread/process detach. - * - * Only enabled when the shared Windows library is built with - * thread safety enabled. - * - * Return: true on success, false on failure - * - *------------------------------------------------------------------------- - */ -BOOL WINAPI -DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved) -{ - /* Don't add our function enter/leave macros since this function will be - * called before the library is initialized. - * - * NOTE: Do NOT call any CRT functions in DllMain! - * This includes any functions that are called by from here! - */ - - BOOL fOkay = true; - - switch (fdwReason) { - case DLL_PROCESS_ATTACH: - break; - - case DLL_PROCESS_DETACH: - break; - - case DLL_THREAD_ATTACH: -#ifdef H5_HAVE_WIN_THREADS - if (H5TS_win32_thread_enter() < 0) - fOkay = false; -#endif /* H5_HAVE_WIN_THREADS */ - break; - - case DLL_THREAD_DETACH: -#ifdef H5_HAVE_WIN_THREADS - if (H5TS_win32_thread_exit() < 0) - fOkay = false; -#endif /* H5_HAVE_WIN_THREADS */ - break; - - default: - /* Shouldn't get here */ - fOkay = false; - break; - } - - return fOkay; -} -#endif /* H5_HAVE_WIN32_API && H5_BUILT_AS_DYNAMIC_LIB && H5_HAVE_WIN_THREADS && H5_HAVE_THREADSAFE*/ diff --git a/src/H5CX.c b/src/H5CX.c index 0e14eeadac1..643c77f9c31 100644 --- a/src/H5CX.c +++ b/src/H5CX.c @@ -42,15 +42,12 @@ #ifdef H5_HAVE_THREADSAFE /* - * The per-thread API context. pthread_once() initializes a special - * key that will be used by all threads to create a stack specific to - * each thread individually. The association of contexts to threads will - * be handled by the pthread library. + * The per-thread API context. * * In order for this macro to work, H5CX_get_my_context() must be preceded * by "H5CX_node_t **ctx =". */ -#define H5CX_get_my_context() H5CX__get_context() +#define H5CX_get_my_context() H5TS_get_api_ctx_ptr() #else /* H5_HAVE_THREADSAFE */ /* * The current API context. @@ -445,9 +442,6 @@ typedef struct H5CX_fapl_cache_t { /********************/ /* Local Prototypes */ /********************/ -#ifdef H5_HAVE_THREADSAFE -static H5CX_node_t **H5CX__get_context(void); -#endif /* H5_HAVE_THREADSAFE */ static void H5CX__push_common(H5CX_node_t *cnode); static H5CX_node_t *H5CX__pop_common(bool update_dxpl_props); @@ -728,55 +722,6 @@ H5CX_term_package(void) FUNC_LEAVE_NOAPI(0) } /* end H5CX_term_package() */ -#ifdef H5_HAVE_THREADSAFE -/*------------------------------------------------------------------------- - * Function: H5CX__get_context - * - * Purpose: Support function for H5CX_get_my_context() to initialize and - * acquire per-thread API context stack. - * - * Return: Success: Non-NULL pointer to head pointer of API context stack for thread - * Failure: NULL - * - *------------------------------------------------------------------------- - */ -static H5CX_node_t ** -H5CX__get_context(void) -{ - H5CX_node_t **ctx = NULL; - - FUNC_ENTER_PACKAGE_NOERR - - ctx = (H5CX_node_t **)H5TS_get_thread_local_value(H5TS_apictx_key_g); - - if (!ctx) { - /* No associated value with current thread - create one */ -#ifdef H5_HAVE_WIN_THREADS - /* Win32 has to use LocalAlloc to match the LocalFree in DllMain */ - ctx = (H5CX_node_t **)LocalAlloc(LPTR, sizeof(H5CX_node_t *)); -#else - /* Use malloc here since this has to match the free in the - * destructor and we want to avoid the codestack there. - */ - ctx = (H5CX_node_t **)malloc(sizeof(H5CX_node_t *)); -#endif /* H5_HAVE_WIN_THREADS */ - assert(ctx); - - /* Reset the thread-specific info */ - *ctx = NULL; - - /* (It's not necessary to release this in this API, it is - * released by the "key destructor" set up in the H5TS - * routines. See calls to pthread_key_create() in H5TS.c -QAK) - */ - H5TS_set_thread_local_value(H5TS_apictx_key_g, (void *)ctx); - } /* end if */ - - /* Set return value */ - FUNC_LEAVE_NOAPI(ctx) -} /* end H5CX__get_context() */ -#endif /* H5_HAVE_THREADSAFE */ - /*------------------------------------------------------------------------- * Function: H5CX_pushed * @@ -1745,7 +1690,6 @@ H5CX_get_ring(void) } /* end H5CX_get_ring() */ #ifdef H5_HAVE_PARALLEL - /*------------------------------------------------------------------------- * Function: H5CX_get_coll_metadata_read * diff --git a/src/H5Eint.c b/src/H5Eint.c index 9590f3cbbee..d01668e731d 100644 --- a/src/H5Eint.c +++ b/src/H5Eint.c @@ -120,6 +120,60 @@ char H5E_mpi_error_str[MPI_MAX_ERROR_STRING]; int H5E_mpi_error_str_len; #endif /* H5_HAVE_PARALLEL */ +/* Default value to initialize error stacks */ +static const H5E_stack_t H5E_err_stack_def = { + 0, /* nused */ + { /*entries[] */ + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}, + {false, {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}}}, + +/* H5E_auto_op_t */ +#ifndef H5_NO_DEPRECATED_SYMBOLS +#ifdef H5_USE_16_API_DEFAULT + {1, TRUE, (H5E_auto1_t)H5Eprint1, (H5E_auto2_t)H5E__print2, (H5E_auto1_t)H5Eprint1, + (H5E_auto2_t)H5E__print2}, +#else /* H5_USE_16_API */ + {2, TRUE, (H5E_auto1_t)H5Eprint1, (H5E_auto2_t)H5E__print2, (H5E_auto1_t)H5Eprint1, + (H5E_auto2_t)H5E__print2}, +#endif /* H5_USE_16_API_DEFAULT */ +#else /* H5_NO_DEPRECATED_SYMBOLS */ + {(H5E_auto2_t)H5E__print2}, +#endif /* H5_NO_DEPRECATED_SYMBOLS */ + + NULL, /* auto_data */ + 0 /* paused */ +}; + /* First & last major and minor error codes registered by the library */ hid_t H5E_first_maj_id_g = H5I_INVALID_HID; hid_t H5E_last_maj_id_g = H5I_INVALID_HID; @@ -280,56 +334,6 @@ H5E_term_package(void) FUNC_LEAVE_NOAPI(n) } /* end H5E_term_package() */ -#ifdef H5_HAVE_THREADSAFE -/*------------------------------------------------------------------------- - * Function: H5E__get_stack - * - * Purpose: Support function for H5E__get_my_stack() to initialize and - * acquire per-thread error stack. - * - * Return: Success: Pointer to an error stack struct (H5E_t *) - * - * Failure: NULL - * - *------------------------------------------------------------------------- - */ -H5E_stack_t * -H5E__get_stack(void) -{ - H5E_stack_t *estack = NULL; - - FUNC_ENTER_PACKAGE_NOERR - - estack = (H5E_stack_t *)H5TS_get_thread_local_value(H5TS_errstk_key_g); - - if (!estack) { - /* No associated value with current thread - create one */ -#ifdef H5_HAVE_WIN_THREADS - /* Win32 has to use LocalAlloc to match the LocalFree in DllMain */ - estack = (H5E_stack_t *)LocalAlloc(LPTR, sizeof(H5E_stack_t)); -#else - /* Use malloc here since this has to match the free in the - * destructor and we want to avoid the codestack there. - */ - estack = (H5E_stack_t *)malloc(sizeof(H5E_stack_t)); -#endif /* H5_HAVE_WIN_THREADS */ - assert(estack); - - /* Set the thread-specific info */ - H5E__set_default_auto(estack); - - /* (It's not necessary to release this in this API, it is - * released by the "key destructor" set up in the H5TS - * routines. See calls to pthread_key_create() in H5TS.c -QAK) - */ - H5TS_set_thread_local_value(H5TS_errstk_key_g, (void *)estack); - } /* end if */ - - /* Set return value */ - FUNC_LEAVE_NOAPI(estack) -} /* end H5E__get_stack() */ -#endif /* H5_HAVE_THREADSAFE */ - /*------------------------------------------------------------------------- * Function: H5E__free_class * @@ -830,24 +834,8 @@ H5E__set_default_auto(H5E_stack_t *stk) { FUNC_ENTER_PACKAGE_NOERR - stk->nused = 0; - -#ifndef H5_NO_DEPRECATED_SYMBOLS -#ifdef H5_USE_16_API_DEFAULT - stk->auto_op.vers = 1; -#else /* H5_USE_16_API */ - stk->auto_op.vers = 2; -#endif /* H5_USE_16_API_DEFAULT */ - - stk->auto_op.func1 = stk->auto_op.func1_default = (H5E_auto1_t)H5Eprint1; - stk->auto_op.func2 = stk->auto_op.func2_default = (H5E_auto2_t)H5E__print2; - stk->auto_op.is_default = true; -#else /* H5_NO_DEPRECATED_SYMBOLS */ - stk->auto_op.func2 = (H5E_auto2_t)H5E__print2; -#endif /* H5_NO_DEPRECATED_SYMBOLS */ - - stk->auto_data = NULL; - stk->paused = 0; + /* Initialize with default error stack */ + memcpy(stk, &H5E_err_stack_def, sizeof(H5E_err_stack_def)); FUNC_LEAVE_NOAPI_VOID } /* end H5E__set_default_auto() */ @@ -933,7 +921,10 @@ H5E__walk1_cb(int n, H5E_error1_t *err_desc, void *client_data) const char *maj_str = "No major description"; /* Major error description */ const char *min_str = "No minor description"; /* Minor error description */ bool have_desc = true; /* Flag to indicate whether the error has a "real" description */ - herr_t ret_value = SUCCEED; +#ifdef H5_HAVE_THREADSAFE + uint64_t thread_id = 0; /* ID of thread */ +#endif + herr_t ret_value = SUCCEED; FUNC_ENTER_PACKAGE_NOERR @@ -962,6 +953,11 @@ H5E__walk1_cb(int n, H5E_error1_t *err_desc, void *client_data) /* Get error class info */ cls_ptr = maj_ptr->cls; +#ifdef H5_HAVE_THREADSAFE + if (H5TS_thread_id(&thread_id) < 0) + HGOTO_DONE(FAIL); +#endif + /* Print error class header if new class */ if (eprint->cls.lib_name == NULL || strcmp(cls_ptr->lib_name, eprint->cls.lib_name) != 0) { /* update to the new class information */ @@ -991,12 +987,12 @@ H5E__walk1_cb(int n, H5E_error1_t *err_desc, void *client_data) } /* end if */ #ifdef H5_HAVE_THREADSAFE else - fprintf(stream, " thread %" PRIu64, H5TS_thread_id()); + fprintf(stream, " thread %" PRIu64, thread_id); #endif } /* end block */ #else #ifdef H5_HAVE_THREADSAFE - fprintf(stream, " thread %" PRIu64, H5TS_thread_id()); + fprintf(stream, " thread %" PRIu64, thread_id); #endif #endif fprintf(stream, ":\n"); @@ -1056,7 +1052,10 @@ H5E__walk2_cb(unsigned n, const H5E_error2_t *err_desc, void *client_data) const char *maj_str = "No major description"; /* Major error description */ const char *min_str = "No minor description"; /* Minor error description */ bool have_desc = true; /* Flag to indicate whether the error has a "real" description */ - herr_t ret_value = SUCCEED; +#ifdef H5_HAVE_THREADSAFE + uint64_t thread_id = 0; /* ID of thread */ +#endif + herr_t ret_value = SUCCEED; FUNC_ENTER_PACKAGE_NOERR @@ -1090,6 +1089,11 @@ H5E__walk2_cb(unsigned n, const H5E_error2_t *err_desc, void *client_data) if (!cls_ptr) HGOTO_DONE(FAIL); +#ifdef H5_HAVE_THREADSAFE + if (H5TS_thread_id(&thread_id) < 0) + HGOTO_DONE(FAIL); +#endif + /* Print error class header if new class */ if (eprint->cls.lib_name == NULL || strcmp(cls_ptr->lib_name, eprint->cls.lib_name) != 0) { /* update to the new class information */ @@ -1119,12 +1123,12 @@ H5E__walk2_cb(unsigned n, const H5E_error2_t *err_desc, void *client_data) } /* end if */ #ifdef H5_HAVE_THREADSAFE else - fprintf(stream, " thread %" PRIu64, H5TS_thread_id()); + fprintf(stream, " thread %" PRIu64, thread_id); #endif } /* end block */ #else #ifdef H5_HAVE_THREADSAFE - fprintf(stream, " thread %" PRIu64, H5TS_thread_id()); + fprintf(stream, " thread %" PRIu64, thread_id); #endif #endif fprintf(stream, ":\n"); diff --git a/src/H5Epkg.h b/src/H5Epkg.h index 1577939c184..b758cbe4ec9 100644 --- a/src/H5Epkg.h +++ b/src/H5Epkg.h @@ -39,15 +39,12 @@ #ifdef H5_HAVE_THREADSAFE /* - * The per-thread error stack. pthread_once() initializes a special - * key that will be used by all threads to create a stack specific to - * each thread individually. The association of stacks to threads will - * be handled by the pthread library. + * The per-thread error stack. * * In order for this macro to work, H5E__get_my_stack() must be preceded * by "H5E_stack_t *estack =". */ -#define H5E__get_my_stack() H5E__get_stack() +#define H5E__get_my_stack() H5TS_get_err_stack() #else /* H5_HAVE_THREADSAFE */ /* * The current error stack. @@ -137,9 +134,6 @@ H5_DLLVAR hid_t H5E_last_min_id_g; /******************************/ /* Package Private Prototypes */ /******************************/ -#ifdef H5_HAVE_THREADSAFE -H5_DLL H5E_stack_t *H5E__get_stack(void); -#endif /* H5_HAVE_THREADSAFE */ H5_DLL H5E_cls_t *H5E__register_class(const char *cls_name, const char *lib_name, const char *version); H5_DLL ssize_t H5E__get_class_name(const H5E_cls_t *cls, char *name, size_t size); H5_DLL H5E_msg_t *H5E__create_msg(H5E_cls_t *cls, H5E_type_t msg_type, const char *msg); diff --git a/src/H5FDsubfiling/CMakeLists.txt b/src/H5FDsubfiling/CMakeLists.txt index dd7f564f402..0fd1f5759ee 100644 --- a/src/H5FDsubfiling/CMakeLists.txt +++ b/src/H5FDsubfiling/CMakeLists.txt @@ -14,17 +14,6 @@ if (HDF5_ENABLE_SUBFILING_VFD) if (NOT H5FD_SOURCES) message (FATAL_ERROR "internal configure error - H5FD_SOURCES not set") endif () - if (NOT H5FD_SUBFILING_MERCURY_DIR) - message (FATAL_ERROR "internal configure error - H5FD_SUBFILING_MERCURY_DIR not set") - endif () - - # Add mercury util sources to Subfiling VFD sources - set (MERCURY_UTIL_SOURCES - ${H5FD_SUBFILING_MERCURY_DIR}/mercury_thread.c - ${H5FD_SUBFILING_MERCURY_DIR}/mercury_thread_condition.c - ${H5FD_SUBFILING_MERCURY_DIR}/mercury_thread_pool.c - ${H5FD_SUBFILING_MERCURY_DIR}/mercury_thread_mutex.c - ) set (HDF5_H5FD_SUBFILING_SOURCES ${H5FD_SUBFILING_DIR}/H5FDioc.c @@ -33,7 +22,6 @@ if (HDF5_ENABLE_SUBFILING_VFD) ${H5FD_SUBFILING_DIR}/H5FDsubfiling.c ${H5FD_SUBFILING_DIR}/H5FDsubfile_int.c ${H5FD_SUBFILING_DIR}/H5subfiling_common.c - ${MERCURY_UTIL_SOURCES} ) # Add Subfiling VFD sources to HDF5 library's H5FD sources diff --git a/src/H5FDsubfiling/H5FDioc.h b/src/H5FDsubfiling/H5FDioc.h index bcacd52252d..8f0255c5583 100644 --- a/src/H5FDsubfiling/H5FDioc.h +++ b/src/H5FDsubfiling/H5FDioc.h @@ -174,18 +174,6 @@ H5_DLL herr_t H5Pset_fapl_ioc(hid_t fapl_id, H5FD_ioc_config_t *vfd_config); * */ H5_DLL herr_t H5Pget_fapl_ioc(hid_t fapl_id, H5FD_ioc_config_t *config_out); -/** - * \brief Internal routine for managing exclusive access to critical sections - * by the #H5FD_IOC driver's worker threads. Not meant to be called - * directly by an HDF5 application - */ -H5_DLL void H5FD_ioc_begin_thread_exclusive(void); -/** - * \brief Internal routine for managing exclusive access to critical sections - * by the #H5FD_IOC driver's worker threads. Not meant to be called - * directly by an HDF5 application - */ -H5_DLL void H5FD_ioc_end_thread_exclusive(void); #ifdef __cplusplus } diff --git a/src/H5FDsubfiling/H5FDioc_priv.h b/src/H5FDsubfiling/H5FDioc_priv.h index eeaeb6a955e..2f17dbe221d 100644 --- a/src/H5FDsubfiling/H5FDioc_priv.h +++ b/src/H5FDsubfiling/H5FDioc_priv.h @@ -17,12 +17,6 @@ #ifndef H5FDioc_priv_H #define H5FDioc_priv_H -/********************/ -/* Standard Headers */ -/********************/ - -#include - /**************/ /* H5 Headers */ /**************/ @@ -35,13 +29,10 @@ #include "H5Iprivate.h" /* IDs */ #include "H5MMprivate.h" /* Memory management */ #include "H5Pprivate.h" /* Property lists */ +#include "H5TSprivate.h" /* Threadsafety */ #include "H5subfiling_common.h" -#include "mercury_thread.h" -#include "mercury_thread_mutex.h" -#include "mercury_thread_pool.h" - /* * Some definitions for debugging the IOC VFD */ @@ -211,9 +202,8 @@ typedef struct ioc_io_queue_entry { bool in_progress; uint32_t counter; - sf_work_request_t wk_req; - struct hg_thread_work thread_wk; - int wk_ret; + sf_work_request_t wk_req; + int wk_ret; /* statistics */ #ifdef H5FD_IOC_COLLECT_STATS @@ -369,7 +359,7 @@ typedef struct ioc_io_queue { int32_t num_failed; int32_t q_len; uint32_t req_counter; - hg_thread_mutex_t q_mutex; + H5TS_mutex_t q_mutex; /* statistics */ #ifdef H5FD_IOC_COLLECT_STATS diff --git a/src/H5FDsubfiling/H5FDioc_threads.c b/src/H5FDsubfiling/H5FDioc_threads.c index 2f6bed7ebc2..da110e72bd7 100644 --- a/src/H5FDsubfiling/H5FDioc_threads.c +++ b/src/H5FDsubfiling/H5FDioc_threads.c @@ -28,13 +28,13 @@ * file's subfiling context object */ typedef struct ioc_data_t { - ioc_io_queue_t io_queue; - hg_thread_t ioc_main_thread; - hg_thread_pool_t *io_thread_pool; - int64_t sf_context_id; + ioc_io_queue_t io_queue; + H5TS_thread_t ioc_main_thread; + H5TS_pool_t *io_thread_pool; + int64_t sf_context_id; - atomic_int sf_ioc_ready; - atomic_int sf_shutdown_flag; + H5TS_atomic_int_t sf_ioc_ready; + H5TS_atomic_int_t sf_shutdown_flag; /* sf_io_ops_pending tracks the number of I/O operations pending so that we can wait * until all I/O operations have been serviced before shutting down the worker thread pool. * The value of this variable must always be non-negative. @@ -42,22 +42,10 @@ typedef struct ioc_data_t { * Note that this is a convenience variable -- we could use io_queue.q_len instead. * However, accessing this field requires locking io_queue.q_mutex. */ - atomic_int sf_io_ops_pending; - atomic_int sf_work_pending; + H5TS_atomic_int_t sf_io_ops_pending; + H5TS_atomic_int_t sf_work_pending; } ioc_data_t; -/* - * NOTES: - * Rather than re-create the code for creating and managing a thread pool, - * I'm utilizing a reasonably well tested implementation from the mercury - * project. At some point, we should revisit this decision or possibly - * directly link against the mercury library. This would make sense if - * we move away from using MPI as the messaging infrastructure and instead - * use mercury for that purpose... - */ - -static hg_thread_mutex_t ioc_thread_mutex = PTHREAD_MUTEX_INITIALIZER; - #ifdef H5FD_IOC_COLLECT_STATS static int sf_write_ops = 0; static int sf_read_ops = 0; @@ -68,7 +56,7 @@ static double sf_queue_delay_time = 0.0; #endif /* Prototypes */ -static HG_THREAD_RETURN_TYPE H5FD__ioc_thread_main(void *arg); +static H5TS_THREAD_RETURN_TYPE H5FD__ioc_thread_main(void *arg); static int H5FD__ioc_file_queue_write_indep(sf_work_request_t *msg, int ioc_idx, int source, MPI_Comm comm, uint32_t counter); @@ -123,45 +111,25 @@ H5FD__ioc_init_threads(void *_sf_context) HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "can't allocate IOC data for IOC main thread"); ioc_data->sf_context_id = sf_context->sf_context_id; ioc_data->io_thread_pool = NULL; - ioc_data->io_queue = (ioc_io_queue_t){/* magic = */ H5FD_IOC__IO_Q_MAGIC, - /* q_head = */ NULL, - /* q_tail = */ NULL, - /* num_pending = */ 0, - /* num_in_progress = */ 0, - /* num_failed = */ 0, - /* q_len = */ 0, - /* req_counter = */ 0, - /* q_mutex = */ - PTHREAD_MUTEX_INITIALIZER, -#ifdef H5FD_IOC_COLLECT_STATS - /* max_q_len = */ 0, - /* max_num_pending = */ 0, - /* max_num_in_progress = */ 0, - /* ind_read_requests = */ 0, - /* ind_write_requests = */ 0, - /* truncate_requests = */ 0, - /* get_eof_requests = */ 0, - /* requests_queued = */ 0, - /* requests_dispatched = */ 0, - /* requests_completed = */ 0 -#endif - }; - - sf_context->ioc_data = ioc_data; /* Initialize atomic vars */ - atomic_init(&ioc_data->sf_ioc_ready, 0); - atomic_init(&ioc_data->sf_shutdown_flag, 0); - atomic_init(&ioc_data->sf_io_ops_pending, 0); - atomic_init(&ioc_data->sf_work_pending, 0); + H5TS_atomic_init_int(&ioc_data->sf_ioc_ready, 0); + H5TS_atomic_init_int(&ioc_data->sf_shutdown_flag, 0); + H5TS_atomic_init_int(&ioc_data->sf_io_ops_pending, 0); + H5TS_atomic_init_int(&ioc_data->sf_work_pending, 0); + + /* Initialize I/O queue */ + memset(&ioc_data->io_queue, 0, sizeof(ioc_data->io_queue)); + ioc_data->io_queue.magic = H5FD_IOC__IO_Q_MAGIC; + if (H5TS_mutex_init(&ioc_data->io_queue.q_mutex, H5TS_MUTEX_TYPE_PLAIN) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize IOC thread queue mutex"); + + sf_context->ioc_data = ioc_data; #ifdef H5FD_IOC_COLLECT_STATS t_start = MPI_Wtime(); #endif - if (hg_thread_mutex_init(&ioc_data->io_queue.q_mutex) < 0) - HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize IOC thread queue mutex"); - /* Allow experimentation with the number of helper threads */ if ((env_value = getenv(H5FD_IOC_THREAD_POOL_SIZE)) != NULL) { int value_check = atoi(env_value); @@ -170,15 +138,15 @@ H5FD__ioc_init_threads(void *_sf_context) } /* Initialize a thread pool for the I/O concentrator's worker threads */ - if (hg_thread_pool_init(thread_pool_size, &ioc_data->io_thread_pool) < 0) + if (H5TS_pool_create(&ioc_data->io_thread_pool, thread_pool_size) < 0) HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't initialize IOC worker thread pool"); /* Create the main IOC thread that will receive and dispatch I/O requests */ - if (hg_thread_create(&ioc_data->ioc_main_thread, H5FD__ioc_thread_main, ioc_data) < 0) + if (H5TS_thread_create(&ioc_data->ioc_main_thread, H5FD__ioc_thread_main, ioc_data) < 0) HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "can't create IOC main thread"); /* Wait until H5FD__ioc_thread_main() reports that it is ready */ - while (atomic_load(&ioc_data->sf_ioc_ready) != 1) + while (H5TS_atomic_load_int(&ioc_data->sf_ioc_ready) != 1) usleep(20); #ifdef H5FD_IOC_COLLECT_STATS @@ -211,24 +179,30 @@ H5FD__ioc_finalize_threads(void *_sf_context) ioc_data = sf_context->ioc_data; if (ioc_data) { - assert(0 == atomic_load(&ioc_data->sf_shutdown_flag)); + assert(0 == H5TS_atomic_load_int(&ioc_data->sf_shutdown_flag)); /* Shutdown the main IOC thread */ - atomic_store(&ioc_data->sf_shutdown_flag, 1); + H5TS_atomic_store_int(&ioc_data->sf_shutdown_flag, 1); /* Allow H5FD__ioc_thread_main to exit.*/ do { usleep(20); - } while (0 != atomic_load(&ioc_data->sf_shutdown_flag)); + } while (0 != H5TS_atomic_load_int(&ioc_data->sf_shutdown_flag)); /* Tear down IOC worker thread pool */ - assert(0 == atomic_load(&ioc_data->sf_io_ops_pending)); - hg_thread_pool_destroy(ioc_data->io_thread_pool); + assert(0 == H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)); + H5TS_pool_destroy(ioc_data->io_thread_pool); - hg_thread_mutex_destroy(&ioc_data->io_queue.q_mutex); + H5TS_mutex_destroy(&ioc_data->io_queue.q_mutex); /* Wait for IOC main thread to exit */ - hg_thread_join(ioc_data->ioc_main_thread); + H5TS_thread_join(ioc_data->ioc_main_thread, NULL); + + /* Destroy atomic vars */ + H5TS_atomic_destroy_int(&ioc_data->sf_ioc_ready); + H5TS_atomic_destroy_int(&ioc_data->sf_shutdown_flag); + H5TS_atomic_destroy_int(&ioc_data->sf_io_ops_pending); + H5TS_atomic_destroy_int(&ioc_data->sf_work_pending); } if (ioc_data->io_queue.num_failed > 0) @@ -299,14 +273,14 @@ H5FD__ioc_finalize_threads(void *_sf_context) * *------------------------------------------------------------------------- */ -static HG_THREAD_RETURN_TYPE +static H5TS_THREAD_RETURN_TYPE H5FD__ioc_thread_main(void *arg) { ioc_data_t *ioc_data = (ioc_data_t *)arg; subfiling_context_t *context = NULL; sf_work_request_t wk_req; int shutdown_requested; - hg_thread_ret_t ret_value = (hg_thread_ret_t)SUCCEED; + H5TS_thread_ret_t ret_value = (H5TS_thread_ret_t)SUCCEED; assert(ioc_data); @@ -320,13 +294,13 @@ H5FD__ioc_thread_main(void *arg) * represent an open file). */ - /* tell H5FD__ioc_init_threads() that ioc_main() is ready to enter its main loop */ - atomic_store(&ioc_data->sf_ioc_ready, 1); + /* tell H5FD__ioc_init_threads() that H5FD__ioc_thread_main() is ready to enter its main loop */ + H5TS_atomic_store_int(&ioc_data->sf_ioc_ready, 1); shutdown_requested = 0; - while ((!shutdown_requested) || (0 < atomic_load(&ioc_data->sf_io_ops_pending)) || - (0 < atomic_load(&ioc_data->sf_work_pending))) { + while ((!shutdown_requested) || (0 < H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)) || + (0 < H5TS_atomic_load_int(&ioc_data->sf_work_pending))) { MPI_Status status; int flag = 0; int mpi_code; @@ -334,7 +308,7 @@ H5FD__ioc_thread_main(void *arg) /* Probe for incoming work requests */ if (MPI_SUCCESS != (mpi_code = (MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, context->sf_msg_comm, &flag, &status)))) - HGOTO_DONE((hg_thread_ret_t)FAIL); + HGOTO_DONE((H5TS_thread_ret_t)FAIL); if (flag) { int count; @@ -342,15 +316,15 @@ H5FD__ioc_thread_main(void *arg) int tag = status.MPI_TAG; if (tag != READ_INDEP && tag != WRITE_INDEP && tag != TRUNC_OP && tag != GET_EOF_OP) - HGOTO_DONE((hg_thread_ret_t)FAIL); + HGOTO_DONE((H5TS_thread_ret_t)FAIL); if (MPI_SUCCESS != (mpi_code = MPI_Get_count(&status, MPI_BYTE, &count))) - HGOTO_DONE((hg_thread_ret_t)FAIL); + HGOTO_DONE((H5TS_thread_ret_t)FAIL); if (count < 0) - HGOTO_DONE((hg_thread_ret_t)FAIL); + HGOTO_DONE((H5TS_thread_ret_t)FAIL); if ((size_t)count > sizeof(sf_work_request_t)) - HGOTO_DONE((hg_thread_ret_t)FAIL); + HGOTO_DONE((H5TS_thread_ret_t)FAIL); /* * Zero out work request, since the received message should @@ -359,7 +333,7 @@ H5FD__ioc_thread_main(void *arg) memset(&wk_req, 0, sizeof(sf_work_request_t)); if (MPI_SUCCESS != (mpi_code = MPI_Recv(&wk_req, count, MPI_BYTE, source, tag, context->sf_msg_comm, MPI_STATUS_IGNORE))) - HGOTO_DONE((hg_thread_ret_t)FAIL); + HGOTO_DONE((H5TS_thread_ret_t)FAIL); /* Dispatch work request to worker threads in thread pool */ @@ -373,7 +347,7 @@ H5FD__ioc_thread_main(void *arg) H5FD__ioc_io_queue_add_entry(ioc_data, &wk_req); - assert(atomic_load(&ioc_data->sf_io_ops_pending) >= 0); + assert(H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending) >= 0); } else { struct timespec sleep_spec = {0, IOC_MAIN_SLEEP_DELAY}; @@ -383,11 +357,11 @@ H5FD__ioc_thread_main(void *arg) H5FD__ioc_io_queue_dispatch_eligible_entries(ioc_data, flag ? 0 : 1); - shutdown_requested = atomic_load(&ioc_data->sf_shutdown_flag); + shutdown_requested = H5TS_atomic_load_int(&ioc_data->sf_shutdown_flag); } /* Reset the shutdown flag */ - atomic_store(&ioc_data->sf_shutdown_flag, 0); + H5TS_atomic_store_int(&ioc_data->sf_shutdown_flag, 0); done: return ret_value; @@ -445,7 +419,7 @@ translate_opcode(io_op_t op) * *------------------------------------------------------------------------- */ -static HG_THREAD_RETURN_TYPE +static H5TS_THREAD_RETURN_TYPE H5FD__ioc_handle_work_request(void *arg) { ioc_io_queue_entry_t *q_entry_ptr = (ioc_io_queue_entry_t *)arg; @@ -454,7 +428,7 @@ H5FD__ioc_handle_work_request(void *arg) ioc_data_t *ioc_data = NULL; int64_t file_context_id = msg->context_id; int op_ret; - hg_thread_ret_t ret_value = 0; + H5TS_thread_ret_t ret_value = 0; assert(q_entry_ptr); assert(q_entry_ptr->magic == H5FD_IOC__IO_Q_ENTRY_MAGIC); @@ -466,7 +440,7 @@ H5FD__ioc_handle_work_request(void *arg) ioc_data = sf_context->ioc_data; assert(ioc_data); - atomic_fetch_add(&ioc_data->sf_work_pending, 1); + H5TS_atomic_fetch_add_int(&ioc_data->sf_work_pending, 1); switch (msg->tag) { case WRITE_INDEP: @@ -498,7 +472,7 @@ H5FD__ioc_handle_work_request(void *arg) break; } - atomic_fetch_sub(&ioc_data->sf_work_pending, 1); + H5TS_atomic_fetch_sub_int(&ioc_data->sf_work_pending, 1); if (op_ret < 0) { #ifdef H5_SUBFILING_DEBUG @@ -514,7 +488,7 @@ H5FD__ioc_handle_work_request(void *arg) #ifdef H5FD_IOC_DEBUG { - int curr_io_ops_pending = atomic_load(&ioc_data->sf_io_ops_pending); + int curr_io_ops_pending = H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending); assert(curr_io_ops_pending > 0); } #endif @@ -522,7 +496,7 @@ H5FD__ioc_handle_work_request(void *arg) /* complete the I/O request */ H5FD__ioc_io_queue_complete_entry(ioc_data, q_entry_ptr); - assert(atomic_load(&ioc_data->sf_io_ops_pending) >= 0); + assert(H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending) >= 0); /* Check the I/O Queue to see if there are any dispatchable entries */ H5FD__ioc_io_queue_dispatch_eligible_entries(ioc_data, 1); @@ -530,37 +504,6 @@ H5FD__ioc_handle_work_request(void *arg) return ret_value; } -/*------------------------------------------------------------------------- - * Function: H5FD_ioc_begin_thread_exclusive - * - * Purpose: Mutex lock to restrict access to code or variables. - * - * Return: integer result of mutex_lock request. - * - *------------------------------------------------------------------------- - */ -void -H5FD_ioc_begin_thread_exclusive(void) -{ - hg_thread_mutex_lock(&ioc_thread_mutex); -} - -/*------------------------------------------------------------------------- - * Function: H5FD_ioc_end_thread_exclusive - * - * Purpose: Mutex unlock. Should only be called by the current holder - * of the locked mutex. - * - * Return: result of mutex_unlock operation. - * - *------------------------------------------------------------------------- - */ -void -H5FD_ioc_end_thread_exclusive(void) -{ - hg_thread_mutex_unlock(&ioc_thread_mutex); -} - static herr_t H5FD__ioc_send_ack_to_client(int ack_val, int dest_rank, int msg_tag, MPI_Comm comm) { @@ -746,13 +689,13 @@ H5FD__ioc_file_queue_write_indep(sf_work_request_t *msg, int ioc_idx, int source sf_queue_delay_time += t_queue_delay; #endif - H5FD_ioc_begin_thread_exclusive(); + H5TS_mutex_lock(&sf_context->mutex); /* Adjust EOF if necessary */ if (sf_eof > sf_context->sf_eof) sf_context->sf_eof = sf_eof; - H5FD_ioc_end_thread_exclusive(); + H5TS_mutex_unlock(&sf_context->mutex); /* * Send a message back to the client that the I/O call has @@ -1236,9 +1179,9 @@ H5FD__ioc_io_queue_add_entry(ioc_data_t *ioc_data, sf_work_request_t *wk_req_ptr H5MM_memcpy((void *)(&(entry_ptr->wk_req)), (const void *)wk_req_ptr, sizeof(sf_work_request_t)); /* must obtain io_queue mutex before appending */ - hg_thread_mutex_lock(&ioc_data->io_queue.q_mutex); + H5TS_mutex_lock(&ioc_data->io_queue.q_mutex); - assert(ioc_data->io_queue.q_len == atomic_load(&ioc_data->sf_io_ops_pending)); + assert(ioc_data->io_queue.q_len == H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)); entry_ptr->counter = ioc_data->io_queue.req_counter++; @@ -1246,7 +1189,7 @@ H5FD__ioc_io_queue_add_entry(ioc_data_t *ioc_data, sf_work_request_t *wk_req_ptr H5FD_IOC__Q_APPEND(&ioc_data->io_queue, entry_ptr); - atomic_fetch_add(&ioc_data->sf_io_ops_pending, 1); + H5TS_atomic_fetch_add_int(&ioc_data->sf_io_ops_pending, 1); #ifdef H5_SUBFILING_DEBUG H5FD__subfiling_log( @@ -1255,7 +1198,7 @@ H5FD__ioc_io_queue_add_entry(ioc_data_t *ioc_data, sf_work_request_t *wk_req_ptr entry_ptr->counter, (entry_ptr->wk_req.tag), (long long)(entry_ptr->wk_req.header[0]), (long long)(entry_ptr->wk_req.header[1]), (long long)(entry_ptr->wk_req.header[2]), ioc_data->io_queue.num_pending, ioc_data->io_queue.num_in_progress, - atomic_load(&ioc_data->sf_io_ops_pending)); + H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)); #endif assert(ioc_data->io_queue.num_pending + ioc_data->io_queue.num_in_progress == ioc_data->io_queue.q_len); @@ -1282,17 +1225,17 @@ H5FD__ioc_io_queue_add_entry(ioc_data_t *ioc_data, sf_work_request_t *wk_req_ptr #endif #ifdef H5_SUBFILING_DEBUG - if (ioc_data->io_queue.q_len != atomic_load(&ioc_data->sf_io_ops_pending)) { + if (ioc_data->io_queue.q_len != H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)) { H5FD__subfiling_log( wk_req_ptr->context_id, - "%s: ioc_data->io_queue->q_len = %d != %d = atomic_load(&ioc_data->sf_io_ops_pending).", __func__, - ioc_data->io_queue.q_len, atomic_load(&ioc_data->sf_io_ops_pending)); + "%s: ioc_data->io_queue->q_len = %d != %d = H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending).", + __func__, ioc_data->io_queue.q_len, H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)); } #endif - assert(ioc_data->io_queue.q_len == atomic_load(&ioc_data->sf_io_ops_pending)); + assert(ioc_data->io_queue.q_len == H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)); - hg_thread_mutex_unlock(&ioc_data->io_queue.q_mutex); + H5TS_mutex_unlock(&ioc_data->io_queue.q_mutex); return; } /* H5FD__ioc_io_queue_add_entry() */ @@ -1351,11 +1294,14 @@ H5FD__ioc_io_queue_dispatch_eligible_entries(ioc_data_t *ioc_data, bool try_lock assert(ioc_data->io_queue.magic == H5FD_IOC__IO_Q_MAGIC); if (try_lock) { - if (hg_thread_mutex_try_lock(&ioc_data->io_queue.q_mutex) < 0) + bool acquired; + + H5TS_mutex_trylock(&ioc_data->io_queue.q_mutex, &acquired); + if (!acquired) return; } else - hg_thread_mutex_lock(&ioc_data->io_queue.q_mutex); + H5TS_mutex_lock(&ioc_data->io_queue.q_mutex); entry_ptr = ioc_data->io_queue.q_head; @@ -1420,9 +1366,6 @@ H5FD__ioc_io_queue_dispatch_eligible_entries(ioc_data_t *ioc_data, bool try_lock assert(ioc_data->io_queue.num_pending + ioc_data->io_queue.num_in_progress == ioc_data->io_queue.q_len); - entry_ptr->thread_wk.func = H5FD__ioc_handle_work_request; - entry_ptr->thread_wk.args = entry_ptr; - #ifdef H5_SUBFILING_DEBUG H5FD__subfiling_log( entry_ptr->wk_req.context_id, @@ -1431,7 +1374,7 @@ H5FD__ioc_io_queue_dispatch_eligible_entries(ioc_data_t *ioc_data, bool try_lock __func__, entry_ptr->counter, (entry_ptr->wk_req.tag), (long long)(entry_ptr->wk_req.header[0]), (long long)(entry_ptr->wk_req.header[1]), (long long)(entry_ptr->wk_req.header[2]), ioc_data->io_queue.num_pending, - ioc_data->io_queue.num_in_progress, atomic_load(&ioc_data->sf_io_ops_pending)); + ioc_data->io_queue.num_in_progress, H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)); #endif #ifdef H5FD_IOC_COLLECT_STATS @@ -1443,16 +1386,16 @@ H5FD__ioc_io_queue_dispatch_eligible_entries(ioc_data_t *ioc_data, bool try_lock entry_ptr->dispatch_time = H5_now_usec(); #endif - hg_thread_pool_post(ioc_data->io_thread_pool, &(entry_ptr->thread_wk)); + H5TS_pool_add_task(ioc_data->io_thread_pool, H5FD__ioc_handle_work_request, entry_ptr); } } entry_ptr = entry_ptr->next; } - assert(ioc_data->io_queue.q_len == atomic_load(&ioc_data->sf_io_ops_pending)); + assert(ioc_data->io_queue.q_len == H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)); - hg_thread_mutex_unlock(&ioc_data->io_queue.q_mutex); + H5TS_mutex_unlock(&ioc_data->io_queue.q_mutex); } /* H5FD__ioc_io_queue_dispatch_eligible_entries() */ /*------------------------------------------------------------------------- @@ -1482,7 +1425,7 @@ H5FD__ioc_io_queue_complete_entry(ioc_data_t *ioc_data, ioc_io_queue_entry_t *en assert(entry_ptr->magic == H5FD_IOC__IO_Q_ENTRY_MAGIC); /* must obtain io_queue mutex before deleting and updating stats */ - hg_thread_mutex_lock(&ioc_data->io_queue.q_mutex); + H5TS_mutex_lock(&ioc_data->io_queue.q_mutex); assert(ioc_data->io_queue.num_pending + ioc_data->io_queue.num_in_progress == ioc_data->io_queue.q_len); assert(ioc_data->io_queue.num_in_progress > 0); @@ -1496,7 +1439,7 @@ H5FD__ioc_io_queue_complete_entry(ioc_data_t *ioc_data, ioc_io_queue_entry_t *en assert(ioc_data->io_queue.num_pending + ioc_data->io_queue.num_in_progress == ioc_data->io_queue.q_len); - atomic_fetch_sub(&ioc_data->sf_io_ops_pending, 1); + H5TS_atomic_fetch_sub_int(&ioc_data->sf_io_ops_pending, 1); #ifdef H5_SUBFILING_DEBUG H5FD__subfiling_log(entry_ptr->wk_req.context_id, @@ -1505,7 +1448,8 @@ H5FD__ioc_io_queue_complete_entry(ioc_data_t *ioc_data, ioc_io_queue_entry_t *en __func__, entry_ptr->counter, entry_ptr->wk_ret, (entry_ptr->wk_req.tag), (long long)(entry_ptr->wk_req.header[0]), (long long)(entry_ptr->wk_req.header[1]), (long long)(entry_ptr->wk_req.header[2]), ioc_data->io_queue.num_pending, - ioc_data->io_queue.num_in_progress, atomic_load(&ioc_data->sf_io_ops_pending)); + ioc_data->io_queue.num_in_progress, + H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)); /* * If this I/O request is a truncate or "get eof" op, make sure @@ -1515,7 +1459,7 @@ H5FD__ioc_io_queue_complete_entry(ioc_data_t *ioc_data, ioc_io_queue_entry_t *en assert(ioc_data->io_queue.num_in_progress == 0); #endif - assert(ioc_data->io_queue.q_len == atomic_load(&ioc_data->sf_io_ops_pending)); + assert(ioc_data->io_queue.q_len == H5TS_atomic_load_int(&ioc_data->sf_io_ops_pending)); #ifdef H5FD_IOC_COLLECT_STATS ioc_data->io_queue.requests_completed++; @@ -1523,7 +1467,7 @@ H5FD__ioc_io_queue_complete_entry(ioc_data_t *ioc_data, ioc_io_queue_entry_t *en entry_ptr->q_time = H5_now_usec(); #endif - hg_thread_mutex_unlock(&ioc_data->io_queue.q_mutex); + H5TS_mutex_unlock(&ioc_data->io_queue.q_mutex); H5MM_free(entry_ptr); diff --git a/src/H5FDsubfiling/H5FDsubfiling_priv.h b/src/H5FDsubfiling/H5FDsubfiling_priv.h index cef71e15b84..fdb946e73ef 100644 --- a/src/H5FDsubfiling/H5FDsubfiling_priv.h +++ b/src/H5FDsubfiling/H5FDsubfiling_priv.h @@ -17,12 +17,6 @@ #ifndef H5FDsubfiling_priv_H #define H5FDsubfiling_priv_H -/********************/ -/* Standard Headers */ -/********************/ - -#include - /**************/ /* H5 Headers */ /**************/ diff --git a/src/H5FDsubfiling/H5subfiling_common.c b/src/H5FDsubfiling/H5subfiling_common.c index 0b7dea833a4..00a2b7017c4 100644 --- a/src/H5FDsubfiling/H5subfiling_common.c +++ b/src/H5FDsubfiling/H5subfiling_common.c @@ -18,6 +18,7 @@ #include "H5Eprivate.h" #include "H5MMprivate.h" +#include "H5TSprivate.h" /* Threadsafety */ typedef struct { /* Format of a context map entry */ uint64_t file_id; /* key value (linear search of the cache) */ @@ -374,6 +375,9 @@ H5FD__subfiling_free_context(subfiling_context_t *sf_context) mpi_finalized = 1; } + if (H5TS_mutex_destroy(&sf_context->mutex) < 0) + HDONE_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "can't destroy subfiling context's mutex"); + if (!mpi_finalized) { if (H5_mpi_comm_free(&sf_context->sf_msg_comm) < 0) HDONE_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "can't free MPI communicator"); @@ -1715,7 +1719,9 @@ H5FD__subfiling_init_context(int64_t context_id, const char *prefix_env, const c HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL, "couldn't create new subfiling object"); /* Set non-zero fields */ - sf_context->h5_file_id = file_id; + sf_context->h5_file_id = file_id; + if (H5TS_mutex_init(&sf_context->mutex, H5TS_MUTEX_TYPE_PLAIN) < 0) + HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "couldn't init subfiling context's mutex"); sf_context->sf_context_id = context_id; sf_context->sf_num_subfiles = subfiling_config->stripe_count; sf_context->sf_eof = HADDR_UNDEF; @@ -3022,7 +3028,7 @@ H5FD__subfiling_log(int64_t sf_context_id, const char *fmt, ...) goto done; } - H5FD_ioc_begin_thread_exclusive(); + H5TS_mutex_lock(&sf_context->mutex); if (sf_context->sf_logfile) { vfprintf(sf_context->sf_logfile, fmt, log_args); @@ -3035,7 +3041,7 @@ H5FD__subfiling_log(int64_t sf_context_id, const char *fmt, ...) fflush(stdout); } - H5FD_ioc_end_thread_exclusive(); + H5TS_mutex_unlock(&sf_context->mutex); done: va_end(log_args); @@ -3059,7 +3065,7 @@ H5FD__subfiling_log_nonewline(int64_t sf_context_id, const char *fmt, ...) goto done; } - H5FD_ioc_begin_thread_exclusive(); + H5TS_mutex_lock(&sf_context->mutex); if (sf_context->sf_logfile) { vfprintf(sf_context->sf_logfile, fmt, log_args); @@ -3070,7 +3076,7 @@ H5FD__subfiling_log_nonewline(int64_t sf_context_id, const char *fmt, ...) fflush(stdout); } - H5FD_ioc_end_thread_exclusive(); + H5TS_mutex_unlock(&sf_context->mutex); done: va_end(log_args); diff --git a/src/H5FDsubfiling/H5subfiling_common.h b/src/H5FDsubfiling/H5subfiling_common.h index cef396b9e42..7e7ada5bcfe 100644 --- a/src/H5FDsubfiling/H5subfiling_common.h +++ b/src/H5FDsubfiling/H5subfiling_common.h @@ -17,12 +17,11 @@ #ifndef H5_SUBFILING_COMMON_H #define H5_SUBFILING_COMMON_H -#include - -#include "H5private.h" -#include "H5FDprivate.h" -#include "H5Iprivate.h" -#include "H5Pprivate.h" +#include "H5private.h" /* Generic Functions */ +#include "H5FDprivate.h" /* File Drivers */ +#include "H5Iprivate.h" /* IDs */ +#include "H5Pprivate.h" /* Property lists */ +#include "H5TSprivate.h" /* Threads */ #include "H5FDsubfiling.h" #include "H5FDioc.h" @@ -211,6 +210,7 @@ typedef struct { int64_t sf_context_id; /* Generated context ID which embeds the cache index */ uint64_t h5_file_id; /* GUID (basically the inode value) */ bool threads_inited; /* Whether the IOC threads for this context were started */ + H5TS_mutex_t mutex; /* Mutex for protecting context */ int file_ref; /* Reference count held by files using this context */ int *sf_fids; /* Array of file IDs for subfiles this rank owns */ int sf_num_fids; /* Number of subfiles this rank owns */ diff --git a/src/H5FDsubfiling/mercury/LICENSE.txt b/src/H5FDsubfiling/mercury/LICENSE.txt deleted file mode 100644 index 6b68f8f11e8..00000000000 --- a/src/H5FDsubfiling/mercury/LICENSE.txt +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2013-2022, UChicago Argonne, LLC and The HDF Group. -Copyright (c) 2022-2023, Intel Corporation. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_compiler_attributes.h b/src/H5FDsubfiling/mercury/src/util/mercury_compiler_attributes.h deleted file mode 100644 index d2b8ed12137..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_compiler_attributes.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef MERCURY_COMPILER_ATTRIBUTES_H -#define MERCURY_COMPILER_ATTRIBUTES_H - -/*************************************/ -/* Public Type and Struct Definition */ -/*************************************/ - -/*****************/ -/* Public Macros */ -/*****************/ - -/* - * __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17. - * In the meantime, to support gcc < 5, we implement __has_attribute - * by hand. - */ -#if !defined(__has_attribute) && defined(__GNUC__) && (__GNUC__ >= 4) -#define __has_attribute(x) __GCC4_has_attribute_##x -#define __GCC4_has_attribute___visibility__ 1 -#define __GCC4_has_attribute___warn_unused_result__ 1 -#define __GCC4_has_attribute___unused__ 1 -#define __GCC4_has_attribute___format__ 1 -#define __GCC4_has_attribute___fallthrough__ 0 -#endif - -/* Visibility of symbols */ -#if defined(_WIN32) -#define HG_ATTR_ABI_IMPORT __declspec(dllimport) -#define HG_ATTR_ABI_EXPORT __declspec(dllexport) -#define HG_ATTR_ABI_HIDDEN -#elif __has_attribute(__visibility__) -#define HG_ATTR_ABI_IMPORT __attribute__((__visibility__("default"))) -#define HG_ATTR_ABI_EXPORT __attribute__((__visibility__("default"))) -#define HG_ATTR_ABI_HIDDEN __attribute__((__visibility__("hidden"))) -#else -#define HG_ATTR_ABI_IMPORT -#define HG_ATTR_ABI_EXPORT -#define HG_ATTR_ABI_HIDDEN -#endif - -#endif /* MERCURY_COMPILER_ATTRIBUTES_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_queue.h b/src/H5FDsubfiling/mercury/src/util/mercury_queue.h deleted file mode 100644 index 946adec75df..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_queue.h +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -/* Code below is derived from sys/queue.h which follows the below notice: - * - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef MERCURY_QUEUE_H -#define MERCURY_QUEUE_H - -#define HG_QUEUE_HEAD_INITIALIZER(name) \ - { \ - NULL, &(name).head \ - } - -#define HG_QUEUE_HEAD_INIT(struct_head_name, var_name) \ - struct struct_head_name var_name = HG_QUEUE_HEAD_INITIALIZER(var_name) - -#define HG_QUEUE_HEAD_DECL(struct_head_name, struct_entry_name) \ - struct struct_head_name { \ - struct struct_entry_name *head; \ - struct struct_entry_name **tail; \ - } - -#define HG_QUEUE_HEAD(struct_entry_name) \ - struct { \ - struct struct_entry_name *head; \ - struct struct_entry_name **tail; \ - } - -#define HG_QUEUE_ENTRY(struct_entry_name) \ - struct { \ - struct struct_entry_name *next; \ - } - -#define HG_QUEUE_INIT(head_ptr) \ - do { \ - (head_ptr)->head = NULL; \ - (head_ptr)->tail = &(head_ptr)->head; \ - } while (/*CONSTCOND*/ 0) - -#define HG_QUEUE_IS_EMPTY(head_ptr) ((head_ptr)->head == NULL) - -#define HG_QUEUE_FIRST(head_ptr) ((head_ptr)->head) - -#define HG_QUEUE_NEXT(entry_ptr, entry_field_name) ((entry_ptr)->entry_field_name.next) - -#define HG_QUEUE_PUSH_TAIL(head_ptr, entry_ptr, entry_field_name) \ - do { \ - (entry_ptr)->entry_field_name.next = NULL; \ - *(head_ptr)->tail = (entry_ptr); \ - (head_ptr)->tail = &(entry_ptr)->entry_field_name.next; \ - } while (/*CONSTCOND*/ 0) - -/* TODO would be nice to not have any condition */ -#define HG_QUEUE_POP_HEAD(head_ptr, entry_field_name) \ - do { \ - if ((head_ptr)->head && ((head_ptr)->head = (head_ptr)->head->entry_field_name.next) == NULL) \ - (head_ptr)->tail = &(head_ptr)->head; \ - } while (/*CONSTCOND*/ 0) - -#define HG_QUEUE_FOREACH(var, head_ptr, entry_field_name) \ - for ((var) = ((head_ptr)->head); (var); (var) = ((var)->entry_field_name.next)) - -/** - * Avoid using those for performance reasons or use mercury_list.h instead - */ - -#define HG_QUEUE_REMOVE(head_ptr, entry_ptr, type, entry_field_name) \ - do { \ - if ((head_ptr)->head == (entry_ptr)) { \ - HG_QUEUE_POP_HEAD((head_ptr), entry_field_name); \ - } \ - else { \ - struct type *curelm = (head_ptr)->head; \ - while (curelm->entry_field_name.next != (entry_ptr)) \ - curelm = curelm->entry_field_name.next; \ - if ((curelm->entry_field_name.next = curelm->entry_field_name.next->entry_field_name.next) == \ - NULL) \ - (head_ptr)->tail = &(curelm)->entry_field_name.next; \ - } \ - } while (/*CONSTCOND*/ 0) - -#endif /* MERCURY_QUEUE_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread.c b/src/H5FDsubfiling/mercury/src/util/mercury_thread.c deleted file mode 100644 index effae48f346..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread.c +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "mercury_thread.h" - -#if !defined(_WIN32) && !defined(__APPLE__) -#include -#endif - -/*---------------------------------------------------------------------------*/ -void -hg_thread_init(hg_thread_t *thread) -{ -#ifdef _WIN32 - *thread = NULL; -#else - *thread = 0; -#endif -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_create(hg_thread_t *thread, hg_thread_func_t f, void *data) -{ -#ifdef _WIN32 - *thread = CreateThread(NULL, 0, f, data, 0, NULL); - if (*thread == NULL) - return HG_UTIL_FAIL; -#else - if (pthread_create(thread, NULL, f, data)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -void -hg_thread_exit(hg_thread_ret_t ret) -{ -#ifdef _WIN32 - ExitThread(ret); -#else - pthread_exit(ret); -#endif -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_join(hg_thread_t thread) -{ -#ifdef _WIN32 - WaitForSingleObject(thread, INFINITE); - CloseHandle(thread); -#else - if (pthread_join(thread, NULL)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_cancel(hg_thread_t thread) -{ -#ifdef _WIN32 - WaitForSingleObject(thread, 0); - CloseHandle(thread); -#else - if (pthread_cancel(thread)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_yield(void) -{ -#ifdef _WIN32 - SwitchToThread(); -#elif defined(__APPLE__) - pthread_yield_np(); -#else - sched_yield(); -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_key_create(hg_thread_key_t *key) -{ - if (!key) - return HG_UTIL_FAIL; - -#ifdef _WIN32 - if ((*key = TlsAlloc()) == TLS_OUT_OF_INDEXES) - return HG_UTIL_FAIL; -#else - if (pthread_key_create(key, NULL)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_key_delete(hg_thread_key_t key) -{ -#ifdef _WIN32 - if (!TlsFree(key)) - return HG_UTIL_FAIL; -#else - if (pthread_key_delete(key)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_getaffinity(hg_thread_t thread, hg_cpu_set_t *cpu_mask) -{ -#if defined(_WIN32) - return HG_UTIL_FAIL; -#elif defined(__APPLE__) - (void)thread; - (void)cpu_mask; - return HG_UTIL_FAIL; -#else - if (pthread_getaffinity_np(thread, sizeof(hg_cpu_set_t), cpu_mask)) - return HG_UTIL_FAIL; - return HG_UTIL_SUCCESS; -#endif -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_setaffinity(hg_thread_t thread, const hg_cpu_set_t *cpu_mask) -{ -#if defined(_WIN32) - if (!SetThreadAffinityMask(thread, *cpu_mask)) - return HG_UTIL_FAIL; - return HG_UTIL_SUCCESS; -#elif defined(__APPLE__) - (void)thread; - (void)cpu_mask; - return HG_UTIL_FAIL; -#else - if (pthread_setaffinity_np(thread, sizeof(hg_cpu_set_t), cpu_mask)) - return HG_UTIL_FAIL; - return HG_UTIL_SUCCESS; -#endif -} diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread.h b/src/H5FDsubfiling/mercury/src/util/mercury_thread.h deleted file mode 100644 index f338226da3f..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread.h +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef MERCURY_THREAD_H -#define MERCURY_THREAD_H - -#if !defined(_WIN32) && !defined(_GNU_SOURCE) -#define _GNU_SOURCE -#endif -#include "mercury_util_config.h" - -#ifdef _WIN32 -#define _WINSOCKAPI_ -#include -typedef HANDLE hg_thread_t; -typedef LPTHREAD_START_ROUTINE hg_thread_func_t; -typedef DWORD hg_thread_ret_t; -#define HG_THREAD_RETURN_TYPE hg_thread_ret_t WINAPI -typedef DWORD hg_thread_key_t; -typedef DWORD_PTR hg_cpu_set_t; -#else -#include -typedef pthread_t hg_thread_t; -typedef void *(*hg_thread_func_t)(void *); -typedef void *hg_thread_ret_t; -#define HG_THREAD_RETURN_TYPE hg_thread_ret_t -typedef pthread_key_t hg_thread_key_t; -#ifdef __APPLE__ -/* Size definition for CPU sets. */ -#define HG_CPU_SETSIZE 1024 -#define HG_NCPUBITS (8 * sizeof(hg_cpu_mask_t)) -/* Type for array elements in 'cpu_set_t'. */ -typedef uint64_t hg_cpu_mask_t; -typedef struct { - hg_cpu_mask_t bits[HG_CPU_SETSIZE / HG_NCPUBITS]; -} hg_cpu_set_t; -#else -typedef cpu_set_t hg_cpu_set_t; -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Initialize the thread. - * - * \param thread [IN/OUT] pointer to thread object - */ -HG_UTIL_PUBLIC void hg_thread_init(hg_thread_t *thread); - -/** - * Create a new thread for the given function. - * - * \param thread [IN/OUT] pointer to thread object - * \param f [IN] pointer to function - * \param data [IN] pointer to data than be passed to function f - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_create(hg_thread_t *thread, hg_thread_func_t f, void *data); - -/** - * Ends the calling thread. - * - * \param ret [IN] exit code for the thread - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC void hg_thread_exit(hg_thread_ret_t ret); - -/** - * Wait for thread completion. - * - * \param thread [IN] thread object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_join(hg_thread_t thread); - -/** - * Terminate the thread. - * - * \param thread [IN] thread object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_cancel(hg_thread_t thread); - -/** - * Yield the processor. - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_yield(void); - -/** - * Obtain handle of the calling thread. - * - * \return - */ -static HG_UTIL_INLINE hg_thread_t hg_thread_self(void); - -/** - * Compare thread IDs. - * - * \return Non-zero if equal, zero if not equal - */ -static HG_UTIL_INLINE int hg_thread_equal(hg_thread_t t1, hg_thread_t t2); - -/** - * Create a thread-specific data key visible to all threads in the process. - * - * \param key [OUT] pointer to thread key object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_key_create(hg_thread_key_t *key); - -/** - * Delete a thread-specific data key previously returned by - * hg_thread_key_create(). - * - * \param key [IN] thread key object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_key_delete(hg_thread_key_t key); - -/** - * Get value from specified key. - * - * \param key [IN] thread key object - * - * \return Pointer to data associated to the key - */ -static HG_UTIL_INLINE void *hg_thread_getspecific(hg_thread_key_t key); - -/** - * Set value to specified key. - * - * \param key [IN] thread key object - * \param value [IN] pointer to data that will be associated - * - * \return Non-negative on success or negative on failure - */ -static HG_UTIL_INLINE int hg_thread_setspecific(hg_thread_key_t key, const void *value); - -/** - * Get affinity mask. - * - * \param thread [IN] thread object - * \param cpu_mask [IN/OUT] cpu mask - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_getaffinity(hg_thread_t thread, hg_cpu_set_t *cpu_mask); - -/** - * Set affinity mask. - * - * \param thread [IN] thread object - * \param cpu_mask [IN] cpu mask - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_setaffinity(hg_thread_t thread, const hg_cpu_set_t *cpu_mask); - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE hg_thread_t -hg_thread_self(void) -{ -#ifdef _WIN32 - return GetCurrentThread(); -#else - return pthread_self(); -#endif -} - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int -hg_thread_equal(hg_thread_t t1, hg_thread_t t2) -{ -#ifdef _WIN32 - return GetThreadId(t1) == GetThreadId(t2); -#else - return pthread_equal(t1, t2); -#endif -} - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE void * -hg_thread_getspecific(hg_thread_key_t key) -{ -#ifdef _WIN32 - return TlsGetValue(key); -#else - return pthread_getspecific(key); -#endif -} - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int -hg_thread_setspecific(hg_thread_key_t key, const void *value) -{ -#ifdef _WIN32 - if (!TlsSetValue(key, (LPVOID)value)) - return HG_UTIL_FAIL; -#else - if (pthread_setspecific(key, value)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -#ifdef __cplusplus -} -#endif - -#endif /* MERCURY_THREAD_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_annotation.h b/src/H5FDsubfiling/mercury/src/util/mercury_thread_annotation.h deleted file mode 100644 index c11875aba1d..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_annotation.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef MERCURY_THREAD_ANNOTATION_H -#define MERCURY_THREAD_ANNOTATION_H - -/* Enable thread safety attributes only with clang. - * The attributes can be safely erased when compiling with other compilers. */ -#if defined(__clang__) && (__clang_major__ > 3) -#define HG_THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) -#else -#define HG_THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op -#endif - -#define HG_LOCK_CAPABILITY(x) HG_THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) - -#define HG_LOCK_ACQUIRE(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) - -#define HG_LOCK_ACQUIRE_SHARED(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) - -#define HG_LOCK_RELEASE(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) - -#define HG_LOCK_RELEASE_SHARED(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) - -#define HG_LOCK_TRY_ACQUIRE(...) HG_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) - -#define HG_LOCK_TRY_ACQUIRE_SHARED(...) \ - HG_THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) - -#define HG_LOCK_NO_THREAD_SAFETY_ANALYSIS HG_THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) - -#endif /* MERCURY_THREAD_ANNOTATION_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.c b/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.c deleted file mode 100644 index 6b70978eb15..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.c +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "mercury_thread_condition.h" - -/*---------------------------------------------------------------------------*/ -int -hg_thread_cond_init(hg_thread_cond_t *cond) -{ -#ifdef _WIN32 - InitializeConditionVariable(cond); -#else - pthread_condattr_t attr; - - pthread_condattr_init(&attr); -#if defined(H5_HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(H5_HAVE_CLOCK_MONOTONIC_COARSE) - /* Must set clock ID if using different clock - * (CLOCK_MONOTONIC_COARSE not supported here) */ - pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); -#endif - if (pthread_cond_init(cond, &attr)) - return HG_UTIL_FAIL; - pthread_condattr_destroy(&attr); -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_cond_destroy(hg_thread_cond_t *cond) -{ -#ifndef _WIN32 - if (pthread_cond_destroy(cond)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.h b/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.h deleted file mode 100644 index dcead57eff8..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.h +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef MERCURY_THREAD_CONDITION_H -#define MERCURY_THREAD_CONDITION_H - -#include "mercury_thread_mutex.h" - -#ifdef _WIN32 -typedef CONDITION_VARIABLE hg_thread_cond_t; -#else -#if defined(H5_HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(H5_HAVE_CLOCK_MONOTONIC_COARSE) -#include -#elif defined(H5_HAVE_SYS_TIME_H) -#include -#endif -#include -typedef pthread_cond_t hg_thread_cond_t; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Initialize the condition. - * - * \param cond [IN/OUT] pointer to condition object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_cond_init(hg_thread_cond_t *cond); - -/** - * Destroy the condition. - * - * \param cond [IN/OUT] pointer to condition object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_cond_destroy(hg_thread_cond_t *cond); - -/** - * Wake one thread waiting for the condition to change. - * - * \param cond [IN/OUT] pointer to condition object - * - * \return Non-negative on success or negative on failure - */ -static HG_UTIL_INLINE int hg_thread_cond_signal(hg_thread_cond_t *cond); - -/** - * Wake all the threads waiting for the condition to change. - * - * \param cond [IN/OUT] pointer to condition object - * - * \return Non-negative on success or negative on failure - */ -static HG_UTIL_INLINE int hg_thread_cond_broadcast(hg_thread_cond_t *cond); - -/** - * Wait for the condition to change. - * - * \param cond [IN/OUT] pointer to condition object - * \param mutex [IN/OUT] pointer to mutex object - * - * \return Non-negative on success or negative on failure - */ -static HG_UTIL_INLINE int hg_thread_cond_wait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex); - -/** - * Wait timeout ms for the condition to change. - * - * \param cond [IN/OUT] pointer to condition object - * \param mutex [IN/OUT] pointer to mutex object - * \param timeout [IN] timeout (in milliseconds) - * - * \return Non-negative on success or negative on failure - */ -static HG_UTIL_INLINE int hg_thread_cond_timedwait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex, - unsigned int timeout); - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int -hg_thread_cond_signal(hg_thread_cond_t *cond) -{ -#ifdef _WIN32 - WakeConditionVariable(cond); -#else - if (pthread_cond_signal(cond)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int -hg_thread_cond_broadcast(hg_thread_cond_t *cond) -{ -#ifdef _WIN32 - WakeAllConditionVariable(cond); -#else - if (pthread_cond_broadcast(cond)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int -hg_thread_cond_wait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex) -{ -#ifdef _WIN32 - if (!SleepConditionVariableCS(cond, mutex, INFINITE)) - return HG_UTIL_FAIL; -#else - if (pthread_cond_wait(cond, mutex)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int -hg_thread_cond_timedwait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex, unsigned int timeout) -{ -#ifdef _WIN32 - if (!SleepConditionVariableCS(cond, mutex, timeout)) - return HG_UTIL_FAIL; -#else -#if defined(H5_HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(H5_HAVE_CLOCK_MONOTONIC_COARSE) - struct timespec now; -#else - struct timeval now; -#endif - struct timespec abs_timeout; - ldiv_t ld; - - /* Need to convert timeout (ms) to absolute time */ -#if defined(H5_HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(H5_HAVE_CLOCK_MONOTONIC_COARSE) - clock_gettime(CLOCK_MONOTONIC_COARSE, &now); - - /* Get sec / nsec */ - ld = ldiv(now.tv_nsec + timeout * 1000000L, 1000000000L); - abs_timeout.tv_nsec = ld.rem; -#elif defined(H5_HAVE_SYS_TIME_H) - gettimeofday(&now, NULL); - - /* Get sec / usec */ - ld = ldiv(now.tv_usec + timeout * 1000L, 1000000L); - abs_timeout.tv_nsec = ld.rem * 1000L; -#endif - abs_timeout.tv_sec = now.tv_sec + ld.quot; - - if (pthread_cond_timedwait(cond, mutex, &abs_timeout)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -#ifdef __cplusplus -} -#endif - -#endif /* MERCURY_THREAD_CONDITION_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.c b/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.c deleted file mode 100644 index 8fc804f6141..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.c +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "mercury_thread_mutex.h" - -#include "mercury_util_error.h" - -#include - -#ifndef _WIN32 -static int -hg_thread_mutex_init_posix(hg_thread_mutex_t *mutex, int kind) -{ - pthread_mutexattr_t mutex_attr; - int ret = HG_UTIL_SUCCESS; - int rc; - - rc = pthread_mutexattr_init(&mutex_attr); - HG_UTIL_CHECK_ERROR(rc != 0, done, ret, HG_UTIL_FAIL, "pthread_mutexattr_init() failed (%s)", - strerror(rc)); - - /* Keep mutex mode as normal and do not expect error checking */ - rc = pthread_mutexattr_settype(&mutex_attr, kind); - HG_UTIL_CHECK_ERROR(rc != 0, done, ret, HG_UTIL_FAIL, "pthread_mutexattr_settype() failed (%s)", - strerror(rc)); - - rc = pthread_mutex_init(mutex, &mutex_attr); - HG_UTIL_CHECK_ERROR(rc != 0, done, ret, HG_UTIL_FAIL, "pthread_mutex_init() failed (%s)", strerror(rc)); - -done: - rc = pthread_mutexattr_destroy(&mutex_attr); - - return ret; -} -#endif - -/*---------------------------------------------------------------------------*/ -int -hg_thread_mutex_init(hg_thread_mutex_t *mutex) -{ - int ret = HG_UTIL_SUCCESS; - -#ifdef _WIN32 - InitializeCriticalSection(mutex); -#else - ret = hg_thread_mutex_init_posix(mutex, PTHREAD_MUTEX_NORMAL); -#endif - - return ret; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_mutex_init_fast(hg_thread_mutex_t *mutex) -{ - int ret = HG_UTIL_SUCCESS; - -#if defined(_WIN32) - ret = hg_thread_mutex_init(mutex); -#elif defined(H5_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP) - /* Set type to PTHREAD_MUTEX_ADAPTIVE_NP to improve performance */ - ret = hg_thread_mutex_init_posix(mutex, PTHREAD_MUTEX_ADAPTIVE_NP); -#else - ret = hg_thread_mutex_init_posix(mutex, PTHREAD_MUTEX_NORMAL); -#endif - - return ret; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_mutex_destroy(hg_thread_mutex_t *mutex) -{ - int ret = HG_UTIL_SUCCESS; - -#ifdef _WIN32 - DeleteCriticalSection(mutex); -#else - int rc; - - rc = pthread_mutex_destroy(mutex); - HG_UTIL_CHECK_ERROR(rc != 0, done, ret, HG_UTIL_FAIL, "pthread_mutex_destroy() failed (%s)", - strerror(rc)); - -done: -#endif - return ret; -} diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.h b/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.h deleted file mode 100644 index 73ceafb8850..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef MERCURY_THREAD_MUTEX_H -#define MERCURY_THREAD_MUTEX_H - -#include "mercury_util_config.h" - -#include "mercury_thread_annotation.h" - -#ifdef _WIN32 -#define _WINSOCKAPI_ -#include -/* clang-format off */ -# define HG_THREAD_MUTEX_INITIALIZER {NULL} -/* clang-format on */ -typedef CRITICAL_SECTION hg_thread_mutex_t; -#else -#include -#define HG_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER -typedef pthread_mutex_t HG_LOCK_CAPABILITY("mutex") hg_thread_mutex_t; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Initialize the mutex. - * - * \param mutex [IN/OUT] pointer to mutex object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_mutex_init(hg_thread_mutex_t *mutex); - -/** - * Initialize the mutex, asking for "fast" mutex. - * - * \param mutex [IN/OUT] pointer to mutex object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_mutex_init_fast(hg_thread_mutex_t *mutex); - -/** - * Destroy the mutex. - * - * \param mutex [IN/OUT] pointer to mutex object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_mutex_destroy(hg_thread_mutex_t *mutex); - -/** - * Lock the mutex. - * - * \param mutex [IN/OUT] pointer to mutex object - */ -static HG_UTIL_INLINE void hg_thread_mutex_lock(hg_thread_mutex_t *mutex) HG_LOCK_ACQUIRE(*mutex); - -/** - * Try locking the mutex. - * - * \param mutex [IN/OUT] pointer to mutex object - * - * \return Non-negative on success or negative on failure - */ -static HG_UTIL_INLINE int hg_thread_mutex_try_lock(hg_thread_mutex_t *mutex) - HG_LOCK_TRY_ACQUIRE(HG_UTIL_SUCCESS, *mutex); - -/** - * Unlock the mutex. - * - * \param mutex [IN/OUT] pointer to mutex object - */ -static HG_UTIL_INLINE void hg_thread_mutex_unlock(hg_thread_mutex_t *mutex) HG_LOCK_RELEASE(*mutex); - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE void -hg_thread_mutex_lock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS -{ -#ifdef _WIN32 - EnterCriticalSection(mutex); -#else - (void)pthread_mutex_lock(mutex); -#endif -} - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int -hg_thread_mutex_try_lock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS -{ -#ifdef _WIN32 - if (!TryEnterCriticalSection(mutex)) - return HG_UTIL_FAIL; -#else - if (pthread_mutex_trylock(mutex)) - return HG_UTIL_FAIL; -#endif - - return HG_UTIL_SUCCESS; -} - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE void -hg_thread_mutex_unlock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS -{ -#ifdef _WIN32 - LeaveCriticalSection(mutex); -#else - (void)pthread_mutex_unlock(mutex); -#endif -} - -#ifdef __cplusplus -} -#endif - -#endif /* MERCURY_THREAD_MUTEX_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.c b/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.c deleted file mode 100644 index 704e84f1ef9..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.c +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "mercury_thread_pool.h" - -#include "mercury_util_error.h" - -#include - -/****************/ -/* Local Macros */ -/****************/ - -/************************************/ -/* Local Type and Struct Definition */ -/************************************/ - -struct hg_thread_pool_private { - struct hg_thread_pool pool; - unsigned int thread_count; - hg_thread_t *threads; -}; - -/********************/ -/* Local Prototypes */ -/********************/ - -/** - * Worker thread run by the thread pool - */ -static HG_THREAD_RETURN_TYPE hg_thread_pool_worker(void *args); - -/*******************/ -/* Local Variables */ -/*******************/ - -/*---------------------------------------------------------------------------*/ -static HG_THREAD_RETURN_TYPE -hg_thread_pool_worker(void *args) -{ - hg_thread_ret_t ret = 0; - hg_thread_pool_t *pool = (hg_thread_pool_t *)args; - struct hg_thread_work *work; - - while (1) { - hg_thread_mutex_lock(&pool->mutex); - - /* If not shutting down and nothing to do, worker sleeps */ - while (!pool->shutdown && HG_QUEUE_IS_EMPTY(&pool->queue)) { - int rc; - - pool->sleeping_worker_count++; - - rc = hg_thread_cond_wait(&pool->cond, &pool->mutex); - HG_UTIL_CHECK_ERROR_NORET(rc != HG_UTIL_SUCCESS, unlock, - "Thread cannot wait on condition variable"); - - pool->sleeping_worker_count--; - } - - if (pool->shutdown && HG_QUEUE_IS_EMPTY(&pool->queue)) - goto unlock; - - /* Grab our task */ - work = HG_QUEUE_FIRST(&pool->queue); - HG_QUEUE_POP_HEAD(&pool->queue, entry); - - /* Unlock */ - hg_thread_mutex_unlock(&pool->mutex); - - /* Get to work */ - (*work->func)(work->args); - } - -unlock: - hg_thread_mutex_unlock(&pool->mutex); - - return ret; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_pool_init(unsigned int thread_count, hg_thread_pool_t **pool_ptr) -{ - int ret = HG_UTIL_SUCCESS, rc; - struct hg_thread_pool_private *priv_pool = NULL; - unsigned int i; - - HG_UTIL_CHECK_ERROR(pool_ptr == NULL, error, ret, HG_UTIL_FAIL, "NULL pointer"); - - priv_pool = (struct hg_thread_pool_private *)malloc(sizeof(struct hg_thread_pool_private)); - HG_UTIL_CHECK_ERROR(priv_pool == NULL, error, ret, HG_UTIL_FAIL, "Could not allocate thread pool"); - - priv_pool->pool.sleeping_worker_count = 0; - priv_pool->thread_count = thread_count; - priv_pool->threads = NULL; - HG_QUEUE_INIT(&priv_pool->pool.queue); - priv_pool->pool.shutdown = 0; - - rc = hg_thread_mutex_init(&priv_pool->pool.mutex); - HG_UTIL_CHECK_ERROR(rc != HG_UTIL_SUCCESS, error, ret, HG_UTIL_FAIL, "Could not initialize mutex"); - - rc = hg_thread_cond_init(&priv_pool->pool.cond); - HG_UTIL_CHECK_ERROR(rc != HG_UTIL_SUCCESS, error, ret, HG_UTIL_FAIL, - "Could not initialize thread condition"); - - priv_pool->threads = (hg_thread_t *)malloc(thread_count * sizeof(hg_thread_t)); - HG_UTIL_CHECK_ERROR(!priv_pool->threads, error, ret, HG_UTIL_FAIL, - "Could not allocate thread pool array"); - - /* Start worker threads */ - for (i = 0; i < thread_count; i++) { - rc = hg_thread_create(&priv_pool->threads[i], hg_thread_pool_worker, (void *)priv_pool); - HG_UTIL_CHECK_ERROR(rc != HG_UTIL_SUCCESS, error, ret, HG_UTIL_FAIL, "Could not create thread"); - } - - *pool_ptr = (struct hg_thread_pool *)priv_pool; - - return ret; - -error: - if (priv_pool) - hg_thread_pool_destroy((struct hg_thread_pool *)priv_pool); - - return ret; -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_pool_destroy(hg_thread_pool_t *pool) -{ - struct hg_thread_pool_private *priv_pool = (struct hg_thread_pool_private *)pool; - int ret = HG_UTIL_SUCCESS, rc; - unsigned int i; - - if (!priv_pool) - goto done; - - if (priv_pool->threads) { - hg_thread_mutex_lock(&priv_pool->pool.mutex); - - priv_pool->pool.shutdown = 1; - - rc = hg_thread_cond_broadcast(&priv_pool->pool.cond); - HG_UTIL_CHECK_ERROR(rc != HG_UTIL_SUCCESS, error, ret, HG_UTIL_FAIL, - "Could not broadcast condition signal"); - - hg_thread_mutex_unlock(&priv_pool->pool.mutex); - - for (i = 0; i < priv_pool->thread_count; i++) { - rc = hg_thread_join(priv_pool->threads[i]); - HG_UTIL_CHECK_ERROR(rc != HG_UTIL_SUCCESS, done, ret, HG_UTIL_FAIL, "Could not join thread"); - } - } - - rc = hg_thread_mutex_destroy(&priv_pool->pool.mutex); - HG_UTIL_CHECK_ERROR(rc != HG_UTIL_SUCCESS, done, ret, HG_UTIL_FAIL, "Could not destroy mutex"); - - rc = hg_thread_cond_destroy(&priv_pool->pool.cond); - HG_UTIL_CHECK_ERROR(rc != HG_UTIL_SUCCESS, done, ret, HG_UTIL_FAIL, "Could not destroy thread condition"); - - free(priv_pool->threads); - free(priv_pool); - -done: - return ret; - -error: - hg_thread_mutex_unlock(&priv_pool->pool.mutex); - - return ret; -} diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.h b/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.h deleted file mode 100644 index 6744eac9693..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.h +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef MERCURY_THREAD_POOL_H -#define MERCURY_THREAD_POOL_H - -#include "mercury_queue.h" -#include "mercury_thread.h" -#include "mercury_thread_condition.h" - -/*************************************/ -/* Public Type and Struct Definition */ -/*************************************/ - -typedef struct hg_thread_pool hg_thread_pool_t; - -struct hg_thread_pool { - unsigned int sleeping_worker_count; - HG_QUEUE_HEAD(hg_thread_work) queue; - int shutdown; - hg_thread_mutex_t mutex; - hg_thread_cond_t cond; -}; - -struct hg_thread_work { - hg_thread_func_t func; - void *args; - HG_QUEUE_ENTRY(hg_thread_work) entry; /* Internal */ -}; - -/*****************/ -/* Public Macros */ -/*****************/ - -/*********************/ -/* Public Prototypes */ -/*********************/ - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Initialize the thread pool. - * - * \param thread_count [IN] number of threads that will be created at - * initialization - * \param pool [OUT] pointer to pool object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_pool_init(unsigned int thread_count, hg_thread_pool_t **pool); - -/** - * Destroy the thread pool. - * - * \param pool [IN/OUT] pointer to pool object - * - * \return Non-negative on success or negative on failure - */ -HG_UTIL_PUBLIC int hg_thread_pool_destroy(hg_thread_pool_t *pool); - -/** - * Post work to the pool. Note that the operation may be queued depending on - * the number of threads and number of tasks already running. - * - * \param pool [IN/OUT] pointer to pool object - * \param work [IN] pointer to work struct - * - * \return Non-negative on success or negative on failure - */ -static HG_UTIL_INLINE int hg_thread_pool_post(hg_thread_pool_t *pool, struct hg_thread_work *work); - -/*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int -hg_thread_pool_post(hg_thread_pool_t *pool, struct hg_thread_work *work) -{ - int ret = HG_UTIL_SUCCESS; - - if (!pool || !work) - return HG_UTIL_FAIL; - - if (!work->func) - return HG_UTIL_FAIL; - - hg_thread_mutex_lock(&pool->mutex); - - /* Are we shutting down ? */ - if (pool->shutdown) { - ret = HG_UTIL_FAIL; - goto unlock; - } - - /* Add task to task queue */ - HG_QUEUE_PUSH_TAIL(&pool->queue, work, entry); - - /* Wake up sleeping worker */ - if (pool->sleeping_worker_count && (hg_thread_cond_signal(&pool->cond) != HG_UTIL_SUCCESS)) - ret = HG_UTIL_FAIL; - -unlock: - hg_thread_mutex_unlock(&pool->mutex); - - return ret; -} - -#ifdef __cplusplus -} -#endif - -#endif /* MERCURY_THREAD_POOL_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_util_config.h b/src/H5FDsubfiling/mercury/src/util/mercury_util_config.h deleted file mode 100644 index 53189fb59a5..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_util_config.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef MERCURY_UTIL_CONFIG_H -#define MERCURY_UTIL_CONFIG_H - -/*************************************/ -/* Public Type and Struct Definition */ -/*************************************/ - -#include "H5private.h" - -/* Type definitions */ -#include -#include -#include - -/*****************/ -/* Public Macros */ -/*****************/ - -/* Reflects any major or incompatible public API changes */ -#define HG_UTIL_VERSION_MAJOR 4 -/* Reflects any minor backwards compatible API or functionality addition */ -#define HG_UTIL_VERSION_MINOR 0 -/* Reflects any backwards compatible bug fixes */ -#define HG_UTIL_VERSION_PATCH 0 - -/* Return codes */ -#define HG_UTIL_SUCCESS 0 -#define HG_UTIL_FAIL -1 - -#include - -/* Inline macro */ -#ifdef _WIN32 -#define HG_UTIL_INLINE __inline -#else -#define HG_UTIL_INLINE __inline__ -#endif - -#define HG_UTIL_PUBLIC -#define HG_UTIL_PRIVATE -#define HG_UTIL_PLUGIN - -#endif /* MERCURY_UTIL_CONFIG_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_util_error.h b/src/H5FDsubfiling/mercury/src/util/mercury_util_error.h deleted file mode 100644 index b5cb5cca2a4..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_util_error.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef MERCURY_UTIL_ERROR_H -#define MERCURY_UTIL_ERROR_H - -#include "mercury_util_config.h" - -/* Branch predictor hints */ -#ifndef _WIN32 -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) -#else -#define likely(x) (x) -#define unlikely(x) (x) -#endif - -/* Error macros */ -#define HG_UTIL_GOTO_DONE(label, ret, ret_val) \ - do { \ - ret = ret_val; \ - goto label; \ - } while (0) - -#define HG_UTIL_GOTO_ERROR(label, ret, err_val, ...) \ - do { \ - ret = err_val; \ - goto label; \ - } while (0) - -/* Check for cond, set ret to err_val and goto label */ -#define HG_UTIL_CHECK_ERROR(cond, label, ret, err_val, ...) \ - do { \ - if (unlikely(cond)) { \ - ret = err_val; \ - goto label; \ - } \ - } while (0) - -#define HG_UTIL_CHECK_ERROR_NORET(cond, label, ...) \ - do { \ - if (unlikely(cond)) { \ - goto label; \ - } \ - } while (0) - -#endif /* MERCURY_UTIL_ERROR_H */ diff --git a/src/H5TS.c b/src/H5TS.c index 30862401f6c..590e20b70ff 100644 --- a/src/H5TS.c +++ b/src/H5TS.c @@ -12,25 +12,28 @@ /* * Purpose: This file contains the framework for ensuring that the global - * library lock is held when an API routine is called. This - * framework works in concert with the FUNC_ENTER_API / FUNC_LEAVE_API - * macros defined in H5private.h. + * library lock is held when an API routine is called. This framework + * works in concert with the FUNC_ENTER_API / FUNC_LEAVE_API macros + * defined in H5private.h. * - * Note: Because this threadsafety framework operates outside the library, - * it does not use the error stack and only uses the "namecheck only" - * FUNC_ENTER_* / FUNC_LEAVE_* macros. + * 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 "H5MMprivate.h" /* Memory management */ +#include "H5private.h" /* Generic Functions */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5TSpkg.h" /* Threadsafety */ #ifdef H5_HAVE_THREADSAFE @@ -42,366 +45,25 @@ /* Local Typedefs */ /******************/ -/* Cancellability structure */ -typedef struct H5TS_cancel_struct { - int previous_state; - unsigned int cancel_count; -} H5TS_cancel_t; - -/* Function pointer typedef for thread callback function */ -typedef void *(*H5TS_thread_cb_t)(void *); - /********************/ /* Local Prototypes */ /********************/ -static void H5TS__key_destructor(void *key_val); -static herr_t H5TS__mutex_acquire(H5TS_mutex_t *mutex, unsigned int lock_count, bool *acquired); -static herr_t H5TS__mutex_unlock(H5TS_mutex_t *mutex, unsigned int *lock_count); /*********************/ /* Package Variables */ /*********************/ +/* API threadsafety info */ +H5TS_api_info_t H5TS_api_info_p; + /*****************************/ /* Library Private Variables */ /*****************************/ -/* Global variable definitions */ -#ifdef H5_HAVE_WIN_THREADS -H5TS_once_t H5TS_first_init_g; -#else -H5TS_once_t H5TS_first_init_g = PTHREAD_ONCE_INIT; -#endif - -/* Thread-local keys, used by other interfaces */ -/* Error stack */ -#ifdef H5_HAVE_WIN_THREADS -H5TS_key_t H5TS_errstk_key_g = TLS_OUT_OF_INDEXES; -#else -H5TS_key_t H5TS_errstk_key_g; -#endif - -#ifdef H5_HAVE_CODESTACK -/* Function stack */ -#ifdef H5_HAVE_WIN_THREADS -H5TS_key_t H5TS_funcstk_key_g = TLS_OUT_OF_INDEXES; -#else -H5TS_key_t H5TS_funcstk_key_g; -#endif -#endif /* H5_HAVE_CODESTACK */ - -/* API context */ -#ifdef H5_HAVE_WIN_THREADS -H5TS_key_t H5TS_apictx_key_g = TLS_OUT_OF_INDEXES; -#else -H5TS_key_t H5TS_apictx_key_g; -#endif - /*******************/ /* Local Variables */ /*******************/ -/* Thread-local keys, used in this module */ -/* Thread cancellation state */ -#ifdef H5_HAVE_WIN_THREADS -static H5TS_key_t H5TS_cancel_key_s = TLS_OUT_OF_INDEXES; -#else -static H5TS_key_t H5TS_cancel_key_s; -#endif - -#ifndef H5_HAVE_WIN_THREADS - -/* An H5TS_tid_t is a record of a thread identifier that is - * available for reuse. - */ -struct _tid; -typedef struct _tid H5TS_tid_t; - -struct _tid { - H5TS_tid_t *next; - uint64_t id; -}; - -/* Pointer to first free thread ID record or NULL. */ -static H5TS_tid_t *H5TS_tid_next_free = NULL; -static uint64_t H5TS_tid_next_id = 0; - -/* Mutual exclusion for access to H5TS_tid_next_free and H5TS_tid_next_id. */ -static pthread_mutex_t H5TS_tid_mtx; - -/* Key for thread-local storage of the thread ID. */ -static H5TS_key_t H5TS_tid_key; - -#endif /* H5_HAVE_WIN_THREADS */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS__key_destructor - * - * USAGE - * H5TS__key_destructor() - * - * RETURNS - * None - * - * DESCRIPTION - * Frees the memory for a key. Called by each thread as it exits. - * Currently all the thread-specific information for all keys are simple - * structures allocated with malloc, so we can free them all uniformly. - *-------------------------------------------------------------------------- - */ -static void -H5TS__key_destructor(void *key_val) -{ - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - - /* Use free() here instead of H5MM_xfree(), to avoid calling the H5CS routines */ - if (key_val != NULL) - free(key_val); - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* end H5TS__key_destructor() */ - -#ifndef H5_HAVE_WIN_THREADS - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_tid_destructor - * - * USAGE - * H5TS_tid_destructor() - * - * RETURNS - * - * DESCRIPTION - * When a thread shuts down, put its ID record on the free list. - * - *-------------------------------------------------------------------------- - */ -static void -H5TS_tid_destructor(void *_v) -{ - H5TS_tid_t *tid = _v; - - if (tid == NULL) - return; - - /* TBD use an atomic CAS */ - pthread_mutex_lock(&H5TS_tid_mtx); - tid->next = H5TS_tid_next_free; - H5TS_tid_next_free = tid; - pthread_mutex_unlock(&H5TS_tid_mtx); -} - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_tid_init - * - * USAGE - * H5TS_tid_init() - * - * RETURNS - * - * DESCRIPTION - * Initialize for integer thread identifiers. - * - *-------------------------------------------------------------------------- - */ -static void -H5TS_tid_init(void) -{ - pthread_mutex_init(&H5TS_tid_mtx, NULL); - pthread_key_create(&H5TS_tid_key, H5TS_tid_destructor); -} - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_thread_id - * - * USAGE - * uint64_t id = H5TS_thread_id() - * - * RETURNS - * Return an integer identifier, ID, for the current thread. - * - * DESCRIPTION - * The ID satisfies the following properties: - * - * 1 1 <= ID <= UINT64_MAX - * 2 ID is constant over the thread's lifetime. - * 3 No two threads share an ID during their lifetimes. - * 4 A thread's ID is available for reuse as soon as it is joined. - * - * ID 0 is reserved. H5TS_thread_id() returns 0 if the library was not - * built with thread safety or if an error prevents it from assigning an - * ID. - * - *-------------------------------------------------------------------------- - */ -uint64_t -H5TS_thread_id(void) -{ - H5TS_tid_t *tid = pthread_getspecific(H5TS_tid_key); - H5TS_tid_t proto_tid; - - /* An ID is already assigned. */ - if (tid != NULL) - return tid->id; - - /* An ID is *not* already assigned: reuse an ID that's on the - * free list, or else generate a new ID. - * - * Allocating memory while holding a mutex is bad form, so - * point `tid` at `proto_tid` if we need to allocate some - * memory. - */ - pthread_mutex_lock(&H5TS_tid_mtx); - if ((tid = H5TS_tid_next_free) != NULL) - H5TS_tid_next_free = tid->next; - else if (H5TS_tid_next_id != UINT64_MAX) { - tid = &proto_tid; - tid->id = ++H5TS_tid_next_id; - } - pthread_mutex_unlock(&H5TS_tid_mtx); - - /* If a prototype ID record was established, copy it to the heap. */ - if (tid == &proto_tid) - if ((tid = malloc(sizeof(*tid))) != NULL) - *tid = proto_tid; - - if (tid == NULL) - return 0; - - /* Finish initializing the ID record and set a thread-local pointer - * to it. - */ - tid->next = NULL; - if (pthread_setspecific(H5TS_tid_key, tid) != 0) { - H5TS_tid_destructor(tid); - return 0; - } - - return tid->id; -} - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_pthread_first_thread_init - * - * USAGE - * H5TS_pthread_first_thread_init() - * - * RETURNS - * - * DESCRIPTION - * Initialization of global API lock, keys for per-thread error stacks and - * cancallability information. Called by the first thread that enters the - * library. - * - * PROGRAMMER: Chee Wai LEE - * May 2, 2000 - * - *-------------------------------------------------------------------------- - */ -void -H5TS_pthread_first_thread_init(void) -{ - H5_g.H5_libinit_g = false; /* Library hasn't been initialized */ - H5_g.H5_libterm_g = false; /* Library isn't being shutdown */ - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN32_API -#ifdef PTW32_STATIC_LIB - pthread_win32_process_attach_np(); -#endif -#endif - - /* initialize global API mutex lock */ - pthread_mutex_init(&H5_g.init_lock.atomic_lock, NULL); - pthread_cond_init(&H5_g.init_lock.cond_var, NULL); - H5_g.init_lock.lock_count = 0; - - pthread_mutex_init(&H5_g.init_lock.atomic_lock2, NULL); - H5_g.init_lock.attempt_lock_count = 0; - - /* Initialize integer thread identifiers. */ - H5TS_tid_init(); - - /* initialize key for thread-specific error stacks */ - pthread_key_create(&H5TS_errstk_key_g, H5TS__key_destructor); - -#ifdef H5_HAVE_CODESTACK - /* initialize key for thread-specific function stacks */ - pthread_key_create(&H5TS_funcstk_key_g, H5TS__key_destructor); -#endif /* H5_HAVE_CODESTACK */ - - /* initialize key for thread-specific API contexts */ - pthread_key_create(&H5TS_apictx_key_g, H5TS__key_destructor); - - /* initialize key for thread cancellability mechanism */ - pthread_key_create(&H5TS_cancel_key_s, H5TS__key_destructor); - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* end H5TS_pthread_first_thread_init() */ -#endif /* H5_HAVE_WIN_THREADS */ - -/*-------------------------------------------------------------------------- - * Function: H5TS__mutex_acquire - * - * Purpose: Attempts to acquire a mutex lock, without blocking - * - * Note: On success, the 'acquired' flag indicates if the HDF5 library - * global lock was acquired. - * - * Note: The Windows threads code is very likely bogus. - * - * Return: Non-negative on success / Negative on failure - *-------------------------------------------------------------------------- - */ -static herr_t -H5TS__mutex_acquire(H5TS_mutex_t *mutex, unsigned int lock_count, bool *acquired) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_PACKAGE_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - EnterCriticalSection(&mutex->CriticalSection); - *acquired = true; -#else /* H5_HAVE_WIN_THREADS */ - /* Attempt to acquire the mutex lock */ - if (0 == pthread_mutex_lock(&mutex->atomic_lock)) { - pthread_t my_thread_id = pthread_self(); - - /* Check if locked already */ - if (mutex->lock_count) { - /* Check for this thread already owning the lock */ - if (pthread_equal(my_thread_id, mutex->owner_thread)) { - /* Already owned by self - increment count */ - mutex->lock_count += lock_count; - *acquired = true; - } - else - *acquired = false; - } - else { - /* Take ownership of the mutex */ - mutex->owner_thread = my_thread_id; - mutex->lock_count = lock_count; - *acquired = true; - } - - if (0 != pthread_mutex_unlock(&mutex->atomic_lock)) - ret_value = -1; - } - else - ret_value = -1; -#endif /* H5_HAVE_WIN_THREADS */ - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS__mutex_acquire() */ - /*-------------------------------------------------------------------------- * Function: H5TSmutex_acquire * @@ -411,192 +73,23 @@ H5TS__mutex_acquire(H5TS_mutex_t *mutex, unsigned int lock_count, bool *acquired * global lock was acquired. * * Return: Non-negative on success / Negative on failure - *-------------------------------------------------------------------------- - */ -herr_t -H5TSmutex_acquire(unsigned int lock_count, bool *acquired) -{ - FUNC_ENTER_API_NAMECHECK_ONLY - - FUNC_LEAVE_API_NAMECHECK_ONLY(H5TS__mutex_acquire(&H5_g.init_lock, lock_count, acquired)) -} -/* end H5TSmutex_acquire() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_mutex_lock - * - * USAGE - * H5TS_mutex_lock(&mutex_var) - * - * RETURNS - * Non-negative on success / Negative on failure - * - * DESCRIPTION - * Recursive lock semantics for HDF5 (locking) - - * Multiple acquisition of a lock by a thread is permitted with a - * corresponding unlock operation required. - * - * PROGRAMMER: Chee Wai LEE - * May 2, 2000 - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS_mutex_lock(H5TS_mutex_t *mutex) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - EnterCriticalSection(&mutex->CriticalSection); -#else /* H5_HAVE_WIN_THREADS */ - /* Acquire the "attempt" lock, increment the attempt lock count, release the lock */ - ret_value = pthread_mutex_lock(&mutex->atomic_lock2); - if (ret_value) - HGOTO_DONE(ret_value); - mutex->attempt_lock_count++; - ret_value = pthread_mutex_unlock(&mutex->atomic_lock2); - if (ret_value) - HGOTO_DONE(ret_value); - - /* Acquire the library lock */ - ret_value = pthread_mutex_lock(&mutex->atomic_lock); - if (ret_value) - HGOTO_DONE(ret_value); - - /* Check if this thread already owns the lock */ - if (mutex->lock_count && pthread_equal(pthread_self(), mutex->owner_thread)) - /* already owned by self - increment count */ - mutex->lock_count++; - else { - /* Wait until the lock is released by current owner thread */ - while (mutex->lock_count) - pthread_cond_wait(&mutex->cond_var, &mutex->atomic_lock); - - /* After we've received the signal, take ownership of the mutex */ - mutex->owner_thread = pthread_self(); - mutex->lock_count = 1; - } - - /* Release the library lock */ - ret_value = pthread_mutex_unlock(&mutex->atomic_lock); - -done: -#endif /* H5_HAVE_WIN_THREADS */ - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS_mutex_lock() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS__mutex_unlock - * - * USAGE - * H5TS__mutex_unlock(&mutex_var, &lock_count) - * - * RETURNS - * Non-negative on success / Negative on failure - * - * DESCRIPTION - * Recursive lock semantics for HDF5 (unlocking) - - * Reset the lock and return the current lock count - * - * PROGRAMMER: Houjun Tang - * Nov 3, 2020 - * - *-------------------------------------------------------------------------- - */ -static herr_t -H5TS__mutex_unlock(H5TS_mutex_t *mutex, unsigned int *lock_count) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* Releases ownership of the specified critical section object. */ - LeaveCriticalSection(&mutex->CriticalSection); -#else /* H5_HAVE_WIN_THREADS */ - - /* Reset the lock count for this thread */ - ret_value = pthread_mutex_lock(&mutex->atomic_lock); - if (ret_value) - HGOTO_DONE(ret_value); - *lock_count = mutex->lock_count; - mutex->lock_count = 0; - ret_value = pthread_mutex_unlock(&mutex->atomic_lock); - - /* If the lock count drops to zero, signal the condition variable, to - * wake another thread. - */ - if (mutex->lock_count == 0) { - int err; - - err = pthread_cond_signal(&mutex->cond_var); - if (err != 0) - ret_value = err; - } - -done: -#endif /* H5_HAVE_WIN_THREADS */ - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS__mutex_unlock */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_mutex_unlock - * - * USAGE - * H5TS_mutex_unlock(&mutex_var) - * - * RETURNS - * Non-negative on success / Negative on failure - * - * DESCRIPTION - * Recursive lock semantics for HDF5 (unlocking) - - * Multiple acquisition of a lock by a thread is permitted with a - * corresponding unlock operation required. - * - * PROGRAMMER: Chee Wai LEE - * May 2, 2000 * *-------------------------------------------------------------------------- */ herr_t -H5TS_mutex_unlock(H5TS_mutex_t *mutex) +H5TSmutex_acquire(unsigned lock_count, bool *acquired) { herr_t ret_value = SUCCEED; - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* Releases ownership of the specified critical section object. */ - LeaveCriticalSection(&mutex->CriticalSection); -#else /* H5_HAVE_WIN_THREADS */ - - /* Decrement the lock count for this thread */ - ret_value = pthread_mutex_lock(&mutex->atomic_lock); - if (ret_value) - HGOTO_DONE(ret_value); - mutex->lock_count--; - ret_value = pthread_mutex_unlock(&mutex->atomic_lock); - - /* If the lock count drops to zero, signal the condition variable, to - * wake another thread. - */ - if (mutex->lock_count == 0) { - int err; + FUNC_ENTER_API_NAMECHECK_ONLY - err = pthread_cond_signal(&mutex->cond_var); - if (err != 0) - ret_value = err; - } + /* Acquire the "API" lock */ + if (H5_UNLIKELY(H5TS__api_mutex_acquire(lock_count, acquired) < 0)) + HGOTO_DONE(FAIL); done: -#endif /* H5_HAVE_WIN_THREADS */ - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_mutex_unlock */ + FUNC_LEAVE_API_NAMECHECK_ONLY(ret_value) +} /* end H5TSmutex_acquire() */ /*-------------------------------------------------------------------------- * Function: H5TSmutex_get_attempt_count @@ -604,30 +97,21 @@ H5TS_mutex_unlock(H5TS_mutex_t *mutex) * Purpose: Get the current count of the global lock attempt * * Return: Non-negative on success / Negative on failure + * + * Programmer: Houjun Tang + * June 24, 2019 + * *-------------------------------------------------------------------------- */ herr_t -H5TSmutex_get_attempt_count(unsigned int *count) +H5TSmutex_get_attempt_count(unsigned *count) { herr_t ret_value = SUCCEED; FUNC_ENTER_API_NAMECHECK_ONLY -#ifdef H5_HAVE_WIN_THREADS - /* Add Win32 equivalent here when async is supported */ -#else /* H5_HAVE_WIN_THREADS */ - ret_value = pthread_mutex_lock(&H5_g.init_lock.atomic_lock2); - if (ret_value) - HGOTO_DONE(ret_value); + *count = H5TS_atomic_load_uint(&H5TS_api_info_p.attempt_lock_count); - *count = H5_g.init_lock.attempt_lock_count; - - ret_value = pthread_mutex_unlock(&H5_g.init_lock.atomic_lock2); - if (ret_value) - HGOTO_DONE(ret_value); - -done: -#endif /* H5_HAVE_WIN_THREADS */ FUNC_LEAVE_API_NAMECHECK_ONLY(ret_value) } /* end H5TSmutex_get_attempt_count() */ @@ -637,351 +121,21 @@ H5TSmutex_get_attempt_count(unsigned int *count) * Purpose: Releases the HDF5 library global lock * * Return: Non-negative on success / Negative on failure + * *-------------------------------------------------------------------------- */ herr_t -H5TSmutex_release(unsigned int *lock_count) +H5TSmutex_release(unsigned *lock_count) { herr_t ret_value = SUCCEED; FUNC_ENTER_API_NAMECHECK_ONLY + /* Release the "API" lock */ *lock_count = 0; - if (0 != H5TS__mutex_unlock(&H5_g.init_lock, lock_count)) - ret_value = -1; + if (H5_UNLIKELY(H5TS__api_mutex_release(lock_count) < 0)) + ret_value = FAIL; FUNC_LEAVE_API_NAMECHECK_ONLY(ret_value) } /* end H5TSmutex_release() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_cancel_count_inc - * - * USAGE - * H5TS_cancel_count_inc() - * - * RETURNS - * Non-negative on success / Negative on failure - * - * DESCRIPTION - * Creates a cancellation counter for a thread if it is the first time - * the thread is entering the library. - * - * if counter value is zero, then set cancellability type of the thread - * to PTHREAD_CANCEL_DISABLE as thread is entering the library and store - * the previous cancellability type into cancellation counter. - * Increase the counter value by 1. - * - * PROGRAMMER: Chee Wai LEE - * May 2, 2000 - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS_cancel_count_inc(void) -{ -#ifndef H5_HAVE_WIN_THREADS - H5TS_cancel_t *cancel_counter; -#endif /* H5_HAVE_WIN_THREADS */ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* unsupported */ -#else /* H5_HAVE_WIN_THREADS */ - /* Acquire the thread's cancellation counter */ - cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_s); - - /* Check if it's created yet */ - if (!cancel_counter) { - /* - * First time thread calls library - create new counter and associate - * with key. - * - * Don't use H5MM calls here since the destructor has to use free in - * order to avoid codestack calls. - */ - cancel_counter = (H5TS_cancel_t *)calloc(1, sizeof(H5TS_cancel_t)); - if (NULL == cancel_counter) - HGOTO_DONE(FAIL); - - /* Set the thread's cancellation counter with the new object */ - ret_value = pthread_setspecific(H5TS_cancel_key_s, (void *)cancel_counter); - if (ret_value) { - free(cancel_counter); - HGOTO_DONE(FAIL); - } - } - - /* Check if thread entering library */ - if (cancel_counter->cancel_count == 0) - /* Set cancellation state to 'disable', and remember previous state */ - ret_value = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_counter->previous_state); - - /* Increment # of times the library API was re-entered, to avoid resetting - * previous cancellation state until the final API routine is returning. - */ - ++cancel_counter->cancel_count; - -done: -#endif /* H5_HAVE_WIN_THREADS */ - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS_cancel_count_inc() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_cancel_count_dec - * - * USAGE - * H5TS_cancel_count_dec() - * - * RETURNS - * 0 on success and a non-zero error code on error. - * - * DESCRIPTION - * If counter value is one, then set cancellability type of the thread - * to the previous cancellability type stored in the cancellation counter. - * (the thread is leaving the library). - * - * Decrement the counter value by 1. - * - * PROGRAMMER: Chee Wai LEE - * May 2, 2000 - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS_cancel_count_dec(void) -{ -#ifndef H5_HAVE_WIN_THREADS - H5TS_cancel_t *cancel_counter; -#endif /* H5_HAVE_WIN_THREADS */ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* unsupported */ -#else /* H5_HAVE_WIN_THREADS */ - /* Acquire the thread's cancellation counter */ - cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_s); - - /* Check for leaving last API routine */ - if (cancel_counter->cancel_count == 1) - /* Reset to previous thread cancellation state, if last API */ - ret_value = pthread_setcancelstate(cancel_counter->previous_state, NULL); - - /* Decrement cancellation counter */ - --cancel_counter->cancel_count; -#endif /* H5_HAVE_WIN_THREADS */ - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS_cancel_count_dec() */ - -#ifdef H5_HAVE_WIN_THREADS -/*-------------------------------------------------------------------------- - * NAME - * H5TS_win32_process_enter - * - * RETURNS - * SUCCEED/FAIL - * - * DESCRIPTION - * Per-process setup on Windows when using Win32 threads. - * - *-------------------------------------------------------------------------- - */ -H5_DLL BOOL CALLBACK -H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex) -{ - BOOL ret_value = true; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - /* Initialize the critical section (can't fail) */ - InitializeCriticalSection(&H5_g.init_lock.CriticalSection); - - /* Set up thread local storage */ - if (TLS_OUT_OF_INDEXES == (H5TS_errstk_key_g = TlsAlloc())) - ret_value = false; - -#ifdef H5_HAVE_CODESTACK - if (TLS_OUT_OF_INDEXES == (H5TS_funcstk_key_g = TlsAlloc())) - ret_value = false; -#endif /* H5_HAVE_CODESTACK */ - - if (TLS_OUT_OF_INDEXES == (H5TS_apictx_key_g = TlsAlloc())) - ret_value = false; - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_win32_process_enter() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_win32_thread_enter - * - * RETURNS - * SUCCEED/FAIL - * - * DESCRIPTION - * Per-thread setup on Windows when using Win32 threads. - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS_win32_thread_enter(void) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - /* Currently a placeholder function. TLS setup is performed - * elsewhere in the library. - * - * WARNING: Do NOT use C standard library functions here. - * CRT functions are not allowed in DllMain, which is where this code - * is used. - */ - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_win32_thread_enter() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_win32_process_exit - * - * RETURNS - * SUCCEED/FAIL - * - * DESCRIPTION - * Per-process cleanup on Windows when using Win32 threads. - * - *-------------------------------------------------------------------------- - */ -void -H5TS_win32_process_exit(void) -{ - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - /* Windows uses a different thread local storage mechanism which does - * not support auto-freeing like pthreads' keys. - * - * This function is currently registered via atexit() and is called - * AFTER H5_term_library(). - */ - - /* Clean up critical section resources (can't fail) */ - DeleteCriticalSection(&H5_g.init_lock.CriticalSection); - - /* Clean up per-process thread local storage */ - if (H5TS_errstk_key_g != TLS_OUT_OF_INDEXES) - TlsFree(H5TS_errstk_key_g); -#ifdef H5_HAVE_CODESTACK - if (H5TS_funcstk_key_g != TLS_OUT_OF_INDEXES) - TlsFree(H5TS_funcstk_key_g); -#endif /* H5_HAVE_CODESTACK */ - if (H5TS_apictx_key_g != TLS_OUT_OF_INDEXES) - TlsFree(H5TS_apictx_key_g); - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* H5TS_win32_process_exit() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_win32_thread_exit - * - * RETURNS - * SUCCEED/FAIL - * - * DESCRIPTION - * Per-thread cleanup on Windows when using Win32 threads. - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS_win32_thread_exit(void) -{ - LPVOID lpvData; - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - /* Windows uses a different thread local storage mechanism which does - * not support auto-freeing like pthreads' keys. - * - * WARNING: Do NOT use C standard library functions here. - * CRT functions are not allowed in DllMain, which is where this code - * is used. - */ - - /* Clean up per-thread thread local storage */ - if (H5TS_errstk_key_g != TLS_OUT_OF_INDEXES) { - lpvData = TlsGetValue(H5TS_errstk_key_g); - if (lpvData) - LocalFree((HLOCAL)lpvData); - } - -#ifdef H5_HAVE_CODESTACK - if (H5TS_funcstk_key_g != TLS_OUT_OF_INDEXES) { - lpvData = TlsGetValue(H5TS_funcstk_key_g); - if (lpvData) - LocalFree((HLOCAL)lpvData); - } -#endif /* H5_HAVE_CODESTACK */ - - if (H5TS_apictx_key_g != TLS_OUT_OF_INDEXES) { - lpvData = TlsGetValue(H5TS_apictx_key_g); - if (lpvData) - LocalFree((HLOCAL)lpvData); - } - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_win32_thread_exit() */ -#endif /* H5_HAVE_WIN_THREADS */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_create_thread - * - * RETURNS - * Thread identifier. - * - * DESCRIPTION - * Spawn off a new thread calling function 'func' with input 'udata'. - * - * PROGRAMMER: Mike McGreevy - * August 31, 2010 - * - *-------------------------------------------------------------------------- - */ -H5TS_thread_t -H5TS_create_thread(H5TS_thread_cb_t func, H5TS_attr_t *attr, void *udata) -{ - H5TS_thread_t ret_value; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* When calling C runtime functions, you should use _beginthread or - * _beginthreadex instead of CreateThread. Threads created with - * CreateThread risk being killed in low-memory situations. Since we - * only create threads in our test code, this is unlikely to be an issue - * and we'll use the easier-to-deal-with CreateThread for now. - * - * NOTE: _beginthread() auto-recycles its handle when execution completes - * so you can't wait on it, making it unsuitable for the existing - * test code. - */ - ret_value = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, udata, 0, NULL); - -#else /* H5_HAVE_WIN_THREADS */ - - pthread_create(&ret_value, attr, (void *(*)(void *))func, udata); - -#endif /* H5_HAVE_WIN_THREADS */ - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_create_thread */ - #endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSatomic.c b/src/H5TSatomic.c new file mode 100644 index 00000000000..1e3798cd026 --- /dev/null +++ b/src/H5TSatomic.c @@ -0,0 +1,167 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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: C11 atomic emulation outines + * + * 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 + +#ifndef H5_HAVE_STDATOMIC_H + +/****************/ +/* Local Macros */ +/****************/ + +/******************/ +/* Local Typedefs */ +/******************/ + +/********************/ +/* Local Prototypes */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_init_int + * + * Purpose: Initializes an atomic 'int' 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_int(H5TS_atomic_int_t *obj, int desired) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Initialize mutex that protects the "atomic" value */ + (void) + 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_int() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_destroy_int + * + * Purpose: Destroys / releases resources for an atomic 'int' 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_int(H5TS_atomic_int_t *obj) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Destroy mutex that protects the "atomic" value */ + (void) + H5TS_mutex_destroy(&obj->mutex); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS_atomic_destroy_int() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_init_uint + * + * Purpose: Initializes an atomic 'unsigned' 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_uint(H5TS_atomic_uint_t *obj, unsigned desired) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Initialize mutex that protects the "atomic" value */ + (void) + 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_uint() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_destroy_uint + * + * Purpose: Destroys / releases resources for an atomic 'unsigned' 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_uint(H5TS_atomic_uint_t *obj) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Destroy mutex that protects the "atomic" value */ + (void) + H5TS_mutex_destroy(&obj->mutex); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS_atomic_destroy_uint() */ + +#endif /* H5_HAVE_STDATOMIC_H */ + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSatomic.h b/src/H5TSatomic.h new file mode 100644 index 00000000000..dc6e5e4ca0e --- /dev/null +++ b/src/H5TSatomic.h @@ -0,0 +1,280 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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: C11 atomic emulation outines + * + * 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 */ +/***********/ + +#ifndef H5_HAVE_STDATOMIC_H + +/****************/ +/* Local Macros */ +/****************/ + +/******************/ +/* Local Typedefs */ +/******************/ + +/********************/ +/* Local Prototypes */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_load_int + * + * Purpose: Retrieves the value of atomic 'int' variable object. + * + * Return: Value of the atomic 'int' + * + *-------------------------------------------------------------------------- + */ +static inline int +H5TS_atomic_load_int(H5TS_atomic_int_t *obj) +{ + int ret_value; + + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Get the value */ + ret_value = obj->value; + + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return ret_value; +} /* end H5TS_atomic_load_int() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_store_int + * + * Purpose: Atomically replaces the value of the atomic 'int' variable + * + * Return: None + * + *-------------------------------------------------------------------------- + */ +static inline void +H5TS_atomic_store_int(H5TS_atomic_int_t *obj, int desired) +{ + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Set the value */ + obj->value = desired; + + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return; +} /* end H5TS_atomic_store_int() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_fetch_add_int + * + * Purpose: Atomically replaces the value of an atomic 'int' variable with the + * result of addition of the 'arg' to the old value of the + * atomic variable. + * + * Return: Returns the value of the atomic variable held previously + * + *-------------------------------------------------------------------------- + */ +static inline int +H5TS_atomic_fetch_add_int(H5TS_atomic_int_t *obj, int arg) +{ + int ret_value; + + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Get the current value */ + ret_value = obj->value; + + /* Increment the value */ + obj->value += arg; + + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return ret_value; +} /* end H5TS_atomic_fetch_add_int() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_fetch_sub_int + * + * Purpose: Atomically replaces the value of an atomic 'int' variable with the + * result of subtracting the 'arg' from the old value of the + * atomic variable. + * + * Return: Returns the value of the atomic variable held previously + * + *-------------------------------------------------------------------------- + */ +static inline int +H5TS_atomic_fetch_sub_int(H5TS_atomic_int_t *obj, int arg) +{ + int ret_value; + + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Get the current value */ + ret_value = obj->value; + + /* Decrement the value */ + obj->value -= arg; + + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return ret_value; +} /* end H5TS_atomic_fetch_sub_int() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_load_uint + * + * Purpose: Retrieves the value of atomic 'unsigned' variable object. + * + * Return: Value of the atomic 'unsigned' + * + *-------------------------------------------------------------------------- + */ +static inline unsigned +H5TS_atomic_load_uint(H5TS_atomic_uint_t *obj) +{ + unsigned ret_value; + + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Get the value */ + ret_value = obj->value; + + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return ret_value; +} /* end H5TS_atomic_load_uint() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_store_uint + * + * Purpose: Atomically replaces the value of the atomic 'unsigned' variable + * + * Return: None + * + *-------------------------------------------------------------------------- + */ +static inline void +H5TS_atomic_store_uint(H5TS_atomic_uint_t *obj, unsigned desired) +{ + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Set the value */ + obj->value = desired; + + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return; +} /* end H5TS_atomic_store_uint() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_fetch_add_uint + * + * Purpose: Atomically replaces the value of an atomic 'unsigned' variable with the + * result of addition of the 'arg' to the old value of the + * atomic variable. + * + * Return: Returns the value of the atomic variable held previously + * + *-------------------------------------------------------------------------- + */ +static inline unsigned +H5TS_atomic_fetch_add_uint(H5TS_atomic_uint_t *obj, unsigned arg) +{ + unsigned ret_value; + + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Get the current value */ + ret_value = obj->value; + + /* Increment the value */ + obj->value += arg; + + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return ret_value; +} /* end H5TS_atomic_fetch_add_uint() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_atomic_fetch_sub_uint + * + * Purpose: Atomically replaces the value of an atomic 'unsigned' variable with the + * result of subtracting the 'arg' from the old value of the + * atomic variable. + * + * Return: Returns the value of the atomic variable held previously + * + *-------------------------------------------------------------------------- + */ +static inline unsigned +H5TS_atomic_fetch_sub_uint(H5TS_atomic_uint_t *obj, unsigned arg) +{ + unsigned ret_value; + + /* Lock mutex that protects the "atomic" value */ + H5TS_mutex_lock(&obj->mutex); + + /* Get the current value */ + ret_value = obj->value; + + /* Decrement the value */ + obj->value -= arg; + + /* Release the object's mutex */ + H5TS_mutex_unlock(&obj->mutex); + + return ret_value; +} /* end H5TS_atomic_fetch_sub_uint() */ + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSbarrier.c b/src/H5TSbarrier.c new file mode 100644 index 00000000000..1dee209dddd --- /dev/null +++ b/src/H5TSbarrier.c @@ -0,0 +1,129 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 thread barrier operations, equivalent + * to the pthread 'pthread_barrier_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 */ +/*******************/ + +/*-------------------------------------------------------------------------- + * Function: H5TS_barrier_init + * + * Purpose: Initialize a thread barrier + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_barrier_init(H5TS_barrier_t *barrier, unsigned count) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == barrier || 0 == count)) + HGOTO_DONE(FAIL); + +#ifdef H5_HAVE_PTHREAD_BARRIER + /* Initialize the barrier */ + if (H5_UNLIKELY(pthread_barrier_init(barrier, NULL, count))) + HGOTO_DONE(FAIL); +#else + /* Initialize fields */ + barrier->count = count; + H5TS_atomic_init_uint(&barrier->openings, count); + H5TS_atomic_init_uint(&barrier->generation, 0); +#endif + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_barrier_init() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_barrier_destroy + * + * Purpose: Destroy an H5TS_barrier_t. All internal components are + * destroyed, but the instance of H5TS_barrier_t is not freed. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_barrier_destroy(H5TS_barrier_t *barrier) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == barrier)) + HGOTO_DONE(FAIL); + +#ifdef H5_HAVE_PTHREAD_BARRIER + if (H5_UNLIKELY(pthread_barrier_destroy(barrier))) + HGOTO_DONE(FAIL); +#else + /* Destroy the (emulated) atomic variables */ + H5TS_atomic_destroy_uint(&barrier->openings); + H5TS_atomic_destroy_uint(&barrier->generation); +#endif + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_barrier_destroy() */ + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSbarrier.h b/src/H5TSbarrier.h new file mode 100644 index 00000000000..e27ec08897f --- /dev/null +++ b/src/H5TSbarrier.h @@ -0,0 +1,97 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 thread barrier operations, equivalent + * to the pthread 'pthread_barrier_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 */ +/*******************/ + +/*-------------------------------------------------------------------------- + * Function: H5TS_barrier_wait + * + * Purpose: Wait at a barrier. + * + * Note: Similar to pthread_barrier_wait, a barrier may be re-used + * multiple times without intervening calls to H5TS_barrier_init. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_barrier_wait(H5TS_barrier_t *barrier) +{ + if (H5_UNLIKELY(NULL == barrier)) + return FAIL; + +#ifdef H5_HAVE_PTHREAD_BARRIER + { + int ret = pthread_barrier_wait(barrier); + if (H5_UNLIKELY(ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)) + return FAIL; + } +#else + { + const unsigned my_generation = H5TS_atomic_load_uint(&barrier->generation); + + /* When the last thread enters, reset the openings & bump the generation */ + if (1 == H5TS_atomic_fetch_sub_uint(&barrier->openings, 1)) { + H5TS_atomic_store_uint(&barrier->openings, barrier->count); + H5TS_atomic_fetch_add_uint(&barrier->generation, 1); + } + else { + /* Not the last thread, wait for the generation to change */ + while (H5TS_atomic_load_uint(&barrier->generation) == my_generation) + H5TS_thread_yield(); + } + } +#endif + + return SUCCEED; +} /* end H5TS_barrier_wait() */ diff --git a/src/H5TSc11.c b/src/H5TSc11.c new file mode 100644 index 00000000000..c1de0fe7a5c --- /dev/null +++ b/src/H5TSc11.c @@ -0,0 +1,85 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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: C11 threads threadsafety routines + * + * 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_C11_THREADS + +/****************/ +/* Local Macros */ +/****************/ + +/******************/ +/* Local Typedefs */ +/******************/ + +/********************/ +/* Local Prototypes */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +#ifdef H5_HAVE_THREADSAFE +/*-------------------------------------------------------------------------- + * Function: H5TS__c11_first_thread_init + * + * Purpose: Initialize global API lock, keys for per-thread error stacks + * and cancallability information. Called by the first thread + * that enters the library. + * + * Return: None + * + *-------------------------------------------------------------------------- + */ +void +H5TS__c11_first_thread_init(void) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Initialize H5TS package */ + H5TS__init(); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__c11_first_thread_init() */ +#endif /* H5_HAVE_THREADSAFE */ + +#endif /* H5_HAVE_C11_THREADS */ diff --git a/src/H5TScond.c b/src/H5TScond.c new file mode 100644 index 00000000000..994bd38c7a8 --- /dev/null +++ b/src/H5TScond.c @@ -0,0 +1,196 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 condition variables, equivalent to + * the pthread 'pthread_cond_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_cond_init + * + * Purpose: Initialize a H5TS_cond_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_init(H5TS_cond_t *cond) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(cnd_init(cond) != thrd_success)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_cond_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_cond_destroy + * + * Purpose: Destroy a H5TS_cond_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_destroy(H5TS_cond_t *cond) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* NOTE: cnd_destroy() can't fail */ + cnd_destroy(cond); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_cond_destroy() */ + +#else +#ifdef H5_HAVE_WIN_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_cond_init + * + * Purpose: Initialize a H5TS_cond_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_init(H5TS_cond_t *cond) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + InitializeConditionVariable(cond); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_cond_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_cond_destroy + * + * Purpose: Destroy a H5TS_cond_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_destroy(H5TS_cond_t *cond) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Condition variables in Windows are not destroyed */ + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_cond_destroy() */ +#else +/*------------------------------------------------------------------------- + * Function: H5TS_cond_init + * + * Purpose: Initialize a H5TS_cond_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_init(H5TS_cond_t *cond) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_cond_init(cond, NULL))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_cond_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_cond_destroy + * + * Purpose: Destroy a H5TS_cond_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_destroy(H5TS_cond_t *cond) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_cond_destroy(cond))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_cond_destroy() */ + +#endif +#endif + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TScond.h b/src/H5TScond.h new file mode 100644 index 00000000000..afc6c305348 --- /dev/null +++ b/src/H5TScond.h @@ -0,0 +1,107 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 condition variables, equivalent to + * the pthread 'pthread_cond_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 + +#define H5TS_cond_wait(cond, mutex) (H5_UNLIKELY(cnd_wait((cond), (mutex)) != thrd_success) ? FAIL : SUCCEED) +#define H5TS_cond_signal(cond) (H5_UNLIKELY(cnd_signal(cond) != thrd_success) ? FAIL : SUCCEED) +#define H5TS_cond_broadcast(cond) (H5_UNLIKELY(cnd_broadcast(cond) != thrd_success) ? FAIL : SUCCEED) + +#else +#ifdef H5_HAVE_WIN_THREADS + +#define H5TS_cond_wait(cond, mutex) \ + (H5_UNLIKELY(!SleepConditionVariableCS(cond, mutex, INFINITE)) ? FAIL : SUCCEED) + +/*------------------------------------------------------------------------- + * Function: H5TS_cond_signal + * + * Purpose: Unblock a thread waiting for a condition variable + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_cond_signal(H5TS_cond_t *cond) +{ + WakeConditionVariable(cond); + + return SUCCEED; +} /* end H5TS_cond_signal() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_cond_broadcast + * + * Purpose: Unblock all threads waiting for a condition variable + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_cond_broadcast(H5TS_cond_t *cond) +{ + WakeAllConditionVariable(cond); + + return SUCCEED; +} /* end H5TS_cond_broadcast() */ +#else + +#define H5TS_cond_wait(cond, mutex) (H5_UNLIKELY(pthread_cond_wait((cond), (mutex))) ? FAIL : SUCCEED) +#define H5TS_cond_signal(cond) (H5_UNLIKELY(pthread_cond_signal(cond)) ? FAIL : SUCCEED) +#define H5TS_cond_broadcast(cond) (H5_UNLIKELY(pthread_cond_broadcast(cond)) ? FAIL : SUCCEED) + +#endif +#endif diff --git a/src/H5TSdevelop.h b/src/H5TSdevelop.h index c3e1a6a65bf..6700d1047d8 100644 --- a/src/H5TSdevelop.h +++ b/src/H5TSdevelop.h @@ -39,9 +39,9 @@ extern "C" { #endif /* HDF5 global library lock routines */ -H5_DLL herr_t H5TSmutex_acquire(unsigned int lock_count, bool *acquired); -H5_DLL herr_t H5TSmutex_release(unsigned int *lock_count); -H5_DLL herr_t H5TSmutex_get_attempt_count(unsigned int *count); +H5_DLL herr_t H5TSmutex_acquire(unsigned lock_count, bool *acquired); +H5_DLL herr_t H5TSmutex_release(unsigned *lock_count); +H5_DLL herr_t H5TSmutex_get_attempt_count(unsigned *count); #ifdef __cplusplus } diff --git a/src/H5TSint.c b/src/H5TSint.c new file mode 100644 index 00000000000..a2761bb2ad3 --- /dev/null +++ b/src/H5TSint.c @@ -0,0 +1,577 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 the framework for ensuring that the global + * library lock is held when an API routine is called. This framework + * works in concert with the FUNC_ENTER_API / FUNC_LEAVE_API macros + * defined in H5private.h. + * + * Note: Because this threadsafety framework operates outside the library, + * it does not use the error stack (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 */ +/****************/ + +#define H5E_FRIEND /* Suppress error about including H5Epkg */ +#include "H5TSmodule.h" /* This source code file is part of the H5TS module */ + +/***********/ +/* Headers */ +/***********/ +#include "H5private.h" /* Generic Functions */ +#include "H5CXprivate.h" /* API Contexts */ +#include "H5Epkg.h" /* Error handling */ +#include "H5TSpkg.h" /* Threadsafety */ + +#ifdef H5_HAVE_THREADSAFE + +/****************/ +/* Local Macros */ +/****************/ + +#ifdef H5_HAVE_C11_THREADS +#define H5TS_ONCE_INIT_FUNC H5TS__c11_first_thread_init +#else +#ifdef H5_HAVE_WIN_THREADS +#define H5TS_ONCE_INIT_FUNC H5TS__win32_process_enter +#else +#define H5TS_ONCE_INIT_FUNC H5TS__pthread_first_thread_init +#endif /* H5_HAVE_WIN_THREADS */ +#endif + +/******************/ +/* Local Typedefs */ +/******************/ + +/* Per-thread info */ +typedef struct H5TS_thread_info_t { + uint64_t id; /* Unique ID for each thread */ + struct H5CX_node_t *api_ctx_node_ptr; /* Pointer to an API context node */ + H5E_stack_t err_stack; /* Error stack */ +} H5TS_thread_info_t; + +/* An H5TS_tinfo_node_t is a thread info that is available for reuse */ +typedef struct H5TS_tinfo_node_t { + struct H5TS_tinfo_node_t *next; + H5TS_thread_info_t info; +} H5TS_tinfo_node_t; + +/********************/ +/* Local Prototypes */ +/********************/ +static H5TS_tinfo_node_t *H5TS__tinfo_create(void); + +/*********************/ +/* Package Variables */ +/*********************/ + +/* Per-thread info */ +H5TS_key_t H5TS_thrd_info_key_g; + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +/* Has threadsafety code been initialized? */ +static H5TS_once_t H5TS_first_init_s = H5TS_ONCE_INITIALIZER; + +/* Pointer to first free thread info record or NULL. */ +static H5TS_tinfo_node_t *H5TS_tinfo_next_free_s = NULL; +static uint64_t H5TS_next_thrd_id_s = 0; + +/* Mutex for access to H5TS_tinfo_next_free_s and H5TS_next_thrd_id_s */ +static H5TS_mutex_t H5TS_tinfo_mtx_s; + +/*------------------------------------------------------------------------- + * Function: H5TS__init + * + * Purpose: Initialize the H5TS interface + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS__init(void) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Initialize the global API lock info */ + if (H5_UNLIKELY(H5TS_mutex_init(&H5TS_api_info_p.api_mutex, H5TS_MUTEX_TYPE_RECURSIVE) < 0)) + HGOTO_DONE(FAIL); + H5TS_api_info_p.lock_count = 0; + H5TS_atomic_init_uint(&H5TS_api_info_p.attempt_lock_count, 0); + + /* Initialize per-thread library info */ + if (H5_UNLIKELY(H5TS__tinfo_init() < 0)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_term_package + * + * Purpose: Terminate this interface. + * + * Note: This function is currently registered via atexit() and is called + * AFTER H5_term_library(). + * + * Return: void + * + *------------------------------------------------------------------------- + */ +void +H5TS_term_package(void) +{ + FUNC_ENTER_NOAPI_NOINIT_NOERR + + /* Reset global API lock info */ + H5TS_mutex_destroy(&H5TS_api_info_p.api_mutex); + H5TS_atomic_destroy_uint(&H5TS_api_info_p.attempt_lock_count); + + /* Clean up per-thread library info */ + H5TS__tinfo_term(); + + FUNC_LEAVE_NOAPI_VOID +} /* end H5TS_term_package() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__api_mutex_acquire + * + * Purpose: Attempts to acquire the API lock, without blocking + * + * Note: On success, the 'acquired' flag indicates if the HDF5 library + * global lock was acquired. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__api_mutex_acquire(unsigned lock_count, bool *acquired) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Attempt to acquire the lock */ + if (H5_UNLIKELY(H5TS_mutex_trylock(&H5TS_api_info_p.api_mutex, acquired) < 0)) + HGOTO_DONE(FAIL); + + /* If acquired, increment the levels of recursion by 'lock_count' - 1 */ + if (*acquired) { + for (unsigned u = 0; u < (lock_count - 1); u++) + if (H5_UNLIKELY(H5TS_mutex_lock(&H5TS_api_info_p.api_mutex) < 0)) + HGOTO_DONE(FAIL); + H5TS_api_info_p.lock_count += lock_count; + } + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__api_mutex_acquire() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_api_lock + * + * Purpose: Increment the global "API" lock counter for accessing the HDF5 + * library, acquiring the lock for the thread if the counter is + * initially 0. + * + * Note: Multiple (usually recursive) acquisitions of the "API" lock by + * the same thread is permitted with corresponding unlock + * operation(s). + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_api_lock(void) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Initialize the thread-safety code, once */ + if (H5_UNLIKELY(!H5_INIT_GLOBAL)) + if (H5_UNLIKELY(H5TS_once(&H5TS_first_init_s, H5TS_ONCE_INIT_FUNC) < 0)) + HGOTO_DONE(FAIL); + + /* Increment the attempt lock count */ + H5TS_atomic_fetch_add_uint(&H5TS_api_info_p.attempt_lock_count, 1); + + /* Acquire the library's API lock */ + if (H5_UNLIKELY(H5TS_mutex_lock(&H5TS_api_info_p.api_mutex) < 0)) + HGOTO_DONE(FAIL); + + /* Increment the lock count for this thread */ + H5TS_api_info_p.lock_count++; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_api_lock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__api_mutex_release + * + * Purpose: Release the global "API" lock for accessing the HDF5 library. + * Passes back the previous lock count to the caller in a + * parameter. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__api_mutex_release(unsigned *lock_count) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Return the current lock count */ + *lock_count = H5TS_api_info_p.lock_count; + + /* Reset recursive lock count */ + H5TS_api_info_p.lock_count = 0; + + /* Release the library's API lock 'lock_count' times */ + for (unsigned u = 0; u < *lock_count; u++) + if (H5_UNLIKELY(H5TS_mutex_unlock(&H5TS_api_info_p.api_mutex) < 0)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS__api_mutex_release */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_api_unlock + * + * Purpose: Decrements the global "API" lock for accessing the + * HDF5 library, releasing the lock when it's been unlocked as + * many times as it was locked. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_api_unlock(void) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Decrement the lock count for this thread */ + H5TS_api_info_p.lock_count--; + + /* Release the library's API lock */ + if (H5_UNLIKELY(H5TS_mutex_unlock(&H5TS_api_info_p.api_mutex) < 0)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_api_unlock */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__tinfo_init + * + * Purpose: Initialize thread-local key for per-thread info + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__tinfo_init(void) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Initialize the critical section for modifying the thread info globals */ + if (H5_UNLIKELY(H5TS_mutex_init(&H5TS_tinfo_mtx_s, H5TS_MUTEX_TYPE_PLAIN)) < 0) + ret_value = FAIL; + + /* Initialize key for thread-specific API contexts */ +#ifdef H5_HAVE_WIN_THREADS + if (H5_UNLIKELY(H5TS_key_create(&H5TS_thrd_info_key_g, NULL) < 0)) + ret_value = FAIL; +#else + if (H5_UNLIKELY(H5TS_key_create(&H5TS_thrd_info_key_g, H5TS__tinfo_destroy) < 0)) + ret_value = FAIL; +#endif + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__tinfo_init() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__tinfo_create + * + * Purpose: Initialize per-thread info and set it for the thread-local key + * + * Return: Pointer to per-thread info node on success / NULL on failure + * + *-------------------------------------------------------------------------- + */ +static H5TS_tinfo_node_t * +H5TS__tinfo_create(void) +{ + uint64_t new_id; + H5TS_tinfo_node_t *tinfo_node; + H5TS_tinfo_node_t *ret_value; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Acquire the lock for modifying the thread info globals */ + /* Note: Must use lock here also, since 'destroy' callback can be + * invoked asynchronously when a thread is joined. + */ + if (H5_UNLIKELY(H5TS_mutex_lock(&H5TS_tinfo_mtx_s) < 0)) + HGOTO_DONE(NULL); + + /* Reuse an info struct that's on the free list if possible */ + if (NULL != (tinfo_node = H5TS_tinfo_next_free_s)) + H5TS_tinfo_next_free_s = tinfo_node->next; + + /* Always use unique ID value for each thread, even when recycling a + * H5TS_tinfo_node_t from the free list. + * + * Note: Don't worry about overflow for ID values + */ + new_id = ++H5TS_next_thrd_id_s; + + /* Release the lock for modifying the thread info globals */ + if (H5_UNLIKELY(H5TS_mutex_unlock(&H5TS_tinfo_mtx_s) < 0)) + HGOTO_DONE(NULL); + + /* If a new info record is needed, allocate it */ + if (NULL == tinfo_node) { + if (H5_UNLIKELY(NULL == (tinfo_node = H5MM_malloc(sizeof(*tinfo_node))))) + HGOTO_DONE(NULL); + tinfo_node->next = NULL; + } + + /* Reset thread info struct */ + memset(tinfo_node, 0, sizeof(*tinfo_node)); + + /* Set up non-zero per-thread info */ + tinfo_node->info.id = new_id; /* ID */ + H5E__set_default_auto(&tinfo_node->info.err_stack); /* Error stack */ + + /* Set a thread-local pointer to the thread's info record */ + if (H5_UNLIKELY(H5TS_key_set_value(H5TS_thrd_info_key_g, tinfo_node))) { + H5TS__tinfo_destroy(tinfo_node); + HGOTO_DONE(NULL); + } + + /* Success */ + ret_value = tinfo_node; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_id + * + * Purpose: Return an identifier for the current thread. + * + * The ID satisfies the following properties: + * 1) ID 0 is reserved + * 2) 1 <= ID <= UINT64_MAX + * 3) ID is constant over a thread's lifetime + * 4) No two threads share an ID + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_id(uint64_t *id) +{ + H5TS_tinfo_node_t *tinfo_node; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Check argument */ + if (H5_UNLIKELY(NULL == id)) + HGOTO_DONE(FAIL); + + /* Check if info for thread has been created */ + if (H5_UNLIKELY(H5TS_key_get_value(H5TS_thrd_info_key_g, (void **)&tinfo_node) < 0)) + HGOTO_DONE(FAIL); + if (NULL == tinfo_node) + /* Create thread info for this thread */ + if (H5_UNLIKELY(NULL == (tinfo_node = H5TS__tinfo_create()))) + HGOTO_DONE(FAIL); + + /* Set return value */ + *id = tinfo_node->info.id; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_id() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_get_api_ctx_ptr + * + * Purpose: Retrieve the address of the pointer to the head of API context + * stack for this thread. (i.e. an H5CX_node_t **) + * + * Return: Success: Non-NULL pointer to head pointer of API context stack for thread + * Failure: NULL + * + *-------------------------------------------------------------------------- + */ +struct H5CX_node_t ** +H5TS_get_api_ctx_ptr(void) +{ + H5TS_tinfo_node_t *tinfo_node; + struct H5CX_node_t **ret_value; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Check if info for thread has been created */ + if (H5_UNLIKELY(H5TS_key_get_value(H5TS_thrd_info_key_g, (void **)&tinfo_node) < 0)) + HGOTO_DONE(NULL); + if (NULL == tinfo_node) + /* Create thread info for this thread */ + if (H5_UNLIKELY(NULL == (tinfo_node = H5TS__tinfo_create()))) + HGOTO_DONE(NULL); + + /* Set return value */ + ret_value = &tinfo_node->info.api_ctx_node_ptr; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_get_api_ctx_ptr() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_get_err_stack + * + * Purpose: Retrieve the address of error stack for this thread. + * (i.e. an H5E_stack_t *) + * + * Return: Success: Non-NULL pointer to error stack for thread + * Failure: NULL + * + *-------------------------------------------------------------------------- + */ +H5E_stack_t * +H5TS_get_err_stack(void) +{ + H5TS_tinfo_node_t *tinfo_node; + H5E_stack_t *ret_value; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Check if info for thread has been created */ + if (H5_UNLIKELY(H5TS_key_get_value(H5TS_thrd_info_key_g, (void **)&tinfo_node) < 0)) + HGOTO_DONE(NULL); + if (NULL == tinfo_node) + /* Create thread info for this thread */ + if (H5_UNLIKELY(NULL == (tinfo_node = H5TS__tinfo_create()))) + HGOTO_DONE(NULL); + + /* Set return value */ + ret_value = &tinfo_node->info.err_stack; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_get_err_stack() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__tinfo_destroy + * + * Purpose: When a thread shuts down, put its info record on the free list. + * + * Note: This routine runs asynchronously _outside_ of the library and + * is not covered by the library's API lock. Therefore, protect + * access to the global variable with a mutex. + * + * Return: None + * + *-------------------------------------------------------------------------- + */ +void +H5TS__tinfo_destroy(void *_tinfo_node) +{ + H5TS_tinfo_node_t *tinfo_node = _tinfo_node; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (tinfo_node) { + /* Add thread info node to the free list */ + H5TS_mutex_lock(&H5TS_tinfo_mtx_s); + tinfo_node->next = H5TS_tinfo_next_free_s; + H5TS_tinfo_next_free_s = tinfo_node; + H5TS_mutex_unlock(&H5TS_tinfo_mtx_s); + } + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} + +/*-------------------------------------------------------------------------- + * Function: H5TS__tinfo_term + * + * Purpose: Terminate per-thread info at library shutdown + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__tinfo_term(void) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Release nodes on the free list */ + if (H5_UNLIKELY(H5TS_mutex_lock(&H5TS_tinfo_mtx_s) < 0)) + HGOTO_DONE(FAIL); + while (H5TS_tinfo_next_free_s) { + H5TS_tinfo_node_t *next = H5TS_tinfo_next_free_s->next; + H5MM_free(H5TS_tinfo_next_free_s); + H5TS_tinfo_next_free_s = next; + } + if (H5_UNLIKELY(H5TS_mutex_unlock(&H5TS_tinfo_mtx_s) < 0)) + HGOTO_DONE(FAIL); + + /* Release critical section / mutex for modifying the thread info globals */ + if (H5_UNLIKELY(H5TS_mutex_destroy(&H5TS_tinfo_mtx_s) < 0)) + HGOTO_DONE(FAIL); + + /* Release key for thread-specific API contexts */ + if (H5_UNLIKELY(H5TS_key_delete(H5TS_thrd_info_key_g) < 0)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__tinfo_term() */ + +#endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSkey.c b/src/H5TSkey.c new file mode 100644 index 00000000000..994898c71f0 --- /dev/null +++ b/src/H5TSkey.c @@ -0,0 +1,227 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 thread-local key operations, + * equivalent to the pthread 'pthread_key_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_key_create + * + * Purpose: Thread-local key creation + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_key_create(H5TS_key_t *key, H5TS_key_destructor_func_t dtor) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity check */ + if (H5_UNLIKELY(NULL == key)) + HGOTO_DONE(FAIL); + + /* Create the key */ + if (H5_UNLIKELY(tss_create(key, dtor) != thrd_success)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_key_create() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_key_delete + * + * Purpose: Thread-local key deletion + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_key_delete(H5TS_key_t key) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Delete the key */ + /* NOTE: tss_delete() can't fail */ + tss_delete(key); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_key_delete() */ + +#else +#ifdef H5_HAVE_WIN_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_key_create + * + * Purpose: Thread-local key creation + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_key_create(H5TS_key_t *key, H5TS_key_destructor_func_t dtor) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity check */ + if (H5_UNLIKELY(NULL == key)) + HGOTO_DONE(FAIL); + + /* Fail if the key has a destructor callback, this is not supported by Windows */ + if (NULL != dtor) + HGOTO_DONE(FAIL); + + /* Create the key */ + if (H5_UNLIKELY(TLS_OUT_OF_INDEXES == (*key = TlsAlloc()))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_key_create() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_key_delete + * + * Purpose: Thread-local key deletion + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_key_delete(H5TS_key_t key) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Delete the key */ + if (TLS_OUT_OF_INDEXES != key) + if (H5_UNLIKELY(0 == TlsFree(key))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_key_delete() */ + +#else +/*------------------------------------------------------------------------- + * Function: H5TS_key_create + * + * Purpose: Thread-local key creation + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_key_create(H5TS_key_t *key, H5TS_key_destructor_func_t dtor) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity check */ + if (H5_UNLIKELY(NULL == key)) + HGOTO_DONE(FAIL); + + /* Create the key */ + if (H5_UNLIKELY(pthread_key_create(key, dtor))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_key_create() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_key_delete + * + * Purpose: Thread-local key deletion + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_key_delete(H5TS_key_t key) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Delete the key */ + if (H5_UNLIKELY(pthread_key_delete(key))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_key_delete() */ + +#endif +#endif + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSkey.h b/src/H5TSkey.h new file mode 100644 index 00000000000..3003e114bcd --- /dev/null +++ b/src/H5TSkey.h @@ -0,0 +1,107 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 thread-local key operations, + * equivalent to the pthread 'pthread_key_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 +#define H5TS_key_set_value(key, value) (H5_UNLIKELY(tss_set((key), (value)) != thrd_success) ? FAIL : SUCCEED) +#define H5TS_key_get_value(key, value) (*(value) = tss_get(key), SUCCEED) + +#else +#ifdef H5_HAVE_WIN_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_key_set_value + * + * Purpose: Set a thread-specific value for a thread-local key + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_key_set_value(H5TS_key_t key, void *value) +{ + /* Set the value for this thread */ + if (H5_UNLIKELY(0 == TlsSetValue(key, (LPVOID)value))) + return FAIL; + + return SUCCEED; +} /* end H5TS_key_set_value() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_key_get_value + * + * Purpose: Get a thread-specific value for a thread-local key + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_key_get_value(H5TS_key_t key, void **value) +{ + /* Get the value for this thread */ + if (H5_UNLIKELY(NULL == (*value = TlsGetValue(key)))) + /* Check for possible error, when NULL value is returned */ + if (H5_UNLIKELY(ERROR_SUCCESS != GetLastError())) + return FAIL; + + return SUCCEED; +} /* end H5TS_key_get_value() */ + +#else + +#define H5TS_key_set_value(key, value) (H5_UNLIKELY(pthread_setspecific((key), (value))) ? FAIL : SUCCEED) +#define H5TS_key_get_value(key, value) (*(value) = pthread_getspecific(key), SUCCEED) + +#endif +#endif diff --git a/src/H5TSmodule.h b/src/H5TSmodule.h new file mode 100644 index 00000000000..2e8bb1d92a6 --- /dev/null +++ b/src/H5TSmodule.h @@ -0,0 +1,28 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 declarations which define macros for the + * H5TS package. Including this header means that the source file + * is part of the H5TS package. + */ +#ifndef H5TSmodule_H +#define H5TSmodule_H + +/* Define the proper control macros for the generic FUNC_ENTER/LEAVE and error + * reporting macros. + */ +#define H5TS_MODULE +#define H5_MY_PKG H5TS +#define H5_MY_PKG_ERR H5E_THREADSAFE + +#endif /* H5TSmodule_H */ diff --git a/src/H5TSmutex.c b/src/H5TSmutex.c new file mode 100644 index 00000000000..5cc66efff89 --- /dev/null +++ b/src/H5TSmutex.c @@ -0,0 +1,297 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 mutex locks, equivalent to the + * pthread 'pthread_mutex_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_mutex_init + * + * Purpose: Initialize a H5TS_mutex_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_init(H5TS_mutex_t *mutex, int type) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(mtx_init(mutex, type) != thrd_success)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_mutex_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_trylock + * + * Purpose: Attempt to lock a H5TS_mutex_t, sets *acquired to TRUE if so + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_trylock(H5TS_mutex_t *mutex, bool *acquired) +{ + int rc; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + rc = mtx_trylock(mutex); + if (thrd_success == rc) + *acquired = true; + else if (thrd_busy == rc) + *acquired = false; + else + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_mutex_trylock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_destroy + * + * Purpose: Destroy a H5TS_mutex_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_destroy(H5TS_mutex_t *mutex) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* NOTE: mtx_destroy() can't fail */ + mtx_destroy(mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_mutex_destroy() */ +#else +#ifdef H5_HAVE_WIN_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_init + * + * Purpose: Initialize a H5TS_mutex_t (does not allocate it) + * + * Note: All Windows CriticalSections are recursive + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_init(H5TS_mutex_t *mutex, int H5_ATTR_UNUSED type) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + InitializeCriticalSection(mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_mutex_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_trylock + * + * Purpose: Attempt to lock a H5TS_mutex_t, sets *acquired to TRUE if so + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_trylock(H5TS_mutex_t *mutex, bool *acquired) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + *acquired = TryEnterCriticalSection(mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_mutex_trylock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_destroy + * + * Purpose: Destroy a H5TS_mutex_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_destroy(H5TS_mutex_t *mutex) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + DeleteCriticalSection(mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_mutex_destroy() */ + +#else +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_init + * + * Purpose: Initialize a H5TS_mutex_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_init(H5TS_mutex_t *mutex, int type) +{ + pthread_mutexattr_t _attr; + pthread_mutexattr_t *attr = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Create mutex attribute */ + if (H5_UNLIKELY(pthread_mutexattr_init(&_attr))) + HGOTO_DONE(FAIL); + attr = &_attr; + + /* Set up recursive mutex, if requested */ + if (H5TS_MUTEX_TYPE_RECURSIVE == type) { + if (H5_UNLIKELY(pthread_mutexattr_settype(attr, PTHREAD_MUTEX_RECURSIVE))) + HGOTO_DONE(FAIL); + } + else + /* Otherwise, use "normal" mutex, without error checking */ + if (H5_UNLIKELY(pthread_mutexattr_settype(attr, PTHREAD_MUTEX_NORMAL))) + HGOTO_DONE(FAIL); + + /* Initialize the mutex */ + if (H5_UNLIKELY(pthread_mutex_init(mutex, attr))) + HGOTO_DONE(FAIL); + +done: + /* Destroy the attribute */ + if (NULL != attr) + if (H5_UNLIKELY(pthread_mutexattr_destroy(attr))) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_mutex_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_trylock + * + * Purpose: Attempt to lock a H5TS_mutex_t, sets *acquired to TRUE if so + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_trylock(H5TS_mutex_t *mutex, bool *acquired) +{ + int rc; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + rc = pthread_mutex_trylock(mutex); + if (0 == rc) + *acquired = true; + else if (EBUSY == rc) + *acquired = false; + else + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_mutex_trylock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_destroy + * + * Purpose: Destroy a H5TS_mutex_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_destroy(H5TS_mutex_t *mutex) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_mutex_destroy(mutex))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_mutex_destroy() */ + +#endif +#endif + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSmutex.h b/src/H5TSmutex.h new file mode 100644 index 00000000000..50167ec6bc8 --- /dev/null +++ b/src/H5TSmutex.h @@ -0,0 +1,101 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 mutex locks, equivalent to the + * pthread 'pthread_mutex_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 + +#define H5TS_mutex_lock(mutex) ((H5_UNLIKELY(mtx_lock(mutex) != thrd_success)) ? FAIL : SUCCEED) +#define H5TS_mutex_unlock(mutex) (H5_UNLIKELY(mtx_unlock(mutex) != thrd_success) ? FAIL : SUCCEED) + +#else +#ifdef H5_HAVE_WIN_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_lock + * + * Purpose: Lock a H5TS_mutex_t + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_mutex_lock(H5TS_mutex_t *mutex) +{ + EnterCriticalSection(mutex); + + return SUCCEED; +} /* end H5TS_mutex_lock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_unlock + * + * Purpose: Unlock a H5TS_mutex_t + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_mutex_unlock(H5TS_mutex_t *mutex) +{ + LeaveCriticalSection(mutex); + + return SUCCEED; +} /* end H5TS_mutex_unlock() */ +#else + +#define H5TS_mutex_lock(mutex) (H5_UNLIKELY(pthread_mutex_lock(mutex)) ? FAIL : SUCCEED) +#define H5TS_mutex_unlock(mutex) (H5_UNLIKELY(pthread_mutex_unlock(mutex)) ? FAIL : SUCCEED) + +#endif +#endif diff --git a/src/H5TSonce.c b/src/H5TSonce.c new file mode 100644 index 00000000000..492dd1de4c0 --- /dev/null +++ b/src/H5TSonce.c @@ -0,0 +1,149 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 thread-local 'once' operations, + * equivalent to the pthread 'pthread_once_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_once + * + * Purpose: Dynamic package initialization + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_once(H5TS_once_t *once, H5TS_once_init_func_t func) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity check */ + if (H5_UNLIKELY(NULL == once || NULL == func)) + HGOTO_DONE(FAIL); + + /* Invoke the function, only once per process */ + call_once(once, func); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_once() */ +#else +#ifdef H5_HAVE_WIN_THREADS +/*------------------------------------------------------------------------- + * Function: H5TS_once + * + * Purpose: Dynamic package initialization + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_once(H5TS_once_t *once, H5TS_once_init_func_t func) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity check */ + if (H5_UNLIKELY(NULL == once || NULL == func)) + HGOTO_DONE(FAIL); + + /* Invoke the function, only once per process */ + if (H5_UNLIKELY(0 == InitOnceExecuteOnce(once, func, NULL, NULL))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_once() */ +#else +/*------------------------------------------------------------------------- + * Function: H5TS_once + * + * Purpose: Dynamic package initialization + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_once(H5TS_once_t *once, H5TS_once_init_func_t func) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity check */ + if (H5_UNLIKELY(NULL == once || NULL == func)) + HGOTO_DONE(FAIL); + + /* Invoke the function, only once per process */ + if (H5_UNLIKELY(pthread_once(once, func))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_once() */ +#endif +#endif + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSpkg.h b/src/H5TSpkg.h new file mode 100644 index 00000000000..c2ff919f2f3 --- /dev/null +++ b/src/H5TSpkg.h @@ -0,0 +1,267 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 declarations which are visible only within + * the H5TS package. Source files outside the H5TS package should + * include H5TSprivate.h instead. + */ +#if !(defined H5TS_FRIEND || defined H5TS_MODULE) +#error "Do not include this file outside the H5TS package!" +#endif + +#ifndef H5TSpkg_H +#define H5TSpkg_H + +#ifdef H5_HAVE_THREADS +/* Get package's private header */ +#include "H5TSprivate.h" + +/* Other private headers needed by this file */ + +/**************************/ +/* Package Private Macros */ +/**************************/ + +/* Enable statistics for recursive R/W lock when H5TS debugging is enabled */ +#ifdef H5TS_DEBUG +#define H5TS_ENABLE_REC_RW_LOCK_STATS 1 +#else +#define H5TS_ENABLE_REC_RW_LOCK_STATS 0 +#endif + +/****************************/ +/* Package Private Typedefs */ +/****************************/ + +#ifdef H5_HAVE_THREADSAFE +/* Info for the global API lock */ +typedef struct H5TS_api_info_t { + /* API lock */ + H5TS_mutex_t api_mutex; + + /* Count of recursive API calls by the same thread */ + unsigned lock_count; + + /* Count of # of attempts to acquire API lock */ + H5TS_atomic_uint_t attempt_lock_count; +} H5TS_api_info_t; +#endif /* H5_HAVE_THREADSAFE */ + +#if H5TS_ENABLE_REC_RW_LOCK_STATS +/****************************************************************************** + * + * Structure H5TS_rw_lock_stats_t + * + * Statistics for the recursive R/W lock. + * + * Since a mutex must be held to read a consistent set of statistics from a + * recursive R/W lock, it simplifies matters to bundle them into a single + * structure. + * + * Individual fields are: + * + * Read lock stats: + * read_locks_granted: The total number of read locks granted, including + * recursive lock requests. + * + * read_locks_released: The total number of read locks released, including + * recursive lock requests. + * + * real_read_locks_granted: The total number of read locks granted, less + * any recursive lock requests. + * + * real_read_locks_released: The total number of read locks released, + * less any recursive lock releases. + * + * max_read_locks: The maximum number of read locks active at any point + * in time. + * + * max_read_lock_recursion_depth: The maximum recursion depth observed + * for any read lock. + * + * read_locks_delayed: The number of read locks not granted immediately. + * + * + * Write lock stats: + * write_locks_granted: The total number of write locks granted, + * including recursive lock requests. + * + * write_locks_released: The total number of write locks released, + * including recursive lock requests. + * + * real_write_locks_granted: The total number of write locks granted, + * less any recursive lock requests. + * + * real_write_locks_released: The total number of write locks released, + * less any recursive lock requests. + * + * max_write_locks: The maximum number of write locks active at any point + * in time. Must be either zero or one. + * + * max_write_lock_recursion_depth: The maximum recursion depth observed + * for any write lock. + * + * write_locks_delayed: The number of write locks not granted immediately. + * + * max_write_locks_pending: The maximum number of pending write locks at + * any point in time. + * + ******************************************************************************/ + +typedef struct H5TS_rw_lock_stats_t { + int64_t read_locks_granted; + int64_t read_locks_released; + int64_t real_read_locks_granted; + int64_t real_read_locks_released; + int64_t max_read_locks; + int64_t max_read_lock_recursion_depth; + int64_t read_locks_delayed; + int64_t write_locks_granted; + int64_t write_locks_released; + int64_t real_write_locks_granted; + int64_t real_write_locks_released; + int64_t max_write_locks; + int64_t max_write_lock_recursion_depth; + int64_t write_locks_delayed; + int64_t max_write_locks_pending; +} H5TS_rw_lock_stats_t; +#endif + +/****************************************************************************** + * + * Structure H5TS_rw_lock_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. + * + * 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. + * + * 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. + * + * 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. + * + * is_key_registered: Flag to track if the rec_read_lock_count_key has been + * registered yet for a lock. + * + * stats: Instance of H5TS_rw_lock_stats_t used to track + * statistics on the recursive R/W lock. + * + ******************************************************************************/ + +typedef enum { + H5TS_RW_LOCK_UNUSED = 0, /* Lock is currently unused */ + H5TS_RW_LOCK_WRITE, /* Lock is a recursive write lock */ + H5TS_RW_LOCK_READ /* Lock is a recursive read lock */ +} H5TS_rw_lock_type_t; + +typedef struct H5TS_rw_lock_t { + /* General fields */ + H5TS_mutex_t mutex; + H5TS_rw_lock_type_t lock_type; + + /* Writer fields */ + H5TS_cond_t writers_cv; + H5TS_thread_t write_thread; + int32_t rec_write_lock_count; + int32_t waiting_writers_count; + + /* Reader fields */ + H5TS_cond_t readers_cv; + int32_t reader_thread_count; + H5TS_key_t rec_read_lock_count_key; + bool is_key_registered; + +#if H5TS_ENABLE_REC_RW_LOCK_STATS + /* Stats */ + H5TS_rw_lock_stats_t stats; +#endif +} H5TS_rw_lock_t; + +/*****************************/ +/* Package Private Variables */ +/*****************************/ + +#ifdef H5_HAVE_THREADSAFE +/* API threadsafety info */ +extern H5TS_api_info_t H5TS_api_info_p; + +/* Per-thread info */ +extern H5TS_key_t H5TS_thrd_info_key_g; +#endif /* H5_HAVE_THREADSAFE */ + +/******************************/ +/* Package Private Prototypes */ +/******************************/ +#ifdef H5_HAVE_THREADSAFE +H5_DLL herr_t H5TS__init(void); +H5_DLL herr_t H5TS__api_mutex_acquire(unsigned lock_count, bool *acquired); +H5_DLL herr_t H5TS__api_mutex_release(unsigned *lock_count); +H5_DLL herr_t H5TS__tinfo_init(void); +H5_DLL void H5TS__tinfo_destroy(void *tinfo_node); +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); + +/* 'once' callbacks */ +#ifdef H5_HAVE_THREADSAFE +#ifdef H5_HAVE_C11_THREADS +H5_DLL void H5TS__c11_first_thread_init(void); +#else +#ifdef H5_HAVE_WIN_THREADS +H5_DLL BOOL CALLBACK H5TS__win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex); +#else +H5_DLL void H5TS__pthread_first_thread_init(void); +#endif /* H5_HAVE_WIN_THREADS */ +#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); +#endif + +#endif /* H5_HAVE_THREADS */ + +#endif /* H5TSpkg_H */ diff --git a/src/H5TSpool.c b/src/H5TSpool.c new file mode 100644 index 00000000000..3099543ba5f --- /dev/null +++ b/src/H5TSpool.c @@ -0,0 +1,294 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 an implementation of a simple thread-pool, + * using the H5TS package's portable objects and operations. + * + * 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 "H5FLprivate.h" /* Free Lists */ +#include "H5TSpkg.h" /* Threadsafety */ + +#ifdef H5_HAVE_THREADS + +/****************/ +/* Local Macros */ +/****************/ + +/******************/ +/* Local Typedefs */ +/******************/ + +/********************/ +/* Local Prototypes */ +/********************/ +static herr_t H5TS__pool_free(H5TS_pool_t *pool); +static H5TS_THREAD_RETURN_TYPE H5TS__pool_do(void *_pool); + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +/* Declare a free list to manage H5TS_pool_t structs */ +H5FL_DEFINE_STATIC(H5TS_pool_t); + +/* Declare a free list to manage sequences of H5TS_thread_t structs */ +H5FL_SEQ_DEFINE_STATIC(H5TS_thread_t); + +/*-------------------------------------------------------------------------- + * Function: H5TS_pool_free + * + * Purpose: Free resources for a thread pool, waiting for tasks to be invoked + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +static herr_t +H5TS__pool_free(H5TS_pool_t *pool) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Sanity check */ + assert(pool); + + /* Join all threads */ + for (unsigned u = 0; u < pool->num_threads; u++) + if (H5_UNLIKELY(H5TS_thread_join(pool->threads[u], NULL) < 0)) + HGOTO_DONE(FAIL); + + /* Destroy the task queue's semaphore */ + if (H5_UNLIKELY(H5TS_semaphore_destroy(&pool->sem) < 0)) + HGOTO_DONE(FAIL); + + /* Destroy the task queue's mutex */ + if (H5_UNLIKELY(H5TS_mutex_destroy(&pool->queue_mutex) < 0)) + HGOTO_DONE(FAIL); + + /* Release memory */ + if (pool->threads) + H5FL_SEQ_FREE(H5TS_thread_t, pool->threads); + H5FL_FREE(H5TS_pool_t, pool); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__pool_free() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__pool_do + * + * Purpose: Routine for worker threads to service tasks + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +static H5TS_THREAD_RETURN_TYPE +H5TS__pool_do(void *_pool) +{ + H5TS_pool_t *pool = (H5TS_pool_t *)_pool; /* Pool for threads */ + H5TS_thread_ret_t ret_value = (H5TS_thread_ret_t)0; + + /* Acquire tasks and invoke them, until pool is shut down */ + while (1) { + H5TS_pool_task_t *task; /* Task to invoke */ + + /* Wait for task */ + if (H5_UNLIKELY(H5TS_semaphore_wait(&pool->sem) < 0)) + return ((H5TS_thread_ret_t)-1); + + /* Acquire the mutex for the task queue */ + if (H5_UNLIKELY(H5TS_mutex_lock(&pool->queue_mutex) < 0)) + return ((H5TS_thread_ret_t)-1); + + /* Check if we have a task */ + if (H5_LIKELY(pool->head)) { + /* Grab our task */ + task = pool->head; + if (task->next) + pool->head = task->next; + else + pool->head = pool->tail = NULL; + + /* Release the task queue's mutex */ + if (H5_UNLIKELY(H5TS_mutex_unlock(&pool->queue_mutex) < 0)) + return ((H5TS_thread_ret_t)-1); + + /* Invoke function for task */ + (*task->func)(task->ctx); + + /* Free the task node */ + free(task); + } + else { + assert(pool->shutdown); + + /* Release the task queue's mutex */ + if (H5_UNLIKELY(H5TS_mutex_unlock(&pool->queue_mutex) < 0)) + return ((H5TS_thread_ret_t)-1); + + break; + } + } + + return (ret_value); +} /* end H5TS__pool_do() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_pool_create + * + * Purpose: Create a new thread pool, with the given # of threads + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_pool_create(H5TS_pool_t **pool, unsigned num_threads) +{ + H5TS_pool_t *new_pool = NULL; /* Newly created pool */ + unsigned u; /* Local index variable */ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity checks */ + if (H5_UNLIKELY(NULL == pool)) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(0 == num_threads)) + HGOTO_DONE(FAIL); + + /* Allocate new pool */ + if (H5_UNLIKELY(NULL == (new_pool = H5FL_MALLOC(H5TS_pool_t)))) + HGOTO_DONE(FAIL); + + /* Initialize pool fields to defaults */ + memset(new_pool, 0, sizeof(*new_pool)); + if (H5_UNLIKELY(H5TS_mutex_init(&new_pool->queue_mutex, H5TS_MUTEX_TYPE_PLAIN) < 0)) + HGOTO_DONE(FAIL); + + /* Create semaphore for task queue */ + if (H5_UNLIKELY(H5TS_semaphore_init(&new_pool->sem, 0) < 0)) + HGOTO_DONE(FAIL); + + /* Allocate array of threads */ + if (H5_UNLIKELY(NULL == (new_pool->threads = H5FL_SEQ_MALLOC(H5TS_thread_t, num_threads)))) + HGOTO_DONE(FAIL); + + /* Start worker threads */ + for (u = 0; u < num_threads; u++) { + /* Create thread, which immediately starts processing tasks */ + if (H5_UNLIKELY(H5TS_thread_create(&new_pool->threads[u], H5TS__pool_do, (void *)new_pool) < 0)) { + /* Set # of threads successfully created (for joining them, in free routine) */ + new_pool->num_threads = u; + HGOTO_DONE(FAIL); + } + } + + /* Set # of threads for pool */ + new_pool->num_threads = num_threads; + + /* Set return value */ + *pool = new_pool; + +done: + if (H5_UNLIKELY(ret_value < 0)) + if (new_pool) { + /* Tell any existing threads that the pool is shutting down */ + new_pool->shutdown = true; + + /* Free pool */ + H5TS__pool_free(new_pool); + } + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_pool_create() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_pool_destroy + * + * Purpose: Destroy a thread pool, waiting for all tasks to be invoked + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_pool_destroy(H5TS_pool_t *pool) +{ + bool have_queue_mutex = false; /* Whether we're holding the task queue mutex */ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity checks */ + if (H5_UNLIKELY(NULL == pool)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex for the task queue */ + if (H5_UNLIKELY(H5TS_mutex_lock(&pool->queue_mutex) < 0)) + HGOTO_DONE(FAIL); + have_queue_mutex = true; + + /* Tell any existing threads that the pool is shutting down */ + pool->shutdown = true; + + /* Release the task queue's mutex */ + if (H5_UNLIKELY(H5TS_mutex_unlock(&pool->queue_mutex) < 0)) + HGOTO_DONE(FAIL); + have_queue_mutex = false; + + /* Add a "shutdown" task for all threads */ + for (unsigned u = 0; u < pool->num_threads; u++) + if (H5_UNLIKELY(H5TS_semaphore_signal(&pool->sem) < 0)) + HGOTO_DONE(FAIL); + + /* Free pool */ + if (H5_UNLIKELY(H5TS__pool_free(pool) < 0)) + HGOTO_DONE(FAIL); + +done: + /* Release the task queue's mutex, if we're holding it */ + if (H5_UNLIKELY(have_queue_mutex)) + if (H5_UNLIKELY(H5TS_mutex_unlock(&pool->queue_mutex) < 0)) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_pool_destroy() */ + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSpool.h b/src/H5TSpool.h new file mode 100644 index 00000000000..5953097b45d --- /dev/null +++ b/src/H5TSpool.h @@ -0,0 +1,151 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 an implementation of a simple thread-pool, + * using the H5TS package's portable objects and operations. + * + * 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 */ +/******************/ + +/* Thread pool task */ +typedef struct H5TS_pool_task_t { + H5TS_thread_start_func_t func; /* Function to invoke with thread */ + void *ctx; /* Context for task's function */ + struct H5TS_pool_task_t *next; /* Pointer to next task */ +} H5TS_pool_task_t; + +/* Thread pool */ +struct H5TS_pool_t { + /* Semaphore */ + H5TS_semaphore_t sem; + + /* Task queue */ + H5TS_mutex_t queue_mutex; /* Mutex to control access to task queue */ + bool shutdown; /* Pool is shutting down */ + H5TS_pool_task_t *head, *tail; /* Task queue */ + + /* Threads */ + unsigned num_threads; /* # of threads in pool */ + H5TS_thread_t *threads; /* Array of worker threads in pool */ +}; + +/********************/ +/* Local Prototypes */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +/*-------------------------------------------------------------------------- + * Function: H5TS_pool_add_task + * + * Purpose: Add a new task to a pool + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_pool_add_task(H5TS_pool_t *pool, H5TS_thread_start_func_t func, void *ctx) +{ + H5TS_pool_task_t *task = NULL; /* Task for function to invoke */ + bool have_queue_mutex = false; /* Whether we're holding the task queue mutex */ + herr_t ret_value = SUCCEED; + + /* Sanity checks */ + if (H5_UNLIKELY(NULL == pool || NULL == func)) + return FAIL; + + /* Allocate & initialize new task */ + if (H5_UNLIKELY(NULL == (task = (H5TS_pool_task_t *)malloc(sizeof(H5TS_pool_task_t))))) + return FAIL; + task->func = func; + task->ctx = ctx; + task->next = NULL; + + /* Acquire the mutex for the task queue */ + if (H5_UNLIKELY(H5TS_mutex_lock(&pool->queue_mutex) < 0)) { + ret_value = FAIL; + goto done; + } + have_queue_mutex = true; + + /* Is pool shutting down? */ + if (H5_UNLIKELY(pool->shutdown)) { + ret_value = FAIL; + goto done; + } + + /* Add task to pool's queue */ + if (NULL != pool->tail) { + pool->tail->next = task; + pool->tail = task; + } + else + pool->head = pool->tail = task; + + /* Avoid freeing the task on error, now */ + task = NULL; + + /* Release the task queue's mutex, if we're holding it */ + if (H5_UNLIKELY(H5TS_mutex_unlock(&pool->queue_mutex) < 0)) { + ret_value = FAIL; + goto done; + } + have_queue_mutex = false; + + /* Signal the semaphore */ + if (H5_UNLIKELY(H5TS_semaphore_signal(&pool->sem) < 0)) + ret_value = FAIL; + +done: + /* Free the task, on failure */ + if (H5_UNLIKELY(ret_value < 0)) { + /* Release the task queue's mutex, if we're still holding it */ + /* (Can only happen on failure) */ + if (H5_UNLIKELY(have_queue_mutex)) + H5TS_mutex_unlock(&pool->queue_mutex); + if (task) + free(task); + } + + return (ret_value); +} /* end H5TS_pool_add_task() */ diff --git a/src/H5TSprivate.h b/src/H5TSprivate.h index e24c409437c..ccba5b8c390 100644 --- a/src/H5TSprivate.h +++ b/src/H5TSprivate.h @@ -21,117 +21,285 @@ #ifndef H5TSprivate_H_ #define H5TSprivate_H_ +#ifdef H5_HAVE_THREADS + #ifdef H5_HAVE_THREADSAFE /* Include package's public headers */ #include "H5TSdevelop.h" +#endif /* H5_HAVE_THREADSAFE */ + +/**************************/ +/* Library Private Macros */ +/**************************/ + +/* Thread-safety sanity-checking annotations */ +#define H5TS_CAPABILITY(x) H5_ATTR_THREAD_ANNOT(capability(x)) +#define H5TS_ACQUIRE(...) H5_ATTR_THREAD_ANNOT(acquire_capability(__VA_ARGS__)) +#define H5TS_ACQUIRE_SHARED(...) H5_ATTR_THREAD_ANNOT(acquire_shared_capability(__VA_ARGS__)) +#define H5TS_RELEASE(...) H5_ATTR_THREAD_ANNOT(release_capability(__VA_ARGS__)) +#define H5TS_RELEASE_SHARED(...) H5_ATTR_THREAD_ANNOT(release_shared_capability(__VA_ARGS__)) +#define H5TS_TRY_ACQUIRE(...) H5_ATTR_THREAD_ANNOT(try_acquire_capability(__VA_ARGS__)) +#define H5TS_TRY_ACQUIRE_SHARED(...) H5_ATTR_THREAD_ANNOT(try_acquire_shared_capability(__VA_ARGS__)) + +#ifdef H5_HAVE_C11_THREADS +/* Static initialization values */ +#define H5TS_ONCE_INITIALIZER ONCE_FLAG_INIT + +/* Thread macros */ +#define H5TS_thread_self() thrd_current() +#define H5TS_thread_equal(t1, t2) thrd_equal((t1), (t2)) +#define H5TS_THREAD_RETURN_TYPE H5TS_thread_ret_t +/* Mutex macros */ +#define H5TS_MUTEX_TYPE_PLAIN mtx_plain +#define H5TS_MUTEX_TYPE_RECURSIVE (mtx_plain | mtx_recursive) +#else #ifdef H5_HAVE_WIN_THREADS +/* Static initialization values */ +#define H5TS_ONCE_INITIALIZER INIT_ONCE_STATIC_INIT + +/* Thread macros */ +#define H5TS_thread_self() GetCurrentThread() +#define H5TS_thread_equal(t1, t2) (GetThreadId(t1) == GetThreadId(t2)) +#define H5TS_THREAD_RETURN_TYPE H5TS_thread_ret_t WINAPI + +/* Mutex macros */ +#define H5TS_MUTEX_TYPE_PLAIN 0 +#define H5TS_MUTEX_TYPE_RECURSIVE 1 +#else +/* Static initialization values */ +#define H5TS_ONCE_INITIALIZER PTHREAD_ONCE_INIT + +/* Thread macros */ +#define H5TS_thread_self() pthread_self() +#define H5TS_thread_equal(t1, t2) pthread_equal((t1), (t2)) +#define H5TS_THREAD_RETURN_TYPE H5TS_thread_ret_t +#define H5TS_THREAD_CANCEL_DISABLE PTHREAD_CANCEL_DISABLE + +/* Mutex macros */ +#define H5TS_MUTEX_TYPE_PLAIN 0 +#define H5TS_MUTEX_TYPE_RECURSIVE 1 +#endif +#endif + +/* Atomics macros */ +#if defined(H5_HAVE_STDATOMIC_H) && !defined(__cplusplus) +/* atomic_int */ +#define H5TS_atomic_init_int(obj, desired) atomic_init((obj), (desired)) +#define H5TS_atomic_load_int(obj) atomic_load(obj) +#define H5TS_atomic_store_int(obj, desired) atomic_store((obj), (desired)) +#define H5TS_atomic_fetch_add_int(obj, arg) atomic_fetch_add((obj), (arg)) +#define H5TS_atomic_fetch_sub_int(obj, arg) atomic_fetch_sub((obj), (arg)) +#define H5TS_atomic_destroy_int(obj) /* void */ -/* Library level data structures */ - -/* Mutexes, Threads, and Attributes */ -typedef struct H5TS_mutex_struct { - CRITICAL_SECTION CriticalSection; -} H5TS_mutex_t; - -/* Portability wrappers around Windows Threads types */ -typedef CRITICAL_SECTION H5TS_mutex_simple_t; -typedef HANDLE H5TS_thread_t; -typedef HANDLE H5TS_attr_t; -typedef DWORD H5TS_key_t; -typedef INIT_ONCE H5TS_once_t; - -/* Defines */ -/* not used on windows side, but need to be defined to something */ -#define H5TS_SCOPE_SYSTEM 0 -#define H5TS_SCOPE_PROCESS 0 -#define H5TS_CALL_CONV WINAPI - -/* Portability function aliases */ -#define H5TS_get_thread_local_value(key) TlsGetValue(key) -#define H5TS_set_thread_local_value(key, value) TlsSetValue(key, value) -#define H5TS_attr_init(attr_ptr) 0 -#define H5TS_attr_setscope(attr_ptr, scope) 0 -#define H5TS_attr_destroy(attr_ptr) 0 -#define H5TS_wait_for_thread(thread) WaitForSingleObject(thread, INFINITE) -#define H5TS_mutex_init(mutex) InitializeCriticalSection(mutex) -#define H5TS_mutex_lock_simple(mutex) EnterCriticalSection(mutex) -#define H5TS_mutex_unlock_simple(mutex) LeaveCriticalSection(mutex) - -/* Functions called from DllMain */ -H5_DLL BOOL CALLBACK H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex); -H5_DLL void H5TS_win32_process_exit(void); -H5_DLL herr_t H5TS_win32_thread_enter(void); -H5_DLL herr_t H5TS_win32_thread_exit(void); - -#define H5TS_thread_id() ((uint64_t)GetCurrentThreadId()) - -#else /* H5_HAVE_WIN_THREADS */ - -/* Library level data structures */ - -/* Mutexes, Threads, and Attributes */ -typedef struct H5TS_mutex_struct { - pthread_t owner_thread; /* current lock owner */ - pthread_mutex_t atomic_lock; /* lock for atomicity of new mechanism */ - pthread_cond_t cond_var; /* condition variable */ - unsigned int lock_count; - - pthread_mutex_t atomic_lock2; /* lock for attempt_lock_count */ - unsigned int attempt_lock_count; -} H5TS_mutex_t; - -/* Portability wrappers around pthread types */ -typedef pthread_t H5TS_thread_t; -typedef pthread_attr_t H5TS_attr_t; -typedef pthread_mutex_t H5TS_mutex_simple_t; +/* atomic_uint */ +#define H5TS_atomic_init_uint(obj, desired) atomic_init((obj), (desired)) +#define H5TS_atomic_load_uint(obj) atomic_load(obj) +#define H5TS_atomic_store_uint(obj, desired) atomic_store((obj), (desired)) +#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 */ + +/****************************/ +/* Library Private Typedefs */ +/****************************/ + +/* Key destructor callback */ +typedef void (*H5TS_key_destructor_func_t)(void *); + +/* Thread pool */ +typedef struct H5TS_pool_t H5TS_pool_t; + +/* Portability aliases */ +#ifdef H5_HAVE_C11_THREADS +typedef thrd_t H5TS_thread_t; +typedef int (*H5TS_thread_start_func_t)(void *); +typedef int H5TS_thread_ret_t; +typedef tss_t H5TS_key_t; +typedef mtx_t H5TS_CAPABILITY("mutex") H5TS_mutex_t; +typedef cnd_t H5TS_cond_t; +typedef once_flag H5TS_once_t; +typedef void (*H5TS_once_init_func_t)(void); +#else +#ifdef H5_HAVE_WIN_THREADS +typedef HANDLE H5TS_thread_t; +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 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_once_init_func_t)(void); +#endif +#endif + +/* Atomics */ +#if defined(H5_HAVE_STDATOMIC_H) && !defined(__cplusplus) +typedef atomic_int H5TS_atomic_int_t; +typedef atomic_uint H5TS_atomic_uint_t; +#else +typedef struct { + H5TS_mutex_t mutex; + int value; +} H5TS_atomic_int_t; +typedef struct { + H5TS_mutex_t mutex; + unsigned value; +} H5TS_atomic_uint_t; +#endif -/* Scope Definitions */ -#define H5TS_SCOPE_SYSTEM PTHREAD_SCOPE_SYSTEM -#define H5TS_SCOPE_PROCESS PTHREAD_SCOPE_PROCESS -#define H5TS_CALL_CONV /* unused - Windows only */ - -/* Portability function aliases */ -#define H5TS_get_thread_local_value(key) pthread_getspecific(key) -#define H5TS_set_thread_local_value(key, value) pthread_setspecific(key, value) -#define H5TS_attr_init(attr_ptr) pthread_attr_init((attr_ptr)) -#define H5TS_attr_setscope(attr_ptr, scope) pthread_attr_setscope(attr_ptr, scope) -#define H5TS_attr_destroy(attr_ptr) pthread_attr_destroy(attr_ptr) -#define H5TS_wait_for_thread(thread) pthread_join(thread, NULL) -#define H5TS_mutex_init(mutex) pthread_mutex_init(mutex, NULL) -#define H5TS_mutex_lock_simple(mutex) pthread_mutex_lock(mutex) -#define H5TS_mutex_unlock_simple(mutex) pthread_mutex_unlock(mutex) - -/* Pthread-only routines */ -H5_DLL uint64_t H5TS_thread_id(void); -H5_DLL void H5TS_pthread_first_thread_init(void); - -#endif /* H5_HAVE_WIN_THREADS */ - -/* Library-scope global variables */ -extern H5TS_once_t H5TS_first_init_g; /* Library initialization */ -extern H5TS_key_t H5TS_errstk_key_g; /* Error stacks */ -#ifdef H5_HAVE_CODESTACK -extern H5TS_key_t H5TS_funcstk_key_g; /* Function stacks */ -#endif /* H5_HAVE_CODESTACK */ -extern H5TS_key_t H5TS_apictx_key_g; /* API contexts */ - -/* Library-scope routines */ -/* (Only used within H5private.h macros) */ -H5_DLL herr_t H5TS_mutex_lock(H5TS_mutex_t *mutex); -H5_DLL herr_t H5TS_mutex_unlock(H5TS_mutex_t *mutex); -H5_DLL herr_t H5TS_cancel_count_inc(void); -H5_DLL herr_t H5TS_cancel_count_dec(void); - -/* Testing routines */ -H5_DLL H5TS_thread_t H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata); - -#else /* H5_HAVE_THREADSAFE */ - -#define H5TS_thread_id() ((uint64_t)0) +/* Thread Barrier */ +#ifdef H5_HAVE_PTHREAD_BARRIER +typedef pthread_barrier_t H5TS_barrier_t; +#else +typedef struct H5TS_barrier_t { + unsigned count; + H5TS_atomic_uint_t openings; + H5TS_atomic_uint_t generation; +} H5TS_barrier_t; +#endif + +/* Semaphores */ +#if defined(_WIN32) +/* + * Windows semaphores + */ + +/* System semaphore */ +typedef HANDLE H5TS_semaphore_t; + +#elif defined(__unix__) && !defined(__MACH__) +/* + * POSIX semaphores + */ +#include +/* System semaphore */ +typedef sem_t H5TS_semaphore_t; + +#else +/* + * Emulated semaphores, for MacOS and unknown platforms + * Can't use POSIX semaphores on MacOS due to: + * http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html + */ + +/* Emulate semaphore w/mutex & condition variable */ +typedef struct H5TS_semaphore_t { + H5TS_mutex_t mutex; + H5TS_cond_t cond; + unsigned waiters; + int counter; +} H5TS_semaphore_t; +#endif + +/*****************************/ +/* Library-private Variables */ +/*****************************/ + +/***************************************/ +/* Library-private Function Prototypes */ +/***************************************/ + +#ifdef H5_HAVE_THREADSAFE +/* Library/thread init/term operations */ +H5_DLL void H5TS_term_package(void); + +/* API locking */ +H5_DLL herr_t H5TS_api_lock(void); +H5_DLL herr_t H5TS_api_unlock(void); + +/* Retrieve per-thread info */ +H5_DLL herr_t H5TS_thread_id(uint64_t *id); +H5_DLL struct H5CX_node_t **H5TS_get_api_ctx_ptr(void); +H5_DLL struct H5E_stack_t *H5TS_get_err_stack(void); #endif /* H5_HAVE_THREADSAFE */ +/* 'Once' operationss */ +H5_DLL herr_t H5TS_once(H5TS_once_t *once, H5TS_once_init_func_t func); + +/* Mutex operations */ +H5_DLL herr_t H5TS_mutex_init(H5TS_mutex_t *mutex, int type); +/* Mutex lock & unlock calls are defined in H5TSmutex.h */ +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); + +/* 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 */ +H5_DLL herr_t H5TS_cond_destroy(H5TS_cond_t *cond); + +/* Thread-specific keys */ +H5_DLL herr_t H5TS_key_create(H5TS_key_t *key, H5TS_key_destructor_func_t dtor); +/* Key set & get calls are defined in H5TSkey.h */ +H5_DLL herr_t H5TS_key_delete(H5TS_key_t key); + +/* Threads */ +H5_DLL herr_t H5TS_thread_create(H5TS_thread_t *thread, H5TS_thread_start_func_t func, void *udata); +H5_DLL herr_t H5TS_thread_join(H5TS_thread_t thread, H5TS_thread_ret_t *ret_val); +H5_DLL herr_t H5TS_thread_detach(H5TS_thread_t thread); +H5_DLL void H5TS_thread_yield(void); + +/* Thread pools */ +H5_DLL herr_t H5TS_pool_create(H5TS_pool_t **pool, unsigned num_threads); +/* Thread pool add task call is defined in H5TSpool.h */ +static inline herr_t H5TS_pool_add_task(H5TS_pool_t *pool, H5TS_thread_start_func_t func, void *ctx); +H5_DLL herr_t H5TS_pool_destroy(H5TS_pool_t *pool); + +/* Emulated C11 atomics */ +#if !(defined(H5_HAVE_STDATOMIC_H) && !defined(__cplusplus)) +/* atomic_int */ +H5_DLL void H5TS_atomic_init_int(H5TS_atomic_int_t *obj, int desired); +/* Atomic 'int' load, store, etc. calls are defined in H5TSatomic.h */ +static inline int H5TS_atomic_load_int(H5TS_atomic_int_t *obj); +static inline void H5TS_atomic_store_int(H5TS_atomic_int_t *obj, int desired); +static inline int H5TS_atomic_fetch_add_int(H5TS_atomic_int_t *obj, int arg); +static inline int H5TS_atomic_fetch_sub_int(H5TS_atomic_int_t *obj, int arg); +H5_DLL void H5TS_atomic_destroy_int(H5TS_atomic_int_t *obj); + +/* atomic_uint */ +H5_DLL void H5TS_atomic_init_uint(H5TS_atomic_uint_t *obj, unsigned desired); +/* Atomic 'unsigned' load, store, etc. calls are defined in H5TSatomic.h */ +static inline unsigned H5TS_atomic_load_uint(H5TS_atomic_uint_t *obj); +static inline void H5TS_atomic_store_uint(H5TS_atomic_uint_t *obj, unsigned desired); +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); +#endif /* H5_HAVE_STDATOMIC_H */ + +/* Barrier related function declarations */ +H5_DLL herr_t H5TS_barrier_init(H5TS_barrier_t *barrier, unsigned count); +/* Barrier wait call is defined in H5TSbarrier.h */ +static inline herr_t H5TS_barrier_wait(H5TS_barrier_t *barrier); +H5_DLL herr_t H5TS_barrier_destroy(H5TS_barrier_t *barrier); + +H5_DLL herr_t H5TS_semaphore_init(H5TS_semaphore_t *sem, unsigned initial_count); +/* Semaphore signal & wait calls are defined in H5TSsemaphore.h */ +static inline herr_t H5TS_semaphore_signal(H5TS_semaphore_t *sem); +static inline herr_t H5TS_semaphore_wait(H5TS_semaphore_t *sem); +H5_DLL herr_t H5TS_semaphore_destroy(H5TS_semaphore_t *sem); + +/* Headers with inlined routines */ +#include "H5TScond.h" +#include "H5TSmutex.h" +#include "H5TSkey.h" +#if !(defined(H5_HAVE_STDATOMIC_H) && !defined(__cplusplus)) +#include "H5TSatomic.h" +#endif /* H5_HAVE_STDATOMIC_H */ +#include "H5TSbarrier.h" +#include "H5TSsemaphore.h" +#include "H5TSpool.h" + +#endif /* H5_HAVE_THREADS */ + #endif /* H5TSprivate_H_ */ diff --git a/src/H5TSpthread.c b/src/H5TSpthread.c new file mode 100644 index 00000000000..e8774b67979 --- /dev/null +++ b/src/H5TSpthread.c @@ -0,0 +1,85 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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: Pthread threadsafety routines + * + * 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_PTHREAD_H + +/****************/ +/* Local Macros */ +/****************/ + +/******************/ +/* Local Typedefs */ +/******************/ + +/********************/ +/* Local Prototypes */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +#ifdef H5_HAVE_THREADSAFE +/*-------------------------------------------------------------------------- + * Function: H5TS__pthread_first_thread_init + * + * Purpose: Initialize global API lock, keys for per-thread error stacks + * and cancallability information. Called by the first thread + * that enters the library. + * + * Return: None + * + *-------------------------------------------------------------------------- + */ +void +H5TS__pthread_first_thread_init(void) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Initialize H5TS package */ + H5TS__init(); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__pthread_first_thread_init() */ +#endif /* H5_HAVE_THREADSAFE */ + +#endif /* H5_HAVE_PTHREAD_H */ diff --git a/src/H5TSrwlock.c b/src/H5TSrwlock.c new file mode 100644 index 00000000000..43bac524c17 --- /dev/null +++ b/src/H5TSrwlock.c @@ -0,0 +1,717 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 */ +/******************/ + +/****************************************************************************** + * + * 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 */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* 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 + * + * Purpose: Update stats for releasing a read lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_rd_unlock(H5TS_rw_lock_t *rw_lock, const H5TS_rec_entry_count_t *count) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + assert(H5TS_RW_LOCK_READ == rw_lock->lock_type); + assert(count); + assert(count->rec_lock_count >= 0); + + rw_lock->stats.read_locks_released++; + + if (count->rec_lock_count == 0) + rw_lock->stats.real_read_locks_released++; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_rd_unlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_wr_lock + * + * Purpose: Update stats for acquiring a write lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_wr_lock(H5TS_rw_lock_t *rw_lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + assert(H5TS_RW_LOCK_WRITE == rw_lock->lock_type); + assert(rw_lock->rec_write_lock_count >= 1); + + rw_lock->stats.write_locks_granted++; + + if (rw_lock->rec_write_lock_count == 1) { + rw_lock->stats.real_write_locks_granted++; + if (rw_lock->rec_write_lock_count > rw_lock->stats.max_write_locks) + rw_lock->stats.max_write_locks = rw_lock->rec_write_lock_count; + } + + if (rw_lock->rec_write_lock_count > rw_lock->stats.max_write_lock_recursion_depth) + rw_lock->stats.max_write_lock_recursion_depth = rw_lock->rec_write_lock_count; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_wr_lock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_wr_lock_delay + * + * Purpose: Update stats for delay in acquiring a write lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_wr_lock_delay(H5TS_rw_lock_t *rw_lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + + rw_lock->stats.write_locks_delayed++; + + if (rw_lock->stats.max_write_locks_pending <= rw_lock->waiting_writers_count) + rw_lock->stats.max_write_locks_pending = rw_lock->waiting_writers_count + 1; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_wr_lock_delay() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__update_stats_wr_unlock + * + * Purpose: Update stats for releasing a write lock + * + * Return: none + * + *-------------------------------------------------------------------------- + */ +static void +H5TS__update_stats_wr_unlock(H5TS_rw_lock_t *rw_lock) +{ + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + assert(rw_lock); + assert(H5TS_RW_LOCK_WRITE == rw_lock->lock_type); + assert(rw_lock->rec_write_lock_count >= 0); + + rw_lock->stats.write_locks_released++; + + if (rw_lock->rec_write_lock_count == 0) + rw_lock->stats.real_write_locks_released++; + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__update_stats_wr_unlock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_lock_get_stats + * + * Purpose: Obtain a copy of the current statistics for a recursive + * read / write lock. + * + * Note: To obtain a consistent set of statistics, the function must + * obtain the lock mutex. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_lock_get_stats(H5TS_rw_lock_t *rw_lock, H5TS_rw_lock_stats_t *stats) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock || NULL == stats)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Copy R/W lock stats */ + *stats = rw_lock->stats; + +done: + if (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)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex) < 0)) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Reset stats */ + memset(&rw_lock->stats, 0, sizeof(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_reset_stats() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_lock_print_stats + * + * Purpose: Print the supplied pthresds recursive R/W lock statistics. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_lock_print_stats(const char *header_str, H5TS_rw_lock_stats_t *stats) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == header_str || NULL == stats)) + HGOTO_DONE(FAIL); + + fprintf(stdout, "\n\n%s\n\n", header_str); + fprintf(stdout, " read_locks_granted = %" PRId64 "\n", stats->read_locks_granted); + fprintf(stdout, " read_locks_released = %" PRId64 "\n", stats->read_locks_released); + fprintf(stdout, " real_read_locks_granted = %" PRId64 "\n", stats->real_read_locks_granted); + fprintf(stdout, " real_read_locks_released = %" PRId64 "\n", stats->real_read_locks_released); + fprintf(stdout, " max_read_locks = %" PRId64 "\n", stats->max_read_locks); + fprintf(stdout, " max_read_lock_recursion_depth = %" PRId64 "\n", stats->max_read_lock_recursion_depth); + fprintf(stdout, " read_locks_delayed = %" PRId64 "\n", stats->read_locks_delayed); + fprintf(stdout, " write_locks_granted = %" PRId64 "\n", stats->write_locks_granted); + fprintf(stdout, " write_locks_released = %" PRId64 "\n", stats->write_locks_released); + fprintf(stdout, " real_write_locks_granted = %" PRId64 "\n", stats->real_write_locks_granted); + fprintf(stdout, " real_write_locks_released = %" PRId64 "\n", stats->real_write_locks_released); + fprintf(stdout, " max_write_locks = %" PRId64 "\n", stats->max_write_locks); + fprintf(stdout, " max_write_lock_recursion_depth = %" PRId64 "\n", + stats->max_write_lock_recursion_depth); + fprintf(stdout, " write_locks_delayed = %" PRId64 "\n", stats->write_locks_delayed); + fprintf(stdout, " max_write_locks_pending = %" PRId64 "\n\n", stats->max_write_locks_pending); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_lock_print_stats() */ +#endif /* H5TS_ENABLE_REC_RW_LOCK_STATS */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_lock_init + * + * Purpose: Initialize the supplied instance of H5TS_rw_lock_t. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_lock_init(H5TS_rw_lock_t *rw_lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + +#ifdef H5_HAVE_WIN_THREADS + /* The current H5TS_rw_lock_t implementation uses H5TS_key_create() with a + * key destructor callback, which is not [currently] supported by Windows. + */ + HGOTO_DONE(FAIL); +#else + /* Initialize the lock */ + 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 + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_lock_init() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_lock_destroy + * + * Purpose: Take down an instance of H5TS_rw_lock_t. All mutex, condition + * variables, and keys are destroyed. However, the instance of + * H5TS_rw_lock_t is not freed. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_lock_destroy(H5TS_rw_lock_t *rw_lock) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + + /* Call the appropriate destroy routines. We are committed + * to the destroy at this point, so call them all, even if one fails + * along the way. + */ + if (H5_UNLIKELY(H5TS_mutex_destroy(&rw_lock->mutex) < 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; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_lock_destroy() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_rdlock + * + * Purpose: Attempt to obtain a read lock on the associated recursive + * read / write lock. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_rdlock(H5TS_rw_lock_t *rw_lock) +{ + H5TS_rec_entry_count_t *count; + H5TS_thread_t my_thread = H5TS_thread_self(); + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex) < 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_RW_LOCK_WRITE == rw_lock->lock_type && + H5TS_thread_equal(my_thread, rw_lock->write_thread))) + HGOTO_DONE(FAIL); + + /* If there is no thread-specific data for this thread, set it up */ + if (!rw_lock->is_key_registered) { + if (H5_UNLIKELY(H5TS_key_create(&rw_lock->rec_read_lock_count_key, 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)) + 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() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__rw_wrlock + * + * Purpose: Attempt to obtain a write lock on the associated recursive + * read / write lock. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__rw_wrlock(H5TS_rw_lock_t *rw_lock) +{ + H5TS_thread_t my_thread = H5TS_thread_self(); + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == rw_lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&rw_lock->mutex) < 0)) + 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); +#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/H5TSsemaphore.c b/src/H5TSsemaphore.c new file mode 100644 index 00000000000..231058b5476 --- /dev/null +++ b/src/H5TSsemaphore.c @@ -0,0 +1,219 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 semaphores, equivalent to POSIX + * semaphore's capabilities. + * + * Emulated semaphores are based off the Netscape Portable Runtime + * implementation: + * https://hg.mozilla.org/projects/nspr/file/3e25d69ba6b268f2817e920a69eb2c091efe17e6/pr/src/threads/prsem.c + * + * 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 */ +/*******************/ + +#if defined(_WIN32) +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_init + * + * Purpose: Initialize a H5TS_semaphore_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_semaphore_init(H5TS_semaphore_t *sem, unsigned initial_count) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + if (H5_UNLIKELY(NULL == (*sem = CreateSemaphore(NULL, (LONG)initial_count, LONG_MAX, NULL)))) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_destroy + * + * Purpose: Destroy a H5TS_semaphore_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_semaphore_destroy(H5TS_semaphore_t *sem) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + if (H5_UNLIKELY(0 == CloseHandle(*sem))) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_destroy() */ + +#elif defined(__unix__) && !defined(__MACH__) +/* + * POSIX semaphores + */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_init + * + * Purpose: Initialize a H5TS_semaphore_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_semaphore_init(H5TS_semaphore_t *sem, unsigned initial_count) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + if (H5_UNLIKELY(0 != sem_init(sem, 0, initial_count))) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_destroy + * + * Purpose: Destroy a H5TS_semaphore_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_semaphore_destroy(H5TS_semaphore_t *sem) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + if (H5_UNLIKELY(0 != sem_destroy(sem))) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_destroy() */ +#else +/* + * Emulate semaphore w/mutex & condition variable + */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_init + * + * Purpose: Initialize a H5TS_semaphore_t (does not allocate it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_semaphore_init(H5TS_semaphore_t *sem, unsigned initial_count) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + if (H5_UNLIKELY(H5TS_mutex_init(&sem->mutex, H5TS_MUTEX_TYPE_PLAIN) < 0)) + return FAIL; + if (H5_UNLIKELY(H5TS_cond_init(&sem->cond) < 0)) { + H5TS_mutex_destroy(&sem->mutex); + return FAIL; + } + sem->waiters = 0; + sem->counter = (int)initial_count; + + return SUCCEED; +} /* end H5TS_semaphore_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_destroy + * + * Purpose: Destroy a H5TS_semaphore_t (does not free it) + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_semaphore_destroy(H5TS_semaphore_t *sem) +{ + herr_t ret_value = SUCCEED; + + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + if (H5_UNLIKELY(H5TS_mutex_destroy(&sem->mutex) < 0)) + ret_value = FAIL; + if (H5_UNLIKELY(H5TS_cond_destroy(&sem->cond) < 0)) + return FAIL; + + return ret_value; +} /* end H5TS_semaphore_destroy() */ +#endif + +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSsemaphore.h b/src/H5TSsemaphore.h new file mode 100644 index 00000000000..0db1a888809 --- /dev/null +++ b/src/H5TSsemaphore.h @@ -0,0 +1,265 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 semaphores, equivalent to POSIX + * semaphore's capabilities. The implementation is based on the + * "lightweight semaphore" describend here: + *https://preshing.com/20150316/semaphores-are-surprisingly-versatile/ and implemented here: + *https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h + * + * 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 */ +/*******************/ + +#if defined(_WIN32) +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_signal + * + * Purpose: Increments (unlocks) the semaphore. If the semaphore's value + * becomes greater than zero, then another thread blocked in a wait + * call will be woken up and proceed to lock the semaphore. + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_semaphore_signal(H5TS_semaphore_t *sem) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + if (H5_UNLIKELY(0 == ReleaseSemaphore(*sem, 1, NULL))) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_signal() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_wait + * + * Purpose: Decrements (locks) the semaphore. If the semaphore's value is + * greater than zero, then the decrement proceeds, and the function + * returns immediately. If the semaphore currently has a value of + * zero or less, then the call blocks until it becomes possible to + * perform the decrement (i.e. the semaphore value rises above zero). + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_semaphore_wait(H5TS_semaphore_t *sem) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + if (H5_UNLIKELY(WAIT_OBJECT_0 != WaitForSingleObject(*sem, INFINITE))) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_wait() */ + +#elif defined(__unix__) && !defined(__MACH__) +/* + * POSIX semaphores + */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_signal + * + * Purpose: Increments (unlocks) the semaphore. If the semaphore's value + * becomes greater than zero, then another thread blocked in a wait + * call will be woken up and proceed to lock the semaphore. + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_semaphore_signal(H5TS_semaphore_t *sem) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + if (H5_UNLIKELY(0 != sem_post(sem))) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_signal() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_wait + * + * Purpose: Decrements (locks) the semaphore. If the semaphore's value is + * greater than zero, then the decrement proceeds, and the function + * returns immediately. If the semaphore currently has a value of + * zero or less, then the call blocks until it becomes possible to + * perform the decrement (i.e. the semaphore value rises above zero). + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_semaphore_wait(H5TS_semaphore_t *sem) +{ + int rc; + + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + /* Loop because of: + * http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error + */ + do { + rc = sem_wait(sem); + } while (rc == -1 && errno == EINTR); + + if (H5_UNLIKELY(0 != rc)) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_wait() */ +#else +/* + * Emulate semaphore w/mutex & condition variable + */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_signal + * + * Purpose: Increments (unlocks) the semaphore. If the semaphore's value + * becomes greater than zero, then another thread blocked in a wait + * call will be woken up and proceed to lock the semaphore. + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_semaphore_signal(H5TS_semaphore_t *sem) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + /* Acquire the mutex for the semaphore */ + if (H5_UNLIKELY(H5TS_mutex_lock(&sem->mutex) < 0)) + return FAIL; + + /* Wake a thread up, if any are waiting */ + if (sem->waiters) + if (H5_UNLIKELY(H5TS_cond_signal(&sem->cond) < 0)) { + H5TS_mutex_unlock(&sem->mutex); + return FAIL; + } + + /* Increment the semaphore's value */ + sem->counter++; + + /* Release the mutex for the semaphore */ + if (H5_UNLIKELY(H5TS_mutex_unlock(&sem->mutex) < 0)) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_signal() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_semaphore_wait + * + * Purpose: Decrements (locks) the semaphore. If the semaphore's value is + * greater than zero, then the decrement proceeds, and the function + * returns immediately. If the semaphore currently has a value of + * zero or less, then the call blocks until it becomes possible to + * perform the decrement (i.e. the semaphore value rises above zero). + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +static inline herr_t +H5TS_semaphore_wait(H5TS_semaphore_t *sem) +{ + /* Check argument */ + if (H5_UNLIKELY(NULL == sem)) + return FAIL; + + /* Acquire the mutex for the semaphore */ + if (H5_UNLIKELY(H5TS_mutex_lock(&sem->mutex) < 0)) + return FAIL; + + /* Wait for semaphore value > 0 */ + while (0 == sem->counter) { + herr_t ret; + + /* Wait for signal that semaphore count has been incremented */ + sem->waiters++; + ret = H5TS_cond_wait(&sem->cond, &sem->mutex); + sem->waiters--; + + /* Check for error */ + if (H5_UNLIKELY(ret < 0)) { + H5TS_mutex_unlock(&sem->mutex); + return FAIL; + } + } + + /* Decrement the semaphore's value */ + sem->counter--; + + /* Release the mutex for the semaphore */ + if (H5_UNLIKELY(H5TS_mutex_unlock(&sem->mutex) < 0)) + return FAIL; + + return SUCCEED; +} /* end H5TS_semaphore_wait() */ +#endif diff --git a/src/H5TSthread.c b/src/H5TSthread.c new file mode 100644 index 00000000000..f8bd1aebe0a --- /dev/null +++ b/src/H5TSthread.c @@ -0,0 +1,343 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 thread operations, equivalent to + * the pthread 'pthread_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_thread_create + * + * Purpose: Spawn off a new thread calling function 'func' with input 'udata'. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_create(H5TS_thread_t *thread, H5TS_thread_start_func_t func, void *udata) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(thrd_create(thread, func, udata) != thrd_success)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_create() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_join + * + * Purpose: Wait for thread termination + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_join(H5TS_thread_t thread, H5TS_thread_ret_t *ret_val) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(thrd_join(thread, ret_val) != thrd_success)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_join() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_detach + * + * Purpose: Detach a thread + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_detach(H5TS_thread_t thread) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(thrd_detach(thread) != thrd_success)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_detach() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_yield + * + * Purpose: Yield thread execution + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +void +H5TS_thread_yield(void) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + thrd_yield(); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* H5TS_thread_yield() */ +#else +#ifdef H5_HAVE_WIN_THREADS +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_create + * + * Purpose: Spawn off a new thread calling function 'func' with input 'udata'. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_create(H5TS_thread_t *thread, H5TS_thread_start_func_t func, void *udata) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* When calling C runtime functions, you should use _beginthread or + * _beginthreadex instead of CreateThread. Threads created with + * CreateThread risk being killed in low-memory situations, but + * we'll use the easier-to-deal-with CreateThread for now. + * + * NOTE: _beginthread() auto-recycles its handle when execution completes + * so you can't wait on it, making it unsuitable for the existing + * test code. + */ + if (H5_UNLIKELY(NULL == (*thread = CreateThread(NULL, 0, func, udata, 0, NULL)))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_create() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_join + * + * Purpose: Wait for thread termination + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_join(H5TS_thread_t thread, H5TS_thread_ret_t *ret_val) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(WAIT_OBJECT_0 != WaitForSingleObject(thread, INFINITE))) + HGOTO_DONE(FAIL); + if (ret_val) + if (H5_UNLIKELY(0 == GetExitCodeThread(thread, ret_val))) + HGOTO_DONE(FAIL); + if (H5_UNLIKELY(0 == CloseHandle(thread))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_join() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_detach + * + * Purpose: Detach a thread + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_detach(H5TS_thread_t thread) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(0 == CloseHandle(thread))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_detach() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_yield + * + * Purpose: Yield thread execution + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +void +H5TS_thread_yield(void) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + SwitchToThread(); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* H5TS_thread_yield() */ +#else +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_create + * + * Purpose: Spawn off a new thread calling function 'func' with input 'udata'. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_create(H5TS_thread_t *thread, H5TS_thread_start_func_t func, void *udata) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_create(thread, NULL, func, udata))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_create() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_join + * + * Purpose: Wait for thread termination + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_join(H5TS_thread_t thread, H5TS_thread_ret_t *ret_val) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_join(thread, ret_val))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_join() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_detach + * + * Purpose: Detach a thread + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_thread_detach(H5TS_thread_t thread) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_detach(thread))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS_thread_detach() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_thread_yield + * + * Purpose: Yield thread execution + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +void +H5TS_thread_yield(void) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + sched_yield(); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* H5TS_thread_yield() */ +#endif +#endif +#endif /* H5_HAVE_THREADS */ diff --git a/src/H5TSwin.c b/src/H5TSwin.c new file mode 100644 index 00000000000..4eca38e34db --- /dev/null +++ b/src/H5TSwin.c @@ -0,0 +1,209 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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: Windows threadsafety routines + * + * 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_WIN_THREADS + +/****************/ +/* Local Macros */ +/****************/ + +/******************/ +/* Local Typedefs */ +/******************/ + +/********************/ +/* Local Prototypes */ +/********************/ +#if defined(H5_BUILT_AS_DYNAMIC_LIB) && defined(H5_HAVE_WIN32_API) +static herr_t H5TS__win32_thread_enter(void); +static herr_t H5TS__win32_thread_exit(void); +#endif + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + +#ifdef H5_HAVE_THREADSAFE +/*-------------------------------------------------------------------------- + * Function: H5TS__win32_process_enter + * + * Purpose: Per-process setup on Windows when using Win32 threads. + * + * Returns: TRUE on success, FALSE on failure + * + *-------------------------------------------------------------------------- + */ +H5_DLL BOOL CALLBACK +H5TS__win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex) +{ + BOOL ret_value = TRUE; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Initialize H5TS package */ + if (H5_UNLIKELY(H5TS__init() < 0)) + HGOTO_DONE(FALSE); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS__win32_process_enter() */ + +#if defined(H5_BUILT_AS_DYNAMIC_LIB) && defined(H5_HAVE_WIN32_API) +/*-------------------------------------------------------------------------- + * Function: H5TS__win32_thread_enter + * + * Purpose: Per-thread setup on Windows when using Win32 threads. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +static herr_t +H5TS__win32_thread_enter(void) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Currently a placeholder function. TLS setup is performed + * elsewhere in the library. + * + * WARNING: Do NOT use C standard library functions here. + * CRT functions are not allowed in DllMain, which is where this code + * is used. + */ + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* H5TS__win32_thread_enter() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__win32_thread_exit + * + * Purpose: Per-thread cleanup on Windows when using Win32 threads. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +static herr_t +H5TS__win32_thread_exit(void) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Windows uses a different thread local storage mechanism which does + * not support auto-freeing like pthreads' keys. + * + * WARNING: Do NOT use C standard library functions here. + * CRT functions are not allowed in DllMain, which is where this code + * is used. + */ + + /* Clean up per-thread thread local storage */ + if (H5TS_thrd_info_key_g != TLS_OUT_OF_INDEXES) { + LPVOID lpvData; + + if (H5_UNLIKELY(H5TS_key_get_value(H5TS_thrd_info_key_g, &lpvData) < 0)) + HGOTO_DONE(FAIL); + if (lpvData) + H5TS__tinfo_destroy(lpvData); + } + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS__win32_thread_exit() */ + +/*------------------------------------------------------------------------- + * Function: DllMain + * + * Purpose: Handles various conditions in the library on Windows. + * + * NOTE: The main purpose of this is for handling Win32 thread cleanup + * on thread/process detach. + * + * Only enabled when the shared Windows library is built with + * thread safety enabled. + * + * Return: true on success, false on failure + * + *------------------------------------------------------------------------- + */ +BOOL WINAPI +DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved) +{ + /* Don't add our function enter/leave macros since this function will be + * called before the library is initialized. + * + * NOTE: Do NOT call any CRT functions in DllMain! + * This includes any functions that are called by from here! + */ + + BOOL fOkay = true; + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + break; + + case DLL_PROCESS_DETACH: + break; + + case DLL_THREAD_ATTACH: + if (H5TS__win32_thread_enter() < 0) + fOkay = false; + break; + + case DLL_THREAD_DETACH: + if (H5TS__win32_thread_exit() < 0) + fOkay = false; + break; + + default: + /* Shouldn't get here */ + fOkay = false; + break; + } + + return fOkay; +} +#endif /* H5_HAVE_WIN32_API && H5_BUILT_AS_DYNAMIC_LIB */ +#endif /* H5_HAVE_THREADSAFE */ + +#endif /* H5_HAVE_WIN_THREADS */ diff --git a/src/H5build_settings.autotools.c.in b/src/H5build_settings.autotools.c.in index 614fd1199b7..5ca1555a157 100644 --- a/src/H5build_settings.autotools.c.in +++ b/src/H5build_settings.autotools.c.in @@ -95,6 +95,7 @@ const char H5build_settings[]= " Build HDF5 Tests: @HDF5_TESTS@\n" " Build HDF5 Tools: @HDF5_TOOLS@\n" " Build GIF Tools: @HDF5_HL_GIF_TOOLS@\n" + " Threads: @THREADS@\n" " Threadsafety: @THREADSAFE@\n" " Default API mapping: @DEFAULT_API_VERSION@\n" " With deprecated public symbols: @DEPRECATED_SYMBOLS@\n" diff --git a/src/H5err.txt b/src/H5err.txt index fceb72618c1..23cab2c0e56 100644 --- a/src/H5err.txt +++ b/src/H5err.txt @@ -78,6 +78,7 @@ MAJOR, H5E_PAGEBUF, Page Buffering MAJOR, H5E_CONTEXT, API Context MAJOR, H5E_MAP, Map MAJOR, H5E_EVENTSET, Event Set +MAJOR, H5E_THREADSAFE, Threadsafety MAJOR, H5E_NONE_MAJOR, No error # Sections (for grouping minor errors) diff --git a/src/H5private.h b/src/H5private.h index 43412735b41..ab005067668 100644 --- a/src/H5private.h +++ b/src/H5private.h @@ -53,10 +53,23 @@ #include #endif -/* Include the Pthreads header, if necessary */ -#if defined(H5_HAVE_THREADSAFE) && defined(H5_HAVE_PTHREAD_H) +/* Include the threading header, if necessary */ +#if defined(H5_HAVE_THREADS) +/* C11 threads */ +#if defined(H5_HAVE_THREADS_H) +#include +#endif + +/* Pthreads */ +#if defined(H5_HAVE_PTHREAD_H) #include #endif +#endif + +/* C11 atomics */ +#if defined(H5_HAVE_STDATOMIC_H) && !defined(__cplusplus) +#include +#endif /* * The `struct stat' data type for stat() and fstat(). This is a POSIX file @@ -123,7 +136,7 @@ #define NOGDI /* Exclude Graphic Display Interface macros */ /* InitOnceExecuteOnce() requires 0x0600 to work on MinGW w/ Win32 threads */ -#if defined(H5_HAVE_MINGW) && defined(H5_HAVE_THREADSAFE) +#if defined(H5_HAVE_MINGW) && defined(H5_HAVE_THREADS) #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0600) #undef _WIN32_WINNT #define _WIN32_WINNT 0x0600 @@ -137,10 +150,6 @@ #include /* For GetUserName() */ #include /* For StrStrIA */ -#ifdef H5_HAVE_THREADSAFE -#include /* For _beginthread() */ -#endif - #endif /*H5_HAVE_WIN32_API*/ #ifndef F_OK @@ -239,6 +248,13 @@ # define H5_ATTR_NO_OPTIMIZE /*void*/ # endif +/* Enable thread-safety annotations when built with clang */ +# if defined(__clang__) +# define H5_ATTR_THREAD_ANNOT(X) __attribute__((X)) +# else +# define H5_ATTR_THREAD_ANNOT(X) /*void*/ +# endif + #else # define H5_ATTR_FORMAT(X, Y, Z) /*void*/ # define H5_ATTR_UNUSED /*void*/ @@ -252,6 +268,7 @@ # define H5_ATTR_FALLTHROUGH /*void*/ # define H5_ATTR_MALLOC /*void*/ # define H5_ATTR_NO_OPTIMIZE /*void*/ +# define H5_ATTR_THREAD_ANNOT(X) /*void*/ #endif /* clang-format on */ @@ -319,11 +336,6 @@ #define ABS(a) (((a) >= 0) ? (a) : -(a)) #endif -/* sign of argument */ -#ifndef SIGN -#define SIGN(a) ((a) > 0 ? 1 : (a) < 0 ? -1 : 0) -#endif - /* test for number that is a power of 2 */ /* (from: http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2) */ #define POWER_OF_TWO(n) (!(n & (n - 1)) && n) @@ -1149,54 +1161,59 @@ H5_DLL herr_t H5_trace_args(struct H5RS_str_t *rs, const char *type, va_list ap) /* global library version information string */ extern char H5_lib_vers_info_g[]; -#include "H5TSprivate.h" - -/* Lock headers */ #ifdef H5_HAVE_THREADSAFE -/* replacement structure for original global variable */ -typedef struct H5_api_struct { - H5TS_mutex_t init_lock; /* API entrance mutex */ - bool H5_libinit_g; /* Has the library been initialized? */ - bool H5_libterm_g; /* Is the library being shutdown? */ -} H5_api_t; +/* Lock headers */ +#include "H5TSprivate.h" -/* Macros for accessing the global variables */ -#define H5_INIT_GLOBAL (H5_g.H5_libinit_g) -#define H5_TERM_GLOBAL (H5_g.H5_libterm_g) +/* Thread cancellation is only possible w/pthreads */ +#if defined(H5_HAVE_PTHREAD_H) +/* Local variable for saving cancellation state */ +#define H5CANCEL_DECL int oldstate = 0; -/* Macro for first thread initialization */ -#ifdef H5_HAVE_WIN_THREADS -#define H5_FIRST_THREAD_INIT InitOnceExecuteOnce(&H5TS_first_init_g, H5TS_win32_process_enter, NULL, NULL); +/* Disable & restore canceling the thread */ +#define H5TS_DISABLE_CANCEL \ + do { \ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate); \ + } while (0) +#define H5TS_RESTORE_CANCEL \ + do { \ + pthread_setcancelstate(oldstate, NULL); \ + } while (0) #else -#define H5_FIRST_THREAD_INIT pthread_once(&H5TS_first_init_g, H5TS_pthread_first_thread_init); -#endif - -/* Macros for threadsafe HDF5 Phase I locks */ -#define H5_API_LOCK H5TS_mutex_lock(&H5_g.init_lock); -#define H5_API_UNLOCK H5TS_mutex_unlock(&H5_g.init_lock); - -/* Macros for thread cancellation-safe mechanism */ -#define H5_API_UNSET_CANCEL H5TS_cancel_count_inc(); +/* Local variable for saving cancellation state */ +#define H5CANCEL_DECL /* */ -#define H5_API_SET_CANCEL H5TS_cancel_count_dec(); - -extern H5_api_t H5_g; +/* Disable & restore canceling the thread */ +#define H5TS_DISABLE_CANCEL /* */ +#define H5TS_RESTORE_CANCEL /* */ +#endif -#else /* H5_HAVE_THREADSAFE */ +/* Macros for entering & leaving an API routine in a threadsafe manner */ +#define H5_API_LOCK \ + /* Acquire the API lock */ \ + H5TS_api_lock(); \ + \ + /* Set thread cancellation state to 'disable', and remember previous state */ \ + H5TS_DISABLE_CANCEL; +#define H5_API_UNLOCK \ + /* Release the API lock */ \ + H5TS_api_unlock(); \ + \ + /* Restore previous thread cancellation state */ \ + H5TS_RESTORE_CANCEL; +#else /* H5_HAVE_THREADSAFE */ -/* disable any first thread init mechanism */ -#define H5_FIRST_THREAD_INIT +/* Local variable for saving cancellation state */ +#define H5CANCEL_DECL /* */ -/* disable locks (sequential version) */ -#define H5_API_LOCK -#define H5_API_UNLOCK +/* No locks (non-threadsafe builds) */ +#define H5_API_LOCK /* */ +#define H5_API_UNLOCK /* */ -/* disable cancellability (sequential version) */ -#define H5_API_UNSET_CANCEL -#define H5_API_SET_CANCEL +#endif /* H5_HAVE_THREADSAFE */ -/* extern global variables */ +/* Library init / term status (global) */ extern bool H5_libinit_g; /* Has the library been initialized? */ extern bool H5_libterm_g; /* Is the library being shutdown? */ @@ -1204,8 +1221,6 @@ extern bool H5_libterm_g; /* Is the library being shutdown? */ #define H5_INIT_GLOBAL (H5_libinit_g) #define H5_TERM_GLOBAL (H5_libterm_g) -#endif /* H5_HAVE_THREADSAFE */ - /* Forward declaration of H5CXpush() / H5CXpop() */ /* (Including H5CXprivate.h creates bad circular dependencies - QAK, 3/18/2018) */ H5_DLL herr_t H5CX_push(void); @@ -1237,18 +1252,13 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); #define FUNC_ENTER_COMMON_NOERR(asrt) FUNC_ENTER_CHECK_NAME(asrt); -/* Threadsafety initialization code for API routines */ -#define FUNC_ENTER_API_THREADSAFE \ - /* Initialize the thread-safe code */ \ - H5_FIRST_THREAD_INIT \ - \ - /* Grab the mutex for the library */ \ - H5_API_UNSET_CANCEL \ - H5_API_LOCK +/* Local variables for API routines */ +#define FUNC_ENTER_API_VARS H5CANCEL_DECL #define FUNC_ENTER_API_COMMON \ + FUNC_ENTER_API_VARS \ FUNC_ENTER_COMMON(H5_IS_API(__func__)); \ - FUNC_ENTER_API_THREADSAFE; + H5_API_LOCK #define FUNC_ENTER_API_INIT(err) \ /* Initialize the library */ \ @@ -1294,8 +1304,7 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); /* * Use this macro for API functions that shouldn't perform _any_ initialization * of the library or an interface, just perform tracing, etc. Examples - * are: H5allocate_memory, H5is_library_threadsafe, public VOL callback - * wrappers (e.g. H5VLfile_create, H5VLdataset_read, etc.), etc. + * are: H5is_library_threadsafe, H5VLretrieve_lib_state, etc. * */ #define FUNC_ENTER_API_NOINIT \ @@ -1317,8 +1326,9 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); { \ { \ { \ + FUNC_ENTER_API_VARS \ FUNC_ENTER_COMMON_NOERR(H5_IS_API(__func__)); \ - FUNC_ENTER_API_THREADSAFE; \ + H5_API_LOCK \ { /* @@ -1333,8 +1343,9 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); { \ { \ { \ + FUNC_ENTER_API_VARS \ FUNC_ENTER_COMMON(H5_IS_API(__func__)); \ - FUNC_ENTER_API_THREADSAFE; \ + H5_API_LOCK \ FUNC_ENTER_API_INIT(err); \ { @@ -1469,10 +1480,6 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); * the last statement executed by a function. *------------------------------------------------------------------------- */ -/* Threadsafety termination code for API routines */ -#define FUNC_LEAVE_API_THREADSAFE \ - H5_API_UNLOCK \ - H5_API_SET_CANCEL #define FUNC_LEAVE_API(ret_value) \ ; \ @@ -1483,7 +1490,7 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); } \ if (H5_UNLIKELY(err_occurred)) \ (void)H5E_dump_api_stack(); \ - FUNC_LEAVE_API_THREADSAFE \ + H5_API_UNLOCK \ return (ret_value); \ } \ } /*end scope from beginning of FUNC_ENTER*/ @@ -1494,7 +1501,7 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); } /*end scope from end of FUNC_ENTER*/ \ if (H5_UNLIKELY(err_occurred)) \ (void)H5E_dump_api_stack(); \ - FUNC_LEAVE_API_THREADSAFE \ + H5_API_UNLOCK \ return (ret_value); \ } \ } \ @@ -1504,7 +1511,7 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); #define FUNC_LEAVE_API_NOFS(ret_value) \ ; \ } /*end scope from end of FUNC_ENTER*/ \ - FUNC_LEAVE_API_THREADSAFE \ + H5_API_UNLOCK \ return (ret_value); \ } \ } \ @@ -1517,7 +1524,7 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); } /*end scope from end of FUNC_ENTER*/ \ if (H5_UNLIKELY(err_occurred)) \ (void)H5E_dump_api_stack(); \ - FUNC_LEAVE_API_THREADSAFE \ + H5_API_UNLOCK \ return (ret_value); \ } \ } \ diff --git a/src/Makefile.am b/src/Makefile.am index 7cc64eff84a..c887a9aa73a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -97,7 +97,9 @@ libhdf5_la_SOURCES= H5.c H5build_settings.c H5checksum.c H5dbg.c H5system.c \ H5Tfloat.c H5Tinit_float.c H5Tnative.c H5Toffset.c H5Toh.c H5Topaque.c \ H5Torder.c H5Tref.c H5Tpad.c H5Tprecis.c H5Tstrpad.c H5Tvisit.c \ H5Tvlen.c \ - H5TS.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 \ 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 \ @@ -164,18 +166,6 @@ include_HEADERS = hdf5.h H5api_adpt.h H5overflow.h H5pubconf.h H5public.h H5vers include_HEADERS += H5ESdevelop.h H5FDdevelop.h H5Idevelop.h H5Ldevelop.h \ H5Tdevelop.h H5TSdevelop.h H5Zdevelop.h -if SUBFILING_VFD_CONDITIONAL - -# Temporarily use internal Mercury until usage of Mercury is refactored -if HAVE_MERCURY_CONDITIONAL - libhdf5_la_SOURCES += H5FDsubfiling/mercury/src/util/mercury_thread.c \ - H5FDsubfiling/mercury/src/util/mercury_thread_condition.c \ - H5FDsubfiling/mercury/src/util/mercury_thread_pool.c \ - H5FDsubfiling/mercury/src/util/mercury_thread_mutex.c -endif - -endif - # install libhdf5.settings in lib directory settingsdir=$(libdir) settings_DATA=libhdf5.settings diff --git a/src/libhdf5.settings.autotools.in b/src/libhdf5.settings.autotools.in index 3854d72e411..f04cca78e23 100644 --- a/src/libhdf5.settings.autotools.in +++ b/src/libhdf5.settings.autotools.in @@ -76,6 +76,7 @@ Dimension scales w/ new references: @DIMENSION_SCALES_WITH_NEW_REF@ Build HDF5 Tests: @HDF5_TESTS@ Build HDF5 Tools: @HDF5_TOOLS@ Build GIF Tools: @HDF5_HL_GIF_TOOLS@ + Threads: @THREADS@ Threadsafety: @THREADSAFE@ Default API mapping: @DEFAULT_API_VERSION@ With deprecated public symbols: @DEPRECATED_SYMBOLS@ diff --git a/src/libhdf5.settings.cmake.in b/src/libhdf5.settings.cmake.in index c9f3e4d413c..02d1796a0b4 100644 --- a/src/libhdf5.settings.cmake.in +++ b/src/libhdf5.settings.cmake.in @@ -74,6 +74,7 @@ Dimension scales w/ new references: @DIMENSION_SCALES_WITH_NEW_REF@ Build HDF5 Tests: @BUILD_TESTING@ Build HDF5 Tools: @HDF5_BUILD_TOOLS@ Build GIF Tools: @HDF5_BUILD_HL_GIF_TOOLS@ + Threads: @HDF5_ENABLE_THREADS@ Threadsafety: @HDF5_ENABLE_THREADSAFE@ Default API mapping: @DEFAULT_API_VERSION@ With deprecated public symbols: @HDF5_ENABLE_DEPRECATED_SYMBOLS@ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fe860d5ae77..c31ef2624f4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -343,11 +343,17 @@ set(mirror_vfd_SOURCES set (ttsafe_SOURCES ${HDF5_TEST_SOURCE_DIR}/ttsafe.h ${HDF5_TEST_SOURCE_DIR}/ttsafe.c - ${HDF5_TEST_SOURCE_DIR}/ttsafe_dcreate.c - ${HDF5_TEST_SOURCE_DIR}/ttsafe_error.c - ${HDF5_TEST_SOURCE_DIR}/ttsafe_cancel.c ${HDF5_TEST_SOURCE_DIR}/ttsafe_acreate.c + ${HDF5_TEST_SOURCE_DIR}/ttsafe_atomic.c ${HDF5_TEST_SOURCE_DIR}/ttsafe_attr_vlen.c + ${HDF5_TEST_SOURCE_DIR}/ttsafe_cancel.c + ${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_semaphore.c + ${HDF5_TEST_SOURCE_DIR}/ttsafe_thread_id.c + ${HDF5_TEST_SOURCE_DIR}/ttsafe_thread_pool.c ) set (H5_EXPRESS_TESTS @@ -423,7 +429,6 @@ set (H5_TESTS cache_logging cork swmr - thread_id # special link vol timer cmpd_dtransform @@ -462,7 +467,6 @@ set (H5_TESTS_MULTIPLE testhdf5 cache_image ttsafe - thread_id # special link mirror_vfd ) # Only build single source tests here @@ -585,31 +589,6 @@ if (HDF5_ENABLE_FORMATTERS) clang_format (HDF5_TEST_ttsafe_FORMAT ttsafe) endif () -######### Special handling for extra link lib of threads ############# -#-- Adding test for thread_id -add_executable (thread_id ${HDF5_TEST_SOURCE_DIR}/thread_id.c) -target_compile_options(thread_id PRIVATE "${HDF5_CMAKE_C_FLAGS}") -target_compile_definitions(thread_id PRIVATE "${HDF5_TEST_COMPILE_DEFS_PRIVATE}") -target_include_directories (thread_id PRIVATE "${HDF5_SRC_INCLUDE_DIRS};${HDF5_SRC_BINARY_DIR};$<$:${MPI_C_INCLUDE_DIRS}>") -if (NOT BUILD_SHARED_LIBS) - TARGET_C_PROPERTIES (thread_id STATIC) - target_link_libraries (thread_id PRIVATE ${HDF5_TEST_LIB_TARGET}) - if (NOT WIN32) - target_link_libraries (thread_id PRIVATE "$<$:Threads::Threads>") - endif () -else () - TARGET_C_PROPERTIES (thread_id SHARED) - target_link_libraries (thread_id PRIVATE ${HDF5_TEST_LIBSH_TARGET} "$<$:Threads::Threads>") -endif () -set_target_properties (thread_id PROPERTIES FOLDER test) - -#----------------------------------------------------------------------------- -# Add Target to clang-format -#----------------------------------------------------------------------------- -if (HDF5_ENABLE_FORMATTERS) - clang_format (HDF5_TEST_thread_id_FORMAT thread_id) -endif () - if (HDF5_BUILD_UTILS) # requires mirror server #-- Adding test for mirror_vfd add_executable (mirror_vfd ${mirror_vfd_SOURCES}) diff --git a/test/Makefile.am b/test/Makefile.am index fdd83e5bdeb..3625ac3dcce 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -69,8 +69,7 @@ TEST_PROG= testhdf5 \ flush1 flush2 app_ref enum set_extent ttsafe enc_dec_plist \ enc_dec_plist_cross_platform getname vfd ros3 s3comms hdfs ntypes \ dangle dtransform reserved cross_read freespace mf vds file_image \ - unregister cache_logging cork swmr thread_id vol timer event_set \ - onion + unregister cache_logging cork swmr vol timer event_set onion # List programs to be built when testing here # @@ -161,8 +160,9 @@ libh5test_la_SOURCES=h5test.c testframe.c cache_common.c swmr_common.c external_ LDADD=libh5test.la $(LIBHDF5) # List the source files for tests that have more than one -ttsafe_SOURCES=ttsafe.c ttsafe_dcreate.c ttsafe_error.c ttsafe_cancel.c \ - ttsafe_acreate.c ttsafe_attr_vlen.c +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 cache_image_SOURCES=cache_image.c genall5.c mirror_vfd_SOURCES=mirror_vfd.c genall5.c diff --git a/test/h5test.c b/test/h5test.c index c18efdc7dff..01216ca1059 100644 --- a/test/h5test.c +++ b/test/h5test.c @@ -1043,8 +1043,14 @@ h5_show_hostname(void) #ifdef H5_HAVE_PARALLEL int mpi_rank, mpi_initialized, mpi_finalized; #endif +#ifdef H5_HAVE_THREADSAFE + uint64_t thread_id = 0; /* ID of thread */ + + if (H5TS_thread_id(&thread_id) < 0) + return; +#endif - /* try show the process or thread id in multiple processes cases*/ + /* try show the process or thread id in multiple processes cases*/ #ifdef H5_HAVE_PARALLEL MPI_Initialized(&mpi_initialized); MPI_Finalized(&mpi_finalized); @@ -1057,11 +1063,11 @@ h5_show_hostname(void) } #ifdef H5_HAVE_THREADSAFE else - printf("thread %" PRIu64 ".", H5TS_thread_id()); + printf("thread %" PRIu64 ".", thread_id); #endif #else #ifdef H5_HAVE_THREADSAFE - printf("thread %" PRIu64 ".", H5TS_thread_id()); + printf("thread %" PRIu64 ".", thread_id); #endif #endif #ifdef H5_HAVE_WIN32_API diff --git a/test/thread_id.c b/test/thread_id.c deleted file mode 100644 index ed1e0a8a611..00000000000 --- a/test/thread_id.c +++ /dev/null @@ -1,324 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * 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. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* Check that a thread ID returned by H5TS_thread_id() possesses the - * following properties: - * - * 1 ID >= 1. - * 2 The ID is constant over the thread's lifetime. - * 3 No two threads share an ID during their lifetimes. - * 4 A thread's ID is available for reuse as soon as it is joined. - */ - -/* - * Include required headers. This file tests internal library functions, - * so we include the private headers here. - */ -#include "testhdf5.h" - -#if defined(H5_HAVE_THREADSAFE) && !defined(H5_HAVE_WIN_THREADS) - -static void my_errx(int, const char *, ...) H5_ATTR_FORMAT(printf, 2, 3); - -static void -my_errx(int code, const char *fmt, ...) -{ - va_list ap; - - (void)fprintf(stderr, "thread_id: "); - va_start(ap, fmt); - (void)vfprintf(stderr, fmt, ap); - va_end(ap); - (void)fputc('\n', stderr); - exit(code); -} - -#if defined(H5_HAVE_DARWIN) - -typedef struct _pthread_barrierattr { - uint8_t unused; -} pthread_barrierattr_t; - -typedef struct _pthread_barrier { - uint32_t magic; - unsigned int count; - uint64_t nentered; - pthread_cond_t cv; - pthread_mutex_t mtx; -} pthread_barrier_t; - -int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *, unsigned int); -int pthread_barrier_wait(pthread_barrier_t *); -int pthread_barrier_destroy(pthread_barrier_t *); - -static const uint32_t barrier_magic = 0xf00dd00f; - -int -pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) -{ - int rc; - - if (count == 0) - return EINVAL; - - if (attr != NULL) - return EINVAL; - - memset(barrier, 0, sizeof(*barrier)); - - barrier->count = count; - - if ((rc = pthread_cond_init(&barrier->cv, NULL)) != 0) - return rc; - - if ((rc = pthread_mutex_init(&barrier->mtx, NULL)) != 0) { - (void)pthread_cond_destroy(&barrier->cv); - return rc; - } - - barrier->magic = barrier_magic; - - return 0; -} - -static void -barrier_lock(pthread_barrier_t *barrier) -{ - int rc; - - if ((rc = pthread_mutex_lock(&barrier->mtx)) != 0) { - my_errx(EXIT_FAILURE, "%s: pthread_mutex_lock: %s", __func__, strerror(rc)); - } -} - -static void -barrier_unlock(pthread_barrier_t *barrier) -{ - int rc; - - if ((rc = pthread_mutex_unlock(&barrier->mtx)) != 0) { - my_errx(EXIT_FAILURE, "%s: pthread_mutex_unlock: %s", __func__, strerror(rc)); - } -} - -int -pthread_barrier_destroy(pthread_barrier_t *barrier) -{ - int rc; - - barrier_lock(barrier); - if (barrier->magic != barrier_magic) - rc = EINVAL; - else if (barrier->nentered % barrier->count != 0) - rc = EBUSY; - else { - rc = 0; - barrier->magic = ~barrier->magic; - } - barrier_unlock(barrier); - - if (rc != 0) - return rc; - - (void)pthread_cond_destroy(&barrier->cv); - (void)pthread_mutex_destroy(&barrier->mtx); - - return 0; -} - -int -pthread_barrier_wait(pthread_barrier_t *barrier) -{ - int rc; - uint64_t threshold; - - if (barrier == NULL) - return EINVAL; - - barrier_lock(barrier); - if (barrier->magic != barrier_magic) { - rc = EINVAL; - goto out; - } - /* Compute the release `threshold`. All threads entering with count = 5 - * and `nentered` in [0, 4] should be released once `nentered` reaches 5: - * call 5 the release `threshold`. All threads entering with count = 5 - * and `nentered` in [5, 9] should be released once `nentered` reaches 10. - */ - threshold = (barrier->nentered / barrier->count + 1) * barrier->count; - barrier->nentered++; - while (barrier->nentered < threshold) { - if ((rc = pthread_cond_wait(&barrier->cv, &barrier->mtx)) != 0) - goto out; - } - rc = pthread_cond_broadcast(&barrier->cv); -out: - barrier_unlock(barrier); - return rc; -} - -#endif /* H5_HAVE_DARWIN */ - -static void my_err(int, const char *, ...) H5_ATTR_FORMAT(printf, 2, 3); - -static void -my_err(int code, const char *fmt, ...) -{ - va_list ap; - int errno_copy = errno; - - (void)fprintf(stderr, "thread_id: "); - va_start(ap, fmt); - (void)vfprintf(stderr, fmt, ap); - va_end(ap); - (void)fprintf(stderr, ": %s\n", strerror(errno_copy)); - exit(code); -} - -#define threads_failure(_call, _result) \ - do { \ - my_errx(EXIT_FAILURE, "%s.%d: " #_call ": %s", __func__, __LINE__, strerror(_result)); \ - } while (false) - -#define NTHREADS 5 - -static volatile bool failed = false; -static pthread_barrier_t barrier; -static bool used[NTHREADS]; -static pthread_mutex_t used_lock; - -static void -atomic_printf(const char *fmt, ...) -{ - char buf[80]; - va_list ap; - ssize_t nprinted, nwritten; - - va_start(ap, fmt); - nprinted = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - if (nprinted == -1) - my_err(EXIT_FAILURE, "%s.%d: vsnprintf", __func__, __LINE__); - else if (nprinted >= (ssize_t)sizeof(buf)) - my_errx(EXIT_FAILURE, "%s.%d: vsnprintf overflowed", __func__, __LINE__); - - nwritten = HDwrite(STDOUT_FILENO, buf, (size_t)nprinted); - if (nwritten < nprinted) { - my_errx(EXIT_FAILURE, "%s.%d: write error or short write", __func__, __LINE__); - } -} - -/* Each thread runs this routine. The routine fetches the current - * thread's ID, makes sure that it is in the expected range, makes - * sure that in this round of testing, no two threads shared the - * same ID, and checks that each thread's ID is constant over its lifetime. - * - * main() checks that every ID in [1, NTHREADS] is used in each round - * of testing. All NTHREADS threads synchronize on a barrier after each - * has fetched its ID. The barrier guarantees that all threads' lifetimes - * overlap at least momentarily, so the IDs will be unique, and there - * will be NTHREADS of them. Further, since thread IDs are assigned - * starting with 1, and the number of threads with IDs alive never exceeds - * NTHREADS, the least ID has to be 1 and the greatest, NTHREADS. - */ -static void * -thread_main(void H5_ATTR_UNUSED *arg) -{ - uint64_t ntid, tid; - - tid = H5TS_thread_id(); - - if (tid < 1 || NTHREADS < tid) { - atomic_printf("unexpected tid %" PRIu64 " FAIL\n", tid); - goto pre_barrier_error; - } - pthread_mutex_lock(&used_lock); - if (used[tid - 1]) { - atomic_printf("reused tid %" PRIu64 " FAIL\n", tid); - pthread_mutex_unlock(&used_lock); - goto pre_barrier_error; - } - used[tid - 1] = true; - pthread_mutex_unlock(&used_lock); - - atomic_printf("tid %" PRIu64 " in [1, %d] PASS\n", tid, NTHREADS); - pthread_barrier_wait(&barrier); - - ntid = H5TS_thread_id(); - if (ntid != tid) { - atomic_printf("tid changed from %" PRIu64 " to %" PRIu64 " FAIL\n", tid, ntid); - failed = true; - } - return NULL; -pre_barrier_error: - pthread_barrier_wait(&barrier); - failed = true; - return NULL; -} - -int -main(void) -{ - int i, rc, times; - pthread_t threads[NTHREADS]; - - /* Run H5open() to initialize the library's thread-ID freelist, - * mutex, etc. - */ - if (H5open() != SUCCEED) - my_errx(EXIT_FAILURE, "%s.%d: H5open failed", __func__, __LINE__); - - if ((rc = pthread_mutex_init(&used_lock, NULL)) == -1) - threads_failure(pthread_mutex_init, rc); - - if ((rc = pthread_barrier_init(&barrier, NULL, NTHREADS)) != 0) - threads_failure(pthread_barrier_init, rc); - - /* Start the test threads and join them twice to make sure that - * the thread IDs are recycled in the second round. - */ - for (times = 0; times < 2; times++) { - - for (i = 0; i < NTHREADS; i++) - used[i] = false; // access synchronized by thread create/join - - for (i = 0; i < NTHREADS; i++) { - rc = pthread_create(&threads[i], NULL, thread_main, NULL); - if (rc != 0) - threads_failure(pthread_create, rc); - } - - for (i = 0; i < NTHREADS; i++) { - rc = pthread_join(threads[i], NULL); - if (rc != 0) - threads_failure(pthread_join, rc); - } - - for (i = 0; i < NTHREADS; i++) { - if (!used[i]) // access synchronized by thread create/join - my_errx(EXIT_FAILURE, "thread ID %d did not run.", i + 1); - } - } - if ((rc = pthread_barrier_destroy(&barrier)) != 0) - threads_failure(pthread_barrier_destroy, rc); - return failed ? EXIT_FAILURE : EXIT_SUCCESS; -} - -#else /*H5_HAVE_THREADSAFE && !H5_HAVE_WIN_THREADS*/ -int -main(void) -{ - fprintf(stderr, "not implemented in this configuration.\n"); - return EXIT_SUCCESS; -} -#endif /*H5_HAVE_THREADSAFE && !H5_HAVE_WIN_THREADS*/ diff --git a/test/ttsafe.c b/test/ttsafe.c index 6fe14fcc390..08022eb1992 100644 --- a/test/ttsafe.c +++ b/test/ttsafe.c @@ -99,9 +99,52 @@ main(int argc, char *argv[]) /* Initialize testing framework */ TestInit(argv[0], NULL, NULL); +#ifdef H5_HAVE_THREADS + MESSAGE(2, ("\nConcurrency Configuration:\n")); + MESSAGE(2, ("\tThreading enabled, using ")); +#ifdef H5_HAVE_C11_THREADS + MESSAGE(2, ("C11 threads\n")); +#else +#ifdef H5_HAVE_WIN_THREADS + MESSAGE(2, ("Windows threads\n")); +#else + MESSAGE(2, ("pthreads\n")); +#endif +#endif +#ifdef H5_HAVE_STDATOMIC_H + MESSAGE(2, ("\tC11 atomics enabled\n")); +#endif +#ifdef H5_HAVE_THREADSAFE + MESSAGE(2, ("\tThreadsafe API enabled\n")); +#endif +#endif + /* Tests are generally arranged from least to most complexity... */ AddTest("is_threadsafe", tts_is_threadsafe, NULL, "library threadsafe status", NULL); +#ifdef H5_HAVE_THREADS + AddTest("thread_pool", tts_thread_pool, NULL, "thread pools", NULL); +#ifndef H5_HAVE_STDATOMIC_H + /* C11 atomics only tested when emulated */ + AddTest("atomics", tts_atomics, NULL, "emulation of C11 atomics", NULL); +#endif /* H5_HAVE_STDATOMIC_H */ +#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", + NULL); + AddTest("rec_rwlock_2", tts_rec_rw_lock_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, + "recursive R/W lock smoke check 3 -- mob of writers", NULL); + AddTest("rec_rwlock_4", tts_rec_rw_lock_smoke_check_4, NULL, + "recursive R/W lock smoke check 4 -- mixed mob", NULL); +#endif /* !H5_HAVE_WIN_THREADS */ +#ifdef H5_HAVE_STDATOMIC_H + AddTest("semaphore", tts_semaphore, NULL, "lightweight system semaphores", NULL); +#endif /* H5_HAVE_STDATOMIC_H */ + #ifdef H5_HAVE_THREADSAFE + AddTest("thread_id", tts_thread_id, NULL, "thread IDs", NULL); + AddTest("dcreate", tts_dcreate, cleanup_dcreate, "multi-dataset creation", NULL); AddTest("error", tts_error, cleanup_error, "per-thread error stacks", NULL); #ifdef H5_HAVE_PTHREAD_H @@ -111,12 +154,21 @@ main(int argc, char *argv[]) AddTest("acreate", tts_acreate, cleanup_acreate, "multi-attribute creation", NULL); AddTest("attr_vlen", tts_attr_vlen, cleanup_attr_vlen, "multi-file-attribute-vlen read", NULL); + /* Developer API routine tests */ + AddTest("developer", tts_develop_api, NULL, "developer API routines", NULL); + #else /* H5_HAVE_THREADSAFE */ printf("Most thread-safety tests skipped because THREADSAFE not enabled\n"); #endif /* H5_HAVE_THREADSAFE */ +#else /* H5_HAVE_THREADS */ + + printf("Most threading tests skipped because THREADS not enabled\n"); + +#endif /* H5_HAVE_THREADS */ + /* Display testing information */ TestInfo(argv[0]); diff --git a/test/ttsafe.h b/test/ttsafe.h index ce92c0173de..d2066782b6b 100644 --- a/test/ttsafe.h +++ b/test/ttsafe.h @@ -23,17 +23,36 @@ */ #include "testhdf5.h" +/* + * This file needs to access private datatypes from the H5TS package. + * This file also needs to access the threadsafety testing code. + */ +#define H5TS_FRIEND /*suppress error about including H5TSpkg */ +#include "H5TSpkg.h" + /* Prototypes for the support routines */ extern char *gen_name(int); /* Prototypes for the test routines */ void tts_is_threadsafe(void); +#ifdef H5_HAVE_THREADS +void tts_thread_pool(void); +void tts_atomics(void); +#ifdef H5_HAVE_STDATOMIC_H +void tts_semaphore(void); +#endif /* H5_HAVE_STDATOMIC_H */ +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); #ifdef H5_HAVE_THREADSAFE void tts_dcreate(void); void tts_error(void); void tts_cancel(void); void tts_acreate(void); void tts_attr_vlen(void); +void tts_thread_id(void); +void tts_develop_api(void); /* Prototypes for the cleanup routines */ void cleanup_dcreate(void); @@ -43,4 +62,5 @@ void cleanup_acreate(void); void cleanup_attr_vlen(void); #endif /* H5_HAVE_THREADSAFE */ +#endif /* H5_HAVE_THREADS */ #endif /* TTSAFE_H */ diff --git a/test/ttsafe_acreate.c b/test/ttsafe_acreate.c index 84e5c6ba9de..b3a83e5690c 100644 --- a/test/ttsafe_acreate.c +++ b/test/ttsafe_acreate.c @@ -35,7 +35,7 @@ #define DATASETNAME "IntData" #define NUM_THREADS 16 -void *tts_acreate_thread(void *); +H5TS_THREAD_RETURN_TYPE tts_acreate_thread(void *); typedef struct acreate_data_struct { hid_t dataset; @@ -102,11 +102,13 @@ tts_acreate(void) attrib_data->datatype = datatype; attrib_data->dataspace = dataspace; attrib_data->current_index = i; - threads[i] = H5TS_create_thread(tts_acreate_thread, NULL, attrib_data); + if (H5TS_thread_create(&threads[i], tts_acreate_thread, attrib_data) < 0) + TestErrPrintf("thread # %d did not start", i); } for (i = 0; i < NUM_THREADS; i++) - H5TS_wait_for_thread(threads[i]); + if (H5TS_thread_join(threads[i], NULL) < 0) + TestErrPrintf("thread %d failed to join", i); /* verify the correctness of the test */ for (i = 0; i < NUM_THREADS; i++) { @@ -136,7 +138,7 @@ tts_acreate(void) CHECK(status, FAIL, "H5Fclose"); } /* end tts_acreate() */ -void * +H5TS_THREAD_RETURN_TYPE tts_acreate_thread(void *client_data) { hid_t attribute = H5I_INVALID_HID; @@ -161,7 +163,8 @@ tts_acreate_thread(void *client_data) CHECK(status, FAIL, "H5Awrite"); status = H5Aclose(attribute); CHECK(status, FAIL, "H5Aclose"); - return NULL; + + return (H5TS_thread_ret_t)0; } /* end tts_acreate_thread() */ void diff --git a/test/ttsafe_atomic.c b/test/ttsafe_atomic.c new file mode 100644 index 00000000000..71046be44af --- /dev/null +++ b/test/ttsafe_atomic.c @@ -0,0 +1,179 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 thread pool routines + * + ********************************************************************/ + +#include "ttsafe.h" + +#ifdef H5_HAVE_THREADS + +#define NUM_THREADS 16 + +static H5TS_atomic_int_t counter_g; + +static H5TS_THREAD_RETURN_TYPE +noop_task(void *_counter) +{ + H5TS_atomic_int_t *counter = (H5TS_atomic_int_t *)_counter; + H5TS_thread_ret_t ret_value = 0; + + VERIFY(H5TS_atomic_load_int(counter), 0, "noop_task"); + + return ret_value; +} + +static H5TS_THREAD_RETURN_TYPE +incr_task(void *_counter) +{ + H5TS_atomic_int_t *counter = (H5TS_atomic_int_t *)_counter; + H5TS_thread_ret_t ret_value = 0; + + H5TS_atomic_fetch_add_int(counter, 1); + + return ret_value; +} + +static H5TS_THREAD_RETURN_TYPE +decr_task(void *_counter) +{ + H5TS_atomic_int_t *counter = (H5TS_atomic_int_t *)_counter; + H5TS_thread_ret_t ret_value = 0; + + H5TS_atomic_fetch_sub_int(counter, 1); + + return ret_value; +} + +/* + ********************************************************************** + * tts_atomics + * + ********************************************************************** + */ +void +tts_atomics(void) +{ + H5TS_pool_t *pool = NULL; + herr_t result; + + /* Initialize the counter */ + H5TS_atomic_init_int(&counter_g, 0); + + /* Sanity checks on bad input */ + result = H5TS_pool_create(NULL, NUM_THREADS); + VERIFY(result, FAIL, "H5TS_pool_create"); + result = H5TS_pool_create(&pool, 0); + VERIFY(result, FAIL, "H5TS_pool_create"); + result = H5TS_pool_add_task(NULL, noop_task, NULL); + VERIFY(result, FAIL, "H5TS_pool_add_task"); + result = H5TS_pool_add_task(pool, NULL, NULL); + VERIFY(result, FAIL, "H5TS_pool_add_task"); + result = H5TS_pool_destroy(NULL); + VERIFY(result, FAIL, "H5TS_pool_destroy"); + + /* Create & destroy empty pool */ + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + /* Create pool, add single 'noop' task, destroy pool */ + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + result = H5TS_pool_add_task(pool, noop_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(H5TS_atomic_load_int(&counter_g), 0, "noop"); + + /* Create pool, add single 'incr' task, destroy pool */ + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + result = H5TS_pool_add_task(pool, incr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(H5TS_atomic_load_int(&counter_g), 1, "single incr"); + + /* Create pool, add pair of 'incr' & 'decr' tasks, destroy pool */ + H5TS_atomic_store_int(&counter_g, 10); + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + result = H5TS_pool_add_task(pool, incr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + + result = H5TS_pool_add_task(pool, decr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(H5TS_atomic_load_int(&counter_g), 10, "incr + decr"); + + /* Create pool, add many 'incr' & 'decr' tasks, destroy pool */ + H5TS_atomic_store_int(&counter_g, 3); + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + for (unsigned u = 0; u < 200; u++) { + result = H5TS_pool_add_task(pool, incr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + } + + for (unsigned u = 0; u < 100; u++) { + result = H5TS_pool_add_task(pool, decr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + } + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(H5TS_atomic_load_int(&counter_g), 103, "200 incr + 100 decr"); + + /* Create pool, add *lots* of 'incr' & 'decr' tasks, destroy pool */ + H5TS_atomic_store_int(&counter_g, 5); + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + for (unsigned u = 0; u < 2 * 1000 * 1000; u++) { + result = H5TS_pool_add_task(pool, incr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + } + + for (unsigned u = 0; u < 1 * 1000 * 1000; u++) { + result = H5TS_pool_add_task(pool, decr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + } + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(H5TS_atomic_load_int(&counter_g), 5 + (1000 * 1000), "2,000,000 incr + 1,000,000 decr"); + + /* Destroy the atomic counter */ + H5TS_atomic_destroy_int(&counter_g); + +} /* end tts_atomics() */ + +#endif /* H5_HAVE_THREADS */ diff --git a/test/ttsafe_attr_vlen.c b/test/ttsafe_attr_vlen.c index bfc2067e19c..913ad0011a9 100644 --- a/test/ttsafe_attr_vlen.c +++ b/test/ttsafe_attr_vlen.c @@ -48,7 +48,7 @@ #define ATTR_NAME "root_attr" #define NUM_THREADS 32 -void *tts_attr_vlen_thread(void *); +H5TS_THREAD_RETURN_TYPE tts_attr_vlen_thread(void *); void tts_attr_vlen(void) @@ -105,18 +105,19 @@ tts_attr_vlen(void) CHECK(ret, H5I_INVALID_HID, "H5Tclose"); /* Start multiple threads and execute tts_attr_vlen_thread() for each thread */ - for (i = 0; i < NUM_THREADS; i++) { - threads[i] = H5TS_create_thread(tts_attr_vlen_thread, NULL, NULL); - } + for (i = 0; i < NUM_THREADS; i++) + if (H5TS_thread_create(&threads[i], tts_attr_vlen_thread, NULL) < 0) + TestErrPrintf("thread # %d did not start", i); /* Wait for the threads to end */ for (i = 0; i < NUM_THREADS; i++) - H5TS_wait_for_thread(threads[i]); + if (H5TS_thread_join(threads[i], NULL) < 0) + TestErrPrintf("thread %d failed to join", i); } /* end tts_attr_vlen() */ /* Start execution for each thread */ -void * +H5TS_THREAD_RETURN_TYPE tts_attr_vlen_thread(void H5_ATTR_UNUSED *client_data) { hid_t fid = H5I_INVALID_HID; /* File ID */ @@ -163,7 +164,7 @@ tts_attr_vlen_thread(void H5_ATTR_UNUSED *client_data) ret = H5Tclose(atid); CHECK(ret, FAIL, "H5Aclose"); - return NULL; + return (H5TS_thread_ret_t)0; } /* end tts_attr_vlen_thread() */ void diff --git a/test/ttsafe_cancel.c b/test/ttsafe_cancel.c index 755df305edc..f2bb4c517a1 100644 --- a/test/ttsafe_cancel.c +++ b/test/ttsafe_cancel.c @@ -17,7 +17,7 @@ * * The main thread spawns a child to perform a series of dataset writes * to a hdf5 file. The main thread and child thread synchronizes within - * a callback function called during a H5Diterate call afterwhich the + * a callback function called during a H5Diterate call after which the * main thread attempts to cancel the child thread. * * The cancellation should only work after the child thread has safely @@ -30,7 +30,7 @@ #include "ttsafe.h" #ifdef H5_HAVE_THREADSAFE -#ifndef H5_HAVE_WIN_THREADS +#ifdef H5_HAVE_PTHREAD_H #define FILENAME "ttsafe_cancel.h5" #define DATASETNAME "commonname" @@ -47,31 +47,19 @@ typedef struct cleanup_struct { hid_t dataspace; } cancel_cleanup_t; -pthread_t childthread; -pthread_mutex_t mutex; -pthread_cond_t cond; +pthread_t childthread; +static H5TS_barrier_t barrier; void tts_cancel(void) { - pthread_attr_t attribute; - hid_t dataset; - int buffer; - int H5_ATTR_NDEBUG_UNUSED ret; - - /* make thread scheduling global */ - ret = pthread_attr_init(&attribute); - assert(ret == 0); -#ifdef H5_HAVE_SYSTEM_SCOPE_THREADS - ret = pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM); - assert(ret == 0); -#endif /* H5_HAVE_SYSTEM_SCOPE_THREADS */ + hid_t dataset; + int buffer; + int ret; - /* Initialize mutex & condition variables */ - ret = pthread_mutex_init(&mutex, NULL); - assert(ret == 0); - ret = pthread_cond_init(&cond, NULL); - assert(ret == 0); + /* Initialize barrier */ + ret = H5TS_barrier_init(&barrier, 2); + CHECK_I(ret, "H5TS_barrier_init"); /* * Create a hdf5 file using H5F_ACC_TRUNC access, default file @@ -79,9 +67,10 @@ tts_cancel(void) */ cancel_file = H5Fcreate(FILENAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); assert(cancel_file >= 0); - ret = pthread_create(&childthread, &attribute, tts_cancel_thread, NULL); + ret = pthread_create(&childthread, NULL, tts_cancel_thread, NULL); + assert(ret == 0); + ret = H5TS_barrier_wait(&barrier); assert(ret == 0); - tts_cancel_barrier(); ret = pthread_cancel(childthread); assert(ret == 0); @@ -98,9 +87,8 @@ tts_cancel(void) ret = H5Fclose(cancel_file); assert(ret >= 0); - /* Destroy the thread attribute */ - ret = pthread_attr_destroy(&attribute); - assert(ret == 0); + ret = H5TS_barrier_destroy(&barrier); + CHECK_I(ret, "H5TS_barrier_destroy"); } /* end tts_cancel() */ void * @@ -177,7 +165,9 @@ tts_cancel_callback(void *elem, hid_t H5_ATTR_UNUSED type_id, unsigned H5_ATTR_U int value = *(int *)elem; herr_t status; - tts_cancel_barrier(); + status = H5TS_barrier_wait(&barrier); + CHECK_I(status, "H5TS_barrier_wait"); + HDsleep(3); if (value != 1) { @@ -208,43 +198,13 @@ cancellation_cleanup(void *arg) CHECK(status, FAIL, "H5Tclose"); status = H5Sclose(cleanup_structure->dataspace); CHECK(status, FAIL, "H5Sclose"); - - /* retained for debugging */ - /* print_func("cancellation noted, cleaning up ... \n"); */ } /* end cancellation_cleanup() */ -/* - * Artificial (and specific to this test) barrier to keep track of whether - * both the main and child threads have reached a point in the program. - */ -void -tts_cancel_barrier(void) -{ - static int count = 2; - int status; - - status = pthread_mutex_lock(&mutex); - VERIFY(status, 0, "pthread_mutex_lock"); - - if (count != 1) { - count--; - status = pthread_cond_wait(&cond, &mutex); - VERIFY(status, 0, "pthread_cond_wait"); - } - else { - status = pthread_cond_signal(&cond); - VERIFY(status, 0, "pthread_cond_signal"); - } - - status = pthread_mutex_unlock(&mutex); - VERIFY(status, 0, "pthread_mutex_unlock"); -} /* end tts_cancel_barrier() */ - void cleanup_cancel(void) { HDunlink(FILENAME); } -#endif /*H5_HAVE_WIN_THREADS*/ +#endif /*H5_HAVE_PTHREAD_H*/ #endif /*H5_HAVE_THREADSAFE*/ diff --git a/test/ttsafe_dcreate.c b/test/ttsafe_dcreate.c index 42c3f6c1276..35ad2efbbf1 100644 --- a/test/ttsafe_dcreate.c +++ b/test/ttsafe_dcreate.c @@ -30,13 +30,13 @@ #define FILENAME "ttsafe_dcreate.h5" #define NUM_THREAD 16 -void *tts_dcreate_creator(void *); +H5TS_THREAD_RETURN_TYPE tts_dcreate_creator(void *); -typedef struct thread_info { +typedef struct thr_info { int id; hid_t file; const char *dsetname; -} thread_info; +} thr_info; /* * Set individual dataset names (rather than generated the names @@ -46,7 +46,7 @@ const char *dsetname[NUM_THREAD] = {"zero", "one", "two", "three", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen"}; -thread_info thread_out[NUM_THREAD]; +thr_info thread_out[NUM_THREAD]; /* ********************************************************************** @@ -60,19 +60,10 @@ tts_dcreate(void) H5TS_thread_t threads[NUM_THREAD]; /* HDF5 data definitions */ - hid_t file = H5I_INVALID_HID; - hid_t dataset = H5I_INVALID_HID; - int datavalue, i; - H5TS_attr_t attribute; - herr_t status; - - /* set pthread attribute to perform global scheduling */ - H5TS_attr_init(&attribute); - - /* set thread scope to system */ -#ifdef H5_HAVE_SYSTEM_SCOPE_THREADS - H5TS_attr_setscope(&attribute, H5TS_SCOPE_SYSTEM); -#endif /* H5_HAVE_SYSTEM_SCOPE_THREADS */ + hid_t file = H5I_INVALID_HID; + hid_t dataset = H5I_INVALID_HID; + int datavalue, i; + herr_t status; /* * Create a hdf5 file using H5F_ACC_TRUNC access, default file @@ -86,11 +77,13 @@ tts_dcreate(void) thread_out[i].id = i; thread_out[i].file = file; thread_out[i].dsetname = dsetname[i]; - threads[i] = H5TS_create_thread(tts_dcreate_creator, NULL, &thread_out[i]); + if (H5TS_thread_create(&threads[i], tts_dcreate_creator, &thread_out[i]) < 0) + TestErrPrintf("thread # %d did not start", i); } for (i = 0; i < NUM_THREAD; i++) - H5TS_wait_for_thread(threads[i]); + if (H5TS_thread_join(threads[i], NULL) < 0) + TestErrPrintf("thread %d failed to join", i); /* compare data to see if it is written correctly */ @@ -122,21 +115,18 @@ tts_dcreate(void) /* close remaining resources */ status = H5Fclose(file); CHECK(status, FAIL, "H5Fclose"); - - /* Destroy the thread attribute */ - H5TS_attr_destroy(&attribute); } /* end tts_dcreate() */ -void * +H5TS_THREAD_RETURN_TYPE tts_dcreate_creator(void *_thread_data) { - hid_t dataspace = H5I_INVALID_HID; - hid_t dataset = H5I_INVALID_HID; - herr_t status; - hsize_t dimsf[1]; /* dataset dimensions */ - struct thread_info thread_data; + hid_t dataspace = H5I_INVALID_HID; + hid_t dataset = H5I_INVALID_HID; + herr_t status; + hsize_t dimsf[1]; /* dataset dimensions */ + struct thr_info thread_data; - memcpy(&thread_data, _thread_data, sizeof(struct thread_info)); + memcpy(&thread_data, _thread_data, sizeof(struct thr_info)); /* define dataspace for dataset */ dimsf[0] = 1; @@ -158,7 +148,7 @@ tts_dcreate_creator(void *_thread_data) status = H5Sclose(dataspace); CHECK(status, FAIL, "H5Sclose"); - return NULL; + return (H5TS_thread_ret_t)0; } /* end tts_dcreate_creator() */ void diff --git a/test/ttsafe_develop.c b/test/ttsafe_develop.c new file mode 100644 index 00000000000..82ee8640ca1 --- /dev/null +++ b/test/ttsafe_develop.c @@ -0,0 +1,165 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 threadsafety developer API routines + * + ********************************************************************/ + +#include "ttsafe.h" + +#ifdef H5_HAVE_THREADSAFE + +typedef struct { + H5TS_barrier_t *barrier; +} tts_develop_api_udata_t; + +/* + ********************************************************************** + * tts_develop_api_thr_1 + * + ********************************************************************** + */ +static H5TS_THREAD_RETURN_TYPE +tts_develop_api_thr_1(void *_udata) +{ + tts_develop_api_udata_t *udata = (tts_develop_api_udata_t *)_udata; + unsigned lock_count = UINT_MAX; + bool acquired = false; + herr_t result; + + /* Acquire the API lock - should acquire it */ + result = H5TSmutex_acquire(1, &acquired); + CHECK_I(result, "H5TSmutex_acquire"); + VERIFY(acquired, true, "H5TSmutex_acquire"); + + result = H5TS_barrier_wait(udata->barrier); + CHECK_I(result, "H5TS_barrier_wait"); + + /* Thread #2 will attempt (unsuccessfully) to acquire the API lock */ + + result = H5TS_barrier_wait(udata->barrier); + CHECK_I(result, "H5TS_barrier_wait"); + + /* Release the API lock */ + result = H5TSmutex_release(&lock_count); + CHECK_I(result, "H5TSmutex_release"); + VERIFY(lock_count, 1, "H5TSmutex_release"); + + return (H5TS_thread_ret_t)0; +} /* end tts_develop_api_thr_1() */ + +/* + ********************************************************************** + * tts_develop_api_thr_2 + * + ********************************************************************** + */ +static H5TS_THREAD_RETURN_TYPE +tts_develop_api_thr_2(void *_udata) +{ + tts_develop_api_udata_t *udata = (tts_develop_api_udata_t *)_udata; + bool acquired = false; + herr_t result; + + /* Thread #1 will acquire the API lock */ + + result = H5TS_barrier_wait(udata->barrier); + CHECK_I(result, "H5TS_barrier_wait"); + + /* Attempt to acquire the API lock - should not acquire it */ + result = H5TSmutex_acquire(1, &acquired); + CHECK_I(result, "H5TSmutex_acquire"); + VERIFY(acquired, false, "H5TSmutex_acquire"); + + result = H5TS_barrier_wait(udata->barrier); + CHECK_I(result, "H5TS_barrier_wait"); + + /* Thread #1 will release the API lock */ + + return (H5TS_thread_ret_t)0; +} /* end tts_develop_api_thr_2() */ + +/* + ********************************************************************** + * tts_develop_api + * + ********************************************************************** + */ +void +tts_develop_api(void) +{ + H5TS_thread_t thread_1, thread_2; + H5TS_barrier_t barrier; + unsigned lock_count = UINT_MAX; + bool acquired = false; + tts_develop_api_udata_t udata; + unsigned api_count_1 = 0, api_count_2 = 0; + herr_t result; + + /* Check that API count increases with each API call */ + result = H5TSmutex_get_attempt_count(&api_count_1); + CHECK_I(result, "H5TSmutex_get_attempt_count"); + + /* No-op API call, to increment the API counter */ + result = H5garbage_collect(); + CHECK_I(result, "H5garbage_collect"); + + result = H5TSmutex_get_attempt_count(&api_count_2); + CHECK_I(result, "H5TSmutex_get_attempt_count"); + + VERIFY(api_count_2, (api_count_1 + 1), "H5TSmutex_get_attempt_count"); + + /* Check H5TSmutex_acquire & H5TSmutex_release in thread callbacks */ + + /* Create the thread barrier for the two threads */ + result = H5TS_barrier_init(&barrier, 2); + CHECK_I(result, "H5TS_barrier_init"); + + /* Create the threads */ + udata.barrier = &barrier; + result = H5TS_thread_create(&thread_1, tts_develop_api_thr_1, &udata); + CHECK_I(result, "H5TS_thread_create"); + result = H5TS_thread_create(&thread_2, tts_develop_api_thr_2, &udata); + CHECK_I(result, "H5TS_thread_create"); + + /* Wait for threads to complete. */ + result = H5TS_thread_join(thread_1, NULL); + CHECK_I(result, "H5TS_thread_join"); + result = H5TS_thread_join(thread_2, NULL); + CHECK_I(result, "H5TS_thread_join"); + + result = H5TS_barrier_destroy(&barrier); + CHECK_I(result, "H5TS_barrier_destroy"); + + /* Test multiple / recursive acquisition of the API lock */ + + /* Acquire the API lock - should acquire it */ + result = H5TSmutex_acquire(1, &acquired); + CHECK_I(result, "H5TSmutex_acquire"); + VERIFY(acquired, true, "H5TSmutex_acquire"); + + /* Acquire the API lock again - should acquire it, since it's the same thread */ + acquired = false; + result = H5TSmutex_acquire(1, &acquired); + CHECK_I(result, "H5TSmutex_acquire"); + VERIFY(acquired, true, "H5TSmutex_acquire"); + + /* Release the API lock */ + result = H5TSmutex_release(&lock_count); + CHECK_I(result, "H5TSmutex_release"); + VERIFY(lock_count, 2, "H5TSmutex_release"); + +} /* end tts_develop_api() */ + +#endif /*H5_HAVE_THREADSAFE*/ diff --git a/test/ttsafe_error.c b/test/ttsafe_error.c index 560a7e1b2f7..95062eaa8d1 100644 --- a/test/ttsafe_error.c +++ b/test/ttsafe_error.c @@ -44,16 +44,16 @@ typedef struct err_num_struct { } err_num_t; /* Global variables */ -hid_t error_file_g = H5I_INVALID_HID; -int error_flag_g = 0; -int error_count_g = 0; -err_num_t expected_g[EXPECTED_ERROR_DEPTH]; -H5TS_mutex_simple_t error_mutex_g; +hid_t error_file_g = H5I_INVALID_HID; +int error_flag_g = 0; +int error_count_g = 0; +err_num_t expected_g[EXPECTED_ERROR_DEPTH]; +H5TS_mutex_t error_mutex_g; /* Prototypes */ -static herr_t error_callback(hid_t, void *); -static herr_t walk_error_callback(unsigned, const H5E_error2_t *, void *); -static void *tts_error_thread(void *); +static herr_t error_callback(hid_t, void *); +static herr_t walk_error_callback(unsigned, const H5E_error2_t *, void *); +static H5TS_THREAD_RETURN_TYPE tts_error_thread(void *); void tts_error(void) @@ -62,7 +62,6 @@ tts_error(void) hid_t vol_id = H5I_INVALID_HID; hid_t dataset = H5I_INVALID_HID; H5TS_thread_t threads[NUM_THREAD]; - H5TS_attr_t attribute; int value, i; herr_t status; @@ -100,16 +99,8 @@ tts_error(void) expected_g[10].maj_num = H5E_LINK; expected_g[10].min_num = H5E_EXISTS; - /* set up mutex for global count of errors */ - H5TS_mutex_init(&error_mutex_g); - - /* make thread scheduling global */ - H5TS_attr_init(&attribute); - -#ifdef H5_HAVE_SYSTEM_SCOPE_THREADS - /* set thread scope to system */ - H5TS_attr_setscope(&attribute, H5TS_SCOPE_SYSTEM); -#endif /* H5_HAVE_SYSTEM_SCOPE_THREADS */ + status = H5TS_mutex_init(&error_mutex_g, H5TS_MUTEX_TYPE_PLAIN); + CHECK_I(status, "H5TS_mutex_init"); def_fapl = H5Pcreate(H5P_FILE_ACCESS); CHECK(def_fapl, H5I_INVALID_HID, "H5Pcreate"); @@ -125,10 +116,12 @@ tts_error(void) CHECK(error_file_g, H5I_INVALID_HID, "H5Fcreate"); for (i = 0; i < NUM_THREAD; i++) - threads[i] = H5TS_create_thread(tts_error_thread, &attribute, NULL); + if (H5TS_thread_create(&threads[i], tts_error_thread, NULL) < 0) + TestErrPrintf("thread # %d did not start", i); for (i = 0; i < NUM_THREAD; i++) - H5TS_wait_for_thread(threads[i]); + if (H5TS_thread_join(threads[i], NULL) < 0) + TestErrPrintf("thread %d failed to join", i); if (error_flag_g) { TestErrPrintf( @@ -160,10 +153,11 @@ tts_error(void) status = H5Idec_ref(vol_id); CHECK(status, FAIL, "H5Idec_ref"); - H5TS_attr_destroy(&attribute); + status = H5TS_mutex_destroy(&error_mutex_g); + CHECK_I(status, "H5TS_mutex_destroy"); } /* end tts_error() */ -static void * +static H5TS_THREAD_RETURN_TYPE tts_error_thread(void H5_ATTR_UNUSED *arg) { hid_t dataspace = H5I_INVALID_HID; @@ -215,15 +209,15 @@ tts_error_thread(void H5_ATTR_UNUSED *arg) status = H5Eset_auto2(H5E_DEFAULT, old_error_cb, old_error_client_data); CHECK(status, FAIL, "H5Eset_auto2"); - return NULL; + return (H5TS_thread_ret_t)0; } /* end tts_error_thread() */ static herr_t error_callback(hid_t H5_ATTR_UNUSED estack_id, void *client_data) { - H5TS_mutex_lock_simple(&error_mutex_g); + H5TS_mutex_lock(&error_mutex_g); error_count_g++; - H5TS_mutex_unlock_simple(&error_mutex_g); + H5TS_mutex_unlock(&error_mutex_g); return H5Ewalk2(H5E_DEFAULT, H5E_WALK_DOWNWARD, walk_error_callback, client_data); } diff --git a/test/ttsafe_rec_rw_lock.c b/test/ttsafe_rec_rw_lock.c new file mode 100644 index 00000000000..f7230af3d9d --- /dev/null +++ b/test/ttsafe_rec_rw_lock.c @@ -0,0 +1,1137 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 recursive R/W lock in the HDF5 library + * ------------------------------------------------------------- + * + * Test the recursive R/W lock in isolation, using a combination of + * error return values and statistics collected by the recursive + * R/W lock to detect any failures. + * + * No file is created. + * + * Multiple threads are created, and allowed to compete for the lock. + * The number of threads, and the number of times they obtain the + * lock depends on the express test level. + * + ********************************************************************/ + +#include "ttsafe.h" + +#ifdef H5_HAVE_THREADS +#ifndef H5_HAVE_WIN_THREADS + +#define MAX_NUM_THREADS 32 +#define MAX_LOCK_CYCLES (1000 * 1000) + +/* structure used to configure test threads in the recursive + * R/W/ lock tests. + */ +/*********************************************************************** + * + * Structure rec_rw_lock_test_udata_t + * + * Arrays of instances of rec_rw_lock_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 + * lock proper. + * + * The fields of the structure are discussed below: + * + * rw_lock: Pointer to the recursive R/W under test. + * + * target_rd_lock_cycles: The number of times the test thread is + * required to obtain and drop the read lock. Note + * that this value restricts the number of initial + * read locks only. Additional recursive locks are + * possible -- see max_recursive_lock_depth below. + * + * target_wr_lock_cycles: The number of times the test thread is + * required to obtain and drop the write lock. Note + * that this value restricts the number of initial + * write locks only. Additional recursive locks are + * possible -- see max_recursive_lock_depth below. + * + * max_recursive_lock_depth: Once a test thread gains a lock, it does + * random recursive leocks and unlocks until it happens + * to drop the lock. The max_recursive_lock_depth + * places an upper bound on the net number of locks. + * Any attempt exceed this limit is converted into + * an unlock. + * + * 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 + * structure (in H5TSprivate.h) for further details. + * + ***********************************************************************/ +typedef struct rec_rw_lock_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; + + /* thread stats fields */ + int64_t read_locks_granted; + int64_t read_locks_released; + int64_t real_read_locks_granted; + int64_t real_read_locks_released; + int64_t write_locks_granted; + int64_t write_locks_released; + int64_t real_write_locks_granted; + int64_t real_write_locks_released; + +} rec_rw_lock_test_udata_t; + +/* + ********************************************************************** + * tts_rw_lock_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. + * Record all operations in the user data for later cross-checking + * with the statistics maintained by the recursive R/W lock. + * + * Note: while the number of read and/or write locks is fixed, the + * number of _recursive_ lock and unlock calls is random, as is the + * order of the read and write locks, if both are enabled. + * + ********************************************************************** + */ +static H5TS_THREAD_RETURN_TYPE +tts_rw_lock_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; + + 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; + + while (rd_locks_remaining > 0 || wr_locks_remaining > 0) { + if (wr_locks_remaining == 0) + read = TRUE; + else if (rd_locks_remaining == 0) + read = FALSE; + else { + if ((rand() % 2) == 0) + read = TRUE; + else + read = FALSE; + } + + if (read) { + result = H5TS__rw_rdlock(rw_lock); + CHECK_I(result, "H5TS__rw_rdlock"); + + udata->read_locks_granted++; + udata->real_read_locks_granted++; + rd_locks_remaining--; + rec_lock_depth = 1; + + 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"); + + rec_lock_depth--; + udata->read_locks_released++; + } + else { + result = H5TS__rw_rdlock(rw_lock); + CHECK_I(result, "H5TS__rw_rdlock"); + + rec_lock_depth++; + udata->read_locks_granted++; + } + } + + udata->real_read_locks_released++; + } + else { + result = H5TS__rw_wrlock(rw_lock); + CHECK_I(result, "H5TS__rw_wrlock"); + + udata->write_locks_granted++; + udata->real_write_locks_granted++; + wr_locks_remaining--; + rec_lock_depth = 1; + + 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"); + + rec_lock_depth--; + udata->write_locks_released++; + } + else { + result = H5TS__rw_wrlock(rw_lock); + CHECK_I(result, "H5TS__rw_wrlock"); + + rec_lock_depth++; + udata->write_locks_granted++; + } + } + + udata->real_write_locks_released++; + } + } + + return (H5TS_thread_ret_t)0; +} /* end tts_rw_lock_smoke_check_test_thread() */ + +/* + ********************************************************************** + * tts_rec_rw_lock_smoke_check_1 + * + * Single thread test to verify basic functionality and error + * rejection of the recursive R/W lock. + * + * 1) Initialize an instance of the recursive R/W lock. + * + * 2) Obtain a read lock. + * + * 3) Drop the read lock. + * + * 4) Verify the expected stats, and then reset them. + * + * 5) Obtain a read lock. + * + * 6) Obtain the read lock a second time. + * + * 7) Drop the read lock. + * + * 8) Drop the read lock a second time. + * + * 9) Verify the expected stats, and then reset them. + * + * 10) Obtain a write lock. + * + * 11) Drop the write lock. + * + * 12) Verify the expected stats, and then reset them. + * + * 13) Obtain a write lock. + * + * 14) Obtain the write lock a second time. + * + * 15) Drop the write lock. + * + * 16) Drop the write lock a second time. + * + * 17) Verify the expected stats, and then reset them. + * + * 18) Obtain a write lock. + * + * 19) Attempt to obtain a read lock -- should fail. + * + * 20) Drop the write lock. + * + * 21) Obtain a read lock. + * + * 22) Attempt to obtain a write lock -- should fail. + * + * 23) Drop the read lock. + * + * 24) Verify the expected stats, and then reset them. + * + * 25) Shut down the recursive R/W lock. + * + ********************************************************************** + */ +void +tts_rec_rw_lock_smoke_check_1(void) +{ + herr_t result; +#if H5TS_ENABLE_REC_RW_LOCK_STATS + H5TS_rw_lock_stats_t stats; +#endif + H5TS_rw_lock_t rec_rw_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"); + + /* 2) Obtain a read lock. */ + result = H5TS__rw_rdlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_rdlock"); + + /* 3) Drop the read lock. */ + result = H5TS__rw_unlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_unlock"); + +#if H5TS_ENABLE_REC_RW_LOCK_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__rw_lock_reset_stats(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_lock_reset_stats"); + + /* clang-format makes this conditional unreadable, so turn it off. */ + /* clang-format off */ + if (stats.read_locks_granted != 1 || + stats.read_locks_released != 1 || + stats.real_read_locks_granted != 1 || + stats.real_read_locks_released != 1 || + stats.max_read_locks != 1 || + stats.max_read_lock_recursion_depth != 1 || + stats.read_locks_delayed != 0 || + stats.write_locks_granted != 0 || + stats.write_locks_released != 0 || + stats.real_write_locks_granted != 0 || + stats.real_write_locks_released != 0 || + stats.max_write_locks != 0 || + stats.max_write_lock_recursion_depth != 0 || + stats.write_locks_delayed != 0 || + stats.max_write_locks_pending != 0 ) { + + TestErrPrintf("Unexpected recursive R/W lock stats -- 1"); + H5TS__rw_lock_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"); + + /* 6) Obtain the read lock a second time. */ + result = H5TS__rw_rdlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_rdlock"); + + /* 7) Drop the read lock. */ + result = H5TS__rw_unlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_unlock"); + + /* 8) Drop the read lock a second time. */ + result = H5TS__rw_unlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_unlock"); + +#if H5TS_ENABLE_REC_RW_LOCK_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__rw_lock_reset_stats(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_lock_reset_stats"); + + /* clang-format makes this conditional unreadable, so turn it off. */ + /* clang-format off */ + if (stats.read_locks_granted != 2 || + stats.read_locks_released != 2 || + stats.real_read_locks_granted != 1 || + stats.real_read_locks_released != 1 || + stats.max_read_locks != 1 || + stats.max_read_lock_recursion_depth != 2 || + stats.read_locks_delayed != 0 || + stats.write_locks_granted != 0 || + stats.write_locks_released != 0 || + stats.real_write_locks_granted != 0 || + stats.real_write_locks_released != 0 || + stats.max_write_locks != 0 || + stats.max_write_lock_recursion_depth != 0 || + stats.write_locks_delayed != 0 || + stats.max_write_locks_pending != 0 ) { + + TestErrPrintf("Unexpected recursive R/W lock stats -- 2"); + H5TS__rw_lock_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"); + + /* 11) Drop the write lock. */ + result = H5TS__rw_unlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_unlock"); + +#if H5TS_ENABLE_REC_RW_LOCK_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__rw_lock_reset_stats(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_lock_reset_stats"); + + /* clang-format makes this conditional unreadable, so turn it off. */ + /* clang-format off */ + if (stats.read_locks_granted != 0 || + stats.read_locks_released != 0 || + stats.real_read_locks_granted != 0 || + stats.real_read_locks_released != 0 || + stats.max_read_locks != 0 || + stats.max_read_lock_recursion_depth != 0 || + stats.read_locks_delayed != 0 || + stats.write_locks_granted != 1 || + stats.write_locks_released != 1 || + stats.real_write_locks_granted != 1 || + stats.real_write_locks_released != 1 || + stats.max_write_locks != 1 || + stats.max_write_lock_recursion_depth != 1 || + stats.write_locks_delayed != 0 || + stats.max_write_locks_pending != 0 ) { + + TestErrPrintf("Unexpected recursive R/W lock stats -- 3"); + H5TS__rw_lock_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"); + + /* 14) Obtain the write lock a second time. */ + result = H5TS__rw_wrlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_wrlock"); + + /* 15) Drop the write lock. */ + result = H5TS__rw_unlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_unlock"); + + /* 16) Drop the write lock a second time. */ + result = H5TS__rw_unlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_unlock"); + +#if H5TS_ENABLE_REC_RW_LOCK_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__rw_lock_reset_stats(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_lock_reset_stats"); + + /* clang-format makes this conditional unreadable, so turn it off. */ + /* clang-format off */ + if (stats.read_locks_granted != 0 || + stats.read_locks_released != 0 || + stats.real_read_locks_granted != 0 || + stats.real_read_locks_released != 0 || + stats.max_read_locks != 0 || + stats.max_read_lock_recursion_depth != 0 || + stats.read_locks_delayed != 0 || + stats.write_locks_granted != 2 || + stats.write_locks_released != 2 || + stats.real_write_locks_granted != 1 || + stats.real_write_locks_released != 1 || + stats.max_write_locks != 1 || + stats.max_write_lock_recursion_depth != 2 || + stats.write_locks_delayed != 0 || + stats.max_write_locks_pending != 0 ) { + + TestErrPrintf("Unexpected recursive R/W lock stats -- 4"); + H5TS__rw_lock_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"); + + /* 19) Attempt to obtain a read lock -- should fail. */ + result = H5TS__rw_rdlock(&rec_rw_lock); + VERIFY(result, FAIL, "H5TS__rw_rdlock"); + + /* 20) Drop the write lock. */ + result = H5TS__rw_unlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_unlock"); + + /* 21) Obtain a read lock. */ + result = H5TS__rw_rdlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_rdlock"); + + /* 22) Attempt to obtain a write lock -- should fail. */ + result = H5TS__rw_wrlock(&rec_rw_lock); + VERIFY(result, FAIL, "H5TS__rw_wrlock"); + + /* 23) Drop the read lock. */ + result = H5TS__rw_unlock(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_unlock"); + +#if H5TS_ENABLE_REC_RW_LOCK_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__rw_lock_reset_stats(&rec_rw_lock); + CHECK_I(result, "H5TS__rw_lock_reset_stats"); + + /* clang-format makes this conditional unreadable, so turn it off. */ + /* clang-format off */ + if (stats.read_locks_granted != 1 || + stats.read_locks_released != 1 || + stats.real_read_locks_granted != 1 || + stats.real_read_locks_released != 1 || + stats.max_read_locks != 1 || + stats.max_read_lock_recursion_depth != 1 || + stats.read_locks_delayed != 0 || + stats.write_locks_granted != 1 || + stats.write_locks_released != 1 || + stats.real_write_locks_granted != 1 || + stats.real_write_locks_released != 1 || + stats.max_write_locks != 1 || + stats.max_write_lock_recursion_depth != 1 || + stats.write_locks_delayed != 0 || + stats.max_write_locks_pending != 0 ) { + + TestErrPrintf("Unexpected recursive R/W lock stats"); + H5TS__rw_lock_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() */ + +/* + ********************************************************************** + * tts_rec_rw_lock_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: + * + * 1) Initialize an instance of the recursive R/W lock. + * + * 2) Setup the user data to be passed to each reader test thread. + * + * 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(). + * + * 4) Wait for all threads to complete. + * + * 5) Examine the user data from the threads, to determine the + * total number of real and recursive read locks and unlocks. + * + * 6) Obtain the stats from the recursive R/W lock, and compare + * with the data gathered above. + * + * 7) Shut down the recursive R/W lock. + * + * The reader threads obtain and drop the read lock a specified + * number of times. Once a reader has a read lock, it does random + * recursive read locks / unlocks until drops the read lock, and then + * repeats the process until the specified number of read locks have + * been acquired and dropped. + * + ********************************************************************** + */ +void +tts_rec_rw_lock_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; +#endif + H5TS_rw_lock_t rec_rw_lock; + +#if H5TS_ENABLE_REC_RW_LOCK_STATS + /* Reset expected stats fields to zero -- we will construct the expected + * stats from the thread udata after completion. + */ + memset(&expected, 0, sizeof(expected)); +#endif + + /* Allocate the udata */ + udata = malloc(sizeof(*udata) * MAX_NUM_THREADS); + if (NULL == udata) { + TestErrPrintf("thread udata allocation failed.\n"); + + /* We can't do anything without the udata, so just return */ + return; + } + + /* Reduce # of threads and test cycles for higher levels of express testing */ + express_test = GetTestExpress(); + if (express_test >= 1) { + num_threads /= 2; + lock_cycles /= 10; + } + if (express_test >= 2) { + num_threads /= 2; + lock_cycles /= 10; + } + if (express_test >= 3) { + num_threads /= 2; + lock_cycles /= 10; + } + + /* 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"); + + /* 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].target_rd_lock_cycles = lock_cycles; + udata[i].max_recursive_lock_depth = 10; + } + +#if H5TS_ENABLE_REC_RW_LOCK_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) + 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 + uint64_t end_time = H5_now_usec(); + uint64_t elap_time = (unsigned long long)(end_time - start_time); + if (verbose) + fprintf(stdout, "elapsed usec: %" PRIu64 ", usec per lock_cycle = %" PRIu64 "\n", elap_time, + (elap_time / (uint64_t)lock_cycles)); +#endif + + /* 5) Examine the user data from the threads, to determine the + * total number of real and recursive read locks and unlocks. + * + * First, tally up the lock entries and exits from the test threads, + * and store this data in the expected recursive R/W/ lock stats.. + * In passing, verify that each thread has done the expected number + * of locks and unlocks. Do these as asserts -- will run checks on + * aggregate data shortly. + */ + + for (i = 0; i < num_threads; i++) { + assert(udata[i].target_rd_lock_cycles == udata[i].real_read_locks_granted); + assert(udata[i].target_rd_lock_cycles == udata[i].real_read_locks_released); + 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 + total_target_rd_lock_cycles += udata[i].target_rd_lock_cycles; + total_target_wr_lock_cycles += udata[i].target_wr_lock_cycles; + + expected.read_locks_granted += udata[i].read_locks_granted; + expected.read_locks_released += udata[i].read_locks_released; + expected.real_read_locks_granted += udata[i].real_read_locks_granted; + expected.real_read_locks_released += udata[i].real_read_locks_released; + expected.write_locks_granted += udata[i].write_locks_granted; + expected.write_locks_released += udata[i].write_locks_released; + expected.real_write_locks_granted += udata[i].real_write_locks_granted; + expected.real_write_locks_released += udata[i].real_write_locks_released; +#endif + } + +#if H5TS_ENABLE_REC_RW_LOCK_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. + */ + if (total_target_rd_lock_cycles != expected.real_read_locks_granted || + total_target_rd_lock_cycles != expected.real_read_locks_released || + total_target_wr_lock_cycles != expected.real_write_locks_granted || + total_target_wr_lock_cycles != expected.real_write_locks_released) + TestErrPrintf("Threads reported unexpected number of locks/unlocks.\n"); + + /* initialize remaining non-zero fields in the expected stats */ + expected.max_read_locks = num_threads; + expected.max_read_lock_recursion_depth = 10; + + /* 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"); + + /* turn off clang-format for readability */ + /* clang-format off */ + if (stats.read_locks_granted != expected.read_locks_granted || + stats.read_locks_released != expected.read_locks_released || + stats.real_read_locks_granted != expected.real_read_locks_granted || + stats.real_read_locks_released != expected.real_read_locks_released || + stats.max_read_locks > expected.max_read_locks || + stats.max_read_locks < 1 || + stats.max_read_lock_recursion_depth > expected.max_read_lock_recursion_depth || + stats.max_read_lock_recursion_depth < 1 || + stats.read_locks_delayed != expected.read_locks_delayed || + stats.write_locks_granted != expected.write_locks_granted || + stats.write_locks_released != expected.write_locks_released || + stats.real_write_locks_granted != expected.real_write_locks_granted || + stats.real_write_locks_released != expected.real_write_locks_released || + stats.max_write_locks != expected.max_write_locks || + stats.max_write_lock_recursion_depth != expected.max_write_lock_recursion_depth || + 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); + } + /* clang-format on */ + + if (verbose) + H5TS__rw_lock_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"); + + /* discard the udata if it exists */ + if (udata) + free(udata); +} /* end tts_rec_rw_lock_smoke_check_2() */ + +/* + ********************************************************************** + * tts_rec_rw_lock_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: + * + * 1) Initialize an instance of the recursive R/W lock. + * + * 2) Setup the user data to be passed to each writer test thread. + * + * 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(). + * + * 4) Wait for all threads to complete. + * + * 5) Examine the user data from the threads, to determine the + * total number of real and recursive read locks and unlock. + * + * 6) Obtain the stats from the recursive R/W lock, and compare + * with the data gathered above. + * + * 7) Shut down the recursive R/W lock. + * + * The writer threads obtain and drop the read lock a specified + * number of times. Once a writeer has a write lock, it does random + * recursive write locks / unlocks until drops the write lock, and then + * repeats the process until the specified number of write locks have + * been acquired and dropped. + * + ********************************************************************** + */ +void +tts_rec_rw_lock_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; +#endif + H5TS_rw_lock_t rec_rw_lock; + +#if H5TS_ENABLE_REC_RW_LOCK_STATS + /* Reset expected stats fields to zero -- we will construct the expected + * stats from the thread udata after completion. + */ + memset(&expected, 0, sizeof(expected)); +#endif + + /* Allocate the udata */ + udata = malloc(sizeof(*udata) * MAX_NUM_THREADS); + if (udata == NULL) { + TestErrPrintf("thread udata allocation failed.\n"); + + /* We can't do anything without the udata, so just return */ + return; + } + + /* Reduce # of threads and test cycles for higher levels of express testing */ + express_test = GetTestExpress(); + if (express_test >= 1) { + num_threads /= 2; + lock_cycles /= 10; + } + if (express_test >= 2) { + num_threads /= 2; + lock_cycles /= 10; + } + if (express_test >= 3) { + num_threads /= 2; + lock_cycles /= 10; + } + + /* 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"); + + /* 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].target_wr_lock_cycles = lock_cycles; + udata[i].max_recursive_lock_depth = 10; + } + +#if H5TS_ENABLE_REC_RW_LOCK_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) + 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 + uint64_t end_time = H5_now_usec(); + uint64_t elap_time = (unsigned long long)(end_time - start_time); + if (verbose) + fprintf(stdout, "elapsed usec: %" PRIu64 ", usec per lock_cycle = %" PRIu64 "\n", elap_time, + (elap_time / (uint64_t)lock_cycles)); +#endif + + /* 5) Examine the user data from the threads, to determine the + * total number of real and recursive read locks and unlock. + * + * First, tally up the lock entries and exits from the test threads, + * and store this data in the expected recursive R/W/ lock stats.. + * In passing, verify that each thread has done the expected number + * of locks and unlocks. Do these as asserts -- will run checks on + * aggregate data shortly. + */ + + for (i = 0; i < num_threads; i++) { + assert(udata[i].target_rd_lock_cycles == udata[i].real_read_locks_granted); + assert(udata[i].target_rd_lock_cycles == udata[i].real_read_locks_released); + 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 + total_target_rd_lock_cycles += udata[i].target_rd_lock_cycles; + total_target_wr_lock_cycles += udata[i].target_wr_lock_cycles; + + expected.read_locks_granted += udata[i].read_locks_granted; + expected.read_locks_released += udata[i].read_locks_released; + expected.real_read_locks_granted += udata[i].real_read_locks_granted; + expected.real_read_locks_released += udata[i].real_read_locks_released; + expected.write_locks_granted += udata[i].write_locks_granted; + expected.write_locks_released += udata[i].write_locks_released; + expected.real_write_locks_granted += udata[i].real_write_locks_granted; + expected.real_write_locks_released += udata[i].real_write_locks_released; +#endif + } + +#if H5TS_ENABLE_REC_RW_LOCK_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. + */ + if (total_target_rd_lock_cycles != expected.real_read_locks_granted || + total_target_rd_lock_cycles != expected.real_read_locks_released || + total_target_wr_lock_cycles != expected.real_write_locks_granted || + total_target_wr_lock_cycles != expected.real_write_locks_released) + TestErrPrintf("Threads reported unexpected number of locks/unlocks.\n"); + + /* initialize remaining non-zero fields in the expected stats */ + expected.max_write_locks = 1; + expected.max_write_lock_recursion_depth = 10; + expected.max_write_locks_pending = num_threads - 1; + + /* 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"); + + /* turn off clang-format for readability */ + /* clang-format off */ + if (stats.read_locks_granted != expected.read_locks_granted || + stats.read_locks_released != expected.read_locks_released || + stats.real_read_locks_granted != expected.real_read_locks_granted || + stats.real_read_locks_released != expected.real_read_locks_released || + stats.max_read_locks != expected.max_read_locks || + stats.max_read_lock_recursion_depth != expected.max_read_lock_recursion_depth || + stats.read_locks_delayed != expected.read_locks_delayed || + stats.write_locks_granted != expected.write_locks_granted || + stats.write_locks_released != expected.write_locks_released || + stats.real_write_locks_granted != expected.real_write_locks_granted || + stats.real_write_locks_released != expected.real_write_locks_released || + stats.max_write_locks != expected.max_write_locks || + stats.max_write_lock_recursion_depth > expected.max_write_lock_recursion_depth || + stats.max_write_lock_recursion_depth < 1 || + 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); + } + /* clang-format on */ + + if (verbose) + H5TS__rw_lock_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"); + + /* discard the udata if it exists */ + if (udata) + free(udata); +} /* end tts_rec_rw_lock_smoke_check_3() */ + +/* + ********************************************************************** + * tts_rec_rw_lock_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: + * + * 1) Initialize an instance of the recursive R/W lock. + * + * 2) Setup the user data to be passed to each writer test thread. + * + * 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(). + * + * 4) Wait for all threads to complete. + * + * 5) Examine the user data from the threads, to determine the + * total number of real and recursive read & write locks and + * unlock. + * + * 6) Obtain the stats from the recursive R/W lock, and compare + * with the data gathered above. + * + * 7) Shut down the recursive R/W lock. + * + * The reader / writer threads obtain and drop the read or write + * locks a specified number of times. Once a thread has a lock, it + * does random recursive locks / unlocks until drops the lock, and then + * repeats the process until the specified number of locks have + * been acquired and dropped. + * + ********************************************************************** + */ +void +tts_rec_rw_lock_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; +#endif + H5TS_rw_lock_t rec_rw_lock; + +#if H5TS_ENABLE_REC_RW_LOCK_STATS + /* Reset expected stats fields to zero -- we will construct the expected + * stats from the thread udata after completion. + */ + memset(&expected, 0, sizeof(expected)); +#endif + + /* Allocate the udata */ + udata = malloc(sizeof(*udata) * MAX_NUM_THREADS); + if (udata == NULL) { + TestErrPrintf("thread udata allocation failed.\n"); + + /* We can't do anything without the udata, so just return */ + return; + } + + /* Reduce # of threads and test cycles for higher levels of express testing */ + express_test = GetTestExpress(); + if (express_test >= 1) { + num_threads /= 2; + lock_cycles /= 10; + } + if (express_test >= 2) { + num_threads /= 2; + lock_cycles /= 10; + } + if (express_test >= 3) { + num_threads /= 2; + lock_cycles /= 10; + } + + /* 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"); + + /* 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].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 + 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) + 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 + uint64_t end_time = H5_now_usec(); + uint64_t elap_time = (unsigned long long)(end_time - start_time); + if (verbose) + fprintf(stdout, "elapsed usec: %" PRIu64 ", usec per lock_cycle = %" PRIu64 "\n", elap_time, + (elap_time / (uint64_t)lock_cycles)); +#endif + + /* 5) Examine the user data from the threads, to determine the + * total number of real and recursive read locks and unlock. + * + * First, tally up the lock entries and exits from the test threads, + * and store this data in the expected recursive R/W/ lock stats.. + * In passing, verify that each thread has done the expected number + * of locks and unlocks. Do these as asserts -- will run checks on + * aggregate data shortly. + */ + + for (i = 0; i < num_threads; i++) { + assert(udata[i].target_rd_lock_cycles == udata[i].real_read_locks_granted); + assert(udata[i].target_rd_lock_cycles == udata[i].real_read_locks_released); + 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 + total_target_rd_lock_cycles += udata[i].target_rd_lock_cycles; + total_target_wr_lock_cycles += udata[i].target_wr_lock_cycles; + + expected.read_locks_granted += udata[i].read_locks_granted; + expected.read_locks_released += udata[i].read_locks_released; + expected.real_read_locks_granted += udata[i].real_read_locks_granted; + expected.real_read_locks_released += udata[i].real_read_locks_released; + expected.write_locks_granted += udata[i].write_locks_granted; + expected.write_locks_released += udata[i].write_locks_released; + expected.real_write_locks_granted += udata[i].real_write_locks_granted; + expected.real_write_locks_released += udata[i].real_write_locks_released; +#endif + } + +#if H5TS_ENABLE_REC_RW_LOCK_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. + */ + if (total_target_rd_lock_cycles != expected.real_read_locks_granted || + total_target_rd_lock_cycles != expected.real_read_locks_released || + total_target_wr_lock_cycles != expected.real_write_locks_granted || + total_target_wr_lock_cycles != expected.real_write_locks_released) + TestErrPrintf("Threads reported unexpected number of locks/unlocks.\n"); + + /* initialize remaining non-zero fields in the expected stats */ + expected.max_read_locks = num_threads; + expected.max_read_lock_recursion_depth = 10; + expected.max_write_locks = 1; + expected.max_write_lock_recursion_depth = 10; + expected.max_write_locks_pending = num_threads - 1; + + /* 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"); + + /* turn off clang-format for readability */ + /* clang-format off */ + if (stats.read_locks_granted != expected.read_locks_granted || + stats.read_locks_released != expected.read_locks_released || + stats.real_read_locks_granted != expected.real_read_locks_granted || + stats.real_read_locks_released != expected.real_read_locks_released || + stats.max_read_locks > expected.max_read_locks || + stats.max_read_locks < 1 || + stats.max_read_lock_recursion_depth > expected.max_read_lock_recursion_depth || + stats.read_locks_delayed < expected.read_locks_delayed || + stats.write_locks_granted != expected.write_locks_granted || + stats.write_locks_released != expected.write_locks_released || + stats.real_write_locks_granted != expected.real_write_locks_granted || + stats.real_write_locks_released != expected.real_write_locks_released || + stats.max_write_locks != expected.max_write_locks || + stats.max_write_lock_recursion_depth > expected.max_write_lock_recursion_depth || + stats.max_write_lock_recursion_depth < 1 || + 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); + } + /* clang-format on */ + + if (verbose) + H5TS__rw_lock_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"); + + /* discard the udata if it exists */ + if (udata) + free(udata); +} /* end tts_rec_rw_lock_smoke_check_4() */ + +#endif /* H5_HAVE_WIN_THREADS */ +#endif /* H5_HAVE_THREADS */ diff --git a/test/ttsafe_semaphore.c b/test/ttsafe_semaphore.c new file mode 100644 index 00000000000..3c042734a8d --- /dev/null +++ b/test/ttsafe_semaphore.c @@ -0,0 +1,268 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 lightweight system-based semaphores + * + ********************************************************************/ + +#include "ttsafe.h" + +#if defined(H5_HAVE_THREADS) && defined(H5_HAVE_STDATOMIC_H) + +#define NUM_PINGPONG (1000 * 1000) +#define NUM_CLIENTSERVER (50 * 1000) + +#define NUM_THREADS 16 + +typedef struct { + H5TS_semaphore_t ping_sem, pong_sem; + unsigned counter; +} pingpong_t; + +typedef struct { + H5TS_semaphore_t ready_sem, work_avail_sem; + unsigned counter; + bool shutdown; +} clientserver_t; + +static H5TS_THREAD_RETURN_TYPE +ping(void *_test_info) +{ + pingpong_t *test_info = (pingpong_t *)_test_info; + herr_t result; + H5TS_thread_ret_t ret_value = 0; + + do { + result = H5TS_semaphore_wait(&test_info->ping_sem); + CHECK_I(result, "H5TS_semaphore_wait"); + + test_info->counter++; + + result = H5TS_semaphore_signal(&test_info->pong_sem); + CHECK_I(result, "H5TS_semaphore_signal"); + } while (test_info->counter < NUM_PINGPONG); + + return ret_value; +} + +static H5TS_THREAD_RETURN_TYPE +pong(void *_test_info) +{ + pingpong_t *test_info = (pingpong_t *)_test_info; + herr_t result; + H5TS_thread_ret_t ret_value = 0; + + do { + result = H5TS_semaphore_wait(&test_info->pong_sem); + CHECK_I(result, "H5TS_semaphore_wait"); + + test_info->counter++; + + result = H5TS_semaphore_signal(&test_info->ping_sem); + CHECK_I(result, "H5TS_semaphore_signal"); + } while (test_info->counter < NUM_PINGPONG); + + return ret_value; +} + +/* + ********************************************************************** + * Ping-pong between two threads, using semaphores + ********************************************************************** + */ +static void +tts_semaphore_pingpong(void) +{ + H5TS_thread_t ping_thread, pong_thread; + pingpong_t test_info; + herr_t result; + + /* Test set up */ + /* NOTE: ping semaphore starts at 1 */ + result = H5TS_semaphore_init(&test_info.ping_sem, 1); + CHECK_I(result, "H5TS_semaphore_init"); + result = H5TS_semaphore_init(&test_info.pong_sem, 0); + CHECK_I(result, "H5TS_semaphore_init"); + test_info.counter = 0; + + /* Start ping & pong threads */ + result = H5TS_thread_create(&ping_thread, ping, &test_info); + CHECK_I(result, "H5TS_thread_create"); + result = H5TS_thread_create(&pong_thread, pong, &test_info); + CHECK_I(result, "H5TS_thread_create"); + + /* Join ping & pong threads */ + result = H5TS_thread_join(ping_thread, NULL); + CHECK_I(result, "H5TS_thread_join"); + result = H5TS_thread_join(pong_thread, NULL); + CHECK_I(result, "H5TS_thread_join"); + + VERIFY(test_info.counter, (NUM_PINGPONG + 1), "ping pong"); + + /* 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"); +} /* end tts_semaphore_pingpong() */ + +static H5TS_THREAD_RETURN_TYPE +client(void *_test_info) +{ + clientserver_t *test_info = (clientserver_t *)_test_info; + herr_t result; + H5TS_thread_ret_t ret_value = 0; + + do { + /* Tell server this client is ready */ + result = H5TS_semaphore_signal(&test_info->ready_sem); + CHECK_I(result, "H5TS_semaphore_signal"); + + /* Wait for work */ + result = H5TS_semaphore_wait(&test_info->work_avail_sem); + CHECK_I(result, "H5TS_semaphore_wait"); + + /* Check for shutdown */ + if (test_info->shutdown) + break; + + /* "work" */ + test_info->counter--; + } while (1); + + return ret_value; +} + +/* + ********************************************************************** + * Many clients, single server + ********************************************************************** + */ +static void +tts_semaphore_clientserver(void) +{ + H5TS_thread_t client_threads[NUM_THREADS]; + clientserver_t test_info[NUM_THREADS]; + unsigned u; + herr_t result; + + for (u = 0; u < NUM_THREADS; u++) { + /* Test set up */ + result = H5TS_semaphore_init(&test_info[u].ready_sem, 0); + CHECK_I(result, "H5TS_semaphore_init"); + result = H5TS_semaphore_init(&test_info[u].work_avail_sem, 0); + CHECK_I(result, "H5TS_semaphore_init"); + test_info[u].counter = 0; + test_info[u].shutdown = false; + + /* Start client thread */ + result = H5TS_thread_create(&client_threads[u], client, &test_info[u]); + CHECK_I(result, "H5TS_thread_create"); + } + + /* Issue "work" to clients */ + for (unsigned v = 0; v < NUM_CLIENTSERVER; v++) + for (u = 0; u < NUM_THREADS; u++) { + /* Wait for client to be ready */ + result = H5TS_semaphore_wait(&test_info[u].ready_sem); + CHECK_I(result, "H5TS_semaphore_signal"); + + /* Set up "work" */ + test_info[u].counter++; + + /* Signal client thread */ + result = H5TS_semaphore_signal(&test_info[u].work_avail_sem); + CHECK_I(result, "H5TS_semaphore_signal"); + } + + /* Tell clients to shut down */ + for (u = 0; u < NUM_THREADS; u++) { + /* Wait for client to be ready */ + result = H5TS_semaphore_wait(&test_info[u].ready_sem); + CHECK_I(result, "H5TS_semaphore_signal"); + + /* Set 'shutdown' flag */ + test_info[u].shutdown = true; + + /* Signal client thread */ + result = H5TS_semaphore_signal(&test_info[u].work_avail_sem); + CHECK_I(result, "H5TS_semaphore_signal"); + } + + /* Wrap up */ + for (u = 0; u < NUM_THREADS; u++) { + /* Join client thread */ + result = H5TS_thread_join(client_threads[u], NULL); + CHECK_I(result, "H5TS_thread_join"); + + /* Verify work counter */ + VERIFY(test_info[u].counter, 0, "client-server"); + + /* Destroy semaphores */ + result = H5TS_semaphore_destroy(&test_info[u].ready_sem); + CHECK_I(result, "H5TS_semaphore_destroy"); + result = H5TS_semaphore_destroy(&test_info[u].work_avail_sem); + CHECK_I(result, "H5TS_semaphore_destroy"); + } +} /* end tts_semaphore_clientserver() */ + +/* + ********************************************************************** + * tts_semaphore + ********************************************************************** + */ +void +tts_semaphore(void) +{ + H5TS_semaphore_t sem; + herr_t result; + + /* Sanity checks on bad input */ + result = H5TS_semaphore_init(NULL, 0); + VERIFY(result, FAIL, "H5TS_semaphore_init"); + result = H5TS_semaphore_signal(NULL); + VERIFY(result, FAIL, "H5TS_semaphore_signal"); + result = H5TS_semaphore_wait(NULL); + VERIFY(result, FAIL, "H5TS_semaphore_wait"); + result = H5TS_semaphore_destroy(NULL); + VERIFY(result, FAIL, "H5TS_semaphore_destroy"); + + /* Create & destroy semaphore */ + result = H5TS_semaphore_init(&sem, 0); + CHECK_I(result, "H5TS_semaphore_init"); + + result = H5TS_semaphore_destroy(&sem); + CHECK_I(result, "H5TS_semaphore_destroy"); + + /* Signal & wait w/same thread */ + result = H5TS_semaphore_init(&sem, 0); + CHECK_I(result, "H5TS_semaphore_init"); + + result = H5TS_semaphore_signal(&sem); + CHECK_I(result, "H5TS_semaphore_signal"); + + result = H5TS_semaphore_wait(&sem); + CHECK_I(result, "H5TS_semaphore_wait"); + + result = H5TS_semaphore_destroy(&sem); + CHECK_I(result, "H5TS_semaphore_destroy"); + + /* Ping-pong test */ + tts_semaphore_pingpong(); + + /* Client-server test */ + tts_semaphore_clientserver(); +} /* end tts_semaphore() */ + +#endif /* defined(H5_HAVE_THREADS) && defined(H5_HAVE_STDATOMIC_H) */ diff --git a/test/ttsafe_thread_id.c b/test/ttsafe_thread_id.c new file mode 100644 index 00000000000..49678645819 --- /dev/null +++ b/test/ttsafe_thread_id.c @@ -0,0 +1,138 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 threadsafety developer API routines + * + ********************************************************************/ + +#include "ttsafe.h" + +#ifdef H5_HAVE_THREADSAFE + +#define CYCLE_COUNT 2 +#define NTHREADS 5 + +static H5TS_barrier_t barrier; +static int times; +static bool used[NTHREADS * CYCLE_COUNT]; +static H5TS_mutex_t used_lock; + +/* Each thread runs this routine. The routine fetches the current + * thread's ID, makes sure that it is in the expected range, makes + * sure that in this round of testing, no two threads shared the + * same ID, and checks that each thread's ID is constant over its lifetime. + * + * main() checks that every ID in the range + * [(times * NTHREADS) + 2, (times * NTHREADS) + NTHREADS + 1] is used in each + * round of testing. All NTHREADS threads synchronize on a barrier after each + * has fetched its ID. The barrier guarantees that all threads' lifetimes + * overlap at least momentarily, so the IDs will be unique, and there + * will be NTHREADS of them. Further, since thread IDs are assigned + * starting with 1 (which the main thread gets), the number of threads with + * IDs alive never exceeds NTHREADS, and thread IDs are never recycled, the + * least ID has to be (times * NTHREADS) + 2 and the greatest, + * (times * NTHREADS) + NTHREADS + 1. + */ +static H5TS_THREAD_RETURN_TYPE +thread_main(void H5_ATTR_UNUSED *arg) +{ + int min_id, max_id; + uint64_t ntid, tid; + + H5TS_thread_id(&tid); + + H5TS_mutex_lock(&used_lock); + min_id = (times * NTHREADS) + 2; + max_id = (times * NTHREADS) + NTHREADS + 1; + + /* Verify that thread ID is in correct range */ + if (tid < (uint64_t)min_id || (uint64_t)max_id < tid) { + TestErrPrintf("unexpected tid %" PRIu64 " FAIL\n", tid); + goto pre_barrier_error; + } + + /* Verify that the thread ID hasn't been re-used */ + if (used[tid - 2]) { + TestErrPrintf("reused tid %" PRIu64 " FAIL\n", tid); + H5TS_mutex_unlock(&used_lock); + goto pre_barrier_error; + } + used[tid - 2] = true; + H5TS_mutex_unlock(&used_lock); + + H5TS_barrier_wait(&barrier); + + /* Verify that the thread ID hasn't changed */ + H5TS_thread_id(&ntid); + if (ntid != tid) + TestErrPrintf("tid changed from %" PRIu64 " to %" PRIu64 " FAIL\n", tid, ntid); + + return (H5TS_thread_ret_t)0; + +pre_barrier_error: + H5TS_barrier_wait(&barrier); + + return (H5TS_thread_ret_t)0; +} + +/* + ********************************************************************** + * tts_thread_id + * + ********************************************************************** + */ +void +tts_thread_id(void) +{ + H5TS_thread_t threads[NTHREADS]; + uint64_t tid; + int i; + herr_t result; + + result = H5TS_mutex_init(&used_lock, H5TS_MUTEX_TYPE_PLAIN); + CHECK_I(result, "H5TS_mutex_lock"); + result = H5TS_barrier_init(&barrier, NTHREADS); + CHECK_I(result, "H5TS_barrier_init"); + + /* Get the thread ID for the main thread, so that the child threads + * always start from a thread ID of 2. + */ + H5TS_thread_id(&tid); + VERIFY(tid, 1, "H5TS_thread_id"); + + /* Start the test threads and join them twice to make sure that + * the thread IDs are recycled in the second round. + */ + memset(used, 0, sizeof(used)); + for (times = 0; times < CYCLE_COUNT; times++) { + for (i = 0; i < NTHREADS; i++) + if (H5TS_thread_create(&threads[i], thread_main, NULL) < 0) + TestErrPrintf("thread %d did not start", i); + + for (i = 0; i < NTHREADS; i++) + if (H5TS_thread_join(threads[i], NULL) < 0) + TestErrPrintf("thread %d failed to join", i); + + /* Access synchronized by thread create/join */ + for (i = 0; i < NTHREADS; i++) { + if (!used[(times * NTHREADS) + i]) + TestErrPrintf("thread ID %d did not run.", i + 1); + } + } + result = H5TS_barrier_destroy(&barrier); + CHECK_I(result, "H5TS_barrier_destroy"); + +} /* end tts_thread_id() */ + +#endif /*H5_HAVE_THREADSAFE*/ diff --git a/test/ttsafe_thread_pool.c b/test/ttsafe_thread_pool.c new file mode 100644 index 00000000000..9dd28360e50 --- /dev/null +++ b/test/ttsafe_thread_pool.c @@ -0,0 +1,210 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 thread pool routines + * + ********************************************************************/ + +#include "ttsafe.h" + +#ifdef H5_HAVE_THREADS + +#define NUM_THREADS 16 + +typedef struct { + H5TS_mutex_t mutex; + int val; +} atomic_counter_t; + +static atomic_counter_t counter_g; + +static H5TS_THREAD_RETURN_TYPE +noop_task(void *_counter) +{ + atomic_counter_t *counter = (atomic_counter_t *)_counter; + herr_t result; + H5TS_thread_ret_t ret_value = 0; + + result = H5TS_mutex_lock(&counter->mutex); + CHECK_I(result, "H5TS_mutex_lock"); + + VERIFY(counter->val, 0, "noop_task"); + + result = H5TS_mutex_unlock(&counter->mutex); + CHECK_I(result, "H5TS_mutex_unlock"); + + return ret_value; +} + +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_mutex_lock(&counter->mutex); + CHECK_I(result, "H5TS_mutex_lock"); + + /* Increment value */ + counter->val++; + + result = H5TS_mutex_unlock(&counter->mutex); + CHECK_I(result, "H5TS_mutex_unlock"); + + return ret_value; +} + +static H5TS_THREAD_RETURN_TYPE +decr_task(void *_counter) +{ + atomic_counter_t *counter = (atomic_counter_t *)_counter; + herr_t result; + H5TS_thread_ret_t ret_value = 0; + + result = H5TS_mutex_lock(&counter->mutex); + CHECK_I(result, "H5TS_mutex_lock"); + + /* Decrement value */ + counter->val--; + + result = H5TS_mutex_unlock(&counter->mutex); + CHECK_I(result, "H5TS_mutex_unlock"); + + return ret_value; +} + +/* + ********************************************************************** + * tts_thread_pool + * + ********************************************************************** + */ +void +tts_thread_pool(void) +{ + H5TS_pool_t *pool = NULL; + herr_t result; + + /* Initialize the counter */ + result = H5TS_mutex_init(&counter_g.mutex, H5TS_MUTEX_TYPE_PLAIN); + CHECK_I(result, "H5TS_mutex_init"); + counter_g.val = 0; + + /* Sanity checks on bad input */ + result = H5TS_pool_create(NULL, NUM_THREADS); + VERIFY(result, FAIL, "H5TS_pool_create"); + result = H5TS_pool_create(&pool, 0); + VERIFY(result, FAIL, "H5TS_pool_create"); + result = H5TS_pool_add_task(NULL, noop_task, NULL); + VERIFY(result, FAIL, "H5TS_pool_add_task"); + result = H5TS_pool_add_task(pool, NULL, NULL); + VERIFY(result, FAIL, "H5TS_pool_add_task"); + result = H5TS_pool_destroy(NULL); + VERIFY(result, FAIL, "H5TS_pool_destroy"); + + /* Create & destroy empty pool */ + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + /* Create pool, add single 'noop' task, destroy pool */ + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + result = H5TS_pool_add_task(pool, noop_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(counter_g.val, 0, "noop"); + + /* Create pool, add single 'incr' task, destroy pool */ + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + result = H5TS_pool_add_task(pool, incr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(counter_g.val, 1, "single incr"); + + /* Create pool, add pair of 'incr' & 'decr' tasks, destroy pool */ + counter_g.val = 10; + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + result = H5TS_pool_add_task(pool, incr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + + result = H5TS_pool_add_task(pool, decr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(counter_g.val, 10, "incr + decr"); + + /* Create pool, add many 'incr' & 'decr' tasks, destroy pool */ + counter_g.val = 3; + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + for (unsigned u = 0; u < 200; u++) { + result = H5TS_pool_add_task(pool, incr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + } + + for (unsigned u = 0; u < 100; u++) { + result = H5TS_pool_add_task(pool, decr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + } + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(counter_g.val, 103, "200 incr + 100 decr"); + + /* Create pool, add *lots* of 'incr' & 'decr' tasks, destroy pool */ + counter_g.val = 5; + result = H5TS_pool_create(&pool, NUM_THREADS); + CHECK_I(result, "H5TS_pool_create"); + + for (unsigned u = 0; u < 2 * 1000 * 1000; u++) { + result = H5TS_pool_add_task(pool, incr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + } + + for (unsigned u = 0; u < 1 * 1000 * 1000; u++) { + result = H5TS_pool_add_task(pool, decr_task, &counter_g); + CHECK_I(result, "H5TS_pool_add_task"); + } + + result = H5TS_pool_destroy(pool); + CHECK_I(result, "H5TS_pool_destroy"); + + VERIFY(counter_g.val, 5 + (1000 * 1000), "2,000,000 incr + 1,000,000 decr"); + + /* Destroy the counter's mutex */ + result = H5TS_mutex_destroy(&counter_g.mutex); + CHECK_I(result, "H5TS_mutex_destroy"); + +} /* end tts_thread_pool() */ + +#endif /*H5_HAVE_THREADS*/