mirror of
https://github.com/HDFGroup/hdf5.git
synced 2026-09-25 04:09:44 +03:00
Refactor threading and other concurrency support (#4469)
Complete overhaul of the concurrency-related aspects of the library (threading, atomics, locking, etc.), adding private routines in the H5TS package to allow internal algorithms to use all of these capabilities. Adds many new features & components in the H5TS package that are equivalent to common concurrency data structures and capabilities: "regular" and recursive mutices, condition variables, semaphores, thread barriers, 'once' support, thread pools, atomic variables, thread-local keys, and spawning & joining internal threads. Now supports C11, pthreads, and Windows threading for all H5TS capabilities, except the recursive readers/writers lock, which is not supported on Windows (because Windows threads don't provide a callback on thread-local variable deletion). The "global" API lock is switched to use a recursive mutex from the H5TS package, instead of its own variant. API context code (H5CX package) and error stacks (H5E package) now use the common thread-local info, instead of their own variants. Subfiling code is switched from using Mercury threading features to the new internal H5TS features. Removes the mercury threading code. Adds a configure option (--enable-threads / HDF5_ENABLE_THREADS), enabled by default, to control whether threading is enabled within the library.
This commit is contained in:
@@ -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
|
||||
|
||||
+6
-4
@@ -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.
|
||||
|
||||
+52
-52
@@ -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)
|
||||
# Subfiling requires thread operations
|
||||
if (NOT Threads_FOUND)
|
||||
message (FATAL_ERROR "Subfiling requires thread operations support")
|
||||
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")
|
||||
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 ()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
+36
-10
@@ -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
|
||||
|
||||
@@ -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
|
||||
#
|
||||
|
||||
@@ -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)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
@@ -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 <curl/curl.h> header file. */
|
||||
#cmakedefine H5_HAVE_CURL_CURL_H @H5_HAVE_CURL_H@
|
||||
|
||||
@@ -261,11 +258,8 @@
|
||||
/* Define to 1 if you have the <pthread.h> 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 <pwd.h> 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@
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -64,23 +64,21 @@ SIMPLE_TEST(timezone = 0);
|
||||
|
||||
#endif /* HAVE_TIMEZONE */
|
||||
|
||||
#ifdef SYSTEM_SCOPE_THREADS
|
||||
#include <stdlib.h>
|
||||
#ifdef PTHREAD_BARRIER
|
||||
#include <pthread.h>
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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@")
|
||||
|
||||
@@ -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
|
||||
|
||||
+185
-163
@@ -2140,8 +2140,182 @@ 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).
|
||||
##
|
||||
## 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.
|
||||
##
|
||||
## 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])
|
||||
|
||||
## Default is to attempt to enable thread support
|
||||
if test "X-$THREADS" = X- ; then
|
||||
THREADS=yes
|
||||
fi
|
||||
|
||||
|
||||
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$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
|
||||
## a library `-lpthread' and their locations might be specified with the
|
||||
## `--with-pthread' command-line switch. The value is an include path
|
||||
## and/or a library path. If the library path is specified then it must
|
||||
## be preceded by a comma.
|
||||
##
|
||||
## The default is to use Pthreads when building with the Autotools, unless
|
||||
## 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 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))
|
||||
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
|
||||
allow_pthreads="no"
|
||||
unset HAVE_PTHREAD
|
||||
AC_DEFINE([HAVE_WIN_THREADS], [yes], [Define to yes if you have win32 threads])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$allow_pthreads" = "yes" ; then
|
||||
case "$withval" in
|
||||
default | yes)
|
||||
AC_CHECK_HEADERS([pthread.h],, [unset HAVE_PTHREAD])
|
||||
if test "x$HAVE_PTHREAD" = "xyes"; then
|
||||
AC_CHECK_LIB([pthread], [pthread_self],, [unset HAVE_PTHREAD])
|
||||
fi
|
||||
;;
|
||||
no)
|
||||
AC_MSG_ERROR([Must use Pthreads with thread safety on non-Windows systems])
|
||||
;;
|
||||
*)
|
||||
case "$withval" in
|
||||
*,*)
|
||||
pthread_inc="`echo $withval | cut -f1 -d,`"
|
||||
pthread_lib="`echo $withval | cut -f2 -d, -s`"
|
||||
;;
|
||||
*)
|
||||
if test -n "$withval"; then
|
||||
pthread_inc="$withval/include"
|
||||
pthread_lib="$withval/lib"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -n "$pthread_inc"; then
|
||||
saved_CPPFLAGS="$CPPFLAGS"
|
||||
saved_AM_CPPFLAGS="$AM_CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS -I$pthread_inc"
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -I$pthread_inc"
|
||||
AC_CHECK_HEADERS([pthread.h],, [CPPFLAGS="$saved_CPPFLAGS"; AM_CPPFLAGS="$saved_AM_CPPFLAGS"; unset HAVE_PTHREAD])
|
||||
else
|
||||
AC_CHECK_HEADERS([pthread.h],, [unset HAVE_PTHREAD])
|
||||
fi
|
||||
|
||||
if test "x$HAVE_PTHREAD" = "xyes"; then
|
||||
if test -n "$pthread_lib"; then
|
||||
saved_LDFLAGS="$LDFLAGS"
|
||||
saved_AM_LDFLAGS="$AM_LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS -L$pthread_lib"
|
||||
AM_LDFLAGS="$AM_LDFLAGS -L$pthread_lib"
|
||||
AC_CHECK_LIB([pthread], [pthread_self],,
|
||||
[LDFLAGS="$saved_LDFLAGS"; AM_LDFLAGS="$saved_AM_LDFLAGS"; unset HAVE_PTHREAD])
|
||||
else
|
||||
AC_CHECK_LIB([pthread], [pthread_self],, [unset HAVE_PTHREAD])
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
## 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 <pthread.h>]])
|
||||
fi
|
||||
|
||||
fi # end of Pthreads checks
|
||||
|
||||
## 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])
|
||||
|
||||
@@ -2195,14 +2369,17 @@ if test "X${ALLOW_UNSUPPORTED}" != "Xyes"; then
|
||||
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)
|
||||
THREADSAFE=yes
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([HAVE_THREADSAFE], [1], [Define if we have thread safe support])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT([error])
|
||||
@@ -2210,128 +2387,6 @@ case "X-$THREADSAFE" in
|
||||
;;
|
||||
esac
|
||||
|
||||
if test "X$THREADSAFE" = "Xyes"; then
|
||||
AC_DEFINE([HAVE_THREADSAFE], [1], [Define if we have thread safe support])
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## Is the Pthreads library present? It has a header file `pthread.h' and
|
||||
## a library `-lpthread' and their locations might be specified with the
|
||||
## `--with-pthread' command-line switch. The value is an include path
|
||||
## and/or a library path. If the library path is specified then it must
|
||||
## be preceded by a comma.
|
||||
##
|
||||
## The default is to use Pthreads when building with the Autotools, unless
|
||||
## we're building w/ MinGW.
|
||||
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 MinGW and you would rather use Pthreads
|
||||
than Win32 threads.])],,
|
||||
[withval=default])
|
||||
|
||||
## 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"
|
||||
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"
|
||||
unset HAVE_PTHREAD
|
||||
AC_DEFINE([HAVE_WIN_THREADS], [1], [Define to 1 if you have win32 threads])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$mingw_use_win32_threads" = "no" ; then
|
||||
case "$withval" in
|
||||
default | yes)
|
||||
AC_CHECK_HEADERS([pthread.h],, [unset HAVE_PTHREAD])
|
||||
if test "x$HAVE_PTHREAD" = "xyes"; then
|
||||
AC_CHECK_LIB([pthread], [pthread_self],, [unset HAVE_PTHREAD])
|
||||
fi
|
||||
;;
|
||||
no)
|
||||
AC_MSG_ERROR([Must use Pthreads with thread safety on non-Windows systems])
|
||||
;;
|
||||
*)
|
||||
case "$withval" in
|
||||
*,*)
|
||||
pthread_inc="`echo $withval | cut -f1 -d,`"
|
||||
pthread_lib="`echo $withval | cut -f2 -d, -s`"
|
||||
;;
|
||||
*)
|
||||
if test -n "$withval"; then
|
||||
pthread_inc="$withval/include"
|
||||
pthread_lib="$withval/lib"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -n "$pthread_inc"; then
|
||||
saved_CPPFLAGS="$CPPFLAGS"
|
||||
saved_AM_CPPFLAGS="$AM_CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS -I$pthread_inc"
|
||||
AM_CPPFLAGS="$AM_CPPFLAGS -I$pthread_inc"
|
||||
AC_CHECK_HEADERS([pthread.h],, [CPPFLAGS="$saved_CPPFLAGS"; AM_CPPFLAGS="$saved_AM_CPPFLAGS"; unset HAVE_PTHREAD])
|
||||
else
|
||||
AC_CHECK_HEADERS([pthread.h],, [unset HAVE_PTHREAD])
|
||||
fi
|
||||
|
||||
if test "x$HAVE_PTHREAD" = "xyes"; then
|
||||
if test -n "$pthread_lib"; then
|
||||
saved_LDFLAGS="$LDFLAGS"
|
||||
saved_AM_LDFLAGS="$AM_LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS -L$pthread_lib"
|
||||
AM_LDFLAGS="$AM_LDFLAGS -L$pthread_lib"
|
||||
AC_CHECK_LIB([pthread], [pthread_self],,
|
||||
[LDFLAGS="$saved_LDFLAGS"; AM_LDFLAGS="$saved_AM_LDFLAGS"; unset HAVE_PTHREAD])
|
||||
else
|
||||
AC_CHECK_LIB([pthread], [pthread_self],, [unset HAVE_PTHREAD])
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
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 <stdlib.h>
|
||||
#include <pthread.h>
|
||||
],[
|
||||
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).])
|
||||
fi
|
||||
|
||||
fi # end of Pthreads checks
|
||||
fi # end of threadsafe processing
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
## 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 <pthread.h>]])
|
||||
AC_CHECK_DECL([pthread_condattr_setclock],
|
||||
[AC_DEFINE([HAVE_PTHREAD_CONDATTR_SETCLOCK], [1],
|
||||
[Define if has pthread_condattr_setclock()])],
|
||||
[],
|
||||
[[#include <pthread.h>]])
|
||||
AC_CHECK_DECL([CLOCK_MONOTONIC_COARSE],
|
||||
[AC_DEFINE([HAVE_CLOCK_MONOTONIC_COARSE], [1],
|
||||
[Define if has CLOCK_MONOTONIC_COARSE])],
|
||||
[],
|
||||
[[#include <time.h>]])
|
||||
|
||||
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"])
|
||||
|
||||
|
||||
## ----------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
+20
-4
@@ -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 "$<$<OR:$<BOOL:${HDF5_ENABLE_THREADSAFE}>,$<BOOL:${HDF5_ENABLE_SUBFILING_VFD}>>:Threads::Threads>"
|
||||
PRIVATE "$<$<OR:$<BOOL:${HDF5_ENABLE_THREADS}>,$<BOOL:${HDF5_ENABLE_SUBFILING_VFD}>>: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
|
||||
"$<$<BOOL:${HDF5_ENABLE_THREADS}>:H5_HAVE_THREADS>"
|
||||
"$<$<BOOL:${HDF5_ENABLE_THREADSAFE}>:H5_HAVE_THREADSAFE>"
|
||||
"$<$<BOOL:${HDF5_ENABLE_TRACE}>:H5_DEBUG_API>" # Enable tracing of the API
|
||||
"$<$<BOOL:${HDF5_ENABLE_DEBUG_APIS}>:${HDF5_DEBUG_APIS}>"
|
||||
|
||||
@@ -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*/
|
||||
|
||||
+2
-58
@@ -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
|
||||
*
|
||||
|
||||
+78
-74
@@ -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");
|
||||
|
||||
+2
-8
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -17,12 +17,6 @@
|
||||
#ifndef H5FDioc_priv_H
|
||||
#define H5FDioc_priv_H
|
||||
|
||||
/********************/
|
||||
/* Standard Headers */
|
||||
/********************/
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
/**************/
|
||||
/* 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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -17,12 +17,6 @@
|
||||
#ifndef H5FDsubfiling_priv_H
|
||||
#define H5FDsubfiling_priv_H
|
||||
|
||||
/********************/
|
||||
/* Standard Headers */
|
||||
/********************/
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
/**************/
|
||||
/* H5 Headers */
|
||||
/**************/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -17,12 +17,11 @@
|
||||
#ifndef H5_SUBFILING_COMMON_H
|
||||
#define H5_SUBFILING_COMMON_H
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#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 */
|
||||
|
||||
@@ -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.
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 <sched.h>
|
||||
#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
|
||||
}
|
||||
@@ -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 <windows.h>
|
||||
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 <pthread.h>
|
||||
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 */
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 <time.h>
|
||||
#elif defined(H5_HAVE_SYS_TIME_H)
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
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 */
|
||||
@@ -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 <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
@@ -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 <windows.h>
|
||||
/* clang-format off */
|
||||
# define HG_THREAD_MUTEX_INITIALIZER {NULL}
|
||||
/* clang-format on */
|
||||
typedef CRITICAL_SECTION hg_thread_mutex_t;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#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 */
|
||||
@@ -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 <stdlib.h>
|
||||
|
||||
/****************/
|
||||
/* 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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*****************/
|
||||
/* 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 <mercury_compiler_attributes.h>
|
||||
|
||||
/* 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 */
|
||||
@@ -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 */
|
||||
+35
-881
@@ -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)
|
||||
H5TSmutex_acquire(unsigned lock_count, bool *acquired)
|
||||
{
|
||||
herr_t ret_value = SUCCEED;
|
||||
|
||||
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);
|
||||
/* 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)
|
||||
} /* 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)
|
||||
{
|
||||
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;
|
||||
|
||||
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 */
|
||||
FUNC_LEAVE_API_NAMECHECK_ONLY(ret_value)
|
||||
} /* end H5TSmutex_acquire() */
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
* Function: H5TSmutex_get_attempt_count
|
||||
@@ -604,30 +97,21 @@ done:
|
||||
* 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 @@ done:
|
||||
* 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 */
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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() */
|
||||
@@ -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 */
|
||||
+196
@@ -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 */
|
||||
+107
@@ -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
|
||||
+3
-3
@@ -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
|
||||
}
|
||||
|
||||
+577
@@ -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 */
|
||||
+227
@@ -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 */
|
||||
+107
@@ -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
|
||||
@@ -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 */
|
||||
+297
@@ -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 */
|
||||
+101
@@ -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
|
||||
+149
@@ -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 */
|
||||
+267
@@ -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 */
|
||||
+294
@@ -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 */
|
||||
+151
@@ -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() */
|
||||
+276
-108
@@ -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"
|
||||
|
||||
#ifdef H5_HAVE_WIN_THREADS
|
||||
|
||||
/* 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;
|
||||
typedef pthread_key_t H5TS_key_t;
|
||||
typedef pthread_once_t H5TS_once_t;
|
||||
|
||||
/* 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)
|
||||
|
||||
#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 */
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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 <semaphore.h>
|
||||
|
||||
/* 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_ */
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
+209
@@ -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 */
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
+79
-72
@@ -53,10 +53,23 @@
|
||||
#include <sys/wait.h>
|
||||
#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 <threads.h>
|
||||
#endif
|
||||
|
||||
/* Pthreads */
|
||||
#if defined(H5_HAVE_PTHREAD_H)
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* C11 atomics */
|
||||
#if defined(H5_HAVE_STDATOMIC_H) && !defined(__cplusplus)
|
||||
#include <stdatomic.h>
|
||||
#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 <winsock2.h> /* For GetUserName() */
|
||||
#include <shlwapi.h> /* For StrStrIA */
|
||||
|
||||
#ifdef H5_HAVE_THREADSAFE
|
||||
#include <process.h> /* 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);
|
||||
/* Local variable for saving cancellation state */
|
||||
#define H5CANCEL_DECL /* */
|
||||
|
||||
/* Disable & restore canceling the thread */
|
||||
#define H5TS_DISABLE_CANCEL /* */
|
||||
#define H5TS_RESTORE_CANCEL /* */
|
||||
#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 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 */
|
||||
|
||||
/* 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();
|
||||
/* No locks (non-threadsafe builds) */
|
||||
#define H5_API_LOCK /* */
|
||||
#define H5_API_UNLOCK /* */
|
||||
|
||||
extern H5_api_t H5_g;
|
||||
#endif /* H5_HAVE_THREADSAFE */
|
||||
|
||||
#else /* H5_HAVE_THREADSAFE */
|
||||
|
||||
/* disable any first thread init mechanism */
|
||||
#define H5_FIRST_THREAD_INIT
|
||||
|
||||
/* disable locks (sequential version) */
|
||||
#define H5_API_LOCK
|
||||
#define H5_API_UNLOCK
|
||||
|
||||
/* disable cancellability (sequential version) */
|
||||
#define H5_API_UNSET_CANCEL
|
||||
#define H5_API_SET_CANCEL
|
||||
|
||||
/* 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); \
|
||||
} \
|
||||
} \
|
||||
|
||||
+3
-13
@@ -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
|
||||
|
||||
@@ -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@
|
||||
|
||||
@@ -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@
|
||||
|
||||
+9
-30
@@ -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};$<$<BOOL:${HDF5_ENABLE_PARALLEL}>:${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 "$<$<BOOL:${HDF5_ENABLE_THREADSAFE}>:Threads::Threads>")
|
||||
endif ()
|
||||
else ()
|
||||
TARGET_C_PROPERTIES (thread_id SHARED)
|
||||
target_link_libraries (thread_id PRIVATE ${HDF5_TEST_LIBSH_TARGET} "$<$<BOOL:${HDF5_ENABLE_THREADSAFE}>: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})
|
||||
|
||||
+4
-4
@@ -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
|
||||
|
||||
|
||||
+9
-3
@@ -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 */
|
||||
|
||||
/* try show the process or thread id in multiple processes cases*/
|
||||
if (H5TS_thread_id(&thread_id) < 0)
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -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*/
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
+19
-59
@@ -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;
|
||||
hid_t dataset;
|
||||
int buffer;
|
||||
int 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 */
|
||||
|
||||
/* 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*/
|
||||
|
||||
+20
-30
@@ -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
|
||||
|
||||
@@ -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*/
|
||||
+20
-26
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -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) */
|
||||
@@ -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*/
|
||||
@@ -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*/
|
||||
Reference in New Issue
Block a user