diff --git a/CMakeLists.txt b/CMakeLists.txt index a2ebfcbec19..c64ebe2c06e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -796,14 +796,6 @@ if (HDF5_ENABLE_SUBFILING_VFD) 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) endif() diff --git a/bin/trace b/bin/trace index 5941f3a89a8..b70d2168cd6 100755 --- a/bin/trace +++ b/bin/trace @@ -509,7 +509,7 @@ for $file (@ARGV) { $file_args = 0; # Ignore some files that do not need tracing macros - unless ($file eq "H5FDmulti.c" or $file eq "src/H5FDmulti.c" or $file eq "H5FDstdio.c" or $file eq "src/H5FDstdio.c" or $file eq "src/H5TS.c" or $file eq "src/H5FDperform.c") { + unless ($file eq "H5FDmulti.c" or $file eq "src/H5FDmulti.c" or $file eq "H5FDstdio.c" or $file eq "src/H5FDstdio.c" or $file eq "src/H5TS.c" or $file eq "src/H5TSmutex.c" or $file eq "src/H5FDperform.c") { # Snarf up the entire file open SOURCE, $file or die "$file: $!\n"; diff --git a/config/clang-warnings/error-general b/config/clang-warnings/error-general index 883dff76f7a..52f8ac865e3 100644 --- a/config/clang-warnings/error-general +++ b/config/clang-warnings/error-general @@ -57,8 +57,6 @@ # Here is a list of tests and examples that have issues with the stricter warnings as error # # NOTE: Test files are not compatible with these warnings as errors -# thread_id.c, -# -Werror=unused-function # dsets.c # -Werror=unused-parameter # diff --git a/config/cmake/ConfigureChecks.cmake b/config/cmake/ConfigureChecks.cmake index 668739e7074..928f22ec825 100644 --- a/config/cmake/ConfigureChecks.cmake +++ b/config/cmake/ConfigureChecks.cmake @@ -433,6 +433,8 @@ endif () if (MINGW OR NOT WINDOWS) foreach (other_test HAVE_ATTRIBUTE + HAVE_BUILTIN_EXPECT + PTHREAD_BARRIER SYSTEM_SCOPE_THREADS HAVE_SOCKLEN_T ) @@ -642,12 +644,6 @@ if (MINGW OR NOT WINDOWS) endif () endif () -# Check for clock_gettime() CLOCK_MONOTONIC_COARSE -set (CMAKE_EXTRA_INCLUDE_FILES time.h) -check_type_size(CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC_COARSE_SIZE) -if (HAVE_CLOCK_MONOTONIC_COARSE_SIZE) - set (${HDF_PREFIX}_HAVE_CLOCK_MONOTONIC_COARSE 1) -endif () unset (CMAKE_EXTRA_INCLUDE_FILES) #----------------------------------------------------------------------------- diff --git a/config/cmake/H5pubconf.h.in b/config/cmake/H5pubconf.h.in index f835da103f8..267781a6d85 100644 --- a/config/cmake/H5pubconf.h.in +++ b/config/cmake/H5pubconf.h.in @@ -107,9 +107,6 @@ /* Define to 1 if you have the `clock_gettime' function. */ #cmakedefine H5_HAVE_CLOCK_GETTIME @H5_HAVE_CLOCK_GETTIME@ -/* Define to 1 if CLOCK_MONOTONIC_COARSE is available */ -#cmakedefine H5_HAVE_CLOCK_MONOTONIC_COARSE @H5_HAVE_CLOCK_MONOTONIC_COARSE@ - /* Define if the function stack tracing code is to be compiled in */ #cmakedefine H5_HAVE_CODESTACK @H5_HAVE_CODESTACK@ @@ -261,11 +258,8 @@ /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_PTHREAD_H @H5_HAVE_PTHREAD_H@ -/* Define to 1 if 'pthread_condattr_setclock()' is available */ -#cmakedefine H5_HAVE_PTHREAD_CONDATTR_SETCLOCK @H5_HAVE_PTHREAD_CONDATTR_SETCLOCK@ - -/* Define to 1 if PTHREAD_MUTEX_ADAPTIVE_NP is available */ -#cmakedefine H5_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP @H5_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP@ +/* Define to 1 if the compiler supports the pthread_barrier_*() routines */ +#cmakedefine H5_HAVE_PTHREAD_BARRIER @H5_HAVE_PTHREAD_BARRIER@ /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_PWD_H @H5_HAVE_PWD_H@ @@ -334,6 +328,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_SZLIB_H @H5_HAVE_SZLIB_H@ +/* Define to 1 if the compiler supports the __builtin_expect() extension */ +#cmakedefine H5_HAVE_BUILTIN_EXPECT @H5_HAVE_BUILTIN_EXPECT@ + #if defined(_WIN32) && !defined(H5_BUILT_AS_DYNAMIC_LIB) /* Not supported on WIN32 platforms with static linking */ /* #undef H5_HAVE_THREADSAFE */ diff --git a/config/cmake/HDF5DeveloperBuild.cmake b/config/cmake/HDF5DeveloperBuild.cmake index f8ccc2f7bef..e71990445bb 100644 --- a/config/cmake/HDF5DeveloperBuild.cmake +++ b/config/cmake/HDF5DeveloperBuild.cmake @@ -174,6 +174,12 @@ if (HDF5_ENABLE_DEBUG_H5FS_ASSERT) list (APPEND HDF5_DEBUG_APIS H5FS_DEBUG_ASSERT) endif () +option (HDF5_ENABLE_DEBUG_H5TS "Enable debugging of H5TS module" OFF) +mark_as_advanced (HDF5_ENABLE_DEBUG_H5TS) +if (HDF5_ENABLE_DEBUG_H5TS) + list (APPEND HDF5_DEBUG_APIS H5TS_DEBUG) +endif () + # If HDF5 free list debugging wasn't specifically enabled, disable # free lists entirely for developer build modes, as they can # make certain types of issues (like references to stale pointers) diff --git a/config/cmake/HDFTests.c b/config/cmake/HDFTests.c index 3d16721346c..90c03a3992d 100644 --- a/config/cmake/HDFTests.c +++ b/config/cmake/HDFTests.c @@ -15,6 +15,21 @@ #define SIMPLE_TEST(x) int main(void){ x; return 0; } +#ifdef HAVE_BUILTIN_EXPECT + +int +main () +{ + void *ptr = (void*) 0; + + if (__builtin_expect (ptr != (void*) 0, 1)) + return 0; + + return 0; +} + +#endif /* HAVE_BUILTIN_EXPECT */ + #ifdef HAVE_ATTRIBUTE int @@ -37,6 +52,22 @@ SIMPLE_TEST(timezone = 0); #endif /* HAVE_TIMEZONE */ +#ifdef PTHREAD_BARRIER +#include + +int main(void) +{ + pthread_barrier_t barr; + int ret; + + ret = pthread_barrier_init(&barr, NULL, 1); + if (ret == 0) + return 0; + return 1; +} + +#endif /* PTHREAD_BARRIER */ + #ifdef SYSTEM_SCOPE_THREADS #include #include diff --git a/config/gnu-warnings/error-general b/config/gnu-warnings/error-general index 73d1dd564cb..406bfd6e6c1 100644 --- a/config/gnu-warnings/error-general +++ b/config/gnu-warnings/error-general @@ -38,8 +38,6 @@ # Here is a list of tests and examples that have issues with the stricter warnings as error # # NOTE: Test files are not compatible with these warnings as errors -# thread_id.c, -# -Werror=unused-function # dsets.c # -Werror=unused-parameter # external.c,perform/sio_engine.c diff --git a/configure.ac b/configure.ac index dff0c4a0afc..2063a221c5d 100644 --- a/configure.ac +++ b/configure.ac @@ -1830,8 +1830,13 @@ 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 thread-safe version of library (requires Pthreads on POSIX +## systems). 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([THREADSAFE]) @@ -1969,6 +1974,15 @@ if test "X$THREADSAFE" = "Xyes"; then ;; 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 ]]) + fi + ## ---------------------------------------------------------------------- ## Check if pthread_attr_setscope(&attribute, PTHREAD_SCOPE_SYSTEM) ## is supported on this system @@ -2126,6 +2140,14 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[int __attribute__((unused)) x]])], AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no])]) +AC_MSG_CHECKING([if compiler supports the __builtin_expect() extension]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[void *ptr = (void*) 0; + if (__builtin_expect (ptr != (void*) 0, 1)) return 0;]])], + [AC_DEFINE([HAVE_BUILTIN_EXPECT], [1], + [Define if supports __builtin_expect() extension]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) + ## ---------------------------------------------------------------------- ## Remove old ways of determining debug/production build. ## These were used in 1.8.x and earlier. We should probably keep these checks @@ -2559,8 +2581,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) @@ -3190,26 +3212,8 @@ if test "X$SUBFILING_VFD" = "Xyes"; then fi # Checks for libraries. - AC_SEARCH_LIBS([shm_open], [rt]) AC_CHECK_LIB([pthread], [pthread_self],[], [echo "Error: Required library pthread not found." && exit 1]) - # Perform various checks for Mercury util code - AC_CHECK_DECL([PTHREAD_MUTEX_ADAPTIVE_NP], - [AC_DEFINE([HAVE_PTHREAD_MUTEX_ADAPTIVE_NP], [1], - [Define if has PTHREAD_MUTEX_ADAPTIVE_NP])], - [], - [[#include ]]) - AC_CHECK_DECL([pthread_condattr_setclock], - [AC_DEFINE([HAVE_PTHREAD_CONDATTR_SETCLOCK], [1], - [Define if has pthread_condattr_setclock()])], - [], - [[#include ]]) - AC_CHECK_DECL([CLOCK_MONOTONIC_COARSE], - [AC_DEFINE([HAVE_CLOCK_MONOTONIC_COARSE], [1], - [Define if has CLOCK_MONOTONIC_COARSE])], - [], - [[#include ]]) - else AC_MSG_RESULT([no]) fi diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt index 4c6367e9611..f51d3933f9b 100644 --- a/release_docs/RELEASE.txt +++ b/release_docs/RELEASE.txt @@ -1118,13 +1118,6 @@ Bug Fixes since HDF5-1.14.0 release of HDF5. The CMake configuration code already avoids installing the executable on the system. - - Fixed a configuration issue that prevented building of the Subfiling VFD on macOS - - Checks were added to the CMake and Autotools code to verify that CLOCK_MONOTONIC_COARSE, - PTHREAD_MUTEX_ADAPTIVE_NP and pthread_condattr_setclock() are available before attempting - to use them in Subfiling VFD-related utility code. Without these checks, attempting - to build the Subfiling VFD on macOS would fail. - - Fixes the ordering of INCLUDES when building with CMake Include directories in the source or build tree should come before other diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a19126c8291..df302f8074f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -640,6 +640,17 @@ IDE_GENERATED_PROPERTIES ("H5T" "${H5T_HDRS}" "${H5T_SOURCES}" ) set (H5TS_SOURCES ${HDF5_SRC_DIR}/H5TS.c + ${HDF5_SRC_DIR}/H5TSbarrier.c + ${HDF5_SRC_DIR}/H5TScond.c + ${HDF5_SRC_DIR}/H5TSexlock.c + ${HDF5_SRC_DIR}/H5TSint.c + ${HDF5_SRC_DIR}/H5TSkey.c + ${HDF5_SRC_DIR}/H5TSmutex.c + ${HDF5_SRC_DIR}/H5TSpthread.c + ${HDF5_SRC_DIR}/H5TSrwlock.c + ${HDF5_SRC_DIR}/H5TStest.c + ${HDF5_SRC_DIR}/H5TSthread.c + ${HDF5_SRC_DIR}/H5TSwin.c ) set (H5TS_HDRS ${HDF5_SRC_DIR}/H5TSdevelop.h @@ -757,6 +768,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 ) @@ -958,6 +970,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 diff --git a/src/H5.c b/src/H5.c index 4b0a4a2f6e9..75a5d1d2e8a 100644 --- a/src/H5.c +++ b/src/H5.c @@ -73,14 +73,9 @@ static int H5__mpi_delete_cb(MPI_Comm comm, int keyval, void *attr_val, int *fla static const unsigned VERS_RELEASE_EXCEPTIONS[] = {0}; static const unsigned VERS_RELEASE_EXCEPTIONS_SIZE = 1; -/* statically initialize block for pthread_once call used in initializing */ -/* the first global mutex */ -#ifdef H5_HAVE_THREADSAFE -H5_api_t H5_g; -#else +/* Library init / term status (global) */ bool H5_libinit_g = false; /* Library hasn't been initialized */ bool H5_libterm_g = false; /* Library isn't being shutdown */ -#endif char H5_lib_vers_info_g[] = H5_VERS_INFO; static bool H5_dont_atexit_g = false; @@ -213,12 +208,13 @@ 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); + (void)atexit(H5TS_term_package); #endif /* H5_HAVE_THREADSAFE && H5_HAVE_WIN_THREADS */ /* Normal library termination code */ @@ -303,13 +299,12 @@ H5_term_library(void) H5E_auto2_t func; #ifdef H5_HAVE_THREADSAFE - /* explicit locking of the API */ - H5_FIRST_THREAD_INIT + /* explicitly lock the API */ 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 */ @@ -1125,7 +1120,7 @@ H5allocate_memory(size_t size, bool clear) { void *ret_value = NULL; - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("*x", "zb", size, clear); if (0 == size) @@ -1136,7 +1131,7 @@ H5allocate_memory(size_t size, bool clear) else ret_value = H5MM_malloc(size); - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5allocate_memory() */ /*------------------------------------------------------------------------- @@ -1168,12 +1163,12 @@ H5resize_memory(void *mem, size_t size) { void *ret_value = NULL; - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("*x", "*xz", mem, size); ret_value = H5MM_realloc(mem, size); - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5resize_memory() */ /*------------------------------------------------------------------------- @@ -1191,13 +1186,13 @@ H5resize_memory(void *mem, size_t size) herr_t H5free_memory(void *mem) { - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE1("e", "*x", mem); /* At this time, it is impossible for this to fail. */ H5MM_xfree(mem); - FUNC_LEAVE_API_NOINIT(SUCCEED) + FUNC_LEAVE_API_REENTER(SUCCEED) } /* end H5free_memory() */ /*------------------------------------------------------------------------- diff --git a/src/H5CS.c b/src/H5CS.c index 3728273ea2a..246ca3c0c4d 100644 --- a/src/H5CS.c +++ b/src/H5CS.c @@ -26,87 +26,13 @@ #include "H5private.h" /* Generic Functions */ #include "H5CSprivate.h" /* Function stack */ +#include "H5CXprivate.h" /* API Contexts */ #include "H5Eprivate.h" /* Error handling */ #ifdef H5_HAVE_CODESTACK #define H5CS_MIN_NSLOTS 16 /* Minimum number of records in an function stack */ -/* A function stack */ -typedef struct H5CS_t { - unsigned nused; /* Number of records currently used in stack */ - unsigned nalloc; /* Number of records current allocated for stack */ - const char **rec; /* Array of function records */ -} H5CS_t; - -#ifdef H5_HAVE_THREADSAFE -/* - * The per-thread function 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. - * - * In order for this macro to work, H5CS_get_my_stack() must be preceded - * by "H5CS_t *fstack =". - */ -static H5CS_t *H5CS__get_stack(void); -#define H5CS_get_my_stack() H5CS__get_stack() -#else /* H5_HAVE_THREADSAFE */ -/* - * The function stack. Eventually we'll have some sort of global table so each - * thread has it's own stack. The stacks will be created on demand when the - * thread first calls H5CS_push(). */ -H5CS_t H5CS_stack_g[1]; -#define H5CS_get_my_stack() (H5CS_stack_g + 0) -#endif /* H5_HAVE_THREADSAFE */ - -#ifdef H5_HAVE_THREADSAFE -/*------------------------------------------------------------------------- - * Function: H5CS__get_stack - * - * Purpose: Support function for H5CS_get_my_stack() to initialize and - * acquire per-thread function stack. - * - * Return: Success: function stack (H5CS_t *) - * - * Failure: NULL - * - *------------------------------------------------------------------------- - */ -static H5CS_t * -H5CS__get_stack(void) -{ - H5CS_t *fstack; - - FUNC_ENTER_PACKAGE_NOERR_NOFS - - fstack = H5TS_get_thread_local_value(H5TS_funcstk_key_g); - if (!fstack) { - /* No associated value with current thread - create one */ -#ifdef H5_HAVE_WIN_THREADS - fstack = (H5CS_t *)LocalAlloc( - LPTR, sizeof(H5CS_t)); /* Win32 has to use LocalAlloc to match the LocalFree in DllMain */ -#else - fstack = - (H5CS_t *)malloc(sizeof(H5CS_t)); /* Don't use H5MM_malloc() here, it causes infinite recursion */ -#endif /* H5_HAVE_WIN_THREADS */ - assert(fstack); - - /* Set the thread-specific info */ - fstack->nused = 0; - fstack->nalloc = 0; - fstack->rec = 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_funcstk_key_g, (void *)fstack); - } /* end if */ - - FUNC_LEAVE_NOAPI_NOFS(fstack) -} /* end H5CS__get_stack() */ -#endif /* H5_HAVE_THREADSAFE */ /*------------------------------------------------------------------------- * Function: H5CS_print_stack @@ -159,7 +85,7 @@ H5CS_print_stack(const H5CS_t *fstack, FILE *stream) herr_t H5CS_push(const char *func_name) { - H5CS_t *fstack = H5CS_get_my_stack(); /* Current function stack for library */ + H5CS_t *fstack = H5CX_get_fstack(); /* Get function stack from API context */ /* Don't push this function on the function stack... :-) */ FUNC_ENTER_NOAPI_NOERR_NOFS @@ -201,7 +127,7 @@ H5CS_push(const char *func_name) herr_t H5CS_pop(void) { - H5CS_t *fstack = H5CS_get_my_stack(); + H5CS_t *fstack = H5CX_get_fstack(); /* Get function stack from API context */ /* Don't push this function on the function stack... :-) */ FUNC_ENTER_NOAPI_NOERR_NOFS @@ -213,6 +139,15 @@ H5CS_pop(void) /* Pop the function. */ fstack->nused--; + if (0 == fstack->nused) { + /* The function name strings are statically allocated (by the compiler) + * and are not allocated, so there's no need to free them. + */ + free(fstack->rec); + fstack->rec = NULL; + fstack->nalloc = 0; + } /* end if */ + FUNC_LEAVE_NOAPI_NOFS(SUCCEED) } /* end H5CS_pop() */ @@ -228,9 +163,9 @@ H5CS_pop(void) H5CS_t * H5CS_copy_stack(void) { - H5CS_t *old_stack = H5CS_get_my_stack(); /* Existing function stack for library */ - H5CS_t *new_stack; /* New function stack, for copy */ - H5CS_t *ret_value = NULL; /* Return value */ + H5CS_t *old_stack = H5CX_get_fstack(); /* Get function stack from API context */ + H5CS_t *new_stack; /* New function stack, for copy */ + H5CS_t *ret_value = NULL; /* Return value */ /* Don't push this function on the function stack... :-) */ FUNC_ENTER_NOAPI_NOFS diff --git a/src/H5CSprivate.h b/src/H5CSprivate.h index 5cdf2d5fdd3..e8bb65b8889 100644 --- a/src/H5CSprivate.h +++ b/src/H5CSprivate.h @@ -19,12 +19,35 @@ /* Private headers needed by this file */ #include "H5private.h" -/* Forward declarations for structure fields */ -struct H5CS_t; -H5_DLL herr_t H5CS_push(const char *func_name); -H5_DLL herr_t H5CS_pop(void); -H5_DLL herr_t H5CS_print_stack(const struct H5CS_t *stack, FILE *stream); -H5_DLL struct H5CS_t *H5CS_copy_stack(void); -H5_DLL herr_t H5CS_close_stack(struct H5CS_t *stack); +/**************************/ +/* Library Private Macros */ +/**************************/ + + +/****************************/ +/* Library Private Typedefs */ +/****************************/ + +/* A function stack */ +typedef struct H5CS_t { + unsigned nused; /* Number of records currently used in stack */ + unsigned nalloc; /* Number of records current allocated for stack */ + const char **rec; /* Array of function records */ +} H5CS_t; + + +/*****************************/ +/* Library-private Variables */ +/*****************************/ + + +/***************************************/ +/* Library-private Function Prototypes */ +/***************************************/ +H5_DLL herr_t H5CS_push(const char *func_name); +H5_DLL herr_t H5CS_pop(void); +H5_DLL herr_t H5CS_print_stack(const H5CS_t *stack, FILE *stream); +H5_DLL H5CS_t *H5CS_copy_stack(void); +H5_DLL herr_t H5CS_close_stack(H5CS_t *stack); #endif /* H5CSprivate_H */ diff --git a/src/H5CX.c b/src/H5CX.c index 0c6c8733703..a9a50b50340 100644 --- a/src/H5CX.c +++ b/src/H5CX.c @@ -50,7 +50,7 @@ * 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. @@ -199,6 +199,11 @@ typedef struct H5CX_t { /* Internal: Metadata cache info */ H5AC_ring_t ring; /* Current metadata cache ring for entries */ +#ifdef H5_HAVE_CODESTACK + /* Internal: Function stack info */ + H5CS_t fstack; /* Current function stack for an API operation */ +#endif + #ifdef H5_HAVE_PARALLEL /* Internal: Parallel I/O settings */ bool coll_metadata_read; /* Whether to use collective I/O for metadata read */ @@ -427,9 +432,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); @@ -710,55 +712,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 * @@ -1726,6 +1679,32 @@ H5CX_get_ring(void) FUNC_LEAVE_NOAPI(ring) } /* end H5CX_get_ring() */ +#ifdef H5_HAVE_CODESTACK +/*------------------------------------------------------------------------- + * Function: H5CX_get_fstack + * + * Purpose: Retrieves the function stack for the current API call context. + * + * Return: Non-NULL on success / NULL on failure + * + *------------------------------------------------------------------------- + */ +H5CS_t * +H5CX_get_fstack(void) +{ + H5CX_node_t **head = NULL; /* Pointer to head of API context list */ + + FUNC_ENTER_NOAPI_NOERR_NOFS + + /* Sanity check */ + head = H5CX_get_my_context(); /* Get the pointer to the head of the API context, for this thread */ + assert(head && *head); + + /* Return value */ + FUNC_LEAVE_NOAPI_NOFS(&(*head)->ctx.fstack) +} /* end H5CX_get_fstack() */ +#endif + #ifdef H5_HAVE_PARALLEL /*------------------------------------------------------------------------- diff --git a/src/H5CXprivate.h b/src/H5CXprivate.h index 2a49d9ef031..450929d472e 100644 --- a/src/H5CXprivate.h +++ b/src/H5CXprivate.h @@ -19,6 +19,9 @@ /* Private headers needed by this file */ #include "H5private.h" /* Generic Functions */ #include "H5ACprivate.h" /* Metadata cache */ +#ifdef H5_HAVE_CODESTACK +#include "H5CSprivate.h" /* Function stack */ +#endif #ifdef H5_HAVE_PARALLEL #include "H5FDprivate.h" /* File drivers */ #endif /* H5_HAVE_PARALLEL */ @@ -87,6 +90,9 @@ H5_DLL herr_t H5CX_get_vol_wrap_ctx(void **wrap_ctx); H5_DLL herr_t H5CX_get_vol_connector_prop(H5VL_connector_prop_t *vol_connector_prop); H5_DLL haddr_t H5CX_get_tag(void); H5_DLL H5AC_ring_t H5CX_get_ring(void); +#ifdef H5_HAVE_CODESTACK +H5_DLL H5CS_t *H5CX_get_fstack(void); +#endif #ifdef H5_HAVE_PARALLEL H5_DLL bool H5CX_get_coll_metadata_read(void); H5_DLL herr_t H5CX_get_mpi_coll_datatypes(MPI_Datatype *btype, MPI_Datatype *ftype); diff --git a/src/H5E.c b/src/H5E.c index 36f07048e94..c31a16b741a 100644 --- a/src/H5E.c +++ b/src/H5E.c @@ -73,7 +73,6 @@ /* Local Prototypes */ /********************/ /* Static function declarations */ -static herr_t H5E__set_default_auto(H5E_t *stk); static H5E_cls_t *H5E__register_class(const char *cls_name, const char *lib_name, const char *version); static herr_t H5E__unregister_class(H5E_cls_t *cls, void **request); static ssize_t H5E__get_class_name(const H5E_cls_t *cls, char *name, size_t size); @@ -91,6 +90,59 @@ static herr_t H5E__append_stack(H5E_t *dst_estack, const H5E_t *src_stack); /* Package Variables */ /*********************/ +/* Default value to initialize R/W locks */ +static const H5E_t H5E_err_stack_def = { + 0, /* nused */ + { /*slot[] */ + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {H5I_INVALID_HID, H5I_INVALID_HID, H5I_INVALID_HID, 0, NULL, NULL, NULL}, + {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 */ +}; + + /*****************************/ /* Library Private Variables */ /*****************************/ @@ -272,81 +324,17 @@ H5E_term_package(void) * *-------------------------------------------------------------------------- */ -static herr_t +void H5E__set_default_auto(H5E_t *stk) { FUNC_ENTER_PACKAGE_NOERR -#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; + /* Initialize with default error stack */ + memcpy(stk, &H5E_err_stack_def, sizeof(H5E_err_stack_def)); - FUNC_LEAVE_NOAPI(SUCCEED) + FUNC_LEAVE_NOAPI_VOID } /* end H5E__set_default_auto() */ -#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_t * -H5E__get_stack(void) -{ - H5E_t *estack = NULL; - - FUNC_ENTER_PACKAGE_NOERR - - estack = (H5E_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_t *)LocalAlloc(LPTR, sizeof(H5E_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_t *)malloc(sizeof(H5E_t)); -#endif /* H5_HAVE_WIN_THREADS */ - assert(estack); - - /* Set the thread-specific info */ - estack->nused = 0; - 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 * diff --git a/src/H5Epkg.h b/src/H5Epkg.h index 546e389c971..0cdbeb0249e 100644 --- a/src/H5Epkg.h +++ b/src/H5Epkg.h @@ -47,7 +47,7 @@ * In order for this macro to work, H5E__get_my_stack() must be preceded * by "H5E_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. @@ -117,15 +117,13 @@ struct H5E_t { * The current error stack. */ H5_DLLVAR H5E_t H5E_stack_g[1]; -#endif /* H5_HAVE_THREADSAFE */ +#endif /******************************/ /* Package Private Prototypes */ /******************************/ H5_DLL herr_t H5E__term_deprec_interface(void); -#ifdef H5_HAVE_THREADSAFE -H5_DLL H5E_t *H5E__get_stack(void); -#endif /* H5_HAVE_THREADSAFE */ +H5_DLL void H5E__set_default_auto(H5E_t *stk); H5_DLL herr_t H5E__push_stack(H5E_t *estack, const char *file, const char *func, unsigned line, hid_t cls_id, hid_t maj_id, hid_t min_id, const char *desc); H5_DLL ssize_t H5E__get_msg(const H5E_msg_t *msg_ptr, H5E_type_t *type, char *msg, size_t size); diff --git a/src/H5FD.c b/src/H5FD.c index f89fdd93b81..cdefd15431a 100644 --- a/src/H5FD.c +++ b/src/H5FD.c @@ -689,7 +689,7 @@ H5FDopen(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr) { H5FD_t *ret_value = NULL; - FUNC_ENTER_API(NULL) + FUNC_ENTER_API_REENTER H5TRACE4("*#", "*sIuia", name, flags, fapl_id, maxaddr); /* Check arguments */ @@ -703,7 +703,7 @@ H5FDopen(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr) HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "unable to open file"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /*------------------------------------------------------------------------- @@ -825,7 +825,7 @@ H5FDclose(H5FD_t *file) { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE1("e", "*#", file); /* Check arguments */ @@ -839,7 +839,7 @@ H5FDclose(H5FD_t *file) HGOTO_ERROR(H5E_VFL, H5E_CANTCLOSEFILE, FAIL, "unable to close file"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDclose() */ /*------------------------------------------------------------------------- @@ -903,14 +903,13 @@ H5FDcmp(const H5FD_t *f1, const H5FD_t *f2) { int ret_value = -1; - FUNC_ENTER_API(-1) /* return value is arbitrary */ + FUNC_ENTER_API_REENTER H5TRACE2("Is", "*#*#", f1, f2); /* Call private function */ ret_value = H5FD_cmp(f1, f2); -done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDcmp() */ /*------------------------------------------------------------------------- @@ -973,7 +972,7 @@ H5FDquery(const H5FD_t *file, unsigned long *flags /*out*/) { int ret_value = 0; - FUNC_ENTER_API((-1)) + FUNC_ENTER_API_REENTER H5TRACE2("Is", "*#*Ul", file, flags); /* Check arguments */ @@ -989,7 +988,7 @@ H5FDquery(const H5FD_t *file, unsigned long *flags /*out*/) HGOTO_ERROR(H5E_VFL, H5E_CANTGET, (-1), "unable to query feature flags"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /*------------------------------------------------------------------------- @@ -1066,7 +1065,7 @@ H5FDalloc(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, hsize_t size) { haddr_t ret_value = HADDR_UNDEF; - FUNC_ENTER_API(HADDR_UNDEF) + FUNC_ENTER_API_REENTER H5TRACE4("a", "*#Mtih", file, type, dxpl_id, size); /* Check arguments */ @@ -1094,7 +1093,7 @@ H5FDalloc(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, hsize_t size) ret_value += file->base_addr; done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDalloc() */ /*------------------------------------------------------------------------- @@ -1116,7 +1115,7 @@ H5FDfree(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, hsize_t siz { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE5("e", "*#Mtiah", file, type, dxpl_id, addr, size); /* Check arguments */ @@ -1140,7 +1139,7 @@ H5FDfree(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, hsize_t siz HGOTO_ERROR(H5E_VFL, H5E_CANTFREE, FAIL, "file deallocation request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDfree() */ /*------------------------------------------------------------------------- @@ -1159,7 +1158,7 @@ H5FDget_eoa(H5FD_t *file, H5FD_mem_t type) { haddr_t ret_value; - FUNC_ENTER_API(HADDR_UNDEF) + FUNC_ENTER_API_REENTER H5TRACE2("a", "*#Mt", file, type); /* Check arguments */ @@ -1178,7 +1177,7 @@ H5FDget_eoa(H5FD_t *file, H5FD_mem_t type) ret_value += file->base_addr; done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDget_eoa() */ /*------------------------------------------------------------------------- @@ -1207,7 +1206,7 @@ H5FDset_eoa(H5FD_t *file, H5FD_mem_t type, haddr_t addr) { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE3("e", "*#Mta", file, type, addr); /* Check arguments */ @@ -1226,7 +1225,7 @@ H5FDset_eoa(H5FD_t *file, H5FD_mem_t type, haddr_t addr) HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "file set eoa request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDset_eoa() */ /*------------------------------------------------------------------------- @@ -1255,7 +1254,7 @@ H5FDget_eof(H5FD_t *file, H5FD_mem_t type) { haddr_t ret_value; - FUNC_ENTER_API(HADDR_UNDEF) + FUNC_ENTER_API_REENTER H5TRACE2("a", "*#Mt", file, type); /* Check arguments */ @@ -1272,7 +1271,7 @@ H5FDget_eof(H5FD_t *file, H5FD_mem_t type) ret_value += file->base_addr; done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDget_eof() */ /*------------------------------------------------------------------------- @@ -1405,7 +1404,7 @@ H5FDread(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE6("e", "*#Mtiaz*x", file, type, dxpl_id, addr, size, buf); /* Check arguments */ @@ -1431,7 +1430,7 @@ H5FDread(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "file read request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDread() */ /*------------------------------------------------------------------------- @@ -1451,7 +1450,7 @@ H5FDwrite(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t siz { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE6("e", "*#Mtiaz*x", file, type, dxpl_id, addr, size, buf); /* Check arguments */ @@ -1477,7 +1476,7 @@ H5FDwrite(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t siz HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "file write request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDwrite() */ /*------------------------------------------------------------------------- @@ -1507,7 +1506,7 @@ H5FDread_vector(H5FD_t *file, hid_t dxpl_id, uint32_t count, H5FD_mem_t types[], { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE7("e", "*#iIu*Mt*a*z**x", file, dxpl_id, count, types, addrs, sizes, bufs); /* Check arguments */ @@ -1555,7 +1554,7 @@ H5FDread_vector(H5FD_t *file, hid_t dxpl_id, uint32_t count, H5FD_mem_t types[], HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "file vector read request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDread_vector() */ /*------------------------------------------------------------------------- @@ -1583,7 +1582,7 @@ H5FDwrite_vector(H5FD_t *file, hid_t dxpl_id, uint32_t count, H5FD_mem_t types[] { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE7("e", "*#iIu*Mt*a*z**x", file, dxpl_id, count, types, addrs, sizes, bufs); /* Check arguments */ @@ -1629,7 +1628,7 @@ H5FDwrite_vector(H5FD_t *file, hid_t dxpl_id, uint32_t count, H5FD_mem_t types[] HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "file vector write request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDwrite_vector() */ /*------------------------------------------------------------------------- @@ -1676,7 +1675,7 @@ H5FDread_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uint32_t count, { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE9("e", "*#MtiIu*i*i*a*z**x", file, type, dxpl_id, count, mem_space_ids, file_space_ids, offsets, element_sizes, bufs); @@ -1728,7 +1727,7 @@ H5FDread_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uint32_t count, HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "file selection read request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDread_selection() */ /*------------------------------------------------------------------------- @@ -1773,7 +1772,7 @@ H5FDwrite_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uint32_t count { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE9("e", "*#MtiIu*i*i*a*z**x", file, type, dxpl_id, count, mem_space_ids, file_space_ids, offsets, element_sizes, bufs); @@ -1826,7 +1825,7 @@ H5FDwrite_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uint32_t count HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "file selection write request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDwrite_selection() */ /*------------------------------------------------------------------------- @@ -1877,7 +1876,7 @@ H5FDread_vector_from_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uin { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE9("e", "*#MtiIu*i*i*a*z**x", file, type, dxpl_id, count, mem_space_ids, file_space_ids, offsets, element_sizes, bufs); @@ -1926,7 +1925,7 @@ H5FDread_vector_from_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uin HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "file selection read request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDread_vector_from_selection() */ /*------------------------------------------------------------------------- @@ -1975,7 +1974,7 @@ H5FDwrite_vector_from_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, ui { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE9("e", "*#MtiIu*i*i*a*z**x", file, type, dxpl_id, count, mem_space_ids, file_space_ids, offsets, element_sizes, bufs); @@ -2024,7 +2023,7 @@ H5FDwrite_vector_from_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, ui HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "file selection write request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDwrite_vector_from_selection() */ /*------------------------------------------------------------------------- @@ -2073,7 +2072,7 @@ H5FDread_from_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uint32_t c { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE9("e", "*#MtiIu*i*i*a*z**x", file, type, dxpl_id, count, mem_space_ids, file_space_ids, offsets, element_sizes, bufs); @@ -2122,7 +2121,7 @@ H5FDread_from_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uint32_t c HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, "file selection read request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDread_from_selection() */ /*------------------------------------------------------------------------- @@ -2170,7 +2169,7 @@ H5FDwrite_from_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uint32_t { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE9("e", "*#MtiIu*i*i*a*z**x", file, type, dxpl_id, count, mem_space_ids, file_space_ids, offsets, element_sizes, bufs); @@ -2219,7 +2218,7 @@ H5FDwrite_from_selection(H5FD_t *file, H5FD_mem_t type, hid_t dxpl_id, uint32_t HGOTO_ERROR(H5E_VFL, H5E_WRITEERROR, FAIL, "file selection write request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDwrite_from_selection() */ /*------------------------------------------------------------------------- @@ -2237,7 +2236,7 @@ H5FDflush(H5FD_t *file, hid_t dxpl_id, hbool_t closing) { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE3("e", "*#ib", file, dxpl_id, closing); /* Check arguments */ @@ -2259,7 +2258,7 @@ H5FDflush(H5FD_t *file, hid_t dxpl_id, hbool_t closing) HGOTO_ERROR(H5E_VFL, H5E_CANTFLUSH, FAIL, "file flush request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDflush() */ /*------------------------------------------------------------------------- @@ -2304,7 +2303,7 @@ H5FDtruncate(H5FD_t *file, hid_t dxpl_id, hbool_t closing) { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE3("e", "*#ib", file, dxpl_id, closing); /* Check arguments */ @@ -2325,7 +2324,7 @@ H5FDtruncate(H5FD_t *file, hid_t dxpl_id, hbool_t closing) HGOTO_ERROR(H5E_VFL, H5E_CANTUPDATE, FAIL, "file flush request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDtruncate() */ /*------------------------------------------------------------------------- @@ -2370,7 +2369,7 @@ H5FDlock(H5FD_t *file, hbool_t rw) { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE2("e", "*#b", file, rw); /* Check arguments */ @@ -2384,7 +2383,7 @@ H5FDlock(H5FD_t *file, hbool_t rw) HGOTO_ERROR(H5E_VFL, H5E_CANTLOCKFILE, FAIL, "file lock request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDlock() */ /*------------------------------------------------------------------------- @@ -2429,7 +2428,7 @@ H5FDunlock(H5FD_t *file) { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE1("e", "*#", file); /* Check arguments */ @@ -2443,7 +2442,7 @@ H5FDunlock(H5FD_t *file) HGOTO_ERROR(H5E_VFL, H5E_CANTUNLOCKFILE, FAIL, "file unlock request failed"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDunlock() */ /*------------------------------------------------------------------------- @@ -2497,7 +2496,7 @@ H5FDctl(H5FD_t *file, uint64_t op_code, uint64_t flags, const void *input, void { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE5("e", "*#ULUL*x**x", file, op_code, flags, input, output); /* Check arguments */ @@ -2517,9 +2516,7 @@ H5FDctl(H5FD_t *file, uint64_t op_code, uint64_t flags, const void *input, void HGOTO_ERROR(H5E_VFL, H5E_FCNTL, FAIL, "VFD ctl request failed"); done: - - FUNC_LEAVE_API(ret_value) - + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDctl() */ /*------------------------------------------------------------------------- @@ -2616,7 +2613,7 @@ H5FDget_vfd_handle(H5FD_t *file, hid_t fapl_id, void **file_handle /*out*/) { herr_t ret_value = SUCCEED; - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE3("e", "*#i**x", file, fapl_id, file_handle); /* Check arguments */ @@ -2639,7 +2636,7 @@ H5FDget_vfd_handle(H5FD_t *file, hid_t fapl_id, void **file_handle /*out*/) *file_handle = NULL; } - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDget_vfd_handle() */ /*-------------------------------------------------------------------------- @@ -2791,7 +2788,7 @@ H5FDdelete(const char *filename, hid_t fapl_id) { herr_t ret_value = SUCCEED; - FUNC_ENTER_API(FAIL) + FUNC_ENTER_API_REENTER H5TRACE2("e", "*si", filename, fapl_id); /* Check arguments */ @@ -2808,5 +2805,5 @@ H5FDdelete(const char *filename, hid_t fapl_id) HGOTO_ERROR(H5E_VFL, H5E_CANTDELETEFILE, FAIL, "unable to delete file"); done: - FUNC_LEAVE_API(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5FDdelete() */ diff --git a/src/H5FDsubfiling/H5FDioc.h b/src/H5FDsubfiling/H5FDioc.h index bcacd52252d..8f0255c5583 100644 --- a/src/H5FDsubfiling/H5FDioc.h +++ b/src/H5FDsubfiling/H5FDioc.h @@ -174,18 +174,6 @@ H5_DLL herr_t H5Pset_fapl_ioc(hid_t fapl_id, H5FD_ioc_config_t *vfd_config); * */ H5_DLL herr_t H5Pget_fapl_ioc(hid_t fapl_id, H5FD_ioc_config_t *config_out); -/** - * \brief Internal routine for managing exclusive access to critical sections - * by the #H5FD_IOC driver's worker threads. Not meant to be called - * directly by an HDF5 application - */ -H5_DLL void H5FD_ioc_begin_thread_exclusive(void); -/** - * \brief Internal routine for managing exclusive access to critical sections - * by the #H5FD_IOC driver's worker threads. Not meant to be called - * directly by an HDF5 application - */ -H5_DLL void H5FD_ioc_end_thread_exclusive(void); #ifdef __cplusplus } diff --git a/src/H5FDsubfiling/H5FDioc_priv.h b/src/H5FDsubfiling/H5FDioc_priv.h index fbce1a0d0f6..d141111e421 100644 --- a/src/H5FDsubfiling/H5FDioc_priv.h +++ b/src/H5FDsubfiling/H5FDioc_priv.h @@ -35,6 +35,7 @@ #include "H5Iprivate.h" /* IDs */ #include "H5MMprivate.h" /* Memory management */ #include "H5Pprivate.h" /* Property lists */ +#include "H5TSprivate.h" /* Threadsafety */ #include "H5subfiling_common.h" #include "H5subfiling_err.h" @@ -370,7 +371,7 @@ typedef struct ioc_io_queue { int32_t num_failed; int32_t q_len; uint32_t req_counter; - hg_thread_mutex_t q_mutex; + H5TS_mutex_t q_mutex; /* statistics */ #ifdef H5FD_IOC_COLLECT_STATS diff --git a/src/H5FDsubfiling/H5FDioc_threads.c b/src/H5FDsubfiling/H5FDioc_threads.c index 85c2561549d..65147f97438 100644 --- a/src/H5FDsubfiling/H5FDioc_threads.c +++ b/src/H5FDsubfiling/H5FDioc_threads.c @@ -56,7 +56,7 @@ typedef struct ioc_data_t { * use mercury for that purpose... */ -static hg_thread_mutex_t ioc_thread_mutex = PTHREAD_MUTEX_INITIALIZER; +static H5TS_mutex_t ioc_thread_mutex = H5TS_MUTEX_INITIALIZER; #ifdef H5FD_IOC_COLLECT_STATS static int sf_write_ops = 0; @@ -163,7 +163,7 @@ initialize_ioc_threads(void *_sf_context) t_start = MPI_Wtime(); #endif - if (hg_thread_mutex_init(&ioc_data->io_queue.q_mutex) < 0) + if (H5TS_mutex_init(&ioc_data->io_queue.q_mutex) < 0) H5_SUBFILING_GOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, (-1), "can't initialize IOC thread queue mutex"); /* Allow experimentation with the number of helper threads */ @@ -229,7 +229,7 @@ finalize_ioc_threads(void *_sf_context) assert(0 == atomic_load(&ioc_data->sf_io_ops_pending)); hg_thread_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); @@ -563,37 +563,6 @@ handle_work_request(void *arg) H5_SUBFILING_FUNC_LEAVE; } -/*------------------------------------------------------------------------- - * 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 send_ack_to_client(int ack_val, int dest_rank, int source_rank, int msg_tag, MPI_Comm comm) { @@ -794,13 +763,13 @@ ioc_file_queue_write_indep(sf_work_request_t *msg, int ioc_idx, int source, MPI_ sf_queue_delay_time += t_queue_delay; #endif - H5FD_ioc_begin_thread_exclusive(); + H5TS_mutex_lock(&ioc_thread_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(&ioc_thread_mutex); /* * Send a message back to the client that the I/O call has @@ -1313,7 +1282,7 @@ 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)); @@ -1375,7 +1344,7 @@ ioc_io_queue_add_entry(ioc_data_t *ioc_data, sf_work_request_t *wk_req_ptr) assert(ioc_data->io_queue.q_len == atomic_load(&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; } /* ioc_io_queue_add_entry() */ @@ -1434,11 +1403,16 @@ 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; + herr_t ret; + + ret = H5TS_mutex_try_lock(&ioc_data->io_queue.q_mutex, &acquired); + assert(SUCCEED == ret); + 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; @@ -1558,7 +1532,7 @@ ioc_io_queue_dispatch_eligible_entries(ioc_data_t *ioc_data, bool try_lock) assert(ioc_data->io_queue.q_len == atomic_load(&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); } /* ioc_io_queue_dispatch_eligible_entries() */ /*------------------------------------------------------------------------- @@ -1593,7 +1567,7 @@ ioc_io_queue_complete_entry(ioc_data_t *ioc_data, ioc_io_queue_entry_t *entry_pt 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); @@ -1639,7 +1613,7 @@ ioc_io_queue_complete_entry(ioc_data_t *ioc_data, ioc_io_queue_entry_t *entry_pt #endif - hg_thread_mutex_unlock(&ioc_data->io_queue.q_mutex); + H5TS_mutex_unlock(&ioc_data->io_queue.q_mutex); ioc_io_queue_free_entry(entry_ptr); diff --git a/src/H5FDsubfiling/H5subfiling_common.c b/src/H5FDsubfiling/H5subfiling_common.c index 1127ae0a386..01426e40abe 100644 --- a/src/H5FDsubfiling/H5subfiling_common.c +++ b/src/H5FDsubfiling/H5subfiling_common.c @@ -18,6 +18,7 @@ #include "H5subfiling_err.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) */ @@ -44,6 +45,8 @@ static size_t sf_topology_cache_num_entries = 0; static file_map_to_context_t *sf_open_file_map = NULL; static int sf_file_map_size = 0; +static H5TS_mutex_t subfiling_log_mutex = H5TS_MUTEX_INITIALIZER; + #define DEFAULT_CONTEXT_CACHE_SIZE 16 #define DEFAULT_TOPOLOGY_CACHE_SIZE 4 #define DEFAULT_FILE_MAP_ENTRIES 8 @@ -3136,7 +3139,7 @@ H5_subfiling_log(int64_t sf_context_id, const char *fmt, ...) goto done; } - H5FD_ioc_begin_thread_exclusive(); + H5TS_mutex_lock(&subfiling_log_mutex); if (sf_context->sf_logfile) { vfprintf(sf_context->sf_logfile, fmt, log_args); @@ -3149,7 +3152,7 @@ H5_subfiling_log(int64_t sf_context_id, const char *fmt, ...) fflush(stdout); } - H5FD_ioc_end_thread_exclusive(); + H5TS_mutex_unlock(&subfiling_log_mutex); done: va_end(log_args); @@ -3171,7 +3174,7 @@ H5_subfiling_log_nonewline(int64_t sf_context_id, const char *fmt, ...) goto done; } - H5FD_ioc_begin_thread_exclusive(); + H5TS_mutex_lock(&subfiling_log_mutex); if (sf_context->sf_logfile) { vfprintf(sf_context->sf_logfile, fmt, log_args); @@ -3182,7 +3185,7 @@ H5_subfiling_log_nonewline(int64_t sf_context_id, const char *fmt, ...) fflush(stdout); } - H5FD_ioc_end_thread_exclusive(); + H5TS_mutex_unlock(&subfiling_log_mutex); done: va_end(log_args); diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_compiler_attributes.h b/src/H5FDsubfiling/mercury/src/util/mercury_compiler_attributes.h deleted file mode 100644 index d2b8ed12137..00000000000 --- a/src/H5FDsubfiling/mercury/src/util/mercury_compiler_attributes.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) 2013-2022 UChicago Argonne, LLC and The HDF Group. - * Copyright (c) 2022-2023 Intel Corporation. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#ifndef MERCURY_COMPILER_ATTRIBUTES_H -#define MERCURY_COMPILER_ATTRIBUTES_H - -/*************************************/ -/* Public Type and Struct Definition */ -/*************************************/ - -/*****************/ -/* Public Macros */ -/*****************/ - -/* - * __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17. - * In the meantime, to support gcc < 5, we implement __has_attribute - * by hand. - */ -#if !defined(__has_attribute) && defined(__GNUC__) && (__GNUC__ >= 4) -#define __has_attribute(x) __GCC4_has_attribute_##x -#define __GCC4_has_attribute___visibility__ 1 -#define __GCC4_has_attribute___warn_unused_result__ 1 -#define __GCC4_has_attribute___unused__ 1 -#define __GCC4_has_attribute___format__ 1 -#define __GCC4_has_attribute___fallthrough__ 0 -#endif - -/* Visibility of symbols */ -#if defined(_WIN32) -#define HG_ATTR_ABI_IMPORT __declspec(dllimport) -#define HG_ATTR_ABI_EXPORT __declspec(dllexport) -#define HG_ATTR_ABI_HIDDEN -#elif __has_attribute(__visibility__) -#define HG_ATTR_ABI_IMPORT __attribute__((__visibility__("default"))) -#define HG_ATTR_ABI_EXPORT __attribute__((__visibility__("default"))) -#define HG_ATTR_ABI_HIDDEN __attribute__((__visibility__("hidden"))) -#else -#define HG_ATTR_ABI_IMPORT -#define HG_ATTR_ABI_EXPORT -#define HG_ATTR_ABI_HIDDEN -#endif - -#endif /* MERCURY_COMPILER_ATTRIBUTES_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_queue.h b/src/H5FDsubfiling/mercury/src/util/mercury_queue.h index 946adec75df..6f8dfef3d18 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_queue.h +++ b/src/H5FDsubfiling/mercury/src/util/mercury_queue.h @@ -40,20 +40,6 @@ #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; \ @@ -75,8 +61,6 @@ #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; \ @@ -91,26 +75,4 @@ (head_ptr)->tail = &(head_ptr)->head; \ } while (/*CONSTCOND*/ 0) -#define HG_QUEUE_FOREACH(var, head_ptr, entry_field_name) \ - for ((var) = ((head_ptr)->head); (var); (var) = ((var)->entry_field_name.next)) - -/** - * Avoid using those for performance reasons or use mercury_list.h instead - */ - -#define HG_QUEUE_REMOVE(head_ptr, entry_ptr, type, entry_field_name) \ - do { \ - if ((head_ptr)->head == (entry_ptr)) { \ - HG_QUEUE_POP_HEAD((head_ptr), entry_field_name); \ - } \ - else { \ - struct type *curelm = (head_ptr)->head; \ - while (curelm->entry_field_name.next != (entry_ptr)) \ - curelm = curelm->entry_field_name.next; \ - if ((curelm->entry_field_name.next = curelm->entry_field_name.next->entry_field_name.next) == \ - NULL) \ - (head_ptr)->tail = &(curelm)->entry_field_name.next; \ - } \ - } while (/*CONSTCOND*/ 0) - #endif /* MERCURY_QUEUE_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread.c b/src/H5FDsubfiling/mercury/src/util/mercury_thread.c index effae48f346..34d0db7c917 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread.c +++ b/src/H5FDsubfiling/mercury/src/util/mercury_thread.c @@ -79,21 +79,6 @@ hg_thread_cancel(hg_thread_t thread) 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) @@ -127,38 +112,3 @@ hg_thread_key_delete(hg_thread_key_t key) return HG_UTIL_SUCCESS; } -/*---------------------------------------------------------------------------*/ -int -hg_thread_getaffinity(hg_thread_t thread, hg_cpu_set_t *cpu_mask) -{ -#if defined(_WIN32) - return HG_UTIL_FAIL; -#elif defined(__APPLE__) - (void)thread; - (void)cpu_mask; - return HG_UTIL_FAIL; -#else - if (pthread_getaffinity_np(thread, sizeof(hg_cpu_set_t), cpu_mask)) - return HG_UTIL_FAIL; - return HG_UTIL_SUCCESS; -#endif -} - -/*---------------------------------------------------------------------------*/ -int -hg_thread_setaffinity(hg_thread_t thread, const hg_cpu_set_t *cpu_mask) -{ -#if defined(_WIN32) - if (!SetThreadAffinityMask(thread, *cpu_mask)) - return HG_UTIL_FAIL; - return HG_UTIL_SUCCESS; -#elif defined(__APPLE__) - (void)thread; - (void)cpu_mask; - return HG_UTIL_FAIL; -#else - if (pthread_setaffinity_np(thread, sizeof(hg_cpu_set_t), cpu_mask)) - return HG_UTIL_FAIL; - return HG_UTIL_SUCCESS; -#endif -} diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread.h b/src/H5FDsubfiling/mercury/src/util/mercury_thread.h index f338226da3f..a9ea1cb27c9 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread.h +++ b/src/H5FDsubfiling/mercury/src/util/mercury_thread.h @@ -21,7 +21,6 @@ typedef LPTHREAD_START_ROUTINE hg_thread_func_t; typedef DWORD hg_thread_ret_t; #define HG_THREAD_RETURN_TYPE hg_thread_ret_t WINAPI typedef DWORD hg_thread_key_t; -typedef DWORD_PTR hg_cpu_set_t; #else #include typedef pthread_t hg_thread_t; @@ -29,18 +28,6 @@ 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 @@ -52,7 +39,7 @@ extern "C" { * * \param thread [IN/OUT] pointer to thread object */ -HG_UTIL_PUBLIC void hg_thread_init(hg_thread_t *thread); +void hg_thread_init(hg_thread_t *thread); /** * Create a new thread for the given function. @@ -63,7 +50,7 @@ HG_UTIL_PUBLIC void hg_thread_init(hg_thread_t *thread); * * \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); +int hg_thread_create(hg_thread_t *thread, hg_thread_func_t f, void *data); /** * Ends the calling thread. @@ -72,7 +59,7 @@ HG_UTIL_PUBLIC int hg_thread_create(hg_thread_t *thread, hg_thread_func_t f, voi * * \return Non-negative on success or negative on failure */ -HG_UTIL_PUBLIC void hg_thread_exit(hg_thread_ret_t ret); +void hg_thread_exit(hg_thread_ret_t ret); /** * Wait for thread completion. @@ -81,7 +68,7 @@ HG_UTIL_PUBLIC void hg_thread_exit(hg_thread_ret_t ret); * * \return Non-negative on success or negative on failure */ -HG_UTIL_PUBLIC int hg_thread_join(hg_thread_t thread); +int hg_thread_join(hg_thread_t thread); /** * Terminate the thread. @@ -90,28 +77,21 @@ HG_UTIL_PUBLIC int hg_thread_join(hg_thread_t thread); * * \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); +int hg_thread_cancel(hg_thread_t thread); /** * Obtain handle of the calling thread. * * \return */ -static HG_UTIL_INLINE hg_thread_t hg_thread_self(void); +static 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); +static 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. @@ -120,7 +100,7 @@ static HG_UTIL_INLINE int hg_thread_equal(hg_thread_t t1, hg_thread_t t2); * * \return Non-negative on success or negative on failure */ -HG_UTIL_PUBLIC int hg_thread_key_create(hg_thread_key_t *key); +int hg_thread_key_create(hg_thread_key_t *key); /** * Delete a thread-specific data key previously returned by @@ -130,7 +110,7 @@ HG_UTIL_PUBLIC int hg_thread_key_create(hg_thread_key_t *key); * * \return Non-negative on success or negative on failure */ -HG_UTIL_PUBLIC int hg_thread_key_delete(hg_thread_key_t key); +int hg_thread_key_delete(hg_thread_key_t key); /** * Get value from specified key. @@ -139,7 +119,7 @@ HG_UTIL_PUBLIC int hg_thread_key_delete(hg_thread_key_t key); * * \return Pointer to data associated to the key */ -static HG_UTIL_INLINE void *hg_thread_getspecific(hg_thread_key_t key); +static inline void *hg_thread_getspecific(hg_thread_key_t key); /** * Set value to specified key. @@ -149,30 +129,10 @@ static HG_UTIL_INLINE void *hg_thread_getspecific(hg_thread_key_t key); * * \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 inline int hg_thread_setspecific(hg_thread_key_t key, const void *value); /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE hg_thread_t +static inline hg_thread_t hg_thread_self(void) { #ifdef _WIN32 @@ -183,7 +143,7 @@ hg_thread_self(void) } /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int +static inline int hg_thread_equal(hg_thread_t t1, hg_thread_t t2) { #ifdef _WIN32 @@ -194,7 +154,7 @@ hg_thread_equal(hg_thread_t t1, hg_thread_t t2) } /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE void * +static inline void * hg_thread_getspecific(hg_thread_key_t key) { #ifdef _WIN32 @@ -205,7 +165,7 @@ hg_thread_getspecific(hg_thread_key_t key) } /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int +static inline int hg_thread_setspecific(hg_thread_key_t key, const void *value) { #ifdef _WIN32 diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.c b/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.c index 6b70978eb15..8534ddfad0f 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.c +++ b/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.c @@ -14,17 +14,8 @@ 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)) + if (pthread_cond_init(cond, NULL)) return HG_UTIL_FAIL; - pthread_condattr_destroy(&attr); #endif return HG_UTIL_SUCCESS; diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.h b/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.h index dcead57eff8..6d9252d7eb5 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.h +++ b/src/H5FDsubfiling/mercury/src/util/mercury_thread_condition.h @@ -13,12 +13,6 @@ #ifdef _WIN32 typedef CONDITION_VARIABLE hg_thread_cond_t; #else -#if defined(H5_HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(H5_HAVE_CLOCK_MONOTONIC_COARSE) -#include -#elif defined(H5_HAVE_SYS_TIME_H) -#include -#endif -#include typedef pthread_cond_t hg_thread_cond_t; #endif @@ -33,7 +27,7 @@ extern "C" { * * \return Non-negative on success or negative on failure */ -HG_UTIL_PUBLIC int hg_thread_cond_init(hg_thread_cond_t *cond); +int hg_thread_cond_init(hg_thread_cond_t *cond); /** * Destroy the condition. @@ -42,7 +36,7 @@ HG_UTIL_PUBLIC int hg_thread_cond_init(hg_thread_cond_t *cond); * * \return Non-negative on success or negative on failure */ -HG_UTIL_PUBLIC int hg_thread_cond_destroy(hg_thread_cond_t *cond); +int hg_thread_cond_destroy(hg_thread_cond_t *cond); /** * Wake one thread waiting for the condition to change. @@ -51,7 +45,7 @@ HG_UTIL_PUBLIC int hg_thread_cond_destroy(hg_thread_cond_t *cond); * * \return Non-negative on success or negative on failure */ -static HG_UTIL_INLINE int hg_thread_cond_signal(hg_thread_cond_t *cond); +static inline int hg_thread_cond_signal(hg_thread_cond_t *cond); /** * Wake all the threads waiting for the condition to change. @@ -60,7 +54,7 @@ static HG_UTIL_INLINE int hg_thread_cond_signal(hg_thread_cond_t *cond); * * \return Non-negative on success or negative on failure */ -static HG_UTIL_INLINE int hg_thread_cond_broadcast(hg_thread_cond_t *cond); +static inline int hg_thread_cond_broadcast(hg_thread_cond_t *cond); /** * Wait for the condition to change. @@ -70,22 +64,10 @@ static HG_UTIL_INLINE int hg_thread_cond_broadcast(hg_thread_cond_t *cond); * * \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 inline int hg_thread_cond_wait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex); /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int +static inline int hg_thread_cond_signal(hg_thread_cond_t *cond) { #ifdef _WIN32 @@ -99,7 +81,7 @@ hg_thread_cond_signal(hg_thread_cond_t *cond) } /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int +static inline int hg_thread_cond_broadcast(hg_thread_cond_t *cond) { #ifdef _WIN32 @@ -113,7 +95,7 @@ hg_thread_cond_broadcast(hg_thread_cond_t *cond) } /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int +static inline int hg_thread_cond_wait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex) { #ifdef _WIN32 @@ -127,45 +109,6 @@ hg_thread_cond_wait(hg_thread_cond_t *cond, hg_thread_mutex_t *mutex) 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 diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.c b/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.c index 8fc804f6141..d4a927bfecd 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.c +++ b/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.c @@ -11,33 +11,6 @@ #include -#ifndef _WIN32 -static int -hg_thread_mutex_init_posix(hg_thread_mutex_t *mutex, int kind) -{ - pthread_mutexattr_t mutex_attr; - int ret = HG_UTIL_SUCCESS; - int rc; - - rc = pthread_mutexattr_init(&mutex_attr); - HG_UTIL_CHECK_ERROR(rc != 0, done, ret, HG_UTIL_FAIL, "pthread_mutexattr_init() failed (%s)", - strerror(rc)); - - /* Keep mutex mode as normal and do not expect error checking */ - rc = pthread_mutexattr_settype(&mutex_attr, kind); - HG_UTIL_CHECK_ERROR(rc != 0, done, ret, HG_UTIL_FAIL, "pthread_mutexattr_settype() failed (%s)", - strerror(rc)); - - rc = pthread_mutex_init(mutex, &mutex_attr); - HG_UTIL_CHECK_ERROR(rc != 0, done, ret, HG_UTIL_FAIL, "pthread_mutex_init() failed (%s)", strerror(rc)); - -done: - rc = pthread_mutexattr_destroy(&mutex_attr); - - return ret; -} -#endif - /*---------------------------------------------------------------------------*/ int hg_thread_mutex_init(hg_thread_mutex_t *mutex) @@ -47,27 +20,14 @@ hg_thread_mutex_init(hg_thread_mutex_t *mutex) #ifdef _WIN32 InitializeCriticalSection(mutex); #else - ret = hg_thread_mutex_init_posix(mutex, PTHREAD_MUTEX_NORMAL); -#endif - - return ret; -} + int rc; -/*---------------------------------------------------------------------------*/ -int -hg_thread_mutex_init_fast(hg_thread_mutex_t *mutex) -{ - int ret = HG_UTIL_SUCCESS; + rc = pthread_mutex_init(mutex, NULL); + HG_UTIL_CHECK_ERROR(rc != 0, done, ret, HG_UTIL_FAIL, "pthread_mutex_init() failed (%s)", + strerror(rc)); -#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); +done: #endif - return ret; } diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.h b/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.h index 73ceafb8850..be2e5110c70 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.h +++ b/src/H5FDsubfiling/mercury/src/util/mercury_thread_mutex.h @@ -36,16 +36,7 @@ extern "C" { * * \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); +int hg_thread_mutex_init(hg_thread_mutex_t *mutex); /** * Destroy the mutex. @@ -54,14 +45,14 @@ HG_UTIL_PUBLIC int hg_thread_mutex_init_fast(hg_thread_mutex_t *mutex); * * \return Non-negative on success or negative on failure */ -HG_UTIL_PUBLIC int hg_thread_mutex_destroy(hg_thread_mutex_t *mutex); +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); +static inline void hg_thread_mutex_lock(hg_thread_mutex_t *mutex) HG_LOCK_ACQUIRE(*mutex); /** * Try locking the mutex. @@ -70,7 +61,7 @@ static HG_UTIL_INLINE void hg_thread_mutex_lock(hg_thread_mutex_t *mutex) HG_LOC * * \return Non-negative on success or negative on failure */ -static HG_UTIL_INLINE int hg_thread_mutex_try_lock(hg_thread_mutex_t *mutex) +static inline int hg_thread_mutex_try_lock(hg_thread_mutex_t *mutex) HG_LOCK_TRY_ACQUIRE(HG_UTIL_SUCCESS, *mutex); /** @@ -78,10 +69,10 @@ static HG_UTIL_INLINE int hg_thread_mutex_try_lock(hg_thread_mutex_t *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 inline void hg_thread_mutex_unlock(hg_thread_mutex_t *mutex) HG_LOCK_RELEASE(*mutex); /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE void +static inline void hg_thread_mutex_lock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS { #ifdef _WIN32 @@ -92,7 +83,7 @@ hg_thread_mutex_lock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS } /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int +static inline int hg_thread_mutex_try_lock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS { #ifdef _WIN32 @@ -107,7 +98,7 @@ hg_thread_mutex_try_lock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANAL } /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE void +static inline void hg_thread_mutex_unlock(hg_thread_mutex_t *mutex) HG_LOCK_NO_THREAD_SAFETY_ANALYSIS { #ifdef _WIN32 diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.h b/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.h index 6744eac9693..5141d584765 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.h +++ b/src/H5FDsubfiling/mercury/src/util/mercury_thread_pool.h @@ -53,7 +53,7 @@ extern "C" { * * \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); +int hg_thread_pool_init(unsigned int thread_count, hg_thread_pool_t **pool); /** * Destroy the thread pool. @@ -62,7 +62,7 @@ HG_UTIL_PUBLIC int hg_thread_pool_init(unsigned int thread_count, hg_thread_pool * * \return Non-negative on success or negative on failure */ -HG_UTIL_PUBLIC int hg_thread_pool_destroy(hg_thread_pool_t *pool); +int hg_thread_pool_destroy(hg_thread_pool_t *pool); /** * Post work to the pool. Note that the operation may be queued depending on @@ -73,10 +73,10 @@ HG_UTIL_PUBLIC int hg_thread_pool_destroy(hg_thread_pool_t *pool); * * \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 inline int hg_thread_pool_post(hg_thread_pool_t *pool, struct hg_thread_work *work); /*---------------------------------------------------------------------------*/ -static HG_UTIL_INLINE int +static inline int hg_thread_pool_post(hg_thread_pool_t *pool, struct hg_thread_work *work) { int ret = HG_UTIL_SUCCESS; diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_util_config.h b/src/H5FDsubfiling/mercury/src/util/mercury_util_config.h index 53189fb59a5..1f3a9ade317 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_util_config.h +++ b/src/H5FDsubfiling/mercury/src/util/mercury_util_config.h @@ -14,37 +14,12 @@ #include "H5private.h" -/* Type definitions */ -#include -#include -#include - /*****************/ /* Public Macros */ /*****************/ -/* Reflects any major or incompatible public API changes */ -#define HG_UTIL_VERSION_MAJOR 4 -/* Reflects any minor backwards compatible API or functionality addition */ -#define HG_UTIL_VERSION_MINOR 0 -/* Reflects any backwards compatible bug fixes */ -#define HG_UTIL_VERSION_PATCH 0 - /* Return codes */ #define HG_UTIL_SUCCESS 0 #define HG_UTIL_FAIL -1 -#include - -/* Inline macro */ -#ifdef _WIN32 -#define HG_UTIL_INLINE __inline -#else -#define HG_UTIL_INLINE __inline__ -#endif - -#define HG_UTIL_PUBLIC -#define HG_UTIL_PRIVATE -#define HG_UTIL_PLUGIN - #endif /* MERCURY_UTIL_CONFIG_H */ diff --git a/src/H5FDsubfiling/mercury/src/util/mercury_util_error.h b/src/H5FDsubfiling/mercury/src/util/mercury_util_error.h index b5cb5cca2a4..edf0485d4f9 100644 --- a/src/H5FDsubfiling/mercury/src/util/mercury_util_error.h +++ b/src/H5FDsubfiling/mercury/src/util/mercury_util_error.h @@ -10,32 +10,12 @@ #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)) { \ + if (H5_UNLIKELY(cond)) { \ ret = err_val; \ goto label; \ } \ @@ -43,7 +23,7 @@ #define HG_UTIL_CHECK_ERROR_NORET(cond, label, ...) \ do { \ - if (unlikely(cond)) { \ + if (H5_UNLIKELY(cond)) { \ goto label; \ } \ } while (0) diff --git a/src/H5TS.c b/src/H5TS.c index 8aadd904c34..d4cec185992 100644 --- a/src/H5TS.c +++ b/src/H5TS.c @@ -12,25 +12,28 @@ /* * Purpose: This file contains the framework for ensuring that the global - * library lock is held when an API routine is called. This - * framework works in concert with the FUNC_ENTER_API / FUNC_LEAVE_API - * macros defined in H5private.h. + * library lock is held when an API routine is called. This framework + * works in concert with the FUNC_ENTER_API / FUNC_LEAVE_API macros + * defined in H5private.h. * - * Note: Because this threadsafety framework operates outside the library, - * it does not use the error stack and only uses the "namecheck only" - * FUNC_ENTER_* / FUNC_LEAVE_* macros. + * Note: Because this threadsafety framework operates outside the library, + * it does not use the error stack (although it does use error macros + * that don't push errors on a stack) and only uses the "namecheck only" + * FUNC_ENTER_* / FUNC_LEAVE_* macros. */ /****************/ /* Module Setup */ /****************/ +#include "H5TSmodule.h" /* This source code file is part of the H5TS module */ + /***********/ /* Headers */ /***********/ -#include "H5private.h" /* Generic Functions */ -#include "H5Eprivate.h" /* Error handling */ -#include "H5MMprivate.h" /* Memory management */ +#include "H5private.h" /* Generic Functions */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5TSpkg.h" /* Threadsafety */ #ifdef H5_HAVE_THREADSAFE @@ -38,369 +41,33 @@ /* Local Macros */ /****************/ + /******************/ /* 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,189 +78,16 @@ H5TS__mutex_acquire(H5TS_mutex_t *mutex, unsigned int lock_count, bool *acquired * global lock was acquired. * * Return: Non-negative on success / Negative on failure - *-------------------------------------------------------------------------- - */ -herr_t -H5TSmutex_acquire(unsigned int lock_count, bool *acquired){ - FUNC_ENTER_API_NAMECHECK_ONLY - - FUNC_LEAVE_API_NAMECHECK_ONLY(H5TS__mutex_acquire(&H5_g.init_lock, lock_count, acquired))} -/* end H5TSmutex_acquire() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_mutex_lock - * - * USAGE - * H5TS_mutex_lock(&mutex_var) - * - * RETURNS - * Non-negative on success / Negative on failure - * - * DESCRIPTION - * Recursive lock semantics for HDF5 (locking) - - * Multiple acquisition of a lock by a thread is permitted with a - * corresponding unlock operation required. - * - * PROGRAMMER: Chee Wai LEE - * May 2, 2000 - * - *-------------------------------------------------------------------------- - */ -herr_t H5TS_mutex_lock(H5TS_mutex_t *mutex) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - EnterCriticalSection(&mutex->CriticalSection); -#else /* H5_HAVE_WIN_THREADS */ - /* Acquire the "attempt" lock, increment the attempt lock count, release the lock */ - ret_value = pthread_mutex_lock(&mutex->atomic_lock2); - if (ret_value) - HGOTO_DONE(ret_value); - mutex->attempt_lock_count++; - ret_value = pthread_mutex_unlock(&mutex->atomic_lock2); - if (ret_value) - HGOTO_DONE(ret_value); - - /* Acquire the library lock */ - ret_value = pthread_mutex_lock(&mutex->atomic_lock); - if (ret_value) - HGOTO_DONE(ret_value); - - /* Check if this thread already owns the lock */ - if (mutex->lock_count && pthread_equal(pthread_self(), mutex->owner_thread)) - /* already owned by self - increment count */ - mutex->lock_count++; - else { - /* Wait until the lock is released by current owner thread */ - while (mutex->lock_count) - pthread_cond_wait(&mutex->cond_var, &mutex->atomic_lock); - - /* After we've received the signal, take ownership of the mutex */ - mutex->owner_thread = pthread_self(); - mutex->lock_count = 1; - } - - /* Release the library lock */ - ret_value = pthread_mutex_unlock(&mutex->atomic_lock); - -done: -#endif /* H5_HAVE_WIN_THREADS */ - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS_mutex_lock() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS__mutex_unlock - * - * USAGE - * H5TS__mutex_unlock(&mutex_var, &lock_count) - * - * RETURNS - * Non-negative on success / Negative on failure - * - * DESCRIPTION - * Recursive lock semantics for HDF5 (unlocking) - - * Reset the lock and return the current lock count - * - * PROGRAMMER: Houjun Tang - * Nov 3, 2020 - * - *-------------------------------------------------------------------------- - */ -static herr_t -H5TS__mutex_unlock(H5TS_mutex_t *mutex, unsigned int *lock_count) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* Releases ownership of the specified critical section object. */ - LeaveCriticalSection(&mutex->CriticalSection); -#else /* H5_HAVE_WIN_THREADS */ - - /* Reset the lock count for this thread */ - ret_value = pthread_mutex_lock(&mutex->atomic_lock); - if (ret_value) - HGOTO_DONE(ret_value); - *lock_count = mutex->lock_count; - mutex->lock_count = 0; - ret_value = pthread_mutex_unlock(&mutex->atomic_lock); - - /* If the lock count drops to zero, signal the condition variable, to - * wake another thread. - */ - if (mutex->lock_count == 0) { - int err; - - err = pthread_cond_signal(&mutex->cond_var); - if (err != 0) - ret_value = err; - } - -done: -#endif /* H5_HAVE_WIN_THREADS */ - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS__mutex_unlock */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_mutex_unlock - * - * USAGE - * H5TS_mutex_unlock(&mutex_var) - * - * RETURNS - * Non-negative on success / Negative on failure - * - * DESCRIPTION - * Recursive lock semantics for HDF5 (unlocking) - - * Multiple acquisition of a lock by a thread is permitted with a - * corresponding unlock operation required. - * - * PROGRAMMER: Chee Wai LEE - * May 2, 2000 * *-------------------------------------------------------------------------- */ herr_t -H5TS_mutex_unlock(H5TS_mutex_t *mutex) +H5TSmutex_acquire(unsigned lock_count, bool *acquired) { - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* Releases ownership of the specified critical section object. */ - LeaveCriticalSection(&mutex->CriticalSection); -#else /* H5_HAVE_WIN_THREADS */ - - /* Decrement the lock count for this thread */ - ret_value = pthread_mutex_lock(&mutex->atomic_lock); - if (ret_value) - HGOTO_DONE(ret_value); - mutex->lock_count--; - ret_value = pthread_mutex_unlock(&mutex->atomic_lock); - - /* If the lock count drops to zero, signal the condition variable, to - * wake another thread. - */ - if (mutex->lock_count == 0) { - int err; + FUNC_ENTER_API_NAMECHECK_ONLY - err = pthread_cond_signal(&mutex->cond_var); - if (err != 0) - ret_value = err; - } - -done: -#endif /* H5_HAVE_WIN_THREADS */ - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_mutex_unlock */ + FUNC_LEAVE_API_NAMECHECK_ONLY(H5TS__mutex_acquire(lock_count, acquired)) +} /* end H5TSmutex_acquire() */ /*-------------------------------------------------------------------------- * Function: H5TSmutex_get_attempt_count @@ -601,30 +95,33 @@ H5TS_mutex_unlock(H5TS_mutex_t *mutex) * Purpose: Get the current count of the global lock attempt * * Return: Non-negative on success / Negative on failure + * + * Programmer: Houjun Tang + * June 24, 2019 + * *-------------------------------------------------------------------------- */ herr_t -H5TSmutex_get_attempt_count(unsigned int *count) +H5TSmutex_get_attempt_count(unsigned *count) { + bool have_mutex = false; 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 = H5_g.init_lock.attempt_lock_count; + /* Acquire the "attempt" lock, retrieve the attempt lock count, release the lock */ + if (H5_UNLIKELY(H5TS_mutex_lock(&H5TS_api_info_p.attempt_mutex) < 0)) + HGOTO_DONE(FAIL); + have_mutex = true; - ret_value = pthread_mutex_unlock(&H5_g.init_lock.atomic_lock2); - if (ret_value) - HGOTO_DONE(ret_value); + *count = H5TS_api_info_p.attempt_lock_count; done: -#endif /* H5_HAVE_WIN_THREADS */ + /* Release the lock */ + if(have_mutex) + if(H5_UNLIKELY(H5TS_mutex_unlock(&H5TS_api_info_p.attempt_mutex) < 0)) + ret_value = FAIL; + FUNC_LEAVE_API_NAMECHECK_ONLY(ret_value) } /* end H5TSmutex_get_attempt_count() */ @@ -634,351 +131,20 @@ H5TSmutex_get_attempt_count(unsigned int *count) * Purpose: Releases the HDF5 library global lock * * Return: Non-negative on success / Negative on failure + * *-------------------------------------------------------------------------- */ herr_t -H5TSmutex_release(unsigned int *lock_count) +H5TSmutex_release(unsigned *lock_count) { herr_t ret_value = SUCCEED; FUNC_ENTER_API_NAMECHECK_ONLY *lock_count = 0; - if (0 != H5TS__mutex_unlock(&H5_g.init_lock, lock_count)) - ret_value = -1; + if (H5_UNLIKELY(H5TS__mutex_release(lock_count) < 0)) + ret_value = FAIL; FUNC_LEAVE_API_NAMECHECK_ONLY(ret_value) } /* end H5TSmutex_release() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_cancel_count_inc - * - * USAGE - * H5TS_cancel_count_inc() - * - * RETURNS - * Non-negative on success / Negative on failure - * - * DESCRIPTION - * Creates a cancellation counter for a thread if it is the first time - * the thread is entering the library. - * - * if counter value is zero, then set cancellability type of the thread - * to PTHREAD_CANCEL_DISABLE as thread is entering the library and store - * the previous cancellability type into cancellation counter. - * Increase the counter value by 1. - * - * PROGRAMMER: Chee Wai LEE - * May 2, 2000 - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS_cancel_count_inc(void) -{ -#ifndef H5_HAVE_WIN_THREADS - H5TS_cancel_t *cancel_counter; -#endif /* H5_HAVE_WIN_THREADS */ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* unsupported */ -#else /* H5_HAVE_WIN_THREADS */ - /* Acquire the thread's cancellation counter */ - cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_s); - - /* Check if it's created yet */ - if (!cancel_counter) { - /* - * First time thread calls library - create new counter and associate - * with key. - * - * Don't use H5MM calls here since the destructor has to use free in - * order to avoid codestack calls. - */ - cancel_counter = (H5TS_cancel_t *)calloc(1, sizeof(H5TS_cancel_t)); - if (NULL == cancel_counter) - HGOTO_DONE(FAIL); - - /* Set the thread's cancellation counter with the new object */ - ret_value = pthread_setspecific(H5TS_cancel_key_s, (void *)cancel_counter); - if (ret_value) { - free(cancel_counter); - HGOTO_DONE(FAIL); - } - } - - /* Check if thread entering library */ - if (cancel_counter->cancel_count == 0) - /* Set cancellation state to 'disable', and remember previous state */ - ret_value = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_counter->previous_state); - - /* Increment # of times the library API was re-entered, to avoid resetting - * previous cancellation state until the final API routine is returning. - */ - ++cancel_counter->cancel_count; - -done: -#endif /* H5_HAVE_WIN_THREADS */ - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS_cancel_count_inc() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_cancel_count_dec - * - * USAGE - * H5TS_cancel_count_dec() - * - * RETURNS - * 0 on success and a non-zero error code on error. - * - * DESCRIPTION - * If counter value is one, then set cancellability type of the thread - * to the previous cancellability type stored in the cancellation counter. - * (the thread is leaving the library). - * - * Decrement the counter value by 1. - * - * PROGRAMMER: Chee Wai LEE - * May 2, 2000 - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS_cancel_count_dec(void) -{ -#ifndef H5_HAVE_WIN_THREADS - H5TS_cancel_t *cancel_counter; -#endif /* H5_HAVE_WIN_THREADS */ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* unsupported */ -#else /* H5_HAVE_WIN_THREADS */ - /* Acquire the thread's cancellation counter */ - cancel_counter = (H5TS_cancel_t *)H5TS_get_thread_local_value(H5TS_cancel_key_s); - - /* Check for leaving last API routine */ - if (cancel_counter->cancel_count == 1) - /* Reset to previous thread cancellation state, if last API */ - ret_value = pthread_setcancelstate(cancel_counter->previous_state, NULL); - - /* Decrement cancellation counter */ - --cancel_counter->cancel_count; -#endif /* H5_HAVE_WIN_THREADS */ - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* end H5TS_cancel_count_dec() */ - -#ifdef H5_HAVE_WIN_THREADS -/*-------------------------------------------------------------------------- - * NAME - * H5TS_win32_process_enter - * - * RETURNS - * SUCCEED/FAIL - * - * DESCRIPTION - * Per-process setup on Windows when using Win32 threads. - * - *-------------------------------------------------------------------------- - */ -H5_DLL BOOL CALLBACK -H5TS_win32_process_enter(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContex) -{ - BOOL ret_value = true; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - /* Initialize the critical section (can't fail) */ - InitializeCriticalSection(&H5_g.init_lock.CriticalSection); - - /* Set up thread local storage */ - if (TLS_OUT_OF_INDEXES == (H5TS_errstk_key_g = TlsAlloc())) - ret_value = false; - -#ifdef H5_HAVE_CODESTACK - if (TLS_OUT_OF_INDEXES == (H5TS_funcstk_key_g = TlsAlloc())) - ret_value = false; -#endif /* H5_HAVE_CODESTACK */ - - if (TLS_OUT_OF_INDEXES == (H5TS_apictx_key_g = TlsAlloc())) - ret_value = false; - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_win32_process_enter() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_win32_thread_enter - * - * RETURNS - * SUCCEED/FAIL - * - * DESCRIPTION - * Per-thread setup on Windows when using Win32 threads. - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS_win32_thread_enter(void) -{ - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - /* Currently a placeholder function. TLS setup is performed - * elsewhere in the library. - * - * WARNING: Do NOT use C standard library functions here. - * CRT functions are not allowed in DllMain, which is where this code - * is used. - */ - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_win32_thread_enter() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_win32_process_exit - * - * RETURNS - * SUCCEED/FAIL - * - * DESCRIPTION - * Per-process cleanup on Windows when using Win32 threads. - * - *-------------------------------------------------------------------------- - */ -void -H5TS_win32_process_exit(void) -{ - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - /* Windows uses a different thread local storage mechanism which does - * not support auto-freeing like pthreads' keys. - * - * This function is currently registered via atexit() and is called - * AFTER H5_term_library(). - */ - - /* Clean up critical section resources (can't fail) */ - DeleteCriticalSection(&H5_g.init_lock.CriticalSection); - - /* Clean up per-process thread local storage */ - if (H5TS_errstk_key_g != TLS_OUT_OF_INDEXES) - TlsFree(H5TS_errstk_key_g); -#ifdef H5_HAVE_CODESTACK - if (H5TS_funcstk_key_g != TLS_OUT_OF_INDEXES) - TlsFree(H5TS_funcstk_key_g); -#endif /* H5_HAVE_CODESTACK */ - if (H5TS_apictx_key_g != TLS_OUT_OF_INDEXES) - TlsFree(H5TS_apictx_key_g); - - FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY -} /* H5TS_win32_process_exit() */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_win32_thread_exit - * - * RETURNS - * SUCCEED/FAIL - * - * DESCRIPTION - * Per-thread cleanup on Windows when using Win32 threads. - * - *-------------------------------------------------------------------------- - */ -herr_t -H5TS_win32_thread_exit(void) -{ - LPVOID lpvData; - herr_t ret_value = SUCCEED; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - - /* Windows uses a different thread local storage mechanism which does - * not support auto-freeing like pthreads' keys. - * - * WARNING: Do NOT use C standard library functions here. - * CRT functions are not allowed in DllMain, which is where this code - * is used. - */ - - /* Clean up per-thread thread local storage */ - if (H5TS_errstk_key_g != TLS_OUT_OF_INDEXES) { - lpvData = TlsGetValue(H5TS_errstk_key_g); - if (lpvData) - LocalFree((HLOCAL)lpvData); - } - -#ifdef H5_HAVE_CODESTACK - if (H5TS_funcstk_key_g != TLS_OUT_OF_INDEXES) { - lpvData = TlsGetValue(H5TS_funcstk_key_g); - if (lpvData) - LocalFree((HLOCAL)lpvData); - } -#endif /* H5_HAVE_CODESTACK */ - - if (H5TS_apictx_key_g != TLS_OUT_OF_INDEXES) { - lpvData = TlsGetValue(H5TS_apictx_key_g); - if (lpvData) - LocalFree((HLOCAL)lpvData); - } - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_win32_thread_exit() */ -#endif /* H5_HAVE_WIN_THREADS */ - -/*-------------------------------------------------------------------------- - * NAME - * H5TS_create_thread - * - * RETURNS - * Thread identifier. - * - * DESCRIPTION - * Spawn off a new thread calling function 'func' with input 'udata'. - * - * PROGRAMMER: Mike McGreevy - * August 31, 2010 - * - *-------------------------------------------------------------------------- - */ -H5TS_thread_t -H5TS_create_thread(H5TS_thread_cb_t func, H5TS_attr_t *attr, void *udata) -{ - H5TS_thread_t ret_value; - - FUNC_ENTER_NOAPI_NAMECHECK_ONLY - -#ifdef H5_HAVE_WIN_THREADS - /* When calling C runtime functions, you should use _beginthread or - * _beginthreadex instead of CreateThread. Threads created with - * CreateThread risk being killed in low-memory situations. Since we - * only create threads in our test code, this is unlikely to be an issue - * and we'll use the easier-to-deal-with CreateThread for now. - * - * NOTE: _beginthread() auto-recycles its handle when execution completes - * so you can't wait on it, making it unsuitable for the existing - * test code. - */ - ret_value = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, udata, 0, NULL); - -#else /* H5_HAVE_WIN_THREADS */ - - pthread_create(&ret_value, attr, (void *(*)(void *))func, udata); - -#endif /* H5_HAVE_WIN_THREADS */ - - FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) -} /* H5TS_create_thread */ - #endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSbarrier.c b/src/H5TSbarrier.c new file mode 100644 index 00000000000..b2f0226ec49 --- /dev/null +++ b/src/H5TSbarrier.c @@ -0,0 +1,228 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_THREADSAFE + +/****************/ +/* Local Macros */ +/****************/ + +#ifndef H5_HAVE_PTHREAD_BARRIER +/* Barrier initialization macro */ +#define H5TS_BARRIER_INIT { \ + PTHREAD_MUTEX_INITIALIZER, /* mutex */ \ + PTHREAD_COND_INITIALIZER, /* cv */ \ + 0, /* count */ \ + 0, /* entered */ \ + 0 /* threshold */ \ + } +#endif + + +/******************/ +/* Local Typedefs */ +/******************/ + + +/********************/ +/* Local Prototypes */ +/********************/ + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + +/* Default value to initialize barriers */ +#ifndef H5_HAVE_PTHREAD_BARRIER +static const H5TS_barrier_t H5TS_barrier_def = H5TS_BARRIER_INIT; +#endif + +/*-------------------------------------------------------------------------- + * 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, uint64_t count) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == barrier || 0 == count)) + HGOTO_DONE(FAIL); + + /* Initialize the barrier */ +#ifdef H5_HAVE_PTHREAD_BARRIER + if (pthread_barrier_init(barrier, NULL, count)) + HGOTO_DONE(FAIL); +#else + memcpy(barrier, &H5TS_barrier_def, sizeof(H5TS_barrier_def)); + + /* Set non-default fields */ + barrier->count = barrier->threshold = count; +#endif + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__barrier_init() */ + +/*-------------------------------------------------------------------------- + * 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 + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__barrier_wait(H5TS_barrier_t *barrier) +{ +#ifndef H5_HAVE_PTHREAD_BARRIER + bool have_mutex = false; +#endif + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == barrier)) + HGOTO_DONE(FAIL); + +#ifdef H5_HAVE_PTHREAD_BARRIER + if (0 != pthread_barrier_wait(barrier)) + HGOTO_DONE(FAIL); +#else + /* Acquire the mutex for the barrier */ + if (H5_UNLIKELY(H5TS_mutex_lock(&barrier->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + barrier->entered++; + if (barrier->entered < barrier->threshold) { + if (H5_UNLIKELY(H5TS_cond_wait(&barrier->cv, &barrier->mutex))) + HGOTO_DONE(FAIL); + } + else { + if (H5_UNLIKELY(H5TS_cond_broadcast(&barrier->cv))) + HGOTO_DONE(FAIL); + + /* Increment threshold count of threads for next barrier */ + barrier->threshold += barrier->count; + } +#endif + +done: +#ifndef H5_HAVE_PTHREAD_BARRIER + if(have_mutex) + if(H5_UNLIKELY(H5TS_mutex_unlock(&barrier->mutex))) + ret_value = FAIL; +#endif + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__barrier_wait() */ + +/*-------------------------------------------------------------------------- + * 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_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == barrier)) + HGOTO_DONE(FAIL); + +#ifdef H5_HAVE_PTHREAD_BARRIER + if (0 != pthread_barrier_destroy(barrier)) + HGOTO_DONE(FAIL); +#else + + /* Acquire the mutex for the barrier */ + if (H5_UNLIKELY(H5TS_mutex_lock(&barrier->mutex))) + HGOTO_DONE(FAIL); + + /* Check for barrier still in use */ + if (H5_UNLIKELY((barrier->threshold - barrier->entered) != barrier->count)) { + (void)H5TS_mutex_unlock(&barrier->mutex); + HGOTO_DONE(FAIL); + } + + /* Release the barrier's mutex */ + if(H5_UNLIKELY(H5TS_mutex_unlock(&barrier->mutex))) + HGOTO_DONE(FAIL); + + /* Call the appropriate pthread 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(&barrier->mutex))) + ret_value = FAIL; + if (H5_UNLIKELY(H5TS_cond_destroy(&barrier->cv))) + ret_value = FAIL; +#endif + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__barrier_destroy() */ + +#endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TScond.c b/src/H5TScond.c new file mode 100644 index 00000000000..b0ed28be47f --- /dev/null +++ b/src/H5TScond.c @@ -0,0 +1,288 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_THREADSAFE + +/****************/ +/* Local Macros */ +/****************/ + + +/******************/ +/* Local Typedefs */ +/******************/ + + +/********************/ +/* Local Prototypes */ +/********************/ + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + + + +#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_wait + * + * Purpose: Wait on a condition variable + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_wait(H5TS_cond_t *cond, H5TS_mutex_t *mutex) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(!SleepConditionVariableCS(cond, mutex, INFINITE))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_cond_wait() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_cond_signal + * + * Purpose: Unblock a thread waiting for a condition variable + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_signal(H5TS_cond_t *cond) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + WakeConditionVariable(cond); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(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 + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_broadcast(H5TS_cond_t *cond) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + WakeAllConditionVariable(cond); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_cond_broadcast() */ + +/*------------------------------------------------------------------------- + * 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_wait + * + * Purpose: Wait on a condition variable + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_wait(H5TS_cond_t *cond, H5TS_mutex_t *mutex) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_cond_wait(cond, mutex))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_cond_wait() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_cond_signal + * + * Purpose: Unblock a thread waiting for a condition variable + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_signal(H5TS_cond_t *cond) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_cond_signal(cond))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* 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 + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_cond_broadcast(H5TS_cond_t *cond) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_cond_broadcast(cond))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_cond_broadcast() */ + +/*------------------------------------------------------------------------- + * 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 /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSdevelop.h b/src/H5TSdevelop.h index c3e1a6a65bf..6700d1047d8 100644 --- a/src/H5TSdevelop.h +++ b/src/H5TSdevelop.h @@ -39,9 +39,9 @@ extern "C" { #endif /* HDF5 global library lock routines */ -H5_DLL herr_t H5TSmutex_acquire(unsigned int lock_count, bool *acquired); -H5_DLL herr_t H5TSmutex_release(unsigned int *lock_count); -H5_DLL herr_t H5TSmutex_get_attempt_count(unsigned int *count); +H5_DLL herr_t H5TSmutex_acquire(unsigned lock_count, bool *acquired); +H5_DLL herr_t H5TSmutex_release(unsigned *lock_count); +H5_DLL herr_t H5TSmutex_get_attempt_count(unsigned *count); #ifdef __cplusplus } diff --git a/src/H5TSexlock.c b/src/H5TSexlock.c new file mode 100644 index 00000000000..bac81136529 --- /dev/null +++ b/src/H5TSexlock.c @@ -0,0 +1,372 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 exclusive locks, equivalent to + * the pthread 'pthread_mutex_t' type and capabilities, except that + * threads that hold the lock are allowed to acquire 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_THREADSAFE + +/****************/ +/* Local Macros */ +/****************/ + +/* Excl lock initialization macro */ +#ifdef H5_HAVE_PTHREAD_H +#define H5TS_EX_LOCK_INIT { \ + H5TS_MUTEX_INITIALIZER, /* mutex */ \ + H5TS_COND_INITIALIZER, /* cond_var */ \ + 0, /* owner_thread */ \ + 0, /* lock_count */ \ + false, /* disable_cancel */ \ + 0 /* previous_state */ \ + } +#else +#define H5TS_EXL_LOCK_INIT { \ + H5TS_MUTEX_INITIALIZER, /* mutex */ \ + H5TS_COND_INITIALIZER, /* cond_var */ \ + 0, /* owner_thread */ \ + 0 /* lock_count */ \ + } +#endif + + +/******************/ +/* Local Typedefs */ +/******************/ + + +/********************/ +/* Local Prototypes */ +/********************/ + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + +/* Default value to initialize exclusive locks */ +static const H5TS_ex_lock_t H5TS_ex_lock_def = H5TS_EX_LOCK_INIT; + + +/*-------------------------------------------------------------------------- + * Function: H5TS__ex_lock_init + * + * Purpose: Initialize the supplied instance of H5TS_ex_lock_t. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__ex_lock_init(H5TS_ex_lock_t *lock, bool disable_cancel) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == lock)) + HGOTO_DONE(FAIL); + + /* Initialize the lock */ + memcpy(lock, &H5TS_ex_lock_def, sizeof(H5TS_ex_lock_def)); + +#ifdef H5_HAVE_PTHREAD_H + /* Set non-default fields */ + lock->disable_cancel = disable_cancel; +#else + (void)disable_cancel; +#endif + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__ex_lock_init() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__ex_lock + * + * Purpose: Acquire a lock on the associated recursive exclusive lock. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__ex_lock(H5TS_ex_lock_t *lock) +{ + H5TS_thread_t my_thread_id = H5TS_thread_self(); + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Acquire the mutex for the lock */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Check if this thread already owns the lock */ + if (lock->lock_count && H5TS_thread_equal(my_thread_id, lock->owner_thread)) + /* Already owned by self - increment count */ + lock->lock_count++; + else { + /* Wait until the mutex is released by current owner thread */ + while (lock->lock_count) + H5TS_cond_wait(&lock->cond_var, &lock->mutex); + + /* After we've received the signal, take ownership of the lock */ + lock->owner_thread = my_thread_id; + lock->lock_count = 1; + +#ifdef H5_HAVE_PTHREAD_H + /* Disable cancellation, if requested for this lock */ + if (lock->disable_cancel) + /* Set cancellation state to 'disable', and remember previous state */ + if (H5_UNLIKELY(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &lock->previous_state))) + HGOTO_DONE(FAIL); +#endif + } + +done: + if(have_mutex) + if(H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex))) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__ex_lock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__ex_acquire + * + * Purpose: Attempts to acquire an exclusive lock, without blocking + * + * Note: On success, the 'acquired' flag indicates if lock was acquired. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__ex_acquire(H5TS_ex_lock_t *lock, unsigned lock_count, bool *acquired) +{ + bool have_mutex = false; + H5TS_thread_t my_thread_id = H5TS_thread_self(); + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Attempt to acquire the lock's mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Check if locked already */ + if (lock->lock_count) { + /* Check for this thread already owning the lock */ + if (H5TS_thread_equal(my_thread_id, lock->owner_thread)) { + /* Already owned by self - increment count */ + lock->lock_count += lock_count; + *acquired = true; + } + else + *acquired = false; + } + else { + /* Take ownership of the lock */ + lock->owner_thread = my_thread_id; + lock->lock_count = lock_count; + *acquired = true; + } + +done: + /* Release the mutex, if acquired */ + if(have_mutex) + if(H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex))) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__ex_acquire() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__ex_release + * + * Purpose: Release an exclusive lock. Passes back the previous lock count + * to the caller in a parameter. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__ex_release(H5TS_ex_lock_t *lock, unsigned int *lock_count) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Attempt to acquire the lock's mutex */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Reset the lock count for this thread */ + *lock_count = lock->lock_count; + lock->lock_count = 0; + + /* Signal the condition variable, to wake any thread waiting on the lock */ + if (H5_UNLIKELY(H5TS_cond_signal(&lock->cond_var))) + HGOTO_DONE(FAIL); + +done: + if (have_mutex) + if (H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex))) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS__ex_release */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__ex_unlock + * + * Purpose: Decrements the lock counter, releasing the lock when the counter + * reaches 0. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__ex_unlock(H5TS_ex_lock_t *lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Acquire the mutex for the lock */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Decrement the lock count for this thread */ + lock->lock_count--; + + if (lock->lock_count == 0) { +#ifdef H5_HAVE_PTHREAD_H + /* Restore previous cancellation state, if requested for this lock */ + if (lock->disable_cancel) + if (H5_UNLIKELY(pthread_setcancelstate(lock->previous_state, NULL))) + HGOTO_DONE(FAIL); +#endif + + /* If the lock count drops to zero, signal the condition variable, to + * wake any thread waiting on the lock. + */ + if (H5_UNLIKELY(H5TS_cond_signal(&lock->cond_var))) + HGOTO_DONE(FAIL); + } + +done: + if(have_mutex) + if(H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex))) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS__ex_unlock */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__ex_lock_destroy + * + * Purpose: Destroy an exlusive lock. All mutex, condition variables, + * etc. are destroyed. However, the instance of H5TS_ex_lock_t + * is not freed. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__ex_lock_destroy(H5TS_ex_lock_t *lock) +{ + bool have_mutex = false; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + if (H5_UNLIKELY(NULL == lock)) + HGOTO_DONE(FAIL); + + /* Acquire the mutex for the lock */ + if (H5_UNLIKELY(H5TS_mutex_lock(&lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Fail if this thread owns the lock */ + if (lock->lock_count && H5TS_thread_equal(H5TS_thread_self(), lock->owner_thread)) + HGOTO_DONE(FAIL); + + /* Release the mutex for the lock */ + if (H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex))) + HGOTO_DONE(FAIL); + have_mutex = false; + + /* Call the appropriate destroy routines. We are committed + * to the destroy at this point, so call them all, even if one fails + * along the way. + */ + if (H5_UNLIKELY(H5TS_mutex_destroy(&lock->mutex))) + ret_value = FAIL; + if (H5_UNLIKELY(H5TS_cond_destroy(&lock->cond_var))) + ret_value = FAIL; + +done: + if(have_mutex) + if(H5_UNLIKELY(H5TS_mutex_unlock(&lock->mutex))) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__ex_lock_destroy() */ + +#endif /* H5_HAVE_THREADSAFE */ + diff --git a/src/H5TSint.c b/src/H5TSint.c new file mode 100644 index 00000000000..7d39fdab027 --- /dev/null +++ b/src/H5TSint.c @@ -0,0 +1,534 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 */ +/****************/ + + +/******************/ +/* 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_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? */ +#ifdef H5_HAVE_WIN_THREADS +static H5TS_once_t H5TS_first_init_s; +#else +static H5TS_once_t H5TS_first_init_s = PTHREAD_ONCE_INIT; +#endif + + +/* 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 */ +#ifdef H5_HAVE_WIN_THREADS +static H5TS_mutex_t H5TS_tinfo_mtx_s; +#else +static H5TS_mutex_t H5TS_tinfo_mtx_s = PTHREAD_MUTEX_INITIALIZER; +#endif + +/*------------------------------------------------------------------------- + * 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 + + /* Destroy global API lock */ + H5TS__ex_lock_destroy(&H5TS_api_info_p.api_lock); + + /* Destroy the "lock acquisition attempt" mutex */ + H5TS_mutex_destroy(&H5TS_api_info_p.attempt_mutex); + + /* Clean up per-process thread local storage */ + H5TS__tinfo_term(); + + FUNC_LEAVE_NOAPI_VOID +} /* end H5TS_term_package() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__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. + * + * Note: The Windows threads code is very likely bogus. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__mutex_acquire(unsigned int lock_count, bool *acquired) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Attempt to acquire the lock */ + if (H5_UNLIKELY(H5TS__ex_acquire(&H5TS_api_info_p.api_lock, lock_count, acquired) < 0)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__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 + +#ifdef H5_HAVE_WIN_THREADS + /* Initialize the thread-safety code */ + if (H5_UNLIKELY(!H5_INIT_GLOBAL)) + InitOnceExecuteOnce(&H5TS_first_init_s, H5TS__win32_process_enter, NULL, NULL); +#else + /* Initialize the thread-safety code */ + if (H5_UNLIKELY(!H5_INIT_GLOBAL)) + pthread_once(&H5TS_first_init_s, H5TS__pthread_first_thread_init); +#endif /* H5_HAVE_WIN_THREADS */ + + /* Acquire the "attempt" mutex, increment the attempt lock count, release the lock */ + if (H5_UNLIKELY(H5TS_mutex_lock(&H5TS_api_info_p.attempt_mutex))) + HGOTO_DONE(FAIL); + H5TS_api_info_p.attempt_lock_count++; + if (H5_UNLIKELY(H5TS_mutex_unlock(&H5TS_api_info_p.attempt_mutex))) + HGOTO_DONE(FAIL); + + /* Acquire the library's exclusive API lock */ + if (H5_UNLIKELY(H5TS__ex_lock(&H5TS_api_info_p.api_lock) < 0)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_api_lock() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__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__mutex_release(unsigned *lock_count) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Release the library's exclusive API lock */ + if (H5_UNLIKELY(H5TS__ex_release(&H5TS_api_info_p.api_lock, lock_count) < 0)) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS__mutex_release */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_api_unlock + * + * Purpose: Decrements the global "API" lock counter for accessing the + * HDF5 library, releasing the lock when the counter reaches 0. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_api_unlock(void) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Release the library's exclusive API lock */ + if (H5_UNLIKELY(H5TS__ex_unlock(&H5TS_api_info_p.api_lock) < 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 */ + H5TS_mutex_init(&H5TS_tinfo_mtx_s); + + /* Initialize key for thread-specific API contexts */ +#ifdef H5_HAVE_WIN_THREADS + if (H5_UNLIKELY(TLS_OUT_OF_INDEXES == (H5TS_thrd_info_key_g = TlsAlloc()))) + ret_value = FAIL; +#else + if (H5_UNLIKELY(pthread_key_create(&H5TS_thrd_info_key_g, H5TS__tinfo_destroy))) + 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. + */ + H5TS_mutex_lock(&H5TS_tinfo_mtx_s); + + /* 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 */ + H5TS_mutex_unlock(&H5TS_tinfo_mtx_s); + + /* 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__set_thread_local_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) 1 <= ID <= UINT64_MAX + * 2) ID is constant over a thread's lifetime. + * 3) No two threads share an ID + * + * 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. + * + * Return: ID for the current thread on success / 0 on failure + * + *-------------------------------------------------------------------------- + */ +uint64_t +H5TS_thread_id(void) +{ + H5TS_tinfo_node_t *tinfo_node; + uint64_t ret_value; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Check if info for thread has been created */ + if (NULL == (tinfo_node = H5TS__get_thread_local_value(H5TS_thrd_info_key_g))) + /* Create thread info for this thread */ + if (H5_UNLIKELY(NULL == (tinfo_node = H5TS__tinfo_create()))) + HGOTO_DONE(0); + + /* Set return value */ + ret_value = 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 (NULL == (tinfo_node = H5TS__get_thread_local_value(H5TS_thrd_info_key_g))) + /* 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_t *) + * + * Return: Success: Non-NULL pointer to error stack for thread + * Failure: NULL + * + *-------------------------------------------------------------------------- + */ +H5E_t * +H5TS_get_err_stack(void) +{ + H5TS_tinfo_node_t *tinfo_node; + H5E_t *ret_value; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Check if info for thread has been created */ + if (NULL == (tinfo_node = H5TS__get_thread_local_value(H5TS_thrd_info_key_g))) + /* 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 */ + H5TS_mutex_lock(&H5TS_tinfo_mtx_s); + 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; + } + H5TS_mutex_unlock(&H5TS_tinfo_mtx_s); + + /* Release critical section / mutex for modifying the thread info globals */ + H5TS_mutex_destroy(&H5TS_tinfo_mtx_s); + + /* Release key for thread-specific API contexts */ +#ifdef H5_HAVE_WIN_THREADS + if (H5TS_thrd_info_key_g != TLS_OUT_OF_INDEXES) + if (H5_UNLIKELY(H5TS__key_delete(H5TS_thrd_info_key_g) == 0)) + ret_value = FAIL; +#else + if (H5_UNLIKELY(H5TS__key_delete(H5TS_thrd_info_key_g))) + ret_value = FAIL; +#endif + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) \ +} /* end H5TS__tinfo_term() */ + +#endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSkey.c b/src/H5TSkey.c new file mode 100644 index 00000000000..4076c5e8dce --- /dev/null +++ b/src/H5TSkey.c @@ -0,0 +1,344 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_THREADSAFE + +/****************/ +/* Local Macros */ +/****************/ + + +/******************/ +/* Local Typedefs */ +/******************/ +#ifdef H5_HAVE_WIN_THREADS + +/* Forward declarations of kwd node structs */ +struct _H5TS_win_kwd_tid_node_t; +struct _H5TS_win_kwd_node_t; + +/* Global list of all kwd's */ +typedef struct _H5TS_win_kwd_node_t { + struct _H5TS_win_kwd_node_t *next; + H5TS_key_t key; + H5TS_key_destructor_func_t dtor; + struct _H5TS_win_kwd_tid_node_t *head_tid_node; +} H5TS_win_kwd_node_t; + +/* Sub-list of all threads that have set a value for a kwd */ +typedef struct _H5TS_win_kwd_tid_node_t { + struct _H5TS_win_kwd_tid_node_t *next; + struct _H5TS_win_kwd_tid_node_t *prev; + uint64_t tid; + H5TS_win_kwd_node_t *kwd_node; +} H5TS_win_kwd_tid_node_t; +#endif + + + +/********************/ +/* Local Prototypes */ +/********************/ +#ifdef H5_HAVE_WIN_THREADS +static herr_t H5TS__add_kwd(H5TS_key_t key, H5TS_key_destructor_func_t dtor); +static herr_t H5TS__set_kwd(H5TS_key_t key, const void *value); +#endif + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + +#ifdef H5_HAVE_WIN_THREADS +/* Per-thread "key with destructor" ("kwd") info */ +static H5TS_key_t H5TS_win_kwd_info_key_s; + +/* Pointer to global list of thread-specific kwd's */ +static H5TS_win_kwd_node_t *H5TS_win_kwd_list_head_s = NULL; + +/* Mutices for access to H5TS_win_kwd_list_head_s & its sub-lists */ +static H5TS_mutex_t H5TS_win_kwd_list_mtx_s; +static H5TS_mutex_t H5TS_win_kwd_sublist_mtx_s; +#endif + + +#ifdef H5_HAVE_WIN_THREADS +/*-------------------------------------------------------------------------- + * Function: H5TS__win_kwd_init + * + * Purpose: Initialize thread-local "key with destructors" infrastructure + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS__win_kwd_init(void) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + + /* Initialize the mutices for modifying the kwd list & sub-list */ + H5TS_mutex_init(&H5TS_win_kwd_list_mtx_s); + H5TS_mutex_init(&H5TS_win_kwd_sublist_mtx_s); + + /* Initialize "base" key for all the thread-specific "keys w/dtors" lists */ + if (H5_UNLIKELY(TLS_OUT_OF_INDEXES == (H5TS_win_kwd_info_key_s = TlsAlloc()))) + ret_value = FAIL; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) \ +} /* end H5TS__win_kwd_init() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__add_kwd + * + * Purpose: Add a newly created key w/dtor to the global list of keys + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +static herr_t +H5TS__add_kwd(H5TS_key_t key, H5TS_key_destructor_func_t dtor) +{ + H5TS_win_kwd_node_t *kwd_node; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity checks */ + assert(dtor); + + /* Create the kwd node for the key */ + if (H5_UNLIKELY(NULL == (kwd_node = H5MM_malloc(sizeof(*kwd_node))))) + HGOTO_DONE(FAIL); + kwd_node->key = key; + kwd_node->dtor = dtor; + + /* Acquire the lock for accessing the kwd list */ + H5TS_mutex_lock(&H5TS_win_kwd_list_mtx_s); + +#ifdef H5TS_DEBUG + { + H5TS_win_kwd_node_t *tmp_kwd_node; + + /* Sanity check that the key isn't already in the list */ + tmp_kwd_node = H5TS_win_kwd_list_head_s; + while (NULL != tmp_kwd_node) { + if (H5_UNLIKELY(key == tmp_kwd_node->key)) + HGOTO_DONE(FAIL); + tmp_kwd_node = tmp_kwd_node->next; + } + } +#endif + + /* Add the kwd node to the list */ + kwd_node->next = H5TS_win_kwd_list_head_s; + H5TS_win_kwd_list_head_s = kwd_node; + + /* Release the lock for accessing the kwd list */ + H5TS_mutex_unlock(&H5TS_win_kwd_list_mtx_s); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) \ +} /* H5TS__add_kwd() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS__set_kwd + * + * Purpose: Add a newly set key to the list of keys w/dtors for a thread + * (if the key has a dtor) + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +static herr_t +H5TS__set_kwd(H5TS_key_t key, const void *value) +{ + H5TS_win_kwd_node_t *kwd_node; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Sanity checks */ + assert(value); + + /* Acquire the lock for accessing the kwd list */ + H5TS_mutex_lock(&H5TS_win_kwd_list_mtx_s); + + /* Search the kwd list for the key */ + kwd_node = H5TS_win_kwd_list_head_s; + while (NULL != kwd_node) { + if (key == kwd_node->key) + break; + kwd_node = kwd_node->next; + } + + /* Release the lock for accessing the kwd list */ + H5TS_mutex_unlock(&H5TS_win_kwd_list_mtx_s); + + /* Check if this thread has been inserted already */ + if (NULL != kwd_node) { + H5TS_win_kwd_tid_node_t *kwd_tid_node; + uint64_t thread_id; + + /* Get the ID for this thread */ + if (H5_UNLIKELY(0 == (thread_id = H5TS_thread_id()))) + HGOTO_DONE(FAIL); + + /* Acquire the lock for accessing the kwd sub-list */ + H5TS_mutex_lock(&H5TS_win_kwd_sublist_mtx_s); + + kwd_tid_node = kwd_node->head_tid_node; + while (NULL != kwd_tid_node) { + if (thread_id == kwd_tid_node->tid) + break; + kwd_tid_node = kwd_tid_node->next; + } + + /* Release the lock for accessing the kwd sub-list */ + H5TS_mutex_unlock(&H5TS_win_kwd_sublist_mtx_s); + + /* If this thread isn't in the kwd tid sub-list, add it */ + if (NULL == kwd_tid_node) { + /* Create the kwd tid node for the thread */ + if (H5_UNLIKELY(NULL == (kwd_tid_node = H5MM_calloc(sizeof(*kwd_tid_node))))) + HGOTO_DONE(FAIL); + kwd_tid_node->tid = thread_id; + kwd_tid_node->kwd_node = kwd_node; + + /* Acquire both locks for accessing the kwd list & sub-lists */ + H5TS_mutex_lock(&H5TS_win_kwd_list_mtx_s); + H5TS_mutex_lock(&H5TS_win_kwd_sublist_mtx_s); + + /* Insert the new kwd tid node in the sub-list */ + kwd_tid_node->next = kwd_node->head_tid_node; + if (NULL != kwd_node->head_tid_node) + kwd_node->head_tid_node->prev = kwd_tid_node; + kwd_node->head_tid_node = kwd_tid_node; + + /* Release both locks for accessing the kwd list & sub-lists */ + H5TS_mutex_unlock(&H5TS_win_kwd_sublist_mtx_s); + H5TS_mutex_unlock(&H5TS_win_kwd_list_mtx_s); + } + } + + /* Add the kwd node to the list */ + kwd_node->next = H5TS_win_kwd_list_head_s; + H5TS_win_kwd_list_head_s = kwd_node; + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) \ +} /* H5TS__add_kwd() */ + +/*------------------------------------------------------------------------- + * 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 + + if (H5_UNLIKELY(NULL == key)) + HGOTO_DONE(FAIL); + + /* Create the key */ + if (H5_UNLIKELY(TLS_OUT_OF_INDEXES == (*key = TlsAlloc()))) + HGOTO_DONE(FAIL); + + /* If the key has a destructor callback, add it to the list of keys w/dtors */ + if (NULL != dtor) + if (H5TS__add_kwd(*key, dtor) < 0) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_key_create() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_key_set_value + * + * Purpose: Set a thread-specific value for a key + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_key_set_value(H5TS_key_t key, const void *value) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + /* Set the value for this thread */ + if (H5_UNLIKELY(0 != TlsSetValue(key, (LPVOID)value))) + HGOTO_DONE(FAIL); + + /* Add the key to the kwd list for this thread, if non-NULL */ + if (NULL != value) + if (H5_UNLIKELY(H5TS_set_kwd(key, value))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_key_create() */ + + + +#endif + +#endif /* H5_HAVE_THREADSAFE */ + diff --git a/src/H5TSmodule.h b/src/H5TSmodule.h new file mode 100644 index 00000000000..2e8bb1d92a6 --- /dev/null +++ b/src/H5TSmodule.h @@ -0,0 +1,28 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://www.hdfgroup.org/licenses. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* + * Purpose: This file contains declarations which define macros for the + * H5TS package. Including this header means that the source file + * is part of the H5TS package. + */ +#ifndef H5TSmodule_H +#define H5TSmodule_H + +/* Define the proper control macros for the generic FUNC_ENTER/LEAVE and error + * reporting macros. + */ +#define H5TS_MODULE +#define H5_MY_PKG H5TS +#define H5_MY_PKG_ERR H5E_THREADSAFE + +#endif /* H5TSmodule_H */ diff --git a/src/H5TSmutex.c b/src/H5TSmutex.c new file mode 100644 index 00000000000..a6cc711e97b --- /dev/null +++ b/src/H5TSmutex.c @@ -0,0 +1,292 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_THREADSAFE + +/****************/ +/* Local Macros */ +/****************/ +#define H5TS_NO_THREAD_SAFETY_ANALYSIS H5_ATTR_THREAD_ANNOT(no_thread_safety_analysis) + + +/******************/ +/* Local Typedefs */ +/******************/ + + +/********************/ +/* Local Prototypes */ +/********************/ + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + + + +#ifdef H5_HAVE_WIN_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) +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + InitializeCriticalSection(mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_mutex_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_lock + * + * Purpose: Lock a H5TS_mutex_t + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_lock(H5TS_mutex_t *mutex) H5TS_NO_THREAD_SAFETY_ANALYSIS +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + EnterCriticalSection(mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_mutex_lock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_try_lock + * + * 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_try_lock(H5TS_mutex_t *mutex, bool *acquired) H5TS_NO_THREAD_SAFETY_ANALYSIS +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + *acquired = TryEnterCriticalSection(mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_mutex_try_lock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_unlock + * + * Purpose: Unlock a H5TS_mutex_t + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_unlock(H5TS_mutex_t *mutex) H5TS_NO_THREAD_SAFETY_ANALYSIS +{ + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + LeaveCriticalSection(mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* end H5TS_mutex_unlock() */ + +/*------------------------------------------------------------------------- + * 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) +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_mutex_init(mutex, NULL))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_mutex_init() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_lock + * + * Purpose: Lock a H5TS_mutex_t + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_lock(H5TS_mutex_t *mutex) H5TS_NO_THREAD_SAFETY_ANALYSIS +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_mutex_lock(mutex))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_mutex_lock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_try_lock + * + * 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_try_lock(H5TS_mutex_t *mutex, bool *acquired) H5TS_NO_THREAD_SAFETY_ANALYSIS +{ + 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 { + assert(EINVAL == rc); + HGOTO_DONE(FAIL); + } + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_mutex_try_lock() */ + +/*------------------------------------------------------------------------- + * Function: H5TS_mutex_unlock + * + * Purpose: Unlock a H5TS_mutex_t + * + * Return: Non-negative on success / Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5TS_mutex_unlock(H5TS_mutex_t *mutex) H5TS_NO_THREAD_SAFETY_ANALYSIS +{ + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NAMECHECK_ONLY + + if (H5_UNLIKELY(pthread_mutex_unlock(mutex))) + HGOTO_DONE(FAIL); + +done: + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS_mutex_unlock() */ + +/*------------------------------------------------------------------------- + * 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 /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSpkg.h b/src/H5TSpkg.h new file mode 100644 index 00000000000..97d478b6587 --- /dev/null +++ b/src/H5TSpkg.h @@ -0,0 +1,370 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_THREADSAFE +/* Get package's private header */ +#include "H5TSprivate.h" + +/* Other private headers needed by this file */ + +/**************************/ +/* Package Private Macros */ +/**************************/ + +#ifdef H5_HAVE_WIN_THREADS + +/* Scope Definitions (Pthreads only) */ +#define H5TS_SCOPE_SYSTEM 0 +#define H5TS_SCOPE_PROCESS 0 + +/* 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__key_delete(key) TlsFree(key) +#define H5TS__attr_init(attr) 0 +#define H5TS__attr_setscope(attr, scope) 0 +#define H5TS__attr_destroy(attr) 0 +#define H5TS__wait_for_thread(thread) WaitForSingleObject(thread, INFINITE) + +#else /* H5_HAVE_WIN_THREADS */ + +/* Scope Definitions (Pthreads only) */ +#define H5TS_SCOPE_SYSTEM PTHREAD_SCOPE_SYSTEM +#define H5TS_SCOPE_PROCESS PTHREAD_SCOPE_PROCESS + +/* 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__key_delete(key) pthread_key_delete(key) +#define H5TS__attr_init(attr) pthread_attr_init(attr) +#define H5TS__attr_setscope(attr, scope) pthread_attr_setscope(attr, scope) +#define H5TS__attr_destroy(attr) pthread_attr_destroy(attr) +#define H5TS__wait_for_thread(thread) pthread_join(thread, NULL) + +#endif /* H5_HAVE_WIN_THREADS */ + + +/****************************/ +/* Package Private Typedefs */ +/****************************/ + +/* Portability wrappers */ +#ifdef H5_HAVE_WIN_THREADS + +typedef HANDLE H5TS_attr_t; +typedef INIT_ONCE H5TS_once_t; + +#else + +typedef pthread_attr_t H5TS_attr_t; +typedef pthread_once_t H5TS_once_t; + +#endif /* H5_HAVE_WIN_THREADS */ + +/* Recursive exclusive locks */ +typedef struct H5TS_ex_lock_t { + H5TS_mutex_t mutex; + H5TS_cond_t cond_var; + H5TS_thread_t owner_thread; + unsigned lock_count; + +/* Thread cancellability only supported with pthreads */ +#ifdef H5_HAVE_PTHREAD_H + /* Cancellation control */ + bool disable_cancel; + int previous_state; +#endif /* H5_HAVE_PTHREAD_H */ +} H5TS_ex_lock_t; + +/* Thread Barrier */ +#ifdef H5_HAVE_PTHREAD_BARRIER +typedef pthread_barrier_t H5TS_barrier_t; +#else +typedef struct H5TS_barrier_t { + H5TS_mutex_t mutex; + H5TS_cond_t cv; + uint64_t count; + uint64_t entered; + uint64_t threshold; +} H5TS_barrier_t; +#endif + +/* Info for the global API lock */ +typedef struct H5TS_api_info_t { + /* API lock */ + H5TS_ex_lock_t api_lock; + + /* Count of # of attempts to acquire API lock */ + H5TS_mutex_t attempt_mutex; /* mutex for attempt_lock_count */ + unsigned attempt_lock_count; +} H5TS_api_info_t; + +#ifdef H5_HAVE_WIN_THREADS + +#else + +/* Enable statistics 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 + +#if H5TS_ENABLE_REC_RW_LOCK_STATS +/****************************************************************************** + * + * Structure H5TS_rw_lock_stats_t + * + * Catchall structure for statistics on the recursive p-threads based + * recursive R/W lock (see declaration of H5TS_rw_lock_t below). + * + * Since the mutex must be held when reading a consistent set of statistics + * from the recursibe R/W lock, it simplifies matters to bundle them into + * a single structure. This structure exists for that purpose. + * + * If you modify this structure, be sure to make equivalent changes to + * the reset_stats code in H5TS__rw_lock_reset_stats(). + * + * 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 readers / writer (R/W) lock is a lock that allows either an arbitrary + * number of readers, or a single writer into a critical region. A recursive + * lock is one that allows a thread that already holds a lock (read or + * write) to successfully request the lock again, only dropping the lock + * when the number of unlock calls equals the number of lock calls. + * + * 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. + * + * This recursive R/W lock implementation is an extension of the R/W lock + * implementation given in "UNIX network programming" Volume 2, Chapter 8 + * by w. Richard Stevens, 2nd edition. + * + * 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. + * + * active_reader_threads: 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. + * + * stats: Instance of H5TS_rw_lock_stats_t used to track + * statistics on the recursive R/W lock. + * + ******************************************************************************/ + +typedef enum { + UNUSED = 0, /* Lock is currently unused */ + WRITE, /* Lock is a recursive write 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 */ + bool is_key_registered; + H5TS_cond_t readers_cv; + int32_t active_reader_threads; + H5TS_key_t rec_read_lock_count_key; + +#if H5TS_ENABLE_REC_RW_LOCK_STATS + /* Stats */ + H5TS_rw_lock_stats_t stats; +#endif +} H5TS_rw_lock_t; + +#endif /* H5_HAVE_WIN_THREADS */ + + +/*****************************/ +/* Package Private Variables */ +/*****************************/ + +/* API threadsafety info */ +extern H5TS_api_info_t H5TS_api_info_p; + +/* Per-thread info */ +extern H5TS_key_t H5TS_thrd_info_key_g; + +/******************************/ +/* Package Private Prototypes */ +/******************************/ +herr_t H5TS__mutex_acquire(unsigned lock_count, bool *acquired); +herr_t H5TS__mutex_release(unsigned *lock_count); +herr_t H5TS__tinfo_init(void); +void H5TS__tinfo_destroy(void *tinfo_node); +herr_t H5TS__tinfo_term(void); + +#ifdef H5_HAVE_WIN_THREADS + +/* Functions called from DllMain */ +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 */ + +/* Thread-specific key routines */ +#ifdef H5_HAVE_WIN_THREADS +H5_DLL herr_t H5TS__win_kwd_init(void); +#endif + +/* 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); + +/* Recursive exclusive lock related function declarations */ +H5_DLL herr_t H5TS__ex_lock_init(H5TS_ex_lock_t *lock, bool disable_cancel); +H5_DLL herr_t H5TS__ex_lock(H5TS_ex_lock_t *lock); +H5_DLL herr_t H5TS__ex_acquire(H5TS_ex_lock_t *lock, unsigned lock_count, bool *acquired); +H5_DLL herr_t H5TS__ex_release(H5TS_ex_lock_t *lock, unsigned int *lock_count); +H5_DLL herr_t H5TS__ex_unlock(H5TS_ex_lock_t *lock); +H5_DLL herr_t H5TS__ex_lock_destroy(H5TS_ex_lock_t *lock); + +/* Barrier related function declarations */ +H5_DLL herr_t H5TS__barrier_init(H5TS_barrier_t *barrier, uint64_t count); +H5_DLL herr_t H5TS__barrier_wait(H5TS_barrier_t *barrier); +H5_DLL herr_t H5TS__barrier_destroy(H5TS_barrier_t *barrier); + +#ifdef H5TS_TESTING +#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 + +/* Testing routines */ +H5_DLL H5TS_thread_t H5TS__create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata); + +#endif /* H5TS_TESTING */ + +#endif /* H5_HAVE_THREADSAFE */ + +#endif /* H5TSpkg_H */ diff --git a/src/H5TSprivate.h b/src/H5TSprivate.h index e24c409437c..56da21d6f12 100644 --- a/src/H5TSprivate.h +++ b/src/H5TSprivate.h @@ -25,111 +25,105 @@ /* Include package's public headers */ #include "H5TSdevelop.h" +/**************************/ +/* 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__)) + +/* Static initialization values */ #ifdef H5_HAVE_WIN_THREADS +#define H5TS_KEY_INITIALIZER {NULL, 0, NULL} +#define H5TS_MUTEX_INITIALIZER {NULL} +#define H5TS_COND_INITIALIZER CONDITION_VARIABLE_INIT +#else +#define H5TS_KEY_INITIALIZER (pthread_key_t)0 +#define H5TS_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +#define H5TS_COND_INITIALIZER PTHREAD_COND_INITIALIZER +#endif + +/* Thread macros */ +#ifdef H5_HAVE_WIN_THREADS +#define H5TS_thread_self() GetCurrentThread() +#define H5TS_thread_equal(t1, t2) (GetThreadId(t1) == GetThreadId(t2)) +#else +#define H5TS_thread_self() pthread_self() +#define H5TS_thread_equal(t1, t2) pthread_equal(t1, t2) +#endif -/* 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); +/****************************/ +/* Library Private Typedefs */ +/****************************/ -#endif /* H5_HAVE_WIN_THREADS */ +/* Key destructor callback */ +typedef void (*H5TS_key_destructor_func_t)(void *); -/* 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 */ +/* Portability aliases */ +#ifdef H5_HAVE_WIN_THREADS +typedef HANDLE H5TS_thread_t; +typedef DWORD H5TS_key_t; +typedef CRITICAL_SECTION H5TS_CAPABILITY("mutex") H5TS_mutex_t; +typedef CONDITION_VARIABLE H5TS_cond_t; +#else +typedef pthread_t H5TS_thread_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; +#endif + +/*****************************/ +/* Library-private Variables */ +/*****************************/ + + +/***************************************/ +/* Library-private Function Prototypes */ +/***************************************/ + +/* Library/thread init/term operations */ +H5_DLL void H5TS_term_package(void); +#ifdef H5_HAVE_WIN_THREADS +H5_DLL herr_t H5TS_win32_thread_enter(void); +H5_DLL herr_t H5TS_win32_thread_exit(void); +#endif /* H5_HAVE_WIN_THREADS */ -/* 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); +/* API locking */ +H5_DLL herr_t H5TS_api_lock(void); +H5_DLL herr_t H5TS_api_unlock(void); -/* Testing routines */ -H5_DLL H5TS_thread_t H5TS_create_thread(void *(*func)(void *), H5TS_attr_t *attr, void *udata); +/* Retrieve per-thread info */ +H5_DLL uint64_t H5TS_thread_id(void); +H5_DLL struct H5CX_node_t **H5TS_get_api_ctx_ptr(void); +H5_DLL struct H5E_t *H5TS_get_err_stack(void); + +/* Mutex operations */ +H5_DLL herr_t H5TS_mutex_init(H5TS_mutex_t *mutex); +H5_DLL herr_t H5TS_mutex_lock(H5TS_mutex_t *mutex) H5TS_ACQUIRE(*mutex); +H5_DLL herr_t H5TS_mutex_try_lock(H5TS_mutex_t *mutex, bool *acquired) H5TS_TRY_ACQUIRE(SUCCEED, *mutex); +H5_DLL herr_t H5TS_mutex_unlock(H5TS_mutex_t *mutex) H5TS_RELEASE(*mutex); +H5_DLL herr_t H5TS_mutex_destroy(H5TS_mutex_t *mutex); + +/* Condition variable operations */ +H5_DLL herr_t H5TS_cond_init(H5TS_cond_t *cond); +H5_DLL herr_t H5TS_cond_wait(H5TS_cond_t *cond, H5TS_mutex_t *mutex); +H5_DLL herr_t H5TS_cond_signal(H5TS_cond_t *cond); +H5_DLL herr_t H5TS_cond_broadcast(H5TS_cond_t *cond); +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); +H5_DLL herr_t H5TS_key_set_value(H5TS_key_t key, const void *value); #else /* H5_HAVE_THREADSAFE */ +/* Non-threadsafe code needs this */ #define H5TS_thread_id() ((uint64_t)0) #endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSpthread.c b/src/H5TSpthread.c new file mode 100644 index 00000000000..a0c3352128c --- /dev/null +++ b/src/H5TSpthread.c @@ -0,0 +1,791 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 */ +#define H5TS_TESTING /* Suppress warning about H5TS testing funcs */ + +/***********/ +/* Headers */ +/***********/ +#include "H5private.h" /* Generic Functions */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5TSpkg.h" /* Threadsafety */ + +#ifdef H5_HAVE_THREADSAFE + +#ifndef H5_HAVE_WIN_THREADS + +/****************/ +/* Local Macros */ +/****************/ + +/* R/W lock initialization macro */ +#if H5TS_ENABLE_REC_RW_LOCK_STATS +#define H5TS_RW_LOCK_INIT { \ + PTHREAD_MUTEX_INITIALIZER, /* mutex */ \ + (H5TS_rw_lock_type_t)UNUSED, /* lock_type */ \ + PTHREAD_COND_INITIALIZER, /* writers_cv */ \ + 0, /* write_thread */ \ + 0, /* rec_write_lock_count */ \ + 0, /* waiting_writers_count */ \ + false, /* is_key_registered */ \ + PTHREAD_COND_INITIALIZER, /* readers_cv */ \ + 0, /* active_reader_threads */ \ + (H5TS_key_t)0, /* rec_read_lock_count_key */ \ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* stats */ \ + } +#else +#define H5TS_RW_LOCK_INIT { \ + PTHREAD_MUTEX_INITIALIZER, /* mutex */ \ + (H5TS_rw_lock_type_t)UNUSED, /* lock_type */ \ + PTHREAD_COND_INITIALIZER, /* writers_cv */ \ + 0, /* write_thread */ \ + 0, /* rec_write_lock_count */ \ + 0, /* waiting_writers_count */ \ + false, /* is_key_registered */ \ + PTHREAD_COND_INITIALIZER, /* readers_cv */ \ + 0, /* active_reader_threads */ \ + (H5TS_key_t)0 /* rec_read_lock_count_key */ \ + } +#endif + + +/******************/ +/* Local Typedefs */ +/******************/ + +/****************************************************************************** + * + * Structure H5TS_rec_read_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 decrements when the thread-specific count drops to zero. + * + * Individual fields are: + * + * rec_lock_count: Count of the number of recursive lock calls, less + * the number of recursive unlock calls. The lock in question + * is decremented when the count drops to zero. + * + ******************************************************************************/ + +typedef struct H5TS_rec_entry_count { + int64_t rec_lock_count; +} H5TS_rec_entry_count_t; + + +/********************/ +/* Local Prototypes */ +/********************/ + +static void H5TS__key_destructor(void *key_val); + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + +/* Default value to initialize R/W locks */ +static const H5TS_rw_lock_t H5TS_rw_lock_def = H5TS_RW_LOCK_INIT; + + +/*-------------------------------------------------------------------------- + * Function: H5TS__key_destructor + * + * Purpose: 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. + * + * Return: None + * + *-------------------------------------------------------------------------- + */ +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 (NULL != key_val) + free(key_val); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__key_destructor() */ + +/*-------------------------------------------------------------------------- + * 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 global API lock */ + H5TS__ex_lock_init(&H5TS_api_info_p.api_lock, true); + + /* Initialize the "lock acquisition attempt" mutex & counter */ + H5TS_mutex_init(&H5TS_api_info_p.attempt_mutex); + H5TS_api_info_p.attempt_lock_count = 0; + + /* Set up thread-local thread-info struct */ + H5TS__tinfo_init(); + + FUNC_LEAVE_NOAPI_VOID_NAMECHECK_ONLY +} /* end H5TS__pthread_first_thread_init() */ + +#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(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->active_reader_threads > rw_lock->stats.max_read_locks) + rw_lock->stats.max_read_locks = rw_lock->active_reader_threads; + } + + 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(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(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(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 (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + 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))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Reset stats */ + memset(&rw_lock->stats, 0, sizeof(rw_lock->stats)); + +done: + if (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + 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); + + /* Initialize the lock */ + memcpy(rw_lock, &H5TS_rw_lock_def, sizeof(H5TS_rw_lock_def)); + +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 pthread 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))) + ret_value = FAIL; + if (H5_UNLIKELY(H5TS_cond_destroy(&rw_lock->readers_cv))) + ret_value = FAIL; + if (H5_UNLIKELY(H5TS_cond_destroy(&rw_lock->writers_cv))) + ret_value = FAIL; + if (rw_lock->is_key_registered) + if (H5_UNLIKELY(H5TS__key_delete(rw_lock->rec_read_lock_count_key))) + 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_id = 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))) + 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(WRITE == rw_lock->lock_type && pthread_equal(my_thread_id, 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(pthread_key_create(&rw_lock->rec_read_lock_count_key, H5TS__key_destructor))) + HGOTO_DONE(FAIL); + rw_lock->is_key_registered = true; + count = NULL; + } + else + count = (H5TS_rec_entry_count_t *)H5TS__get_thread_local_value(rw_lock->rec_read_lock_count_key); + if (NULL == count) { + if (H5_UNLIKELY(NULL == (count = calloc(1, sizeof(*count))))) + HGOTO_DONE(FAIL); + + if (H5_UNLIKELY(H5TS__set_thread_local_value(rw_lock->rec_read_lock_count_key, (void *)count))) + HGOTO_DONE(FAIL); + } + + if (count->rec_lock_count > 0) { /* This is a recursive lock */ + assert(READ == rw_lock->lock_type); + assert(rw_lock->active_reader_threads > 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 (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))) + HGOTO_DONE(FAIL); + } while (WRITE == rw_lock->lock_type); + } + + /* Set counter's lock type (which might already be set) & increment + * number of reader threads + */ + rw_lock->lock_type = READ; + rw_lock->active_reader_threads++; + } + + /* 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 (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + 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_id = 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))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Check for initial write lock request on this thread */ + if (WRITE != rw_lock->lock_type || !pthread_equal(my_thread_id, rw_lock->write_thread)) { + /* Fail if attempting to acquire a write lock on a thread that holds + * a read lock + */ + if (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 */ + count = (H5TS_rec_entry_count_t *)H5TS__get_thread_local_value(rw_lock->rec_read_lock_count_key); + if (H5_UNLIKELY(NULL != count && count->rec_lock_count > 0)) + HGOTO_DONE(FAIL); + } + + /* If lock is already held, wait to acquire it */ + if (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 (UNUSED != rw_lock->lock_type); + } + + /* Set lock type & owner thread */ + rw_lock->lock_type = WRITE; + rw_lock->write_thread = my_thread_id; + } + + /* 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 (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + 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))) + HGOTO_DONE(FAIL); + have_mutex = true; + + /* Error check */ + if (H5_UNLIKELY(UNUSED == rw_lock->lock_type)) /* Unlocking an unused lock? */ + HGOTO_DONE(FAIL); + + if (WRITE == rw_lock->lock_type) { /* Drop a write lock */ + /* Sanity checks */ + assert(0 == rw_lock->active_reader_threads); + 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 = 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->active_reader_threads > 0); + assert(0 == rw_lock->rec_write_lock_count); + count = (H5TS_rec_entry_count_t *)H5TS__get_thread_local_value(rw_lock->rec_read_lock_count_key); + 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->active_reader_threads--; + + /* Check if lock is unused now */ + if (0 == rw_lock->active_reader_threads) + rw_lock->lock_type = UNUSED; + } + } + + /* Signal condition variable if lock is unused now */ + if (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))) + HGOTO_DONE(FAIL); + } + else { + if (H5_UNLIKELY(H5TS_cond_broadcast(&rw_lock->readers_cv))) + HGOTO_DONE(FAIL); + } + } + +done: + if (have_mutex) + H5TS_mutex_unlock(&rw_lock->mutex); + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* end H5TS__rw_unlock() */ + +#endif /* H5_HAVE_WIN_THREADS */ + +#endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSrwlock.c b/src/H5TSrwlock.c new file mode 100644 index 00000000000..a454c25facb --- /dev/null +++ b/src/H5TSrwlock.c @@ -0,0 +1,71 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_THREADSAFE + +/****************/ +/* Local Macros */ +/****************/ + + +/******************/ +/* Local Typedefs */ +/******************/ + + +/********************/ +/* Local Prototypes */ +/********************/ + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + + + +#endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TStest.c b/src/H5TStest.c new file mode 100644 index 00000000000..3ddbab9c92f --- /dev/null +++ b/src/H5TStest.c @@ -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: Threadsafety testing functions + * + * 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 */ +#define H5TS_TESTING /* Suppress warning about H5TS testing funcs */ + +/***********/ +/* Headers */ +/***********/ +#include "H5private.h" /* Generic Functions */ +#include "H5TSpkg.h" /* Threadsafety */ + +#ifdef H5_HAVE_THREADSAFE + +/****************/ +/* Local Macros */ +/****************/ + +/******************/ +/* Local Typedefs */ +/******************/ + +/* Function pointer typedef for thread callback function */ +typedef void *(*H5TS_thread_cb_t)(void *); + +/********************/ +/* Local Prototypes */ +/********************/ + +/*********************/ +/* Package Variables */ +/*********************/ + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + +/*******************/ +/* Local Variables */ +/*******************/ + + +/*-------------------------------------------------------------------------- + * NAME + * H5TS__create_thread + * + * RETURNS + * Thread identifier. + * + * DESCRIPTION + * Spawn off a new thread calling function 'func' with input 'udata'. + * + *-------------------------------------------------------------------------- + */ +H5TS_thread_t +H5TS__create_thread(H5TS_thread_cb_t func, H5TS_attr_t *attr, void *udata) +{ + H5TS_thread_t ret_value; + + FUNC_ENTER_PACKAGE_NAMECHECK_ONLY + +#ifdef H5_HAVE_WIN_THREADS + /* When calling C runtime functions, you should use _beginthread or + * _beginthreadex instead of CreateThread. Threads created with + * CreateThread risk being killed in low-memory situations. Since we + * only create threads in our test code, this is unlikely to be an issue + * and we'll use the easier-to-deal-with CreateThread for now. + * + * NOTE: _beginthread() auto-recycles its handle when execution completes + * so you can't wait on it, making it unsuitable for the existing + * test code. + */ + ret_value = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, udata, 0, NULL); + +#else /* H5_HAVE_WIN_THREADS */ + + pthread_create(&ret_value, attr, (void *(*)(void *))func, udata); + +#endif /* H5_HAVE_WIN_THREADS */ + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS__create_thread */ + +#endif /* H5_HAVE_THREADSAFE */ + diff --git a/src/H5TSthread.c b/src/H5TSthread.c new file mode 100644 index 00000000000..209d67e1339 --- /dev/null +++ b/src/H5TSthread.c @@ -0,0 +1,69 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_THREADSAFE + +/****************/ +/* Local Macros */ +/****************/ + + +/******************/ +/* Local Typedefs */ +/******************/ + + +/********************/ +/* Local Prototypes */ +/********************/ + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + + + +#endif /* H5_HAVE_THREADSAFE */ diff --git a/src/H5TSwin.c b/src/H5TSwin.c new file mode 100644 index 00000000000..18c306ab1a5 --- /dev/null +++ b/src/H5TSwin.c @@ -0,0 +1,164 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_THREADSAFE + +#ifdef H5_HAVE_WIN_THREADS + +/****************/ +/* Local Macros */ +/****************/ + + +/******************/ +/* Local Typedefs */ +/******************/ + + +/********************/ +/* Local Prototypes */ +/********************/ + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + + +/*-------------------------------------------------------------------------- + * 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 global API lock */ + H5TS__ex_lock_init(&H5TS_api_info_p.api_lock, true); + + /* Initialize the global "lock acquisition attempt" critical section & counter */ + H5TS_mutex_init(&H5TS_api_info_p.attempt_mutex); + H5TS_api_info_p.attempt_lock_count = 0; + + /* Set up per-thread key infrastructure */ + if (H5_UNLIKELY(H5TS__win_kwd_init() < 0)) + ret_value = FALSE; + + /* Initialize per-thread library info */ + if (H5_UNLIKELY(H5TS__tinfo_init() < 0)) + ret_value = FALSE; + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(ret_value) +} /* H5TS__win32_process_enter() */ + +/*-------------------------------------------------------------------------- + * Function: H5TS_win32_thread_enter + * + * Purpose: Per-thread setup on Windows when using Win32 threads. + * + * Return: Non-negative on success / Negative on failure + * + *-------------------------------------------------------------------------- + */ +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 + * + *-------------------------------------------------------------------------- + */ +herr_t +H5TS_win32_thread_exit(void) +{ + 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 = H5TS__get_thread_local_value(H5TS_thrd_info_key_g); + if (lpvData) + H5TS__tinfo_destroy(lpvData); + } + + FUNC_LEAVE_NOAPI_NAMECHECK_ONLY(SUCCEED) +} /* H5TS_win32_thread_exit() */ + +#endif /* H5_HAVE_WIN_THREADS */ + +#endif /* H5_HAVE_THREADSAFE */ + + diff --git a/src/H5Tnative.c b/src/H5Tnative.c index 0e7d395bbc4..48f13debe5b 100644 --- a/src/H5Tnative.c +++ b/src/H5Tnative.c @@ -715,10 +715,12 @@ H5T__get_native_float(size_t size, H5T_direction_t direction, size_t *struct_ali match = H5T_NATIVE_FLOAT_MATCH_DOUBLE; native_size = sizeof(double); } +#if H5_SIZEOF_LONG_DOUBLE != H5_SIZEOF_DOUBLE else if (size <= sizeof(long double)) { match = H5T_NATIVE_FLOAT_MATCH_LDOUBLE; native_size = sizeof(long double); } +#endif else { /* If not match, return the biggest datatype */ match = H5T_NATIVE_FLOAT_MATCH_LDOUBLE; native_size = sizeof(long double); diff --git a/src/H5VLcallback.c b/src/H5VLcallback.c index 0d7cd1b96bf..3ec70ab04b6 100644 --- a/src/H5VLcallback.c +++ b/src/H5VLcallback.c @@ -221,7 +221,7 @@ H5VLinitialize(hid_t connector_id, hid_t vipl_id) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("e", "ii", connector_id, vipl_id); /* Check args */ @@ -233,7 +233,7 @@ H5VLinitialize(hid_t connector_id, hid_t vipl_id) HGOTO_ERROR(H5E_VOL, H5E_CANTCLOSEOBJ, FAIL, "VOL connector did not initialize"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLinitialize() */ /*------------------------------------------------------------------------- @@ -252,7 +252,7 @@ H5VLterminate(hid_t connector_id) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE1("e", "i", connector_id); /* Check args */ @@ -264,7 +264,7 @@ H5VLterminate(hid_t connector_id) HGOTO_ERROR(H5E_VOL, H5E_CANTCLOSEOBJ, FAIL, "VOL connector did not terminate cleanly"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLterminate() */ /*--------------------------------------------------------------------------- @@ -283,7 +283,7 @@ H5VLget_cap_flags(hid_t connector_id, uint64_t *cap_flags /*out*/) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("e", "i*UL", connector_id, cap_flags); /* Check args */ @@ -295,7 +295,7 @@ H5VLget_cap_flags(hid_t connector_id, uint64_t *cap_flags /*out*/) *cap_flags = cls->cap_flags; done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLget_cap_flags */ /*--------------------------------------------------------------------------- @@ -314,7 +314,7 @@ H5VLget_value(hid_t connector_id, H5VL_class_value_t *value /*out*/) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("e", "i*VC", connector_id, value); /* Check args */ @@ -326,7 +326,7 @@ H5VLget_value(hid_t connector_id, H5VL_class_value_t *value /*out*/) *value = cls->value; done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLget_value */ /*------------------------------------------------------------------------- @@ -433,7 +433,7 @@ H5VLcopy_connector_info(hid_t connector_id, void **dst_vol_info, void *src_vol_i H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE3("e", "i**x*x", connector_id, dst_vol_info, src_vol_info); /* Check args and get class pointer */ @@ -445,7 +445,7 @@ H5VLcopy_connector_info(hid_t connector_id, void **dst_vol_info, void *src_vol_i HGOTO_ERROR(H5E_VOL, H5E_CANTCOPY, FAIL, "unable to copy VOL connector info object"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLcopy_connector_info() */ /*------------------------------------------------------------------------- @@ -595,7 +595,7 @@ H5VLfree_connector_info(hid_t connector_id, void *info) { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("e", "i*x", connector_id, info); /* Free the VOL connector info object */ @@ -603,7 +603,7 @@ H5VLfree_connector_info(hid_t connector_id, void *info) HGOTO_ERROR(H5E_VOL, H5E_CANTRELEASE, FAIL, "unable to release VOL connector info object"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLfree_connector_info() */ /*--------------------------------------------------------------------------- @@ -621,7 +621,7 @@ H5VLconnector_info_to_str(const void *info, hid_t connector_id, char **str) { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE3("e", "*xi**s", info, connector_id, str); /* Only serialize info object, if it's non-NULL */ @@ -644,7 +644,7 @@ H5VLconnector_info_to_str(const void *info, hid_t connector_id, char **str) *str = NULL; done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLconnector_info_to_str() */ /*--------------------------------------------------------------------------- @@ -662,7 +662,7 @@ H5VLconnector_str_to_info(const char *str, hid_t connector_id, void **info /*out { herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE3("e", "*si**x", str, connector_id, info); /* Call internal routine */ @@ -670,7 +670,7 @@ H5VLconnector_str_to_info(const char *str, hid_t connector_id, void **info /*out HGOTO_ERROR(H5E_VOL, H5E_CANTDECODE, FAIL, "can't deserialize connector info"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLconnector_str_to_info() */ /*--------------------------------------------------------------------------- @@ -689,7 +689,7 @@ H5VLget_object(void *obj, hid_t connector_id) H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("*x", "*xi", obj, connector_id); /* Check args */ @@ -705,7 +705,7 @@ H5VLget_object(void *obj, hid_t connector_id) ret_value = obj; done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLget_object */ /*------------------------------------------------------------------------- @@ -762,7 +762,7 @@ H5VLget_wrap_ctx(void *obj, hid_t connector_id, void **wrap_ctx /*out*/) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE3("e", "*xi**x", obj, connector_id, wrap_ctx); /* Check args and get class pointer */ @@ -774,7 +774,7 @@ H5VLget_wrap_ctx(void *obj, hid_t connector_id, void **wrap_ctx /*out*/) HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "unable to retrieve VOL connector object wrap context"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLget_wrap_ctx() */ /*------------------------------------------------------------------------- @@ -827,7 +827,7 @@ H5VLwrap_object(void *obj, H5I_type_t obj_type, hid_t connector_id, void *wrap_c H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("*x", "*xIti*x", obj, obj_type, connector_id, wrap_ctx); /* Check args and get class pointer */ @@ -841,7 +841,7 @@ H5VLwrap_object(void *obj, H5I_type_t obj_type, hid_t connector_id, void *wrap_c HGOTO_ERROR(H5E_VOL, H5E_CANTGET, NULL, "unable to wrap object"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLwrap_object */ /*------------------------------------------------------------------------- @@ -894,7 +894,7 @@ H5VLunwrap_object(void *obj, hid_t connector_id) H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("*x", "*xi", obj, connector_id); /* Check args and get class pointer */ @@ -908,7 +908,7 @@ H5VLunwrap_object(void *obj, hid_t connector_id) HGOTO_ERROR(H5E_VOL, H5E_CANTGET, NULL, "unable to unwrap object"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLunwrap_object */ /*------------------------------------------------------------------------- @@ -958,7 +958,7 @@ H5VLfree_wrap_ctx(void *wrap_ctx, hid_t connector_id) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("e", "*xi", wrap_ctx, connector_id); /* Check args and get class pointer */ @@ -970,7 +970,7 @@ H5VLfree_wrap_ctx(void *wrap_ctx, hid_t connector_id) HGOTO_ERROR(H5E_VOL, H5E_CANTRELEASE, FAIL, "unable to release VOL connector object wrap context"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* H5VLfree_wrap_ctx() */ /*------------------------------------------------------------------------- @@ -1059,7 +1059,7 @@ H5VLattr_create(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_ H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE10("*x", "*x*#i*siiiii**x", obj, loc_params, connector_id, name, type_id, space_id, acpl_id, aapl_id, dxpl_id, req); @@ -1075,7 +1075,7 @@ H5VLattr_create(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_ HGOTO_ERROR(H5E_VOL, H5E_CANTCREATE, NULL, "unable to create attribute"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLattr_create() */ /*------------------------------------------------------------------------- @@ -1162,7 +1162,7 @@ H5VLattr_open(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_id H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE7("*x", "*x*#i*sii**x", obj, loc_params, connector_id, name, aapl_id, dxpl_id, req); /* Check args and get class pointer */ @@ -1176,7 +1176,7 @@ H5VLattr_open(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_id HGOTO_ERROR(H5E_VOL, H5E_CANTOPENOBJ, NULL, "unable to open attribute"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLattr_open() */ /*------------------------------------------------------------------------- @@ -1259,7 +1259,7 @@ H5VLattr_read(void *obj, hid_t connector_id, hid_t mem_type_id, void *buf, hid_t H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*xii*xi**x", obj, connector_id, mem_type_id, buf, dxpl_id, req); /* Check args and get class pointer */ @@ -1273,7 +1273,7 @@ H5VLattr_read(void *obj, hid_t connector_id, hid_t mem_type_id, void *buf, hid_t HGOTO_ERROR(H5E_VOL, H5E_READERROR, FAIL, "unable to read attribute"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLattr_read() */ /*------------------------------------------------------------------------- @@ -1358,7 +1358,7 @@ H5VLattr_write(void *obj, hid_t connector_id, hid_t mem_type_id, const void *buf H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*xii*xi**x", obj, connector_id, mem_type_id, buf, dxpl_id, req); /* Check args and get class pointer */ @@ -1372,7 +1372,7 @@ H5VLattr_write(void *obj, hid_t connector_id, hid_t mem_type_id, const void *buf HGOTO_ERROR(H5E_VOL, H5E_WRITEERROR, FAIL, "unable to write attribute"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLattr_write() */ /*------------------------------------------------------------------------- @@ -1455,7 +1455,7 @@ H5VLattr_get(void *obj, hid_t connector_id, H5VL_attr_get_args_t *args, hid_t dx H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -1471,7 +1471,7 @@ H5VLattr_get(void *obj, hid_t connector_id, H5VL_attr_get_args_t *args, hid_t dx HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "unable to get attribute information"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLattr_get() */ /*------------------------------------------------------------------------- @@ -1560,7 +1560,7 @@ H5VLattr_specific(void *obj, const H5VL_loc_params_t *loc_params, hid_t connecto H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*x*#i*!i**x", obj, loc_params, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -1575,7 +1575,7 @@ H5VLattr_specific(void *obj, const H5VL_loc_params_t *loc_params, hid_t connecto HERROR(H5E_VOL, H5E_CANTOPERATE, "unable to execute attribute 'specific' callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLattr_specific() */ /*------------------------------------------------------------------------- @@ -1661,7 +1661,7 @@ H5VLattr_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, hid H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -1676,7 +1676,7 @@ H5VLattr_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, hid HERROR(H5E_VOL, H5E_CANTOPERATE, "unable to execute attribute optional callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLattr_optional() */ /*------------------------------------------------------------------------- @@ -1795,7 +1795,7 @@ H5VLattr_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*out*/) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xii**x", obj, connector_id, dxpl_id, req); /* Check args and get class pointer */ @@ -1809,7 +1809,7 @@ H5VLattr_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*out*/) HGOTO_ERROR(H5E_VOL, H5E_CANTCLOSEOBJ, FAIL, "unable to close attribute"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLattr_close() */ /*------------------------------------------------------------------------- @@ -1901,7 +1901,7 @@ H5VLdataset_create(void *obj, const H5VL_loc_params_t *loc_params, hid_t connect H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE11("*x", "*x*#i*siiiiii**x", obj, loc_params, connector_id, name, lcpl_id, type_id, space_id, dcpl_id, dapl_id, dxpl_id, req); @@ -1917,7 +1917,7 @@ H5VLdataset_create(void *obj, const H5VL_loc_params_t *loc_params, hid_t connect HGOTO_ERROR(H5E_VOL, H5E_CANTCREATE, NULL, "unable to create dataset"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdataset_create() */ /*------------------------------------------------------------------------- @@ -2004,7 +2004,7 @@ H5VLdataset_open(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE7("*x", "*x*#i*sii**x", obj, loc_params, connector_id, name, dapl_id, dxpl_id, req); /* Check args and get class pointer */ @@ -2018,7 +2018,7 @@ H5VLdataset_open(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector HGOTO_ERROR(H5E_VOL, H5E_CANTOPENOBJ, NULL, "unable to open dataset"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdataset_open() */ /*------------------------------------------------------------------------- @@ -2181,7 +2181,7 @@ H5VLdataset_read(size_t count, void *obj[], hid_t connector_id, hid_t mem_type_i size_t i; /* Local index variable */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE9("e", "z**xi*i*i*ii**x**x", count, obj, connector_id, mem_type_id, mem_space_id, file_space_id, dxpl_id, buf, req); @@ -2207,7 +2207,7 @@ H5VLdataset_read(size_t count, void *obj[], hid_t connector_id, hid_t mem_type_i HGOTO_ERROR(H5E_VOL, H5E_CANTINIT, FAIL, "unable to read dataset"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdataset_read() */ /*------------------------------------------------------------------------- @@ -2371,7 +2371,7 @@ H5VLdataset_write(size_t count, void *obj[], hid_t connector_id, hid_t mem_type_ size_t i; /* Local index variable */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE9("e", "z**xi*i*i*ii**x**x", count, obj, connector_id, mem_type_id, mem_space_id, file_space_id, dxpl_id, buf, req); @@ -2397,7 +2397,7 @@ H5VLdataset_write(size_t count, void *obj[], hid_t connector_id, hid_t mem_type_ HGOTO_ERROR(H5E_VOL, H5E_CANTINIT, FAIL, "unable to write dataset"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdataset_write() */ /*------------------------------------------------------------------------- @@ -2482,7 +2482,7 @@ H5VLdataset_get(void *obj, hid_t connector_id, H5VL_dataset_get_args_t *args, hi H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -2496,7 +2496,7 @@ H5VLdataset_get(void *obj, hid_t connector_id, H5VL_dataset_get_args_t *args, hi HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "unable to execute dataset get callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdataset_get() */ /*------------------------------------------------------------------------- @@ -2582,7 +2582,7 @@ H5VLdataset_specific(void *obj, hid_t connector_id, H5VL_dataset_specific_args_t H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -2596,7 +2596,7 @@ H5VLdataset_specific(void *obj, hid_t connector_id, H5VL_dataset_specific_args_t HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "unable to execute dataset specific callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdataset_specific() */ /*------------------------------------------------------------------------- @@ -2681,7 +2681,7 @@ H5VLdataset_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -2695,7 +2695,7 @@ H5VLdataset_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "unable to execute dataset optional callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdataset_optional() */ /*------------------------------------------------------------------------- @@ -2831,7 +2831,7 @@ H5VLdataset_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*out H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xii**x", obj, connector_id, dxpl_id, req); /* Check args and get class pointer */ @@ -2845,7 +2845,7 @@ H5VLdataset_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*out HGOTO_ERROR(H5E_VOL, H5E_CANTCLOSEOBJ, FAIL, "unable to close dataset"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdataset_close() */ /*------------------------------------------------------------------------- @@ -2935,7 +2935,7 @@ H5VLdatatype_commit(void *obj, const H5VL_loc_params_t *loc_params, hid_t connec H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE10("*x", "*x*#i*siiiii**x", obj, loc_params, connector_id, name, type_id, lcpl_id, tcpl_id, tapl_id, dxpl_id, req); @@ -2951,7 +2951,7 @@ H5VLdatatype_commit(void *obj, const H5VL_loc_params_t *loc_params, hid_t connec HGOTO_ERROR(H5E_VOL, H5E_CANTCREATE, NULL, "unable to commit datatype"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdatatype_commit() */ /*------------------------------------------------------------------------- @@ -3038,7 +3038,7 @@ H5VLdatatype_open(void *obj, const H5VL_loc_params_t *loc_params, hid_t connecto H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE7("*x", "*x*#i*sii**x", obj, loc_params, connector_id, name, tapl_id, dxpl_id, req); /* Check args and get class pointer */ @@ -3052,7 +3052,7 @@ H5VLdatatype_open(void *obj, const H5VL_loc_params_t *loc_params, hid_t connecto HGOTO_ERROR(H5E_VOL, H5E_CANTOPENOBJ, NULL, "unable to open datatype"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdatatype_open() */ /*------------------------------------------------------------------------- @@ -3137,7 +3137,7 @@ H5VLdatatype_get(void *obj, hid_t connector_id, H5VL_datatype_get_args_t *args, H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -3151,7 +3151,7 @@ H5VLdatatype_get(void *obj, hid_t connector_id, H5VL_datatype_get_args_t *args, HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "unable to execute datatype get callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdatatype_get() */ /*------------------------------------------------------------------------- @@ -3237,7 +3237,7 @@ H5VLdatatype_specific(void *obj, hid_t connector_id, H5VL_datatype_specific_args H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -3251,7 +3251,7 @@ H5VLdatatype_specific(void *obj, hid_t connector_id, H5VL_datatype_specific_args HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "unable to execute datatype specific callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdatatype_specific() */ /*------------------------------------------------------------------------- @@ -3380,7 +3380,7 @@ H5VLdatatype_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -3394,7 +3394,7 @@ H5VLdatatype_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "unable to execute datatype optional callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdatatype_optional() */ /*------------------------------------------------------------------------- @@ -3524,7 +3524,7 @@ H5VLdatatype_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*ou H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xii**x", obj, connector_id, dxpl_id, req); /* Check args and get class pointer */ @@ -3538,7 +3538,7 @@ H5VLdatatype_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*ou HGOTO_ERROR(H5E_VOL, H5E_CANTCLOSEOBJ, FAIL, "unable to close datatype"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLdatatype_close() */ /*------------------------------------------------------------------------- @@ -3627,7 +3627,7 @@ H5VLfile_create(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id, H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("*x", "*sIuiii**x", name, flags, fcpl_id, fapl_id, dxpl_id, req); /* Get the VOL info from the fapl */ @@ -3645,7 +3645,7 @@ H5VLfile_create(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id, HGOTO_ERROR(H5E_VOL, H5E_CANTCREATE, NULL, "unable to create file"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLfile_create() */ /*------------------------------------------------------------------------- @@ -3888,7 +3888,7 @@ H5VLfile_open(const char *name, unsigned flags, hid_t fapl_id, hid_t dxpl_id, vo H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("*x", "*sIuii**x", name, flags, fapl_id, dxpl_id, req); /* Get the VOL info from the fapl */ @@ -3906,7 +3906,7 @@ H5VLfile_open(const char *name, unsigned flags, hid_t fapl_id, hid_t dxpl_id, vo HGOTO_ERROR(H5E_VOL, H5E_CANTOPENOBJ, NULL, "unable to open file"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLfile_open() */ /*------------------------------------------------------------------------- @@ -3989,7 +3989,7 @@ H5VLfile_get(void *obj, hid_t connector_id, H5VL_file_get_args_t *args, hid_t dx H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -4003,7 +4003,7 @@ H5VLfile_get(void *obj, hid_t connector_id, H5VL_file_get_args_t *args, hid_t dx HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "unable to execute file get callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLfile_get() */ /*------------------------------------------------------------------------- @@ -4124,7 +4124,7 @@ H5VLfile_specific(void *obj, hid_t connector_id, H5VL_file_specific_args_t *args H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -4136,7 +4136,7 @@ H5VLfile_specific(void *obj, hid_t connector_id, H5VL_file_specific_args_t *args HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "unable to execute file specific callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLfile_specific() */ /*------------------------------------------------------------------------- @@ -4220,7 +4220,7 @@ H5VLfile_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, hid H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -4234,7 +4234,7 @@ H5VLfile_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, hid HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "unable to execute file optional callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLfile_optional() */ /*------------------------------------------------------------------------- @@ -4364,7 +4364,7 @@ H5VLfile_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*out*/) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xii**x", obj, connector_id, dxpl_id, req); /* Check args and get class pointer */ @@ -4378,7 +4378,7 @@ H5VLfile_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*out*/) HGOTO_ERROR(H5E_VOL, H5E_CANTCLOSEFILE, FAIL, "unable to close file"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLfile_close() */ /*------------------------------------------------------------------------- @@ -4466,7 +4466,7 @@ H5VLgroup_create(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE9("*x", "*x*#i*siiii**x", obj, loc_params, connector_id, name, lcpl_id, gcpl_id, gapl_id, dxpl_id, req); @@ -4482,7 +4482,7 @@ H5VLgroup_create(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector HGOTO_ERROR(H5E_VOL, H5E_CANTCREATE, NULL, "unable to create group"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLgroup_create() */ /*------------------------------------------------------------------------- @@ -4569,7 +4569,7 @@ H5VLgroup_open(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_i H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE7("*x", "*x*#i*sii**x", obj, loc_params, connector_id, name, gapl_id, dxpl_id, req); /* Check args and get class pointer */ @@ -4583,7 +4583,7 @@ H5VLgroup_open(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_i HGOTO_ERROR(H5E_VOL, H5E_CANTINIT, NULL, "unable to open group"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLgroup_open() */ /*------------------------------------------------------------------------- @@ -4666,7 +4666,7 @@ H5VLgroup_get(void *obj, hid_t connector_id, H5VL_group_get_args_t *args, hid_t H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -4680,7 +4680,7 @@ H5VLgroup_get(void *obj, hid_t connector_id, H5VL_group_get_args_t *args, hid_t HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "unable to execute group get callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLgroup_get() */ /*------------------------------------------------------------------------- @@ -4765,7 +4765,7 @@ H5VLgroup_specific(void *obj, hid_t connector_id, H5VL_group_specific_args_t *ar H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -4779,7 +4779,7 @@ H5VLgroup_specific(void *obj, hid_t connector_id, H5VL_group_specific_args_t *ar HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "unable to execute group specific callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLgroup_specific() */ /*------------------------------------------------------------------------- @@ -4866,7 +4866,7 @@ H5VLgroup_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, hi H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -4881,7 +4881,7 @@ H5VLgroup_optional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, hi HERROR(H5E_VOL, H5E_CANTOPERATE, "unable to execute group optional callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLgroup_optional() */ /*------------------------------------------------------------------------- @@ -5011,7 +5011,7 @@ H5VLgroup_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*out*/ H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xii**x", obj, connector_id, dxpl_id, req); /* Check args and get class pointer */ @@ -5025,7 +5025,7 @@ H5VLgroup_close(void *obj, hid_t connector_id, hid_t dxpl_id, void **req /*out*/ HGOTO_ERROR(H5E_VOL, H5E_CANTCLOSEOBJ, FAIL, "unable to close group"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLgroup_close() */ /*------------------------------------------------------------------------- @@ -5126,7 +5126,7 @@ H5VLlink_create(H5VL_link_create_args_t *args, void *obj, const H5VL_loc_params_ H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE8("e", "*!*x*#iiii**x", args, obj, loc_params, connector_id, lcpl_id, lapl_id, dxpl_id, req); /* Get class pointer */ @@ -5138,7 +5138,7 @@ H5VLlink_create(H5VL_link_create_args_t *args, void *obj, const H5VL_loc_params_ HGOTO_ERROR(H5E_VOL, H5E_CANTCREATE, FAIL, "unable to create link"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLlink_create() */ /*------------------------------------------------------------------------- @@ -5232,7 +5232,7 @@ H5VLlink_copy(void *src_obj, const H5VL_loc_params_t *loc_params1, void *dst_obj H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE9("e", "*x*#*x*#iiii**x", src_obj, loc_params1, dst_obj, loc_params2, connector_id, lcpl_id, lapl_id, dxpl_id, req); @@ -5245,7 +5245,7 @@ H5VLlink_copy(void *src_obj, const H5VL_loc_params_t *loc_params1, void *dst_obj HGOTO_ERROR(H5E_VOL, H5E_CANTCOPY, FAIL, "unable to copy object"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLlink_copy() */ /*------------------------------------------------------------------------- @@ -5339,7 +5339,7 @@ H5VLlink_move(void *src_obj, const H5VL_loc_params_t *loc_params1, void *dst_obj H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE9("e", "*x*#*x*#iiii**x", src_obj, loc_params1, dst_obj, loc_params2, connector_id, lcpl_id, lapl_id, dxpl_id, req); @@ -5352,7 +5352,7 @@ H5VLlink_move(void *src_obj, const H5VL_loc_params_t *loc_params1, void *dst_obj HGOTO_ERROR(H5E_VOL, H5E_CANTMOVE, FAIL, "unable to move object"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLlink_move() */ /*------------------------------------------------------------------------- @@ -5438,7 +5438,7 @@ H5VLlink_get(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_id, H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*x*#i*!i**x", obj, loc_params, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -5452,7 +5452,7 @@ H5VLlink_get(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_id, HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "unable to execute link get callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLlink_get() */ /*------------------------------------------------------------------------- @@ -5541,7 +5541,7 @@ H5VLlink_specific(void *obj, const H5VL_loc_params_t *loc_params, hid_t connecto H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*x*#i*!i**x", obj, loc_params, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -5556,7 +5556,7 @@ H5VLlink_specific(void *obj, const H5VL_loc_params_t *loc_params, hid_t connecto HERROR(H5E_VOL, H5E_CANTOPERATE, "unable to execute link specific callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLlink_specific() */ /*------------------------------------------------------------------------- @@ -5642,7 +5642,7 @@ H5VLlink_optional(void *obj, const H5VL_loc_params_t *loc_params, hid_t connecto H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*x*#i*!i**x", obj, loc_params, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -5656,7 +5656,7 @@ H5VLlink_optional(void *obj, const H5VL_loc_params_t *loc_params, hid_t connecto HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "unable to execute link optional callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLlink_optional() */ /*------------------------------------------------------------------------- @@ -5804,7 +5804,7 @@ H5VLobject_open(void *obj, const H5VL_loc_params_t *params, hid_t connector_id, H5VL_class_t *cls; /* VOL connector's class struct */ void *ret_value = NULL; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("*x", "*x*#i*Iti**x", obj, params, connector_id, opened_type, dxpl_id, req); /* Check args and get class pointer */ @@ -5818,7 +5818,7 @@ H5VLobject_open(void *obj, const H5VL_loc_params_t *params, hid_t connector_id, HGOTO_ERROR(H5E_VOL, H5E_CANTOPENOBJ, NULL, "unable to open object"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLobject_open() */ /*------------------------------------------------------------------------- @@ -5914,7 +5914,7 @@ H5VLobject_copy(void *src_obj, const H5VL_loc_params_t *src_loc_params, const ch H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE11("e", "*x*#*s*x*#*siiii**x", src_obj, src_loc_params, src_name, dst_obj, dst_loc_params, dst_name, connector_id, ocpypl_id, lcpl_id, dxpl_id, req); @@ -5930,7 +5930,7 @@ H5VLobject_copy(void *src_obj, const H5VL_loc_params_t *src_loc_params, const ch HGOTO_ERROR(H5E_VOL, H5E_CANTCOPY, FAIL, "unable to copy object"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLobject_copy() */ /*------------------------------------------------------------------------- @@ -6016,7 +6016,7 @@ H5VLobject_get(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_i H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*x*#i*!i**x", obj, loc_params, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -6030,7 +6030,7 @@ H5VLobject_get(void *obj, const H5VL_loc_params_t *loc_params, hid_t connector_i HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "unable to execute object get callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLobject_get() */ /*------------------------------------------------------------------------- @@ -6119,7 +6119,7 @@ H5VLobject_specific(void *obj, const H5VL_loc_params_t *loc_params, hid_t connec H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*x*#i*!i**x", obj, loc_params, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -6134,7 +6134,7 @@ H5VLobject_specific(void *obj, const H5VL_loc_params_t *loc_params, hid_t connec HERROR(H5E_VOL, H5E_CANTOPERATE, "unable to execute object specific callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLobject_specific() */ /*------------------------------------------------------------------------- @@ -6220,7 +6220,7 @@ H5VLobject_optional(void *obj, const H5VL_loc_params_t *loc_params, hid_t connec H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*x*#i*!i**x", obj, loc_params, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -6234,7 +6234,7 @@ H5VLobject_optional(void *obj, const H5VL_loc_params_t *loc_params, hid_t connec HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "unable to execute object optional callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLobject_optional() */ /*------------------------------------------------------------------------- @@ -6391,7 +6391,7 @@ H5VLintrospect_get_conn_cls(void *obj, hid_t connector_id, H5VL_get_conn_lvl_t l H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xiVL**#", obj, connector_id, lvl, conn_cls); /* Check args */ @@ -6409,7 +6409,7 @@ H5VLintrospect_get_conn_cls(void *obj, hid_t connector_id, H5VL_get_conn_lvl_t l HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "can't query connector class"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLintrospect_get_conn_cls() */ /*------------------------------------------------------------------------- @@ -6463,7 +6463,7 @@ H5VLintrospect_get_cap_flags(const void *info, hid_t connector_id, uint64_t *cap H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE3("e", "*xi*UL", info, connector_id, cap_flags); /* Check args */ @@ -6479,7 +6479,7 @@ H5VLintrospect_get_cap_flags(const void *info, hid_t connector_id, uint64_t *cap HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "can't query connector's capability flags"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLintrospect_get_cap_flags() */ /*------------------------------------------------------------------------- @@ -6567,7 +6567,7 @@ H5VLintrospect_opt_query(void *obj, hid_t connector_id, H5VL_subclass_t subcls, H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xiVSIs*UL", obj, connector_id, subcls, opt_type, flags); /* Get class pointer */ @@ -6579,7 +6579,7 @@ H5VLintrospect_opt_query(void *obj, hid_t connector_id, H5VL_subclass_t subcls, HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "can't query optional operation support"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLintrospect_opt_query() */ /*------------------------------------------------------------------------- @@ -6670,7 +6670,7 @@ H5VLrequest_wait(void *req, hid_t connector_id, uint64_t timeout, H5VL_request_s H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xiUL*#", req, connector_id, timeout, status); /* Get class pointer */ @@ -6682,7 +6682,7 @@ H5VLrequest_wait(void *req, hid_t connector_id, uint64_t timeout, H5VL_request_s HGOTO_ERROR(H5E_VOL, H5E_CANTRELEASE, FAIL, "unable to wait on request"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLrequest_wait() */ /*------------------------------------------------------------------------- @@ -6775,7 +6775,7 @@ H5VLrequest_notify(void *req, hid_t connector_id, H5VL_request_notify_t cb, void H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xiVN*x", req, connector_id, cb, ctx); /* Get class pointer */ @@ -6787,7 +6787,7 @@ H5VLrequest_notify(void *req, hid_t connector_id, H5VL_request_notify_t cb, void HGOTO_ERROR(H5E_VOL, H5E_CANTSET, FAIL, "unable to register notify callback for request"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLrequest_notify() */ /*------------------------------------------------------------------------- @@ -6877,7 +6877,7 @@ H5VLrequest_cancel(void *req, hid_t connector_id, H5VL_request_status_t *status H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE3("e", "*xi*#", req, connector_id, status); /* Get class pointer */ @@ -6889,7 +6889,7 @@ H5VLrequest_cancel(void *req, hid_t connector_id, H5VL_request_status_t *status HGOTO_ERROR(H5E_VOL, H5E_CANTRELEASE, FAIL, "unable to cancel request"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLrequest_cancel() */ /*------------------------------------------------------------------------- @@ -6981,7 +6981,7 @@ H5VLrequest_specific(void *req, hid_t connector_id, H5VL_request_specific_args_t H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE3("e", "*xi*!", req, connector_id, args); /* Get class pointer */ @@ -6994,7 +6994,7 @@ H5VLrequest_specific(void *req, hid_t connector_id, H5VL_request_specific_args_t "unable to execute asynchronous request specific callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLrequest_specific() */ /*------------------------------------------------------------------------- @@ -7086,7 +7086,7 @@ H5VLrequest_optional(void *req, hid_t connector_id, H5VL_optional_args_t *args) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE3("e", "*xi*!", req, connector_id, args); /* Get class pointer */ @@ -7099,7 +7099,7 @@ H5VLrequest_optional(void *req, hid_t connector_id, H5VL_optional_args_t *args) "unable to execute asynchronous request optional callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLrequest_optional() */ /*------------------------------------------------------------------------- @@ -7226,7 +7226,7 @@ H5VLrequest_free(void *req, hid_t connector_id) H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE2("e", "*xi", req, connector_id); /* Get class pointer */ @@ -7238,7 +7238,7 @@ H5VLrequest_free(void *req, hid_t connector_id) HGOTO_ERROR(H5E_VOL, H5E_CANTRELEASE, FAIL, "unable to free request"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLrequest_free() */ /*------------------------------------------------------------------------- @@ -7319,7 +7319,7 @@ H5VLblob_put(void *obj, hid_t connector_id, const void *buf, size_t size, void * H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*xi*xz*x*x", obj, connector_id, buf, size, blob_id, ctx); /* Get class pointer */ @@ -7333,7 +7333,7 @@ H5VLblob_put(void *obj, hid_t connector_id, const void *buf, size_t size, void * HGOTO_ERROR(H5E_VOL, H5E_CANTSET, FAIL, "blob put failed"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLblob_put() */ /*------------------------------------------------------------------------- @@ -7414,7 +7414,7 @@ H5VLblob_get(void *obj, hid_t connector_id, const void *blob_id, void *buf /*out H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE6("e", "*xi*x*xz*x", obj, connector_id, blob_id, buf, size, ctx); /* Get class pointer */ @@ -7428,7 +7428,7 @@ H5VLblob_get(void *obj, hid_t connector_id, const void *blob_id, void *buf /*out HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "blob get failed"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLblob_get() */ /*------------------------------------------------------------------------- @@ -7508,7 +7508,7 @@ H5VLblob_specific(void *obj, hid_t connector_id, void *blob_id, H5VL_blob_specif H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xi*x*!", obj, connector_id, blob_id, args); /* Get class pointer */ @@ -7522,7 +7522,7 @@ H5VLblob_specific(void *obj, hid_t connector_id, void *blob_id, H5VL_blob_specif HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "blob specific operation failed"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLblob_specific() */ /*------------------------------------------------------------------------- @@ -7602,7 +7602,7 @@ H5VLblob_optional(void *obj, hid_t connector_id, void *blob_id, H5VL_optional_ar H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE4("e", "*xi*x*!", obj, connector_id, blob_id, args); /* Get class pointer */ @@ -7616,7 +7616,7 @@ H5VLblob_optional(void *obj, hid_t connector_id, void *blob_id, H5VL_optional_ar HGOTO_ERROR(H5E_VOL, H5E_CANTOPERATE, FAIL, "blob optional operation failed"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLblob_optional() */ /*------------------------------------------------------------------------- @@ -7724,7 +7724,7 @@ H5VLtoken_cmp(void *obj, hid_t connector_id, const H5O_token_t *token1, const H5 H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*k*k*Is", obj, connector_id, token1, token2, cmp_value); /* Check args and get class pointer */ @@ -7740,7 +7740,7 @@ H5VLtoken_cmp(void *obj, hid_t connector_id, const H5O_token_t *token1, const H5 HGOTO_ERROR(H5E_VOL, H5E_CANTCOMPARE, FAIL, "object token comparison failed"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLtoken_cmp() */ /*------------------------------------------------------------------------- @@ -7829,7 +7829,7 @@ H5VLtoken_to_str(void *obj, H5I_type_t obj_type, hid_t connector_id, const H5O_t H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xIti*k**s", obj, obj_type, connector_id, token, token_str); /* Check args and get class pointer */ @@ -7847,7 +7847,7 @@ H5VLtoken_to_str(void *obj, H5I_type_t obj_type, hid_t connector_id, const H5O_t HGOTO_ERROR(H5E_VOL, H5E_CANTSERIALIZE, FAIL, "object token to string failed"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLtoken_to_str() */ /*------------------------------------------------------------------------- @@ -7936,7 +7936,7 @@ H5VLtoken_from_str(void *obj, H5I_type_t obj_type, hid_t connector_id, const cha H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xIti*s*k", obj, obj_type, connector_id, token_str, token); /* Check args and get class pointer */ @@ -7954,7 +7954,7 @@ H5VLtoken_from_str(void *obj, H5I_type_t obj_type, hid_t connector_id, const cha HGOTO_ERROR(H5E_VOL, H5E_CANTUNSERIALIZE, FAIL, "object token from string failed"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLtoken_from_str() */ /*------------------------------------------------------------------------- @@ -8037,7 +8037,7 @@ H5VLoptional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, hid_t dx H5VL_class_t *cls; /* VOL connector's class struct */ herr_t ret_value = SUCCEED; /* Return value */ - FUNC_ENTER_API_NOINIT + FUNC_ENTER_API_REENTER H5TRACE5("e", "*xi*!i**x", obj, connector_id, args, dxpl_id, req); /* Check args and get class pointer */ @@ -8051,5 +8051,5 @@ H5VLoptional(void *obj, hid_t connector_id, H5VL_optional_args_t *args, hid_t dx HERROR(H5E_VOL, H5E_CANTOPERATE, "unable to execute optional callback"); done: - FUNC_LEAVE_API_NOINIT(ret_value) + FUNC_LEAVE_API_REENTER(ret_value) } /* end H5VLoptional() */ diff --git a/src/H5err.txt b/src/H5err.txt index fceb72618c1..23cab2c0e56 100644 --- a/src/H5err.txt +++ b/src/H5err.txt @@ -78,6 +78,7 @@ MAJOR, H5E_PAGEBUF, Page Buffering MAJOR, H5E_CONTEXT, API Context MAJOR, H5E_MAP, Map MAJOR, H5E_EVENTSET, Event Set +MAJOR, H5E_THREADSAFE, Threadsafety MAJOR, H5E_NONE_MAJOR, No error # Sections (for grouping minor errors) diff --git a/src/H5private.h b/src/H5private.h index 8cc52180679..b33056805e9 100644 --- a/src/H5private.h +++ b/src/H5private.h @@ -148,13 +148,15 @@ #include "uthash.h" /* - * NT doesn't define SIGBUS, but since NT only runs on processors - * that do not have alignment constraints a SIGBUS would never be - * raised, so we just replace it with SIGILL (which also should - * never be raised by the hdf5 library). + * Does the compiler support the __builtin_expect() syntax? + * It's not a problem if not. */ -#ifndef SIGBUS -#define SIGBUS SIGILL +#if H5_HAVE_BUILTIN_EXPECT +#define H5_LIKELY(expression) __builtin_expect(!!(expression), 1) +#define H5_UNLIKELY(expression) __builtin_expect(!!(expression), 0) +#else +#define H5_LIKELY(expression) (expression) +#define H5_UNLIKELY(expression) (expression) #endif /* @@ -231,6 +233,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*/ @@ -245,6 +254,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 */ @@ -1250,54 +1260,24 @@ 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[]; +/* Lock headers */ #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; - -/* Macros for accessing the global variables */ -#define H5_INIT_GLOBAL (H5_g.H5_libinit_g) -#define H5_TERM_GLOBAL (H5_g.H5_libterm_g) - -/* 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); -#else -#define H5_FIRST_THREAD_INIT pthread_once(&H5TS_first_init_g, H5TS_pthread_first_thread_init); -#endif - -/* Macros for threadsafe HDF5 Phase I locks */ -#define H5_API_LOCK H5TS_mutex_lock(&H5_g.init_lock); -#define H5_API_UNLOCK H5TS_mutex_unlock(&H5_g.init_lock); - -/* Macros for thread cancellation-safe mechanism */ -#define H5_API_UNSET_CANCEL H5TS_cancel_count_inc(); - -#define H5_API_SET_CANCEL H5TS_cancel_count_dec(); - -extern H5_api_t H5_g; +/* Macros for acquiring & releasing threadsafe API lock */ +#define H5_API_LOCK H5TS_api_lock(); +#define H5_API_UNLOCK H5TS_api_unlock(); #else /* H5_HAVE_THREADSAFE */ -/* disable any first thread init mechanism */ -#define H5_FIRST_THREAD_INIT - -/* disable locks (sequential version) */ +/* No locks (non-threadsafe builds) */ #define H5_API_LOCK #define H5_API_UNLOCK -/* disable cancellability (sequential version) */ -#define H5_API_UNSET_CANCEL -#define H5_API_SET_CANCEL +#endif /* H5_HAVE_THREADSAFE */ -/* extern global variables */ +/* Library init / term status (global) */ extern bool H5_libinit_g; /* Has the library been initialized? */ extern bool H5_libterm_g; /* Is the library being shutdown? */ @@ -1305,8 +1285,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 */ - #ifdef H5_HAVE_CODESTACK /* Include required function stack header */ @@ -1329,7 +1307,7 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); { \ static bool func_check = false; \ \ - if (!func_check) { \ + if (H5_UNLIKELY(!func_check)) { \ /* Check function naming status */ \ assert(asrt && \ "Function naming conventions are incorrect - check H5_IS_API|PUB|PRIV|PKG macros in " \ @@ -1350,39 +1328,30 @@ 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 H5TRACE_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 */ \ - if (!H5_INIT_GLOBAL && !H5_TERM_GLOBAL) { \ - if (H5_init_library() < 0) \ + if (H5_UNLIKELY(!H5_INIT_GLOBAL && !H5_TERM_GLOBAL)) { \ + if (H5_UNLIKELY(H5_init_library() < 0)) \ HGOTO_ERROR(H5E_FUNC, H5E_CANTINIT, err, "library initialization failed"); \ } #define FUNC_ENTER_API_PUSH(err) \ - /* Push the name of this function on the function stack */ \ - H5_PUSH_FUNC \ - \ /* Push the API context */ \ - if (H5CX_push() < 0) \ + if (H5_UNLIKELY(H5CX_push() < 0)) \ HGOTO_ERROR(H5E_FUNC, H5E_CANTSET, err, "can't set API context"); \ else \ api_ctx_pushed = true; + \ + /* Push the name of this function on the function stack */ \ + H5_PUSH_FUNC /* Use this macro for all "normal" API functions */ #define FUNC_ENTER_API(err) \ @@ -1411,11 +1380,27 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); FUNC_ENTER_API_PUSH(err); \ { +/* + * Use this macro for "developer" API functions that re-enter the library from + * a plugin / connector / filter / etc, and shouldn't (need to) perform + * initialization of the library or an interface, just perform tracing, etc. + * Examples are: H5allocate_memory, public VOL callback + * wrappers (e.g. H5VLfile_create, H5VLdataset_read, etc.), public VFL + * callback wrappers (e.g. H5FDopen, H5FDwrite, etc.), etc. + * + */ +#define FUNC_ENTER_API_REENTER \ + { \ + { \ + { \ + FUNC_ENTER_API_COMMON \ + H5_PUSH_FUNC \ + { + /* * 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 \ @@ -1423,7 +1408,6 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); { \ { \ FUNC_ENTER_API_COMMON \ - H5_PUSH_FUNC \ { /* @@ -1440,7 +1424,7 @@ 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 \ { /* @@ -1456,7 +1440,7 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); { \ { \ FUNC_ENTER_COMMON(H5_IS_API(__func__)); \ - FUNC_ENTER_API_THREADSAFE; \ + H5_API_LOCK \ FUNC_ENTER_API_INIT(err); \ { @@ -1529,13 +1513,12 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); #define FUNC_ENTER_NOAPI_NOFS \ { \ FUNC_ENTER_COMMON(!H5_IS_API(__func__)); \ - \ { /* * Use this macro for non-API functions which fall into these categories: * - functions which shouldn't push their name on the function stack - * (so far, just the H5CS routines themselves) + * (so far, just the H5CS routines themselves and H5CX_get_fstack) * * This macro is used for functions which fit the above categories _and_ * also don't use the 'FUNC' variable (i.e. don't push errors on the error stack) @@ -1626,10 +1609,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_COMMON(ret_value) H5TRACE_RETURN(ret_value); @@ -1637,27 +1616,40 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); ; \ } /*end scope from end of FUNC_ENTER*/ \ FUNC_LEAVE_API_COMMON(ret_value); \ - if (api_ctx_pushed) { \ + H5_POP_FUNC \ + if (H5_LIKELY(api_ctx_pushed)) { \ (void)H5CX_pop(true); \ api_ctx_pushed = false; \ } \ - H5_POP_FUNC \ - if (err_occurred) \ + if (H5_UNLIKELY(err_occurred)) \ (void)H5E_dump_api_stack(true); \ - FUNC_LEAVE_API_THREADSAFE \ + H5_API_UNLOCK \ return (ret_value); \ } \ } /*end scope from beginning of FUNC_ENTER*/ +/* Use this macro to match the FUNC_ENTER_API_REENTER macro */ +#define FUNC_LEAVE_API_REENTER(ret_value) \ + ; \ + } /*end scope from end of FUNC_ENTER*/ \ + FUNC_LEAVE_API_COMMON(ret_value); \ + H5_POP_FUNC \ + if (H5_UNLIKELY(err_occurred)) \ + (void)H5E_dump_api_stack(TRUE); \ + H5_API_UNLOCK \ + return (ret_value); \ + } \ + } \ + } /*end scope from beginning of FUNC_ENTER*/ + /* Use this macro to match the FUNC_ENTER_API_NOINIT macro */ #define FUNC_LEAVE_API_NOINIT(ret_value) \ ; \ } /*end scope from end of FUNC_ENTER*/ \ FUNC_LEAVE_API_COMMON(ret_value); \ - H5_POP_FUNC \ - if (err_occurred) \ + if (H5_UNLIKELY(err_occurred)) \ (void)H5E_dump_api_stack(true); \ - FUNC_LEAVE_API_THREADSAFE \ + H5_API_UNLOCK \ return (ret_value); \ } \ } \ @@ -1668,7 +1660,7 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); ; \ } /*end scope from end of FUNC_ENTER*/ \ FUNC_LEAVE_API_COMMON(ret_value); \ - FUNC_LEAVE_API_THREADSAFE \ + H5_API_UNLOCK \ return (ret_value); \ } \ } \ @@ -1679,9 +1671,9 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); #define FUNC_LEAVE_API_NOPUSH(ret_value) \ ; \ } /*end scope from end of FUNC_ENTER*/ \ - if (err_occurred) \ + if (H5_UNLIKELY(err_occurred)) \ (void)H5E_dump_api_stack(true); \ - FUNC_LEAVE_API_THREADSAFE \ + H5_API_UNLOCK \ return (ret_value); \ } \ } \ @@ -1718,7 +1710,7 @@ H5_DLL herr_t H5CX_pop(bool update_dxpl_props); /* * Use this macro for non-API functions which fall into these categories: * - functions which didn't push their name on the function stack - * (so far, just the H5CS routines themselves) + * (so far, just the H5CS routines themselves and H5CX_get_fstack) */ #define FUNC_LEAVE_NOAPI_NOFS(ret_value) \ ; \ diff --git a/src/Makefile.am b/src/Makefile.am index e6625777712..70bef1fb7c7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -96,7 +96,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 H5TSbarrier.c H5TScond.c H5TSexlock.c H5TSint.c H5TSkey.c \ + H5TSmutex.c H5TSpthread.c H5TSrwlock.c H5TStest.c H5TSthread.c \ + H5TSwin.c \ H5VL.c H5VLcallback.c H5VLdyn_ops.c H5VLint.c H5VLnative.c \ H5VLnative_attr.c H5VLnative_blob.c H5VLnative_dataset.c \ H5VLnative_datatype.c H5VLnative_file.c H5VLnative_group.c \ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 34e6f29e4d5..a9b2bc614c9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -343,11 +343,14 @@ 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_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_thread_id.c ) set (H5_TESTS @@ -582,24 +585,6 @@ if (HDF5_ENABLE_FORMATTERS) clang_format (HDF5_TEST_ttsafe_FORMAT ttsafe) endif () -######### Special handling for extra link lib of threads ############# -#-- Adding test for thread_id -add_executable (thread_id ${HDF5_TEST_SOURCE_DIR}/thread_id.c) -target_compile_options(thread_id PRIVATE "${HDF5_CMAKE_C_FLAGS}") -target_compile_definitions(thread_id PRIVATE "${HDF5_TEST_COMPILE_DEFS_PRIVATE}") -target_include_directories (thread_id PRIVATE "${HDF5_SRC_INCLUDE_DIRS};${HDF5_SRC_BINARY_DIR};$<$:${MPI_C_INCLUDE_DIRS}>") -if (NOT BUILD_SHARED_LIBS) - TARGET_C_PROPERTIES (thread_id STATIC) - target_link_libraries (thread_id PRIVATE ${HDF5_TEST_LIB_TARGET}) - if (NOT WIN32) - target_link_libraries (thread_id PRIVATE "$<$:Threads::Threads>") - endif () -else () - TARGET_C_PROPERTIES (thread_id SHARED) - target_link_libraries (thread_id PRIVATE ${HDF5_TEST_LIBSH_TARGET} "$<$:Threads::Threads>") -endif () -set_target_properties (thread_id PROPERTIES FOLDER test) - #----------------------------------------------------------------------------- # Add Target to clang-format #----------------------------------------------------------------------------- diff --git a/test/Makefile.am b/test/Makefile.am index fdd83e5bdeb..eb5bdd0f88a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -69,8 +69,7 @@ TEST_PROG= testhdf5 \ flush1 flush2 app_ref enum set_extent ttsafe enc_dec_plist \ enc_dec_plist_cross_platform getname vfd ros3 s3comms hdfs ntypes \ dangle dtransform reserved cross_read freespace mf vds file_image \ - unregister cache_logging cork swmr thread_id vol timer event_set \ - onion + unregister cache_logging cork swmr vol timer event_set onion # List programs to be built when testing here # @@ -161,8 +160,9 @@ libh5test_la_SOURCES=h5test.c testframe.c cache_common.c swmr_common.c external_ LDADD=libh5test.la $(LIBHDF5) # List the source files for tests that have more than one -ttsafe_SOURCES=ttsafe.c ttsafe_dcreate.c ttsafe_error.c ttsafe_cancel.c \ - ttsafe_acreate.c ttsafe_attr_vlen.c +ttsafe_SOURCES=ttsafe.c ttsafe_acreate.c ttsafe_attr_vlen.c ttsafe_cancel.c \ + ttsafe_dcreate.c ttsafe_develop.c ttsafe_error.c ttsafe_rec_rw_lock.c \ + ttsafe_thread_id.c cache_image_SOURCES=cache_image.c genall5.c mirror_vfd_SOURCES=mirror_vfd.c genall5.c diff --git a/test/onion.c b/test/onion.c index 5b9bb929dc6..eba52668753 100644 --- a/test/onion.c +++ b/test/onion.c @@ -1537,6 +1537,12 @@ compare_file_bytes_exactly(const char *filepath, hid_t fapl_id, size_t nbytes, c H5FD_t *raw_vfile = NULL; /* virtual file to look at raw file contents */ unsigned char *act_buf = NULL; /* allocated area for actual file bytes */ uint64_t filesize = 0; + bool api_ctx_pushed = false; /* Whether API context pushed */ + + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; if (NULL == (raw_vfile = H5FDopen(filepath, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF))) TEST_ERROR; @@ -1570,9 +1576,17 @@ compare_file_bytes_exactly(const char *filepath, hid_t fapl_id, size_t nbytes, c TEST_ERROR; free(act_buf); + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + if (raw_vfile != NULL) H5FDclose(raw_vfile); free(act_buf); @@ -1599,6 +1613,7 @@ verify_history_as_expected_onion(H5FD_t *raw_file, struct expected_history *filt uint64_t readsize = 0; uint8_t *ui8p = NULL; uint32_t buf_checksum = 0; + bool api_ctx_pushed = false; /* Whether API context pushed */ /* memset to avoid bad frees on errors */ memset(&rev_out, 0, sizeof(H5FD_onion_revision_record_t)); @@ -1613,6 +1628,11 @@ verify_history_as_expected_onion(H5FD_t *raw_file, struct expected_history *filt rev_out.version = H5FD_ONION_REVISION_RECORD_VERSION_CURR; rev_out.archival_index.version = H5FD_ONION_ARCHIVAL_INDEX_VERSION_CURR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + filesize = (uint64_t)H5FDget_eof(raw_file, H5FD_MEM_DRAW); if (H5FDset_eoa(raw_file, H5FD_MEM_DRAW, filesize) < 0) TEST_ERROR; @@ -1780,12 +1800,20 @@ verify_history_as_expected_onion(H5FD_t *raw_file, struct expected_history *filt free(rev_out.archival_index.list); } + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + free(history_out.record_locs); history_out.record_locs = NULL; return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + free(buf); free(rev_out.comment); free(rev_out.archival_index.list); @@ -1828,6 +1856,7 @@ verify_stored_onion_create_0_open(struct onion_filepaths *paths, H5FD_onion_fapl }; unsigned char *ptr = NULL; uint32_t checksum = 0; + bool api_ctx_pushed = false; /* Whether API context pushed */ /* Finish populating expected header bytes */ ptr = hdr_exp_bytes + 8; /* WARNING: must match format */ @@ -1843,6 +1872,11 @@ verify_stored_onion_create_0_open(struct onion_filepaths *paths, H5FD_onion_fapl UINT32ENCODE(ptr, checksum); ptr = NULL; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Look at h5 file: should have zero bytes */ file = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); @@ -1869,6 +1903,11 @@ verify_stored_onion_create_0_open(struct onion_filepaths *paths, H5FD_onion_fapl TEST_ERROR; file = NULL; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* Look at onion file: should have header */ if (compare_file_bytes_exactly(paths->onion, fapl_id, H5FD_ONION_ENCODED_SIZE_HEADER, hdr_exp_bytes) < 0) TEST_ERROR; @@ -1884,6 +1923,9 @@ verify_stored_onion_create_0_open(struct onion_filepaths *paths, H5FD_onion_fapl return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + if (file != NULL) (void)H5FDclose(file); free(act_buf); @@ -1928,6 +1970,7 @@ test_create_oniontarget(bool truncate_canonical, bool with_initial_data) H5FD_t *vfile_ro = NULL; /* Onion virtual file for read-only */ struct expected_history filter; char *buf = NULL; + bool api_ctx_pushed = false; /* Whether API context pushed */ if (true == truncate_canonical && true == with_initial_data) TESTING("onion creation; truncate extant canonical; w/ initial data"); @@ -1954,6 +1997,11 @@ test_create_oniontarget(bool truncate_canonical, bool with_initial_data) HDremove(paths->onion); HDremove(paths->recovery); + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Create canonical file to be truncated */ if (true == truncate_canonical) { /* Create canonical file. */ @@ -2001,9 +2049,19 @@ test_create_oniontarget(bool truncate_canonical, bool with_initial_data) if (NULL == vfile_rw) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + if (verify_stored_onion_create_0_open(paths, &onion_info) < 0) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + H5E_BEGIN_TRY { vfile_ro = H5FDopen(paths->canon, H5F_ACC_RDONLY, fapl_id, HADDR_UNDEF); @@ -2085,11 +2143,21 @@ test_create_oniontarget(bool truncate_canonical, bool with_initial_data) TEST_ERROR; vfile_rw = NULL; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* Look at h5 file: should be known-empty */ if (compare_file_bytes_exactly(paths->canon, onion_info.backing_fapl_id, 8, (const unsigned char *)"ONIONEOF") < 0) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Look at recovery file: should be gone */ H5E_BEGIN_TRY { @@ -2104,6 +2172,11 @@ test_create_oniontarget(bool truncate_canonical, bool with_initial_data) if (NULL == vfile_raw) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + filter.page_size = onion_info.page_size; filter.n_revisions = 1; filter.origin_eof = 0; @@ -2116,6 +2189,11 @@ test_create_oniontarget(bool truncate_canonical, bool with_initial_data) if (verify_history_as_expected_onion(vfile_raw, &filter) < 0) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + if (H5FDclose(vfile_raw) < 0) TEST_ERROR; vfile_raw = NULL; @@ -2155,6 +2233,11 @@ test_create_oniontarget(bool truncate_canonical, bool with_initial_data) TEST_ERROR; vfile_ro = NULL; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* * CLEANUP */ @@ -2171,6 +2254,9 @@ test_create_oniontarget(bool truncate_canonical, bool with_initial_data) return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + if (paths != NULL) { HDremove(paths->canon); @@ -2241,6 +2327,7 @@ test_several_revisions_with_logical_gaps(void) uint64_t a_off = ONION_TEST_PAGE_SIZE_5 + 7; /* 39 */ uint64_t b_off = (((a_off + a_list_size_s + ONION_TEST_PAGE_SIZE_5 - 1) >> 5) << 5) + ONION_TEST_PAGE_SIZE_5 + 7; /* full page between */ + bool api_ctx_pushed = false; /* Whether API context pushed */ TESTING("multiple revisions with gaps and overlap"); @@ -2295,6 +2382,11 @@ test_several_revisions_with_logical_gaps(void) if (do_onion_open_and_writes(paths->canon, &onion_info, 4, about) < 0) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Inspect logical file */ /* THIS IS THE INITIAL FILE, SHOULD ONLY HAVE 8 BYTES */ @@ -2477,6 +2569,11 @@ test_several_revisions_with_logical_gaps(void) if (NULL == file) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + filter.page_size = onion_info.page_size; filter.n_revisions = 4; filter.origin_eof = 0; @@ -2509,10 +2606,20 @@ test_several_revisions_with_logical_gaps(void) if (verify_history_as_expected_onion(file, &filter) < 0) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + if (H5FDclose(file) < 0) TEST_ERROR; file = NULL; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* CLEANUP */ if (H5Pclose(onion_info.backing_fapl_id) < 0) @@ -2527,6 +2634,9 @@ test_several_revisions_with_logical_gaps(void) return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + if (paths != NULL) { HDremove(paths->canon); @@ -2572,6 +2682,12 @@ do_onion_open_and_writes(const char *filename, H5FD_onion_fapl_info_t *onion_inf H5FD_t *file = NULL; /* Onion virtual file for read/write */ unsigned char *buf_vfy = NULL; size_t i = 0; + bool api_ctx_pushed = false; /* Whether API context pushed */ + + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; for (i = 0; i < n_ops; i++) { size_t j = 0; @@ -2633,9 +2749,17 @@ do_onion_open_and_writes(const char *filename, H5FD_onion_fapl_info_t *onion_inf fapl_id = H5I_INVALID_HID; } /* end for each open-close cycle */ + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + if (file != NULL) (void)H5FDclose(file); @@ -2685,6 +2809,7 @@ test_page_aligned_history_create(void) H5FD_onion_history_t history_out; size_t i = 0; uint64_t a_off = b_list_size_s - a_list_size_s; + bool api_ctx_pushed = false; /* Whether API context pushed */ TESTING("page-aligned history on onion-created file"); @@ -2731,6 +2856,11 @@ test_page_aligned_history_create(void) if (do_onion_open_and_writes(paths->canon, &onion_info, 2, about) < 0) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Inspect logical file */ if (NULL == (buf = malloc(b_list_size_s * sizeof(unsigned char)))) TEST_ERROR; @@ -2817,6 +2947,11 @@ test_page_aligned_history_create(void) TEST_ERROR; file = NULL; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* CLEANUP */ if (H5Pclose(onion_info.backing_fapl_id) < 0) @@ -2835,6 +2970,8 @@ test_page_aligned_history_create(void) return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); if (paths != NULL) { HDremove(paths->canon); diff --git a/test/thread_id.c b/test/thread_id.c deleted file mode 100644 index ed1e0a8a611..00000000000 --- a/test/thread_id.c +++ /dev/null @@ -1,324 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Copyright by The HDF Group. * - * All rights reserved. * - * * - * This file is part of HDF5. The full HDF5 copyright notice, including * - * terms governing use, modification, and redistribution, is contained in * - * the COPYING file, which can be found at the root of the source code * - * distribution tree, or in https://www.hdfgroup.org/licenses. * - * If you do not have access to either file, you may request a copy from * - * help@hdfgroup.org. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -/* Check that a thread ID returned by H5TS_thread_id() possesses the - * following properties: - * - * 1 ID >= 1. - * 2 The ID is constant over the thread's lifetime. - * 3 No two threads share an ID during their lifetimes. - * 4 A thread's ID is available for reuse as soon as it is joined. - */ - -/* - * Include required headers. This file tests internal library functions, - * so we include the private headers here. - */ -#include "testhdf5.h" - -#if defined(H5_HAVE_THREADSAFE) && !defined(H5_HAVE_WIN_THREADS) - -static void my_errx(int, const char *, ...) H5_ATTR_FORMAT(printf, 2, 3); - -static void -my_errx(int code, const char *fmt, ...) -{ - va_list ap; - - (void)fprintf(stderr, "thread_id: "); - va_start(ap, fmt); - (void)vfprintf(stderr, fmt, ap); - va_end(ap); - (void)fputc('\n', stderr); - exit(code); -} - -#if defined(H5_HAVE_DARWIN) - -typedef struct _pthread_barrierattr { - uint8_t unused; -} pthread_barrierattr_t; - -typedef struct _pthread_barrier { - uint32_t magic; - unsigned int count; - uint64_t nentered; - pthread_cond_t cv; - pthread_mutex_t mtx; -} pthread_barrier_t; - -int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *, unsigned int); -int pthread_barrier_wait(pthread_barrier_t *); -int pthread_barrier_destroy(pthread_barrier_t *); - -static const uint32_t barrier_magic = 0xf00dd00f; - -int -pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) -{ - int rc; - - if (count == 0) - return EINVAL; - - if (attr != NULL) - return EINVAL; - - memset(barrier, 0, sizeof(*barrier)); - - barrier->count = count; - - if ((rc = pthread_cond_init(&barrier->cv, NULL)) != 0) - return rc; - - if ((rc = pthread_mutex_init(&barrier->mtx, NULL)) != 0) { - (void)pthread_cond_destroy(&barrier->cv); - return rc; - } - - barrier->magic = barrier_magic; - - return 0; -} - -static void -barrier_lock(pthread_barrier_t *barrier) -{ - int rc; - - if ((rc = pthread_mutex_lock(&barrier->mtx)) != 0) { - my_errx(EXIT_FAILURE, "%s: pthread_mutex_lock: %s", __func__, strerror(rc)); - } -} - -static void -barrier_unlock(pthread_barrier_t *barrier) -{ - int rc; - - if ((rc = pthread_mutex_unlock(&barrier->mtx)) != 0) { - my_errx(EXIT_FAILURE, "%s: pthread_mutex_unlock: %s", __func__, strerror(rc)); - } -} - -int -pthread_barrier_destroy(pthread_barrier_t *barrier) -{ - int rc; - - barrier_lock(barrier); - if (barrier->magic != barrier_magic) - rc = EINVAL; - else if (barrier->nentered % barrier->count != 0) - rc = EBUSY; - else { - rc = 0; - barrier->magic = ~barrier->magic; - } - barrier_unlock(barrier); - - if (rc != 0) - return rc; - - (void)pthread_cond_destroy(&barrier->cv); - (void)pthread_mutex_destroy(&barrier->mtx); - - return 0; -} - -int -pthread_barrier_wait(pthread_barrier_t *barrier) -{ - int rc; - uint64_t threshold; - - if (barrier == NULL) - return EINVAL; - - barrier_lock(barrier); - if (barrier->magic != barrier_magic) { - rc = EINVAL; - goto out; - } - /* Compute the release `threshold`. All threads entering with count = 5 - * and `nentered` in [0, 4] should be released once `nentered` reaches 5: - * call 5 the release `threshold`. All threads entering with count = 5 - * and `nentered` in [5, 9] should be released once `nentered` reaches 10. - */ - threshold = (barrier->nentered / barrier->count + 1) * barrier->count; - barrier->nentered++; - while (barrier->nentered < threshold) { - if ((rc = pthread_cond_wait(&barrier->cv, &barrier->mtx)) != 0) - goto out; - } - rc = pthread_cond_broadcast(&barrier->cv); -out: - barrier_unlock(barrier); - return rc; -} - -#endif /* H5_HAVE_DARWIN */ - -static void my_err(int, const char *, ...) H5_ATTR_FORMAT(printf, 2, 3); - -static void -my_err(int code, const char *fmt, ...) -{ - va_list ap; - int errno_copy = errno; - - (void)fprintf(stderr, "thread_id: "); - va_start(ap, fmt); - (void)vfprintf(stderr, fmt, ap); - va_end(ap); - (void)fprintf(stderr, ": %s\n", strerror(errno_copy)); - exit(code); -} - -#define threads_failure(_call, _result) \ - do { \ - my_errx(EXIT_FAILURE, "%s.%d: " #_call ": %s", __func__, __LINE__, strerror(_result)); \ - } while (false) - -#define NTHREADS 5 - -static volatile bool failed = false; -static pthread_barrier_t barrier; -static bool used[NTHREADS]; -static pthread_mutex_t used_lock; - -static void -atomic_printf(const char *fmt, ...) -{ - char buf[80]; - va_list ap; - ssize_t nprinted, nwritten; - - va_start(ap, fmt); - nprinted = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - if (nprinted == -1) - my_err(EXIT_FAILURE, "%s.%d: vsnprintf", __func__, __LINE__); - else if (nprinted >= (ssize_t)sizeof(buf)) - my_errx(EXIT_FAILURE, "%s.%d: vsnprintf overflowed", __func__, __LINE__); - - nwritten = HDwrite(STDOUT_FILENO, buf, (size_t)nprinted); - if (nwritten < nprinted) { - my_errx(EXIT_FAILURE, "%s.%d: write error or short write", __func__, __LINE__); - } -} - -/* Each thread runs this routine. The routine fetches the current - * thread's ID, makes sure that it is in the expected range, makes - * sure that in this round of testing, no two threads shared the - * same ID, and checks that each thread's ID is constant over its lifetime. - * - * main() checks that every ID in [1, NTHREADS] is used in each round - * of testing. All NTHREADS threads synchronize on a barrier after each - * has fetched its ID. The barrier guarantees that all threads' lifetimes - * overlap at least momentarily, so the IDs will be unique, and there - * will be NTHREADS of them. Further, since thread IDs are assigned - * starting with 1, and the number of threads with IDs alive never exceeds - * NTHREADS, the least ID has to be 1 and the greatest, NTHREADS. - */ -static void * -thread_main(void H5_ATTR_UNUSED *arg) -{ - uint64_t ntid, tid; - - tid = H5TS_thread_id(); - - if (tid < 1 || NTHREADS < tid) { - atomic_printf("unexpected tid %" PRIu64 " FAIL\n", tid); - goto pre_barrier_error; - } - pthread_mutex_lock(&used_lock); - if (used[tid - 1]) { - atomic_printf("reused tid %" PRIu64 " FAIL\n", tid); - pthread_mutex_unlock(&used_lock); - goto pre_barrier_error; - } - used[tid - 1] = true; - pthread_mutex_unlock(&used_lock); - - atomic_printf("tid %" PRIu64 " in [1, %d] PASS\n", tid, NTHREADS); - pthread_barrier_wait(&barrier); - - ntid = H5TS_thread_id(); - if (ntid != tid) { - atomic_printf("tid changed from %" PRIu64 " to %" PRIu64 " FAIL\n", tid, ntid); - failed = true; - } - return NULL; -pre_barrier_error: - pthread_barrier_wait(&barrier); - failed = true; - return NULL; -} - -int -main(void) -{ - int i, rc, times; - pthread_t threads[NTHREADS]; - - /* Run H5open() to initialize the library's thread-ID freelist, - * mutex, etc. - */ - if (H5open() != SUCCEED) - my_errx(EXIT_FAILURE, "%s.%d: H5open failed", __func__, __LINE__); - - if ((rc = pthread_mutex_init(&used_lock, NULL)) == -1) - threads_failure(pthread_mutex_init, rc); - - if ((rc = pthread_barrier_init(&barrier, NULL, NTHREADS)) != 0) - threads_failure(pthread_barrier_init, rc); - - /* Start the test threads and join them twice to make sure that - * the thread IDs are recycled in the second round. - */ - for (times = 0; times < 2; times++) { - - for (i = 0; i < NTHREADS; i++) - used[i] = false; // access synchronized by thread create/join - - for (i = 0; i < NTHREADS; i++) { - rc = pthread_create(&threads[i], NULL, thread_main, NULL); - if (rc != 0) - threads_failure(pthread_create, rc); - } - - for (i = 0; i < NTHREADS; i++) { - rc = pthread_join(threads[i], NULL); - if (rc != 0) - threads_failure(pthread_join, rc); - } - - for (i = 0; i < NTHREADS; i++) { - if (!used[i]) // access synchronized by thread create/join - my_errx(EXIT_FAILURE, "thread ID %d did not run.", i + 1); - } - } - if ((rc = pthread_barrier_destroy(&barrier)) != 0) - threads_failure(pthread_barrier_destroy, rc); - return failed ? EXIT_FAILURE : EXIT_SUCCESS; -} - -#else /*H5_HAVE_THREADSAFE && !H5_HAVE_WIN_THREADS*/ -int -main(void) -{ - fprintf(stderr, "not implemented in this configuration.\n"); - return EXIT_SUCCESS; -} -#endif /*H5_HAVE_THREADSAFE && !H5_HAVE_WIN_THREADS*/ diff --git a/test/ttsafe.c b/test/ttsafe.c index 6fe14fcc390..de0f211dcda 100644 --- a/test/ttsafe.c +++ b/test/ttsafe.c @@ -102,6 +102,8 @@ main(int argc, char *argv[]) /* Tests are generally arranged from least to most complexity... */ AddTest("is_threadsafe", tts_is_threadsafe, NULL, "library threadsafe status", NULL); #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,6 +113,15 @@ 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); + /* 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); + + /* 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"); diff --git a/test/ttsafe.h b/test/ttsafe.h index ce92c0173de..b6ea7660a6a 100644 --- a/test/ttsafe.h +++ b/test/ttsafe.h @@ -23,6 +23,14 @@ */ #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 */ +#define H5TS_TESTING +#include "H5TSpkg.h" + /* Prototypes for the support routines */ extern char *gen_name(int); @@ -34,6 +42,12 @@ void tts_error(void); void tts_cancel(void); void tts_acreate(void); void tts_attr_vlen(void); +void tts_thread_id(void); +void tts_rec_rw_lock_smoke_check_1(void); +void tts_rec_rw_lock_smoke_check_2(void); +void tts_rec_rw_lock_smoke_check_3(void); +void tts_rec_rw_lock_smoke_check_4(void); +void tts_develop_api(void); /* Prototypes for the cleanup routines */ void cleanup_dcreate(void); diff --git a/test/ttsafe_acreate.c b/test/ttsafe_acreate.c index 84e5c6ba9de..83b94923b6e 100644 --- a/test/ttsafe_acreate.c +++ b/test/ttsafe_acreate.c @@ -102,11 +102,11 @@ 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); + threads[i] = H5TS__create_thread(tts_acreate_thread, NULL, attrib_data); } for (i = 0; i < NUM_THREADS; i++) - H5TS_wait_for_thread(threads[i]); + H5TS__wait_for_thread(threads[i]); /* verify the correctness of the test */ for (i = 0; i < NUM_THREADS; i++) { diff --git a/test/ttsafe_attr_vlen.c b/test/ttsafe_attr_vlen.c index bfc2067e19c..2c577a5e13d 100644 --- a/test/ttsafe_attr_vlen.c +++ b/test/ttsafe_attr_vlen.c @@ -106,12 +106,12 @@ tts_attr_vlen(void) /* 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); + threads[i] = H5TS__create_thread(tts_attr_vlen_thread, NULL, NULL); } /* Wait for the threads to end */ for (i = 0; i < NUM_THREADS; i++) - H5TS_wait_for_thread(threads[i]); + H5TS__wait_for_thread(threads[i]); } /* end tts_attr_vlen() */ diff --git a/test/ttsafe_dcreate.c b/test/ttsafe_dcreate.c index 42c3f6c1276..a4dfda81283 100644 --- a/test/ttsafe_dcreate.c +++ b/test/ttsafe_dcreate.c @@ -67,11 +67,11 @@ tts_dcreate(void) herr_t status; /* set pthread attribute to perform global scheduling */ - H5TS_attr_init(&attribute); + H5TS__attr_init(&attribute); /* set thread scope to system */ #ifdef H5_HAVE_SYSTEM_SCOPE_THREADS - H5TS_attr_setscope(&attribute, H5TS_SCOPE_SYSTEM); + H5TS__attr_setscope(&attribute, H5TS_SCOPE_SYSTEM); #endif /* H5_HAVE_SYSTEM_SCOPE_THREADS */ /* @@ -86,11 +86,11 @@ 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]); + threads[i] = H5TS__create_thread(tts_dcreate_creator, NULL, &thread_out[i]); } for (i = 0; i < NUM_THREAD; i++) - H5TS_wait_for_thread(threads[i]); + H5TS__wait_for_thread(threads[i]); /* compare data to see if it is written correctly */ @@ -124,7 +124,7 @@ tts_dcreate(void) CHECK(status, FAIL, "H5Fclose"); /* Destroy the thread attribute */ - H5TS_attr_destroy(&attribute); + H5TS__attr_destroy(&attribute); } /* end tts_dcreate() */ void * diff --git a/test/ttsafe_develop.c b/test/ttsafe_develop.c new file mode 100644 index 00000000000..95188747471 --- /dev/null +++ b/test/ttsafe_develop.c @@ -0,0 +1,160 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 void * +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"); + + H5TS__barrier_wait(udata->barrier); + + /* Thread #2 will attempt (unsuccessfully) to acquire the API lock */ + + H5TS__barrier_wait(udata->barrier); + + /* Release the API lock */ + result = H5TSmutex_release(&lock_count); + CHECK_I(result, "H5TSmutex_release"); + VERIFY(lock_count, 1, "H5TSmutex_release"); + + return NULL; +} /* end tts_develop_api_thr_1() */ + +/* + ********************************************************************** + * tts_develop_api_thr_2 + * + ********************************************************************** + */ +static void * +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 */ + + H5TS__barrier_wait(udata->barrier); + + /* 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"); + + H5TS__barrier_wait(udata->barrier); + + /* Thread #1 will release the API lock */ + + return NULL; +} /* 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; + thread_1 = H5TS__create_thread(tts_develop_api_thr_1, NULL, &udata); + thread_2 = H5TS__create_thread(tts_develop_api_thr_2, NULL, &udata); + + /* Wait for threads to complete. */ + H5TS__wait_for_thread(thread_1); + H5TS__wait_for_thread(thread_2); + + result = H5TS__barrier_destroy(&barrier); + CHECK_I(result, "H5TS__barrier_destroy"); + + + /* Test multiple / recursive acquisition of the API lock */ + + /* Acquire the API lock - should acquire it */ + result = H5TSmutex_acquire(1, &acquired); + CHECK_I(result, "H5TSmutex_acquire"); + VERIFY(acquired, true, "H5TSmutex_acquire"); + + /* Acquire the API lock again - should acquire it, since it's the same thread */ + acquired = false; + result = H5TSmutex_acquire(1, &acquired); + CHECK_I(result, "H5TSmutex_acquire"); + VERIFY(acquired, true, "H5TSmutex_acquire"); + + /* Release the API lock */ + result = H5TSmutex_release(&lock_count); + CHECK_I(result, "H5TSmutex_release"); + VERIFY(lock_count, 2, "H5TSmutex_release"); + +} /* end tts_develop_api() */ + +#endif /*H5_HAVE_THREADSAFE*/ + diff --git a/test/ttsafe_error.c b/test/ttsafe_error.c index 560a7e1b2f7..6e5261d146e 100644 --- a/test/ttsafe_error.c +++ b/test/ttsafe_error.c @@ -48,7 +48,7 @@ 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; +H5TS_mutex_t error_mutex_g; /* Prototypes */ static herr_t error_callback(hid_t, void *); @@ -104,11 +104,11 @@ tts_error(void) H5TS_mutex_init(&error_mutex_g); /* make thread scheduling global */ - H5TS_attr_init(&attribute); + H5TS__attr_init(&attribute); #ifdef H5_HAVE_SYSTEM_SCOPE_THREADS /* set thread scope to system */ - H5TS_attr_setscope(&attribute, H5TS_SCOPE_SYSTEM); + H5TS__attr_setscope(&attribute, H5TS_SCOPE_SYSTEM); #endif /* H5_HAVE_SYSTEM_SCOPE_THREADS */ def_fapl = H5Pcreate(H5P_FILE_ACCESS); @@ -125,10 +125,10 @@ 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); + threads[i] = H5TS__create_thread(tts_error_thread, &attribute, NULL); for (i = 0; i < NUM_THREAD; i++) - H5TS_wait_for_thread(threads[i]); + H5TS__wait_for_thread(threads[i]); if (error_flag_g) { TestErrPrintf( @@ -160,7 +160,8 @@ tts_error(void) status = H5Idec_ref(vol_id); CHECK(status, FAIL, "H5Idec_ref"); - H5TS_attr_destroy(&attribute); + H5TS__attr_destroy(&attribute); + H5TS_mutex_destroy(&error_mutex_g); } /* end tts_error() */ static void * @@ -221,9 +222,9 @@ tts_error_thread(void H5_ATTR_UNUSED *arg) static herr_t error_callback(hid_t H5_ATTR_UNUSED estack_id, void *client_data) { - H5TS_mutex_lock_simple(&error_mutex_g); + H5TS_mutex_lock(&error_mutex_g); error_count_g++; - H5TS_mutex_unlock_simple(&error_mutex_g); + H5TS_mutex_unlock(&error_mutex_g); return H5Ewalk2(H5E_DEFAULT, H5E_WALK_DOWNWARD, walk_error_callback, client_data); } diff --git a/test/ttsafe_rec_rw_lock.c b/test/ttsafe_rec_rw_lock.c new file mode 100644 index 00000000000..330b403d0f3 --- /dev/null +++ b/test/ttsafe_rec_rw_lock.c @@ -0,0 +1,1113 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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_THREADSAFE +#ifndef H5_HAVE_WIN_THREADS + +#define MAX_NUM_THREADS 64 +#define MAX_LOCK_CYCLES (10 * 1000 * 1000) + +/* structure used to configure test threads in the recurive + * 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 void * +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 ((HDrand() % 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 || (HDrand() % 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 || (HDrand() % 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 NULL; +} /* 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; + } + +uint64_t start_time = H5_now_usec(); + /* 3) Create the reader threads, each with its own user data. */ + for (i = 0; i < num_threads; i++) + threads[i] = H5TS__create_thread(tts_rw_lock_smoke_check_test_thread, NULL, &udata[i]); + + /* 4) Wait for all threads to complete. */ + for (i = 0; i < num_threads; i++) + H5TS__wait_for_thread(threads[i]); +uint64_t end_time = H5_now_usec(); +uint64_t elap_time = (unsigned long long)(end_time - start_time); +fprintf(stdout, "elapsed usec: %llu, usec per lock_cycle = %llu\n", elap_time, (elap_time / (uint64_t)lock_cycles)); + + /* 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; + } + +uint64_t start_time = H5_now_usec(); + /* 3) Create the writer threads, each with its own user data. */ + for (i = 0; i < num_threads; i++) + threads[i] = H5TS__create_thread(tts_rw_lock_smoke_check_test_thread, NULL, &(udata[i])); + + /* 4) Wait for all threads to complete. */ + for (i = 0; i < num_threads; i++) + H5TS__wait_for_thread(threads[i]); +uint64_t end_time = H5_now_usec(); +uint64_t elap_time = (unsigned long long)(end_time - start_time); +fprintf(stdout, "elapsed usec: %llu, usec per lock_cycle = %llu\n", elap_time, (elap_time / (uint64_t)lock_cycles)); + + /* 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; + } + +uint64_t start_time = H5_now_usec(); + /* 3) Create the reader threads, each with its own user data. */ + for (i = 0; i < num_threads; i++) + threads[i] = H5TS__create_thread(tts_rw_lock_smoke_check_test_thread, NULL, &(udata[i])); + + /* 4) Wait for all threads to complete. */ + for (i = 0; i < num_threads; i++) + H5TS__wait_for_thread(threads[i]); +uint64_t end_time = H5_now_usec(); +uint64_t elap_time = (unsigned long long)(end_time - start_time); +fprintf(stdout, "elapsed usec: %llu, usec per lock_cycle = %llu\n", elap_time, (elap_time / (uint64_t)lock_cycles)); + + /* 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_THREADSAFE*/ diff --git a/test/ttsafe_thread_id.c b/test/ttsafe_thread_id.c new file mode 100644 index 00000000000..aa77499eb4b --- /dev/null +++ b/test/ttsafe_thread_id.c @@ -0,0 +1,137 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 volatile bool failed = false; +static H5TS_barrier_t barrier; +static int times; +static bool used[NTHREADS * CYCLE_COUNT]; +static H5TS_mutex_t used_lock = PTHREAD_MUTEX_INITIALIZER; + +/* 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 void * +thread_main(void H5_ATTR_UNUSED *arg) +{ + int min_id, max_id; + uint64_t ntid, tid; + + tid = H5TS_thread_id(); + + 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 */ + ntid = H5TS_thread_id(); + if (ntid != tid) + TestErrPrintf("tid changed from %" PRIu64 " to %" PRIu64 " FAIL\n", tid, ntid); + + return NULL; + +pre_barrier_error: + H5TS__barrier_wait(&barrier); + + return NULL; +} + +/* + ********************************************************************** + * tts_thread_id + * + ********************************************************************** + */ +void +tts_thread_id(void) +{ + H5TS_thread_t threads[NTHREADS]; + uint64_t tid; + int i; + herr_t result; + + 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. + */ + tid = H5TS_thread_id(); + 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++) + threads[i] = H5TS__create_thread(thread_main, NULL, NULL); + + for (i = 0; i < NTHREADS; i++) + H5TS__wait_for_thread(threads[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_init"); + +} /* end tts_thread_id() */ + +#endif /*H5_HAVE_THREADSAFE*/ + + diff --git a/test/vfd.c b/test/vfd.c index 5a86920652e..8ce87e82d02 100644 --- a/test/vfd.c +++ b/test/vfd.c @@ -4241,26 +4241,34 @@ test_vector_io__read_v_indiv(H5FD_t *lf, uint32_t count, H5FD_mem_t types[], had uint32_t i; size_t size = SIZE_MAX; H5FD_mem_t type = H5FD_MEM_NTYPES; + bool api_ctx_pushed = false; /* Whether API context pushed */ - for (i = 0; i < count; i++) { + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + for (i = 0; i < count; i++) { SET_SIZE(size_fixed, sizes, size, i); - SET_TYPE(type_fixed, types, type, i); - if (H5FDread(lf, type, H5P_DEFAULT, addrs[i], size, read_bufs[i]) < 0) { - - if (verbose) { - + if (verbose) fprintf(stdout, "%s: H5FDread() failed on entry %d.\n", __func__, i); - } result = false; break; } } - return (result); + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + +error: + if (api_ctx_pushed) + H5CX_pop(false); + return (result); } /* end test_vector_io__read_v_indiv() */ /*------------------------------------------------------------------------- @@ -4289,26 +4297,31 @@ test_vector_io__write_v_indiv(H5FD_t *lf, uint32_t count, H5FD_mem_t types[], ha uint32_t i; size_t size = SIZE_MAX; H5FD_mem_t type = H5FD_MEM_NTYPES; + bool api_ctx_pushed = false; /* Whether API context pushed */ - for (i = 0; i < count; i++) { + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + for (i = 0; i < count; i++) { SET_SIZE(size_fixed, sizes, size, i); - SET_TYPE(type_fixed, types, type, i); - if (H5FDwrite(lf, type, H5P_DEFAULT, addrs[i], size, write_bufs[i]) < 0) { - - if (verbose) { - + if (verbose) fprintf(stdout, "%s: HDwrite() failed on entry %d.\n", __func__, i); - } result = false; break; } } - return (result); + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; +error: + return (result); } /* end test_vector_io__write_v_indiv() */ /*------------------------------------------------------------------------- @@ -4466,12 +4479,12 @@ static herr_t test_vector_io(const char *vfd_name) { char test_title[80]; - bool size_fixed_0 = false; /* whether remaining entry */ - bool size_fixed_1 = false; /* sizes in vector are fixed. */ - bool size_fixed_2 = false; /* */ - bool type_fixed_0 = false; /* whether remaining entry */ - bool type_fixed_1 = false; /* types in vector are fixed. */ - bool type_fixed_2 = false; /* */ + bool size_fixed_0 = false; /* whether remaining entry */ + bool size_fixed_1 = false; /* sizes in vector are fixed. */ + bool size_fixed_2 = false; /* */ + bool type_fixed_0 = false; /* whether remaining entry */ + bool type_fixed_1 = false; /* types in vector are fixed. */ + bool type_fixed_2 = false; /* */ bool verbose = false; hid_t fapl_id = H5I_INVALID_HID; /* file access property list ID */ haddr_t eoa; /* file eoa */ @@ -4518,6 +4531,7 @@ test_vector_io(const char *vfd_name) void *f_read_bufs_0[VECTOR_LEN]; /* fixed read bufs vector */ void *f_read_bufs_1[VECTOR_LEN]; /* fixed read bufs vector */ void *f_read_bufs_2[VECTOR_LEN]; /* fixed read bufs vector */ + bool api_ctx_pushed = false; /* Whether API context pushed */ snprintf(test_title, sizeof(test_title), "vector I/O with %s VFD", vfd_name); @@ -4564,6 +4578,11 @@ test_vector_io(const char *vfd_name) f_read_bufs_2, 'B'))) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + flags = H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC; if (NULL == (lf = H5FDopen(filename, flags, fapl_id, HADDR_UNDEF))) @@ -4647,6 +4666,11 @@ test_vector_io(const char *vfd_name) if (H5FDread_vector(lf, H5P_DEFAULT, count, f_types_0, f_addrs_0, f_sizes_0, f_read_bufs_0) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* verify that the expected data is read */ if (!test_vector_io__verify_v(count, types_0, sizes_0, write_bufs_0, read_bufs_0, "zero")) TEST_ERROR; @@ -4660,6 +4684,11 @@ test_vector_io(const char *vfd_name) if (!test_vector_io__write_v_indiv(lf, count, types_1, addrs_1, sizes_1, write_bufs_1)) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + if (H5FDread_vector(lf, H5P_DEFAULT, 1, &(types_1[0]), &(addrs_1[0]), &(sizes_1[0]), &(read_bufs_1[0])) < 0) TEST_ERROR; @@ -4672,13 +4701,28 @@ test_vector_io(const char *vfd_name) &(read_bufs_1[3])) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* for fixed size, write individually, and the read back in a single call */ if (!test_vector_io__write_v_indiv(lf, count, f_types_1, f_addrs_1, f_sizes_1, f_write_bufs_1)) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + if (H5FDread_vector(lf, H5P_DEFAULT, count, f_types_1, f_addrs_1, f_sizes_1, f_read_bufs_1) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* verify that the expected data is read */ if (!test_vector_io__verify_v(count, types_1, sizes_1, write_bufs_1, read_bufs_1, "one")) TEST_ERROR; @@ -4686,6 +4730,11 @@ test_vector_io(const char *vfd_name) if (!test_vector_io__verify_v(count, f_types_1, f_sizes_1, f_write_bufs_1, f_read_bufs_1, "fixed one")) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Write the contents of a vector as several vector writes, then * read it back in individual reads. */ @@ -4701,13 +4750,28 @@ test_vector_io(const char *vfd_name) &(write_bufs_2[3])) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + if (!test_vector_io__read_v_indiv(lf, count, types_2, addrs_2, sizes_2, read_bufs_2)) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* for fixed size, write as a single vector, read back individually */ if (H5FDwrite_vector(lf, H5P_DEFAULT, count, f_types_2, f_addrs_2, f_sizes_2, f_write_bufs_2) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + if (!test_vector_io__read_v_indiv(lf, count, f_types_2, f_addrs_2, f_sizes_2, f_read_bufs_2)) TEST_ERROR; @@ -4718,6 +4782,11 @@ test_vector_io(const char *vfd_name) if (!test_vector_io__verify_v(count, f_types_2, f_sizes_2, f_write_bufs_2, f_read_bufs_2, "fixed two")) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* make note of eoa -- needed after we re-open the file */ if (HADDR_UNDEF == (eoa = H5FDget_eoa(lf, H5FD_MEM_DEFAULT))) TEST_ERROR; @@ -4804,6 +4873,11 @@ test_vector_io(const char *vfd_name) if (H5FDread_vector(lf, H5P_DEFAULT, count, f_types_2, f_addrs_2, f_sizes_2, f_read_bufs_2) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* verify the contents. */ if (!test_vector_io__verify_v(count, types_0, sizes_0, write_bufs_0, read_bufs_0, "zero-")) TEST_ERROR; @@ -4823,9 +4897,19 @@ test_vector_io(const char *vfd_name) if (!test_vector_io__verify_v(count, f_types_2, f_sizes_2, f_write_bufs_2, f_read_bufs_2, "fixed two-")) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + if (H5FDclose(lf) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + h5_delete_test_file(FILENAME[0], fapl_id); /* Close the fapl */ @@ -4877,6 +4961,9 @@ test_vector_io(const char *vfd_name) return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + H5E_BEGIN_TRY { H5Pclose(fapl_id); @@ -4908,6 +4995,7 @@ test_selection_io_write(H5FD_t *lf, H5FD_mem_t type, uint32_t count, hid_t mem_s const void **bufs; /* Avoids cast/const warnings */ int i; int j; + bool api_ctx_pushed = false; /* Whether API context pushed */ if (NULL == (bufs = calloc(count, sizeof(void *)))) TEST_ERROR; @@ -4920,16 +5008,29 @@ test_selection_io_write(H5FD_t *lf, H5FD_mem_t type, uint32_t count, hid_t mem_s bufs[i] = wbufs[i]; } + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Issue write call */ if (H5FDwrite_selection(lf, type, H5P_DEFAULT, count, mem_spaces, file_spaces, offsets, element_sizes, bufs) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + free(bufs); return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + free(bufs); return -1; } /* end test_selection_io_write() */ @@ -4957,6 +5058,7 @@ test_selection_io_read_verify(H5FD_t *lf, H5FD_mem_t type, uint32_t count, hid_t int *rbufs[2] = {rbuf1, rbuf2}; int i; int j; + bool api_ctx_pushed = false; /* Whether API context pushed */ /* Initialize read buffer */ for (i = 0; i < (int)rbufcount; i++) @@ -4970,11 +5072,21 @@ test_selection_io_read_verify(H5FD_t *lf, H5FD_mem_t type, uint32_t count, hid_t else rbufs[i] = rbufs[rbufcount - 1]; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Issue read call */ if (H5FDread_selection(lf, type, H5P_DEFAULT, count, mem_spaces, file_spaces, offsets, element_sizes, (void **)rbufs) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* Verify result */ for (i = 0; i < (int)rbufcount; i++) for (j = 0; j < SEL_IO_DIM0 * SEL_IO_DIM1; j++) @@ -5001,6 +5113,9 @@ test_selection_io_read_verify(H5FD_t *lf, H5FD_mem_t type, uint32_t count, hid_t return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + return -1; } /* end test_selection_io_read_verify() */ @@ -5050,7 +5165,8 @@ test_selection_io(const char *vfd_name) int erbuf1[SEL_IO_DIM0 * SEL_IO_DIM1]; /* 1D expected read buffer */ int erbuf2[SEL_IO_DIM0][SEL_IO_DIM1]; /* 2D expected read buffer */ int *erbufs[2] = {erbuf1, erbuf2[0]}; /* Array of expected read buffers */ - int shorten_element_sizes; /* Whether to shorten the element sizes array */ + int shorten_element_sizes; /* Whether to shorten the element sizes array */ + bool api_ctx_pushed = false; /* Whether API context pushed */ snprintf(test_title, sizeof(test_title), "selection I/O with %s VFD", vfd_name); @@ -5098,18 +5214,38 @@ test_selection_io(const char *vfd_name) if ((file_spaces[1] = H5Screate_simple(2, dims2, NULL)) < 0) TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Create file */ flags = H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC; if (NULL == (lf = H5FDopen(filename, flags, fapl_id, HADDR_UNDEF))) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* Loop over memory types */ for (type = 1; type < H5FD_MEM_NTYPES; type++) { + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* Allocate space for I/O */ addrs[0] = H5FDalloc(lf, type, H5P_DEFAULT, (hsize_t)(sizeof(int) * SEL_IO_DIM0 * SEL_IO_DIM1)); addrs[1] = H5FDalloc(lf, type, H5P_DEFAULT, (hsize_t)(sizeof(int) * SEL_IO_DIM0 * SEL_IO_DIM1)); + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + /* * Test 1: Simple 1D contiguous I/O */ @@ -5823,6 +5959,11 @@ test_selection_io(const char *vfd_name) element_sizes[1] = element_sizes[0]; } + /* Push API context */ + if (H5CX_push() < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = true; + /* * Cleanup */ @@ -5830,6 +5971,11 @@ test_selection_io(const char *vfd_name) if (H5FDclose(lf) < 0) TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed && H5CX_pop(false) < 0) + FAIL_STACK_ERROR; + api_ctx_pushed = false; + h5_delete_test_file(FILENAME[0], fapl_id); /* Close the fapl */ @@ -5848,6 +5994,9 @@ test_selection_io(const char *vfd_name) return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + H5E_BEGIN_TRY { H5Pclose(fapl_id); diff --git a/testpar/t_vfd.c b/testpar/t_vfd.c index cce5cf775e8..07c44a93929 100644 --- a/testpar/t_vfd.c +++ b/testpar/t_vfd.c @@ -356,6 +356,7 @@ setup_vfd_test_file(int file_name_id, char *file_name, int mpi_size, H5FD_mpio_x hid_t dxpl_id = H5I_INVALID_HID; /* data access property list ID */ unsigned flags = 0; /* file open flags */ H5FD_t *lf = NULL; /* VFD struct ptr */ + bool api_ctx_pushed = false; /* Whether API context pushed */ assert(vfd_name); assert(lf_ptr); @@ -509,6 +510,12 @@ setup_vfd_test_file(int file_name_id, char *file_name, int mpi_size, H5FD_mpio_x if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* Open the VFD test file with the specified VFD. */ if (pass) { @@ -595,6 +602,10 @@ setup_vfd_test_file(int file_name_id, char *file_name, int mpi_size, H5FD_mpio_x H5Pclose(dxpl_id); } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + return; } /* setup_vfd_test_file() */ @@ -616,6 +627,7 @@ takedown_vfd_test_file(int mpi_rank, char *filename, H5FD_t **lf_ptr, hid_t *fap const char *fcn_name = "takedown_vfd_test_file()"; int cp = 0; bool show_progress = false; + bool api_ctx_pushed = false; /* Whether API context pushed */ assert(lf_ptr); assert(fapl_id_ptr); @@ -624,6 +636,12 @@ takedown_vfd_test_file(int mpi_rank, char *filename, H5FD_t **lf_ptr, hid_t *fap if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* Close the test file if it is open, regardless of the value of pass. * This should let the test program shut down more cleanly. */ @@ -637,6 +655,11 @@ takedown_vfd_test_file(int mpi_rank, char *filename, H5FD_t **lf_ptr, hid_t *fap } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -745,6 +768,7 @@ vector_read_test_1(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ haddr_t addrs[1]; size_t sizes[1]; void *bufs[1]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -788,6 +812,12 @@ vector_read_test_1(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Using rank zero, write the entire increasing_fi_buf to * the file. */ @@ -806,12 +836,23 @@ vector_read_test_1(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* 3) Barrier */ MPI_Barrier(comm); if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 4) On each rank, zero the read buffer, and then read * INTS_PER_RANK * sizeof(int32) bytes from the file * starting at offset mpi_rank * INTS_PER_RANK * @@ -867,6 +908,11 @@ vector_read_test_1(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -975,6 +1021,7 @@ vector_read_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ haddr_t addrs[1]; size_t sizes[1]; void *bufs[1]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -1018,6 +1065,12 @@ vector_read_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Using rank zero, write the entire decreasing_fi_buf to * the file. */ @@ -1036,6 +1089,11 @@ vector_read_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* 3) Barrier */ MPI_Barrier(comm); @@ -1054,6 +1112,12 @@ vector_read_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 5) On even ranks, read INTS_PER_RANK * sizeof(int32) * bytes from the file starting at offset mpi_rank * * INTS_PER_RANK * sizeof(int32_t) in both the file and @@ -1158,6 +1222,11 @@ vector_read_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -1276,6 +1345,7 @@ vector_read_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ haddr_t addrs[4]; size_t sizes[4]; void *bufs[4]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -1319,6 +1389,12 @@ vector_read_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Using rank zero, write the entire negative_fi_buf to * the file. */ @@ -1337,6 +1413,11 @@ vector_read_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* 3) Barrier */ MPI_Barrier(comm); @@ -1358,6 +1439,12 @@ vector_read_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 5) On each rank, do a vector read from the file, with * each rank's vector having four elements, with each * element reading INTS_PER_RANK / 4 * sizeof(int32) @@ -1428,6 +1515,11 @@ vector_read_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -1629,6 +1721,7 @@ vector_read_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ haddr_t addrs[4]; size_t sizes[4]; void *bufs[4]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -1672,6 +1765,12 @@ vector_read_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Using rank zero, write the entire negative_fi_buf to * the file. */ @@ -1690,6 +1789,11 @@ vector_read_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* 3) Barrier */ MPI_Barrier(comm); @@ -1708,6 +1812,12 @@ vector_read_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 5) For each rank, define base_index equal to: * * mpi_rank * INTS_PER_RANK @@ -1816,6 +1926,11 @@ vector_read_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* 6) On each rank, verify that read_fi_buf contains the * the expected values -- that is the matching values from * increasing_fi_buf where ever there was a read, and zero @@ -2056,6 +2171,7 @@ vector_read_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ haddr_t addrs[(INTS_PER_RANK / 16) + 1]; size_t sizes[2]; void *bufs[(INTS_PER_RANK / 16) + 1]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -2099,6 +2215,12 @@ vector_read_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Using rank zero, write the entire negative_fi_buf to * the file. */ @@ -2117,6 +2239,11 @@ vector_read_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* 3) Barrier */ MPI_Barrier(comm); @@ -2135,6 +2262,12 @@ vector_read_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 5) For each rank, define base_index equal to: * * mpi_rank * INTS_PER_RANK @@ -2176,6 +2309,11 @@ vector_read_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer_ } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -2292,6 +2430,7 @@ vector_write_test_1(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer haddr_t addrs[1]; size_t sizes[1]; const void *bufs[1]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -2335,6 +2474,12 @@ vector_write_test_1(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Write the entire increasing_fi_buf to the file, with * exactly one buffer per vector per rank. Use either * independent or collective I/O as specified. @@ -2355,6 +2500,11 @@ vector_write_test_1(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -2365,6 +2515,12 @@ vector_write_test_1(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 4) On each rank, read the entire file into the read_fi_buf, * and compare against increasing_fi_buf. Report failure * if any differences are detected. @@ -2391,6 +2547,11 @@ vector_write_test_1(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -2479,6 +2640,7 @@ vector_write_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer haddr_t addrs[1]; size_t sizes[1]; const void *bufs[1]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -2522,6 +2684,12 @@ vector_write_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Write the odd blocks of the increasing_fi_buf to the file, * with the odd ranks writing the odd blocks, and the even * ranks writing an empty vector. @@ -2589,6 +2757,11 @@ vector_write_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -2599,6 +2772,12 @@ vector_write_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 5) On each rank, read the entire file into the read_fi_buf, * and compare against increasing_fi_buf. Report failure * if any differences are detected. @@ -2643,6 +2822,11 @@ vector_write_test_2(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -2732,6 +2916,7 @@ vector_write_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer haddr_t addrs[4]; size_t sizes[4]; const void *bufs[4]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -2775,6 +2960,12 @@ vector_write_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) For each rank, construct a vector with base address * (mpi_rank * INTS_PER_RANK) and writing all bytes from * that address to ((mpi_rank + 1) * INTS_PER_RANK) - 1. @@ -2818,6 +3009,11 @@ vector_write_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -2828,6 +3024,12 @@ vector_write_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 4) On each rank, read the entire file into the read_fi_buf, * and compare against increasing_fi_buf, * decreasing_fi_buf, negative_fi_buf, and zero_fi_buf as @@ -2897,6 +3099,11 @@ vector_write_test_3(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -2992,6 +3199,7 @@ vector_write_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer haddr_t addrs[4]; size_t sizes[4]; const void *bufs[4]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -3035,6 +3243,12 @@ vector_write_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) For each rank, construct a vector with base address * (mpi_rank * INTS_PER_RANK) and writing all bytes from * that address to ((mpi_rank + 1) * INTS_PER_RANK) - 1. @@ -3079,6 +3293,11 @@ vector_write_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -3089,6 +3308,12 @@ vector_write_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 4) On each rank, read the entire file into the read_fi_buf, * and compare against increasing_fi_buf, * decreasing_fi_buf, negative_fi_buf, and zero_fi_buf as @@ -3158,6 +3383,11 @@ vector_write_test_4(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -3289,6 +3519,7 @@ vector_write_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer haddr_t addrs[4]; size_t sizes[4]; const void *bufs[4]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -3332,6 +3563,12 @@ vector_write_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Set the test file in a known state by writing zeros * to all bytes in the test file. Since we have already * tested this, do this via a vector write of zero_fi_buf. @@ -3351,6 +3588,11 @@ vector_write_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -3361,6 +3603,12 @@ vector_write_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 4) For each rank, define base_index equal to: * * mpi_rank * INTS_PER_RANK @@ -3468,6 +3716,11 @@ vector_write_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -3477,6 +3730,12 @@ vector_write_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 6) On each rank, read the entire file into the read_fi_buf, * and compare against increasing_fi_buf, * decreasing_fi_buf, negative_fi_buf, and zero_fi_buf as @@ -3619,6 +3878,11 @@ vector_write_test_5(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -3723,6 +3987,7 @@ vector_write_test_6(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer haddr_t addrs[(INTS_PER_RANK / 16) + 1]; size_t sizes[2]; const void *bufs[(INTS_PER_RANK / 16) + 1]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -3766,6 +4031,12 @@ vector_write_test_6(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Using rank zero, write the entire negative_fi_buf to * the file. */ @@ -3783,12 +4054,23 @@ vector_write_test_6(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* 3) Barrier */ MPI_Barrier(comm); if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 4) For each rank, define base_index equal to: * * mpi_rank * INTS_PER_RANK @@ -3830,6 +4112,11 @@ vector_write_test_6(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -3839,6 +4126,12 @@ vector_write_test_6(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 6) On each rank, read the entire file into the read_fi_buf, * and compare against zero_fi_buf, and increasing_fi_buf * as appropriate. Report failure if any differences are @@ -3872,6 +4165,11 @@ vector_write_test_6(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } /* end if */ + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -3980,6 +4278,7 @@ vector_write_test_7(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer haddr_t addrs[8]; size_t sizes[8]; const void *bufs[8]; + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -4023,6 +4322,12 @@ vector_write_test_7(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Set the test file in a known state by writing zeros * to all bytes in the test file. Since we have already * tested this, do this via a vector write of zero_fi_buf. @@ -4042,6 +4347,11 @@ vector_write_test_7(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -4052,6 +4362,12 @@ vector_write_test_7(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + if (pass) { base_index = mpi_rank * INTS_PER_RANK; @@ -4075,6 +4391,11 @@ vector_write_test_7(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -4084,6 +4405,12 @@ vector_write_test_7(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 6) On each rank, read the entire file into the read_fi_buf, * and compare against increasing_fi_buf, and zero_fi_buf as * appropriate. Report failure if any differences are @@ -4133,6 +4460,11 @@ vector_write_test_7(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -4231,9 +4563,9 @@ vector_write_test_8(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer uint32_t count = 0; size_t sizes[4]; H5FD_mem_t types[2]; - haddr_t *tt_addrs = NULL; /* For storing addrs */ const void **tt_bufs = NULL; /* For storing buf pointers */ + bool api_ctx_pushed = false; /* Whether API context pushed */ pass = true; @@ -4288,6 +4620,12 @@ vector_write_test_8(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 2) Using rank zero, write the entire negative_fi_buf to * the file. */ @@ -4305,12 +4643,23 @@ vector_write_test_8(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* 3) Barrier */ MPI_Barrier(comm); if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 4) For each rank, define base_index equal to: * * mpi_rank * INTS_PER_RANK @@ -4357,6 +4706,11 @@ vector_write_test_8(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -4366,6 +4720,12 @@ vector_write_test_8(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* 6) On each rank, read the entire file into the read_fi_buf, * and compare against increasing_fi_buf * Report failure if any differences are detected. @@ -4390,6 +4750,11 @@ vector_write_test_8(int file_name_id, int mpi_rank, int mpi_size, H5FD_mpio_xfer } } /* end if */ + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (show_progress) fprintf(stdout, "%s: cp = %d, pass = %d.\n", fcn_name, cp++, pass); @@ -4650,6 +5015,7 @@ test_selection_io_read_verify(hid_t dxpl, int mpi_rank, hsize_t start[], hsize_t size_t bufsize; int i; int j; + bool api_ctx_pushed = false; /* Whether API context pushed */ bufsize = (hsize_t)(sel_dim0 * sel_dim1) * sizeof(int); if ((rbuf1 = malloc(bufsize)) == NULL) @@ -4671,6 +5037,12 @@ test_selection_io_read_verify(hid_t dxpl, int mpi_rank, hsize_t start[], hsize_t else rbufs[i] = rbufs[rbufcount - 1]; + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* Issue read call */ if (H5FDread_selection(lf, type, dxpl, count, mem_spaces, file_spaces, offsets, element_sizes, (void **)rbufs) < 0) @@ -4709,6 +5081,11 @@ test_selection_io_read_verify(hid_t dxpl, int mpi_rank, hsize_t start[], hsize_t return 0; error: + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (rbuf1) free(rbuf1); if (rbuf2) @@ -4727,6 +5104,7 @@ test_selection_io_write(hid_t dxpl, H5FD_t *lf, H5FD_mem_t type, uint32_t count, const void **bufs = NULL; /* Avoids cast/const warnings */ int i; int j; + bool api_ctx_pushed = false; /* Whether API context pushed */ if (NULL == (bufs = calloc(count, sizeof(void *)))) goto error; @@ -4739,6 +5117,12 @@ test_selection_io_write(hid_t dxpl, H5FD_t *lf, H5FD_mem_t type, uint32_t count, bufs[i] = wb[i]; } + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* Issue write call */ if (H5FDwrite_selection(lf, type, dxpl, count, mem_spaces, file_spaces, offsets, element_sizes, bufs) < 0) goto error; @@ -4749,6 +5133,11 @@ test_selection_io_write(hid_t dxpl, H5FD_t *lf, H5FD_mem_t type, uint32_t count, return 0; error: + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + if (bufs) free(bufs); return -1; @@ -5959,13 +6348,13 @@ test_selection_io_real(int mpi_rank, int mpi_size, H5FD_t *lf, hid_t dxpl) hid_t file_spaces[2] = {H5I_INVALID_HID, H5I_INVALID_HID}; /* file dataspaces vector */ hsize_t dims1[1]; /* 1d dimension sizes */ hsize_t dims2[2]; /* 2d dimension sizes */ - H5FD_mem_t type; /* File type */ haddr_t addrs[2]; /* File allocation address */ size_t element_sizes[2] = {sizeof(int), sizeof(int)}; /* Element size */ size_t bufsize; /* Buffer size */ int i; int j; + bool api_ctx_pushed = false; /* Whether API context pushed */ curr_nerrors = nerrors; @@ -6036,9 +6425,20 @@ test_selection_io_real(int mpi_rank, int mpi_size, H5FD_t *lf, hid_t dxpl) /* Loop over memory types */ for (type = 1; type < H5FD_MEM_NTYPES; type++) { + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + addrs[0] = H5FDalloc(lf, type, H5P_DEFAULT, (sizeof(int) * (hsize_t)sel_dim0 * (hsize_t)sel_dim1)); addrs[1] = H5FDalloc(lf, type, H5P_DEFAULT, (sizeof(int) * (hsize_t)sel_dim0 * (hsize_t)sel_dim1)); + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + test_selection_io_types_1d(mpi_rank, mpi_size, lf, dxpl, type, addrs, element_sizes, mem_spaces, file_spaces, dims1); test_selection_io_types_2d(mpi_rank, mpi_size, lf, dxpl, type, addrs, element_sizes, mem_spaces, @@ -6089,12 +6489,12 @@ test_selection_io(int mpi_rank, int mpi_size) hid_t fapl = H5I_INVALID_HID; /* File access property list */ char filename[1024]; /* Test file name */ unsigned flags = 0; /* File access flags */ - unsigned collective; /* Types of I/O for testing */ hid_t dxpl = H5I_INVALID_HID; /* Dataset transfer property list */ hid_t def_dxpl = H5I_INVALID_HID; /* dxpl: independent access */ hid_t col_xfer_dxpl = H5I_INVALID_HID; /* dxpl: collective access with collective I/O */ hid_t ind_io_dxpl = H5I_INVALID_HID; /* dxpl: collective access with individual I/O */ + bool api_ctx_pushed = false; /* Whether API context pushed */ /* If I use fapl in this call, I got an environment printout */ h5_fixname(SELECT_FNAME, H5P_DEFAULT, filename, sizeof(filename)); @@ -6105,12 +6505,23 @@ test_selection_io(int mpi_rank, int mpi_size) if (H5Pset_fapl_mpio(fapl, comm, info) < 0) P_TEST_ERROR; + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* Create file */ flags = H5F_ACC_RDWR | H5F_ACC_CREAT | H5F_ACC_TRUNC; if (NULL == (lf = H5FDopen(filename, flags, fapl, HADDR_UNDEF))) P_TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* Default dxpl which will be H5FD_MPIO_INDEPENDENT by default */ def_dxpl = H5Pcreate(H5P_DATASET_XFER); @@ -6148,10 +6559,21 @@ test_selection_io(int mpi_rank, int mpi_size) test_selection_io_real(mpi_rank, mpi_size, lf, dxpl); } + /* Push API context */ + if (H5CX_push() < 0) + pass = FALSE; + else + api_ctx_pushed = true; + /* Close file */ if (H5FDclose(lf) < 0) P_TEST_ERROR; + /* Pop API context */ + if (api_ctx_pushed) + H5CX_pop(false); + api_ctx_pushed = false; + /* Close the fapl */ if (H5Pclose(fapl) < 0) P_TEST_ERROR; diff --git a/utils/mirror_vfd/mirror_writer.c b/utils/mirror_vfd/mirror_writer.c index f1569657deb..f91b00c8f5d 100644 --- a/utils/mirror_vfd/mirror_writer.c +++ b/utils/mirror_vfd/mirror_writer.c @@ -220,12 +220,19 @@ session_init(struct mirror_writer_opts *opts) static int session_stop(struct mirror_session *session) { + bool api_ctx_pushed = false; /* Whether API context pushed */ int ret_value = 0; assert(session && (session->magic == MW_SESSION_MAGIC)); mirror_log(session->loginfo, V_INFO, "session_stop()"); + /* Push API context */ + if (H5CX_push() < 0) + ret_value--; + else + api_ctx_pushed = true; + /* Close HDF5 file if it is still open (probably in error) */ if (session->file) { mirror_log(session->loginfo, V_WARN, "HDF5 file still open at cleanup"); @@ -248,6 +255,9 @@ session_stop(struct mirror_session *session) session->magic++; free(session); + if (api_ctx_pushed) + H5CX_pop(false); + return ret_value; } /* end session_stop() */ @@ -396,11 +406,18 @@ reply_error(struct mirror_session *session, const char *msg) static int do_close(struct mirror_session *session) { + bool api_ctx_pushed = false; /* Whether API context pushed */ assert(session && (session->magic == MW_SESSION_MAGIC)); mirror_log(session->loginfo, V_INFO, "do_close()"); + /* Push API context */ + if (H5CX_push() < 0) + return -1; + else + api_ctx_pushed = true; + if (NULL == session->file) { mirror_log(session->loginfo, V_ERR, "no file to close!"); reply_error(session, "no file to close"); @@ -420,6 +437,9 @@ do_close(struct mirror_session *session) return -1; } + if (api_ctx_pushed) + H5CX_pop(false); + return 0; } /* end do_close() */ @@ -436,11 +456,18 @@ do_lock(struct mirror_session *session, const unsigned char *xmit_buf) { size_t decode_ret = 0; H5FD_mirror_xmit_lock_t xmit_lock; + bool api_ctx_pushed = false; /* Whether API context pushed */ assert(session && (session->magic == MW_SESSION_MAGIC) && xmit_buf); mirror_log(session->loginfo, V_INFO, "do_lock()"); + /* Push API context */ + if (H5CX_push() < 0) + return -1; + else + api_ctx_pushed = true; + decode_ret = H5FD_mirror_xmit_decode_lock(&xmit_lock, xmit_buf); if (H5FD_MIRROR_XMIT_LOCK_SIZE != decode_ret) { mirror_log(session->loginfo, V_ERR, "can't decode set-eoa xmit"); @@ -467,6 +494,9 @@ do_lock(struct mirror_session *session, const unsigned char *xmit_buf) return -1; } + if (api_ctx_pushed) + H5CX_pop(false); + return 0; } /* end do_lock() */ @@ -484,6 +514,7 @@ do_open(struct mirror_session *session, const H5FD_mirror_xmit_open_t *xmit_open hid_t fapl_id = H5I_INVALID_HID; unsigned _flags = 0; haddr_t _maxaddr = HADDR_UNDEF; + bool api_ctx_pushed = false; /* Whether API context pushed */ assert(session && (session->magic == MW_SESSION_MAGIC) && xmit_open && true == H5FD_mirror_xmit_is_open(xmit_open)); @@ -533,6 +564,12 @@ do_open(struct mirror_session *session, const H5FD_mirror_xmit_open_t *xmit_open goto error; } + /* Push API context */ + if (H5CX_push() < 0) + goto error; + else + api_ctx_pushed = true; + session->file = H5FDopen(xmit_open->filename, _flags, fapl_id, _maxaddr); if (NULL == session->file) { mirror_log(session->loginfo, V_ERR, "H5FDopen()"); @@ -553,9 +590,15 @@ do_open(struct mirror_session *session, const H5FD_mirror_xmit_open_t *xmit_open return -1; } + if (api_ctx_pushed) + H5CX_pop(false); + return 0; error: + if (api_ctx_pushed) + H5CX_pop(false); + if (fapl_id > 0) { H5E_BEGIN_TRY { @@ -579,11 +622,18 @@ do_set_eoa(struct mirror_session *session, const unsigned char *xmit_buf) { size_t decode_ret = 0; H5FD_mirror_xmit_eoa_t xmit_seoa; + bool api_ctx_pushed = false; /* Whether API context pushed */ assert(session && (session->magic == MW_SESSION_MAGIC) && xmit_buf); mirror_log(session->loginfo, V_INFO, "do_set_eoa()"); + /* Push API context */ + if (H5CX_push() < 0) + return -1; + else + api_ctx_pushed = true; + decode_ret = H5FD_mirror_xmit_decode_set_eoa(&xmit_seoa, xmit_buf); if (H5FD_MIRROR_XMIT_EOA_SIZE != decode_ret) { mirror_log(session->loginfo, V_ERR, "can't decode set-eoa xmit"); @@ -611,6 +661,9 @@ do_set_eoa(struct mirror_session *session, const unsigned char *xmit_buf) return -1; } + if (api_ctx_pushed) + H5CX_pop(false); + return 0; } /* end do_set_eoa() */ @@ -625,11 +678,18 @@ do_set_eoa(struct mirror_session *session, const unsigned char *xmit_buf) static int do_truncate(struct mirror_session *session) { + bool api_ctx_pushed = false; /* Whether API context pushed */ assert(session && (session->magic == MW_SESSION_MAGIC)); mirror_log(session->loginfo, V_INFO, "do_truncate()"); + /* Push API context */ + if (H5CX_push() < 0) + return -1; + else + api_ctx_pushed = true; + /* default DXPL ID (0), 0 for "false" closing -- both probably unused */ if (H5FDtruncate(session->file, 0, 0) < 0) { mirror_log(session->loginfo, V_ERR, "H5FDtruncate()"); @@ -643,6 +703,9 @@ do_truncate(struct mirror_session *session) return -1; } + if (api_ctx_pushed) + H5CX_pop(false); + return 0; } /* end do_truncate() */ @@ -657,10 +720,18 @@ do_truncate(struct mirror_session *session) static int do_unlock(struct mirror_session *session) { + bool api_ctx_pushed = false; /* Whether API context pushed */ + assert(session && (session->magic == MW_SESSION_MAGIC)); mirror_log(session->loginfo, V_INFO, "do_unlock()"); + /* Push API context */ + if (H5CX_push() < 0) + return -1; + else + api_ctx_pushed = true; + if (H5FDunlock(session->file) < 0) { mirror_log(session->loginfo, V_ERR, "H5FDunlock()"); reply_error(session, "remote H5FDunlock() failure"); @@ -673,6 +744,9 @@ do_unlock(struct mirror_session *session) return -1; } + if (api_ctx_pushed) + H5CX_pop(false); + return 0; } /* end do_unlock() */ @@ -700,6 +774,7 @@ do_write(struct mirror_session *session, const unsigned char *xmit_buf) char *buf = NULL; ssize_t nbytes_in_packet = 0; H5FD_mirror_xmit_write_t xmit_write; + bool api_ctx_pushed = false; /* Whether API context pushed */ assert(session && (session->magic == MW_SESSION_MAGIC) && xmit_buf); @@ -745,6 +820,12 @@ do_write(struct mirror_session *session, const unsigned char *xmit_buf) mirror_log(session->loginfo, V_INFO, "to write %zu bytes at %zu", xmit_write.size, addr); + /* Push API context */ + if (H5CX_push() < 0) + return -1; + else + api_ctx_pushed = true; + /* The given write may be: * 1. larger than the allowed single buffer size * 2. larger than the native size_t of this system @@ -790,6 +871,9 @@ do_write(struct mirror_session *session, const unsigned char *xmit_buf) return -1; } + if (api_ctx_pushed) + H5CX_pop(false); + return 0; } /* end do_write() */