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