From f6e7edcedf0339bd18ca0a995f9877a778180f5f Mon Sep 17 00:00:00 2001 From: vchoi Date: Fri, 26 Feb 2021 13:54:11 -0600 Subject: Add the two VFD SWMR demo programs to the test directory so that they can be built as needed. --- MANIFEST | 1 + configure.ac | 10 + m4/ax_with_curses.m4 | 571 +++++++++++++++++++++++++++++++++++ test/Makefile.am | 16 +- test/vfd_swmr_common.c | 19 ++ test/vfd_swmr_common.h | 9 + test/vfd_swmr_credel.c | 483 +++++++++++++++++++++++++++++ test/vfd_swmr_gaussians.c | 753 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 1861 insertions(+), 1 deletion(-) create mode 100644 m4/ax_with_curses.m4 create mode 100644 test/vfd_swmr_credel.c create mode 100644 test/vfd_swmr_gaussians.c diff --git a/MANIFEST b/MANIFEST index 4b14d3f..d493763 100644 --- a/MANIFEST +++ b/MANIFEST @@ -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 is present and should be used +# HAVE_NCURSESW_H - if should be used +# HAVE_NCURSES_H - if should be used +# HAVE_NCURSESW_CURSES_H - if should be used +# HAVE_NCURSES_CURSES_H - if 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 . 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 +# #elif defined HAVE_NCURSESW_H +# # include +# #elif defined HAVE_NCURSES_CURSES_H +# # include +# #elif defined HAVE_NCURSES_H +# # include +# #elif defined HAVE_CURSES_H +# # include +# #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 +# Copyright (c) 2009 Damian Pietras +# Copyright (c) 2012 Reuben Thomas +# Copyright (c) 2011 John Zaitseff +# +# 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 . +# +# 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 + ]], [[ + 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 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 + ]], [[ + 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 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 + ]], [[ + 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 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 + ]], [[ + 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 is present]) + ]) + + AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + @%:@include + ]], [[ + 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 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 + ]], [[ + 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 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 + @%:@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 + ]], [[ + 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 + ]], [[ + 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 +#include /* 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 +#include +#include +#include /* basename(3) */ +#include +#include +#include +#include +#include /* struct timespec, nanosleep */ +#include /* 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 +#include +#include +#include +#include /* basename(3) */ +#include /* expf(3) */ +#include /* setlocale(3) */ +#include +#include +#include +#include +#include /* struct timespec, nanosleep(2), time(3), + * clock_gettime(2) + */ +#include /* getopt, PATH_MAX, ... */ + +#include /* 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; +} -- cgit v0.12 From d9f44665cd19620118cd5f6a4fdfac5050200e70 Mon Sep 17 00:00:00 2001 From: vchoi Date: Mon, 1 Mar 2021 18:10:07 -0600 Subject: Revert "Add the two VFD SWMR demo programs to the test directory so that they can be built as needed." This reverts commit f6e7edcedf0339bd18ca0a995f9877a778180f5f. --- MANIFEST | 1 - configure.ac | 10 - m4/ax_with_curses.m4 | 571 ----------------------------------- test/Makefile.am | 16 +- test/vfd_swmr_common.c | 19 -- test/vfd_swmr_common.h | 9 - test/vfd_swmr_credel.c | 483 ----------------------------- test/vfd_swmr_gaussians.c | 753 ---------------------------------------------- 8 files changed, 1 insertion(+), 1861 deletions(-) delete mode 100644 m4/ax_with_curses.m4 delete mode 100644 test/vfd_swmr_credel.c delete mode 100644 test/vfd_swmr_gaussians.c diff --git a/MANIFEST b/MANIFEST index d493763..4b14d3f 100644 --- a/MANIFEST +++ b/MANIFEST @@ -56,7 +56,6 @@ ./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 76d8f3e..a641a45 100644 --- a/configure.ac +++ b/configure.ac @@ -1439,15 +1439,6 @@ 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 @@ -3737,7 +3728,6 @@ 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 deleted file mode 100644 index 945a626..0000000 --- a/m4/ax_with_curses.m4 +++ /dev/null @@ -1,571 +0,0 @@ -# =========================================================================== -# 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 is present and should be used -# HAVE_NCURSESW_H - if should be used -# HAVE_NCURSES_H - if should be used -# HAVE_NCURSESW_CURSES_H - if should be used -# HAVE_NCURSES_CURSES_H - if 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 . 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 -# #elif defined HAVE_NCURSESW_H -# # include -# #elif defined HAVE_NCURSES_CURSES_H -# # include -# #elif defined HAVE_NCURSES_H -# # include -# #elif defined HAVE_CURSES_H -# # include -# #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 -# Copyright (c) 2009 Damian Pietras -# Copyright (c) 2012 Reuben Thomas -# Copyright (c) 2011 John Zaitseff -# -# 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 . -# -# 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 - ]], [[ - 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 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 - ]], [[ - 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 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 - ]], [[ - 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 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 - ]], [[ - 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 is present]) - ]) - - AC_CACHE_CHECK([for working ncurses.h], [ax_cv_header_ncurses_h], [ - AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - @%:@include - ]], [[ - 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 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 - ]], [[ - 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 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 - @%:@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 - ]], [[ - 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 - ]], [[ - 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 0aec03c..2b1d7d5 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -120,24 +120,10 @@ 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 \ - 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 + gen_sizes_lheap gen_file_image gen_plist gen_bad_offset gen_bounds if BUILD_ALL_CONDITIONAL noinst_PROGRAMS=$(BUILD_ALL_PROGS) diff --git a/test/vfd_swmr_common.c b/test/vfd_swmr_common.c index 9352a37..3f03ce7 100644 --- a/test/vfd_swmr_common.c +++ b/test/vfd_swmr_common.c @@ -418,22 +418,3 @@ 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 bbcb28a..f64ee87 100644 --- a/test/vfd_swmr_common.h +++ b/test/vfd_swmr_common.h @@ -19,7 +19,6 @@ /***********/ #include -#include /* For demo */ #include "h5test.h" /**********/ @@ -36,11 +35,6 @@ /* 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 */ /************/ @@ -94,9 +88,6 @@ 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 deleted file mode 100644 index fe6f0d5..0000000 --- a/test/vfd_swmr_credel.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * 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 -#include -#include -#include /* basename(3) */ -#include -#include -#include -#include -#include /* struct timespec, nanosleep */ -#include /* 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 deleted file mode 100644 index 17231a9..0000000 --- a/test/vfd_swmr_gaussians.c +++ /dev/null @@ -1,753 +0,0 @@ -#include -#include -#include -#include -#include /* basename(3) */ -#include /* expf(3) */ -#include /* setlocale(3) */ -#include -#include -#include -#include -#include /* struct timespec, nanosleep(2), time(3), - * clock_gettime(2) - */ -#include /* getopt, PATH_MAX, ... */ - -#include /* 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; -} -- cgit v0.12 From c18b6eaea86281a478c61af5a342e76d4b6fce34 Mon Sep 17 00:00:00 2001 From: vchoi Date: Tue, 2 Mar 2021 11:59:33 -0600 Subject: Add test settings for vfd_swmr_bigset_writer.c based on HDF5TestExpress: Default, Exhaustive, and quick runs. --- test/testvfdswmr.sh.in | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/test/testvfdswmr.sh.in b/test/testvfdswmr.sh.in index a66ddf4..a2e47b1 100644 --- a/test/testvfdswmr.sh.in +++ b/test/testvfdswmr.sh.in @@ -40,6 +40,30 @@ nsofterrors=0 # soft errors are expected to occur some of the time # on a couple of nondeterministic tests. ############################################################################### +## test parameters for vfd_swmr_bigset_writer.c based on HDF5TestExpress: +## 0: Exhaustive run: Tests take a long time to run. +## 1: Default run. +## 2+: Quick run +############################################################################### +if [ -z $HDF5TestExpress ]; then # Set to default when not set + HDF5TestExpress=1 +fi +## +##Default setting +BIGSET_n=25 # -n option: # of iterations +BIGSET_few_s=20 # -s option: # of datasets (for few_big test) +BIGSET_many_s=500 # -s option: # of datasets (for many_small test) +if [[ "$HDF5TestExpress" -eq 0 ]] ; then # Setting for exhaustive run + BIGSET_n=50 + BIGSET_few_s=40 + BIGSET_many_s=1000 +elif [[ "$HDF5TestExpress" -gt 1 ]]; then # Setting for quick run + BIGSET_n=10 + BIGSET_few_s=10 + BIGSET_many_s=100 +fi + +############################################################################### ## definitions for message file to coordinate test runs ############################################################################### WRITER_MESSAGE=VFD_SWMR_WRITER_MESSAGE # The message file created by writer that the open is complete @@ -641,11 +665,11 @@ for options in "-d 1" "-d 1 -F" "-d 2" "-d 2 -F" "-d 1 -V" "-d 1 -M" "-d 1 -V -F # echo launch vfd_swmr_bigset_writer many small, options $options catch_out_err_and_rc vfd_swmr_bigset_writer \ - ../vfd_swmr_bigset_writer -n 50 $options -s 1000 -r 16 -c 16 -q & + ../vfd_swmr_bigset_writer -n $BIGSET_n $options -s $BIGSET_many_s -r 16 -c 16 -q & pid_writer=$! catch_out_err_and_rc vfd_swmr_bigset_reader \ - ../vfd_swmr_bigset_reader -n 50 $options -s 1000 -r 16 -c 16 -q -W & + ../vfd_swmr_bigset_reader -n $BIGSET_n $options -s $BIGSET_many_s -r 16 -c 16 -q -W & pid_reader=$! # Wait for the reader to finish before signalling the @@ -690,11 +714,11 @@ for options in "-d 1" "-d 1 -F" "-d 2" "-d 2 -F" "-d 1 -V" "-d 1 -M" "-d 1 -V -F fi echo launch vfd_swmr_bigset_writer few big, options $options catch_out_err_and_rc vfd_swmr_bigset_writer \ - ../vfd_swmr_bigset_writer -n 50 $options -s 40 -r 256 -c 256 -q & + ../vfd_swmr_bigset_writer -n $BIGSET_n $options -s $BIGSET_few_s -r 256 -c 256 -q & pid_writer=$! catch_out_err_and_rc vfd_swmr_bigset_reader \ - ../vfd_swmr_bigset_reader -n 50 $options -s 40 -r 256 -c 256 -q -W & + ../vfd_swmr_bigset_reader -n $BIGSET_n $options -s $BIGSET_few_s -r 256 -c 256 -q -W & pid_reader=$! # Wait for the reader to finish before signalling the -- cgit v0.12 From b0ce859b2c2f3862df5c00c7c84f8abd777f3a62 Mon Sep 17 00:00:00 2001 From: vchoi Date: Thu, 4 Mar 2021 13:36:39 -0600 Subject: Add the two VFD SWMR demo programs to "examples" directory. Provide instructions on compiling and running the demos. --- examples/README | 27 ++ examples/credel.c | 484 ++++++++++++++++++++++++++++++++ examples/gaussians.c | 759 +++++++++++++++++++++++++++++++++++++++++++++++++++ examples/nbcompat.c | 17 ++ examples/nbcompat.h | 36 +++ 5 files changed, 1323 insertions(+) create mode 100644 examples/credel.c create mode 100644 examples/gaussians.c create mode 100644 examples/nbcompat.c create mode 100644 examples/nbcompat.h diff --git a/examples/README b/examples/README index e0a3364..4ab596d 100644 --- a/examples/README +++ b/examples/README @@ -15,3 +15,30 @@ installed. Compile scripts from other locations can be used by setting an environment variable prefix to the path of the directory containing the bin directory with the compile scripts h5cc, h5fc, etc. For example, export prefix=/usr/local/hdf5 to use h5cc, h5fc, etc. in /usr/local/hdf5/bin. + +*************************************************************************************** +Instruction for compiling and running the two VFD SWMR demo programs in this directory: + +credel.c: +========= +To compile: + h5cc -o credel credel.c nbcompat.c + +To run: + ./credel -v (on one window) + h5ls --vfd=swmr --poll=100 -r -d ./credel.h5 (on another window) + +vfd_swmr_gaussians.c +==================== +To compile: + h5cc -o gaussians ./gaussians.c ./nbcompat.c -lcurses +To link as writer: + ln -s gaussians wgaussians +To link as reader: + ln -s gaussians rgaussians + +To run standalone: + ./gaussians +To run as writer and reader: + ./wgaussians (on one window) + ./rgaussians (on another window) diff --git a/examples/credel.c b/examples/credel.c new file mode 100644 index 0000000..218baeb --- /dev/null +++ b/examples/credel.c @@ -0,0 +1,484 @@ +#include +#include +#include +#include /* basename(3) */ +#include +#include +#include +#include +#include /* struct timespec, nanosleep */ +#include /* getopt, PATH_MAX, ... */ + +#include "hdf5.h" +#include "nbcompat.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, int 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 < __arraycount(data); i++) { + for (j = 0; j < __arraycount(data[i]); j++) { + int k = (didx + j + i) % __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 = millis / 1000UL; + s->update_interval.tv_nsec = (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 < __arraycount(s->dataset); i++) + s->dataset[i] = H5I_INVALID_HID; + + for (i = 0; i < __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 >= 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); + 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 + const hid_t ds = s->dataset[didx % ndatasets]; + 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 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); + + fapl = H5Pcreate(H5P_FILE_ACCESS); + if (fapl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + fcpl = H5Pcreate(H5P_FILE_CREATE); + if (fcpl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + config.version = H5F__CURR_VFD_SWMR_CONFIG_VERSION; + config.tick_len = 4; + config.max_lag = 5; +#if 0 /* raw-data flushing is not implemented */ + config.flush_raw_data = true; +#endif + config.writer = true; + config.md_pages_reserved = 128; + strlcpy(config.md_file_path, "./my_md_file", + sizeof(config.md_file_path)); + + /* Enable page buffering */ + if (H5Pset_page_buffer_size(fapl, 4096, 100, 0) < 0) + errx(EXIT_FAILURE, "H5Pset_page_buffer_size failed"); + + /* Enable VFD SWMR configuration */ + if (s->use_vfd_swmr && H5Pset_vfd_swmr_config(fapl, &config) < 0) + errx(EXIT_FAILURE, "H5Pset_vfd_swmr_config failed"); + + /* 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/examples/gaussians.c b/examples/gaussians.c new file mode 100644 index 0000000..0ceeb8b --- /dev/null +++ b/examples/gaussians.c @@ -0,0 +1,759 @@ +#include +#include +#include +#include +#include /* basename(3) */ +#include /* expf(3) */ +#include /* setlocale(3) */ +#include +#include +#include +#include +#include /* struct timespec, nanosleep(2), time(3), + * clock_gettime(2) + */ +#include /* getopt, PATH_MAX, ... */ + +#include /* for MIN(a, b) */ + +#include "hdf5.h" +#include "nbcompat.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; + + *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 = millis / 1000UL; + s->update_interval.tv_nsec = + (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__); + } + + const int nprinted = snprintf(s->output_file, sizeof(s->output_file), + "%s.h5", s->progname); + if (nprinted < 0 || nprinted >= 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]; + + 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]); + } + + 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 hssize_t hang_back = 2 * updates_per_tick; + const int frameno = *framenop; + + const int lead = frameno + hang_back - dims[0]; + 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. */ + *framenop = dims[0] - hang_back; + } 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 >= 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; + } + + hsize_t offset[RANK] = {frameno, 0, 0}; + + 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] = {frameno + 1, ROWS, COLS}; + hsize_t offset[RANK] = {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 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)); +} + +int +stepno(float v) +{ + if (v < 1. / 8.) + return 0; + if (v < 3. / 8.) + return 1; + if (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, 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 < ncenters; k++) { + mat[i][j] += gaussian(i - center[k].x, + j - center[k].y, radius); + } + } + } +} + +static void +move_centers(vec_t *center, vec_t *direction, size_t ncenters) +{ + const float steplen = .01; + int k; + bool recursed[2] = {false, false}; + + for (k = 0; k < 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; + + fapl = H5Pcreate(H5P_FILE_ACCESS); + if (fapl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + fcpl = H5Pcreate(H5P_FILE_CREATE); + if (fcpl < 0) { + errx(EXIT_FAILURE, "%s.%d H5Pcreate failed", + __func__, __LINE__); + } + + memset(&config, '\0', sizeof(config)); + + config.version = H5F__CURR_VFD_SWMR_CONFIG_VERSION; + config.tick_len = SWMR_TICK_LEN; + config.max_lag = 5; + config.writer = rw; + config.md_pages_reserved = 128; + +#if 0 /* raw-data flushing is not implemented; default open-tries is ok */ + config.flush_raw_data = true; + config.md_open_tries = 1; +#endif + + strlcpy(config.md_file_path, "./my_md_file", sizeof(config.md_file_path)); + + /* Enable page buffering */ + if (H5Pset_page_buffer_size(fapl, 4096, 100, 0) < 0) + errx(EXIT_FAILURE, "H5Pset_page_buffer_size failed"); + + /* Enable VFD SWMR configuration */ + if (H5Pset_vfd_swmr_config(fapl, &config) < 0) + errx(EXIT_FAILURE, "H5Pset_vfd_swmr_config failed"); + + /* 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("gaussians.h5", H5F_ACC_TRUNC, fcpl, fapl); + func = "H5Fcreate"; + } else { + s->file = H5Fopen("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++) { + mat[i][j] += (float)random() / RAND_MAX * (9. / 64.); + } + } +} + +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; + + 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 - 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); + fprintf(stderr, "avg. elapsed %.3f ms\n", + (double)s.total_elapsed_ns / s.total_loops / 1000000); + disestablish_handler(&osa); + return EXIT_SUCCESS; +} diff --git a/examples/nbcompat.c b/examples/nbcompat.c new file mode 100644 index 0000000..03e477f --- /dev/null +++ b/examples/nbcompat.c @@ -0,0 +1,17 @@ +#include "nbcompat.h" + +size_t +strlcpy(char *dst, const char *src, size_t size) +{ + char *d; + const char *s; + + for (d = dst, s = src; (s - src) < size; d++, s++) { + *d = *s; + if (*s == '\0') + return s - src; + } + + dst[size - 1] = '\0'; + return size; +} diff --git a/examples/nbcompat.h b/examples/nbcompat.h new file mode 100644 index 0000000..98f8c7d --- /dev/null +++ b/examples/nbcompat.h @@ -0,0 +1,36 @@ +#ifndef NB_COMPAT_H +#define NB_COMPAT_H + +#include /* for size_t */ + +#ifndef __arraycount +#define __arraycount(__a) (sizeof(__a) / sizeof((__a)[0])) +#endif + +size_t strlcpy(char *, const char *, size_t); + +#define timespeccmp(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#define timespecadd(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \ + if ((vsp)->tv_nsec >= 1000000000L) { \ + (vsp)->tv_sec++; \ + (vsp)->tv_nsec -= 1000000000L; \ + } \ + } while (/* CONSTCOND */ 0) +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (/* CONSTCOND */ 0) +#define timespec2ns(x) (((uint64_t)(x)->tv_sec) * 1000000000L + (x)->tv_nsec) + +#endif /* NB_COMPAT_H */ -- cgit v0.12