diff --git a/.github/workflows/ci_cmake.yml b/.github/workflows/ci_cmake.yml index 10a144ff..a9f35458 100644 --- a/.github/workflows/ci_cmake.yml +++ b/.github/workflows/ci_cmake.yml @@ -22,7 +22,7 @@ jobs: linux: runs-on: ubuntu-22.04 name: CMake build on Linux - timeout-minutes: 15 + timeout-minutes: 60 strategy: matrix: @@ -92,16 +92,16 @@ jobs: - name: Upload package if: github.event.action == 'published' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: binary-archive + name: linux-binary-archive-${{ matrix.cc }}-${{ matrix.mpi }}-shared-${{ matrix.shared }} path: build/package - name: Upload log files if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: linux_cmake_log + name: linux_cmake_log-${{ matrix.cc }}-${{ matrix.mpi }}-shared-${{ matrix.shared }} path: | ./build/CMakeFiles/CMakeConfigureLog.yaml ./build/Testing/Temporary/LastTest.log @@ -110,7 +110,7 @@ jobs: needs: linux runs-on: ubuntu-22.04 name: CMake with Valgrind - timeout-minutes: 15 + timeout-minutes: 60 strategy: matrix: @@ -143,7 +143,7 @@ jobs: - name: Upload log files if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linux_cmake_valgrind_log path: | @@ -151,9 +151,11 @@ jobs: ./build/Testing/Temporary/LastTest.log mac: - runs-on: macos-latest + # macos-14 is to use Apple Silicon hardware as most Apple users nowadays would have + # https://github.blog/changelog/2024-01-30-github-actions-introducing-the-new-m1-macos-runner-available-to-open-source/ + runs-on: macos-14 name: CMake build on MacOS - timeout-minutes: 15 + timeout-minutes: 60 strategy: matrix: @@ -198,16 +200,16 @@ jobs: - name: Upload package if: github.event.action == 'published' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: binary-archive + name: mac-binary-archive-${{ matrix.cc }}-shared-${{ matrix.shared }} path: build/package - name: Upload log files if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: mac_cmake_log + name: mac_cmake_log-${{ matrix.cc }}-shared-${{ matrix.shared }} path: | ./build/CMakeFiles/CMakeConfigureLog.yaml ./build/Testing/Temporary/LastTest.log @@ -215,7 +217,7 @@ jobs: windows: runs-on: windows-latest name: CMake build on Windows - timeout-minutes: 20 + timeout-minutes: 60 strategy: matrix: @@ -253,14 +255,14 @@ jobs: - name: Upload package if: github.event.action == 'published' - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: binary-archive + name: windows-binary-archive path: build/package - name: Upload log files if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: windows_cmake_log path: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 67d7cd28..9ebf6efb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ $<$:m> $<$:${WINSOCK_LIBRARIES}> ) -# imported target, for use from FetchContent +# imported target, for use from parent project add_library(SC::SC INTERFACE IMPORTED GLOBAL) target_link_libraries(SC::SC INTERFACE sc) diff --git a/CMakePresets.json b/CMakePresets.json index 966cd383..62fc934a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -30,7 +30,7 @@ "noTestsAction": "error", "scheduleRandom": true, "stopOnFailure": false, - "timeout": 30 + "timeout": 60 }, "filter": { "exclude": { diff --git a/cmake/config.cmake b/cmake/config.cmake index d311775a..74d4f6de 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -49,11 +49,16 @@ endif() find_package(Threads) -find_package(jansson CONFIG) -if(TARGET jansson::jansson) - set(SC_HAVE_JSON 1 CACHE BOOL "JSON features enabled") +if(json) + message(STATUS "Using builtin jansson") + include(${CMAKE_CURRENT_LIST_DIR}/jansson.cmake) else() - set(SC_HAVE_JSON 0 CACHE BOOL "JSON features disabled") + find_package(jansson CONFIG) + if(TARGET jansson::jansson) + set(SC_HAVE_JSON 1 CACHE BOOL "JSON features enabled") + else() + set(SC_HAVE_JSON 0 CACHE BOOL "JSON features disabled") + endif() endif() # --- set global compile environment @@ -74,6 +79,7 @@ if(mpi) set(SC_CC \"${MPI_C_COMPILER}\") set(SC_CPP ${MPI_C_COMPILER}) else() + set(SC_ENABLE_MPI 0) set(SC_CC \"${CMAKE_C_COMPILER}\") set(SC_CPP ${CMAKE_C_COMPILER}) endif() @@ -104,7 +110,16 @@ endif() set(SC_ENABLE_PTHREAD ${CMAKE_USE_PTHREADS_INIT}) set(SC_ENABLE_MEMALIGN 1) -if(mpi) +if(NOT SC_ENABLE_MPI EQUAL CACHE{SC_ENABLE_MPI}) + # user has requested a different MPI setting, so we need to clear these cache variables to recheck + unset(SC_ENABLE_MPICOMMSHARED CACHE) + unset(SC_ENABLE_MPITHREAD CACHE) + unset(SC_ENABLE_MPIWINSHARED CACHE) + unset(SC_ENABLE_MPIIO CACHE) + unset(SC_ENABLE_MPI CACHE) +endif() + +if(SC_ENABLE_MPI) check_symbol_exists(MPI_COMM_TYPE_SHARED mpi.h SC_ENABLE_MPICOMMSHARED) # perform check to set SC_ENABLE_MPIIO include(cmake/check_mpiio.cmake) @@ -201,9 +216,8 @@ endif() check_type_size(int SC_SIZEOF_INT BUILTIN_TYPES_ONLY) check_type_size("unsigned int" SC_SIZEOF_UNSIGNED_INT BUILTIN_TYPES_ONLY) check_type_size(long SC_SIZEOF_LONG BUILTIN_TYPES_ONLY) -check_type_size("long long" SC_SIZEOF_LONG_LONG BUILTIN_TYPES_ONLY) check_type_size("unsigned long" SC_SIZEOF_UNSIGNED_LONG BUILTIN_TYPES_ONLY) -check_type_size("unsigned long long" SC_SIZEOF_UNSIGNED_LONG_LONG BUILTIN_TYPES_ONLY) +check_type_size("long long" SC_SIZEOF_LONG_LONG BUILTIN_TYPES_ONLY) set(SC_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) if(CMAKE_BUILD_TYPE MATCHES "Debug") @@ -214,6 +228,9 @@ endif() configure_file(${CMAKE_CURRENT_LIST_DIR}/sc_config.h.in ${PROJECT_BINARY_DIR}/include/sc_config.h) +file(TIMESTAMP ${PROJECT_BINARY_DIR}/include/sc_config.h _t) +message(VERBOSE "sc_config.h was last generated ${_t}") + # --- sanity check of MPI sc_config.h unset(SC_ENABLE_MPI) @@ -226,9 +243,17 @@ set(CMAKE_REQUIRED_LIBRARIES) set(CMAKE_REQUIRED_DEFINITIONS) # libsc and current project must both be compiled with/without MPI -check_symbol_exists(SC_ENABLE_MPI ${PROJECT_BINARY_DIR}/include/sc_config.h SC_ENABLE_MPI) -check_symbol_exists(SC_ENABLE_MPIIO ${PROJECT_BINARY_DIR}/include/sc_config.h SC_ENABLE_MPIIO) +check_symbol_exists("SC_ENABLE_MPI" ${PROJECT_BINARY_DIR}/include/sc_config.h SC_ENABLE_MPI) +check_symbol_exists("SC_ENABLE_MPIIO" ${PROJECT_BINARY_DIR}/include/sc_config.h SC_ENABLE_MPIIO) +# check consistency of MPI configuration if(mpi AND NOT (SC_ENABLE_MPI AND SC_ENABLE_MPIIO)) message(FATAL_ERROR "libsc MPI support was requested, but not configured in ${PROJECT_BINARY_DIR}/include/sc_config.h") endif() + +# check consistency of MPI I/O configuration +if(mpi AND (SC_ENABLE_MPI AND NOT SC_ENABLE_MPIIO)) + message(WARNING "libsc MPI configured but MPI I/O is not configured/found: DEPRECATED") + message(NOTICE "This configuration is DEPRECATED and will be disallowed in the future.") + message(NOTICE "If the MPI File API is not available, please disable MPI altogether.") +endif() diff --git a/cmake/git.cmake b/cmake/git.cmake index f88e9846..4718b8e9 100644 --- a/cmake/git.cmake +++ b/cmake/git.cmake @@ -5,7 +5,7 @@ set(PROJECT_MINOR 0) set(PROJECT_PATCH 0) set(PROJECT_VERSION 0.0.0) find_program(GIT_VERSION_GEN NAMES git-version-gen - PATHS ${CMAKE_SOURCE_DIR}/build-aux NO_DEFAULT_PATH) + PATHS ${PROJECT_SOURCE_DIR}/build-aux NO_DEFAULT_PATH) if(GIT_VERSION_GEN) execute_process(COMMAND ${GIT_VERSION_GEN} .tarball-version WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/cmake/jansson.cmake b/cmake/jansson.cmake new file mode 100644 index 00000000..df2ce358 --- /dev/null +++ b/cmake/jansson.cmake @@ -0,0 +1,52 @@ +include(ExternalProject) +include(GNUInstallDirs) + +set(SC_HAVE_JSON 1 CACHE BOOL "using SC-built JANSSON") + +set(jansson_url "https://github.com/akheron/jansson/releases/download/v2.14/jansson-2.14.tar.bz2") + +set(JANSSON_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include) + +if(BUILD_SHARED_LIBS) + if(WIN32) + set(JANSSON_LIBRARIES ${CMAKE_INSTALL_FULL_BINDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}jansson${CMAKE_SHARED_LIBRARY_SUFFIX}) + else() + set(JANSSON_LIBRARIES ${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}jansson${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() +else() + set(JANSSON_LIBRARIES ${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}jansson${CMAKE_STATIC_LIBRARY_SUFFIX}) +endif() + +set(jansson_cmake_args +-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} +-DJANSSON_BUILD_SHARED_LIBS:BOOL=${BUILD_SHARED_LIBS} +-DCMAKE_BUILD_TYPE=Release +-DJANSSON_EXAMPLES:BOOL=off +-DJANSSON_WITHOUT_TESTS:BOOL=on +-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON +-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} +) + +ExternalProject_Add(JANSSON +URL ${jansson_url} +CMAKE_ARGS ${jansson_cmake_args} +BUILD_BYPRODUCTS ${JANSSON_LIBRARIES} +CONFIGURE_HANDLED_BY_BUILD ON +USES_TERMINAL_DOWNLOAD true +USES_TERMINAL_UPDATE true +USES_TERMINAL_CONFIGURE true +USES_TERMINAL_BUILD true +USES_TERMINAL_INSTALL true +USES_TERMINAL_TEST true +) + + +# --- imported target + +file(MAKE_DIRECTORY ${JANSSON_INCLUDE_DIRS}) +# avoid race condition + +add_library(jansson::jansson INTERFACE IMPORTED GLOBAL) +add_dependencies(jansson::jansson JANSSON) # to avoid include directory race condition +target_link_libraries(jansson::jansson INTERFACE ${JANSSON_LIBRARIES}) +target_include_directories(jansson::jansson INTERFACE ${JANSSON_INCLUDE_DIRS}) diff --git a/cmake/options.cmake b/cmake/options.cmake index a7bab317..48145398 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -1,6 +1,8 @@ option(mpi "use MPI library" off) option(openmp "use OpenMP" off) option(zlib "build ZLIB" off) +option(json "build Jansson" off) + option(SC_BUILD_TESTING "build libsc self-tests" on) option(TEST_WITH_VALGRIND "run self-tests with valgrind" OFF) option(BUILD_SHARED_LIBS "build shared libsc") diff --git a/cmake/sc_config.h.in b/cmake/sc_config.h.in index 481e9ff9..d4d05f49 100644 --- a/cmake/sc_config.h.in +++ b/cmake/sc_config.h.in @@ -213,14 +213,11 @@ #ifndef SC_SIZEOF_LONG #define SC_SIZEOF_LONG @SC_SIZEOF_LONG@ #endif -#ifndef SC_SIZEOF_LONG_LONG -#define SC_SIZEOF_LONG_LONG @SC_SIZEOF_LONG_LONG@ -#endif #ifndef SC_SIZEOF_UNSIGNED_LONG #define SC_SIZEOF_UNSIGNED_LONG @SC_SIZEOF_UNSIGNED_LONG@ #endif -#ifndef SC_SIZEOF_UNSIGNED_LONG_LONG -#define SC_SIZEOF_UNSIGNED_LONG_LONG @SC_SIZEOF_UNSIGNED_LONG_LONG@ +#ifndef SC_SIZEOF_LONG_LONG +#define SC_SIZEOF_LONG_LONG @SC_SIZEOF_LONG_LONG@ #endif /* The size of `void *', as computed by sizeof. */ diff --git a/cmake/zlib.cmake b/cmake/zlib.cmake index 15d7dfd5..c5bd7563 100644 --- a/cmake/zlib.cmake +++ b/cmake/zlib.cmake @@ -6,7 +6,7 @@ set(SC_HAVE_ZLIB 1 CACHE BOOL "using SC-built Zlib") # default zlib source archive if (NOT DEFINED SC_BUILD_ZLIB_ARCHIVE_FILE) if (NOT DEFINED SC_BUILD_ZLIB_VERSION) - set(SC_BUILD_ZLIB_VERSION 2.1.5) + set(SC_BUILD_ZLIB_VERSION 2.1.6) endif() set(SC_BUILD_ZLIB_ARCHIVE_FILE https://github.com/zlib-ng/zlib-ng/archive/refs/tags/${SC_BUILD_ZLIB_VERSION}.tar.gz CACHE STRING "zlib source archive (URL or local filepath).") endif() @@ -43,7 +43,6 @@ URL ${SC_BUILD_ZLIB_ARCHIVE_FILE} CMAKE_ARGS ${zlib_cmake_args} BUILD_BYPRODUCTS ${ZLIB_LIBRARIES} CONFIGURE_HANDLED_BY_BUILD ON -INACTIVITY_TIMEOUT 60 USES_TERMINAL_DOWNLOAD true USES_TERMINAL_UPDATE true USES_TERMINAL_CONFIGURE true diff --git a/config/sc_include.m4 b/config/sc_include.m4 index 90d468b7..02fb9737 100644 --- a/config/sc_include.m4 +++ b/config/sc_include.m4 @@ -1,4 +1,4 @@ - +dnl dnl sc_include.m4 - general custom macros dnl dnl This file is part of the SC Library. @@ -8,6 +8,18 @@ dnl Copyright (C) 2008,2009 Carsten Burstedde, Lucas Wilcox. dnl Documentation for macro names: brackets indicate optional arguments +dnl SC_SINGLE_LINE +dnl Print 72 characters '-' without any trailing newline. +AC_DEFUN([SC_SINGLE_LINE],[dnl +------------------------------------------------------------------------dnl +]) + +dnl SC_DOUBLE_LINE +dnl Print 72 characters '=' without any trailing newline. +AC_DEFUN([SC_DOUBLE_LINE],[dnl +========================================================================dnl +]) + dnl SC_VERSION(PREFIX) dnl Expose major, minor, and point version numbers as CPP defines. dnl Also creates a makefile variable PACKAGE_PREFIX with value PREFIX. @@ -406,9 +418,15 @@ dnl This macro prints messages at the end of the configure run. dnl AC_DEFUN([SC_FINAL_MESSAGES], [ +if test "x$HAVE_PKG_MPI" = xyes && test "x$HAVE_PKG_MPIIO" != xyes ; then +AC_MSG_NOTICE([SC_SINGLE_LINE +$1 has been configured --enable-mpi and --disable-mpiio. +This configuration is DEPRECATED and will be disallowed in the future. +If the MPI File API is not available, please configure to --disable-mpi.]) +fi if test "x$$1_HAVE_ZLIB" = x ; then -AC_MSG_NOTICE([- $1 ---------------------------------------------------- -We did not find a recent zlib containing the function adler32_combine. +AC_MSG_NOTICE([SC_SINGLE_LINE +$1 did not find a recent zlib containing the function adler32_combine. This is OK if the following does not matter to you: - Calling some functions that rely on zlib will abort your program. These include sc_array_checksum and sc_vtk_write_compressed. @@ -417,9 +435,10 @@ This is OK if the following does not matter to you: You can fix this by compiling a recent zlib and pointing LIBS to it.]) fi if test "x$$1_HAVE_JSON" = x ; then -AC_MSG_NOTICE([- $1 ---------------------------------------------------- -We did not find a JSON library containing json_integer and json_real. +AC_MSG_NOTICE([SC_SINGLE_LINE +$1 did not find a JSON library containing json_integer and json_real. This means that loading JSON files for option values will fail. You can fix this by installing the jansson development library.]) fi +AC_MSG_NOTICE([SC_SINGLE_LINE]) ]) diff --git a/config/sc_mpi.m4 b/config/sc_mpi.m4 index cd158b8a..ad1af6ed 100644 --- a/config/sc_mpi.m4 +++ b/config/sc_mpi.m4 @@ -12,6 +12,7 @@ dnl on the configure command line. dnl Likewise for F77, FC and CXX if enabled in SC_MPI_CONFIG. dnl --disable-mpiio Only effective if --enable-mpi is given. In this case, dnl do not use MPI I/O in sc and skip the compile-and-link test. +dnl DEPRECATED: we will disallow this combination in the future. dnl --disable-mpithread Only effective if --enable-mpi is given. In this case, dnl do not use MPI_Init_thread () and skip compile-and-link test. dnl --disable-mpishared Only effective if --enable-mpi is given. In this case, @@ -71,14 +72,14 @@ dnl The shell variable SC_ENABLE_MPIIO is set if --disable-mpiio is not given. dnl If not disabled, MPI I/O will be verified by a compile/link test below. AC_ARG_ENABLE([mpiio], [AS_HELP_STRING([--disable-mpiio], - [do not use MPI I/O (even if MPI is enabled)])],, + [do not use MPI I/O (this option is DEPRECATED)])],, [enableval=yes]) if test "x$enableval" = xyes ; then if test "x$HAVE_PKG_MPI" = xyes ; then HAVE_PKG_MPIIO=yes fi elif test "x$enableval" != xno ; then - AC_MSG_WARN([Ignoring --enable-mpiio with unsupported argument]) + AC_MSG_ERROR([use --disable-mpiio without an argument (option DEPRECATED)]) fi AC_MSG_CHECKING([whether we are using MPI I/O]) AC_MSG_RESULT([$HAVE_PKG_MPIIO]) @@ -279,6 +280,7 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM( int mpiret = MPI_Init ((int *) 0, (char ***) 0); if (mpiret == MPI_ERR_ARG || + mpiret == MPI_ERR_COUNT || mpiret == MPI_ERR_UNKNOWN || mpiret == MPI_ERR_OTHER || mpiret == MPI_ERR_NO_MEM) { @@ -513,7 +515,7 @@ dnl ]) ]) if test "x$HAVE_PKG_MPIIO" = xyes ; then SC_MPIIO_C_COMPILE_AND_LINK(, - [AC_MSG_ERROR([MPI I/O not found; you may try --disable-mpiio])]) + [AC_MSG_ERROR([MPI I/O not found; you may --disable-mpi altogether])]) fi if test "x$HAVE_PKG_MPITHREAD" = xyes ; then SC_MPITHREAD_C_COMPILE_AND_LINK(, diff --git a/config/sc_soversion.in b/config/sc_soversion.in index 50f3968c..507e077d 100644 --- a/config/sc_soversion.in +++ b/config/sc_soversion.in @@ -4,4 +4,4 @@ # This file is included by src/Makefile.am and parsed by cmake/config.cmake. -SC_SOVERSION = 2:0:0 +SC_SOVERSION = 3:0:0 diff --git a/configure.ac b/configure.ac index f543dba7..3135c2bd 100644 --- a/configure.ac +++ b/configure.ac @@ -66,9 +66,8 @@ AC_C_RESTRICT AC_CHECK_SIZEOF([int]) AC_CHECK_SIZEOF([unsigned int]) AC_CHECK_SIZEOF([long]) -AC_CHECK_SIZEOF([long long]) AC_CHECK_SIZEOF([unsigned long]) -AC_CHECK_SIZEOF([unsigned long long]) +AC_CHECK_SIZEOF([long long]) AC_CHECK_SIZEOF([void *]) AC_TYPE_SIZE_T AC_TYPE_SSIZE_T diff --git a/doc/author_dreyer.txt b/doc/author_dreyer.txt new file mode 100644 index 00000000..fd9aca27 --- /dev/null +++ b/doc/author_dreyer.txt @@ -0,0 +1 @@ +I place my contributions to libsc under the FreeBSD license. Lukas Dreyer diff --git a/doc/libsc-build-wdeps.sh b/doc/libsc-build-wdeps.sh index 798bb274..2015e62b 100755 --- a/doc/libsc-build-wdeps.sh +++ b/doc/libsc-build-wdeps.sh @@ -13,10 +13,6 @@ # set installation root to local subdirectory PREFIX="$PWD/local" -# download zlib -ZVER=1.3 -ZSHA=ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e - # download jansson JVER=2.14 JSHA=5798d010e41cf8d76b66236cfb2f2543c8d082181d16bc3085ab49538d4b9929 @@ -33,15 +29,15 @@ bdie () { } # download, build and install zlib -ZTAR="zlib-$ZVER.tar.gz" -wget -N "https://www.zlib.net/$ZTAR" && \ -test `sha256sum "$ZTAR" | cut -d ' ' -f1` = "$ZSHA" && \ +ZTAR="zlib.tar.gz" +wget -N "https://www.zlib.net/current/$ZTAR" && \ +ZDIR=`tar -tzf "$ZTAR" | head -1 | perl -p -e 's/.*(zlib-[\d\.]+).*/\1/'` && \ tar -xvzf "$ZTAR" && \ -cd "zlib-$ZVER" && \ +cd $ZDIR && \ ./configure --prefix="$PREFIX/zlib" && \ make -j install && \ cd .. && \ -rm -r "zlib-$ZVER" "$ZTAR" || bdie "zlib" +rm -r "$ZDIR" "$ZTAR" || bdie "zlib" # download, build and install jansson JTAR="jansson-$JVER.tar.gz" diff --git a/doc/mainpage.dox b/doc/mainpage.dox index 3a3f62c9..df9ff5a7 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -55,7 +55,9 @@ * compiler wrappers. If this option is not given, wrappers * for MPI routines are used instead and the code is compiled * in serial only. - * * `--disable-mpiio` may be used to avoid using `MPI_File` based calls. + * * `--disable-mpiio` may be used to avoid using `MPI_File` based calls. The + usage of `--disable-mpiio` is deprecated and should not be + used anymore. * * A typical development configure line looks as follows: * > `relative/path/to/configure CFLAGS="-Wall -O0 -g" --enable-mpi --enable-debug` @@ -88,7 +90,7 @@ * data in serial and the data can be stored element-wise compressed. * * If MPI I/O is available, it is used to write and read in - * parallel. Otherwise, the behaviour is emulated using serial I/O and MPI. + * parallel. The configuration case of MPI without MPI I/O is deprecated. * Without MPI this module still enables the user to write and read equivalent * files but only in serial. * diff --git a/doc/release_notes.txt b/doc/release_notes.txt index 749d8fe7..f127813e 100644 --- a/doc/release_notes.txt +++ b/doc/release_notes.txt @@ -1,6 +1,10 @@ # Release notes for the sc software library +## Link to latest version DOI + +[![DOI](https://zenodo.org/badge/3974693.svg)](https://zenodo.org/doi/10.5281/zenodo.10838739) + ## General - We have been reminded, for good reason, to maintain release notes. @@ -24,23 +28,30 @@ There have been breaking changes, very strictly speaking. ### Build system + - Bump library version according to libtool to 3:0:0. - No longer install getopt and puff original headers. - Make sc_getopt.h a purely internal header file. - Add a generic external library installation option. - Add script doc/p4est-build-wdeps.sh for autotools build. - Further align CMake with the autoconf build system. - Update CMake logic to configure zlib and jansson. - - Add CMake option to run tests with Valgrind + - Add CMake option to run tests with Valgrind. + - Warn if configuring MPI on and MPI I/O off. ### Functionality - - Further align I/O wrappers with and without MPI I/O. + - Further align I/O wrappers with and without MPI. + - Change the semantic of sc_io_open to always truncate a newly created file. - Simplify the replacements for MPI_{read,write}_at*. - Further updates to the MPI I/O wrapper code. + - Deprecate the configuration with MPI but without MPI I/O. + - Remove sc_io_{write,read}_all. - Add sc_io_source, sink_destroy_null functions. - Make sure not to sc_io_source_read an array beyond its length. - Add sc_io_file_load, save functions and test program. + - Update sc_hash_array to hide much of internal state. - Update zlib and base64 encode/compression routines. + - Remove the MPI_UNSIGNED_LONG_LONG datatype wrapper. ## 2.8.5 diff --git a/src/sc3_mpi_types.h b/src/sc3_mpi_types.h index 103d9f30..49051e39 100644 --- a/src/sc3_mpi_types.h +++ b/src/sc3_mpi_types.h @@ -89,6 +89,7 @@ typedef MPI_Op sc3_MPI_Op_t; #define SC3_MPI_MAX_ERROR_STRING MPI_MAX_ERROR_STRING #define SC3_MPI_SUCCESS MPI_SUCCESS #define SC3_MPI_ERR_ARG MPI_ERR_ARG +#define SC3_MPI_ERR_COUNT MPI_ERR_COUNT #define SC3_MPI_ERR_UNKNOWN MPI_ERR_UNKNOWN #define SC3_MPI_ERR_OTHER MPI_ERR_OTHER #define SC3_MPI_ERR_NO_MEM MPI_ERR_NO_MEM @@ -119,6 +120,9 @@ typedef MPI_Op sc3_MPI_Op_t; typedef enum sc3_MPI_IO_Errorcode { /* only MPI I/O error classes */ + /* WARNING: This enum is only used in the deprecated case of activated MPI but + * deactivated MPI I/O. + */ SC3_MPI_ERR_FILE = MPI_ERR_LASTCODE, SC3_MPI_ERR_NOT_SAME, SC3_MPI_ERR_AMODE, @@ -195,9 +199,9 @@ sc3_MPI_Op_t; /** We wrap some MPI error codes and the I/O error classes. */ typedef enum sc3_MPI_Errorcode { - /* we separate the error values from errno values */ SC3_MPI_SUCCESS = 0, /**< An MPI function has exited successfully. */ SC3_MPI_ERR_ARG = 14000, /**< An MPI function encountered invalid arguments. */ + SC3_MPI_ERR_COUNT, /**< An MPI function encountered an invalid count argument. */ SC3_MPI_ERR_UNKNOWN, /**< An MPI function has produced an unknown error. */ SC3_MPI_ERR_OTHER, /**< An MPI function has produced some known error. */ SC3_MPI_ERR_NO_MEM, /**< An MPI function ran out of memory. */ diff --git a/src/sc_containers.c b/src/sc_containers.c index 97f30947..954bae28 100644 --- a/src/sc_containers.c +++ b/src/sc_containers.c @@ -1523,7 +1523,7 @@ struct sc_hash_array_data sc_array_t *pa; sc_hash_function_t hash_fn; sc_equal_function_t equal_fn; - void *user_data; + sc_hash_foreach_t foreach_fn; void *current_item; }; @@ -1545,7 +1545,7 @@ sc_hash_array_hash_fn (const void *v, const void *u) p = (l == -1L) ? internal_data->current_item : sc_array_index_ssize_t (internal_data->pa, l); - return internal_data->hash_fn (p, internal_data->user_data); + return internal_data->hash_fn (p, internal_data->the_hash_array.user_data); } static int @@ -1562,7 +1562,8 @@ sc_hash_array_equal_fn (const void *v1, const void *v2, const void *u) p2 = (l2 == -1L) ? internal_data->current_item : sc_array_index_ssize_t (internal_data->pa, l2); - return internal_data->equal_fn (p1, p2, internal_data->user_data); + return internal_data->equal_fn + (p1, p2, internal_data->the_hash_array.user_data); } sc_hash_array_t * @@ -1573,8 +1574,9 @@ sc_hash_array_new (size_t elem_size, sc_hash_function_t hash_fn, sc_hash_array_data_t *had; /* save one allocation by storing the hash array inside its context */ - had = SC_ALLOC (sc_hash_array_data_t, 1); + had = SC_ALLOC_ZERO (sc_hash_array_data_t, 1); hash_array = &had->the_hash_array; + hash_array->user_data = user_data; hash_array->internal_data = had; /* initialize all members */ @@ -1582,8 +1584,6 @@ sc_hash_array_new (size_t elem_size, sc_hash_function_t hash_fn, had->pa = &hash_array->a; had->hash_fn = hash_fn; had->equal_fn = equal_fn; - had->user_data = user_data; - had->current_item = NULL; hash_array->h = sc_hash_new (sc_hash_array_hash_fn, sc_hash_array_equal_fn, had, NULL); @@ -1607,6 +1607,12 @@ sc_hash_array_is_valid (sc_hash_array_t * hash_array) size_t zz, position; void *v; + SC_ASSERT (hash_array != NULL); + + if (hash_array->a.elem_count != hash_array->h->elem_count) { + return 0; + } + for (zz = 0; zz < hash_array->a.elem_count; ++zz) { v = sc_array_index (&hash_array->a, zz); found = sc_hash_array_lookup (hash_array, v, &position); @@ -1631,6 +1637,12 @@ sc_hash_array_lookup (sc_hash_array_t * hash_array, void *v, size_t *position) int found; void **found_void; + /* verify general invariant */ + SC_ASSERT (hash_array != NULL); + SC_ASSERT (hash_array->a.elem_count == hash_array->h->elem_count); + SC_ASSERT (hash_array->internal_data->foreach_fn == NULL); + SC_ASSERT (hash_array->internal_data->current_item == NULL); + hash_array->internal_data->current_item = v; found = sc_hash_lookup (hash_array->h, (void *) (-1L), &found_void); hash_array->internal_data->current_item = NULL; @@ -1653,7 +1665,11 @@ sc_hash_array_insert_unique (sc_hash_array_t * hash_array, void *v, int added; void **found_void; + /* verify general invariant */ + SC_ASSERT (hash_array != NULL); SC_ASSERT (hash_array->a.elem_count == hash_array->h->elem_count); + SC_ASSERT (hash_array->internal_data->foreach_fn == NULL); + SC_ASSERT (hash_array->internal_data->current_item == NULL); hash_array->internal_data->current_item = v; added = sc_hash_insert_unique (hash_array->h, (void *) (-1L), &found_void); @@ -1674,6 +1690,37 @@ sc_hash_array_insert_unique (sc_hash_array_t * hash_array, void *v, } } +static int +sc_hash_array_foreach_fn (void **v, const void *u) +{ + const sc_hash_array_data_t *internal_data = + (const sc_hash_array_data_t *) u; + + SC_ASSERT (internal_data != NULL); + SC_ASSERT (internal_data->foreach_fn != NULL); + + return internal_data->foreach_fn + (v, internal_data->the_hash_array.user_data); +} + +void +sc_hash_array_foreach (sc_hash_array_t * hash_array, sc_hash_foreach_t fn) +{ + /* verify general invariant */ + SC_ASSERT (hash_array != NULL); + SC_ASSERT (hash_array->a.elem_count == hash_array->h->elem_count); + SC_ASSERT (hash_array->internal_data->foreach_fn == NULL); + SC_ASSERT (hash_array->internal_data->current_item == NULL); + + /* verify remaining input arguments */ + SC_ASSERT (fn != NULL); + + /* rely on internal hash table's foreach function */ + hash_array->internal_data->foreach_fn = fn; + sc_hash_foreach (hash_array->h, sc_hash_array_foreach_fn); + hash_array->internal_data->foreach_fn = NULL; +} + void sc_hash_array_rip (sc_hash_array_t * hash_array, sc_array_t * rip) { diff --git a/src/sc_containers.h b/src/sc_containers.h index 1998561f..4fbc375a 100644 --- a/src/sc_containers.h +++ b/src/sc_containers.h @@ -98,7 +98,7 @@ typedef unsigned int (*sc_hash_function_t) (const void *v, const void *u); typedef int (*sc_equal_function_t) (const void *v1, const void *v2, const void *u); -/** Function to call on every data item of a hash table. +/** Function to call on every data item of a hash table or hash array. * \param [in] v The address of the pointer to the current object. * \param [in] u Arbitrary user data. * \return Return true if the traversal should continue, false to stop. @@ -940,10 +940,10 @@ typedef struct sc_hash { /* interface variables */ size_t elem_count; /**< total number of objects contained */ + void *user_data; /**< User data passed to hash function. */ /* implementation variables */ sc_array_t *slots; /**< The slot count is slots->elem_count. */ - void *user_data; /**< User data passed to hash function. */ sc_hash_function_t hash_fn; /**< Function called to compute the hash value. */ sc_equal_function_t equal_fn; /**< Function called to check objects for equality. */ size_t resize_checks; /**< Running count of resize checks. */ @@ -1067,6 +1067,9 @@ typedef struct sc_hash_array_data sc_hash_array_data_t; */ typedef struct sc_hash_array { + /* interface variables */ + void *user_data; /**< Context passed by the user. */ + /* implementation variables */ sc_array_t a; /**< Array storing the elements. */ sc_hash_t *h; /**< Hash map pointing into element array. */ @@ -1134,6 +1137,13 @@ int sc_hash_array_lookup (sc_hash_array_t * hash_array, void *sc_hash_array_insert_unique (sc_hash_array_t * hash_array, void *v, size_t *position); +/** Invoke a callback for every member of the hash array. + * \param [in,out] hash_array Valid hash array. + * \param [in] fn Callback executed on every hash array element. + */ +void sc_hash_array_foreach (sc_hash_array_t * hash_array, + sc_hash_foreach_t fn); + /** Extract the array data from a hash array and destroy everything else. * \param [in] hash_array The hash array is destroyed after extraction. * \param [in] rip Array structure that will be overwritten. diff --git a/src/sc_io.c b/src/sc_io.c index 86c30549..02b2f499 100644 --- a/src/sc_io.c +++ b/src/sc_io.c @@ -1643,6 +1643,7 @@ sc_io_open (sc_MPI_Comm mpicomm, const char *filename, return errcode; #else + /* WARNING: This code with activated MPI (SC_ENABLE_MPI) is deprecated. */ /* allocate internal file context */ *mpifile = (sc_MPI_File) SC_ALLOC (struct sc_no_mpiio_file, 1); (*mpifile)->filename = filename; @@ -1705,16 +1706,6 @@ sc_io_read (sc_MPI_File mpifile, void *ptr, size_t zcount, #endif } -int -sc_io_read_at_legal (void) -{ -#ifdef SC_ENABLE_MPIIO - return 1; -#else - return 0; -#endif -} - int sc_io_read_at (sc_MPI_File mpifile, sc_MPI_Offset offset, void *ptr, int count, sc_MPI_Datatype t, int *ocount) @@ -1743,6 +1734,8 @@ sc_io_read_at (sc_MPI_File mpifile, sc_MPI_Offset offset, void *ptr, return errcode; #else + /* WARNING: This code with activated MPI (SC_ENABLE_MPI) is deprecated. */ + /* The value count > 0 is only legal on rank 0. * On all other ranks the code is only legal for count == 0. */ @@ -1826,6 +1819,9 @@ sc_io_read_at_all (sc_MPI_File mpifile, sc_MPI_Offset offset, void *ptr, return errcode; #elif defined SC_ENABLE_MPI /* MPI but no MPI IO */ + + /* WARNING: This code and configuration case is deprecated. */ + { int mpisize, rank, count, size; int active, errval; @@ -1968,13 +1964,6 @@ sc_io_read_at_all (sc_MPI_File mpifile, sc_MPI_Offset offset, void *ptr, #endif } -int -sc_io_read_all (sc_MPI_File mpifile, void *ptr, int count, sc_MPI_Datatype t, - int *ocount) -{ - return sc_io_read_at_all (mpifile, 0, ptr, count, t, ocount); -} - void sc_io_write (sc_MPI_File mpifile, const void *ptr, size_t zcount, sc_MPI_Datatype t, const char *errmsg) @@ -2001,12 +1990,6 @@ sc_io_write (sc_MPI_File mpifile, const void *ptr, size_t zcount, #endif } -int -sc_io_write_at_legal (void) -{ - return sc_io_read_at_legal (); -} - int sc_io_write_at (sc_MPI_File mpifile, sc_MPI_Offset offset, const void *ptr, int count, sc_MPI_Datatype t, @@ -2036,6 +2019,8 @@ sc_io_write_at (sc_MPI_File mpifile, sc_MPI_Offset offset, return errcode; #else + /* WARNING: This code with activated MPI (SC_ENABLE_MPI) is deprecated. */ + /* The value count > 0 is only legal on rank 0. * On all other ranks the code is only legal for count == 0. */ @@ -2119,6 +2104,9 @@ sc_io_write_at_all (sc_MPI_File mpifile, sc_MPI_Offset offset, return errcode; #elif defined SC_ENABLE_MPI /* MPI but no MPI IO */ + + /* WARNING: This code and configuration case is deprecated. */ + /* offset is ignored and we use here the append mode. * This is the case since the C-standard open mode * "wb" would earse the existing file and create a @@ -2265,13 +2253,6 @@ sc_io_write_at_all (sc_MPI_File mpifile, sc_MPI_Offset offset, #endif } -int -sc_io_write_all (sc_MPI_File mpifile, const void *ptr, int count, - sc_MPI_Datatype t, int *ocount) -{ - return sc_io_write_at_all (mpifile, 0, ptr, count, t, ocount); -} - int sc_io_close (sc_MPI_File * mpifile) { @@ -2285,6 +2266,9 @@ sc_io_close (sc_MPI_File * mpifile) mpiret = sc_io_error_class (mpiret, &eclass); SC_CHECK_MPI (mpiret); #else + + /* WARNING: This code with activated MPI (SC_ENABLE_MPI) is deprecated. */ + int retval; eclass = sc_MPI_SUCCESS; diff --git a/src/sc_io.h b/src/sc_io.h index 3c84389b..4e22232b 100644 --- a/src/sc_io.h +++ b/src/sc_io.h @@ -32,7 +32,7 @@ * provide functions centered around \ref sc_io_sink_new and \ref * sc_io_source_new. * - To abstract parallel file I/O in a way that works both with and - * without MPI I/O support, we provide \ref sc_io_open, \ref sc_io_write + * without MPI support, we provide \ref sc_io_open, \ref sc_io_write * and friends. * - To write to the VTK binary compressed format, we provide suitable * functions to base64 encode and zlib-compress as required; see @@ -44,22 +44,6 @@ * and base64-encoded format and back that is unambiguously defined and * human-friendly. * - * \note For the function \ref sc_io_write_at_all without MPI IO but with MPI - * the \b offset argument is ignored. In this case the function writes at - * the current end of the file. Hereby, the MPI ranks write in the - * rank-induced order. That is why the function may work equivalent to - * the MPI IO and non-MPI case but it can not be guaranteed. - * Furthermore, it is important to notice that \ref sc_io_write_at and - * \ref sc_io_read_at are only valid to call with zcount > 0 on rank 0, - * if MPI IO is not available. During runtime this can be checked by the - * user by calling \ref sc_io_read_at_legal and \ref sc_io_write_at_legal, - * respectively. - * The recommended way of reading/writing with multiple ranks with - * zcount > 0 if \ref sc_io_read_at_legal / \ref sc_io_write_at_legal - * returns 0 is to use \ref sc_io_read_at_all / \ref sc_io_write_at_all - * with the whished zcount on the ranks whished by the user and set zcount - * to 0 on the remaining ranks. - * * \ingroup io */ @@ -582,14 +566,15 @@ void sc_fread (void *ptr, size_t size, */ void sc_fflush_fsync_fclose (FILE * file); -/** Opens a MPI file or without MPI I/O or even without MPI a file context. +/** Opens a MPI file or without MPI a file context. + * * \param[in] mpicomm MPI communicator * \param[in] filename The path to the file that we want to open. * \param[in] amode An access mode. * \param[in] mpiinfo The MPI info * \param[out] mpifile The MPI file that is opened. This can be a * an actual MPI IO file or an internal file - * conntext to preserve some MPI IO functionalities + * conntext to preserve some IO functionalities * without MPI IO and to have working code without * MPI at all. This output variable is only filled if the * return value of the function is \ref sc_MPI_SUCCESS. @@ -623,23 +608,10 @@ void sc_io_read (sc_MPI_File mpifile, void *ptr, size_t zcount, sc_MPI_Datatype t, const char *errmsg); -/** Check for restricted usage of \ref sc_io_read_at. - * - * \return 0 if the restriction described in the note of \ref - * sc_io_read_at applies, i.e. count > 0 is only legal - * on rank 0. This is equivalent to MPI I/O being not - * available. - * Otherwise, the function returns 1, i.e. MPI I/O is - * available and the restriction in the note of \ref - * sc_io_read_at does not apply, i.e. the user can pass - * any valid count on any valid rank. - * - */ -int sc_io_read_at_legal (void); - /** Read MPI file content into memory for an explicit offset. * This function does not update the file pointer of the MPI file. * Contrary to \ref sc_io_read, it does not abort on read errors. + * * \param [in,out] mpifile MPI file object opened for reading. * \param [in] offset Starting offset in counts of the type \b t. * \param [in] ptr Data array to read from disk. @@ -649,11 +621,6 @@ int sc_io_read_at_legal (void); * \return A sc_MPI_ERR_* as defined in \ref sc_mpi.h. * The error code can be passed to * \ref sc_MPI_Error_string. - * \note If MPI I/O is not available this function has restricted - * functionality in the sense that for \b count > 0, this - * function is only legal to call on rank 0. On all other - * ranks \b count must be 0. If this requirement is - * violated this function returns \ref sc_MPI_ERR_ARG. */ int sc_io_read_at (sc_MPI_File mpifile, sc_MPI_Offset offset, void *ptr, @@ -662,6 +629,7 @@ int sc_io_read_at (sc_MPI_File mpifile, /** Read MPI file content collectively into memory for an explicit offset. * This function does not update the file pointer of the MPI file. + * * \param [in,out] mpifile MPI file object opened for reading. * \param [in] offset Starting offset in counts of the type \b t. * \param [in] ptr Data array to read from disk. @@ -677,24 +645,6 @@ int sc_io_read_at_all (sc_MPI_File mpifile, int count, sc_MPI_Datatype t, int *ocount); -/** Read memory content collectively from an MPI file. - * A call of this function is equivalent to call \ref sc_io_read_at_all - * with offset = 0 but the call of this function is not equivalent - * to a call of MPI_File_read_all since this function ignores the current - * position of the file cursor. - * \param [in,out] mpifile MPI file object opened for reading. - * \param [in] ptr Data array to read from disk. - * \param [in] count Number of array members. - * \param [in] t The MPI type for each array member. - * \param [out] ocount The number of read elements of type \b t. - * \return A sc_MPI_ERR_* as defined in \ref sc_mpi.h. - * The error code can be passed to - * \ref sc_MPI_Error_string. - */ -int sc_io_read_all (sc_MPI_File mpifile, void *ptr, - int count, sc_MPI_Datatype t, - int *ocount); - #define sc_mpi_write sc_io_write /**< For backwards compatibility. */ /** Write memory content to an MPI file. @@ -713,23 +663,10 @@ void sc_io_write (sc_MPI_File mpifile, const void *ptr, size_t zcount, sc_MPI_Datatype t, const char *errmsg); -/** Check for restricted usage of \ref sc_io_write_at. - * - * \return 0 if the restriction described in the note of \ref - * sc_io_write_at applies, i.e. count > 0 is only legal - * on rank 0. This is equivalent to MPI I/O being not - * available. - * Otherwise, the function returns 1, i.e. MPI I/O is - * available and the restriction in the note of \ref - * sc_io_write_at does not apply, i.e. the user can pass - * any valid count on any valid rank. - * - */ -int sc_io_write_at_legal (void); - /** Write MPI file content into memory for an explicit offset. * This function does not update the file pointer that is part of mpifile. - * Contrary to \ref sc_io_write, it does not abort on read errors. + * Contrary to \ref sc_io_write, it does not abort on read errors. + * * \param [in,out] mpifile MPI file object opened for reading. * \param [in] offset Starting offset in etype, where the etype is given by * the type t. @@ -740,11 +677,6 @@ int sc_io_write_at_legal (void); * \return A sc_MPI_ERR_* as defined in \ref sc_mpi.h. * The error code can be passed to * \ref sc_MPI_Error_string. - * \note If MPI I/O is not available this function has restricted - * functionality in the sense that for \b count > 0, this - * function is only legal to call on rank 0. On all other - * ranks \b count must be 0. If this requirement is - * violated this function returns \ref sc_MPI_ERR_ARG. */ int sc_io_write_at (sc_MPI_File mpifile, sc_MPI_Offset offset, @@ -754,15 +686,9 @@ int sc_io_write_at (sc_MPI_File mpifile, /** Write MPI file content collectively into memory for an explicit offset. * This function does not update the file pointer that is part of mpifile. * - * \note If there is no MPI IO but MPI available, the offset parameter is - * ignored and the ranks just write at the current end of the file - * according to their rank-induced order. * \param [in,out] mpifile MPI file object opened for reading. * \param [in] offset Starting offset in etype, where the etype is given by - * the type t. This parameter is ignored in the case of - * having MPI but no MPI IO. In this case this function - * writes to the current end of the file as described - * above. + * the type t. * \param [in] ptr Data array to write to disk. * \param [in] count Number of array members. * \param [in] t The MPI type for each array member. @@ -776,25 +702,8 @@ int sc_io_write_at_all (sc_MPI_File mpifile, const void *ptr, int count, sc_MPI_Datatype t, int *ocount); -/** Write memory content collectively to an MPI file. - * A call of this function is equivalent to call \ref sc_io_write_at_all - * with offset = 0 but the call of this function is not equivalent - * to a call of MPI_File_write_all since this function ignores the current - * position of the file cursor. - * \param [in,out] mpifile MPI file object opened for writing. - * \param [in] ptr Data array to write to disk. - * \param [in] count Number of array members. - * \param [in] t The MPI type for each array member. - * \param [out] ocount The number of written elements of type \b t. - * \return A sc_MPI_ERR_* as defined in \ref sc_mpi.h. - * The error code can be passed to - * \ref sc_MPI_Error_string. - */ -int sc_io_write_all (sc_MPI_File mpifile, - const void *ptr, int count, - sc_MPI_Datatype t, int *ocount); - /** Close collectively a sc_MPI_File. + * * \param[in] file MPI file object that is closed. * \return A sc_MPI_ERR_* as defined in \ref sc_mpi.h. * The error code can be passed to diff --git a/src/sc_mpi.c b/src/sc_mpi.c index ce11ae96..6dfa04fc 100644 --- a/src/sc_mpi.c +++ b/src/sc_mpi.c @@ -450,6 +450,73 @@ sc_MPI_Wtime (void) return (double) tv.tv_sec + 1.e-6 * tv.tv_usec; } +int +sc_MPI_Pack (const void *inbuf, int incount, sc_MPI_Datatype datatype, + void *outbuf, int outsize, int *position, sc_MPI_Comm comm) +{ + int mpiret; + int size; + + SC_ASSERT (incount >= 0); + SC_ASSERT (position != NULL); + + mpiret = sc_MPI_Pack_size (incount, datatype, comm, &size); + SC_CHECK_MPI (mpiret); + + /* Check that we have enough space to pack the datatypes */ + if (*position + size > outsize) { + return sc_MPI_ERR_NO_SPACE; + } + + /* Copy the contiguous memory */ + memcpy ((char *) outbuf + *position, inbuf, size); + *position += size; + + return sc_MPI_SUCCESS; +} + +int +sc_MPI_Unpack (const void *inbuf, int insize, int *position, + void *outbuf, int outcount, sc_MPI_Datatype datatype, + sc_MPI_Comm comm) +{ + int mpiret; + int size; + + SC_ASSERT (position != NULL); + SC_ASSERT (outcount >= 0); + + mpiret = sc_MPI_Pack_size (outcount, datatype, comm, &size); + SC_CHECK_MPI (mpiret); + + /* Check that the message is big enough for the datatypes that we want */ + if (*position + size > insize) { + return sc_MPI_ERR_NO_SPACE; + } + + /* Copy the contiguous memory */ + memcpy (outbuf, (char *) inbuf + *position, size); + *position += size; + + return sc_MPI_SUCCESS; +} + +int +sc_MPI_Pack_size (int incount, sc_MPI_Datatype datatype, sc_MPI_Comm comm, + int *size) +{ + int mpiret; + + SC_ASSERT (incount >= 0); + SC_ASSERT (size != NULL); + + mpiret = sc_MPI_Type_size (datatype, size); + SC_CHECK_MPI (mpiret); + *size *= incount; + + return sc_MPI_SUCCESS; +} + #else /* SC_ENABLE_MPI */ #ifndef SC_ENABLE_MPITHREAD @@ -680,10 +747,10 @@ sc_MPI_Error_string (int errorcode, char *string, int *resultlen) size_t sc_mpi_sizeof (sc_MPI_Datatype t) { - if (t == sc_MPI_CHAR) - return sizeof (char); if (t == sc_MPI_BYTE) return 1; + if (t == sc_MPI_CHAR || t == sc_MPI_UNSIGNED_CHAR) + return sizeof (char); if (t == sc_MPI_SHORT || t == sc_MPI_UNSIGNED_SHORT) return sizeof (short); if (t == sc_MPI_INT || t == sc_MPI_UNSIGNED) @@ -692,8 +759,6 @@ sc_mpi_sizeof (sc_MPI_Datatype t) return sizeof (long); if (t == sc_MPI_LONG_LONG_INT) return sizeof (long long); - if (t == sc_MPI_UNSIGNED_LONG_LONG) - return sizeof (unsigned long long); if (t == sc_MPI_FLOAT) return sizeof (float); if (t == sc_MPI_DOUBLE) @@ -702,6 +767,8 @@ sc_mpi_sizeof (sc_MPI_Datatype t) return sizeof (long double); if (t == sc_MPI_2INT) return 2 * sizeof (int); + if (t == sc_MPI_DOUBLE_INT) + return sizeof (double) + sizeof (int); SC_ABORT_NOT_REACHED (); } diff --git a/src/sc_mpi.h b/src/sc_mpi.h index b74fa0ea..70b74ef3 100644 --- a/src/sc_mpi.h +++ b/src/sc_mpi.h @@ -107,6 +107,7 @@ sc_tag_t; /* constants */ #define sc_MPI_SUCCESS MPI_SUCCESS #define sc_MPI_ERR_ARG MPI_ERR_ARG +#define sc_MPI_ERR_COUNT MPI_ERR_COUNT #define sc_MPI_ERR_UNKNOWN MPI_ERR_UNKNOWN #define sc_MPI_ERR_OTHER MPI_ERR_OTHER #define sc_MPI_ERR_NO_MEM MPI_ERR_NO_MEM @@ -138,6 +139,9 @@ sc_tag_t; typedef enum sc_MPI_IO_Errorcode { /* only MPI I/O error classes */ + /* WARNING: This enum is only used in the deprecated case of activated MPI but + * deactivated MPI I/O. + */ sc_MPI_ERR_FILE = MPI_ERR_LASTCODE, sc_MPI_ERR_NOT_SAME, sc_MPI_ERR_AMODE, @@ -153,7 +157,6 @@ typedef enum sc_MPI_IO_Errorcode sc_MPI_ERR_FILE_IN_USE, sc_MPI_ERR_DUP_DATAREP, sc_MPI_ERR_CONVERSION, - sc_MPI_ERR_COUNT, sc_MPI_ERR_IO, sc_MPI_ERR_LASTCODE } @@ -182,22 +185,22 @@ sc_MPI_IO_Errorcode_t; #define sc_MPI_REQUEST_NULL MPI_REQUEST_NULL #define sc_MPI_DATATYPE_NULL MPI_DATATYPE_NULL +#define sc_MPI_BYTE MPI_BYTE #define sc_MPI_CHAR MPI_CHAR -#define sc_MPI_SIGNED_CHAR MPI_SIGNED_CHAR #define sc_MPI_UNSIGNED_CHAR MPI_UNSIGNED_CHAR -#define sc_MPI_BYTE MPI_BYTE #define sc_MPI_SHORT MPI_SHORT #define sc_MPI_UNSIGNED_SHORT MPI_UNSIGNED_SHORT #define sc_MPI_INT MPI_INT -#define sc_MPI_2INT MPI_2INT #define sc_MPI_UNSIGNED MPI_UNSIGNED #define sc_MPI_LONG MPI_LONG #define sc_MPI_UNSIGNED_LONG MPI_UNSIGNED_LONG #define sc_MPI_LONG_LONG_INT MPI_LONG_LONG_INT -#define sc_MPI_UNSIGNED_LONG_LONG MPI_UNSIGNED_LONG_LONG #define sc_MPI_FLOAT MPI_FLOAT #define sc_MPI_DOUBLE MPI_DOUBLE #define sc_MPI_LONG_DOUBLE MPI_LONG_DOUBLE +#define sc_MPI_2INT MPI_2INT +#define sc_MPI_DOUBLE_INT MPI_DOUBLE_INT +#define sc_MPI_PACKED MPI_PACKED #define sc_MPI_OP_NULL MPI_OP_NULL #define sc_MPI_MAX MPI_MAX @@ -288,6 +291,9 @@ sc_MPI_IO_Errorcode_t; #define sc_MPI_Wait MPI_Wait /* The MPI_Waitsome, MPI_Waitall and MPI_Testall functions are wrapped. */ #define sc_MPI_Type_size MPI_Type_size +#define sc_MPI_Pack MPI_Pack +#define sc_MPI_Unpack MPI_Unpack +#define sc_MPI_Pack_size MPI_Pack_size #else /* !SC_ENABLE_MPI */ #include @@ -296,6 +302,7 @@ sc_MPI_IO_Errorcode_t; #define sc_MPI_SUCCESS SC3_MPI_SUCCESS /**< Emulate \c SC_MPI_SUCCESS. */ #define sc_MPI_ERR_ARG SC3_MPI_ERR_ARG /**< Emulate \c SC_MPI_ERR_ARG. */ +#define sc_MPI_ERR_COUNT SC3_MPI_ERR_COUNT /**< Emulate \c SC_MPI_ERR_COUNT. */ #define sc_MPI_ERR_UNKNOWN SC3_MPI_ERR_UNKNOWN /**< Emulate \c SC_MPI_ERR_UNKNOWN. */ #define sc_MPI_ERR_OTHER SC3_MPI_ERR_OTHER /**< Emulate \c SC_MPI_ERR_OTHER. */ #define sc_MPI_ERR_NO_MEM SC3_MPI_ERR_NO_MEM /**< Emulate \c SC_MPI_ERR_NO_MEM. */ @@ -347,7 +354,6 @@ sc_MPI_IO_Errorcode_t; #define sc_MPI_DATATYPE_NULL SC3_MPI_DATATYPE_NULL #define sc_MPI_CHAR ((sc_MPI_Datatype) 0x4c000101) -#define sc_MPI_SIGNED_CHAR ((sc_MPI_Datatype) 0x4c000118) #define sc_MPI_UNSIGNED_CHAR ((sc_MPI_Datatype) 0x4c000102) #define sc_MPI_BYTE SC3_MPI_BYTE #define sc_MPI_SHORT ((sc_MPI_Datatype) 0x4c000203) @@ -358,11 +364,11 @@ sc_MPI_IO_Errorcode_t; #define sc_MPI_LONG SC3_MPI_LONG #define sc_MPI_UNSIGNED_LONG ((sc_MPI_Datatype) 0x4c000408) #define sc_MPI_LONG_LONG_INT SC3_MPI_LONG_LONG -#define sc_MPI_UNSIGNED_LONG_LONG ((sc_MPI_Datatype) 0x4c000409) #define sc_MPI_FLOAT SC3_MPI_FLOAT #define sc_MPI_DOUBLE SC3_MPI_DOUBLE #define sc_MPI_DOUBLE_INT SC3_MPI_DOUBLE_INT #define sc_MPI_LONG_DOUBLE ((sc_MPI_Datatype) 0x4c000c0c) +#define sc_MPI_PACKED ((sc_MPI_Datatype) 0x4c001001) #define sc_MPI_OP_NULL SC3_MPI_OP_NULL #define sc_MPI_MINLOC SC3_MPI_MINLOC @@ -445,6 +451,47 @@ int sc_MPI_Comm_free (sc_MPI_Comm *freecomm); */ int sc_MPI_Type_size (sc_MPI_Datatype datatype, int *size); +/** Pack several instances of the same datatype into contiguous memory. + * \param [in] inbuf Buffer of elements of type \b datatype. + * \param [in] incount Number of elements in \b inbuf. + * \param [in] datatype Datatype of elements in \b inbuf. + * \param [out] outbuf Output buffer in which elements are packed. + * \param [in] outsize Size of output buffer in bytes. + * \param [in, out] position The current position in the output buffer. + * \param [in] comm Valid MPI communicator. + * \return MPI_SUCCESS on success. + */ +int sc_MPI_Pack (const void *inbuf, int incount, + sc_MPI_Datatype datatype, void *outbuf, + int outsize, int *position, + sc_MPI_Comm comm); + +/** Unpack contiguous memory into several instances of the same datatype. + * \param [in] inbuf Buffer of packed data. + * \param [in] insize Number of bytes in \b inbuf + * \param [in, out] position The current position in the input buffer. + * \param [out] outbuf Output buffer in which elements are unpacked. + * \param [in] outcount Number of elements to unpack. + * \param [in] datatype Datatype of elements to be unpacked. + * \param [in] comm Valid MPI communicator. + * \return MPI_SUCCESS on success. + */ +int sc_MPI_Unpack (const void *inbuf, int insize, + int *position, void *outbuf, int outcount, + sc_MPI_Datatype datatype, + sc_MPI_Comm comm); + +/** Determine space needed to pack several instances of the same datatype. + * \param [in] incount Number of elements to pack. + * \param [in] datatype Datatype of elements to pack. + * \param [in] comm Valid MPI communicator. + * \param [out] size Number of bytes needed to packed \b incount + * instances of \b datatype. + * \return MPI_SUCCESS on success. + */ +int sc_MPI_Pack_size (int incount, sc_MPI_Datatype datatype, + sc_MPI_Comm comm, int *size); + /** Query size of an MPI communicator. * \param [in] mpicomm Valid MPI communicator. * \param [out] mpisize Without MPI this is always 1. @@ -694,8 +741,8 @@ int sc_MPI_Error_class (int errorcode, int *errorclass); int sc_MPI_Error_string (int errorcode, char *string, int *resultlen); -/** Return the size of MPI data types. - * \param [in] t MPI data type. +/** Return the size of MPI datatypes. + * \param [in] t MPI datatype. * \return Returns the size in bytes. */ size_t sc_mpi_sizeof (sc_MPI_Datatype t); diff --git a/src/sc_scda.h b/src/sc_scda.h index 83404f1d..eaa63ae3 100644 --- a/src/sc_scda.h +++ b/src/sc_scda.h @@ -135,10 +135,10 @@ * - \ref sc_scda_ferror_class and * - \ref sc_scda_ferror_string. * - * If MPI I/O is available \b errcode may encode an MPI error code. In this case + * If MPI is available \b errcode may encode an MPI error code. In this case * the two error examination functions output the error class and error string * as it would be output by the corresponding MPI functions, respectively. - * Without MPI I/O or MPI it is still possible that \b errcode encodes an I/O + * Without MPI it is still possible that \b errcode encodes an I/O * operation related error code. This case does not differ concerning the error * code examiniation. * Moreover, \b errcode can encode an error code related to \b scda, i.e. @@ -214,7 +214,7 @@ typedef uint64_t sc_scda_ulong; typedef enum sc_scda_ret { SC_SCDA_FERR_SUCCESS = 0, /**< successful function call */ - SC_SCDA_FERR_FORMAT = sc_MPI_ERR_LASTCODE, /**< File not conforming to the + SC_SCDA_FERR_FORMAT = 15000, /**< File not conforming to the \b scda format. */ SC_SCDA_FERR_USAGE, /**< Incorrect workflow of an \b scda reading function. For example, the user might have identified a @@ -264,7 +264,7 @@ sc_scda_ret_t; */ typedef struct sc_scda_ferror { - int mpiret; /**< MPI function return value; without MPI I/O or MPI + int mpiret; /**< MPI function return value; without MPI this variable can get filled by other I/O operation error codes, which are still interpretable by the error \b scda examination functions */ @@ -287,8 +287,8 @@ sc_scda_fopen_options_t; /**< type for \ref sc_scda_fopen_options */ * It is collective and creates the file on a parallel file system. * \note * All parameters are collective. - * This function leaves the file open if MPI I/O is available. - * Independent of the availability of MPI I/O the user can write one or more + * This function leaves the file open if MPI is available. + * Independent of the availability of MPI the user can write one or more * file sections before closing the file (context) using \ref sc_scda_fclose. * * It is the user's responsibility to write any further @@ -303,8 +303,7 @@ sc_scda_fopen_options_t; /**< type for \ref sc_scda_fopen_options */ * introduced in this \b scda * [preprint](https://doi.org/10.48550/arXiv.2307.06789). * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent errors. + * This function returns NULL on I/O errors. * * \param [in] mpicomm The MPI communicator that is used to open the * parallel file. @@ -347,9 +346,7 @@ sc_scda_fcontext_t *sc_scda_fopen_write (sc_MPI_Comm mpicomm, * \note * All parameters except of \b data are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_write. @@ -393,9 +390,7 @@ sc_scda_fcontext_t *sc_scda_fwrite_inline (sc_scda_fcontext_t * fc, * \note * All parameters except of \b block_data are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_write. @@ -455,9 +450,7 @@ sc_scda_fcontext_t *sc_scda_fwrite_block (sc_scda_fcontext_t * fc, * \note * All parameters except of \b array_data are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_write. @@ -568,9 +561,7 @@ int sc_scda_proc_sizes (sc_array_t * elem_sizes, * \note * All parameters except of \b array_data and \b elem_sizes are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_write. @@ -672,8 +663,7 @@ sc_scda_fcontext_t *sc_scda_fwrite_varray (sc_scda_fcontext_t * fc, * introduced in this \b scda * [preprint](https://doi.org/10.48550/arXiv.2307.06789). * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent errors. + * This function returns NULL on I/O errors. * * \param [in] mpicomm The MPI communicator that is used to open the * parallel file. @@ -715,9 +705,7 @@ sc_scda_fcontext_t *sc_scda_fopen_read (sc_MPI_Comm mpicomm, * \note * All parameters are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_read. @@ -784,9 +772,7 @@ sc_scda_fcontext_t *sc_scda_fread_section_header (sc_scda_fcontext_t * fc, * \note * All parameters except of \b data are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_read. @@ -820,9 +806,7 @@ sc_scda_fcontext_t *sc_scda_fread_inline_data (sc_scda_fcontext_t * fc, * \note * All parameters except of \b data_block are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_read. @@ -862,9 +846,7 @@ sc_scda_fcontext_t *sc_scda_fread_block_data (sc_scda_fcontext_t * fc, * \note * All parameters except of \b array_data are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_read. @@ -926,9 +908,7 @@ sc_scda_fcontext_t *sc_scda_fread_array_data (sc_scda_fcontext_t * fc, * \note * All parameters except of \b elem_sizes are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_read. @@ -975,9 +955,7 @@ sc_scda_fcontext_t *sc_scda_fread_varray_sizes (sc_scda_fcontext_t * fc, * \note * All parameters except of \b array_data and \b elem_sizes are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously opened by \ref * sc_scda_fopen_read. @@ -1091,9 +1069,7 @@ int sc_scda_ferror_string (sc_scda_ferror_t errcode, char *str, * \note * All parameters are collective. * - * This function returns NULL on MPI I/O errors. - * Without MPI I/O the function may abort on file system dependent - * errors. + * This function returns NULL on I/O errors. * * \param [in,out] fc File context previously created by * \ref sc_scda_fopen_write or \ref diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8139d804..97cec0e7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,3 +1,5 @@ +include(CTest) + set(sc_tests allgather arrays keyvalue notify reduce search sortb version) if(SC_HAVE_RANDOM AND SC_HAVE_SRANDOM) diff --git a/test/Makefile.am b/test/Makefile.am index 47d6bd29..0ae79a29 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -18,6 +18,7 @@ sc_test_programs = \ test/sc_test_sortb \ test/sc_test_version \ test/sc_test_helpers \ + test/sc_test_mpi_pack \ test/sc_test_scda ## Reenable and properly verify pqueue when it is actually used @@ -41,6 +42,7 @@ test_sc_test_sort_SOURCES = test/test_sort.c test_sc_test_sortb_SOURCES = test/test_sortb.c test_sc_test_version_SOURCES = test/test_version.c test_sc_test_helpers_SOURCES = test/test_helpers.c +test_sc_test_mpi_pack_SOURCES = test/test_mpi_pack.c test_sc_test_scda_SOURCES = test/test_scda.c TESTS += $(sc_test_programs) diff --git a/test/test_mpi_pack.c b/test/test_mpi_pack.c new file mode 100644 index 00000000..a54b9410 --- /dev/null +++ b/test/test_mpi_pack.c @@ -0,0 +1,226 @@ +/* + This file is part of the SC Library. + The SC Library provides support for parallel scientific applications. + + Copyright (C) 2020 individual authors + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +/** Test the mpi pack and unpack function + * Construct multiple test messages, each with different size double arrays depending on their type + * Pack, Unpack and Compare +*/ + +#define TEST_NUM_TYPES 3 +const int num_values[TEST_NUM_TYPES] = { 2, 5, 6 }; + +typedef struct message +{ + int8_t type; + double *values; +} +test_message_t; + +static void +test_message_construct (test_message_t *message, int8_t type, + double startvalue) +{ + int ival; + + message->type = type; + message->values = SC_ALLOC (double, num_values[type]); + for (ival = 0; ival < num_values[type]; ival++) { + message->values[ival] = startvalue * ival; + } +} + +static void +test_message_destroy (test_message_t *message) +{ + SC_FREE (message->values); +} + +static int +test_message_equal (test_message_t *message1, test_message_t *message2) +{ + int ivalue; + + if (message1->type != message2->type) { + return 0; + } + for (ivalue = 0; ivalue < num_values[message1->type]; ivalue++) { + if (message1->values[ivalue] != message2->values[ivalue]) { + return 0; + } + } + return 1; +} + +/* Pack several instances of test messages into contiguous memory */ +static int +test_message_MPI_Pack (const test_message_t *messages, int incount, + void *outbuf, int outsize, int *position, + sc_MPI_Comm comm) +{ + int imessage; + + for (imessage = 0; imessage < incount; imessage++) { + int mpiret; + + mpiret = + sc_MPI_Pack (&(messages[imessage].type), sizeof (int8_t), sc_MPI_BYTE, + outbuf, outsize, position, comm); + SC_CHECK_MPI (mpiret); + mpiret = sc_MPI_Pack (messages[imessage].values, + num_values[messages[imessage].type], sc_MPI_DOUBLE, + outbuf, outsize, position, comm); + SC_CHECK_MPI (mpiret); + } + return 0; +} + +/* Unpack contiguous memory into several instances of the same datatype */ +static int +test_message_MPI_Unpack (const void *inbuf, int insize, + int *position, test_message_t *messages, + int outcount, sc_MPI_Comm comm) +{ + int imessage; + + for (imessage = 0; imessage < outcount; imessage++) { + int mpiret; + + mpiret = + sc_MPI_Unpack (inbuf, insize, position, &(messages[imessage].type), + sizeof (int8_t), sc_MPI_BYTE, comm); + SC_CHECK_MPI (mpiret); + + messages[imessage].values = + SC_ALLOC (double, num_values[messages[imessage].type]); + mpiret = + sc_MPI_Unpack (inbuf, insize, position, messages[imessage].values, + num_values[messages[imessage].type], sc_MPI_DOUBLE, + comm); + SC_CHECK_MPI (mpiret); + } + return 0; +} + +/* Determine how much space in bytes is needed to pack several test messages */ +static int +test_message_MPI_Pack_size (int incount, const test_message_t *messages, + sc_MPI_Comm comm, int *size) +{ + int imessage; + + *size = 0; + for (imessage = 0; imessage < incount; imessage++) { + int pack_size; + int single_message_size = 0; + int mpiret; + + mpiret = + sc_MPI_Pack_size (sizeof (int8_t), sc_MPI_BYTE, comm, &pack_size); + SC_CHECK_MPI (mpiret); + single_message_size += pack_size; + + mpiret = sc_MPI_Pack_size (1, sc_MPI_DOUBLE, comm, &pack_size); + SC_CHECK_MPI (mpiret); + single_message_size += num_values[messages[imessage].type] * pack_size; + + *size += single_message_size; + } + return 0; +} + +int +main (int argc, char **argv) +{ + int mpiret; + sc_MPI_Comm mpicomm; + + const int num_test_messages = 5; + int imessage; + test_message_t *messages; + test_message_t *unpacked_messages; + + char *pack_buffer; + int buffer_size = 0; + int position = 0; + + /* standard initialization */ + mpiret = sc_MPI_Init (&argc, &argv); + SC_CHECK_MPI (mpiret); + mpicomm = sc_MPI_COMM_WORLD; + sc_init (mpicomm, 1, 1, NULL, SC_LP_DEFAULT); + + /* allocate and construct test messages */ + messages = SC_ALLOC (test_message_t, num_test_messages); + for (imessage = 0; imessage < num_test_messages; imessage++) { + test_message_construct (messages + imessage, imessage % TEST_NUM_TYPES, + imessage + 1.0); + } + + /* get message size, pack, unpack and compare */ + test_message_MPI_Pack_size (num_test_messages, messages, mpicomm, + &buffer_size); + + pack_buffer = SC_ALLOC (char, buffer_size); + test_message_MPI_Pack (messages, num_test_messages, pack_buffer, + buffer_size, &position, mpicomm); + SC_CHECK_ABORT (position == buffer_size, "message not of full size"); + + unpacked_messages = SC_ALLOC (test_message_t, num_test_messages); + position = 0; + test_message_MPI_Unpack (pack_buffer, buffer_size, &position, + unpacked_messages, num_test_messages, mpicomm); + SC_CHECK_ABORT (position == buffer_size, "message not of full size"); + + for (imessage = 0; imessage < num_test_messages; imessage++) { + SC_CHECK_ABORT (test_message_equal + (messages + imessage, unpacked_messages + imessage), + "message do not equal"); + } + + /* free up memory */ + SC_FREE (pack_buffer); + + for (imessage = 0; imessage < num_test_messages; imessage++) { + test_message_destroy (messages + imessage); + test_message_destroy (unpacked_messages + imessage); + } + SC_FREE (messages); + SC_FREE (unpacked_messages); + + /* clean up and exit */ + sc_finalize (); + + mpiret = sc_MPI_Finalize (); + SC_CHECK_MPI (mpiret); + + return EXIT_SUCCESS; +}