diff options
author | vchoi <vchoi@jelly.ad.hdfgroup.org> | 2021-02-26 19:54:11 (GMT) |
---|---|---|
committer | vchoi <vchoi@jelly.ad.hdfgroup.org> | 2021-02-26 19:54:11 (GMT) |
commit | f6e7edcedf0339bd18ca0a995f9877a778180f5f (patch) | |
tree | 18db02191fdd45c89153386fa4ffb1f5aa3adc8a | |
parent | 511b940da6fa60e163f6f94e423f6cbd2879468d (diff) | |
download | hdf5-f6e7edcedf0339bd18ca0a995f9877a778180f5f.zip hdf5-f6e7edcedf0339bd18ca0a995f9877a778180f5f.tar.gz hdf5-f6e7edcedf0339bd18ca0a995f9877a778180f5f.tar.bz2 |
Add the two VFD SWMR demo programs to the test directory so that they can be built as needed.
-rw-r--r-- | MANIFEST | 1 | ||||
-rw-r--r-- | configure.ac | 10 | ||||
-rw-r--r-- | m4/ax_with_curses.m4 | 571 | ||||
-rw-r--r-- | test/Makefile.am | 16 | ||||
-rw-r--r-- | test/vfd_swmr_common.c | 19 | ||||
-rw-r--r-- | test/vfd_swmr_common.h | 9 | ||||
-rw-r--r-- | test/vfd_swmr_credel.c | 483 | ||||
-rw-r--r-- | test/vfd_swmr_gaussians.c | 753 |
8 files changed, 1861 insertions, 1 deletions
@@ -56,6 +56,7 @@ ./m4/ax_prog_javah.m4 ./m4/ax_try_compile_java.m4 ./m4/ax_try_run_java.m4 +./m4/ax_with_curses.m4 ./bin/COPYING ./bin/bbrelease _DO_NOT_DISTRIBUTE_ diff --git a/configure.ac b/configure.ac index 6d869f9..d7a29fd 100644 --- a/configure.ac +++ b/configure.ac @@ -1439,6 +1439,15 @@ case "X-$withval" in esac ## ---------------------------------------------------------------------- +## Check if the CURSES library is present. +## This is required to build one of the VFD SWMR demo program. +## +AX_WITH_CURSES +if test "x$ax_cv_curses" != xyes; then + AC_MSG_ERROR([A curses library is required to build the VFD SWMR demo: vfd_swmr_gaussians]) +fi + +## ---------------------------------------------------------------------- ## Make the external filters list available to *.in files ## At this point it's unset (no external filters by default) but it ## will be filled in during the deflate (zlib) and szip processing @@ -3734,6 +3743,7 @@ AM_CONDITIONAL([BUILD_TESTS_CONDITIONAL], [test "X$HDF5_TESTS" = "Xyes"]) AM_CONDITIONAL([BUILD_TESTS_PARALLEL_CONDITIONAL], [test -n "$TESTPARALLEL"]) AM_CONDITIONAL([BUILD_TOOLS_CONDITIONAL], [test "X$HDF5_TOOLS" = "Xyes"]) + ## ---------------------------------------------------------------------- ## Build the Makefiles. ## diff --git a/m4/ax_with_curses.m4 b/m4/ax_with_curses.m4 new file mode 100644 index 0000000..945a626 --- /dev/null +++ b/m4/ax_with_curses.m4 @@ -0,0 +1,571 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_with_curses.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_WITH_CURSES +# +# DESCRIPTION +# +# This macro checks whether a SysV or X/Open-compatible Curses library is +# present, along with the associated header file. The NcursesW +# (wide-character) library is searched for first, followed by Ncurses, +# then the system-default plain Curses. The first library found is the +# one returned. Finding libraries will first be attempted by using +# pkg-config, and should the pkg-config files not be available, will +# fallback to combinations of known flags itself. +# +# The following options are understood: --with-ncursesw, --with-ncurses, +# --without-ncursesw, --without-ncurses. The "--with" options force the +# macro to use that particular library, terminating with an error if not +# found. The "--without" options simply skip the check for that library. +# The effect on the search pattern is: +# +# (no options) - NcursesW, Ncurses, Curses +# --with-ncurses --with-ncursesw - NcursesW only [*] +# --without-ncurses --with-ncursesw - NcursesW only [*] +# --with-ncursesw - NcursesW only [*] +# --with-ncurses --without-ncursesw - Ncurses only [*] +# --with-ncurses - NcursesW, Ncurses [**] +# --without-ncurses --without-ncursesw - Curses only +# --without-ncursesw - Ncurses, Curses +# --without-ncurses - NcursesW, Curses +# +# [*] If the library is not found, abort the configure script. +# +# [**] If the second library (Ncurses) is not found, abort configure. +# +# The following preprocessor symbols may be defined by this macro if the +# appropriate conditions are met: +# +# HAVE_CURSES - if any SysV or X/Open Curses library found +# HAVE_CURSES_ENHANCED - if library supports X/Open Enhanced functions +# HAVE_CURSES_COLOR - if library supports color (enhanced functions) +# HAVE_CURSES_OBSOLETE - if library supports certain obsolete features +# HAVE_NCURSESW - if NcursesW (wide char) library is to be used +# HAVE_NCURSES - if the Ncurses library is to be used +# +# HAVE_CURSES_H - if <curses.h> is present and should be used +# HAVE_NCURSESW_H - if <ncursesw.h> should be used +# HAVE_NCURSES_H - if <ncurses.h> should be used +# HAVE_NCURSESW_CURSES_H - if <ncursesw/curses.h> should be used +# HAVE_NCURSES_CURSES_H - if <ncurses/curses.h> should be used +# +# (These preprocessor symbols are discussed later in this document.) +# +# The following output variables are defined by this macro; they are +# precious and may be overridden on the ./configure command line: +# +# CURSES_LIBS - library to add to xxx_LDADD +# CURSES_CFLAGS - include paths to add to xxx_CPPFLAGS +# +# In previous versions of this macro, the flags CURSES_LIB and +# CURSES_CPPFLAGS were defined. These have been renamed, in keeping with +# AX_WITH_CURSES's close bigger brother, PKG_CHECK_MODULES, which should +# eventually supersede the use of AX_WITH_CURSES. Neither the library +# listed in CURSES_LIBS, nor the flags in CURSES_CFLAGS are added to LIBS, +# respectively CPPFLAGS, by default. You need to add both to the +# appropriate xxx_LDADD/xxx_CPPFLAGS line in your Makefile.am. For +# example: +# +# prog_LDADD = @CURSES_LIBS@ +# prog_CPPFLAGS = @CURSES_CFLAGS@ +# +# If CURSES_LIBS is set on the configure command line (such as by running +# "./configure CURSES_LIBS=-lmycurses"), then the only header searched for +# is <curses.h>. If the user needs to specify an alternative path for a +# library (such as for a non-standard NcurseW), the user should use the +# LDFLAGS variable. +# +# The following shell variables may be defined by this macro: +# +# ax_cv_curses - set to "yes" if any Curses library found +# ax_cv_curses_enhanced - set to "yes" if Enhanced functions present +# ax_cv_curses_color - set to "yes" if color functions present +# ax_cv_curses_obsolete - set to "yes" if obsolete features present +# +# ax_cv_ncursesw - set to "yes" if NcursesW library found +# ax_cv_ncurses - set to "yes" if Ncurses library found +# ax_cv_plaincurses - set to "yes" if plain Curses library found +# ax_cv_curses_which - set to "ncursesw", "ncurses", "plaincurses" or "no" +# +# These variables can be used in your configure.ac to determine the level +# of support you need from the Curses library. For example, if you must +# have either Ncurses or NcursesW, you could include: +# +# AX_WITH_CURSES +# if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then +# AC_MSG_ERROR([requires either NcursesW or Ncurses library]) +# fi +# +# If any Curses library will do (but one must be present and must support +# color), you could use: +# +# AX_WITH_CURSES +# if test "x$ax_cv_curses" != xyes || test "x$ax_cv_curses_color" != xyes; then +# AC_MSG_ERROR([requires an X/Open-compatible Curses library with color]) +# fi +# +# Certain preprocessor symbols and shell variables defined by this macro +# can be used to determine various features of the Curses library. In +# particular, HAVE_CURSES and ax_cv_curses are defined if the Curses +# library found conforms to the traditional SysV and/or X/Open Base Curses +# definition. Any working Curses library conforms to this level. +# +# HAVE_CURSES_ENHANCED and ax_cv_curses_enhanced are defined if the +# library supports the X/Open Enhanced Curses definition. In particular, +# the wide-character types attr_t, cchar_t and wint_t, the functions +# wattr_set() and wget_wch() and the macros WA_NORMAL and _XOPEN_CURSES +# are checked. The Ncurses library does NOT conform to this definition, +# although NcursesW does. +# +# HAVE_CURSES_COLOR and ax_cv_curses_color are defined if the library +# supports color functions and macros such as COLOR_PAIR, A_COLOR, +# COLOR_WHITE, COLOR_RED and init_pair(). These are NOT part of the +# X/Open Base Curses definition, but are part of the Enhanced set of +# functions. The Ncurses library DOES support these functions, as does +# NcursesW. +# +# HAVE_CURSES_OBSOLETE and ax_cv_curses_obsolete are defined if the +# library supports certain features present in SysV and BSD Curses but not +# defined in the X/Open definition. In particular, the functions +# getattrs(), getcurx() and getmaxx() are checked. +# +# To use the HAVE_xxx_H preprocessor symbols, insert the following into +# your system.h (or equivalent) header file: +# +# #if defined HAVE_NCURSESW_CURSES_H +# # include <ncursesw/curses.h> +# #elif defined HAVE_NCURSESW_H +# # include <ncursesw.h> +# #elif defined HAVE_NCURSES_CURSES_H +# # include <ncurses/curses.h> +# #elif defined HAVE_NCURSES_H +# # include <ncurses.h> +# #elif defined HAVE_CURSES_H +# # include <curses.h> +# #else +# # error "SysV or X/Open-compatible Curses header file required" +# #endif +# +# For previous users of this macro: you should not need to change anything +# in your configure.ac or Makefile.am, as the previous (serial 10) +# semantics are still valid. However, you should update your system.h (or +# equivalent) header file to the fragment shown above. You are encouraged +# also to make use of the extended functionality provided by this version +# of AX_WITH_CURSES, as well as in the additional macros +# AX_WITH_CURSES_PANEL, AX_WITH_CURSES_MENU and AX_WITH_CURSES_FORM. +# +# LICENSE +# +# Copyright (c) 2009 Mark Pulford <mark@kyne.com.au> +# Copyright (c) 2009 Damian Pietras <daper@daper.net> +# Copyright (c) 2012 Reuben Thomas <rrt@sc3d.org> +# Copyright (c) 2011 John Zaitseff <J.Zaitseff@zap.org.au> +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see <https://www.gnu.org/licenses/>. +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 18 + +# internal function to factorize common code that is used by both ncurses +# and ncursesw +AC_DEFUN([_FIND_CURSES_FLAGS], [ + # THG CHANGES! + # + # In order to avoid a dependency on pkg-conf, that part of this macro + # has been removed. This code always uses the "fallback" option. + + AC_CACHE_CHECK([for $1], [ax_cv_$1], [ + AS_ECHO() + pkg_cv__ax_cv_$1_libs="-l$1" + pkg_cv__ax_cv_$1_cppflags="-D_GNU_SOURCE $CURSES_CFLAGS" + LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_libs" + CPPFLAGS="$ax_saved_CPPFLAGS $pkg_cv__ax_cv_$1_cppflags" + + AC_MSG_CHECKING([for initscr() with $pkg_cv__ax_cv_$1_libs]) + AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], + [ + AC_MSG_RESULT([yes]) + AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_libs]) + AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[ + ax_cv_$1=yes + ],[ + AC_MSG_RESULT([no]) + m4_if( + [$1],[ncursesw],[pkg_cv__ax_cv_$1_libs="$pkg_cv__ax_cv_$1_libs -ltinfow"], + [$1],[ncurses],[pkg_cv__ax_cv_$1_libs="$pkg_cv__ax_cv_$1_libs -ltinfo"] + ) + LIBS="$ax_saved_LIBS $pkg_cv__ax_cv_$1_libs" + + AC_MSG_CHECKING([for nodelay() with $pkg_cv__ax_cv_$1_libs]) + AC_LINK_IFELSE([AC_LANG_CALL([], [nodelay])],[ + ax_cv_$1=yes + ],[ + ax_cv_$1=no + ]) + ]) + ],[ + ax_cv_$1=no + ]) + ]) +]) + +AU_ALIAS([MP_WITH_CURSES], [AX_WITH_CURSES]) +AC_DEFUN([AX_WITH_CURSES], [ + AC_ARG_VAR([CURSES_LIBS], [linker library for Curses, e.g. -lcurses]) + AC_ARG_VAR([CURSES_CFLAGS], [preprocessor flags for Curses, e.g. -I/usr/include/ncursesw]) + AC_ARG_WITH([ncurses], [AS_HELP_STRING([--with-ncurses], + [force the use of Ncurses or NcursesW])], + [], [with_ncurses=check]) + AC_ARG_WITH([ncursesw], [AS_HELP_STRING([--without-ncursesw], + [do not use NcursesW (wide character support)])], + [], [with_ncursesw=check]) + + ax_saved_LIBS=$LIBS + ax_saved_CPPFLAGS=$CPPFLAGS + + AS_IF([test "x$with_ncurses" = xyes || test "x$with_ncursesw" = xyes], + [ax_with_plaincurses=no], [ax_with_plaincurses=check]) + + ax_cv_curses_which=no + + # Test for NcursesW + AS_IF([test "x$CURSES_LIBS" = x && test "x$with_ncursesw" != xno], [ + _FIND_CURSES_FLAGS([ncursesw]) + + AS_IF([test "x$ax_cv_ncursesw" = xno && test "x$with_ncursesw" = xyes], [ + AC_MSG_ERROR([--with-ncursesw specified but could not find NcursesW library]) + ]) + + AS_IF([test "x$ax_cv_ncursesw" = xyes], [ + ax_cv_curses=yes + ax_cv_curses_which=ncursesw + CURSES_LIBS="$pkg_cv__ax_cv_ncursesw_libs" + CURSES_CFLAGS="$pkg_cv__ax_cv_ncursesw_cppflags" + AC_DEFINE([HAVE_NCURSESW], [1], [Define to 1 if the NcursesW library is present]) + AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) + + AC_CACHE_CHECK([for working ncursesw/curses.h], [ax_cv_header_ncursesw_curses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include <ncursesw/curses.h> + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_header_ncursesw_curses_h=yes], + [ax_cv_header_ncursesw_curses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSESW_CURSES_H], [1], [Define to 1 if <ncursesw/curses.h> is present]) + ]) + + AC_CACHE_CHECK([for working ncursesw.h], [ax_cv_header_ncursesw_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include <ncursesw.h> + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_header_ncursesw_h=yes], + [ax_cv_header_ncursesw_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncursesw_h" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSESW_H], [1], [Define to 1 if <ncursesw.h> is present]) + ]) + + AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h_with_ncursesw], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include <ncurses.h> + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_header_ncurses_h_with_ncursesw=yes], + [ax_cv_header_ncurses_h_with_ncursesw=no]) + ]) + AS_IF([test "x$ax_cv_header_ncurses_h_with_ncursesw" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if <ncurses.h> is present]) + ]) + + AS_IF([test "x$ax_cv_header_ncursesw_curses_h" = xno && test "x$ax_cv_header_ncursesw_h" = xno && test "x$ax_cv_header_ncurses_h_with_ncursesw" = xno], [ + AC_MSG_WARN([could not find a working ncursesw/curses.h, ncursesw.h or ncurses.h]) + ]) + ]) + ]) + unset pkg_cv__ax_cv_ncursesw_libs + unset pkg_cv__ax_cv_ncursesw_cppflags + + # Test for Ncurses + AS_IF([test "x$CURSES_LIBS" = x && test "x$with_ncurses" != xno && test "x$ax_cv_curses_which" = xno], [ + _FIND_CURSES_FLAGS([ncurses]) + + AS_IF([test "x$ax_cv_ncurses" = xno && test "x$with_ncurses" = xyes], [ + AC_MSG_ERROR([--with-ncurses specified but could not find Ncurses library]) + ]) + + AS_IF([test "x$ax_cv_ncurses" = xyes], [ + ax_cv_curses=yes + ax_cv_curses_which=ncurses + CURSES_LIBS="$pkg_cv__ax_cv_ncurses_libs" + CURSES_CFLAGS="$pkg_cv__ax_cv_ncurses_cppflags" + AC_DEFINE([HAVE_NCURSES], [1], [Define to 1 if the Ncurses library is present]) + AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) + + AC_CACHE_CHECK([for working ncurses/curses.h], [ax_cv_header_ncurses_curses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include <ncurses/curses.h> + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + ]])], + [ax_cv_header_ncurses_curses_h=yes], + [ax_cv_header_ncurses_curses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xyes], [ + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSES_CURSES_H], [1], [Define to 1 if <ncurses/curses.h> is present]) + ]) + + AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include <ncurses.h> + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + ]])], + [ax_cv_header_ncurses_h=yes], + [ax_cv_header_ncurses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_ncurses_h" = xyes], [ + ax_cv_curses_color=yes + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + AC_DEFINE([HAVE_NCURSES_H], [1], [Define to 1 if <ncurses.h> is present]) + ]) + + AS_IF([test "x$ax_cv_header_ncurses_curses_h" = xno && test "x$ax_cv_header_ncurses_h" = xno], [ + AC_MSG_WARN([could not find a working ncurses/curses.h or ncurses.h]) + ]) + ]) + ]) + unset pkg_cv__ax_cv_ncurses_libs + unset pkg_cv__ax_cv_ncurses_cppflags + + # Test for plain Curses (or if CURSES_LIBS was set by user) + AS_IF([test "x$with_plaincurses" != xno && test "x$ax_cv_curses_which" = xno], [ + AS_IF([test "x$CURSES_LIBS" != x], [ + LIBS="$ax_saved_LIBS $CURSES_LIBS" + ], [ + LIBS="$ax_saved_LIBS -lcurses" + ]) + + AC_CACHE_CHECK([for Curses library], [ax_cv_plaincurses], [ + AC_LINK_IFELSE([AC_LANG_CALL([], [initscr])], + [ax_cv_plaincurses=yes], [ax_cv_plaincurses=no]) + ]) + + AS_IF([test "x$ax_cv_plaincurses" = xyes], [ + ax_cv_curses=yes + ax_cv_curses_which=plaincurses + AS_IF([test "x$CURSES_LIBS" = x], [ + CURSES_LIBS="-lcurses" + ]) + AC_DEFINE([HAVE_CURSES], [1], [Define to 1 if a SysV or X/Open compatible Curses library is present]) + + # Check for base conformance (and header file) + + AC_CACHE_CHECK([for working curses.h], [ax_cv_header_curses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include <curses.h> + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + initscr(); + ]])], + [ax_cv_header_curses_h=yes], + [ax_cv_header_curses_h=no]) + ]) + AS_IF([test "x$ax_cv_header_curses_h" = xyes], [ + AC_DEFINE([HAVE_CURSES_H], [1], [Define to 1 if <curses.h> is present]) + + # Check for X/Open Enhanced conformance + + AC_CACHE_CHECK([for X/Open Enhanced Curses conformance], [ax_cv_plaincurses_enhanced], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include <curses.h> + @%:@ifndef _XOPEN_CURSES + @%:@error "this Curses library is not enhanced" + "this Curses library is not enhanced" + @%:@endif + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + attr_t d = WA_NORMAL; + cchar_t e; + wint_t f; + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + wattr_set(stdscr, d, 0, NULL); + wget_wch(stdscr, &f); + ]])], + [ax_cv_plaincurses_enhanced=yes], + [ax_cv_plaincurses_enhanced=no]) + ]) + AS_IF([test "x$ax_cv_plaincurses_enhanced" = xyes], [ + ax_cv_curses_enhanced=yes + ax_cv_curses_color=yes + AC_DEFINE([HAVE_CURSES_ENHANCED], [1], [Define to 1 if library supports X/Open Enhanced functions]) + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + ]) + + # Check for color functions + + AC_CACHE_CHECK([for Curses color functions], [ax_cv_plaincurses_color], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@define _XOPEN_SOURCE_EXTENDED 1 + @%:@include <curses.h> + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + chtype c = COLOR_PAIR(1) & A_COLOR; + initscr(); + init_pair(1, COLOR_WHITE, COLOR_RED); + ]])], + [ax_cv_plaincurses_color=yes], + [ax_cv_plaincurses_color=no]) + ]) + AS_IF([test "x$ax_cv_plaincurses_color" = xyes], [ + ax_cv_curses_color=yes + AC_DEFINE([HAVE_CURSES_COLOR], [1], [Define to 1 if library supports color (enhanced functions)]) + ]) + + # Check for obsolete functions + + AC_CACHE_CHECK([for obsolete Curses functions], [ax_cv_plaincurses_obsolete], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include <curses.h> + ]], [[ + chtype a = A_BOLD; + int b = KEY_LEFT; + int g = getattrs(stdscr); + int h = getcurx(stdscr) + getmaxx(stdscr); + initscr(); + ]])], + [ax_cv_plaincurses_obsolete=yes], + [ax_cv_plaincurses_obsolete=no]) + ]) + AS_IF([test "x$ax_cv_plaincurses_obsolete" = xyes], [ + ax_cv_curses_obsolete=yes + AC_DEFINE([HAVE_CURSES_OBSOLETE], [1], [Define to 1 if library supports certain obsolete features]) + ]) + ]) + + AS_IF([test "x$ax_cv_header_curses_h" = xno], [ + AC_MSG_WARN([could not find a working curses.h]) + ]) + ]) + ]) + + AS_IF([test "x$ax_cv_curses" != xyes], [ax_cv_curses=no]) + AS_IF([test "x$ax_cv_curses_enhanced" != xyes], [ax_cv_curses_enhanced=no]) + AS_IF([test "x$ax_cv_curses_color" != xyes], [ax_cv_curses_color=no]) + AS_IF([test "x$ax_cv_curses_obsolete" != xyes], [ax_cv_curses_obsolete=no]) + + LIBS=$ax_saved_LIBS + CPPFLAGS=$ax_saved_CPPFLAGS + + unset ax_saved_LIBS + unset ax_saved_CPPFLAGS +])dnl diff --git a/test/Makefile.am b/test/Makefile.am index 2b1d7d5..0aec03c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -120,10 +120,24 @@ endif # --enable-build-all at configure time. # The gen_old_* files can only be compiled with older versions of the library # so do not appear in this list. +# Add the two VFD SWMR demo programs and build it as needed. BUILD_ALL_PROGS=gen_bad_ohdr gen_bogus gen_cross gen_deflate gen_filters gen_new_array \ gen_new_fill gen_new_group gen_new_mtime gen_new_super gen_noencoder \ gen_nullspace gen_udlinks space_overflow gen_filespace gen_specmetaread \ - gen_sizes_lheap gen_file_image gen_plist gen_bad_offset gen_bounds + gen_sizes_lheap gen_file_image gen_plist gen_bad_offset gen_bounds \ + vfd_swmr_gaussians vfd_swmr_credel + +# VFD SWMR demo: vfd_swmr_gaussians needs the curses library +vfd_swmr_gaussians_LDFLAGS=$(AM_LDFLAGS) @CURSES_LIBS@ +vfd_swmr_gaussians=$(AM_CFLAGS) @CURSES_LIBS@ + +# Writer for VFD SWMR demo +w_vfd_swmr_gaussians: vfd_swmr_gaussians + @test -e w_vfd_swmr_gaussians || ln -s vfd_swmr_gaussians w_vfd_swmr_gaussians + +# Reader for VFD SWMR demo +r_vfd_swmr_gaussians: vfd_swmr_gaussians + @test -e r_vfd_swmr_gaussians || ln -s vfd_swmr_gaussians r_vfd_swmr_gaussians if BUILD_ALL_CONDITIONAL noinst_PROGRAMS=$(BUILD_ALL_PROGS) diff --git a/test/vfd_swmr_common.c b/test/vfd_swmr_common.c index 043b7ed..9da0640 100644 --- a/test/vfd_swmr_common.c +++ b/test/vfd_swmr_common.c @@ -333,3 +333,22 @@ fetch_env_ulong(const char *varname, unsigned long limit, unsigned long *valp) *valp = ul; return 1; } + +/* + * For demo credel.c: need to write up description + */ +size_t +strlcpy(char *dst, const char *src, size_t size) +{ + char *d; + const char *s; + + for (d = dst, s = src; (s - src) < (int)size; d++, s++) { + *d = *s; + if (*s == '\0') + return (size_t) (s - src); + } + + dst[size - 1] = '\0'; + return size; +} diff --git a/test/vfd_swmr_common.h b/test/vfd_swmr_common.h index a4bf50e..86f5f45 100644 --- a/test/vfd_swmr_common.h +++ b/test/vfd_swmr_common.h @@ -19,6 +19,7 @@ /***********/ #include <stdarg.h> +#include <unistd.h> /* For demo */ #include "h5test.h" /**********/ @@ -35,6 +36,11 @@ /* The message sent by writer that the file open is done--releasing the file lock */ #define VFD_SWMR_WRITER_MESSAGE "VFD_SWMR_WRITER_MESSAGE" +/* For demo */ +#ifndef __arraycount +#define __arraycount(__a) (sizeof(__a) / sizeof((__a)[0])) +#endif + /************/ /* Typedefs */ /************/ @@ -88,6 +94,9 @@ H5TEST_DLL void esnprintf(char *, size_t, const char *, ...) H5TEST_DLL int fetch_env_ulong(const char *, unsigned long, unsigned long *); +/* For demo */ +H5TEST_DLL size_t strlcpy(char *, const char *, size_t); + #ifdef __cplusplus } #endif diff --git a/test/vfd_swmr_credel.c b/test/vfd_swmr_credel.c new file mode 100644 index 0000000..fe6f0d5 --- /dev/null +++ b/test/vfd_swmr_credel.c @@ -0,0 +1,483 @@ +/* + * Copyright by The HDF Group. + * Copyright by the Board of Trustees of the University of Illinois. + * 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. + */ + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <libgen.h> /* basename(3) */ +#include <signal.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> /* struct timespec, nanosleep */ +#include <unistd.h> /* getopt, PATH_MAX, ... */ + +#include "hdf5.h" +#include "vfd_swmr_common.h" + +#define DATAROWS 1 +#define DATACOLS 10 +static const hsize_t dims[2] = {DATAROWS, DATACOLS}; +static const hsize_t chunk_dims[2] = {1, 1}; +static volatile sig_atomic_t unbroken = 1; + +typedef struct { + uint64_t created, deleted; +} stats_pair_t; + +typedef struct { + stats_pair_t datasets, groups; + uint64_t iterations; +} stats_t; + +typedef struct { + hid_t dataset[4], dataspace, dapl, dcpl, file, group[2]; + char output_file[PATH_MAX]; + char progname[PATH_MAX]; + struct timespec update_interval; + int verbose; + bool oneshot; + bool print_stats; + bool use_vfd_swmr; + uint64_t iterations_limit; + stats_t stats; +} state_t; + +#define ALL_HID_INITIALIZER (state_t){ \ + .dataspace = H5I_INVALID_HID \ + , .file = H5I_INVALID_HID \ + , .verbose = 0 \ + , .oneshot = false \ + , .use_vfd_swmr = true \ + , .print_stats = false \ + , .iterations_limit = UINT64_MAX \ + , .output_file = "" \ + , .update_interval = (struct timespec){ \ + .tv_sec = 0 \ + , .tv_nsec = 1000000000UL / 10 /* 1/10 second */} \ + , .stats = {{0, 0}, {0, 0}}} + +static void state_init(state_t *, int, char **); + +static void +write_dataset(state_t *s, const int64_t didx) +{ + const int ndatasets = __arraycount(s->dataset); + hid_t ds; + int32_t data[DATAROWS][DATACOLS]; + herr_t status; + unsigned int i, j; + + for (i = 0; i < (int)__arraycount(data); i++) { + for (j = 0; j < (int)__arraycount(data[i]); j++) { + int64_t k = (didx + j + i ) % (int64_t)__arraycount(data[i]); + + data[i][j] = (0 <= k && k < 3) ? 1 : 0; + if (s->verbose > 1) + fprintf(stderr, " %" PRId32, data[i][j]); + } + if (s->verbose > 1) + fprintf(stderr, "\n"); + } + + ds = s->dataset[didx % ndatasets]; + status = H5Dwrite(ds, H5T_NATIVE_INT32, H5S_ALL, H5S_ALL, + H5P_DEFAULT, data); + + if (status < 0) + errx(EXIT_FAILURE, "H5Dwrite failed"); + + if (H5Dflush(ds) < 0) + errx(EXIT_FAILURE, "H5Dflush failed"); +} + +static void +usage(const char *progname) +{ + fprintf(stderr, "usage: %s [-u milliseconds]\n" + "\n" + "-o: oneshot mode, perform one iteration, wait for a signal, " + "then quit.\n" + "-s: print statistics at end\n" + "-S: disable VFD SWMR mode\n" + "-u ms: milliseconds interval between updates to %s.h5\n" + "-v: verbose mode, mention creation/deletion; -vv: print data\n" + "\n", + progname, progname); + exit(EXIT_FAILURE); +} + +static void +print_stats(stats_t *s) +{ + printf("%10" PRIu64 " groups created\n", s->groups.created); + printf("%10" PRIu64 " groups deleted\n", s->groups.deleted); + printf("%10" PRIu64 " datasets created\n", s->datasets.created); + printf("%10" PRIu64 " datasets deleted\n", s->datasets.deleted); + printf("%10" PRIu64 " iterations\n", s->iterations); +} + +static void +state_init(state_t *s, int argc, char **argv) +{ + int ch, i, rc; + char tfile[PATH_MAX]; + char *end; + unsigned long millis; + uintmax_t niters; + + *s = ALL_HID_INITIALIZER; + strlcpy(tfile, argv[0], sizeof(tfile)); + strlcpy(s->progname, basename(tfile), sizeof(s->progname)); + while ((ch = getopt(argc, argv, "n:ou:sSv")) != -1) { + switch (ch) { + case 'n': + niters = strtoumax(optarg, &end, 0); + if (niters == UINTMAX_MAX && errno == ERANGE) { + err(EXIT_FAILURE, "option -%c argument \"%s\"", + ch, optarg); + } else if (*end != '\0') { + errx(EXIT_FAILURE, + "garbage after -%c argument \"%s\"", ch, + optarg); + } + s->iterations_limit = niters; + break; + case 'o': + s->oneshot = true; + break; + case 's': + s->print_stats = true; + break; + case 'S': + s->use_vfd_swmr = false; + break; + case 'u': + errno = 0; + millis = strtoul(optarg, &end, 0); + if (millis == ULONG_MAX && errno == ERANGE) { + err(EXIT_FAILURE, "option -%c argument \"%s\"", + ch, optarg); + } else if (*end != '\0') { + errx(EXIT_FAILURE, + "garbage after -%c argument \"%s\"", ch, + optarg); + } + s->update_interval.tv_sec = (time_t)(millis / 1000UL); + s->update_interval.tv_nsec = (long)((millis * 1000000UL) % 1000000000UL); + warnx("%lu milliseconds between updates", millis); + break; + case 'v': + s->verbose++; + break; + case '?': + default: + usage(s->progname); + } + } + argc -= optind; + argv += optind; + + for (i = 0; i < (int)__arraycount(s->dataset); i++) + s->dataset[i] = H5I_INVALID_HID; + + for (i = 0; i < (int)__arraycount(s->group); i++) + s->group[i] = H5I_INVALID_HID; + + rc = snprintf(s->output_file, sizeof(s->output_file), "%s.h5", s->progname); + if (rc == -1 || rc >= (int)sizeof(s->output_file)) + errx(EXIT_FAILURE, "output filename was truncated"); +} + +static void +delete_group(state_t *s, const int64_t gidx) +{ + hid_t g; + const int ngroups = __arraycount(s->group); + char gname[32]; + + assert(0 <= gidx); + + snprintf(gname, sizeof(gname), "/group-%" PRId64, gidx); + g = s->group[gidx % ngroups]; + + if (H5Ldelete(s->file, gname, H5P_DEFAULT) < 0) { + errx(EXIT_FAILURE, "%s: H5Ldelete(, \"%s\", ) failed", + __func__, gname); + } + + if (H5Gclose(g) < 0) + errx(EXIT_FAILURE, "H5Gclose failed"); + + if (s->verbose > 0) + fprintf(stderr, "Deleted group %s\n", gname); + + s->group[gidx % ngroups] = H5I_INVALID_HID; + s->stats.groups.deleted++; +} + +static void +create_group(state_t *s, const int64_t gidx) +{ + const int ngroups = __arraycount(s->group); + hid_t g; + char gname[32]; + + assert(0 <= gidx); + assert(s->group[gidx % ngroups] == H5I_INVALID_HID); + + snprintf(gname, sizeof(gname), "/group-%" PRId64, gidx); + g = H5Gcreate(s->file, gname, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + if (g < 0) + errx(EXIT_FAILURE, "H5Gcreate failed"); + + if (s->verbose > 0) + fprintf(stderr, "Created group %s\n", gname); + s->group[gidx % ngroups] = g; + s->stats.groups.created++; +} + +static void +delete_dataset(state_t *s, const int64_t didx) +{ + const int ndatasets = __arraycount(s->dataset); + const hid_t ds = s->dataset[didx % ndatasets]; + char dname[32]; + const int64_t gidx = didx / 2; + + assert(0 <= gidx && 0 <= didx); + + snprintf(dname, sizeof(dname), "/group-%" PRId64 "/dataset-%" PRId64, + gidx, didx); + if (H5Ldelete(s->file, dname, H5P_DEFAULT) < 0) { + errx(EXIT_FAILURE, "%s: H5Ldelete(, \"%s\", ) failed", + __func__, dname); + } + + if (s->verbose > 0) + fprintf(stderr, "Deleted dataset %s\n", dname); +#if 1 + if (H5Dclose(ds) < 0) + errx(EXIT_FAILURE, "H5Dclose failed"); +#endif + s->dataset[didx % ndatasets] = H5I_INVALID_HID; + s->stats.datasets.deleted++; +} + +static void +create_dataset(state_t *s, const int64_t didx) +{ + const int ndatasets = __arraycount(s->dataset); + char dname[32]; + const int64_t gidx = didx / 2; + hid_t ds; + + assert(0 <= gidx && 0 <= didx); + assert(s->dataset[didx % ndatasets] == H5I_INVALID_HID); + + s->dataspace = H5Screate_simple(__arraycount(dims), dims, NULL); + + if (s->dataspace < 0) + errx(EXIT_FAILURE, "H5Screate_simple failed"); + + snprintf(dname, sizeof(dname), "/group-%" PRId64 "/dataset-%" PRId64, + gidx, didx); + ds = H5Dcreate2(s->file, dname, + H5T_STD_I32BE, s->dataspace, + H5P_DEFAULT, s->dcpl, s->dapl); + + if (H5Sclose(s->dataspace) < 0) + errx(EXIT_FAILURE, "H5Sclose failed"); + + s->dataspace = H5I_INVALID_HID; + + if (ds < 0) + errx(EXIT_FAILURE, "H5Dcreate(, \"%s\", ) failed", dname); + + if (s->verbose > 0) + fprintf(stderr, "Created dataset %s\n", dname); + s->dataset[didx % ndatasets] = ds; + s->stats.datasets.created++; +} + +static void +create_and_write_dataset(state_t *s, const int64_t didx) +{ +#if 0 + const int64_t gidx = didx / 2; + const int ngroups = __arraycount(s->group); + const hid_t g = s->group[gidx % ngroups]; + + if (H5Odisable_mdc_flushes(g) < 0) + err(EXIT_FAILURE, "H5Odisable_mdc_flushes failed"); +#endif + + create_dataset(s, didx); + write_dataset(s, didx); + +#if 0 + if (H5Oenable_mdc_flushes(g) < 0) + err(EXIT_FAILURE, "H5Oenable_mdc_flushes failed"); +#endif +} + +static void +handle_signal(int H5_ATTR_UNUSED signo) +{ + char msg[] = "Handling signal\n"; + write(STDERR_FILENO, msg, sizeof(msg) - 1); + unbroken = 0; +} + +static void +disestablish_handler(const struct sigaction *osa) +{ + if (sigaction(SIGINT, osa, NULL) == -1) + err(EXIT_FAILURE, "%s: sigaction", __func__); +} + +static void +establish_handler(struct sigaction *osa) +{ + struct sigaction sa; + + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = handle_signal; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGINT, &sa, osa) == -1) + err(EXIT_FAILURE, "%s: sigaction", __func__); +} + +int +main(int argc, char **argv) +{ + hid_t fapl, fcpl; + struct sigaction osa; + state_t storage; + state_t *s = &storage; + int64_t i; + H5F_vfd_swmr_config_t config; + + memset(&config, '\0', sizeof(config)); + + state_init(s, argc, argv); + + /* config, tick_len, max_lag, writer, flush_raw_data, md_pages_reserved, md_file_path */ + init_vfd_swmr_config(&config, 4, 5, true, FALSE, 128, "./my_md_file"); + + /* use_latest_format, use_vfd_swmr, only_meta_page, config */ + fapl = vfd_swmr_create_fapl(false, s->use_vfd_swmr, true, &config); + if (fapl < 0) { + errx(EXIT_FAILURE, "%s.%d vfd_swmr_create_fapl failed", + __func__, __LINE__); + } + + fcpl = H5Pcreate(H5P_FILE_CREATE); + if (fcpl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + /* Set file space strategy to paged aggregation in fcpl. + * Page buffering *requires* this strategy. + * + * I set the free-space threshold to 1GB so that deleted + * datasets are not recycled. + */ + if (H5Pset_file_space_strategy(fcpl, + H5F_FSPACE_STRATEGY_PAGE, false, 1024 * 1024 * 1024) < 0) + errx(EXIT_FAILURE, "H5Pset_file_space_strategy failed"); + + s->file = H5Fcreate(s->output_file, H5F_ACC_TRUNC, fcpl, fapl); + + H5Pclose(fapl); + + if (s->file < 0) + errx(EXIT_FAILURE, "H5Fcreate failed"); + + if ((s->dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed", + __func__, __LINE__); + } + if ((s->dapl = H5Pcreate(H5P_DATASET_ACCESS)) < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed", + __func__, __LINE__); + } + if (H5Pset_chunk(s->dcpl, 2, chunk_dims) < 0) + errx(EXIT_FAILURE, "H5Pset_chunk failed"); + if (H5Pset_chunk_cache(s->dapl, H5D_CHUNK_CACHE_NSLOTS_DEFAULT, 0, + H5D_CHUNK_CACHE_W0_DEFAULT) < 0) + errx(EXIT_FAILURE, "H5Pset_chunk_cache failed"); + + establish_handler(&osa); + + for (i = 0; i < 4; i++) { + s->stats.iterations++; + if (i % 2 == 0) + create_group(s, i / 2); + create_and_write_dataset(s, i); + } + + for (i = 5; unbroken; i += 2) { + delete_dataset(s, i - 5); + delete_dataset(s, i - 4); + delete_group(s, (i - 4) / 2); + create_group(s, i / 2); + create_and_write_dataset(s, i - 1); + create_and_write_dataset(s, i); + if (s->oneshot || ++s->stats.iterations >= s->iterations_limit) + break; + nanosleep(&s->update_interval, NULL); + } + + if (s->oneshot) { + sigset_t mask; + sigemptyset(&mask); + H5Fvfd_swmr_end_tick(s->file); + (void)sigsuspend(&mask); + } +#if 0 + fprintf(stderr, "Interrupted. Cleaning up.\n"); + + int j; + for (--i, j = 0; j < 4; j++, --i) { + if (i % 2 == 1) { + delete_dataset(s, i - 1); + delete_dataset(s, i); + delete_group(s, i / 2); + } + } + + for (j = 0; j < 4; j++) { + assert(s->dataset[j] == H5I_INVALID_HID); + assert(s->group[j / 2] == H5I_INVALID_HID); + } +#endif + + if (s->print_stats) + print_stats(&s->stats); + + if (H5Fclose(s->file) < 0) + errx(EXIT_FAILURE, "H5Fclose failed"); + + if (H5Pclose(s->dapl) < 0) + errx(EXIT_FAILURE, "H5Pclose failed"); + + if (H5Pclose(s->dcpl) < 0) + errx(EXIT_FAILURE, "H5Pclose failed"); + + disestablish_handler(&osa); + + return EXIT_SUCCESS; +} diff --git a/test/vfd_swmr_gaussians.c b/test/vfd_swmr_gaussians.c new file mode 100644 index 0000000..17231a9 --- /dev/null +++ b/test/vfd_swmr_gaussians.c @@ -0,0 +1,753 @@ +#include <assert.h> +#include <curses.h> +#include <err.h> +#include <errno.h> +#include <libgen.h> /* basename(3) */ +#include <math.h> /* expf(3) */ +#include <locale.h> /* setlocale(3) */ +#include <signal.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> /* struct timespec, nanosleep(2), time(3), + * clock_gettime(2) + */ +#include <unistd.h> /* getopt, PATH_MAX, ... */ + +#include <sys/param.h> /* for MIN(a, b) */ + +#include "hdf5.h" +#include "vfd_swmr_common.h" +#include "H5time_private.h" + +#define SWMR_TICK_LEN 4 /* in 100 ms */ + +typedef enum { + STANDALONE = 0 + , READ = 1 + , WRITE = 2 +} personality_t; + +typedef enum { + TOP = 0 + , BOTTOM + , LEFT + , RIGHT + , NSIDES +} side_t; + +typedef struct { + bool side[NSIDES]; +} inside_t; + +typedef struct { + float x, y; +} vec_t; + +#define RANK 3 +#define ROWS 20 +#define COLS 40 +static const hsize_t original_dims[RANK] = {0, ROWS, COLS}; +static const hsize_t max_dims[RANK] = {H5S_UNLIMITED, ROWS, COLS}; +static const hsize_t frame_dims[RANK] = {1, ROWS, COLS}; +static const hsize_t *chunk_dims = frame_dims; +static volatile sig_atomic_t unbroken = 1; + +typedef struct { + /* main-loop statistics */ + uint64_t max_elapsed_ns, min_elapsed_ns, total_elapsed_ns; + uint64_t total_loops; + hid_t dataset, memspace, dcpl, file, group; + char output_file[PATH_MAX]; + char progname[PATH_MAX]; + struct timespec update_interval; + bool fuzz; + bool constantrate; + unsigned int partstep; +} state_t; + +#define ALL_HID_INITIALIZER (state_t){ \ + .total_elapsed_ns = 0 \ + , .total_loops = 0 \ + , .min_elapsed_ns = UINT64_MAX \ + , .max_elapsed_ns = 0 \ + , .memspace = H5I_INVALID_HID \ + , .file = H5I_INVALID_HID \ + , .constantrate = false \ + , .partstep = 0 \ + , .output_file = "" \ + , .update_interval = (struct timespec){ \ + .tv_sec = 0 \ + , .tv_nsec = 1000000000UL / 30 /* 1/30 second */}} + +static void state_init(state_t *, int, char **); + +static void +usage(const char *progname) +{ + fprintf(stderr, "usage: %s [-u milliseconds]\n" + "\n" + "-c: increase the frame number continously (reader mode)\n" + "-f: add \"fuzz\" (linear noise) to the data (writer mode)\n" + "-u ms: milliseconds interval between updates to %s.h5\n" + "\n", + progname, progname); + exit(EXIT_FAILURE); +} + +static void +state_init(state_t *s, int argc, char **argv) +{ + int ch; + char tfile[PATH_MAX]; + char *end; + unsigned long millis; + int nprinted; + + *s = ALL_HID_INITIALIZER; + strlcpy(tfile, argv[0], sizeof(tfile)); + strlcpy(s->progname, basename(tfile), sizeof(s->progname)); + + while ((ch = getopt(argc, argv, "cfu:")) != -1) { + switch (ch) { + case 'c': + s->constantrate = true; + break; + case 'f': + s->fuzz = true; + break; + case 'u': + errno = 0; + millis = strtoul(optarg, &end, 0); + if (millis == ULONG_MAX && errno == ERANGE) { + err(EXIT_FAILURE, + "option -p argument \"%s\"", optarg); + } else if (*end != '\0') { + errx(EXIT_FAILURE, + "garbage after -p argument \"%s\"", optarg); + } + s->update_interval.tv_sec = (time_t)(millis / 1000UL); + s->update_interval.tv_nsec = + (long)((millis * 1000000UL) % 1000000000UL); + + warnx("%lu milliseconds between updates", millis); + break; + case '?': + default: + usage(s->progname); + } + } + argc -= optind; + argv += optind; + + s->dataset = H5I_INVALID_HID; + s->group = H5I_INVALID_HID; + + s->memspace = H5Screate_simple(RANK, frame_dims, NULL); + + if (s->memspace < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed", + __func__, __LINE__); + } + + nprinted = snprintf(s->output_file, sizeof(s->output_file), + "%s.h5", s->progname); + if (nprinted < 0 || nprinted >= (int)sizeof(s->output_file)) { + errx(EXIT_FAILURE, "%s.%d: output filename truncated.", + __func__, __LINE__); + } +} + +static void +matrix_read(state_t *s, int *framenop, float mat[ROWS][COLS]) +{ + hid_t ds, filespace; + herr_t status; + hsize_t dims[RANK]; + int lead; + hssize_t temp; + + const uint64_t update_ms = timespec2ns(&s->update_interval) / 1000000; + const uint32_t tick_ms = SWMR_TICK_LEN * 100; + const uint64_t updates_per_tick = (tick_ms + update_ms - 1) / update_ms; + const uint64_t tmp = 2 * updates_per_tick; + const hssize_t hang_back = (hssize_t)tmp; + const int frameno = *framenop; + hsize_t offset[RANK] = {(hsize_t)frameno, 0, 0}; + + ds = s->dataset; + + if (H5Drefresh(ds) < 0) + errx(EXIT_FAILURE, "H5Drefresh failed"); + + filespace = H5Dget_space(ds); + + if (H5Sget_simple_extent_dims(filespace, dims, NULL) < 0) + errx(EXIT_FAILURE, "H5Sget_simple_extent_dims failed"); + + if (dims[1] != original_dims[1] || dims[2] != original_dims[2]) { + errx(EXIT_FAILURE, "Unexpected dimensions N x %ju x %ju", + (uintmax_t)dims[1], (uintmax_t)dims[2]); + } + + + temp = frameno + hang_back - (hssize_t)dims[0]; + lead = (int)temp; + + if (s->constantrate) { + *framenop = frameno + 1; + } else if (lead > hang_back * 2) { + if (++s->partstep % 3 == 0) + *framenop = frameno + 1; + } else if (lead > 0) { + if (++s->partstep % 2 == 0) + *framenop = frameno + 1; + } else if (lead == 0) { + *framenop = frameno + 1; + } else if (lead < -hang_back * 2) { + /* We're way behind, so jump close to the front. */ + temp = (hssize_t)dims[0] - hang_back; + *framenop = (int)temp; + } else /* lead < 0 */ { + *framenop = frameno + 1; + if (++s->partstep % 2 == 0) + *framenop = frameno + 2; + } + +#if 0 + if (!s->constantrate && (lead < -2 || 2 < lead)) { + int gain = 31250 / 4; + const struct timespec prior_integral = s->update_integral; + struct timespec current_interval; + if (lead > 0) + gain *= 2; + struct timespec adjustment = (struct timespec){.tv_sec = 0, + .tv_nsec = gain * MAX(MIN(4, lead), -4)}; + /* XXX clamp it XXX */ + timespecadd(&s->update_integral, + &adjustment, &s->update_integral); + timespecadd(&s->update_integral, + &s->update_interval, ¤t_interval); + if (timespeccmp(¤t_interval, &s->min_interval, <=)) + s->update_integral = prior_integral; + } +#endif + + if (frameno >= (int)dims[0]) { + int i, j; + for (i = 0; i < ROWS; i++) { + for (j = 0; j < COLS; j++) + mat[i][j] = ((i + j) % 2 == 0) ? 0. : 1.; + } + return; + } + + if (H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL, + frame_dims, NULL) < 0) + errx(EXIT_FAILURE, "H5Sselect_hyperslab failed"); + + status = H5Dread(ds, H5T_NATIVE_FLOAT, s->memspace, filespace, + H5P_DEFAULT, mat); + + if (status < 0) + errx(EXIT_FAILURE, "H5Dread failed"); + + if (H5Sclose(filespace) < 0) + errx(EXIT_FAILURE, "H5Sclose failed"); +} + +static void +matrix_write(state_t *s, int frameno, float mat[ROWS][COLS]) +{ + hid_t ds, filespace; + herr_t status; + hsize_t size[RANK] = {(hsize_t)frameno + 1, ROWS, COLS}; + hsize_t offset[RANK] = {(hsize_t)frameno, 0, 0}; + + ds = s->dataset; + + if (H5Dset_extent(ds, size) < 0) + errx(EXIT_FAILURE, "H5Dset_extent failed"); + + filespace = H5Dget_space(ds); + + if (H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL, + frame_dims, NULL) < 0) + errx(EXIT_FAILURE, "H5Sselect_hyperslab failed"); + + status = H5Dwrite(ds, H5T_NATIVE_FLOAT, s->memspace, filespace, + H5P_DEFAULT, mat); + + if (status < 0) + errx(EXIT_FAILURE, "H5Dwrite failed"); + + if (H5Sclose(filespace) < 0) + errx(EXIT_FAILURE, "H5Sclose failed"); + + if (H5Dflush(ds) < 0) + errx(EXIT_FAILURE, "H5Dflush failed"); +} + +static void +open_group(state_t *s) +{ + hid_t g; + const char *gname = "/group-0"; + + assert(s->group == H5I_INVALID_HID); + + g = H5Gopen(s->file, gname, H5P_DEFAULT); + + if (g < 0) + errx(EXIT_FAILURE, "H5Gcreate failed"); + + fprintf(stderr, "Opened group %s\n", gname); + s->group = g; +} + +static void +create_group(state_t *s) +{ + hid_t g; + const char *gname = "/group-0"; + + assert(s->group == H5I_INVALID_HID); + + g = H5Gcreate(s->file, gname, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + if (g < 0) + errx(EXIT_FAILURE, "H5Gcreate failed"); + + s->group = g; +} + +static void +open_dataset(state_t *s) +{ + const char *dname = "/group-0/dataset-0"; + hid_t ds; + hid_t filespace; + hid_t ty; + hsize_t dims[RANK], maxdims[RANK]; + + assert(s->dataset == H5I_INVALID_HID); + + ds = H5Dopen(s->file, dname, H5P_DEFAULT); + + if (ds < 0) + errx(EXIT_FAILURE, "H5Dopen(, \"%s\", ) failed", dname); + + if ((ty = H5Dget_type(ds)) < 0) + errx(EXIT_FAILURE, "H5Dget_type failed"); + + if (H5Tequal(ty, H5T_IEEE_F32BE) <= 0) + errx(EXIT_FAILURE, "Unexpected data type"); + + if ((filespace = H5Dget_space(ds)) < 0) + errx(EXIT_FAILURE, "H5Dget_space failed"); + + if (H5Sget_simple_extent_ndims(filespace) != RANK) + errx(EXIT_FAILURE, "Unexpected rank"); + + if (H5Sget_simple_extent_dims(filespace, dims, maxdims) < 0) + errx(EXIT_FAILURE, "H5Sget_simple_extent_dims failed"); + + if (dims[1] != original_dims[1] || dims[2] != original_dims[2]) { + errx(EXIT_FAILURE, "Unexpected dimensions ? x %ju x %ju", + (uintmax_t)dims[1], (uintmax_t)dims[2]); + } + + if (maxdims[1] != original_dims[1] || maxdims[2] != original_dims[2]) { + errx(EXIT_FAILURE, + "Unexpected maximum dimensions ? x %ju x %ju", + (uintmax_t)dims[1], (uintmax_t)dims[2]); + } + + fprintf(stderr, "Opened dataset %s\n", dname); + s->dataset = ds; +} + +static void +create_dataset(state_t *s) +{ + const char *dname = "/group-0/dataset-0"; + hid_t ds; + hid_t filespace; + + assert(s->dataset == H5I_INVALID_HID); + + filespace = H5Screate_simple(__arraycount(original_dims), original_dims, + max_dims); + + if (filespace < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed", + __func__, __LINE__); + } + + if ((s->dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) { + errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed", + __func__, __LINE__); + } + + if (H5Pset_chunk(s->dcpl, RANK, chunk_dims) < 0) + errx(EXIT_FAILURE, "H5Pset_chunk failed"); + + ds = H5Dcreate2(s->file, dname, H5T_IEEE_F32BE, filespace, + H5P_DEFAULT, s->dcpl, H5P_DEFAULT); + + if (H5Sclose(filespace) < 0) + errx(EXIT_FAILURE, "H5Sclose failed"); + + filespace = H5I_INVALID_HID; + + if (ds < 0) + errx(EXIT_FAILURE, "H5Dcreate(, \"%s\", ) failed", dname); + + s->dataset = ds; +} + +static void +handle_signal(int H5_ATTR_UNUSED signo) +{ + unbroken = 0; +} + +static void +disestablish_handler(const struct sigaction *osa) +{ + if (sigaction(SIGINT, osa, NULL) == -1) + err(EXIT_FAILURE, "%s: sigaction", __func__); +} + +static void +establish_handler(struct sigaction *osa) +{ + struct sigaction sa; + + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = handle_signal; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGINT, &sa, osa) == -1) + err(EXIT_FAILURE, "%s: sigaction", __func__); +} + +static void +step(vec_t *center, vec_t *direction, float steplen, bool *recursed) +{ + static const float top = 0., bottom = (float)COLS, + left = 0., right = (float)ROWS; + struct { + bool top, bottom, left, right; + } bounce = {false, false, false, false}; + float xback, yback; + vec_t before = *center; + vec_t after = (vec_t){.x = before.x + direction->x * steplen, + .y = before.y + direction->y * steplen}; + + if (before.x < right && after.x >= right) { + xback = (right - before.x) / (after.x - before.x); + bounce.right = true; + } else if (before.x > left && after.x <= left) { + xback = (before.x - left) / (before.x - after.x); + bounce.left = true; + } else + xback = 0.; + + if (before.y < bottom && after.y >= bottom) { + yback = (bottom - before.y) / (after.y - before.y); + bounce.bottom = true; + } else if (before.y > top && after.y <= top) { + yback = (before.y - top) / (before.y - after.y); + bounce.top = true; + } else + yback = 0.; + + /* I shorten the step length until a corner crossing becomes + * a side crossing. + */ + if ((bounce.top && bounce.right) || + (bounce.right && bounce.bottom) || + (bounce.bottom && bounce.left) || + (bounce.left && bounce.top)) { + + float newsteplen = steplen * 2 / 3; + if (recursed != NULL) + *recursed = true; + step(center, direction, newsteplen, NULL); + step(center, direction, steplen - newsteplen, NULL); + } + if (bounce.right || bounce.left) { + after.x = before.x + direction->x * (2 * xback - 1) * steplen; + direction->x = -direction->x; + } + if (bounce.top || bounce.bottom) { + after.y = before.y + direction->y * (2 * yback - 1) * steplen; + direction->y = -direction->y; + } + *center = after; +} + +static float +gaussian(float x, float y, float r) +{ + return expf(-(x * x + y * y) / (r * r)); +} + +static int +stepno(float v) +{ + if ((double)v < 1. / 8.) + return 0; + if ((double)v < 3. / 8.) + return 1; + if ((double)v < 7 / 8.) + return 2; + + return 3; +} + +static void +draw_border(WINDOW *w) +{ + wborder(w, 0, 0, 0, 0, 0, 0, 0, 0); +} + +static void +matrix_draw(WINDOW *w, float mat[ROWS][COLS]) +{ + int ch, i, j; + static char steps[] = " .oO"; + + wclear(w); + draw_border(w); + for (i = 0; i < ROWS; i++) { + wmove(w, 1 + i, 1); + for (j = 0; j < COLS; j++) { + ch = steps[stepno(mat[i][j])]; + waddch(w, (const chtype)ch); + } + } + + wnoutrefresh(w); +} + +static void +matrix_compute(vec_t *center, size_t ncenters, float mat[ROWS][COLS]) +{ + int i, j, k; + float radius = 4; + + for (i = 0; i < ROWS; i++) { + for (j = 0; j < COLS; j++) { + mat[i][j] = 0.; + for (k = 0; k < (int)ncenters; k++) { + mat[i][j] += gaussian((float)i - center[k].x, + (float)j - center[k].y, radius); + } + } + } +} + +static void +move_centers(vec_t *center, vec_t *direction, size_t ncenters) +{ + const float steplen = (float).01; + int k; + bool recursed[2] = {false, false}; + + for (k = 0; k < (int)ncenters; k++) { + recursed[k] = false; + step(¢er[k], &direction[k], steplen, &recursed[k]); + } +} + +static void +matrix_open(state_t *s, bool rw) +{ + const char *func; + hid_t fapl, fcpl; + H5F_vfd_swmr_config_t config; + + memset(&config, '\0', sizeof(config)); + + /* config, tick_len, max_lag, writer, flush_raw_data, md_pages_reserved, md_file_path */ + init_vfd_swmr_config(&config, SWMR_TICK_LEN, 5, rw, FALSE, 128, "./my_md_file"); + + /* use_latest_format, use_vfd_swmr, only_meta_page, config */ + fapl = vfd_swmr_create_fapl(false, true, true, &config); + if (fapl < 0) { + errx(EXIT_FAILURE, "%s.%d vfd_swmr_create_fapl failed", + __func__, __LINE__); + } + + fcpl = H5Pcreate(H5P_FILE_CREATE); + if (fcpl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + /* Set file space strategy to paged aggregation in fcpl. + * Page buffering *requires* this strategy. + * + * I set the free-space threshold to 1GB so that deleted + * datasets are not recycled. + */ + if (H5Pset_file_space_strategy(fcpl, + H5F_FSPACE_STRATEGY_PAGE, false, 1024 * 1024 * 1024) < 0) + errx(EXIT_FAILURE, "H5Pset_file_space_strategy failed"); + + if (rw) { + s->file = H5Fcreate("vfd_swmr_gaussians.h5", H5F_ACC_TRUNC, fcpl, fapl); + func = "H5Fcreate"; + } else { + s->file = H5Fopen("vfd_swmr_gaussians.h5", H5F_ACC_RDONLY, fapl); + func = "H5Fopen"; + } + + H5Pclose(fapl); + + if (s->file < 0) + errx(EXIT_FAILURE, "%s failed", func); +} + +static void +fuzz(float mat[ROWS][COLS]) +{ + int i, j; + + for (i = 0; i < ROWS; i++) { + for (j = 0; j < COLS; j++) { + long int temp = random() / RAND_MAX * (long int)(9. / 64.); + mat[i][j] += (float)temp; + } + } +} + +int +main(int argc, char **argv) +{ + char buf[32]; + float mat[ROWS][COLS]; + int frameno; + vec_t center[2] = {{.x = .5, .y = .5}, + {.x = ROWS - .5, .y = COLS - .5}}; + vec_t direction[2] = {{.x = 3, .y = 7}, {.x = 43, .y = 41}}; + struct sigaction osa; + WINDOW *topw = NULL, *w = NULL; + personality_t personality; + state_t s; + uint64_t temp; + + srandom((unsigned int)time(NULL)); + + setlocale(LC_ALL, ""); + + state_init(&s, argc, argv); + + switch (s.progname[0]) { + case 'r': + personality = READ; + break; + case 'w': + personality = WRITE; + break; + default: + personality = STANDALONE; + break; + } + establish_handler(&osa); + + switch (personality) { + case WRITE: + matrix_open(&s, true); + create_group(&s); + create_dataset(&s); + break; + case READ: + matrix_open(&s, false); + open_group(&s); + open_dataset(&s); + break; + default: + break; + } + + if ((topw = initscr()) == NULL) + errx(EXIT_FAILURE, "initscr failed"); + else if ((w = subwin(topw, ROWS + 2, COLS + 2, 0, 0)) == NULL) + errx(EXIT_FAILURE, "subwin failed"); + + for (frameno = 0; unbroken; ) { + struct timespec elapsed, start, stop; + uint64_t elapsed_ns; + clock_gettime(CLOCK_MONOTONIC, &start); + + switch (personality) { + case READ: + matrix_read(&s, &frameno, mat); + break; + case WRITE: + case STANDALONE: + matrix_compute(center, __arraycount(center), mat); + if (s.fuzz) + fuzz(mat); + break; + } + switch (personality) { + case READ: + case STANDALONE: + matrix_draw(w, mat); +#if 0 + wmove(topw, ROWS + 3, 0); + waddstr(topw, "\"Don't cross the streams.\""); +#endif + break; + case WRITE: + matrix_write(&s, frameno, mat); + break; + } + + snprintf(buf, sizeof(buf), "Frame %d.", frameno); + wmove(topw, ROWS + 2, 0); + waddstr(topw, buf); + snprintf(buf, sizeof(buf), "Rate %lld/s.", + 1000000000ULL / timespec2ns(&s.update_interval)); + wmove(topw, ROWS + 2, COLS + 2 - (int)strlen(buf)); + waddstr(topw, buf); + wnoutrefresh(topw); + doupdate(); + + nanosleep(&s.update_interval, NULL); + + switch (personality) { + case STANDALONE: + case WRITE: + move_centers(center, direction, __arraycount(center)); + frameno++; + break; + case READ: + break; + } + clock_gettime(CLOCK_MONOTONIC, &stop); + + timespecsub(&stop, &start, &elapsed); + elapsed_ns = timespec2ns(&elapsed); + + if (elapsed_ns < s.min_elapsed_ns) + s.min_elapsed_ns = elapsed_ns; + if (elapsed_ns > s.max_elapsed_ns) + s.max_elapsed_ns = elapsed_ns; + s.total_elapsed_ns += elapsed_ns; + s.total_loops++; + } + endwin(); + fprintf(stderr, "Iteration stats:\n"); + fprintf(stderr, "min. elapsed %" PRIu64 " ms\n", + s.min_elapsed_ns / 1000000); + fprintf(stderr, "max. elapsed %" PRIu64 " ms\n", + s.max_elapsed_ns / 1000000); + temp = s.total_elapsed_ns / s.total_loops / 1000000; + fprintf(stderr, "avg. elapsed %.3f ms\n", (double)temp); + disestablish_handler(&osa); + return EXIT_SUCCESS; +} |