summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--COPYING66
-rw-r--r--MANIFEST16
-rwxr-xr-xbin/trace1
-rw-r--r--config/gnu-flags4
-rw-r--r--config/gnu-warnings/error-51
-rw-r--r--config/gnu-warnings/error-general2
-rw-r--r--configure.ac26
-rw-r--r--doc/Diagnostic-Logging.md88
-rw-r--r--doc/VFD_SWMR_Guide.md24
-rw-r--r--doc/VFD_SWMR_Punch_List.md192
-rw-r--r--doc/VFD_SWMR_RFC_190902.docxbin0 -> 157882 bytes
-rw-r--r--doc/VFD_SWMR_RFC_200107.docxbin0 -> 169600 bytes
-rw-r--r--doc/VFD_SWMR_RFC_2020-02-03.docxbin0 -> 138626 bytes
-rw-r--r--hl/tools/h5watch/h5watch.c2
-rw-r--r--release_docs/RELEASE.txt2
-rw-r--r--src/H5.c1
-rw-r--r--src/H5AC.c70
-rw-r--r--src/H5ACmpio.c2
-rw-r--r--src/H5ACprivate.h2
-rw-r--r--src/H5ACproxy_entry.c1
-rw-r--r--src/H5B2cache.c3
-rw-r--r--src/H5Bcache.c1
-rw-r--r--src/H5C.c1184
-rw-r--r--src/H5Cdbg.c16
-rw-r--r--src/H5Cepoch.c27
-rw-r--r--src/H5Cpkg.h219
-rw-r--r--src/H5Cprefetched.c1
-rw-r--r--src/H5Cprivate.h226
-rw-r--r--src/H5Ctag.c6
-rw-r--r--src/H5Dbtree.c3
-rw-r--r--src/H5Dbtree2.c3
-rw-r--r--src/H5Dchunk.c68
-rw-r--r--src/H5Dearray.c3
-rw-r--r--src/H5Dfarray.c3
-rw-r--r--src/H5Dint.c2
-rw-r--r--src/H5Dnone.c3
-rw-r--r--src/H5Dpkg.h5
-rw-r--r--src/H5Dsingle.c3
-rw-r--r--src/H5Dvirtual.c213
-rw-r--r--src/H5EAcache.c5
-rw-r--r--src/H5F.c105
-rw-r--r--src/H5FAcache.c3
-rw-r--r--src/H5FD.c104
-rw-r--r--src/H5FDcore.c1
-rw-r--r--src/H5FDfamily.c1
-rw-r--r--src/H5FDhdfs.c1
-rw-r--r--src/H5FDint.c3
-rw-r--r--src/H5FDlog.c1
-rw-r--r--src/H5FDmulti.c1
-rw-r--r--src/H5FDpkg.h1
-rw-r--r--src/H5FDprivate.h214
-rw-r--r--src/H5FDpublic.h7
-rw-r--r--src/H5FDsec2.c1
-rw-r--r--src/H5FDsplitter.c1
-rw-r--r--src/H5FDstdio.c1
-rw-r--r--src/H5FDtest.c72
-rw-r--r--src/H5FDvfd_swmr.c1607
-rw-r--r--src/H5FDvfd_swmr.h38
-rw-r--r--src/H5FDvfd_swmr_instr.c28
-rw-r--r--src/H5FDvfd_swmr_private.h93
-rw-r--r--src/H5FScache.c2
-rw-r--r--src/H5FSprivate.h1
-rw-r--r--src/H5FSsection.c2
-rw-r--r--src/H5Fint.c306
-rw-r--r--src/H5Fio.c83
-rw-r--r--src/H5Fpkg.h126
-rw-r--r--src/H5Fprivate.h42
-rw-r--r--src/H5Fpublic.h97
-rw-r--r--src/H5Fquery.c23
-rw-r--r--src/H5Fsfile.c30
-rw-r--r--src/H5Fspace.c1
-rw-r--r--src/H5Fsuper_cache.c269
-rw-r--r--src/H5Ftest.c376
-rw-r--r--src/H5Fvfd_swmr.c2111
-rw-r--r--src/H5Gcache.c1
-rw-r--r--src/H5HFcache.c3
-rw-r--r--src/H5HG.c3
-rw-r--r--src/H5HGcache.c1
-rw-r--r--src/H5HGprivate.h2
-rw-r--r--src/H5HGtrap.c30
-rw-r--r--src/H5HLcache.c2
-rw-r--r--src/H5MF.c349
-rw-r--r--src/H5MFaggr.c3
-rw-r--r--src/H5MFprivate.h2
-rw-r--r--src/H5MFsection.c27
-rw-r--r--src/H5MV.c721
-rw-r--r--src/H5MVmodule.h33
-rw-r--r--src/H5MVpkg.h85
-rw-r--r--src/H5MVprivate.h58
-rw-r--r--src/H5MVsection.c395
-rw-r--r--src/H5Ocache.c2
-rw-r--r--src/H5Oflush.c59
-rw-r--r--src/H5Oprivate.h11
-rw-r--r--src/H5PB.c5028
-rw-r--r--src/H5PBpkg.h1917
-rw-r--r--src/H5PBprivate.h701
-rw-r--r--src/H5Pfapl.c233
-rw-r--r--src/H5Pint.c1
-rw-r--r--src/H5Ppublic.h6
-rw-r--r--src/H5SMcache.c2
-rw-r--r--src/H5VLnative.h3
-rw-r--r--src/H5VLnative_file.c35
-rw-r--r--src/H5private.h59
-rw-r--r--src/H5public.h137
-rw-r--r--src/H5queue.h847
-rw-r--r--src/H5retry_private.h114
-rw-r--r--src/H5system.c25
-rw-r--r--src/H5time_private.h109
-rw-r--r--src/Makefile.am19
-rw-r--r--src/hdf5.h1
-rw-r--r--src/hlog.c366
-rw-r--r--src/hlog.h138
-rw-r--r--test/CMakeLists.txt19
-rw-r--r--test/Makefile.am29
-rw-r--r--test/cache_common.c11
-rw-r--r--test/cache_image.c31
-rw-r--r--test/cache_tagging.c53
-rw-r--r--test/earray.c4
-rw-r--r--test/enc_dec_plist_cross_platform.c117
-rw-r--r--test/err_compat.c6
-rw-r--r--test/farray.c4
-rw-r--r--test/fheap.c5
-rw-r--r--test/file_image.c2
-rw-r--r--test/gen_plist.c2
-rw-r--r--test/genall5.c3491
-rw-r--r--test/genall5.h63
-rw-r--r--test/h5test.c14
-rw-r--r--test/h5test.h4
-rw-r--r--test/hyperslab.c6
-rw-r--r--test/istore.c6
-rw-r--r--test/page_buffer.c1733
-rw-r--r--test/stubs.c25
-rw-r--r--test/supervise.subr32
-rw-r--r--test/swmr_addrem_writer.c2
-rw-r--r--test/swmr_common.c19
-rw-r--r--test/swmr_common.h2
-rw-r--r--test/swmr_reader.c2
-rw-r--r--test/swmr_remove_reader.c2
-rw-r--r--test/swmr_remove_writer.c2
-rw-r--r--test/swmr_sparse_reader.c5
-rw-r--r--test/swmr_sparse_writer.c2
-rw-r--r--test/swmr_start_write.c2
-rw-r--r--test/swmr_writer.c2
-rw-r--r--test/testfiles/err_compat_14
-rw-r--r--test/testfiles/plist_files/dcpl_32bebin221 -> 239 bytes
-rw-r--r--test/testfiles/plist_files/dcpl_32lebin221 -> 239 bytes
-rw-r--r--test/testfiles/plist_files/dcpl_64bebin221 -> 239 bytes
-rw-r--r--test/testfiles/plist_files/dcpl_64lebin221 -> 239 bytes
-rw-r--r--test/testfiles/plist_files/def_dcpl_32bebin132 -> 150 bytes
-rw-r--r--test/testfiles/plist_files/def_dcpl_32lebin132 -> 150 bytes
-rw-r--r--test/testfiles/plist_files/def_dcpl_64bebin132 -> 150 bytes
-rw-r--r--test/testfiles/plist_files/def_dcpl_64lebin132 -> 150 bytes
-rw-r--r--test/testfiles/plist_files/def_dxpl_32bebin225 -> 225 bytes
-rw-r--r--test/testfiles/plist_files/def_dxpl_32lebin225 -> 225 bytes
-rw-r--r--test/testfiles/plist_files/def_dxpl_64bebin225 -> 225 bytes
-rw-r--r--test/testfiles/plist_files/def_dxpl_64lebin225 -> 225 bytes
-rw-r--r--test/testfiles/plist_files/def_fapl_32bebin1651 -> 3745 bytes
-rw-r--r--test/testfiles/plist_files/def_fapl_32lebin1651 -> 3745 bytes
-rw-r--r--test/testfiles/plist_files/def_fapl_64bebin1651 -> 3745 bytes
-rw-r--r--test/testfiles/plist_files/def_fapl_64lebin1651 -> 3745 bytes
-rw-r--r--test/testfiles/plist_files/dxpl_32bebin229 -> 229 bytes
-rw-r--r--test/testfiles/plist_files/dxpl_32lebin229 -> 229 bytes
-rw-r--r--test/testfiles/plist_files/dxpl_64bebin229 -> 229 bytes
-rw-r--r--test/testfiles/plist_files/dxpl_64lebin229 -> 229 bytes
-rw-r--r--test/testfiles/plist_files/fapl_32bebin1653 -> 3747 bytes
-rw-r--r--test/testfiles/plist_files/fapl_32lebin1653 -> 3747 bytes
-rw-r--r--test/testfiles/plist_files/fapl_64bebin1653 -> 3747 bytes
-rw-r--r--test/testfiles/plist_files/fapl_64lebin1653 -> 3747 bytes
-rw-r--r--test/testfiles/plist_files/lapl_32bebin1756 -> 3850 bytes
-rw-r--r--test/testfiles/plist_files/lapl_32lebin1756 -> 3850 bytes
-rw-r--r--test/testfiles/plist_files/lapl_64bebin1756 -> 3850 bytes
-rw-r--r--test/testfiles/plist_files/lapl_64lebin1756 -> 3850 bytes
-rw-r--r--test/testvfdswmr.sh.in746
-rw-r--r--test/tmisc.c1
-rw-r--r--test/unlink.c2
-rw-r--r--test/vfd_swmr.c3474
-rw-r--r--test/vfd_swmr_addrem_writer.c502
-rw-r--r--test/vfd_swmr_bigset_writer.c1104
-rw-r--r--test/vfd_swmr_common.c262
-rw-r--r--test/vfd_swmr_common.h114
-rw-r--r--test/vfd_swmr_generator.c399
-rw-r--r--test/vfd_swmr_group_writer.c343
-rw-r--r--test/vfd_swmr_reader.c677
-rw-r--r--test/vfd_swmr_remove_reader.c596
-rw-r--r--test/vfd_swmr_remove_writer.c426
-rw-r--r--test/vfd_swmr_sparse_reader.c535
-rw-r--r--test/vfd_swmr_sparse_writer.c488
-rw-r--r--test/vfd_swmr_vlstr_reader.c232
-rw-r--r--test/vfd_swmr_vlstr_writer.c322
-rw-r--r--test/vfd_swmr_writer.c454
-rw-r--r--test/vfd_swmr_zoo_writer.c412
-rw-r--r--testpar/t_2Gio.c2
-rw-r--r--testpar/t_cache.c4
-rw-r--r--tools/lib/h5tools.c89
-rw-r--r--tools/lib/h5tools.h4
-rw-r--r--tools/lib/h5tools_dump.c4
-rw-r--r--tools/libtest/h5tools_test_utils.c1
-rw-r--r--tools/src/h5dump/h5dump_xml.c2
-rw-r--r--tools/src/h5ls/h5ls.c72
-rw-r--r--tools/src/h5stat/h5stat.c5
-rw-r--r--tools/testfiles/help-1.ls2
-rw-r--r--tools/testfiles/help-2.ls2
-rw-r--r--tools/testfiles/help-3.ls2
-rw-r--r--tools/testfiles/textlinksrc-nodangle-1.ls2
-rw-r--r--tools/testfiles/tgroup-1.ls2
206 files changed, 32165 insertions, 4598 deletions
diff --git a/.gitignore b/.gitignore
index 3caf16a..bdeae42 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,11 @@
# .gitignore file for HDF5
+# ctags
+**/tags
+
+# vim swap files
+**/.*.swp
+
# Makefile.in files
**/Makefile.in
diff --git a/COPYING b/COPYING
index 659bf1c..ee80529 100644
--- a/COPYING
+++ b/COPYING
@@ -54,6 +54,72 @@ http://support.hdfgroup.org/ftp/HDF5/releases/COPYING_LBNL_HDF5.
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
+Portions of HDF5 are Copyright (c) David Young. The applicable copyright
+notice and licensing terms are reproduced here:
+
+Copyright (c) 2004, 2005, 2006, 2007 David Young. All rights reserved.
+
+This file contains code contributed by David Young.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
+YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGE.
+
+-----------------------------------------------------------------------------
+-----------------------------------------------------------------------------
+
+Portions of HDF5 are Copyright (c) Urbana-Champaign Independent Media Center.
+The applicable copyright notice and licensing terms are reproduced here:
+
+Copyright (c) 2004 Urbana-Champaign Independent Media Center.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT
+MEDIA CENTER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT
+MEDIA CENTER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-----------------------------------------------------------------------------
+-----------------------------------------------------------------------------
+
Contributors: National Center for Supercomputing Applications (NCSA) at
the University of Illinois, Fortner Software, Unidata Program Center
(netCDF), The Independent JPEG Group (JPEG), Jean-loup Gailly and Mark Adler
diff --git a/MANIFEST b/MANIFEST
index 9720ebf..4b14d3f 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -521,6 +521,8 @@
./src/hdf5.lnt _DO_NOT_DISTRIBUTE_
./src/hdf5-win.lnt _DO_NOT_DISTRIBUTE_
./src/hdf5-lin.lnt _DO_NOT_DISTRIBUTE_
+./src/hlog.c
+./src/hlog.h
./src/COPYING
./src/H5.c
./src/H5checksum.c
@@ -714,6 +716,8 @@
./src/H5FDstdio.c
./src/H5FDstdio.h
./src/H5FDtest.c
+./src/H5FDvfd_swmr.c
+./src/H5FDvfd_swmr.h
./src/H5FDwindows.c
./src/H5FDwindows.h
./src/H5FL.c
@@ -823,6 +827,11 @@
./src/H5MPpkg.h
./src/H5MPprivate.h
./src/H5MPtest.c
+./src/H5MV.c
+./src/H5MVmodule.h
+./src/H5MVsection.c
+./src/H5MVpkg.h
+./src/H5MVprivate.h
./src/H5O.c
./src/H5Oainfo.c
./src/H5Oalloc.c
@@ -1220,6 +1229,7 @@
./test/testmeta.c
./test/testswmr.sh.in
./test/testvdsswmr.sh.in
+./test/testvfdswmr.sh.in
./test/testvds_env.sh.in
./test/tfile.c
./test/tgenprop.c
@@ -1271,6 +1281,12 @@
./test/vds_swmr_gen.c
./test/vds_swmr_reader.c
./test/vds_swmr_writer.c
+./test/vfd_swmr.c
+./test/vfd_swmr_common.c
+./test/vfd_swmr_common.h
+./test/vfd_swmr_generator.c
+./test/vfd_swmr_reader.c
+./test/vfd_swmr_writer.c
./test/vfd.c
./test/vol.c
./test/vol_plugin.c
diff --git a/bin/trace b/bin/trace
index fd0248e..8d8b1cd 100755
--- a/bin/trace
+++ b/bin/trace
@@ -154,6 +154,7 @@ $Source = "";
"H5F_info1_t" => "x",
"H5F_info2_t" => "x",
"H5F_retry_info_t" => "x",
+ "H5F_vfd_swmr_config_t" => "x",
"H5FD_t" => "x",
"H5FD_class_t" => "x",
"H5FD_stream_fapl_t" => "x",
diff --git a/config/gnu-flags b/config/gnu-flags
index 7cf90b8..9c0323c 100644
--- a/config/gnu-flags
+++ b/config/gnu-flags
@@ -20,7 +20,7 @@
#
# For now, do not promote any warnings to errors.
#
-PROMOTE_ERRORS_DFLT=no
+PROMOTE_ERRORS_DFLT=yes
#
# This filter rewrites -Werror= as -W, in that way demoting warnings
@@ -193,6 +193,8 @@ if test "X-gcc" = "X-$cc_vendor"; then
H5_CFLAGS="$H5_CFLAGS $(load_gnu_arguments general)"
H5_ECFLAGS="$H5_ECFLAGS $(load_gnu_arguments error-general)"
+ H5_CFLAGS="$H5_CFLAGS -Wno-c++-compat"
+
######################
# Developer warnings #
######################
diff --git a/config/gnu-warnings/error-5 b/config/gnu-warnings/error-5
index f7e1138..000f218 100644
--- a/config/gnu-warnings/error-5
+++ b/config/gnu-warnings/error-5
@@ -10,3 +10,4 @@
# warnings to errors only for GCC 5 and later.
#
-Werror=shadow
+-Werror=int-conversion
diff --git a/config/gnu-warnings/error-general b/config/gnu-warnings/error-general
index f0e61f8..28e8ae9 100644
--- a/config/gnu-warnings/error-general
+++ b/config/gnu-warnings/error-general
@@ -5,7 +5,9 @@
-Werror=bad-function-cast
-Werror=declaration-after-statement
-Werror=implicit-function-declaration
+-Werror=int-to-pointer-cast
-Werror=missing-declarations
+-Werror=missing-field-initializers
-Werror=missing-prototypes
-Werror=nested-externs
-Werror=old-style-definition
diff --git a/configure.ac b/configure.ac
index f79e9e6..6d869f9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2904,6 +2904,31 @@ fi
## Direct VFD files are not built if not required.
AM_CONDITIONAL([DIRECT_VFD_CONDITIONAL], [test "X$DIRECT_VFD" = "Xyes"])
+## Checkpoint the cache
+AC_CACHE_SAVE
+
+## ----------------------------------------------------------------------
+## Use custom examples path.
+##
+AC_MSG_CHECKING([for custom examples path definition])
+AC_ARG_WITH([examplesdir],
+ [AS_HELP_STRING([--with-examplesdir=location],
+ [Specify path for examples
+ [default="DATAROOTDIR/hdf5_examples"]])],,
+ withval="${datarootdir}/hdf5_examples")
+
+if test "X$withval" = "X"; then
+ AC_MSG_RESULT([default])
+ examplesdir="${datarootdir}/hdf5_examples"
+else
+ AC_MSG_RESULT([$withval])
+ examplesdir=$withval
+fi
+
+AC_SUBST([examplesdir])
+AC_DEFINE_UNQUOTED([EXAMPLESDIR], ["$examplesdir"],
+ [Define the examples directory])
+
## ----------------------------------------------------------------------
## Check whether the Mirror VFD can be built.
## Auto-enabled if the required libraries are present.
@@ -3824,6 +3849,7 @@ AC_CONFIG_FILES([src/libhdf5.settings
test/testlibinfo.sh
test/testlinks_env.sh
test/testswmr.sh
+ test/testvfdswmr.sh
test/testvds_env.sh
test/testvdsswmr.sh
test/test_filter_plugin.sh
diff --git a/doc/Diagnostic-Logging.md b/doc/Diagnostic-Logging.md
new file mode 100644
index 0000000..e71e737
--- /dev/null
+++ b/doc/Diagnostic-Logging.md
@@ -0,0 +1,88 @@
+# The Hierarchical Log Library (`hlog`)
+
+A program uses the hierarchical log library, `hlog`, to organize
+its diagnostic messages into categories and subcategories and to
+turn on and off message categories to produce the most useful
+diagnostic trace.
+
+A typical program will define one or more log *outlets*. An outlet
+is a named target for diagnostic messages. Each outlet has a *state*
+(*on*, *off*, or *pass*) and at most one *parent outlet*. Usually, the
+parent-child relationships between outlets form a tree rooted at the
+outlet "all", which is supplied by the library. Outlets may form a
+"forest" if a program supplies its own root outlets.
+
+A program sends messages to an outlet using `hlog` API calls.
+Messages sent to an outlet that is *on* are copied to the error
+stream. Messages sent to an outlet that is *off* are discarded.
+When a message is sent to an outlet in *pass* state, the `hlog`
+uses the outlet ancestors to decide what to do with the message.
+
+## Sending messages with `hlog_fast`
+
+A program calls `hlog_fast(outlet name, format string, ...)` to
+write a formatted message to the named outlet. `hlog_fast` uses
+the outlet state to do decide what to do with the message. If the
+outlet is *on*, then the message is written to the standard error
+stream. If the outlet is *off*, then the message is discarded.
+If the outlet is in state *pass*, and the outlet has no parent,
+then the message is discarded. If the outlet does have a parent,
+then `hlog_fast` looks at the parent state and decides whether to
+discard, write, or recurse.
+
+`hlog_fast` precedes each diagnostic message with a timestamp (decimal
+seconds with 9 digits right of the decimal point), a colon, and a single
+space. Each message is followed with a newline ("\n"). The effective
+timestamp resolution may be much less than one nanosecond. Timestamps
+increase monotonically. The timestamp origin is currently unspecified.
+
+## Defining log outlets
+
+`hlog` provides macros for declaring outlets, and for statically
+configuring an outlet, its parent, and its initial state.
+
+Use `HLOG_OUTLET_DECL(name)` to declare shared outlets in header
+files. `HLOG_OUTLET_DECL(name)` declares an `extern` symbol.
+There must not be any quotation marks on *name*.
+
+Use `HLOG_OUTLET_SHORT_DEFN(name, parent)` to define an outlet with the
+given name and parent in state *pass*. There must not be any quotation
+marks on *name*.
+
+Use `HLOG_OUTLET_MEDIUM_DEFN(name, parent, state)` to define an outlet
+with the given name, parent, and state. The state is given by an
+`hlog_outlet_state_t`, one of `HLOG_OUTLET_S_ON`, `HLOG_OUTLET_S_OFF`,
+or `HLOG_OUTLET_S_PASS`. There must not be any quotation marks on
+*name*.
+
+## Enabling and disabling outlets with the environment
+
+An environment variable, `HLOG`, sets initial outlet states for a
+program. If `HLOG` may be set to the empty string, in which case
+outlet states stay at their program defaults. `HLOG` may also be
+set to one or more *outlet name*=*state* pairs, separated by either
+whitespace or commas. *state* is one of *pass*, *on*, or *off*,
+and *outlet-name* is a string matching `[_a-zA-Z][_a-zA-Z0-9]*`.
+For example, to enable the `tick` outlet and `pbrm` outlets while
+the program `./vfd_swmr_zoo_writer` runs, you can use this command
+in `csh` or Bourne shell:
+
+```
+env HLOG="tick=on pbrm=on" ./vfd_swmr_zoo_writer
+```
+
+# Implementation notes
+
+`hlog_fast(outlet name, format string, ...)` is implemented as a
+macro that only evaluates its format string or other arguments if
+it decides to write the message to `stderr`. `hlog_fast` avoids
+repeatedly walking child-parent links by caching its decision to
+write or discard in the named outlet.
+
+# Future improvements
+
+The timestamp origin is unspecified, now. For the user's convenience,
+the timestamp probably should be measured from `hlog` library
+initialization. Also, `hlog` should provide a routine for setting
+the timestamp origin to the current time.
+
diff --git a/doc/VFD_SWMR_Guide.md b/doc/VFD_SWMR_Guide.md
new file mode 100644
index 0000000..4c70720
--- /dev/null
+++ b/doc/VFD_SWMR_Guide.md
@@ -0,0 +1,24 @@
+# VFD SWMR User's Guide
+
+## Caveats
+
+A few library functions are known to work incorrectly with VFD
+SWMR.
+
+* Variable-length data written using VFD SWMR may be inaccessible
+ or inconsistent to a VFD SWMR reader until the writer closes
+ the file. Instead of the proper variable-length data, a reader may
+ read NULL or arbitrary bytes. Inconsistencies may also cause the
+ reader to crash.
+
+* Applications should take care using HDF5 iteration APIs, especially
+ when iterating large numbers of objects or using long-running
+ application callbacks. While the library is in an iteration routine,
+ it does not track changes made by the writer. If the library spends more than
+ `max_lag` ticks in the routine, then its view of the HDF5 file will become
+ stale. Under those circumstances, HDF5 content could be mis-read, or the
+ library could crash with a diagnostic assertion.
+
+* At the present level of development, the writer cannot invalidate a reader's HDF5 object handles (`hid_t`s). If a reader holds an object open---that is, it has a valid handle (`hid_t`) for the object---while the writer deletes it, then
+ reading content through the handle may yield corrupted data or the data from some
+ other object, or the library may crash.
diff --git a/doc/VFD_SWMR_Punch_List.md b/doc/VFD_SWMR_Punch_List.md
new file mode 100644
index 0000000..5674116
--- /dev/null
+++ b/doc/VFD_SWMR_Punch_List.md
@@ -0,0 +1,192 @@
+VFD SWMR Punch List
+
+<span style="background:green">This text should be on a green background </span>
+
+This document lives at [https://bitbucket.hdfgroup.org/users/dyoung/repos/vchoi_fork/browse/doc/VFD_SWMR_Punch_List.md?at=refs%2Fheads%2Ffeature%2Fvfd_swmr](https://bitbucket.hdfgroup.org/users/dyoung/repos/vchoi_fork/browse/doc/VFD_SWMR_Punch_List.md?at=refs%2Fheads%2Ffeature%2Fvfd_swmr)
+
+An [interactive tutorial](https://www.markdowntutorial.com/) on Markdown
+covers the basics quickly and effectively.
+
+I know I have used this [Markdown "cheat
+sheet"](https://commonmark.org/help/) before---it provides a nice visual
+guide to the syntax.
+
+22 November 2019
+
+1. Design optimizations in index writes. Perhaps write deltas only,
+ with a full index every n ticks?
+
+2. Design work for NFS and object store versions.
+
+3. Design work for journaling variation.
+
+4. **Vailin, complete** Add support for opening multiple files in
+ either VFD SWMR writer or VFD SWMR writer mode. See addition of
+ the EOT queue in section 3.2.2 of the RFC, and related changes in
+ sections 3.3 and 3.3.2.
+
+5. **Vailin, complete** Add the pb\_expansion\_threshold field
+ to the H5F\_vfd\_swmr\_config\_t structure, and update
+ H5Pset\_vfd\_swmr\_config() and H5Pget\_vfd\_swmr\_config()
+ accordingly.
+
+6. **Vailin, in progress** Implement end tick now API call. See section
+ 3.1.2 of the RFC for specifications.
+
+7. **Vailin, in progress** Implement enable / disable EOT call. See
+ section 3.1.3 of the RFC for specifications.
+
+8. Implement option to flush raw data as part of EOT -- write
+ performance test to characterize the performance hit.
+
+ Currently, the raw data is always flushed at EOT. We need a knob
+ that turns on and off the raw-data flush.
+
+9. **Vailin, complete** Move VFD SWMR specific H5F code to its own file
+ -- say H5Fvfd\_swmr.c.
+
+10. Update the new page buffer to support the pb\_expansion\_threshold,
+ and trigger an early end of tick if the threshold is exceeded.
+
+11. **David, complete** Modify metadata file write call to allow the
+ location of the index to float and thus be of arbitrary size.
+
+12. **David, complete** Add code to remove entries from the index after
+ they have been written to the HDF5 file, and have not been modified
+ for at least max\_lag ticks.
+
+13. Tidy short cuts in the initial implementation. These include:
+
+ - **Vailin complete** Odd behavior in the superblock refresh
+ routine (see comments in code). Figure out what is going on, and
+ then either bypass the issue or fix it as seems appropriate.
+
+ - **Vailin complete** Comment H5F\_vfd\_swmr\_config\_t in
+ H5Fpublic.h properly.
+
+ - Cleanup EOA hack in H5FD\_read().
+
+ - **David, complete** Address file open failures in SWMR
+ tests. These appear to be cases in which the writer finishes
+ before the reader opens -- causing the reader to fail as
+ the metadata file no longer exists. If so, handle this more
+ gracefully.
+
+14. Add support for specifying the VFD that sits under the VFD SWMR
+ reader VFD. Probably do this as part of the pluggable VFD design
+ that Jake is working on.
+
+15. Modify the metadata cache so that we don't allocate space for the
+ page hash table unless the file is opened in VFD SWMR reader mode.
+
+16. Implement the logging facility (section 3.14 RFC)
+
+17. **David, probably fixed by various changes since September 2019**
+ Test code to expose existing page buffer bugs and fix same. Note
+ that they seem to appear primarily on Jelly.
+
+ - DLL pre remove sanity check assertion failure
+
+ - vfd\_swmr\_addrem\_writer: H5PB.c:2981:
+ H5PB\_\_mark\_entry\_dirty: Assertion
+ '(entry\_ptr)-\>delay\_write\_until \> (pb\_ptr)-\>cur\_tick'
+ failed.
+
+18. Flesh out designs for unit, integration and performance tests suites
+ as outlined in the RFC. Implement same.
+
+19. **David, complete** Fix memory leak in sparse-reader test.
+
+ David found that the shadow index was leaked by VFD SWMR readers and
+ plugged the leak. Now the sparse reader tests do not use up all of
+ the memory on `jelly`.
+
+20. **David, complete** Test John's patch that repairs the superblock
+ flags mismatch that crashes the reader.
+
+ David found that the patch fixed the demo crashes.
+
+21. Investigate a potential time-of-check, time-of-use race condition
+ involving EOA/EOF and the skip\_read variable in some of the H5PB
+ routines.
+
+ This race condition may only affect raw-data access.
+
+ It may be necessary to poll the superblock for the current EOA. The
+ reader's refresh routine for the superblock should propagate the new
+ EOA to the VFDs.
+
+22. Understand use of H5F\_t on branch feature/vfd\_swmr instead of
+ H5C\_t as on develop branch.
+
+23. *Temporarily* reserve a new superblock flag for VFD SWMR for
+ development purposes.
+
+24. Prior to feature merge, *permanently* reserve a new superblock flag.
+
+25. Revisit the global heap. The global heap receives data types (a form
+ of metadata) and variable-length (VL) data such as strings. It is
+ translated to raw data on its way down the HDF5 software stack. If
+ the global heap is modified/replaced as currently planned, then VFD
+ SWMR does not have to deal with it. However, if the global heap
+ overhaul does not take place, then we have more work to do.
+
+26. **David, complete** Fix the expand/shrink test.
+
+ The test appeared to fail because the dataset extent was enlarged
+ before the data was written, so arbitrary data was present. If a
+ tick snuck in between the `H5Dset_extent` and the `H5Dwrite`, then
+ the reader read the arbitrary data.
+
+ In the `gaussian` test, I have a heuristic that avoids reading
+ arbitrary data. Replicating that in the expand/shrink test has
+ fixed it. Essentially, the reader trails the writer by a bit.
+
+ Ultimately, we should suspend ticks over the H5Dset_extent/H5Dwrite.
+
+27. **Vailin, complete** Change the field name "vfd\_swmr\_writer" to
+ "writer" in "struct H5F_vfd_swmr_config_t" and all references to it.
+ See page 11 in the RFC.
+
+28. **Vailin, complete** Fix bug as stated on page 9 in the RFC section
+ 3.1.1: Given that the VFD SWMR configuration FAPL property is
+ set, the writer field must be consistent with the flags passed in
+ the H5Fopen() (either H5F_ACC_RDWR for the VFD SWMR writer, or
+ H5F_ACC_RDONLY for the VFD SWMR readers).
+
+29. **Vailin, complete** Fix the FSM bug when setting the FSM
+ threshold to a non-default value. The problem was manifested
+ when setting the threshold value in vfd_swmr_generator.c via
+ H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE,
+ 1024 * 1024 * 1024) and then run the vfd_swmr_addrem_writer.c.
+
+30. **David, merged, needs unit test** Add a delay to the FSM so that
+ freed regions are not reused before max\_lag ticks are up.
+
+31. Create a large dataset with a small chunk size to verify that page
+ management in the extensible array is handled properly by VFD SWMR.
+
+32. Make a fixed-size dataset with a small chunk size to verify ... see
+ #31.
+
+33. Make automated tests out of the `credel` and `gaussians` tests.
+
+34. Conserve space in the shadow file. In
+ vfd_swmr_enlarge_shadow_index(), we postpone reclamation of the old
+ shadow index for max\_lag ticks, but it's not necessary to wait that
+ long. David traded some space efficiency for simplicity, for now.
+ See the note in the source code.
+
+35. In the documentation, we should note that if a writer deletes an
+ object that the reader has open (possesses an hid_t), and max_lag
+ ticks later the reader tries to access the object by hid_t, the
+ reader gets an error result, or crashes, or accesses the wrong
+ object.
+
+36. Make variable-length data work. It cannot work in a simple and
+ reliable fashion with VFD SWMR while structural metadata is mixed
+ with user data and stored in raw data pages. For a simple example,
+ see the variable-length string test, `vfd_swmr_vlstr`.
+
+37. Let a writer start writing in VFD SWMR mode after the reader has started
+ reading in VFD SWMR mode. Let the reader start reading right away.
diff --git a/doc/VFD_SWMR_RFC_190902.docx b/doc/VFD_SWMR_RFC_190902.docx
new file mode 100644
index 0000000..384628f
--- /dev/null
+++ b/doc/VFD_SWMR_RFC_190902.docx
Binary files differ
diff --git a/doc/VFD_SWMR_RFC_200107.docx b/doc/VFD_SWMR_RFC_200107.docx
new file mode 100644
index 0000000..4f38846
--- /dev/null
+++ b/doc/VFD_SWMR_RFC_200107.docx
Binary files differ
diff --git a/doc/VFD_SWMR_RFC_2020-02-03.docx b/doc/VFD_SWMR_RFC_2020-02-03.docx
new file mode 100644
index 0000000..ef9a9db
--- /dev/null
+++ b/doc/VFD_SWMR_RFC_2020-02-03.docx
Binary files differ
diff --git a/hl/tools/h5watch/h5watch.c b/hl/tools/h5watch/h5watch.c
index a05c63b..bda0d40 100644
--- a/hl/tools/h5watch/h5watch.c
+++ b/hl/tools/h5watch/h5watch.c
@@ -206,7 +206,7 @@ doprint(hid_t did, hsize_t *start, hsize_t *block, int rank)
info.dset_format = "DSET-%s ";
info.dset_hidefileno = 0;
- info.obj_format = "-%lu:"H5_PRINTF_HADDR_FMT;
+ info.obj_format = "-%lu:%" PRIuHADDR;
info.obj_hidefileno = 0;
info.dset_blockformat_pre = "%sBlk%lu: ";
diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt
index b9641d1..3d662c7 100644
--- a/release_docs/RELEASE.txt
+++ b/release_docs/RELEASE.txt
@@ -724,7 +724,7 @@ Bug Fixes since HDF5-1.10.3 release
performance for the elimination of this assertion and of any complaints
from MPI implementations about the file offsets used being out of order.
- (JTH - 2019/10/07, HDFFV-10792)
+ (JTH - 2019/10/07)
- Fixed the iteration error in test_versionbounds() in test/dtypes.c
diff --git a/src/H5.c b/src/H5.c
index 31b8546..4b9b36c 100644
--- a/src/H5.c
+++ b/src/H5.c
@@ -85,7 +85,6 @@ char H5_lib_vers_info_g[] = H5_VERS_INFO;
static hbool_t H5_dont_atexit_g = FALSE;
H5_debug_t H5_debug_g; /* debugging info */
-
/*******************/
/* Local Variables */
/*******************/
diff --git a/src/H5AC.c b/src/H5AC.c
index 6972a31..9402634 100644
--- a/src/H5AC.c
+++ b/src/H5AC.c
@@ -101,27 +101,29 @@ hbool_t H5_coll_api_sanity_check_g = false;
*/
static const H5AC_class_t *const H5AC_class_s[] = {
- H5AC_BT, /* ( 0) B-tree nodes */
- H5AC_SNODE, /* ( 1) symbol table nodes */
- H5AC_LHEAP_PRFX, /* ( 2) local heap prefix */
- H5AC_LHEAP_DBLK, /* ( 3) local heap data block */
- H5AC_GHEAP, /* ( 4) global heap */
- H5AC_OHDR, /* ( 5) object header */
- H5AC_OHDR_CHK, /* ( 6) object header chunk */
- H5AC_BT2_HDR, /* ( 7) v2 B-tree header */
- H5AC_BT2_INT, /* ( 8) v2 B-tree internal node */
- H5AC_BT2_LEAF, /* ( 9) v2 B-tree leaf node */
- H5AC_FHEAP_HDR, /* (10) fractal heap header */
- H5AC_FHEAP_DBLOCK, /* (11) fractal heap direct block */
- H5AC_FHEAP_IBLOCK, /* (12) fractal heap indirect block */
- H5AC_FSPACE_HDR, /* (13) free space header */
- H5AC_FSPACE_SINFO, /* (14) free space sections */
- H5AC_SOHM_TABLE, /* (15) shared object header message master table */
- H5AC_SOHM_LIST, /* (16) shared message index stored as a list */
- H5AC_EARRAY_HDR, /* (17) extensible array header */
- H5AC_EARRAY_IBLOCK, /* (18) extensible array index block */
- H5AC_EARRAY_SBLOCK, /* (19) extensible array super block */
- H5AC_EARRAY_DBLOCK, /* (20) extensible array data block */
+ H5AC_BT, /* ( 0) B-tree nodes */
+ H5AC_SNODE, /* ( 1) symbol table nodes */
+ H5AC_LHEAP_PRFX, /* ( 2) local heap prefix */
+ H5AC_LHEAP_DBLK, /* ( 3) local heap data block */
+ H5AC_GHEAP, /* ( 4) global heap */
+ H5AC_OHDR, /* ( 5) object header */
+ H5AC_OHDR_CHK, /* ( 6) object header chunk */
+ H5AC_BT2_HDR, /* ( 7) v2 B-tree header */
+ H5AC_BT2_INT, /* ( 8) v2 B-tree internal node */
+ H5AC_BT2_LEAF, /* ( 9) v2 B-tree leaf node */
+ H5AC_FHEAP_HDR, /* (10) fractal heap header */
+ H5AC_FHEAP_DBLOCK, /* (11) fractal heap direct block */
+ H5AC_FHEAP_IBLOCK, /* (12) fractal heap indirect block */
+ H5AC_FSPACE_HDR, /* (13) free space header */
+ H5AC_FSPACE_SINFO, /* (14) free space sections */
+ H5AC_SOHM_TABLE, /* (15) shared object header message */
+ /* master table */
+ H5AC_SOHM_LIST, /* (16) shared message index stored as */
+ /* a list */
+ H5AC_EARRAY_HDR, /* (17) extensible array header */
+ H5AC_EARRAY_IBLOCK, /* (18) extensible array index block */
+ H5AC_EARRAY_SBLOCK, /* (19) extensible array super block */
+ H5AC_EARRAY_DBLOCK, /* (20) extensible array data block */
H5AC_EARRAY_DBLK_PAGE, /* (21) extensible array data block page */
H5AC_FARRAY_HDR, /* (22) fixed array header */
H5AC_FARRAY_DBLOCK, /* (23) fixed array data block */
@@ -278,6 +280,11 @@ H5AC_cache_image_pending(const H5F_t *f)
* matzke@llnl.gov
* Jul 9 1997
*
+ * Changes: Added code to configrue the metadata cache for VFD SWMR
+ * reader operations when indicated.
+ *
+ * JRM -- 1/15/19
+ *
*-------------------------------------------------------------------------
*/
herr_t
@@ -416,6 +423,23 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr, H5AC_cache_image_co
if(H5C_log_set_up(f->shared->cache, H5F_MDC_LOG_LOCATION(f), H5C_LOG_STYLE_JSON, H5F_START_MDC_LOG_ON_ACCESS(f)) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_LOGGING, FAIL, "mdc logging setup failed")
+ /* Configure the metadata cache for VFD SWMR reader operation if
+ * specified.
+ */
+ if ( ( H5F_VFD_SWMR_CONFIG(f) ) &&
+ ( !f->shared->vfd_swmr_config.writer ) ) {
+
+ HDassert(!(H5F_INTENT(f) & H5F_ACC_RDWR));
+ HDassert(f->shared->fs_page_size > 0);
+
+ if ( H5C_set_vfd_swmr_reader(f->shared->cache, TRUE,
+ f->shared->fs_page_size) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTSET, FAIL, \
+ "can't configure MDC for VFD SWMR reader operations");
+
+ }
+
/* Set the cache parameters */
if(H5AC_set_cache_auto_resize_config(f->shared->cache, config_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTSET, FAIL, "auto resize configuration failed")
@@ -2310,7 +2334,7 @@ done:
*------------------------------------------------------------------------------
*/
herr_t
-H5AC_expunge_tag_type_metadata(H5F_t *f, haddr_t tag, int type_id, unsigned flags)
+H5AC_expunge_tag_type_metadata(H5F_t *f, haddr_t tag, int type_id, unsigned flags, hbool_t type_match)
{
/* Variable Declarations */
herr_t ret_value = SUCCEED;
@@ -2323,7 +2347,7 @@ H5AC_expunge_tag_type_metadata(H5F_t *f, haddr_t tag, int type_id, unsigned flag
HDassert(f->shared);
/* Call cache level function to expunge entries with specified tag and type id */
- if(H5C_expunge_tag_type_metadata(f, tag, type_id, flags) < 0)
+ if(H5C_expunge_tag_type_metadata(f, tag, type_id, flags, type_match)<0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Cannot expunge tagged type entries")
done:
diff --git a/src/H5ACmpio.c b/src/H5ACmpio.c
index f097e83..824475d 100644
--- a/src/H5ACmpio.c
+++ b/src/H5ACmpio.c
@@ -498,7 +498,7 @@ done:
*-------------------------------------------------------------------------
*/
static herr_t
-H5AC__construct_candidate_list(H5AC_t *cache_ptr, H5AC_aux_t H5_ATTR_NDEBUG_UNUSED *aux_ptr,
+H5AC__construct_candidate_list(H5AC_t *cache_ptr, H5AC_aux_t H5_ATTR_SANITY_CHECK *aux_ptr,
int sync_point_op)
{
herr_t ret_value = SUCCEED; /* Return value */
diff --git a/src/H5ACprivate.h b/src/H5ACprivate.h
index b932e16..7010a60 100644
--- a/src/H5ACprivate.h
+++ b/src/H5ACprivate.h
@@ -439,7 +439,7 @@ H5_DLL void H5AC_set_ring(H5AC_ring_t ring, H5AC_ring_t *orig_ring);
H5_DLL herr_t H5AC_unsettle_entry_ring(void *entry);
H5_DLL herr_t H5AC_unsettle_ring(H5F_t * f, H5AC_ring_t ring);
H5_DLL herr_t H5AC_expunge_tag_type_metadata(H5F_t *f, haddr_t tag, int type_id,
- unsigned flags);
+ unsigned flags, hbool_t type_match);
H5_DLL herr_t H5AC_get_tag(const void *thing, /*OUT*/ haddr_t *tag);
/* Virtual entry routines */
diff --git a/src/H5ACproxy_entry.c b/src/H5ACproxy_entry.c
index 498d023..1302b83 100644
--- a/src/H5ACproxy_entry.c
+++ b/src/H5ACproxy_entry.c
@@ -82,6 +82,7 @@ const H5AC_class_t H5AC_PROXY_ENTRY[1] = {{
H5AC__proxy_entry_notify, /* 'notify' callback */
H5AC__proxy_entry_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
diff --git a/src/H5B2cache.c b/src/H5B2cache.c
index 80cb6c5..5b0b8b2 100644
--- a/src/H5B2cache.c
+++ b/src/H5B2cache.c
@@ -113,6 +113,7 @@ const H5AC_class_t H5AC_BT2_HDR[1] = {{
H5B2__cache_hdr_notify, /* 'notify' callback */
H5B2__cache_hdr_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5B2 inherits cache-like properties from H5AC */
@@ -131,6 +132,7 @@ const H5AC_class_t H5AC_BT2_INT[1] = {{
H5B2__cache_int_notify, /* 'notify' callback */
H5B2__cache_int_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5B2 inherits cache-like properties from H5AC */
@@ -149,6 +151,7 @@ const H5AC_class_t H5AC_BT2_LEAF[1] = {{
H5B2__cache_leaf_notify, /* 'notify' callback */
H5B2__cache_leaf_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
diff --git a/src/H5Bcache.c b/src/H5Bcache.c
index c2c7a80..24a6716 100644
--- a/src/H5Bcache.c
+++ b/src/H5Bcache.c
@@ -82,6 +82,7 @@ const H5AC_class_t H5AC_BT[1] = {{
NULL, /* 'notify' callback */
H5B__cache_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/*******************/
diff --git a/src/H5C.c b/src/H5C.c
index 91e4158..948e781 100644
--- a/src/H5C.c
+++ b/src/H5C.c
@@ -80,6 +80,7 @@
/* Headers */
/***********/
#include "H5private.h" /* Generic Functions */
+#include "H5retry_private.h" /* Retry loops. */
#include "H5Cpkg.h" /* Cache */
#include "H5CXprivate.h" /* API Contexts */
#include "H5Eprivate.h" /* Error handling */
@@ -320,14 +321,22 @@ H5C_create(size_t max_cache_size,
cache_ptr->slist_ring_size[i] = (size_t)0;
} /* end for */
- for(i = 0; i < H5C__HASH_TABLE_LEN; i++)
+ for(i = 0; i < H5C__HASH_TABLE_LEN; i++) {
(cache_ptr->index)[i] = NULL;
+ }
cache_ptr->il_len = 0;
cache_ptr->il_size = (size_t)0;
cache_ptr->il_head = NULL;
cache_ptr->il_tail = NULL;
+ /* Fields supporting VFD SWMR */
+ cache_ptr->vfd_swmr_reader = FALSE;
+ for(i = 0; i < H5C__PAGE_HASH_TABLE_LEN; i++) {
+ (cache_ptr->page_index)[i] = NULL;
+ }
+ cache_ptr->page_size = 0;
+
/* Tagging Field Initializations */
cache_ptr->ignore_tags = FALSE;
cache_ptr->num_objs_corked = 0;
@@ -737,8 +746,9 @@ herr_t
H5C_prep_for_file_close(H5F_t *f)
{
H5C_t * cache_ptr;
- hbool_t image_generated = FALSE; /* Whether a cache image was generated */
- herr_t ret_value = SUCCEED; /* Return value */
+ hbool_t image_generated = FALSE; /* Whether a cache image was */
+ /* generated */
+ herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
@@ -919,6 +929,396 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5C_evict_or_refresh_all_entries_in_page
+ *
+ * Purpose: When a file is opened in VFD SWMR reader mode, we must be
+ * able to ensure that the metadata cache contains no stale
+ * entries at the end of each tick.
+ *
+ * To do this, we must identify pages that have changed in
+ * the last tick, and either evict, or refresh all modified
+ * entries in the modified pages. If an evicted entry is
+ * needed subsequently, it must be reloaded, almost always
+ * from the metadata file.
+ *
+ * This function performs this function of a given page buffer
+ * page.
+ *
+ * This is done by mapping the supplied page to associated
+ * hash bucket in the page_index, and then scanning the
+ * contents of the bucket for entries residing in the
+ * target page.
+ *
+ * For each such entry, we test to see if it is pinned.
+ * If it is not, we simply evict it.
+ *
+ * Pinned entries may in turn be divided into tagged and
+ * un-tagged entries.
+ *
+ * For pinned tagged entries, it would be best if we could
+ * simply tell the associated cache client to refresh it.
+ * However, until we have that facility, we look up its tag,
+ * and evict all entries associated with that on disk object.
+ *
+ * For pinned, un-tagged entries (i.e. super block, global
+ * heaps, etc. we must instruct the client to refresh the
+ * entry. Fortunately, this is only necessary for the
+ * super block in the initial VFD SWMR implementation.
+ *
+ * Note that there is also the possibility that while the
+ * page was modified, one or more metadata entries in
+ * that page were not. Eventually we should write code
+ * to detect this -- but not for the prototype.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 12/16/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_evict_or_refresh_all_entries_in_page(H5F_t * f, uint64_t page,
+ uint32_t length, uint64_t tick)
+{
+ int i;
+ size_t image_len;
+ size_t original_image_len;
+ void * image_ptr = NULL;
+ void * new_image_ptr = NULL;
+ unsigned flush_flags = (H5C__FLUSH_INVALIDATE_FLAG |
+ H5C__FLUSH_CLEAR_ONLY_FLAG);
+ haddr_t tag;
+ H5C_t * cache_ptr = NULL;
+ H5C_cache_entry_t * entry_ptr;
+ H5C_cache_entry_t * follow_ptr = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
+ bool found = false;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(f->shared);
+
+ cache_ptr = f->shared->cache;
+
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->vfd_swmr_reader);
+
+#if 0 /* JRM */
+ HDfprintf(stderr,
+ "H5C_evict_or_refresh_all_entries_in_page() entering. page = %lld\n",
+ page);
+#endif /* JRM */
+
+ /* since file must be opened R/O for a VFD SWMR reader, the skip
+ * list must be empty. Verify this.
+ */
+ HDassert(cache_ptr->slist_len == 0);
+
+ i = H5C__PI_HASH_FCN(page);
+
+ entry_ptr = (cache_ptr->page_index)[i];
+
+ while (entry_ptr) {
+
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ if ( entry_ptr->page == page ) {
+
+ HDassert(entry_ptr->addr >= (haddr_t)(page * cache_ptr->page_size));
+ HDassert(entry_ptr->addr <
+ (haddr_t)((page+1) * cache_ptr->page_size));
+ HDassert(length == cache_ptr->page_size ||
+ page * cache_ptr->page_size + length <=
+ entry_ptr->addr + entry_ptr->size);
+
+ found = true;
+
+ /* since end of tick occurs only on API call entry in
+ * the VFD SWMR reader case, the entry must not be protected.
+ *
+ * since the VFD SWMR reader must have opened the file R/O,
+ * the entry must be clean.
+ */
+ HDassert(!(entry_ptr->is_protected));
+ HDassert(!(entry_ptr->is_dirty));
+
+ /* we must evict the entry, as page has been modified, and
+ * thus the entry may be out of date.
+ *
+ * Note that we should eventually modify this code to be more
+ * intelligent, and only evict entries if they have in fact changed.
+ * However, no time for that in the first cut.
+ */
+ if ( entry_ptr->is_pinned ) {
+
+ /* if the entry has tag_info and there is no refresh
+ * callback, a call to H5C_evict_tagged_entries() is the
+ * only option available.
+ */
+ if ( ( entry_ptr->tag_info ) &&
+ ( entry_ptr->type->refresh == NULL ) ) {
+
+ tag = entry_ptr->tag_info->tag;
+
+ HDassert(!(entry_ptr->tag_info->corked));
+#if 0 /* JRM */
+ HDfprintf(stderr,
+ "evicting tagged entries addr/page/tag == %lld/%lld/%lld\n",
+ entry_ptr->addr, entry_ptr->page, tag);
+#endif /* JRM */
+
+ /* passing TRUE for the match_global parameter. Look
+ * into this and verify that it is the right thing to
+ * do.
+ */
+ if ( H5C_evict_tagged_entries(f, tag, TRUE) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, \
+ "can't evict pinned and tagged entries")
+
+ /* Both follow_ptr and entry_ptr may have been removed.
+ * Set both to NULL to force the scan to restart.
+ */
+ follow_ptr = entry_ptr = NULL;
+ } else if ( entry_ptr->type->refresh ) {
+#if 0 /* JRM */
+ HDfprintf(stderr, "refreshing addr/page/tag == %lld/%lld\n",
+ entry_ptr->addr, entry_ptr->page);
+#endif /* JRM */
+ /* If there is a refresh callback, use it to minimize
+ * overhead.
+ *
+ * At present, the only refresh call is for the
+ * superblock. This is essential, as the superblock
+ * is manually pinned for as long as the file is open,
+ * and thus cannot be evicted.
+ *
+ * there may be other examples of this, but for the
+ * prototype, we seem to be able to avoid them.
+ */
+
+ /* 1) Get the on disk size of the entry. Since the
+ * the entry is already loaded, we can use the
+ * size listed in the entry.
+ *
+ * This will almost always be correct, but we
+ * allow a second try as it is possible that the
+ * version of the entry may change on the writer.
+ */
+ image_len = entry_ptr->size;
+ original_image_len = image_len;
+
+ /* 2) Allocate and read the buffer.
+ *
+ * Note that this will be satisfied from the metadata
+ * file via the VFD SWMR reade VFD.
+ *
+ * For this reason, we don't nead to check for reads
+ * past the EOA. Torn reads and checksums are also
+ * not an issue, since pages in the metadata file
+ * are checksumed and re-tried if necessary in the
+ * VFD SWMR reader VFD.
+ */
+ if ( NULL == (image_ptr = (uint8_t *)
+ H5MM_malloc(image_len + H5C_IMAGE_EXTRA_SPACE)) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, \
+ "memory allocation failed for image buffer")
+
+#if H5C_DO_MEMORY_SANITY_CHECKS
+ HDmemcpy(image_ptr + image_len, H5C_IMAGE_SANITY_VALUE,
+ H5C_IMAGE_EXTRA_SPACE);
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
+ if ( H5F_block_read(f, entry_ptr->type->mem_type,
+ entry_ptr->addr,
+ image_len, image_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_READERROR, FAIL, \
+ "Can't read image (1)")
+
+ /* 3) Call the refresh callback. If it doesn't
+ * request a different image size, goto 6)
+ */
+ if ( entry_ptr->type->refresh(f, (void *)entry_ptr,
+ image_ptr, &image_len) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, FAIL, \
+ "Can't refresh entry (1)")
+
+ if ( image_len != original_image_len ) {
+
+ /* 4) If image_len has changed, re-allocate and re-read
+ * the image.
+ *
+ * Note: Generate a log entry in this case
+ */
+
+ if ( NULL == (new_image_ptr = H5MM_realloc(image_ptr,
+ image_len + H5C_IMAGE_EXTRA_SPACE)) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, \
+ "re-alloc of image buffer failed.")
+
+ image_ptr = new_image_ptr;
+
+#if H5C_DO_MEMORY_SANITY_CHECKS
+ HDmemcpy(image_ptr + image_len, H5C_IMAGE_SANITY_VALUE,
+ H5C_IMAGE_EXTRA_SPACE);
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
+ if ( H5F_block_read(f, entry_ptr->type->mem_type,
+ entry_ptr->addr,
+ image_len, image_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_READERROR, FAIL, \
+ "Can't read image (2)")
+
+ /* 5) Call the refresh callback again. Requesting
+ * a different buffer size again is an error.
+ */
+ original_image_len = image_len;
+ if ( entry_ptr->type->refresh(f, (void *)entry_ptr,
+ image_ptr,
+ &image_len) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, FAIL, \
+ "Can't refresh entry (2)")
+
+ if ( image_len != original_image_len )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "2nd refresh call changed image_len.")
+ }
+
+ /* 6) Mark the entry as having been looked at this
+ * this tick to accooadate later sanity chackes.
+ */
+ entry_ptr->refreshed_in_tick = tick;
+
+ /* 7) Free the old image if it exists, and replace
+ * it with the new image.
+ */
+ if ( entry_ptr->image_ptr ) {
+
+ entry_ptr->image_ptr = H5MM_xfree(entry_ptr->image_ptr);
+ }
+ entry_ptr->image_ptr = image_ptr;
+
+ /* 8) Since *entry_ptr has been refreshed and not
+ * evicted, we can leave entry_ptr defined, and
+ * and continue the scan of the bucket from
+ * that point.
+ */
+
+ } else {
+
+ /* The entry is pinned, is not tagged, and has no
+ * refresh callback.
+ *
+ * This should be un-reachable. If it is reached, we
+ * probably have another refresh callback to write.
+ */
+ HDassert(FALSE);
+ }
+ } else { /* simply evict the entry */
+
+ /* since the entry is clean, it must not be on the
+ * skip list -- thus no need for the
+ * H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG.
+ */
+#if 0 /* JRM */
+ if ( entry_ptr->tag_info ) {
+
+ HDfprintf(stderr,
+ "evicting entry addr/page/tag == %lld/%lld/%lld\n",
+ entry_ptr->addr, entry_ptr->page,
+ entry_ptr->tag_info->tag);
+ } else {
+ HDfprintf(stderr,
+ "evicting entry addr/page == %lld/%lld no tag\n",
+ entry_ptr->addr, entry_ptr->page);
+ }
+#endif /* JRM */
+ if ( H5C__flush_single_entry(f, entry_ptr, flush_flags) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, \
+ "can't evict unpinned entry")
+
+ /* *entry_ptr should be evicted -- set entry_ptr to NULL */
+ entry_ptr = NULL;
+ }
+
+ /* If entry_ptr is NULL, it was evicted, and we must continue
+ * the scan from follow_ptr, or start at the head of the
+ * bucket list it follow_ptr is NULL as well.
+ *
+ * If follow_ptr isn't NULL, set entry_ptr to follow_ptr->pi_next.
+ * Otherwise, set entry_ptr to point to the first item in the hash
+ * bucket.
+ */
+ if ( entry_ptr ) {
+
+ /* *entry_ptr was refreshed, not evicted. Continue the
+ * the scan from that point, and update follow_ptr.
+ */
+ follow_ptr = entry_ptr;
+ entry_ptr = entry_ptr->pi_next;
+
+ } else if ( follow_ptr ) {
+
+ /* *entry_ptr was evicted. Since follow_ptr is not NULL,
+ * we can continue the scan from that point.
+ */
+ entry_ptr = follow_ptr->pi_next;
+
+ } else {
+
+ /* follow_ptr is null as well, so we have to re-start
+ * the scan from the head of the page index bucket list.
+ */
+
+ entry_ptr = (cache_ptr->page_index)[i];
+ }
+ } else {
+
+ /* entry belongs to another page -- skip it and go on. */
+ follow_ptr = entry_ptr;
+ entry_ptr = entry_ptr->pi_next;
+ }
+ } /* end while */
+
+ /* at this point, all entries residing in the target page should have
+ * been either evicted or refreshed -- verify this.
+ */
+ entry_ptr = (cache_ptr->page_index)[i];
+
+ while (entry_ptr) {
+
+ HDassert((entry_ptr->page != page) ||
+ (entry_ptr->refreshed_in_tick == tick));;
+
+ entry_ptr = entry_ptr->pi_next;
+ }
+
+ if (!found) {
+ hlog_fast(mdc_invalidation, "no MDC match for page %" PRIu64
+ " length %" PRIu32 " tick %" PRIu64, page, length, tick);
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C_evict_or_refresh_all_entries_in_page() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5C_expunge_entry
*
* Purpose: Use this function to tell the cache to expunge an entry
@@ -937,7 +1337,8 @@ H5C_expunge_entry(H5F_t *f, const H5C_class_t *type, haddr_t addr, unsigned flag
{
H5C_t * cache_ptr;
H5C_cache_entry_t * entry_ptr = NULL;
- unsigned flush_flags = (H5C__FLUSH_INVALIDATE_FLAG | H5C__FLUSH_CLEAR_ONLY_FLAG);
+ unsigned flush_flags = (H5C__FLUSH_INVALIDATE_FLAG |
+ H5C__FLUSH_CLEAR_ONLY_FLAG);
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
@@ -1249,7 +1650,8 @@ H5C_insert_entry(H5F_t * f,
hbool_t insert_pinned;
hbool_t flush_last;
#ifdef H5_HAVE_PARALLEL
- hbool_t coll_access = FALSE; /* whether access to the cache entry is done collectively */
+ hbool_t coll_access = FALSE; /* whether access to the cache */
+ /* entry is done collectively */
#endif /* H5_HAVE_PARALLEL */
hbool_t set_flush_marker;
hbool_t write_permitted = TRUE;
@@ -1268,6 +1670,11 @@ H5C_insert_entry(H5F_t * f,
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
+
+ /* if this is a VFD SWMR reader, verify that the page size is defined */
+ HDassert( ( ! cache_ptr->vfd_swmr_reader ) ||
+ ( cache_ptr->page_size > 0 ) );
+
HDassert( type );
HDassert( type->mem_type == cache_ptr->class_table_ptr[type->id]->mem_type );
HDassert( type->image_len );
@@ -1372,25 +1779,39 @@ H5C_insert_entry(H5F_t * f,
#endif /* H5_HAVE_PARALLEL */
/* initialize cache image related fields */
- entry_ptr->include_in_image = FALSE;
- entry_ptr->lru_rank = 0;
- entry_ptr->image_dirty = FALSE;
- entry_ptr->fd_parent_count = 0;
- entry_ptr->fd_parent_addrs = NULL;
- entry_ptr->fd_child_count = 0;
- entry_ptr->fd_dirty_child_count = 0;
- entry_ptr->image_fd_height = 0;
- entry_ptr->prefetched = FALSE;
- entry_ptr->prefetch_type_id = 0;
- entry_ptr->age = 0;
- entry_ptr->prefetched_dirty = FALSE;
+ entry_ptr->include_in_image = FALSE;
+ entry_ptr->lru_rank = 0;
+ entry_ptr->image_dirty = FALSE;
+ entry_ptr->fd_parent_count = 0;
+ entry_ptr->fd_parent_addrs = NULL;
+ entry_ptr->fd_child_count = 0;
+ entry_ptr->fd_dirty_child_count = 0;
+ entry_ptr->image_fd_height = 0;
+ entry_ptr->prefetched = FALSE;
+ entry_ptr->prefetch_type_id = 0;
+ entry_ptr->age = 0;
+ entry_ptr->prefetched_dirty = FALSE;
#ifndef NDEBUG /* debugging field */
- entry_ptr->serialization_count = 0;
+ entry_ptr->serialization_count = 0;
#endif /* NDEBUG */
- entry_ptr->tl_next = NULL;
- entry_ptr->tl_prev = NULL;
- entry_ptr->tag_info = NULL;
+ /* initialize tag list fields */
+ entry_ptr->tl_next = NULL;
+ entry_ptr->tl_prev = NULL;
+ entry_ptr->tag_info = NULL;
+
+ /* initialize fields supporting VFD SWMR */
+ if ( cache_ptr->vfd_swmr_reader ) {
+
+ entry_ptr->page = (addr / cache_ptr->page_size);
+
+ } else {
+
+ entry_ptr->page = 0;
+ }
+ entry_ptr->refreshed_in_tick = 0;
+ entry_ptr->pi_next = NULL;
+ entry_ptr->pi_prev = NULL;
/* Apply tag to newly inserted entry */
if(H5C__tag_entry(cache_ptr, entry_ptr) < 0)
@@ -1399,36 +1820,60 @@ H5C_insert_entry(H5F_t * f,
H5C__RESET_CACHE_ENTRY_STATS(entry_ptr)
- if(cache_ptr->flash_size_increase_possible &&
- (entry_ptr->size > cache_ptr->flash_size_increase_threshold))
- if(H5C__flash_increase_cache_size(cache_ptr, 0, entry_ptr->size) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C__flash_increase_cache_size failed")
+ if ( cache_ptr->flash_size_increase_possible &&
+ ( entry_ptr->size > cache_ptr->flash_size_increase_threshold ) ) {
+
+ if ( H5C__flash_increase_cache_size(cache_ptr, 0, entry_ptr->size) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \
+ "H5C__flash_increase_cache_size failed")
+ }
+
+ if(cache_ptr->index_size >= cache_ptr->max_cache_size) {
- if(cache_ptr->index_size >= cache_ptr->max_cache_size)
empty_space = 0;
- else
+
+ } else {
+
empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
+ }
- if(cache_ptr->evictions_enabled &&
- (((cache_ptr->index_size + entry_ptr->size) > cache_ptr->max_cache_size)
+ if ( ( cache_ptr->evictions_enabled ) &&
+ ( ( (cache_ptr->index_size + entry_ptr->size) >
+ cache_ptr->max_cache_size
+ )
||
- (((empty_space + cache_ptr->clean_index_size) < cache_ptr->min_clean_size)))) {
+ ( (empty_space + cache_ptr->clean_index_size) <
+ cache_ptr->min_clean_size
+ )
+ )
+ ) {
size_t space_needed;
- if(empty_space <= entry_ptr->size)
+ if ( empty_space <= entry_ptr->size ) {
+
cache_ptr->cache_full = TRUE;
+ }
+
+ if ( cache_ptr->check_write_permitted != NULL ) {
+
+ if ( ( cache_ptr->check_write_permitted)(f, &write_permitted) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \
+ "Can't get write_permitted")
+
+ } else {
- if(cache_ptr->check_write_permitted != NULL) {
- if((cache_ptr->check_write_permitted)(f, &write_permitted) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "Can't get write_permitted")
- } /* end if */
- else
write_permitted = cache_ptr->write_permitted;
+ }
HDassert(entry_ptr->size <= H5C_MAX_ENTRY_SIZE);
space_needed = entry_ptr->size;
- if(space_needed > cache_ptr->max_cache_size)
+
+ if ( space_needed > cache_ptr->max_cache_size ) {
+
space_needed = cache_ptr->max_cache_size;
+ }
/* Note that space_needed is just the amount of space that
* needed to insert the new entry without exceeding the cache
@@ -1455,8 +1900,10 @@ H5C_insert_entry(H5F_t * f,
* no point in worrying about the third.
*/
- if(H5C__make_space_in_cache(f, space_needed, write_permitted) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C__make_space_in_cache failed")
+ if ( H5C__make_space_in_cache(f, space_needed, write_permitted) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, \
+ "H5C__make_space_in_cache failed")
} /* end if */
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL)
@@ -1471,30 +1918,65 @@ H5C_insert_entry(H5F_t * f,
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed just before done")
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "an extreme sanity check failed just before done")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/* If the entry's type has a 'notify' callback send a 'after insertion'
* notice now that the entry is fully integrated into the cache.
*/
- if(entry_ptr->type->notify &&
- (entry_ptr->type->notify)(H5C_NOTIFY_ACTION_AFTER_INSERT, entry_ptr) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry inserted into cache")
+ if ( ( entry_ptr->type->notify ) &&
+ ( (entry_ptr->type->notify)(H5C_NOTIFY_ACTION_AFTER_INSERT,
+ entry_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, \
+ "can't notify client about entry inserted into cache")
H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr)
#ifdef H5_HAVE_PARALLEL
- if(H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI))
- coll_access = H5CX_get_coll_metadata_read();
+ if(H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI)) {
+
+ coll_access = (H5P_USER_TRUE == f->coll_md_read ? TRUE : FALSE);
+
+ /* If not explicitly disabled, get the cmdr setting from the
+ * API context
+ */
+ if(!coll_access && H5P_FORCE_FALSE != f->coll_md_read) {
+
+ coll_access = H5CX_get_coll_metadata_read();
+ }
+ } /* end if */
entry_ptr->coll_access = coll_access;
+
if(coll_access) {
H5C__INSERT_IN_COLL_LIST(cache_ptr, entry_ptr, FAIL)
- /* Make sure the size of the collective entries in the cache remain in check */
- if(cache_ptr->max_cache_size * 80 < cache_ptr->coll_list_size * 100)
- if(H5C_clear_coll_entries(cache_ptr, TRUE) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "can't clear collective metadata entries")
+ /* Make sure the size of the collective entries in the cache
+ * remain in check
+ */
+ if(H5P_USER_TRUE == f->coll_md_read) {
+
+ if ( cache_ptr->max_cache_size * 80 <
+ cache_ptr->coll_list_size * 100) {
+
+ if(H5C_clear_coll_entries(cache_ptr, TRUE) < 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "can't clear collective metadata entries")
+ } /* end if */
+ } /* end if */
+ else {
+ if ( cache_ptr->max_cache_size * 40 <
+ cache_ptr->coll_list_size * 100) {
+
+ if(H5C_clear_coll_entries(cache_ptr, TRUE) < 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "can't clear collective metadata entries")
+ } /* end if */
+ } /* end else */
} /* end if */
#endif
@@ -1503,14 +1985,17 @@ done:
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
- HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
if(ret_value < 0 && entry_tagged)
if(H5C__untag_entry(cache_ptr, entry_ptr) < 0)
- HDONE_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry from tag list")
+ HDONE_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, \
+ "can't remove entry from tag list")
FUNC_LEAVE_NOAPI(ret_value)
+
} /* H5C_insert_entry() */
@@ -1806,6 +2291,10 @@ done:
* Programmer: John Mainzer
* 6/2/04
*
+ * Changes: Added code to update cache entry page field required
+ * by VFD SWMR.
+ * JRM -- 12/13/18
+ *
*-------------------------------------------------------------------------
*/
herr_t
@@ -1816,29 +2305,38 @@ H5C_move_entry(H5C_t * cache_ptr,
{
H5C_cache_entry_t * entry_ptr = NULL;
H5C_cache_entry_t * test_entry_ptr = NULL;
- herr_t ret_value = SUCCEED; /* Return value */
+ herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ /* if this is a VFD SWMR reader, verify that the page size is defined */
+ HDassert(( ! cache_ptr->vfd_swmr_reader ) ||
+ ( cache_ptr->page_size > 0 ) );
+
HDassert(type);
HDassert(H5F_addr_defined(old_addr));
HDassert(H5F_addr_defined(new_addr));
HDassert(H5F_addr_ne(old_addr, new_addr));
#if H5C_DO_EXTREME_SANITY_CHECKS
- if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
- (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
- (H5C_validate_lru_list(cache_ptr) < 0))
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
+ if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
+ ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
+ ( H5C_validate_lru_list(cache_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
H5C__SEARCH_INDEX(cache_ptr, old_addr, entry_ptr, FAIL)
- if(entry_ptr == NULL || entry_ptr->type != type)
+ if ( ( entry_ptr == NULL ) || ( entry_ptr->type != type ) ) {
+
/* the old item doesn't exist in the cache, so we are done. */
HGOTO_DONE(SUCCEED)
+ }
HDassert(entry_ptr->addr == old_addr);
HDassert(entry_ptr->type == type);
@@ -1847,16 +2345,21 @@ H5C_move_entry(H5C_t * cache_ptr,
/* (Moving a R/O entry would mark it dirty, which shouldn't
* happen. QAK - 2016/12/02)
*/
- if(entry_ptr->is_read_only)
+ if ( entry_ptr->is_read_only )
HGOTO_ERROR(H5E_CACHE, H5E_CANTMOVE, FAIL, "can't move R/O entry")
H5C__SEARCH_INDEX(cache_ptr, new_addr, test_entry_ptr, FAIL)
- if(test_entry_ptr != NULL) { /* we are hosed */
- if(test_entry_ptr->type == type)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTMOVE, FAIL, "target already moved & reinserted???")
+ if ( test_entry_ptr != NULL ) { /* we are hosed */
+
+ if ( test_entry_ptr->type == type )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMOVE, FAIL, \
+ "target already moved & reinserted???")
else
- HGOTO_ERROR(H5E_CACHE, H5E_CANTMOVE, FAIL, "new address already in use?")
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMOVE, FAIL, \
+ "new address already in use?")
} /* end if */
/* If we get this far we have work to do. Remove *entry_ptr from
@@ -1874,10 +2377,12 @@ H5C_move_entry(H5C_t * cache_ptr,
* change the addr. If the entry is only in the process of being flushed,
* don't mark it as dirty either, lest we confuse the flush call back.
*/
- if(!entry_ptr->destroy_in_progress) {
+ if ( ! entry_ptr->destroy_in_progress ) {
+
H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr, FAIL)
- if(entry_ptr->in_slist) {
+ if ( entry_ptr->in_slist ) {
+
HDassert(cache_ptr->slist_ptr);
H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE)
} /* end if */
@@ -1885,8 +2390,18 @@ H5C_move_entry(H5C_t * cache_ptr,
entry_ptr->addr = new_addr;
- if(!entry_ptr->destroy_in_progress) {
- hbool_t was_dirty; /* Whether the entry was previously dirty */
+ /* update the page in which the entry resides if the file is opened
+ * as a VFD SWMR reader.
+ */
+ if ( cache_ptr->vfd_swmr_reader ) {
+
+ entry_ptr->page = (new_addr / cache_ptr->page_size);
+
+ }
+
+ if ( ! entry_ptr->destroy_in_progress ) {
+
+ hbool_t was_dirty; /* Whether the entry was previously dirty */
/* Remember previous dirty status */
was_dirty = entry_ptr->is_dirty;
@@ -1895,11 +2410,17 @@ H5C_move_entry(H5C_t * cache_ptr,
entry_ptr->is_dirty = TRUE;
/* This shouldn't be needed, but it keeps the test code happy */
- if(entry_ptr->image_up_to_date) {
+ if ( entry_ptr->image_up_to_date ) {
+
entry_ptr->image_up_to_date = FALSE;
- if(entry_ptr->flush_dep_nparents > 0)
- if(H5C__mark_flush_dep_unserialized(entry_ptr) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate serialization status to fd parents")
+
+ if ( entry_ptr->flush_dep_nparents > 0 ) {
+
+ if ( H5C__mark_flush_dep_unserialized(entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, \
+ "Can't propagate serialization status to fd parents")
+ }
} /* end if */
/* Modify cache data structures */
@@ -1907,23 +2428,35 @@ H5C_move_entry(H5C_t * cache_ptr,
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
/* Skip some actions if we're in the middle of flushing the entry */
- if(!entry_ptr->flush_in_progress) {
+ if ( !entry_ptr->flush_in_progress ) {
+
/* Update the replacement policy for the entry */
H5C__UPDATE_RP_FOR_MOVE(cache_ptr, entry_ptr, was_dirty, FAIL)
/* Check for entry changing status and do notifications, etc. */
if(!was_dirty) {
- /* If the entry's type has a 'notify' callback send a 'entry dirtied'
- * notice now that the entry is fully integrated into the cache.
+
+ /* If the entry's type has a 'notify' callback send a 'entry
+ * dirtied' notice now that the entry is fully integrated
+ * into the cache.
*/
- if(entry_ptr->type->notify &&
- (entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_DIRTIED, entry_ptr) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag set")
+ if ( ( entry_ptr->type->notify ) &&
+ ( (entry_ptr->type->notify)
+ (H5C_NOTIFY_ACTION_ENTRY_DIRTIED, entry_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, \
+ "can't notify client about entry dirty flag set")
- /* Propagate the dirty flag up the flush dependency chain if appropriate */
- if(entry_ptr->flush_dep_nparents > 0)
- if(H5C__mark_flush_dep_dirty(entry_ptr) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, "Can't propagate flush dep dirty flag")
+ /* Propagate the dirty flag up the flush dependency chain
+ * if appropriate
+ */
+ if ( entry_ptr->flush_dep_nparents > 0 ) {
+
+ if ( H5C__mark_flush_dep_dirty(entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, \
+ "Can't propagate flush dep dirty flag")
+ }
} /* end if */
} /* end if */
} /* end if */
@@ -1931,14 +2464,18 @@ H5C_move_entry(H5C_t * cache_ptr,
H5C__UPDATE_STATS_FOR_MOVE(cache_ptr, entry_ptr)
done:
+
#if H5C_DO_EXTREME_SANITY_CHECKS
- if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
- (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
- (H5C_validate_lru_list(cache_ptr) < 0))
- HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
+ if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
+ ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
+ ( H5C_validate_lru_list(cache_ptr) < 0 ) )
+
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
+
} /* H5C_move_entry() */
@@ -2879,6 +3416,43 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5C_set_vfd_swmr_reader()
+ *
+ * Purpose: Set cache_ptr->vfd_swmr_reader and cache_ptr->page_size to
+ * the values specified in the parameter list.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 1/15/19
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_set_vfd_swmr_reader(H5C_t *cache_ptr, hbool_t vfd_swmr_reader,
+ hsize_t page_size)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ if((cache_ptr == NULL) || (cache_ptr->magic != H5C__H5C_T_MAGIC))
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry")
+
+ cache_ptr->vfd_swmr_reader = vfd_swmr_reader;
+ cache_ptr->page_size = page_size;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5C_set_vfd_swmr_reader() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5C_unpin_entry()
*
* Purpose: Unpin a cache entry. The entry can be either protected or
@@ -6387,14 +6961,24 @@ H5C__flush_single_entry(H5F_t *f, H5C_cache_entry_t *entry_ptr, unsigned flags)
/* Check if we have to update the page buffer with cleared entries
* so it doesn't go out of date
*/
+
+ /* VFD SWMR TODO: Think on this, and decide if we need to extend
+ * this for multi page metadata entries.
+ */
if(update_page_buffer) {
/* Sanity check */
HDassert(!destroy);
HDassert(entry_ptr->image_ptr);
- if(f->shared->page_buf && f->shared->page_buf->page_size >= entry_ptr->size)
- if(H5PB_update_entry(f->shared->page_buf, entry_ptr->addr, entry_ptr->size, entry_ptr->image_ptr) > 0)
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Failed to update PB with metadata cache")
+ if ( ( f->shared->pb_ptr ) &&
+ ( f->shared->pb_ptr->page_size >= entry_ptr->size ) ) {
+
+ if ( H5PB_update_entry(f->shared->pb_ptr, entry_ptr->addr,
+ entry_ptr->size, entry_ptr->image_ptr) > 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "Failed to update PB with metadata cache")
+ }
} /* end if */
if(cache_ptr->log_flush)
@@ -6491,6 +7075,14 @@ done:
*
* Programmer: John Mainzer, 5/18/04
*
+ * Changes: Please maintain the change list and do not delete entries
+ * unless the have been folded into the header comment.
+ *
+ * Reverted optimization that avoided re-reading the prefix
+ * of a metadata entry when a speculative read proved too
+ * small.
+ * JRM -- 3/25/20
+ *
*-------------------------------------------------------------------------
*/
static void *
@@ -6502,17 +7094,22 @@ H5C_load_entry(H5F_t * f,
haddr_t addr,
void * udata)
{
- hbool_t dirty = FALSE; /* Flag indicating whether thing was dirtied during deserialize */
- uint8_t * image = NULL; /* Buffer for disk image */
- void * thing = NULL; /* Pointer to thing loaded */
- H5C_cache_entry_t *entry = NULL; /* Alias for thing loaded, as cache entry */
- size_t len; /* Size of image in file */
+ hbool_t dirty = FALSE; /* Flag indicating whether thing */
+ /* was dirtied during deserialize */
+ uint8_t * image = NULL; /* Buffer for disk image */
+ void * thing = NULL; /* Pointer to thing loaded */
+ H5C_cache_entry_t *entry = NULL; /* Alias for thing loaded, as */
+ /* cache entry */
+#if 0
+ size_t init_len;
+#endif
+ size_t len; /* Size of image in file */
#ifdef H5_HAVE_PARALLEL
- int mpi_rank = 0; /* MPI process rank */
- MPI_Comm comm = MPI_COMM_NULL; /* File MPI Communicator */
- int mpi_code; /* MPI error code */
+ int mpi_rank = 0; /* MPI process rank */
+ MPI_Comm comm = MPI_COMM_NULL; /* File MPI Communicator */
+ int mpi_code; /* MPI error code */
#endif /* H5_HAVE_PARALLEL */
- void * ret_value = NULL; /* Return value */
+ void * ret_value = NULL; /* Return value */
FUNC_ENTER_NOAPI_NOINIT
@@ -6520,13 +7117,25 @@ H5C_load_entry(H5F_t * f,
HDassert(f);
HDassert(f->shared);
HDassert(f->shared->cache);
+ HDassert(f->shared->cache->magic == H5C__H5C_T_MAGIC );
+
+ /* if this is a VFD SWMR reader, verify that the page size is defined */
+ HDassert( ( ! f->shared->cache->vfd_swmr_reader ) ||
+ ( f->shared->cache->page_size > 0 ) );
+
HDassert(type);
HDassert(H5F_addr_defined(addr));
HDassert(type->get_initial_load_size);
- if(type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG)
+
+ if ( type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG ) {
+
HDassert(type->get_final_load_size);
- else
+
+ } else {
+
HDassert(NULL == type->get_final_load_size);
+ }
+
HDassert(type->deserialize);
/* Can't see how skip reads could be usefully combined with
@@ -6535,44 +7144,64 @@ H5C_load_entry(H5F_t * f,
HDassert(!((type->flags & H5C__CLASS_SKIP_READS) &&
(type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG)));
- /* Call the get_initial_load_size callback, to retrieve the initial size of image */
- if(type->get_initial_load_size(udata, &len) < 0)
+ /* Call the get_initial_load_size callback, to retrieve the initial
+ * size of image
+ */
+ if ( type->get_initial_load_size(udata, &len) < 0 )
+
HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, NULL, "can't retrieve image size")
+
HDassert(len > 0);
+#if 0
+ init_len = len;
+#endif
+
/* Check for possible speculative read off the end of the file */
- if(type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG)
- if(H5C__verify_len_eoa(f, type, addr, &len, FALSE) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "invalid len with respect to EOA")
+ if ( type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG ) {
+
+ if ( H5C__verify_len_eoa(f, type, addr, &len, FALSE) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, \
+ "invalid len with respect to EOA")
+ }
/* Allocate the buffer for reading the on-disk entry image */
- if(NULL == (image = (uint8_t *)H5MM_malloc(len + H5C_IMAGE_EXTRA_SPACE)))
- HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "memory allocation failed for on disk image buffer")
+ if ( NULL == (image = (uint8_t *)H5MM_malloc(len + H5C_IMAGE_EXTRA_SPACE)) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, \
+ "memory allocation failed for on disk image buffer")
+
#if H5C_DO_MEMORY_SANITY_CHECKS
H5MM_memcpy(image + len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
#ifdef H5_HAVE_PARALLEL
- if(H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI)) {
- if((mpi_rank = H5F_mpi_get_rank(f)) < 0)
+ if ( H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI) ) {
+
+ if ( (mpi_rank = H5F_mpi_get_rank(f)) < 0 )
+
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "Can't get MPI rank")
- if((comm = H5F_mpi_get_comm(f)) == MPI_COMM_NULL)
+
+ if ( (comm = H5F_mpi_get_comm(f)) == MPI_COMM_NULL )
+
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "get_comm request failed")
+
} /* end if */
#endif /* H5_HAVE_PARALLEL */
/* Get the on-disk entry image */
- if(0 == (type->flags & H5C__CLASS_SKIP_READS)) {
- unsigned tries, max_tries; /* The # of read attempts */
- unsigned retries; /* The # of retries */
- htri_t chk_ret; /* return from verify_chksum callback */
- size_t actual_len = len; /* The actual length, after speculative reads have been resolved */
- uint64_t nanosec = 1; /* # of nanoseconds to sleep between retries */
- void *new_image; /* Pointer to image */
- hbool_t len_changed = TRUE; /* Whether to re-check speculative entries */
-
- /* Get the # of read attempts */
- max_tries = tries = H5F_GET_READ_ATTEMPTS(f);
+ if ( 0 == (type->flags & H5C__CLASS_SKIP_READS) ) {
+
+ unsigned tries; /* The # of retries */
+ htri_t chk_ret; /* return from verify_chksum callback */
+ size_t actual_len = len; /* The actual length, after speculative */
+ /* reads have been resolved */
+ void *new_image; /* Pointer to image */
+ hbool_t len_changed = TRUE; /* Whether to re-check speculative */
+ /* entries */
+ bool do_try;
+ h5_retry_t retry;
/*
* This do/while loop performs the following till the metadata checksum
@@ -6581,32 +7210,48 @@ H5C_load_entry(H5F_t * f,
* --determine the actual size of the metadata
* --perform checksum verification
*/
- do {
- if(actual_len != len) {
- if(NULL == (new_image = H5MM_realloc(image, len + H5C_IMAGE_EXTRA_SPACE)))
- HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "image null after H5MM_realloc()")
+ for (do_try = h5_retry_init(&retry, H5F_GET_READ_ATTEMPTS(f),
+ 1, H5_RETRY_ONE_HOUR / 3600 / 100);
+ do_try;
+ do_try = h5_retry_next(&retry)) {
+ if ( actual_len != len ) {
+
+ if ( NULL == (new_image = H5MM_realloc(image,
+ len + H5C_IMAGE_EXTRA_SPACE)) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, \
+ "image null after H5MM_realloc()")
+
image = (uint8_t *)new_image;
+
#if H5C_DO_MEMORY_SANITY_CHECKS
- H5MM_memcpy(image + len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
+ H5MM_memcpy(image + len, H5C_IMAGE_SANITY_VALUE,
+ H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
} /* end if */
#ifdef H5_HAVE_PARALLEL
- if(!coll_access || 0 == mpi_rank) {
+ if ( !coll_access || 0 == mpi_rank ) {
#endif /* H5_HAVE_PARALLEL */
- if(H5F_block_read(f, type->mem_type, addr, len, image) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_READERROR, NULL, "Can't read image*")
+
+ if ( H5F_block_read(f, type->mem_type, addr, len, image) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_READERROR, NULL, \
+ "Can't read image*")
#ifdef H5_HAVE_PARALLEL
} /* end if */
/* if the collective metadata read optimization is turned on,
* bcast the metadata read from process 0 to all ranks in the file
* communicator
*/
- if(coll_access) {
+ if ( coll_access ) {
+
int buf_size;
H5_CHECKED_ASSIGN(buf_size, int, len, size_t);
- if(MPI_SUCCESS != (mpi_code = MPI_Bcast(image, buf_size, MPI_BYTE, 0, comm)))
+ if ( MPI_SUCCESS !=
+ (mpi_code = MPI_Bcast(image, buf_size, MPI_BYTE, 0, comm)))
+
HMPI_GOTO_ERROR(NULL, "MPI_Bcast failed", mpi_code)
} /* end if */
#endif /* H5_HAVE_PARALLEL */
@@ -6614,46 +7259,118 @@ H5C_load_entry(H5F_t * f,
/* If the entry could be read speculatively and the length is still
* changing, check for updating the actual size
*/
- if((type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG) && len_changed) {
+ if( ( type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG ) &&
+ ( len_changed ) ) {
+
/* Retrieve the actual length */
actual_len = len;
- if(type->get_final_load_size(image, len, udata, &actual_len) < 0)
- continue; /* Transfer control to while() and count towards retries */
+ if ( type->get_final_load_size(image, len, udata,
+ &actual_len) < 0 ) {
+
+ /* Transfer control to while() and count towards retries */
+ continue;
+ }
/* Check for the length changing */
- if(actual_len != len) {
- /* Verify that the length isn't past the EOA for the file */
- if(H5C__verify_len_eoa(f, type, addr, &actual_len, TRUE) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "actual_len exceeds EOA")
+ if ( actual_len != len ) {
+
+ /* Verify that the length isn't past the EOA for
+ * the file
+ */
+ if ( H5C__verify_len_eoa(f, type, addr,
+ &actual_len, TRUE) < 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, \
+ "actual_len exceeds EOA")
/* Expand buffer to new size */
- if(NULL == (new_image = H5MM_realloc(image, actual_len + H5C_IMAGE_EXTRA_SPACE)))
- HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "image null after H5MM_realloc()")
+ if ( NULL ==
+ (new_image = H5MM_realloc(image,
+ actual_len + H5C_IMAGE_EXTRA_SPACE)))
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, \
+ "image null after H5MM_realloc()")
+
image = (uint8_t *)new_image;
+
#if H5C_DO_MEMORY_SANITY_CHECKS
- H5MM_memcpy(image + actual_len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
+ H5MM_memcpy(image + actual_len, H5C_IMAGE_SANITY_VALUE,
+ H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
- if(actual_len > len) {
+ if ( actual_len > len ) {
#ifdef H5_HAVE_PARALLEL
- if(!coll_access || 0 == mpi_rank) {
+ if ( !coll_access || 0 == mpi_rank ) {
#endif /* H5_HAVE_PARALLEL */
- /* If the thing's image needs to be bigger for a speculatively
- * loaded thing, go get the on-disk image again (the extra portion).
+#if 0 /* JRM */
+ /* If the thing's image needs to be bigger for
+ * a speculatively loaded thing, go get the
+ * on-disk image again (the extra portion).
*/
- if(H5F_block_read(f, type->mem_type, addr + len, actual_len - len, image + len) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't read image")
+ if ( H5F_block_read(f, type->mem_type, addr + len,
+ actual_len - len, image + len) < 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, \
+ "can't read image")
+#else /* JRM */
+
+ /* the original version of this code re-read
+ * the entire buffer. At some point, someone
+ * reworked this code to avoid re-reading the
+ * initial portion of the buffer.
+ *
+ * In addition to being of questionable utility,
+ * this optimization changed the invarient that
+ * that metadata is read and written atomically.
+ * While this didn't cause immediate problems,
+ * the page buffer in VFD SWMR depends on this
+ * invarient in its management of multi-page
+ * metadata entries.
+ *
+ * To repair this issue, I have reverted to
+ * the original algorithm for managing the
+ * speculative load case. Note that I have
+ * done so crudely -- before merge, we should
+ * remove the infrastructure that supports the
+ * optimization.
+ *
+ * We should also verify my impression that the
+ * that the optimization is of no measurable
+ * value. If it is, we will put it back, but
+ * disable it in the VFD SWMR case.
+ *
+ * While this issue was detected in the global
+ * heap case, note that the super bloc, the
+ * local heap, and the fractal heap also use
+ * speculative loads.
+ *
+ * JRM -- 3/24/20
+ */
+ if ( H5F_block_read(f, type->mem_type, addr,
+ actual_len, image) < 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, \
+ "can't read image")
+#endif /* JRM */
#ifdef H5_HAVE_PARALLEL
}
- /* If the collective metadata read optimization is turned on,
- * Bcast the metadata read from process 0 to all ranks in the file
- * communicator */
- if(coll_access) {
+ /* If the collective metadata read optimization is
+ * turned on, Bcast the metadata read from process
+ * 0 to all ranks in the file communicator
+ */
+ if ( coll_access ) {
+
int buf_size;
- H5_CHECKED_ASSIGN(buf_size, int, actual_len - len, size_t);
- if(MPI_SUCCESS != (mpi_code = MPI_Bcast(image + len, buf_size, MPI_BYTE, 0, comm)))
- HMPI_GOTO_ERROR(NULL, "MPI_Bcast failed", mpi_code)
+ H5_CHECKED_ASSIGN(buf_size, int, actual_len - len, \
+ size_t);
+
+ if ( MPI_SUCCESS !=
+ (mpi_code = MPI_Bcast(image + len, buf_size,
+ MPI_BYTE, 0, comm)) )
+
+ HMPI_GOTO_ERROR(NULL, "MPI_Bcast failed", \
+ mpi_code)
} /* end if */
#endif /* H5_HAVE_PARALLEL */
} /* end if */
@@ -6674,28 +7391,48 @@ H5C_load_entry(H5F_t * f,
break;
/* Verify the checksum for the metadata image */
- if((chk_ret = type->verify_chksum(image, actual_len, udata)) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, NULL, "failure from verify_chksum callback")
+ if ( (chk_ret = type->verify_chksum(image, actual_len, udata)) < 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, NULL, \
+ "failure from verify_chksum callback")
+
if(chk_ret == TRUE)
break;
-
- /* Sleep for some time */
- H5_nanosleep(nanosec);
- nanosec *= 2; /* Double the sleep time next time */
- } while(--tries);
+ }
/* Check for too many tries */
- if(tries == 0)
- HGOTO_ERROR(H5E_CACHE, H5E_READERROR, NULL, "incorrect metadatda checksum after all read attempts")
+ if (!do_try) {
+#if 0 /* JRM */
+ haddr_t eoa;
+ int64_t page = (int64_t)(addr / f->shared->cache->page_size);
+
+ eoa = H5F_get_eoa(f, type->mem_type);
+
+ HDfprintf(stderr, "addr = 0x%llx, init_len = %lld, len = %lld\n",
+ (int64_t)addr, (int64_t)init_len, (int64_t)len);
+ HDfprintf(stderr, "type = %s, eoa = 0x%llx, tick = %lld\n",
+ type->name, (int64_t)eoa, f->shared->tick_num);
+ HDfprintf(stderr, "page = %lld, index_len = %d\n",
+ page, f->shared->mdf_idx_entries_used);
+ H5FD_vfd_swmr_dump_status(f->shared->lf, page);
+#endif /* JRM */
+ HGOTO_ERROR(H5E_CACHE, H5E_READERROR, NULL, \
+ "incorrect metadata checksum after all read attempts addr %" PRIuHADDR " size %zu", addr, len);
+ }
/* Calculate and track the # of retries */
- retries = max_tries - tries;
- if(retries) /* Does not track 0 retry */
- if(H5F_track_metadata_read_retries(f, (unsigned)type->mem_type, retries) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "cannot track read tries = %u ", retries)
+ if ((tries = h5_retry_tries(&retry)) > 1) { /* Does not track 0 retry */
+
+ if ( H5F_track_metadata_read_retries(f, (unsigned)type->mem_type,
+ tries - 1) < 0)
+
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, \
+ "cannot track read tries = %u ", tries)
+ }
/* Set the final length (in case it wasn't set earlier) */
len = actual_len;
+
} /* end if !H5C__CLASS_SKIP_READS */
/* Deserialize the on-disk image into the native memory form */
@@ -6733,7 +7470,7 @@ H5C_load_entry(H5F_t * f,
entry->image_ptr = image;
entry->image_up_to_date = !dirty;
entry->type = type;
- entry->is_dirty = dirty;
+ entry->is_dirty = dirty;
entry->dirtied = FALSE;
entry->is_protected = FALSE;
entry->is_read_only = FALSE;
@@ -6793,9 +7530,23 @@ H5C_load_entry(H5F_t * f,
entry->serialization_count = 0;
#endif /* NDEBUG */
- entry->tl_next = NULL;
- entry->tl_prev = NULL;
- entry->tag_info = NULL;
+ /* initialize tag list fields */
+ entry->tl_next = NULL;
+ entry->tl_prev = NULL;
+ entry->tag_info = NULL;
+
+ /* initialize fields supporting VFD SWMR */
+ if ( f->shared->cache->vfd_swmr_reader ) {
+
+ entry->page = (addr / f->shared->cache->page_size);
+
+ } else {
+
+ entry->page = 0;
+ }
+ entry->refreshed_in_tick = 0;
+ entry->pi_next = NULL;
+ entry->pi_prev = NULL;
H5C__RESET_CACHE_ENTRY_STATS(entry);
@@ -8500,6 +9251,11 @@ done:
* Programmer: Mohamad Chaarawi
* 2/10/16
*
+ * Changes: Added code to update the page field in the VFD SWMR reader
+ * case.
+ *
+ * JRM -- 12/14/18
+ *
*-------------------------------------------------------------------------
*/
herr_t
@@ -8517,6 +9273,11 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr)
HDassert(f);
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ /* if this is a VFD SWMR reader, verify that the page size is defined */
+ HDassert( ( ! cache_ptr->vfd_swmr_reader ) ||
+ ( cache_ptr->page_size > 0 ) );
+
HDassert(entry_ptr);
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(!entry_ptr->image_up_to_date);
@@ -8534,10 +9295,14 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to pre-serialize entry")
/* Check for any flags set in the pre-serialize callback */
- if(serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET) {
+ if ( serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET ) {
+
/* Check for unexpected flags from serialize callback */
- if(serialize_flags & ~(H5C__SERIALIZE_RESIZED_FLAG | H5C__SERIALIZE_MOVED_FLAG))
- HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unknown serialize flag(s)")
+ if ( serialize_flags & ~(H5C__SERIALIZE_RESIZED_FLAG |
+ H5C__SERIALIZE_MOVED_FLAG) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unknown serialize flag(s)")
#ifdef H5_HAVE_PARALLEL
/* In the parallel case, resizes and moves in
@@ -8566,28 +9331,40 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr)
* If that ceases to be the case, further
* tests will be necessary.
*/
- if(cache_ptr->aux_ptr != NULL)
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "resize/move in serialize occurred in parallel case")
+ if ( cache_ptr->aux_ptr != NULL )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "resize/move in serialize occurred in parallel case")
#endif
/* If required, resize the buffer and update the entry and the cache
- * data structures */
- if(serialize_flags & H5C__SERIALIZE_RESIZED_FLAG) {
+ * data structures
+ */
+ if ( serialize_flags & H5C__SERIALIZE_RESIZED_FLAG ) {
+
/* Sanity check */
HDassert(new_len > 0);
/* Allocate a new image buffer */
- if(NULL == (entry_ptr->image_ptr = H5MM_realloc(entry_ptr->image_ptr, new_len + H5C_IMAGE_EXTRA_SPACE)))
- HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer")
+ if ( NULL == (entry_ptr->image_ptr =
+ H5MM_realloc(entry_ptr->image_ptr,
+ new_len + H5C_IMAGE_EXTRA_SPACE)) )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, \
+ "memory allocation failed for on disk image buffer")
+
#if H5C_DO_MEMORY_SANITY_CHECKS
- H5MM_memcpy(((uint8_t *)entry_ptr->image_ptr) + new_len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
+ H5MM_memcpy(((uint8_t *)entry_ptr->image_ptr) + new_len,
+ H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
/* Update statistics for resizing the entry */
- H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_len);
+ H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, \
+ new_len);
/* Update the hash table for the size change */
- H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len, entry_ptr, !(entry_ptr->is_dirty));
+ H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, \
+ new_len, entry_ptr, !(entry_ptr->is_dirty));
/* The entry can't be protected since we are in the process of
* flushing it. Thus we must update the replacement policy data
@@ -8602,21 +9379,25 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr)
*/
HDassert(entry_ptr->is_dirty);
HDassert(entry_ptr->in_slist);
- H5C__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len);
+ H5C__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, \
+ new_len);
/* Finally, update the entry for its new size */
entry_ptr->size = new_len;
+
} /* end if */
/* If required, udate the entry and the cache data structures
* for a move
*/
- if(serialize_flags & H5C__SERIALIZE_MOVED_FLAG) {
+ if ( serialize_flags & H5C__SERIALIZE_MOVED_FLAG ) {
+
/* Update stats and entries relocated counter */
H5C__UPDATE_STATS_FOR_MOVE(cache_ptr, entry_ptr)
/* We must update cache data structures for the change in address */
if(entry_ptr->addr == old_addr) {
+
/* Delete the entry from the hash table and the slist */
H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr, FAIL);
H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE);
@@ -8624,21 +9405,37 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr)
/* Update the entry for its new address */
entry_ptr->addr = new_addr;
+ /* In the VFD SWMR reader case, update the entry page field */
+ if ( cache_ptr->vfd_swmr_reader ) {
+
+ entry_ptr->page = (new_addr / cache_ptr->page_size);
+ }
+
/* And then reinsert in the index and slist */
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL);
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL);
- } /* end if */
- else /* move is already done for us -- just do sanity checks */
+
+ } else { /* move is already done for us -- just do sanity checks */
+
HDassert(entry_ptr->addr == new_addr);
+ HDassert(( ! cache_ptr->vfd_swmr_reader ) ||
+ ( entry_ptr->page ==
+ (entry_ptr->addr / cache_ptr->page_size) ));
+ }
} /* end if */
} /* end if(serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET) */
/* Serialize object into buffer */
- if(entry_ptr->type->serialize(f, entry_ptr->image_ptr, entry_ptr->size, (void *)entry_ptr) < 0)
+ if ( entry_ptr->type->serialize(f, entry_ptr->image_ptr, entry_ptr->size,
+ (void *)entry_ptr) < 0)
+
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to serialize entry")
+
#if H5C_DO_MEMORY_SANITY_CHECKS
- HDassert(0 == HDmemcmp(((uint8_t *)entry_ptr->image_ptr) + entry_ptr->size, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE));
+ HDassert(0 == HDmemcmp(((uint8_t *)entry_ptr->image_ptr) + entry_ptr->size,
+ H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE));
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
entry_ptr->image_up_to_date = TRUE;
/* Propagate the fact that the entry is serialized up the
@@ -8648,12 +9445,19 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr)
* for flush dependency parents.
*/
HDassert(entry_ptr->flush_dep_nunser_children == 0);
- if(entry_ptr->flush_dep_nparents > 0)
- if(H5C__mark_flush_dep_serialized(entry_ptr) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate serialization status to fd parents")
+
+ if ( entry_ptr->flush_dep_nparents > 0 ) {
+
+ if ( H5C__mark_flush_dep_serialized(entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, \
+ "Can't propagate serialization status to fd parents")
+ }
done:
+
FUNC_LEAVE_NOAPI(ret_value)
+
} /* H5C__generate_image */
diff --git a/src/H5Cdbg.c b/src/H5Cdbg.c
index d5599f2..749d49e 100644
--- a/src/H5Cdbg.c
+++ b/src/H5Cdbg.c
@@ -289,8 +289,8 @@ H5C_dump_cache_skip_list(H5C_t * cache_ptr, char * calling_fcn)
HDassert(calling_fcn != NULL);
HDfprintf(stdout, "\n\nDumping metadata cache skip list from %s.\n", calling_fcn);
- HDfprintf(stdout, " slist len = %u.\n", cache_ptr->slist_len);
- HDfprintf(stdout, " slist size = %lld.\n", (long long)(cache_ptr->slist_size));
+ HDfprintf(stdout, " slist len = %" PRIu32 ".\n", cache_ptr->slist_len);
+ HDfprintf(stdout, " slist size = %zu.\n", cache_ptr->slist_size);
if(cache_ptr->slist_len > 0) {
/* If we get this far, all entries in the cache are listed in the
@@ -310,10 +310,10 @@ H5C_dump_cache_skip_list(H5C_t * cache_ptr, char * calling_fcn)
HDassert( entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC );
HDfprintf(stdout,
- "%s%d 0x%016llx %4lld %d/%d %d %s\n",
+ "%s%d 0x%016" PRIxHADDR " %4zu %d/%d %d %s\n",
cache_ptr->prefix, i,
- (long long)(entry_ptr->addr),
- (long long)(entry_ptr->size),
+ entry_ptr->addr,
+ entry_ptr->size,
(int)(entry_ptr->is_protected),
(int)(entry_ptr->is_pinned),
(int)(entry_ptr->is_dirty),
@@ -408,10 +408,10 @@ H5C_dump_coll_write_list(H5C_t * cache_ptr, char * calling_fcn)
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDfprintf(stdout,
- "%s%d 0x%016llx %4lld %d/%d %d %s\n",
+ "%s%d 0x%016" PRIxHADDR " %4%zu %d/%d %d %s\n",
cache_ptr->prefix, i,
- (long long)(entry_ptr->addr),
- (long long)(entry_ptr->size),
+ entry_ptr->addr,
+ entry_ptr->size,
(int)(entry_ptr->is_protected),
(int)(entry_ptr->is_pinned),
(int)(entry_ptr->is_dirty),
diff --git a/src/H5Cepoch.c b/src/H5Cepoch.c
index 6451019..e6a395f 100644
--- a/src/H5Cepoch.c
+++ b/src/H5Cepoch.c
@@ -91,20 +91,21 @@ static herr_t H5C__epoch_marker_fsf_size(const void H5_ATTR_UNUSED * thing,
const H5AC_class_t H5AC_EPOCH_MARKER[1] = {{
- /* id = */ H5AC_EPOCH_MARKER_ID,
- /* name = */ "epoch marker",
- /* mem_type = */ H5FD_MEM_DEFAULT, /* value doesn't matter */
- /* flags = */ H5AC__CLASS_NO_FLAGS_SET,
+ /* id = */ H5AC_EPOCH_MARKER_ID,
+ /* name = */ "epoch marker",
+ /* mem_type = */ H5FD_MEM_DEFAULT, /* value doesn't matter */
+ /* flags = */ H5AC__CLASS_NO_FLAGS_SET,
/* get_initial_load_size = */ H5C__epoch_marker_get_initial_load_size,
- /* get_final_load_size = */ H5C__epoch_marker_get_final_load_size,
- /* verify_chksum = */ H5C__epoch_marker_verify_chksum,
- /* deserialize = */ H5C__epoch_marker_deserialize,
- /* image_len = */ H5C__epoch_marker_image_len,
- /* pre_serialize = */ H5C__epoch_marker_pre_serialize,
- /* serialize = */ H5C__epoch_marker_serialize,
- /* notify = */ H5C__epoch_marker_notify,
- /* free_icr = */ H5C__epoch_marker_free_icr,
- /* fsf_size = */ H5C__epoch_marker_fsf_size,
+ /* get_final_load_size = */ H5C__epoch_marker_get_final_load_size,
+ /* verify_chksum = */ H5C__epoch_marker_verify_chksum,
+ /* deserialize = */ H5C__epoch_marker_deserialize,
+ /* image_len = */ H5C__epoch_marker_image_len,
+ /* pre_serialize = */ H5C__epoch_marker_pre_serialize,
+ /* serialize = */ H5C__epoch_marker_serialize,
+ /* notify = */ H5C__epoch_marker_notify,
+ /* free_icr = */ H5C__epoch_marker_free_icr,
+ /* fsf_size = */ H5C__epoch_marker_fsf_size,
+ /* refresh = */ NULL,
}};
diff --git a/src/H5Cpkg.h b/src/H5Cpkg.h
index 8712af5..d9a1641 100644
--- a/src/H5Cpkg.h
+++ b/src/H5Cpkg.h
@@ -48,8 +48,9 @@
#define H5C__MAX_EPOCH_MARKERS 10
/* Cache configuration settings */
-#define H5C__HASH_TABLE_LEN (64 * 1024) /* must be a power of 2 */
-#define H5C__H5C_T_MAGIC 0x005CAC0E
+#define H5C__HASH_TABLE_LEN (64 * 1024) /* must be a power of 2 */
+#define H5C__PAGE_HASH_TABLE_LEN ( 4 * 1024) /* must be a poser of 2 */
+#define H5C__H5C_T_MAGIC 0x005CAC0E
/* Initial allocated size of the "flush_dep_parent" array */
#define H5C_FLUSH_DEP_PARENT_INIT 8
@@ -977,14 +978,31 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
*
* JRM -- 10/15/15
*
+ * - Updated the existing index macros to maintain a second
+ * hash table when cache_ptr->vfd_swrm_writer is true. This
+ * hash table bins entries by the page buffer page they reside
+ * in, thus facilitating the eviction of entries on a given page
+ * when that page is modified.
+ *
+ * JRM -- 12/14/18
+ *
***********************************************************************/
-/* H5C__HASH_TABLE_LEN is defined in H5Cpkg.h. It mut be a power of two. */
+/* H5C__HASH_TABLE_LEN is defined in H5Cpkg.h. It must be a power of two. */
#define H5C__HASH_MASK ((size_t)(H5C__HASH_TABLE_LEN - 1) << 3)
#define H5C__HASH_FCN(x) (int)((unsigned)((x) & H5C__HASH_MASK) >> 3)
+
+/* H5C__PAGE_HASH_TABLE_LEN is defined in H5Cpkg.h.
+ * It must ve a power of two.
+ */
+#define H5C__PI_HASH_MASK ((uint64_t)(H5C__PAGE_HASH_TABLE_LEN - 1))
+
+#define H5C__PI_HASH_FCN(x) (int)(((uint64_t)(x)) & H5C__PI_HASH_MASK)
+
+
#if H5C_DO_SANITY_CHECKS
#define H5C__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) \
@@ -994,6 +1012,8 @@ if ( ( (cache_ptr) == NULL ) || \
( ! H5F_addr_defined((entry_ptr)->addr) ) || \
( (entry_ptr)->ht_next != NULL ) || \
( (entry_ptr)->ht_prev != NULL ) || \
+ ( (entry_ptr)->pi_next != NULL ) || \
+ ( (entry_ptr)->pi_prev != NULL ) || \
( (entry_ptr)->size <= 0 ) || \
( H5C__HASH_FCN((entry_ptr)->addr) < 0 ) || \
( H5C__HASH_FCN((entry_ptr)->addr) >= H5C__HASH_TABLE_LEN ) || \
@@ -1039,45 +1059,52 @@ if ( ( (cache_ptr) == NULL ) || \
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, "post HT insert SC failed") \
}
-#define H5C__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) \
-if ( ( (cache_ptr) == NULL ) || \
- ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \
- ( (cache_ptr)->index_len < 1 ) || \
- ( (entry_ptr) == NULL ) || \
- ( (cache_ptr)->index_size < (entry_ptr)->size ) || \
- ( ! H5F_addr_defined((entry_ptr)->addr) ) || \
- ( (entry_ptr)->size <= 0 ) || \
- ( H5C__HASH_FCN((entry_ptr)->addr) < 0 ) || \
- ( H5C__HASH_FCN((entry_ptr)->addr) >= H5C__HASH_TABLE_LEN ) || \
- ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] \
- == NULL ) || \
- ( ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] \
- != (entry_ptr) ) && \
- ( (entry_ptr)->ht_prev == NULL ) ) || \
- ( ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] == \
- (entry_ptr) ) && \
- ( (entry_ptr)->ht_prev != NULL ) ) || \
- ( (cache_ptr)->index_size != \
- ((cache_ptr)->clean_index_size + \
- (cache_ptr)->dirty_index_size) ) || \
- ( (cache_ptr)->index_size < ((cache_ptr)->clean_index_size) ) || \
- ( (cache_ptr)->index_size < ((cache_ptr)->dirty_index_size) ) || \
- ( (entry_ptr)->ring <= H5C_RING_UNDEFINED ) || \
- ( (entry_ptr)->ring >= H5C_RING_NTYPES ) || \
- ( (cache_ptr)->index_ring_len[(entry_ptr)->ring] <= 0 ) || \
- ( (cache_ptr)->index_ring_len[(entry_ptr)->ring] > \
- (cache_ptr)->index_len ) || \
- ( (cache_ptr)->index_ring_size[(entry_ptr)->ring] < \
- (entry_ptr)->size ) || \
- ( (cache_ptr)->index_ring_size[(entry_ptr)->ring] > \
- (cache_ptr)->index_size ) || \
- ( (cache_ptr)->index_ring_size[(entry_ptr)->ring] != \
- ((cache_ptr)->clean_index_ring_size[(entry_ptr)->ring] + \
- (cache_ptr)->dirty_index_ring_size[(entry_ptr)->ring]) ) || \
- ( (cache_ptr)->index_len != (cache_ptr)->il_len ) || \
- ( (cache_ptr)->index_size != (cache_ptr)->il_size ) ) { \
- HDassert(FALSE); \
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "pre HT remove SC failed") \
+#define H5C__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) \
+if ( ( (cache_ptr) == NULL ) || \
+ ( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \
+ ( (cache_ptr)->index_len < 1 ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (cache_ptr)->index_size < (entry_ptr)->size ) || \
+ ( ! H5F_addr_defined((entry_ptr)->addr) ) || \
+ ( (entry_ptr)->size <= 0 ) || \
+ ( H5C__HASH_FCN((entry_ptr)->addr) < 0 ) || \
+ ( H5C__HASH_FCN((entry_ptr)->addr) >= H5C__HASH_TABLE_LEN ) || \
+ ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] \
+ == NULL ) || \
+ ( ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] \
+ != (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev == NULL ) ) || \
+ ( ( ((cache_ptr)->index)[(H5C__HASH_FCN((entry_ptr)->addr))] == \
+ (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev != NULL ) ) || \
+ ( (cache_ptr)->index_size != \
+ ((cache_ptr)->clean_index_size + \
+ (cache_ptr)->dirty_index_size) ) || \
+ ( ( (cache_ptr)->vfd_swmr_reader ) && \
+ ( ( ( (cache_ptr)->page_index[(H5C__PI_HASH_FCN((entry_ptr)->page))] \
+ != (entry_ptr) ) && \
+ ( (entry_ptr)->pi_prev == NULL ) ) || \
+ ( ( (cache_ptr)->page_index[(H5C__PI_HASH_FCN((entry_ptr)->page))] \
+ == (entry_ptr) ) && \
+ ( (entry_ptr)->pi_prev != NULL ) ) ) ) || \
+ ( (cache_ptr)->index_size < ((cache_ptr)->clean_index_size) ) || \
+ ( (cache_ptr)->index_size < ((cache_ptr)->dirty_index_size) ) || \
+ ( (entry_ptr)->ring <= H5C_RING_UNDEFINED ) || \
+ ( (entry_ptr)->ring >= H5C_RING_NTYPES ) || \
+ ( (cache_ptr)->index_ring_len[(entry_ptr)->ring] <= 0 ) || \
+ ( (cache_ptr)->index_ring_len[(entry_ptr)->ring] > \
+ (cache_ptr)->index_len ) || \
+ ( (cache_ptr)->index_ring_size[(entry_ptr)->ring] < \
+ (entry_ptr)->size ) || \
+ ( (cache_ptr)->index_ring_size[(entry_ptr)->ring] > \
+ (cache_ptr)->index_size ) || \
+ ( (cache_ptr)->index_ring_size[(entry_ptr)->ring] != \
+ ((cache_ptr)->clean_index_ring_size[(entry_ptr)->ring] + \
+ (cache_ptr)->dirty_index_ring_size[(entry_ptr)->ring]) ) || \
+ ( (cache_ptr)->index_len != (cache_ptr)->il_len ) || \
+ ( (cache_ptr)->index_size != (cache_ptr)->il_size ) ) { \
+ HDassert(FALSE && "pre HT remove SC failed"); \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "pre HT remove SC failed") \
}
#define H5C__POST_HT_REMOVE_SC(cache_ptr, entry_ptr) \
@@ -1087,7 +1114,9 @@ if ( ( (cache_ptr) == NULL ) || \
( ! H5F_addr_defined((entry_ptr)->addr) ) || \
( (entry_ptr)->size <= 0 ) || \
( (entry_ptr)->ht_prev != NULL ) || \
- ( (entry_ptr)->ht_prev != NULL ) || \
+ ( (entry_ptr)->ht_next != NULL ) || \
+ ( (entry_ptr)->pi_prev != NULL ) || \
+ ( (entry_ptr)->pi_next != NULL ) || \
( (cache_ptr)->index_size != \
((cache_ptr)->clean_index_size + \
(cache_ptr)->dirty_index_size) ) || \
@@ -1102,7 +1131,7 @@ if ( ( (cache_ptr) == NULL ) || \
(cache_ptr)->dirty_index_ring_size[(entry_ptr)->ring]) ) || \
( (cache_ptr)->index_len != (cache_ptr)->il_len ) || \
( (cache_ptr)->index_size != (cache_ptr)->il_size ) ) { \
- HDassert(FALSE); \
+ HDassert(FALSE && "post HT remove SC failed"); \
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "post HT remove SC failed") \
}
@@ -1118,7 +1147,9 @@ if ( ( (cache_ptr) == NULL ) || \
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, "pre HT search SC failed") \
}
-/* (Keep in sync w/H5C_TEST__POST_SUC_HT_SEARCH_SC macro in test/cache_common.h -QAK) */
+/* (Keep in sync w/H5C_TEST__POST_SUC_HT_SEARCH_SC macro in
+ * test/cache_common.h -QAK)
+ */
#define H5C__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, k, fail_val) \
if ( ( (cache_ptr) == NULL ) || \
( (cache_ptr)->magic != H5C__H5C_T_MAGIC ) || \
@@ -1137,15 +1168,19 @@ if ( ( (cache_ptr) == NULL ) || \
( (entry_ptr)->ht_prev->ht_next != (entry_ptr) ) ) || \
( ( (entry_ptr)->ht_next != NULL ) && \
( (entry_ptr)->ht_next->ht_prev != (entry_ptr) ) ) ) { \
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, "post successful HT search SC failed") \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \
+ "post successful HT search SC failed") \
}
-/* (Keep in sync w/H5C_TEST__POST_HT_SHIFT_TO_FRONT macro in test/cache_common.h -QAK) */
+/* (Keep in sync w/H5C_TEST__POST_HT_SHIFT_TO_FRONT macro in
+ * test/cache_common.h -QAK)
+ */
#define H5C__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k, fail_val) \
if ( ( (cache_ptr) == NULL ) || \
( ((cache_ptr)->index)[k] != (entry_ptr) ) || \
( (entry_ptr)->ht_prev != NULL ) ) { \
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, "post HT shift to front SC failed") \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, fail_val, \
+ "post HT shift to front SC failed") \
}
#define H5C__PRE_HT_ENTRY_SIZE_CHANGE_SC(cache_ptr, old_size, new_size, \
@@ -1180,7 +1215,8 @@ if ( ( (cache_ptr) == NULL ) || \
( (cache_ptr)->index_len != (cache_ptr)->il_len ) || \
( (cache_ptr)->index_size != (cache_ptr)->il_size ) ) { \
HDassert(FALSE); \
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "pre HT entry size change SC failed") \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "pre HT entry size change SC failed") \
}
#define H5C__POST_HT_ENTRY_SIZE_CHANGE_SC(cache_ptr, old_size, new_size, \
@@ -1210,7 +1246,8 @@ if ( ( (cache_ptr) == NULL ) || \
( (cache_ptr)->index_len != (cache_ptr)->il_len ) || \
( (cache_ptr)->index_size != (cache_ptr)->il_size ) ) { \
HDassert(FALSE); \
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "post HT entry size change SC failed") \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "post HT entry size change SC failed") \
}
#define H5C__PRE_HT_UPDATE_FOR_ENTRY_CLEAN_SC(cache_ptr, entry_ptr) \
@@ -1237,7 +1274,8 @@ if ( \
((cache_ptr)->clean_index_ring_size[(entry_ptr)->ring] + \
(cache_ptr)->dirty_index_ring_size[(entry_ptr)->ring]) ) ) { \
HDassert(FALSE); \
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "pre HT update for entry clean SC failed") \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "pre HT update for entry clean SC failed") \
}
#define H5C__PRE_HT_UPDATE_FOR_ENTRY_DIRTY_SC(cache_ptr, entry_ptr) \
@@ -1264,7 +1302,8 @@ if ( \
((cache_ptr)->clean_index_ring_size[(entry_ptr)->ring] + \
(cache_ptr)->dirty_index_ring_size[(entry_ptr)->ring]) ) ) { \
HDassert(FALSE); \
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "pre HT update for entry dirty SC failed") \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "pre HT update for entry dirty SC failed") \
}
#define H5C__POST_HT_UPDATE_FOR_ENTRY_CLEAN_SC(cache_ptr, entry_ptr) \
@@ -1280,7 +1319,8 @@ if ( ( (cache_ptr)->index_size != \
((cache_ptr)->clean_index_ring_size[(entry_ptr)->ring] + \
(cache_ptr)->dirty_index_ring_size[(entry_ptr)->ring]) ) ) { \
HDassert(FALSE); \
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "post HT update for entry clean SC failed") \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "post HT update for entry clean SC failed") \
}
#define H5C__POST_HT_UPDATE_FOR_ENTRY_DIRTY_SC(cache_ptr, entry_ptr) \
@@ -1296,7 +1336,8 @@ if ( ( (cache_ptr)->index_size != \
((cache_ptr)->clean_index_ring_size[(entry_ptr)->ring] + \
(cache_ptr)->dirty_index_ring_size[(entry_ptr)->ring]) ) ) { \
HDassert(FALSE); \
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "post HT update for entry dirty SC failed") \
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
+ "post HT update for entry dirty SC failed") \
}
#else /* H5C_DO_SANITY_CHECKS */
@@ -1324,6 +1365,14 @@ if ( ( (cache_ptr)->index_size != \
{ \
int k; \
H5C__PRE_HT_INSERT_SC(cache_ptr, entry_ptr, fail_val) \
+ if ( cache_ptr->vfd_swmr_reader ) { \
+ k = H5C__PI_HASH_FCN((entry_ptr)->page); \
+ if ( ( (cache_ptr)->page_index)[k] != NULL ) { \
+ (entry_ptr)->pi_next = ((cache_ptr)->page_index)[k]; \
+ (entry_ptr)->pi_next->pi_prev = (entry_ptr); \
+ } \
+ ((cache_ptr)->page_index)[k] = (entry_ptr); \
+ } \
k = H5C__HASH_FCN((entry_ptr)->addr); \
if(((cache_ptr)->index)[k] != NULL) { \
(entry_ptr)->ht_next = ((cache_ptr)->index)[k]; \
@@ -1359,13 +1408,30 @@ if ( ( (cache_ptr)->index_size != \
{ \
int k; \
H5C__PRE_HT_REMOVE_SC(cache_ptr, entry_ptr) \
+ if ( cache_ptr->vfd_swmr_reader ) { \
+ k = H5C__PI_HASH_FCN((entry_ptr)->page); \
+ if ( (entry_ptr)->pi_next ) { \
+ (entry_ptr)->pi_next->pi_prev = (entry_ptr)->pi_prev; \
+ } \
+ if ( (entry_ptr)->pi_prev ) { \
+ (entry_ptr)->pi_prev->pi_next = (entry_ptr)->pi_next; \
+ } \
+ if ( ( (cache_ptr)->page_index)[k] == (entry_ptr) ) { \
+ ((cache_ptr)->page_index)[k] = (entry_ptr)->pi_next; \
+ } \
+ (entry_ptr)->pi_next = NULL; \
+ (entry_ptr)->pi_prev = NULL; \
+ } \
k = H5C__HASH_FCN((entry_ptr)->addr); \
- if((entry_ptr)->ht_next) \
+ if ( (entry_ptr)->ht_next ) { \
(entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \
- if((entry_ptr)->ht_prev) \
+ } \
+ if ( (entry_ptr)->ht_prev ) { \
(entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \
- if(((cache_ptr)->index)[k] == (entry_ptr)) \
+ } \
+ if ( ( (cache_ptr)->index)[k] == (entry_ptr) ) { \
((cache_ptr)->index)[k] = (entry_ptr)->ht_next; \
+ } \
(entry_ptr)->ht_next = NULL; \
(entry_ptr)->ht_prev = NULL; \
(cache_ptr)->index_len--; \
@@ -1373,7 +1439,7 @@ if ( ( (cache_ptr)->index_size != \
((cache_ptr)->index_ring_len[entry_ptr->ring])--; \
((cache_ptr)->index_ring_size[entry_ptr->ring]) \
-= (entry_ptr)->size; \
- if((entry_ptr)->is_dirty) { \
+ if ( (entry_ptr)->is_dirty ) { \
(cache_ptr)->dirty_index_size -= (entry_ptr)->size; \
((cache_ptr)->dirty_index_ring_size[entry_ptr->ring]) \
-= (entry_ptr)->size; \
@@ -1382,7 +1448,7 @@ if ( ( (cache_ptr)->index_size != \
((cache_ptr)->clean_index_ring_size[entry_ptr->ring]) \
-= (entry_ptr)->size; \
} \
- if((entry_ptr)->flush_me_last) { \
+ if ( (entry_ptr)->flush_me_last ) { \
(cache_ptr)->num_last_entries--; \
HDassert((cache_ptr)->num_last_entries <= 1); \
} \
@@ -3690,6 +3756,36 @@ typedef struct H5C_tag_info_t {
*
* This field is NULL if the index is empty.
*
+ * Page Index:
+ *
+ * For the VFD SWMR reader, it is necessary to map modified pages to
+ * entries contained in that page so that they can be invalidated. The
+ * page index is a hash table that provides this service. Note that it
+ * is only maintained for files that are opened in VFD SWMR reader mode.
+ *
+ * Structurally, the page index is identical to the index in the page
+ * buffer. Specifically, it is a hash table with chaining. The hash
+ * table size must be a power of two, not the usual prime number. The
+ * hash function simply clips the high order bits off the page offset
+ * of the entry's base address.
+ *
+ * The page index is maintained by the same macros that maintain the
+ * regular index. As such, it does not require separate length and
+ * size fields, as it shares them with the regular index. Instead,
+ * the only ancilary field needed is the vfd_swrm_reader boolean, which
+ * indicates whether the page index must be maintained.
+ *
+ * vfd_swmr_reader: Boolean flag that is TRUE iff the file has been
+ * opened as a VFD SWMR reader. The remaining fields in
+ * the page index section are valid iff this field is TRUE.
+ *
+ * page_index Array of pointer to H5C_cache_entry_t of size
+ * H5C__PAGE_HASH_TABLE_LEN. This size must be a power of
+ * two, not the usual prime number.
+ *
+ * page_size: Convenience copy of the page size used by the page
+ * buffer.
+ *
*
* With the addition of the take ownership flag, it is possible that
* an entry may be removed from the cache as the result of the flush of
@@ -4679,6 +4775,11 @@ struct H5C_t {
H5C_cache_entry_t * il_head;
H5C_cache_entry_t * il_tail;
+ /* Fields supporting VFD SWMR */
+ hbool_t vfd_swmr_reader;
+ H5C_cache_entry_t * page_index[H5C__PAGE_HASH_TABLE_LEN];
+ hsize_t page_size;
+
/* Fields to detect entries removed during scans */
int64_t entries_removed_counter;
H5C_cache_entry_t * last_entry_removed_ptr;
diff --git a/src/H5Cprefetched.c b/src/H5Cprefetched.c
index 954dd60..0c32fd5 100644
--- a/src/H5Cprefetched.c
+++ b/src/H5Cprefetched.c
@@ -106,6 +106,7 @@ const H5AC_class_t H5AC_PREFETCHED_ENTRY[1] = {{
/* notify = */ H5C__prefetched_entry_notify,
/* free_icr = */ H5C__prefetched_entry_free_icr,
/* fsf_size = */ H5C__prefetched_entry_fsf_size,
+ /* refresh = */ NULL,
}};
diff --git a/src/H5Cprivate.h b/src/H5Cprivate.h
index 0ba0234..23091cb 100644
--- a/src/H5Cprivate.h
+++ b/src/H5Cprivate.h
@@ -384,10 +384,11 @@ typedef struct H5C_t H5C_t;
*
* The typedef for the get_load_size callback is as follows:
*
- * typedef herr_t (*H5C_get_final_load_size_func_t)(const void *image_ptr,
- * size_t image_len,
- * void *udata_ptr,
- * size_t *actual_len_ptr);
+ * typedef
+ * herr_t (*H5C_get_final_load_size_func_t)(const void *image_ptr,
+ * size_t image_len,
+ * void *udata_ptr,
+ * size_t *actual_len_ptr);
*
* The parameters of the get_load_size callback are as follows:
*
@@ -404,7 +405,8 @@ typedef struct H5C_t H5C_t;
* actual_len_ptr: Pointer to the location containing the actual length
* of the metadata entry on disk.
*
- * Processing in the get_final_load_size function should proceed as follows:
+ * Processing in the get_final_load_size function should proceed as
+ * follows:
*
* If successful, the function will place the length in the *actual_len_ptr
* associated with supplied image and/or user data and then return SUCCEED.
@@ -843,6 +845,103 @@ typedef struct H5C_t H5C_t;
* push error information on the error stack with the error API
* routines.
*
+ * REFRESH_ENTRY: Pointer to the refresh entry callback.
+ *
+ * This callback exists to support VFD SWMR readers, and should not
+ * be used outside this context.
+ *
+ * At the end of each tick, the VFD SWMR reader is informed of pages
+ * in the page buffer that have been modified since the last tick.
+ *
+ * To avoid message from the past bugs, it is necessary to either
+ * evict or refresh entries that have been modified in the past tick,
+ * and thus reside in such modified pages.
+ *
+ * To this end, the metadata cache is informed of all such pages,
+ * and must either evict, or update all entries contained in these
+ * pages, or determine that the entry in question has not been modified,
+ * and thus that no action is required.
+ *
+ * If the entry is unpinned, it is possible to simply evict it, and
+ * this is probably the most efficient way to address the issue.
+ *
+ * If the entry is pinned and tagged, it is possible to evict the
+ * entire on disk data structure of which it is part via the evict
+ * tagged entry facility. This is inefficient, but it is simple and
+ * uses existing code -- hence this is plan A for the initial
+ * implementation of VFD SWMR.
+ *
+ * However, there remains the case of the pinned entry that is not
+ * tagged, and thus not subject to eviction via the evict tagged
+ * entries call -- the most important example of this is the super
+ * block which is pinned and may not be evicted until file close.
+ *
+ * Another example is free space manager headers -- however, these
+ * are a non-issue in the context of VFD SWMR readers as such files
+ * must only be opened R/O and thus will not have active free space
+ * managers.
+ *
+ * The refresh entry callback exists to address this issue. As
+ * indicated above, it is essential for the superblock, and desireable
+ * whenever it is not possible to simply evict an entry that resides
+ * in a modified page cache page.
+ *
+ * Functionally, the call is similar to the deserialize call, the
+ * primary difference being that the client receives both a pointer
+ * to the existing entry, and a buffer containing its image. The
+ * client must deserialize this image an update itself as appropriate.
+ *
+ * The typedef for the VFD SWMR refresh callback is as follows:
+ *
+ * typedef void *(*H5C_vfd_swmr_refresh_func_t)(H5F_t * f,
+ * void * entry_ptr,
+ * const void * image_ptr,
+ * size_t * len_ptr);
+ *
+ * The parameters of the deserialize callback are as follows:
+ *
+ * f: Pointer to the containing instance of H5F_t.
+ *
+ * entry_ptr: Pointer to the metadata cache entry that is being
+ * refreshed. This entry is place on the protected list
+ * for the duration of the refresh callback as the client
+ * will typically modify it during the refresh operation.
+ *
+ * image_ptr: Pointer to a buffer of length *len_ptr containing the
+ * most recent version of the entry's on disk image from
+ * the VFD SWMR metadata file. The length of the buffer
+ * is specified in the len parameter below.
+ *
+ * len_ptr: Pointer to size_t containing the length in
+ * bytes of the buffer pointed to by *image_ptr.
+ *
+ * If the supplied buffer is too small, the callback must
+ * place the correct value in *len_ptr and return success.
+ * The metadata cache will read the larger image, and call
+ * the refresh function again.
+ *
+ * Processing in the refresh function should proceed as follows:
+ *
+ * The target entry will be protected for the duration of the
+ * refresh call. This allows entry resizes if necessary, and
+ * prevents re-entrant refresh calls.
+ *
+ * If the supplied image contains valid data, and is of the correct
+ * length, the refresh function must parse it, and apply updates to
+ * the in core representatin of the metadata cache entry as required.
+ * Note that since the file is opened R/O, any updates must not
+ * cause the entry to be marked dirty.
+ *
+ * If the image contains valid data, but is too small, the refresh
+ * callback must copy the correct image length to *len_ptr, and
+ * return success. The metadata cache will make a second call with
+ * the correct image length. If the entry must change size, the
+ * refresh callback must call H5C_resize_entry().
+ *
+ * If the image contains invalid data, or if, for whatever reason,
+ * the refresh function cannot apply its contents, the refresh
+ * function must return failure.
+ *
***************************************************************************/
/* Actions that can be reported to 'notify' client callback */
@@ -861,44 +960,59 @@ typedef enum H5C_notify_action_t {
*/
H5C_NOTIFY_ACTION_ENTRY_DIRTIED, /* Entry has been marked dirty. */
H5C_NOTIFY_ACTION_ENTRY_CLEANED, /* Entry has been marked clean. */
- H5C_NOTIFY_ACTION_CHILD_DIRTIED, /* Dependent child has been marked dirty. */
- H5C_NOTIFY_ACTION_CHILD_CLEANED, /* Dependent child has been marked clean. */
- H5C_NOTIFY_ACTION_CHILD_UNSERIALIZED, /* Dependent child has been marked unserialized. */
- H5C_NOTIFY_ACTION_CHILD_SERIALIZED /* Dependent child has been marked serialized. */
+ H5C_NOTIFY_ACTION_CHILD_DIRTIED, /* Dependent child has been marked
+ * dirty.
+ */
+ H5C_NOTIFY_ACTION_CHILD_CLEANED, /* Dependent child has been marked
+ * clean.
+ */
+ H5C_NOTIFY_ACTION_CHILD_UNSERIALIZED, /* Dependent child has been marked
+ * unserialized.
+ */
+ H5C_NOTIFY_ACTION_CHILD_SERIALIZED /* Dependent child has been marked
+ * serialized.
+ */
} H5C_notify_action_t;
/* Cache client callback function pointers */
-typedef herr_t (*H5C_get_initial_load_size_func_t)(void *udata_ptr, size_t *image_len_ptr);
+typedef herr_t (*H5C_get_initial_load_size_func_t)(void *udata_ptr,
+ size_t *image_len_ptr);
typedef herr_t (*H5C_get_final_load_size_func_t)(const void *image_ptr,
size_t image_len, void *udata_ptr, size_t *actual_len_ptr);
-typedef htri_t (*H5C_verify_chksum_func_t)(const void *image_ptr, size_t len, void *udata_ptr);
+typedef htri_t (*H5C_verify_chksum_func_t)(const void *image_ptr, size_t len,
+ void *udata_ptr);
typedef void *(*H5C_deserialize_func_t)(const void *image_ptr,
size_t len, void *udata_ptr, hbool_t *dirty_ptr);
-typedef herr_t (*H5C_image_len_func_t)(const void *thing, size_t *image_len_ptr);
+typedef herr_t (*H5C_image_len_func_t)(const void *thing,
+ size_t *image_len_ptr);
typedef herr_t (*H5C_pre_serialize_func_t)(H5F_t *f, void *thing, haddr_t addr,
- size_t len, haddr_t *new_addr_ptr, size_t *new_len_ptr, unsigned *flags_ptr);
+ size_t len, haddr_t *new_addr_ptr, size_t *new_len_ptr,
+ unsigned *flags_ptr);
typedef herr_t (*H5C_serialize_func_t)(const H5F_t *f, void *image_ptr,
size_t len, void *thing);
typedef herr_t (*H5C_notify_func_t)(H5C_notify_action_t action, void *thing);
typedef herr_t (*H5C_free_icr_func_t)(void *thing);
typedef herr_t (*H5C_get_fsf_size_t)(const void * thing, hsize_t *fsf_size_ptr);
+typedef herr_t (*H5C_vfd_swmr_refresh_func_t)(H5F_t * f, void * entry_ptr,
+ const void * image_ptr, size_t *len_ptr);
/* Metadata cache client class definition */
typedef struct H5C_class_t {
- int id;
- const char * name;
- H5FD_mem_t mem_type;
- unsigned flags;
+ int id;
+ const char * name;
+ H5FD_mem_t mem_type;
+ unsigned flags;
H5C_get_initial_load_size_func_t get_initial_load_size;
H5C_get_final_load_size_func_t get_final_load_size;
- H5C_verify_chksum_func_t verify_chksum;
- H5C_deserialize_func_t deserialize;
- H5C_image_len_func_t image_len;
- H5C_pre_serialize_func_t pre_serialize;
- H5C_serialize_func_t serialize;
- H5C_notify_func_t notify;
- H5C_free_icr_func_t free_icr;
- H5C_get_fsf_size_t fsf_size;
+ H5C_verify_chksum_func_t verify_chksum;
+ H5C_deserialize_func_t deserialize;
+ H5C_image_len_func_t image_len;
+ H5C_pre_serialize_func_t pre_serialize;
+ H5C_serialize_func_t serialize;
+ H5C_notify_func_t notify;
+ H5C_free_icr_func_t free_icr;
+ H5C_get_fsf_size_t fsf_size;
+ H5C_vfd_swmr_refresh_func_t refresh;
} H5C_class_t;
/* Type definitions of callback functions used by the cache as a whole */
@@ -1574,6 +1688,35 @@ typedef int H5C_ring_t;
* tag_info: Pointer to the common tag state for all entries belonging to
* an object. NULL for untagged entries.
*
+ * Fields supporting VFD SWMR
+ *
+ * The following fields exist to support the page index. These fields are
+ * only defined when the vfd_swmr_reader field in the associated instance of
+ * H5C_t is set to TRUE.
+ *
+ * page: Page offset of the page containing the base address of the
+ * metadata cache entry.
+ *
+ * refreshed_in_tick: When an entry is refreshed as part of the VFD SWMR
+ * reader end of tick processing, this field is used to
+ * record the tick in which this occured. The field is
+ * used primarily for sanity checking.
+ *
+ * pi_next: Next pointer used by the page index hash table that maps
+ * page buffer pages to any metadata cache entries that
+ * reside in the target page.
+ *
+ * This field points to the next entry in the doubly linked
+ * list of entries in the hash bin, or NULL if there is no
+ * next entry.
+ *
+ * pi_prev: Prev pointer used by the page index hash table that maps
+ * page buffer pages to any metadata cache entries that
+ * reside in the target page.
+ *
+ * This field points to the next entry in the doubly linked
+ * list of entries in the hash bin, or NULL if there is no
+ * next entry
*
* Cache entry stats collection fields:
*
@@ -1673,6 +1816,12 @@ typedef struct H5C_cache_entry_t {
struct H5C_cache_entry_t *tl_prev;
struct H5C_tag_info_t *tag_info;
+ /* fields supporting VFD SWMR */
+ uint64_t page;
+ uint64_t refreshed_in_tick;
+ struct H5C_cache_entry_t *pi_next;
+ struct H5C_cache_entry_t *pi_prev;
+
#if H5C_COLLECT_CACHE_ENTRY_STATS
/* cache entry stats fields */
int32_t accesses;
@@ -2239,13 +2388,15 @@ H5_DLL void H5C_def_auto_resize_rpt_fcn(H5C_t *cache_ptr, int32_t version,
size_t old_min_clean_size, size_t new_min_clean_size);
H5_DLL herr_t H5C_dest(H5F_t *f);
H5_DLL herr_t H5C_evict(H5F_t *f);
+H5_DLL herr_t H5C_evict_or_refresh_all_entries_in_page(H5F_t * f, uint64_t page,
+ uint32_t length, uint64_t tick);
H5_DLL herr_t H5C_expunge_entry(H5F_t *f, const H5C_class_t *type, haddr_t addr,
unsigned flags);
H5_DLL herr_t H5C_flush_cache(H5F_t *f, unsigned flags);
H5_DLL herr_t H5C_flush_tagged_entries(H5F_t *f, haddr_t tag);
H5_DLL herr_t H5C_force_cache_image_load(H5F_t * f);
H5_DLL herr_t H5C_evict_tagged_entries(H5F_t *f, haddr_t tag, hbool_t match_global);
-H5_DLL herr_t H5C_expunge_tag_type_metadata(H5F_t *f, haddr_t tag, int type_id, unsigned flags);
+H5_DLL herr_t H5C_expunge_tag_type_metadata(H5F_t *f, haddr_t tag, int type_id, unsigned flags, hbool_t type_match);
H5_DLL herr_t H5C_get_tag(const void *thing, /*OUT*/ haddr_t *tag);
#if H5C_DO_TAGGING_SANITY_CHECKS
herr_t H5C_verify_tag(int id, haddr_t tag);
@@ -2265,7 +2416,8 @@ H5_DLL herr_t H5C_get_entry_status(const H5F_t *f, haddr_t addr,
hbool_t *is_protected_ptr, hbool_t *is_pinned_ptr, hbool_t *is_corked_ptr,
hbool_t *is_flush_dep_parent_ptr, hbool_t *is_flush_dep_child_ptr,
hbool_t *image_up_to_date_ptr);
-H5_DLL herr_t H5C_get_evictions_enabled(const H5C_t *cache_ptr, hbool_t *evictions_enabled_ptr);
+H5_DLL herr_t H5C_get_evictions_enabled(const H5C_t *cache_ptr,
+ hbool_t *evictions_enabled_ptr);
H5_DLL void * H5C_get_aux_ptr(const H5C_t *cache_ptr);
H5_DLL herr_t H5C_image_stats(H5C_t * cache_ptr, hbool_t print_header);
H5_DLL herr_t H5C_insert_entry(H5F_t *f, const H5C_class_t *type, haddr_t addr,
@@ -2280,7 +2432,8 @@ H5_DLL herr_t H5C_move_entry(H5C_t *cache_ptr, const H5C_class_t *type,
haddr_t old_addr, haddr_t new_addr);
H5_DLL herr_t H5C_pin_protected_entry(void *thing);
H5_DLL herr_t H5C_prep_for_file_close(H5F_t *f);
-H5_DLL herr_t H5C_create_flush_dependency(void *parent_thing, void *child_thing);
+H5_DLL herr_t H5C_create_flush_dependency(void *parent_thing,
+ void *child_thing);
H5_DLL void * H5C_protect(H5F_t *f, const H5C_class_t *type, haddr_t addr,
void *udata, unsigned flags);
H5_DLL herr_t H5C_reset_cache_hit_rate_stats(H5C_t *cache_ptr);
@@ -2288,13 +2441,17 @@ H5_DLL herr_t H5C_resize_entry(void *thing, size_t new_size);
H5_DLL herr_t H5C_set_cache_auto_resize_config(H5C_t *cache_ptr, H5C_auto_size_ctl_t *config_ptr);
H5_DLL herr_t H5C_set_cache_image_config(const H5F_t *f, H5C_t *cache_ptr,
H5C_cache_image_ctl_t *config_ptr);
-H5_DLL herr_t H5C_set_evictions_enabled(H5C_t *cache_ptr, hbool_t evictions_enabled);
+H5_DLL herr_t H5C_set_evictions_enabled(H5C_t *cache_ptr,
+ hbool_t evictions_enabled);
+H5_DLL herr_t H5C_set_vfd_swmr_reader(H5C_t *cache_ptr,
+ hbool_t vfd_swmr_reader, hsize_t page_size);
H5_DLL herr_t H5C_set_prefix(H5C_t *cache_ptr, char *prefix);
H5_DLL herr_t H5C_stats(H5C_t *cache_ptr, const char *cache_name,
hbool_t display_detailed_stats);
H5_DLL void H5C_stats__reset(H5C_t *cache_ptr);
H5_DLL herr_t H5C_unpin_entry(void *thing);
-H5_DLL herr_t H5C_destroy_flush_dependency(void *parent_thing, void *child_thing);
+H5_DLL herr_t H5C_destroy_flush_dependency(void *parent_thing,
+ void *child_thing);
H5_DLL herr_t H5C_unprotect(H5F_t *f, haddr_t addr, void *thing,
unsigned int flags);
H5_DLL herr_t H5C_validate_cache_image_config(H5C_cache_image_ctl_t * ctl_ptr);
@@ -2304,15 +2461,18 @@ H5_DLL herr_t H5C_ignore_tags(H5C_t *cache_ptr);
H5_DLL hbool_t H5C_get_ignore_tags(const H5C_t *cache_ptr);
H5_DLL uint32_t H5C_get_num_objs_corked(const H5C_t *cache_ptr);
H5_DLL herr_t H5C_retag_entries(H5C_t * cache_ptr, haddr_t src_tag, haddr_t dest_tag);
-H5_DLL herr_t H5C_cork(H5C_t *cache_ptr, haddr_t obj_addr, unsigned action, hbool_t *corked);
-H5_DLL herr_t H5C_get_entry_ring(const H5F_t *f, haddr_t addr, H5C_ring_t *ring);
+H5_DLL herr_t H5C_cork(H5C_t *cache_ptr, haddr_t obj_addr, unsigned action,
+ hbool_t *corked);
+H5_DLL herr_t H5C_get_entry_ring(const H5F_t *f, haddr_t addr,
+ H5C_ring_t *ring);
H5_DLL herr_t H5C_unsettle_entry_ring(void *thing);
H5_DLL herr_t H5C_unsettle_ring(H5F_t * f, H5C_ring_t ring);
H5_DLL herr_t H5C_remove_entry(void *thing);
H5_DLL herr_t H5C_cache_image_status(H5F_t * f, hbool_t *load_ci_ptr,
hbool_t *write_ci_ptr);
H5_DLL hbool_t H5C_cache_image_pending(const H5C_t *cache_ptr);
-H5_DLL herr_t H5C_get_mdc_image_info(H5C_t *cache_ptr, haddr_t *image_addr, hsize_t *image_len);
+H5_DLL herr_t H5C_get_mdc_image_info(H5C_t *cache_ptr, haddr_t *image_addr,
+ hsize_t *image_len);
/* Logging functions */
H5_DLL herr_t H5C_start_logging(H5C_t *cache);
diff --git a/src/H5Ctag.c b/src/H5Ctag.c
index e92d0e4..2573e93 100644
--- a/src/H5Ctag.c
+++ b/src/H5Ctag.c
@@ -75,6 +75,7 @@ typedef struct {
H5F_t *f; /* File pointer for evicting entry */
int type_id; /* Cache entry type to expunge */
unsigned flags; /* Flags for expunging entry */
+ hbool_t type_match;
} H5C_tag_iter_ettm_ctx_t;
/* Typedef for tagged entry iterator callback context - mark corked */
@@ -837,7 +838,7 @@ H5C__expunge_tag_type_metadata_cb(H5C_cache_entry_t *entry, void *_ctx)
HDassert(ctx);
/* Found one with the same tag and type id */
- if(entry->type->id == ctx->type_id)
+ if(entry->type->id == ctx->type_id || !ctx->type_match)
if(H5C_expunge_entry(ctx->f, entry->type, entry->addr, ctx->flags) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, H5_ITER_ERROR, "can't expunge entry")
@@ -861,7 +862,7 @@ done:
*-------------------------------------------------------------------------
*/
herr_t
-H5C_expunge_tag_type_metadata(H5F_t *f, haddr_t tag, int type_id, unsigned flags)
+H5C_expunge_tag_type_metadata(H5F_t *f, haddr_t tag, int type_id, unsigned flags, hbool_t type_match)
{
H5C_t *cache; /* Pointer to cache structure */
H5C_tag_iter_ettm_ctx_t ctx; /* Context for iterator callback */
@@ -881,6 +882,7 @@ H5C_expunge_tag_type_metadata(H5F_t *f, haddr_t tag, int type_id, unsigned flags
ctx.f = f;
ctx.type_id = type_id;
ctx.flags = flags;
+ ctx.type_match = type_match;
/* Iterate through hash table entries, expunge those with specified tag and type id */
if(H5C__iter_tagged_entries(cache, tag, FALSE, H5C__expunge_tag_type_metadata_cb, &ctx) < 0)
diff --git a/src/H5Dbtree.c b/src/H5Dbtree.c
index 098e01b..7741e99 100644
--- a/src/H5Dbtree.c
+++ b/src/H5Dbtree.c
@@ -166,7 +166,8 @@ const H5D_chunk_ops_t H5D_COPS_BTREE[1] = {{
H5D__btree_idx_size, /* size */
H5D__btree_idx_reset, /* reset */
H5D__btree_idx_dump, /* dump */
- H5D__btree_idx_dest /* destroy */
+ H5D__btree_idx_dest, /* destroy */
+ NULL /* close */
}};
diff --git a/src/H5Dbtree2.c b/src/H5Dbtree2.c
index ccb786b..65a020f 100644
--- a/src/H5Dbtree2.c
+++ b/src/H5Dbtree2.c
@@ -161,7 +161,8 @@ const H5D_chunk_ops_t H5D_COPS_BT2[1] = {{
H5D__bt2_idx_size, /* size */
H5D__bt2_idx_reset, /* reset */
H5D__bt2_idx_dump, /* dump */
- H5D__bt2_idx_dest /* destroy */
+ H5D__bt2_idx_dest, /* destroy */
+ H5D__bt2_idx_dest /* close (same as destroy) */
}};
diff --git a/src/H5Dchunk.c b/src/H5Dchunk.c
index ee83564..7e7d6b4 100644
--- a/src/H5Dchunk.c
+++ b/src/H5Dchunk.c
@@ -267,6 +267,8 @@ static herr_t H5D__chunk_flush(H5D_t *dset);
static herr_t H5D__chunk_io_term(const H5D_chunk_map_t *fm);
static herr_t H5D__chunk_dest(H5D_t *dset);
+static herr_t H5D__chunk_index_close(const H5D_t *, bool);
+
/* Chunk query operation callbacks */
static int H5D__get_num_chunks_cb(const H5D_chunk_rec_t *chunk_rec, void *_udata);
static int H5D__get_chunk_info_cb(const H5D_chunk_rec_t *chunk_rec, void *_udata);
@@ -2626,6 +2628,19 @@ H5D__chunk_read(H5D_io_info_t *io_info, const H5D_type_info_t *type_info,
chunk_node = H5D_CHUNK_GET_NEXT_NODE(fm, chunk_node);
} /* end while */
+ /* Stopgap fix for VFD SWMR: close the chunk index so that
+ * pinned/tagged entries in the metadata cache (MDC) are released.
+ *
+ * Extensible chunked datasets use extensible arrays or btrees as
+ * chunk indices. Open chunk indices leave pinned/tagged entries
+ * in the MDC, and VFD SWMR cannot (yet) evict or refresh those
+ * entries. After we write refresh routines for those entries, this
+ * stopgap fix can go away.
+ */
+ if(H5D__chunk_index_close(io_info->dset, false) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL,
+ "unable to close chunk index")
+
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5D__chunk_read() */
@@ -2883,6 +2898,41 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__chunk_io_term() */
+/* Close the given dataset's chunk index, or destroy it if `destroy`
+ * is true. A closed index merely releases holds on metadata cache
+ * entries; the index can be reopened. Once a dataset's index is
+ * destroyed, however, the dataset must not try to use the index, again.
+ *
+ * A useful side-effect of closing the chunk index is the release
+ * pinned/tagged metadata cache entries connected with the index.
+ */
+static herr_t
+H5D__chunk_index_close(const H5D_t *dset, bool destroy)
+{
+ H5D_chk_idx_info_t idx_info;
+ H5O_storage_chunk_t *sc = &(dset->shared->layout.storage.u.chunk);
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5D_chunk_close_func_t fn;
+
+ FUNC_ENTER_STATIC
+
+ H5D_CHUNK_STORAGE_INDEX_CHK(sc);
+
+ idx_info.f = dset->oloc.file;
+ idx_info.pline = &dset->shared->dcpl_cache.pline;
+ idx_info.layout = &dset->shared->layout.u.chunk;
+ idx_info.storage = sc;
+
+ fn = destroy ? sc->ops->dest : sc->ops->close;
+
+ if (fn != NULL && (*fn)(&idx_info) < 0) {
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL,
+ "unable to release chunk index info")
+ }
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
/*-------------------------------------------------------------------------
* Function: H5D__chunk_dest
@@ -2900,18 +2950,15 @@ done:
static herr_t
H5D__chunk_dest(H5D_t *dset)
{
- H5D_chk_idx_info_t idx_info; /* Chunked index info */
H5D_rdcc_t *rdcc = &(dset->shared->cache.chunk); /* Dataset's chunk cache */
H5D_rdcc_ent_t *ent = NULL, *next = NULL; /* Pointer to current & next cache entries */
int nerrors = 0; /* Accumulated count of errors */
- H5O_storage_chunk_t *sc = &(dset->shared->layout.storage.u.chunk);
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC_TAG(dset->oloc.addr)
/* Sanity checks */
HDassert(dset);
- H5D_CHUNK_STORAGE_INDEX_CHK(sc);
/* Flush all the cached chunks */
for(ent = rdcc->head; ent; ent = next) {
@@ -2929,15 +2976,10 @@ H5D__chunk_dest(H5D_t *dset)
rdcc->slot = H5FL_SEQ_FREE(H5D_rdcc_ent_ptr_t, rdcc->slot);
HDmemset(rdcc, 0, sizeof(H5D_rdcc_t));
- /* Compose chunked index info struct */
- idx_info.f = dset->oloc.file;
- idx_info.pline = &dset->shared->dcpl_cache.pline;
- idx_info.layout = &dset->shared->layout.u.chunk;
- idx_info.storage = sc;
-
- /* Free any index structures */
- if(sc->ops->dest && (sc->ops->dest)(&idx_info) < 0)
- HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to release chunk index info")
+ if (H5D__chunk_index_close(dset, true) < 0) {
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL,
+ "unable to close chunk index")
+ }
done:
FUNC_LEAVE_NOAPI_TAG(ret_value)
@@ -5696,7 +5738,7 @@ H5D__chunk_addrmap_cb(const H5D_chunk_rec_t *chunk_rec, void *_udata)
/* Set it in the userdata to return */
udata->chunk_addr[chunk_index] = chunk_rec->chunk_addr;
- FUNC_LEAVE_NOAPI(H5_ITER_CONT)
+ FUNC_LEAVE_NOAPI(ret_value)
} /* H5D__chunk_addrmap_cb() */
diff --git a/src/H5Dearray.c b/src/H5Dearray.c
index a53489e..8f34a07 100644
--- a/src/H5Dearray.c
+++ b/src/H5Dearray.c
@@ -165,7 +165,8 @@ const H5D_chunk_ops_t H5D_COPS_EARRAY[1] = {{
H5D__earray_idx_size, /* size */
H5D__earray_idx_reset, /* reset */
H5D__earray_idx_dump, /* dump */
- H5D__earray_idx_dest /* destroy */
+ H5D__earray_idx_dest, /* destroy */
+ H5D__earray_idx_dest /* close (same as destroy) */
}};
diff --git a/src/H5Dfarray.c b/src/H5Dfarray.c
index a9202c2..1417bc2 100644
--- a/src/H5Dfarray.c
+++ b/src/H5Dfarray.c
@@ -161,7 +161,8 @@ const H5D_chunk_ops_t H5D_COPS_FARRAY[1] = {{
H5D__farray_idx_size, /* size */
H5D__farray_idx_reset, /* reset */
H5D__farray_idx_dump, /* dump */
- H5D__farray_idx_dest /* destroy */
+ H5D__farray_idx_dest, /* destroy */
+ NULL /* close */
}};
diff --git a/src/H5Dint.c b/src/H5Dint.c
index c063bb9..8ba9b4f 100644
--- a/src/H5Dint.c
+++ b/src/H5Dint.c
@@ -3421,7 +3421,7 @@ done:
HDONE_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "address undefined")
/* Expunge from cache all v1 B-tree type entries associated with tag */
- if(H5AC_expunge_tag_type_metadata(dataset->oloc.file, dataset->oloc.addr, H5AC_BT_ID, H5AC__NO_FLAGS_SET))
+ if(H5AC_expunge_tag_type_metadata(dataset->oloc.file, dataset->oloc.addr, H5AC_BT_ID, H5AC__NO_FLAGS_SET, TRUE))
HDONE_ERROR(H5E_DATASET, H5E_CANTEXPUNGE, FAIL, "unable to expunge index metadata")
} /* end if */
diff --git a/src/H5Dnone.c b/src/H5Dnone.c
index 40ddcb8..e054f08 100644
--- a/src/H5Dnone.c
+++ b/src/H5Dnone.c
@@ -95,7 +95,8 @@ const H5D_chunk_ops_t H5D_COPS_NONE[1] = {{
H5D__none_idx_size, /* size */
H5D__none_idx_reset, /* reset */
H5D__none_idx_dump, /* dump */
- NULL /* dest */
+ NULL, /* dest */
+ NULL /* close */
}};
diff --git a/src/H5Dpkg.h b/src/H5Dpkg.h
index 37a27d3..7f2f18a 100644
--- a/src/H5Dpkg.h
+++ b/src/H5Dpkg.h
@@ -308,7 +308,7 @@ typedef herr_t (*H5D_chunk_size_func_t)(const H5D_chk_idx_info_t *idx_info,
typedef herr_t (*H5D_chunk_reset_func_t)(H5O_storage_chunk_t *storage, hbool_t reset_addr);
typedef herr_t (*H5D_chunk_dump_func_t)(const H5O_storage_chunk_t *storage,
FILE *stream);
-typedef herr_t (*H5D_chunk_dest_func_t)(const H5D_chk_idx_info_t *idx_info);
+typedef herr_t (*H5D_chunk_close_func_t)(const H5D_chk_idx_info_t *idx_info);
/* Typedef for grouping chunk I/O routines */
typedef struct H5D_chunk_ops_t {
@@ -327,7 +327,8 @@ typedef struct H5D_chunk_ops_t {
H5D_chunk_size_func_t size; /* Routine to get size of indexing information */
H5D_chunk_reset_func_t reset; /* Routine to reset indexing information */
H5D_chunk_dump_func_t dump; /* Routine to dump indexing information */
- H5D_chunk_dest_func_t dest; /* Routine to destroy indexing information in memory */
+ H5D_chunk_close_func_t dest; /* Routine to destroy indexing information in memory */
+ H5D_chunk_close_func_t close; /* Routine to destroy indexing information in memory */
} H5D_chunk_ops_t;
/* Structure holding information about a chunk's selection for mapping */
diff --git a/src/H5Dsingle.c b/src/H5Dsingle.c
index 33274bb..3fa9bc2 100644
--- a/src/H5Dsingle.c
+++ b/src/H5Dsingle.c
@@ -97,7 +97,8 @@ const H5D_chunk_ops_t H5D_COPS_SINGLE[1] = {{
H5D__single_idx_size, /* size */
H5D__single_idx_reset, /* reset */
H5D__single_idx_dump, /* dump */
- NULL /* destroy */
+ NULL, /* destroy */
+ NULL /* close */
}};
diff --git a/src/H5Dvirtual.c b/src/H5Dvirtual.c
index e07f538..e0bfb1b 100644
--- a/src/H5Dvirtual.c
+++ b/src/H5Dvirtual.c
@@ -313,6 +313,8 @@ done:
herr_t
H5D_virtual_update_min_dims(H5O_layout_t *layout, size_t idx)
{
+ H5O_storage_virtual_t *virt = &layout->storage.u.virt;
+ H5O_storage_virtual_ent_t *ent = &virt->list[idx];
H5S_sel_type sel_type;
int rank;
hsize_t bounds_start[H5S_MAX_RANK];
@@ -324,10 +326,10 @@ H5D_virtual_update_min_dims(H5O_layout_t *layout, size_t idx)
HDassert(layout);
HDassert(layout->type == H5D_VIRTUAL);
- HDassert(idx < layout->storage.u.virt.list_nalloc);
+ HDassert(idx < virt->list_nalloc);
/* Get type of selection */
- if(H5S_SEL_ERROR == (sel_type = H5S_GET_SELECT_TYPE(layout->storage.u.virt.list[idx].source_dset.virtual_select)))
+ if(H5S_SEL_ERROR == (sel_type = H5S_GET_SELECT_TYPE(ent->source_dset.virtual_select)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to get selection type")
/* Do not update min_dims for "all" or "none" selections */
@@ -335,19 +337,19 @@ H5D_virtual_update_min_dims(H5O_layout_t *layout, size_t idx)
HGOTO_DONE(SUCCEED)
/* Get rank of vspace */
- if((rank = H5S_GET_EXTENT_NDIMS(layout->storage.u.virt.list[idx].source_dset.virtual_select)) < 0)
+ if((rank = H5S_GET_EXTENT_NDIMS(ent->source_dset.virtual_select)) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to get number of dimensions")
/* Get selection bounds */
- if(H5S_SELECT_BOUNDS(layout->storage.u.virt.list[idx].source_dset.virtual_select, bounds_start, bounds_end) < 0)
+ if(H5S_SELECT_BOUNDS(ent->source_dset.virtual_select, bounds_start, bounds_end) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to get selection bounds")
/* Update min_dims */
for(i = 0; i < rank; i++)
/* Don't check unlimited dimensions in the selection */
- if((i != layout->storage.u.virt.list[idx].unlim_dim_virtual)
- && (bounds_end[i] >= layout->storage.u.virt.min_dims[i]))
- layout->storage.u.virt.min_dims[i] = bounds_end[i] + (hsize_t)1;
+ if((i != ent->unlim_dim_virtual)
+ && (bounds_end[i] >= virt->min_dims[i]))
+ virt->min_dims[i] = bounds_end[i] + (hsize_t)1;
done:
FUNC_LEAVE_NOAPI(ret_value)
@@ -419,6 +421,7 @@ done:
herr_t
H5D__virtual_store_layout(H5F_t *f, H5O_layout_t *layout)
{
+ H5O_storage_virtual_t *virt = &layout->storage.u.virt;
uint8_t *heap_block = NULL; /* Block to add to heap */
size_t *str_size = NULL; /* Array for VDS entry string lengths */
uint8_t *heap_block_p; /* Pointer into the heap block, while encoding */
@@ -433,16 +436,16 @@ H5D__virtual_store_layout(H5F_t *f, H5O_layout_t *layout)
/* Sanity checking */
HDassert(f);
HDassert(layout);
- HDassert(layout->storage.u.virt.serial_list_hobjid.addr == HADDR_UNDEF);
+ HDassert(virt->serial_list_hobjid.addr == HADDR_UNDEF);
/* Create block if # of used entries > 0 */
- if(layout->storage.u.virt.list_nused > 0) {
+ if(virt->list_nused > 0) {
/* Set the low/high bounds according to 'f' for the API context */
H5CX_set_libver_bounds(f);
/* Allocate array for caching results of strlen */
- if(NULL == (str_size = (size_t *)H5MM_malloc(2 * layout->storage.u.virt.list_nused * sizeof(size_t))))
+ if(NULL == (str_size = (size_t *)H5MM_malloc(2 * virt->list_nused * sizeof(size_t))))
HGOTO_ERROR(H5E_OHDR, H5E_RESOURCE, FAIL, "unable to allocate string length array")
/*
@@ -453,29 +456,30 @@ H5D__virtual_store_layout(H5F_t *f, H5O_layout_t *layout)
block_size = (size_t)1 + H5F_SIZEOF_SIZE(f);
/* Calculate size of each entry */
- for(i = 0; i < layout->storage.u.virt.list_nused; i++) {
+ for(i = 0; i < virt->list_nused; i++) {
+ H5O_storage_virtual_ent_t *ent = &virt->list[i];
hssize_t select_serial_size; /* Size of serialized selection */
- HDassert(layout->storage.u.virt.list[i].source_file_name);
- HDassert(layout->storage.u.virt.list[i].source_dset_name);
- HDassert(layout->storage.u.virt.list[i].source_select);
- HDassert(layout->storage.u.virt.list[i].source_dset.virtual_select);
+ HDassert(ent->source_file_name);
+ HDassert(ent->source_dset_name);
+ HDassert(ent->source_select);
+ HDassert(ent->source_dset.virtual_select);
/* Source file name */
- str_size[2 * i] = HDstrlen(layout->storage.u.virt.list[i].source_file_name) + (size_t)1;
+ str_size[2 * i] = HDstrlen(ent->source_file_name) + (size_t)1;
block_size += str_size[2 * i];
/* Source dset name */
- str_size[(2 * i) + 1] = HDstrlen(layout->storage.u.virt.list[i].source_dset_name) + (size_t)1;
+ str_size[(2 * i) + 1] = HDstrlen(ent->source_dset_name) + (size_t)1;
block_size += str_size[(2 * i) + 1];
/* Source selection */
- if((select_serial_size = H5S_SELECT_SERIAL_SIZE(layout->storage.u.virt.list[i].source_select)) < 0)
+ if((select_serial_size = H5S_SELECT_SERIAL_SIZE(ent->source_select)) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTENCODE, FAIL, "unable to check dataspace selection size")
block_size += (size_t)select_serial_size;
/* Virtual dataset selection */
- if((select_serial_size = H5S_SELECT_SERIAL_SIZE(layout->storage.u.virt.list[i].source_dset.virtual_select)) < 0)
+ if((select_serial_size = H5S_SELECT_SERIAL_SIZE(ent->source_dset.virtual_select)) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTENCODE, FAIL, "unable to check dataspace selection size")
block_size += (size_t)select_serial_size;
} /* end for */
@@ -498,25 +502,26 @@ H5D__virtual_store_layout(H5F_t *f, H5O_layout_t *layout)
*heap_block_p++ = (uint8_t)H5O_LAYOUT_VDS_GH_ENC_VERS;
/* Number of entries */
- tmp_nentries = (hsize_t)layout->storage.u.virt.list_nused;
+ tmp_nentries = (hsize_t)virt->list_nused;
H5F_ENCODE_LENGTH(f, heap_block_p, tmp_nentries)
/* Encode each entry */
- for(i = 0; i < layout->storage.u.virt.list_nused; i++) {
+ for(i = 0; i < virt->list_nused; i++) {
+ H5O_storage_virtual_ent_t *ent = &virt->list[i];
/* Source file name */
- H5MM_memcpy((char *)heap_block_p, layout->storage.u.virt.list[i].source_file_name, str_size[2 * i]);
+ H5MM_memcpy((char *)heap_block_p, ent->source_file_name, str_size[2 * i]);
heap_block_p += str_size[2 * i];
/* Source dataset name */
- H5MM_memcpy((char *)heap_block_p, layout->storage.u.virt.list[i].source_dset_name, str_size[(2 * i) + 1]);
+ H5MM_memcpy((char *)heap_block_p, ent->source_dset_name, str_size[(2 * i) + 1]);
heap_block_p += str_size[(2 * i) + 1];
/* Source selection */
- if(H5S_SELECT_SERIALIZE(layout->storage.u.virt.list[i].source_select, &heap_block_p) < 0)
+ if(H5S_SELECT_SERIALIZE(ent->source_select, &heap_block_p) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTCOPY, FAIL, "unable to serialize source selection")
/* Virtual selection */
- if(H5S_SELECT_SERIALIZE(layout->storage.u.virt.list[i].source_dset.virtual_select, &heap_block_p) < 0)
+ if(H5S_SELECT_SERIALIZE(ent->source_dset.virtual_select, &heap_block_p) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTCOPY, FAIL, "unable to serialize virtual selection")
} /* end for */
@@ -525,7 +530,7 @@ H5D__virtual_store_layout(H5F_t *f, H5O_layout_t *layout)
UINT32ENCODE(heap_block_p, chksum)
/* Insert block into global heap */
- if(H5HG_insert(f, block_size, heap_block, &(layout->storage.u.virt.serial_list_hobjid)) < 0) /* Casting away const OK --NAF */
+ if(H5HG_insert(f, block_size, heap_block, &(virt->serial_list_hobjid)) < 0) /* Casting away const OK --NAF */
HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "unable to insert virtual dataset heap block")
} /* end if */
@@ -556,6 +561,7 @@ herr_t
H5D__virtual_copy_layout(H5O_layout_t *layout)
{
H5O_storage_virtual_ent_t *orig_list = NULL;
+ H5O_storage_virtual_t *virt = &layout->storage.u.virt;
hid_t orig_source_fapl;
hid_t orig_source_dapl;
H5P_genplist_t *plist;
@@ -569,127 +575,129 @@ H5D__virtual_copy_layout(H5O_layout_t *layout)
/* Save original entry list and top-level property lists and reset in layout
* so the originals aren't closed on error */
- orig_source_fapl = layout->storage.u.virt.source_fapl;
- layout->storage.u.virt.source_fapl = -1;
- orig_source_dapl = layout->storage.u.virt.source_dapl;
- layout->storage.u.virt.source_dapl = -1;
- orig_list = layout->storage.u.virt.list;
- layout->storage.u.virt.list = NULL;
+ orig_source_fapl = virt->source_fapl;
+ virt->source_fapl = -1;
+ orig_source_dapl = virt->source_dapl;
+ virt->source_dapl = -1;
+ orig_list = virt->list;
+ virt->list = NULL;
/* Copy entry list */
- if(layout->storage.u.virt.list_nused > 0) {
+ if(virt->list_nused > 0) {
HDassert(orig_list);
/* Allocate memory for the list */
- if(NULL == (layout->storage.u.virt.list = (H5O_storage_virtual_ent_t *)H5MM_calloc(layout->storage.u.virt.list_nused * sizeof(H5O_storage_virtual_ent_t))))
+ if(NULL == (virt->list = H5MM_calloc(virt->list_nused * sizeof(virt->list[0]))))
HGOTO_ERROR(H5E_DATASET, H5E_CANTALLOC, FAIL, "unable to allocate memory for virtual dataset entry list")
- layout->storage.u.virt.list_nalloc = layout->storage.u.virt.list_nused;
+ virt->list_nalloc = virt->list_nused;
/* Copy the list entries, though set source_dset.dset and sub_dset to
* NULL */
- for(i = 0; i < layout->storage.u.virt.list_nused; i++) {
+ for(i = 0; i < virt->list_nused; i++) {
+ H5O_storage_virtual_ent_t *ent = &virt->list[i];
+
/* Copy virtual selection */
- if(NULL == (layout->storage.u.virt.list[i].source_dset.virtual_select
+ if(NULL == (ent->source_dset.virtual_select
= H5S_copy(orig_list[i].source_dset.virtual_select, FALSE, TRUE)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "unable to copy virtual selection")
/* Copy original source names */
- if(NULL == (layout->storage.u.virt.list[i].source_file_name
+ if(NULL == (ent->source_file_name
= H5MM_strdup(orig_list[i].source_file_name)))
HGOTO_ERROR(H5E_DATASET, H5E_RESOURCE, FAIL, "unable to duplicate source file name")
- if(NULL == (layout->storage.u.virt.list[i].source_dset_name
+ if(NULL == (ent->source_dset_name
= H5MM_strdup(orig_list[i].source_dset_name)))
HGOTO_ERROR(H5E_DATASET, H5E_RESOURCE, FAIL, "unable to duplicate source dataset name")
/* Copy source selection */
- if(NULL == (layout->storage.u.virt.list[i].source_select
+ if(NULL == (ent->source_select
= H5S_copy(orig_list[i].source_select, FALSE, TRUE)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "unable to copy source selection")
/* Initialize clipped selections */
if(orig_list[i].unlim_dim_virtual < 0) {
- layout->storage.u.virt.list[i].source_dset.clipped_source_select = layout->storage.u.virt.list[i].source_select;
- layout->storage.u.virt.list[i].source_dset.clipped_virtual_select = layout->storage.u.virt.list[i].source_dset.virtual_select;
+ ent->source_dset.clipped_source_select = ent->source_select;
+ ent->source_dset.clipped_virtual_select = ent->source_dset.virtual_select;
} /* end if */
/* Copy parsed names */
- if(H5D__virtual_copy_parsed_name(&layout->storage.u.virt.list[i].parsed_source_file_name, orig_list[i].parsed_source_file_name) < 0)
+ if(H5D__virtual_copy_parsed_name(&ent->parsed_source_file_name, orig_list[i].parsed_source_file_name) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "unable to copy parsed source file name")
- layout->storage.u.virt.list[i].psfn_static_strlen = orig_list[i].psfn_static_strlen;
- layout->storage.u.virt.list[i].psfn_nsubs = orig_list[i].psfn_nsubs;
- if(H5D__virtual_copy_parsed_name(&layout->storage.u.virt.list[i].parsed_source_dset_name, orig_list[i].parsed_source_dset_name) < 0)
+ ent->psfn_static_strlen = orig_list[i].psfn_static_strlen;
+ ent->psfn_nsubs = orig_list[i].psfn_nsubs;
+ if(H5D__virtual_copy_parsed_name(&ent->parsed_source_dset_name, orig_list[i].parsed_source_dset_name) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "unable to copy parsed source dataset name")
- layout->storage.u.virt.list[i].psdn_static_strlen = orig_list[i].psdn_static_strlen;
- layout->storage.u.virt.list[i].psdn_nsubs = orig_list[i].psdn_nsubs;
+ ent->psdn_static_strlen = orig_list[i].psdn_static_strlen;
+ ent->psdn_nsubs = orig_list[i].psdn_nsubs;
/* Copy source names in source dset or add reference as appropriate
*/
if(orig_list[i].source_dset.file_name) {
if(orig_list[i].source_dset.file_name
== orig_list[i].source_file_name)
- layout->storage.u.virt.list[i].source_dset.file_name = layout->storage.u.virt.list[i].source_file_name;
+ ent->source_dset.file_name = ent->source_file_name;
else if(orig_list[i].parsed_source_file_name
&& (orig_list[i].source_dset.file_name
!= orig_list[i].parsed_source_file_name->name_segment)) {
- HDassert(layout->storage.u.virt.list[i].parsed_source_file_name);
- HDassert(layout->storage.u.virt.list[i].parsed_source_file_name->name_segment);
- layout->storage.u.virt.list[i].source_dset.file_name = layout->storage.u.virt.list[i].parsed_source_file_name->name_segment;
+ HDassert(ent->parsed_source_file_name);
+ HDassert(ent->parsed_source_file_name->name_segment);
+ ent->source_dset.file_name = ent->parsed_source_file_name->name_segment;
} /* end if */
else
- if(NULL == (layout->storage.u.virt.list[i].source_dset.file_name
+ if(NULL == (ent->source_dset.file_name
= H5MM_strdup(orig_list[i].source_dset.file_name)))
HGOTO_ERROR(H5E_DATASET, H5E_RESOURCE, FAIL, "unable to duplicate source file name")
} /* end if */
if(orig_list[i].source_dset.dset_name) {
if(orig_list[i].source_dset.dset_name
== orig_list[i].source_dset_name)
- layout->storage.u.virt.list[i].source_dset.dset_name = layout->storage.u.virt.list[i].source_dset_name;
+ ent->source_dset.dset_name = ent->source_dset_name;
else if(orig_list[i].parsed_source_dset_name
&& (orig_list[i].source_dset.dset_name
!= orig_list[i].parsed_source_dset_name->name_segment)) {
- HDassert(layout->storage.u.virt.list[i].parsed_source_dset_name);
- HDassert(layout->storage.u.virt.list[i].parsed_source_dset_name->name_segment);
- layout->storage.u.virt.list[i].source_dset.dset_name = layout->storage.u.virt.list[i].parsed_source_dset_name->name_segment;
+ HDassert(ent->parsed_source_dset_name);
+ HDassert(ent->parsed_source_dset_name->name_segment);
+ ent->source_dset.dset_name = ent->parsed_source_dset_name->name_segment;
} /* end if */
else
- if(NULL == (layout->storage.u.virt.list[i].source_dset.dset_name
+ if(NULL == (ent->source_dset.dset_name
= H5MM_strdup(orig_list[i].source_dset.dset_name)))
HGOTO_ERROR(H5E_DATASET, H5E_RESOURCE, FAIL, "unable to duplicate source dataset name")
} /* end if */
/* Copy other fields in entry */
- layout->storage.u.virt.list[i].unlim_dim_source = orig_list[i].unlim_dim_source;
- layout->storage.u.virt.list[i].unlim_dim_virtual = orig_list[i].unlim_dim_virtual;
- layout->storage.u.virt.list[i].unlim_extent_source = orig_list[i].unlim_extent_source;
- layout->storage.u.virt.list[i].unlim_extent_virtual = orig_list[i].unlim_extent_virtual;
- layout->storage.u.virt.list[i].clip_size_source = orig_list[i].clip_size_source;
- layout->storage.u.virt.list[i].clip_size_virtual = orig_list[i].clip_size_virtual;
- layout->storage.u.virt.list[i].source_space_status = orig_list[i].source_space_status;
- layout->storage.u.virt.list[i].virtual_space_status = orig_list[i].virtual_space_status;
+ ent->unlim_dim_source = orig_list[i].unlim_dim_source;
+ ent->unlim_dim_virtual = orig_list[i].unlim_dim_virtual;
+ ent->unlim_extent_source = orig_list[i].unlim_extent_source;
+ ent->unlim_extent_virtual = orig_list[i].unlim_extent_virtual;
+ ent->clip_size_source = orig_list[i].clip_size_source;
+ ent->clip_size_virtual = orig_list[i].clip_size_virtual;
+ ent->source_space_status = orig_list[i].source_space_status;
+ ent->virtual_space_status = orig_list[i].virtual_space_status;
} /* end for */
} /* end if */
else {
/* Zero out other fields related to list, just to be sure */
- layout->storage.u.virt.list = NULL;
- layout->storage.u.virt.list_nalloc = 0;
+ virt->list = NULL;
+ virt->list_nalloc = 0;
} /* end else */
/* Copy property lists */
if(orig_source_fapl >= 0) {
if(NULL == (plist = (H5P_genplist_t *)H5I_object_verify(orig_source_fapl, H5I_GENPROP_LST)))
HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a property list")
- if((layout->storage.u.virt.source_fapl = H5P_copy_plist(plist, FALSE)) < 0)
+ if((virt->source_fapl = H5P_copy_plist(plist, FALSE)) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "can't copy fapl")
} /* end if */
if(orig_source_dapl >= 0) {
if(NULL == (plist = (H5P_genplist_t *)H5I_object_verify(orig_source_dapl, H5I_GENPROP_LST)))
HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a property list")
- if((layout->storage.u.virt.source_dapl = H5P_copy_plist(plist, FALSE)) < 0)
+ if((virt->source_dapl = H5P_copy_plist(plist, FALSE)) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "can't copy dapl")
} /* end if */
/* New layout is not fully initialized */
- layout->storage.u.virt.init = FALSE;
+ virt->init = FALSE;
done:
/* Release allocated resources on failure */
@@ -721,6 +729,7 @@ herr_t
H5D__virtual_reset_layout(H5O_layout_t *layout)
{
size_t i, j;
+ H5O_storage_virtual_t *virt = &layout->storage.u.virt;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE
@@ -731,53 +740,54 @@ H5D__virtual_reset_layout(H5O_layout_t *layout)
/* Free the list entries. Note we always attempt to free everything even in
* the case of a failure. Because of this, and because we free the list
* afterwards, we do not need to zero out the memory in the list. */
- for(i = 0; i < layout->storage.u.virt.list_nused; i++) {
+ for(i = 0; i < virt->list_nused; i++) {
+ H5O_storage_virtual_ent_t *ent = &virt->list[i];
/* Free source_dset */
- if(H5D__virtual_reset_source_dset(&layout->storage.u.virt.list[i], &layout->storage.u.virt.list[i].source_dset) < 0)
+ if(H5D__virtual_reset_source_dset(ent, &ent->source_dset) < 0)
HDONE_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to reset source dataset")
/* Free original source names */
- (void)H5MM_xfree(layout->storage.u.virt.list[i].source_file_name);
- (void)H5MM_xfree(layout->storage.u.virt.list[i].source_dset_name);
+ (void)H5MM_xfree(ent->source_file_name);
+ (void)H5MM_xfree(ent->source_dset_name);
/* Free sub_dset */
- for(j = 0; j < layout->storage.u.virt.list[i].sub_dset_nalloc; j++)
- if(H5D__virtual_reset_source_dset(&layout->storage.u.virt.list[i], &layout->storage.u.virt.list[i].sub_dset[j]) < 0)
+ for(j = 0; j < ent->sub_dset_nalloc; j++)
+ if(H5D__virtual_reset_source_dset(ent, &ent->sub_dset[j]) < 0)
HDONE_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to reset source dataset")
- layout->storage.u.virt.list[i].sub_dset = (H5O_storage_virtual_srcdset_t *)H5MM_xfree(layout->storage.u.virt.list[i].sub_dset);
+ ent->sub_dset = H5MM_xfree(ent->sub_dset);
/* Free source_select */
- if(layout->storage.u.virt.list[i].source_select)
- if(H5S_close(layout->storage.u.virt.list[i].source_select) < 0)
+ if(ent->source_select)
+ if(H5S_close(ent->source_select) < 0)
HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, FAIL, "unable to release source selection")
/* Free parsed_source_file_name */
- H5D_virtual_free_parsed_name(layout->storage.u.virt.list[i].parsed_source_file_name);
+ H5D_virtual_free_parsed_name(ent->parsed_source_file_name);
/* Free parsed_source_dset_name */
- H5D_virtual_free_parsed_name(layout->storage.u.virt.list[i].parsed_source_dset_name);
- } /* end for */
+ H5D_virtual_free_parsed_name(ent->parsed_source_dset_name);
+ }
/* Free the list */
- layout->storage.u.virt.list = (H5O_storage_virtual_ent_t *)H5MM_xfree(layout->storage.u.virt.list);
- layout->storage.u.virt.list_nalloc = (size_t)0;
- layout->storage.u.virt.list_nused = (size_t)0;
- (void)HDmemset(layout->storage.u.virt.min_dims, 0, sizeof(layout->storage.u.virt.min_dims));
+ virt->list = H5MM_xfree(virt->list);
+ virt->list_nalloc = (size_t)0;
+ virt->list_nused = (size_t)0;
+ (void)HDmemset(virt->min_dims, 0, sizeof(virt->min_dims));
/* Close access property lists */
- if(layout->storage.u.virt.source_fapl >= 0) {
- if(H5I_dec_ref(layout->storage.u.virt.source_fapl) < 0)
+ if(virt->source_fapl >= 0) {
+ if(H5I_dec_ref(virt->source_fapl) < 0)
HDONE_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "can't close source fapl")
- layout->storage.u.virt.source_fapl = -1;
- } /* end if */
- if(layout->storage.u.virt.source_dapl >= 0) {
- if(H5I_dec_ref(layout->storage.u.virt.source_dapl) < 0)
+ virt->source_fapl = -1;
+ }
+ if(virt->source_dapl >= 0) {
+ if(H5I_dec_ref(virt->source_dapl) < 0)
HDONE_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "can't close source dapl")
- layout->storage.u.virt.source_dapl = -1;
- } /* end if */
+ virt->source_dapl = -1;
+ }
/* The list is no longer initialized */
- layout->storage.u.virt.init = FALSE;
+ virt->init = FALSE;
/* Note the lack of a done: label. This is because there are no HGOTO_ERROR
* calls. If one is added, a done: label must also be added */
@@ -915,7 +925,14 @@ H5D__virtual_open_source_dset(const H5D_t *vdset,
intent = H5F_INTENT(vdset->oloc.file);
/* Try opening the file */
- src_file = H5F_prefix_open_file(vdset->oloc.file, H5F_PREFIX_VDS, vdset->shared->vds_prefix, source_dset->file_name, intent, vdset->shared->layout.storage.u.virt.source_fapl);
+ /* XXX Pass the special file-access property list ID,
+ * H5P_FILE_ACCESS_ANY_VFD, so that if the file is already open in
+ * VFD SWMR mode, the library just creates a new H5F_t for the file
+ * instead of returning an error because of the discrepancy between
+ * the default file-access properties and the already-open file's
+ * VFD SWMR properties.
+ */
+ src_file = H5F_prefix_open_file(vdset->oloc.file, H5F_PREFIX_VDS, vdset->shared->vds_prefix, source_dset->file_name, intent, H5P_FILE_ACCESS_ANY_VFD);
/* If we opened the source file here, we should close it when leaving */
if(src_file)
@@ -2781,7 +2798,7 @@ H5D__virtual_write_one(H5D_io_info_t *io_info, const H5D_type_info_t *type_info,
* extent in the unlimited dimension. -NAF */
/* Project intersection of file space and mapping virtual space onto
* mapping source space */
- if(H5S_select_project_intersection(source_dset->virtual_select, source_dset->clipped_source_select, file_space, &projected_src_space, TRUE) < 0)
+ if(H5S_select_project_intersection(source_dset->clipped_virtual_select, source_dset->clipped_source_select, file_space, &projected_src_space, TRUE) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTCLIP, FAIL, "can't project virtual intersection onto source space")
/* Perform write on source dataset */
diff --git a/src/H5EAcache.c b/src/H5EAcache.c
index affa127..2ae4f84 100644
--- a/src/H5EAcache.c
+++ b/src/H5EAcache.c
@@ -145,6 +145,7 @@ const H5AC_class_t H5AC_EARRAY_HDR[1] = {{
H5EA__cache_hdr_notify, /* 'notify' callback */
H5EA__cache_hdr_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5EA index block inherits cache-like properties from H5AC */
@@ -163,6 +164,7 @@ const H5AC_class_t H5AC_EARRAY_IBLOCK[1] = {{
H5EA__cache_iblock_notify, /* 'notify' callback */
H5EA__cache_iblock_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5EA super block inherits cache-like properties from H5AC */
@@ -181,6 +183,7 @@ const H5AC_class_t H5AC_EARRAY_SBLOCK[1] = {{
H5EA__cache_sblock_notify, /* 'notify' callback */
H5EA__cache_sblock_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5EA data block inherits cache-like properties from H5AC */
@@ -199,6 +202,7 @@ const H5AC_class_t H5AC_EARRAY_DBLOCK[1] = {{
H5EA__cache_dblock_notify, /* 'notify' callback */
H5EA__cache_dblock_free_icr, /* 'free_icr' callback */
H5EA__cache_dblock_fsf_size, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5EA data block page inherits cache-like properties from H5AC */
@@ -217,6 +221,7 @@ const H5AC_class_t H5AC_EARRAY_DBLK_PAGE[1] = {{
H5EA__cache_dblk_page_notify, /* 'notify' callback */
H5EA__cache_dblk_page_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
diff --git a/src/H5F.c b/src/H5F.c
index 9d426ac..054c547 100644
--- a/src/H5F.c
+++ b/src/H5F.c
@@ -2044,3 +2044,108 @@ done:
FUNC_LEAVE_API(ret_value)
} /* H5Fset_dset_no_attrs_hint */
+
+/*-------------------------------------------------------------------------
+ * Function: H5Fvfd_swmr_end_tick()
+ *
+ * Purpose: To trigger end of tick processing
+ *
+ * Return: Non-negative on success/Negative on errors
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Fvfd_swmr_end_tick(hid_t file_id)
+{
+ H5VL_object_t *vol_obj = NULL; /* File info */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ /* Note: use the version of FUNC_ENTER_API without EOT processing */
+ FUNC_ENTER_API_NO_EOT(FAIL)
+ H5TRACE1("e", "i", file_id);
+
+ vol_obj = (H5VL_object_t *)H5I_object_verify(file_id, H5I_FILE);
+ if(NULL == vol_obj)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid file identifier")
+
+ /* Check on this when go parallel for VFD SWMR */
+ /* Set up collective metadata if appropriate */
+ if(H5CX_set_loc(file_id) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "can't set collective metadata read info")
+
+ if(H5VL_file_optional(vol_obj, H5VL_NATIVE_FILE_VFD_SWMR_END_TICK, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "unable to trigger end of tick processing for VFD SWMR")
+
+done:
+ /* Note: use the version of FUNC_LEAVE_API without EOT processing */
+ FUNC_LEAVE_API_NO_EOT(ret_value)
+} /* H5Fvfd_swmr_end_tick() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5Fvfd_swmr_disable_end_of_tick()
+ *
+ * Purpose: Disable end of tick processing
+ *
+ * Return: Non-negative on success/Negative on errors
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Fvfd_swmr_disable_end_of_tick(hid_t file_id)
+{
+
+ H5VL_object_t *vol_obj = NULL; /* File info */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE1("e", "i", file_id);
+
+ vol_obj = (H5VL_object_t *)H5I_object_verify(file_id, H5I_FILE);
+ if(NULL == vol_obj)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid file identifier")
+
+ /* Check on this when go parallel for VFD SWMR */
+ /* Set up collective metadata if appropriate */
+ if(H5CX_set_loc(file_id) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "can't set collective metadata read info")
+
+ if(H5VL_file_optional(vol_obj, H5VL_NATIVE_FILE_VFD_SWMR_DISABLE_EOT, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "unable to disable EOT for VFD SWMR")
+
+done:
+ FUNC_LEAVE_API(ret_value)
+} /* H5Fvfd_swmr_disable_end_of_tick() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5Fvfd_swmr_enable_end_of_tick()
+ *
+ * Purpose: Enable end of tick processing
+ *
+ * Return: Non-negative on success/Negative on errors
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Fvfd_swmr_enable_end_of_tick(hid_t file_id)
+{
+
+ H5VL_object_t *vol_obj = NULL; /* File info */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE1("e", "i", file_id);
+
+ vol_obj = (H5VL_object_t *)H5I_object_verify(file_id, H5I_FILE);
+ if(NULL == vol_obj)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid file identifier")
+
+ /* Check on this when go parallel for VFD SWMR */
+ /* Set up collective metadata if appropriate */
+ if(H5CX_set_loc(file_id) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "can't set collective metadata read info")
+
+ if(H5VL_file_optional(vol_obj, H5VL_NATIVE_FILE_VFD_SWMR_ENABLE_EOT, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "unable to enable EOT for VFD SWMR")
+
+done:
+ FUNC_LEAVE_API(ret_value)
+} /* H5Fvfd_swmr_enable_end_of_tick() */
diff --git a/src/H5FAcache.c b/src/H5FAcache.c
index 8f5e696..f2f6990 100644
--- a/src/H5FAcache.c
+++ b/src/H5FAcache.c
@@ -122,6 +122,7 @@ const H5AC_class_t H5AC_FARRAY_HDR[1] = {{
H5FA__cache_hdr_notify, /* 'notify' callback */
H5FA__cache_hdr_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5FA data block inherits cache-like properties from H5AC */
@@ -140,6 +141,7 @@ const H5AC_class_t H5AC_FARRAY_DBLOCK[1] = {{
H5FA__cache_dblock_notify, /* 'notify' callback */
H5FA__cache_dblock_free_icr, /* 'free_icr' callback */
H5FA__cache_dblock_fsf_size, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5FA data block page inherits cache-like properties from H5AC */
@@ -158,6 +160,7 @@ const H5AC_class_t H5AC_FARRAY_DBLK_PAGE[1] = {{
H5FA__cache_dblk_page_notify, /* 'notify' callback */
H5FA__cache_dblk_page_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
diff --git a/src/H5FD.c b/src/H5FD.c
index 2cd69df..600a825 100644
--- a/src/H5FD.c
+++ b/src/H5FD.c
@@ -92,6 +92,8 @@ hbool_t H5_PKG_INIT_VAR = FALSE;
*/
static unsigned long H5FD_file_serial_no_g;
+static TAILQ_HEAD(_all_vfds, H5FD_t) all_vfds = TAILQ_HEAD_INITIALIZER(all_vfds);
+
/* File driver ID class */
static const H5I_class_t H5I_VFL_CLS[1] = {{
H5I_VFL, /* ID class value */
@@ -677,6 +679,91 @@ done:
FUNC_LEAVE_API(ret_value)
}
+/* Return `other` if `self` has no de-duplication method. Otherwise, return
+ * `other` if it duplicates `self`, `self` if `other` does NOT duplicate it,
+ * NULL if `other` conflicts with `self` or if there is an error.
+ *
+ * Unlike H5FD_deduplicate(), this routine does not free `self` under any
+ * circumstances.
+ */
+static H5FD_t *
+H5FD_dedup(H5FD_t *self, H5FD_t *other, hid_t fapl)
+{
+ H5FD_t *(*dedup)(H5FD_t *, H5FD_t *, hid_t);
+
+ if ((dedup = self->cls->dedup) != NULL)
+ return (*dedup)(self, other, fapl);
+
+ if (H5FDcmp(self, other) == 0)
+ return self;
+
+ return other;
+}
+
+/* If any other open H5FD_t is functionally equivalent to `file` under
+ * the given file-access properties, then return it and close `file`.
+ *
+ * If any other open H5FD_t is not equivalent to `file`, but its
+ * operation would conflict with `file`, then return NULL and close `file`.
+ */
+H5FD_t *
+H5FD_deduplicate(H5FD_t *file, hid_t fapl)
+{
+ H5FD_t *deduped = file, *item;
+
+ TAILQ_FOREACH(item, &all_vfds, link) {
+ /* skip "self" */
+ if (item == file)
+ continue;
+
+ /* skip files with exclusive owners, for now */
+ if (item->exc_owner != NULL)
+ continue;
+
+ if ((deduped = H5FD_dedup(item, file, fapl)) != file)
+ goto finish;
+ }
+
+ /* If we reach this stage, then we identified neither a conflict nor a
+ * duplicate. If any lower VFD with an exclusive owner matches `file`,
+ * return NULL to indicate the conflict.
+ */
+ TAILQ_FOREACH(item, &all_vfds, link) {
+ if (item == file || item->exc_owner == NULL)
+ continue;
+
+ if (H5FDcmp(file, item) == 0) {
+ deduped = NULL;
+ break;
+ }
+ }
+
+finish:
+ if (deduped != file && H5FD_close(file) < 0) {
+ HERROR(H5E_FILE, H5E_CANTOPENFILE, "could not close file");
+ return NULL;
+ }
+ return deduped;
+}
+
+/* Return `true` if a second H5FD_t identical to `file`
+ * has an exclusive owner, `false` otherwise.
+ */
+bool
+H5FD_has_conflict(H5FD_t *file)
+{
+ H5FD_t *item;
+
+ TAILQ_FOREACH(item, &all_vfds, link) {
+ // skip "self", skip unowned
+ if (item == file || item->exc_owner == NULL)
+ continue;
+ if (H5FDcmp(file, item) == 0)
+ return true;
+ }
+ return false;
+}
+
/*-------------------------------------------------------------------------
* Function: H5FD_open
@@ -693,7 +780,7 @@ H5FD_t *
H5FD_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
{
H5FD_class_t *driver; /* VFD for file */
- H5FD_t *file = NULL; /* VFD file struct */
+ H5FD_t *file;
H5FD_driver_prop_t driver_prop; /* Property for driver ID & info */
H5P_genplist_t *plist; /* Property list pointer */
unsigned long driver_flags = 0; /* File-inspecific driver feature flags */
@@ -737,9 +824,14 @@ H5FD_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
/* Dispatch to file driver */
if(HADDR_UNDEF == maxaddr)
maxaddr = driver->maxaddr;
+#if 0 /* JRM */
+ HDfprintf(stderr, "H5FD_open(): calling %s.open().\n", driver->name);
+#endif /* JRM */
if(NULL == (file = (driver->open)(name, flags, fapl_id, maxaddr)))
HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "open failed")
+ file->exc_owner = NULL;
+
/* Set the file access flags */
file->access_flags = flags;
@@ -771,10 +863,13 @@ H5FD_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
/* (This will be changed later, when the superblock is located) */
file->base_addr = 0;
+ TAILQ_INSERT_TAIL(&all_vfds, file, link);
+
/* Set return value */
ret_value = file;
done:
+ /* XXX We leak H5FD_t's on many error conditions. */
/* Can't cleanup 'file' information, since we don't know what type it is */
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD_open() */
@@ -829,6 +924,7 @@ herr_t
H5FD_close(H5FD_t *file)
{
const H5FD_class_t *driver;
+ H5FD_t *item;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI(FAIL)
@@ -842,6 +938,12 @@ H5FD_close(H5FD_t *file)
if(H5I_dec_ref(file->driver_id) < 0)
HGOTO_ERROR(H5E_VFL, H5E_CANTDEC, FAIL, "can't close driver ID")
+ TAILQ_FOREACH(item, &all_vfds, link) {
+ if (item->exc_owner == file)
+ item->exc_owner = NULL;
+ }
+ TAILQ_REMOVE(&all_vfds, file, link);
+
/* Dispatch to the driver for actual close. If the driver fails to
* close the file then the file will be in an unusable state.
*/
diff --git a/src/H5FDcore.c b/src/H5FDcore.c
index 0551dd0..394380b 100644
--- a/src/H5FDcore.c
+++ b/src/H5FDcore.c
@@ -179,6 +179,7 @@ static const H5FD_class_t H5FD_core_g = {
H5FD__core_truncate, /* truncate */
H5FD_core_lock, /* lock */
H5FD_core_unlock, /* unlock */
+ NULL, /* dedup */
H5FD_FLMAP_DICHOTOMY /* fl_map */
};
diff --git a/src/H5FDfamily.c b/src/H5FDfamily.c
index d110ef7..cc97a0f 100644
--- a/src/H5FDfamily.c
+++ b/src/H5FDfamily.c
@@ -139,6 +139,7 @@ static const H5FD_class_t H5FD_family_g = {
H5FD_family_truncate, /*truncate */
H5FD_family_lock, /*lock */
H5FD_family_unlock, /*unlock */
+ NULL, /*dedup */
H5FD_FLMAP_DICHOTOMY /*fl_map */
};
diff --git a/src/H5FDhdfs.c b/src/H5FDhdfs.c
index 3d086ea..2c06420 100644
--- a/src/H5FDhdfs.c
+++ b/src/H5FDhdfs.c
@@ -521,6 +521,7 @@ static const H5FD_class_t H5FD_hdfs_g = {
H5FD_hdfs_truncate, /* truncate */
H5FD_hdfs_lock, /* lock */
H5FD_hdfs_unlock, /* unlock */
+ NULL, /* dedup */
H5FD_FLMAP_DICHOTOMY /* fl_map */
};
diff --git a/src/H5FDint.c b/src/H5FDint.c
index 8a2148a..97c81ab 100644
--- a/src/H5FDint.c
+++ b/src/H5FDint.c
@@ -183,6 +183,7 @@ H5FD_read(H5FD_t *file, H5FD_mem_t type, haddr_t addr, size_t size, void *buf/*o
* objects being written within the file by the application performing
* SWMR write operations.
*/
+#if 0 /* JRM */
if(!(file->access_flags & H5F_ACC_SWMR_READ)) {
haddr_t eoa;
@@ -192,6 +193,7 @@ H5FD_read(H5FD_t *file, H5FD_mem_t type, haddr_t addr, size_t size, void *buf/*o
if((addr + file->base_addr + size) > eoa)
HGOTO_ERROR(H5E_ARGS, H5E_OVERFLOW, FAIL, "addr overflow, addr = %llu, size = %llu, eoa = %llu", (unsigned long long)(addr + file->base_addr), (unsigned long long)size, (unsigned long long)eoa)
}
+#endif /* JRM */
/* Dispatch to driver */
if((file->cls->read)(file, type, dxpl_id, addr + file->base_addr, size, buf) < 0)
@@ -394,4 +396,3 @@ H5FD_driver_query(const H5FD_class_t *driver, unsigned long *flags/*out*/)
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5FD_driver_query() */
-
diff --git a/src/H5FDlog.c b/src/H5FDlog.c
index 78b7742..0d37260 100644
--- a/src/H5FDlog.c
+++ b/src/H5FDlog.c
@@ -215,6 +215,7 @@ static const H5FD_class_t H5FD_log_g = {
H5FD_log_truncate, /*truncate */
H5FD_log_lock, /*lock */
H5FD_log_unlock, /*unlock */
+ NULL, /*dedup */
H5FD_FLMAP_DICHOTOMY /*fl_map */
};
diff --git a/src/H5FDmulti.c b/src/H5FDmulti.c
index 72f4da5..132c2c3 100644
--- a/src/H5FDmulti.c
+++ b/src/H5FDmulti.c
@@ -170,6 +170,7 @@ static const H5FD_class_t H5FD_multi_g = {
H5FD_multi_truncate, /*truncate */
H5FD_multi_lock, /*lock */
H5FD_multi_unlock, /*unlock */
+ NULL, /*dedup */
H5FD_FLMAP_DEFAULT /*fl_map */
};
diff --git a/src/H5FDpkg.h b/src/H5FDpkg.h
index 22b5d17..903ce06 100644
--- a/src/H5FDpkg.h
+++ b/src/H5FDpkg.h
@@ -55,6 +55,7 @@ H5_DLL herr_t H5FD__free_real(H5FD_t *file, H5FD_mem_t type, haddr_t addr, hsize
/* Testing functions */
#ifdef H5FD_TESTING
H5_DLL hbool_t H5FD__supports_swmr_test(const char *vfd_name);
+H5_DLL herr_t H5FD__vfd_swmr_reader_md_test(H5FD_t *file, unsigned num_entries, H5FD_vfd_swmr_idx_entry_t index[]);
#endif /* H5FD_TESTING */
#endif /* _H5FDpkg_H */
diff --git a/src/H5FDprivate.h b/src/H5FDprivate.h
index 2e3d3ce..9f5309d 100644
--- a/src/H5FDprivate.h
+++ b/src/H5FDprivate.h
@@ -36,7 +36,205 @@
/**************************/
/* Length of filename buffer */
-#define H5FD_MAX_FILENAME_LEN 1024
+#define H5FD_MAX_FILENAME_LEN 1024
+
+/*
+ * VFD SWMR
+ */
+/* Metadata file header */
+#define H5FD_MD_HEADER_OFF 0 /* Header offset in the metadata file */
+#define H5FD_MD_HEADER_MAGIC "VHDR" /* Header magic */
+#define H5FD_SIZEOF_CHKSUM 4 /* Size of checksum */
+
+/* Size of the header in the metadata file */
+#define H5FD_MD_HEADER_SIZE \
+ ( \
+ H5_SIZEOF_MAGIC /* Signature */ \
+ + 4 /* Page size */ \
+ + 8 /* Tick number */ \
+ + 8 /* Index offset */ \
+ + 8 /* Index length number */ \
+ + H5FD_SIZEOF_CHKSUM /* Metadata header checksum */ \
+ )
+
+/* Size of an index entry in the metadata file */
+#define H5FD_MD_INDEX_ENTRY_SIZE \
+ ( \
+ 4 /* HDF5 file page offset */ \
+ + 4 /* Metadata file page offset */ \
+ + 4 /* Length */ \
+ + H5FD_SIZEOF_CHKSUM /* Index entry checksum */ \
+ )
+
+/* Metadata file index magic */
+#define H5FD_MD_INDEX_MAGIC "VIDX" /* Index magic */
+
+/* Size of the metadata file index */
+#define H5FD_MD_INDEX_SIZE(N) /* N is number of entries in index */ \
+ ( \
+ H5_SIZEOF_MAGIC /* Signature */ \
+ + 8 /* Tick num */ \
+ + 4 /* Number of entries */ \
+ + (N * H5FD_MD_INDEX_ENTRY_SIZE) /* Index entries */ \
+ + H5FD_SIZEOF_CHKSUM /* Metadata index checksum */ \
+ )
+
+/* Retries for metadata file */
+#define H5FD_VFD_SWMR_MD_FILE_RETRY_MAX 50 /* Maximum retries when opening the MD file */
+#define H5FD_VFD_SWMR_MD_LOAD_RETRY_MAX 120 /* Maximum retries when trying to load the MD file header and index */
+#define H5FD_VFD_SWMR_MD_INDEX_RETRY_MAX 5 /* Maximum retries when deserializing the MD file index */
+
+
+
+/* Internal representation of metadata file index entry */
+
+/*----------------------------------------------------------------------------
+ *
+ * struct H5FD_vfd_swmr_idx_entry_t
+ *
+ * Indicies into the VFD SWMR metadata file are maintained in arrays of
+ * instances of H5FD_vfd_swmr_index_t.
+ *
+ * The fields of H5FD_vfd_swmr_idx_entry_t are discussed below.
+ *
+ * hdf5_page_offset: Unsigned 64-bit value containing the base address of the
+ * metadata page, or multi page metadata entry in the HDF5
+ * file IN PAGES.
+ *
+ * To obtain byte offset, multiply this value by the page size.
+ *
+ * md_file_page_offset: Unsigned 64-bit value containing the base address of
+ * the metadata page, or multi page metadata entry in the metadata
+ * file IN PAGES.
+ *
+ * To obtain byte offset, multiply this value by the page size.
+ *
+ * length: The length of the metadata page or multi- page metadata entry
+ * in BYTES.
+ *
+ * chksum: Checksum for the metadata page or multi-page metadata entry.
+ * For the VFD SWMR writer, this value is undefined until the
+ * referenced entry has been written to the metadata file.
+ *
+ * entry_ptr: Used by the VFD SWMR writer only.
+ *
+ * For the VFD SWMR reader, this field should always be NULL.
+ * If the referenced metadata page or multi-page metadata
+ * entry was modified in the current tick, this field points to
+ * a buffer in the page buffer containing its value.
+ * This field is used by the metadata file creation/update code
+ * to access the metadata pages or multi-page metadata entries
+ * so that their current values can be copied into the metadata
+ * file. After this copy, this field should be set to NULL.
+ *
+ * tick_of_last_change: Number of the last tick in which this index entry
+ * was changed.
+ *
+ * Used by the VFD SWMR writer only.
+ *
+ * For the VFD SWMR reader, this field will always be set to 0.
+ *
+ * clean: Used by the VFD SWMR writer only.
+ *
+ * Set to TRUE whenever the referenced metadata page or
+ * multi-page metadata entry is written to the HDF5 file.
+ * Set to FALSE whenever it is marked dirty in the page buffer.
+ *
+ * tick_of_last_flush: Number of the tick in which this entry was last
+ * written to the lower file or zero if it has never been flushed.
+ *
+ * Used by the VFD SWMR writer only.
+ *
+ * For the VFD SWMR reader, this field should always be 0.
+ *
+ * delayed_flush: If the flush of the referenced metadata page or multi-page
+ * metadata entry must be delayed, the earliest tick in which
+ * it may be flushed, or zero if there is no such constraint.
+ *
+ * Used by the VFD SWMR writer only.
+ *
+ * moved_to_lower_file: Set to TRUE iff the entry referenced is in the
+ * lower file and is therefore about to be removed from the
+ * metadata file
+ *
+ *----------------------------------------------------------------------------
+ */
+typedef struct H5FD_vfd_swmr_idx_entry_t {
+ uint64_t hdf5_page_offset;
+ uint64_t md_file_page_offset;
+ uint32_t length;
+ uint32_t chksum;
+ void *entry_ptr;
+ uint64_t tick_of_last_change;
+ hbool_t clean;
+ uint64_t tick_of_last_flush;
+ uint64_t delayed_flush;
+ bool moved_to_lower_file;
+ bool garbage;
+} H5FD_vfd_swmr_idx_entry_t;
+
+/*
+ * tick_num: Sequence number of the current tick.
+ * Initialized to zero on file creation/open, and incremented by the
+ * VFD SWMR writer at the end of each tick.
+ * num_entries: The number of entires in the index.
+ * entries: The array of index entries
+ */
+typedef struct H5FD_vfd_swmr_md_index {
+ uint64_t tick_num;
+ uint32_t num_entries;
+ H5FD_vfd_swmr_idx_entry_t *entries;
+} H5FD_vfd_swmr_md_index;
+
+
+/*
+ * fs_page_size: Size of pages in both the HDF5 file and the metadata file IN BYTES
+ * tick_num: Sequence number of the current tick.
+ * Initialized to zero on file creation/open, and incremented by the
+ * VFD SWMR writer at the end of each tick.
+ * index_offset: The offset of the current metadata file index in the metadata file
+ * IN BYTES.
+ * index_length: The length of the current metadata file index IN BYTES.
+ */
+typedef struct H5FD_vfd_swmr_md_header {
+ uint32_t fs_page_size;
+ uint64_t tick_num;
+ uint64_t index_offset;
+ size_t index_length;
+} H5FD_vfd_swmr_md_header;
+
+static inline H5FD_vfd_swmr_idx_entry_t *
+vfd_swmr_pageno_to_mdf_idx_entry(H5FD_vfd_swmr_idx_entry_t *idx,
+ uint32_t nindices, uint64_t target_page, bool reuse_garbage)
+{
+ uint32_t top;
+ uint32_t bottom;
+ uint32_t probe;
+
+ if (nindices < 1)
+ return NULL;
+
+ bottom = 0;
+ top = nindices;
+
+ do {
+ probe = (top + bottom) / 2;
+
+ if (idx[probe].hdf5_page_offset < target_page)
+ bottom = probe + 1;
+ else if (idx[probe].hdf5_page_offset > target_page)
+ top = probe;
+ else /* found it */
+ return (reuse_garbage || !idx[probe].garbage) ? &idx[probe] : NULL;
+ } while (bottom < top);
+ /* Previous interval was [top - 1, top] or [bottom, bottom + 1].
+ * The new interval is [top, top] or [bottom, bottom], respectively.
+ * We probed idx[bottom] in the last step, and idx[top] (if it is
+ * not out of bounds) in an earlier round. So there is nothing
+ * to be found at (top + bottom) / 2.
+ */
+ return NULL;
+}
#ifdef H5_HAVE_PARALLEL
/* ======== Temporary data transfer properties ======== */
@@ -119,6 +317,8 @@ H5_DLL herr_t H5FD_free_driver_info(hid_t driver_id, const void *driver_info);
H5_DLL hid_t H5FD_register(const void *cls, size_t size, hbool_t app_ref);
H5_DLL H5FD_t *H5FD_open(const char *name, unsigned flags, hid_t fapl_id,
haddr_t maxaddr);
+bool H5FD_has_conflict(H5FD_t *);
+H5FD_t *H5FD_deduplicate(H5FD_t *, hid_t);
H5_DLL herr_t H5FD_close(H5FD_t *file);
H5_DLL int H5FD_cmp(const H5FD_t *f1, const H5FD_t *f2);
H5_DLL herr_t H5FD_driver_query(const H5FD_class_t *driver, unsigned long *flags/*out*/);
@@ -146,6 +346,18 @@ H5_DLL herr_t H5FD_get_vfd_handle(H5FD_t *file, hid_t fapl, void** file_handle);
H5_DLL herr_t H5FD_set_base_addr(H5FD_t *file, haddr_t base_addr);
H5_DLL haddr_t H5FD_get_base_addr(const H5FD_t *file);
H5_DLL herr_t H5FD_set_paged_aggr(H5FD_t *file, hbool_t paged);
+H5_DLL herr_t H5FD_get_driver_name(const H5FD_t *file, char **driver_name);
+
+/* Function prototypes for VFD SWMR */
+H5_DLL int shadow_image_defer_free(struct H5F_shared_t *,
+ const H5FD_vfd_swmr_idx_entry_t *);
+H5_DLL herr_t H5FD_vfd_swmr_get_tick_and_idx(H5FD_t *_file, hbool_t read_index,
+ uint64_t *tick_ptr, uint32_t *num_entries_ptr,
+ H5FD_vfd_swmr_idx_entry_t index[]);
+H5_DLL H5FD_vfd_swmr_idx_entry_t *vfd_swmr_enlarge_shadow_index(struct H5F_t *);
+H5_DLL void H5FD_vfd_swmr_dump_status(H5FD_t *, uint64_t);
+H5_DLL void H5FD_vfd_swmr_set_pb_configured(H5FD_t *_file);
+H5_DLL void H5FD_vfd_swmr_record_elapsed_ticks(H5FD_t *, uint64_t);
/* Function prototypes for MPI based VFDs*/
#ifdef H5_HAVE_PARALLEL
diff --git a/src/H5FDpublic.h b/src/H5FDpublic.h
index 61bf212..a921c29 100644
--- a/src/H5FDpublic.h
+++ b/src/H5FDpublic.h
@@ -18,6 +18,7 @@
#ifndef _H5FDpublic_H
#define _H5FDpublic_H
+#include "H5queue.h"
#include "H5public.h"
#include "H5Fpublic.h" /*for H5F_close_degree_t */
@@ -302,6 +303,7 @@ typedef struct H5FD_class_t {
herr_t (*truncate)(H5FD_t *file, hid_t dxpl_id, hbool_t closing);
herr_t (*lock)(H5FD_t *file, hbool_t rw);
herr_t (*unlock)(H5FD_t *file);
+ H5FD_t *(*dedup)(H5FD_t *, H5FD_t *, hid_t);
H5FD_mem_t fl_map[H5FD_MEM_NTYPES];
} H5FD_class_t;
@@ -319,6 +321,11 @@ typedef struct H5FD_free_t {
struct H5FD_t {
hid_t driver_id; /*driver ID for this file */
const H5FD_class_t *cls; /*constant class info */
+
+ TAILQ_ENTRY(H5FD_t) link; /* Linkage for list of all VFs. */
+ H5FD_t *exc_owner; /* Pointer to an exclusive owner
+ * or NULL if none.
+ */
unsigned long fileno; /* File 'serial' number */
unsigned access_flags; /* File access flags (from create or open) */
unsigned long feature_flags; /* VFL Driver feature Flags */
diff --git a/src/H5FDsec2.c b/src/H5FDsec2.c
index 3551905..37a6ae9 100644
--- a/src/H5FDsec2.c
+++ b/src/H5FDsec2.c
@@ -171,6 +171,7 @@ static const H5FD_class_t H5FD_sec2_g = {
H5FD_sec2_truncate, /* truncate */
H5FD_sec2_lock, /* lock */
H5FD_sec2_unlock, /* unlock */
+ NULL, /* dedup */
H5FD_FLMAP_DICHOTOMY /* fl_map */
};
diff --git a/src/H5FDsplitter.c b/src/H5FDsplitter.c
index 4ed3c4a..fae4bb4 100644
--- a/src/H5FDsplitter.c
+++ b/src/H5FDsplitter.c
@@ -164,6 +164,7 @@ static const H5FD_class_t H5FD_splitter_g = {
H5FD_splitter_truncate, /* truncate */
H5FD_splitter_lock, /* lock */
H5FD_splitter_unlock, /* unlock */
+ NULL, /* dedup */
H5FD_FLMAP_DICHOTOMY /* fl_map */
};
diff --git a/src/H5FDstdio.c b/src/H5FDstdio.c
index d29a1b4..3135709 100644
--- a/src/H5FDstdio.c
+++ b/src/H5FDstdio.c
@@ -209,6 +209,7 @@ static const H5FD_class_t H5FD_stdio_g = {
H5FD_stdio_truncate, /* truncate */
H5FD_stdio_lock, /* lock */
H5FD_stdio_unlock, /* unlock */
+ NULL, /* dedup */
H5FD_FLMAP_DICHOTOMY /* fl_map */
};
diff --git a/src/H5FDtest.c b/src/H5FDtest.c
index 2eb176d..53e31c7 100644
--- a/src/H5FDtest.c
+++ b/src/H5FDtest.c
@@ -25,8 +25,8 @@
/* Module Setup */
/****************/
-#include "H5FDmodule.h" /* This source code file is part of the H5FD module */
-#define H5FD_TESTING /* Suppress warning about H5FD testing funcs */
+#include "H5FDmodule.h" /* This source code file is part of the H5FD module */
+#define H5FD_TESTING /* Suppress warning about H5FD testing funcs */
/***********/
@@ -34,6 +34,8 @@
/***********/
#include "H5private.h" /* Generic Functions */
#include "H5FDpkg.h" /* File Drivers */
+#include "H5FLprivate.h" /* Free Lists */
+#include "H5Eprivate.h" /* Error handling */
/****************/
/* Local Macros */
@@ -63,6 +65,8 @@
/*****************************/
/* Library Private Variables */
/*****************************/
+/* Declare external the free list for H5FD_vfd_swmr_idx_entry_t */
+H5FL_SEQ_EXTERN(H5FD_vfd_swmr_idx_entry_t);
/*******************/
@@ -113,3 +117,67 @@ H5FD__supports_swmr_test(const char *vfd_name)
} /* end H5FD__supports_swmr_test() */
+/*
+ * Tests for VFD SWMR
+ */
+/*-------------------------------------------------------------------------
+ * Function: H5FD__vfd_swmr_md_test
+ *
+ * Purpose: Verify the info obtained from the driver's local copy is as
+ * indicated by the parameter: num_entries and index
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5FD__vfd_swmr_reader_md_test(H5FD_t *file, unsigned num_entries, H5FD_vfd_swmr_idx_entry_t index[])
+{
+ unsigned vfd_num_entries = 0;
+ H5FD_vfd_swmr_idx_entry_t *vfd_index = NULL;
+ unsigned i;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* Retrieve index from VFD SWMR driver */
+ /* Initial call to get # of entries */
+ if(H5FD_vfd_swmr_get_tick_and_idx(file, TRUE, NULL, &vfd_num_entries, vfd_index) < 0)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "Error in retrieving index from driver")
+
+ /* Verify number of index entries */
+ if(vfd_num_entries != num_entries)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "Error in retrieving index from driver")
+
+ if(vfd_num_entries) {
+ /* Allocate memory for index entries */
+ if(NULL == (vfd_index = H5FL_SEQ_MALLOC(H5FD_vfd_swmr_idx_entry_t, vfd_num_entries)))
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL, "memory allocation failed for index entries")
+
+ /* Second call to retrieve the index */
+ if(H5FD_vfd_swmr_get_tick_and_idx(file, FALSE, NULL, &vfd_num_entries, vfd_index) < 0)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "Error in retrieving index from driver")
+
+ /* Verify index entries */
+ for(i = 0; i < vfd_num_entries; i++) {
+ if(vfd_index[i].length != index[i].length)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect length read from metadata file")
+
+ if(vfd_index[i].hdf5_page_offset != index[i].hdf5_page_offset)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect hdf5_page_offset read from metadata file")
+
+ if(vfd_index[i].md_file_page_offset != index[i].md_file_page_offset)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect md_file_page_offset read from metadata file")
+
+ if(vfd_index[i].chksum != index[i].chksum)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect chksum read from metadata file")
+ }
+ }
+
+done:
+ /* Free local copy of index entries */
+ if(vfd_num_entries && vfd_index)
+ vfd_index = (H5FD_vfd_swmr_idx_entry_t *)H5FL_SEQ_FREE(H5FD_vfd_swmr_idx_entry_t, vfd_index);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5FD__vfd_swmr_reader_md_test() */
diff --git a/src/H5FDvfd_swmr.c b/src/H5FDvfd_swmr.c
new file mode 100644
index 0000000..f0e0cfd
--- /dev/null
+++ b/src/H5FDvfd_swmr.c
@@ -0,0 +1,1607 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the COPYING file, which can be found at the root of the source code *
+ * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. *
+ * If you do not have access to either file, you may request a copy from *
+ * help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Purpose: VFD SWMR driver for the reader
+ */
+
+#include "H5FDdrvr_module.h" /* This source code file is part of the H5FD driver module */
+
+
+#include "H5Eprivate.h" /* Error handling */
+#include "H5Fprivate.h" /* File access */
+#include "H5FDprivate.h" /* File drivers */
+#include "H5FDvfd_swmr.h" /* VFD SWMR file driver */
+#include "H5FLprivate.h" /* Free Lists */
+#include "H5Iprivate.h" /* IDs */
+#include "H5MMprivate.h" /* Memory management */
+#include "H5Pprivate.h" /* Property lists */
+#include "H5retry_private.h"/* Retry loops. */
+
+/* The driver identification number, initialized at runtime */
+static hid_t H5FD_VFD_SWMR_g = 0;
+
+typedef struct H5FD_vfd_swmr_t {
+ H5FD_t pub; /* public stuff, must be */
+ /* first */
+
+ /* HDF5 file */
+ char hdf5_filename[H5FD_MAX_FILENAME_LEN]; /* Name of the HDF5 file from */
+ /* open */
+ H5FD_t *hdf5_file_lf; /* Driver info for the HDF5 */
+ /* file */
+
+ /* Metadata file */
+ int md_fd; /* File descriptor for the */
+ /* metadata file */
+ uint32_t md_pages_reserved; /* # of pages reserved at the */
+ /* head of the metadata file */
+ char md_file_path[H5FD_MAX_FILENAME_LEN]; /* Name of the metadate file */
+ H5FD_vfd_swmr_md_header md_header; /* Metadata file header */
+ H5FD_vfd_swmr_md_index md_index; /* Metadata file index */
+
+ uint32_t api_elapsed_nslots;
+ uint64_t *api_elapsed_ticks; /* Histogram of ticks elapsed
+ * inside the API (reader only).
+ */
+ hbool_t pb_configured; /* boolean flag set to TRUE */
+ /* when the page buffer is */
+ /* and to FALSE otherwise. */
+ /* Used for sanity checking. */
+ H5F_vfd_swmr_config_t config;
+ bool writer; /* True iff configured to write. */
+} H5FD_vfd_swmr_t;
+
+#define MAXADDR (((haddr_t)1<<(8*sizeof(HDoff_t)-1))-1)
+
+/* Prototypes */
+static herr_t H5FD_vfd_swmr_term(void);
+static H5FD_t *H5FD_vfd_swmr_open(const char *name, unsigned flags,
+ hid_t fapl_id, haddr_t maxaddr);
+static herr_t H5FD_vfd_swmr_close(H5FD_t *_file);
+static int H5FD_vfd_swmr_cmp(const H5FD_t *_f1, const H5FD_t *_f2);
+static H5FD_t *H5FD_vfd_swmr_dedup(H5FD_t *, H5FD_t *, hid_t);
+static herr_t H5FD_vfd_swmr_query(const H5FD_t *_f1, unsigned long *flags);
+static haddr_t H5FD_vfd_swmr_get_eoa(const H5FD_t *_file, H5FD_mem_t type);
+static herr_t H5FD_vfd_swmr_set_eoa(H5FD_t *_file, H5FD_mem_t type,
+ haddr_t addr);
+static haddr_t H5FD_vfd_swmr_get_eof(const H5FD_t *_file, H5FD_mem_t type);
+static herr_t H5FD_vfd_swmr_get_handle(H5FD_t *_file, hid_t fapl,
+ void** file_handle);
+static herr_t H5FD_vfd_swmr_read(H5FD_t *_file, H5FD_mem_t type,
+ hid_t fapl_id, haddr_t addr, size_t size, void *buf);
+static herr_t H5FD_vfd_swmr_write(H5FD_t *_file, H5FD_mem_t type,
+ hid_t fapl_id, haddr_t addr, size_t size, const void *buf);
+static herr_t H5FD_vfd_swmr_truncate(H5FD_t *_file, hid_t dxpl_id,
+ hbool_t closing);
+static herr_t H5FD_vfd_swmr_lock(H5FD_t *_file, hbool_t rw);
+static herr_t H5FD_vfd_swmr_unlock(H5FD_t *_file);
+
+/* VFD SWMR */
+static htri_t H5FD__vfd_swmr_header_deserialize(H5FD_vfd_swmr_t *,
+ H5FD_vfd_swmr_md_header *);
+static htri_t H5FD__vfd_swmr_index_deserialize(const H5FD_vfd_swmr_t *file,
+ H5FD_vfd_swmr_md_index *md_index, const H5FD_vfd_swmr_md_header *md_header);
+static herr_t H5FD__vfd_swmr_load_hdr_and_idx(H5FD_vfd_swmr_t *, hbool_t);
+
+HLOG_OUTLET_SHORT_DEFN(index_motion, swmr);
+HLOG_OUTLET_SHORT_DEFN(swmr_stats, swmr);
+HLOG_OUTLET_SHORT_DEFN(swmr_read, swmr);
+HLOG_OUTLET_SHORT_DEFN(swmr_read_exception, swmr_read);
+HLOG_OUTLET_MEDIUM_DEFN(swmr_read_err, swmr_read_exception, HLOG_OUTLET_S_ON);
+
+static const H5FD_class_t H5FD_vfd_swmr_g = {
+ "vfd_swmr", /* name */
+ MAXADDR, /* maxaddr */
+ H5F_CLOSE_WEAK, /* fc_degree */
+ H5FD_vfd_swmr_term, /* terminate */
+ NULL, /* sb_size */
+ NULL, /* sb_encode */
+ NULL, /* sb_decode */
+ 0, /* fapl_size */
+ NULL, /* fapl_get */
+ NULL, /* fapl_copy */
+ NULL, /* fapl_free */
+ 0, /* dxpl_size */
+ NULL, /* dxpl_copy */
+ NULL, /* dxpl_free */
+ H5FD_vfd_swmr_open, /* open */
+ H5FD_vfd_swmr_close, /* close */
+ H5FD_vfd_swmr_cmp, /* cmp */
+ H5FD_vfd_swmr_query, /* query */
+ NULL, /* get_type_map */
+ NULL, /* alloc */
+ NULL, /* free */
+ H5FD_vfd_swmr_get_eoa, /* get_eoa */
+ H5FD_vfd_swmr_set_eoa, /* set_eoa */
+ H5FD_vfd_swmr_get_eof, /* get_eof */
+ H5FD_vfd_swmr_get_handle, /* get_handle */
+ H5FD_vfd_swmr_read, /* read */
+ H5FD_vfd_swmr_write, /* write */
+ NULL, /* flush */
+ H5FD_vfd_swmr_truncate, /* truncate */
+ H5FD_vfd_swmr_lock, /* lock */
+ H5FD_vfd_swmr_unlock, /* unlock */
+ H5FD_vfd_swmr_dedup, /* dedup */
+ H5FD_FLMAP_DICHOTOMY /* fl_map */
+};
+
+/* Declare a free list to manage the H5FD_vfd_swmr_t struct */
+H5FL_DEFINE_STATIC(H5FD_vfd_swmr_t);
+
+/* Declare a free list to manage the H5FD_vfd_swmr_idx_entry_t sequence information */
+H5FL_SEQ_DEFINE(H5FD_vfd_swmr_idx_entry_t);
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD__init_package
+ *
+ * Purpose: Initializes any interface-specific data or routines.
+ *
+b
+ * Return: Non-negative on success/Negative on failure
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD__init_package(void)
+{
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ if(H5FD_vfd_swmr_init() < 0)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "unable to initialize swmr VFD")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5FD__init_package() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_init
+ *
+ * Purpose: Initialize this driver by registering the driver with the
+ * library.
+ *
+ * Return: Success: The driver ID for the VFD SWMR driver.
+ * Failure: Negative
+ *
+ * Programmer: Robb Matzke
+ * Thursday, July 29, 1999
+ *
+ *-------------------------------------------------------------------------
+ */
+hid_t
+H5FD_vfd_swmr_init(void)
+{
+ hid_t ret_value = H5I_INVALID_HID; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ if(H5I_VFL != H5I_get_type(H5FD_VFD_SWMR_g))
+ H5FD_VFD_SWMR_g = H5FD_register(&H5FD_vfd_swmr_g, sizeof(H5FD_class_t), FALSE);
+
+ /* Set return value */
+ ret_value = H5FD_VFD_SWMR_g;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FD_vfd_swmr_init() */
+
+
+/*---------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_term
+ *
+ * Purpose: Shut down the VFD
+ *
+ * Returns: SUCCEED (Can't fail)
+ *
+ * Programmer: Quincey Koziol
+ * Friday, Jan 30, 2004
+ *
+ *---------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_vfd_swmr_term(void)
+{
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Reset VFL ID */
+ H5FD_VFD_SWMR_g = 0;
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5FD_vfd_swmr_term() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5Pset_fapl_vfd_swmr (Not yet)
+ *
+ * Purpose: Modify the file access property list to use the H5FD_SWMR
+ * driver
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Pset_fapl_vfd_swmr(hid_t fapl_id)
+{
+ H5P_genplist_t *plist; /* Property list pointer */
+ herr_t ret_value;
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE1("e", "i", fapl_id);
+
+ if(NULL == (plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file access property list")
+
+ ret_value = H5P_set_driver(plist, H5FD_VFD_SWMR, NULL);
+
+done:
+ FUNC_LEAVE_API(ret_value)
+} /* end H5Pset_fapl_vfd_swmr() */
+
+static herr_t
+H5FD__swmr_reader_open(H5FD_vfd_swmr_t *file)
+{
+ h5_retry_t retry; /* retry state */
+ bool do_try; /* more tries remain */
+ herr_t ret_value = SUCCEED;
+ FUNC_ENTER_STATIC
+
+ file->api_elapsed_nslots = file->config.max_lag + 1;
+
+ file->api_elapsed_ticks =
+ calloc(file->api_elapsed_nslots, sizeof(*file->api_elapsed_ticks));
+
+ if (file->api_elapsed_ticks == NULL) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL,
+ "could not allocate API elapsed ticks");
+ }
+
+ /* Retry on opening the metadata file */
+ for (do_try = h5_retry_init(&retry, H5FD_VFD_SWMR_MD_FILE_RETRY_MAX,
+ H5_RETRY_DEFAULT_MINIVAL,
+ H5_RETRY_DEFAULT_MAXIVAL);
+ do_try;
+ do_try = h5_retry_next(&retry)) {
+ if((file->md_fd = HDopen(file->md_file_path, O_RDONLY)) >= 0)
+ break;
+ }
+
+ /* Exhaust all retries for opening the md file */
+ if(!do_try)
+ HGOTO_ERROR(H5E_VFL, H5E_OPENERROR, FAIL,
+ "unable to open the metadata file after all retry attempts");
+
+ /* Retry on loading and decoding the header and index in the
+ * metadata file
+ */
+ if(H5FD__vfd_swmr_load_hdr_and_idx(file, TRUE) < 0)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTGET, FAIL,
+ "unable to load/decode the md file header/index");
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_open
+ *
+ * Purpose: Open the metadata file and the underlying HDF5 file
+ *
+ * Return: Success: A pointer to a new file data structure. The
+ * public fields will be initialized by the
+ * caller, which is always H5FD_open().
+ * Failure: NULL
+ *
+ *-------------------------------------------------------------------------
+ */
+static H5FD_t *
+H5FD_vfd_swmr_open(const char *name, unsigned flags, hid_t fapl_id,
+ haddr_t maxaddr)
+{
+ H5FD_vfd_swmr_t *file = NULL;
+ size_t page_buf_size;
+ H5P_genplist_t *plist;
+ H5F_vfd_swmr_config_t *vfd_swmr_config;
+ H5FD_t *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* Get file access property list */
+ if(NULL == (plist = (H5P_genplist_t *)H5I_object(fapl_id))) {
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL,
+ "not a file access property list");
+ }
+
+ if (H5P_get(plist, H5F_ACS_PAGE_BUFFER_SIZE_NAME, &page_buf_size) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "can't get page buffer size");
+
+ /* Paged allocation, too, has to be enabled, but the page buffer
+ * initialization (H5PB_create) will detect a conflicting configuration
+ * and return an error.
+ */
+ if (page_buf_size == 0) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL,
+ "page buffering must be enabled");
+ }
+
+ /* Create the new driver struct */
+ if(NULL == (file = H5FL_CALLOC(H5FD_vfd_swmr_t))) {
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL,
+ "unable to allocate file struct");
+ }
+
+ vfd_swmr_config = &file->config;
+
+ /* Get VFD SWMR configuration */
+ if(H5P_get(plist, H5F_ACS_VFD_SWMR_CONFIG_NAME, vfd_swmr_config) < 0) {
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL,
+ "can't get VFD SWMR config info");
+ }
+
+ file->md_fd = -1;
+ file->hdf5_file_lf = NULL;
+ file->md_pages_reserved = vfd_swmr_config->md_pages_reserved;
+
+ /* Retain a copy of the name used to open the HDF5 file */
+ HDstrncpy(file->hdf5_filename, name, sizeof(file->hdf5_filename));
+ file->hdf5_filename[sizeof(file->hdf5_filename) - 1] = '\0';
+
+ /* Retain a copy of the metadata file name */
+ HDstrncpy(file->md_file_path, vfd_swmr_config->md_file_path,
+ sizeof(file->md_file_path));
+ file->md_file_path[sizeof(file->md_file_path) - 1] = '\0';
+
+ file->writer = vfd_swmr_config->writer;
+
+ /* Ensure that this is the reader */
+ if (!vfd_swmr_config->writer && H5FD__swmr_reader_open(file) < 0) {
+ HGOTO_ERROR(H5E_VFL, H5E_OPENERROR, NULL,
+ "perform reader-specific opening steps failed");
+ }
+
+ /* Hard-wired to open the underlying HDF5 file with SEC2 */
+ if((file->hdf5_file_lf = H5FD_open(name, flags, H5P_FILE_ACCESS_DEFAULT,
+ maxaddr)) == NULL)
+ HGOTO_ERROR(H5E_VFL, H5E_CANTOPENFILE, NULL, "can't set driver info");
+
+ file->hdf5_file_lf->exc_owner = &file->pub;
+
+ /* set pb_configured to FALSE. This field should not exist, but
+ * until we modify the file open procedure to create the page buffer
+ * before there is any file I/O when opening a file VFD SWMR reader,
+ * we need to be able to turn off sanity checking in the read function
+ * until the page buffer is enabled. This field exists for this
+ * purpose, and should be remove when it is no longer necessary.
+ *
+ * JRM -- 1/29/19
+ */
+ file->pb_configured = FALSE;
+
+ /* Set return value */
+ ret_value = &file->pub;
+
+done:
+ /* Handle closing if error */
+ if(NULL == ret_value && file) {
+
+ if(H5FD_vfd_swmr_close(&file->pub) < 0)
+
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, NULL, "error from closing")
+
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FD_vfd_swmr_open() */
+
+static void
+swmr_reader_close(H5FD_vfd_swmr_t *file)
+{
+ vfd_swmr_reader_did_increase_tick_to(0);
+
+ if (file->api_elapsed_ticks != NULL) {
+ uint32_t i;
+ for (i = 0; i < file->api_elapsed_nslots; i++) {
+ hlog_fast(swmr_stats,
+ "%s: %" PRIu32 " ticks elapsed in API %" PRIu64 " times",
+ __func__, i, file->api_elapsed_ticks[i]);
+ }
+ free(file->api_elapsed_ticks);
+ }
+
+ /* Close the metadata file */
+ if(file->md_fd >= 0 && HDclose(file->md_fd) < 0) {
+ /* Push error, but keep going */
+ HERROR(H5E_FILE, H5E_CANTCLOSEFILE,
+ "unable to close the metadata file");
+ }
+
+ /* Free the index entries */
+ if(file->md_index.num_entries && file->md_index.entries)
+ file->md_index.entries = H5FL_SEQ_FREE(H5FD_vfd_swmr_idx_entry_t,
+ file->md_index.entries);
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_close
+ *
+ * Purpose: Handle closing for VFD SWMR driver
+ * --close the underlying HDF5 file
+ * --close the metadata file if open
+ * --free the index entries if available
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_vfd_swmr_close(H5FD_t *_file)
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ if (file->hdf5_file_lf != NULL) {
+ if (file->hdf5_file_lf->exc_owner != NULL) {
+ assert(file->hdf5_file_lf->exc_owner == &file->pub);
+ file->hdf5_file_lf->exc_owner = NULL;
+ }
+
+ /* Close the underlying file */
+ if (H5FD_close(file->hdf5_file_lf) < 0)
+ /* Push error, but keep going */
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, \
+ "unable to close the HDF5 file")
+ }
+
+ if (!file->writer)
+ (void)swmr_reader_close(file);
+
+ /* Release the driver info */
+ file = H5FL_FREE(H5FD_vfd_swmr_t, file);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* end H5FD_vfd_swmr_close() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_cmp
+ *
+ * Purpose: Compares two files belonging to this driver using an
+ * arbitrary (but consistent) ordering.
+ *
+ * Return: Success: A value like strcmp()
+ * Failure: never fails (arguments were checked by the
+ * caller).
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+H5FD_vfd_swmr_cmp(const H5FD_t *_f1, const H5FD_t *_f2)
+{
+ const H5FD_vfd_swmr_t *f1 = (const H5FD_vfd_swmr_t *)_f1;
+ const H5FD_vfd_swmr_t *f2 = (const H5FD_vfd_swmr_t *)_f2;
+ int ret_value = 0;
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ ret_value = H5FD_cmp(f1->hdf5_file_lf, f2->hdf5_file_lf);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FD_vfd_swmr_cmp() */
+
+static H5FD_t *
+H5FD_vfd_swmr_dedup(H5FD_t *_self, H5FD_t *_other, hid_t fapl)
+{
+ H5FD_vfd_swmr_t *self = (H5FD_vfd_swmr_t *)_self;
+
+ assert(_self->driver_id == H5FD_VFD_SWMR_g);
+
+ if (_self->cls == _other->cls) {
+ H5FD_vfd_swmr_t *other = (H5FD_vfd_swmr_t *)_other;
+ H5P_genplist_t *plist;
+ H5F_vfd_swmr_config_t *config;
+ bool equal_configs;
+
+ if (H5FD_cmp(self->hdf5_file_lf, other->hdf5_file_lf) != 0)
+ return _other;
+
+ /* If fapl == _ANY_VFD, then the match between lower files is
+ * sufficient.
+ */
+ if (fapl == H5P_FILE_ACCESS_ANY_VFD)
+ return _self;
+
+ /* If fapl != _ANY_VFD, then we have either a duplicate or
+ * a conflict. If the VFD SWMR parameters match, then
+ * return `self` to indicate a duplicate. Otherwise, return
+ * NULL to indicate a mismatch.
+ */
+ if (NULL == (plist = H5I_object(fapl))) {
+ HERROR(H5E_ARGS, H5E_BADTYPE, "could not get fapl");
+ return NULL;
+ }
+
+ if ((config = malloc(sizeof(*config))) == NULL) {
+ HERROR(H5E_ARGS, H5E_BADTYPE, "could not allocate config");
+ return NULL;
+ }
+ if (H5P_get(plist, H5F_ACS_VFD_SWMR_CONFIG_NAME, config) < 0) {
+ HERROR(H5E_PLIST, H5E_CANTGET, "cannot get VFD SWMR config");
+ return NULL;
+ }
+
+ equal_configs = memcmp(&self->config, config, sizeof(*config)) == 0;
+
+ free(config);
+
+ if (equal_configs)
+ return _self;
+
+ HERROR(H5E_PLIST, H5E_CANTGET, "inconsistent VFD SWMR config");
+ return NULL;
+ } else if (H5FD_cmp(self->hdf5_file_lf, _other) == 0) {
+ return (fapl == H5P_FILE_ACCESS_ANY_VFD) ? _self : NULL;
+ } else {
+ return _other;
+ }
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_query
+ *
+ * Purpose: Set the flags that this VFL driver is capable of supporting.
+ * (listed in H5FDpublic.h)
+ *
+ * Return: SUCCEED (Can't fail)
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_vfd_swmr_query(const H5FD_t H5_ATTR_UNUSED *_file, unsigned long *flags /* out */)
+{
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Set the VFL feature flags that this driver supports */
+ if(flags) {
+ *flags = 0;
+ *flags |= H5FD_FEAT_AGGREGATE_METADATA; /* OK to aggregate */
+ /* metadata allocations */
+
+ *flags |= H5FD_FEAT_ACCUMULATE_METADATA; /* OK to accumulate */
+ /* metadata for faster */
+ /* writes */
+
+ *flags |= H5FD_FEAT_DATA_SIEVE; /* OK to perform data */
+ /* sieving for faster */
+ /* raw data reads & */
+ /* writes */
+
+ *flags |= H5FD_FEAT_AGGREGATE_SMALLDATA; /* OK to aggregate */
+ /* "small" raw data */
+ /* allocations */
+
+ *flags |= H5FD_FEAT_POSIX_COMPAT_HANDLE; /* get_handle callback */
+ /* returns a POSIX file */
+ /* descriptor */
+
+ *flags |= H5FD_FEAT_SUPPORTS_SWMR_IO; /* VFD supports the */
+ /* single-writer/ */
+ /* multiple-readers */
+ /* (SWMR) pattern */
+
+ *flags |= H5FD_FEAT_DEFAULT_VFD_COMPATIBLE; /* VFD creates a file */
+ /* which can be opened */
+ /* with the default VFD */
+
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5FD_vfd_swmr_query() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_get_eoa
+ *
+ * Purpose: Gets the end-of-address marker for the file for the
+ * underlying HDF5 file. The EOA marker is the first address
+ * past the last byte allocated in the format address space.
+ *
+ * Return: The end-of-address marker.
+ *
+ *-------------------------------------------------------------------------
+ */
+static haddr_t
+H5FD_vfd_swmr_get_eoa(const H5FD_t *_file, H5FD_mem_t type)
+{
+ const H5FD_vfd_swmr_t *file = (const H5FD_vfd_swmr_t *)_file;
+ haddr_t ret_value = HADDR_UNDEF;
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ if((ret_value = H5FD_get_eoa(file->hdf5_file_lf, type)) == HADDR_UNDEF)
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, HADDR_UNDEF, \
+ "unable to get HDF5 file eoa")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FD_vfd_swmr_get_eoa() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_set_eoa
+ *
+ * Purpose: Set the end-of-address marker for the underlying HDF5 file.
+ * This function is called shortly after an existing HDF5 file
+ * is opened in order to tell the driver where the end of the
+ * HDF5 data is located.
+ *
+ * Return: SUCCEED (Can't fail)
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_vfd_swmr_set_eoa(H5FD_t *_file, H5FD_mem_t type, haddr_t addr)
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ if(H5FD_set_eoa(file->hdf5_file_lf, type, addr) < 0)
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "unable to set HDF5 file eoa")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FD_vfd_swmr_set_eoa() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_get_eof
+ *
+ * Purpose: Returns the end-of-file marker, which is the greater of
+ * either the filesystem end-of-file or the HDF5 end-of-address
+ * markers for the underlying HDF5 file
+ *
+ * Return: End of file address, the first address past the end of the
+ * "file", either the filesystem file or the HDF5 file.
+ *
+ *-------------------------------------------------------------------------
+ */
+static haddr_t
+H5FD_vfd_swmr_get_eof(const H5FD_t *_file, H5FD_mem_t type)
+{
+ const H5FD_vfd_swmr_t *file = (const H5FD_vfd_swmr_t *)_file;
+ haddr_t ret_value = HADDR_UNDEF;
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* LATER: need to determine the metadata file or underlying HDF5 file ? */
+ if((ret_value = H5FD_get_eof(file->hdf5_file_lf, type)) == HADDR_UNDEF)
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, HADDR_UNDEF, \
+ "unable to set file eoa")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FD_vfd_swmr_get_eof() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_get_handle
+ *
+ * Purpose: Returns the file handle for the underling HDF5 file
+ *
+ * Returns: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_vfd_swmr_get_handle(H5FD_t *_file, hid_t fapl, void **file_handle)
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ if(!file_handle)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "file handle not valid")
+
+ /* LATER? H5P_get(plist, H5F_ACS_SWMR_FILE_NAME, &type) */
+
+ if((ret_value = H5FD_get_vfd_handle(file->hdf5_file_lf,
+ fapl, file_handle)) < 0)
+
+ HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, \
+ "unable to get handle for HDF5 file")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FD_vfd_swmr_get_handle() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_read
+ *
+ * Purpose: If the target page or multi-page metadata entry is
+ * defined in the current metadata file index, satisfy
+ * the read from the metadata file. Otherwise, pass the
+ * read through to the underlying VFD.
+ *
+ * Under normal operating conditions, the size of the
+ * read must always match the size supplied in the
+ * metadata file index. However, until we modify the
+ * file open process for VFD SWMR readers to create the
+ * page buffer before any reads, we must allow non
+ * full page / non full multi-page metadata entry reads
+ * until the page buffer is created.
+ *
+ * This is tracked by the pb_configured flag in
+ * H5FD_vfd_swmr_t. If this field is FALSE, the function
+ * must allow reads smaller than the size listed in the
+ * index, and possibly starting anywhere in the page.
+ * Note, however, that these reads must not cross page
+ * boundaries.
+ *
+ * Once we modify the file open code to start up the
+ * page buffer before we attempt any reads, this exception
+ * will not longer be necessary, and should be removed.
+ *
+ * JRM -- 1/29/19
+ *
+ * Return: Success: SUCCEED. Result is stored in caller-supplied
+ * buffer BUF.
+ * Failure: FAIL, Contents of buffer BUF are undefined.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_vfd_swmr_read(H5FD_t *_file, H5FD_mem_t type,
+ hid_t H5_ATTR_UNUSED dxpl_id,
+ const haddr_t addr, size_t size, void * const buf /*out*/)
+{
+ const size_t init_size = size;
+ haddr_t target_page;
+ haddr_t page_offset;
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file;
+ H5FD_vfd_swmr_idx_entry_t *index, *entry;
+ uint32_t num_entries = 0;
+ uint32_t fs_page_size;
+ herr_t ret_value = SUCCEED;
+ char *p = buf;
+
+ if (file->writer)
+ return H5FD_read(file->hdf5_file_lf, type, addr, size, buf);
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ HDassert(file && file->pub.cls);
+ HDassert(buf);
+
+ index = file->md_index.entries;
+ num_entries = file->md_index.num_entries;
+ fs_page_size = file->md_header.fs_page_size;
+
+ /* Try finding the addr from the index */
+ target_page = addr / fs_page_size;
+
+ entry = vfd_swmr_pageno_to_mdf_idx_entry(index, num_entries, target_page,
+ false);
+
+ hlog_fast(swmr_read, "%s: enter type %d addr %" PRIuHADDR " size %zu "
+ "file %s", __func__, type, addr, size,
+ (entry == NULL) ? "lower" : "shadow");
+
+ if (entry == NULL) {
+ /* Cannot find addr in index, read from the underlying hdf5 file */
+ if(H5FD_read(file->hdf5_file_lf, type, addr, size, buf) < 0) {
+ HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL, \
+ "file read request failed");
+ }
+ HGOTO_DONE(SUCCEED);
+ }
+
+ /* Found in index, read from the metadata file */
+ HDassert(addr >= target_page * fs_page_size);
+
+ page_offset = addr - (target_page * fs_page_size);
+
+ HDassert( ( page_offset == 0 ) ||
+ ( ( ! file->pb_configured ) &&
+ ( page_offset + size <= fs_page_size ) ) );
+
+ HDassert(entry->hdf5_page_offset * fs_page_size <= addr);
+ HDassert(addr < (entry->hdf5_page_offset + 1) * fs_page_size);
+ HDassert(page_offset + init_size <= entry->length);
+
+ if(HDlseek(file->md_fd, (HDoff_t)
+ ((entry->md_file_page_offset * fs_page_size)
+ + page_offset), SEEK_SET) < 0)
+ HGOTO_ERROR(H5E_VFL, H5E_SEEKERROR, FAIL, "unable to seek in metadata file")
+
+ /* Coding borrowed from sec2 read */
+ while(size > 0) {
+
+ h5_posix_io_t bytes_in; /* # of bytes to read */
+ h5_posix_io_ret_t bytes_read; /* # of bytes actually read */
+
+ /* Trying to read more bytes than the return type can handle is
+ * undefined behavior in POSIX.
+ */
+ if(size > H5_POSIX_MAX_IO_BYTES)
+ bytes_in = MIN(H5_POSIX_MAX_IO_BYTES, size);
+ else
+ bytes_in = (h5_posix_io_t)size;
+
+ do {
+ bytes_read = HDread(file->md_fd, p, bytes_in);
+ } while (-1 == bytes_read && EINTR == errno);
+
+ if(-1 == bytes_read)
+ HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "error reading the page/multi-page entry from the md file")
+
+ HDassert(0 <= bytes_read && (size_t)bytes_read <= size);
+
+ size -= (size_t)bytes_read;
+ p += bytes_read;
+ }
+
+ /* Verify stored and computed checksums are equal.
+ *
+ * Ignore the checksum if the buffer (buf, size) is not large enough
+ * to hold the entire shadow image. Assume that the caller will
+ * read the entry fully, later.
+ *
+ * Ignore checksum if the page buffer is not configured---this
+ * is John's hack to allow the library to find the superblock
+ * signature.
+ */
+ if (!file->pb_configured) {
+ hlog_fast(swmr_read_exception,
+ "%s: skipping checksum, page buffer not configured", __func__);
+ } else if (entry->length != init_size) {
+ hlog_fast(swmr_read_exception,
+ "%s: skipping checksum, buffer size != entry size", __func__);
+ } else if (H5_checksum_metadata(buf, entry->length, 0) != entry->chksum) {
+ H5FD_vfd_swmr_md_header tmp_header;
+
+ hlog_fast(swmr_read_err, "%s: bad checksum", __func__);
+ hlog_fast(swmr_read_err, "addr %" PRIuHADDR " page %" PRIuHADDR
+ " len %zu type %d ...", addr, addr / fs_page_size, init_size, type);
+ hlog_fast(swmr_read_err, "... index[%" PRId64 "] lower pgno %" PRIu64
+ " shadow pgno %" PRIu64 " len %" PRIu32 " sum %" PRIx32,
+ (int64_t)(entry - index), entry->hdf5_page_offset,
+ entry->md_file_page_offset, entry->length, entry->chksum);
+
+ if (H5FD__vfd_swmr_header_deserialize(file, &tmp_header) != TRUE) {
+ HGOTO_ERROR(H5E_VFL, H5E_CANTLOAD, FAIL,
+ "checksum error in shadow file entry; could not load header");
+ }
+
+ hlog_fast(swmr_read_err, "... header tick last read %" PRIu64
+ " latest %" PRIu64, file->md_header.tick_num, tmp_header.tick_num);
+
+ HGOTO_ERROR(H5E_VFL, H5E_CANTLOAD, FAIL,
+ "checksum error in shadow file entry");
+ }
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FD_vfd_swmr_read() */
+
+
+/*
+ * Function: H5FD_vfd_swmr_write
+ *
+ * Purpose: Writes SIZE bytes of data to FILE beginning at address ADDR
+ * from buffer BUF according to data transfer properties in
+ * DXPL_ID.
+ *
+ * Return: SUCCEED/FAIL
+ */
+static herr_t
+H5FD_vfd_swmr_write(H5FD_t *_file, H5FD_mem_t type,
+ hid_t H5_ATTR_UNUSED dxpl_id, haddr_t addr, size_t size, const void *buf)
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file;
+
+ HDassert(file->writer);
+
+ return H5FD_write(file->hdf5_file_lf, type, addr, size, buf);
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_truncate
+ *
+ * Purpose: Makes sure that the true file size is the same (or larger)
+ * than the end-of-address for the underlying HDF5 file
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_vfd_swmr_truncate(H5FD_t *_file, hid_t H5_ATTR_UNUSED dxpl_id,
+ hbool_t closing)
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file; /* VFD SWMR file struct */
+
+ /* The VFD SWMR vfd should only be used by the VFD SWMR reader,
+ * and thus this file should only be opened R/O.
+ *
+ * Thus this function should never be called and should return error
+ *
+ * For now, just assert FALSE.
+ */
+ HDassert(file->writer);
+
+ return H5FD_truncate(file->hdf5_file_lf, closing);
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_lock
+ *
+ * Purpose: To place an advisory lock on the underlying HDF5 file.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_vfd_swmr_lock(H5FD_t *_file, hbool_t rw)
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file; /* VFD SWMR file struct */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ HDassert(file);
+
+ if(H5FD_lock(file->hdf5_file_lf, rw) < 0)
+
+ HGOTO_ERROR(H5E_IO, H5E_CANTLOCK, FAIL, \
+ "unable to lock the HDF5 file")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FD_vfd_swmr_lock() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_unlock
+ *
+ * Purpose: To remove the existing lock on the underlying HDF5 file
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5FD_vfd_swmr_unlock(H5FD_t *_file)
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file; /* VFD SWMR file struct */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ HDassert(file);
+
+ if(H5FD_unlock(file->hdf5_file_lf) < 0)
+
+ HGOTO_ERROR(H5E_IO, H5E_CANTUNLOCK, FAIL, \
+ "unable to unlock the HDF5 file")
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* end H5FD_vfd_swmr_unlock() */
+
+
+/*
+ * Function: H5FD__vfd_swmr_load_hdr_and_idx()
+ *
+ * Purpose: Load and decode the header and index in the metadata file
+ *
+ * In H5FD__vfd_swmr_load_hdr_and_idx(), we follow this protocol for reading
+ * the shadow file:
+ *
+ * 0 If the maximum number of retries have been attempted, then exit
+ * with an error.
+ *
+ * 1 Try to read the shadow file *header*. If successful, continue to 2.
+ *
+ * If there is a hard failure, then return an error. If there is a failure
+ * that may be transient, then sleep and retry at 0.
+ *
+ * 2 If the tick number in the header is less than the tick last read by the
+ * VFD, then return an error.
+ *
+ * 3 If the tick number in the header is equal to the last tick read by the
+ * VFD, then exit without doing anything.
+ *
+ * 4 Try to read the shadow file *index*. If successful, continue to 5.
+ *
+ * If there is a hard failure, then return an error. If there is a failure
+ * that may be transient, then sleep and retry at 0.
+ *
+ * 5 If a different tick number was read from the index than from the index,
+ * then continue at 0.
+ *
+ * 6 Try to *re-read* the shadow file *header*. If successful, continue to 7.
+ *
+ * If there is a hard failure, then return an error. If there is a failure
+ * that may be transient, then sleep and retry at 0.
+ *
+ * 7 Compare the header that was read previously with the new header. If
+ * the new header is different than the old, then we may not have read
+ * the index at the right shadow-file offset, or the index may have been
+ * read in an inconsistent state, so sleep and retry at 0. Otherwise,
+ * return success.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ */
+static herr_t
+H5FD__vfd_swmr_load_hdr_and_idx(H5FD_vfd_swmr_t *file, hbool_t open)
+{
+ bool do_try;
+ h5_retry_t retry;
+ H5FD_vfd_swmr_md_header md_header; /* Metadata file header, take 1 */
+ H5FD_vfd_swmr_md_header md_header_two; /* Metadata file header, take 2 */
+ H5FD_vfd_swmr_md_index md_index; /* Metadata file index */
+ herr_t ret_value = SUCCEED; /* Return value */
+ htri_t rc;
+ static uint64_t last_index_offset = 0;
+
+ FUNC_ENTER_STATIC
+
+ for (do_try = h5_retry_init(&retry, H5FD_VFD_SWMR_MD_LOAD_RETRY_MAX,
+ H5_RETRY_ONE_SECOND / 10, H5_RETRY_ONE_SECOND);
+ do_try;
+ do_try = h5_retry_next(&retry)) {
+
+ /* Load and decode the header. Go around again on a temporary
+ * failure (FALSE). Bail on an irrecoverable failure (FAIL).
+ */
+ rc = H5FD__vfd_swmr_header_deserialize(file, &md_header);
+
+ /* Temporary failure, try again. */
+ if (rc == FALSE)
+ continue;
+
+ if (rc != TRUE)
+ HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "could not read header");
+
+ if (md_header.index_offset != last_index_offset) {
+ hlog_fast(index_motion, "index offset changed %" PRIu64 "\n",
+ md_header.index_offset);
+ last_index_offset = md_header.index_offset;
+ }
+
+ if (open)
+ ; // ignore tick number on open
+ else if (md_header.tick_num == file->md_header.tick_num) {
+ /* If the tick number in the header hasn't increased since last
+ * time, then there is not a complete new index to read, so
+ * get out.
+ */
+ HGOTO_DONE(SUCCEED);
+ } else if (md_header.tick_num < file->md_header.tick_num) {
+ /* The tick number must not move backward. */
+ HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL,
+ "tick number in header moved backwards");
+ }
+
+ HDassert(md_header.tick_num > file->md_header.tick_num || open);
+
+ /* Load and decode the index. Go around again on a temporary
+ * failure (FALSE). Bail on an irrecoverable failure (FAIL).
+ */
+ rc = H5FD__vfd_swmr_index_deserialize(file, &md_index, &md_header);
+
+ if (rc == FALSE)
+ continue;
+
+ if (rc != TRUE)
+ HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL, "could not read index");
+
+ /* If the tick_num is the same in both header and index,
+ * and the header reads the same the second time as the first time,
+ * then we should have a consistent picture of the index.
+ */
+ if (md_header.tick_num == md_index.tick_num &&
+ (rc = H5FD__vfd_swmr_header_deserialize(file,
+ &md_header_two)) == TRUE &&
+ md_header.tick_num == md_header_two.tick_num &&
+ md_header.index_length == md_header_two.index_length &&
+ md_header.index_offset == md_header_two.index_offset)
+ break;
+
+ if (md_index.entries != NULL) {
+
+ HDassert(md_index.num_entries);
+ md_index.entries = (H5FD_vfd_swmr_idx_entry_t *)
+ H5FL_SEQ_FREE(H5FD_vfd_swmr_idx_entry_t,
+ md_index.entries);
+ }
+
+ if (rc == FAIL) {
+ HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL,
+ "could not re-read header");
+ }
+ }
+
+ /* Exhaust all retries for loading and decoding the md file header
+ * and index
+ */
+ if (!do_try) {
+ HGOTO_ERROR(H5E_VFL, H5E_CANTLOAD, FAIL, \
+ "error in loading/decoding the metadata file header and index")
+ }
+
+ /* Free VFD local entries */
+ if (file->md_index.entries != NULL) {
+
+ HDassert(file->md_index.num_entries);
+
+ file->md_index.entries = (H5FD_vfd_swmr_idx_entry_t *)
+ H5FL_SEQ_FREE(H5FD_vfd_swmr_idx_entry_t, file->md_index.entries);
+ }
+
+ /* Copy header and index to VFD */
+ file->md_header = md_header;
+ file->md_index = md_index;
+ md_index.entries = NULL;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5FD__vfd_swmr_load_hdr_and_idx() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD__vfd_swmr_header_deserialize()
+ *
+ * Purpose: To load and decode the header in the metadata file
+ * --Retry to get a file with size at least the size of the header
+ * --Retry on loading the valid magic and checksum for the header
+ * --Decode the header
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi
+ *
+ *-------------------------------------------------------------------------
+ */
+static htri_t
+H5FD__vfd_swmr_header_deserialize(H5FD_vfd_swmr_t *file,
+ H5FD_vfd_swmr_md_header *md_header)
+{
+ uint8_t image[H5FD_MD_HEADER_SIZE]; /* Buffer for element data */
+ uint32_t stored_chksum; /* Stored metadata checksum */
+ uint32_t computed_chksum; /* Computed metadata checksum */
+ uint8_t *p;
+ htri_t ret_value = FAIL;
+ uint64_t index_length;
+ ssize_t nread;
+
+ FUNC_ENTER_STATIC
+
+ /* Set file pointer to the beginning the file */
+ if (lseek(file->md_fd, H5FD_MD_HEADER_OFF, SEEK_SET) < 0) {
+ HGOTO_ERROR(H5E_VFL, H5E_SEEKERROR, FAIL, \
+ "unable to seek in metadata file");
+ }
+
+ /* Read the header */
+ nread = read(file->md_fd, image, H5FD_MD_HEADER_SIZE);
+
+ /* Try again if a signal interrupted the read. */
+ if (nread == -1 && errno == EINTR)
+ HGOTO_DONE(FALSE);
+
+ /* We cannot recover from any other error by trying again,
+ * so bail out.
+ */
+ if (nread == -1) {
+ HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL,
+ "error in reading the shadow header");
+ }
+
+ if ((uint64_t)nread < H5FD_MD_HEADER_SIZE)
+ HGOTO_DONE(FALSE);
+
+ /* Verify magic number */
+ if (memcmp(image, H5FD_MD_HEADER_MAGIC, H5_SIZEOF_MAGIC) != 0)
+ HGOTO_DONE(FALSE);
+
+ /* Verify stored and computed checksums are equal */
+ H5F_get_checksums(image, H5FD_MD_HEADER_SIZE, &stored_chksum,
+ &computed_chksum);
+
+ if (stored_chksum != computed_chksum)
+ HGOTO_DONE(FALSE);
+
+ /* Header magic is already valid */
+ p = image + H5_SIZEOF_MAGIC;
+
+ /* Deserialize page size, tick number, index offset, index length */
+ UINT32DECODE(p, md_header->fs_page_size);
+ UINT64DECODE(p, md_header->tick_num);
+ UINT64DECODE(p, md_header->index_offset);
+ if ((index_length = uint64_decode(&p)) > SIZE_MAX) {
+ HGOTO_ERROR(H5E_VFL, H5E_BADVALUE, FAIL,
+ "index is too large to hold in core");
+ }
+
+ md_header->index_length = (size_t)index_length;
+
+ /* Checksum is already valid */
+ UINT32DECODE(p, stored_chksum);
+
+ /* Sanity check */
+ HDassert((size_t)(p - image) <= H5FD_MD_HEADER_SIZE);
+
+#if 0 /* JRM */
+ HDfprintf(stderr,
+ "---read header ps/tick/idx_off/idx_len = %d / %lld / %lld / %lld\n",
+ md_header->fs_page_size, md_header->tick_num,
+ md_header->index_offset, md_header->index_length);
+#endif /* JRM */
+
+ ret_value = TRUE;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5FD__vfd_swmr_header_deserialize() */
+
+
+
+/*
+ * Function: H5FD__vfd_swmr_index_deserialize()
+ *
+ * Purpose: Load and decode the index in the metadata file
+ * --Retry to get a file with size at least the size of the
+ * (header+index)
+ * --Retry on loading the valid magic and checksum for the index
+ * --Decode the index
+ * --Decode the index entries if the tick number in the header and
+ * the index match
+ *
+ * Return: Success: TRUE
+ * Failure: FAIL
+ * Retry: FALSE
+ *
+ */
+static htri_t
+H5FD__vfd_swmr_index_deserialize(const H5FD_vfd_swmr_t *file,
+ H5FD_vfd_swmr_md_index *md_index, const H5FD_vfd_swmr_md_header *md_header)
+{
+ uint8_t *image; /* Buffer */
+ uint8_t *p = NULL; /* Pointer to buffer */
+ uint32_t stored_chksum; /* Stored metadata checksum value */
+ uint32_t computed_chksum; /* Computed metadata checksum value */
+ unsigned i; /* Local index variable */
+ htri_t ret_value = TRUE;
+ ssize_t nread;
+
+ FUNC_ENTER_STATIC
+
+ /* Allocate buffer for reading index */
+ if (NULL == (image = H5MM_malloc(md_header->index_length))) {
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL,
+ "memory allocation failed for index's on disk image buffer");
+ }
+
+ /* We may seek past EOF. That's ok, the read(2) will catch that. */
+ if (lseek(file->md_fd, (HDoff_t)md_header->index_offset, SEEK_SET) < 0){
+ HGOTO_ERROR(H5E_VFL, H5E_SEEKERROR, FAIL,
+ "unable to seek in metadata file");
+ }
+
+ nread = read(file->md_fd, image, md_header->index_length);
+
+ /* Try again if a signal interrupted the read. */
+ if (nread == -1 && errno == EINTR)
+ HGOTO_DONE(FALSE);
+
+ /* We cannot recover from any other error by trying again,
+ * so bail out.
+ */
+ if (nread == -1) {
+ HGOTO_ERROR(H5E_VFL, H5E_READERROR, FAIL,
+ "error in reading the header in metadata file");
+ }
+
+ /* Try again if the read was not full.
+ *
+ * XXX XXX XXX
+ * A short read should not be possible under the protocol that
+ * I intend to adopt: the writer will write(2) the new index.
+ * In a second write(2), the header describing that index
+ * will be written. POSIX will guarantee that the former
+ * write is visible before the latter. Under the protocol,
+ * there should always be `index_length` bytes available to
+ * read at `index_offset`. If not, the reader should treat it
+ * like an unrecoverable error instead of retrying.
+ */
+ if ((size_t)nread < md_header->index_length)
+ HGOTO_DONE(FALSE);
+
+ /* If the index magic is incorrect, then assume that is a
+ * temporary error and try again.
+ *
+ * XXX XXX XXX
+ * Under the new protocol, where the index is written in
+ * one write(2), and the header is written in a distinct
+ * second write(2), and the header and index are read in
+ * the reverse order, the index magic usually will be intact.
+ *
+ * It is possible under the new protocol that we read
+ * the header on tick `t`, then an arbitrary delay
+ * occurs (the user taps Control-Z, say), and then we
+ * read the index on tick `t + max_lag + 1` or later.
+ * In the mean time, the index may have moved, and its
+ * storage may have been reused. In that case, we could
+ * read bad magic. It's possible to recover by
+ * re-reading the header.
+ */
+ if (memcmp(image, H5FD_MD_INDEX_MAGIC, H5_SIZEOF_MAGIC) != 0)
+ HGOTO_DONE(FALSE);
+
+ /* Verify stored and computed checksums are equal */
+ H5F_get_checksums(image, md_header->index_length, &stored_chksum,
+ &computed_chksum);
+
+ if (stored_chksum != computed_chksum)
+ HGOTO_DONE(FALSE);
+
+ p = image + H5_SIZEOF_MAGIC;
+
+ /* Deserialize the index info: tick number, number of entries, entries,
+ * checksum
+ */
+ UINT64DECODE(p, md_index->tick_num);
+ UINT32DECODE(p, md_index->num_entries);
+
+ /* Read index entries */
+ if(md_index->num_entries) {
+ /* Allocate memory for index entries */
+ md_index->entries = H5FL_SEQ_CALLOC(H5FD_vfd_swmr_idx_entry_t,
+ md_index->num_entries);
+ if (NULL == md_index->entries) {
+ HGOTO_ERROR(H5E_VFL, H5E_CANTALLOC, FAIL,
+ "memory allocation failed for index entries");
+ }
+
+ /* Decode index entries */
+ for (i = 0; i < md_index->num_entries; i++) {
+ UINT32DECODE(p, md_index->entries[i].hdf5_page_offset);
+ UINT32DECODE(p, md_index->entries[i].md_file_page_offset);
+ UINT32DECODE(p, md_index->entries[i].length);
+ UINT32DECODE(p, md_index->entries[i].chksum);
+ }
+ } else
+ md_index->entries = NULL;
+
+ /* Checksum is already valid */
+ UINT32DECODE(p, stored_chksum);
+
+ /* Sanity check */
+ HDassert((size_t)(p - image) <= md_header->index_length);
+
+#if 0 /* JRM */
+ HDfprintf(stderr,
+ " ---- read index tick/num_entries = %lld / %d \n",
+ md_index->tick_num, md_index->num_entries);
+#endif /* JRM */
+
+
+done:
+ if (image != NULL)
+ image = H5MM_xfree(image);
+
+ if (ret_value == FAIL && md_index->entries != NULL) {
+
+ HDassert(md_index->num_entries != 0);
+
+ md_index->entries =
+ H5FL_SEQ_FREE(H5FD_vfd_swmr_idx_entry_t, md_index->entries);
+ }
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5FD__vfd_swmr_index_deserialize() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_get_tick_and_idx()
+ *
+ * Purpose: Retrieve tick_num, num_entries and index from the metadata
+ * file
+ *
+ * --If the parameter "reload_hdr_and_index" is true, load and
+ * decode the header and index via
+ * H5FD__vfd_swmr_load_hdr_and_idx(), which may replace the
+ * VFD's local copies of header and index with the
+ * latest info read.
+ *
+ * --Return tick_num, num_entries and index from the VFD's
+ * local copies.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5FD_vfd_swmr_get_tick_and_idx(H5FD_t *_file, hbool_t reload_hdr_and_index,
+ uint64_t *tick_ptr, uint32_t *num_entries_ptr,
+ H5FD_vfd_swmr_idx_entry_t index[])
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file; /* VFD SWMR file struct */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ assert(index == NULL || num_entries_ptr != NULL);
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Load and decode the header and index as indicated */
+ if (reload_hdr_and_index &&
+ H5FD__vfd_swmr_load_hdr_and_idx(file, FALSE) < 0) {
+ HGOTO_ERROR(H5E_VFL, H5E_CANTLOAD, FAIL,
+ "unable to load/decode md header and index")
+ }
+
+ /* Return tick_num */
+ if(tick_ptr != NULL)
+ *tick_ptr = file->md_header.tick_num;
+
+ if (index != NULL) {
+
+ if (*num_entries_ptr < file->md_index.num_entries) {
+ HGOTO_ERROR(H5E_VFL, H5E_CANTLOAD, FAIL,
+ "not enough space to copy index");
+ }
+
+ HDmemcpy(index, file->md_index.entries,
+ (file->md_index.num_entries *
+ sizeof(file->md_index.entries[0])));
+ }
+
+ if(num_entries_ptr != NULL)
+ *num_entries_ptr = file->md_index.num_entries;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5FD_vfd_swmr_get_tick_and_idx() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_dump_status
+ *
+ * Purpose: Dump a variety of information about the vfd swmr reader
+ * vfd to stderr for debugging purposes.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+void
+H5FD_vfd_swmr_dump_status(H5FD_t *_file, uint64_t page)
+{
+ hbool_t in_index = FALSE;
+ int i = 0;
+ uint32_t num_entries;
+ H5FD_vfd_swmr_idx_entry_t *index;
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file; /* VFD SWMR file struct */
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ HDassert(file);
+
+ index = file->md_index.entries;
+ num_entries = file->md_index.num_entries;
+
+ while ( ( ! in_index ) && ( i < (int)num_entries ) ) {
+
+ if ( index[i].hdf5_page_offset == page ) {
+
+ in_index = TRUE;
+ }
+
+ HDassert( ( i == 0 ) ||
+ ( index[i-1].hdf5_page_offset < index[i].hdf5_page_offset ) );
+
+ i++;
+ }
+
+ HDfprintf(stderr,
+ "fd: tick = %" PRIu64 ", index_len = %" PRIu32 ", page %" PRIu64
+ " in index = %s.\n",
+ file->md_index.tick_num, num_entries, page,
+ in_index ? "true" : "false");
+
+ FUNC_LEAVE_NOAPI_VOID
+
+} /* H5FD_vfd_swmr_dump_status() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_vfd_swmr_set_pb_configured
+ *
+ * Purpose: Set the pb_configured field.
+ *
+ * This notifies the VFD that the page buffer is configured,
+ * and that therefore all reads to the metadata file should
+ * read complete pages or multi-page metadata entries.
+ *
+ * This function in necessary because we haven't modified
+ * the file open code to configure the page buffer prior
+ * to any file I/O when opening a file VFD SWMR reader.
+ * Once this is done, this function should be removed.
+ *
+ * Return: VOID
+ *
+ * Programmer: JRM -- 1/29/19
+ *
+ *-------------------------------------------------------------------------
+ */
+void
+H5FD_vfd_swmr_set_pb_configured(H5FD_t *_file)
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file; /* VFD SWMR file struct */
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ HDassert(file);
+
+ file->pb_configured = TRUE;
+
+ FUNC_LEAVE_NOAPI_VOID
+
+} /* H5FD_vfd_swmr_set_pb_configured() */
+
+void
+H5FD_vfd_swmr_record_elapsed_ticks(H5FD_t *_file, uint64_t elapsed)
+{
+ H5FD_vfd_swmr_t *file = (H5FD_vfd_swmr_t *)_file;
+
+ uint32_t elapsed_idx = MIN(elapsed, file->api_elapsed_nslots);
+
+ file->api_elapsed_ticks[elapsed_idx]++;
+}
diff --git a/src/H5FDvfd_swmr.h b/src/H5FDvfd_swmr.h
new file mode 100644
index 0000000..86e9d0f
--- /dev/null
+++ b/src/H5FDvfd_swmr.h
@@ -0,0 +1,38 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * The public header file for the VFD SWMR driver.
+ */
+#ifndef H5FDswmr_H
+#define H5FDswmr_H
+
+#include "H5api_adpt.h" /* H5_DLL */
+#include "H5public.h" /* uint64_t *ahem* */
+#include "H5Ipublic.h" /* hid_t */
+
+#define H5FD_VFD_SWMR (H5FD_vfd_swmr_init())
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+H5_DLL hid_t H5FD_vfd_swmr_init(void);
+H5_DLL herr_t H5Pset_fapl_vfd_swmr(hid_t fapl_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/H5FDvfd_swmr_instr.c b/src/H5FDvfd_swmr_instr.c
new file mode 100644
index 0000000..865483c
--- /dev/null
+++ b/src/H5FDvfd_swmr_instr.c
@@ -0,0 +1,28 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the COPYING file, which can be found at the root of the source code *
+ * distribution tree, or in https://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 "H5private.h" /* H5_ATTR_UNUSED */
+#include "H5Fpublic.h"
+#include "H5FDvfd_swmr.h"
+
+bool
+vfd_swmr_writer_may_increase_tick_to(uint64_t H5_ATTR_UNUSED tick_num,
+ bool H5_ATTR_UNUSED wait_for_reader)
+{
+ return true;
+}
+
+void
+vfd_swmr_reader_did_increase_tick_to(uint64_t H5_ATTR_UNUSED tick_num)
+{
+ return;
+}
diff --git a/src/H5FDvfd_swmr_private.h b/src/H5FDvfd_swmr_private.h
new file mode 100644
index 0000000..67a72ec
--- /dev/null
+++ b/src/H5FDvfd_swmr_private.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2019 The HDF Group. All rights reserved.
+ *
+ * This file is part of HDF5. The full HDF5 copyright notice, including
+ * terms governing use, modification, and redistribution, is contained in
+ * the COPYING file, which can be found at the root of the source code
+ * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.
+ * If you do not have access to either file, you may request a copy from
+ * help@hdfgroup.org.
+ */
+
+#ifndef _H5FDvfd_swmr_private_H
+#define _H5FDvfd_swmr_private_H
+
+#include "H5queue.h" /* for TAILQ_* */
+#include "hlog.h" /* for TAILQ_* */
+
+/* Forward declaration */
+struct H5F_t;
+struct H5F_shared_t;
+struct H5FD_vfd_swmr_idx_entry_t;
+
+/*
+ * struct eot_queue_entry_t
+ *
+ * This is the structure for an entry on the end-of-tick queue (EOT queue) of files
+ * opened in either VFD SWMR write or VFD SWMR read mode. This queue is maintained
+ * in increasing end of tick time order.
+ * The structure contains all information required to determine whether the end
+ * of tick has arrived for the specified file, and to initiate end of tick processing
+ * if it has.
+ *
+ * The fields of eot_queue_entry_t are discussed below:
+ *
+ * vfd_swmr_file: Pointer to the instance of H5F_file_t containing the shared
+ * fields of the associated file that has been opened in VFD SWMR mode
+ * NOTE: for the time being use H5F_t instead of H5F_file_t
+ *
+ * vfd_swmr_writer: Boolean flag that is set to TRUE if the associated file
+ * has been opened in VFD SWMR writer mode, and FALSE if it has been
+ * opened in VFD SWMR reader mode.
+ *
+ * tick_num: Number of the current tick of the target file.
+ *
+ * end_of_tick: Expiration time of the current tick of the target file.
+ *
+ * link: Forward and backward linkage between the next element and the previous
+ * element (or the queue head). Note that if there is a following entry,
+ * `next`, then `next->end_of_tick` must be greater than or equal to
+ * `end_of_tick`.
+ */
+typedef struct eot_queue_entry {
+ hbool_t vfd_swmr_writer;
+ uint64_t tick_num;
+ struct timespec end_of_tick;
+ struct H5F_t *vfd_swmr_file; /* NOTE: for the time being use H5F_t instead H5F_file_t */
+ TAILQ_ENTRY(eot_queue_entry) link;
+} eot_queue_entry_t;
+
+extern unsigned int vfd_swmr_api_entries_g;
+
+/* The head of the EOT queue */
+typedef TAILQ_HEAD(eot_queue, eot_queue_entry) eot_queue_t;
+
+extern eot_queue_t eot_queue_g;
+
+HLOG_OUTLET_DECL(swmr);
+HLOG_OUTLET_DECL(pbwr);
+HLOG_OUTLET_DECL(shadow_index_reclaim);
+HLOG_OUTLET_DECL(mdc_invalidation);
+
+/***************************************/
+/* Library-private Function Prototypes */
+/***************************************/
+
+H5_DLL herr_t H5F_vfd_swmr_init(struct H5F_t *f, hbool_t file_create);
+H5_DLL herr_t H5F_vfd_swmr_close_or_flush(struct H5F_t *f, hbool_t closing);
+H5_DLL herr_t H5F_update_vfd_swmr_metadata_file(struct H5F_t *f,
+ uint32_t index_len, struct H5FD_vfd_swmr_idx_entry_t *index);
+H5_DLL herr_t H5F_vfd_swmr_writer__delay_write(struct H5F_shared_t *, uint64_t,
+ uint64_t *);
+H5_DLL herr_t H5F_vfd_swmr_writer__prep_for_flush_or_close(struct H5F_t *f);
+herr_t H5F_vfd_swmr_process_eot_queue(bool);
+H5_DLL herr_t H5F_vfd_swmr_writer_end_of_tick(struct H5F_t *f, bool);
+H5_DLL herr_t H5F_vfd_swmr_writer__dump_index(struct H5F_shared_t *);
+H5_DLL herr_t H5F_vfd_swmr_reader_end_of_tick(struct H5F_t *f, bool);
+
+H5_DLL herr_t H5F_vfd_swmr_remove_entry_eot(struct H5F_t *f);
+H5_DLL herr_t H5F_vfd_swmr_insert_entry_eot(struct H5F_t *f);
+H5_DLL void H5F_vfd_swmr_update_entry_eot(eot_queue_entry_t *);
+H5_DLL herr_t H5F_dump_eot_queue(void);
+
+#endif /* _H5FDvfd_swmr_private_H */
diff --git a/src/H5FScache.c b/src/H5FScache.c
index c3e3998..214e6aa 100644
--- a/src/H5FScache.c
+++ b/src/H5FScache.c
@@ -123,6 +123,7 @@ const H5AC_class_t H5AC_FSPACE_HDR[1] = {{
H5FS__cache_hdr_notify, /* 'notify' callback */
H5FS__cache_hdr_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5FS section info inherits cache-like properties from H5AC */
@@ -141,6 +142,7 @@ const H5AC_class_t H5AC_FSPACE_SINFO[1] = {{
H5FS__cache_sinfo_notify, /* 'notify' callback */
H5FS__cache_sinfo_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
diff --git a/src/H5FSprivate.h b/src/H5FSprivate.h
index d2e1f90..07152ba 100644
--- a/src/H5FSprivate.h
+++ b/src/H5FSprivate.h
@@ -130,6 +130,7 @@ struct H5FS_section_info_t {
typedef enum H5FS_client_t {
H5FS_CLIENT_FHEAP_ID = 0, /* Free space is used by fractal heap */
H5FS_CLIENT_FILE_ID, /* Free space is used by file */
+ H5FS_CLIENT_MD_VFD_ID, /* Free space is used by the metadata file for VFD SWMR */
H5FS_NUM_CLIENT_ID /* Number of free space client IDs (must be last) */
} H5FS_client_t;
diff --git a/src/H5FSsection.c b/src/H5FSsection.c
index d783901..77e7a89 100644
--- a/src/H5FSsection.c
+++ b/src/H5FSsection.c
@@ -1653,7 +1653,7 @@ H5FS_sect_try_merge(H5F_t *f, H5FS_t *fspace, H5FS_section_info_t *sect,
} /* end if */
else {
/* Check if section is merged */
- if(sect->size > saved_fs_size) {
+ if(sect->size != saved_fs_size) {
if(H5FS__sect_link(fspace, sect, flags) < 0)
HGOTO_ERROR(H5E_FSPACE, H5E_CANTINSERT, FAIL, "can't insert free space section into skip list")
sinfo_modified = TRUE;
diff --git a/src/H5Fint.c b/src/H5Fint.c
index 0bda894..0531223 100644
--- a/src/H5Fint.c
+++ b/src/H5Fint.c
@@ -33,6 +33,7 @@
#include "H5Iprivate.h" /* IDs */
#include "H5Lprivate.h" /* Links */
#include "H5MFprivate.h" /* File memory management */
+#include "H5MVprivate.h" /* File memory management for VFD SWMR */
#include "H5MMprivate.h" /* Memory management */
#include "H5Pprivate.h" /* Property lists */
#include "H5SMprivate.h" /* Shared Object Header Messages */
@@ -86,12 +87,10 @@ static herr_t H5F__build_actual_name(const H5F_t *f, const H5P_genplist_t *fapl,
static herr_t H5F__flush_phase1(H5F_t *f);
static herr_t H5F__flush_phase2(H5F_t *f, hbool_t closing);
-
/*********************/
/* Package Variables */
/*********************/
-
/*****************************/
/* Library Private Variables */
/*****************************/
@@ -233,14 +232,18 @@ H5F_get_access_plist(H5F_t *f, hbool_t app_ref)
efc_size = H5F__efc_max_nfiles(f->shared->efc);
if(H5P_set(new_plist, H5F_ACS_EFC_SIZE_NAME, &efc_size) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, H5I_INVALID_HID, "can't set elink file cache size")
- if(f->shared->page_buf != NULL) {
- if(H5P_set(new_plist, H5F_ACS_PAGE_BUFFER_SIZE_NAME, &(f->shared->page_buf->max_size)) < 0)
+ if(f->shared->pb_ptr != NULL) {
+ if(H5P_set(new_plist, H5F_ACS_PAGE_BUFFER_SIZE_NAME, &(f->shared->pb_ptr->max_size)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, H5I_INVALID_HID, "can't set page buffer size")
- if(H5P_set(new_plist, H5F_ACS_PAGE_BUFFER_MIN_META_PERC_NAME, &(f->shared->page_buf->min_meta_perc)) < 0)
+ if(H5P_set(new_plist, H5F_ACS_PAGE_BUFFER_MIN_META_PERC_NAME, &(f->shared->pb_ptr->min_meta_perc)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, H5I_INVALID_HID, "can't set minimum metadata fraction of page buffer")
- if(H5P_set(new_plist, H5F_ACS_PAGE_BUFFER_MIN_RAW_PERC_NAME, &(f->shared->page_buf->min_raw_perc)) < 0)
+ if(H5P_set(new_plist, H5F_ACS_PAGE_BUFFER_MIN_RAW_PERC_NAME, &(f->shared->pb_ptr->min_raw_perc)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, H5I_INVALID_HID, "can't set minimum raw data fraction of page buffer")
} /* end if */
+
+ if(H5P_set(new_plist, H5F_ACS_VFD_SWMR_CONFIG_NAME, &(f->shared->vfd_swmr_config)) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set initial metadata cache resize config.")
+
#ifdef H5_HAVE_PARALLEL
if(H5P_set(new_plist, H5_COLL_MD_READ_FLAG_NAME, &(f->shared->coll_md_read)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, H5I_INVALID_HID, "can't set collective metadata read flag")
@@ -1097,6 +1100,25 @@ H5F__new(H5F_shared_t *shared, unsigned flags, hid_t fcpl_id, hid_t fapl_id, H5F
if(H5P_get(plist, H5F_ACS_OBJECT_FLUSH_CB_NAME, &(f->shared->object_flush)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "can't get object flush cb info")
+ /* Get VFD SWMR configuration */
+ if(H5P_get(plist, H5F_ACS_VFD_SWMR_CONFIG_NAME, &(f->shared->vfd_swmr_config)) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get VFD SWMR config info")
+
+ /* Initialization for VFD SWMR */
+ f->shared->vfd_swmr = FALSE;
+ f->shared->vfd_swmr_writer = FALSE;
+ f->shared->tick_num = 0;
+ f->shared->mdf_idx = NULL;
+ f->shared->mdf_idx_len = 0;
+ f->shared->mdf_idx_entries_used = 0;
+ f->shared->old_mdf_idx = NULL;
+ f->shared->old_mdf_idx_len = 0;
+ f->shared->old_mdf_idx_entries_used = 0;
+
+ f->shared->vfd_swmr_md_fd = -1;
+ f->shared->fs_man_md = NULL;
+ TAILQ_INIT(&f->shared->shadow_defrees);
+
/* Get the VOL connector info */
if(H5F__set_vol_conn(f) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "can't cache VOL connector info")
@@ -1329,6 +1351,12 @@ H5F__dest(H5F_t *f, hbool_t flush)
/* Push error, but keep going*/
HDONE_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "problems closing file")
+ /* If this is a VFD SWMR writer, prep for flush or close */
+ if((f->shared->vfd_swmr) && (f->shared->vfd_swmr_writer) &&
+ (H5F_vfd_swmr_writer__prep_for_flush_or_close(f) < 0))
+ /* Push error, but keep going*/
+ HDONE_ERROR(H5E_IO, H5E_CANTFLUSH, FAIL, "vfd swmr prep for flush or close failed")
+
/* Shutdown the page buffer cache */
if(H5PB_dest(f->shared) < 0)
/* Push error, but keep going*/
@@ -1370,6 +1398,17 @@ H5F__dest(H5F_t *f, hbool_t flush)
/* Push error, but keep going*/
HDONE_ERROR(H5E_FILE, H5E_CANTDEC, FAIL, "can't close property list")
+ /* VFD SWMR: closing down */
+ if(H5F_ACC_RDWR & H5F_INTENT(f) && f->shared->vfd_swmr_md_fd >= 0) {
+ if(H5F_vfd_swmr_close_or_flush(f, TRUE) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to close the metadata file")
+ }
+
+ if(f->shared->vfd_swmr) {
+ if(H5F_vfd_swmr_remove_entry_eot(f) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to remove entry from EOT queue")
+ }
+
/* Clean up the cached VOL connector ID & info */
if(f->shared->vol_info)
if(H5VL_free_connector_info(f->shared->vol_id, f->shared->vol_info) < 0)
@@ -1386,6 +1425,15 @@ H5F__dest(H5F_t *f, hbool_t flush)
/* Push error, but keep going*/
HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to close file")
+ /* A VFD SWMR reader may still have a metadata index at this stage.
+ * If so, free it.
+ */
+ if (f->shared->vfd_swmr && f->shared->mdf_idx != NULL) {
+ HDfree(f->shared->mdf_idx);
+ f->shared->mdf_idx = NULL;
+ f->shared->mdf_idx_len = 0;
+ }
+
/* Free mount table */
f->shared->mtab.child = (H5F_mount_t *)H5MM_xfree(f->shared->mtab.child);
f->shared->mtab.nalloc = 0;
@@ -1408,6 +1456,11 @@ H5F__dest(H5F_t *f, hbool_t flush)
* Only decrement the reference count.
*/
--f->shared->nrefs;
+
+ if(f->shared->vfd_swmr) {
+ if(H5F_vfd_swmr_remove_entry_eot(f) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to remove entry from EOT queue")
+ }
}
/* Free the non-shared part of the file */
@@ -1518,10 +1571,33 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
hbool_t use_file_locking; /*read from env var */
hbool_t ci_load = FALSE; /* whether MDC ci load requested */
hbool_t ci_write = FALSE; /* whether MDC CI write requested */
- H5F_t *ret_value = NULL; /*actual return value */
+ hbool_t file_create = FALSE; /* creating a new file or not */
+ H5F_vfd_swmr_config_t *vfd_swmr_config_ptr = NULL; /* Points to VFD SMWR config info */
+ H5F_t *ret_value = NULL; /*actual return value */
FUNC_ENTER_NOAPI(NULL)
+ /* Get the file access property list, for future queries */
+ if(NULL == (a_plist = (H5P_genplist_t *)H5I_object(fapl_id)))
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not file access property list")
+
+ /* Allocate space for VFD SWMR configuration info */
+ if(NULL == (vfd_swmr_config_ptr = H5MM_calloc(sizeof(H5F_vfd_swmr_config_t))))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "can't allocate memory for mdc log file name")
+
+ /* Get VFD SWMR configuration */
+ if(H5P_get(a_plist, H5F_ACS_VFD_SWMR_CONFIG_NAME, vfd_swmr_config_ptr) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get VFD SWMR config info")
+
+ /* When configured with VFD SWMR */
+ if(vfd_swmr_config_ptr->version) {
+ /* Verify that file access flags are consistent with VFD SWMR configuartion */
+ if((flags & H5F_ACC_RDWR) && !vfd_swmr_config_ptr->writer)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "file access is writer but VFD SWMR config is reader")
+ if((flags & H5F_ACC_RDWR) == 0 && vfd_swmr_config_ptr->writer)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "file access is reader but VFD SWMR config is writer")
+ }
+
/*
* If the driver has a `cmp' method then the driver is capable of
* determining when two file handles refer to the same file and the
@@ -1568,11 +1644,19 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to open file: name = '%s', tent_flags = %x", name, tent_flags)
} /* end if */
+ /* Avoid reusing a virtual file opened exclusively by a second virtual
+ * file, or opening the same file twice with different parameters.
+ */
+ if ((lf = H5FD_deduplicate(lf, fapl_id)) == NULL) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL,
+ "an already-open file conflicts with '%s'", name);
+ }
+
/* Is the file already open? */
if((shared = H5F__sfile_search(lf)) != NULL) {
/*
- * The file is already open, so use that one instead of the one we
- * just opened. We only one one H5FD_t* per file so one doesn't
+ * The file is already open, so use the corresponding H5F_shared_t.
+ * We only allow one H5FD_t* per file so one doesn't
* confuse the other. But fail if this request was to truncate the
* file (since we can't do that while the file is open), or if the
* request was to create a non-existent file (since the file already
@@ -1580,8 +1664,6 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
* readers don't expect the file to change under them), or if the
* SWMR write/read access flags don't agree.
*/
- if(H5FD_close(lf) < 0)
- HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to close low-level file info")
if(flags & H5F_ACC_TRUNC)
HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to truncate a file which is already open")
if(flags & H5F_ACC_EXCL)
@@ -1651,10 +1733,6 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
shared = file->shared;
lf = shared->lf;
- /* Get the file access property list, for future queries */
- if(NULL == (a_plist = (H5P_genplist_t *)H5I_object(fapl_id)))
- HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not file access property list")
-
/* Check if page buffering is enabled */
if(H5P_get(a_plist, H5F_ACS_PAGE_BUFFER_SIZE_NAME, &page_buf_size) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "can't get page buffer size")
@@ -1674,6 +1752,7 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "can't get minimum raw data fraction of page buffer")
} /* end if */
+
/*
* Read or write the file superblock, depending on whether the file is
* empty or not.
@@ -1700,6 +1779,9 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
*/
if(H5G_mkroot(file, TRUE) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "unable to create/open root group")
+
+ file_create = TRUE;
+
} /* end if */
else if(1 == shared->nrefs) {
/* Read the superblock if it hasn't been read before. */
@@ -1714,8 +1796,22 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
/* Open the root group */
if(H5G_mkroot(file, FALSE) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to read root group")
+
} /* end if */
+ /* Checked if configured for VFD SWMR */
+ if(H5F_VFD_SWMR_CONFIG(file)) {
+ /* Initialization for VFD SWMR writer and reader */
+ if(1 == shared->nrefs) {
+ if(H5F_vfd_swmr_init(file, file_create) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, NULL, "file open fail with initialization for VFD SWMR")
+ }
+
+ /* Insert the entry that corresponds to file onto the EOT queue */
+ if(H5F_vfd_swmr_insert_entry_eot(file) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, NULL, "unable to insert entry into the EOT queue")
+ }
+
/*
* Decide the file close degree. If it's the first time to open the
* file, set the degree to access property list value; if it's the
@@ -1781,7 +1877,7 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
} /* version 3 superblock */
file->shared->sblock->status_flags |= H5F_SUPER_WRITE_ACCESS;
- if(H5F_INTENT(file) & H5F_ACC_SWMR_WRITE)
+ if(H5F_INTENT(file) & H5F_ACC_SWMR_WRITE || H5F_USE_VFD_SWMR(file))
file->shared->sblock->status_flags |= H5F_SUPER_SWMR_WRITE_ACCESS;
/* Flush the superblock & superblock extension */
@@ -1793,7 +1889,7 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, NULL, "unable to flush superblock extension")
/* Remove the file lock for SWMR_WRITE */
- if(use_file_locking && (H5F_INTENT(file) & H5F_ACC_SWMR_WRITE)) {
+ if(use_file_locking && ((H5F_INTENT(file) & H5F_ACC_SWMR_WRITE) || H5F_USE_VFD_SWMR(file))) {
if(H5FD_unlock(file->shared->lf) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to unlock the file")
} /* end if */
@@ -1801,7 +1897,7 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
else { /* H5F_ACC_RDONLY: check consistency of status_flags */
/* Skip check of status_flags for file with < superblock version 3 */
if(file->shared->sblock->super_vers >= HDF5_SUPERBLOCK_VERSION_3) {
- if(H5F_INTENT(file) & H5F_ACC_SWMR_READ) {
+ if(H5F_INTENT(file) & H5F_ACC_SWMR_READ || H5F_USE_VFD_SWMR(file)) {
if((file->shared->sblock->status_flags & H5F_SUPER_WRITE_ACCESS &&
!(file->shared->sblock->status_flags & H5F_SUPER_SWMR_WRITE_ACCESS))
||
@@ -1820,9 +1916,17 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
ret_value = file;
done:
- if((NULL == ret_value) && file)
+ if((NULL == ret_value) && file) {
+ if(file->shared->root_grp && file->shared->nrefs == 1) {
+ if(H5AC_expunge_tag_type_metadata(file, H5G_oloc(file->shared->root_grp)->addr, H5AC_OHDR_ID, H5AC__NO_FLAGS_SET, FALSE) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTEXPUNGE, NULL, "unable to expunge root group tagged entries")
+ }
+
if(H5F__dest(file, FALSE) < 0)
HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, NULL, "problems closing file")
+ }
+ if(vfd_swmr_config_ptr)
+ H5MM_free(vfd_swmr_config_ptr);
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5F_open() */
@@ -1951,6 +2055,12 @@ H5F__flush_phase2(H5F_t *f, hbool_t closing)
/* Push error, but keep going*/
HDONE_ERROR(H5E_IO, H5E_CANTFLUSH, FAIL, "unable to flush metadata accumulator")
+ /* If this is a VFD SWMR writer, prep for flush or close */
+ if((f->shared->vfd_swmr) && (f->shared->vfd_swmr_writer) &&
+ (H5F_vfd_swmr_writer__prep_for_flush_or_close(f) < 0))
+ /* Push error, but keep going*/
+ HDONE_ERROR(H5E_IO, H5E_CANTFLUSH, FAIL, "vfd swmr prep for flush or close failed")
+
/* Flush the page buffer */
if(H5PB_flush(f->shared) < 0)
/* Push error, but keep going*/
@@ -1994,6 +2104,14 @@ H5F__flush(H5F_t *f)
/* Push error, but keep going*/
HDONE_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush file data")
+ /* VFD SWMR when flushing the HDF5 file */
+ if(f->shared->nrefs == 1 && f->shared->vfd_swmr_writer && f->shared->vfd_swmr_md_fd >= 0) {
+ HDassert(H5F_ACC_RDWR & H5F_INTENT(f));
+
+ if(H5F_vfd_swmr_close_or_flush(f, FALSE) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "unable to encode and write to the metadata file")
+ }
+
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5F__flush() */
@@ -3469,7 +3587,16 @@ H5F__start_swmr_write(H5F_t *f)
/* Refresh (reopen) the objects (groups & datasets) in the file */
for(u = 0; u < grp_dset_count; u++)
- if(H5O_refresh_metadata_reopen(obj_ids[u], &obj_glocs[u], vol_connector, TRUE) < 0)
+ /* XXX This routine probably should prepare legitimate
+ * H5O_refresh_state_t's, above, and pass those instead of NULL, so
+ * that non-persistent object properties---e.g., dataset access
+ * properties---are copied from old objects to reopened objects.
+ *
+ * Passing NULL here shouldn't introduce any bugs in Legacy SWMR,
+ * however, and it lets VFD SWMR development proceed, so I'm not
+ * going to sweat it, now.
+ */
+ if(H5O_refresh_metadata_reopen(obj_ids[u], &obj_glocs[u], NULL, vol_connector, TRUE) < 0)
HGOTO_ERROR(H5E_ATOM, H5E_CLOSEERROR, FAIL, "can't refresh-close object")
/* Unlock the file */
@@ -3666,3 +3793,142 @@ H5F_set_min_dset_ohdr(H5F_t *f, hbool_t minimize)
FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5F_set_min_dset_ohdr() */
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__vfd_swmr_end_tick()
+ *
+ * Purpose: To trigger end of tick processing
+ *
+ * Return: Non-negative on success/Negative on errors
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F__vfd_swmr_end_tick(H5F_t *f)
+{
+ eot_queue_entry_t *curr;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(f->shared);
+
+ /* The file should be opened with VFD SWMR configured.*/
+ if(!(H5F_USE_VFD_SWMR(f)))
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "must have VFD SWMR configured for this public routine")
+
+ /* Search EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if (curr->vfd_swmr_file == f)
+ break;
+ }
+
+ /* If the file does not exist on the EOT queue, flag an error */
+ if(curr == NULL)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "EOT for the file has been disabled")
+
+ if (f->shared->vfd_swmr_writer) {
+ if (H5F_vfd_swmr_writer_end_of_tick(f, true) < 0)
+ HGOTO_ERROR(H5E_FUNC, H5E_CANTSET, FAIL,
+ "end of tick error for VFD SWMR writer");
+ } else if (H5F_vfd_swmr_reader_end_of_tick(f, true) < 0) {
+ HGOTO_ERROR(H5E_FUNC, H5E_CANTSET, FAIL,
+ "end of tick error for VFD SWMR reader");
+ }
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5F__vfd_swmr_end_tick() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__vfd_swmr_disable_end_of_tick()
+ *
+ * Purpose: To disable end of tick processing
+ *
+ * Return: Non-negative on success/Negative on errors
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F__vfd_swmr_disable_end_of_tick(H5F_t *f)
+{
+ eot_queue_entry_t *curr;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(f->shared);
+
+ /* The file should be opened with VFD SWMR configured.*/
+ if(!(H5F_USE_VFD_SWMR(f)))
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "must have VFD SWMR configured for this public routine")
+
+ /* Search EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if (curr->vfd_swmr_file == f)
+ break;
+ }
+
+ /* If the file does not exist on the EOT queue, flag an error */
+ if(curr == NULL)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "EOT for the file has already been disabled")
+
+ /* Remove the entry that corresponds to "f" from the EOT queue */
+ if(H5F_vfd_swmr_remove_entry_eot(f) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to remove entry from EOT queue")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5F__vfd_swmr_disable_end_of_tick() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__vfd_swmr_enable_end_of_tick()
+ *
+ * Purpose: To enable end of tick processing
+ *
+ * Return: Non-negative on success/Negative on errors
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F__vfd_swmr_enable_end_of_tick(H5F_t *f)
+{
+ eot_queue_entry_t *curr;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(f->shared);
+
+ /* The file should be opened with VFD SWMR configured.*/
+ if(!(H5F_USE_VFD_SWMR(f)))
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "must have VFD SWMR configured for this public routine")
+
+ /* Search EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if (curr->vfd_swmr_file == f)
+ break;
+ }
+
+ /* If the file already exists on the EOT queue, flag an error */
+ if(curr != NULL)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "EOT for the file has already been enabled")
+
+ /* Insert the entry that corresponds to "f" onto the EOT queue */
+ if(H5F_vfd_swmr_insert_entry_eot(f) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to insert entry into the EOT queue")
+
+ /* Check if the tick has expired, if so call end of tick processing */
+ if(H5F_vfd_swmr_process_eot_queue(true) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "error processing EOT queue")
+
+ /* FUNC_LEAVE_API could do the check, but not so for reader_end_of_tick() */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5F__vfd_swmr_enable_end_of_tick() */
diff --git a/src/H5Fio.c b/src/H5Fio.c
index 34dd0d6..592d5f1 100644
--- a/src/H5Fio.c
+++ b/src/H5Fio.c
@@ -84,17 +84,11 @@
* address for the file.
*
* Return: Non-negative on success/Negative on failure
- *
- * Programmer: Robb Matzke
- * matzke@llnl.gov
- * Jul 10 1997
- *
*-------------------------------------------------------------------------
*/
herr_t
H5F_shared_block_read(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr, size_t size, void *buf/*out*/)
{
- H5FD_mem_t map_type; /* Mapped memory type */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
@@ -108,11 +102,8 @@ H5F_shared_block_read(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr, size_t
if(H5F_addr_le(f_sh->tmp_addr, (addr + size)))
HGOTO_ERROR(H5E_IO, H5E_BADRANGE, FAIL, "attempting I/O in temporary file space")
- /* Treat global heap as raw data */
- map_type = (type == H5FD_MEM_GHEAP) ? H5FD_MEM_DRAW : type;
-
/* Pass through page buffer layer */
- if(H5PB_read(f_sh, map_type, addr, size, buf) < 0)
+ if(H5PB_read(f_sh, type, addr, size, buf) < 0)
HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "read through page buffer failed")
done:
@@ -128,40 +119,12 @@ done:
* address for the file.
*
* Return: Non-negative on success/Negative on failure
- *
- * Programmer: Robb Matzke
- * matzke@llnl.gov
- * Jul 10 1997
- *
*-------------------------------------------------------------------------
*/
herr_t
H5F_block_read(H5F_t *f, H5FD_mem_t type, haddr_t addr, size_t size, void *buf/*out*/)
{
- H5FD_mem_t map_type; /* Mapped memory type */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_NOAPI(FAIL)
-
- /* Sanity checks */
- HDassert(f);
- HDassert(f->shared);
- HDassert(buf);
- HDassert(H5F_addr_defined(addr));
-
- /* Check for attempting I/O on 'temporary' file address */
- if(H5F_addr_le(f->shared->tmp_addr, (addr + size)))
- HGOTO_ERROR(H5E_IO, H5E_BADRANGE, FAIL, "attempting I/O in temporary file space")
-
- /* Treat global heap as raw data */
- map_type = (type == H5FD_MEM_GHEAP) ? H5FD_MEM_DRAW : type;
-
- /* Pass through page buffer layer */
- if(H5PB_read(f->shared, map_type, addr, size, buf) < 0)
- HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "read through page buffer failed")
-
-done:
- FUNC_LEAVE_NOAPI(ret_value)
+ return H5F_shared_block_read(f->shared, type, addr, size, buf);
} /* end H5F_block_read() */
@@ -173,17 +136,11 @@ done:
* address.
*
* Return: Non-negative on success/Negative on failure
- *
- * Programmer: Robb Matzke
- * matzke@llnl.gov
- * Jul 10 1997
- *
*-------------------------------------------------------------------------
*/
herr_t
H5F_shared_block_write(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr, size_t size, const void *buf)
{
- H5FD_mem_t map_type; /* Mapped memory type */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
@@ -198,11 +155,8 @@ H5F_shared_block_write(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr, size_t
if(H5F_addr_le(f_sh->tmp_addr, (addr + size)))
HGOTO_ERROR(H5E_IO, H5E_BADRANGE, FAIL, "attempting I/O in temporary file space")
- /* Treat global heap as raw data */
- map_type = (type == H5FD_MEM_GHEAP) ? H5FD_MEM_DRAW : type;
-
/* Pass through page buffer layer */
- if(H5PB_write(f_sh, map_type, addr, size, buf) < 0)
+ if(H5PB_write(f_sh, type, addr, size, buf) < 0)
HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "write through page buffer failed")
done:
@@ -218,41 +172,12 @@ done:
* address.
*
* Return: Non-negative on success/Negative on failure
- *
- * Programmer: Robb Matzke
- * matzke@llnl.gov
- * Jul 10 1997
- *
*-------------------------------------------------------------------------
*/
herr_t
H5F_block_write(H5F_t *f, H5FD_mem_t type, haddr_t addr, size_t size, const void *buf)
{
- H5FD_mem_t map_type; /* Mapped memory type */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_NOAPI(FAIL)
-
- /* Sanity checks */
- HDassert(f);
- HDassert(f->shared);
- HDassert(H5F_INTENT(f) & H5F_ACC_RDWR);
- HDassert(buf);
- HDassert(H5F_addr_defined(addr));
-
- /* Check for attempting I/O on 'temporary' file address */
- if(H5F_addr_le(f->shared->tmp_addr, (addr + size)))
- HGOTO_ERROR(H5E_IO, H5E_BADRANGE, FAIL, "attempting I/O in temporary file space")
-
- /* Treat global heap as raw data */
- map_type = (type == H5FD_MEM_GHEAP) ? H5FD_MEM_DRAW : type;
-
- /* Pass through page buffer layer */
- if(H5PB_write(f->shared, map_type, addr, size, buf) < 0)
- HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "write through page buffer failed")
-
-done:
- FUNC_LEAVE_NOAPI(ret_value)
+ return H5F_shared_block_write(f->shared, type, addr, size, buf);
} /* end H5F_block_write() */
diff --git a/src/H5Fpkg.h b/src/H5Fpkg.h
index 46185ff..1136cb4 100644
--- a/src/H5Fpkg.h
+++ b/src/H5Fpkg.h
@@ -26,12 +26,16 @@
#ifndef _H5Fpkg_H
#define _H5Fpkg_H
+/* BSD queue macros */
+#include "H5queue.h"
+
/* Get package's private header */
#include "H5Fprivate.h"
/* Other private headers needed by this file */
#include "H5private.h" /* Generic Functions */
#include "H5ACprivate.h" /* Metadata cache */
+#include "H5FDprivate.h" /* VFD -- for VFD SWMR */
#include "H5Bprivate.h" /* B-trees */
#include "H5FLprivate.h" /* Free Lists */
#include "H5FOprivate.h" /* File objects */
@@ -213,6 +217,26 @@ typedef struct H5F_mtab_t {
H5F_mount_t *child; /* An array of mount records */
} H5F_mtab_t;
+/*
+ * VFD SWMR: Entry for the delayed free space release doubly linked list
+ *
+ * md_file_page_offset: Unsigned 64-bit value containing the base address
+ * of the metadata page, or multi page metadata entry
+ * in the metadata file IN PAGES.
+ * To obtain byte offset, multiply this value by the
+ * page size.
+ * length: The length of the metadata page or multi page
+ * metadata entry in BYTES.
+ * tick_num: Sequence # of the current tick
+ * link: tailqueue linkage
+ */
+typedef struct shadow_defree {
+ uint64_t offset;
+ uint32_t length;
+ uint64_t tick_num;
+ TAILQ_ENTRY(shadow_defree) link;
+} shadow_defree_t;
+
/* Structure specifically to store superblock. This was originally
* maintained entirely within H5F_shared_t, but is now extracted
* here because the superblock is now handled by the cache */
@@ -232,6 +256,19 @@ typedef struct H5F_super_t {
H5G_entry_t *root_ent; /* Root group symbol table entry */
} H5F_super_t;
+/* VFD SWMR: deferred free on the lower VFD. */
+typedef struct lower_defree {
+ SIMPLEQ_ENTRY(lower_defree) link;
+ H5FD_mem_t alloc_type;
+ haddr_t addr;
+ hsize_t size;
+ uint64_t free_after_tick;
+} lower_defree_t;
+
+typedef SIMPLEQ_HEAD(lower_defree_queue, lower_defree) lower_defree_queue_t;
+
+typedef TAILQ_HEAD(shadow_defree_queue, shadow_defree) shadow_defree_queue_t;
+
/*
* Define the structure to store the file information for HDF5 files. One of
* these structures is allocated per file, not per H5Fopen(). That is, set of
@@ -271,7 +308,9 @@ struct H5F_shared_t {
unsigned long feature_flags; /* VFL Driver feature Flags */
haddr_t maxaddr; /* Maximum address for file */
- H5PB_t *page_buf; /* The page buffer cache */
+ H5PB_t *pb_ptr; /* pointer to the page buffer, or NULL */
+ /* if the page buffer is disabled. */
+
H5AC_t *cache; /* The object cache */
H5AC_cache_config_t
mdc_initCacheCfg; /* initial configuration for the */
@@ -357,6 +396,81 @@ struct H5F_shared_t {
H5F_object_flush_t object_flush; /* Information for object flush callback */
hbool_t crt_dset_min_ohdr_flag; /* flag to minimize created dataset object header */
+ /* VFD SWMR */
+
+ /* Configuration info */
+ H5F_vfd_swmr_config_t vfd_swmr_config; /* Copy of the VFD SWMR
+ * configuration from the
+ * FAPL used to open the file
+ */
+ haddr_t writer_index_offset;
+ hbool_t vfd_swmr; /* The file is opened with VFD
+ * SWMR configured or not
+ */
+ hbool_t vfd_swmr_writer; /* This is the VFD SWMR writer or
+ * not
+ */
+ uint64_t tick_num; /* Number of the current tick */
+ struct timespec end_of_tick; /* End time of the current tick */
+
+ lower_defree_queue_t lower_defrees; /* For use by VFD SWMR writers. */
+ /* VFD SWMR metadata file index */
+ H5FD_vfd_swmr_idx_entry_t * mdf_idx; /* pointer to an array of instance
+ * of H5FD_vfd_swmr_idx_entry_t of
+ * length mdf_idx_len. This array
+ * is used by the vfd swmr writer
+ * to assemble the metadata file
+ * index at the end of each tick,
+ * and by the vfd swmr readers to
+ * track changes in the index.
+ * With one brief exception during
+ * writer end of tick processing,
+ * this index will always be sorted
+ * in increasing HDF5 file page
+ * offset order.
+ *
+ * This field should be NULL unless
+ * the index is defined.
+ */
+ uint32_t mdf_idx_len; /* number of entries in the array
+ * of instances of
+ * H5FD_vfd_swmr_idx_entry_t pointed
+ * to by mdf_idx above. Note that
+ * not all entries in the index
+ * need be used.
+ */
+ uint32_t mdf_idx_entries_used; /* Number of entries in *mdf_idx
+ * that are in use -- these will
+ * be contiguous at indicies 0
+ * through mdf_idx_entries_used - 1.
+ */
+
+ /* Old VFD SWMMR metadata file index. These fields are used only
+ * by the VFD SWMR reader to store the previous version of the
+ * metadata file index so that it can be compared with the current
+ * version to identify page buffer and metadata cache entries that
+ * must be evicted or refreshed to avoid message from the past bugs.
+ */
+ H5FD_vfd_swmr_idx_entry_t * old_mdf_idx;
+ uint32_t old_mdf_idx_len;
+ uint32_t old_mdf_idx_entries_used;
+
+ /* Metadata file for VFD SWMR writer */
+ int vfd_swmr_md_fd; /* POSIX: file descriptor for the
+ * metadata file
+ */
+ haddr_t vfd_swmr_md_eoa; /* POSIX: eoa for the metadata
+ * file
+ */
+
+ /* Free space manager for the metadata file */
+ H5FS_t *fs_man_md; /* Free-space manager */
+ H5F_fs_state_t fs_state_md; /* State of the free space
+ * manager
+ */
+
+ /* Delayed free space release doubly linked list */
+ shadow_defree_queue_t shadow_defrees;
char *extpath; /* Path for searching target external link file */
#ifdef H5_HAVE_PARALLEL
@@ -366,6 +480,7 @@ struct H5F_shared_t {
};
+
/*
* This is the top-level file descriptor. One of these structures is
* allocated every time H5Fopen() is called although they may contain pointers
@@ -412,6 +527,9 @@ H5_DLL herr_t H5F__start_swmr_write(H5F_t *f);
H5_DLL herr_t H5F__close(H5F_t *f);
H5_DLL herr_t H5F__set_libver_bounds(H5F_t *f, H5F_libver_t low, H5F_libver_t high);
H5_DLL herr_t H5F__get_cont_info(const H5F_t *f, H5VL_file_cont_info_t *info);
+H5_DLL herr_t H5F__vfd_swmr_end_tick(H5F_t *f);
+H5_DLL herr_t H5F__vfd_swmr_disable_end_of_tick(H5F_t *f);
+H5_DLL herr_t H5F__vfd_swmr_enable_end_of_tick(H5F_t *f);
/* File mount related routines */
H5_DLL herr_t H5F__mount(H5G_loc_t *loc, const char *name, H5F_t *child, hid_t plist_id);
@@ -471,6 +589,12 @@ H5_DLL herr_t H5F__get_sohm_mesg_count_test(hid_t fid, unsigned type_id, size_t
H5_DLL herr_t H5F__check_cached_stab_test(hid_t file_id);
H5_DLL herr_t H5F__get_maxaddr_test(hid_t file_id, haddr_t *maxaddr);
H5_DLL herr_t H5F__get_sbe_addr_test(hid_t file_id, haddr_t *sbe_addr);
+
+/* VFD SWMR testing routines */
+H5_DLL herr_t H5F__vfd_swmr_writer_create_open_flush_test(hid_t file_id, hbool_t create);
+H5_DLL herr_t H5F__vfd_swmr_writer_md_test(hid_t, unsigned,
+ struct H5FD_vfd_swmr_idx_entry_t *, unsigned);
+
H5_DLL htri_t H5F__same_file_test(hid_t file_id1, hid_t file_id2);
#endif /* H5F_TESTING */
diff --git a/src/H5Fprivate.h b/src/H5Fprivate.h
index c5d4c89..fdacecd 100644
--- a/src/H5Fprivate.h
+++ b/src/H5Fprivate.h
@@ -203,6 +203,20 @@ typedef struct H5F_t H5F_t;
(p) += 8; \
}
+static inline uint64_t
+uint64_decode(uint8_t **pp)
+{
+ int i;
+ uint8_t *p = *pp;
+ uint64_t v = 0;
+
+ for (i = 0; i < 8; i++) {
+ v = (v << 8) | p[7 - i];
+ }
+ *pp += 8;
+ return v;
+}
+
# define UINT64DECODE(p, n) { \
/* WE DON'T CHECK FOR OVERFLOW! */ \
size_t _i; \
@@ -333,6 +347,10 @@ typedef struct H5F_t H5F_t;
#define H5F_THRESHOLD(F) ((F)->shared->threshold)
#define H5F_PGEND_META_THRES(F) ((F)->shared->fs.pgend_meta_thres)
#define H5F_POINT_OF_NO_RETURN(F) ((F)->shared->fs.point_of_no_return)
+#define H5F_FIRST_ALLOC_DEALLOC(F) ((F)->shared->first_alloc_dealloc)
+#define H5F_EOA_PRE_FSM_FSALLOC(F) ((F)->shared->eoa_pre_fsm_fsalloc)
+#define H5F_USE_VFD_SWMR(F) ((F)->shared->vfd_swmr)
+#define H5F_VFD_SWMR_MD_EOA(F) ((F)->shared->vfd_swmr_md_eoa)
#define H5F_NULL_FSM_ADDR(F) ((F)->shared->null_fsm_addr)
#define H5F_GET_MIN_DSET_OHDR(F) ((F)->shared->crt_dset_min_ohdr_flag)
#define H5F_SET_MIN_DSET_OHDR(F, V) ((F)->shared->crt_dset_min_ohdr_flag = (V))
@@ -395,6 +413,9 @@ typedef struct H5F_t H5F_t;
#define H5F_THRESHOLD(F) (H5F_get_threshold(F))
#define H5F_PGEND_META_THRES(F) (H5F_get_pgend_meta_thres(F))
#define H5F_POINT_OF_NO_RETURN(F) (H5F_get_point_of_no_return(F))
+#define H5F_FIRST_ALLOC_DEALLOC(F) (H5F_get_first_alloc_dealloc(F))
+#define H5F_EOA_PRE_FSM_FSALLOC(F) (H5F_get_eoa_pre_fsm_fsalloc(F))
+#define H5F_USE_VFD_SWMR(F) (H5F_use_vfd_swmr(F))
#define H5F_NULL_FSM_ADDR(F) (H5F_get_null_fsm_addr(F))
#define H5F_GET_MIN_DSET_OHDR(F) (H5F_get_min_dset_ohdr(F))
#define H5F_SET_MIN_DSET_OHDR(F, V) (H5F_set_min_dset_ohdr((F), (V)))
@@ -523,6 +544,21 @@ typedef struct H5F_t H5F_t;
#define H5F_ACS_MPI_PARAMS_INFO_NAME "mpi_params_info" /* the MPI info struct */
#endif /* H5_HAVE_PARALLEL */
+/* Default configuration for VFD SWMR: not configured */
+#define H5F_ACS_VFD_SWMR_CONFIG_NAME "vfd_swmr_config"
+#define H5F__DEFAULT_VFD_SWMR_CONFIG \
+{ \
+ /* int32_t version = */ 0, \
+ /* int32_t tick_len = */ 0, \
+ /* int32_t max_lag = */ 0, \
+ /* hbool_t vfd_swmr_writer = */ FALSE, \
+ /* hbool_t flush_raw_data = */ FALSE, \
+ /* int32_t md_pages_reserved = */ 0, \
+ /* int32_t pb_expansion_threshold = */ 0, \
+ /* char md_file_path[] = */ "", \
+ /* char log_file_path[] = */ "" \
+}
+
/* ======================== File Mount properties ====================*/
#define H5F_MNT_SYM_LOCAL_NAME "local" /* Whether absolute symlinks local to file. */
@@ -591,6 +627,10 @@ typedef struct H5F_t H5F_t;
#define H5F_SHARED_PAGED_AGGR(F_SH) ((F_SH)->fs_strategy == H5F_FSPACE_STRATEGY_PAGE && (F_SH)->fs_page_size)
#define H5F_PAGED_AGGR(F) (F->shared->fs_strategy == H5F_FSPACE_STRATEGY_PAGE && F->shared->fs_page_size)
+/* Check for file configured with VFD SWMR */
+#define H5F_SHARED_VFD_SWMR_CONFIG(S) (S->vfd_swmr_config.version >= H5F__CURR_VFD_SWMR_CONFIG_VERSION)
+#define H5F_VFD_SWMR_CONFIG(F) H5F_SHARED_VFD_SWMR_CONFIG(F->shared)
+
/* Metadata read attempt values */
#define H5F_METADATA_READ_ATTEMPTS 1 /* Default # of read attempts for non-SWMR access */
#define H5F_SWMR_METADATA_READ_ATTEMPTS 100 /* Default # of read attempts for SWMR access */
@@ -882,5 +922,7 @@ H5_DLL herr_t H5F_cwfs_remove_heap(H5F_shared_t *shared, struct H5HG_heap_t *hea
/* Debugging functions */
H5_DLL herr_t H5F_debug(H5F_t *f, FILE * stream, int indent, int fwidth);
+H5_DLL hbool_t H5F_use_vfd_swmr(const H5F_t *f);
+
#endif /* _H5Fprivate_H */
diff --git a/src/H5Fpublic.h b/src/H5Fpublic.h
index 02568c9..d4f6341 100644
--- a/src/H5Fpublic.h
+++ b/src/H5Fpublic.h
@@ -219,6 +219,96 @@ typedef struct H5F_retry_info_t {
/* Callback for H5Pset_object_flush_cb() in a file access property list */
typedef herr_t (*H5F_flush_cb_t)(hid_t object_id, void *udata);
+/* VFD SWMR configuration data used by H5Pset/get_vfd_swmr_config */
+#define H5F__CURR_VFD_SWMR_CONFIG_VERSION 1
+#define H5F__MAX_VFD_SWMR_FILE_NAME_LEN 1024
+#define H5F__MAX_PB_EXPANSION_THRESHOLD 100
+/*
+ * struct H5F_vfd_swmr_config_t
+ *
+ * Instances of H5F_vfd_swmr_config_t are used by VFD SWMR writers and readers
+ * to pass necessary configuration data to the HDF5 library on file open (or
+ * creation, in the case of writers).
+ *
+ * The fields of the structure are discussed below:
+ * version:
+ * An integer field indicating the version of the H5F_vfd_swmr_config
+ * structure used. This field must always be set to a known version
+ * number. The most recent version of the structure will always be
+ * H5F__CURR_VFD_SWMR_CONFIG_VERSION.
+ *
+ * tick_len:
+ * An integer field containing the length of a tick in tenths of
+ * a second. If tick_len is zero, end of tick processing may only be
+ * triggered manually via the H5Fvfd_swmr_end_tick() function.
+ *
+ * max_lag:
+ * An integer field indicating the maximum expected lag (in ticks)
+ * between the writer and the readers. This value must be at least 3,
+ * with 10 being the recommended minimum value.
+ *
+ * writer:
+ * A boolean flag indicating whether the file opened with this FAPL entry
+ * will be opened R/W. (i.e. as a VFD SWMR writer)
+ *
+ * flush_raw_data:
+ * A boolean flag indicating whether raw data should be flushed
+ * as part of the end of tick processing. If set to TRUE, raw
+ * data will be flushed and thus be consistent with the metadata file.
+ * However, this will also greatly increase end of tick I/O, and will
+ * likely break any real time guarantees unless a very large tick_len
+ * is selected.
+ *
+ * md_pages_reserved:
+ * An integer field indicating the number of pages reserved
+ * at the head of the metadata file. This value must be greater than
+ * or equal to 1.
+ * When the metadata file is created, the specified number of pages is
+ * reserved at the head of the metadata file. In the current
+ * implementation, the size of the metadata file header plus the
+ * index is limited to this size.
+ * Further, in the POSIX case, when readers check for an updated index,
+ * this check will start with a read of md_pages_reserved pages from
+ * the head of the metadata file.
+ *
+ * pb_expansion_threshold:
+ * An integer field indicating the threshold for the page buffer size.
+ * During a tick, the page buffer must expand as necessary to retain copies
+ * of all modified metadata pages and multi-page metadata entries.
+ * If the page buffer size exceeds this thresold, an early end of tick
+ * will be triggered.
+ * Note that this is not a limit on the maximum page buffer size, as the
+ * metadata cache is flushed as part of end of tick processing.
+ * This threshold must be in the range [0, 100]. If the threshold is 0,
+ * the feature is disabled. For all other values, the page buffer size is
+ * multiplied by this threshold. If this value is exceeded, an early end
+ * of tick is triggered.
+ *
+ * md_file_path:
+ * POSIX: this field contains the path of the metadata file.
+ * NFS: it contains the path and base name of the metadata file
+ * updater files.
+ * Object store: it contains the base URL for the objects used
+ * to store metadata file updater objects.
+ *
+ * log_file_path:
+ * This field contains the path to the log file. If defined, this path should
+ * be unique to each process. If this field contains the empty string, a log
+ * file will not be created.
+ *
+ */
+typedef struct H5F_vfd_swmr_config_t {
+ int32_t version;
+ uint32_t tick_len;
+ uint32_t max_lag;
+ hbool_t writer;
+ hbool_t flush_raw_data;
+ uint32_t md_pages_reserved;
+ uint32_t pb_expansion_threshold;
+ char md_file_path[H5F__MAX_VFD_SWMR_FILE_NAME_LEN + 1];
+ char log_file_path[H5F__MAX_VFD_SWMR_FILE_NAME_LEN + 1];
+} H5F_vfd_swmr_config_t;
+
/*********************/
/* Public Prototypes */
/*********************/
@@ -281,6 +371,13 @@ H5_DLL herr_t H5Fget_mdc_image_info(hid_t file_id, haddr_t *image_addr, hsize_t
H5_DLL herr_t H5Fget_dset_no_attrs_hint(hid_t file_id, hbool_t *minimize);
H5_DLL herr_t H5Fset_dset_no_attrs_hint(hid_t file_id, hbool_t minimize);
+/* VFD SWMR */
+H5_DLL herr_t H5Fvfd_swmr_end_tick(hid_t file_id);
+H5_DLL herr_t H5Fvfd_swmr_disable_end_of_tick(hid_t file_id);
+H5_DLL herr_t H5Fvfd_swmr_enable_end_of_tick(hid_t file_id);
+H5_DLL bool vfd_swmr_writer_may_increase_tick_to(uint64_t, bool);
+H5_DLL void vfd_swmr_reader_did_increase_tick_to(uint64_t);
+
#ifdef H5_HAVE_PARALLEL
H5_DLL herr_t H5Fset_mpi_atomicity(hid_t file_id, hbool_t flag);
H5_DLL herr_t H5Fget_mpi_atomicity(hid_t file_id, hbool_t *flag);
diff --git a/src/H5Fquery.c b/src/H5Fquery.c
index e1b11c8..b565f22 100644
--- a/src/H5Fquery.c
+++ b/src/H5Fquery.c
@@ -1364,3 +1364,26 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5F_get_cont_info */
+
+/*-------------------------------------------------------------------------
+ * Function: H5F_use_vfd_swmr
+ *
+ * Purpose: Quick and dirty routine to determine if VFD SWMR is
+ * enabled for this file.
+ * (Mainly added to stop non-file routines from poking about in the
+ * H5F_t data structure)
+ *
+ * Return: TRUE/FALSE on success/abort on failure (shouldn't fail)
+ *-------------------------------------------------------------------------
+ */
+hbool_t
+H5F_use_vfd_swmr(const H5F_t *f)
+{
+ /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ HDassert(f);
+ HDassert(f->shared);
+
+ FUNC_LEAVE_NOAPI(f->shared->vfd_swmr)
+} /* end H5F_use_vfd_swmr() */
diff --git a/src/H5Fsfile.c b/src/H5Fsfile.c
index 9a9bbab..3cbd490 100644
--- a/src/H5Fsfile.c
+++ b/src/H5Fsfile.c
@@ -138,28 +138,14 @@ done:
H5F_shared_t *
H5F__sfile_search(H5FD_t *lf)
{
- H5F_sfile_node_t *curr; /* Current shared file node */
- H5F_shared_t *ret_value = NULL; /* Return value */
-
- FUNC_ENTER_PACKAGE_NOERR
-
- /* Sanity check */
- HDassert(lf);
-
- /* Iterate through low-level files for matching low-level file info */
- curr = H5F_sfile_head_g;
- while(curr) {
- /* Check for match */
- if(0 == H5FD_cmp(curr->shared->lf, lf))
- HGOTO_DONE(curr->shared)
-
- /* Advance to next shared file node */
- curr = curr->next;
- } /* end while */
-
-done:
- FUNC_LEAVE_NOAPI(ret_value)
-} /* end H5F__sfile_search() */
+ H5F_sfile_node_t *curr;
+
+ for (curr = H5F_sfile_head_g; curr != NULL; curr = curr->next) {
+ if(curr->shared->lf == lf)
+ return curr->shared;
+ }
+ return NULL;
+}
/*-------------------------------------------------------------------------
diff --git a/src/H5Fspace.c b/src/H5Fspace.c
index 6baf163..7de8b20 100644
--- a/src/H5Fspace.c
+++ b/src/H5Fspace.c
@@ -43,6 +43,7 @@
/****************/
+
/******************/
/* Local Typedefs */
/******************/
diff --git a/src/H5Fsuper_cache.c b/src/H5Fsuper_cache.c
index 119548c..d1227d0 100644
--- a/src/H5Fsuper_cache.c
+++ b/src/H5Fsuper_cache.c
@@ -84,6 +84,8 @@ static herr_t H5F__cache_drvrinfo_image_len(const void *thing, size_t *image_len
static herr_t H5F__cache_drvrinfo_serialize(const H5F_t *f, void *image, size_t len,
void *thing);
static herr_t H5F__cache_drvrinfo_free_icr(void *thing);
+static herr_t H5F__cache_superblock_refresh(H5F_t *f, void * _thing, const void * _image,
+ size_t * len_ptr);
/* Local encode/decode routines */
static herr_t H5F__superblock_prefix_decode(H5F_super_t *sblock,
@@ -114,6 +116,7 @@ const H5AC_class_t H5AC_SUPERBLOCK[1] = {{
NULL, /* 'notify' callback */
H5F__cache_superblock_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ H5F__cache_superblock_refresh, /* VFD SWMR 'refresh' callback */
}};
/* H5F driver info block inherits cache-like properties from H5AC */
@@ -132,6 +135,7 @@ const H5AC_class_t H5AC_DRVRINFO[1] = {{
NULL, /* 'notify' callback */
H5F__cache_drvrinfo_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
@@ -1087,3 +1091,268 @@ H5F__cache_drvrinfo_free_icr(void *_thing)
FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5F__cache_drvrinfo_free_icr() */
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__cache_superblock_refresh
+ *
+ * Purpose: Examine the supplied image buffer, and update the
+ * superblock accordingly.
+ *
+ * This function is only called when the file is opened in
+ * VFD SWMR reader mode -- which implies that the file has
+ * been opened R/O. Thus the internal representation of
+ * the superblock must be clean, and may be modified without
+ * concern for local changes.
+ *
+ * Further, most of the superblock is fixed once the file
+ * is created, for the most part, this function simply
+ * verifies the expected values.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: John Mainzer
+ * 12/21/19
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__cache_superblock_refresh(H5F_t *f, void * _thing, const void * _image,
+ size_t * len_ptr)
+{
+ H5F_super_t *sblock = (H5F_super_t *)_thing;
+ const uint8_t *image = (const uint8_t *)_image;
+ size_t expected_image_len;
+ unsigned super_vers; /* Superblock version */
+ uint8_t sizeof_addr; /* Size of addresses in file */
+ uint8_t sizeof_size; /* Size of offsets in file */
+ uint32_t status_flags; /* File status flags */
+ unsigned sym_leaf_k; /* Size of leaves in symbol tables */
+ haddr_t base_addr; /* Absolute base address for rel.addrs. */
+ /* (superblock for file is at this offset) */
+ haddr_t stored_eof;
+ haddr_t ext_addr; /* Relative address of superblock extension */
+ haddr_t driver_addr; /* File driver information block address */
+ haddr_t root_addr; /* Root group address */
+ H5G_entry_t root_ent; /* Root group symbol table entry */
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ /* santity checks */
+ HDassert(f);
+ HDassert(sblock);
+ HDassert(sblock == f->shared->sblock);
+ HDassert(image);
+ HDassert(len_ptr);
+ HDassert(*len_ptr >= H5F_SUPERBLOCK_FIXED_SIZE + 6);
+
+ /* skip the signature */
+ image += H5F_SIGNATURE_LEN;
+
+ /* get the superblock version */
+ super_vers = *image++;
+
+ if ( sblock->super_vers != super_vers )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected superblock vers")
+
+ /* verify sizes of addresses and offsets */
+ if(super_vers < HDF5_SUPERBLOCK_VERSION_2) {
+ sizeof_addr = image[4];
+ sizeof_size = image[5];
+ } /* end if */
+ else {
+ sizeof_addr = image[0];
+ sizeof_size = image[1];
+ } /* end else */
+
+ if ( sblock->sizeof_addr != sizeof_addr )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected sizeof_addr")
+
+ if ( sblock->sizeof_size != sizeof_size )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected sizeof_size")
+
+ /* compute expected image len */
+ expected_image_len = H5F_SUPERBLOCK_FIXED_SIZE +
+ (size_t)H5F_SUPERBLOCK_VARLEN_SIZE(super_vers, sizeof_addr, sizeof_size);
+
+ if ( expected_image_len != *len_ptr ) {
+
+ *len_ptr = expected_image_len;
+ HGOTO_DONE(SUCCEED)
+ }
+
+ /* at this point, we know that the supplied image is of
+ * the correct length.
+ */
+
+ /* validate the older version of the superblock */
+ if(sblock->super_vers < HDF5_SUPERBLOCK_VERSION_2) {
+
+ unsigned snode_btree_k; /* B-tree symbol table internal node 'K' value */
+ unsigned chunk_btree_k; /* B-tree chunk internal node 'K' value */
+
+ /* Freespace version (hard-wired) */
+ if(HDF5_FREESPACE_VERSION != *image++)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "bad free space version number")
+
+ /* Root group version number (hard-wired) */
+ if(HDF5_OBJECTDIR_VERSION != *image++)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "bad object directory version number")
+
+ /* Skip over reserved byte */
+ image++;
+
+ /* Shared header version number (hard-wired) */
+ if(HDF5_SHAREDHEADER_VERSION != *image++)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "bad shared-header format version number")
+
+ /* Skip over size of file addresses (already decoded and checked) */
+ image++;
+
+ /* Skip over size of file sizes (already decoded and checked) */
+ image++;
+
+ /* Skip over reserved byte */
+ image++;
+
+ /* Various B-tree sizes */
+ UINT16DECODE(image, sym_leaf_k);
+ if ( sym_leaf_k != sblock->sym_leaf_k )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected sym_leaf_k")
+
+ /* Need 'get' call to set other array values */
+ UINT16DECODE(image, snode_btree_k);
+ if ( snode_btree_k != sblock->btree_k[H5B_SNODE_ID] )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected snode_btree_k")
+
+ /* File status flags (not really used yet) */
+ /* If the file has closed, the status flags will be zero.
+ * Allow this.
+ */
+ UINT32DECODE(image, status_flags);
+ if ( ( status_flags != sblock->status_flags ) &&
+ ( status_flags != 0 ) )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected status_flags")
+
+ /*
+ * If the superblock version # is greater than 0, read in the indexed
+ * storage B-tree internal 'K' value
+ */
+ if(sblock->super_vers > HDF5_SUPERBLOCK_VERSION_DEF) {
+ UINT16DECODE(image, chunk_btree_k);
+
+ if ( chunk_btree_k != sblock->btree_k[H5B_CHUNK_ID] )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected chunk_btree_k")
+
+ /* Reserved bytes are present only in version 1 */
+ if(sblock->super_vers == HDF5_SUPERBLOCK_VERSION_1)
+ image += 2; /* reserved */
+ } /* end if */
+
+ /* Remainder of "variable-sized" portion of superblock */
+ H5F_addr_decode(f, (const uint8_t **)&image, &base_addr/*out*/);
+ H5F_addr_decode(f, (const uint8_t **)&image, &ext_addr/*out*/);
+ H5F_addr_decode(f, (const uint8_t **)&image, &stored_eof/*out*/);
+ H5F_addr_decode(f, (const uint8_t **)&image, &driver_addr/*out*/);
+
+ if ( base_addr != sblock->base_addr )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected base_addr")
+
+ if ( ext_addr != sblock->ext_addr )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected ext_addr")
+
+ /* use stored_eof to update EOA below */
+
+ if ( driver_addr != sblock->driver_addr )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected driver_addr")
+
+ /* decode the root group symbol table entry */
+ if(H5G_ent_decode(f, (const uint8_t **)&image, &root_ent) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTDECODE, FAIL, "can't decode root group symbol table entry")
+
+ /* Set the root group address to the correct value */
+ root_addr = root_ent.header;
+
+ if ( root_addr != sblock->root_addr )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected root_addr")
+
+ HDassert(root_ent.type == H5G_CACHED_STAB);
+
+ if ( ( root_ent.type != sblock->root_ent->type ) ||
+ ( root_ent.cache.stab.btree_addr !=
+ sblock->root_ent->cache.stab.btree_addr ) ||
+ ( root_ent.cache.stab.heap_addr !=
+ sblock->root_ent->cache.stab.heap_addr ) )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected root_ent data")
+
+
+ /* NOTE: Driver info block is decoded separately, later */
+
+ } /* end if */
+ else {
+ uint32_t read_chksum;
+ uint32_t computed_chksum;
+
+ /* Skip over size of file addresses (already decoded and checked) */
+ image++;
+
+ /* Skip over size of file sizes (already decoded and checked) */
+ image++;
+
+ /* File status flags (not really used yet) */
+ status_flags = *image++;
+
+ /* If the file has closed, the status flags will be zero.
+ * Allow this.
+ */
+ if ( ( status_flags != sblock->status_flags ) &&
+ ( status_flags != 0 ) )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected status_flags")
+
+ /* Base, superblock extension, end of file & root group object
+ * header addresses
+ */
+ H5F_addr_decode(f, (const uint8_t **)&image, &base_addr/*out*/);
+ H5F_addr_decode(f, (const uint8_t **)&image, &ext_addr/*out*/);
+ H5F_addr_decode(f, (const uint8_t **)&image, &stored_eof/*out*/);
+ H5F_addr_decode(f, (const uint8_t **)&image, &root_addr/*out*/);
+
+ if ( base_addr != sblock->base_addr )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected base_addr")
+
+ if ( ext_addr != sblock->ext_addr )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected ext_addr")
+
+ if ( root_addr != sblock->root_addr )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected root_addr")
+
+ /* use stored_eof to update EOA below */
+
+ /* Decode checksum */
+ UINT32DECODE(image, read_chksum);
+
+ if ( H5F_get_checksums((const uint8_t *)_image,
+ (size_t)(image - (const uint8_t *)_image),
+ NULL, &computed_chksum) < 0 )
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "can't compute chksum")
+
+ if ( read_chksum != computed_chksum )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "unexpected checksum")
+
+ } /* end else */
+
+ /* Sanity check */
+ HDassert((size_t)(image - (const uint8_t *)_image) <= *len_ptr);
+
+ /* update the EOA */
+ if(H5F__set_eoa(f, H5FD_MEM_DEFAULT, stored_eof - base_addr) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to update EOA")
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* end H5F__cache_superblock_refresh() */
diff --git a/src/H5Ftest.c b/src/H5Ftest.c
index 49a2a22..f249d45 100644
--- a/src/H5Ftest.c
+++ b/src/H5Ftest.c
@@ -38,12 +38,14 @@
/* Headers */
/***********/
#include "H5private.h" /* Generic Functions */
+#include "H5FDprivate.h" /* File Drivers */
#include "H5CXprivate.h" /* API Contexts */
#include "H5Eprivate.h" /* Error handling */
#include "H5Fpkg.h" /* File access */
#include "H5Gpkg.h" /* Groups */
#include "H5Iprivate.h" /* IDs */
#include "H5SMpkg.h" /* Shared object header messages */
+#include "H5MMprivate.h" /* Memory management */
#include "H5VLprivate.h" /* Virtual Object Layer */
@@ -65,6 +67,11 @@
/********************/
/* Local Prototypes */
/********************/
+static herr_t H5F__vfd_swmr_decode_md_hdr(int md_fd, H5FD_vfd_swmr_md_header *md_hdr);
+static herr_t H5F__vfd_swmr_decode_md_idx(int md_fd, H5FD_vfd_swmr_md_header *md_hdr, H5FD_vfd_swmr_md_index *md_idx);
+static herr_t H5F__vfd_swmr_verify_md_hdr_and_idx(H5F_t *f,
+ H5FD_vfd_swmr_md_header *md_hdr, H5FD_vfd_swmr_md_index *md_idx,
+ unsigned num_entries, H5FD_vfd_swmr_idx_entry_t *index);
/*********************/
@@ -75,6 +82,8 @@
/*****************************/
/* Library Private Variables */
/*****************************/
+/* Declare external the free list for H5FD_vfd_swmr_idx_entry_t */
+H5FL_SEQ_EXTERN(H5FD_vfd_swmr_idx_entry_t);
/*******************/
@@ -233,6 +242,373 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5F__get_sbe_addr_test() */
+/*
+ * VFD SWMR tests
+ */
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__vfd_swmr_writer_create_open_flush_test
+ *
+ * Purpose: Verify info in the header and index when:
+ * (1) creating an HDF5 file
+ * (2) opening an existing HDF5 file
+ * (3) flushing an HDF5 file
+ *
+ * Open the metadata file
+ * Verify the file size is as expected (md_pages_reserved)
+ * For file create:
+ * --No header magic is found
+ * For file open or file flush:
+ * --Read and decode the header and index in the metadata file
+ * --Verify info in the header and index read from
+ * the metadata file is as expected (empty index)
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F__vfd_swmr_writer_create_open_flush_test(hid_t file_id, hbool_t file_create)
+{
+ H5F_t *f; /* File pointer */
+ h5_stat_t stat_buf; /* Buffer for stat info */
+ H5FD_vfd_swmr_md_header md_hdr; /* Header for the metadata file */
+ H5FD_vfd_swmr_md_index md_idx; /* Indedx for the metadata file */
+ int md_fd = -1; /* The metadata file descriptor */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments */
+ if(NULL == (f = (H5F_t *)H5VL_object_verify(file_id, H5I_FILE)))
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file")
+
+ /* Open the metadata file */
+ if((md_fd = HDopen(f->shared->vfd_swmr_config.md_file_path, O_RDONLY)) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "error opening metadata file")
+
+ /* Verify the minimum size for the metadata file */
+ if(HDstat(f->shared->vfd_swmr_config.md_file_path, &stat_buf) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_BADFILE, FAIL, "unable to stat the metadata file")
+ if(stat_buf.st_size < (HDoff_t)((hsize_t)f->shared->vfd_swmr_config.md_pages_reserved * f->shared->fs_page_size))
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect metadata file size")
+
+ if(file_create) { /* Creating file */
+ uint32_t hdr_magic;
+
+ /* Seek to the beginning of the file */
+ if(HDlseek(md_fd, (HDoff_t)H5FD_MD_HEADER_OFF, SEEK_SET) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_SEEKERROR, FAIL, "error seeking metadata file")
+
+ /* Try to read the magic for header */
+ if(HDread(md_fd, &hdr_magic, H5_SIZEOF_MAGIC) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_READERROR, FAIL, "error reading metadata file")
+
+ /* Verify that there is no header magic in the metadata file */
+ if(HDmemcmp(&hdr_magic, H5FD_MD_HEADER_MAGIC, (size_t)H5_SIZEOF_MAGIC) == 0)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "error finding header magic in the metadata file")
+
+ } else { /* Opening or flushing the file */
+
+ HDmemset(&md_hdr, 0, sizeof(H5FD_vfd_swmr_md_header));
+ HDmemset(&md_idx, 0, sizeof(H5FD_vfd_swmr_md_index));
+
+ /* Decode the header */
+ if(H5F__vfd_swmr_decode_md_hdr(md_fd, &md_hdr) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTDECODE, FAIL, "error decoding header in the metadata file")
+
+ /* Decode the index */
+ if(H5F__vfd_swmr_decode_md_idx(md_fd, &md_hdr, &md_idx) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTDECODE, FAIL, "error decoding index in the metadata file")
+
+ /* Verify info in header and index read from the metadata file */
+ if(H5F__vfd_swmr_verify_md_hdr_and_idx(f, &md_hdr, &md_idx, 0, NULL) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect info found in header and index of the metadata file")
+ }
+
+done:
+ /* Free the index entries */
+ if(!file_create && md_idx.entries) {
+ HDassert(md_idx.num_entries);
+ H5MM_free(md_idx.entries);
+ }
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5F__vfd_swmr_writer_create_open_flush_test() */
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__vfd_swmr_decode_md_hdr
+ *
+ * Purpose: Decode header and verify header magic
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__vfd_swmr_decode_md_hdr(int md_fd, H5FD_vfd_swmr_md_header *md_hdr)
+{
+ uint64_t index_length;
+ uint8_t image[H5FD_MD_HEADER_SIZE]; /* Buffer for the header image */
+ uint8_t *p = NULL; /* Points to the image */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ p = image;
+
+ /* Seek to the beginning of the file */
+ if(HDlseek(md_fd, (HDoff_t)H5FD_MD_HEADER_OFF, SEEK_SET) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_SEEKERROR, FAIL, "error seeking metadata file")
+
+ /* Read the header */
+ if(HDread(md_fd, image, H5FD_MD_HEADER_SIZE) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_READERROR, FAIL, "error reading metadata file")
+
+ /* Verify magic for header */
+ if(HDmemcmp(p, H5FD_MD_HEADER_MAGIC, (size_t)H5_SIZEOF_MAGIC) != 0)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "does not find header magic in the metadata file")
+
+ p += H5_SIZEOF_MAGIC;
+
+ /* Deserialize fs_page_size, tick_num, index_offset, index_length */
+ UINT32DECODE(p, md_hdr->fs_page_size);
+ UINT64DECODE(p, md_hdr->tick_num);
+ UINT64DECODE(p, md_hdr->index_offset);
+ if ((index_length = uint64_decode(&p)) > SIZE_MAX) {
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "index is too long")
+ }
+ md_hdr->index_length = (size_t)index_length;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5F__vfd_swmr_decode_md_hdr() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__vfd_swmr_decode_md_idx
+ *
+ * Purpose: Decode index and verify index magic
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__vfd_swmr_decode_md_idx(int md_fd, H5FD_vfd_swmr_md_header *md_hdr, H5FD_vfd_swmr_md_index *md_idx)
+{
+ uint8_t *image = NULL; /* Points to the buffer for the index image */
+ uint8_t *p = NULL; /* Points to the image */
+ unsigned i; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Allocate buffer for the index image */
+ if(NULL == (image = H5MM_malloc(md_hdr->index_length)))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL, "memory allocation failed for index on disk buffer")
+
+ p = image;
+
+ /* Seek to the position of the index */
+ if(HDlseek(md_fd, (HDoff_t)md_hdr->index_offset, SEEK_SET) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_SEEKERROR, FAIL, "unable to seek in metadata file")
+
+ /* Read the index */
+ if(HDread(md_fd, image, md_hdr->index_length) < (int64_t)md_hdr->index_length)
+ HGOTO_ERROR(H5E_FILE, H5E_READERROR, FAIL, "error in reading the header in metadata file")
+
+ /* Verify magic for index */
+ if(HDmemcmp(p, H5FD_MD_INDEX_MAGIC, H5_SIZEOF_MAGIC) != 0)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "no header magic in the metadata file")
+
+ p += H5_SIZEOF_MAGIC;
+
+ /* Deserialize tick_num and num_entries */
+ UINT64DECODE(p, md_idx->tick_num);
+ UINT32DECODE(p, md_idx->num_entries);
+
+ /* Deserialize index entries */
+ if(md_idx->num_entries) {
+ md_idx->entries =
+ H5MM_calloc(md_idx->num_entries * sizeof(md_idx->entries[0]));
+ /* Allocate memory for the index entries */
+ if(NULL == md_idx->entries)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL, "memory allocation failed for index entries")
+
+ /* Decode index entries */
+ for(i = 0; i < md_idx->num_entries; i++) {
+ UINT32DECODE(p, md_idx->entries[i].hdf5_page_offset);
+ UINT32DECODE(p, md_idx->entries[i].md_file_page_offset);
+ UINT32DECODE(p, md_idx->entries[i].length);
+ UINT32DECODE(p, md_idx->entries[i].chksum);
+ } /* end for */
+
+ } /* end if */
+
+done:
+ /* Free the buffer */
+ if(image)
+ H5MM_free(image);
+ if(ret_value < 0) {
+ /* Free the index entries */
+ if(md_idx->entries) {
+ HDassert(md_idx->num_entries);
+ H5MM_free(md_idx->entries);
+ }
+ }
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5F__vfd_swmr_decode_md_idx() */
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__vfd_swmr_verify_md_hdr_idx_test
+ *
+ * Purpose: Verify the header and index in the metadata file:
+ * --fs_page_size in md header is the same as that stored in "f"
+ * --index_length in md header is as indicated by num_entries
+ * --index_offset in md header is right after the header
+ * --number of entries in md index is num_entries
+ * --entries in md index is as indicated by num_entries and index
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__vfd_swmr_verify_md_hdr_and_idx(H5F_t *f,
+ H5FD_vfd_swmr_md_header *md_hdr, H5FD_vfd_swmr_md_index *md_idx,
+ unsigned num_entries, H5FD_vfd_swmr_idx_entry_t *index)
+{
+ unsigned i; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Verify fs_page_size read from header in the metadata file is fs_page_size in f */
+ if(md_hdr->fs_page_size != f->shared->fs_page_size)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect fs_page_size read from metadata file")
+
+ /* Verify index_length read from header in the metadata file is the size of num_entries index */
+ if(md_hdr->index_length != H5FD_MD_INDEX_SIZE(num_entries))
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect index_length read from metadata file")
+
+ /* Verify index_offset read from header in the metadata file is the size of md header */
+ if(md_hdr->index_offset != f->shared->fs_page_size)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect index_offset read from metadata file")
+
+ /* Verify num_entries read from index in the metadata file is num_entries */
+ if(md_idx->num_entries != num_entries)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect num_entries read from metadata file")
+
+ /* Verify empty/non-empty index entries */
+ if(num_entries == 0) {
+ /* Verify the index is empty */
+ if(md_idx->entries != NULL)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect entries in index")
+ } else {
+ /* Verify entries */
+ for(i = 0; i < num_entries; i++) {
+ if(md_idx->entries[i].length != index[i].length)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect length read from metadata file")
+
+ if(md_idx->entries[i].hdf5_page_offset != index[i].hdf5_page_offset)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect hdf5_page_offset read from metadata file")
+
+ if(md_idx->entries[i].md_file_page_offset != index[i].md_file_page_offset)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect md_file_page_offset read from metadata file")
+
+ if(md_idx->entries[i].chksum != index[i].chksum)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect chksum read from metadata file")
+ }
+ }
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5F__vfd_swmr_verify_md_hdr_and_idx() */
+
+static unsigned
+count_shadow_defrees(shadow_defree_queue_t *shadow_defrees)
+{
+ shadow_defree_t *shadow_defree;
+ unsigned count = 0;
+
+ TAILQ_FOREACH(shadow_defree, shadow_defrees, link)
+ count++;
+
+ return count;
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__vfd_swmr_writer_md_test
+ *
+ * Purpose: Update the metadata file with the input index and verify
+ * the following:
+ * --info read from the metadata file is as indicated by
+ * the input: num_entries, index
+ * --# of entries on the delayed list is as indicated by
+ * the input: nshadow_defrees
+ *
+ * Return: SUCCEED/FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F__vfd_swmr_writer_md_test(hid_t file_id, unsigned num_entries,
+ H5FD_vfd_swmr_idx_entry_t *index, unsigned nshadow_defrees)
+{
+ H5F_t *f; /* File pointer */
+ int md_fd = -1; /* The metadata file descriptor */
+ H5FD_vfd_swmr_md_header md_hdr; /* Header for the metadata file */
+ H5FD_vfd_swmr_md_index md_idx; /* Index for the metadata file */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ HDmemset(&md_hdr, 0, sizeof(H5FD_vfd_swmr_md_header));
+ HDmemset(&md_idx, 0, sizeof(H5FD_vfd_swmr_md_index));
+
+ /* Check arguments */
+ if(NULL == (f = (H5F_t *)H5VL_object_verify(file_id, H5I_FILE)))
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file")
+
+ /* Update the metadata file with the input index */
+ if(H5F_update_vfd_swmr_metadata_file(f, num_entries, index) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL, "error updating the md file with the index")
+
+ /* Verify the number of entries in the delayed list is as expected */
+ if(count_shadow_defrees(&f->shared->shadow_defrees) == nshadow_defrees)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect # of entries in the delayed list")
+
+ /* Open the metadata file */
+ if((md_fd = HDopen(f->shared->vfd_swmr_config.md_file_path, O_RDONLY)) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "error opening metadata file")
+
+ /* Decode the header in the metadata file */
+ if(H5F__vfd_swmr_decode_md_hdr(md_fd, &md_hdr) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTDECODE, FAIL, "error decoding header in the metadata file")
+
+ /* Decode the index in the metadata file */
+ if(H5F__vfd_swmr_decode_md_idx(md_fd, &md_hdr, &md_idx) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "error decoding index in the metadata file")
+
+ /* Verify info read from the metadata file is the same as the input index */
+ if(H5F__vfd_swmr_verify_md_hdr_and_idx(f, &md_hdr, &md_idx, num_entries, index) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "incorrect info found in header and index of the metadata file")
+
+done:
+ /* Free the index entries */
+ if(md_idx.entries) {
+ HDassert(md_idx.num_entries);
+ H5MM_free(md_idx.entries);
+ }
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5F__vfd_swmr_writer_md_test() */
+
/*-------------------------------------------------------------------------
* Function: H5F__same_file_test
diff --git a/src/H5Fvfd_swmr.c b/src/H5Fvfd_swmr.c
new file mode 100644
index 0000000..376fa38
--- /dev/null
+++ b/src/H5Fvfd_swmr.c
@@ -0,0 +1,2111 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5Fvfd_swmr.c
+ * Oct 10 2019
+ *
+ * Purpose: Functions for VFD SWMR.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/****************/
+/* Module Setup */
+/****************/
+
+#include "H5Fmodule.h" /* This source code file is part of the H5F module */
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5Aprivate.h" /* Attributes */
+#include "H5ACprivate.h" /* Metadata cache */
+#include "H5CXprivate.h" /* API Contexts */
+#include "H5Dprivate.h" /* Datasets */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5Fpkg.h" /* File access */
+#include "H5FDprivate.h" /* File drivers */
+#include "H5Gprivate.h" /* Groups */
+#include "H5Iprivate.h" /* IDs */
+#include "H5Lprivate.h" /* Links */
+#include "H5MFprivate.h" /* File memory management */
+#include "H5MVprivate.h" /* File memory management for VFD SWMR */
+#include "H5MMprivate.h" /* Memory management */
+#include "H5Pprivate.h" /* Property lists */
+#include "H5SMprivate.h" /* Shared Object Header Messages */
+#include "H5Tprivate.h" /* Datatypes */
+#include "hlog.h"
+
+/****************/
+/* Local Macros */
+/****************/
+
+#define nanosecs_per_second 1000000000 /* nanoseconds per second */
+#define nanosecs_per_tenth_sec 100000000 /* nanoseconds per 0.1 second */
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static herr_t H5F__vfd_swmr_update_end_of_tick_and_tick_num(H5F_shared_t *,
+ hbool_t);
+static herr_t H5F__vfd_swmr_construct_write_md_hdr(H5F_shared_t *, uint32_t);
+static herr_t H5F__vfd_swmr_construct_write_md_idx(H5F_shared_t *, uint32_t,
+ struct H5FD_vfd_swmr_idx_entry_t[]);
+static herr_t H5F__idx_entry_cmp(const void *_entry1, const void *_entry2);
+static herr_t H5F__vfd_swmr_create_index(H5F_shared_t *);
+static herr_t H5F__vfd_swmr_writer__wait_a_tick(H5F_t *);
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+/*
+ * Globals for VFD SWMR
+ */
+
+unsigned int vfd_swmr_api_entries_g = 0;/* Times the library was entered
+ * and re-entered minus the times
+ * it was exited. We only perform
+ * the end-of-tick processing
+ * on the 0->1 and 1->0
+ * transitions.
+ */
+HLOG_OUTLET_SHORT_DEFN(swmr, all);
+HLOG_OUTLET_SHORT_DEFN(eot, swmr);
+HLOG_OUTLET_SHORT_DEFN(eotq, eot);
+HLOG_OUTLET_SHORT_DEFN(shadow_defrees, swmr);
+HLOG_OUTLET_MEDIUM_DEFN(noisy_shadow_defrees, shadow_defrees,
+ HLOG_OUTLET_S_OFF);
+HLOG_OUTLET_SHORT_DEFN(shadow_index_enlarge, swmr);
+HLOG_OUTLET_SHORT_DEFN(shadow_index_reclaim, swmr);
+HLOG_OUTLET_SHORT_DEFN(shadow_index_update, swmr);
+HLOG_OUTLET_SHORT_DEFN(tick, swmr);
+HLOG_OUTLET_SHORT_DEFN(mdc_invalidation, swmr);
+
+/*
+ * The head of the end of tick queue (EOT queue) for files opened in either
+ * VFD SWMR write or VFD SWMR read mode
+ */
+eot_queue_t eot_queue_g = TAILQ_HEAD_INITIALIZER(eot_queue_g);
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+/* Declare a free list to manage the shadow_defree_t struct */
+H5FL_DEFINE(shadow_defree_t);
+
+/* Declare a free list to manage the eot_queue_entry_t struct */
+H5FL_DEFINE(eot_queue_entry_t);
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F_vfd_swmr_init
+ *
+ * Purpose: Initialize globals and the corresponding fields in
+ * file pointer.
+ *
+ * For both VFD SWMR writer and reader:
+ *
+ * --set end_of_tick to the current time + tick length
+ *
+ * For VFD SWMR writer:
+ *
+ * --set f->shared->tick_num to 1
+ * --create the metadata file
+ * --when opening an existing HDF5 file, write header and
+ * empty index in the metadata file
+ *
+ * For VFD SWMR reader:
+ *
+ * --set f->shared->tick_num to the current tick read from the
+ * metadata file
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi -- 11/??/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_vfd_swmr_init(H5F_t *f, hbool_t file_create)
+{
+ hsize_t md_size; /* Size of the metadata file */
+ haddr_t hdr_addr, idx_addr; /* Addresses returned from H5MV_alloc() */
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5F_shared_t *shared = f->shared;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ HDassert(H5F_SHARED_VFD_SWMR_CONFIG(shared));
+
+ shared->vfd_swmr = TRUE;
+
+ if(H5F_SHARED_INTENT(shared) & H5F_ACC_RDWR) {
+
+ HDassert(shared->vfd_swmr_config.writer);
+
+ SIMPLEQ_INIT(&shared->lower_defrees);
+ shared->vfd_swmr_writer = TRUE;
+ shared->tick_num = 1;
+
+ if ( H5PB_vfd_swmr__set_tick(shared) < 0 )
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, \
+ "Can't update page buffer current tick")
+
+ /* Create the metadata file */
+ if ( ((shared->vfd_swmr_md_fd =
+ HDopen(shared->vfd_swmr_config.md_file_path, O_CREAT|O_RDWR,
+ H5_POSIX_CREATE_MODE_RW))) < 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, \
+ "unable to create the metadata file")
+
+ md_size = (hsize_t)shared->vfd_swmr_config.md_pages_reserved *
+ shared->fs_page_size;
+
+ assert(shared->fs_page_size >= H5FD_MD_HEADER_SIZE);
+
+ /* Allocate an entire page from the shadow file for the header. */
+ if ((hdr_addr = H5MV_alloc(f, shared->fs_page_size)) == HADDR_UNDEF){
+ HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL,
+ "error allocating shadow-file header");
+ }
+ HDassert(H5F_addr_eq(hdr_addr, H5FD_MD_HEADER_OFF));
+
+ idx_addr = H5MV_alloc(f, md_size - shared->fs_page_size);
+ if (idx_addr == HADDR_UNDEF) {
+ HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL,
+ "error allocating shadow-file index");
+ }
+
+ HDassert(H5F_addr_eq(idx_addr, shared->fs_page_size));
+
+ shared->writer_index_offset = idx_addr;
+
+ /* Set the metadata file size to md_pages_reserved */
+ if ( -1 == HDftruncate(shared->vfd_swmr_md_fd, (HDoff_t)md_size) )
+ HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL,
+ "truncate fail for the metadata file");
+
+ /* Set eof for metadata file to md_pages_reserved */
+ shared->vfd_swmr_md_eoa = (haddr_t)md_size;
+
+ /* When opening an existing HDF5 file, create header and empty
+ * index in the metadata file
+ */
+ if (!file_create) {
+
+ if (H5F__vfd_swmr_construct_write_md_idx(shared, 0, NULL) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL,
+ "fail to create index in md");
+
+ if (H5F__vfd_swmr_construct_write_md_hdr(shared, 0) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL,
+ "fail to create header in md");
+ }
+
+ } else { /* VFD SWMR reader */
+
+ HDassert(!shared->vfd_swmr_config.writer);
+
+ shared->vfd_swmr_writer = FALSE;
+
+ HDassert(shared->mdf_idx == NULL);
+
+ /* allocate an index to save the initial index */
+ if (H5F__vfd_swmr_create_index(shared) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL,
+ "unable to allocate metadata file index");
+
+ /* Set tick_num to the current tick read from the metadata file */
+ shared->mdf_idx_entries_used = shared->mdf_idx_len;
+ if (H5FD_vfd_swmr_get_tick_and_idx(shared->lf, FALSE,
+ &shared->tick_num,
+ &(shared->mdf_idx_entries_used),
+ shared->mdf_idx) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTLOAD, FAIL,
+ "unable to load/decode metadata file");
+
+ assert(shared->tick_num != 0);
+ vfd_swmr_reader_did_increase_tick_to(shared->tick_num);
+
+ hlog_fast(tick, "%s first tick %" PRIu64,
+ __func__, shared->tick_num);
+
+#if 0 /* JRM */
+ HDfprintf(stderr,
+ "##### initialized index: tick/used/len = %lld/%d/%d #####\n",
+ shared->tick_num, shared->mdf_idx_entries_used,
+ shared->mdf_idx_len);
+#endif /* JRM */
+ }
+
+ /* Update end_of_tick */
+ if (H5F__vfd_swmr_update_end_of_tick_and_tick_num(shared, FALSE) < 0) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL,
+ "unable to update end of tick");
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5F_vfd_swmr_init() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F_vfd_swmr_close_or_flush
+ *
+ * Purpose: Used by the VFD SWMR writer when the HDF5 file is closed
+ * or flushed:
+ *
+ * 1) For file close:
+ * --write header and an empty index to the metadata file
+ * --increment tick_num
+ * --close the metadata file
+ * --unlink the metadata file
+ * --close the free-space manager for the metadata file
+ *
+ * 2) For file flush:
+ * --write header and an empty index to the metadata file
+ * --increment tick_num
+ * --start a new tick (??check with JM for sure)
+ * ??update end_of_tick
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi -- 11/??/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_vfd_swmr_close_or_flush(H5F_t *f, hbool_t closing)
+{
+ H5F_shared_t *shared = f->shared;
+ shadow_defree_t *curr;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ HDassert(shared->vfd_swmr_writer);
+ HDassert(shared->vfd_swmr_md_fd >= 0);
+
+ /* Write empty index to the md file */
+ if (H5F__vfd_swmr_construct_write_md_idx(shared, 0, NULL) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "fail to create index in md");
+
+ /* Write header to the md file */
+ if (H5F__vfd_swmr_construct_write_md_hdr(shared, 0) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "fail to create header in md");
+
+ if ( closing ) { /* For file close */
+
+ ++shared->tick_num;
+
+ /* Close the md file */
+ if(HDclose(shared->vfd_swmr_md_fd) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL,
+ "unable to close the metadata file");
+ shared->vfd_swmr_md_fd = -1;
+
+ /* Unlink the md file */
+ if ( HDunlink(shared->vfd_swmr_config.md_file_path) < 0 )
+ HGOTO_ERROR(H5E_FILE, H5E_CANTREMOVE, FAIL,
+ "unable to unlink the metadata file");
+
+ /* Close the free-space manager for the metadata file */
+ if ( H5MV_close(f) < 0 )
+ HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL,
+ "unable to close the free-space manager for the metadata file");
+
+ /* Free the delayed list */
+ while ((curr = TAILQ_FIRST(&shared->shadow_defrees)) != NULL) {
+ TAILQ_REMOVE(&shared->shadow_defrees, curr, link);
+ H5FL_FREE(shadow_defree_t, curr);
+ }
+ hlog_fast(shadow_defrees, "Emptied deferred shadow frees.");
+
+ assert(TAILQ_EMPTY(&shared->shadow_defrees));
+ } else { /* For file flush */
+ /* Update end_of_tick */
+ if (H5F__vfd_swmr_update_end_of_tick_and_tick_num(shared, TRUE) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTSET, FAIL,
+ "unable to update end of tick");
+ }
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+static int
+shadow_range_defer_free(H5F_shared_t *shared, uint64_t offset, uint32_t length)
+{
+ shadow_defree_t *shadow_defree;
+
+ if (NULL == (shadow_defree = H5FL_CALLOC(shadow_defree_t)))
+ return -1;
+
+ shadow_defree->offset = offset;
+ shadow_defree->length = length;
+ shadow_defree->tick_num = shared->tick_num;
+
+ if (TAILQ_EMPTY(&shared->shadow_defrees))
+ hlog_fast(shadow_defrees, "Adding first deferred shadow free.");
+
+ TAILQ_INSERT_HEAD(&shared->shadow_defrees, shadow_defree, link);
+ return 0;
+}
+
+int
+shadow_image_defer_free(H5F_shared_t *shared,
+ const H5FD_vfd_swmr_idx_entry_t *entry)
+{
+ return shadow_range_defer_free(shared,
+ entry->md_file_page_offset * shared->fs_page_size, entry->length);
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F_update_vfd_swmr_metadata_file()
+ *
+ * Purpose: Update the metadata file with the input index
+ *
+ * --Sort index
+ *
+ * --For each non-null entry_ptr in the index entries:
+ * --Insert previous image of the entry onto the delayed list
+ * --Allocate space for the entry in the metadata file
+ * --Compute checksum
+ * --Update index entry
+ * --Write the entry to the metadata file
+ * --Set entry_ptr to NULL
+ *
+ * --Construct on disk image of the index and write index to the
+ * metadata file
+ *
+ * --Construct on disk image of the header and write header to
+ * the metadata file
+ *
+ * --Release time out entries from the delayed list to the
+ * free-space manager
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: Vailin Choi 11/??/18
+ *
+ * Changes: None.
+ *
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_update_vfd_swmr_metadata_file(H5F_t *f, uint32_t num_entries,
+ H5FD_vfd_swmr_idx_entry_t *index)
+{
+ H5F_shared_t *shared = f->shared;
+ shadow_defree_t *prev;
+ shadow_defree_t *shadow_defree;
+ haddr_t md_addr; /* Address in the metadata file */
+ uint32_t i; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
+ bool queue_was_nonempty;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sort index entries by increasing offset in the HDF5 file */
+ if (num_entries > 0) {
+ HDqsort(index, num_entries, sizeof(*index), H5F__idx_entry_cmp);
+ /* Assert that no HDF5 page offsets are duplicated. */
+ for (i = 1; i < num_entries; i++)
+ assert(index[i - 1].hdf5_page_offset < index[i].hdf5_page_offset);
+ }
+
+ /* For each non-null entry_ptr in the index:
+ *
+ * --Insert previous image of the entry (if exists) to the
+ * beginning of the delayed list
+ *
+ * --Allocate space for the entry in the metadata file
+ *
+ * --Compute checksum, update the index entry, write entry to
+ * the metadata file
+ *
+ * --Set entry_ptr to NULL
+ */
+ for (i = 0; i < num_entries; i++) {
+
+ if (index[i].entry_ptr == NULL)
+ continue;
+
+ /* Prepend previous image of the entry to the delayed list */
+ if ( index[i].md_file_page_offset ) {
+ if (shadow_image_defer_free(shared, &index[i]) == -1) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL, \
+ "unable to allocate the delayed entry")
+ }
+ }
+
+ /* Allocate space for the entry in the metadata file */
+ if((md_addr = H5MV_alloc(f, index[i].length)) == HADDR_UNDEF)
+ HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, \
+ "error in allocating space from the metadata file")
+
+ hlog_fast(noisy_shadow_defrees,
+ "shadow index %" PRIu32 " page offset %" PRIu64 " -> %" PRIuHADDR,
+ i, index[i].md_file_page_offset * shared->fs_page_size, md_addr);
+
+ HDassert(md_addr % shared->fs_page_size == 0);
+
+ /* Compute checksum and update the index entry */
+ index[i].md_file_page_offset = md_addr / shared->fs_page_size;
+ index[i].chksum = H5_checksum_metadata(index[i].entry_ptr,
+ index[i].length, 0);
+
+#if 0 /* JRM */
+ HDfprintf(stderr,
+ "writing index[%d] fo/mdfo/l/chksum/fc/lc = %lld/%lld/%ld/%lx/%lx/%lx\n",
+ i,
+ index[i].hdf5_page_offset,
+ index[i].md_file_page_offset,
+ index[i].length,
+ index[i].chksum,
+ (((char*)(index[i].entry_ptr))[0]),
+ (((char*)(index[i].entry_ptr))[4095]));
+
+ HDassert(md_addr == index[i].md_file_page_offset *
+ shared->fs_page_size);
+ HDassert(shared->fs_page_size == 4096);
+#endif /* JRM */
+
+ /* Seek and write the entry to the metadata file */
+ if ( HDlseek(shared->vfd_swmr_md_fd, (HDoff_t)md_addr,
+ SEEK_SET) < 0)
+
+ HGOTO_ERROR(H5E_FILE, H5E_SEEKERROR, FAIL, \
+ "unable to seek in the metadata file")
+
+ if ( HDwrite(shared->vfd_swmr_md_fd, index[i].entry_ptr,
+ index[i].length) != (ssize_t)index[i].length )
+
+ HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, \
+ "error in writing the page/multi-page entry to metadata file")
+
+ index[i].entry_ptr = NULL;
+ }
+
+ /* Construct and write index to the metadata file */
+ if (H5F__vfd_swmr_construct_write_md_idx(shared, num_entries, index) < 0)
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, \
+ "fail to construct & write index to md")
+
+ /* Construct and write header to the md file */
+ if (H5F__vfd_swmr_construct_write_md_hdr(shared, num_entries) < 0)
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, \
+ "fail to construct & write header to md")
+
+ queue_was_nonempty = !TAILQ_EMPTY(&shared->shadow_defrees);
+
+ /*
+ * Release time out entries from the delayed list by scanning the
+ * list from the bottom up:
+ *
+ * --release to the metadata file free space manager all index
+ * entries that have resided on the list for more than
+ * max_lag ticks
+ *
+ * --remove the associated entries from the list
+ */
+
+ if (shared->tick_num <= shared->vfd_swmr_config.max_lag)
+ goto done; // It is too early for any reclamations to be due.
+
+ TAILQ_FOREACH_REVERSE_SAFE(shadow_defree, &shared->shadow_defrees,
+ shadow_defree_queue, link, prev) {
+
+ if (shadow_defree->tick_num + shared->vfd_swmr_config.max_lag >
+ shared->tick_num) {
+ break; // No more entries are due for reclamation.
+ }
+
+ if (H5MV_free(f, shadow_defree->offset, shadow_defree->length) < 0) {
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL,
+ "unable to flush clean entry");
+ }
+
+ hlog_fast(noisy_shadow_defrees,
+ "released %" PRIu32 " bytes at %" PRIu64,
+ shadow_defree->length, shadow_defree->offset);
+
+ TAILQ_REMOVE(&shared->shadow_defrees, shadow_defree, link);
+
+ H5FL_FREE(shadow_defree_t, shadow_defree);
+ }
+
+ if (queue_was_nonempty && TAILQ_EMPTY(&shared->shadow_defrees))
+ hlog_fast(shadow_defrees, "Removed last deferred shadow free.");
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* end H5F_update_vfd_swmr_metadata_file() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F_vfd_swmr_writer__delay_write
+ *
+ * Purpose: Given the base address of a page of metadata, or of a multi-
+ * page metadata entry, determine whether the write must be
+ * delayed.
+ *
+ * At the conceptual level, the VFD SWMR writer must delay the
+ * write of any metadata page or multi-page metadata that
+ * overwrites an existing metadata page or multi-page metadata
+ * entry until it has appeared in the metadata file index for
+ * at least max_lag ticks. Since the VFD SWMR reader goes
+ * to the HDF5 file for any piece of metadata not listed in
+ * the metadata file index, failure to delay such writes can
+ * result in message from the future bugs.
+ *
+ * The easy case is pages or multi-page metadata entries
+ * have just been allocated. Obviously, these can be written
+ * immediately. This case is tracked and tested by the page
+ * buffer proper.
+ *
+ * This routine looks up the supplied page in the metadata file
+ * index.
+ *
+ * If the entry doesn't exist, the function sets
+ * *untilp to the current tick plus max_lag.
+ *
+ * If the entry exists, the function sets *untilp
+ * equal to the entries delayed flush field if it is greater than
+ * or equal to the current tick, or zero otherwise.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: John Mainzer 11/4/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_vfd_swmr_writer__delay_write(H5F_shared_t *shared, uint64_t page,
+ uint64_t *untilp)
+{
+ uint64_t until;
+ H5FD_vfd_swmr_idx_entry_t *ie_ptr;
+ H5FD_vfd_swmr_idx_entry_t *idx;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ HDassert(shared);
+ HDassert(shared->vfd_swmr);
+ HDassert(shared->vfd_swmr_writer);
+
+ idx = shared->mdf_idx;
+
+ HDassert(idx != NULL || shared->tick_num <= 1);
+
+ /* do a binary search on the metadata file index to see if
+ * it already contains an entry for `page`.
+ */
+
+ if (idx == NULL) {
+ ie_ptr = NULL;
+ } else {
+ ie_ptr = vfd_swmr_pageno_to_mdf_idx_entry(idx,
+ shared->mdf_idx_entries_used, page, false);
+ }
+
+ if (ie_ptr == NULL)
+ until = shared->tick_num + shared->vfd_swmr_config.max_lag;
+ else if (ie_ptr->delayed_flush >= shared->tick_num)
+ until = ie_ptr->delayed_flush;
+ else
+ until = 0;
+
+ if (until != 0 &&
+ (until < shared->tick_num ||
+ shared->tick_num + shared->vfd_swmr_config.max_lag < until))
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "VFD SWMR write delay out of range")
+
+ *untilp = until;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5F_vfd_swmr_writer__delay_write() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F_vfd_swmr_writer__prep_for_flush_or_close
+ *
+ * Purpose: In the context of the VFD SWMR writer, two issues must be
+ * addressed before the page buffer can be flushed -- as is
+ * necessary on both HDF5 file flush or close:
+ *
+ * 1) We must force an end of tick so as to clean the tick list
+ * in the page buffer.
+ *
+ * 2) If the page buffer delayed write list is not empty, we
+ * must repeatedly wait a tick and then run the writer end
+ * of tick function until the delayed write list drains.
+ *
+ * This function manages these details.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: John Mainzer 11/27/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_vfd_swmr_writer__prep_for_flush_or_close(H5F_t *f)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+ H5F_shared_t *shared = f->shared;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ HDassert(shared->vfd_swmr);
+ HDassert(shared->vfd_swmr_writer);
+ HDassert(shared->pb_ptr);
+
+ /* since we are about to flush the page buffer, force and end of
+ * tick so as to avoid attempts to flush entries on the page buffer
+ * tick list that were modified during the current tick.
+ */
+ if ( H5F_vfd_swmr_writer_end_of_tick(f, true) < 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, \
+ "H5F_vfd_swmr_writer_end_of_tick() failed.")
+
+ while(shared->pb_ptr->dwl_len > 0) {
+
+ if(H5F__vfd_swmr_writer__wait_a_tick(f) < 0)
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL, "wait a tick failed.")
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5F_vfd_swmr_writer__prep_for_flush_or_close() */
+
+static int
+clean_shadow_index(H5F_t *f, uint32_t nentries,
+ H5FD_vfd_swmr_idx_entry_t *idx, uint32_t *ndeletedp)
+{
+ H5F_shared_t *shared = f->shared;
+ uint32_t i, j, ndeleted, max_lag = shared->vfd_swmr_config.max_lag;
+ uint64_t tick_num = shared->tick_num;
+ H5FD_vfd_swmr_idx_entry_t *ie;
+
+ for (i = j = ndeleted = 0; i < nentries; i++) {
+ ie = &idx[i];
+
+ if (ie->clean) {
+ hlog_fast(shadow_index_reclaim,
+ "Visiting clean shadow index slot %" PRIu32
+ " lower page %" PRIu64 " last flush %" PRIu64 " ticks ago",
+ i, ie->hdf5_page_offset, tick_num - ie->tick_of_last_flush);
+ }
+
+ if (ie->clean && ie->tick_of_last_flush + max_lag < tick_num) {
+
+ assert(!ie->garbage);
+ assert(ie->entry_ptr == NULL);
+
+ hlog_fast(shadow_index_reclaim,
+ "Reclaiming shadow index slot %" PRIu32
+ " lower page %" PRIu64, i, ie->hdf5_page_offset);
+
+ if (ie->md_file_page_offset != 0) {
+ if (shadow_image_defer_free(shared, ie) == -1)
+ return -1;
+ ie->md_file_page_offset = 0;
+ }
+ ndeleted++;
+ continue;
+ }
+ if (j != i)
+ idx[j] = *ie;
+ j++;
+ }
+ *ndeletedp = ndeleted;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F_vfd_swmr_writer_end_of_tick
+ *
+ * Purpose: Main routine for managing the end of tick for the VFD
+ * SWMR writer.
+ *
+ * This function performs all end of tick operations for the
+ * writer -- specifically:
+ *
+ * 1) If requested, flush all raw data to the HDF5 file.
+ *
+ * (Not for first cut.)
+ *
+ * 2) Flush the metadata cache to the page buffer.
+ *
+ * Note that we must run a tick after the destruction
+ * of the metadata cache, since this operation will usually
+ * dirty the first page in the HDF5 file. However, the
+ * metadata cache will no longer exist at this point.
+ *
+ * Thus, we must check for the existance of the metadata
+ * cache, and only attempt to flush it if it exists.
+ *
+ * 3) If this is the first tick (i.e. tick == 1), create the
+ * in memory version of the metadata file index.
+ *
+ * 4) Scan the page buffer tick list, and use it to update
+ * the metadata file index, adding or modifying entries as
+ * appropriate.
+ *
+ * 5) Scan the metadata file index for entries that can be
+ * removed -- specifically entries that have been written
+ * to the HDF5 file more than max_lag ticks ago, and haven't
+ * been modified since.
+ *
+ * (This is an optimization -- address it later)
+ *
+ * 6) Update the metadata file. Must do this before we
+ * release the tick list, as otherwise the page buffer
+ * entry images may not be available.
+ *
+ * 7) Release the page buffer tick list.
+ *
+ * 8) Release any delayed writes whose delay has expired.
+ *
+ * 9) Increment the tick, and update the end of tick.
+ *
+ * In passing, generate log entries as appropriate.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: John Mainzer 11/4/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_vfd_swmr_writer_end_of_tick(H5F_t *f, bool wait_for_reader)
+{
+ H5F_shared_t *shared = f->shared;
+ uint32_t idx_entries_added = 0;
+ uint32_t idx_entries_modified = 0;
+ uint32_t idx_entries_removed = 0;
+ uint32_t idx_ent_not_in_tl = 0;
+ uint32_t idx_ent_not_in_tl_flushed = 0;
+ herr_t ret_value = SUCCEED; /* Return value */
+ bool incr_tick = false;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ HDassert(shared);
+ HDassert(shared->pb_ptr);
+ HDassert(shared->vfd_swmr_writer);
+
+ if (!vfd_swmr_writer_may_increase_tick_to(shared->tick_num + 1,
+ wait_for_reader))
+ goto update_eot;
+
+ incr_tick = true;
+
+ /* 1) If requested, flush all raw data to the HDF5 file.
+ *
+ * (Not for first cut.)
+ */
+ HDassert(!shared->vfd_swmr_config.flush_raw_data);
+
+#if 1
+ /* Test to see if b-tree corruption seen in VFD SWMR tests
+ * is caused by client hiding data from the metadata cache. Do
+ * this by calling H5D_flush_all(), which flushes any cached
+ * dataset storage. Eventually, we will do this regardless
+ * when the above flush_raw_data flag is set.
+ */
+
+ if ( H5D_flush_all(f) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "unable to flush dataset cache")
+
+
+ if(H5MF_free_aggrs(f) < 0)
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTRELEASE, FAIL, "can't release file space")
+
+
+ if ( shared->cache ) {
+
+ if ( H5AC_flush(f) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "Can't flush metadata cache to the page buffer")
+ }
+
+
+
+ if ( H5FD_truncate(shared->lf, FALSE) < 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "low level truncate failed")
+#endif
+
+ /* 2) If it exists, flush the metadata cache to the page buffer. */
+ if ( shared->cache ) {
+
+ if ( H5AC_flush(f) < 0 )
+
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, \
+ "Can't flush metadata cache to the page buffer")
+ }
+
+
+ /* 3) If this is the first tick (i.e. tick == 1), create the
+ * in memory version of the metadata file index.
+ */
+ if ( ( shared->tick_num == 1 ) &&
+ ( H5F__vfd_swmr_create_index(shared) < 0 ) )
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL, \
+ "unable to allocate metadata file index")
+
+
+ /* 4) Scan the page buffer tick list, and use it to update
+ * the metadata file index, adding or modifying entries as
+ * appropriate.
+ */
+ if ( H5PB_vfd_swmr__update_index(f, &idx_entries_added,
+ &idx_entries_modified,
+ &idx_ent_not_in_tl,
+ &idx_ent_not_in_tl_flushed) < 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "can't update MD file index")
+
+
+ /* 5) Scan the metadata file index for entries that can be
+ * removed -- specifically entries that have been written
+ * to the HDF5 file more than max_lag ticks ago, and haven't
+ * been modified since.
+ */
+ if (clean_shadow_index(f,
+ shared->mdf_idx_entries_used + idx_entries_added,
+ shared->mdf_idx, &idx_entries_removed) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "can't clean shadow file index")
+
+ /* 6) Update the metadata file. Must do this before we
+ * release the tick list, as otherwise the page buffer
+ * entry images may not be available.
+ *
+ * Note that this operation will restore the index to
+ * sorted order.
+ */
+ if (H5F_update_vfd_swmr_metadata_file(f,
+ shared->mdf_idx_entries_used + idx_entries_added -
+ idx_entries_removed,
+ shared->mdf_idx) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "can't update MD file")
+
+ /* at this point the metadata file index should be sorted -- update
+ * shared->mdf_idx_entries_used.
+ */
+ shared->mdf_idx_entries_used += idx_entries_added;
+ shared->mdf_idx_entries_used -= idx_entries_removed;
+
+ HDassert(shared->mdf_idx_entries_used <= shared->mdf_idx_len);
+
+#if 0 /* JRM */
+ H5F__vfd_swmr_writer__dump_index(f);
+#endif /* JRM */
+
+ /* 7) Release the page buffer tick list. */
+ if ( H5PB_vfd_swmr__release_tick_list(shared) < 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "can't release tick list")
+
+
+ /* 8) Release any delayed writes whose delay has expired */
+ if ( H5PB_vfd_swmr__release_delayed_writes(shared) < 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "can't release delayed writes")
+
+
+update_eot:
+
+ /* 9) Increment the tick, and update the end of tick. */
+
+ /* Update end_of_tick */
+ if (H5F__vfd_swmr_update_end_of_tick_and_tick_num(shared, incr_tick) < 0)
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, \
+ "unable to update end of tick")
+
+ /* Remove the entry from the EOT queue */
+ if(H5F_vfd_swmr_remove_entry_eot(f) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to remove entry from EOT queue")
+
+ /* Re-insert the entry that corresponds to f onto the EOT queue */
+ if(H5F_vfd_swmr_insert_entry_eot(f) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "unable to insert entry into the EOT queue")
+
+ hlog_fast(eot, "%s leave tick %" PRIu64 " idx len %" PRIu32,
+ __func__, shared->tick_num, shared->mdf_idx_entries_used);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F_vfd_swmr_writer__dump_index
+ *
+ * Purpose: Dump a summary of the metadata file index.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: John Mainzer 12/14/19
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_vfd_swmr_writer__dump_index(H5F_shared_t *shared)
+{
+ unsigned int i;
+ uint32_t mdf_idx_len;
+ uint32_t mdf_idx_entries_used;
+ H5FD_vfd_swmr_idx_entry_t * index = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ HDassert(shared);
+ HDassert(shared->vfd_swmr);
+ HDassert(shared->mdf_idx);
+
+
+ index = shared->mdf_idx;
+ mdf_idx_len = shared->mdf_idx_len;
+ mdf_idx_entries_used = shared->mdf_idx_entries_used;
+
+ HDfprintf(stderr, "\n\nDumping Index:\n\n");
+ HDfprintf(stderr,
+ "index len / entries used = %" PRIu32 " / %" PRIu32 "\n\n",
+ mdf_idx_len, mdf_idx_entries_used);
+
+ for ( i = 0; i < mdf_idx_entries_used; i++ ) {
+
+ HDfprintf(stderr, "%u: %" PRIu64 " %" PRIu64 " %" PRIu32 "\n",
+ i, index[i].hdf5_page_offset, index[i].md_file_page_offset,
+ index[i].length);
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* end H5F_vfd_swmr_writer__dump_index() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5F_vfd_swmr_reader_end_of_tick
+ *
+ * Purpose: Main routine for VFD SWMR reader end of tick operations.
+ * The following operations must be performed:
+ *
+ * 1) Direct the VFD SWMR reader VFD to load the current header
+ * from the metadata file, and report the current tick.
+ *
+ * If the tick reported has not increased since the last
+ * call, do nothing and exit.
+ *
+ * 2) If the tick has increased, obtain a copy of the new
+ * index from the VFD SWMR reader VFD, and compare it with
+ * the old index to identify all pages that have been updated
+ * in the previous tick.
+ *
+ * If any such pages or multi-page metadata entries are found:
+ *
+ * a) direct the page buffer to evict any such superceeded
+ * pages, and
+ *
+ * b) direct the metadata cache to either evict or refresh
+ * any entries residing in the superceeded pages.
+ *
+ * Note that this operation MUST be performed in this order,
+ * as the metadata cache will refer to the page buffer
+ * when refreshing entries.
+ *
+ * 9) Increment the tick, and update the end of tick.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: John Mainzer 12/29/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_vfd_swmr_reader_end_of_tick(H5F_t *f, bool entering_api)
+{
+ uint64_t tmp_tick_num = 0;
+ H5FD_vfd_swmr_idx_entry_t * tmp_mdf_idx;
+ uint32_t entries_added = 0;
+ uint32_t entries_removed = 0;
+ uint32_t entries_moved = 0;
+ uint32_t tmp_mdf_idx_len;
+ uint32_t tmp_mdf_idx_entries_used;
+ uint32_t mdf_idx_entries_used;
+ H5F_shared_t *shared = f->shared;
+ struct {
+ uint64_t pgno;
+ uint32_t length;
+ } *change = NULL;
+ herr_t ret_value = SUCCEED;
+ uint32_t i, j, nchanges;
+ H5FD_t *file = shared->lf;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ HDassert(shared->pb_ptr);
+ HDassert(shared->vfd_swmr);
+ HDassert(!shared->vfd_swmr_writer);
+ HDassert(file);
+
+ hlog_fast(eot, "%s enter file %p index len %" PRIu32 " used %" PRIu32,
+ __func__, (void *)file,
+ shared->mdf_idx_len, shared->mdf_idx_entries_used);
+
+ /* 1) Direct the VFD SWMR reader VFD to load the current header
+ * from the metadata file, and report the current tick.
+ *
+ * If the tick reported has not increased since the last
+ * call, do nothing and exit.
+ */
+ if ( H5FD_vfd_swmr_get_tick_and_idx(file, TRUE, &tmp_tick_num,
+ NULL, NULL) < 0 )
+
+ HGOTO_ERROR(H5E_ARGS, H5E_CANTGET, FAIL, \
+ "error in retrieving tick_num from driver")
+
+ hlog_fast(tick,
+ "%s last tick %" PRIu64 " new tick %" PRIu64,
+ __func__, shared->tick_num, tmp_tick_num);
+
+ /* This is ok if we're entering the API, but it should
+ * not happen if we're exiting the API.
+ */
+ assert(entering_api || tmp_tick_num <
+ shared->tick_num + shared->vfd_swmr_config.max_lag);
+
+ if (!entering_api) {
+ H5FD_vfd_swmr_record_elapsed_ticks(shared->lf,
+ tmp_tick_num - shared->tick_num);
+ }
+
+ if ( tmp_tick_num != shared->tick_num ) {
+ const H5FD_vfd_swmr_idx_entry_t *new_mdf_idx;
+ const H5FD_vfd_swmr_idx_entry_t *old_mdf_idx;
+ uint32_t new_mdf_idx_entries_used;
+ uint32_t old_mdf_idx_entries_used;
+
+ /* swap the old and new metadata file indexes */
+
+ tmp_mdf_idx = shared->old_mdf_idx;
+ tmp_mdf_idx_len = shared->old_mdf_idx_len;
+ tmp_mdf_idx_entries_used = shared->old_mdf_idx_entries_used;
+
+ shared->old_mdf_idx = shared->mdf_idx;
+ shared->old_mdf_idx_len = shared->mdf_idx_len;
+ shared->old_mdf_idx_entries_used = shared->mdf_idx_entries_used;
+
+ shared->mdf_idx = tmp_mdf_idx;
+ shared->mdf_idx_len = tmp_mdf_idx_len;
+ shared->mdf_idx_entries_used = tmp_mdf_idx_entries_used;
+
+ /* if shared->mdf_idx is NULL, allocate an index */
+ if (shared->mdf_idx == NULL &&
+ H5F__vfd_swmr_create_index(shared) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL,
+ "unable to allocate metadata file index");
+
+ mdf_idx_entries_used = shared->mdf_idx_len;
+
+#if 0 /* JRM */
+ HDfprintf(stderr, "--- reader EOT mdf_idx_entries_used = %d ---\n",
+ mdf_idx_entries_used);
+#endif /* JRM */
+
+ if (H5FD_vfd_swmr_get_tick_and_idx(file, FALSE, NULL,
+ &mdf_idx_entries_used,
+ shared->mdf_idx) < 0)
+ HGOTO_ERROR(H5E_ARGS, H5E_CANTGET, FAIL,
+ "error in retrieving tick_num from driver");
+
+ HDassert(mdf_idx_entries_used <= shared->mdf_idx_len);
+
+ shared->mdf_idx_entries_used = mdf_idx_entries_used;
+
+#if 0 /* JRM */
+ HDfprintf(stderr,
+ "--- reader EOT index used / len = %" PRIu32 "/%" PRIu32 " ---\n",
+ shared->mdf_idx_entries_used, shared->mdf_idx_len);
+#endif /* JRM */
+
+ new_mdf_idx = shared->mdf_idx;
+ old_mdf_idx = shared->old_mdf_idx;
+ new_mdf_idx_entries_used = shared->mdf_idx_entries_used;
+ old_mdf_idx_entries_used = shared->old_mdf_idx_entries_used;
+
+ change = malloc(sizeof(change[0]) *
+ (old_mdf_idx_entries_used + new_mdf_idx_entries_used));
+
+ if (change == NULL) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL,
+ "unable to allocate removed pages list");
+ }
+
+ /* If an old metadata file index exists, compare it with the
+ * new index and evict any modified, new, or deleted pages
+ * and any associated metadata cache entries.
+ *
+ * Note that we must evict in two passes---page buffer first,
+ * and then metadata cache. This is necessary as the metadata
+ * cache may attempt to refresh entries rather than evict them,
+ * in which case it may access an entry in the page buffer.
+ */
+
+ for (i = j = nchanges = 0;
+ i < old_mdf_idx_entries_used &&
+ j < new_mdf_idx_entries_used; ) {
+ const H5FD_vfd_swmr_idx_entry_t *oent = &old_mdf_idx[i],
+ *nent = &new_mdf_idx[j];
+
+ /* Verify that the old and new indices are sorted as expected. */
+ HDassert(i == 0 ||
+ oent[-1].hdf5_page_offset < oent[0].hdf5_page_offset);
+
+ HDassert(j == 0 ||
+ nent[-1].hdf5_page_offset < nent[0].hdf5_page_offset);
+
+ if (oent->hdf5_page_offset == nent->hdf5_page_offset) {
+
+ if (oent->md_file_page_offset != nent->md_file_page_offset) {
+
+ /* It's ok if the length changes, I think, but I need
+ * to think about how to perform MDC invalidation in the
+ * case where the new entry is *longer*, because the
+ * extension could overlap with a second entry.
+ */
+ assert(oent->length == nent->length);
+
+ hlog_fast(shadow_index_update,
+ "shadow page for slot %" PRIu32 " lower page %" PRIu64
+ " moved, %" PRIu64 " -> %" PRIu64, i,
+ oent->hdf5_page_offset,
+ oent->md_file_page_offset,
+ nent->md_file_page_offset);
+
+ /* the page has been altered -- evict it and
+ * any contained metadata cache entries.
+ */
+ change[nchanges].pgno = oent->hdf5_page_offset;
+ change[nchanges].length = oent->length;
+ nchanges++;
+ entries_moved++;
+ }
+ i++;
+ j++;
+
+ } else if (oent->hdf5_page_offset < nent->hdf5_page_offset) {
+ /* the page has been removed from the new version
+ * of the index. Evict it and any contained metadata
+ * cache entries.
+ *
+ * If we are careful about removing entries from the
+ * the index so as to ensure that they haven't changed
+ * for several ticks, we can probably omit this. However,
+ * lets not worry about this for the first cut.
+ */
+ hlog_fast(shadow_index_update,
+ "writer removed shadow index slot %" PRIu32
+ " for page %" PRIu64, i, oent->hdf5_page_offset);
+
+ change[nchanges].pgno = oent->hdf5_page_offset;
+ change[nchanges].length = oent->length;
+ nchanges++;
+ entries_removed++;
+ i++;
+
+ } else { /* oent->hdf5_page_offset >
+ * nent->hdf5_page_offset
+ */
+
+ hlog_fast(shadow_index_update,
+ "writer added shadow index slot %" PRIu32
+ " for page %" PRIu64, j, nent->hdf5_page_offset);
+
+ /* The page has been added to the index. */
+ change[nchanges].pgno = nent->hdf5_page_offset;
+ change[nchanges].length = nent->length;
+ nchanges++;
+ entries_added++;
+ j++;
+ }
+ }
+
+ for (; j < new_mdf_idx_entries_used; j++) {
+ const H5FD_vfd_swmr_idx_entry_t *nent = &new_mdf_idx[j];
+ hlog_fast(shadow_index_update,
+ "writer added shadow index slot %" PRIu32
+ " for page %" PRIu64, j, nent->hdf5_page_offset);
+ change[nchanges].pgno = nent->hdf5_page_offset;
+ change[nchanges].length = nent->length;
+ nchanges++;
+ entries_added++;
+ }
+
+ /* cleanup any left overs in the old index */
+ for (; i < old_mdf_idx_entries_used; i++) {
+ const H5FD_vfd_swmr_idx_entry_t *oent = &old_mdf_idx[i];
+
+ /* the page has been removed from the new version of the
+ * index. Evict it from the page buffer and also evict any
+ * contained metadata cache entries
+ */
+
+ hlog_fast(shadow_index_update,
+ "writer removed shadow index slot %" PRIu32
+ " for page %" PRIu64, i, oent->hdf5_page_offset);
+
+ change[nchanges].pgno = oent->hdf5_page_offset;
+ change[nchanges].length = oent->length;
+ nchanges++;
+ entries_removed++;
+ }
+ for (i = 0; i < nchanges; i++) {
+ haddr_t page_addr =
+ (haddr_t)(change[i].pgno * shared->pb_ptr->page_size);
+ if (H5PB_remove_entry(shared, page_addr) < 0) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL,
+ "remove page buffer entry failed");
+ }
+ }
+ for (i = 0; i < nchanges; i++) {
+ hlog_fast(mdc_invalidation,
+ "invalidating MDC entries at page %" PRIu64
+ " length %" PRIu32 " tick %" PRIu64,
+ change[i].pgno, change[i].length, tmp_tick_num);
+ if (H5C_evict_or_refresh_all_entries_in_page(f,
+ change[i].pgno, change[i].length,
+ tmp_tick_num) < 0) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL,
+ "evict or refresh stale MDC entries failed");
+ }
+ }
+
+#if 0 /* JRM */
+ HDfprintf(stderr, "--- reader EOT pre new tick index "
+ "used/len = %" PRIu32 "/ %" PRIu32 " ---\n",
+ shared->mdf_idx_entries_used, shared->mdf_idx_len);
+#endif /* JRM */
+ /* At this point, we should have evicted or refreshed all stale
+ * page buffer and metadata cache entries.
+ *
+ * Start the next tick.
+ */
+ shared->tick_num = tmp_tick_num;
+
+ vfd_swmr_reader_did_increase_tick_to(tmp_tick_num);
+
+ /* Update end_of_tick */
+ if (H5F__vfd_swmr_update_end_of_tick_and_tick_num(shared,
+ FALSE) < 0) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL,
+ "unable to update end of tick");
+ }
+ }
+
+ /* Remove the entry from the EOT queue */
+ if(H5F_vfd_swmr_remove_entry_eot(f) < 0) {
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL,
+ "unable to remove entry from EOT queue")
+ }
+
+ /* Re-insert the entry that corresponds to f onto the EOT queue */
+ if(H5F_vfd_swmr_insert_entry_eot(f) < 0) {
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL,
+ "unable to insert entry into the EOT queue")
+ }
+
+done:
+
+ hlog_fast(eot, "%s exit tick %" PRIu64
+ " len %" PRIu32 " -> %" PRIu32
+ " used %" PRIu32 " -> %" PRIu32
+ " added %" PRIu32 " removed %" PRIu32 " moved %" PRIu32 " %s",
+ __func__, shared->tick_num,
+ shared->old_mdf_idx_len, shared->mdf_idx_len,
+ shared->old_mdf_idx_entries_used, shared->mdf_idx_entries_used,
+ entries_added, entries_removed, entries_moved,
+ (ret_value == SUCCEED) ? "success" : "failure");
+
+ if (change != NULL)
+ free(change);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* end H5F_vfd_swmr_reader_end_of_tick() */
+
+static void
+insert_eot_entry(eot_queue_entry_t *entry_ptr)
+{
+ eot_queue_entry_t *prec_ptr; /* The predecessor entry on the EOT end of tick queue */
+
+ /* Find the insertion point for the entry on the EOT queue */
+ TAILQ_FOREACH_REVERSE(prec_ptr, &eot_queue_g, eot_queue, link) {
+ if (timespeccmp(&prec_ptr->end_of_tick, &entry_ptr->end_of_tick, <=))
+ break;
+ }
+
+ hlog_fast(eotq, "%s: entry %p after %p file %p "
+ "tick %" PRIu64 " ending %jd.%09ld", __func__,
+ (void *)entry_ptr, (void *)prec_ptr, (void *)entry_ptr->vfd_swmr_file,
+ entry_ptr->tick_num, (intmax_t)entry_ptr->end_of_tick.tv_sec,
+ entry_ptr->end_of_tick.tv_nsec);
+
+ /* Insert the entry onto the EOT queue */
+ if (prec_ptr != NULL)
+ TAILQ_INSERT_AFTER(&eot_queue_g, prec_ptr, entry_ptr, link);
+ else
+ TAILQ_INSERT_HEAD(&eot_queue_g, entry_ptr, link);
+}
+
+
+/* Update an entry on the EOT queue and move it to its proper place.
+ */
+void
+H5F_vfd_swmr_update_entry_eot(eot_queue_entry_t *entry)
+{
+ H5F_t *f = entry->vfd_swmr_file;
+ H5F_shared_t *shared = f->shared;
+
+ /* Free the entry on the EOT queue that corresponds to f */
+
+ TAILQ_REMOVE(&eot_queue_g, entry, link);
+
+ hlog_fast(eotq, "%s: updating entry %p file %p "
+ "tick %" PRIu64 " ending %jd.%09ld", __func__,
+ (void *)entry, (void *)entry->vfd_swmr_file,
+ entry->tick_num, (intmax_t)entry->end_of_tick.tv_sec,
+ entry->end_of_tick.tv_nsec);
+
+ assert(entry->vfd_swmr_writer == shared->vfd_swmr_writer);
+ entry->tick_num = shared->tick_num;
+ entry->end_of_tick = shared->end_of_tick;
+
+ hlog_fast(eotq, "%s: ... to tick %" PRIu64 " ending %jd.%09ld", __func__,
+ entry->tick_num, (intmax_t)entry->end_of_tick.tv_sec,
+ entry->end_of_tick.tv_nsec);
+
+ insert_eot_entry(entry);
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F__vfd_swmr_remove_entry_eot
+ *
+ * Purpose: Remove an entry from the EOT queue
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi -- 11/18/2019
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_vfd_swmr_remove_entry_eot(H5F_t *f)
+{
+ eot_queue_entry_t *curr;
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Free the entry on the EOT queue that corresponds to f */
+
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if (curr->vfd_swmr_file == f)
+ break;
+ }
+
+ if (curr != NULL) {
+ hlog_fast(eotq, "%s: entry %p file %p "
+ "tick %" PRIu64 " ending %jd.%09ld", __func__,
+ (void *)curr, (void *)curr->vfd_swmr_file, curr->tick_num,
+ (intmax_t)curr->end_of_tick.tv_sec,
+ curr->end_of_tick.tv_nsec);
+ TAILQ_REMOVE(&eot_queue_g, curr, link);
+ curr = H5FL_FREE(eot_queue_entry_t, curr);
+ }
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* H5F_vfd_swmr_remove_entry_eot() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F_vfd_swmr_insert_entry_eot
+ *
+ * Purpose: Insert an entry onto the EOT queue
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi -- 11/18/2019
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_vfd_swmr_insert_entry_eot(H5F_t *f)
+{
+ H5F_shared_t *shared = f->shared;
+ eot_queue_entry_t *entry_ptr; /* An entry on the EOT end of tick queue */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Allocate an entry to be inserted onto the EOT queue */
+ if (NULL == (entry_ptr = H5FL_CALLOC(eot_queue_entry_t)))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTALLOC, FAIL, "unable to allocate the end of tick queue entry")
+
+ /* Initialize the entry */
+ entry_ptr->vfd_swmr_writer = shared->vfd_swmr_writer;
+ entry_ptr->tick_num = shared->tick_num;
+ entry_ptr->end_of_tick = shared->end_of_tick;
+ entry_ptr->vfd_swmr_file = f;
+
+ insert_eot_entry(entry_ptr);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5F_vfd_swmr_insert_entry_eot() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F_dump_eot_queue()
+ *
+ * Purpose: Dump the contents of the EOT queue
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi -- 11/18/2019
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_dump_eot_queue(void)
+{
+ int i;
+ eot_queue_entry_t *curr;
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ for (curr = TAILQ_FIRST(&eot_queue_g), i = 0;
+ curr != NULL;
+ curr = TAILQ_NEXT(curr, link), i++) {
+ HDfprintf(stderr, "%d: %s tick_num %" PRIu64
+ ", end_of_tick %jd.%09ld, vfd_swmr_file %p\n",
+ i, curr->vfd_swmr_writer ? "writer" : "not writer",
+ curr->tick_num,
+ curr->end_of_tick.tv_sec, curr->end_of_tick.tv_nsec,
+ curr->vfd_swmr_file);
+ }
+
+ if(i == 0)
+ HDfprintf(stderr, "EOT head is null\n");
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+
+} /* H5F_dump_eot_queue() */
+
+/*
+ * Beginning of static functions
+ */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F__vfd_swmr_update_end_of_tick_and_tick_num
+ *
+ * Purpose: Update end_of_tick (shared->end_of_tick)
+ * Update tick_num (shared->tick_num)
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi -- 11/??/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__vfd_swmr_update_end_of_tick_and_tick_num(H5F_shared_t *shared,
+ hbool_t incr_tick_num)
+{
+ struct timespec curr; /* Current time in struct timespec */
+ struct timespec new_end_of_tick; /* new end_of_tick in struct timespec */
+ int64_t curr_nsecs; /* current time in nanoseconds */
+ int64_t tlen_nsecs; /* tick_len in nanoseconds */
+ int64_t new_end_nsecs; /* new end_of_tick in nanoseconds */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Get current time in struct timespec */
+ if ( HDclock_gettime(CLOCK_MONOTONIC, &curr) < 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, \
+ "can't get time via clock_gettime")
+
+ /* Convert curr to nsecs */
+ curr_nsecs = curr.tv_sec * nanosecs_per_second + curr.tv_nsec;
+
+ /* Convert tick_len to nanosecs */
+ tlen_nsecs = shared->vfd_swmr_config.tick_len * nanosecs_per_tenth_sec;
+
+ /*
+ * Update shared->tick_num
+ */
+ if ( incr_tick_num ) {
+
+ shared->tick_num++;
+
+ hlog_fast(tick, "%s tick %" PRIu64 " -> %" PRIu64,
+ __func__, shared->tick_num - 1, shared->tick_num);
+
+ if ( H5PB_vfd_swmr__set_tick(shared) < 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, \
+ "Can't update page buffer current tick")
+ }
+
+ /*
+ * Update shared->end_of_tick
+ */
+ /* Calculate new end_of_tick */
+
+ /* TODO: The modulo operation is very expensive on most machines --
+ * re-work this code so as to avoid it.
+ *
+ * JRM -- 11/12/18
+ */
+
+ new_end_nsecs = curr_nsecs + tlen_nsecs;
+ new_end_of_tick.tv_nsec = (long)(new_end_nsecs % nanosecs_per_second);
+ new_end_of_tick.tv_sec = new_end_nsecs / nanosecs_per_second;
+
+ shared->end_of_tick = new_end_of_tick;
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5F__vfd_swmr_update_end_of_tick_and_tick_num() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F__vfd_swmr_construct_write_md_hdr
+ *
+ * Purpose: Encode and write header to the metadata file.
+ *
+ * This is used by the VFD SWMR writer:
+ *
+ * --when opening an existing HDF5 file
+ * --when closing the HDF5 file
+ * --after flushing an HDF5 file
+ * --when updating the metadata file
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi -- 11/??/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__vfd_swmr_construct_write_md_hdr(H5F_shared_t *shared, uint32_t num_entries)
+{
+ uint8_t image[H5FD_MD_HEADER_SIZE]; /* Buffer for header */
+ uint8_t *p = NULL; /* Pointer to buffer */
+ uint32_t metadata_chksum; /* Computed metadata checksum value */
+ /* Size of header and index */
+ const size_t hdr_size = H5FD_MD_HEADER_SIZE;
+ ssize_t nwritten;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /*
+ * Encode metadata file header
+ */
+ p = image;
+
+ /* Encode magic for header */
+ HDmemcpy(p, H5FD_MD_HEADER_MAGIC, (size_t)H5_SIZEOF_MAGIC);
+ p += H5_SIZEOF_MAGIC;
+
+ /* Encode page size, tick number, index offset, index length */
+ UINT32ENCODE(p, shared->fs_page_size);
+ UINT64ENCODE(p, shared->tick_num);
+ UINT64ENCODE(p, shared->writer_index_offset);
+ UINT64ENCODE(p, H5FD_MD_INDEX_SIZE(num_entries));
+
+ /* Calculate checksum for header */
+ metadata_chksum = H5_checksum_metadata(image, (size_t)(p - image), 0);
+
+ /* Encode checksum for header */
+ UINT32ENCODE(p, metadata_chksum);
+
+ /* Sanity checks on header */
+ HDassert(p - image == (ptrdiff_t)hdr_size);
+
+ /* Set to beginning of the file */
+ if ( HDlseek(shared->vfd_swmr_md_fd, H5FD_MD_HEADER_OFF, SEEK_SET) < 0 )
+
+ HGOTO_ERROR(H5E_VFL, H5E_SEEKERROR, FAIL, \
+ "unable to seek in metadata file")
+
+ nwritten = HDwrite(shared->vfd_swmr_md_fd, image, hdr_size);
+ /* Write header to the metadata file */
+ if (nwritten != (ssize_t)hdr_size) {
+ HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, \
+ "error in writing header to metadata file")
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5F__vfd_swmr_construct_write_md_hdr() */
+
+
+/*-------------------------------------------------------------------------
+
+ * Function: H5F__vfd_swmr_construct_write_md_idx
+ *
+ * Purpose: Encode and write index to the metadata file.
+ *
+ * This is used by the VFD SWMR writer:
+ *
+ * --when opening an existing HDF5 file
+ * --when closing the HDF5 file
+ * --after flushing an HDF5 file
+ * --when updating the metadata file
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Vailin Choi -- 11/??/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__vfd_swmr_construct_write_md_idx(H5F_shared_t *shared,
+ uint32_t num_entries, struct H5FD_vfd_swmr_idx_entry_t index[])
+{
+ uint8_t *image = NULL; /* Pointer to buffer */
+ uint8_t *p = NULL; /* Pointer to buffer */
+ uint32_t metadata_chksum; /* Computed metadata checksum value */
+ /* Size of index */
+ const size_t idx_size = H5FD_MD_INDEX_SIZE(num_entries);
+ ssize_t nwritten;
+ unsigned i; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ HDassert(num_entries == 0 || index != NULL);
+
+ /* Allocate space for the buffer to hold the index */
+ if ( (image = HDmalloc(idx_size)) == NULL )
+
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, \
+ "memory allocation failed for md index")
+
+ /*
+ * Encode metadata file index
+ */
+ p = image;
+
+ /* Encode magic for index */
+ HDmemcpy(p, H5FD_MD_INDEX_MAGIC, H5_SIZEOF_MAGIC);
+ p += H5_SIZEOF_MAGIC;
+
+ /* Encode tick number */
+ UINT64ENCODE(p, shared->tick_num);
+
+ /* Encode number of entries in index */
+ UINT32ENCODE(p, num_entries);
+
+ /* Encode the index entries */
+ for(i = 0; i < num_entries; i++) {
+ UINT32ENCODE(p, index[i].hdf5_page_offset);
+ UINT32ENCODE(p, index[i].md_file_page_offset);
+ UINT32ENCODE(p, index[i].length);
+ UINT32ENCODE(p, index[i].chksum);
+ }
+
+ /* Calculate checksum for index */
+ metadata_chksum = H5_checksum_metadata(image, (size_t)(p - image), 0);
+
+ /* Encode checksum for index */
+ UINT32ENCODE(p, metadata_chksum);
+
+ /* Sanity checks on index */
+ HDassert(p - image == (ptrdiff_t)idx_size);
+
+ /* Verify the md file descriptor exists */
+ HDassert(shared->vfd_swmr_md_fd >= 0);
+
+ if (HDlseek(shared->vfd_swmr_md_fd,
+ (HDoff_t)shared->writer_index_offset, SEEK_SET) < 0)
+ HGOTO_ERROR(H5E_VFL, H5E_SEEKERROR, FAIL, \
+ "unable to seek in metadata file")
+
+ nwritten = HDwrite(shared->vfd_swmr_md_fd, image, idx_size);
+ /* Write index to the metadata file */
+ if (nwritten != (ssize_t)idx_size) {
+ HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, \
+ "error in writing index to metadata file")
+ }
+
+done:
+
+ if ( image ) {
+
+ HDfree(image);
+ }
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5F__vfd_swmr_construct_write_idx() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5F__idx_entry_cmp()
+ *
+ * Purpose: Callback used by HDqsort to sort entries in the index
+ *
+ * Return: 0 if the entries are the same
+ * -1 if entry1's offset is less than that of entry2
+ * 1 if entry1's offset is greater than that of entry2
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__idx_entry_cmp(const void *_entry1, const void *_entry2)
+{
+ const H5FD_vfd_swmr_idx_entry_t *entry1 = _entry1;
+ const H5FD_vfd_swmr_idx_entry_t *entry2 = _entry2;
+
+ int ret_value = 0; /* Return value */
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Sanity checks */
+ HDassert(entry1);
+ HDassert(entry2);
+
+ if(entry1->hdf5_page_offset < entry2->hdf5_page_offset)
+ ret_value = -1;
+ else if(entry1->hdf5_page_offset > entry2->hdf5_page_offset)
+ ret_value = 1;
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5F__idx_entry_cmp() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F__vfd_swmr_create_index
+ *
+ * Purpose: Allocate and initialize the index for the VFD SWMR metadata
+ * file.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: John Mainzer 11/5/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__vfd_swmr_create_index(H5F_shared_t *shared)
+{
+ size_t bytes_available;
+ size_t entries_in_index;
+ H5FD_vfd_swmr_idx_entry_t * index;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ HDassert(shared->vfd_swmr);
+ HDassert(shared->mdf_idx == NULL);
+ HDassert(shared->mdf_idx_len == 0);
+ HDassert(shared->mdf_idx_entries_used == 0);
+
+ bytes_available =
+ (size_t)shared->fs_page_size *
+ (size_t)(shared->vfd_swmr_config.md_pages_reserved - 1);
+
+ HDassert(bytes_available > 0);
+
+ entries_in_index =
+ (bytes_available - H5FD_MD_INDEX_SIZE(0)) / H5FD_MD_INDEX_ENTRY_SIZE;
+
+ HDassert(entries_in_index > 0);
+
+ index = HDcalloc(entries_in_index, sizeof(index[0]));
+
+ if (index == NULL) {
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL,
+ "memory allocation failed for md index")
+ }
+
+ HDassert(entries_in_index <= UINT32_MAX);
+
+ shared->mdf_idx = index;
+ shared->mdf_idx_len = (uint32_t)entries_in_index;
+ shared->mdf_idx_entries_used = 0;
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+H5FD_vfd_swmr_idx_entry_t *
+vfd_swmr_enlarge_shadow_index(H5F_t *f)
+{
+ H5F_shared_t *shared = f->shared;
+ H5FD_vfd_swmr_idx_entry_t *ret_value = NULL;
+ haddr_t idx_addr;
+ hsize_t idx_size;
+ H5FD_vfd_swmr_idx_entry_t *new_mdf_idx = NULL, *old_mdf_idx;
+ uint32_t new_mdf_idx_len, old_mdf_idx_len;
+
+ FUNC_ENTER_NOAPI(NULL)
+
+ hlog_fast(shadow_index_enlarge, "Enlarging shadow index.");
+
+ old_mdf_idx = shared->mdf_idx;
+ old_mdf_idx_len = shared->mdf_idx_len;
+
+ if (UINT32_MAX - old_mdf_idx_len >= old_mdf_idx_len)
+ new_mdf_idx_len = old_mdf_idx_len * 2;
+ else
+ new_mdf_idx_len = UINT32_MAX;
+
+ idx_size = H5FD_MD_INDEX_SIZE(new_mdf_idx_len);
+
+ idx_addr = H5MV_alloc(f, idx_size);
+
+ if (idx_addr == HADDR_UNDEF) {
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL,
+ "shadow-file allocation failed for index")
+ }
+
+ new_mdf_idx = HDmalloc(new_mdf_idx_len * sizeof(new_mdf_idx[0]));
+
+ if (new_mdf_idx == NULL) {
+ (void)H5MV_free(f, idx_addr, idx_size);
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL,
+ "memory allocation failed for md index")
+ }
+
+ /* Copy the old index in its entirety to the new, instead of copying
+ * just the _entries_used, because the caller may have been in the
+ * process of adding entries, and some callers may not update
+ * _entries_used immediately.
+ */
+ memcpy(new_mdf_idx, old_mdf_idx, sizeof(new_mdf_idx[0]) * old_mdf_idx_len);
+
+ shared->writer_index_offset = idx_addr;
+ ret_value = shared->mdf_idx = new_mdf_idx;
+ shared->mdf_idx_len = new_mdf_idx_len;
+
+ /* Postpone reclamation of the old index until max_lag ticks from now.
+ * It's only necessary to wait until after the new index is in place,
+ * so it's possible that some disused shadow storage will build up
+ * past what is strictly necessary, but it seems like a reasonable
+ * trade-off for simplicity.
+ */
+ if (shadow_range_defer_free(shared, shared->writer_index_offset,
+ H5FD_MD_INDEX_SIZE(old_mdf_idx_len)) == -1) {
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL,
+ "could not schedule index reclamation");
+ }
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5F__vfd_swmr_writer__wait_a_tick
+ *
+ * Purpose: Before a file that has been opened by a VFD SWMR writer,
+ * all pending delayed writes must be allowed drain.
+ *
+ * This function facilitates this by sleeping for a tick, and
+ * then running the writer end of tick function.
+ *
+ * It should only be called as part the flush or close operations.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: John Mainzer 11/23/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5F__vfd_swmr_writer__wait_a_tick(H5F_t *f)
+{
+ int result;
+ struct timespec req;
+ struct timespec rem;
+ uint64_t tick_in_nsec;
+ H5F_shared_t *shared = f->shared;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ HDassert(shared->vfd_swmr);
+ HDassert(shared->vfd_swmr_writer);
+
+ tick_in_nsec = shared->vfd_swmr_config.tick_len * nanosecs_per_tenth_sec;
+ req.tv_nsec = (long)(tick_in_nsec % nanosecs_per_second);
+ req.tv_sec = (time_t)(tick_in_nsec / nanosecs_per_second);
+
+ result = HDnanosleep(&req, &rem);
+
+ while ( result == -1 ) {
+
+ req = rem;
+ result = HDnanosleep(&req, &rem);
+ }
+
+ if ( result != 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, "HDnanosleep() failed.")
+
+ if ( H5F_vfd_swmr_writer_end_of_tick(f, false) < 0 )
+
+ HGOTO_ERROR(H5E_FILE, H5E_SYSTEM, FAIL, \
+ "H5F_vfd_swmr_writer_end_of_tick() failed.")
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5F__vfd_swmr_writer__wait_a_tick() */
+
+herr_t
+H5F_vfd_swmr_process_eot_queue(bool entering_api)
+{
+ struct timespec now;
+ eot_queue_entry_t *first_head, *head;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ first_head = head = TAILQ_FIRST(&eot_queue_g);
+
+ do {
+ H5F_t *f = head->vfd_swmr_file;
+ H5F_shared_t *shared = f->shared;
+
+ if(HDclock_gettime(CLOCK_MONOTONIC, &now) < 0) {
+ HGOTO_ERROR(H5E_FUNC, H5E_CANTGET, FAIL,
+ "can't get time via clock_gettime");
+ }
+ if(timespeccmp(&now, &head->end_of_tick, <))
+ break;
+ /* If the H5F_shared_t is labeled with a later EOT time than
+ * the queue entry is, then we have already performed the
+ * H5F_shared_t's EOT processing. That can happen if
+ * multiple H5F_t share the H5F_shared_t. Just update the
+ * EOT queue entry and move to the next.
+ */
+ if (timespeccmp(&head->end_of_tick, &shared->end_of_tick, <)) {
+ H5F_vfd_swmr_update_entry_eot(head);
+ } else if (shared->vfd_swmr_writer) {
+ if (H5F_vfd_swmr_writer_end_of_tick(f, false) < 0)
+ HGOTO_ERROR(H5E_FUNC, H5E_CANTSET, FAIL,
+ "end of tick error for VFD SWMR writer");
+ } else if (H5F_vfd_swmr_reader_end_of_tick(f, entering_api) < 0) {
+ HGOTO_ERROR(H5E_FUNC, H5E_CANTSET, FAIL,
+ "end of tick error for VFD SWMR reader");
+ }
+ } while ((head = TAILQ_FIRST(&eot_queue_g)) != NULL && head != first_head);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
diff --git a/src/H5Gcache.c b/src/H5Gcache.c
index 13a33a3..ca67d56 100644
--- a/src/H5Gcache.c
+++ b/src/H5Gcache.c
@@ -101,6 +101,7 @@ const H5AC_class_t H5AC_SNODE[1] = {{
NULL, /* 'notify' callback */
H5G__cache_node_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
diff --git a/src/H5HFcache.c b/src/H5HFcache.c
index 8dbdf25..22a2623 100644
--- a/src/H5HFcache.c
+++ b/src/H5HFcache.c
@@ -146,6 +146,7 @@ const H5AC_class_t H5AC_FHEAP_HDR[1] = {{
NULL, /* 'notify' callback */
H5HF__cache_hdr_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5HF indirect block inherits cache-like properties from H5AC */
@@ -164,6 +165,7 @@ const H5AC_class_t H5AC_FHEAP_IBLOCK[1] = {{
H5HF__cache_iblock_notify, /* 'notify' callback */
H5HF__cache_iblock_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5HF direct block inherits cache-like properties from H5AC */
@@ -182,6 +184,7 @@ const H5AC_class_t H5AC_FHEAP_DBLOCK[1] = {{
H5HF__cache_dblock_notify, /* 'notify' callback */
H5HF__cache_dblock_free_icr, /* 'free_icr' callback */
H5HF__cache_dblock_fsf_size, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
diff --git a/src/H5HG.c b/src/H5HG.c
index f9d780c..8177dc3 100644
--- a/src/H5HG.c
+++ b/src/H5HG.c
@@ -610,6 +610,9 @@ H5HG_read(H5F_t *f, H5HG_t *hobj, void *object/*out*/, size_t *buf_size)
if(NULL == (heap = H5HG__protect(f, hobj->addr, H5AC__READ_ONLY_FLAG)))
HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, NULL, "unable to protect global heap")
+ if (hobj->idx >= heap->nused && H5HG_trap("out of bounds"))
+ HGOTO_ERROR(H5E_HEAP, H5E_BADRANGE, NULL, "address out of bounds")
+
HDassert(hobj->idx < heap->nused);
HDassert(heap->obj[hobj->idx].begin);
size = heap->obj[hobj->idx].size;
diff --git a/src/H5HGcache.c b/src/H5HGcache.c
index 7485aad..938c575 100644
--- a/src/H5HGcache.c
+++ b/src/H5HGcache.c
@@ -95,6 +95,7 @@ const H5AC_class_t H5AC_GHEAP[1] = {{
NULL, /* 'notify' callback */
H5HG__cache_heap_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
diff --git a/src/H5HGprivate.h b/src/H5HGprivate.h
index 573ef39..760c0bf 100644
--- a/src/H5HGprivate.h
+++ b/src/H5HGprivate.h
@@ -73,5 +73,7 @@ H5_DLL size_t H5HG_get_free_size(const H5HG_heap_t *h);
H5_DLL herr_t H5HG_debug(H5F_t *f, haddr_t addr, FILE *stream, int indent,
int fwidth);
+bool H5HG_trap(const char *);
+
#endif /* _H5HGprivate_H */
diff --git a/src/H5HGtrap.c b/src/H5HGtrap.c
new file mode 100644
index 0000000..2f09d48
--- /dev/null
+++ b/src/H5HGtrap.c
@@ -0,0 +1,30 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/****************/
+/* Module Setup */
+/****************/
+
+#include "H5HGmodule.h" /* This source code file is part of the H5HG module */
+
+/*
+ * Headers
+ */
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5HGpkg.h" /* Global heaps */
+
+bool
+H5HG_trap(const char *reason)
+{
+ return false;
+}
diff --git a/src/H5HLcache.c b/src/H5HLcache.c
index 734ec5c..5a7321f 100644
--- a/src/H5HLcache.c
+++ b/src/H5HLcache.c
@@ -118,6 +118,7 @@ const H5AC_class_t H5AC_LHEAP_PRFX[1] = {{
NULL, /* 'notify' callback */
H5HL__cache_prefix_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
const H5AC_class_t H5AC_LHEAP_DBLK[1] = {{
@@ -135,6 +136,7 @@ const H5AC_class_t H5AC_LHEAP_DBLK[1] = {{
H5HL__cache_datablock_notify, /* 'notify' callback */
H5HL__cache_datablock_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
diff --git a/src/H5MF.c b/src/H5MF.c
index fac6620..22438d3 100644
--- a/src/H5MF.c
+++ b/src/H5MF.c
@@ -22,6 +22,9 @@
*-------------------------------------------------------------------------
*/
+#include "H5queue.h"
+#include "hlog.h"
+
/****************/
/* Module Setup */
/****************/
@@ -43,6 +46,8 @@
#include "H5VMprivate.h" /* Vectors and arrays */
+#include "hlog.h"
+
/****************/
/* Local Macros */
/****************/
@@ -88,6 +93,7 @@ typedef struct {
/* Local Prototypes */
/********************/
+static herr_t H5MF__xfree_impl(H5F_t *, H5FD_mem_t, haddr_t, hsize_t);
/* Allocator routines */
static haddr_t H5MF__alloc_pagefs(H5F_t *f, H5FD_mem_t alloc_type, hsize_t size);
@@ -125,11 +131,92 @@ hbool_t H5_PKG_INIT_VAR = FALSE;
/* Library Private Variables */
/*****************************/
-
/*******************/
/* Local Variables */
/*******************/
+HLOG_OUTLET_DECL(h5mf);
+HLOG_OUTLET_SHORT_DEFN(h5mf, all);
+HLOG_OUTLET_SHORT_DEFN(h5mf_defer, h5mf);
+HLOG_OUTLET_SHORT_DEFN(h5mf_free, h5mf);
+HLOG_OUTLET_SHORT_DEFN(h5mf_alloc, h5mf);
+HLOG_OUTLET_MEDIUM_DEFN(noisy_h5mf_alloc, h5mf_alloc, HLOG_OUTLET_S_OFF);
+HLOG_OUTLET_SHORT_DEFN(h5mf_extend, h5mf);
+HLOG_OUTLET_SHORT_DEFN(h5mf_shrink, h5mf);
+
+static herr_t
+defer_free(H5F_shared_t *shared, H5FD_mem_t alloc_type, haddr_t addr,
+ hsize_t size)
+{
+ lower_defree_t *df;
+
+ if ((df = malloc(sizeof(*df))) == NULL)
+ return FAIL;
+
+ df->alloc_type = alloc_type;
+ df->addr = addr;
+ df->size = size;
+ df->free_after_tick = shared->tick_num + shared->vfd_swmr_config.max_lag;
+
+ hlog_fast(h5mf_defer,
+ "%s.%d: deferred free at %" PRIuHADDR ", %" PRIuHSIZE
+ " bytes until tick %" PRIu64, __FILE__, __LINE__, addr, size,
+ df->free_after_tick);
+
+ SIMPLEQ_INSERT_TAIL(&shared->lower_defrees, df, link);
+
+ return SUCCEED;
+}
+
+static uint64_t
+H5MF_total_deferred_frees(H5F_shared_t *shared)
+{
+ lower_defree_t *df;
+ uint64_t total = 0;
+
+ SIMPLEQ_FOREACH(df, &shared->lower_defrees, link)
+ total += df->size;
+
+ return total;
+}
+
+herr_t
+H5MF_process_deferred_frees(H5F_t *f, const uint64_t tick_num)
+{
+ lower_defree_t *df;
+ herr_t err = SUCCEED;
+ H5F_shared_t *shared = f->shared;
+ lower_defree_queue_t defrees = SIMPLEQ_HEAD_INITIALIZER(defrees);
+
+ /* Have to empty the queue before processing it because we
+ * could re-enter this routine through H5MF__xfree_impl. If
+ * items were still on the queue, we would enter
+ * H5MF_process_deferred_frees() recursively until the queue was empty.
+ */
+ SIMPLEQ_CONCAT(&defrees, &shared->lower_defrees);
+
+ while ((df = SIMPLEQ_FIRST(&defrees)) != NULL) {
+ if (tick_num <= df->free_after_tick)
+ break;
+ hlog_fast(h5mf_defer,
+ "%s.%d: processing free at %" PRIuHADDR ", %" PRIuHSIZE " bytes",
+ __FILE__, __LINE__, df->addr, df->size);
+ SIMPLEQ_REMOVE_HEAD(&defrees, link);
+ if (H5MF__xfree_impl(f, df->alloc_type, df->addr, df->size) < 0)
+ err = FAIL;
+ free(df);
+ }
+
+ if (err != SUCCEED) {
+ hlog_fast(h5mf_defer, "%s.%d: error: dropped entries on the floor",
+ __FILE__, __LINE__);
+ }
+
+ /* Save remaining entries for processing, later. */
+ SIMPLEQ_CONCAT(&shared->lower_defrees, &defrees);
+
+ return err;
+}
/*-------------------------------------------------------------------------
@@ -795,9 +882,11 @@ H5MF_alloc(H5F_t *f, H5FD_mem_t alloc_type, hsize_t size)
haddr_t ret_value = HADDR_UNDEF; /* Return value */
FUNC_ENTER_NOAPI_TAG(H5AC__FREESPACE_TAG, HADDR_UNDEF)
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: alloc_type = %u, size = %Hu\n", FUNC, (unsigned)alloc_type, size);
-#endif /* H5MF_ALLOC_DEBUG */
+
+ hlog_fast(h5mf_alloc,
+ "%s: enter %p type %u size %" PRIuHSIZE " tick %" PRIu64,
+ __func__, (void *)f->shared, (unsigned)alloc_type, size,
+ f->shared->vfd_swmr_writer ? f->shared->tick_num : 0);
/* check arguments */
HDassert(f);
@@ -805,11 +894,15 @@ HDfprintf(stderr, "%s: alloc_type = %u, size = %Hu\n", FUNC, (unsigned)alloc_typ
HDassert(f->shared->lf);
HDassert(size > 0);
+ if (!f->shared->vfd_swmr_writer)
+ ; // not a VFD SWMR writer, do not process deferrals
+ else if (H5MF_process_deferred_frees(f, f->shared->tick_num) < 0) {
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, HADDR_UNDEF,
+ "could not process deferrals")
+ }
H5MF__alloc_to_fs_type(f->shared, alloc_type, size, &fs_type);
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: Check 1.0\n", FUNC);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
+ hlog_fast(noisy_h5mf_alloc, "%s: Check 1.0", __func__);
/* Set the ring type in the API context */
if(H5MF__fsm_type_is_self_referential(f->shared, fs_type))
@@ -843,9 +936,9 @@ HDfprintf(stderr, "%s: Check 1.0\n", FUNC);
/* If no space is found from the free-space manager, continue further action */
if(!H5F_addr_defined(ret_value)) {
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: Check 2.0\n", FUNC);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
+
+ hlog_fast(noisy_h5mf_alloc, "%s: Check 2.0", __func__);
+
if(f->shared->fs_strategy == H5F_FSPACE_STRATEGY_PAGE) {
HDassert(f->shared->fs_page_size >= H5F_FILE_SPACE_PAGE_SIZE_MIN);
if(HADDR_UNDEF == (ret_value = H5MF__alloc_pagefs(f, alloc_type, size)))
@@ -857,18 +950,17 @@ HDfprintf(stderr, "%s: Check 2.0\n", FUNC);
} /* end else */
} /* end if */
HDassert(H5F_addr_defined(ret_value));
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: Check 3.0\n", FUNC);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
+
+ hlog_fast(noisy_h5mf_alloc, "%s: Check 3.0", FUNC);
done:
/* Reset the ring in the API context */
if(orig_ring != H5AC_RING_INV)
H5AC_set_ring(orig_ring, NULL);
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: Leaving: ret_value = %a, size = %Hu\n", FUNC, ret_value, size);
-#endif /* H5MF_ALLOC_DEBUG */
+ hlog_fast(h5mf_alloc,
+ "%s: leave %p type %u addr %" PRIuHADDR " size %" PRIuHSIZE,
+ __func__, (void *)f->shared, (unsigned)alloc_type, ret_value, size);
#ifdef H5MF_ALLOC_DEBUG_DUMP
H5MF__sects_dump(f, stderr);
#endif /* H5MF_ALLOC_DEBUG_DUMP */
@@ -986,7 +1078,7 @@ HDfprintf(stderr, "%s: alloc_type = %u, size = %Hu\n", FUNC, (unsigned)alloc_typ
/* Insert the new page into the Page Buffer list of new pages so
we don't read an empty page from disk */
- if(f->shared->page_buf != NULL && H5PB_add_new_page(f->shared, alloc_type, new_page) < 0)
+ if(f->shared->pb_ptr != NULL && H5PB_add_new_page(f->shared, alloc_type, new_page) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINSERT, HADDR_UNDEF, "can't add new page to Page Buffer new page list")
ret_value = new_page;
@@ -1094,17 +1186,14 @@ done:
herr_t
H5MF_xfree(H5F_t *f, H5FD_mem_t alloc_type, haddr_t addr, hsize_t size)
{
- H5F_mem_page_t fs_type; /* Free space type (mapped from allocation type) */
- H5MF_free_section_t *node = NULL; /* Free space section pointer */
- unsigned ctype; /* section class type */
- H5AC_ring_t orig_ring = H5AC_RING_INV; /* Original ring value */
- H5AC_ring_t fsm_ring; /* Ring of FSM */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI_TAG(H5AC__FREESPACE_TAG, FAIL)
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: Entering - alloc_type = %u, addr = %a, size = %Hu\n", FUNC, (unsigned)alloc_type, addr, size);
-#endif /* H5MF_ALLOC_DEBUG */
+
+ hlog_fast(h5mf_free, "%s: Entering - alloc_type %u addr %" PRIuHADDR
+ " size %" PRIuHSIZE " tick %" PRIu64,
+ __func__, (unsigned)alloc_type, addr, size,
+ f->shared->vfd_swmr_writer ? f->shared->tick_num : 0);
/* check arguments */
HDassert(f);
@@ -1112,6 +1201,39 @@ HDfprintf(stderr, "%s: Entering - alloc_type = %u, addr = %a, size = %Hu\n", FUN
HGOTO_DONE(SUCCEED)
HDassert(addr != 0); /* Can't deallocate the superblock :-) */
+ if (!f->shared->vfd_swmr_writer || f->shared->closing ||
+ alloc_type != H5FD_MEM_DRAW) {
+ /* VFD SWMR writers defer raw-data allocations until the
+ * file starts to close.
+ */
+ ret_value = H5MF__xfree_impl(f, alloc_type, addr, size);
+ } else if (defer_free(f->shared, alloc_type, addr, size) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTFREE, FAIL, "could not defer")
+ else if (H5MF_process_deferred_frees(f, f->shared->tick_num) < 0) {
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL,
+ "could not process deferrals")
+ }
+
+done:
+ FUNC_LEAVE_NOAPI_TAG(ret_value)
+}
+
+static herr_t
+H5MF__xfree_inner_impl(H5F_t *f, H5FD_mem_t alloc_type, haddr_t addr, hsize_t size)
+{
+ H5F_mem_page_t fs_type; /* Free space type (mapped from allocation type) */
+ H5MF_free_section_t *node = NULL; /* Free space section pointer */
+ unsigned ctype; /* section class type */
+ H5AC_ring_t orig_ring = H5AC_RING_INV; /* Original ring value */
+ H5AC_ring_t fsm_ring; /* Ring of FSM */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ hlog_fast(h5mf_free,
+ "%s: enter %p type %u addr %" PRIuHADDR " size %" PRIuHSIZE,
+ __func__, (void *)f->shared, (unsigned)alloc_type, addr, size);
+
+ FUNC_ENTER_STATIC_TAG(H5AC__FREESPACE_TAG)
+
H5MF__alloc_to_fs_type(f->shared, alloc_type, size, &fs_type);
/* Set the ring type in the API context */
@@ -1149,28 +1271,27 @@ HDfprintf(stderr, "%s: Entering - alloc_type = %u, addr = %a, size = %Hu\n", FUN
* see if we can avoid creating one by checking if the freed
* space is at the end of the file
*/
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: fs_addr = %a\n", FUNC, f->shared->fs_addr[fs_type]);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
+
+ hlog_fast(h5mf_free, "%s: fs_addr %" PRIuHADDR, __func__,
+ f->shared->fs_addr[fs_type]);
+
if(!H5F_addr_defined(f->shared->fs_addr[fs_type])) {
htri_t status; /* "can absorb" status for section into */
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: Trying to avoid starting up free space manager\n", FUNC);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
+ hlog_fast(h5mf_free, "%s: Trying to avoid starting up free space manager", __func__);
+
/* Try to shrink the file or absorb the block into a block aggregator */
if((status = H5MF_try_shrink(f, alloc_type, addr, size)) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTMERGE, FAIL, "can't check for absorbing block")
else if(status > 0)
- /* Indicate success */
HGOTO_DONE(SUCCEED)
else if(size < f->shared->fs_threshold) {
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: dropping addr = %a, size = %Hu, on the floor!\n", FUNC, addr, size);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
+ hlog_fast(h5mf_free, "%s: dropping addr %" PRIuHADDR
+ " size %" PRIuHSIZE " on the floor!",
+ __func__, addr, size);
HGOTO_DONE(SUCCEED)
- } /* end else-if */
- } /* end if */
+ }
+ }
/* If we are deleting the free space manager, leave now, to avoid
* [re-]starting it.
@@ -1183,11 +1304,10 @@ HDfprintf(stderr, "%s: dropping addr = %a, size = %Hu, on the floor!\n", FUNC, a
*/
if(f->shared->fs_state[fs_type] == H5F_FS_STATE_DELETING ||
!H5F_HAVE_FREE_SPACE_MANAGER(f)) {
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: dropping addr = %a, size = %Hu, on the floor!\n", FUNC, addr, size);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
+ hlog_fast(h5mf_free, "%s: dropping addr %" PRIuHADDR
+ " size %" PRIuHSIZE " on the floor!", __func__, addr, size);
HGOTO_DONE(SUCCEED)
- } /* end if */
+ }
/* There's either already a free space manager, or the freed
* space isn't at the end of the file, so start up (or create)
@@ -1195,7 +1315,7 @@ HDfprintf(stderr, "%s: dropping addr = %a, size = %Hu, on the floor!\n", FUNC, a
*/
if(H5MF__start_fstype(f, fs_type) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, FAIL, "can't initialize file free space")
- } /* end if */
+ }
/* Create the free-space section for the freed section */
ctype = H5MF_SECT_CLASS_TYPE(f, size);
@@ -1206,20 +1326,15 @@ HDfprintf(stderr, "%s: dropping addr = %a, size = %Hu, on the floor!\n", FUNC, a
if(size >= f->shared->fs_threshold) {
HDassert(f->shared->fs_man[fs_type]);
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: Before H5FS_sect_add()\n", FUNC);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
+ hlog_fast(h5mf_free, "%s: Before H5FS_sect_add()", __func__);
/* Add to the free space for the file */
if(H5MF__add_sect(f, alloc_type, f->shared->fs_man[fs_type], node) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINSERT, FAIL, "can't add section to file free space")
node = NULL;
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: After H5FS_sect_add()\n", FUNC);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
- } /* end if */
- else {
+ hlog_fast(h5mf_free, "%s: After H5FS_sect_add()", __func__);
+ } else {
htri_t merged; /* Whether node was merged */
H5MF_sect_ud_t udata; /* User data for callback */
@@ -1235,7 +1350,7 @@ HDfprintf(stderr, "%s: After H5FS_sect_add()\n", FUNC);
else if(merged == TRUE) /* successfully merged */
/* Indicate that the node was used */
node = NULL;
- } /* end else */
+ }
done:
/* Reset the ring in the API context */
@@ -1247,14 +1362,65 @@ done:
if(H5MF__sect_free((H5FS_section_info_t *)node) < 0)
HDONE_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't free simple section node")
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: Leaving, ret_value = %d\n", FUNC, ret_value);
-#endif /* H5MF_ALLOC_DEBUG */
+ hlog_fast(h5mf_free,
+ "%s: %p leave %d", __func__, (void *)f->shared, ret_value);
+
#ifdef H5MF_ALLOC_DEBUG_DUMP
H5MF__sects_dump(f, stderr);
#endif /* H5MF_ALLOC_DEBUG_DUMP */
FUNC_LEAVE_NOAPI_TAG(ret_value)
-} /* end H5MF_xfree() */
+}
+
+static herr_t
+H5MF__xfree_impl(H5F_t *f, H5FD_mem_t alloc_type, haddr_t addr, hsize_t size)
+{
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC_TAG(H5AC__FREESPACE_TAG)
+
+ ret_value = H5MF__xfree_inner_impl(f, alloc_type, addr, size);
+
+ /* If the page buffer is enabled, notify it so that it can remove any
+ * pages that lie in the freed region.
+ *
+ * This is necessary in normal (AKA non VFD SWMR mode) as if single large
+ * metadata entry is allocated out of the freed space, writes to the
+ * entry will by-pass the page buffer. If a dirty intersecting
+ * entry is left in the page buffer, it could introduce corruption
+ * if it is flushed after the large metadata entry is written.
+ *
+ * Further, in the VFD SWMR case, the large metadata entry will typically
+ * be buffered in the page buffer. If an intersecting entry is left
+ * in the page buffer, in addition to causing potential corruption in
+ * the HDF5 file, it may also result in overlaping entries in the page
+ * buffer and metadata file index.
+ *
+ * It's ok to remove the page from the PB without flushing to
+ * the shadow file or to the underlying HDF5 file because any
+ * writes to the page in this tick have not yet become visible
+ * to the reader, and any writes to the page in previous ticks are
+ * recorded in the shadow file.
+ *
+ * Note: This is not the correct place for this call, as it is
+ * sometimes bypassed by a HGOTO_DONE earlier in the function.
+ * This causes the assertion failure in fheap when run at
+ * express test level 0. Discuss with Vailin.
+ *
+ * JRM -- 4/28/20
+ */
+ if (ret_value == SUCCEED && f->shared->pb_ptr &&
+ size >= f->shared->fs_page_size) {
+
+ HDassert(H5F_SHARED_PAGED_AGGR(f->shared));
+
+ if (H5PB_remove_entries(f->shared, addr, size) < 0) {
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTFREE, FAIL,
+ "can't remove entries from page buffer")
+ }
+ }
+done:
+ FUNC_LEAVE_NOAPI_TAG(ret_value)
+}
/*-------------------------------------------------------------------------
@@ -1293,9 +1459,10 @@ H5MF_try_extend(H5F_t *f, H5FD_mem_t alloc_type, haddr_t addr, hsize_t size,
htri_t ret_value = FALSE; /* Return value */
FUNC_ENTER_NOAPI_TAG(H5AC__FREESPACE_TAG, FAIL)
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: Entering: alloc_type = %u, addr = %a, size = %Hu, extra_requested = %Hu\n", FUNC, (unsigned)alloc_type, addr, size, extra_requested);
-#endif /* H5MF_ALLOC_DEBUG */
+
+ hlog_fast(h5mf_extend, "%s: Entering: alloc_type %u addr %" PRIuHADDR
+ " size %" PRIuHSIZE " extra_requested %" PRIuHSIZE, __func__,
+ (unsigned)alloc_type, addr, size, extra_requested);
/* Sanity check */
HDassert(f);
@@ -1343,9 +1510,9 @@ HDfprintf(stderr, "%s: Entering: alloc_type = %u, addr = %a, size = %Hu, extra_r
/* Try extending the block at EOA */
if((ret_value = H5F__try_extend(f, map_type, end, extra_requested + frag_size)) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTEXTEND, FAIL, "error extending file")
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: extended = %t\n", FUNC, ret_value);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
+
+ hlog_fast(h5mf_extend, "%s: extended %s", __func__,
+ htri_to_string(ret_value));
/* If extending at EOA succeeds: */
/* for paged aggregation, put the fragment into the large-sized free-space manager */
@@ -1381,10 +1548,9 @@ HDfprintf(stderr, "%s: extended = %t\n", FUNC, ret_value);
if((ret_value = H5MF__aggr_try_extend(f, aggr, map_type, end, extra_requested)) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTEXTEND, FAIL, "error extending aggregation block")
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: H5MF__aggr_try_extend = %t\n", FUNC, ret_value);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
- } /* end if */
+ hlog_fast(h5mf_extend, "%s: H5MF__aggr_try_extend %s", __func__,
+ htri_to_string(ret_value));
+ }
/* If no extension so far, try to extend into a free-space section */
if(ret_value == FALSE && ((f->shared->fs_strategy == H5F_FSPACE_STRATEGY_FSM_AGGR) ||
@@ -1405,10 +1571,10 @@ HDfprintf(stderr, "%s: H5MF__aggr_try_extend = %t\n", FUNC, ret_value);
if(f->shared->fs_man[fs_type]) {
if((ret_value = H5FS_sect_try_extend(f, f->shared->fs_man[fs_type], addr, size, extra_requested, H5FS_ADD_RETURNED_SPACE, &udata)) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTEXTEND, FAIL, "error extending block in free space manager")
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: Try to H5FS_sect_try_extend = %t\n", FUNC, ret_value);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
- } /* end if */
+
+ hlog_fast(h5mf_extend, "%s: Try to H5FS_sect_try_extend %s",
+ __func__, htri_to_string(ret_value));
+ }
/* For paged aggregation and a metadata block: try to extend into page end threshold */
if(ret_value == FALSE && H5F_PAGED_AGGR(f) && map_type != H5FD_MEM_DRAW) {
@@ -1416,21 +1582,20 @@ HDfprintf(stderr, "%s: Try to H5FS_sect_try_extend = %t\n", FUNC, ret_value);
if(frag_size <= H5F_PGEND_META_THRES(f) && extra_requested <= frag_size)
ret_value = TRUE;
-#ifdef H5MF_ALLOC_DEBUG_MORE
-HDfprintf(stderr, "%s: Try to extend into the page end threshold = %t\n", FUNC, ret_value);
-#endif /* H5MF_ALLOC_DEBUG_MORE */
- } /* end if */
- } /* end if */
- } /* allow_extend */
+
+ hlog_fast(h5mf_extend, "%s: Try to extend into the page end threshold %s", __func__, htri_to_string(ret_value));
+ }
+ }
+ }
done:
/* Reset the ring in the API context */
if(orig_ring != H5AC_RING_INV)
H5AC_set_ring(orig_ring, NULL);
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: Leaving: ret_value = %t\n", FUNC, ret_value);
-#endif /* H5MF_ALLOC_DEBUG */
+
+ hlog_fast(h5mf_extend, "%s: Leaving: ret_value %s", __func__, htri_to_string(ret_value));
+
#ifdef H5MF_ALLOC_DEBUG_DUMP
H5MF__sects_dump(f, stderr);
#endif /* H5MF_ALLOC_DEBUG_DUMP */
@@ -1465,9 +1630,9 @@ H5MF_try_shrink(H5F_t *f, H5FD_mem_t alloc_type, haddr_t addr, hsize_t size)
htri_t ret_value = FALSE; /* Return value */
FUNC_ENTER_NOAPI_TAG(H5AC__FREESPACE_TAG, FAIL)
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: Entering - alloc_type = %u, addr = %a, size = %Hu\n", FUNC, (unsigned)alloc_type, addr, size);
-#endif /* H5MF_ALLOC_DEBUG */
+
+ hlog_fast(h5mf_shrink, "%s: Entering - alloc_type %u addr %" PRIuHADDR
+ " size %" PRIuHSIZE, __func__, (unsigned)alloc_type, addr, size);
/* check arguments */
HDassert(f);
@@ -1521,9 +1686,9 @@ done:
if(node && H5MF__sect_free((H5FS_section_info_t *)node) < 0)
HDONE_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't free simple section node")
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: Leaving, ret_value = %d\n", FUNC, ret_value);
-#endif /* H5MF_ALLOC_DEBUG */
+ hlog_fast(h5mf_shrink, "%s: Leaving, ret_value %d", __func__,
+ ret_value);
+
FUNC_LEAVE_NOAPI_TAG(ret_value)
} /* end H5MF_try_shrink() */
@@ -1546,27 +1711,27 @@ H5MF_close(H5F_t *f)
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI_TAG(H5AC__FREESPACE_TAG, FAIL)
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: Entering\n", FUNC);
-#endif /* H5MF_ALLOC_DEBUG */
+
+ hlog_fast(h5mf, "%s: entering", __func__);
/* check args */
HDassert(f);
HDassert(f->shared);
+ hlog_fast(h5mf, "%s: total deferred frees %" PRIu64, __func__,
+ H5MF_total_deferred_frees(f->shared));
+
if(H5F_PAGED_AGGR(f)) {
if((ret_value = H5MF__close_pagefs(f)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTFREE, FAIL, "can't close free-space managers for 'page' file space")
- } /* end if */
- else {
+ } else {
if((ret_value = H5MF__close_aggrfs(f)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTFREE, FAIL, "can't close free-space managers for 'aggr' file space")
} /* end else */
done:
-#ifdef H5MF_ALLOC_DEBUG
-HDfprintf(stderr, "%s: Leaving\n", FUNC);
-#endif /* H5MF_ALLOC_DEBUG */
+ hlog_fast(h5mf, "%s: leaving", __func__);
+
FUNC_LEAVE_NOAPI_TAG(ret_value)
} /* end H5MF_close() */
diff --git a/src/H5MFaggr.c b/src/H5MFaggr.c
index 0124555..476dacd 100644
--- a/src/H5MFaggr.c
+++ b/src/H5MFaggr.c
@@ -753,6 +753,9 @@ H5MF_free_aggrs(H5F_t *f)
HDassert(f->shared);
HDassert(f->shared->lf);
+ if (f->shared->closing && H5MF_process_deferred_frees(f, UINT64_MAX) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTFREE, FAIL, "could not process deferrals")
+
/* Retrieve metadata aggregator info, if available */
if(H5MF__aggr_query(f, &(f->shared->meta_aggr), &ma_addr, &ma_size) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGET, FAIL, "can't query metadata aggregator stats")
diff --git a/src/H5MFprivate.h b/src/H5MFprivate.h
index acd773b..c7e9e6e 100644
--- a/src/H5MFprivate.h
+++ b/src/H5MFprivate.h
@@ -67,6 +67,8 @@ H5_DLL ssize_t H5MF_get_free_sections(H5F_t *f, H5FD_mem_t type, size_t nsects,
/* File 'temporary' space allocation routines */
H5_DLL haddr_t H5MF_alloc_tmp(H5F_t *f, hsize_t size);
+herr_t H5MF_process_deferred_frees(H5F_t *, uint64_t);
+
/* 'block aggregator' routines */
H5_DLL herr_t H5MF_free_aggrs(H5F_t *f);
H5_DLL htri_t H5MF_aggrs_try_shrink_eoa(H5F_t *f);
diff --git a/src/H5MFsection.c b/src/H5MFsection.c
index 715ece4..1b9e756 100644
--- a/src/H5MFsection.c
+++ b/src/H5MFsection.c
@@ -771,13 +771,16 @@ H5MF__sect_small_merge(H5FS_section_info_t **_sect1, H5FS_section_info_t *_sect2
if(H5MF_xfree(udata->f, udata->alloc_type, (*sect1)->sect_info.addr, (*sect1)->sect_info.size) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTFREE, FAIL, "can't free merged section")
- /* Need to free possible metadata page in the PB cache */
- /* This is in response to the data corruption bug from fheap.c with page buffering + page strategy */
- /* Note: Large metadata page bypasses the PB cache */
- /* Note: Update of raw data page (large or small sized) is handled by the PB cache */
- if(udata->f->shared->page_buf != NULL && udata->alloc_type != H5FD_MEM_DRAW)
+ /* Need to free possible raw/metadata page in the page buffer.
+ * This is in response to the data corruption bug from test/fheap.c
+ * when page buffering + page aggregation strategy are used.
+ * Note: Large raw/metadata page bypasses the page buffer.
+ * Note: Update of raw data page (large or small sized) is handled
+ * by the PB cache
+ */
+ if(udata->f->shared->pb_ptr != NULL)
if(H5PB_remove_entry(udata->f->shared, (*sect1)->sect_info.addr) < 0)
- HGOTO_ERROR(H5E_RESOURCE, H5E_CANTFREE, FAIL, "can't free merged section")
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTFREE, FAIL, "can't free merged section from page buffer")
if(H5MF__sect_free((H5FS_section_info_t *)(*sect1)) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't free section node")
@@ -819,12 +822,21 @@ H5MF__sect_large_can_merge(const H5FS_section_info_t *_sect1,
const H5MF_free_section_t *sect2 = (const H5MF_free_section_t *)_sect2; /* File free section */
htri_t ret_value = FALSE; /* Return value */
- FUNC_ENTER_STATIC_NOERR
+ FUNC_ENTER_STATIC
/* Check arguments. */
HDassert(sect1);
HDassert(sect2);
HDassert(sect1->sect_info.type == sect2->sect_info.type); /* Checks "MERGE_SYM" flag */
+ if (!H5F_addr_lt(sect1->sect_info.addr, sect2->sect_info.addr)) {
+ fprintf(stderr, "%s.%d: sect1->sect_info.addr %" PRIuHADDR
+ ", sect2->sect_info.addr %" PRIuHADDR "\n", __func__, __LINE__,
+ sect1->sect_info.addr, sect2->sect_info.addr);
+ fprintf(stderr, "%s.%d: sect1->sect_info.size %" PRIuHSIZE
+ ", sect2->sect_info.size %" PRIuHSIZE "\n", __func__, __LINE__,
+ sect1->sect_info.size, sect2->sect_info.size);
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't merge")
+ }
HDassert(H5F_addr_lt(sect1->sect_info.addr, sect2->sect_info.addr));
ret_value = H5F_addr_eq(sect1->sect_info.addr + sect1->sect_info.size, sect2->sect_info.addr);
@@ -833,6 +845,7 @@ H5MF__sect_large_can_merge(const H5FS_section_info_t *_sect1,
HDfprintf(stderr, "%s: Leaving: ret_value = %t\n", FUNC, ret_value);
#endif /* H5MF_ALLOC_DEBUG_MORE */
+done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5MF__sect_large_can_merge() */
diff --git a/src/H5MV.c b/src/H5MV.c
new file mode 100644
index 0000000..5b73b9a
--- /dev/null
+++ b/src/H5MV.c
@@ -0,0 +1,721 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5MV.c
+ *
+ * Purpose: Free-space manager for VFD SWMR's metadata file
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/****************/
+/* Module Setup */
+/****************/
+
+#define H5F_FRIEND /*suppress error about including H5Fpkg */
+#define H5FS_FRIEND /*suppress error about including H5Fpkg */
+#include "H5MVmodule.h" /* This source code file is part of the H5MV module */
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5Fpkg.h" /* File access */
+#include "H5FSpkg.h" /* File free space */
+#include "H5Iprivate.h" /* IDs */
+#include "H5MVpkg.h" /* File memory management */
+#include "H5VMprivate.h" /* Vectors and arrays */
+
+#include "hlog.h"
+
+/****************/
+/* Local Macros */
+/****************/
+
+/* Define this to display debugging information for VFD SWMR */
+/* #define H5MV_VFD_SWMR_DEBUG */
+
+#define H5MV_FSPACE_SHRINK 80 /* Percent of "normal" size to shrink serialized free space size */
+#define H5MV_FSPACE_EXPAND 120 /* Percent of "normal" size to expand serialized free space size */
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+/* User data for section info iterator callback for iterating over free space sections */
+typedef struct {
+ H5F_sect_info_t *sects; /* section info to be retrieved */
+ size_t sect_count; /* # of sections requested */
+ size_t sect_idx; /* the current count of sections */
+} H5MV_sect_iter_ud_t;
+
+
+/********************/
+/* Package Typedefs */
+/********************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static haddr_t H5MV__extend_md(H5F_shared_t *, hsize_t);
+
+/* Space allocation routines */
+H5_DLL haddr_t H5MV__alloc_md(H5F_shared_t *, hsize_t);
+H5_DLL htri_t H5MV__try_extend_md(H5F_shared_t *, haddr_t, hsize_t);
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+/* Package initialization variable */
+hbool_t H5_PKG_INIT_VAR = FALSE;
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+HLOG_OUTLET_SHORT_DEFN(h5mv, all);
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__create()
+ *
+ * Purpose: Create free space manager for the metadata file by creating
+ * a free-space structure
+ *
+ * Return: Success: non-negative
+ * Failure: negative
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5MV__create(H5F_t *f)
+{
+ H5F_shared_t *shared = f->shared;
+ /* Free space section classes implemented for file */
+ const H5FS_section_class_t *classes[] = { H5MV_FSPACE_SECT_CLS_SIMPLE };
+ H5FS_create_t fs_create; /* Free space creation parameters */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /*
+ * Check arguments.
+ */
+ HDassert(shared->fs_state_md == H5F_FS_STATE_CLOSED);
+
+ /* Set the free space creation parameters */
+ fs_create.client = H5FS_CLIENT_MD_VFD_ID;
+ fs_create.shrink_percent = H5MV_FSPACE_SHRINK;
+ fs_create.expand_percent = H5MV_FSPACE_EXPAND;
+ fs_create.max_sect_addr = 1 + H5VM_log2_gen((uint64_t)shared->maxaddr);
+ fs_create.max_sect_size = shared->maxaddr;
+
+ if(NULL == (shared->fs_man_md = H5FS_create(f, NULL, &fs_create, NELMTS(classes), classes, f, shared->fs_page_size, shared->fs_page_size)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, FAIL, "can't initialize free space info")
+
+ /* Set the state for the free space manager to "open", if it is now */
+ if(shared->fs_man_md)
+ shared->fs_state_md = H5F_FS_STATE_OPEN;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV__create() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV_close
+ *
+ * Purpose: Close the free space manager for the metadata file
+ *
+ * Return: Success: non-negative
+ * Failure: negative
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5MV_close(H5F_t *f)
+{
+ H5F_shared_t *shared = f->shared;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /*
+ * Check arguments.
+ */
+ HDassert(shared);
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: Trying to close free space manager\n", FUNC);
+#endif
+
+ /* Close an existing free space structure for the file */
+ if(shared->fs_man_md) {
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: Going to close free space manager\n", FUNC);
+#endif
+ HDassert(shared->fs_state_md != H5F_FS_STATE_CLOSED);
+
+ if(H5FS_close(f, shared->fs_man_md) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't release free space info")
+ }
+
+ shared->fs_man_md = NULL;
+ shared->fs_state_md = H5F_FS_STATE_CLOSED;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV_close() */
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__find_sect
+ *
+ * Purpose: To find a section from the specified free-space manager
+ * to fulfill the request.
+ * If found, re-add the left-over space back to the manager.
+ *
+ * Return: TRUE if a section is found to fulfill the request
+ * FALSE if not
+ *
+ *-------------------------------------------------------------------------
+ */
+htri_t
+H5MV__find_sect(H5F_t *f, hsize_t size, H5FS_t *fspace, haddr_t *addr)
+{
+ H5MV_free_section_t *node; /* Free space section pointer */
+ htri_t ret_value = FAIL; /* Whether an existing free list node was found */
+
+ FUNC_ENTER_PACKAGE
+
+ HDassert(f);
+ HDassert(fspace);
+
+ /* Try to get a section from the free space manager */
+ if((ret_value = H5FS_sect_find(f, fspace, size, (H5FS_section_info_t **)&node)) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "error locating free space in file")
+
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: section found = %t\n", FUNC, ret_value);
+#endif
+
+ /* Check for actually finding section */
+ if(ret_value) {
+ /* Sanity check */
+ HDassert(node);
+
+ /* Retrieve return value */
+ if(addr)
+ *addr = node->sect_info.addr;
+
+ /* Check for eliminating the section */
+ if(node->sect_info.size == size) {
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: freeing node\n", FUNC);
+#endif
+
+ /* Free section node */
+ if(H5MV__sect_free(&node->sect_info) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't free simple section node")
+ } /* end if */
+ else {
+ /* Adjust information for section */
+ node->sect_info.addr += size;
+ node->sect_info.size -= size;
+
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: adding node, node->sect_info.addr = %a, node->sect_info.size = %Hu\n", FUNC, node->sect_info.addr, node->sect_info.size);
+#endif
+
+ /* Re-add the section to the free-space manager */
+ if(H5FS_sect_add(f, fspace, &node->sect_info, H5FS_ADD_RETURNED_SPACE, f) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINSERT, FAIL, "can't re-add section to file free space")
+ } /* end else */
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV__find_sect() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV_alloc
+ *
+ * Purpose: Allocate SIZE bytes of file memory and return the relative
+ * address where that contiguous chunk of file memory exists.
+ *
+ * Return: Success: The file address of new chunk.
+ * Failure: HADDR_UNDEF
+ *
+ *-------------------------------------------------------------------------
+ */
+haddr_t
+H5MV_alloc(H5F_t *f, hsize_t size)
+{
+ H5F_shared_t *shared = f->shared;
+ haddr_t eoa; /* EOA for the file */
+ hsize_t frag_size = 0; /* Fragment size */
+ hsize_t misalign_size = 0; /* Mis-aligned size */
+ H5MV_free_section_t *node = NULL; /* Free space section pointer */
+ haddr_t ret_value = HADDR_UNDEF; /* Return value */
+
+ FUNC_ENTER_NOAPI(HADDR_UNDEF)
+ hlog_fast(h5mv, "%s: enter size %" PRIuHSIZE, __func__, size);
+
+ /* check arguments */
+ HDassert(shared->vfd_swmr_md_fd >= 0);
+ HDassert(size > 0);
+
+ /* Search for large enough space in the free space manager */
+ if(shared->fs_man_md != NULL) {
+ if(H5MV__find_sect(f, size, shared->fs_man_md, &ret_value) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, HADDR_UNDEF, "error locating a node")
+ }
+
+ /* If no space is found from the free-space manager or no free-space manager, extend md's EOF */
+ if(!H5F_addr_defined(ret_value)) {
+
+ /* Get the EOA for the metadata file */
+ if(HADDR_UNDEF == (eoa = H5MV_get_vfd_swmr_md_eoa(shared)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGET, HADDR_UNDEF, "Unable to get eoa for VFD SWMR metadata file")
+
+ /* If EOA is mis-aligned, calculate the fragment size */
+ if(H5F_addr_gt(eoa, 0) && (misalign_size = eoa % shared->fs_page_size))
+ frag_size = shared->fs_page_size - misalign_size;
+
+ /* Allocate from end of file */
+ if(HADDR_UNDEF == (ret_value = H5MV__alloc_md(shared, size + frag_size)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, HADDR_UNDEF, "allocation failed")
+
+ /* If there is a mis-aligned fragment at EOA */
+ if(frag_size) {
+ /* Start up the free-space manager if not so */
+ if(shared->fs_man_md == NULL) {
+ if(H5MV__create(f) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, HADDR_UNDEF, "can't initialize free space manager")
+ }
+ HDassert(shared->fs_man_md);
+
+ /* Create the free-space section for the fragment */
+ if(NULL == (node = H5MV__sect_new(eoa, frag_size)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, HADDR_UNDEF, "can't initialize free space section")
+
+ /* Add the section */
+ if(H5FS_sect_add(f, shared->fs_man_md, &node->sect_info, H5FS_ADD_RETURNED_SPACE, f) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINSERT, HADDR_UNDEF, "can't re-add section to file free space")
+
+ node = NULL;
+ }
+ ret_value += frag_size;
+
+ } /* end if */
+
+ HDassert(H5F_addr_defined(ret_value));
+
+done:
+ hlog_fast(h5mv, "%s: leave addr %" PRIuHADDR " size %" PRIuHSIZE,
+ __func__, ret_value, size);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV_alloc() */
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV_free
+ *
+ * Purpose: Frees part of a file, making that part of the file
+ * available for reuse.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5MV_free(H5F_t *f, haddr_t addr, hsize_t size)
+{
+ H5F_shared_t *shared = f->shared;
+ H5MV_free_section_t *node = NULL; /* Free space section pointer */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ hlog_fast(h5mv, "%s: enter addr %" PRIuHADDR " size %" PRIuHSIZE,
+ __func__, addr, size);
+
+ /* check arguments */
+ HDassert(f);
+ if(!H5F_addr_defined(addr) || 0 == size)
+ HGOTO_DONE(SUCCEED)
+ HDassert(addr != 0);
+
+
+ /* Check if the free space manager for the file has been initialized */
+ if(shared->fs_man_md == NULL) {
+ /* If there's no free space manager for objects of this type,
+ * see if we can avoid creating one by checking if the freed
+ * space is at the end of the file
+ */
+ htri_t status; /* "can absorb" status for section into */
+
+ hlog_fast(h5mv, "%s: trying to avoid starting up free space manager",
+ __func__);
+
+ /* Try to shrink the file */
+ if((status = H5MV_try_shrink(f, addr, size)) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTMERGE, FAIL, "can't check for absorbing block")
+ else if(status > 0)
+ HGOTO_DONE(SUCCEED)
+
+ /* If we are deleting the free space manager, leave now, to avoid
+ * [re-]starting it: dropping free space section on the floor.
+ */
+ if(shared->fs_state_md == H5F_FS_STATE_DELETING) {
+ hlog_fast(h5mv, "%s: dropping addr %" PRIuHADDR
+ " size %" PRIuHSIZE " on the floor!", __func__, addr, size);
+ HGOTO_DONE(SUCCEED)
+ }
+
+ /* There's either already a free space manager, or the freed
+ * space isn't at the end of the file, so start up (or create)
+ * the file space manager
+ */
+ if(H5MV__create(f) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, FAIL, "can't initialize free space manager")
+ }
+
+ /* Create the free-space section for the freed section */
+ if(NULL == (node = H5MV__sect_new(addr, size)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, FAIL, "can't initialize free space section")
+
+ HDassert(shared->fs_man_md);
+
+ hlog_fast(h5mv, "%s: before H5FS_sect_add, addr %" PRIuHADDR
+ " size %" PRIuHSIZE, __func__, addr, size);
+
+ /* Add the section */
+ if(H5FS_sect_add(f, shared->fs_man_md, &node->sect_info, H5FS_ADD_RETURNED_SPACE, f) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINSERT, FAIL, "can't re-add section to file free space")
+
+ node = NULL;
+
+ hlog_fast(h5mv, "%s: after H5FS_sect_add", __func__);
+
+done:
+ /* Release section node, if allocated and not added to section list or merged */
+ if(node)
+ if(H5MV__sect_free(&node->sect_info) < 0)
+ HDONE_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't free simple section node")
+
+ hlog_fast(h5mv, "%s: leave %d", __func__, ret_value);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV_free() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV_try_extend
+ *
+ * Purpose: Extend a block at EOA in the file if possible.
+ *
+ * Return: Success: TRUE(1) - Block was extended
+ * FALSE(0) - Block could not be extended
+ * Failure: FAIL
+ *
+ *-------------------------------------------------------------------------
+ */
+htri_t
+H5MV_try_extend(H5F_t *f, haddr_t addr, hsize_t size, hsize_t extra_requested)
+{
+ H5F_shared_t *shared = f->shared;
+ haddr_t end; /* End of block to extend */
+ htri_t ret_value = FALSE; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: Entering: addr = %a, size = %Hu, extra_requested = %Hu\n", FUNC, addr, size, extra_requested);
+#endif
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(H5F_SHARED_INTENT(shared) & H5F_ACC_RDWR);
+
+ /* Compute end of block to extend */
+ end = addr + size;
+
+ /* Try extending the block at EOA */
+ if((ret_value = H5MV__try_extend_md(shared, end, extra_requested)) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTEXTEND, FAIL, "error extending file")
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: extended = %t\n", FUNC, ret_value);
+#endif
+
+ /* If no extension so far, try to extend into a free-space section */
+ if(ret_value == FALSE) {
+
+ /* Try to extend the block into a free-space section */
+ if(shared->fs_man_md) {
+ if((ret_value = H5FS_sect_try_extend(f, shared->fs_man_md, addr, size, extra_requested, H5FS_ADD_RETURNED_SPACE, NULL)) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTEXTEND, FAIL, "error extending block in free space manager")
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: Try to H5FS_sect_try_extend = %t\n", FUNC, ret_value);
+#endif
+ } /* end if */
+
+ } /* end if */
+
+done:
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: Leaving: ret_value = %t\n", FUNC, ret_value);
+#endif
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV_try_extend() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV_try_shrink
+ *
+ * Purpose: Try to shrink the size of a file with a block
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ *-------------------------------------------------------------------------
+ */
+htri_t
+H5MV_try_shrink(H5F_t *f, haddr_t addr, hsize_t size)
+{
+ H5F_shared_t *shared = f->shared;
+ H5MV_free_section_t *node = NULL; /* Free space section pointer */
+ htri_t ret_value = FALSE; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: Entering - addr = %a, size = %Hu\n", FUNC, addr, size);
+#endif
+
+ /* check arguments */
+ HDassert(shared->lf);
+ HDassert(H5F_addr_defined(addr));
+ HDassert(size > 0);
+
+ /* Create free-space section for block */
+ if(NULL == (node = H5MV__sect_new(addr, size)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, FAIL, "can't initialize free space section")
+
+ /* Check if the block can shrink the container */
+ if((ret_value = H5MV__sect_can_shrink(&node->sect_info, f)) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTMERGE, FAIL, "can't check if section can shrink container")
+ else if(ret_value > 0) {
+ /* Shrink or absorb the section */
+ if(H5MV__sect_shrink((H5FS_section_info_t **)&node, f) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTSHRINK, FAIL, "can't shrink container")
+ } /* end if */
+
+done:
+ /* Free section node allocated */
+ if(node && H5MV__sect_free(&node->sect_info) < 0)
+ HDONE_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't free simple section node")
+
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: Leaving, ret_value = %d\n", FUNC, ret_value);
+#endif
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV_try_shrink() */
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__free_md
+ *
+ * Purpose: Release space at the end of the metadata file's allocated space
+ *
+ * Return: Success: Non-negative
+ * Failure: Negative
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5MV__free_md(H5F_shared_t *shared, haddr_t addr, hsize_t size)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check args */
+ HDassert(shared);
+ HDassert(size > 0);
+
+ /* Sanity checking */
+ if(!H5F_addr_defined(addr))
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "invalid file offset")
+
+ /* XXX what's maxaddr used here for? */
+ if(addr > shared->maxaddr || H5F_addr_overflow(addr, size) || (addr + size) > shared->maxaddr)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "invalid file free space region to free")
+
+ /* Check if this free block is at the end of file allocated space.
+ * Truncate it if this is true.
+ */
+ if(shared->vfd_swmr_md_eoa == (addr + size))
+ shared->vfd_swmr_md_eoa = addr;
+ else {
+ /* leak memory */
+#ifdef H5MV_VFD_SWMR_DEBUG
+HDfprintf(stderr, "%s: LEAKED MEMORY!!! addr = %a, size = %Hu\n", FUNC, addr, size);
+#endif
+ } /* end else */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV__free_md() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__alloc_md
+ *
+ * Purpose: Allocate space at the end of the metadata file
+ *
+ * Return: Success: Non-negative
+ * Failure: Negative
+ *
+ *-------------------------------------------------------------------------
+ */
+haddr_t
+H5MV__alloc_md(H5F_shared_t *shared, hsize_t size)
+{
+ haddr_t ret_value = HADDR_UNDEF; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* check args */
+ HDassert(shared);
+ HDassert(size > 0);
+
+ /* Extend the EOA space of the metadata file */
+ ret_value = H5MV__extend_md(shared, size);
+
+ if(!H5F_addr_defined(ret_value))
+ HGOTO_ERROR(H5E_VFL, H5E_NOSPACE, HADDR_UNDEF, "driver eoa update request failed")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* end H5MV__alloc_md() */
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__try_extend_md
+ *
+ * Purpose: Try to extend a block at the end of the metadata file, if possible.
+ *
+ * Return: Success: Non-negative
+ * Failure: Negative
+ *
+ *-------------------------------------------------------------------------
+ */
+htri_t
+H5MV__try_extend_md(H5F_shared_t *shared, haddr_t blk_end, hsize_t extra_requested)
+{
+ htri_t ret_value = FALSE; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* check args */
+ HDassert(shared);
+ HDassert(extra_requested > 0);
+
+ /* Check if the block is exactly at the end of the file */
+ if(H5F_addr_eq(blk_end, shared->vfd_swmr_md_eoa)) {
+
+ /* Extend the EOA space of the metadata file */
+ if(HADDR_UNDEF == H5MV__extend_md(shared, extra_requested))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTEXTEND, FAIL, "driver extend request failed")
+
+ /* Indicate success */
+ ret_value = TRUE;
+ }
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV__try_extend_md() */
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__extend_md
+ *
+ * Purpose: Extend the EOA space of the metadata file.
+ *
+ * Return: Success: Non-negative
+ * Failure: Negative
+ *
+ *-------------------------------------------------------------------------
+ */
+static haddr_t
+H5MV__extend_md(H5F_shared_t *shared, hsize_t size)
+{
+ haddr_t eoa;
+ haddr_t ret_value = HADDR_UNDEF; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* Get current end-of-allocated space address */
+ eoa = shared->vfd_swmr_md_eoa;
+
+ /* Check for overflow when extending */
+ /* XXX why does this check maxaddr? That should have no bearing on
+ * the metadata file.
+ */
+ if(H5F_addr_overflow(eoa, size) || (eoa + size) > shared->maxaddr)
+ HGOTO_ERROR(H5E_FILE, H5E_NOSPACE, HADDR_UNDEF, "file allocation request failed")
+
+ /* Set the address to return */
+ ret_value = eoa;
+
+ /* Extend the end-of-allocated space address */
+ shared->vfd_swmr_md_eoa += size;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV_get_vfd_swmr_md_eoa
+ *
+ * Purpose: Quick and dirty routine to retrieve the EOA for the metadata file
+ * (Mainly added to stop non-file routines from poking about in the
+ * H5F_t data structure)
+ *
+ * Return: The EOA for the metadata file
+ *-------------------------------------------------------------------------
+ */
+haddr_t
+H5MV_get_vfd_swmr_md_eoa(const H5F_shared_t *shared)
+{
+ /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ HDassert(shared->vfd_swmr);
+
+ FUNC_LEAVE_NOAPI(shared->vfd_swmr_md_eoa)
+}
diff --git a/src/H5MVmodule.h b/src/H5MVmodule.h
new file mode 100644
index 0000000..5a95767
--- /dev/null
+++ b/src/H5MVmodule.h
@@ -0,0 +1,33 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by The HDF Group. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the COPYING file, which can be found at the root of the source code *
+ * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. *
+ * If you do not have access to either file, you may request a copy from *
+ * help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Programmer: Quincey Koziol <koziol@hdfgroup.org>
+ * Saturday, September 12, 2015
+ *
+ * Purpose: This file contains declarations which define macros for the
+ * H5MV package. Including this header means that the source file
+ * is part of the H5MV package.
+ */
+#ifndef _H5MVmodule_H
+#define _H5MVmodule_H
+
+/* Define the proper control macros for the generic FUNC_ENTER/LEAVE and error
+ * reporting macros.
+ */
+#define H5MV_MODULE
+#define H5_MY_PKG H5MV
+#define H5_MY_PKG_ERR H5E_RESOURCE
+#define H5_MY_PKG_INIT NO
+
+#endif /* _H5MVmodule_H */
+
diff --git a/src/H5MVpkg.h b/src/H5MVpkg.h
new file mode 100644
index 0000000..cb29879
--- /dev/null
+++ b/src/H5MVpkg.h
@@ -0,0 +1,85 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Programmer: Quincey Koziol <koziol@hdfgroup.org>
+ * Tuesday, January 8, 2008
+ *
+ * Purpose: This file contains declarations which are visible only within
+ * the H5MV package. Source files outside the H5MV package should
+ * include H5MVprivate.h instead.
+ */
+#if !(defined H5MV_FRIEND || defined H5MV_MODULE)
+#error "Do not include this file outside the H5MV package!"
+#endif
+
+#ifndef _H5MVpkg_H
+#define _H5MVpkg_H
+
+/* Get package's private header */
+#include "H5MVprivate.h"
+
+/* Other private headers needed by this file */
+#include "H5FSprivate.h" /* File free space */
+
+
+/**************************/
+/* Package Private Macros */
+/**************************/
+
+/* Define this to display information about file allocations */
+/* #define H5MV_VFD_SWMR_DEBUG */
+
+/* Free-space section types for file */
+/* (values stored in free space data structures in file) */
+#define H5MV_FSPACE_SECT_SIMPLE 0 /* For non-paged aggregation: section is a range of actual bytes in file */
+
+/****************************/
+/* Package Private Typedefs */
+/****************************/
+
+/* File free space section info */
+typedef struct H5MV_free_section_t {
+ H5FS_section_info_t sect_info; /* Free space section information (must be first in struct) */
+} H5MV_free_section_t;
+
+/*****************************/
+/* Package Private Variables */
+/*****************************/
+
+/* H5MF single section inherits serializable properties from H5FS_section_class_t */
+H5_DLLVAR H5FS_section_class_t H5MV_FSPACE_SECT_CLS_SIMPLE[1];
+
+
+/******************************/
+/* Package Private Prototypes */
+/******************************/
+
+H5_DLL htri_t H5MV__find_sect(H5F_t *f, hsize_t size, H5FS_t *fspace, haddr_t *addr);
+H5_DLL herr_t H5MV__create(H5F_t *f);
+
+/* free-space section routines */
+H5_DLL H5MV_free_section_t *H5MV__sect_new(haddr_t sect_off, hsize_t sect_size);
+H5_DLL herr_t H5MV__sect_free(H5FS_section_info_t *sect);
+H5_DLL htri_t H5MV__sect_can_shrink(const H5FS_section_info_t *_sect, void *udata);
+H5_DLL herr_t H5MV__sect_shrink(H5FS_section_info_t **_sect, void *udata);
+H5_DLL haddr_t H5MV_get_vfd_swmr_md_eoa(const H5F_shared_t *);
+H5_DLL herr_t H5MV__free_md(H5F_shared_t *, haddr_t, hsize_t);
+
+
+/* Testing routines */
+#ifdef H5MF_TESTING
+#endif /* H5MF_TESTING */
+
+#endif /* _H5MFpkg_H */
+
diff --git a/src/H5MVprivate.h b/src/H5MVprivate.h
new file mode 100644
index 0000000..2c0e95a
--- /dev/null
+++ b/src/H5MVprivate.h
@@ -0,0 +1,58 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5MVprivate.h
+ *
+ * Purpose: Private header file for file memory management.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _H5MVprivate_H
+#define _H5MVprivate_H
+
+/* Private headers needed by this file */
+#include "H5Fprivate.h" /* File access */
+
+/**************************/
+/* Library Private Macros */
+/**************************/
+
+
+/****************************/
+/* Library Private Typedefs */
+/****************************/
+
+
+/*****************************/
+/* Library-private Variables */
+/*****************************/
+
+
+/***************************************/
+/* Library-private Function Prototypes */
+/***************************************/
+
+/* File space manager routines */
+
+H5_DLL herr_t H5MV_close(H5F_t *f);
+
+/* File space allocation routines */
+H5_DLL haddr_t H5MV_alloc(H5F_t *f, hsize_t size);
+H5_DLL herr_t H5MV_free(H5F_t *f, haddr_t addr, hsize_t size);
+H5_DLL herr_t H5MV_try_extend(H5F_t *f, haddr_t addr, hsize_t size, hsize_t extra_requested);
+H5_DLL htri_t H5MV_try_shrink(H5F_t *f, haddr_t addr, hsize_t size);
+
+#endif /* end _H5MVprivate_H */
+
diff --git a/src/H5MVsection.c b/src/H5MVsection.c
new file mode 100644
index 0000000..81f1a00
--- /dev/null
+++ b/src/H5MVsection.c
@@ -0,0 +1,395 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Programmer: Quincey Koziol <koziol@hdfgroup.org>
+ * Tuesday, January 8, 2008
+ *
+ * Purpose: Free space section callbacks for file.
+ *
+ */
+
+/****************/
+/* Module Setup */
+/****************/
+
+#define H5F_FRIEND /*suppress error about including H5Fpkg */
+#include "H5MVmodule.h" /* This source code file is part of the H5MF module */
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5Fpkg.h" /* File access */
+#include "H5MVpkg.h" /* File memory management */
+
+
+/****************/
+/* Local Macros */
+/****************/
+
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+
+/********************/
+/* Package Typedefs */
+/********************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+/* 'simple' section callbacks */
+static htri_t H5MV__sect_can_merge(const H5FS_section_info_t *sect1,
+ const H5FS_section_info_t *sect2, void *udata);
+static herr_t H5MV__sect_merge(H5FS_section_info_t **sect1,
+ H5FS_section_info_t *sect2, void *udata);
+static herr_t H5MV__sect_valid(const H5FS_section_class_t *cls,
+ const H5FS_section_info_t *sect);
+static H5FS_section_info_t *H5MV__sect_split(H5FS_section_info_t *sect,
+ hsize_t frag_size);
+
+
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+/* Class info for "simple" free space sections */
+H5FS_section_class_t H5MV_FSPACE_SECT_CLS_SIMPLE[1] = {{
+ /* Class variables */
+ H5MV_FSPACE_SECT_SIMPLE, /* Section type */
+ 0, /* Extra serialized size */
+ H5FS_CLS_MERGE_SYM | H5FS_CLS_ADJUST_OK | H5FS_CLS_GHOST_OBJ, /* Class flags */
+ NULL, /* Class private info */
+
+ /* Class methods */
+ NULL, /* Initialize section class */
+ NULL, /* Terminate section class */
+
+ /* Object methods */
+ NULL, /* Add section */
+ NULL, /* Serialize section */
+ NULL, /* Deserialize section */
+ H5MV__sect_can_merge, /* Can sections merge? */
+ H5MV__sect_merge, /* Merge sections */
+ H5MV__sect_can_shrink, /* Can section shrink container?*/
+ H5MV__sect_shrink, /* Shrink container w/section */
+ H5MV__sect_free, /* Free section */
+ H5MV__sect_valid, /* Check validity of section */
+ H5MV__sect_split, /* Split section node for alignment */
+ NULL, /* Dump debugging for section */
+}};
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+/* Declare a free list to manage the H5MF_free_section_t struct */
+H5FL_DEFINE(H5MV_free_section_t);
+
+/*
+ * "simple" section callbacks
+ */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__sect_new
+ *
+ * Purpose: Create a new section and return it to the caller
+ *
+ * Return: Pointer to new section on success/NULL on failure
+ *
+ *-------------------------------------------------------------------------
+ */
+H5MV_free_section_t *
+H5MV__sect_new(haddr_t sect_off, hsize_t sect_size)
+{
+ H5MV_free_section_t *sect; /* 'Simple' free space section to add */
+ H5MV_free_section_t *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(sect_size);
+
+ /* Create free space section node */
+ if(NULL == (sect = H5FL_MALLOC(H5MV_free_section_t)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for free section node")
+
+ /* Set the information passed in */
+ sect->sect_info.addr = sect_off;
+ sect->sect_info.size = sect_size;
+
+ /* Set the section's class & state */
+ sect->sect_info.type = H5MV_FSPACE_SECT_SIMPLE;
+ sect->sect_info.state = H5FS_SECT_LIVE;
+
+ /* Set return value */
+ ret_value = sect;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5MV__sect_new() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__sect_free
+ *
+ * Purpose: Free a 'simple' section node
+ *
+ * Return: Success: non-negative
+ * Failure: negative
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5MV__sect_free(H5FS_section_info_t *_sect)
+{
+ H5MV_free_section_t *sect = (H5MV_free_section_t *)_sect; /* File free section */
+
+ FUNC_ENTER_PACKAGE_NOERR
+
+ /* Check arguments. */
+ HDassert(sect);
+
+ /* Release the section */
+ sect = H5FL_FREE(H5MV_free_section_t, sect);
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* H5MV__sect_free() */
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__sect_can_merge
+ *
+ * Purpose: Can two sections of this type merge?
+ *
+ * Note: Second section must be "after" first section
+ *
+ * Return: Success: non-negative (TRUE/FALSE)
+ * Failure: negative
+ *
+ *-------------------------------------------------------------------------
+ */
+static htri_t
+H5MV__sect_can_merge(const H5FS_section_info_t *_sect1,
+ const H5FS_section_info_t *_sect2, void H5_ATTR_UNUSED *_udata)
+{
+ const H5MV_free_section_t *sect1 = (const H5MV_free_section_t *)_sect1; /* File free section */
+ const H5MV_free_section_t *sect2 = (const H5MV_free_section_t *)_sect2; /* File free section */
+ htri_t ret_value = FAIL; /* Return value */
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Check arguments. */
+ HDassert(sect1);
+ HDassert(sect2);
+ HDassert(sect1->sect_info.type == sect2->sect_info.type); /* Checks "MERGE_SYM" flag */
+ HDassert(H5F_addr_lt(sect1->sect_info.addr, sect2->sect_info.addr));
+
+ /* Check if second section adjoins first section */
+ ret_value = H5F_addr_eq(sect1->sect_info.addr + sect1->sect_info.size, sect2->sect_info.addr);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5MV__sect_can_merge() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__sect_merge
+ *
+ * Purpose: Merge two sections of this type
+ *
+ * Note: Second section always merges into first node
+ *
+ * Return: Success: non-negative
+ * Failure: negative
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5MV__sect_merge(H5FS_section_info_t **_sect1, H5FS_section_info_t *_sect2,
+ void H5_ATTR_UNUSED *_udata)
+{
+ H5MV_free_section_t **sect1 = (H5MV_free_section_t **)_sect1; /* File free section */
+ H5MV_free_section_t *sect2 = (H5MV_free_section_t *)_sect2; /* File free section */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Check arguments. */
+ HDassert(sect1);
+ HDassert((*sect1)->sect_info.type == H5MV_FSPACE_SECT_SIMPLE);
+ HDassert(sect2);
+ HDassert(sect2->sect_info.type == H5MV_FSPACE_SECT_SIMPLE);
+ HDassert(H5F_addr_eq((*sect1)->sect_info.addr + (*sect1)->sect_info.size, sect2->sect_info.addr));
+
+ /* Add second section's size to first section */
+ (*sect1)->sect_info.size += sect2->sect_info.size;
+
+ /* Get rid of second section */
+ if(H5MV__sect_free((H5FS_section_info_t *)sect2) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't free section node")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5MV__sect_merge() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__sect_can_shrink
+ *
+ * Purpose: Can this section shrink the container?
+ *
+ * Return: Success: non-negative (TRUE/FALSE)
+ * Failure: negative
+ *
+ *-------------------------------------------------------------------------
+ */
+htri_t
+H5MV__sect_can_shrink(const H5FS_section_info_t *_sect, void *_udata)
+{
+ const H5MV_free_section_t *sect = (const H5MV_free_section_t *)_sect; /* File free section */
+ H5F_t *f = (H5F_t *)_udata;
+ H5F_shared_t *shared = f->shared;
+ haddr_t eoa; /* End of address space in the file */
+ haddr_t end; /* End of section to extend */
+ htri_t ret_value = FALSE; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Check arguments. */
+ HDassert(sect);
+
+ /* Retrieve the end oa the file's address space */
+ if(HADDR_UNDEF == (eoa = H5MV_get_vfd_swmr_md_eoa(shared)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGET, FAIL, "get_eoa request for VFD SWMR metadata file failed")
+
+ /* Compute address of end of section to check */
+ end = sect->sect_info.addr + sect->sect_info.size;
+
+ /* Check if the section is exactly at the end of the allocated space in the file */
+ if(H5F_addr_eq(end, eoa))
+ /* Indicate shrinking can occur */
+ ret_value = TRUE;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5MV__sect_can_shrink() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__sect_shrink
+ *
+ * Purpose: Shrink container with section
+ *
+ * Return: Success: non-negative
+ * Failure: negative
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5MV__sect_shrink(H5FS_section_info_t **_sect, void *_udata)
+{
+ H5F_t *f = (H5F_t *)_udata;
+ H5F_shared_t *shared = f->shared;
+ H5MV_free_section_t **sect = (H5MV_free_section_t **)_sect; /* File free section */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Check arguments. */
+ HDassert(sect);
+ HDassert(H5F_SHARED_INTENT(shared) & H5F_ACC_RDWR);
+
+ /* Release section's space at EOA */
+ if(H5MV__free_md(shared, (*sect)->sect_info.addr, (*sect)->sect_info.size) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTFREE, FAIL, "free request for VFD SWMR metadata file failed")
+
+ /* Free the section */
+ if(H5MV__sect_free(&(*sect)->sect_info) < 0)
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "can't free simple section node")
+
+ /* Mark section as freed, for free space manager */
+ *sect = NULL;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5MV__sect_shrink() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__sect_valid
+ *
+ * Purpose: Check the validity of a section
+ *
+ * Return: Success: non-negative
+ * Failure: negative
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5MV__sect_valid(const H5FS_section_class_t H5_ATTR_UNUSED *cls, const H5FS_section_info_t *_sect)
+{
+ const H5MV_free_section_t *sect = (const H5MV_free_section_t *)_sect; /* File free section */
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Check arguments. */
+ HDassert(sect);
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* H5MV__sect_valid() */
+
+/*-------------------------------------------------------------------------
+ * Function: H5MV__sect_split
+ *
+ * Purpose: Split SECT into 2 sections: fragment for alignment & the aligned section
+ * SECT's addr and size are updated to point to the aligned section
+ *
+ * Return: Success: the fragment for aligning sect
+ * Failure: null
+ *
+ *-------------------------------------------------------------------------
+ */
+static H5FS_section_info_t *
+H5MV__sect_split(H5FS_section_info_t *sect, hsize_t frag_size)
+{
+ H5MV_free_section_t *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Allocate space for new section */
+ if(NULL == (ret_value = H5MV__sect_new(sect->addr, frag_size)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "can't initialize free space section")
+
+ /* Set new section's info */
+ sect->addr += frag_size;
+ sect->size -= frag_size;
+
+done:
+ FUNC_LEAVE_NOAPI((H5FS_section_info_t *)ret_value)
+} /* end H5MV__sect_split() */
diff --git a/src/H5Ocache.c b/src/H5Ocache.c
index 45c55fd..53ae461 100644
--- a/src/H5Ocache.c
+++ b/src/H5Ocache.c
@@ -117,6 +117,7 @@ const H5AC_class_t H5AC_OHDR[1] = {{
H5O__cache_notify, /* 'notify' callback */
H5O__cache_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* H5O object header chunk inherits cache-like properties from H5AC */
@@ -135,6 +136,7 @@ const H5AC_class_t H5AC_OHDR_CHK[1] = {{
H5O__cache_chk_notify, /* 'notify' callback */
H5O__cache_chk_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* VFD SWMR 'refresh' callback */
}};
/* Declare external the free list for H5O_unknown_t's */
diff --git a/src/H5Oflush.c b/src/H5Oflush.c
index 898184b..eeed75e 100644
--- a/src/H5Oflush.c
+++ b/src/H5Oflush.c
@@ -28,6 +28,7 @@
#include "H5Omodule.h" /* This source code file is part of the H5O module */
#define H5T_FRIEND /* Suppress error about including H5Tpkg */
+#define H5D_FRIEND /* Suppress error about including H5Dpkg */
/***********/
/* Headers */
@@ -40,6 +41,7 @@
#include "H5Fprivate.h" /* Files */
#include "H5Gprivate.h" /* Groups */
#include "H5Iprivate.h" /* IDs */
+#include "H5Dpkg.h" /* Datasets */
#include "H5Opkg.h" /* Objects */
#include "H5Tpkg.h" /* Datatypes */
@@ -287,7 +289,6 @@ done:
herr_t
H5O_refresh_metadata(hid_t oid, H5O_loc_t oloc)
{
- H5VL_object_t *vol_obj = NULL; /* VOL object associated with the ID */
hbool_t objs_incr = FALSE; /* Whether the object count in the file was incremented */
herr_t ret_value = SUCCEED; /* Return value */
@@ -298,7 +299,9 @@ H5O_refresh_metadata(hid_t oid, H5O_loc_t oloc)
H5G_loc_t obj_loc;
H5O_loc_t obj_oloc;
H5G_name_t obj_path;
- H5O_shared_t cached_H5O_shared;
+ H5O_refresh_state_t state;
+ H5D_t *ds;
+ const H5VL_object_t *vol_obj;
H5VL_t *connector = NULL;
/* Create empty object location */
@@ -312,11 +315,6 @@ H5O_refresh_metadata(hid_t oid, H5O_loc_t oloc)
H5F_incr_nopen_objs(oloc.file);
objs_incr = TRUE;
- /* Save important datatype state */
- if(H5I_get_type(oid) == H5I_DATATYPE)
- if(H5T_save_refresh_state(oid, &cached_H5O_shared) < 0)
- HGOTO_ERROR(H5E_DATATYPE, H5E_CANTOPENOBJ, FAIL, "unable to save datatype state")
-
/* Get the VOL object from the ID and cache a pointer to the connector.
* The vol_obj will disappear when the underlying object is closed, so
* we can't use that directly.
@@ -325,6 +323,24 @@ H5O_refresh_metadata(hid_t oid, H5O_loc_t oloc)
HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid object identifier")
connector = vol_obj->connector;
+ /* Save important datatype state */
+ switch (H5I_get_type(oid)) {
+ case H5I_DATATYPE:
+ if (H5T_save_refresh_state(oid, &state.shared_ohdr_info) < 0)
+ HGOTO_ERROR(H5E_DATATYPE, H5E_CANTOPENOBJ, FAIL,
+ "unable to save datatype state");
+ break;
+ case H5I_DATASET:
+ ds = (H5D_t *)vol_obj->data;
+ state.dapl_id = ds->shared->dapl_id;
+ if (H5I_inc_ref(state.dapl_id, false) < 0)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL,
+ "could not increase refcnt");
+ break;
+ default:
+ break;
+ }
+
/* Bump the number of references on the VOL connector.
* If you don't do this, VDS refreshes can accidentally close the connector.
*/
@@ -335,16 +351,27 @@ H5O_refresh_metadata(hid_t oid, H5O_loc_t oloc)
HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to refresh object")
/* Re-open the object, re-fetching its metadata */
- if((H5O_refresh_metadata_reopen(oid, &obj_loc, connector, FALSE)) < 0)
+ if (H5O_refresh_metadata_reopen(oid, &obj_loc, &state, connector,
+ FALSE) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to refresh object")
/* Restore the number of references on the VOL connector */
connector->nrefs--;
/* Restore important datatype state */
- if(H5I_get_type(oid) == H5I_DATATYPE)
- if(H5T_restore_refresh_state(oid, &cached_H5O_shared) < 0)
+ switch (H5I_get_type(oid)) {
+ case H5I_DATATYPE:
+ if(H5T_restore_refresh_state(oid, &state.shared_ohdr_info) < 0)
HGOTO_ERROR(H5E_DATATYPE, H5E_CANTOPENOBJ, FAIL, "unable to restore datatype state")
+ break;
+ case H5I_DATASET:
+ if (H5I_dec_ref(state.dapl_id) < 0)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL,
+ "could not decrease refcnt");
+ break;
+ default:
+ break;
+ }
} /* end if */
@@ -442,7 +469,8 @@ done:
*-------------------------------------------------------------------------
*/
herr_t
-H5O_refresh_metadata_reopen(hid_t oid, H5G_loc_t *obj_loc, H5VL_t *vol_connector, hbool_t start_swmr)
+H5O_refresh_metadata_reopen(hid_t oid, H5G_loc_t *obj_loc,
+ const H5O_refresh_state_t *state, H5VL_t *vol_connector, hbool_t start_swmr)
{
void *object = NULL; /* Object for this operation */
H5I_type_t type; /* Type of object for the ID */
@@ -471,9 +499,12 @@ H5O_refresh_metadata_reopen(hid_t oid, H5G_loc_t *obj_loc, H5VL_t *vol_connector
break;
case H5I_DATASET:
- /* Re-open the dataset */
- if(NULL == (object = H5D_open(obj_loc, H5P_DATASET_ACCESS_DEFAULT)))
- HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "unable to open dataset")
+ object = H5D_open(obj_loc,
+ (state == NULL) ? H5P_DATASET_ACCESS_DEFAULT : state->dapl_id);
+ if(NULL == object) {
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL,
+ "unable to open dataset");
+ }
if(!start_swmr) /* No need to handle multiple opens when H5Fstart_swmr_write() */
if(H5D_mult_refresh_reopen((H5D_t *)object) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTOPENOBJ, FAIL, "unable to finish refresh for dataset")
diff --git a/src/H5Oprivate.h b/src/H5Oprivate.h
index 0be6d89..9a1c1ed 100644
--- a/src/H5Oprivate.h
+++ b/src/H5Oprivate.h
@@ -275,6 +275,14 @@ typedef struct H5O_shared_t {
} u;
} H5O_shared_t;
+/* Storage for non-persistent (i.e., not stored in the HDF5 file) information
+ * that has to be preserved when an object is closed & reopened by
+ * H5O_refresh_metadata().
+ */
+typedef union _H5O_refresh_state {
+ hid_t dapl_id; // dataset refresh: access plist
+ H5O_shared_t shared_ohdr_info; // datatype refresh
+} H5O_refresh_state_t;
/*
* Link Info Message.
@@ -972,7 +980,8 @@ H5_DLL herr_t H5O_msg_get_flags(const H5O_loc_t *loc, unsigned type_id, uint8_t
H5_DLL herr_t H5O_flush(H5O_loc_t *oloc, hid_t obj_id);
H5_DLL herr_t H5O_flush_common(H5O_loc_t *oloc, hid_t obj_id);
H5_DLL herr_t H5O_refresh_metadata(hid_t oid, H5O_loc_t oloc);
-H5_DLL herr_t H5O_refresh_metadata_reopen(hid_t oid, H5G_loc_t *obj_loc, H5VL_t *vol_driver, hbool_t start_swmr);
+H5_DLL herr_t H5O_refresh_metadata_reopen(hid_t, H5G_loc_t *,
+ const H5O_refresh_state_t *, H5VL_t *, hbool_t);
/* Cache corking functions */
H5_DLL herr_t H5O_disable_mdc_flushes(H5O_loc_t *oloc);
diff --git a/src/H5PB.c b/src/H5PB.c
index 907fe82..1e0bab7 100644
--- a/src/H5PB.c
+++ b/src/H5PB.c
@@ -13,9 +13,11 @@
/*-------------------------------------------------------------------------
*
- * Created: H5PB.c
- *
- * Purpose: Page Buffer routines.
+ * Created: H5PB2.c
+ *
+ * Purpose: Re-implementation of the page buffer with added features to
+ * support VFD SWMR.
+ * JRM -- 10/11/18
*
*-------------------------------------------------------------------------
*/
@@ -24,96 +26,50 @@
/* Module Setup */
/****************/
-#define H5F_FRIEND /* Suppress error about including H5Fpkg */
-#include "H5PBmodule.h" /* This source code file is part of the H5PB module */
+#define H5F_FRIEND /* suppress error about including H5Fpkg */
+#include "H5PBmodule.h" /* This source code file is part of the
+ * H5PB module
+ */
/***********/
/* Headers */
/***********/
-#include "H5private.h" /* Generic Functions */
-#include "H5Eprivate.h" /* Error handling */
-#include "H5Fpkg.h" /* Files */
-#include "H5FDprivate.h" /* File drivers */
-#include "H5Iprivate.h" /* IDs */
-#include "H5MMprivate.h" /* Memory management */
-#include "H5PBpkg.h" /* File access */
-#include "H5SLprivate.h" /* Skip List */
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5Fpkg.h" /* Files */
+#include "H5FDprivate.h" /* File drivers */
+#include "H5Iprivate.h" /* IDs */
+#include "H5FLprivate.h" /* Free lists */
+#include "H5MMprivate.h" /* Memory management */
+#include "H5PBpkg.h" /* File access */
+
+#include "hlog.h"
/****************/
/* Local Macros */
/****************/
-#define H5PB__PREPEND(page_ptr, head_ptr, tail_ptr, len) { \
- if((head_ptr) == NULL) { \
- (head_ptr) = (page_ptr); \
- (tail_ptr) = (page_ptr); \
- } /* end if */ \
- else { \
- (head_ptr)->prev = (page_ptr); \
- (page_ptr)->next = (head_ptr); \
- (head_ptr) = (page_ptr); \
- } /* end else */ \
- (len)++; \
-} /* H5PB__PREPEND() */
-
-#define H5PB__REMOVE(page_ptr, head_ptr, tail_ptr, len) { \
- if((head_ptr) == (page_ptr)) { \
- (head_ptr) = (page_ptr)->next; \
- if((head_ptr) != NULL) \
- (head_ptr)->prev = NULL; \
- } /* end if */ \
- else \
- (page_ptr)->prev->next = (page_ptr)->next; \
- if((tail_ptr) == (page_ptr)) { \
- (tail_ptr) = (page_ptr)->prev; \
- if((tail_ptr) != NULL) \
- (tail_ptr)->next = NULL; \
- } /* end if */ \
- else \
- (page_ptr)->next->prev = (page_ptr)->prev; \
- page_ptr->next = NULL; \
- page_ptr->prev = NULL; \
- (len)--; \
-}
-
-#define H5PB__INSERT_LRU(page_buf, page_ptr) { \
- HDassert(page_buf); \
- HDassert(page_ptr); \
- /* insert the entry at the head of the list. */ \
- H5PB__PREPEND((page_ptr), (page_buf)->LRU_head_ptr, \
- (page_buf)->LRU_tail_ptr, (page_buf)->LRU_list_len) \
-}
-#define H5PB__REMOVE_LRU(page_buf, page_ptr) { \
- HDassert(page_buf); \
- HDassert(page_ptr); \
- /* remove the entry from the list. */ \
- H5PB__REMOVE((page_ptr), (page_buf)->LRU_head_ptr, \
- (page_buf)->LRU_tail_ptr, (page_buf)->LRU_list_len) \
-}
-
-#define H5PB__MOVE_TO_TOP_LRU(page_buf, page_ptr) { \
- HDassert(page_buf); \
- HDassert(page_ptr); \
- /* Remove entry and insert at the head of the list. */ \
- H5PB__REMOVE((page_ptr), (page_buf)->LRU_head_ptr, \
- (page_buf)->LRU_tail_ptr, (page_buf)->LRU_list_len) \
- H5PB__PREPEND((page_ptr), (page_buf)->LRU_head_ptr, \
- (page_buf)->LRU_tail_ptr, (page_buf)->LRU_list_len) \
-}
+/* Round _x down to nearest _size. */
+#ifndef rounddown
+#define rounddown(_x, _size) (((_x) / (_size)) * (_size))
+#endif
+/* Round _x up to nearest _size. */
+#ifndef roundup
+#define roundup(_x, _size) ((((_x) + (_size) - 1) / (_size)) * (_size))
+#endif
/******************/
/* Local Typedefs */
/******************/
-/* Iteration context for destroying page buffer */
-typedef struct {
- H5PB_t *page_buf;
- hbool_t actual_slist;
-} H5PB_ud1_t;
-
+typedef struct _metadata_section {
+ haddr_t addr;
+ size_t len;
+ const char *buf;
+} metadata_section_t;
/********************/
/* Package Typedefs */
@@ -123,10 +79,50 @@ typedef struct {
/********************/
/* Local Prototypes */
/********************/
-static herr_t H5PB__insert_entry(H5PB_t *page_buf, H5PB_entry_t *page_entry);
-static htri_t H5PB__make_space(H5F_shared_t *f_sh, H5PB_t *page_buf, H5FD_mem_t inserted_type);
-static herr_t H5PB__write_entry(H5F_shared_t *f_sh, H5PB_entry_t *page_entry);
+static H5PB_entry_t * H5PB__allocate_page(H5PB_t *pb_ptr, size_t buf_size,
+ hbool_t clean_image);
+
+static herr_t H5PB__create_new_page(H5PB_t *pb_ptr, haddr_t addr, size_t size,
+ H5FD_mem_t type, hbool_t clean_image, H5PB_entry_t **entry_ptr_ptr);
+
+static void H5PB__deallocate_page(H5PB_entry_t *entry_ptr);
+
+static herr_t H5PB__evict_entry(H5F_shared_t *, H5PB_entry_t *, bool, bool);
+
+static herr_t H5PB__flush_entry(H5F_shared_t *, H5PB_t *, H5PB_entry_t *);
+
+static herr_t H5PB__load_page(H5F_shared_t *, H5PB_t *, haddr_t,
+ H5FD_mem_t, H5PB_entry_t **);
+
+static herr_t H5PB__make_space(H5F_shared_t *, H5PB_t *, H5FD_mem_t);
+
+static herr_t H5PB__mark_entry_clean(H5PB_t *, H5PB_entry_t *);
+
+static herr_t H5PB__mark_entry_dirty(H5F_shared_t *, H5PB_t *, H5PB_entry_t *);
+
+static herr_t H5PB__read_meta(H5F_shared_t *, H5FD_mem_t, haddr_t,
+ size_t, void *);
+
+static herr_t H5PB__read_raw(H5F_shared_t *, H5FD_mem_t, haddr_t,
+ size_t, void *);
+
+static herr_t H5PB__write_meta(H5F_shared_t *, H5FD_mem_t, haddr_t,
+ size_t, const void *);
+
+static herr_t H5PB__write_raw(H5F_shared_t *, H5FD_mem_t, haddr_t,
+ size_t, const void *);
+
+static void metadata_section_split(size_t, haddr_t, size_t, const void *,
+ metadata_section_t *);
+
+static herr_t metadata_multipart_read(H5F_shared_t *, H5FD_mem_t, haddr_t,
+ size_t, void *);
+
+static herr_t metadata_multipart_write(H5F_shared_t *, H5FD_mem_t, haddr_t,
+ size_t, const void *);
+
+static void H5PB_log_access_by_size_counts(const H5PB_t *);
/*********************/
/* Package Variables */
@@ -144,1374 +140,4288 @@ hbool_t H5_PKG_INIT_VAR = FALSE;
/*******************/
/* Local Variables */
/*******************/
+
+
/* Declare a free list to manage the H5PB_t struct */
H5FL_DEFINE_STATIC(H5PB_t);
/* Declare a free list to manage the H5PB_entry_t struct */
H5FL_DEFINE_STATIC(H5PB_entry_t);
+HLOG_OUTLET_DECL(pagebuffer);
+HLOG_OUTLET_SHORT_DEFN(pagebuffer, all);
+HLOG_OUTLET_SHORT_DEFN(pb_access_sizes, pagebuffer);
+HLOG_OUTLET_SHORT_DEFN(pbflush, pagebuffer);
+HLOG_OUTLET_SHORT_DEFN(pbflush_entry, pbflush);
+HLOG_OUTLET_SHORT_DEFN(pbio, pagebuffer);
+HLOG_OUTLET_SHORT_DEFN(pbrd, pbio);
+HLOG_OUTLET_SHORT_DEFN(pbwr, pbio);
+HLOG_OUTLET_SHORT_DEFN(lengthen_pbentry, pagebuffer);
+HLOG_OUTLET_SHORT_DEFN(pbrm, pagebuffer);
/*-------------------------------------------------------------------------
- * Function: H5PB_reset_stats
*
- * Purpose: This function was created without documentation.
- * What follows is my best understanding of Mohamad's intent.
+ * Function: H5PB_reset_stats
*
- * Reset statistics collected for the page buffer layer.
+ * Purpose: Reset statistics collected for the page buffer layer.
*
* Return: Non-negative on success/Negative on failure
*
- * Programmer: Mohamad Chaarawi
+ * Programmer: John Mainzer -- 10/12/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
herr_t
-H5PB_reset_stats(H5PB_t *page_buf)
+H5PB_reset_stats(H5PB_t *pb_ptr)
{
+ int i;
+
FUNC_ENTER_NOAPI_NOERR
/* Sanity checks */
- HDassert(page_buf);
-
- page_buf->accesses[0] = 0;
- page_buf->accesses[1] = 0;
- page_buf->hits[0] = 0;
- page_buf->hits[1] = 0;
- page_buf->misses[0] = 0;
- page_buf->misses[1] = 0;
- page_buf->evictions[0] = 0;
- page_buf->evictions[1] = 0;
- page_buf->bypasses[0] = 0;
- page_buf->bypasses[1] = 0;
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+
+ for ( i = 0; i < H5PB__NUM_STAT_TYPES; i++ ) {
+
+ pb_ptr->bypasses[i] = 0;
+ pb_ptr->accesses[i] = 0;
+ pb_ptr->hits[i] = 0;
+ pb_ptr->misses[i] = 0;
+ pb_ptr->loads[i] = 0;
+ pb_ptr->insertions[i] = 0;
+ pb_ptr->flushes[i] = 0;
+ pb_ptr->evictions[i] = 0;
+ pb_ptr->clears[i] = 0;
+ }
+
+ pb_ptr->max_lru_len = 0;
+ pb_ptr->max_lru_size = 0;
+ pb_ptr->lru_md_skips = 0;
+ pb_ptr->lru_rd_skips = 0;
+ pb_ptr->total_ht_insertions = 0;
+ pb_ptr->total_ht_deletions = 0;
+ pb_ptr->successful_ht_searches = 0;
+ pb_ptr->total_successful_ht_search_depth = 0;
+ pb_ptr->failed_ht_searches = 0;
+ pb_ptr->total_failed_ht_search_depth = 0;
+ pb_ptr->max_index_len = 0;
+ pb_ptr->max_clean_index_len = 0;
+ pb_ptr->max_dirty_index_len = 0;
+ pb_ptr->max_clean_index_size = 0;
+ pb_ptr->max_dirty_index_size = 0;
+ pb_ptr->max_index_size = 0;
+ pb_ptr->max_rd_pages = 0;
+ pb_ptr->max_md_pages = 0;
+ pb_ptr->max_mpmde_count = 0;
+ pb_ptr->lru_tl_skips = 0;
+ pb_ptr->max_tl_len = 0;
+ pb_ptr->max_tl_size = 0;
+ pb_ptr->delayed_writes = 0;
+ pb_ptr->total_delay = 0;
+ pb_ptr->max_dwl_len = 0;
+ pb_ptr->max_dwl_size = 0;
+ pb_ptr->total_dwl_ins_depth = 0;
FUNC_LEAVE_NOAPI(SUCCEED)
+
} /* H5PB_reset_stats() */
/*-------------------------------------------------------------------------
- * Function: H5PB_get_stats
+ * Function: H5PB_get_stats
*
* Purpose: This function was created without documentation.
* What follows is my best understanding of Mohamad's intent.
*
- * Retrieve statistics collected about page accesses for the page buffer layer.
- * --accesses: the number of metadata and raw data accesses to the page buffer layer
- * --hits: the number of metadata and raw data hits in the page buffer layer
- * --misses: the number of metadata and raw data misses in the page buffer layer
- * --evictions: the number of metadata and raw data evictions from the page buffer layer
- * --bypasses: the number of metadata and raw data accesses that bypass the page buffer layer
+ * Retrieve statistics collected about page accesses for the
+ * page buffer layer.
*
- * Return: Non-negative on success/Negative on failure
+ * --accesses: the number of metadata and raw data accesses
+ * to the page buffer layer
+ *
+ * --hits: the number of metadata and raw data hits in
+ * the page buffer layer
+ *
+ * --misses: the number of metadata and raw data misses in
+ * the page buffer layer
+ *
+ * --evictions: the number of metadata and raw data evictions
+ * from the page buffer layer
*
- * Programmer: Mohamad Chaarawi
+ * --bypasses: the number of metadata and raw data accesses
+ * that bypass the page buffer layer
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Mohamad Chaarawi
*
*-------------------------------------------------------------------------
*/
herr_t
-H5PB_get_stats(const H5PB_t *page_buf, unsigned accesses[2], unsigned hits[2],
+H5PB_get_stats(const H5PB_t *pb_ptr, unsigned accesses[2], unsigned hits[2],
unsigned misses[2], unsigned evictions[2], unsigned bypasses[2])
{
FUNC_ENTER_NOAPI_NOERR
/* Sanity checks */
- HDassert(page_buf);
-
- accesses[0] = page_buf->accesses[0];
- accesses[1] = page_buf->accesses[1];
- hits[0] = page_buf->hits[0];
- hits[1] = page_buf->hits[1];
- misses[0] = page_buf->misses[0];
- misses[1] = page_buf->misses[1];
- evictions[0] = page_buf->evictions[0];
- evictions[1] = page_buf->evictions[1];
- bypasses[0] = page_buf->bypasses[0];
- bypasses[1] = page_buf->bypasses[1];
+ HDassert(pb_ptr);
+
+ accesses[0] = (unsigned)pb_ptr->accesses[0];
+ accesses[1] = (unsigned)pb_ptr->accesses[1];
+ accesses[2] = (unsigned)pb_ptr->accesses[2];
+ hits[0] = (unsigned)pb_ptr->hits[0];
+ hits[1] = (unsigned)pb_ptr->hits[1];
+ hits[2] = (unsigned)pb_ptr->hits[2];
+ misses[0] = (unsigned)pb_ptr->misses[0];
+ misses[1] = (unsigned)pb_ptr->misses[1];
+ misses[2] = (unsigned)pb_ptr->misses[2];
+ evictions[0] = (unsigned)pb_ptr->evictions[0];
+ evictions[1] = (unsigned)pb_ptr->evictions[1];
+ evictions[2] = (unsigned)pb_ptr->evictions[2];
+ bypasses[0] = (unsigned)pb_ptr->bypasses[0];
+ bypasses[1] = (unsigned)pb_ptr->bypasses[1];
+ bypasses[2] = (unsigned)pb_ptr->bypasses[2];
FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5PB_get_stats */
/*-------------------------------------------------------------------------
- * Function: H5PB_print_stats()
*
- * Purpose: This function was created without documentation.
- * What follows is my best understanding of Mohamad's intent.
+ * Function: H5PB_print_stats()
*
- * Print out statistics collected for the page buffer layer.
+ * Purpose: Print out statistics collected for the page buffer layer.
*
- * Return: Non-negative on success/Negative on failure
+ * Return: Non-negative on success/Negative on failure
*
- * Programmer: Mohamad Chaarawi
+ * Programmer: John Mainzer -- 10/12/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
herr_t
-H5PB_print_stats(const H5PB_t *page_buf)
+H5PB_print_stats(const H5PB_t *pb_ptr)
{
+ double ave_succ_search_depth = 0.0L;
+ double ave_failed_search_depth = 0.0L;
+ double ave_delayed_write = 0.0L;
+ double ave_delayed_write_ins_depth = 0.0L;
+
FUNC_ENTER_NOAPI_NOINIT_NOERR
- HDassert(page_buf);
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+
+ HDfprintf(stdout, "\n\nPage Buffer Statistics (raw/meta/mpmde): \n\n");
+
+ HDfprintf(stdout, "bypasses = %lld (%lld/%lld/%lld)\n",
+ (pb_ptr->bypasses[0] + pb_ptr->bypasses[1] + pb_ptr->bypasses[2]),
+ pb_ptr->bypasses[0], pb_ptr->bypasses[1], pb_ptr->bypasses[2]);
+
+ HDfprintf(stdout, "acesses = %lld (%lld/%lld/%lld)\n",
+ (pb_ptr->accesses[0] + pb_ptr->accesses[1] + pb_ptr->accesses[2]),
+ pb_ptr->accesses[0], pb_ptr->accesses[1], pb_ptr->accesses[2]);
+
+ HDfprintf(stdout, "hits = %lld (%lld/%lld/%lld)\n",
+ (pb_ptr->hits[0] + pb_ptr->hits[1] + pb_ptr->hits[2]),
+ pb_ptr->hits[0], pb_ptr->hits[1], pb_ptr->hits[2]);
+
+ HDfprintf(stdout, "misses = %lld (%lld/%lld/%lld)\n",
+ (pb_ptr->misses[0] + pb_ptr->misses[1] + pb_ptr->misses[2]),
+ pb_ptr->misses[0], pb_ptr->misses[1], pb_ptr->misses[2]);
+
+ HDfprintf(stdout, "loads = %lld (%lld/%lld/%lld)\n",
+ (pb_ptr->loads[0] + pb_ptr->loads[1] + pb_ptr->loads[2]),
+ pb_ptr->loads[0], pb_ptr->loads[1], pb_ptr->loads[2]);
+
+ HDfprintf(stdout, "insertions = %lld (%lld/%lld/%lld)\n",
+ (pb_ptr->insertions[0] + pb_ptr->insertions[1] +
+ pb_ptr->insertions[2]),
+ pb_ptr->insertions[0], pb_ptr->insertions[1],
+ pb_ptr->insertions[2]);
+
+ HDfprintf(stdout, "flushes = %lld (%lld/%lld/%lld)\n",
+ (pb_ptr->flushes[0] + pb_ptr->flushes[1] + pb_ptr->flushes[2]),
+ pb_ptr->flushes[0], pb_ptr->flushes[1], pb_ptr->flushes[2]);
+
+ HDfprintf(stdout, "evictions = %lld (%lld/%lld/%lld)\n",
+ (pb_ptr->evictions[0] + pb_ptr->evictions[1] +
+ pb_ptr->evictions[2]),
+ pb_ptr->evictions[0], pb_ptr->evictions[1], pb_ptr->evictions[2]);
+
+ HDfprintf(stdout, "clears = %lld (%lld/%lld/%lld)\n",
+ (pb_ptr->clears[0] + pb_ptr->clears[1] + pb_ptr->clears[2]),
+ pb_ptr->clears[0], pb_ptr->clears[1], pb_ptr->clears[2]);
+
+ HDfprintf(stdout, "max LRU len / size = %lld / %lld\n",
+ pb_ptr->max_lru_len, pb_ptr->max_lru_size);
+
+ HDfprintf(stdout,
+ "LRU make space md/rd/tl skips = %lld/%lld/%lld\n",
+ pb_ptr->lru_md_skips, pb_ptr->lru_rd_skips,
+ pb_ptr->lru_tl_skips);
+
+ HDfprintf(stdout, "hash table insertions / deletions = %lld / %lld\n",
+ pb_ptr->total_ht_insertions, pb_ptr->total_ht_deletions);
- HDprintf("PAGE BUFFER STATISTICS:\n");
+ if ( pb_ptr->successful_ht_searches > 0 ) {
- HDprintf("******* METADATA\n");
- HDprintf("\t Total Accesses: %u\n", page_buf->accesses[0]);
- HDprintf("\t Hits: %u\n", page_buf->hits[0]);
- HDprintf("\t Misses: %u\n", page_buf->misses[0]);
- HDprintf("\t Evictions: %u\n", page_buf->evictions[0]);
- HDprintf("\t Bypasses: %u\n", page_buf->bypasses[0]);
- HDprintf("\t Hit Rate = %f%%\n", ((double)page_buf->hits[0]/(page_buf->accesses[0] - page_buf->bypasses[0]))*100);
- HDprintf("*****************\n\n");
+ ave_succ_search_depth =
+ (double)(pb_ptr->total_successful_ht_search_depth) /
+ (double)(pb_ptr->successful_ht_searches);
+ }
+ HDfprintf(stdout, "successful ht searches / ave depth = %lld / %llf\n",
+ pb_ptr->successful_ht_searches, ave_succ_search_depth);
- HDprintf("******* RAWDATA\n");
- HDprintf("\t Total Accesses: %u\n", page_buf->accesses[1]);
- HDprintf("\t Hits: %u\n", page_buf->hits[1]);
- HDprintf("\t Misses: %u\n", page_buf->misses[1]);
- HDprintf("\t Evictions: %u\n", page_buf->evictions[1]);
- HDprintf("\t Bypasses: %u\n", page_buf->bypasses[1]);
- HDprintf("\t Hit Rate = %f%%\n", ((double)page_buf->hits[1]/(page_buf->accesses[1]-page_buf->bypasses[0]))*100);
- HDprintf("*****************\n\n");
+ if ( pb_ptr->failed_ht_searches > 0 ) {
+
+ ave_failed_search_depth =
+ (double)(pb_ptr->total_failed_ht_search_depth) /
+ (double)(pb_ptr->failed_ht_searches);
+ }
+ HDfprintf(stdout, "failed ht searches / ave depth = %lld / %llf\n",
+ pb_ptr->failed_ht_searches, ave_failed_search_depth);
+
+ HDfprintf(stdout, "max index length / size = %lld / %lld\n",
+ pb_ptr->max_index_len, pb_ptr->max_index_size);
+
+ HDfprintf(stdout, "max rd / md / mpmde entries = %lld / %lld / %lld\n",
+ pb_ptr->max_rd_pages, pb_ptr->max_md_pages,
+ pb_ptr->max_mpmde_count);
+
+ HDfprintf(stdout, "tick list max len / size = %lld / %lld\n",
+ pb_ptr->max_tl_len, pb_ptr->max_tl_size);
+
+ HDfprintf(stdout, "delayed write list max len / size = %lld / %lld\n",
+ pb_ptr->max_dwl_len, pb_ptr->max_dwl_size);
+
+ if ( pb_ptr->delayed_writes > 0 ) {
+
+ ave_delayed_write = (double)(pb_ptr->total_delay) /
+ (double)(pb_ptr->delayed_writes);
+ ave_delayed_write_ins_depth = (double)(pb_ptr->total_dwl_ins_depth) /
+ (double)(pb_ptr->delayed_writes);
+ }
+ HDfprintf(stdout,
+ "delayed writes / ave delay / ave ins depth = %lld / %llf / %llf\n",
+ pb_ptr->delayed_writes, ave_delayed_write, ave_delayed_write_ins_depth);
FUNC_LEAVE_NOAPI(SUCCEED)
+
} /* H5PB_print_stats */
/*-------------------------------------------------------------------------
- * Function: H5PB_create
*
- * Purpose: Create and setup the PB on the file.
+ * Function: H5PB_add_new_page
*
- * Return: Non-negative on success/Negative on failure
+ * Purpose: Insert a new blank page to the page buffer if the page
+ * buffer is configured to allow pages of the specified
+ * type.
+ *
+ * This function is called by the MF layer when a new page
+ * is allocated to indicate to the page buffer layer that
+ * a read of the page from the file is not necessary since
+ * it's an empty page.
+ *
+ * For purposes of the VFD SWMR writer, we also track pages
+ * that are inserted via this call, as the fact that the
+ * page was allocated implies that an earlier version does
+ * not exist in the HDF5 file, and thus we need not concern
+ * ourselves with delaying the write of this pages to avoid
+ * messages from the future on the reader.
+ *
+ * Note that this function inserts the new page without
+ * attempting to make space. This can result in the page
+ * buffer exceeding its maximum size.
+ *
+ * Note also that it is possible that the page (marked clean)
+ * will be evicted before its first use.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/12/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5PB_add_new_page(H5F_shared_t *shared, H5FD_mem_t type, haddr_t page_addr)
+{
+ hbool_t can_insert = TRUE;
+ H5PB_t *pb_ptr = NULL;
+ H5PB_entry_t *entry_ptr = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(shared);
+ HDassert(shared->pb_ptr);
+
+ pb_ptr = shared->pb_ptr;
+
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+
+ if ( H5FD_MEM_DRAW == type ) { /* raw data page insertion */
+
+ if ( pb_ptr->min_md_pages == pb_ptr->max_pages ) {
+
+ can_insert = FALSE;
+
+ }
+ } else { /* metadata page insertion */
+
+ if ( pb_ptr->min_rd_pages == pb_ptr->max_pages ) {
+
+ can_insert = FALSE;
+ }
+ }
+
+ if ( can_insert ) {
+
+ if ( H5PB__create_new_page(pb_ptr, page_addr,
+ (size_t)(pb_ptr->page_size),
+ type, TRUE, &entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "new page buffer page creation failed.")
+
+ /* make note that this page was allocated, not loaded from file */
+ entry_ptr->loaded = FALSE;
+
+ /* updates stats */
+ H5PB__UPDATE_STATS_FOR_INSERTION(pb_ptr, entry_ptr);
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB_add_new_page */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB_create
*
- * Programmer: Mohamad Chaarawi
+ * Purpose: Setup a page buffer for the supplied file.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/11/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
herr_t
-H5PB_create(H5F_shared_t *f_sh, size_t size, unsigned page_buf_min_meta_perc, unsigned page_buf_min_raw_perc)
+H5PB_create(H5F_shared_t *shared, size_t size, unsigned page_buf_min_meta_perc,
+ unsigned page_buf_min_raw_perc)
{
- H5PB_t *page_buf = NULL;
+ hbool_t vfd_swmr_writer = FALSE;
+ int i;
+ int32_t min_md_pages;
+ int32_t min_rd_pages;
+ H5PB_t *pb_ptr = NULL;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
- HDassert(f_sh);
+ HDassert(shared);
+ HDassert(page_buf_min_meta_perc <= 100);
+ HDassert(page_buf_min_raw_perc <= 100);
+ HDassert((page_buf_min_meta_perc + page_buf_min_raw_perc) <= 100);
/* Check args */
- if(f_sh->fs_strategy != H5F_FSPACE_STRATEGY_PAGE)
- HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "Enabling Page Buffering requires PAGE file space strategy")
- /* round down the size if it is larger than the page size */
- else if(size > f_sh->fs_page_size) {
+ if ( shared->fs_strategy != H5F_FSPACE_STRATEGY_PAGE )
+
+ HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, \
+ "Enabling Page Buffering requires PAGE file space strategy")
+
+ else if ( size > shared->fs_page_size ) {
+
+ /* round size down to the next multiple of fs_page_size */
+
hsize_t temp_size;
- temp_size = (size / f_sh->fs_page_size) * f_sh->fs_page_size;
+ temp_size = (size / shared->fs_page_size) * shared->fs_page_size;
+
H5_CHECKED_ASSIGN(size, size_t, temp_size, hsize_t);
- } /* end if */
- else if(0 != size % f_sh->fs_page_size)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTINIT, FAIL, "Page Buffer size must be >= to the page size")
- /* Allocate the new page buffering structure */
- if(NULL == (page_buf = H5FL_CALLOC(H5PB_t)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "memory allocation failed")
+ } /* end if */
+ else if ( 0 != size % shared->fs_page_size )
- page_buf->max_size = size;
- H5_CHECKED_ASSIGN(page_buf->page_size, size_t, f_sh->fs_page_size, hsize_t);
- page_buf->min_meta_perc = page_buf_min_meta_perc;
- page_buf->min_raw_perc = page_buf_min_raw_perc;
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTINIT, FAIL, \
+ "Page Buffer size must be >= to the page size")
/* Calculate the minimum page count for metadata and raw data
- * based on the fractions provided
+ * based on the fractions provided
*/
- page_buf->min_meta_count = (unsigned)((size * page_buf_min_meta_perc) / (f_sh->fs_page_size * 100));
- page_buf->min_raw_count = (unsigned)((size * page_buf_min_raw_perc) / (f_sh->fs_page_size * 100));
+ min_md_pages = (int32_t)((size * page_buf_min_meta_perc) /
+ (shared->fs_page_size * 100));
+ min_rd_pages = (int32_t)((size * page_buf_min_raw_perc) /
+ (shared->fs_page_size * 100));
+ HDassert(min_md_pages >= 0);
+ HDassert(min_rd_pages >= 0);
+ HDassert((min_md_pages + min_rd_pages) <=
+ (int32_t)(size / shared->fs_page_size));
+
+
+ /* compute vfd_swmr_writer */
+ if ( ( H5F_SHARED_VFD_SWMR_CONFIG(shared) ) && ( H5F_SHARED_INTENT(shared) & H5F_ACC_RDWR ) ) {
- if(NULL == (page_buf->slist_ptr = H5SL_create(H5SL_TYPE_HADDR, NULL)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTCREATE, FAIL, "can't create skip list")
- if(NULL == (page_buf->mf_slist_ptr = H5SL_create(H5SL_TYPE_HADDR, NULL)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTCREATE, FAIL, "can't create skip list")
+ HDassert(shared->vfd_swmr_config.writer);
+ vfd_swmr_writer = TRUE;
+ }
- if(NULL == (page_buf->page_fac = H5FL_fac_init(page_buf->page_size)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTINIT, FAIL, "can't create page factory")
- f_sh->page_buf = page_buf;
+ /* Allocate the new page buffering structure */
+ if(NULL == (pb_ptr = H5FL_MALLOC(H5PB_t)))
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "memory allocation failed")
+
+ /* initialize the new instance of H5PB_t */
+
+ pb_ptr->magic = H5PB__H5PB_T_MAGIC;
+ pb_ptr->page_size = shared->fs_page_size;
+ H5_CHECKED_ASSIGN(pb_ptr->page_size, size_t, \
+ shared->fs_page_size, hsize_t);
+ pb_ptr->max_pages = (int32_t)(size / shared->fs_page_size);
+ pb_ptr->curr_pages = 0;
+ pb_ptr->curr_md_pages = 0;
+ pb_ptr->curr_rd_pages = 0;
+ pb_ptr->min_md_pages = min_md_pages;
+ pb_ptr->min_rd_pages = min_rd_pages;
+
+ pb_ptr->max_size = size;
+ pb_ptr->min_meta_perc = page_buf_min_meta_perc;
+ pb_ptr->min_raw_perc = page_buf_min_raw_perc;
+
+ /* index */
+ for ( i = 0; i < H5PB__HASH_TABLE_LEN; i++ )
+ pb_ptr->ht[i] = NULL;
+ pb_ptr->index_len = 0;
+ pb_ptr->clean_index_len = 0;
+ pb_ptr->dirty_index_len = 0;
+ pb_ptr->index_size = 0;
+ pb_ptr->clean_index_size = 0;
+ pb_ptr->dirty_index_size = 0;
+ pb_ptr->il_len = 0;
+ pb_ptr->il_size = 0;
+ pb_ptr->il_head = NULL;
+ pb_ptr->il_tail = NULL;
+
+ /* LRU */
+ pb_ptr->LRU_len = 0;
+ pb_ptr->LRU_size = 0;
+ pb_ptr->LRU_head_ptr = NULL;
+ pb_ptr->LRU_tail_ptr = NULL;
+
+
+ /* VFD SWMR specific fields.
+ * The following fields are defined iff vfd_swmr_writer is TRUE.
+ */
+ pb_ptr->vfd_swmr_writer = vfd_swmr_writer;
+ pb_ptr->mpmde_count = 0;
+ pb_ptr->cur_tick = 0;
+
+ /* delayed write list */
+ pb_ptr->max_delay = 0;
+ pb_ptr->dwl_len = 0;
+ pb_ptr->dwl_size = 0;
+ pb_ptr->dwl_head_ptr = NULL;
+ pb_ptr->dwl_tail_ptr = NULL;
+
+ /* tick list */
+ pb_ptr->tl_len = 0;
+ pb_ptr->tl_size = 0;
+ pb_ptr->tl_head_ptr = NULL;
+ pb_ptr->tl_tail_ptr = NULL;
+
+ H5PB_reset_stats(pb_ptr);
+
+ shared->pb_ptr = pb_ptr;
+
+ /* if this is a VFD SWMR reader, inform the reader VFD that the
+ * page buffer is configured. Note that this is for sanity
+ * checking, and only needed until we modify the file open
+ * code to create the page buffer before any file reads in
+ * the VFD SWMR reader case. After that, this code should be
+ * removed.
+ * JRM -- 1/29/19
+ */
+ if ( ( H5F_SHARED_VFD_SWMR_CONFIG(shared) ) &&
+ ( 0 == (H5F_SHARED_INTENT(shared) & H5F_ACC_RDWR) ) ) {
+
+ HDassert(shared->lf);
+ HDassert(! shared->vfd_swmr_config.writer);
+
+ H5FD_vfd_swmr_set_pb_configured(shared->lf);
+ }
done:
- if(ret_value < 0) {
- if(page_buf != NULL) {
- if(page_buf->slist_ptr != NULL)
- H5SL_close(page_buf->slist_ptr);
- if(page_buf->mf_slist_ptr != NULL)
- H5SL_close(page_buf->mf_slist_ptr);
- if(page_buf->page_fac != NULL)
- H5FL_fac_term(page_buf->page_fac);
- page_buf = H5FL_FREE(H5PB_t, page_buf);
- } /* end if */
- } /* end if */
+
+ if ( ret_value < 0 ) {
+
+ if ( pb_ptr != NULL ) {
+
+ pb_ptr = H5FL_FREE(H5PB_t, pb_ptr);
+
+ }
+ }
FUNC_LEAVE_NOAPI(ret_value)
+
} /* H5PB_create */
/*-------------------------------------------------------------------------
- * Function: H5PB__flush_cb
*
- * Purpose: Callback to flush PB skiplist entries.
+ * Function: H5PB_dest
*
- * Return: Non-negative on success/Negative on failure
+ * Purpose: Flush (if necessary) and evict all entries in the page
+ * buffer, and then discard the page buffer.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/22/18
*
- * Programmer: Mohamad Chaarawi
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
-static herr_t
-H5PB__flush_cb(void *item, void H5_ATTR_UNUSED *key, void *_op_data)
+herr_t
+H5PB_dest(H5F_shared_t *shared)
{
- H5PB_entry_t *page_entry = (H5PB_entry_t *)item; /* Pointer to page entry node */
- H5F_shared_t *f_sh = (H5F_shared_t *)_op_data;
+ int i;
+ H5PB_t *pb_ptr = NULL;
+ H5PB_entry_t *entry_ptr = NULL;
+ H5PB_entry_t *evict_ptr = NULL;
herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_NOAPI(FAIL)
- /* Sanity checks */
- HDassert(page_entry);
- HDassert(f_sh);
+ /* Sanity check */
+ HDassert(shared);
+
+ /* flush and destroy the page buffer, if it exists */
+ if ( shared->pb_ptr ) {
+
+ pb_ptr = shared->pb_ptr;
+
+ H5PB_log_access_by_size_counts(pb_ptr);
+
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+
+ /* the current implementation if very inefficient, and will
+ * fail if there are any outstanding delayed writes -- must fix this
+ */
+ for ( i = 0; i < H5PB__HASH_TABLE_LEN; i++ ) {
+
+ entry_ptr = pb_ptr->ht[i];
+
+ while ( entry_ptr ) {
+
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+
+ evict_ptr = entry_ptr;
+ entry_ptr = entry_ptr->ht_next;
- /* Flush the page if it's dirty */
- if(page_entry->is_dirty)
- if(H5PB__write_entry(f_sh, page_entry) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "file write failed")
+ if ( evict_ptr->is_dirty ) {
+
+ if ( H5PB__flush_entry(shared, pb_ptr, evict_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, \
+ "Can't flush entry")
+ }
+
+ if ( H5PB__evict_entry(shared, evict_ptr, TRUE, true) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "forced eviction failed")
+
+ entry_ptr = pb_ptr->ht[i];
+ }
+ }
+
+ /* regular operations fields */
+ HDassert(pb_ptr->curr_pages == 0);
+ HDassert(pb_ptr->curr_md_pages == 0);
+ HDassert(pb_ptr->curr_rd_pages == 0);
+ HDassert(pb_ptr->index_len == 0);
+ HDassert(pb_ptr->index_size == 0);
+ HDassert(pb_ptr->LRU_len == 0);
+ HDassert(pb_ptr->LRU_size == 0);
+ HDassert(pb_ptr->LRU_head_ptr == NULL);
+ HDassert(pb_ptr->LRU_tail_ptr == NULL);
+
+ /* VFD SWMR fields */
+ HDassert(pb_ptr->dwl_len == 0);
+ HDassert(pb_ptr->dwl_size == 0);
+ HDassert(pb_ptr->dwl_head_ptr == NULL);
+ HDassert(pb_ptr->dwl_tail_ptr == NULL);
+
+ HDassert(pb_ptr->tl_len == 0);
+ HDassert(pb_ptr->tl_size == 0);
+ HDassert(pb_ptr->tl_head_ptr == NULL);
+ HDassert(pb_ptr->tl_tail_ptr == NULL);
+
+ pb_ptr->magic = 0;
+ shared->pb_ptr = H5FL_FREE(H5PB_t, pb_ptr);
+ }
done:
+
FUNC_LEAVE_NOAPI(ret_value)
-} /* H5PB__flush_cb() */
+
+} /* H5PB_dest */
/*-------------------------------------------------------------------------
- * Function: H5PB_flush
*
- * Purpose: Flush/Free all the PB entries to the file.
+ * Function: H5PB_flush
*
- * Return: Non-negative on success/Negative on failure
+ * Purpose: If the page buffer is defined, flush all entries.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/22/18
*
- * Programmer: Mohamad Chaarawi
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
herr_t
-H5PB_flush(H5F_shared_t *f_sh)
+H5PB_flush(H5F_shared_t *shared)
{
+ int i;
+ H5PB_t *pb_ptr = NULL;
+ H5PB_entry_t *entry_ptr = NULL;
+ H5PB_entry_t *flush_ptr = NULL;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity check */
- HDassert(f_sh);
+ HDassert(shared);
- /* Flush all the entries in the PB skiplist, if we have write access on the file */
- if(f_sh->page_buf && (H5F_ACC_RDWR & H5F_SHARED_INTENT(f_sh))) {
- H5PB_t *page_buf = f_sh->page_buf;
+ pb_ptr = shared->pb_ptr;
- /* Iterate over all entries in page buffer skip list */
- if(H5SL_iterate(page_buf->slist_ptr, H5PB__flush_cb, f_sh))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_BADITER, FAIL, "can't flush page buffer skip list")
- } /* end if */
+ if ( pb_ptr ) {
+
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+
+ /* the current implementation is very inefficient, and will
+ * fail if there are any delayed writes -- must fix this
+ */
+ for ( i = 0; i < H5PB__HASH_TABLE_LEN; i++ ) {
+
+ entry_ptr = pb_ptr->ht[i];
+
+ while ( entry_ptr ) {
+
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+
+ flush_ptr = entry_ptr;
+ entry_ptr = entry_ptr->ht_next;
+ hlog_fast(pbflush, "%s: visiting %zu-byte page %" PRIu64,
+ __func__, flush_ptr->size, flush_ptr->page);
+
+ if ( flush_ptr->is_dirty ) {
+
+ if (flush_ptr->delay_write_until != 0) {
+ hlog_fast(pbflush, "%s: delaying %zu-byte page %" PRIu64
+ " until %" PRIu64 " (now %" PRIu64 ")",
+ __func__, flush_ptr->size, flush_ptr->page,
+ flush_ptr->delay_write_until,
+ shared->tick_num);
+ continue;
+ }
+
+ if ( H5PB__flush_entry(shared, pb_ptr, flush_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, \
+ "Can't flush entry")
+ }
+ }
+ }
+ }
done:
+
FUNC_LEAVE_NOAPI(ret_value)
+
} /* H5PB_flush */
/*-------------------------------------------------------------------------
- * Function: H5PB__dest_cb
*
- * Purpose: Callback to free PB skiplist entries.
+ * Function: H5PB_page_exists
*
- * Return: Non-negative on success/Negative on failure
+ * Purpose: Test to see if a page buffer page exists at the specified
+ * address. Set *page_exists_ptr to TRUE or FALSE accordingly.
+ *
+ * This function exists for the convenience of the test
+ * code
+ *
+ * Return: Non-negative on success/Negative on failure
*
- * Programmer: Mohamad Chaarawi
+ * Programmer: John Mainzer -- 10/22/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
-static herr_t
-H5PB__dest_cb(void *item, void H5_ATTR_UNUSED *key, void *_op_data)
+herr_t
+H5PB_page_exists(H5F_shared_t *shared, haddr_t addr, hbool_t *page_exists_ptr)
{
- H5PB_entry_t *page_entry = (H5PB_entry_t *)item; /* Pointer to page entry node */
- H5PB_ud1_t *op_data = (H5PB_ud1_t *)_op_data;
+ uint64_t page;
+ H5PB_t *pb_ptr = NULL;
+ H5PB_entry_t *entry_ptr = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
- FUNC_ENTER_STATIC_NOERR
+ /* Sanity check */
+ HDassert(shared);
+ HDassert(shared->pb_ptr);
- /* Sanity checking */
- HDassert(page_entry);
- HDassert(op_data);
- HDassert(op_data->page_buf);
+ pb_ptr = shared->pb_ptr;
- /* Remove entry from LRU list */
- if(op_data->actual_slist) {
- H5PB__REMOVE_LRU(op_data->page_buf, page_entry)
- page_entry->page_buf_ptr = H5FL_FAC_FREE(op_data->page_buf->page_fac, page_entry->page_buf_ptr);
- } /* end if */
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(page_exists_ptr);
- /* Free page entry */
- page_entry = H5FL_FREE(H5PB_entry_t, page_entry);
+ /* Calculate the page offset */
+ page = (addr / pb_ptr->page_size);
- FUNC_LEAVE_NOAPI(SUCCEED)
-} /* H5PB__dest_cb() */
+ /* the supplied address should be page aligned */
+ HDassert(addr == page * pb_ptr->page_size);
+
+ /* Search for page in the hash table */
+ H5PB__SEARCH_INDEX(pb_ptr, page, entry_ptr, FAIL)
+
+ HDassert((NULL == entry_ptr) || (entry_ptr->addr == addr));
+
+ *page_exists_ptr = ( entry_ptr != NULL );
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB_page_exists */
+
+static void
+H5PB_count_meta_access_by_size(H5PB_t *pb, size_t size)
+{
+ const size_t nslots = NELMTS(pb->access_size_count);
+ size_t i, hi;
+
+ for (hi = pb->page_size, i = 0; i < nslots - 1; i++, hi *= 2){
+ if (size <= hi)
+ break;
+ }
+ pb->access_size_count[i]++;
+}
+
+static void
+H5PB_log_access_by_size_counts(const H5PB_t *pb)
+{
+ const size_t nslots = NELMTS(pb->access_size_count);
+ size_t i, lo, hi;
+
+ hlog_fast(pb_access_sizes, "page buffer %p metadata accesses by size:",
+ (const void *)pb);
+
+ for (lo = 0, hi = pb->page_size, i = 0;
+ i < nslots - 1;
+ i++, lo = hi + 1, hi *= 2) {
+ hlog_fast(pb_access_sizes,
+ "%16" PRIu64 " accesses %8zu - %8zu bytes long",
+ pb->access_size_count[i], lo, hi);
+ }
+
+ hlog_fast(pb_access_sizes,
+ "%16" PRIu64 " accesses %8zu - greater bytes long",
+ pb->access_size_count[i], lo);
+}
/*-------------------------------------------------------------------------
- * Function: H5PB_dest
*
- * Purpose: Flush and destroy the PB on the file if it exists.
+ * Function: H5PB_read
*
- * Return: Non-negative on success/Negative on failure
+ * Purpose: Satisfy the read from the page buffer if possible.
+ *
+ * 1) If the page buffer is disabled, simply read from the
+ * HDF5 file and return.
+ *
+ * 2) If the read is for raw data, and the page buffer is
+ * configured for metadata only (i.e. min_md_pages ==
+ * max_pages), simply read from the HDF5 file and return.
+ *
+ * 3) If the read is for raw data, and it of page size or
+ * larger, read it directly from the HDF5 file.
+ *
+ * It is possible that the page buffer contains dirty pages
+ * that intersect with the read -- test for this and update
+ * the read buffer from the page buffer if any such pages
+ * exist.
+ *
+ * Note that no pages are inserted into the page buffer in
+ * this case.
+ *
+ * 4) If the read is for raw data, and it is of size less
+ * than the page size, satisfy the read from the page
+ * buffer, loading and inserting pages into the
+ * page buffer as necessary
+ *
+ * 5) If the read is for metadata, and the page buffer is
+ * configured for raw data only (i.e. min_rd_pages ==
+ * max_pages), simply read from the HDF5 file and return.
+ *
+ * The free space manager guarantees that allocations larger
+ * than one page will be page alligned, and that allocations
+ * of size less than or equal to page size will not cross page
+ * boundaries. Further, unlike raw data, metadata is always
+ * written and read atomically.
+ *
+ * In principle, this should make it easy to discriminate
+ * between small and multi-page metadata entries so that
+ * pages containing the former will be buffered and the
+ * latter be read directly from file.
+ *
+ * Unfortunately, the metadata cache does not always know the
+ * size of metadata entries when it tries to read them. In
+ * such cases, it issues speculative reads that may be either
+ * smaller or larger than the actual size of the piece of
+ * metadata that is finally read.
+ *
+ * Since we are guaranteed that all metadata allocations larger
+ * that one page are page aligned, we can safely clip at the
+ * page boundary any non page aligned metadata read that crosses
+ * page boundaries.
+ *
+ * However, page aligned reads could wind up being either
+ * small or multi-page. This results in two scenarios that
+ * we must handle:
+ *
+ * a) A page aligned read of size less than one page
+ * turns out to be mult-page.
+ *
+ * In this case, the initial speculative read will
+ * result in a page load and insertion into the page
+ * buffer. This page must be evicted on the subsequent
+ * read of size greater than page size.
+ *
+ * In the context of VFD SWMR, it is also possible that
+ * that the multi-page metadata entry is already in the
+ * page buffer -- in which case the initial read should
+ * be satisfied from the multi-page page buffer entry.
+ *
+ * b) A page aligned, larger than one page read turns out
+ * to be small (less than one page).
+ *
+ * If there is already a page in the page buffer with
+ * same address, we can safely clip the original
+ * read to page size
+ *
+ * The above considerations resolve into the following cases:
+ *
+ * 6) If the read is for metadata and not page aligned, clip
+ * the read to the end of the current page if necessary.
+ * Load the relevant page if necessary and satisfy the
+ * read from the page buffer. Note that it there is an
+ * existing page, it must not be a multi-page metadata
+ * entry. It it is, flag an error.
+ *
+ * 7) If the read is for metadata, is page aligned, is larger
+ * than one page, and there is no entry in the page buffer,
+ * satisfy the read from the file
+ *
+ * 8) If the read is for metadata, is page aligned, is larger
+ * than one page, and there is a regular entry at the target
+ * page address, test to see if the last read was for the
+ * same address.
*
- * Programmer: Mohamad Chaarawi
+ * If was, evict the page, and satisfy the read from file.
+ * Flag an error if the page was dirty.
+ *
+ * If the last read was for a different page, clip the read
+ * to one page, and satisfy the read from the existing
+ * regular entry.
+ *
+ * 9) If the read is for metadata, is page aligned, is larger
+ * than one page, and there is a multi-page metadata entry
+ * at the target page address, test to see if
+ * pb_ptr->vfd_swmr_write is TRUE.
+ *
+ * If it is, satisfy the read from the multi-page metadata
+ * entry, clipping the read if necessary.
+ *
+ * if pb_ptr->vfd_swmr_write is FALSE, flag an error.
+ *
+ * 10) If the read is for metadata, is page aligned, is no
+ * larger than a page, test to see if the page buffer
+ * contains a page at the target address.
+ *
+ * If it doesn't, load the page and satisfy the read
+ * from it.
+ *
+ * If it contains a regular page entry, satisfy the read
+ * from it.
+ *
+ * If it contains a multipage metadata entry at the target
+ * address, satisfy the read from the multi-page metadata
+ * entry if pb_ptr->vfd_swmr_write is TRUE, and flag an
+ * error otherwise.
+ *
+ * Observe that this function handles casses 1, 2, and 5
+ * directly, calls H5PB_read_raw() for cases 3 & 4, and
+ * calls H5PB_read_meta() for cases 6), 7, 8, 9), and 10).
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/11/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
+/* TBD Add optional raw-data bypass here and at H5PB_write when we
+ * are operating in parallel mode.
+ */
herr_t
-H5PB_dest(H5F_shared_t *f_sh)
+H5PB_read(H5F_shared_t *shared, H5FD_mem_t type, haddr_t addr, size_t size,
+ void *buf/*out*/)
{
- herr_t ret_value = SUCCEED; /* Return value */
+ H5PB_t *pb_ptr; /* Page buffer for this file */
+ herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
- /* Sanity checks */
- HDassert(f_sh);
+ hlog_fast(pbrd, "%s %p type %d %" PRIuHADDR " size %zu",
+ __func__, (void *)shared, type, addr, size);
- /* flush and destroy the page buffer, if it exists */
- if(f_sh->page_buf) {
- H5PB_t *page_buf = f_sh->page_buf;
- H5PB_ud1_t op_data; /* Iteration context */
+ pb_ptr = shared->pb_ptr;
+
+ if (pb_ptr != NULL && type != H5FD_MEM_DRAW)
+ H5PB_count_meta_access_by_size(pb_ptr, size);
- if(H5PB_flush(f_sh) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTFLUSH, FAIL, "can't flush page buffer")
+ HDassert(pb_ptr == NULL || pb_ptr->magic == H5PB__H5PB_T_MAGIC);
- /* Set up context info */
- op_data.page_buf = page_buf;
+ /* Bypass the page buffer in case
+ * 1) page buffer is disabled
+ * _) MPI I/O is enabled
+ * 2) page buffer configured for metadata only, and it's a raw-data access
+ * 5) page buffer configured for raw data only, and it's a metadata access
+ */
+ if (pb_ptr == NULL || H5F_SHARED_HAS_FEATURE(shared, H5FD_FEAT_HAS_MPI) ||
+ (H5FD_MEM_DRAW == type && pb_ptr->min_md_pages == pb_ptr->max_pages) ||
+ (H5FD_MEM_DRAW != type && pb_ptr->min_rd_pages == pb_ptr->max_pages)) {
- /* Destroy the skip list containing all the entries in the PB */
- op_data.actual_slist = TRUE;
- if(H5SL_destroy(page_buf->slist_ptr, H5PB__dest_cb, &op_data))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTCLOSEOBJ, FAIL, "can't destroy page buffer skip list")
+ if (H5FD_read(shared->lf, type, addr, size, buf) < 0) {
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL,
+ "read through lower VFD failed");
+ }
- /* Destroy the skip list containing the new entries */
- op_data.actual_slist = FALSE;
- if(H5SL_destroy(page_buf->mf_slist_ptr, H5PB__dest_cb, &op_data))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTCLOSEOBJ, FAIL, "can't destroy page buffer skip list")
+ if (pb_ptr != NULL)
+ H5PB__UPDATE_STATS_FOR_BYPASS(pb_ptr, type, size);
+ HGOTO_DONE(SUCCEED);
+ }
- /* Destroy the page factory */
- if(H5FL_fac_term(page_buf->page_fac) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTRELEASE, FAIL, "can't destroy page buffer page factory")
+ if (H5FD_MEM_DRAW == type) { /* cases 3 and 4 */
+ if (H5PB__read_raw(shared, type, addr, size, buf) < 0)
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "raw read failed");
+ } else if (metadata_multipart_read(shared, type, addr, size, buf) < 0)
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "meta read failed");
- f_sh->page_buf = H5FL_FREE(H5PB_t, page_buf);
- } /* end if */
+ H5PB__UPDATE_STATS_FOR_ACCESS(pb_ptr, type, size);
done:
FUNC_LEAVE_NOAPI(ret_value)
-} /* H5PB_dest */
+}
+
+/* Remove the entry corresponding to lower-file page number `page`.
+ * Return 0 if there was no such entry or if the entry was removed
+ * successfully. Return -1 on error.
+ *
+ * If `only_mark` is true, then the entry corresponding shadow-index
+ * entry is not removed. Instead, it is marked as garbage. This is
+ * a stop-gap fix for a performance problem in H5PB_dest(): deleting
+ * all of the index entries took time quadratic in their number because
+ * this routine performs an O(n) copy of index entries.
+ */
+static int
+shadow_idx_entry_remove(H5F_shared_t *shared, uint64_t page, bool only_mark)
+{
+ ptrdiff_t i;
+ H5FD_vfd_swmr_idx_entry_t *entry;
+
+ entry = vfd_swmr_pageno_to_mdf_idx_entry(shared->mdf_idx,
+ shared->mdf_idx_entries_used, page, false);
+
+ if (entry == NULL)
+ return 0;
+
+ if (shared->vfd_swmr_writer && entry->md_file_page_offset != 0) {
+ if (shadow_image_defer_free(shared, entry) != 0)
+ return -1;
+ entry->md_file_page_offset = 0;
+ }
+
+ if (only_mark) {
+ entry->garbage = true;
+ return 0;
+ }
+
+ i = entry - shared->mdf_idx;
+
+ if (shared->mdf_idx_entries_used > i + 1) {
+ const size_t ntocopy =
+ (size_t)(shared->mdf_idx_entries_used - (i + 1));
+ memmove(&shared->mdf_idx[i],
+ &shared->mdf_idx[i + 1],
+ ntocopy * sizeof(shared->mdf_idx[i + 1]));
+ }
+ shared->mdf_idx_entries_used--;
+ return 0;
+}
/*-------------------------------------------------------------------------
- * Function: H5PB_add_new_page
*
- * Purpose: Add a new page to the new page skip list. This is called
- * from the MF layer when a new page is allocated to
- * indicate to the page buffer layer that a read of the page
- * from the file is not necessary since it's an empty page.
+ * Function: H5PB_remove_entry
+ *
+ * Purpose: Remove possible metadata entry with ADDR from the PB cache.
+ *
+ * This is in response to the data corruption bug from fheap.c
+ * with page buffering + page strategy.
+ *
+ * Note: Large metadata page bypasses the PB cache.
+ *
+ * Note: Update of raw data page (large or small sized) is
+ * handled by the PB cache.
*
* Return: Non-negative on success/Negative on failure
*
- * Programmer: Mohamad Chaarawi
+ * Programmer: Vailin Choi; Feb 2017
+ *
+ * Changes: Reworked function for re-implementation of the page buffer.
+ *
+ * In the context of VFD SWMR, it is possible that the
+ * discarded page or multi-page metadata entry has been
+ * modified during the current tick and/or is subject to a
+ * delayed write. We must detect this, and remove the entry
+ * from the tick list and/or delayed write list before it is
+ * evicted.
+ *
+ * Vailin: I think we need to do this for raw data as well.
+ *
+ * JRM -- 10/23/18
+ *
+ * We also need to evict modified pages from the page
+ * buffer in the VFD SWMR reader case to avoid message from
+ * the past bugs. This function will serve for this for
+ * now, but for efficiency, we may want a version that takes
+ * a list of pages instead.
+ *
+ * JRM -- 12/30/18
*
*-------------------------------------------------------------------------
*/
herr_t
-H5PB_add_new_page(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t page_addr)
+H5PB_remove_entry(H5F_shared_t *shared, haddr_t addr)
{
- H5PB_t *page_buf; /* Page buffer to operate on */
- H5PB_entry_t *page_entry = NULL; /* Pointer to the corresponding page entry */
- herr_t ret_value = SUCCEED; /* Return value */
+ uint64_t page;
+ H5PB_t *pb_ptr;
+ H5PB_entry_t *entry_ptr = NULL;
+ herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI(FAIL)
- /* Sanity checks */
- HDassert(f_sh);
- page_buf = f_sh->page_buf;
- HDassert(page_buf);
+ pb_ptr = shared->pb_ptr;
- /* If there is an existing page, this means that at some point the
- * file free space manager freed and re-allocated a page at the same
- * address. No need to do anything here then...
- */
- /* MSC - to be safe, might want to dig in the MF layer and remove
- * the page when it is freed from this list if it still exists and
- * remove this check
- */
- if(NULL == H5SL_search(page_buf->mf_slist_ptr, &(page_addr))) {
- /* Create the new PB entry */
- if(NULL == (page_entry = H5FL_CALLOC(H5PB_entry_t)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "memory allocation failed")
-
- /* Initialize page fields */
- page_entry->addr = page_addr;
- page_entry->type = (H5F_mem_page_t)type;
- page_entry->is_dirty = FALSE;
-
- /* Insert entry in skip list */
- if(H5SL_insert(page_buf->mf_slist_ptr, page_entry, &(page_entry->addr)) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_BADVALUE, FAIL, "Can't insert entry in skip list")
- } /* end if */
+ /* Calculate the page offset */
+ page = (addr / pb_ptr->page_size);
+
+ HDassert(addr == page * pb_ptr->page_size);
+
+ /* Search for page in the hash table */
+ H5PB__SEARCH_INDEX(pb_ptr, page, entry_ptr, FAIL)
+
+ if ( entry_ptr ) {
+
+ HDassert(entry_ptr->addr == addr);
+
+ /* A page or a metadata multi-page with vfd_swmr_writer (case 7) */
+ HDassert( (entry_ptr->size == pb_ptr->page_size) ||
+ (entry_ptr->size > pb_ptr->page_size &&
+ entry_ptr->mem_type != H5FD_MEM_DRAW &&
+ pb_ptr->vfd_swmr_writer) );
+
+ if ( entry_ptr->modified_this_tick ) {
+
+ H5PB__REMOVE_FROM_TL(pb_ptr, entry_ptr, FAIL);
+
+ entry_ptr->modified_this_tick = FALSE;
+ }
+
+ if ( entry_ptr->delay_write_until > 0 ) {
+
+ entry_ptr->delay_write_until = 0;
+
+ H5PB__REMOVE_FROM_DWL(pb_ptr, entry_ptr, FAIL)
+
+ if ( ! ( entry_ptr->is_mpmde ) ) {
+
+ H5PB__UPDATE_RP_FOR_INSERTION(pb_ptr, entry_ptr, FAIL);
+ }
+ }
+
+ /* if the entry is dirty, mark it clean before we evict */
+ if ( ( entry_ptr->is_dirty ) &&
+ ( H5PB__mark_entry_clean(pb_ptr, entry_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "mark entry clean failed")
+
+ if ( H5PB__evict_entry(shared, entry_ptr, TRUE, false) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, "forced eviction failed")
+
+ assert(!shared->vfd_swmr_writer || vfd_swmr_pageno_to_mdf_idx_entry(shared->mdf_idx, shared->mdf_idx_entries_used, page, false) == NULL);
+ }
done:
- if(ret_value < 0)
- if(page_entry)
- page_entry = H5FL_FREE(H5PB_entry_t, page_entry);
FUNC_LEAVE_NOAPI(ret_value)
-} /* H5PB_add_new_page */
+
+} /* H5PB_remove_entry */
+
+herr_t
+H5PB_remove_entries(H5F_shared_t *shared, haddr_t addr, hsize_t size)
+{
+ H5PB_t *pb_ptr;
+ H5PB_entry_t *entry_ptr;
+ herr_t ret_value = SUCCEED;
+ metadata_section_t section[3] = {{0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}};
+ int i;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ pb_ptr = shared->pb_ptr;
+
+ HDassert(addr % pb_ptr->page_size == 0);
+
+ if (size > pb_ptr->page_size) {
+ hlog_fast(pbrm,
+ "removing multipage region [%" PRIuHADDR ", %" PRIuHADDR ")",
+ addr, addr + size);
+ }
+
+ metadata_section_split(pb_ptr->page_size, addr, size, NULL, section);
+
+ for (i = 0; i < 3; i++) {
+ metadata_section_t *iter = &section[i];
+
+ if (iter->len == 0)
+ continue;
+
+ if (iter->len < size) {
+ hlog_fast(pbrm, "removing entry [%" PRIuHADDR ", %" PRIuHADDR ") "
+ "for split region [%" PRIuHADDR ", %" PRIuHADDR ")",
+ iter->addr, iter->addr + iter->len, addr, addr + size);
+ }
+
+ assert(iter->addr % pb_ptr->page_size == 0);
+
+ if (H5PB_remove_entry(shared, iter->addr) < 0)
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, "forced eviction failed")
+ }
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
/*-------------------------------------------------------------------------
- * Function: H5PB_update_entry
*
- * Purpose: In PHDF5, entries that are written by other processes and just
- * marked clean by this process have to have their corresponding
- * pages updated if they exist in the page buffer.
- * This routine checks and update the pages.
+ * Function: H5PB_update_entry
*
- * Return: Non-negative on success/Negative on failure
+ * Purpose: In PHDF5, metadata cache entries that are written by other
+ * processes are simply marked clean in the current process.
+ * However, if the page buffer is enabled, entries marked
+ * clean must still be written to the page buffer so as to
+ * keep the contents of metadata pages consistent on all
+ * processes.
+ *
+ * Do this as follows:
+ *
+ * 1) Test to see if the page buffer is configured to accept
+ * metadata pages. If it isn't, return.
+ *
+ * 2) Test to see if the page buffer contains the page that
+ * contains the supplied metadata cache entry. If it
+ * doesn't, return.
+ *
+ * 3) Write the supplied buffer to page at the appropriate
+ * offset.
*
- * Programmer: Mohamad Chaarawi
+ * Note that at present, page buffering is disabled in the
+ * parallel case. Thus this function has not been tested.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/23/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
-herr_t
-H5PB_update_entry(H5PB_t *page_buf, haddr_t addr, size_t size, const void *buf)
+herr_t
+H5PB_update_entry(H5PB_t *pb_ptr, haddr_t addr, size_t size, const void *buf)
{
- H5PB_entry_t *page_entry; /* Pointer to the corresponding page entry */
+ uint64_t page;
+ size_t offset;
+ H5PB_entry_t *entry_ptr = NULL;
haddr_t page_addr;
+ herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_NOAPI_NOERR
+ FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
- HDassert(page_buf);
- HDassert(size <= page_buf->page_size);
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(size > 0);
+ HDassert(size <= pb_ptr->page_size);
HDassert(buf);
- /* calculate the aligned address of the first page */
- page_addr = (addr / page_buf->page_size) * page_buf->page_size;
+ if ( pb_ptr->min_rd_pages < pb_ptr->max_pages ) {
- /* search for the page and update if found */
- page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&page_addr));
- if(page_entry) {
- haddr_t offset;
+ /* page buffer is configured to accept metadata pages */
- HDassert(addr + size <= page_addr + page_buf->page_size);
- offset = addr - page_addr;
- H5MM_memcpy((uint8_t *)page_entry->page_buf_ptr + offset, buf, size);
+ /* Calculate the aligned address of the containing page */
+ page = (addr / pb_ptr->page_size);
+ page_addr = page * pb_ptr->page_size;
- /* move to top of LRU list */
- H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry)
- } /* end if */
+ H5PB__SEARCH_INDEX(pb_ptr, page, entry_ptr, FAIL)
+
+ if ( entry_ptr ) {
+
+ HDassert( entry_ptr->is_metadata );
+ HDassert( ! (entry_ptr->is_mpmde) );
+ HDassert(addr + size <= page_addr + pb_ptr->page_size);
+
+ offset = addr - page_addr;
+
+ HDmemcpy(((uint8_t *)(entry_ptr->image_ptr) + offset),
+ buf, size);
+
+ /* should we mark the page dirty? If so, replace the following
+ * with a call to H5PB__mark_entry_dirty()
+ */
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+ }
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
- FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5PB_update_entry */
/*-------------------------------------------------------------------------
- * Function: H5PB_remove_entry
*
- * Purpose: Remove possible metadata entry with ADDR from the PB cache.
- * This is in response to the data corruption bug from fheap.c
- * with page buffering + page strategy.
- * Note: Large metadata page bypasses the PB cache.
- * Note: Update of raw data page (large or small sized) is handled by the PB cache.
+ * Function: H5PB_vfd_swmr__release_delayed_writes
*
- * Return: Non-negative on success/Negative on failure
+ * Purpose: After the tick list has been released, and before the
+ * beginning of the next tick, we must scan the delayed
+ * write list, and release those entries whose delays have
+ * expired.
*
- * Programmer: Vailin Choi; Feb 2017
+ * Note that pages of metadata, and multi-page metadata entries
+ * are handled differently.
+ *
+ * Regular pages are removed from the delayed write list and
+ * inserted in the replacement policy
+ *
+ * In contrast, multi-page metadata entries are simply
+ * flushed and evicted.
+ *
+ * Since the delayed write list is sorted in decreasing
+ * delay_write_until order, we start our scan at the bottom
+ * of the delayed write list and continue upwards until no
+ * expired entries remain.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 11/15/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
-herr_t
-H5PB_remove_entry(const H5F_shared_t *f_sh, haddr_t addr)
+herr_t
+H5PB_vfd_swmr__release_delayed_writes(H5F_shared_t *shared)
{
- H5PB_t *page_buf; /* Page buffer to operate on */
- H5PB_entry_t *page_entry = NULL; /* Pointer to the page entry being searched */
+ H5PB_t * pb_ptr = NULL;
+ H5PB_entry_t *entry_ptr = NULL;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
- HDassert(f_sh);
- page_buf = f_sh->page_buf;
- HDassert(page_buf);
+ HDassert(shared);
+ HDassert(shared->vfd_swmr);
+ HDassert(shared->vfd_swmr_writer);
- /* Search for address in the skip list */
- page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&addr));
+ pb_ptr = shared->pb_ptr;
- /* If found, remove the entry from the PB cache */
- if(page_entry) {
- HDassert(page_entry->type != H5F_MEM_PAGE_DRAW);
- if(NULL == H5SL_remove(page_buf->slist_ptr, &(page_entry->addr)))
- HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Page Entry is not in skip list")
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(pb_ptr->vfd_swmr_writer);
- /* Remove from LRU list */
- H5PB__REMOVE_LRU(page_buf, page_entry)
- HDassert(H5SL_count(page_buf->slist_ptr) == page_buf->LRU_list_len);
+ while (pb_ptr->dwl_tail_ptr &&
+ pb_ptr->dwl_tail_ptr->delay_write_until <= shared->tick_num) {
- page_buf->meta_count--;
+ entry_ptr = pb_ptr->dwl_tail_ptr;
- page_entry->page_buf_ptr = H5FL_FAC_FREE(page_buf->page_fac, page_entry->page_buf_ptr);
- page_entry = H5FL_FREE(H5PB_entry_t, page_entry);
- } /* end if */
+ HDassert(entry_ptr->is_dirty);
+
+ entry_ptr->delay_write_until = 0;
+
+ H5PB__REMOVE_FROM_DWL(pb_ptr, entry_ptr, FAIL)
+
+ if ( entry_ptr->is_mpmde ) { /* flush and evict now */
+
+ if ( H5PB__flush_entry(shared, pb_ptr, entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, \
+ "flush of mpmde failed")
+
+ if ( H5PB__evict_entry(shared, entry_ptr, TRUE, false) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "eviction of mpmde failed")
+
+ } else { /* insert it in the replacement policy */
+
+ H5PB__UPDATE_RP_FOR_INSERT_APPEND(pb_ptr, entry_ptr, FAIL)
+ }
+ }
done:
+
FUNC_LEAVE_NOAPI(ret_value)
-} /* H5PB_remove_entry */
+
+} /* H5PB_vfd_swmr__release_delayed_writes() */
/*-------------------------------------------------------------------------
- * Function: H5PB_read
*
- * Purpose: Reads in the data from the page containing it if it exists
- * in the PB cache; otherwise reads in the page through the VFD.
+ * Function: H5PB_vfd_swmr__release_tick_list
*
- * Return: Non-negative on success/Negative on failure
+ * Purpose: After the metadata file has been updated, and before the
+ * beginning of the next tick, we must release the tick list.
+ *
+ * This function performs this function.
+ *
+ * In passing, flush and evict any multi-page metadata entries
+ * that are not subject to a delayed write.
*
- * Programmer: Mohamad Chaarawi
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 11/12/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
-herr_t
-H5PB_read(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr, size_t size, void *buf/*out*/)
+herr_t
+H5PB_vfd_swmr__release_tick_list(H5F_shared_t *shared)
{
- H5PB_t *page_buf; /* Page buffering info for this file */
- H5PB_entry_t *page_entry; /* Pointer to the corresponding page entry */
- H5FD_t *file; /* File driver pointer */
- haddr_t first_page_addr, last_page_addr; /* Addresses of the first and last pages covered by I/O */
- haddr_t offset;
- haddr_t search_addr; /* Address of current page */
- hsize_t num_touched_pages; /* Number of pages accessed */
- size_t access_size;
- hbool_t bypass_pb = FALSE; /* Whether to bypass page buffering */
- hsize_t i; /* Local index variable */
+ H5PB_t * pb_ptr = NULL;
+ H5PB_entry_t *entry_ptr = NULL;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
- HDassert(f_sh);
- HDassert(type != H5FD_MEM_GHEAP);
+ HDassert(shared);
+ HDassert(shared->vfd_swmr);
+ HDassert(shared->vfd_swmr_writer);
- /* Get pointer to page buffer info for this file */
- page_buf = f_sh->page_buf;
+ pb_ptr = shared->pb_ptr;
-#ifdef H5_HAVE_PARALLEL
- if(H5F_SHARED_HAS_FEATURE(f_sh, H5FD_FEAT_HAS_MPI)) {
-#if 1
- bypass_pb = TRUE;
-#else
- /* MSC - why this stopped working ? */
- int mpi_size;
-
- if((mpi_size = H5F_shared_mpi_get_size(f_sh)) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "can't retrieve MPI communicator size")
- if(1 != mpi_size)
- bypass_pb = TRUE;
-#endif
- } /* end if */
-#endif
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(pb_ptr->vfd_swmr_writer);
- /* If page buffering is disabled, or the I/O size is larger than that of a
- * single page, or if this is a parallel raw data access, bypass page
- * buffering.
- */
- if(NULL == page_buf || size >= page_buf->page_size ||
- (bypass_pb && H5FD_MEM_DRAW == type)) {
- if(H5F__accum_read(f_sh, type, addr, size, buf) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "read through metadata accumulator failed")
+ /* remove all entries from the tick list */
+ while ( pb_ptr->tl_head_ptr ) {
- /* Update statistics */
- if(page_buf) {
- if(type == H5FD_MEM_DRAW)
- page_buf->bypasses[1] ++;
- else
- page_buf->bypasses[0] ++;
- } /* end if */
-
- /* If page buffering is disabled, or if this is a large metadata access,
- * or if this is parallel raw data access, we are done here
- */
- if(NULL == page_buf || (size >= page_buf->page_size && H5FD_MEM_DRAW != type) ||
- (bypass_pb && H5FD_MEM_DRAW == type))
- HGOTO_DONE(SUCCEED)
- } /* end if */
+ entry_ptr = pb_ptr->tl_head_ptr;
- /* Update statistics */
- if(page_buf) {
- if(type == H5FD_MEM_DRAW)
- page_buf->accesses[1]++;
- else
- page_buf->accesses[0]++;
- } /* end if */
+ H5PB__REMOVE_FROM_TL(pb_ptr, entry_ptr, FAIL)
- /* Calculate the aligned address of the first page */
- first_page_addr = (addr / page_buf->page_size) * page_buf->page_size;
+ entry_ptr->modified_this_tick = FALSE;
- /* For Raw data calculate the aligned address of the last page and
- * the number of pages accessed if more than 1 page is accessed
- */
- if(H5FD_MEM_DRAW == type) {
- last_page_addr = ((addr + size - 1) / page_buf->page_size) * page_buf->page_size;
-
- /* How many pages does this write span */
- num_touched_pages = (last_page_addr / page_buf->page_size + 1) -
- (first_page_addr / page_buf->page_size);
- if(first_page_addr == last_page_addr) {
- HDassert(1 == num_touched_pages);
- last_page_addr = HADDR_UNDEF;
- } /* end if */
- } /* end if */
- /* Otherwise set last page addr to HADDR_UNDEF */
- else {
- num_touched_pages = 1;
- last_page_addr = HADDR_UNDEF;
- } /* end else */
+ if ( entry_ptr->is_mpmde ) {
+
+ HDassert(entry_ptr->is_dirty);
+
+ if ( entry_ptr->delay_write_until == 0 ) {
+
+ /* flush and evict the multi-page metadata entry immediately */
+ if ( H5PB__flush_entry(shared, pb_ptr, entry_ptr) < 0 )
- /* Translate to file driver I/O info object */
- file = f_sh->lf;
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, \
+ "flush of mpmde failed")
- /* Copy raw data from dirty pages into the read buffer if the read
- request spans pages in the page buffer*/
- if(H5FD_MEM_DRAW == type && size >= page_buf->page_size) {
- H5SL_node_t *node;
+ if ( H5PB__evict_entry(shared, entry_ptr, TRUE, false) < 0 )
- /* For each touched page in the page buffer, check if it
- * exists in the page Buffer and is dirty. If it does, we
- * update the buffer with what's in the page so we get the up
- * to date data into the buffer after the big read from the file.
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "eviction of mpmde failed")
+ }
+ }
+ /* if the entry is not a multi-page metadata entry, it must already
+ * be on either the replacment policy or the delayed write list.
+ * In either case, it will be flush when possible and necessary.
*/
- node = H5SL_find(page_buf->slist_ptr, (void *)(&first_page_addr));
- for(i = 0; i < num_touched_pages; i++) {
- search_addr = i*page_buf->page_size + first_page_addr;
+ }
+
+ HDassert(pb_ptr->tl_head_ptr == NULL);
+ HDassert(pb_ptr->tl_tail_ptr == NULL);
+ HDassert(pb_ptr->tl_len == 0);
+ HDassert(pb_ptr->tl_size == 0);
- /* if we still haven't located a starting page, search again */
- if(!node && i!=0)
- node = H5SL_find(page_buf->slist_ptr, (void *)(&search_addr));
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
- /* if the current page is in the Page Buffer, do the updates */
- if(node) {
- page_entry = (H5PB_entry_t *)H5SL_item(node);
+} /* H5PB_vfd_swmr__release_tick_list */
- HDassert(page_entry);
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB_vfd_swmr__set_tick
+ *
+ * Purpose: At the beginning of each tick, the page buffer must be told
+ * to synchronize its copy of the current tick with that of
+ * the file to which the page buffer belongs.
+ *
+ * This function performs this function.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 11/20/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5PB_vfd_swmr__set_tick(H5F_shared_t *shared)
+{
+ H5PB_t * pb_ptr = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
- /* If the current page address falls out of the access
- block, then there are no more pages to go over */
- if(page_entry->addr >= addr + size)
- break;
+ FUNC_ENTER_NOAPI(FAIL)
- HDassert(page_entry->addr == search_addr);
+ /* Sanity checks */
+ HDassert(shared);
+ HDassert(shared->vfd_swmr);
+ HDassert(shared->vfd_swmr_writer);
- if(page_entry->is_dirty) {
- /* special handling for the first page if it is not a full page access */
- if(i == 0 && first_page_addr != addr) {
- offset = addr - first_page_addr;
- HDassert(page_buf->page_size > offset);
+ pb_ptr = shared->pb_ptr;
- H5MM_memcpy(buf, (uint8_t *)page_entry->page_buf_ptr + offset,
- page_buf->page_size - (size_t)offset);
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(pb_ptr->vfd_swmr_writer);
- /* move to top of LRU list */
- H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry)
- } /* end if */
- /* special handling for the last page if it is not a full page access */
- else if(num_touched_pages > 1 && i == num_touched_pages-1 && search_addr < addr+size) {
- offset = (num_touched_pages-2)*page_buf->page_size +
- (page_buf->page_size - (addr - first_page_addr));
+ /* the tick must always increase by 1 -- verify this */
+ if ( shared->tick_num != pb_ptr->cur_tick + 1 )
- H5MM_memcpy((uint8_t *)buf + offset, page_entry->page_buf_ptr,
- (size_t)((addr + size) - last_page_addr));
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "shared->tick_num (%" PRIu64 ") != (%" PRIu64 ") pb_ptr->cur_tick + 1 ?!?!", shared->tick_num, pb_ptr->cur_tick)
- /* move to top of LRU list */
- H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry)
- } /* end else-if */
- /* copy the entire fully accessed pages */
- else {
- offset = i*page_buf->page_size;
-
- H5MM_memcpy((uint8_t *)buf+(i*page_buf->page_size) , page_entry->page_buf_ptr,
- page_buf->page_size);
- } /* end else */
- } /* end if */
- node = H5SL_next(node);
- } /* end if */
- } /* end for */
- } /* end if */
- else {
- /* A raw data access could span 1 or 2 PB entries at this point so
- we need to handle that */
- HDassert(1 == num_touched_pages || 2 == num_touched_pages);
- for(i = 0 ; i < num_touched_pages; i++) {
- haddr_t buf_offset;
-
- /* Calculate the aligned address of the page to search for it in the skip list */
- search_addr = (0==i ? first_page_addr : last_page_addr);
-
- /* Calculate the access size if the access spans more than 1 page */
- if(1 == num_touched_pages)
- access_size = size;
- else
- access_size = (0 == i ? (size_t)((first_page_addr + page_buf->page_size) - addr) : (size - access_size));
-
- /* Lookup the page in the skip list */
- page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&search_addr));
-
- /* if found */
- if(page_entry) {
- offset = (0 == i ? addr - page_entry->addr : 0);
- buf_offset = (0 == i ? 0 : size - access_size);
-
- /* copy the requested data from the page into the input buffer */
- H5MM_memcpy((uint8_t *)buf + buf_offset, (uint8_t *)page_entry->page_buf_ptr + offset, access_size);
-
- /* Update LRU */
- H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry)
-
- /* Update statistics */
- if(type == H5FD_MEM_DRAW)
- page_buf->hits[1]++;
- else
- page_buf->hits[0]++;
- } /* end if */
- /* if not found */
- else {
- void *new_page_buf = NULL;
- size_t page_size = page_buf->page_size;
- haddr_t eoa;
-
- /* make space for new entry */
- if((H5SL_count(page_buf->slist_ptr) * page_buf->page_size) >= page_buf->max_size) {
- htri_t can_make_space;
-
- /* check if we can make space in page buffer */
- if((can_make_space = H5PB__make_space(f_sh, page_buf, type)) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "make space in Page buffer Failed")
-
- /* if make_space returns 0, then we can't use the page
- buffer for this I/O and we need to bypass */
- if(0 == can_make_space) {
- /* make space can't return FALSE on second touched page since the first is of the same type */
- HDassert(0 == i);
-
- /* read entire block from VFD and return */
- if(H5FD_read(file, type, addr, size, buf) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "driver read request failed")
-
- /* Break out of loop */
- break;
- } /* end if */
- } /* end if */
-
- /* Read page from VFD */
- if(NULL == (new_page_buf = H5FL_FAC_MALLOC(page_buf->page_fac)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTALLOC, FAIL, "memory allocation failed for page buffer entry")
-
- /* Read page through the VFD layer, but make sure we don't read past the EOA. */
-
- /* Retrieve the 'eoa' for the file */
- if(HADDR_UNDEF == (eoa = H5F_shared_get_eoa(f_sh, type)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "driver get_eoa request failed")
-
- /* If the entire page falls outside the EOA, then fail */
- if(search_addr > eoa)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_BADVALUE, FAIL, "reading an entire page that is outside the file EOA")
-
- /* Adjust the read size to not go beyond the EOA */
- if(search_addr + page_size > eoa)
- page_size = (size_t)(eoa - search_addr);
-
- /* Read page from VFD */
- if(H5FD_read(file, type, search_addr, page_size, new_page_buf) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "driver read request failed")
-
- /* Copy the requested data from the page into the input buffer */
- offset = (0 == i ? addr - search_addr : 0);
- buf_offset = (0 == i ? 0 : size - access_size);
- H5MM_memcpy((uint8_t *)buf + buf_offset, (uint8_t *)new_page_buf + offset, access_size);
-
- /* Create the new PB entry */
- if(NULL == (page_entry = H5FL_CALLOC(H5PB_entry_t)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "memory allocation failed")
-
- page_entry->page_buf_ptr = new_page_buf;
- page_entry->addr = search_addr;
- page_entry->type = (H5F_mem_page_t)type;
- page_entry->is_dirty = FALSE;
-
- /* Insert page into PB */
- if(H5PB__insert_entry(page_buf, page_entry) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTSET, FAIL, "error inserting new page in page buffer")
-
- /* Update statistics */
- if(type == H5FD_MEM_DRAW)
- page_buf->misses[1]++;
- else
- page_buf->misses[0]++;
- } /* end else */
- } /* end for */
- } /* end else */
+ pb_ptr->cur_tick = shared->tick_num;
done:
+
FUNC_LEAVE_NOAPI(ret_value)
-} /* end H5PB_read() */
+
+} /* H5PB_vfd_swmr__release_tick_list */
/*-------------------------------------------------------------------------
- * Function: H5PB_write
*
- * Purpose: Write data into the Page Buffer. If the page exists in the
- * cache, update it; otherwise read it from disk, update it, and
- * insert into cache.
+ * Function: H5PB_vfd_swmr__update_index
*
- * Return: Non-negative on success/Negative on failure
+ * Purpose: In the VFD SWMR writer, all metadata writes to the page
+ * buffer during a tick are buffered in the page buffer in
+ * the tick list. Further, the metadata cache is flushed
+ * to the page buffer at the end of the tick so that all
+ * metadata changes during the tick are reflected in the
+ * tick list.
+ *
+ * Once this is done, the internal representation of the
+ * metadata file index must be updated from the tick list
+ * so that the metadata file can be updated, and the tick
+ * list can be emptied and prepared to buffer metadata changes
+ * in the next tick.
+ *
+ * This function is called to accomplish this. Its cycle of
+ * operation is as follows:
+ *
+ * 1) Scan the tick list. For each entry (*entry), test
+ * to see if it appears in the index.
+ *
+ * If it does the entry must have been modified in the
+ * past tick. Update the index entry (*ie_ptr) as follows:
+ *
+ * a) Set ie_ptr->entry_ptr = entry->image_ptr. This
+ * is needed to give the metadata file update code
+ * access to the image of the target page or multi-page
+ * multi-date entry. Note that ie_ptr->entry_ptr will
+ * be set to NULL as soon as the metadata file is updated,
+ * so the buffer pointed to by entry->image_ptr can
+ * be safely discarded at any time after the metadata
+ * file update.
+ *
+ * b) Set ie_ptr->tick_of_last_change to the current tick.
+ *
+ * c) If entry->is_dirty, set ie_ptr->clean to FALSE.
+ * If entry->is_dirty is FALSE, set ie_ptr->clean
+ * to TRUE and set ie_ptr->tick_of_last_flush to the
+ * current tick.
+ *
+ * If the tick list entry (*entry) doesn't appear in
+ * the index, allocate a metadata file index entry (*ie_ptr),
+ * and initialize it as follows:
+ *
+ * ie_ptr->hdf5_page_offset = entry->page
+ * ie_ptr->length = entry->size
+ * ie_ptr->delayed_flush = entry->delay_write_until
+ *
+ * and then update the new entry as per the existing entry
+ * case described above.
+ *
+ * 2) Scan the internal representation of the metadata file
+ * index for entries that do not appear in the tick list.
+ * For each such entry (*ie_ptr), proceed as follows:
+ *
+ * 1) If ie_ptr->clean, we are done -- proceed to the
+ * next index entry that doesn't appear in the tick list.
+ *
+ * 2) Test to see if the cognate entry appears in the page
+ * buffer. If it doesn't, it must have been flushed and
+ * evicted in the past tick. Set
+ *
+ * ie_ptr->clean = TRUE, and
+ *
+ * ie_ptr->tick_of_last_flush = current tick
+ *
+ * and proceed to the next index entry that doesn't
+ * appear in the tick list.
+ *
+ * 3) If the cognate entry does appear in the page buffer
+ * and is clean, proceed as per 2) above.
+ *
+ * 4) In all other cases, do nothing, and proceed to the
+ * next index entry that does not appear in the tick list.
+ *
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 11/9/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5PB_vfd_swmr__update_index(H5F_t *f,
+ uint32_t * idx_ent_added_ptr,
+ uint32_t * idx_ent_modified_ptr,
+ uint32_t * idx_ent_not_in_tl_ptr,
+ uint32_t * idx_ent_not_in_tl_flushed_ptr)
+{
+ H5F_shared_t * const shared = f->shared;
+ const uint64_t tick_num = shared->tick_num;
+ uint32_t i;
+ uint32_t idx_ent_added = 0;
+ uint32_t idx_ent_modified = 0;
+ uint32_t idx_ent_not_in_tl = 0;
+ uint32_t idx_ent_not_in_tl_flushed = 0;
+ H5PB_t * pb_ptr = NULL;
+ H5PB_entry_t *entry;
+ H5FD_vfd_swmr_idx_entry_t * ie_ptr = NULL;
+ H5FD_vfd_swmr_idx_entry_t * idx = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ HDassert(shared->vfd_swmr);
+ HDassert(shared->vfd_swmr_writer);
+
+ idx = shared->mdf_idx;
+
+ HDassert(idx);
+
+ pb_ptr = shared->pb_ptr;
+
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(pb_ptr->vfd_swmr_writer);
+
+ HDassert(idx_ent_added_ptr);
+ HDassert(idx_ent_modified_ptr);
+ HDassert(idx_ent_not_in_tl_ptr);
+ HDassert(idx_ent_not_in_tl_flushed_ptr);
+
+ /* scan the tick list and insert or update metadata file index entries
+ * as appropriate.
+ */
+
+ for (entry = pb_ptr->tl_head_ptr; entry != NULL; entry = entry->tl_next) {
+ uint64_t target_page = entry->page;
+
+ HDassert(entry->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+
+ /* see if the shadow index already contains an entry for *entry. */
+
+ ie_ptr = vfd_swmr_pageno_to_mdf_idx_entry(idx,
+ shared->mdf_idx_entries_used, target_page, false);
+
+ if ( ie_ptr == NULL ) { /* alloc new entry in the metadata file index*/
+ uint32_t new_index_entry_index;
+
+ new_index_entry_index = shared->mdf_idx_entries_used +
+ idx_ent_added++;
+
+ if (new_index_entry_index >= shared->mdf_idx_len &&
+ (idx = vfd_swmr_enlarge_shadow_index(f)) == NULL) {
+ HDfprintf(stderr,
+ "\n\nmax mdf index len (%" PRIu32 ") exceeded.\n\n",
+ shared->mdf_idx_len);
+ HDfprintf(stderr, "tick = %" PRIu64 ".\n", tick_num);
+ exit(EXIT_FAILURE);
+ }
+
+ ie_ptr = idx + new_index_entry_index;
+
+ /* partial initialization of new entry -- rest done later */
+ ie_ptr->hdf5_page_offset = target_page;
+ ie_ptr->md_file_page_offset = 0; /* undefined at this point */
+ ie_ptr->chksum = 0; /* undefined at this point */
+ /* ie_ptr->entry_ptr initialized below */
+ /* ie_ptr->tick_of_last_change initialized below */
+ /* ie_ptr->clean initialized below */
+ /* ie_ptr->tick_of_last_flush initialized below */
+ ie_ptr->delayed_flush = entry->delay_write_until;
+ ie_ptr->moved_to_lower_file = false;
+ ie_ptr->garbage = false;
+ ie_ptr->length = (uint32_t)entry->size;
+
+ } else {
+ /* If entry->size changed, discard the too-small (too-big?)
+ * shadow region and set the shadow-file page number to 0
+ * so that H5F_update_vfd_swmr_metadata_file() will
+ * allocate a new one.
+ */
+ if (ie_ptr->length != (uint32_t)entry->size) {
+ int ret;
+
+ ret = shadow_image_defer_free(shared, ie_ptr);
+ HDassert(ret == 0);
+
+ ie_ptr->md_file_page_offset = 0;
+ ie_ptr->length = (uint32_t)entry->size;
+ }
+
+ idx_ent_modified++;
+ }
+
+ ie_ptr->entry_ptr = entry->image_ptr;
+ ie_ptr->tick_of_last_change = tick_num;
+ assert(entry->is_dirty);
+ ie_ptr->clean = false;
+ ie_ptr->tick_of_last_flush = 0;
+ }
+
+ /* scan the metadata file index for entries that don't appear in the
+ * tick list. If the index entry is dirty, and either doesn't appear
+ * in the page buffer, or is clean in the page buffer, mark the index
+ * entry clean and as having been flushed in the current tick.
+ */
+ for ( i = 0; i < shared->mdf_idx_entries_used; i++ ) {
+
+ HDassert(i == 0 ||
+ idx[i - 1].hdf5_page_offset < idx[i].hdf5_page_offset);
+
+ ie_ptr = idx + i;
+
+ if (ie_ptr->tick_of_last_change == tick_num)
+ continue;
+
+ idx_ent_not_in_tl++;
+
+ if (ie_ptr->clean)
+ continue;
+
+ H5PB__SEARCH_INDEX(pb_ptr, ie_ptr->hdf5_page_offset, entry, FAIL);
+
+ if (entry == NULL || !entry->is_dirty) {
+ hlog_fast(shadow_index_reclaim,
+ "Marking shadow index slot %" PRIu32 " clean at tick %" PRIu64,
+ i, tick_num);
+ idx_ent_not_in_tl_flushed++;
+ ie_ptr->clean = TRUE;
+ ie_ptr->tick_of_last_flush = tick_num;
+ }
+ }
+
+ HDassert(idx_ent_modified + idx_ent_not_in_tl ==
+ shared->mdf_idx_entries_used);
+
+ HDassert(idx_ent_modified + idx_ent_not_in_tl + idx_ent_added <=
+ shared->mdf_idx_len);
+
+ *idx_ent_added_ptr = idx_ent_added;
+ *idx_ent_modified_ptr = idx_ent_modified;
+ *idx_ent_not_in_tl_ptr = idx_ent_not_in_tl;
+ *idx_ent_not_in_tl_flushed_ptr = idx_ent_not_in_tl_flushed;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB_write
*
- * Programmer: Mohamad Chaarawi
+ * Purpose: Write data into the Page Buffer if practical, and to file
+ * otherwise. Specifically:
+ *
+ * 1) If the page buffer is disabled, simply write to the
+ * HDF5 file and return.
+ *
+ * 2) If the write is raw data, and the page buffer is
+ * configured for metadata only (i.e. min_md_pages ==
+ * max_pages), simply write to the HDF5 file and return.
+ *
+ * 3) If the write is raw data, and it of page size or
+ * larger, write directly from the HDF5 file.
+ *
+ * It is possible that the write intersects one or more
+ * pages in the page buffer -- test for this and update
+ * any partially written pages, and evict any pages
+ * that are completely overwritten.
+ *
+ * Note that no pages are inserted into the page buffer in
+ * this case.
+ *
+ * 4) If the write is of raw data, and it is of size less
+ * than the page size, write the page into the page
+ * buffer, loading and inserting pages into the
+ * page buffer as necessary
+ *
+ * 5) If the write is of metadata, and the page buffer is
+ * configured for raw data only (i.e. min_rd_pages ==
+ * max_pages), simply write to the HDF5 file and return.
+ *
+ * 6) If the write is of metadata, the write is larger than
+ * one page, and vfd_swmr_writer is FALSE, simply read
+ * from the HDF5 file. There is no need to check the
+ * page buffer, as metadata is always read atomically,
+ * and entries of this size are not buffered in the page
+ * buffer.
+ *
+ * 7) If the write is of metadata, the write is larger than
+ * one page, and vfd_swmr_writer is TRUE, the write must
+ * buffered in the page buffer until the end of the tick.
+ *
+ * If it doesn't exist already, create a multi-page metadata
+ * entry in the page buffer and copy the write into it.
+ * Insert the new entry in the tick list if necessary.
+ *
+ * Test to see if the write of the multi-page metadata
+ * entry must be delayed. If so, place the entry in
+ * the delayed write list. Otherwise, the multi-page
+ * metadata entry will be written to the HDF5 file and
+ * evicted when the tick list is released at the of the
+ * tick.
+ *
+ *
+ * 8) If the write is of metadata, and the write is of size
+ * less than or equal to the page size, write the data
+ * into the page buffer, loading and inserting a page
+ * if necessary.
+ *
+ * If, in addition, vfd_swmr_writer is TRUE, add the page
+ * touched by the write to the tick list.
+ *
+ * Observe that this function handles casses 1, 2, 5, and 6
+ * directly, calls H5PB_write_raw() for cases 3 & 4, and
+ * calls H5PB_read_meta() for cases 7, and 8.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/11/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
herr_t
-H5PB_write(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr,
- size_t size, const void *buf)
+H5PB_write(H5F_shared_t *shared, H5FD_mem_t type, haddr_t addr, size_t size,
+ const void *buf)
{
- H5PB_t *page_buf; /* Page buffering info for this file */
- H5PB_entry_t *page_entry; /* Pointer to the corresponding page entry */
- H5FD_t *file; /* File driver pointer */
- haddr_t first_page_addr, last_page_addr; /* Addresses of the first and last pages covered by I/O */
- haddr_t offset;
- haddr_t search_addr; /* Address of current page */
- hsize_t num_touched_pages; /* Number of pages accessed */
- size_t access_size;
+ H5PB_t *pb_ptr; /* Page buffer for this file */
hbool_t bypass_pb = FALSE; /* Whether to bypass page buffering */
- hsize_t i; /* Local index variable */
- herr_t ret_value = SUCCEED; /* Return value */
+ herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
- /* Sanity checks */
- HDassert(f_sh);
+ hlog_fast(pbwr, "%s %p type %d addr %" PRIuHADDR " size %zu",
+ __func__, (void *)shared, type, addr, size);
+
+ pb_ptr = shared->pb_ptr;
+
+ if (pb_ptr != NULL && type != H5FD_MEM_DRAW)
+ H5PB_count_meta_access_by_size(pb_ptr, size);
+
+ if ( pb_ptr == NULL ) {
+
+ bypass_pb = TRUE; /* case 1) -- page buffer is disabled */
+
+ } else {
+
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+
+ if ( H5FD_MEM_DRAW == type ) { /* raw data write */
- /* Get pointer to page buffer info for this file */
- page_buf = f_sh->page_buf;
+ if ( pb_ptr->min_md_pages == pb_ptr->max_pages ) {
+
+ /* case 2) -- page buffer configured for metadata only */
+ bypass_pb = TRUE;
+
+ }
+ } else { /* metadata write */
+
+ if ( pb_ptr->min_rd_pages == pb_ptr->max_pages ) {
+
+ /* case 5) -- page buffer configured for raw data only */
+ bypass_pb = TRUE;
+
+ } else if ( ( size >= pb_ptr->page_size ) &&
+ ( ! ( pb_ptr->vfd_swmr_writer ) ) ) {
+
+ /* case 6) -- md read larger than one page and
+ * pb_ptr->vfd_swmr_writer is FALSE.
+ */
+ bypass_pb = TRUE;
+ }
+ }
+ }
#ifdef H5_HAVE_PARALLEL
- if(H5F_SHARED_HAS_FEATURE(f_sh, H5FD_FEAT_HAS_MPI)) {
-#if 1
+ /* at present, the page buffer must be disabled in the parallel case.
+ * However, just in case ...
+ */
+ if(H5F_SHARED_HAS_FEATURE(shared, H5FD_FEAT_HAS_MPI)) {
+
bypass_pb = TRUE;
-#else
- /* MSC - why this stopped working ? */
- int mpi_size;
-
- if((mpi_size = H5F_shared_mpi_get_size(f_sh)) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "can't retrieve MPI communicator size")
- if(1 != mpi_size)
- bypass_pb = TRUE;
-#endif
+
} /* end if */
-#endif
+#endif /* H5_HAVE_PARALLEL */
- /* If page buffering is disabled, or the I/O size is larger than that of a
- * single page, or if this is a parallel raw data access, bypass page
- * buffering.
- */
- if(NULL == page_buf || size >= page_buf->page_size || bypass_pb) {
- if(H5F__accum_write(f_sh, type, addr, size, buf) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "write through metadata accumulator failed")
+ if ( bypass_pb ) { /* cases 1, 2. 5, and 6 */
+
+ if ( H5FD_write(shared->lf, type, addr, size, buf) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL,
+ "write through lower VFD failed")
/* Update statistics */
- if(page_buf) {
- if(type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP)
- page_buf->bypasses[1]++;
- else
- page_buf->bypasses[0]++;
- } /* end if */
-
- /* If page buffering is disabled, or if this is a large metadata access,
- * or if this is a parallel raw data access, we are done here
+ if ( pb_ptr ) {
+
+ H5PB__UPDATE_STATS_FOR_BYPASS(pb_ptr, type, size);
+ }
+ } else {
+
+ if ( H5FD_MEM_DRAW == type ) { /* cases 3 and 4 */
+
+ if ( H5PB__write_raw(shared, type, addr, size, buf) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, \
+ "H5PB_read_raw() failed")
+
+ } else { /* cases 7, and 8 */
+
+ if ( metadata_multipart_write(shared, type, addr, size, buf) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, \
+ "H5PB_read_meta() failed")
+ }
+
+ H5PB__UPDATE_STATS_FOR_ACCESS(pb_ptr, type, size);
+ }
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* end H5PB_write() */
+
+
+/**************************************************************************/
+/***************************** STATIC FUNCTIONS ***************************/
+/**************************************************************************/
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__allocate_page
+ *
+ * Purpose: Allocate an instance of H5PB_entry_t and its associated
+ * buffer. The supplied size must be greater than or
+ * equal to pb_ptr->page_size, and equal to that value if
+ * pb_ptr->vfd_swmr_writer is FALSE.
+ *
+ * The associated buffer is zeroed if clean_image is TRUE.
+ *
+ * Return: Pointer to the newly allocated instance of H5PB_entry_t
+ * on success, and NULL on failure.
+ *
+ * Programmer: John Mainzer -- 10/12/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static H5PB_entry_t *
+H5PB__allocate_page(H5PB_t *pb_ptr, size_t size, hbool_t clean_image)
+{
+ H5PB_entry_t *entry_ptr = NULL;
+ void * image_ptr = NULL;
+ H5PB_entry_t *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_NOAPI(NULL)
+
+ /* sanity checks */
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(size >= pb_ptr->page_size);
+ HDassert((size == pb_ptr->page_size) || (pb_ptr->vfd_swmr_writer));
+
+ /* allocate the entry and its associated image buffer */
+ if ( NULL == (entry_ptr = H5FL_MALLOC(H5PB_entry_t)))
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, NULL, \
+ "memory allocation for H5PB_entry_t failed")
+
+ if ( clean_image ) {
+
+ image_ptr = H5MM_calloc(size);
+
+ } else {
+
+ image_ptr = H5MM_malloc(size);
+ }
+
+ if ( NULL == image_ptr )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, NULL, \
+ "memory allocation for page image failed")
+
+ /* initialize the new page buffer entry */
+ entry_ptr->magic = H5PB__H5PB_ENTRY_T_MAGIC;
+ entry_ptr->pb_ptr = pb_ptr;
+ entry_ptr->addr = HADDR_UNDEF;
+ entry_ptr->page = 0;
+ entry_ptr->size = size;
+ entry_ptr->image_ptr = image_ptr;
+ entry_ptr->mem_type = H5FD_MEM_DEFAULT;
+ entry_ptr->is_metadata = FALSE;
+ entry_ptr->is_mpmde = FALSE;
+ entry_ptr->is_dirty = FALSE;
+
+ /* fields supporting the hash table */
+ entry_ptr->ht_prev = NULL;
+ entry_ptr->ht_next = NULL;
+ entry_ptr->il_prev = NULL;
+ entry_ptr->il_next = NULL;
+
+ /* fields supporting replacement policise */
+ entry_ptr->next = NULL;
+ entry_ptr->prev = NULL;
+
+ /* fields supporting VFD SWMR */
+ entry_ptr->is_mpmde = FALSE;
+ entry_ptr->loaded = FALSE;
+ entry_ptr->modified_this_tick = FALSE;
+ entry_ptr->delay_write_until = 0;
+ entry_ptr->tl_next = NULL;
+ entry_ptr->tl_prev = NULL;
+
+ ret_value = entry_ptr;
+
+done:
+
+ if ( NULL == ret_value ) {
+
+ if ( entry_ptr ) {
+
+ entry_ptr->magic = 0;
+ entry_ptr = H5FL_FREE(H5PB_entry_t, entry_ptr);
+ }
+
+ if ( image_ptr ) {
+
+ image_ptr = H5MM_xfree(image_ptr);
+ }
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB__allocate_page() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__create_new_page
+ *
+ * Purpose: Create a new page and insert it in the page buffer with
+ * the specified address and type. If entry_ptr_ptr is not
+ * NULL, return a pointer to the new entry in *entry_ptr_ptr.
+ *
+ * Throw an error if a page already exists at the specified
+ * address.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/12/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5PB__create_new_page(H5PB_t *pb_ptr, haddr_t addr, size_t size,
+ H5FD_mem_t type, hbool_t clean_image, H5PB_entry_t **entry_ptr_ptr)
+{
+ hbool_t inserted_in_index = FALSE;
+ hbool_t inserted_in_lru = FALSE;
+ uint64_t page;
+ H5PB_entry_t *entry_ptr = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+
+ page = (uint64_t)addr / (uint64_t)(pb_ptr->page_size);
+
+ HDassert((uint64_t)(addr) == (page * (uint64_t)(pb_ptr->page_size)));
+
+ HDassert(size >= pb_ptr->page_size);
+ HDassert((size == pb_ptr->page_size) ||
+ ((pb_ptr->vfd_swmr_writer) && (type != H5FD_MEM_DRAW)));
+ HDassert((NULL == entry_ptr_ptr) || (NULL == *entry_ptr_ptr));
+
+ H5PB__SEARCH_INDEX(pb_ptr, page, entry_ptr, FAIL);
+
+ if ( entry_ptr != NULL ) {
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "page buffer already contains a page at the specified address")
+ }
+
+ entry_ptr = H5PB__allocate_page(pb_ptr, size, clean_image);
+
+ if ( NULL == entry_ptr )
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, \
+ "Can't allocate new page buffer entry")
+
+ /* perform additional initialization */
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->pb_ptr == pb_ptr);
+ entry_ptr->addr = addr;
+ entry_ptr->page = page;
+ HDassert(entry_ptr->size == size);
+ HDassert(entry_ptr->image_ptr);
+ entry_ptr->mem_type = type;
+ entry_ptr->is_metadata = (type != H5FD_MEM_DRAW);
+ entry_ptr->is_mpmde = ((entry_ptr->is_metadata) &&
+ (size > pb_ptr->page_size));
+ entry_ptr->is_dirty = FALSE;
+
+ /* insert in the hash table */
+ H5PB__INSERT_IN_INDEX(pb_ptr, entry_ptr, FAIL)
+ inserted_in_index = TRUE;
+
+ /* insert at the head of the LRU if it isn't a multi-page metadata entry */
+ if ( ! entry_ptr->is_mpmde ) {
+
+ H5PB__UPDATE_RP_FOR_INSERTION(pb_ptr, entry_ptr, FAIL)
+ inserted_in_lru = TRUE;
+ }
+
+ /* updates stats */
+ H5PB__UPDATE_STATS_FOR_INSERTION(pb_ptr, entry_ptr);
+
+ if ( entry_ptr_ptr ) {
+
+ *entry_ptr_ptr = entry_ptr;
+ }
+
+done:
+
+ if ( ret_value < 0 ) {
+
+ if ( entry_ptr ) {
+
+ if ( inserted_in_lru ) {
+
+ H5PB__UPDATE_RP_FOR_EVICTION(pb_ptr, entry_ptr, FAIL);
+ }
+
+ if ( inserted_in_index ) {
+
+ H5PB__DELETE_FROM_INDEX(pb_ptr, entry_ptr, FAIL)
+ }
+
+ H5PB__deallocate_page(entry_ptr);
+ entry_ptr = NULL;
+ }
+ }
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB_add_new_page */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__deallocate_page
+ *
+ * Purpose: Free the supplied instance of H5PB_entry_t and its
+ * associated buffer. The entry must be clean and removed
+ * from the page buffer before this function is called.
+ *
+ * Return: void
+ *
+ * Programmer: John Mainzer -- 10/12/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static void
+H5PB__deallocate_page(H5PB_entry_t *entry_ptr)
+{
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* sanity checks */
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->size > 0);
+ HDassert(entry_ptr->image_ptr);
+ HDassert(!(entry_ptr->is_dirty));
+ HDassert(entry_ptr->ht_next == NULL);
+ HDassert(entry_ptr->ht_prev == NULL);
+ HDassert(entry_ptr->il_next == NULL);
+ HDassert(entry_ptr->il_prev == NULL);
+ HDassert(entry_ptr->next == NULL);
+ HDassert(entry_ptr->prev == NULL);
+ HDassert(entry_ptr->tl_next == NULL);
+ HDassert(entry_ptr->tl_prev == NULL);
+
+ entry_ptr->magic = 0;
+ entry_ptr->image_ptr = H5MM_xfree(entry_ptr->image_ptr);
+ entry_ptr = H5FL_FREE(H5PB_entry_t, entry_ptr);
+
+ FUNC_LEAVE_NOAPI_VOID
+
+} /* H5PB__deallocate_page() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__evict_entry
+ *
+ * Purpose: Evict the target entry from the from the page buffer, and
+ * de-allocate its associated image and instance of
+ * H5PB_entry_t.
+ *
+ * In general, entries must be clean before they can be
+ * evicted, and the minimum metadata and raw data limits
+ * must be respected. Attempts to evict an entry that
+ * that do not respect these constraints will generate
+ * and error unless the force parameter is TRUE, in which
+ * case, these constraints are ignored.
+ *
+ * If `only_mark` is true, then the page-table entry's
+ * corresponding shadow-index entry is not removed. Instead,
+ * it is marked as garbage. This is a stop-gap fix for a
+ * performance problem in H5PB_dest(): deleting all of the
+ * index entries took time quadratic in their number.
+ *
+ * In the context of VFD SWMR, there is also the requirement
+ * that entries to be evicted not be on the tick list, and
+ * also not reside on the delayed write list. In the rare
+ * case in which such a page is discarded by the free space
+ * manager, it must be removed from the tick list and/or the
+ * delayed write list before being evicted by this function.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/14/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5PB__evict_entry(H5F_shared_t *shared, H5PB_entry_t *entry_ptr, bool force,
+ bool only_mark)
+{
+ H5PB_t *pb_ptr = shared->pb_ptr;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* sanity checks */
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->size > 0);
+ HDassert(entry_ptr->image_ptr);
+ /* entries on either the tick list or the delayed write
+ * list may not be evicted -- verify this.
+ */
+ HDassert(!(entry_ptr->modified_this_tick));
+ HDassert(entry_ptr->delay_write_until == 0);
+
+ if ( ( ! force ) && ( entry_ptr->is_dirty ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "Attempt to evict a dirty entry");
+
+ if ( ! force ) {
+
+ /* it is OK to evict an metadata page if pb_ptr->curr_md_pages ==
+ * pb_ptr->min_md_pages - 1 if we are about to replace it with another
+ * metadata page.
+ *
+ * Similarly, it is OK to evict an raw data page if
+ * pb_ptr->curr_rd_pages == pb_ptr->min_rd_pages - 1 if we are
+ * about to replace it with another raw data page.
+ *
+ * Assume sanity checks have been made before this call, and
+ * allow the above without testing the intended replacement.
*/
- if(NULL == page_buf || (size >= page_buf->page_size && H5FD_MEM_DRAW != type) ||
- (bypass_pb && H5FD_MEM_DRAW == type))
- HGOTO_DONE(SUCCEED)
+ if ( ( entry_ptr->is_metadata ) &&
+ ( pb_ptr->curr_md_pages < pb_ptr->min_md_pages ) ) {
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "Attempt to violate min_md_pages");
+
+ } else if ( ( ! entry_ptr->is_metadata ) &&
+ ( pb_ptr->curr_rd_pages < pb_ptr->min_rd_pages ) ) {
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "Attempt to violate min_rd_pages");
+ }
+ } else if ( ( entry_ptr->is_dirty ) &&
+ ( H5PB__mark_entry_clean(pb_ptr, entry_ptr) < 0 ) ) {
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, "mark entry clean failed")
+ }
+
+ /* if the entry is in the replacement policy, remove it */
+ if ( ! (entry_ptr->is_mpmde) ) {
+
+ H5PB__UPDATE_RP_FOR_EVICTION(pb_ptr, entry_ptr, FAIL)
+ }
+
+ /* remove the entry from the hash table */
+ H5PB__DELETE_FROM_INDEX(pb_ptr, entry_ptr, FAIL)
+
+ /* We need to remove the entry from the shadow file index in
+ * the VFD SWMR case.
+ *
+ * If a multipage metadata entry is deallocated, and a new, single-page
+ * metadata entry is allocated at the same base address, then
+ * the old shadow index entry will still tell the size of the previous
+ * image, which is greater than a page, and a shadow-file flush will
+ * access bytes past the end of the entry's image.
+ *
+ * When we add code to allow entries
+ * to age out of the metadata file index, that may provide
+ * code that we can reuse to perform this invalidation.
+ *
+ * It's also possible (I think) for the index-entry size to be set
+ * to one page, and then for a multipage entry to appear later at that
+ * same index entry. The recorded size will still say the same, but
+ * the image will be bigger. So the shadow file will never see the
+ * entire image written, just the first page of the image.
+ */
+ if (shared->vfd_swmr_writer &&
+ shadow_idx_entry_remove(shared, entry_ptr->page, only_mark) == -1) {
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL,
+ "failed to remove shadow index entry")
+ }
-#ifdef H5_HAVE_PARALLEL
- if(bypass_pb) {
- if(H5PB_update_entry(page_buf, addr, size, buf) > 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTUPDATE, FAIL, "failed to update PB with metadata cache")
- HGOTO_DONE(SUCCEED)
- } /* end if */
+ /* update stats for eviction */
+ H5PB__UPDATE_STATS_FOR_EVICTION(pb_ptr, entry_ptr)
+
+ /* deallocate the page */
+ H5PB__deallocate_page(entry_ptr);
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB__evict_entry() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__flush_entry
+ *
+ * Purpose: Flush the target entry to file.
+ *
+ * Under normal circumstances, the entry will be in the
+ * replacement policy. In this, also update the replacement
+ * policy for flush.
+ *
+ * If pb_ptr->vfd_swmr_writer, it is possible that the target
+ * is a multi-page metadata entry. In this case, the entry
+ * is not in the replacement policy, and thus the policy
+ * should not be updated.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/14/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5PB__flush_entry(H5F_shared_t *shared, H5PB_t *pb_ptr, H5PB_entry_t *const entry_ptr)
+{
+ haddr_t eoa; /* Current EOA for the file */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* sanity checks */
+ HDassert(shared);
+ HDassert(shared->lf);
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->size > 0);
+ HDassert(entry_ptr->size >= pb_ptr->page_size);
+ HDassert((entry_ptr->size == pb_ptr->page_size) || (entry_ptr->is_mpmde));
+ HDassert(entry_ptr->image_ptr);
+ HDassert(entry_ptr->is_dirty);
+ HDassert((pb_ptr->vfd_swmr_writer) || (!(entry_ptr->is_mpmde)));
+ HDassert(0 == entry_ptr->delay_write_until);
+
+ hlog_fast(pbflush_entry,
+ "%s: flushing %zu-byte page %" PRIu64 " @ %" PRIuHADDR,
+ __func__, entry_ptr->size, entry_ptr->page, entry_ptr->addr);
+
+ /* Retrieve the 'eoa' for the file */
+ if ( HADDR_UNDEF == (eoa = H5FD_get_eoa(shared->lf, entry_ptr->mem_type)) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, \
+ "driver get_eoa request failed")
+
+ /* TODO: update the free space manager to inform the page buffer when
+ * space is de-allocated so that the following assertions will be
+ * true in all cases.
+ */
+
+ /* Verify that the base addresss of the page is within the EOA. If it
+ * isn't, the associated page has been discarded and should have been
+ * removed from the page buffer. This is a bug in the HDF5 library, so
+ * an assertion is adequate here.
+ */
+ HDassert( eoa > entry_ptr->addr );
+
+ /* Space at the end of the file should be allocate in increments of
+ * pages. Thus the entire page should be within the EOA. Again,
+ * an assertion is adequate here.
+ */
+ HDassert( eoa >= entry_ptr->addr + entry_ptr->size );
+
+ /* flush the entry */
+ if ( H5FD_write(shared->lf, entry_ptr->mem_type, entry_ptr->addr,
+ entry_ptr->size, entry_ptr->image_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "file write failed")
+
+ /* mark the entry clean */
+ if ( H5PB__mark_entry_clean(pb_ptr, entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, "mark entry clean failed")
+
+
+ /* if the entry is on the LRU, update the replacement policy */
+ if (!entry_ptr->is_mpmde) {
+ HDassert(entry_ptr->delay_write_until == 0);
+
+ H5PB__UPDATE_RP_FOR_FLUSH(pb_ptr, entry_ptr, FAIL)
+ }
+
+ /* update stats for flush */
+ H5PB__UPDATE_STATS_FOR_FLUSH(pb_ptr, entry_ptr)
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB__flush_entry() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__load_page
+ *
+ * Purpose: Load the page with the specified base address and insert
+ * it into the page buffer. If necessary and possible, make
+ * space for the new page first.
+ *
+ * Note that the size of the page is always pb_ptr->page_size,
+ * even in the VFD SWMR case, as in this context, multi-page
+ * metadata entries are always written in full, and they
+ * may only enter the page buffer as the result of a write.
+ *
+ * In the context of VFD SWMR, when a page is loaded from
+ * file, it is possible that the VFD SWMR writer must delay
+ * writes to the page to avoid the possibility of message from
+ * the future bugs on the VFD SWMR reader. For this reason,
+ * make note of the fact that the entry has been loaded from
+ * from file, so that the necessary checks can be made when
+ * writing to the page.
+ *
+ * Return: SUCCEED if no errors are encountered, and
+ * FAIL otherwise.
+ *
+ * Programmer: John Mainzer -- 10/18/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5PB__load_page(H5F_shared_t *shared, H5PB_t *pb_ptr, haddr_t addr,
+ H5FD_mem_t type, H5PB_entry_t **entry_ptr_ptr)
+{
+ hbool_t skip_read = FALSE;
+ haddr_t eof = HADDR_UNDEF;
+ H5PB_entry_t *entry_ptr = NULL;
+ void *image_ptr = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* sanity checks */
+ HDassert(shared);
+ HDassert(shared->lf);
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert((entry_ptr_ptr == NULL) || (*entry_ptr_ptr == NULL));
+
+#if 0 /* JRM */
+ haddr_t eoa;
+ /* Retrieve the 'eoa' for the file */
+ if ( HADDR_UNDEF == (eoa = H5FD_get_eoa(shared->lf, type)))
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, \
+ "driver get_eoa request failed")
+ if ( addr + ((haddr_t)(pb_ptr->page_size)) > eoa )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "Attempt to load page that extends past EOA")
+#endif /* JRM */
+ if ( HADDR_UNDEF == (eof = H5FD_get_eof(shared->lf, H5FD_MEM_DEFAULT)) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, \
+ "driver get_eof request failed")
+
+#if 0
+ /* It is possible that this page been allocated but not
+ * written. Skip the read if addr > EOF. In this case, tell
+ * H5PB__create_new_page() to zero the page image.
+ *
+ * Don't set "skip_read = (addr >= eof);" when accumulator is used.
+ */
+ skip_read = (addr >= eof);
#endif
- } /* end if */
- /* Update statistics */
- if(page_buf) {
- if(type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP)
- page_buf->accesses[1]++;
- else
- page_buf->accesses[0]++;
- } /* end if */
+ /* make space in the page buffer if necessary */
+ if ( ( pb_ptr->curr_pages >= pb_ptr->max_pages ) &&
+ ( H5PB__make_space(shared, pb_ptr, type) < 0 ) )
- /* Calculate the aligned address of the first page */
- first_page_addr = (addr / page_buf->page_size) * page_buf->page_size;
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "H5PB__make_space() reports an error")
+
+
+ /* Create a new page buffer page and insert it into the page buffer */
+ if ( H5PB__create_new_page(pb_ptr, addr, (size_t)(pb_ptr->page_size),
+ type, skip_read, &entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "can't create new page buffer page")
+
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->addr == addr);
+
+ image_ptr = entry_ptr->image_ptr;
+
+ HDassert(image_ptr);
- /* For raw data calculate the aligned address of the last page and
- * the number of pages accessed if more than 1 page is accessed
+ /* Read the contents of the page from file, and store it in the
+ * image buffer associated with the new entry.
*/
- if(H5FD_MEM_DRAW == type) {
- last_page_addr = (addr + size - 1) / page_buf->page_size * page_buf->page_size;
-
- /* how many pages does this write span */
- num_touched_pages = (last_page_addr/page_buf->page_size + 1) -
- (first_page_addr / page_buf->page_size);
- if(first_page_addr == last_page_addr) {
- HDassert(1 == num_touched_pages);
- last_page_addr = HADDR_UNDEF;
- } /* end if */
- } /* end if */
- /* Otherwise set last page addr to HADDR_UNDEF */
- else {
- num_touched_pages = 1;
- last_page_addr = HADDR_UNDEF;
- } /* end else */
+ if ( ( ! skip_read ) &&
+ ( H5FD_read(shared->lf, type, addr, entry_ptr->size, image_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "driver read request failed")
+
+ /* If in fact the page was read from file, make note of this fact
+ * for purposes of VFD SWMR delayed writes in the VFD SWMR writer.
+ */
+ entry_ptr->loaded = ! skip_read;
+
+ H5PB__UPDATE_STATS_FOR_LOAD(pb_ptr, entry_ptr)
+
+ if ( entry_ptr_ptr ) {
+
+ *entry_ptr_ptr = entry_ptr;
+ }
- /* Translate to file driver I/O info object */
- file = f_sh->lf;
+done:
+
+ /* add cleanup in case of failure */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB__load_page() */
- /* Check if existing pages for raw data need to be updated since raw data access is not atomic */
- if(H5FD_MEM_DRAW == type && size >= page_buf->page_size) {
- /* For each touched page, check if it exists in the page buffer, and
- * update it with the data in the buffer to keep it up to date
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__make_space
+ *
+ * Purpose: Evict one or more pages from the page buffer so as to
+ * reduce the size of the page buffer to pb_ptr->max_pages - 1.
+ * if possible.
+ *
+ * Note that the function must not be called under
+ * nonsensical conditions -- thus if either
+ *
+ * 1) the inserted type is metadata and min_rd_pages ==
+ * max_pages, or
+ *
+ * 2) the inserted type is raw data and min_md_pages ==
+ * max_pages
+ *
+ * holds, the function has been called in error, and an
+ * assertion failure is appropriate.
+ *
+ * If the page buffer is below its maximum size, we are
+ * done, and the function simply returns.
+ *
+ * Otherwise, scan upwards from the bottom of the LRU list,
+ * examining each entry in turn.
+ *
+ * If the entry is dirty, flush it, move it to the top of the
+ * LRU, and continue with the scan. Note in the VFD SWMR case,
+ * we do not have to concern ourselves with delayed writes in
+ * this context, as all entries which are subject to delayed
+ * writes must reside on the delayed write list, not the LRU list.
+ *
+ * If the entry is:
+ *
+ * 1) clean
+ *
+ * 2) either:
+ *
+ * a) the target entry is metadata and
+ * curr_md_pages > min_md_pages.
+ *
+ * b) the target entry is raw data and
+ * curr_rd_pages > min_rd_pages.
+ *
+ * c) the target entry is metadata, the inserted_type
+ * is metadata, and curr_md_pages == min_md_pages.
+ *
+ * d) the target entry is raw data, the inserted_type
+ * is raw data, and curr_rd_pages == min_rd_pages.
+ *
+ * 3) The entry is not on the tick list (which can only
+ * happen if pb_ptr->vfd_swmr_writer is TRUE).
+ *
+ * evict the entry and test to see if pb_ptr->curr_pages <
+ * pb_ptr->max_pages. If it is, return. Otherwise, continue
+ * the scan until either the above condidtion is fulfilled,
+ * or the head of the LRU is reach.
+ *
+ * Under normal circumstances, it should always be possible
+ * to reduce the size of the page buffer below pb_ptr->max_pages.
+ * However, due to prohibition on evicting entries on the
+ * tick list, and either flushing or evicting entries on the
+ * delayed write list, this will not in general be the case
+ * if pb_ptr->vfd_swmr_writer is TRUE. In this case, the
+ * page buffer may exceed its maximum size by an arbitrary
+ * amount.
+ *
+ * If this situation occurs with any regularity, we will
+ * need a mechanism to avoid attempts to make space when
+ * it is not possible to do so.
+ *
+ * Return: SUCCEED if no errors are encountered, and
+ * FAIL otherwise.
+ *
+ * Programmer: John Mainzer -- 10/14/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5PB__make_space(H5F_shared_t *shared, H5PB_t *pb_ptr, H5FD_mem_t inserted_type)
+{
+ hbool_t inserting_md;
+ H5PB_entry_t *search_ptr;
+ H5PB_entry_t *flush_ptr;
+ H5PB_entry_t *evict_ptr;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* sanity checks */
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(pb_ptr->min_md_pages + pb_ptr->min_rd_pages <= pb_ptr->max_pages);
+
+ inserting_md = ( H5FD_MEM_DRAW != inserted_type );
+
+ if ( ( inserting_md ) && ( pb_ptr->min_rd_pages == pb_ptr->max_pages ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL,
+ "can't make space for metadata -- pb config for raw data only")
+
+ if ( ( ! inserting_md ) && ( pb_ptr->min_md_pages == pb_ptr->max_pages ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL,
+ "can't make space for raw data -- pb config for metadata only")
+
+ search_ptr = pb_ptr->LRU_tail_ptr;
+
+ while ( ( search_ptr ) && ( pb_ptr->curr_pages >= pb_ptr->max_pages ) ) {
+
+ HDassert(search_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+
+ if ( search_ptr->modified_this_tick ) { /* entry is on tick list */
+
+ search_ptr = search_ptr->prev;
+ H5PB__UPDATE_STATS_FOR_LRU_TL_SKIP(pb_ptr);
+
+ } else if ( ( inserting_md ) &&
+ ( ! (search_ptr->is_metadata) ) &&
+ ( pb_ptr->curr_rd_pages <= pb_ptr->min_rd_pages ) ) {
+
+ search_ptr = search_ptr->prev;
+ H5PB__UPDATE_STATS_FOR_LRU_RD_SKIP(pb_ptr);
+
+ } else if ( ( ! inserting_md ) &&
+ ( search_ptr->is_metadata ) &&
+ ( pb_ptr->curr_md_pages <= pb_ptr->min_md_pages ) ) {
+
+ search_ptr = search_ptr->prev;
+ H5PB__UPDATE_STATS_FOR_LRU_MD_SKIP(pb_ptr);
+
+ } else if ( search_ptr->is_dirty ) {
+
+ /* One can make the argument that we should test for dirty
+ * entries first, instead of skipping potentially dirty
+ * entries in the above clauses. However, I suspect that
+ * this would result in excessive flushes. Lets try it
+ * this way for now.
+ */
+
+ flush_ptr = search_ptr;
+
+ /* if the *search_ptr has a predecessor in the LRU,
+ * set set search_ptr equal to search_ptr->prev. Otherwise,
+ * leave search_ptr unchanged, so that it can be examined
+ * on the next pass through the while loop after it has been
+ * flushed.
+ */
+ if ( search_ptr->prev ) {
+
+ search_ptr = search_ptr->prev;
+ }
+
+ if ( H5PB__flush_entry(shared, pb_ptr, flush_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, \
+ "Can't flush entry")
+
+ } else { /* evict the entry */
+
+ evict_ptr = search_ptr;
+ search_ptr = search_ptr->prev;
+ if ( H5PB__evict_entry(shared, evict_ptr, FALSE, false) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, \
+ "Can't evict entry")
+ }
+ }
+
+ HDassert( ( search_ptr == NULL ) ||
+ ( pb_ptr->curr_pages < pb_ptr->max_pages ) );
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB__make_space() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__mark_entry_clean
+ *
+ * Purpose: Mark the target entry clean
+ *
+ * This function is typically used when an entry has been
+ * completely overwritten and is about to be evicted. In
+ * this case, the entry must be marked clean to avoid
+ * sanity check failures on evictions.
+ *
+ * While this function does update the index for the
+ * entry clean, it does not update the replacement policy.
+ * If this is desired, it must be done by the caller.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/14/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5PB__mark_entry_clean(H5PB_t *pb_ptr, H5PB_entry_t *entry_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* sanity checks */
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->size > 0);
+ HDassert(entry_ptr->size >= pb_ptr->page_size);
+ HDassert((entry_ptr->size == pb_ptr->page_size) || (entry_ptr->is_mpmde));
+ HDassert(entry_ptr->image_ptr);
+ HDassert((pb_ptr->vfd_swmr_writer) || (!(entry_ptr->is_mpmde)));
+
+ /* mark the entry clean */
+ entry_ptr->is_dirty = FALSE;
+
+ /* update the index for the entry clean */
+ H5PB__UPDATE_INDEX_FOR_ENTRY_CLEAN(pb_ptr, entry_ptr)
+
+ /* don't update the replacement policy -- this will be done by
+ * the caller if desired.
+ */
+
+done:
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB__mark_entry_clean() */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__mark_entry_dirty
+ *
+ * Purpose: Mark the target entry as dirty.
+ *
+ * If pb_ptr->vfd_swmr_writer is FALSE, the entry will be
+ * in the replacement policy. In this, we simply mark the
+ * entry as dirty, and update the replacement policy for an
+ * access.
+ *
+ * If pb_ptr->vfd_swmr_writer, it is possible that we must
+ * delay writes to the target page or multi-page metadata
+ * entry to avoid message from the future bugs on the VFD
+ * SWMR readers. In such cases we must set the
+ * delay_write_until field and insert the entry on the
+ * delayed write list instead of the replacement policy.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/14/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5PB__mark_entry_dirty(H5F_shared_t *shared, H5PB_t *pb_ptr, H5PB_entry_t *entry_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* sanity checks */
+ HDassert(pb_ptr);
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->size > 0);
+ HDassert(entry_ptr->size >= pb_ptr->page_size);
+ HDassert((entry_ptr->size == pb_ptr->page_size) || (entry_ptr->is_mpmde));
+ HDassert(entry_ptr->image_ptr);
+ HDassert((pb_ptr->vfd_swmr_writer) || (!(entry_ptr->is_mpmde)));
+
+ /* mark the entry dirty if necessary */
+ if ( ! ( entry_ptr->is_dirty ) ) {
+
+ entry_ptr->is_dirty = TRUE;
+
+ H5PB__UPDATE_INDEX_FOR_ENTRY_DIRTY(pb_ptr, entry_ptr)
+
+ /* since the entry was clean, there can be no pending delayed write */
+ HDassert(entry_ptr->delay_write_until == 0);
+
+ if ( ( pb_ptr->vfd_swmr_writer ) &&
+ ( entry_ptr->loaded ) &&
+ ( entry_ptr->mem_type != H5FD_MEM_DRAW ) &&
+ ( H5F_vfd_swmr_writer__delay_write(shared, entry_ptr->page,
+ &(entry_ptr->delay_write_until)) < 0 ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "get delayed write request failed")
+
+ if ( entry_ptr->delay_write_until > 0 ) {
+
+ if ( ! ( entry_ptr->is_mpmde ) ) {
+
+ /* remove the entry from the replacement policy */
+
+ H5PB__UPDATE_RP_FOR_REMOVE(pb_ptr, entry_ptr, FAIL)
+ }
+
+ H5PB__INSERT_IN_DWL(pb_ptr, entry_ptr, FAIL)
+
+ } else if ( ! (entry_ptr->is_mpmde) ) {
+
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+
+ } else {
+
+ /* the entry should be a multi-page metadata entry that
+ * has been modified this tick. Thus no action is required.
+ */
+ HDassert(entry_ptr->is_mpmde);
+ HDassert(pb_ptr->vfd_swmr_writer);
+ }
+ } else if ( ( ! (entry_ptr->is_mpmde) ) &&
+ ( entry_ptr->delay_write_until == 0 ) ) {
+
+ /* the entry is dirty and on the replacement policy -- just update
+ * the replacement policy for an access
*/
- for(i = 0; i < num_touched_pages; i++) {
- search_addr = i * page_buf->page_size + first_page_addr;
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+ }
- /* Special handling for the first page if it is not a full page update */
- if(i == 0 && first_page_addr != addr) {
- /* Lookup the page in the skip list */
- page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&search_addr));
- if(page_entry) {
- offset = addr - first_page_addr;
- HDassert(page_buf->page_size > offset);
-
- /* Update page's data */
- H5MM_memcpy((uint8_t *)page_entry->page_buf_ptr + offset, buf, page_buf->page_size - (size_t)offset);
-
- /* Mark page dirty and push to top of LRU */
- page_entry->is_dirty = TRUE;
- H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry)
- } /* end if */
- } /* end if */
- /* Special handling for the last page if it is not a full page update */
- else if(num_touched_pages > 1 && i == (num_touched_pages - 1) &&
- (search_addr + page_buf->page_size) != (addr + size)) {
- HDassert(search_addr+page_buf->page_size > addr+size);
-
- /* Lookup the page in the skip list */
- page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&search_addr));
- if(page_entry) {
- offset = (num_touched_pages - 2) * page_buf->page_size +
- (page_buf->page_size - (addr - first_page_addr));
-
- /* Update page's data */
- H5MM_memcpy(page_entry->page_buf_ptr, (const uint8_t *)buf + offset,
- (size_t)((addr + size) - last_page_addr));
+done:
- /* Mark page dirty and push to top of LRU */
- page_entry->is_dirty = TRUE;
- H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry)
- } /* end if */
- } /* end else-if */
- /* Discard all fully written pages from the page buffer */
- else {
- page_entry = (H5PB_entry_t *)H5SL_remove(page_buf->slist_ptr, (void *)(&search_addr));
- if(page_entry) {
- /* Remove from LRU list */
- H5PB__REMOVE_LRU(page_buf, page_entry)
-
- /* Decrement page count of appropriate type */
- if(H5F_MEM_PAGE_DRAW == page_entry->type || H5F_MEM_PAGE_GHEAP == page_entry->type)
- page_buf->raw_count--;
- else
- page_buf->meta_count--;
-
- /* Free page info */
- page_entry->page_buf_ptr = H5FL_FAC_FREE(page_buf->page_fac, page_entry->page_buf_ptr);
- page_entry = H5FL_FREE(H5PB_entry_t, page_entry);
- } /* end if */
- } /* end else */
- } /* end for */
- } /* end if */
- else {
- /* An access could span 1 or 2 PBs at this point so we need to handle that */
- HDassert(1 == num_touched_pages || 2 == num_touched_pages);
- for(i = 0; i < num_touched_pages; i++) {
- haddr_t buf_offset;
-
- /* Calculate the aligned address of the page to search for it in the skip list */
- search_addr = (0 == i ? first_page_addr : last_page_addr);
-
- /* Calculate the access size if the access spans more than 1 page */
- if(1 == num_touched_pages)
- access_size = size;
- else
- access_size = (0 == i ? (size_t)(first_page_addr + page_buf->page_size - addr) : (size - access_size));
-
- /* Lookup the page in the skip list */
- page_entry = (H5PB_entry_t *)H5SL_search(page_buf->slist_ptr, (void *)(&search_addr));
-
- /* If found */
- if(page_entry) {
- offset = (0 == i ? addr - page_entry->addr : 0);
- buf_offset = (0 == i ? 0 : size - access_size);
-
- /* Copy the requested data from the input buffer into the page */
- H5MM_memcpy((uint8_t *)page_entry->page_buf_ptr + offset, (const uint8_t *)buf + buf_offset, access_size);
-
- /* Mark page dirty and push to top of LRU */
- page_entry->is_dirty = TRUE;
- H5PB__MOVE_TO_TOP_LRU(page_buf, page_entry)
-
- /* Update statistics */
- if(type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP)
- page_buf->hits[1]++;
- else
- page_buf->hits[0]++;
- } /* end if */
- /* If not found */
- else {
- void *new_page_buf;
- size_t page_size = page_buf->page_size;
-
- /* Make space for new entry */
- if((H5SL_count(page_buf->slist_ptr) * page_buf->page_size) >= page_buf->max_size) {
- htri_t can_make_space;
-
- /* Check if we can make space in page buffer */
- if((can_make_space = H5PB__make_space(f_sh, page_buf, type)) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_NOSPACE, FAIL, "make space in Page buffer Failed")
-
- /* If make_space returns 0, then we can't use the page
- * buffer for this I/O and we need to bypass
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* H5PB__mark_entry_dirty() */
+
+static void
+metadata_section_split(size_t pgsz, haddr_t addr, size_t len, const void *_buf,
+ metadata_section_t *section)
+{
+ int i;
+ size_t totlen = 0;
+ haddr_t whole_pgaddr, tail_pgaddr;
+ const char *buf = _buf;
+ metadata_section_t *head = &section[0], *middle = &section[1],
+ *tail = &section[2];
+
+ /* Try to find the address of the first whole page, and the address of
+ * the page after the last whole page.
+ */
+ whole_pgaddr = roundup(addr, pgsz);
+ tail_pgaddr = rounddown(addr + len, pgsz);
+
+ /* In the degenerate case where the first whole page is "after" the last,
+ * actually the entire access lands between page boundaries.
+ */
+ if (whole_pgaddr > tail_pgaddr) {
+ assert(len < pgsz);
+ head->addr = addr;
+ head->len = len;
+ head->buf = buf;
+ return;
+ }
+
+ /* `head` spans any range beginning before the first page boundary. */
+ if (addr < whole_pgaddr) {
+ head->buf = buf;
+ head->len = pgsz - addr % pgsz;
+ head->addr = addr;
+ }
+
+ /* `middle` spans one or more whole pages in between the end of
+ * `head` and before the beginning of `tail`.
+ */
+ if (whole_pgaddr < tail_pgaddr) {
+ middle->buf = (buf == NULL) ? NULL : &buf[whole_pgaddr - addr];
+ middle->len = tail_pgaddr - whole_pgaddr;
+ middle->addr = whole_pgaddr;
+ }
+
+ /* `tail` spans residual bytes that follow the last page boundary. */
+ if (tail_pgaddr < addr + len) {
+ tail->len = (addr + len) - tail_pgaddr;
+ tail->buf = (buf == NULL) ? NULL : &buf[tail_pgaddr - addr];
+ tail->addr = tail_pgaddr;
+ }
+
+ for (i = 0; i < 3; i++) {
+ metadata_section_t *iter = &section[i];
+ if (iter->len == 0)
+ continue;
+ assert(iter->addr == addr + totlen);
+ assert(iter->buf == ((buf == NULL) ? NULL : &buf[totlen]));
+// assert(i == 0 || iter[-1].buf + iter[-1].len == iter->buf);
+ totlen += iter->len;
+ }
+
+ assert(totlen == len);
+}
+
+static herr_t
+metadata_multipart_read(H5F_shared_t *shared, H5FD_mem_t type, haddr_t addr,
+ size_t len, void *_buf/*out*/)
+{
+ herr_t rc;
+ int i;
+ const size_t pgsz = shared->pb_ptr->page_size;
+ metadata_section_t section[3] = {{0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}};
+
+ metadata_section_split(pgsz, addr, len, _buf, section);
+
+ for (i = 0; i < 3; i++) {
+ metadata_section_t *iter = &section[i];
+ if (iter->buf == NULL)
+ continue;
+ rc = H5PB__read_meta(shared, type, iter->addr, iter->len,
+ (void *)(uintptr_t)iter->buf);
+ if (rc < 0)
+ return rc;
+ }
+
+ return SUCCEED;
+}
+
+static herr_t
+metadata_multipart_write(H5F_shared_t *shared, H5FD_mem_t type,
+ haddr_t addr, size_t len, const void *_buf/*out*/)
+{
+ herr_t rc;
+ int i;
+ const size_t pgsz = shared->pb_ptr->page_size;
+ metadata_section_t section[3] = {{0, 0, NULL}, {0, 0, NULL}, {0, 0, NULL}};
+
+ metadata_section_split(pgsz, addr, len, _buf, section);
+
+ for (i = 0; i < 3; i++) {
+ metadata_section_t *iter = &section[i];
+
+ if (iter->buf == NULL)
+ continue;
+ rc = H5PB__write_meta(shared, type, iter->addr, iter->len, iter->buf);
+ if (rc < 0)
+ return rc;
+ }
+
+ return SUCCEED;
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5PB__read_meta
+ *
+ * Purpose: Satisfy a metadata read in cases 7, 8, 9, and 10)
+ * H5PB_read(). Specifically:
+ *
+ * 6) If the read is for metadata and not page aligned, clip
+ * the read to the end of the current page if necessary.
+ * Load the relevant page if necessary and satisfy the
+ * read from the page buffer. Note that it there is an
+ * existing page, it must not be a multi-page metadata
+ * entry. It it is, flag an error.
+ *
+ * 7) If the read is for metadata, is page aligned, is larger
+ * than one page, and there is no entry in the page buffer,
+ * satisfy the read from the file
+ *
+ * 8) If the read is for metadata, is page aligned, is larger
+ * than one page, and there is a regular entry at the target
+ * page address, test to see if the last read was for the
+ * same address.
+ *
+ * If was, evict the page, and satisfy the read from file.
+ * Flag an error if the page was dirty.
+ *
+ * If the last read was for a different page, clip the read
+ * to one page, and satisfy the read from the existing
+ * regular entry.
+ *
+ * 9) If the read is for metadata, is page aligned, is larger
+ * than one page, and there is a multi-page metadata entry
+ * at the target page address, test to see if
+ * pb_ptr->vfd_swmr_write is TRUE.
+ *
+ * If it is, satisfy the read from the multi-page metadata
+ * entry, clipping the read if necessary.
+ *
+ * if pb_ptr->vfd_swmr_write is FALSE, flag an error.
+ *
+ * 10) If the read is for metadata, is page aligned, is no
+ * larger than a page, test to see if the page buffer
+ * contains a page at the target address.
+ *
+ * If it doesn't, load the page and satisfy the read
+ * from it.
+ *
+ * If it contains a regular page entry, satisfy the read
+ * from it.
+ *
+ * If it contains a multipage metadata entry at the target
+ * address, satisfy the read from the multi-page metadata
+ * entry if pb_ptr->vfd_swmr_write is TRUE, and flag an
+ * error otherwise.
+ *
+ * The above case analysis may be a bit hard to read. If so,
+ * the table shown below may help to clarify. Here:
+ *
+ * P/A == page aligned
+ * size > PL == size > page length
+ * PA == previous address
+ * A == current address
+ *
+ * In the entry exists column:
+ *
+ * N == no entry
+ * R == regular (1 page) entry
+ * MPMDE == multi-page metadata entry
+ *
+ * | size | entry | VFD | |
+ * P/A: | > PL | exists | SWMR | PA == A | Comments:
+ * ------+------+--------+------+---------+-------------------------------------
+ * N | X | N || R | X | X | Clip read to page boundary if
+ * | | | | | necessary
+ * | | | | | Load entry if necessary
+ * | | | | | Satisfy read from entry (case 6)
+ * ------+------+--------+------+---------+-------------------------------------
+ * N | X | MPMDE | X | X | Error (case 6)
+ * ------+------+--------+------+---------+-------------------------------------
+ * | | | | |
+ * ------+------+--------+------+---------+-------------------------------------
+ * Y | Y | N | X | X | Satisfy read from file (case 7)
+ * ------+------+--------+------+---------+-------------------------------------
+ * Y | Y | R | X | N | Clip read to page boundary
+ * | | | | | Satisfy read from entry (case 8)
+ * ------+------+--------+------+---------+-------------------------------------
+ * Y | Y | R | X | Y | Evict entry
+ * | | | | | (must be clean -- flag error if not)
+ * | | | | | Satisfy read from file (case 8)
+ * ------+------+--------+------+---------+-------------------------------------
+ * Y | Y | MPMDE | N | X | Error (case 9)
+ * ------+------+--------+------+---------+-------------------------------------
+ * Y | Y | MPMDE | Y | X | Clip read to MPE size if required.
+ * | | | | | Satify read from MPE (case 9)
+ * ------+------+--------+------+---------+-------------------------------------
+ * | | | | |
+ * ------+------+--------+------+---------+-------------------------------------
+ * Y | N | N | X | X | Load entry
+ * | | | | | Satisfy read from entry (case 10)
+ * ------+------+--------+------+---------+-------------------------------------
+ * Y | N | R | X | X | Satisfy read from entry (case 10)
+ * ------+------+--------+------+---------+-------------------------------------
+ * Y | N | MPMDE | Y | X | Satisfy read from entry (case 10)
+ * ------+------+--------+------+---------+-------------------------------------
+ * Y | N | MPMDE | N | X | Error (case 10)
+ * ------+------+--------+------+---------+-------------------------------------
+ *
+ * Observe that the above cases imply that:
+ *
+ * 1) The page buffer is defined.
+ *
+ * 2) The page buffer has been configured to accept at least
+ * one page of metadata.
+ *
+ * 3) This is a metadata read.
+ *
+ * Note also that if the metadata read is of size
+ * no larger than page size, it may not cross page
+ * boundaries.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/11/18
+ *
+ * Changes: None.
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5PB__read_meta(H5F_shared_t *shared, H5FD_mem_t type, haddr_t addr, size_t size,
+ void *buf/*out*/)
+{
+ H5PB_t *pb_ptr; /* Page buffer for this file */
+ H5PB_entry_t *entry_ptr; /* Pointer to page buffer entry */
+ H5FD_t *file; /* File driver pointer */
+ uint64_t page; /* page offset of addr */
+ haddr_t page_addr; /* page containing addr */
+ static haddr_t prev_addr = HADDR_UNDEF; /* addr of last call */
+ size_t offset; /* offset of read in page */
+ size_t clipped_size; /* possibley clipped size */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(shared);
+ HDassert(shared->pb_ptr);
+
+ pb_ptr = shared->pb_ptr;
+
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(pb_ptr->min_rd_pages < pb_ptr->max_pages);
+ HDassert(shared->lf);
+
+ file = shared->lf;
+
+ HDassert(H5FD_MEM_DRAW != type);
+ HDassert(buf);
+
+ /* Calculate the aligned address of the first page */
+ page = (addr / pb_ptr->page_size);
+ page_addr = page * pb_ptr->page_size;
+
+ if ( page_addr != addr ) { /* case 6 */
+
+ /* If the read is for metadata and not page aligned, clip
+ * the read to the end of the current page if necessary.
+ * Load the relevant page if necessary and satisfy the
+ * read from the page buffer. Note that it there is an
+ * existing page, it must not be a multi-page metadata
+ * entry. It it is, flag an error.
+ */
+
+ offset = addr - page_addr;
+
+ if ( (offset + size) <= pb_ptr->page_size ) {
+
+ clipped_size = size;
+
+ } else {
+
+ clipped_size = size - ( (offset + size) - pb_ptr->page_size);
+ }
+
+ HDassert( clipped_size > 0 );
+ HDassert( clipped_size <= size );
+ HDassert( (offset + clipped_size) <= pb_ptr->page_size );
+
+ /* get the containing page */
+ H5PB__SEARCH_INDEX(pb_ptr, page, entry_ptr, FAIL)
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, ((entry_ptr) != NULL), \
+ TRUE, FALSE)
+
+ if ( ( NULL == entry_ptr ) &&
+ ( H5PB__load_page(shared, pb_ptr, page_addr, type, &entry_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "page buffer page load request failed (1)")
+
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->addr == page_addr);
+ HDassert(entry_ptr->is_metadata);
+ HDassert(!(entry_ptr->is_mpmde));
+
+ /* copy data from the page into read buffer */
+ HDmemcpy((uint8_t *)buf, (uint8_t *)(entry_ptr->image_ptr) + offset,
+ clipped_size);
+
+ /* if the entry is on the LRU, update the replacement policy */
+ if ( ( ! (entry_ptr->is_mpmde) ) &&
+ ( entry_ptr->delay_write_until == 0 ) ) {
+
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+ }
+ } else {
+
+ HDassert( page_addr == addr );
+
+ if ( size >= pb_ptr->page_size ) {
+
+ /* search the page buffer for an entry at page */
+ H5PB__SEARCH_INDEX(pb_ptr, page, entry_ptr, FAIL)
+
+
+ if ( entry_ptr == NULL ) { /* case 7 */
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, FALSE, TRUE, size > pb_ptr->page_size)
+
+ /* If the read is for metadata, is page aligned, is larger
+ * than one page, and there is no entry in the page buffer,
+ * satisfy the read from the file
+ */
+ if ( H5FD_read(file, type, addr, size, buf) < 0)
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "driver read request failed (1)")
+
+ H5PB__UPDATE_STATS_FOR_BYPASS(pb_ptr, type, size);
+ } else {
+
+ HDassert( entry_ptr );
+ HDassert( entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC );
+ HDassert( entry_ptr->is_metadata );
+
+ if ( ! ( entry_ptr->is_mpmde ) ) { /* case 8 */
+
+ /* If the read is for metadata, is page aligned, is larger
+ * than one page, and there is a regular entry at the target
+ * page address, test to see if the last read was for the
+ * same address.
+ *
+ * If it was, evict the page, and satisfy the read from
+ * file. Flag an error if the page was dirty.
+ *
+ * If the last read was for a different page, clip the read
+ * to one page, and satisfy the read from the existing
+ * regular entry.
*/
- if(0 == can_make_space) {
- HDassert(0 == i);
-
- /* Write to VFD and return */
- if(H5FD_write(file, type, addr, size, buf) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "driver write request failed")
-
- /* Break out of loop */
- break;
- } /* end if */
- } /* end if */
-
- /* Don't bother searching if there is no write access */
- if(H5F_ACC_RDWR & H5F_SHARED_INTENT(f_sh))
- /* Lookup & remove the page from the new skip list page if
- * it exists to see if this is a new page from the MF layer
+
+ HDassert( entry_ptr->size == pb_ptr->page_size );
+
+ if ( addr == prev_addr ) {
+
+ /* since this is a second try, don't update
+ * hit rate stats.
+ */
+
+ HDassert( ! ( entry_ptr->is_dirty ) );
+
+ if (H5PB__evict_entry(shared, entry_ptr, TRUE, false) < 0)
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "forced eviction failed (1)")
+ if ( H5FD_read(file, type, addr, size, buf) < 0)
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "driver read request failed (2)")
+
+ H5PB__UPDATE_STATS_FOR_BYPASS(pb_ptr, type, size);
+ } else {
+
+ HDassert( entry_ptr->image_ptr );
+
+ /* copy data from the page into read buffer */
+ HDmemcpy((uint8_t *)buf,
+ (uint8_t *)(entry_ptr->image_ptr),
+ entry_ptr->size);
+
+ /* if the entry is on the LRU, update the replacement
+ * policy
+ */
+ if ( ( ! (entry_ptr->is_mpmde) ) &&
+ ( entry_ptr->delay_write_until == 0 ) ) {
+
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+ }
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, TRUE, TRUE, FALSE)
+ }
+ } else { /* case 9 */
+
+ /* If the read is for metadata, is page aligned, is larger
+ * than one page, and there is a multi-page metadata entry
+ * at the target page address, test to see if
+ * pb_ptr->vfd_swmr_write is TRUE.
+ *
+ * If it is, satisfy the read from the multi-page metadata
+ * entry, clipping the read if necessary.
+ *
+ * if pb_ptr->vfd_swmr_write is FALSE, flag an error.
*/
- page_entry = (H5PB_entry_t *)H5SL_remove(page_buf->mf_slist_ptr, (void *)(&search_addr));
-
- /* Calculate offset into the buffer of the page and the user buffer */
- offset = (0 == i ? addr - search_addr : 0);
- buf_offset = (0 == i ? 0 : size - access_size);
-
- /* If found, then just update the buffer pointer to the newly allocate buffer */
- if(page_entry) {
- /* Allocate space for the page buffer */
- if(NULL == (new_page_buf = H5FL_FAC_MALLOC(page_buf->page_fac)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTALLOC, FAIL, "memory allocation failed for page buffer entry")
- HDmemset(new_page_buf, 0, (size_t)offset);
- HDmemset((uint8_t *)new_page_buf + offset + access_size, 0, page_size - ((size_t)offset + access_size));
-
- page_entry->page_buf_ptr = new_page_buf;
-
- /* Update statistics */
- if(type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP)
- page_buf->hits[1]++;
- else
- page_buf->hits[0]++;
- } /* end if */
- /* Otherwise read page through the VFD layer, but make sure we don't read past the EOA. */
- else {
- haddr_t eoa, eof = HADDR_UNDEF;
-
- /* Allocate space for the page buffer */
- if(NULL == (new_page_buf = H5FL_FAC_CALLOC(page_buf->page_fac)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTALLOC, FAIL, "memory allocation failed for page buffer entry")
-
- /* Create the new loaded PB entry */
- if(NULL == (page_entry = H5FL_CALLOC(H5PB_entry_t)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTALLOC, FAIL, "memory allocation failed")
-
- page_entry->page_buf_ptr = new_page_buf;
- page_entry->addr = search_addr;
- page_entry->type = (H5F_mem_page_t)type;
-
- /* Retrieve the 'eoa' for the file */
- if(HADDR_UNDEF == (eoa = H5F_shared_get_eoa(f_sh, type)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "driver get_eoa request failed")
-
- /* If the entire page falls outside the EOA, then fail */
- if(search_addr > eoa)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_BADVALUE, FAIL, "writing to a page that is outside the file EOA")
-
- /* Retrieve the 'eof' for the file - The MPI-VFD EOF
- * returned will most likely be HADDR_UNDEF, so skip
- * that check.
+ HDassert( entry_ptr->is_mpmde );
+ HDassert( pb_ptr->vfd_swmr_writer );
+
+ if ( size > entry_ptr->size ) {
+
+ clipped_size = entry_ptr->size;
+
+ } else {
+
+ clipped_size = size;
+ }
+
+ /* copy data from the page into read buffer */
+ HDmemcpy((uint8_t *)buf, (uint8_t *)(entry_ptr->image_ptr),
+ clipped_size);
+
+ /* if the entry is on the LRU, update the replacement
+ * policy
*/
- if(!H5F_SHARED_HAS_FEATURE(f_sh, H5FD_FEAT_HAS_MPI))
- if(HADDR_UNDEF == (eof = H5FD_get_eof(f_sh->lf, H5FD_MEM_DEFAULT)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "driver get_eof request failed")
-
- /* Adjust the read size to not go beyond the EOA */
- if(search_addr + page_size > eoa)
- page_size = (size_t)(eoa - search_addr);
-
- if(search_addr < eof) {
- if(H5FD_read(file, type, search_addr, page_size, new_page_buf) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "driver read request failed")
-
- /* Update statistics */
- if(type == H5FD_MEM_DRAW || type == H5FD_MEM_GHEAP)
- page_buf->misses[1]++;
- else
- page_buf->misses[0]++;
- } /* end if */
- } /* end else */
-
- /* Copy the requested data from the page into the input buffer */
- H5MM_memcpy((uint8_t *)new_page_buf + offset, (const uint8_t *)buf+buf_offset, access_size);
-
- /* Page is dirty now */
- page_entry->is_dirty = TRUE;
-
- /* Insert page into PB, evicting other pages as necessary */
- if(H5PB__insert_entry(page_buf, page_entry) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTSET, FAIL, "error inserting new page in page buffer")
- } /* end else */
- } /* end for */
- } /* end else */
+ if ( ( ! (entry_ptr->is_mpmde) ) &&
+ ( entry_ptr->delay_write_until == 0 ) ) {
+
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+ }
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, TRUE, TRUE, TRUE)
+ }
+ }
+ } else { /* case 10 */
+
+ /* If the read is for metadata, is page aligned, is no
+ * larger than a page, test to see if the page buffer
+ * contains a page at the target address.
+ *
+ * If it doesn't, load the page and satisfy the read
+ * from it.
+ *
+ * If it contains a regular page entry, satisfy the read
+ * from it.
+ *
+ * If it contains a multipage metadata entry at the target
+ * address, satisfy the read from the multi-page metadata
+ * entry if pb_ptr->vfd_swmr_write is TRUE, and flag an
+ * error otherwise.
+ */
+ HDassert( size <= pb_ptr->page_size );
+
+ /* get the containing page */
+ H5PB__SEARCH_INDEX(pb_ptr, page, entry_ptr, FAIL)
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, (entry_ptr != NULL), \
+ TRUE, FALSE)
+
+ if ( ( NULL == entry_ptr ) &&
+ ( H5PB__load_page(shared, pb_ptr, page_addr, type, &entry_ptr) < 0))
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "page buffer page load request failed (2)")
+
+ HDassert( entry_ptr );
+ HDassert( entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC );
+ HDassert( entry_ptr->is_metadata );
+ HDassert( ( ! ( entry_ptr->is_mpmde ) ) ||
+ ( pb_ptr->vfd_swmr_writer) );
+
+ /* copy data from the page into read buffer */
+ HDmemcpy((uint8_t *)buf, (uint8_t *)(entry_ptr->image_ptr), size);
+
+ /* if the entry is on the LRU, update the replacement policy */
+ if ( ( ! (entry_ptr->is_mpmde) ) &&
+ ( entry_ptr->delay_write_until == 0 ) ) {
+
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+ }
+ }
+ }
+
+ prev_addr = addr;
done:
+
FUNC_LEAVE_NOAPI(ret_value)
-} /* end H5PB_write() */
+
+} /* end H5PB__read_meta() */
/*-------------------------------------------------------------------------
- * Function: H5PB__insert_entry()
*
- * Purpose: This function was created without documentation.
- * What follows is my best understanding of Mohamad's intent.
+ * Function: H5PB__read_raw
*
- * Insert the supplied page into the page buffer, both the
- * skip list and the LRU.
+ * Purpose: Satisfy a raw data read in cases 3 and 4 from H5PB_read().
+ * Specifically:
*
- * As best I can tell, this function imposes no limit on the
- * number of entries in the page buffer beyond an assertion
- * failure it the page count exceeds the limit.
+ * 3) If the read is for raw data, and it is larger than the
+ * page size, read it directly from the HDF5 file.
*
- * JRM -- 12/22/16
+ * It is possible that the page buffer contains dirty pages
+ * that intersect with the read -- test for this and update
+ * the read buffer from the page buffer if any such pages
+ * exist.
*
+ * Note that no pages are inserted into the page buffer in
+ * this case.
*
- * Return: Non-negative on success/Negative on failure
+ * 4) If the read is for raw data, and it is of size less
+ * than or equal to the page size, satisfy the read from
+ * the page buffer, loading and inserting pages into the
+ * page buffer as necessary
+ *
+ * Observe that this implies that:
*
- * Programmer: Mohamad Chaarawi
+ * 1) The page buffer is defined.
+ *
+ * 2) The page buffer has been configured to accept at least
+ * one page of raw data.
+ *
+ * 2) This is a raw data read.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/11/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
static herr_t
-H5PB__insert_entry(H5PB_t *page_buf, H5PB_entry_t *page_entry)
+H5PB__read_raw(H5F_shared_t *shared, H5FD_mem_t type, haddr_t addr, size_t size,
+ void *buf/*out*/)
{
- herr_t ret_value = SUCCEED; /* Return value */
+ H5PB_t *pb_ptr; /* Page buffer for this file */
+ H5PB_entry_t *entry_ptr; /* Pointer to page buffer entry */
+ uint64_t first_page; /* page offset of first I/O */
+ uint64_t last_page; /* page offset of last I/O */
+ uint64_t search_page; /* page offset of current page */
+ haddr_t first_page_addr; /* address of first page of I/O */
+ haddr_t last_page_addr; /* address of last page of I/O */
+ haddr_t search_addr; /* Address of current page */
+ hsize_t num_touched_pages; /* Number of pages accessed */
+ size_t offset; /* offset of read in page */
+ size_t length; /* length of read in page */
+ hsize_t i; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(shared);
+ HDassert(shared->pb_ptr);
+
+ pb_ptr = shared->pb_ptr;
+
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(pb_ptr->min_md_pages < pb_ptr->max_pages);
+ HDassert(H5FD_MEM_DRAW == type);
+
+
+ /* Calculate the aligned address of the first page */
+ first_page = (addr / pb_ptr->page_size);
+ first_page_addr = first_page * pb_ptr->page_size;
+
+ /* Calculate the aligned address of the last page */
+ last_page = ((addr + size - 1) / pb_ptr->page_size);
+ last_page_addr = last_page * pb_ptr->page_size;
+
+ /* Calculate number of pages that this read spans. */
+ num_touched_pages = last_page - first_page + 1;
+
+ if ( first_page_addr == last_page_addr ) {
+
+ HDassert(1 == num_touched_pages);
+ last_page_addr = HADDR_UNDEF;
+
+ }
+
+ /* case 3) raw data read of page size or greater. */
+ if ( size >= pb_ptr->page_size ) {
+
+ if ( H5FD_read(shared->lf, type, addr, size, buf) < 0)
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, "read failed")
+
+
+ H5PB__UPDATE_STATS_FOR_BYPASS(pb_ptr, type, size);
+
+
+ /* For each page that intersects with the above read, check to see
+ * if it exists in the page buffer, and if so, if it is dirty.
+ *
+ * If it does and is, update the read buffer with the contents
+ * of the page so we get the up to date data into the buffer
+ * after the big read from the file.
+ */
+ search_page = first_page;
+ search_addr = first_page_addr;
+
+ for(i = 0; i < num_touched_pages; i++) {
+
+ H5PB__SEARCH_INDEX(pb_ptr, search_page, entry_ptr, FAIL)
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, (entry_ptr != NULL), \
+ FALSE, FALSE)
+
+ if ( entry_ptr ) {
+
+ HDassert( entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC );
+ HDassert( ! ( entry_ptr->is_metadata ) );
+ HDassert( entry_ptr->page == search_page );
+ HDassert( entry_ptr->addr == search_addr );
+ HDassert( entry_ptr->size == pb_ptr->page_size );
+ HDassert( entry_ptr->delay_write_until == 0 );
+ /* This page and [addr, addr + size) should NOT be disjoint. */
+ HDassert(!(addr + size <= entry_ptr->addr || entry_ptr->addr + entry_ptr->size <= addr));
+
+ if ( entry_ptr->is_dirty ) {
+
+ if ( i == 0 ) {
+
+ /* handle the possible partial access of the
+ * first page.
+ */
+
+ HDassert( search_addr == first_page_addr );
+ HDassert( search_page == first_page );
+
+ offset = addr - first_page_addr;
+
+ HDassert((( offset == 0 ) && (search_addr == addr )) ||
+ (( offset > 0 ) && ( search_addr < addr )));
+
+ HDassert(pb_ptr->page_size >= offset);
+
+ HDassert( size >= pb_ptr->page_size - (size_t)offset );
+
+ HDmemcpy(buf, (uint8_t *)entry_ptr->image_ptr + offset,
+ pb_ptr->page_size - (size_t)offset);
+
+ } else if ( i == num_touched_pages - 1 ) {
+
+ /* handle the possible partial access of the
+ * last page.
+ */
+ HDassert( i > 0 );
+ HDassert( search_addr == last_page_addr );
+ HDassert( search_page == last_page );
+ HDassert( addr < last_page_addr );
+ HDassert( last_page_addr < addr + size );
+
+ offset = (num_touched_pages - 2) * pb_ptr->page_size +
+ (pb_ptr->page_size - (addr - first_page_addr));
+
+ HDmemcpy((uint8_t *)buf + offset, entry_ptr->image_ptr,
+ (size_t)((addr + size) - last_page_addr));
+
+ } else {
+
+ /* this is an internal page -- copy it in its
+ * entireity.
+ */
+
+ offset = (i - 1) * pb_ptr->page_size +
+ (pb_ptr->page_size - (addr - first_page_addr));
+
+ HDassert ( addr + offset == search_addr );
+ HDassert ( offset + pb_ptr->page_size <= size );
+
+ HDmemcpy((uint8_t *)buf + offset,
+ entry_ptr->image_ptr,
+ pb_ptr->page_size);
+ }
+
+ /* we have touched the entry -- move it to the top
+ * of the LRU if it resides there.
+ *
+ * The entry will be on the LRU if both it is not
+ * a multi-page metadata entry and it is not
+ * subject to a delayed write.
+ *
+ * As this is a raw data page buffer entry, both of
+ * these must be true, and are asserted above.
+ *
+ * Thus, just update the LRU.
+ */
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+
+ } /* if ( entry_ptr->is_dirty ) */
+ } /* if ( entry_ptr ) */
+
+ search_page++;
+ search_addr += pb_ptr->page_size;
+
+ } /* end for */
+ } else {
+ /* case 4: Raw data read of size less than page size.
+ *
+ * In this case, read the desired data from the page buffer, loading
+ * pages if necessary.
+ */
+ HDassert(size < pb_ptr->page_size);
+
+ /* first page */
+ offset = addr - first_page_addr;
+
+ if ( (offset + size) <= pb_ptr->page_size ) {
+
+ HDassert(num_touched_pages == 1);
+ length = size;
+
+ } else {
- FUNC_ENTER_STATIC
+ HDassert(num_touched_pages == 2);
+ length = size - (pb_ptr->page_size - offset);
+ }
- /* Insert entry in skip list */
- if(H5SL_insert(page_buf->slist_ptr, page_entry, &(page_entry->addr)) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTINSERT, FAIL, "can't insert entry in skip list")
- HDassert(H5SL_count(page_buf->slist_ptr) * page_buf->page_size <= page_buf->max_size);
+ /* get the first page */
+ H5PB__SEARCH_INDEX(pb_ptr, first_page, entry_ptr, FAIL)
- /* Increment appropriate page count */
- if(H5F_MEM_PAGE_DRAW == page_entry->type || H5F_MEM_PAGE_GHEAP == page_entry->type)
- page_buf->raw_count++;
- else
- page_buf->meta_count++;
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, (entry_ptr != NULL), \
+ FALSE, FALSE)
- /* Insert entry in LRU */
- H5PB__INSERT_LRU(page_buf, page_entry)
+ if ( ( NULL == entry_ptr ) &&
+ ( H5PB__load_page(shared, pb_ptr, first_page_addr,
+ type, &entry_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "page buffer page load request failed (1)")
+
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->addr == first_page_addr);
+
+
+ /* copy data from first page into read buffer */
+ HDmemcpy((uint8_t *)buf, ((uint8_t *)(entry_ptr->image_ptr) + offset),
+ length);
+
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+
+ /* second page, if it exists */
+ if ( num_touched_pages == 2 ) {
+
+ offset = length;
+ length = size - offset;
+
+ HDassert(offset + length == size);
+
+ /* get the second page */
+ H5PB__SEARCH_INDEX(pb_ptr, last_page, entry_ptr, FAIL)
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, (entry_ptr != NULL), \
+ FALSE, FALSE)
+
+ if ( ( NULL == entry_ptr ) &&
+ ( H5PB__load_page(shared, pb_ptr, last_page_addr,
+ type, &entry_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "page buffer page load request failed (2)")
+
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->addr == last_page_addr);
+ HDassert(entry_ptr->page == last_page);
+
+ /* copy data from second page into read buffer */
+ HDmemcpy(((uint8_t *)(buf) + offset),
+ (uint8_t *)(entry_ptr->image_ptr), length);
+
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, FAIL)
+ }
+ } /* end else */
done:
+
FUNC_LEAVE_NOAPI(ret_value)
-} /* end H5PB__insert_entry() */
+
+} /* end H5PB__read_raw() */
/*-------------------------------------------------------------------------
- * Function: H5PB__make_space()
*
- * Purpose: This function was created without documentation.
- * What follows is my best understanding of Mohamad's intent.
+ * Function: H5PB__write_meta
*
- * If necessary and if possible, evict a page from the page
- * buffer to make space for the supplied page. Depending on
- * the page buffer configuration and contents, and the page
- * supplied this may or may not be possible.
+ * Purpose: Satisfy a metadata write in cases 7 and 8 from H5PB_write().
+ * Specifically:
*
- * JRM -- 12/22/16
+ * 7) If the write is of metadata, the write is larger than
+ * one page, and vfd_swmr_writer is TRUE, the write must
+ * buffered in the page buffer until the end of the tick.
*
- * Return: Non-negative on success/Negative on failure
+ * If it doesn't exist already, create a multi-page metadata
+ * entry in the page buffer and copy the write into it.
+ * Insert the new entry in the tick list if necessary.
+ *
+ * Test to see if the write of the multi-page metadata
+ * entry must be delayed. If so, place the entry in
+ * the delayed write list. Otherwise, the multi-page
+ * metadata entry will be written to the HDF5 file and
+ * evicted when the tick list is released at the of the
+ * tick.
+ *
+ * 8) If the write is of metadata, and the write is of size
+ * less than or equal to the page size, write the data
+ * into the page buffer, loading and inserting a page
+ * if necessary.
+ *
+ * If, in addition, vfd_swmr_writer is TRUE, we must:
+ *
+ * * add the page touched by the write to the tick list
+ * so that it will be buffered until the end of the
+ * tick.
+ *
+ * * test to see if the write must be delayed, and
+ * add the page to the delayed write list if so.
+ *
+ * Observe that this implies that:
+ *
+ * 1) The page buffer is defined.
+ *
+ * 2) The page buffer has been configured to accept at least
+ * one page of metadata.
+ *
+ * 3) This is a metadata write.
*
- * Programmer: Mohamad Chaarawi
+ * Note also that if the metadata write is of size
+ * no larger than page size, it may not cross page
+ * boundaries.
+ *
+ * Further, for writes larger than page size (case 7 only),
+ * the base address must be page aligned.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer -- 10/11/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
-static htri_t
-H5PB__make_space(H5F_shared_t *f_sh, H5PB_t *page_buf, H5FD_mem_t inserted_type)
+static herr_t
+H5PB__write_meta(H5F_shared_t *shared, H5FD_mem_t type, haddr_t addr,
+ size_t size, const void *buf/*in*/)
{
- H5PB_entry_t *page_entry; /* Pointer to page eviction candidate */
- htri_t ret_value = TRUE; /* Return value */
+ H5PB_t *pb_ptr; /* Page buffer for this file */
+ H5PB_entry_t *entry_ptr; /* Pointer to page buffer entry */
+ uint64_t page; /* page offset of addr */
+ haddr_t page_addr; /* page containg addr */
+ size_t offset; /* offset of write in page */
+ herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_NOAPI(FAIL)
- /* Sanity check */
- HDassert(f_sh);
- HDassert(page_buf);
-
- /* Get oldest entry */
- page_entry = page_buf->LRU_tail_ptr;
-
- if(H5FD_MEM_DRAW == inserted_type) {
- /* If threshould is 100% metadata and page buffer is full of
- metadata, then we can't make space for raw data */
- if(0 == page_buf->raw_count && page_buf->min_meta_count == page_buf->meta_count) {
- HDassert(page_buf->meta_count * page_buf->page_size == page_buf->max_size);
- HGOTO_DONE(FALSE)
- } /* end if */
-
- /* check the metadata threshold before evicting metadata items */
- while(1) {
- if(page_entry->prev && H5F_MEM_PAGE_META == page_entry->type &&
- page_buf->min_meta_count >= page_buf->meta_count)
- page_entry = page_entry->prev;
- else
- break;
- } /* end while */
- } /* end if */
- else {
- /* If threshould is 100% raw data and page buffer is full of
- raw data, then we can't make space for meta data */
- if(0 == page_buf->meta_count && page_buf->min_raw_count == page_buf->raw_count) {
- HDassert(page_buf->raw_count * page_buf->page_size == page_buf->max_size);
- HGOTO_DONE(FALSE)
- } /* end if */
-
- /* check the raw data threshold before evicting raw data items */
- while(1) {
- if(page_entry->prev && (H5F_MEM_PAGE_DRAW == page_entry->type || H5F_MEM_PAGE_GHEAP == page_entry->type) &&
- page_buf->min_raw_count >= page_buf->raw_count)
- page_entry = page_entry->prev;
- else
- break;
- } /* end while */
- } /* end else */
+ /* Sanity checks */
+ HDassert(shared);
+ HDassert(shared->pb_ptr);
+
+ pb_ptr = shared->pb_ptr;
+
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(pb_ptr->min_rd_pages < pb_ptr->max_pages);
+ HDassert(H5FD_MEM_DRAW != type);
+ HDassert(buf);
- /* Remove from page index */
- if(NULL == H5SL_remove(page_buf->slist_ptr, &(page_entry->addr)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_BADVALUE, FAIL, "Tail Page Entry is not in skip list")
+ /* Calculate the aligned address of the first page */
+ page = (addr / pb_ptr->page_size);
+ page_addr = page * pb_ptr->page_size;
+
+ /* if size > pb_ptr->page_size, addr must be page aligned */
+ HDassert((size <= pb_ptr->page_size) || (addr == page_addr));
+
+ H5PB__SEARCH_INDEX(pb_ptr, page, entry_ptr, FAIL)
+
+ /* case 7) metadata write of size greater than page size. */
+ if ( size > pb_ptr->page_size ) {
+
+ offset = 0;
+
+ /* The write must be for a multi-page metadata entry, and
+ * we must be running as a VFD SWMR writer.
+ *
+ * This requires the following actions:
+ *
+ * 1) If the multi-page metadata entry is not already in the
+ * page buffer, create an entry for it.
+ *
+ * 2) Overwrite the image of the entry with the write buffer.
+ *
+ * 3) If the entry is not already on the tick list, add it to
+ * the tick list.
+ *
+ * 4) If the entry is not already on the delayed write list,
+ * test to see if it should be, and move it from the
+ * LRU to the delayed write list and set the delay_write_until
+ * field appropriately.
+ *
+ * This is done via the call to H5PB__mark_entry_dirty()
+ */
+ HDassert(pb_ptr->vfd_swmr_writer);
+ HDassert(addr == page_addr);
- /* Remove entry from LRU list */
- H5PB__REMOVE_LRU(page_buf, page_entry)
- HDassert(H5SL_count(page_buf->slist_ptr) == page_buf->LRU_list_len);
+ /* If we're about to overwrite a single-page entry with multiple
+ * pages, lengthen the entry.
+ */
+ if (entry_ptr != NULL && entry_ptr->size < size) {
+ H5PB_entry_t *overlap;
+ void *new_image = H5MM_malloc(size);
+ uint64_t iter_page;
+ uint64_t last_page = page +
+ roundup(size, pb_ptr->page_size) / pb_ptr->page_size;
+
+ hlog_fast(lengthen_pbentry,
+ "lengthening page %" PRIu64 " from %zu bytes to %zu, "
+ "last page %" PRIu64 "\n", page, entry_ptr->size, size,
+ last_page);
+
+ for (iter_page = page + 1; iter_page < last_page; iter_page++) {
+ H5PB__SEARCH_INDEX(pb_ptr, iter_page, overlap, FAIL)
+ assert(overlap == NULL);
+ }
+ if (new_image == NULL) {
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL,
+ "couldn't extend entry");
+ }
+ H5PB__UPDATE_RP_FOR_REMOVE(pb_ptr, entry_ptr, FAIL)
+
+ /* To keep statistics for the index and the tick-list up-to-date,
+ * it's expedient to remove and re-insert entries there.
+ */
+ H5PB__DELETE_FROM_INDEX(pb_ptr, entry_ptr, FAIL)
+ if (entry_ptr->modified_this_tick)
+ H5PB__REMOVE_FROM_TL(pb_ptr, entry_ptr, FAIL)
+
+ entry_ptr->image_ptr = H5MM_xfree(entry_ptr->image_ptr);
+ entry_ptr->image_ptr = new_image;
+ entry_ptr->is_mpmde = true;
+ entry_ptr->size = size;
+
+ if (entry_ptr->modified_this_tick)
+ H5PB__INSERT_IN_TL(pb_ptr, entry_ptr, FAIL)
+ H5PB__INSERT_IN_INDEX(pb_ptr, entry_ptr, FAIL)
+ }
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, (entry_ptr != NULL), \
+ TRUE, TRUE)
+
+ if ( NULL == entry_ptr ) {
+
+ /* the multi-page metadata entry is not currently in the page
+ * buffer. Create an entry for it, and insert it into the LRU.
+ *
+ * Don't bother to try to make space for it, as VFD SWMR
+ * ignores the limits on page buffer size.
+ */
+ if ( H5PB__create_new_page(pb_ptr, addr, size, type,
+ FALSE, &entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "can't create new page buffer page")
+
+ /* set entry_ptr->loaded to TRUE so as to trigger the
+ * the delayed write test in H5PB__mark_entry_dirty().
+ */
+ entry_ptr->loaded = TRUE;
+ }
+
+ /* at this point, one way or the other, the multi-page metadata
+ * entry must be in the page buffer.
+ */
+ HDassert(entry_ptr->is_mpmde);
+ HDassert(size == entry_ptr->size);
+ HDassert(type == entry_ptr->mem_type);
- /* Decrement appropriate page type counter */
- if(H5F_MEM_PAGE_DRAW == page_entry->type || H5F_MEM_PAGE_GHEAP == page_entry->type)
- page_buf->raw_count--;
- else
- page_buf->meta_count--;
+ } else {
+ /* case 8) metadata write of size no larger than page size */
- /* Flush page if dirty */
- if(page_entry->is_dirty)
- if(H5PB__write_entry(f_sh, page_entry) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "file write failed")
+ offset = addr - page_addr;
- /* Update statistics */
- if(page_entry->type == H5F_MEM_PAGE_DRAW || H5F_MEM_PAGE_GHEAP == page_entry->type)
- page_buf->evictions[1]++;
- else
- page_buf->evictions[0]++;
+ /* write cannot cross page boundaries. */
+ HDassert((offset + size) <= pb_ptr->page_size);
- /* Release page */
- page_entry->page_buf_ptr = H5FL_FAC_FREE(page_buf->page_fac, page_entry->page_buf_ptr);
- page_entry = H5FL_FREE(H5PB_entry_t, page_entry);
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, (entry_ptr != NULL), \
+ TRUE, FALSE)
+
+ if (NULL == entry_ptr &&
+ H5PB__load_page(shared, pb_ptr, page_addr, type, &entry_ptr) < 0) {
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "page buffer page load request failed (1)")
+ }
+
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->addr == page_addr);
+ HDassert(!(entry_ptr->is_mpmde));
+ HDassert(entry_ptr->size == pb_ptr->page_size);
+ HDassert(size <= entry_ptr->size);
+ }
+
+ HDassert(entry_ptr->is_metadata);
+
+ /* copy data from the write buffer into the page image */
+ HDmemcpy((uint8_t *)(entry_ptr->image_ptr) + offset, buf, size);
+
+ if (H5PB__mark_entry_dirty(shared, pb_ptr, entry_ptr) < 0)
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, "mark entry dirty failed")
+
+ /* Force the page buffer to retain the page until the end of
+ * the tick: add the entry to the tick list if it is not
+ * already present.
+ */
+ if (pb_ptr->vfd_swmr_writer && !entry_ptr->modified_this_tick) {
+ entry_ptr->modified_this_tick = true;
+ H5PB__INSERT_IN_TL(pb_ptr, entry_ptr, FAIL)
+ }
done:
+
FUNC_LEAVE_NOAPI(ret_value)
-} /* end H5PB__make_space() */
+
+} /* end H5PB__write_meta() */
/*-------------------------------------------------------------------------
- * Function: H5PB__write_entry()
*
- * Purpose: ???
+ * Function: H5PB__write_raw
*
- * This function was created without documentation.
- * What follows is my best understanding of Mohamad's intent.
+ * Purpose: Satisfy a raw data write in cases 3 and 4 from H5PB_write().
+ * Specifically:
*
- * Return: Non-negative on success/Negative on failure
+ * 3) If the write is raw data, and it of page size or
+ * larger, write directly to the HDF5 file.
+ *
+ * It is possible that the write intersects one or more
+ * pages in the page buffer -- test for this and update
+ * any partially written pages, and evict any pages
+ * that are completely overwritten.
+ *
+ * Note that no pages are inserted into the page buffer in
+ * this case.
+ *
+ * 4) If the write is of raw data, and it is of size less
+ * than the page size, write the page into the page
+ * buffer, loading and inserting pages into the
+ * page buffer as necessary
+ *
+ * Observe that this implies that:
+ *
+ * 1) The page buffer is defined.
+ *
+ * 2) The page buffer has been configured to accept at least
+ * one page of raw data.
+ *
+ * 2) This is a raw data write.
+ *
+ * Return: Non-negative on success/Negative on failure
*
- * Programmer: Mohamad Chaarawi
+ * Programmer: John Mainzer -- 10/11/18
+ *
+ * Changes: None.
*
*-------------------------------------------------------------------------
*/
static herr_t
-H5PB__write_entry(H5F_shared_t *f_sh, H5PB_entry_t *page_entry)
+H5PB__write_raw(H5F_shared_t *shared, H5FD_mem_t type, haddr_t addr, size_t size,
+ const void *buf/*out*/)
{
- haddr_t eoa; /* Current EOA for the file */
- herr_t ret_value = SUCCEED; /* Return value */
+ H5PB_t *pb_ptr; /* Page buffer for this file */
+ H5PB_entry_t *entry_ptr; /* Pointer to page buffer entry */
+ uint64_t first_page; /* page offset of first I/O */
+ uint64_t last_page; /* page offset of last I/O */
+ uint64_t search_page; /* page offset of current page */
+ haddr_t first_page_addr; /* address of first page of I/O */
+ haddr_t last_page_addr; /* address of last page of I/O */
+ haddr_t search_addr; /* Address of current page */
+ hsize_t num_touched_pages; /* Number of pages accessed */
+ hsize_t i; /* Local index variable */
+ size_t length; /* length of write in a page */
+ size_t offset; /* offset of write in a page */
+ herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_NOAPI(FAIL)
- /* Sanity check */
- HDassert(f_sh);
- HDassert(page_entry);
+ /* Sanity checks */
+ HDassert(shared);
+ HDassert(shared->pb_ptr);
- /* Retrieve the 'eoa' for the file */
- if(HADDR_UNDEF == (eoa = H5F_shared_get_eoa(f_sh, (H5FD_mem_t)page_entry->type)))
- HGOTO_ERROR(H5E_PAGEBUF, H5E_CANTGET, FAIL, "driver get_eoa request failed")
+ pb_ptr = shared->pb_ptr;
- /* If the starting address of the page is larger than
- * the EOA, then the entire page is discarded without writing.
- */
- if(page_entry->addr <= eoa) {
- H5FD_t *file; /* File driver I/O info */
- size_t page_size = f_sh->page_buf->page_size;
+ HDassert(pb_ptr->magic == H5PB__H5PB_T_MAGIC);
+ HDassert(pb_ptr->min_md_pages < pb_ptr->max_pages);
+ HDassert(shared->lf);
- /* Adjust the page length if it exceeds the EOA */
- if((page_entry->addr + page_size) > eoa)
- page_size = (size_t)(eoa - page_entry->addr);
+ HDassert(H5FD_MEM_DRAW == type);
- /* Translate to file driver I/O info object */
- file = f_sh->lf;
+ /* Calculate the aligned address of the first page */
+ first_page = (addr / pb_ptr->page_size);
+ first_page_addr = first_page * pb_ptr->page_size;
- if(H5FD_write(file, (H5FD_mem_t)page_entry->type, page_entry->addr, page_size, page_entry->page_buf_ptr) < 0)
- HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, "file write failed")
- } /* end if */
+ /* Calculate the aligned address of the last page */
+ last_page = ((addr + size - 1) / pb_ptr->page_size);
+ last_page_addr = last_page * pb_ptr->page_size;
+
+ /* Calculate number of pages that this read spans. */
+ num_touched_pages = last_page - first_page + 1;
+
+ if ( first_page_addr == last_page_addr ) {
+
+ HDassert(1 == num_touched_pages);
+ last_page_addr = HADDR_UNDEF;
+
+ }
+
+ /* case 3) raw data write of page size or greater. */
+ if ( size >= pb_ptr->page_size ) {
+ if ( H5FD_write(shared->lf, type, addr, size, buf) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_WRITEERROR, FAIL, \
+ "write through metadata accumulator failed")
+
+
+ H5PB__UPDATE_STATS_FOR_BYPASS(pb_ptr, type, size);
+
+ /* For each page that intersects with the above write, check to see
+ * if it exists in the page buffer.
+ *
+ * If it does and is, and if the write overwrites page fully,
+ * mark the page clean and evict it.
+ *
+ * If the write only partially intersects a page, update the
+ * page and mark it dirty.
+ */
+ search_page = first_page;
+ search_addr = first_page_addr;
+
+ for(i = 0; i < num_touched_pages; i++) {
+
+ H5PB__SEARCH_INDEX(pb_ptr, search_page, entry_ptr, FAIL)
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, (entry_ptr != NULL), \
+ FALSE, FALSE)
+
+ if ( entry_ptr ) {
+
+ HDassert( entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC );
+ HDassert( ! ( entry_ptr->is_metadata ) );
+ HDassert( entry_ptr->page == search_page );
+ HDassert( entry_ptr->addr == search_addr );
+ HDassert( entry_ptr->size == pb_ptr->page_size );
+ HDassert( entry_ptr->delay_write_until == 0 );
+ HDassert( entry_ptr->addr <= addr + size );
+
+ if ( ( addr <= entry_ptr->addr ) &&
+ ( entry_ptr->addr + entry_ptr->size <= addr + size ) ) {
+
+ /* the page is completely overwritten -- mark it clean
+ * and evict it.
+ */
+ if ( ( entry_ptr->is_dirty ) &&
+ ( H5PB__mark_entry_clean(pb_ptr, entry_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "mark entry clean failed")
+
+ if (H5PB__evict_entry(shared, entry_ptr, TRUE, false) < 0)
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "forced eviction failed (1)")
+
+ } else if ( i == 0 ) {
+
+ /* handle partial overwrite of the first page. */
+
+ HDassert( search_addr == first_page_addr );
+ HDassert( search_page == first_page );
+ HDassert( search_addr < addr );
+ HDassert( entry_ptr->addr + entry_ptr->size <=
+ addr + size );
+
+ offset = addr - first_page_addr;
+
+ HDassert( offset > 0 );
+ HDassert( pb_ptr->page_size >= offset );
+ HDassert( size >= pb_ptr->page_size - (size_t)offset );
+
+ HDmemcpy((uint8_t *)entry_ptr->image_ptr + offset, buf,
+ pb_ptr->page_size - (size_t)offset);
+
+ if ( H5PB__mark_entry_dirty(shared, pb_ptr, entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "mark entry dirty failed (1)")
+
+ } else if ( i == num_touched_pages - 1 ) {
+
+ /* handle partial overwrite of the last page. */
+ HDassert( i > 0 );
+ HDassert( search_addr == last_page_addr );
+ HDassert( search_page == last_page );
+ HDassert( addr < last_page_addr );
+ HDassert( last_page_addr < addr + size );
- page_entry->is_dirty = FALSE;
+ offset = (num_touched_pages - 2) * pb_ptr->page_size +
+ (pb_ptr->page_size - (addr - first_page_addr));
+
+ HDmemcpy(entry_ptr->image_ptr,
+ (const uint8_t *)buf + offset,
+ (size_t)((addr + size) - last_page_addr));
+
+ if ( H5PB__mark_entry_dirty(shared, pb_ptr, entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "mark entry dirty failed (2)")
+ } else {
+
+ /* this should be un-reachable */
+ HDassert(FALSE);
+
+ }
+ } /* if ( entry_ptr ) */
+
+ search_page++;
+ search_addr += pb_ptr->page_size;
+
+ } /* end for */
+ } else {
+ /* case 4: Raw data write of size less than page size.
+ *
+ * In this case, write the data to the page buffer, loading
+ * pages if necessary.
+ */
+ HDassert(size < pb_ptr->page_size);
+
+ /* first page */
+ offset = addr - first_page_addr;
+
+ if ( (offset + size) <= pb_ptr->page_size ) {
+
+ HDassert(num_touched_pages == 1);
+ length = size;
+
+ } else {
+
+ HDassert(num_touched_pages == 2);
+ length = pb_ptr->page_size - offset;
+ HDassert( offset + length == pb_ptr->page_size );
+ }
+
+ /* get the first page */
+ H5PB__SEARCH_INDEX(pb_ptr, first_page, entry_ptr, FAIL)
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, (entry_ptr != NULL), \
+ FALSE, FALSE)
+
+ if ( ( NULL == entry_ptr ) &&
+ ( H5PB__load_page(shared, pb_ptr, first_page_addr,
+ type, &entry_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "page buffer page load request failed (1)")
+
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->addr == first_page_addr);
+
+
+ /* copy data from the write buffer into the first page */
+ HDmemcpy(((uint8_t *)(entry_ptr->image_ptr)) + offset,
+ (const uint8_t *)buf, length);
+
+ if ( H5PB__mark_entry_dirty(shared, pb_ptr, entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "mark entry dirty failed (3)")
+
+ /* second page, if it exists */
+ if ( num_touched_pages == 2 ) {
+
+ offset = length;
+ length = size - offset;
+
+ HDassert(offset + length == size);
+
+ /* get the first page */
+ H5PB__SEARCH_INDEX(pb_ptr, last_page, entry_ptr, FAIL)
+
+ /* update hit rate stats */
+ H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, (entry_ptr != NULL), \
+ FALSE, FALSE)
+
+ if ( ( NULL == entry_ptr ) &&
+ ( H5PB__load_page(shared, pb_ptr, last_page_addr,
+ type, &entry_ptr) < 0 ) )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_READERROR, FAIL, \
+ "page buffer page load request failed (2)")
+
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5PB__H5PB_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->addr == last_page_addr);
+ HDassert(entry_ptr->page == last_page);
+
+ /* copy data from the write buffer into the first page */
+ HDmemcpy((uint8_t *)(entry_ptr->image_ptr),
+ ((const uint8_t *)(buf) + offset), length);
+
+ if ( H5PB__mark_entry_dirty(shared, pb_ptr, entry_ptr) < 0 )
+
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "mark entry dirty failed (3)")
+ }
+ }
done:
+
FUNC_LEAVE_NOAPI(ret_value)
-} /* end H5PB__write_entry() */
+
+} /* end H5PB__write_raw() */
diff --git a/src/H5PBpkg.h b/src/H5PBpkg.h
index 6b9168b..fb9f29f 100644
--- a/src/H5PBpkg.h
+++ b/src/H5PBpkg.h
@@ -21,38 +21,1923 @@
/* Get package's private header */
#include "H5PBprivate.h"
-/* Other private headers needed by this file */
+/*
+ * File: H5PBpkg.h
+ *
+ * Purpose: This file contains declarations which are normally visible
+ * only within the H5PB package.
+ *
+ * Source files outside the H5PB package should include
+ * H5PBprivate.h instead.
+ *
+ * Programmer: John Mainzer -- 10/07/18
+ */
/**************************/
/* Package Private Macros */
/**************************/
+/* page buffer configuration settings */
+#define H5PB__H5PB_ENTRY_T_MAGIC 0x02030405
+#define H5PB__DO_SANITY_CHECKS TRUE
+#define H5PB__COLLECT_PAGE_BUFFER_STATS TRUE
+
+
+/****************************************************************************
+ *
+ * We maintain doubly linked lists of instances of H5PB_entry_t for a
+ * variety of reasons -- LRU list, tick list, and the delayed write list
+ * at present. The following macros support linking and unlinking
+ * instances of H5PB_entry_t by both their regular and tick list next
+ * and previous pointers. Note that the tick list and the delayed write
+ * list are only used in the context of VFD SWMR
+ *
+ * The size and length fields are also maintained.
+ *
+ * Note that the relevant pair of prev and next pointers are presumed to be
+ * NULL on entry in the insertion macros.
+ *
+ * Finally, observe that the sanity checking macros evaluate to the empty
+ * string when H5PB__DO_SANITY_CHECKS is FALSE. They also contain calls
+ * to the HGOTO_ERROR macro, which may not be appropriate in all cases.
+ * If so, we will need versions of the insertion and deletion macros which
+ * do not reference the sanity checking macros.
+ * JRM - 10/07/18
+ *
+ ****************************************************************************/
+
+#if H5PB__DO_SANITY_CHECKS
+
+#define H5PB__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+if ( ( (head_ptr) == NULL ) || \
+ ( (tail_ptr) == NULL ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (len) <= 0 ) || \
+ ( (Size) < (int64_t)((entry_ptr)->size ) ) || \
+ ( ( (Size) == (int64_t)((entry_ptr)->size) ) && ( ! ( (len) == 1 ) ) ) || \
+ ( ( (entry_ptr)->prev == NULL ) && ( (head_ptr) != (entry_ptr) ) ) || \
+ ( ( (entry_ptr)->next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \
+ ( ( (len) == 1 ) && \
+ ( ! ( ( (head_ptr) == (entry_ptr) ) && \
+ ( (tail_ptr) == (entry_ptr) ) && \
+ ( (entry_ptr)->next == NULL ) && \
+ ( (entry_ptr)->prev == NULL ) && \
+ ( (Size) == (int64_t)((entry_ptr)->size) ) \
+ ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, (fv), "DLL pre remove SC failed") \
+}
+
+#define H5PB__DLL_SC(head_ptr, tail_ptr, len, Size, fv) \
+if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (head_ptr) != (tail_ptr) ) \
+ ) || \
+ ( (len) < 0 ) || \
+ ( (Size) < 0 ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (head_ptr) != (tail_ptr) ) || \
+ ( (head_ptr) == NULL ) || ( (head_ptr)->size != (size_t)(Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (head_ptr) == NULL ) || ( (head_ptr)->prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, (fv), "DLL sanity check failed") \
+}
+
+#define H5PB__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+if ( ( (entry_ptr) == NULL ) || \
+ ( (entry_ptr)->next != NULL ) || \
+ ( (entry_ptr)->prev != NULL ) || \
+ ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (head_ptr) != (tail_ptr) ) \
+ ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (head_ptr) != (tail_ptr) ) || \
+ ( (head_ptr) == NULL ) || ( (head_ptr)->size != (size_t)(Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (head_ptr) == NULL ) || ( (head_ptr)->prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HDassert(FALSE); \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, (fv), "DLL pre insert SC failed") \
+}
+
+#else /* H5PB__DO_SANITY_CHECKS */
+
+#define H5PB__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv)
+#define H5PB__DLL_SC(head_ptr, tail_ptr, len, Size, fv)
+#define H5PB__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv)
+
+#endif /* H5PB__DO_SANITY_CHECKS */
+
+
+#define H5PB__DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \
+{ \
+ H5PB__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (tail_ptr)->next = (entry_ptr); \
+ (entry_ptr)->prev = (tail_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += (int64_t)((entry_ptr)->size); \
+} /* H5PB__DLL_APPEND() */
+
+#define H5PB__DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \
+{ \
+ H5PB__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (head_ptr)->prev = (entry_ptr); \
+ (entry_ptr)->next = (head_ptr); \
+ (head_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += (int64_t)((entry_ptr)->size); \
+} /* H5PB__DLL_PREPEND() */
+
+#define H5PB__DLL_INSERT_BEFORE(entry_ptr, suc_ptr, head_ptr, tail_ptr, len, \
+ Size, fail_val) \
+{ \
+ HDassert( ((suc_ptr) == NULL) || \
+ ((suc_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC) ); \
+ \
+ if ( suc_ptr == NULL ) \
+ /* list empty or no successor -- append */ \
+ H5PB__DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \
+ \
+ else if ( suc_ptr->prev == NULL ) \
+ /* successor at head of list -- prepend */ \
+ H5PB__DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \
+ \
+ else /* sucessor in body of list -- insert before it */ \
+ { \
+ H5PB__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ HDassert(suc_ptr->prev->magic == H5PB__H5PB_ENTRY_T_MAGIC); \
+ HDassert(suc_ptr->prev->next == suc_ptr); \
+ entry_ptr->prev = suc_ptr->prev; \
+ entry_ptr->prev->next = entry_ptr; \
+ entry_ptr->next = suc_ptr; \
+ suc_ptr->prev = entry_ptr; \
+ (len)++; \
+ (Size) += (int64_t)((entry_ptr)->size); \
+ } \
+} /* H5PB__DLL_INSERT_BEFORE() */
+
+#define H5PB__DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val) \
+{ \
+ H5PB__DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ { \
+ if ( (head_ptr) == (entry_ptr) ) \
+ { \
+ (head_ptr) = (entry_ptr)->next; \
+ if ( (head_ptr) != NULL ) \
+ (head_ptr)->prev = NULL; \
+ } \
+ else \
+ (entry_ptr)->prev->next = (entry_ptr)->next; \
+ if ( (tail_ptr) == (entry_ptr) ) \
+ { \
+ (tail_ptr) = (entry_ptr)->prev; \
+ if ( (tail_ptr) != NULL ) \
+ (tail_ptr)->next = NULL; \
+ } \
+ else \
+ (entry_ptr)->next->prev = (entry_ptr)->prev; \
+ entry_ptr->next = NULL; \
+ entry_ptr->prev = NULL; \
+ (len)--; \
+ (Size) -= (int64_t)((entry_ptr)->size); \
+ } \
+} /* H5PB__DLL_REMOVE() */
+
+
+#if H5PB__DO_SANITY_CHECKS
+
+#define H5PB__IL_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \
+if ( ( (hd_ptr) == NULL ) || \
+ ( (tail_ptr) == NULL ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (len) <= 0 ) || \
+ ( (Size) < (int64_t)((entry_ptr)->size) ) || \
+ ( ( (Size) == (int64_t)((entry_ptr)->size) ) && \
+ ( ! ( (len) == 1 ) ) ) || \
+ ( ( (entry_ptr)->il_prev == NULL ) && ( (hd_ptr) != (entry_ptr) ) ) || \
+ ( ( (entry_ptr)->il_next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \
+ ( ( (len) == 1 ) && \
+ ( ! ( ( (hd_ptr) == (entry_ptr) ) && ( (tail_ptr) == (entry_ptr) ) && \
+ ( (entry_ptr)->il_next == NULL ) && \
+ ( (entry_ptr)->il_prev == NULL ) && \
+ ( (Size) == (int64_t)((entry_ptr)->size) ) \
+ ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, (fv), "il DLL pre remove SC failed") \
+}
+
+#define H5PB__IL_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \
+if ( ( (entry_ptr) == NULL ) || \
+ ( (entry_ptr)->il_next != NULL ) || \
+ ( (entry_ptr)->il_prev != NULL ) || \
+ ( ( ( (hd_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (hd_ptr) != (tail_ptr) ) \
+ ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (hd_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \
+ ( (hd_ptr) == NULL ) || ( (int64_t)((hd_ptr)->size) != (Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (hd_ptr) == NULL ) || ( (hd_ptr)->il_prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->il_next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, (fv), "IL DLL pre insert SC failed") \
+}
+
+#define H5PB__IL_DLL_SC(head_ptr, tail_ptr, len, Size, fv) \
+if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (head_ptr) != (tail_ptr) ) \
+ ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (head_ptr) != (tail_ptr) ) || \
+ ( (head_ptr) == NULL ) || ( (int64_t)((head_ptr)->size) != (Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (head_ptr) == NULL ) || ( (head_ptr)->il_prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->il_next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, (fv), "IL DLL sanity check failed") \
+}
+
+#else /* H5PB__DO_SANITY_CHECKS */
+
+#define H5PB__IL_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv)
+#define H5PB__IL_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv)
+#define H5PB__IL_DLL_SC(head_ptr, tail_ptr, len, Size, fv)
+
+#endif /* H5PB__DO_SANITY_CHECKS */
+
+
+#define H5PB__IL_DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val)\
+{ \
+ H5PB__IL_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (tail_ptr)->il_next = (entry_ptr); \
+ (entry_ptr)->il_prev = (tail_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += (int64_t)((entry_ptr)->size); \
+ H5PB__IL_DLL_SC(head_ptr, tail_ptr, len, Size, fail_val) \
+} /* H5PB__IL_DLL_APPEND() */
+
+#define H5PB__IL_DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+{ \
+ H5PB__DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (head_ptr)->il_prev = (entry_ptr); \
+ (entry_ptr)->il_next = (head_ptr); \
+ (head_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += (int64_t)((entry_ptr)->size); \
+} /* H5PB__DLL_PREPEND() */
+
+
+#define H5PB__IL_DLL_INSERT_BEFORE(entry_ptr, suc_ptr, head_ptr, tail_ptr, \
+ len, Size, fail_val) \
+{ \
+ HDassert( ((suc_ptr) == NULL) || \
+ ((suc_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC) ); \
+ \
+ if ( suc_ptr == NULL ) \
+ /* list empty or no successor -- append */ \
+ H5PB__IL_DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ \
+ else if ( suc_ptr->il_prev == NULL ) \
+ /* successor at head of list -- prepend */ \
+ H5PB__IL_DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ \
+ else /* sucessor in body of list -- insert before it */ \
+ { \
+ H5PB__IL_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ HDassert(suc_ptr->il_prev->magic == H5PB__H5PB_ENTRY_T_MAGIC); \
+ HDassert(suc_ptr->il_prev->il_next == suc_ptr); \
+ entry_ptr->il_prev = suc_ptr->il_prev; \
+ entry_ptr->il_prev->il_next = entry_ptr; \
+ entry_ptr->il_next = suc_ptr; \
+ suc_ptr->il_prev = entry_ptr; \
+ (len)++; \
+ (Size) += (int64_t)((entry_ptr)->size); \
+ } \
+} /* H5PB__DLL_INSERT_BEFORE() */
+
+#define H5PB__IL_DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+{ \
+ H5PB__IL_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+ { \
+ if ( (head_ptr) == (entry_ptr) ) \
+ { \
+ (head_ptr) = (entry_ptr)->il_next; \
+ if ( (head_ptr) != NULL ) \
+ (head_ptr)->il_prev = NULL; \
+ } \
+ else \
+ (entry_ptr)->il_prev->il_next = (entry_ptr)->il_next; \
+ if ( (tail_ptr) == (entry_ptr) ) \
+ { \
+ (tail_ptr) = (entry_ptr)->il_prev; \
+ if ( (tail_ptr) != NULL ) \
+ (tail_ptr)->il_next = NULL; \
+ } \
+ else \
+ (entry_ptr)->il_next->il_prev = (entry_ptr)->il_prev; \
+ entry_ptr->il_next = NULL; \
+ entry_ptr->il_prev = NULL; \
+ (len)--; \
+ (Size) -= (int64_t)((entry_ptr)->size); \
+ } \
+ H5PB__IL_DLL_SC(head_ptr, tail_ptr, len, Size, fv) \
+} /* H5PB__IL_DLL_REMOVE() */
+
+
+#if H5PB__DO_SANITY_CHECKS
+
+#define H5PB__TL_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \
+if ( ( (hd_ptr) == NULL ) || \
+ ( (tail_ptr) == NULL ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (len) <= 0 ) || \
+ ( (Size) < (int64_t)((entry_ptr)->size ) ) || \
+ ( ( (Size) == (int64_t)((entry_ptr)->size) ) && ( ! ( (len) == 1 ) ) ) || \
+ ( ( (entry_ptr)->tl_prev == NULL ) && ( (hd_ptr) != (entry_ptr) ) ) || \
+ ( ( (entry_ptr)->tl_next == NULL ) && ( (tail_ptr) != (entry_ptr) ) ) || \
+ ( ( (len) == 1 ) && \
+ ( ! ( ( (hd_ptr) == (entry_ptr) ) && ( (tail_ptr) == (entry_ptr) ) && \
+ ( (entry_ptr)->tl_next == NULL ) && \
+ ( (entry_ptr)->tl_prev == NULL ) && \
+ ( (Size) == (int64_t)((entry_ptr)->size) ) \
+ ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, (fv), "TL DLL pre remove SC failed") \
+}
+
+#define H5PB__TL_DLL_SC(head_ptr, tail_ptr, len, Size, fv) \
+if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (head_ptr) != (tail_ptr) ) \
+ ) || \
+ ( (len) < 0 ) || \
+ ( (Size) < 0 ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (head_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \
+ ( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (head_ptr) == NULL ) || ( (head_ptr)->tl_prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->tl_next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, (fv), "TL DLL sanity check failed") \
+}
+
+#define H5PB__TL_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv) \
+if ( ( (entry_ptr) == NULL ) || \
+ ( (entry_ptr)->tl_next != NULL ) || \
+ ( (entry_ptr)->tl_prev != NULL ) || \
+ ( ( ( (hd_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
+ ( (hd_ptr) != (tail_ptr) ) \
+ ) || \
+ ( ( (len) == 1 ) && \
+ ( ( (hd_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \
+ ( (hd_ptr) == NULL ) || ( (int64_t)((hd_ptr)->size) != (Size) ) \
+ ) \
+ ) || \
+ ( ( (len) >= 1 ) && \
+ ( ( (hd_ptr) == NULL ) || ( (hd_ptr)->tl_prev != NULL ) || \
+ ( (tail_ptr) == NULL ) || ( (tail_ptr)->tl_next != NULL ) \
+ ) \
+ ) \
+ ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, (fv), "TL DLL pre insert SC failed") \
+}
+
+#else /* H5PB__DO_SANITY_CHECKS */
+
+#define H5PB__TL_DLL_PRE_REMOVE_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv)
+#define H5PB__TL_DLL_SC(head_ptr, tail_ptr, len, Size, fv)
+#define H5PB__TL_DLL_PRE_INSERT_SC(entry_ptr, hd_ptr, tail_ptr, len, Size, fv)
+
+#endif /* H5PB__DO_SANITY_CHECKS */
+
+
+#define H5PB__TL_DLL_APPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fail_val)\
+{ \
+ H5PB__TL_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, \
+ fail_val) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (tail_ptr)->tl_next = (entry_ptr); \
+ (entry_ptr)->tl_prev = (tail_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += entry_ptr->size; \
+} /* H5PB__AUX_DLL_APPEND() */
+
+#define H5PB__TL_DLL_PREPEND(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+{ \
+ H5PB__TL_DLL_PRE_INSERT_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+ if ( (head_ptr) == NULL ) \
+ { \
+ (head_ptr) = (entry_ptr); \
+ (tail_ptr) = (entry_ptr); \
+ } \
+ else \
+ { \
+ (head_ptr)->tl_prev = (entry_ptr); \
+ (entry_ptr)->tl_next = (head_ptr); \
+ (head_ptr) = (entry_ptr); \
+ } \
+ (len)++; \
+ (Size) += (int64_t)(entry_ptr->size); \
+} /* H5PB__TL_DLL_PREPEND() */
+
+#define H5PB__TL_DLL_REMOVE(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+{ \
+ H5PB__TL_DLL_PRE_REMOVE_SC(entry_ptr, head_ptr, tail_ptr, len, Size, fv) \
+ { \
+ if ( (head_ptr) == (entry_ptr) ) \
+ { \
+ (head_ptr) = (entry_ptr)->tl_next; \
+ if ( (head_ptr) != NULL ) \
+ (head_ptr)->tl_prev = NULL; \
+ } \
+ else \
+ (entry_ptr)->tl_prev->tl_next = (entry_ptr)->tl_next; \
+ if ( (tail_ptr) == (entry_ptr) ) \
+ { \
+ (tail_ptr) = (entry_ptr)->tl_prev; \
+ if ( (tail_ptr) != NULL ) \
+ (tail_ptr)->tl_next = NULL; \
+ } \
+ else \
+ (entry_ptr)->tl_next->tl_prev = (entry_ptr)->tl_prev; \
+ entry_ptr->tl_next = NULL; \
+ entry_ptr->tl_prev = NULL; \
+ (len)--; \
+ (Size) -= (int64_t)(entry_ptr->size); \
+ } \
+} /* H5PB__TL_DLL_REMOVE() */
+
+
+/***********************************************************************
+ *
+ * Stats collection macros
+ *
+ * The following macros must handle stats collection when this collection
+ * is enabled, and evaluate to the empty string when it is not.
+ *
+ * The sole exception to this rule is
+ * H5PB__UPDATE_PB_HIT_RATE_STATS(), which is always active as
+ * the page buffer hit rate stats are always collected and available.
+ *
+ ***********************************************************************/
+
+#if H5PB__COLLECT_PAGE_BUFFER_STATS
+
+#define H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, hit, is_metadata, is_mpmde) \
+{ \
+ int ii; \
+ \
+ HDassert(pb_ptr); \
+ HDassert((pb_ptr)->magic == H5PB__H5PB_T_MAGIC); \
+ \
+ if ( is_metadata ) { \
+ if ( is_mpmde ) { \
+ ii = H5PB__STATS_MPMDE; \
+ } else { \
+ ii = H5PB__STATS_MD; \
+ } \
+ } else { \
+ ii = H5PB__STATS_RD; \
+ } \
+ if ( hit ) \
+ ((pb_ptr)->hits[ii])++; \
+ else \
+ ((pb_ptr)->misses[ii])++; \
+} /* H5PB__UPDATE_PB_HIT_RATE_STATS */
+
+#define H5PB__UPDATE_HT_SIZE_STATS(pb_ptr) \
+ if ( (pb_ptr)->index_len > (pb_ptr)->max_index_len ) \
+ (pb_ptr)->max_index_len = (pb_ptr)->index_len; \
+ if ( (pb_ptr)->clean_index_len > (pb_ptr)->max_clean_index_len ) \
+ (pb_ptr)->max_clean_index_len = (pb_ptr)->clean_index_len; \
+ if ( (pb_ptr)->dirty_index_len > (pb_ptr)->max_dirty_index_len ) \
+ (pb_ptr)->max_dirty_index_len = (pb_ptr)->dirty_index_len; \
+ if ( (pb_ptr)->index_size > (pb_ptr)->max_index_size ) \
+ (pb_ptr)->max_index_size = (pb_ptr)->index_size; \
+ if ( (pb_ptr)->clean_index_size > (pb_ptr)->max_clean_index_size ) \
+ (pb_ptr)->max_clean_index_size = (pb_ptr)->clean_index_size; \
+ if ( (pb_ptr)->dirty_index_size > (pb_ptr)->max_dirty_index_size ) \
+ (pb_ptr)->max_dirty_index_size = (pb_ptr)->dirty_index_size; \
+ if ( (pb_ptr)->curr_md_pages > (pb_ptr)->max_md_pages ) \
+ (pb_ptr)->max_md_pages = (pb_ptr)->curr_md_pages; \
+ if ( (pb_ptr)->curr_rd_pages > (pb_ptr)->max_rd_pages ) \
+ (pb_ptr)->max_rd_pages = (pb_ptr)->curr_rd_pages; \
+ if ( (pb_ptr)->mpmde_count > (pb_ptr)->max_mpmde_count ) \
+ (pb_ptr)->max_rd_pages = (pb_ptr)->curr_rd_pages;
+
+#define H5PB__UPDATE_STATS_FOR_HT_INSERTION(pb_ptr) \
+ ((pb_ptr)->total_ht_insertions)++;
+
+
+#define H5PB__UPDATE_STATS_FOR_HT_DELETION(pb_ptr) \
+ (pb_ptr)->total_ht_deletions++;
+
+#define H5PB__UPDATE_STATS_FOR_HT_SEARCH(pb_ptr, success, depth) \
+ HDassert(depth >= 0); \
+ if ( success ) { \
+ (pb_ptr)->successful_ht_searches++; \
+ (pb_ptr)->total_successful_ht_search_depth += (int64_t)depth; \
+ } else { \
+ (pb_ptr)->failed_ht_searches++; \
+ (pb_ptr)->total_failed_ht_search_depth += (int64_t)depth; \
+ }
+
+#define H5PB__UPDATE_LRU_SIZE_STATS(pb_ptr) \
+ if ( (pb_ptr)->LRU_len > (pb_ptr)->max_lru_len ) \
+ (pb_ptr)->max_lru_len = (pb_ptr)->LRU_len; \
+ if ( (pb_ptr)->LRU_size > (pb_ptr)->max_lru_size ) \
+ (pb_ptr)->max_lru_size = (pb_ptr)->LRU_size;
+
+#define H5PB__UPDATE_STATS_FOR_LRU_MD_SKIP(pb_ptr) \
+ ((pb_ptr)->lru_md_skips)++;
+
+#define H5PB__UPDATE_STATS_FOR_LRU_RD_SKIP(pb_ptr) \
+ ((pb_ptr)->lru_rd_skips)++;
+
+#define H5PB__UPDATE_STATS_FOR_LRU_TL_SKIP(pb_ptr) \
+{ \
+ HDassert(pb_ptr->vfd_swmr_writer); \
+ ((pb_ptr)->lru_tl_skips)++; \
+}
+
+#define H5PB__UPDATE_TL_SIZE_STATS(pb_ptr) \
+{ \
+ HDassert((pb_ptr)->vfd_swmr_writer); \
+ if ( (pb_ptr)->tl_len > (pb_ptr)->max_tl_len ) \
+ (pb_ptr)->max_tl_len = (pb_ptr)->tl_len; \
+ if ( (pb_ptr)->tl_size > (pb_ptr)->max_tl_size ) \
+ (pb_ptr)->max_tl_size = (pb_ptr)->tl_size; \
+}
+
+#define H5PB__UPDATE_DWL_SIZE_STATS(pb_ptr) \
+{ \
+ HDassert((pb_ptr)->vfd_swmr_writer); \
+ if ( (pb_ptr)->dwl_len > (pb_ptr)->max_dwl_len ) \
+ (pb_ptr)->max_dwl_len = (pb_ptr)->dwl_len; \
+ if ( (pb_ptr)->dwl_size > (pb_ptr)->max_dwl_size ) \
+ (pb_ptr)->max_dwl_size = (pb_ptr)->dwl_size; \
+}
+
+#define H5PB__UPDATE_DWL_DELAYED_WRITES(pb_ptr, insertion_depth, delay) \
+{ \
+ HDassert((pb_ptr)->vfd_swmr_writer); \
+ (pb_ptr)->delayed_writes++; \
+ (pb_ptr)->total_delay += (int64_t)(delay); \
+ (pb_ptr)->total_dwl_ins_depth += (insertion_depth); \
+}
+
+
+#define H5PB__UPDATE_STATS_FOR_ACCESS(pb_ptr, type, size) \
+{ \
+ int _i; \
+ \
+ HDassert(pb_ptr); \
+ HDassert((pb_ptr)->magic == H5PB__H5PB_T_MAGIC); \
+ \
+ if ( H5FD_MEM_DRAW == (type) ) { \
+ _i = H5PB__STATS_RD; \
+ } else if ( (size) > (pb_ptr)->page_size ) { \
+ _i = H5PB__STATS_MPMDE; \
+ } else { \
+ _i = H5PB__STATS_MD; \
+ } \
+ ((pb_ptr)->accesses[_i])++; \
+} /* H5PB__UPDATE_STATS_FOR_ACCESS */
+
+
+#define H5PB__UPDATE_STATS_FOR_BYPASS(pb_ptr, type, size) \
+{ \
+ int ii; \
+ \
+ HDassert(pb_ptr); \
+ HDassert((pb_ptr)->magic == H5PB__H5PB_T_MAGIC); \
+ \
+ if ( H5FD_MEM_DRAW == (type) ) { \
+ ii = H5PB__STATS_RD; \
+ } else if ( (size) > (pb_ptr)->page_size ) { \
+ ii = H5PB__STATS_MPMDE; \
+ } else { \
+ ii = H5PB__STATS_MD; \
+ } \
+ ((pb_ptr)->bypasses[ii])++; \
+} /* H5PB__UPDATE_STATS_FOR_BYPASS */
+
+
+#define H5PB__UPDATE_STATS_FOR_FLUSH(pb_ptr, entry_ptr) \
+{ \
+ int i; \
+ \
+ HDassert(pb_ptr); \
+ HDassert((pb_ptr)->magic == H5PB__H5PB_T_MAGIC); \
+ HDassert(entry_ptr); \
+ HDassert((entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC); \
+ \
+ if ( (entry_ptr)->is_metadata ) { \
+ if ( (entry_ptr)->is_mpmde ) { \
+ i = H5PB__STATS_MPMDE; \
+ } else { \
+ i = H5PB__STATS_MD; \
+ } \
+ } else { \
+ i = H5PB__STATS_RD; \
+ } \
+ ((pb_ptr)->flushes[i])++; \
+} /* H5PB__UPDATE_STATS_FOR_FLUSH */
+
+
+#define H5PB__UPDATE_STATS_FOR_EVICTION(pb_ptr, entry_ptr) \
+{ \
+ int i; \
+ \
+ HDassert(pb_ptr); \
+ HDassert((pb_ptr)->magic == H5PB__H5PB_T_MAGIC); \
+ HDassert(entry_ptr); \
+ HDassert((entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC); \
+ \
+ if ( (entry_ptr)->is_metadata ) { \
+ if ( (entry_ptr)->is_mpmde ) { \
+ i = H5PB__STATS_MPMDE; \
+ } else { \
+ i = H5PB__STATS_MD; \
+ } \
+ } else { \
+ i = H5PB__STATS_RD; \
+ } \
+ ((pb_ptr)->evictions[i])++; \
+} /* H5PB__UPDATE_STATS_FOR_EVICTION */
+
+
+#define H5PB__UPDATE_STATS_FOR_CLEAR(pb_ptr, entry_ptr) \
+{ \
+ int i; \
+ \
+ HDassert(pb_ptr); \
+ HDassert((pb_ptr)->magic == H5PB__H5PB_T_MAGIC); \
+ HDassert(entry_ptr); \
+ HDassert((entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC); \
+ \
+ if ( (entry_ptr)->is_metadata ) { \
+ if ( (entry_ptr)->is_mpmde ) { \
+ i = H5PB__STATS_MPMDE; \
+ } else { \
+ i = H5PB__STATS_MD; \
+ } \
+ } else { \
+ i = H5PB__STATS_RD; \
+ } \
+ ((pb_ptr)->clears[i])++; \
+} /* H5PB__UPDATE_STATS_FOR_CLEAR */
+
+
+#define H5PB__UPDATE_STATS_FOR_INSERTION(pb_ptr, entry_ptr) \
+{ \
+ int i; \
+ \
+ HDassert(pb_ptr); \
+ HDassert((pb_ptr)->magic == H5PB__H5PB_T_MAGIC); \
+ HDassert(entry_ptr); \
+ HDassert((entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC); \
+ \
+ if ( (entry_ptr)->is_metadata ) { \
+ if ( (entry_ptr)->is_mpmde ) { \
+ i = H5PB__STATS_MPMDE; \
+ } else { \
+ i = H5PB__STATS_MD; \
+ } \
+ } else { \
+ i = H5PB__STATS_RD; \
+ } \
+ ((pb_ptr)->insertions[i])++; \
+} /* H5PB__UPDATE_STATS_FOR_INSERTION */
+
+#define H5PB__UPDATE_STATS_FOR_LOAD(pb_ptr, entry_ptr) \
+{ \
+ int i; \
+ \
+ HDassert(pb_ptr); \
+ HDassert((pb_ptr)->magic == H5PB__H5PB_T_MAGIC); \
+ HDassert(entry_ptr); \
+ HDassert((entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC); \
+ \
+ if ( (entry_ptr)->is_metadata ) { \
+ if ( (entry_ptr)->is_mpmde ) { \
+ i = H5PB__STATS_MPMDE; \
+ } else { \
+ i = H5PB__STATS_MD; \
+ } \
+ } else { \
+ i = H5PB__STATS_RD; \
+ } \
+ ((pb_ptr)->loads[i])++; \
+} /* H5PB__UPDATE_STATS_FOR_LOAD */
+
+#else /* H5PB__COLLECT_PAGE_BUFFER_STATS */
+
+#define H5PB__UPDATE_PB_HIT_RATE_STATS(pb_ptr, hit, is_metadata, is_mpmde)
+#define H5PB__UPDATE_HT_SIZE_STATS(pb_ptr)
+#define H5PB__UPDATE_STATS_FOR_HT_INSERTION(pb_ptr)
+#define H5PB__UPDATE_STATS_FOR_HT_DELETION(pb_ptr)
+#define H5PB__UPDATE_HT_SEARCH_STATS(pb_ptr, success, depth)
+#define H5PB__UPDATE_LRU_SIZE_STATS(pb_ptr)
+#define H5PB__UPDATE_STATS_FOR_LRU_MD_SKIP(pb_ptr)
+#define H5PB__UPDATE_STATS_FOR_LRU_RD_SKIP(pb_ptr)
+#define H5PB__UPDATE_STATS_FOR_LRU_TL_SKIP(pb_ptr)
+#define H5PB__UPDATE_STATS_FOR_LRU_DWL_SKIP(pb_ptr)
+#define H5PB__UPDATE_TL_SIZE_STATS(pb_ptr)
+#define H5PB__UPDATE_DWL_SIZE_STATS(pb_ptr)
+#define H5PB__UPDATE_DWL_DELAYED_WRITES(pb_ptr, insertion_depth, delay)
+#define H5PB__UPDATE_STATS_FOR_ACCESS(pb_ptr, type, size)
+#define H5PB__UPDATE_STATS_FOR_BYPASS(pb_ptr, type, size)
+#define H5PB__UPDATE_STATS_FOR_FLUSH(pb_ptr, entry_ptr)
+#define H5PB__UPDATE_STATS_FOR_EVICTION(pb_ptr, entry_ptr)
+#define H5PB__UPDATE_STATS_FOR_CLEAR(pb_ptr, entry_ptr)
+#define H5PB__UPDATE_STATS_FOR_INSERTION(pb_ptr, entry_ptr)
+#define H5PB__UPDATE_STATS_FOR_LOAD(pb_ptr, entry_ptr)
+
+#endif /* H5PB__COLLECT_PAGE_BUFFER_STATS */
+
+
+/***********************************************************************
+ *
+ * Hash table access and manipulation macros:
+ *
+ * The following macros handle searches, insertions, and deletion in
+ * the hash table.
+ *
+ * Note that the input to the hash function is the page of the page
+ * buffer entry, not it address (recall that page * page_size) == addr).
+ *
+ * JRM -- 10/09/18
+ *
+ * Changes:
+ *
+ * - None
+ *
+ ***********************************************************************/
+
+#define H5PB__HASH_MASK ((uint64_t)(H5PB__HASH_TABLE_LEN - 1))
+
+#define H5PB__HASH_FCN(x) (int)(((uint64_t)(x)) & H5PB__HASH_MASK)
+
+#if H5PB__DO_SANITY_CHECKS
+
+#define H5PB__PRE_HT_INSERT_SC(pb_ptr, entry_ptr, fail_val) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->magic != H5PB__H5PB_T_MAGIC ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (entry_ptr)->ht_next != NULL ) || \
+ ( (entry_ptr)->ht_prev != NULL ) || \
+ ( (entry_ptr)->size <= 0 ) || \
+ ( H5PB__HASH_FCN((entry_ptr)->page) < 0 ) || \
+ ( H5PB__HASH_FCN((entry_ptr)->page) >= H5PB__HASH_TABLE_LEN ) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + \
+ (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_len != (pb_ptr)->il_len ) || \
+ ( (pb_ptr)->index_size != (pb_ptr)->il_size ) || \
+ ( (pb_ptr)->curr_pages < 0 ) || \
+ ( (pb_ptr)->curr_rd_pages < 0 ) || \
+ ( (pb_ptr)->curr_md_pages < 0 ) || \
+ ( ((pb_ptr)->curr_pages != \
+ ((pb_ptr)->curr_md_pages + (pb_ptr)->curr_rd_pages)) ) || \
+ ( (pb_ptr)->mpmde_count < 0 ) || \
+ ( (pb_ptr)->index_len != \
+ ((pb_ptr)->curr_pages + (pb_ptr)->mpmde_count) ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, fail_val, "pre HT insert SC failed") \
+}
+
+#define H5PB__POST_HT_INSERT_SC(pb_ptr, entry_ptr, fail_val) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->magic != H5PB__H5PB_T_MAGIC ) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + \
+ (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_len != (pb_ptr)->il_len ) || \
+ ( (pb_ptr)->index_len != \
+ ((pb_ptr)->curr_pages + (pb_ptr)->mpmde_count) ) || \
+ ( (pb_ptr)->index_size != (pb_ptr)->il_size) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, fail_val, "post HT insert SC failed") \
+}
+
+#define H5PB__PRE_HT_REMOVE_SC(pb_ptr, entry_ptr) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->magic != H5PB__H5PB_T_MAGIC ) || \
+ ( (pb_ptr)->index_len < 1 ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (pb_ptr)->index_size < (int64_t)((entry_ptr)->size) ) || \
+ ( (entry_ptr)->size <= 0 ) || \
+ ( H5PB__HASH_FCN((entry_ptr)->page) < 0 ) || \
+ ( H5PB__HASH_FCN((entry_ptr)->page) >= H5PB__HASH_TABLE_LEN ) || \
+ ( ((pb_ptr)->ht)[(H5PB__HASH_FCN((entry_ptr)->page))] \
+ == NULL ) || \
+ ( ( ((pb_ptr)->ht)[(H5PB__HASH_FCN((entry_ptr)->page))] \
+ != (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev == NULL ) ) || \
+ ( ( ((pb_ptr)->ht)[(H5PB__HASH_FCN((entry_ptr)->page))] == \
+ (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev != NULL ) ) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + \
+ (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_len != (pb_ptr)->il_len ) || \
+ ( (pb_ptr)->index_size != (pb_ptr)->il_size ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, "pre HT remove SC failed") \
+}
+
+#define H5PB__POST_HT_REMOVE_SC(pb_ptr, entry_ptr) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->magic != H5PB__H5PB_T_MAGIC ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (entry_ptr)->size <= 0 ) || \
+ ( (entry_ptr)->ht_prev != NULL ) || \
+ ( (entry_ptr)->ht_prev != NULL ) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + \
+ (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_len != (pb_ptr)->il_len ) || \
+ ( (pb_ptr)->index_size != (pb_ptr)->il_size ) || \
+ ( (pb_ptr)->curr_pages < 0 ) || \
+ ( (pb_ptr)->curr_rd_pages < 0 ) || \
+ ( (pb_ptr)->curr_md_pages < 0 ) || \
+ ( ((pb_ptr)->curr_pages != \
+ ((pb_ptr)->curr_md_pages + (pb_ptr)->curr_rd_pages)) ) || \
+ ( (pb_ptr)->mpmde_count < 0 ) || \
+ ( (pb_ptr)->index_len != \
+ ((pb_ptr)->curr_pages + (pb_ptr)->mpmde_count) ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, "post HT remove SC failed") \
+}
+
+#define H5PB__PRE_HT_SEARCH_SC(pb_ptr, page, fail_val) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->magic != H5PB__H5PB_T_MAGIC ) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + (pb_ptr)->dirty_index_size) ) || \
+ ( H5PB__HASH_FCN(page) < 0 ) || \
+ ( H5PB__HASH_FCN(page) >= H5PB__HASH_TABLE_LEN ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, fail_val, "pre HT search SC failed") \
+}
+
+#define H5PB__POST_SUC_HT_SEARCH_SC(pb_ptr, entry_ptr, k, fail_val) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->magic != H5PB__H5PB_T_MAGIC ) || \
+ ( (pb_ptr)->index_len < 1 ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (pb_ptr)->index_size < (int64_t)((entry_ptr)->size )) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + (pb_ptr)->dirty_index_size) ) || \
+ ( (entry_ptr)->size <= 0 ) || \
+ ( ((pb_ptr)->ht)[k] == NULL ) || \
+ ( ( ((pb_ptr)->ht)[k] != (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev == NULL ) ) || \
+ ( ( ((pb_ptr)->ht)[k] == (entry_ptr) ) && \
+ ( (entry_ptr)->ht_prev != NULL ) ) || \
+ ( ( (entry_ptr)->ht_prev != NULL ) && \
+ ( (entry_ptr)->ht_prev->ht_next != (entry_ptr) ) ) || \
+ ( ( (entry_ptr)->ht_next != NULL ) && \
+ ( (entry_ptr)->ht_next->ht_prev != (entry_ptr) ) ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, fail_val, \
+ "post successful HT search SC failed") \
+}
+
+#define H5PB__POST_HT_SHIFT_TO_FRONT_SC(pb_ptr, entry_ptr, k, fail_val) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( ((pb_ptr)->ht)[k] != (entry_ptr) ) || \
+ ( (entry_ptr)->ht_prev != NULL ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, fail_val, \
+ "post HT shift to front SC failed") \
+}
+
+#define H5PB__PRE_HT_ENTRY_SIZE_CHANGE_SC(pb_ptr, old_size, new_size, \
+ entry_ptr, was_clean) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->index_len <= 0 ) || \
+ ( (pb_ptr)->index_size <= 0 ) || \
+ ( (new_size) <= 0 ) || \
+ ( (old_size) > (pb_ptr)->index_size ) || \
+ ( ( (pb_ptr)->index_len == 1 ) && \
+ ( (pb_ptr)->index_size != (old_size) ) ) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + \
+ (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) || \
+ ( ( !( was_clean ) || \
+ ( (pb_ptr)->clean_index_size < (old_size) ) ) && \
+ ( ( (was_clean) ) || \
+ ( (pb_ptr)->dirty_index_size < (old_size) ) ) ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (pb_ptr)->index_len != (pb_ptr)->il_len ) || \
+ ( (pb_ptr)->index_size != (pb_ptr)->il_size ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "pre HT entry size change SC failed") \
+}
+
+#define H5PB__POST_HT_ENTRY_SIZE_CHANGE_SC(pb_ptr, old_size, new_size, \
+ entry_ptr) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->index_len <= 0 ) || \
+ ( (pb_ptr)->index_size <= 0 ) || \
+ ( (new_size) > (pb_ptr)->index_size ) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + \
+ (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) || \
+ ( ( !((entry_ptr)->is_dirty ) || \
+ ( (pb_ptr)->dirty_index_size < (new_size) ) ) && \
+ ( ( ((entry_ptr)->is_dirty) ) || \
+ ( (pb_ptr)->clean_index_size < (new_size) ) ) ) || \
+ ( ( (pb_ptr)->index_len == 1 ) && \
+ ( (pb_ptr)->index_size != (new_size) ) ) || \
+ ( (pb_ptr)->index_len != (pb_ptr)->il_len ) || \
+ ( (pb_ptr)->index_size != (pb_ptr)->il_size ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "post HT entry size change SC failed") \
+}
+
+#define H5PB__PRE_HT_UPDATE_FOR_ENTRY_CLEAN_SC(pb_ptr, entry_ptr) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->magic != H5PB__H5PB_T_MAGIC ) || \
+ ( (pb_ptr)->index_len <= 0 ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (entry_ptr)->is_dirty != FALSE ) || \
+ ( (pb_ptr)->index_size < (int64_t)((entry_ptr)->size) ) || \
+ ( (pb_ptr)->dirty_index_size < (int64_t)((entry_ptr)->size) ) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "pre HT update for entry clean SC failed") \
+}
+
+#define H5PB__PRE_HT_UPDATE_FOR_ENTRY_DIRTY_SC(pb_ptr, entry_ptr) \
+if ( ( (pb_ptr) == NULL ) || \
+ ( (pb_ptr)->magic != H5PB__H5PB_T_MAGIC ) || \
+ ( (pb_ptr)->index_len <= 0 ) || \
+ ( (entry_ptr) == NULL ) || \
+ ( (entry_ptr)->is_dirty != TRUE ) || \
+ ( (pb_ptr)->index_size < (int64_t)((entry_ptr)->size) ) || \
+ ( (pb_ptr)->clean_index_size < (int64_t)((entry_ptr)->size) ) || \
+ ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "pre HT update for entry dirty SC failed") \
+}
+
+#define H5PB__POST_HT_UPDATE_FOR_ENTRY_CLEAN_SC(pb_ptr, entry_ptr) \
+if ( ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "post HT update for entry clean SC failed") \
+}
+
+#define H5PB__POST_HT_UPDATE_FOR_ENTRY_DIRTY_SC(pb_ptr, entry_ptr) \
+if ( ( (pb_ptr)->index_size != \
+ ((pb_ptr)->clean_index_size + (pb_ptr)->dirty_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->clean_index_size) ) || \
+ ( (pb_ptr)->index_size < ((pb_ptr)->dirty_index_size) ) ) { \
+ HGOTO_ERROR(H5E_PAGEBUF, H5E_SYSTEM, FAIL, \
+ "post HT update for entry dirty SC failed") \
+}
+
+#else /* H5PB__DO_SANITY_CHECKS */
+
+#define H5PB__PRE_HT_INSERT_SC(pb_ptr, entry_ptr, fail_val)
+#define H5PB__POST_HT_INSERT_SC(pb_ptr, entry_ptr, fail_val)
+#define H5PB__PRE_HT_REMOVE_SC(pb_ptr, entry_ptr)
+#define H5PB__POST_HT_REMOVE_SC(pb_ptr, entry_ptr)
+#define H5PB__PRE_HT_SEARCH_SC(pb_ptr, Addr, fail_val)
+#define H5PB__POST_SUC_HT_SEARCH_SC(pb_ptr, entry_ptr, k, fail_val)
+#define H5PB__POST_HT_SHIFT_TO_FRONT_SC(pb_ptr, entry_ptr, k, fail_val)
+#define H5PB__PRE_HT_UPDATE_FOR_ENTRY_CLEAN_SC(pb_ptr, entry_ptr)
+#define H5PB__PRE_HT_UPDATE_FOR_ENTRY_DIRTY_SC(pb_ptr, entry_ptr)
+#define H5PB__PRE_HT_ENTRY_SIZE_CHANGE_SC(pb_ptr, old_size, new_size, \
+ entry_ptr, was_clean)
+#define H5PB__POST_HT_ENTRY_SIZE_CHANGE_SC(pb_ptr, old_size, new_size, \
+ entry_ptr)
+#define H5PB__POST_HT_UPDATE_FOR_ENTRY_CLEAN_SC(pb_ptr, entry_ptr)
+#define H5PB__POST_HT_UPDATE_FOR_ENTRY_DIRTY_SC(pb_ptr, entry_ptr)
+
+#endif /* H5PB__DO_SANITY_CHECKS */
+
+
+#define H5PB__INSERT_IN_INDEX(pb_ptr, entry_ptr, fail_val) \
+{ \
+ int k; \
+ H5PB__PRE_HT_INSERT_SC(pb_ptr, entry_ptr, fail_val) \
+ k = H5PB__HASH_FCN((entry_ptr)->page); \
+ if(((pb_ptr)->ht)[k] != NULL) { \
+ (entry_ptr)->ht_next = ((pb_ptr)->ht)[k]; \
+ (entry_ptr)->ht_next->ht_prev = (entry_ptr); \
+ } \
+ ((pb_ptr)->ht)[k] = (entry_ptr); \
+ (pb_ptr)->index_len++; \
+ (pb_ptr)->index_size += (int64_t)((entry_ptr)->size); \
+ if((entry_ptr)->is_dirty) { \
+ (pb_ptr)->dirty_index_size += (int64_t)((entry_ptr)->size); \
+ } else { \
+ (pb_ptr)->clean_index_size += (int64_t)((entry_ptr)->size); \
+ } \
+ if ( (entry_ptr)->is_metadata ) { \
+ if ( (entry_ptr)->is_mpmde ) { \
+ ((pb_ptr)->mpmde_count)++; \
+ } else { \
+ ((pb_ptr)->curr_md_pages)++; \
+ (pb_ptr)->curr_pages++; \
+ } \
+ } else { \
+ ((pb_ptr)->curr_rd_pages)++; \
+ (pb_ptr)->curr_pages++; \
+ } \
+ H5PB__IL_DLL_APPEND((entry_ptr), (pb_ptr)->il_head, \
+ (pb_ptr)->il_tail, (pb_ptr)->il_len, \
+ (pb_ptr)->il_size, fail_val) \
+ H5PB__UPDATE_STATS_FOR_HT_INSERTION(pb_ptr) \
+ H5PB__UPDATE_HT_SIZE_STATS(pb_ptr) \
+ H5PB__POST_HT_INSERT_SC(pb_ptr, entry_ptr, fail_val) \
+}
+
+#define H5PB__DELETE_FROM_INDEX(pb_ptr, entry_ptr, fail_val) \
+{ \
+ int k; \
+ H5PB__PRE_HT_REMOVE_SC(pb_ptr, entry_ptr) \
+ k = H5PB__HASH_FCN((entry_ptr)->page); \
+ if((entry_ptr)->ht_next) \
+ (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \
+ if((entry_ptr)->ht_prev) \
+ (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \
+ if(((pb_ptr)->ht)[k] == (entry_ptr)) \
+ ((pb_ptr)->ht)[k] = (entry_ptr)->ht_next; \
+ (entry_ptr)->ht_next = NULL; \
+ (entry_ptr)->ht_prev = NULL; \
+ (pb_ptr)->index_len--; \
+ (pb_ptr)->index_size -= (int64_t)((entry_ptr)->size); \
+ if((entry_ptr)->is_dirty) { \
+ (pb_ptr)->dirty_index_size -= (int64_t)((entry_ptr)->size); \
+ } else { \
+ (pb_ptr)->clean_index_size -= (int64_t)((entry_ptr)->size); \
+ } \
+ if ( (entry_ptr)->is_metadata ) { \
+ if ( (entry_ptr)->is_mpmde ) { \
+ ((pb_ptr)->mpmde_count)--; \
+ } else { \
+ ((pb_ptr)->curr_md_pages)--; \
+ (pb_ptr)->curr_pages--; \
+ } \
+ } else { \
+ ((pb_ptr)->curr_rd_pages)--; \
+ (pb_ptr)->curr_pages--; \
+ } \
+ H5PB__IL_DLL_REMOVE((entry_ptr), (pb_ptr)->il_head, \
+ (pb_ptr)->il_tail, (pb_ptr)->il_len, \
+ (pb_ptr)->il_size, fail_val) \
+ H5PB__UPDATE_STATS_FOR_HT_DELETION(pb_ptr) \
+ H5PB__POST_HT_REMOVE_SC(pb_ptr, entry_ptr) \
+}
+
+#define H5PB__SEARCH_INDEX(pb_ptr, Page, entry_ptr, fail_val) \
+{ \
+ int k; \
+ int depth = 0; \
+ H5PB__PRE_HT_SEARCH_SC(pb_ptr, Page, fail_val) \
+ k = H5PB__HASH_FCN(Page); \
+ entry_ptr = ((pb_ptr)->ht)[k]; \
+ while(entry_ptr) { \
+ if ( (Page) == (entry_ptr)->page ) { \
+ H5PB__POST_SUC_HT_SEARCH_SC(pb_ptr, entry_ptr, k, fail_val) \
+ if ( (entry_ptr) != ((pb_ptr)->ht)[k] ) { \
+ if ( (entry_ptr)->ht_next ) \
+ (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev; \
+ HDassert((entry_ptr)->ht_prev != NULL); \
+ (entry_ptr)->ht_prev->ht_next = (entry_ptr)->ht_next; \
+ ((pb_ptr)->ht)[k]->ht_prev = (entry_ptr); \
+ (entry_ptr)->ht_next = ((pb_ptr)->ht)[k]; \
+ (entry_ptr)->ht_prev = NULL; \
+ ((pb_ptr)->ht)[k] = (entry_ptr); \
+ H5PB__POST_HT_SHIFT_TO_FRONT_SC(pb_ptr, entry_ptr, k, fail_val)\
+ } \
+ break; \
+ } \
+ (entry_ptr) = (entry_ptr)->ht_next; \
+ (depth)++; \
+ } \
+ H5PB__UPDATE_STATS_FOR_HT_SEARCH(pb_ptr, (entry_ptr != NULL), depth) \
+}
+
+#define H5PB__UPDATE_INDEX_FOR_ENTRY_CLEAN(pb_ptr, entry_ptr) \
+{ \
+ H5PB__PRE_HT_UPDATE_FOR_ENTRY_CLEAN_SC(pb_ptr, entry_ptr); \
+ (pb_ptr)->dirty_index_size -= (int64_t)((entry_ptr)->size); \
+ (pb_ptr)->clean_index_size += (int64_t)((entry_ptr)->size); \
+ H5PB__POST_HT_UPDATE_FOR_ENTRY_CLEAN_SC(pb_ptr, entry_ptr); \
+}
+
+#define H5PB__UPDATE_INDEX_FOR_ENTRY_DIRTY(pb_ptr, entry_ptr) \
+{ \
+ H5PB__PRE_HT_UPDATE_FOR_ENTRY_DIRTY_SC(pb_ptr, entry_ptr); \
+ (pb_ptr)->clean_index_size -= (int64_t)((entry_ptr)->size); \
+ (pb_ptr)->dirty_index_size += (int64_t)((entry_ptr)->size); \
+ H5PB__POST_HT_UPDATE_FOR_ENTRY_DIRTY_SC(pb_ptr, entry_ptr); \
+}
+
+#define H5PB__UPDATE_INDEX_FOR_SIZE_CHANGE(pb_ptr, old_size, new_size, \
+ entry_ptr, was_clean) \
+{ \
+ H5PB__PRE_HT_ENTRY_SIZE_CHANGE_SC(pb_ptr, old_size, new_size, \
+ entry_ptr, was_clean) \
+ (pb_ptr)->index_size -= (old_size); \
+ (pb_ptr)->index_size += (new_size); \
+ if(was_clean) { \
+ (pb_ptr)->clean_index_size -= (old_size); \
+ } else { \
+ (pb_ptr)->dirty_index_size -= (old_size); \
+ } \
+ if((entry_ptr)->is_dirty) { \
+ (pb_ptr)->dirty_index_size += (new_size); \
+ } else { \
+ (pb_ptr)->clean_index_size += (new_size); \
+ } \
+ H5PB__DLL_UPDATE_FOR_SIZE_CHANGE((pb_ptr)->il_len, \
+ (pb_ptr)->il_size, \
+ (old_size), (new_size)) \
+ H5PB__POST_HT_ENTRY_SIZE_CHANGE_SC(pb_ptr, old_size, new_size, \
+ entry_ptr) \
+}
+
+
+/***********************************************************************
+ *
+ * Replacement policy update macros
+ *
+ * The following macros handle updates to the replacement policy for
+ * insertions, flushes, and evictions.
+ *
+ * At present, the only replacement policy is a modified LRU policy.
+ *
+ * JRM -- 10/09/18
+ *
+ ***********************************************************************/
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__UPDATE_RP_FOR_EVICTION
+ *
+ * Purpose: Update the replacement policy data structures for an
+ * eviction of the specified page buffer entry.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 10/09/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__UPDATE_RP_FOR_EVICTION(pb_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (pb_ptr) ); \
+ HDassert( (pb_ptr)->magic == H5PB__H5PB_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC ); \
+ HDassert( !((entry_ptr)->is_dirty) ); \
+ HDassert( (entry_ptr)->size >= pb_ptr->page_size ); \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list. */ \
+ \
+ H5PB__DLL_REMOVE((entry_ptr), (pb_ptr)->LRU_head_ptr, \
+ (pb_ptr)->LRU_tail_ptr, (pb_ptr)->LRU_len, \
+ (pb_ptr)->LRU_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ \
+} /* H5PB__UPDATE_RP_FOR_EVICTION */
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__UPDATE_RP_FOR_REMOVE
+ *
+ * Purpose: Update the replacement policy data structures for the
+ * removal of the specified page buffer entry from the
+ * replacement policy, but not from the page buffer.
+ *
+ * At present, this this only happens when an entry is
+ * dirtied, and subject to a delayed write.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 10/09/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__UPDATE_RP_FOR_REMOVE(pb_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (pb_ptr) ); \
+ HDassert( (pb_ptr)->magic == H5PB__H5PB_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC ); \
+ HDassert( ! ((entry_ptr)->is_mpmde) ); \
+ HDassert( (entry_ptr)->size == pb_ptr->page_size ); \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* remove the entry from the LRU list. */ \
+ \
+ H5PB__DLL_REMOVE((entry_ptr), (pb_ptr)->LRU_head_ptr, \
+ (pb_ptr)->LRU_tail_ptr, (pb_ptr)->LRU_len, \
+ (pb_ptr)->LRU_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ \
+} /* H5PB__UPDATE_RP_FOR_EVICTION */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__UPDATE_RP_FOR_ACCESS
+ *
+ * Purpose: Update the replacement policy data structures for an
+ * access of the specified page buffer entry.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 10/09/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (pb_ptr) ); \
+ HDassert( (pb_ptr)->magic == H5PB__H5PB_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC ); \
+ HDassert( (entry_ptr)->size >= pb_ptr->page_size ); \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* Move entry to the head of the LRU */ \
+ \
+ H5PB__DLL_REMOVE((entry_ptr), (pb_ptr)->LRU_head_ptr, \
+ (pb_ptr)->LRU_tail_ptr, (pb_ptr)->LRU_len, \
+ (pb_ptr)->LRU_size, (fail_val)) \
+ \
+ H5PB__DLL_PREPEND((entry_ptr), (pb_ptr)->LRU_head_ptr, \
+ (pb_ptr)->LRU_tail_ptr, (pb_ptr)->LRU_len, \
+ (pb_ptr)->LRU_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ \
+} /* H5PB__UPDATE_RP_FOR_ACCESS */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__UPDATE_RP_FOR_FLUSH
+ *
+ * Purpose: Update the replacement policy data structures for a flush
+ * of the specified page buffer entry.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 10/09/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__UPDATE_RP_FOR_FLUSH(pb_ptr, entry_ptr, fail_val) \
+{ \
+ H5PB__UPDATE_RP_FOR_ACCESS(pb_ptr, entry_ptr, fail_val) \
+ \
+} /* H5PB__UPDATE_RP_FOR_FLUSH */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__UPDATE_RP_FOR_INSERT_APPEND
+ *
+ * Purpose: Update the replacement policy data structures for an
+ * insertion of the specified cache entry.
+ *
+ * Unlike H5PB__UPDATE_RP_FOR_INSERTION below, mark the
+ * new entry as the LEAST recently used entry, not the
+ * most recently used.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 10/10/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__UPDATE_RP_FOR_INSERT_APPEND(pb_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (pb_ptr) ); \
+ HDassert( (pb_ptr)->magic == H5PB__H5PB_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC ); \
+ HDassert( (entry_ptr)->size == pb_ptr->page_size ); \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the tail of the LRU list. */ \
+ \
+ H5PB__DLL_APPEND((entry_ptr), (pb_ptr)->LRU_head_ptr, \
+ (pb_ptr)->LRU_tail_ptr, (pb_ptr)->LRU_len, \
+ (pb_ptr)->LRU_size, (fail_val)) \
+ \
+ H5PB__UPDATE_LRU_SIZE_STATS(pb_ptr) \
+ \
+ /* End modified LRU specific code. */ \
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__UPDATE_RP_FOR_INSERTION
+ *
+ * Purpose: Update the replacement policy data structures for an
+ * insertion of the specified cache entry.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 10/10/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__UPDATE_RP_FOR_INSERTION(pb_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (pb_ptr) ); \
+ HDassert( (pb_ptr)->magic == H5PB__H5PB_T_MAGIC ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC ); \
+ HDassert( (entry_ptr)->size >= pb_ptr->page_size ); \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the head of the LRU list. */ \
+ \
+ H5PB__DLL_PREPEND((entry_ptr), (pb_ptr)->LRU_head_ptr, \
+ (pb_ptr)->LRU_tail_ptr, (pb_ptr)->LRU_len, \
+ (pb_ptr)->LRU_size, (fail_val)) \
+ \
+ H5PB__UPDATE_LRU_SIZE_STATS(pb_ptr) \
+ \
+ /* End modified LRU specific code. */ \
+}
+
+
+/***********************************************************************
+ *
+ * Tick list management macros
+ *
+ * When the target file is opened in VFD SWMR writer mode, the page
+ * buffer must retain copies of all metadata writes during each tick so
+ * that the metadata file can be updated correctly in end of tick
+ * processing.
+ *
+ * Once tick processing is complete, all entries are removed from the
+ * tick list, to leave it empty for the next tick. Metadata pages from
+ * the tick list are already in the replacement policy, and thus require
+ * no further action.
+ *
+ * Multi-page metadata entries are evicted from the page buffer if they
+ * are not subject to delayed write, or left in the delayed write list
+ * for later flush and eviction if they are.
+ *
+ * The macros required to support this are defined below.
+ *
+ * JRM -- 10/09/18
+ *
+ ***********************************************************************/
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__INSERT_IN_TL
+ *
+ * Purpose: Insert the specified page buffer entry at the head of the
+ * tick list.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 10/10/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__INSERT_IN_TL(pb_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (pb_ptr) ); \
+ HDassert( (pb_ptr)->magic == H5PB__H5PB_T_MAGIC ); \
+ HDassert( (pb_ptr)->vfd_swmr_writer ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC ); \
+ HDassert( (entry_ptr)->modified_this_tick ); \
+ HDassert( (entry_ptr)->size >= pb_ptr->page_size ); \
+ \
+ /* insert the entry at the head of the tick list. */ \
+ \
+ H5PB__TL_DLL_PREPEND((entry_ptr), (pb_ptr)->tl_head_ptr, \
+ (pb_ptr)->tl_tail_ptr, (pb_ptr)->tl_len, \
+ (pb_ptr)->tl_size, (fail_val)) \
+ \
+ H5PB__UPDATE_TL_SIZE_STATS(pb_ptr) \
+ \
+} /* H5PB__INSERT_IN_TL */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__REMOVE_FROM_TL
+ *
+ * Purpose: Remove the specified page buffer entry from the tick list.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 10/10/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__REMOVE_FROM_TL(pb_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (pb_ptr) ); \
+ HDassert( (pb_ptr)->magic == H5PB__H5PB_T_MAGIC ); \
+ HDassert( (pb_ptr)->vfd_swmr_writer ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC ); \
+ HDassert( (entry_ptr)->modified_this_tick ); \
+ HDassert( (entry_ptr)->size >= pb_ptr->page_size ); \
+ \
+ /* remove the entry from the tick list. */ \
+ \
+ H5PB__TL_DLL_REMOVE((entry_ptr), (pb_ptr)->tl_head_ptr, \
+ (pb_ptr)->tl_tail_ptr, (pb_ptr)->tl_len, \
+ (pb_ptr)->tl_size, (fail_val)) \
+ \
+ \
+} /* H5PB__REMOVE_FROM_TL */
+
+
+/***********************************************************************
+ *
+ * Delayed write list management macros
+ *
+ * When the target file is opened in VFD SWMR writer mode, the page
+ * buffer must delay flush of all metadata pages and multi-page metadata
+ * entries that:
+ *
+ * 1) have not appeared in the metadata file index for at least max_lag
+ * ticks, and
+ *
+ * 2) a previous version of the metadata page or multi-page metadata
+ * cache entry exists in the file.
+ *
+ * Failure to do so can result in VFD SWMR readers to receive messages
+ * from the future.
+ *
+ * To minimize overhead, the delayed write list is sorted in decreasing
+ * values of the constituent delay_write_until fields.
+ *
+ * Entries are removed from the delayed write list when their
+ * delay_write_until fields are satisfied. Metadata pages are inserted
+ * at the bottom of the replacement policy, and multi-page metadata
+ * entries are immediately flushed and evicted.
+ *
+ * The macros required to support this are defined below.
+ *
+ * JRM -- 10/09/18
+ *
+ ***********************************************************************/
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__INSERT_IN_DWL
+ *
+ * Insert the supplied page buffer entry in the delayed write list
+ * maintaining the invarient:
+ *
+ * entry_ptr->next == NULL ||
+ * entry_ptr->delay_write_until >= entry_ptr->next->delay_write_until
+ *
+ * In passing update pb_ptr->max_delay if appropriate.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 10/10/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__INSERT_IN_DWL(pb_ptr, entry_ptr, fail_val) \
+{ \
+ int insertion_depth = 0; \
+ uint64_t delay; \
+ H5PB_entry_t * suc_ptr; \
+ \
+ HDassert( (pb_ptr) ); \
+ HDassert( (pb_ptr)->magic == H5PB__H5PB_T_MAGIC ); \
+ HDassert( (pb_ptr)->vfd_swmr_writer ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC ); \
+ HDassert( (entry_ptr)->size >= pb_ptr->page_size ); \
+ HDassert( (entry_ptr)->delay_write_until > (pb_ptr)->cur_tick ); \
+ \
+ delay = (entry_ptr)->delay_write_until - (pb_ptr)->cur_tick; \
+ suc_ptr = pb_ptr->dwl_head_ptr; \
+ \
+ while ( (suc_ptr) && \
+ ((suc_ptr)->delay_write_until > (entry_ptr)->delay_write_until) ) \
+ { \
+ insertion_depth++; \
+ suc_ptr = suc_ptr->next; \
+ } \
+ \
+ H5PB__DLL_INSERT_BEFORE((entry_ptr), (suc_ptr), \
+ (pb_ptr)->dwl_head_ptr, \
+ (pb_ptr)->dwl_tail_ptr, (pb_ptr)->dwl_len, \
+ (pb_ptr)->dwl_size, (fail_val)) \
+ \
+ if ( entry_ptr->delay_write_until > pb_ptr->max_delay ) \
+ pb_ptr->max_delay = entry_ptr->delay_write_until; \
+ \
+ H5PB__UPDATE_DWL_SIZE_STATS(pb_ptr) \
+ H5PB__UPDATE_DWL_DELAYED_WRITES(pb_ptr, insertion_depth, delay) \
+ \
+} /* H5PB__INSERT_IN_DWL */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Macro: H5PB__REMOVE_FROM_DWL
+ *
+ * Purpose: Remove the specified page buffer entry from the delayed
+ * write list.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 10/10/18
+ *
+ * Modifications:
+ *
+ * None.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#define H5PB__REMOVE_FROM_DWL(pb_ptr, entry_ptr, fail_val) \
+{ \
+ HDassert( (pb_ptr) ); \
+ HDassert( (pb_ptr)->magic == H5PB__H5PB_T_MAGIC ); \
+ HDassert( (pb_ptr)->vfd_swmr_writer ); \
+ HDassert( (entry_ptr) ); \
+ HDassert( (entry_ptr)->magic == H5PB__H5PB_ENTRY_T_MAGIC ); \
+ HDassert( (entry_ptr)->size >= pb_ptr->page_size ); \
+ HDassert( (entry_ptr)->delay_write_until == 0 ); \
+ \
+ /* remove the entry from the delayed write list. */ \
+ \
+ H5PB__DLL_REMOVE((entry_ptr), (pb_ptr)->dwl_head_ptr, \
+ (pb_ptr)->dwl_tail_ptr, (pb_ptr)->dwl_len, \
+ (pb_ptr)->dwl_size, (fail_val)) \
+ \
+ \
+} /* H5PB__REMOVE_FROM_DWLL */
+
+
/****************************/
/* Package Private Typedefs */
/****************************/
-typedef struct H5PB_entry_t {
- void *page_buf_ptr; /* Pointer to the buffer containing the data */
- haddr_t addr; /* Address of the page in the file */
- H5F_mem_page_t type; /* Type of the page entry (H5F_MEM_PAGE_RAW/META) */
- hbool_t is_dirty; /* Flag indicating whether the page has dirty data or not */
+/****************************************************************************
+ *
+ * structure H5PB_entry_t
+ *
+ * Individual instances of the H5PB_entry_t structure are used to manage
+ * individual pages in the page buffer. In the case of a VFD SWMR writer,
+ * they are also used to manage multi-page metadata entries.
+ *
+ * The fields of this structure are discussed below:
+ *
+ * JRM - 9/27/18
+ *
+ * magic: Unsigned 32 bit integer that must always be set to
+ * H5PB__H5PB_ENTRY_T_MAGIC when the entry is valid.
+ *
+ * pb_ptr: Pointer to the page buffer that contains this entry.
+ *
+ * addr: Base address of the page in the file.
+ *
+ * page: Page offset of the page -- i.e. addr / pb_ptr->page_size.
+ * Note that addr must always equal page * pb_ptr->page_size.
+ *
+ * size: Size of the page buffer entry in bytes. Under normal
+ * circumstance, this will always be equal to pb_ptr->page_size.
+ * However, in the context of a VFD SWMR writer, the page
+ * buffer may be used to store multi-page metadata entries
+ * until the end of tick, or to delay writes of such entries
+ * for up to max_lag ticks.
+ *
+ * In such cases, size must be greater than pb_ptr->page_size.
+ *
+ * image_ptr: Pointer to void. When not NULL, this field points to a
+ * dynamically allocated block of size bytes in which the
+ * on disk image of the page. In the context of VFD SWMR,
+ * it points to the image of the multi-page metadata entry.
+ *
+ * mem_type: Type (H5F_mem_t) of the page buffer entry. This value
+ * is needed when reading or writing the entry from/to file.
+ *
+ * is_metadata: Boolean flag that is set to TRUE iff the associated
+ * entry is a page of metadata (or, in the context of VFD
+ * SWMR, a multi-page metadata entry).
+ *
+ * is_dirty: Boolean flag indicating whether the contents of the page
+ * buffer entry has been modified since the last time it
+ * was written to disk.
+ *
+ *
+ * Fields supporting the hash table:
+ *
+ * Entries in the page buffer are indexed by a more or less conventional
+ * hash table with chaining (see header comment on H5PB_t for futher details).
+ * If there are multiple entries in any hash bin, they are stored in a doubly
+ * linked list.
+ *
+ * To facilitate flushing the page buffer, we also maintain a doubly linked
+ * list of all entries in the page buffer.
+ *
+ * ht_next: Next pointer used by the hash table to store multiple
+ * entries in a single hash bin. This field points to the
+ * next entry in the doubly linked list of entries in the
+ * hash bin, or NULL if there is no next entry.
+ *
+ * ht_prev: Prev pointer used by the hash table to store multiple
+ * entries in a single hash bin. This field points to the
+ * previous entry in the doubly linked list of entries in
+ * the hash bin, or NULL if there is no previuos entry.
+ *
+ * il_next: Next pointer used by the index to maintain a doubly linked
+ * list of all entries in the index (and thus in the page buffer).
+ * This field contains a pointer to the next entry in the
+ * index list, or NULL if there is no next entry.
+ *
+ * il_prev: Prev pointer used by the index to maintain a doubly linked
+ * list of all entries in the index (and thus in the page buffer).
+ * This field contains a pointer to the previous entry in the
+ * index list, or NULL if there is no previous entry.
+ *
+ *
+ * Fields supporting replacement policies:
+ *
+ * The page buffer must have a replacement policy, and it will usually be
+ * necessary for this structure to contain fields supporting that policy.
+ *
+ * At present, only a modified LRU replacement policy is contemplated,
+ * (see header comment for H5PB_t for details), for which the following
+ * fields are adequate.
+ *
+ * next: Next pointer in either the LRU, or (in the context of
+ * VFD SWMR) the delayed write list. If there is no next entry
+ * on the list, this field should be set to NULL.
+ *
+ * prev: Prev pointer in either the LRU, or (in the context of
+ * VFD SWMR) the delayed write list. If there is no previous
+ * entry on the list, this field should be set to NULL.
+ *
+ * Fields supporting VFD SWMR:
+ *
+ * is_mpmde: Boolean flag that is set to TRUE iff the entry
+ * is a multi-page metadata entry. In the absense of VFD
+ * SWMR, the field should always be set to FALSE.
+ *
+ * Observe that:
+ *
+ * is_mpmde <==> is_metadata && size > pb_ptr->page_size
+ *
+ * loaded: Boolean flag that is set to TRUE iff the entry was loaded
+ * from file. This is a necessary input in determining
+ * whether the write of the entry must be delayed.
+ *
+ * This field is only maintained in the VFD SWMR case
+ * and should be false otherwise.
+ *
+ * modified_this_tick: This field is set to TRUE iff pb_ptr->vfd_swrm_write
+ * and the entry has been modified in the current tick. If
+ * modified_this_tick is TRUE, the entry must also be in the
+ * tick list.
+ *
+ * delay_write_until: Unsigned 64 bit integer containing the first tick
+ * in which the entry may be written to file, or 0 if there
+ * is no such constraint. It should be set ot 0 when VFD
+ * is not enabled.
+ *
+ * tl_next: Next pointer on the list of entries modified in the current
+ * tick, If the enty is not on the tick list, or if there is
+ * no next entry on the list, this field should be set to NULL.
+ *
+ * tl_prev: Prev pointer on the list of entries modified in the current
+ * tick, If the enty is not on the tick list, or if there is
+ * no previous entry on the list, this field should be set to
+ * NULL.
+ *
+ ****************************************************************************/
- /* Fields supporting replacement policies */
- struct H5PB_entry_t *next; /* next pointer in the LRU list */
- struct H5PB_entry_t *prev; /* previous pointer in the LRU list */
-} H5PB_entry_t;
+#define H5PB__H5PB_ENTRY_T_MAGIC 0x02030405
+struct H5PB_entry_t {
-/*****************************/
-/* Package Private Variables */
-/*****************************/
+ uint32_t magic;
+ H5PB_t *pb_ptr;
+ haddr_t addr;
+ uint64_t page;
+ size_t size;
+ void *image_ptr;
+ H5FD_mem_t mem_type;
+ hbool_t is_metadata;
+ hbool_t is_dirty;
+ /* fields supporting the hash table: */
+ struct H5PB_entry_t *ht_next;
+ struct H5PB_entry_t *ht_prev;
+ struct H5PB_entry_t *il_next;
+ struct H5PB_entry_t *il_prev;
-/******************************/
-/* Package Private Prototypes */
-/******************************/
+ /* fields supporting replacement policies: */
+ struct H5PB_entry_t *next;
+ struct H5PB_entry_t *prev;
+ /* fields supporting VFD SWMR */
+ hbool_t is_mpmde;
+ hbool_t loaded;
+ hbool_t modified_this_tick;
+ uint64_t delay_write_until;
+ struct H5PB_entry_t *tl_next;
+ struct H5PB_entry_t *tl_prev;
+
+}; /* H5PB_entry_t */
#endif /* _H5PBpkg_H */
diff --git a/src/H5PBprivate.h b/src/H5PBprivate.h
index 82d010d..983d183 100644
--- a/src/H5PBprivate.h
+++ b/src/H5PBprivate.h
@@ -11,68 +11,642 @@
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-/*-------------------------------------------------------------------------
+/*
+ * File: H5PBprivate.h
*
- * Created: H5PBprivate.h
- * June 2014
- * Mohamad Chaarawi
+ * Purpose: This file contains declarations which are normally visible
+ * within the HDF5 library, but are not visible at the user
+ * level
*
- *-------------------------------------------------------------------------
+ * Programmer: John Mainzer -- 10/07/18
*/
#ifndef _H5PBprivate_H
#define _H5PBprivate_H
/* Include package's public header */
-#ifdef NOT_YET
-#include "H5PBpublic.h"
-#endif /* NOT_YET */
+
+/* no H5PBpublic.h at present */
+
/* Private headers needed by this header */
#include "H5private.h" /* Generic Functions */
-#include "H5Fprivate.h" /* File access */
-#include "H5FLprivate.h" /* Free Lists */
-#include "H5SLprivate.h" /* Skip List */
/**************************/
/* Library Private Macros */
/**************************/
+#define H5PB__HASH_TABLE_LEN 4096 /* must be a power of 2 */
+
/****************************/
/* Library Private Typedefs */
/****************************/
-/* Forward declaration for a page buffer entry */
-struct H5PB_entry_t;
+/* Typedef for the page buffer entry structure (defined in H5PBpkg.h) */
+typedef struct H5PB_entry_t H5PB_entry_t;
+
+
+
+/******************************************************************************
+ *
+ * structure H5PB_t
+ *
+ * Catchall structure for all variables specific to an instance of the page
+ * buffer.
+ *
+ * At present, the page buffer serves two purposes in the HDF5 library.
+ *
+ * Under normal operating conditions, it serves as a normal page buffer whose
+ * purpose is to minimize and optimize file I/O by aggregating small metadata
+ * and raw data writes into pages, and by caching frequently used pages.
+ *
+ * In addition, when a file is opened for VFD SWMR writing, the page buffer is
+ * used to retain copies of all metadata pages and multi-page metadata entries
+ * that are written in a given tick, and under certain cases, to delay metadata
+ * page and/or multi-page metadata entry writes for some number of ticks.
+ * If the entry has not appeared in the VFD SWMR index for at least max_lag
+ * ticks, this is necessary to avoid message from the future bugs. See the
+ * VFD SWMR RFC for further details.
+ *
+ * To reflect this, the fields of this structure are divided into three
+ * sections. Specifically fields needed for general operations, fields needed
+ * for VFD SWMR, and statistics.
+ *
+ * FIELDS FOR GENERAL OPERATIONS:
+ *
+ * magic: Unsigned 32 bit integer that must always be set to
+ * H5PB__H5PB_T_MAGIC. This field is used to validate pointers to
+ * instances of H5PB_t.
+ *
+ * page_size: size_t containing the page buffer page size in bytes.
+ *
+ * max_pages: 64 bit integer containing the nominal maximum number
+ * of pages in the page buffer. Note that on creation, the page
+ * buffer is empty, and that under certain circumstances (mostly
+ * related to VFD SWMR) this limit can be exceeded by large
+ * amounts.
+ *
+ * curr_pages: 64 bit integer containing the current number of pages
+ * in the page buffer. curr_pages must always equal the sum of
+ * curr_md_pages + curr_rd_pages.
+ *
+ * Note that in the context of VFD SWMR, this count does NOT
+ * include multi-page metadata entries.
+ *
+ * curr_md_pages: 64 bit integer containing the current number of
+ * metadata pages in the page buffer.
+ *
+ * Note that in the context of VFD SWMR, this count does NOT
+ * include multi-page metadata entries.
+ *
+ * curr_rd_pages: 64 bit integer containing the current number of
+ * raw data pages in the page buffer.
+ *
+ * min_md_pages: 64 bit integer containing the number of pages in the
+ * page buffer reserved for metadata. No metadata page may be
+ * evicted from the page buffer if curr_md_pages is less than or
+ * equal to this value.
+ *
+ * min_rd_pages: 64 bin integer containing the number of pages in the
+ * page buffer reserved for raw data. No page or raw data may be
+ * evicted from the page buffer if curr_rd_pages is less than or
+ * equal to this value.
+ *
+ * The FAPL fields are used to store the page buffer configuration data
+ * provided to the page buffer in the H5PB_create() call.
+ *
+ * max_size: Maximum page buffer size supplied by the FAPL.
+ *
+ * min_meta_perc: Percent of the page buffer reserved for metadata as
+ * supplied in the FAPL.
+ *
+ * min_raw_perc: Percent of the page buffer reserved for metadata as
+ * supplied in the FAPL.
+ *
+ * The purpose of the index is to allow us to efficiently look up all pages
+ * (and multi-page metadata entries in the context of VFD SWMR) in the
+ * page buffer.
+ *
+ * This function is provided by a hash table with chaining, albeit with one
+ * un-unusual feature.
+ *
+ * Specifically hash table size must be a power of two, and the hash function
+ * simply clips the high order bits off the page offset of the entry.
+ *
+ * This should work, as space is typically allocated sequentually, and thus
+ * via a reverse principle of locality argument, hot pages are unlikely to
+ * hash to the same bucket. That said, we must collect statistics to alert
+ * us should this not be the case.
+ *
+ * We also maintain a linked list of all entries in the index to facilitate
+ * flush operations.
+ *
+ * index Array of pointer to H5PB_entry_t of size
+ * H5PB__HASH_TABLE_LEN. This size must ba a power of 2,
+ * not the usual prime number.
+ *
+ * index_len: Number of entries currently in the hash table used to index
+ * the page buffer. index_len should always equal
+ * clean_index_len + dirty_index_len.
+ *
+ * clean_index_len: Number of clean entries currently in the hash table
+ * used to index the page buffer.
+ *
+ * dirty_index_len: Number of dirty entries currently in the hash table
+ * used to index the page buffer.
+ *
+ * index_size: Number of bytes currently stored in the hash table used to
+ * index the page buffer. Under normal circumstances, this
+ * value will be index_len * page size. However, if
+ * vfd_swmr_writer is TRUE, it may be larger.
+ *
+ * index_size should always equal clean_index_size +
+ * dirty_index_size.
+ *
+ * clean_index_size: Number of bytes of clean entries currently stored in
+ * the hash table used to index the page buffer.
+ *
+ * dirty_index_size: Number of bytes of dirty entries currently stored in
+ * the hash table used to index the page buffer.
+ *
+ * il_len: Number of entries on the index list.
+ *
+ * This must always be equal to index_len. As such, this
+ * field is redundant. However, the existing linked list
+ * management macros expect to maintain a length field, so
+ * this field exists primarily to avoid adding complexity to
+ * these macros.
+ *
+ * il_size: Number of bytes of cache entries currently stored in the
+ * index list.
+ *
+ * This must always be equal to index_size. As such, this
+ * field is redundant. However, the existing linked list
+ * management macros expect to maintain a size field, so
+ * this field exists primarily to avoid adding complexity to
+ * these macros.
+ *
+ * il_head: Pointer to the head of the doubly linked list of entries in
+ * the index list. Note that cache entries on this list are
+ * linked by their il_next and il_prev fields.
+ *
+ * This field is NULL if the index is empty.
+ *
+ * il_tail: Pointer to the tail of the doubly linked list of entries in
+ * the index list. Note that cache entries on this list are
+ * linked by their il_next and il_prev fields.
+ *
+ * This field is NULL if the index is empty.
+ *
+ *
+ * Fields supporting the modified LRU policy:
+ *
+ * See most any OS text for a discussion of the LRU replacement policy.
+ *
+ * Under normal operating circumstances (i.e. vfd_swmr_writer is FALSE)
+ * all entries will reside both in the index and in the LRU. Further,
+ * all entries will be of size page_size.
+ *
+ * The VFD SWMR writer case (i.e. vfd_swmr_writer is TRUE) is complicated
+ * by the requirements that we:
+ *
+ * 1) buffer all metadat writes (including multi-page metadata writes) that
+ * occur during a tick, and
+ *
+ * 2) when necessary, delay metadata writes for up to max_lag ticks to
+ * avoid message from the future bugs on the VFD SWMR readers.
+ *
+ * See discussion of fields supporting VFD SWMR below for details.
+ *
+ * Discussions of the individual fields used by the modified LRU replacement
+ * policy follow:
+ *
+ * LRU_len: Number of page buffer entries currently on the LRU.
+ *
+ * Observe that LRU_len + dwl_len must always equal
+ * index_len.
+ *
+ * LRU_size: Number of bytes of page buffer entries currently residing
+ * on the LRU list.
+ *
+ * Observe that LRU_size + dwl_size must always equal
+ * index_size.
+ *
+ * LRU_head_ptr: Pointer to the head of the doubly linked LRU list. Page
+ * buffer entries on this list are linked by their next and
+ * prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * LRU_tail_ptr: Pointer to the tail of the doubly linked LRU list. Page
+ * buffer entries on this list are linked by their next and
+ * prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ *
+ * FIELDS SUPPORTING VFD SWMR:
+ *
+ * If the file is opened as a VFD SWMR writer (i.e. vfd_swmr_writer == TRUE),
+ * the page buffer must retain the data necessary to update the metadata
+ * file at the end of each tick, and also delay writes as necessary so as
+ * to avoid message from the future bugs on the VFD SWMR readers.
+ *
+ * The tick list exists to allow us to buffer copies of all metadata writes
+ * during a tick, and the delayed write list supports delayed writes.
+ *
+ * If a regular page is written to during a tick, it is placed on the tick
+ * list. If there is no reason to delay its write to file (i.e. either
+ * it was just allocated, or it has existed in the metadata file index for
+ * at least max_lag ticks), it is also placed on the LRU, where it may be
+ * flushed, but not evicted. If its write must be delayed, it is placed on
+ * the delayed write list, where it must remain until its write delay is
+ * satisfied -- at which point it is moved to the LRU.
+ *
+ * If a multi-page metadata entry is written during a tick, it is placed on
+ * the tick list. If, in addition, the write of the entry must be delayed,
+ * it is also place on the delayed write list. Note that multi-page metadata
+ * entries may never appear on the LRU.
+ *
+ * At the end of each tick, the tick list is emptied.
+ *
+ * Regular pages are simply removed from the tick list, as they must already
+ * appear on either the LRU or the delayed write list.
+ *
+ * Multi-page metadata entries that are not also on the delayed write list
+ * are simply flushed and evicted.
+ *
+ * The delayed write list is also scanned at the end of each tick. Regular
+ * entries that are now flushable are placed at the head of the LRU. Multi-
+ * page metadata entries that are flushable are flushed and evicted.
+ *
+ * The remainder of this sections contains discussions of the fields and
+ * data structures used to support the above operations.
+ *
+ * vfd_swmr_writer: Boolean flag that is set to TRUE iff the file is
+ * the file is opened in VFD SWMR mode. The remaining
+ * VFD SWMR fields are defined iff vfd_swmr_writer is TRUE.
+ *
+ * mpmde_count: int64_t containing the number of multi-page metadata
+ * entries currently resident in the page buffer. Observe
+ * that index_len should always equal curr_pages + mpmde_count.
+ *
+ * cur_tick: uint64_t containing the current tick. This is a copy of
+ * the same field in the associated instance of H5F_file_t,
+ * and is maintained as a convenience.
+ *
+ * In the context of VFD SWMR the delayed write list allows us to delay
+ * metadata writes to the HDF5 file until it appears in all indexes in the
+ * last max_lag ticks. This is essential if a version of the page or
+ * multi-page metadata entry already exists in the HDF5 file -- failure to
+ * delay the write can result in a message from the future which will
+ * likely be perciived as file corruption by the reader.
+ *
+ * To facilitate identification of entries that must be removed from the
+ * DWL during the end of tick scan, the list always observes the following
+ * invarient for any entry on the list:
+ *
+ * entry_ptr->next == NULL ||
+ * entry_ptr->delay_write_until >= entry_ptr->next->delay_write_until
+ *
+ * Discussion of the fields used to implement the delayed write list follows:
+ *
+ * max_delay: Maximum of the delay_write_until fields of the entries on
+ * the delayed write list. This must never be more than max_lag
+ * ticks in advance of the current tick, and should be set to
+ * zero if the delayed write list is empty.
+ *
+ * dwl_len: Number of page buffer entries currently on the delayed
+ * write list.
+ *
+ * Observe that LRU_len + dwl_len must always equal
+ * index_len.
+ *
+ * dwl_size: Number of bytes of page buffer entries currently residing
+ * on the LRU list.
+ *
+ * Observe that LRU_size + dwl_size must always equal
+ * index_size.
+ *
+ * dwl_head_ptr: Pointer to the head of the doubly linked delayed write list.
+ * Page buffer entries on this list are linked by their next and
+ * prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * dwl_tail_ptr: Pointer to the tail of the doubly linked delayed write list.
+ * Page buffer entries on this list are linked by their next and
+ * prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * For VFD SWMR to function, copies of all pages modified during a tick must
+ * be retained in the page buffer to allow correct updates to the index and
+ * metadata file at the end of tick.
+ *
+ * To implement this, all entries modified during the current tick are placed
+ * on the tick list. Entries are removed from the tick list during end of
+ * tick processing, so each tick starts with an empty tick list.
+ *
+ * Unless the entry also resides on the delayed write list, entries on the
+ * tick list may be flushed, but they may not be evicted.
+ *
+ * Discussion of the fields used to implement the tick list follows:
+ *
+ * tl_len: Number of page buffer entries currently on the tick list
+ *
+ * tl_size: Number of bytes of page buffer entries currently residing
+ * on the tick list.
+ *
+ * tl_head_ptr: Pointer to the head of the doubly linked tick list.
+ * Page buffer entries on this list are linked by their tl_next
+ * and tl_prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ * tl_tail_ptr: Pointer to the tail of the doubly linked tick list.
+ * Page buffer entries on this list are linked by their tl_next
+ * and tl_prev fields.
+ *
+ * This field is NULL if the list is empty.
+ *
+ *
+ * STATISTICS:
+ *
+ * Multi-page metadata entries (which may only appear in VFD
+ * SWMR mode) are NOT counted in the following statistics.
+ *
+ * Note that all statistics fields contain only data since the last time
+ * that statistics were reset.
+ *
+ * bypasses: Array of int64_t of length H5PB__NUM_STAT_TYPES containing
+ * the number of times that the page buffer has been
+ * bypassed for raw data, metadata, and for multi-page
+ * metadata entries (VFD SWMR only) as indexed by 5PB__STATS_MD,
+ * H5PB__STATS_RD, and H5PB__STATS_MPMDE respectively.
+ *
+ * accesses: Array of int64_t of length H5PB__NUM_STAT_TYPES containing
+ * the number of page buffer accesses for raw data, metadata,
+ * and for multi-page metadata entries (VFD SWMR only) as
+ * indexed by 5PB__STATS_MD, H5PB__STATS_RD, and
+ * H5PB__STATS_MPMDE respectively.
+ *
+ * hits: Array of int64_t of length H5PB__NUM_STAT_TYPES containing
+ * the number of page buffer hits for raw data, metadata,
+ * and for multi-page metadata entries (VFD SWMR only) as
+ * indexed by 5PB__STATS_MD, H5PB__STATS_RD, and
+ * H5PB__STATS_MPMDE respectively.
+ *
+ * misses: Array of int64_t of length H5PB__NUM_STAT_TYPES containing
+ * the number of page buffer misses for raw data, metadata,
+ * and for multi-page metadata entries (VFD SWMR only) as
+ * indexed by 5PB__STATS_MD, H5PB__STATS_RD, and
+ * H5PB__STATS_MPMDE respectively.
+ *
+ * loads: Array of int64_t of length H5PB__NUM_STAT_TYPES containing
+ * the number of page buffer loads for raw data, metadata,
+ * and for multi-page metadata entries (VFD SWMR only) as
+ * indexed by 5PB__STATS_MD, H5PB__STATS_RD, and
+ * H5PB__STATS_MPMDE respectively.
+ *
+ * insertions: Array of int64_t of length H5PB__NUM_STAT_TYPES containing
+ * the number of page buffer insertions of raw data, metadata,
+ * and for multi-page metadata entries (VFD SWMR only) as
+ * indexed by 5PB__STATS_MD, H5PB__STATS_RD, and
+ * H5PB__STATS_MPMDE respectively.
+ *
+ * flushes: Array of int64_t of length H5PB__NUM_STAT_TYPES containing
+ * the number of page buffer flushes of raw data, metadata,
+ * and for multi-page metadata entries (VFD SWMR only) as
+ * indexed by 5PB__STATS_MD, H5PB__STATS_RD, and
+ * H5PB__STATS_MPMDE respectively.
+ *
+ * evictions: Array of int64_t of length H5PB__NUM_STAT_TYPES containing
+ * the number of page buffer evictions of raw data, metadata,
+ * and for multi-page metadata entries (VFD SWMR only) as
+ * indexed by 5PB__STATS_MD, H5PB__STATS_RD, and
+ * H5PB__STATS_MPMDE respectively.
+ *
+ * clears: Array of int64_t of length H5PB__NUM_STAT_TYPES containing
+ * the number of page buffer entry clears of raw data, metadata,
+ * and for multi-page metadata entries (VFD SWMR only) as
+ * indexed by 5PB__STATS_MD, H5PB__STATS_RD, and
+ * H5PB__STATS_MPMDE respectively.
+ *
+ * max_lru_len: int64_t containing the maximum number of entries that
+ * have appeared in the LRU.
+ *
+ * max_lru_size: int64_t containing the maximum size of the LRU.
+ *
+ * lru_md_skips: When searching for an entry to evict, metadata entries on
+ * the LRU must be skipped if the number of metadata pages
+ * in the page buffer fails to exceed min_md_pages.
+ *
+ * This int64_t is used to keep a count of these skips.
+ *
+ * If this number becomes excessive, it will be necessary to
+ * add a holding tank for such entries.
+ *
+ * lru_rd_skips: When searching for an entry to evict, raw data entries on
+ * the LRU must be skipped if the number of raw data pages
+ * in the page buffer fails to exceed min_rd_pages.
+ *
+ * This int64_t is used to keep a count of these skips.
+ *
+ * If this number becomes excessive, it will be necessary to
+ * add a holding tank for such entries.
+ *
+ * Multi-page metadata entries (which appear only in VFD SWMR mode) are
+ * listed in the hash take, and thus they are counted in the following
+ * statistics.
+ *
+ * total_ht_insertions: Number of times entries have been inserted into the
+ * hash table.
+ *
+ * total_ht_deletions: Number of times entries have been deleted from the
+ * hash table.
+ *
+ * successful_ht_searches: int64 containing the total number of successful
+ * searches of the hash table.
+ *
+ * total_successful_ht_search_depth: int64 containing the total number of
+ * entries other than the targets examined in successful
+ * searches of the hash table.
+ *
+ * failed_ht_searches: int64 containing the total number of unsuccessful
+ * searches of the hash table.
+ *
+ * total_failed_ht_search_depth: int64 containing the total number of
+ * entries examined in unsuccessful searches of the hash
+ * table.
+ *
+ * max_index_len: Largest value attained by the index_len field.
+ *
+ * max_clean_index_len: Largest value attained by the clean_index_len field.
+ *
+ * max_dirty_index_len: Largest value attained by the dirty_index_len field.
+ *
+ * max_index_size: Largest value attained by the index_size field.
+ *
+ * max_clean_index_size: Largest value attained by the clean_index_size field.
+ *
+ * max_dirty_index_size: Largest value attained by the dirty_index_size field.
+ *
+ * max_rd_pages: Maximum number of raw data pages in the page buffer.
+ *
+ * max_md_pages: Maximum number of metadata pages in the page buffer.
+ *
+ *
+ * Statistics pretaining to VFD SWMR.
+ *
+ * max_mpmde_count: Maximum number of multi-page metadata entries in the
+ * page buffer.
+ *
+ * lru_tl_skips: When searching for an entry to evict, metadata entries on
+ * the LRU must be skipped if they also reside on the tick list.
+ *
+ * This int64_t is used to keep a count of these skips.
+ *
+ * If this number becomes excessive, it will be necessary to
+ * add a holding tank for such entries.
+ *
+ * max_tl_len: int64_t containing the maximum value of tl_len.
+ *
+ * max_tl_size: int64_t containing the maximum value of tl_size.
+ *
+ * delayed_writes: int64_t containing the total number of delayed writes.
+ *
+ * total_delay: int64_t containing the total number of ticks by which
+ * entry writes have been delayed.
+ *
+ * max_dwl_len: int64_t containing the maximum value of dwl_len.
+ *
+ * max_dwl_size: int64_t containing the maximum value of dwl_size.
+ *
+ * total_dwl_ins_depth: int64_t containing the total insertion depth
+ * required to maintain the odering invarient on the
+ * delayed write list.
+ *
+ ******************************************************************************/
+
+#define H5PB__H5PB_T_MAGIC 0x01020304
+
+#define H5PB__STATS_MD 0
+#define H5PB__STATS_RD 1
+#define H5PB__STATS_MPMDE 2
+#define H5PB__NUM_STAT_TYPES 3
-/* Typedef for the main structure for the page buffer */
typedef struct H5PB_t {
- size_t max_size; /* The total page buffer size */
- size_t page_size; /* Size of a single page */
- unsigned min_meta_perc; /* Minimum ratio of metadata entries required before evicting meta entries */
- unsigned min_raw_perc; /* Minimum ratio of raw data entries required before evicting raw entries */
- unsigned meta_count; /* Number of entries for metadata */
- unsigned raw_count; /* Number of entries for raw data */
- unsigned min_meta_count; /* Minimum # of entries for metadata */
- unsigned min_raw_count; /* Minimum # of entries for raw data */
-
- H5SL_t *slist_ptr; /* Skip list with all the active page entries */
- H5SL_t *mf_slist_ptr; /* Skip list containing newly allocated page entries inserted from the MF layer */
-
- size_t LRU_list_len; /* Number of entries in the LRU (identical to slist_ptr count) */
- struct H5PB_entry_t *LRU_head_ptr; /* Head pointer of the LRU */
- struct H5PB_entry_t *LRU_tail_ptr; /* Tail pointer of the LRU */
-
- H5FL_fac_head_t *page_fac; /* Factory for allocating pages */
-
- /* Statistics */
- unsigned accesses[2];
- unsigned hits[2];
- unsigned misses[2];
- unsigned evictions[2];
- unsigned bypasses[2];
+
+ /* Fields for general operations: */
+
+ uint32_t magic;
+ size_t page_size;
+ int64_t max_pages;
+ int64_t curr_pages;
+ int64_t curr_md_pages;
+ int64_t curr_rd_pages;
+ int64_t min_md_pages;
+ int64_t min_rd_pages;
+
+ /* FAPL fields */
+ size_t max_size;
+ unsigned min_meta_perc;
+ unsigned min_raw_perc;
+
+ /* index */
+ H5PB_entry_t *(ht[H5PB__HASH_TABLE_LEN]);
+ int64_t index_len;
+ int64_t clean_index_len;
+ int64_t dirty_index_len;
+ int64_t index_size;
+ int64_t clean_index_size;
+ int64_t dirty_index_size;
+ int64_t il_len;
+ int64_t il_size;
+ H5PB_entry_t * il_head;
+ H5PB_entry_t * il_tail;
+
+ /* LRU */
+ int64_t LRU_len;
+ int64_t LRU_size;
+ H5PB_entry_t * LRU_head_ptr;
+ H5PB_entry_t * LRU_tail_ptr;
+
+
+ /* Fields for VFD SWMR operations: */
+
+ hbool_t vfd_swmr_writer;
+ int64_t mpmde_count;
+ uint64_t cur_tick;
+
+ /* delayed write list */
+ uint64_t max_delay;
+ int64_t dwl_len;
+ int64_t dwl_size;
+ H5PB_entry_t * dwl_head_ptr;
+ H5PB_entry_t * dwl_tail_ptr;
+
+ /* tick list */
+ int64_t tl_len;
+ int64_t tl_size;
+ H5PB_entry_t * tl_head_ptr;
+ H5PB_entry_t * tl_tail_ptr;
+
+ /* Statistics: */
+
+ /* general operations statistics: */
+ /* these statistics count pages only, not multi-page metadata entries
+ * (that occur only in the VFD SWMR writer case).
+ */
+ int64_t bypasses[H5PB__NUM_STAT_TYPES];
+ int64_t accesses[H5PB__NUM_STAT_TYPES];
+ int64_t hits[H5PB__NUM_STAT_TYPES];
+ int64_t misses[H5PB__NUM_STAT_TYPES];
+ int64_t loads[H5PB__NUM_STAT_TYPES];
+ int64_t insertions[H5PB__NUM_STAT_TYPES];
+ int64_t flushes[H5PB__NUM_STAT_TYPES];
+ int64_t evictions[H5PB__NUM_STAT_TYPES];
+ int64_t clears[H5PB__NUM_STAT_TYPES];
+ uint64_t access_size_count[6];
+ int64_t max_lru_len;
+ int64_t max_lru_size;
+ int64_t lru_md_skips;
+ int64_t lru_rd_skips;
+
+ /* In the VFD SWMR case, both pages and multi-page metadata entries
+ * are stored in the index. Thus mult-page metadata entries are
+ * included in the index related statistics.
+ */
+ int64_t total_ht_insertions;
+ int64_t total_ht_deletions;
+ int64_t successful_ht_searches;
+ int64_t total_successful_ht_search_depth;
+ int64_t failed_ht_searches;
+ int64_t total_failed_ht_search_depth;
+ int64_t max_index_len;
+ int64_t max_clean_index_len;
+ int64_t max_dirty_index_len;
+ int64_t max_index_size;
+ int64_t max_clean_index_size;
+ int64_t max_dirty_index_size;
+ int64_t max_rd_pages;
+ int64_t max_md_pages;
+
+
+ /* vfd swmr statistics */
+ int64_t max_mpmde_count;
+ int64_t lru_tl_skips;
+ int64_t max_tl_len;
+ int64_t max_tl_size;
+ int64_t delayed_writes;
+ int64_t total_delay;
+ int64_t max_dwl_len;
+ int64_t max_dwl_size;
+ int64_t total_dwl_ins_depth;
+
} H5PB_t;
/*****************************/
@@ -85,20 +659,49 @@ typedef struct H5PB_t {
/***************************************/
/* General routines */
-H5_DLL herr_t H5PB_create(H5F_shared_t *f_sh, size_t page_buffer_size, unsigned page_buf_min_meta_perc, unsigned page_buf_min_raw_perc);
-H5_DLL herr_t H5PB_flush(H5F_shared_t *f_sh);
-H5_DLL herr_t H5PB_dest(H5F_shared_t *f_sh);
-H5_DLL herr_t H5PB_add_new_page(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t page_addr);
-H5_DLL herr_t H5PB_update_entry(H5PB_t *page_buf, haddr_t addr, size_t size, const void *buf);
-H5_DLL herr_t H5PB_remove_entry(const H5F_shared_t *f_sh, haddr_t addr);
-H5_DLL herr_t H5PB_read(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr, size_t size, void *buf/*out*/);
-H5_DLL herr_t H5PB_write(H5F_shared_t *f_sh, H5FD_mem_t type, haddr_t addr, size_t size, const void *buf);
+H5_DLL herr_t H5PB_create(H5F_shared_t *shared, size_t page_buffer_size,
+ unsigned page_buf_min_meta_perc, unsigned page_buf_min_raw_perc);
+
+H5_DLL herr_t H5PB_flush(H5F_shared_t *);
+
+H5_DLL herr_t H5PB_dest(H5F_shared_t *);
+
+H5_DLL herr_t H5PB_add_new_page(H5F_shared_t *, H5FD_mem_t, haddr_t);
+
+H5_DLL herr_t H5PB_update_entry(H5PB_t *, haddr_t, size_t, const void *);
+
+H5_DLL herr_t H5PB_remove_entry(H5F_shared_t *, haddr_t);
+H5_DLL herr_t H5PB_remove_entries(H5F_shared_t *, haddr_t, hsize_t);
+
+H5_DLL herr_t H5PB_read(H5F_shared_t *, H5FD_mem_t, haddr_t,
+ size_t, void * /*out*/);
+
+H5_DLL herr_t H5PB_write(H5F_shared_t *, H5FD_mem_t, haddr_t,
+ size_t, const void *);
+
+/* VFD SWMR specific routines */
+H5_DLL herr_t H5PB_vfd_swmr__release_delayed_writes(H5F_shared_t *);
+
+H5_DLL herr_t H5PB_vfd_swmr__release_tick_list(H5F_shared_t *);
+
+H5_DLL herr_t H5PB_vfd_swmr__set_tick(H5F_shared_t *);
+
+H5_DLL herr_t H5PB_vfd_swmr__update_index(H5F_t *f,
+ uint32_t * idx_ent_added_ptr, uint32_t * idx_ent_modified_ptr,
+ uint32_t * idx_ent_not_in_tl_ptr, uint32_t * idx_ent_not_in_tl_flushed_ptr);
/* Statistics routines */
-H5_DLL herr_t H5PB_reset_stats(H5PB_t *page_buf);
+H5_DLL herr_t H5PB_reset_stats(H5PB_t *);
+
H5_DLL herr_t H5PB_get_stats(const H5PB_t *page_buf, unsigned accesses[2],
- unsigned hits[2], unsigned misses[2], unsigned evictions[2], unsigned bypasses[2]);
-H5_DLL herr_t H5PB_print_stats(const H5PB_t *page_buf);
+ unsigned hits[2], unsigned misses[2], unsigned evictions[2],
+ unsigned bypasses[2]);
+
+H5_DLL herr_t H5PB_print_stats(const H5PB_t *);
+
+
+/* test & debug functions */
+H5_DLL herr_t H5PB_page_exists(H5F_shared_t *, haddr_t, hbool_t *);
#endif /* !_H5PBprivate_H */
diff --git a/src/H5Pfapl.c b/src/H5Pfapl.c
index 30b590f..ed6f06a 100644
--- a/src/H5Pfapl.c
+++ b/src/H5Pfapl.c
@@ -47,6 +47,7 @@
#ifdef H5_HAVE_WINDOWS
#include "H5FDwindows.h" /* Win32 I/O */
#endif
+#include "H5FDvfd_swmr.h" /* Posix unbuffered I/O file driver */
/* Includes needed to set default VOL connector */
#include "H5VLnative_private.h" /* Native VOL connector */
@@ -282,6 +283,11 @@
#define H5F_ACS_VOL_CONN_CMP H5P__facc_vol_cmp
#define H5F_ACS_VOL_CONN_CLOSE H5P__facc_vol_close
+/* Definitions for the VFD SWMR configuration */
+#define H5F_ACS_VFD_SWMR_CONFIG_SIZE sizeof(H5F_vfd_swmr_config_t)
+#define H5F_ACS_VFD_SWMR_CONFIG_DEF H5F__DEFAULT_VFD_SWMR_CONFIG
+#define H5F_ACS_VFD_SWMR_CONFIG_ENC H5P__facc_vfd_swmr_config_enc
+#define H5F_ACS_VFD_SWMR_CONFIG_DEC H5P__facc_vfd_swmr_config_dec
/******************/
/* Local Typedefs */
@@ -329,6 +335,8 @@ static herr_t H5P__facc_multi_type_enc(const void *value, void **_pp, size_t *si
static herr_t H5P__facc_multi_type_dec(const void **_pp, void *value);
static herr_t H5P__facc_libver_type_enc(const void *value, void **_pp, size_t *size);
static herr_t H5P__facc_libver_type_dec(const void **_pp, void *value);
+static herr_t H5P__facc_vfd_swmr_config_enc(const void *value, void **_pp, size_t *size);
+static herr_t H5P__facc_vfd_swmr_config_dec(const void **_pp, void *value);
/* Metadata cache log location property callbacks */
static herr_t H5P_facc_mdc_log_location_enc(const void *value, void **_pp, size_t *size);
@@ -448,7 +456,9 @@ static const size_t H5F_def_page_buf_size_g = H5F_ACS_PAGE_BUFFER_SIZE_DEF;
static const unsigned H5F_def_page_buf_min_meta_perc_g = H5F_ACS_PAGE_BUFFER_MIN_META_PERC_DEF; /* Default page buffer minimum metadata size */
static const unsigned H5F_def_page_buf_min_raw_perc_g = H5F_ACS_PAGE_BUFFER_MIN_RAW_PERC_DEF; /* Default page buffer mininum raw data size */
-
+static const H5F_vfd_swmr_config_t H5F_def_vfd_swmr_config_g = H5F_ACS_VFD_SWMR_CONFIG_DEF; /* Default vfd swmr configuration */
+
+
/*-------------------------------------------------------------------------
* Function: H5P__facc_reg_prop
*
@@ -698,6 +708,12 @@ H5P__facc_reg_prop(H5P_genclass_t *pclass)
NULL, NULL, NULL, NULL) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class")
+ /* Register the default VFD SWMR configuration */
+ if(H5P__register_real(pclass, H5F_ACS_VFD_SWMR_CONFIG_NAME, H5F_ACS_VFD_SWMR_CONFIG_SIZE, &H5F_def_vfd_swmr_config_g,
+ NULL, NULL, NULL, H5F_ACS_VFD_SWMR_CONFIG_ENC, H5F_ACS_VFD_SWMR_CONFIG_DEC,
+ NULL, NULL, NULL, NULL) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class")
+
/* Register the file VOL connector ID & info */
/* (Note: this property should not have an encode/decode callback -QAK) */
if(H5P__register_real(pclass, H5F_ACS_VOL_CONN_NAME, H5F_ACS_VOL_CONN_SIZE, &def_vol_prop,
@@ -705,6 +721,13 @@ H5P__facc_reg_prop(H5P_genclass_t *pclass)
H5F_ACS_VOL_CONN_DEL, H5F_ACS_VOL_CONN_COPY, H5F_ACS_VOL_CONN_CMP, H5F_ACS_VOL_CONN_CLOSE) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class")
+ if (H5P_LST_FILE_ACCESS_ANY_VFD_g == H5I_INVALID_HID) {
+ H5P_LST_FILE_ACCESS_ANY_VFD_g = H5P_create_id(pclass, false);
+ if (H5P_LST_FILE_ACCESS_ANY_VFD_g == H5I_INVALID_HID) {
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL,
+ "can't create any-vfd fapl");
+ }
+ }
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5P__facc_reg_prop() */
@@ -3976,6 +3999,109 @@ H5P__facc_libver_type_dec(const void **_pp, void *_value)
FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5P__facc_libver_type_dec() */
+
+/*-------------------------------------------------------------------------
+ * Function: H5P__facc_vfd_swmr_config_enc
+ *
+ * Purpose: Callback routine which is called whenever the VFD SWMR config
+ * property in the file access property list is encoded.
+ *
+ * Return: Success: Non-negative
+ * Failure: Negative
+ *
+ * Programmer: Vailin Choi; July 2018
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5P__facc_vfd_swmr_config_enc(const void *value, void **_pp, size_t *size)
+{
+ const H5F_vfd_swmr_config_t *config = (const H5F_vfd_swmr_config_t *)value; /* Create local aliases for values */
+ uint8_t **pp = (uint8_t **)_pp;
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Sanity check */
+ HDassert(value);
+ HDcompile_assert(sizeof(size_t) <= sizeof(uint64_t));
+
+ if(NULL != *pp) {
+
+ /* int */
+ INT32ENCODE(*pp, (int32_t)config->version);
+ INT32ENCODE(*pp, (int32_t)config->tick_len);
+ INT32ENCODE(*pp, (int32_t)config->max_lag);
+ H5_ENCODE_UNSIGNED(*pp, config->writer);
+ H5_ENCODE_UNSIGNED(*pp, config->flush_raw_data);
+ INT32ENCODE(*pp, (int32_t)config->md_pages_reserved);
+ INT32ENCODE(*pp, (int32_t)config->pb_expansion_threshold);
+ HDmemcpy(*pp, (const uint8_t *)(config->md_file_path), (size_t)(H5F__MAX_VFD_SWMR_FILE_NAME_LEN + 1));
+ *pp += H5F__MAX_VFD_SWMR_FILE_NAME_LEN + 1;
+ HDmemcpy(*pp, (const uint8_t *)(config->log_file_path), (size_t)(H5F__MAX_VFD_SWMR_FILE_NAME_LEN + 1));
+ *pp += H5F__MAX_VFD_SWMR_FILE_NAME_LEN + 1;
+
+ } /* end if */
+
+ /* Compute encoded size */
+ *size += ( (5 * sizeof(int32_t)) +
+ (2 * sizeof(unsigned)) +
+ (2 * (H5F__MAX_VFD_SWMR_FILE_NAME_LEN + 1)) );
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5P__facc_vfd_swmr_config_enc() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5P__facc_vfd_swmr_config_dec
+ *
+ * Purpose: Callback routine which is called whenever the VFD SWMR
+ * config property in the file access property list is decoded.
+ *
+ * Return: Success: Non-negative
+ * Failure: Negative
+ *
+ * Programmer: Vailin Choi; July 2018
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5P__facc_vfd_swmr_config_dec(const void **_pp, void *_value)
+{
+ H5F_vfd_swmr_config_t *config = (H5F_vfd_swmr_config_t *)_value;
+ const uint8_t **pp = (const uint8_t **)_pp;
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Sanity checks */
+ HDassert(pp);
+ HDassert(*pp);
+ HDassert(config);
+ HDcompile_assert(sizeof(size_t) <= sizeof(uint64_t));
+
+ /* Set property to default value */
+ HDmemcpy(config, &H5F_def_vfd_swmr_config_g, sizeof(H5F_vfd_swmr_config_t));
+
+ /* int */
+ INT32DECODE(*pp, config->version);
+ INT32DECODE(*pp, config->tick_len);
+ INT32DECODE(*pp, config->max_lag);
+
+ H5_DECODE_UNSIGNED(*pp, config->writer);
+ H5_DECODE_UNSIGNED(*pp, config->flush_raw_data);
+
+ /* int */
+ INT32DECODE(*pp, config->md_pages_reserved);
+ INT32DECODE(*pp, config->pb_expansion_threshold);
+
+ HDstrcpy(config->md_file_path, (const char *)(*pp));
+ *pp += H5F__MAX_VFD_SWMR_FILE_NAME_LEN + 1;
+
+ HDstrcpy(config->log_file_path, (const char *)(*pp));
+ *pp += H5F__MAX_VFD_SWMR_FILE_NAME_LEN + 1;
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5P__facc_vfd_swmr_config_dec() */
+
/*-------------------------------------------------------------------------
* Function: H5Pset_metadata_read_attempts
@@ -5411,6 +5537,75 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5P_set_vol() */
+/*-------------------------------------------------------------------------
+ * Function: H5Pset_vfd_swmr_config
+ *
+ * Purpose: Set VFD SWMR configuration in the target FAPL.
+ * Note: Hard-wired to set the driver in the fapl
+ * to use the SWMR VFD driver; this will be changed
+ * later
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Vailin Choi; July 2018
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Pset_vfd_swmr_config(hid_t plist_id, H5F_vfd_swmr_config_t *config_ptr)
+{
+ H5P_genplist_t *plist; /* Property list pointer */
+ size_t name_len;
+ herr_t ret_value = SUCCEED; /* return value */
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE2("e", "i*x", plist_id, config_ptr);
+
+ /* Get the plist structure */
+ if(NULL == (plist = H5P_object_verify(plist_id,H5P_FILE_ACCESS)))
+ HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID")
+
+ /* Validate the input configuration */
+
+ /* Check args */
+ if(config_ptr == NULL)
+ HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "NULL config_ptr on entry")
+
+ /* This field must always be set to a known version */
+ if(config_ptr->version != H5F__CURR_VFD_SWMR_CONFIG_VERSION)
+ HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "Unknown config version")
+
+ /* This field must be at least 3 */
+ if(config_ptr->max_lag < 3 )
+ HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "max_lag must be at least 3")
+
+ /* This field must be >= 2 */
+ if(config_ptr->md_pages_reserved < 2 )
+ HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "md_pages_reserved must be at least 2")
+
+ /* This field must be in the range [0, 100] */
+ if(config_ptr->pb_expansion_threshold > H5F__MAX_PB_EXPANSION_THRESHOLD)
+ HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "pb_expansion_threshold out of range")
+
+ /* Must provide the path for the metadata file */
+ name_len = HDstrlen(config_ptr->md_file_path);
+ if(name_len == 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "md_file_path is empty")
+ else if(name_len > H5F__MAX_VFD_SWMR_FILE_NAME_LEN)
+ HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "md_file_path is too long")
+
+ /* Set the modified config */
+ if(H5P_set(plist, H5F_ACS_VFD_SWMR_CONFIG_NAME, config_ptr) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set metadata cache initial config")
+
+ if(H5P_set_driver(plist, H5FD_VFD_SWMR, NULL) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set VFD SWMR driver info");
+
+done:
+ FUNC_LEAVE_API(ret_value)
+} /* H5Pset_vfd_swmr_config() */
+
+
/*-------------------------------------------------------------------------
* Function: H5P_reset_vol_class
@@ -5530,6 +5725,42 @@ done:
FUNC_LEAVE_API(ret_value)
} /* end H5Pget_vol_id() */
+/*-------------------------------------------------------------------------
+ * Function: H5Pget_vfd_swmr_config
+ *
+ * Purpose: Retrieve the VFD SWMR configuration from the target FAPL.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Vailin Choi; July 2018
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Pget_vfd_swmr_config(hid_t plist_id, H5F_vfd_swmr_config_t *config_ptr)
+{
+ H5P_genplist_t *plist; /* Property list pointer */
+ herr_t ret_value = SUCCEED; /* return value */
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE2("e", "i*x", plist_id, config_ptr);
+
+ /* Get the plist structure */
+ if(NULL == (plist = H5P_object_verify(plist_id,H5P_FILE_ACCESS)))
+ HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID")
+
+ /* Validate the config_ptr */
+ if(config_ptr == NULL)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL config_ptr on entry.")
+
+ /* Get the current VFD SWMR configuration */
+ if(H5P_get(plist, H5F_ACS_VFD_SWMR_CONFIG_NAME, config_ptr) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET,FAIL, "can't get VFD SWMR config")
+
+done:
+ FUNC_LEAVE_API(ret_value)
+} /* H5Pget_vfd_swmr_config() */
+
/*-------------------------------------------------------------------------
* Function: H5Pget_vol_info
diff --git a/src/H5Pint.c b/src/H5Pint.c
index 2e463b2..19ed63e 100644
--- a/src/H5Pint.c
+++ b/src/H5Pint.c
@@ -178,6 +178,7 @@ H5P_genclass_t *H5P_CLS_REFERENCE_ACCESS_g = NULL;
*/
hid_t H5P_LST_FILE_CREATE_ID_g = H5I_INVALID_HID;
hid_t H5P_LST_FILE_ACCESS_ID_g = H5I_INVALID_HID;
+hid_t H5P_LST_FILE_ACCESS_ANY_VFD_g = H5I_INVALID_HID;
hid_t H5P_LST_DATASET_CREATE_ID_g = H5I_INVALID_HID;
hid_t H5P_LST_DATASET_ACCESS_ID_g = H5I_INVALID_HID;
hid_t H5P_LST_DATASET_XFER_ID_g = H5I_INVALID_HID;
diff --git a/src/H5Ppublic.h b/src/H5Ppublic.h
index bb33561..8d3c92b 100644
--- a/src/H5Ppublic.h
+++ b/src/H5Ppublic.h
@@ -78,6 +78,7 @@
*/
#define H5P_FILE_CREATE_DEFAULT (H5OPEN H5P_LST_FILE_CREATE_ID_g)
#define H5P_FILE_ACCESS_DEFAULT (H5OPEN H5P_LST_FILE_ACCESS_ID_g)
+#define H5P_FILE_ACCESS_ANY_VFD (H5OPEN H5P_LST_FILE_ACCESS_ANY_VFD_g)
#define H5P_DATASET_CREATE_DEFAULT (H5OPEN H5P_LST_DATASET_CREATE_ID_g)
#define H5P_DATASET_ACCESS_DEFAULT (H5OPEN H5P_LST_DATASET_ACCESS_ID_g)
#define H5P_DATASET_XFER_DEFAULT (H5OPEN H5P_LST_DATASET_XFER_ID_g)
@@ -212,6 +213,7 @@ H5_DLLVAR hid_t H5P_CLS_REFERENCE_ACCESS_ID_g;
/* (Internal to library, do not use! Use macros above) */
H5_DLLVAR hid_t H5P_LST_FILE_CREATE_ID_g;
H5_DLLVAR hid_t H5P_LST_FILE_ACCESS_ID_g;
+H5_DLLVAR hid_t H5P_LST_FILE_ACCESS_ANY_VFD_g;
H5_DLLVAR hid_t H5P_LST_DATASET_CREATE_ID_g;
H5_DLLVAR hid_t H5P_LST_DATASET_ACCESS_ID_g;
H5_DLLVAR hid_t H5P_LST_DATASET_XFER_ID_g;
@@ -392,6 +394,10 @@ H5_DLL herr_t H5Pset_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t
H5_DLL herr_t H5Pget_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t *config_ptr /*out*/);
H5_DLL herr_t H5Pset_page_buffer_size(hid_t plist_id, size_t buf_size, unsigned min_meta_per, unsigned min_raw_per);
H5_DLL herr_t H5Pget_page_buffer_size(hid_t plist_id, size_t *buf_size, unsigned *min_meta_per, unsigned *min_raw_per);
+/* VFD SWMR configuration */
+H5_DLL herr_t H5Pset_vfd_swmr_config(hid_t plist_id, H5F_vfd_swmr_config_t *config_ptr);
+H5_DLL herr_t H5Pget_vfd_swmr_config(hid_t plist_id, H5F_vfd_swmr_config_t *config_ptr);
+
/* Dataset creation property list (DCPL) routines */
H5_DLL herr_t H5Pset_layout(hid_t plist_id, H5D_layout_t layout);
diff --git a/src/H5SMcache.c b/src/H5SMcache.c
index 7f243a6..998fe9b 100644
--- a/src/H5SMcache.c
+++ b/src/H5SMcache.c
@@ -95,6 +95,7 @@ const H5AC_class_t H5AC_SOHM_TABLE[1] = {{
NULL, /* 'notify' callback */
H5SM__cache_table_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* 'refresh' callback */
}};
const H5AC_class_t H5AC_SOHM_LIST[1] = {{
@@ -112,6 +113,7 @@ const H5AC_class_t H5AC_SOHM_LIST[1] = {{
NULL, /* 'notify' callback */
H5SM__cache_list_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
+ NULL, /* 'refresh' callback */
}};
diff --git a/src/H5VLnative.h b/src/H5VLnative.h
index b607abc..e6be0bd 100644
--- a/src/H5VLnative.h
+++ b/src/H5VLnative.h
@@ -80,6 +80,9 @@
#define H5VL_NATIVE_FILE_GET_MPI_ATOMICITY 26 /* H5Fget_mpi_atomicity */
#define H5VL_NATIVE_FILE_SET_MPI_ATOMICITY 27 /* H5Fset_mpi_atomicity */
#define H5VL_NATIVE_FILE_POST_OPEN 28 /* Adjust file after open, with wrapping context */
+#define H5VL_NATIVE_FILE_VFD_SWMR_DISABLE_EOT 29
+#define H5VL_NATIVE_FILE_VFD_SWMR_ENABLE_EOT 30
+#define H5VL_NATIVE_FILE_VFD_SWMR_END_TICK 31
/* Values for native VOL connector group optional VOL operations */
#ifndef H5_NO_DEPRECATED_SYMBOLS
diff --git a/src/H5VLnative_file.c b/src/H5VLnative_file.c
index 5275898..563f4a6 100644
--- a/src/H5VLnative_file.c
+++ b/src/H5VLnative_file.c
@@ -679,11 +679,11 @@ H5VL__native_file_optional(void *obj, H5VL_file_optional_t optional_type,
case H5VL_NATIVE_FILE_RESET_PAGE_BUFFERING_STATS:
{
/* Sanity check */
- if(NULL == f->shared->page_buf)
+ if(NULL == f->shared->pb_ptr)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "page buffering not enabled on file")
/* Reset the statistics */
- if(H5PB_reset_stats(f->shared->page_buf) < 0)
+ if(H5PB_reset_stats(f->shared->pb_ptr) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "can't reset stats for page buffering")
break;
@@ -699,11 +699,11 @@ H5VL__native_file_optional(void *obj, H5VL_file_optional_t optional_type,
unsigned *bypasses = HDva_arg(arguments, unsigned *);
/* Sanity check */
- if(NULL == f->shared->page_buf)
+ if(NULL == f->shared->pb_ptr)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "page buffering not enabled on file")
/* Get the statistics */
- if(H5PB_get_stats(f->shared->page_buf, accesses, hits, misses, evictions, bypasses) < 0)
+ if(H5PB_get_stats(f->shared->pb_ptr, accesses, hits, misses, evictions, bypasses) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "can't retrieve stats for page buffering")
break;
@@ -828,6 +828,33 @@ H5VL__native_file_optional(void *obj, H5VL_file_optional_t optional_type,
break;
}
+ /* H5Fvfd_swmr_disable_end_of_tick() */
+ case H5VL_NATIVE_FILE_VFD_SWMR_DISABLE_EOT:
+ {
+ /* Call package routine */
+ if(H5F__vfd_swmr_disable_end_of_tick((H5F_t *)obj) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "can't disable EOT for VFD SWMR")
+ break;
+ }
+
+ /* H5Fvfd_swmr_enable_end_of_tick() */
+ case H5VL_NATIVE_FILE_VFD_SWMR_ENABLE_EOT:
+ {
+ /* Call package routine */
+ if(H5F__vfd_swmr_enable_end_of_tick((H5F_t *)obj) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "can't enable EOT for VFD SWMR")
+ break;
+ }
+
+ /* H5Fvfd_swmr_end_tick() */
+ case H5VL_NATIVE_FILE_VFD_SWMR_END_TICK:
+ {
+ /* Call package routine */
+ if(H5F__vfd_swmr_end_tick((H5F_t *)obj) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "can't trigger EOT processing for VFD SWMR")
+ break;
+ }
+
default:
HGOTO_ERROR(H5E_VOL, H5E_UNSUPPORTED, FAIL, "invalid optional operation")
} /* end switch */
diff --git a/src/H5private.h b/src/H5private.h
index 836d7d5..3e11b4f 100644
--- a/src/H5private.h
+++ b/src/H5private.h
@@ -1058,6 +1058,9 @@ typedef off_t h5_stat_size_t;
#ifndef HDgettimeofday
#define HDgettimeofday(S,P) gettimeofday(S,P)
#endif /* HDgettimeofday */
+#ifndef HDclock_gettime
+ #define HDclock_gettime(C,T) clock_gettime(C,T)
+#endif /* HDclock_gettime */
#ifndef HDgetuid
#define HDgetuid() getuid()
#endif /* HDgetuid */
@@ -2176,11 +2179,54 @@ H5_DLL herr_t H5CX_pop(void);
\
BEGIN_MPE_LOG
+#include "H5FDvfd_swmr_private.h"
+#include "H5time_private.h" /* for timespeccmp */
+
+#define VFD_SWMR_ENTER(err) \
+ do { \
+ /* TBD assert that the API lock is held. The API lock */ \
+ /* synchronizes access to `vfd_swmr_api_entries_g` */ \
+ if (vfd_swmr_api_entries_g++ > 0) \
+ ; /* Do nothing: we are *re-*entering the API. */ \
+ else if (TAILQ_EMPTY(&eot_queue_g)) \
+ ; /* Nothing to do. */ \
+ else if (H5F_vfd_swmr_process_eot_queue(true) < 0) { \
+ HGOTO_ERROR(H5E_FUNC, H5E_CANTSET, err, \
+ "error processing EOT queue") \
+ } \
+ } while (0)
+
+#define VFD_SWMR_LEAVE(err) \
+ do { \
+ /* TBD assert that the API lock is held. The API lock */ \
+ /* synchronizes access to `vfd_swmr_api_entries_g` */ \
+ if (--vfd_swmr_api_entries_g > 0) \
+ ; /* Do nothing: we are still in an API call. */ \
+ else if (err_occurred) \
+ ; /* Do nothing: an error occurred. */ \
+ else if (TAILQ_EMPTY(&eot_queue_g)) \
+ ; /* Nothing to do. */ \
+ else if (H5F_vfd_swmr_process_eot_queue(false) < 0) { \
+ HDONE_ERROR(H5E_FUNC, H5E_CANTSET, err, \
+ "error processing EOT queue") \
+ } \
+ } while (0)
+
/* Use this macro for all "normal" API functions */
#define FUNC_ENTER_API(err) {{ \
FUNC_ENTER_API_COMMON \
FUNC_ENTER_API_INIT(err); \
FUNC_ENTER_API_PUSH(err); \
+ VFD_SWMR_ENTER(err); \
+ /* Clear thread error stack entering public functions */ \
+ H5E_clear_stack(NULL); \
+ {
+
+/* Use this macro when VFD SWMR EOT is not used on entering an API function */
+#define FUNC_ENTER_API_NO_EOT(err) {{ \
+ FUNC_ENTER_API_COMMON \
+ FUNC_ENTER_API_INIT(err); \
+ FUNC_ENTER_API_PUSH(err); \
/* Clear thread error stack entering public functions */ \
H5E_clear_stack(NULL); \
{
@@ -2193,6 +2239,7 @@ H5_DLL herr_t H5CX_pop(void);
FUNC_ENTER_API_COMMON \
FUNC_ENTER_API_INIT(err); \
FUNC_ENTER_API_PUSH(err); \
+ VFD_SWMR_ENTER(err); \
{
/*
@@ -2397,6 +2444,18 @@ H5_DLL herr_t H5CX_pop(void);
H5TRACE_RETURN(ret_value);
#define FUNC_LEAVE_API(ret_value) \
+ VFD_SWMR_LEAVE(ret_value); \
+ FUNC_LEAVE_API_COMMON(ret_value); \
+ (void)H5CX_pop(); \
+ H5_POP_FUNC \
+ if(err_occurred) \
+ (void)H5E_dump_api_stack(TRUE); \
+ FUNC_LEAVE_API_THREADSAFE \
+ return(ret_value); \
+}} /*end scope from beginning of FUNC_ENTER*/
+
+/* Use this macro when VFD SWMR EOT is not used on leaving an API function */
+#define FUNC_LEAVE_API_NO_EOT(ret_value) \
FUNC_LEAVE_API_COMMON(ret_value); \
(void)H5CX_pop(); \
H5_POP_FUNC \
diff --git a/src/H5public.h b/src/H5public.h
index d3edd23..3a3de88 100644
--- a/src/H5public.h
+++ b/src/H5public.h
@@ -177,42 +177,82 @@ typedef long long ssize_t;
#endif
#endif
+/* int64_t type is used for creation order field for links. It may be
+ * defined in Posix.1g, otherwise it is defined here.
+ */
+#if H5_SIZEOF_INT64_T>=8
+#elif H5_SIZEOF_INT>=8
+ typedef int int64_t;
+# undef H5_SIZEOF_INT64_T
+# define H5_SIZEOF_INT64_T H5_SIZEOF_INT
+#elif H5_SIZEOF_LONG>=8
+ typedef long int64_t;
+# undef H5_SIZEOF_INT64_T
+# define H5_SIZEOF_INT64_T H5_SIZEOF_LONG
+#elif H5_SIZEOF_LONG_LONG>=8
+ typedef long long int64_t;
+# undef H5_SIZEOF_INT64_T
+# define H5_SIZEOF_INT64_T H5_SIZEOF_LONG_LONG
+#else
+# error "nothing appropriate for int64_t"
+#endif
+
+/* uint64_t type is used for fields for H5O_info_t. It may be
+ * defined in Posix.1g, otherwise it is defined here.
+ */
+#if H5_SIZEOF_UINT64_T>=8
+#elif H5_SIZEOF_INT>=8
+ typedef unsigned uint64_t;
+# undef H5_SIZEOF_UINT64_T
+# define H5_SIZEOF_UINT64_T H5_SIZEOF_INT
+#elif H5_SIZEOF_LONG>=8
+ typedef unsigned long uint64_t;
+# undef H5_SIZEOF_UINT64_T
+# define H5_SIZEOF_UINT64_T H5_SIZEOF_LONG
+#elif H5_SIZEOF_LONG_LONG>=8
+ typedef unsigned long long uint64_t;
+# undef H5_SIZEOF_UINT64_T
+# define H5_SIZEOF_UINT64_T H5_SIZEOF_LONG_LONG
+#else
+# error "nothing appropriate for uint64_t"
+#endif
+
/*
* The sizes of file objects have their own types defined here, use a 64-bit
* type.
*/
-#if H5_SIZEOF_LONG_LONG >= 8
-H5_GCC_DIAG_OFF(long-long)
-typedef unsigned long long hsize_t;
-typedef signed long long hssize_t;
-H5_GCC_DIAG_ON(long-long)
-# define H5_SIZEOF_HSIZE_T H5_SIZEOF_LONG_LONG
-# define H5_SIZEOF_HSSIZE_T H5_SIZEOF_LONG_LONG
-#else
-# error "nothing appropriate for hsize_t"
-#endif
-#define HSIZE_UNDEF ((hsize_t)(hssize_t)(-1))
+typedef uint64_t hsize_t;
+typedef int64_t hssize_t;
+#define PRIXHSIZE PRIX64
+#define PRIdHSIZE PRId64
+#define PRIiHSIZE PRIi64
+#define PRIoHSIZE PRIo64
+#define PRIuHSIZE PRIu64
+#define PRIxHSIZE PRIx64
+#define H5_SIZEOF_HSIZE_T H5_SIZEOF_UINT64_T
+#define H5_SIZEOF_HSSIZE_T H5_SIZEOF_INT64_T
+#define HSIZE_UNDEF UINT64_MAX
/*
* File addresses have their own types.
*/
#if H5_SIZEOF_INT >= 8
typedef unsigned haddr_t;
-# define HADDR_UNDEF ((haddr_t)(-1))
+# define HADDR_UNDEF UINT_MAX
# define H5_SIZEOF_HADDR_T H5_SIZEOF_INT
# ifdef H5_HAVE_PARALLEL
# define HADDR_AS_MPI_TYPE MPI_UNSIGNED
# endif /* H5_HAVE_PARALLEL */
#elif H5_SIZEOF_LONG >= 8
typedef unsigned long haddr_t;
-# define HADDR_UNDEF ((haddr_t)(long)(-1))
+# define HADDR_UNDEF ULONG_MAX
# define H5_SIZEOF_HADDR_T H5_SIZEOF_LONG
# ifdef H5_HAVE_PARALLEL
# define HADDR_AS_MPI_TYPE MPI_UNSIGNED_LONG
# endif /* H5_HAVE_PARALLEL */
#elif H5_SIZEOF_LONG_LONG >= 8
typedef unsigned long long haddr_t;
-# define HADDR_UNDEF ((haddr_t)(long long)(-1))
+# define HADDR_UNDEF ULLONG_MAX
# define H5_SIZEOF_HADDR_T H5_SIZEOF_LONG_LONG
# ifdef H5_HAVE_PARALLEL
# define HADDR_AS_MPI_TYPE MPI_LONG_LONG_INT
@@ -221,15 +261,25 @@ H5_GCC_DIAG_ON(long-long)
# error "nothing appropriate for haddr_t"
#endif
#if H5_SIZEOF_HADDR_T == H5_SIZEOF_INT
-# define H5_PRINTF_HADDR_FMT "%u"
+# define PRIXHADDR "X"
+# define PRIoHADDR "o"
+# define PRIuHADDR "u"
+# define PRIxHADDR "x"
#elif H5_SIZEOF_HADDR_T == H5_SIZEOF_LONG
-# define H5_PRINTF_HADDR_FMT "%lu"
+# define PRIXHADDR "lX"
+# define PRIoHADDR "lo"
+# define PRIuHADDR "lu"
+# define PRIxHADDR "lx"
#elif H5_SIZEOF_HADDR_T == H5_SIZEOF_LONG_LONG
-# define H5_PRINTF_HADDR_FMT "%" H5_PRINTF_LL_WIDTH "u"
+# define PRIXHADDR H5_PRINTF_LL_WIDTH "X"
+# define PRIoHADDR H5_PRINTF_LL_WIDTH "o"
+# define PRIuHADDR H5_PRINTF_LL_WIDTH "u"
+# define PRIxHADDR H5_PRINTF_LL_WIDTH "x"
#else
# error "nothing appropriate for H5_PRINTF_HADDR_FMT"
#endif
-#define HADDR_MAX (HADDR_UNDEF-1)
+#define H5_PRINTF_HADDR_FMT "%" PRIuHADDR
+#define HADDR_MAX (HADDR_UNDEF-1)
/* uint32_t type is used for creation order field for messages. It may be
* defined in Posix.1g, otherwise it is defined here.
@@ -251,46 +301,6 @@ H5_GCC_DIAG_ON(long-long)
# error "nothing appropriate for uint32_t"
#endif
-/* int64_t type is used for creation order field for links. It may be
- * defined in Posix.1g, otherwise it is defined here.
- */
-#if H5_SIZEOF_INT64_T>=8
-#elif H5_SIZEOF_INT>=8
- typedef int int64_t;
-# undef H5_SIZEOF_INT64_T
-# define H5_SIZEOF_INT64_T H5_SIZEOF_INT
-#elif H5_SIZEOF_LONG>=8
- typedef long int64_t;
-# undef H5_SIZEOF_INT64_T
-# define H5_SIZEOF_INT64_T H5_SIZEOF_LONG
-#elif H5_SIZEOF_LONG_LONG>=8
- typedef long long int64_t;
-# undef H5_SIZEOF_INT64_T
-# define H5_SIZEOF_INT64_T H5_SIZEOF_LONG_LONG
-#else
-# error "nothing appropriate for int64_t"
-#endif
-
-/* uint64_t type is used for fields for H5O_info_t. It may be
- * defined in Posix.1g, otherwise it is defined here.
- */
-#if H5_SIZEOF_UINT64_T>=8
-#elif H5_SIZEOF_INT>=8
- typedef unsigned uint64_t;
-# undef H5_SIZEOF_UINT64_T
-# define H5_SIZEOF_UINT64_T H5_SIZEOF_INT
-#elif H5_SIZEOF_LONG>=8
- typedef unsigned long uint64_t;
-# undef H5_SIZEOF_UINT64_T
-# define H5_SIZEOF_UINT64_T H5_SIZEOF_LONG
-#elif H5_SIZEOF_LONG_LONG>=8
- typedef unsigned long long uint64_t;
-# undef H5_SIZEOF_UINT64_T
-# define H5_SIZEOF_UINT64_T H5_SIZEOF_LONG_LONG
-#else
-# error "nothing appropriate for uint64_t"
-#endif
-
/* Common iteration orders */
typedef enum {
H5_ITER_UNKNOWN = -1, /* Unknown order */
@@ -328,6 +338,17 @@ typedef struct H5_ih_info_t {
hsize_t heap_size;
} H5_ih_info_t;
+static inline const char *
+htri_to_string(htri_t v)
+{
+ if (v == 0)
+ return "false";
+ else if (v < 0)
+ return "error";
+ else
+ return "true";
+}
+
/* Tokens are unique and permanent identifiers that are
* used to reference HDF5 objects in a container. */
diff --git a/src/H5queue.h b/src/H5queue.h
new file mode 100644
index 0000000..816acca
--- /dev/null
+++ b/src/H5queue.h
@@ -0,0 +1,847 @@
+/* $NetBSD: queue.h,v 1.70.10.1 2017/10/02 13:21:41 martin Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list. Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction. Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Include the definition of NULL only on NetBSD because sys/null.h
+ * is not available elsewhere. This conditional makes the header
+ * portable and it can simply be dropped verbatim into any system.
+ * The caveat is that on other systems some other header
+ * must provide NULL before the macros can be used.
+ */
+#ifdef __NetBSD__
+#include <sys/null.h>
+#endif
+
+#if defined(QUEUEDEBUG)
+# if defined(_KERNEL)
+# define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__)
+# else
+# include <err.h>
+# define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__)
+# endif
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = (head)->slh_first; \
+ (var) != SLIST_END(head); \
+ (var) = (var)->field.sle_next)
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) != SLIST_END(head) && \
+ ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) do { \
+ (head)->slh_first = SLIST_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_AFTER(slistelm, field) do { \
+ (slistelm)->field.sle_next = \
+ SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while(curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods.
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = ((head)->lh_first); \
+ (var) != LIST_END(head); \
+ (var) = ((var)->field.le_next))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) != LIST_END(head) && \
+ ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_MOVE(head1, head2, field) do { \
+ LIST_INIT((head2)); \
+ if (!LIST_EMPTY((head1))) { \
+ (head2)->lh_first = (head1)->lh_first; \
+ (head2)->lh_first->field.le_prev = &(head2)->lh_first; \
+ LIST_INIT((head1)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+/*
+ * List functions.
+ */
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \
+ if ((head)->lh_first && \
+ (head)->lh_first->field.le_prev != &(head)->lh_first) \
+ QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_OP(elm, field) \
+ if ((elm)->field.le_next && \
+ (elm)->field.le_next->field.le_prev != \
+ &(elm)->field.le_next) \
+ QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \
+ __FILE__, __LINE__); \
+ if (*(elm)->field.le_prev != (elm)) \
+ QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \
+ (elm)->field.le_next = (void *)1L; \
+ (elm)->field.le_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_LIST_OP(elm, field)
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
+#endif
+
+#define LIST_INIT(head) do { \
+ (head)->lh_first = LIST_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != \
+ LIST_END(head)) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REMOVE(elm, field) do { \
+ QUEUEDEBUG_LIST_OP((elm), field) \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->sqh_first); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = ((var)->field.sqe_next))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->sqh_first); \
+ (var) != SIMPLEQ_END(head) && \
+ ((next = ((var)->field.sqe_next)), 1); \
+ (var) = (next))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+ == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->sqh_first == (elm)) { \
+ SIMPLEQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->sqh_first; \
+ while (curelm->field.sqe_next != (elm)) \
+ curelm = curelm->field.sqe_next; \
+ if ((curelm->field.sqe_next = \
+ curelm->field.sqe_next->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(curelm)->field.sqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_CONCAT(head1, head2) do { \
+ if (!SIMPLEQ_EMPTY((head2))) { \
+ *(head1)->sqh_last = (head2)->sqh_first; \
+ (head1)->sqh_last = (head2)->sqh_last; \
+ SIMPLEQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_LAST(head, type, field) \
+ (SIMPLEQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->sqh_last) - offsetof(struct type, field))))
+
+/*
+ * Tail queue definitions.
+ */
+#define _TAILQ_HEAD(name, type, qual) \
+struct name { \
+ qual type *tqh_first; /* first element */ \
+ qual type *qual *tqh_last; /* addr of last next element */ \
+}
+#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { TAILQ_END(head), &(head).tqh_first }
+
+#define _TAILQ_ENTRY(type, qual) \
+struct { \
+ qual type *tqe_next; /* next element */ \
+ qual type *qual *tqe_prev; /* address of previous next element */\
+}
+#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue access methods.
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) (NULL)
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)(void *)((head)->tqh_last))->tqh_last))
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)(void *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head))
+
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var) != TAILQ_END(head); \
+ (var) = ((var)->field.tqe_next))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->tqh_first); \
+ (var) != TAILQ_END(head) && \
+ ((next) = TAILQ_NEXT(var, field), 1); (var) = (next))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) != TAILQ_END(head) && \
+ ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev))
+
+/*
+ * Tail queue functions.
+ */
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \
+ if ((head)->tqh_first && \
+ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
+ QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \
+ if (*(head)->tqh_last != NULL) \
+ QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_OP(elm, field) \
+ if ((elm)->field.tqe_next && \
+ (elm)->field.tqe_next->field.tqe_prev != \
+ &(elm)->field.tqe_next) \
+ QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \
+ __FILE__, __LINE__); \
+ if (*(elm)->field.tqe_prev != (elm)) \
+ QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \
+ if ((elm)->field.tqe_next == NULL && \
+ (head)->tqh_last != &(elm)->field.tqe_next) \
+ QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\
+ (head), (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \
+ (elm)->field.tqe_next = (void *)1L; \
+ (elm)->field.tqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
+#define QUEUEDEBUG_TAILQ_OP(elm, field)
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
+#endif
+
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = TAILQ_END(head); \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \
+ (elm)->field.tqe_next = TAILQ_END(head); \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \
+ TAILQ_END(head)) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \
+ QUEUEDEBUG_TAILQ_OP((elm), field) \
+ if (((elm)->field.tqe_next) != TAILQ_END(head)) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \
+ TAILQ_END(head)) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first; /* first element */ \
+ struct type **stqh_last; /* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+#define STAILQ_END(head) NULL
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head))
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_INIT(head) do { \
+ (head)->stqh_first = NULL; \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (head)->stqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.stqe_next = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (listelm)->field.stqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->stqh_first == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->stqh_first; \
+ while (curelm->field.stqe_next != (elm)) \
+ curelm = curelm->field.stqe_next; \
+ if ((curelm->field.stqe_next = \
+ curelm->field.stqe_next->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(curelm)->field.stqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->stqh_first); \
+ (var); \
+ (var) = ((var)->field.stqe_next))
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->stqh_last) - offsetof(struct type, field))))
+
+
+#ifndef _KERNEL
+/*
+ * Circular queue definitions. Do not use. We still keep the macros
+ * for compatibility but because of pointer aliasing issues their use
+ * is discouraged!
+ */
+
+/*
+ * __launder_type(): We use this ugly hack to work around the the compiler
+ * noticing that two types may not alias each other and elide tests in code.
+ * We hit this in the CIRCLEQ macros when comparing 'struct name *' and
+ * 'struct type *' (see CIRCLEQ_HEAD()). Modern compilers (such as GCC
+ * 4.8) declare these comparisons as always false, causing the code to
+ * not run as designed.
+ *
+ * This hack is only to be used for comparisons and thus can be fully const.
+ * Do not use for assignment.
+ *
+ * If we ever choose to change the ABI of the CIRCLEQ macros, we could fix
+ * this by changing the head/tail sentinal values, but see the note above
+ * this one.
+ */
+static __inline const void * __launder_type(const void *);
+static __inline const void *
+__launder_type(const void *__x)
+{
+ __asm __volatile("" : "+r" (__x));
+ return __x;
+}
+
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \
+ if ((head)->cqh_first != CIRCLEQ_ENDC(head) && \
+ (head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head), \
+ __FILE__, __LINE__); \
+ if ((head)->cqh_last != CIRCLEQ_ENDC(head) && \
+ (head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \
+ if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) { \
+ if ((head)->cqh_last != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ } else { \
+ if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ } \
+ if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) { \
+ if ((head)->cqh_first != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ } else { \
+ if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ }
+#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \
+ (elm)->field.cqe_next = (void *)1L; \
+ (elm)->field.cqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field)
+#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field)
+#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field)
+#endif
+
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \
+ if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+ QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->cqh_first); \
+ (var) != CIRCLEQ_ENDC(head); \
+ (var) = ((var)->field.cqe_next))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = ((head)->cqh_last); \
+ (var) != CIRCLEQ_ENDC(head); \
+ (var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+/* For comparisons */
+#define CIRCLEQ_ENDC(head) (__launder_type(head))
+/* For assignments */
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head))
+
+#define CIRCLEQ_LOOP_NEXT(head, elm, field) \
+ (((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
+ ? ((head)->cqh_first) \
+ : (elm->field.cqe_next))
+#define CIRCLEQ_LOOP_PREV(head, elm, field) \
+ (((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
+ ? ((head)->cqh_last) \
+ : (elm->field.cqe_prev))
+#endif /* !_KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/src/H5retry_private.h b/src/H5retry_private.h
new file mode 100644
index 0000000..6621957
--- /dev/null
+++ b/src/H5retry_private.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2019 The HDF Group. All rights reserved.
+ *
+ * This file is part of HDF5. The full HDF5 copyright notice, including
+ * terms governing use, modification, and redistribution, is contained in
+ * the COPYING file, which can be found at the root of the source code
+ * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.
+ * If you do not have access to either file, you may request a copy from
+ * help@hdfgroup.org.
+ */
+
+#ifndef _H5retry_private_H
+#define _H5retry_private_H
+
+/*
+ * Data types and functions for retry loops.
+ */
+
+/* State for a retry loop. No user-serviceable parts inside. */
+typedef struct h5_retry_t {
+ uint64_t maxival; /* maximum sleep interval (nanoseconds) */
+ unsigned maxtries; /* maximum permissible tries */
+ unsigned tries; /* remaining tries */
+ uint64_t ival; /* nanoseconds sleep interval before clamping to
+ * maxival
+ */
+} h5_retry_t;
+
+/* Default minimum/maximum retry intervals: 1/10s minimum, 1s maximum. */
+#define H5_RETRY_DEFAULT_MINIVAL ( 100ULL * 1000ULL * 1000ULL)
+#define H5_RETRY_DEFAULT_MAXIVAL ( 1000ULL * 1000ULL * 1000ULL)
+/* One hour: */
+#define H5_RETRY_ONE_SECOND (1000ULL * 1000ULL * 1000ULL)
+#define H5_RETRY_ONE_HOUR (3600ULL * H5_RETRY_ONE_SECOND)
+
+/* If any tries remain, decrease the number of remaining tries and
+ * return true. Otherwise, return false.
+ *
+ * XXX This is not part of the API. XXX
+ */
+static inline bool
+h5_retry_decrement(struct h5_retry_t *r)
+{
+ if (r->tries == 0)
+ return false;
+ --r->tries;
+ return true;
+}
+
+/* Establish state for a retry loop in `r`. The loop will retry no
+ * more than `maxtries` times, sleeping for no fewer than `minival`
+ * nanoseconds between tries. After each try, the sleep time will
+ * increase to `maxival` nanoseconds or twice the previous sleep time,
+ * whichever is less.
+ *
+ * `h5_retry_init` always returns true. This is to help one use
+ * it in a loop like this:
+ *
+ * for (do_try = h5_retry_init(&r, 100, H5_RETRY_DEFAULT_MINIVAL,
+ * H5_RETRY_DEFAULT_MAXIVAL);
+ * do_try;
+ * do_try = h5_retry_next(&r)) {
+ * .
+ * .
+ * .
+ * }
+ *
+ * Note well: the program will enter the body of the loop, above, no more
+ * than 101 times: once for an initial try, and then 100 times for retries.
+ */
+static inline bool
+h5_retry_init(struct h5_retry_t *r, unsigned int maxtries, uint64_t minival,
+ uint64_t maxival)
+{
+ memset(r, '\0', sizeof(*r));
+ assert(0 < maxtries);
+ assert(0 < minival && minival <= maxival);
+ r->tries = r->maxtries = maxtries;
+ r->ival = minival;
+ r->maxival = maxival;
+ return h5_retry_decrement(r);
+}
+
+/* If any tries remain, sleep for the mininum interval, or twice the
+ * previous sleep time, and return true. If no tries remain, return false.
+ */
+static inline bool
+h5_retry_next(struct h5_retry_t *r)
+{
+ uint64_t ival;
+
+ if (!h5_retry_decrement(r))
+ return false;
+ ival = r->ival;
+ if (r->maxival < ival)
+ ival = r->maxival;
+ else if (UINT64_MAX - ival >= ival)
+ r->ival += ival;
+
+ H5_nanosleep(ival);
+
+ return true;
+}
+
+/* Return the number of tries performed since `h5_retry_init()`
+ * was called on `r`.
+ */
+static inline unsigned
+h5_retry_tries(struct h5_retry_t *r)
+{
+ return r->maxtries - r->tries;
+}
+
+#endif /* _H5retry_private_H */
diff --git a/src/H5system.c b/src/H5system.c
index 24935fd..fff4c8c 100644
--- a/src/H5system.c
+++ b/src/H5system.c
@@ -1448,15 +1448,32 @@ done:
void
H5_nanosleep(uint64_t nanosec)
{
+ const uint64_t nanosec_per_sec = 1000 * 1000 * 1000;
struct timespec sleeptime; /* Struct to hold time to sleep */
FUNC_ENTER_NOAPI_NOINIT_NOERR
- /* Set up time to sleep */
- sleeptime.tv_sec = 0;
- sleeptime.tv_nsec = (long)nanosec;
+ /* Set up time to sleep
+ *
+ * Assuming ILP32 or LP64 or wider architecture, (long)operand
+ * satisfies 0 <= operand < nanosec_per_sec < LONG_MAX.
+ *
+ * It's harder to be sure that we don't overflow time_t.
+ */
+ sleeptime.tv_sec = (time_t)(nanosec / nanosec_per_sec);
+ sleeptime.tv_nsec = (long)(nanosec % nanosec_per_sec);
- HDnanosleep(&sleeptime, NULL);
+ /* Sleep for up to `sleeptime` and, in the event of an interruption,
+ * save the unslept time back to `sleeptime`.
+ */
+ while (HDnanosleep(&sleeptime, &sleeptime) == -1) {
+ /* If we were just interrupted, sleep for the remaining time.
+ * Otherwise, the error was essentially impossible, so just stop
+ * sleeping.
+ */
+ if (errno != EINTR)
+ break;
+ }
FUNC_LEAVE_NOAPI_VOID
} /* end H5_nanosleep() */
diff --git a/src/H5time_private.h b/src/H5time_private.h
new file mode 100644
index 0000000..30cbaf1
--- /dev/null
+++ b/src/H5time_private.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2019 The HDF Group. All rights reserved.
+ *
+ * This file is part of HDF5. The full HDF5 copyright notice, including
+ * terms governing use, modification, and redistribution, is contained in
+ * the COPYING file, which can be found at the root of the source code
+ * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.
+ * If you do not have access to either file, you may request a copy from
+ * help@hdfgroup.org.
+ */
+
+/*
+ * Portions of this file derive from <sys/time.h> in NetBSD. Applicable
+ * copyright notices and licenses are reproduced here:
+ */
+
+/*-
+ * Copyright (c) 2017 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)time.h 8.5 (Berkeley) 5/4/95
+ */
+
+#ifndef _H5time_private_H
+#define _H5time_private_H
+
+#ifdef __NetBSD__
+#include <sys/time.h>
+#else
+#define timespecclear(tsp) (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
+#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec)
+#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) * 1000000000UL + (uint64_t)(x)->tv_nsec)
+#endif
+
+#endif /* _H5time_private_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index 787b502..cb3fa3c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -59,11 +59,13 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5lib_settings.c H5system.c \
H5F.c H5Faccum.c H5Fcwfs.c H5Fdbg.c H5Fdeprec.c H5Fefc.c H5Ffake.c \
H5Fint.c H5Fio.c H5Fmount.c H5Fquery.c H5Fsfile.c H5Fspace.c \
H5Fsuper.c H5Fsuper_cache.c H5Ftest.c \
+ H5Fvfd_swmr.c \
H5FA.c H5FAcache.c H5FAdbg.c H5FAdblock.c H5FAdblkpage.c H5FAhdr.c \
H5FAint.c H5FAstat.c H5FAtest.c \
H5FD.c H5FDcore.c H5FDfamily.c H5FDhdfs.c H5FDint.c H5FDlog.c \
H5FDmulti.c H5FDsec2.c H5FDspace.c \
H5FDsplitter.c H5FDstdio.c H5FDtest.c \
+ H5FDvfd_swmr.c H5FDvfd_swmr_instr.c \
H5FL.c H5FO.c H5FS.c H5FScache.c H5FSdbg.c H5FSint.c H5FSsection.c \
H5FSstat.c H5FStest.c \
H5G.c H5Gbtree2.c H5Gcache.c H5Gcompact.c H5Gdense.c H5Gdeprec.c \
@@ -72,13 +74,12 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5lib_settings.c H5system.c \
H5HF.c H5HFbtree2.c H5HFcache.c H5HFdbg.c H5HFdblock.c H5HFdtable.c \
H5HFhdr.c H5HFhuge.c H5HFiblock.c H5HFiter.c H5HFman.c H5HFsection.c \
H5HFspace.c H5HFstat.c H5HFtest.c H5HFtiny.c \
- H5HG.c H5HGcache.c H5HGdbg.c H5HGquery.c \
+ H5HG.c H5HGcache.c H5HGdbg.c H5HGquery.c H5HGtrap.c \
H5HL.c H5HLcache.c H5HLdbg.c H5HLint.c H5HLprfx.c H5HLdblk.c \
- H5HP.c \
- H5I.c H5Itest.c \
- H5L.c H5Ldeprec.c H5Lexternal.c \
+ H5HP.c H5I.c H5Itest.c H5L.c H5Ldeprec.c H5Lexternal.c \
H5M.c \
H5MF.c H5MFaggr.c H5MFdbg.c H5MFsection.c \
+ H5MV.c H5MVsection.c \
H5MM.c H5MP.c H5MPtest.c \
H5O.c H5Odeprec.c H5Oainfo.c H5Oalloc.c H5Oattr.c H5Oattribute.c \
H5Obogus.c H5Obtreek.c H5Ocache.c H5Ocache_image.c H5Ochunk.c \
@@ -113,8 +114,9 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5lib_settings.c H5system.c \
H5VLnative_token.c \
H5VLpassthru.c \
H5VM.c H5WB.c H5Z.c \
- H5Zdeflate.c H5Zfletcher32.c H5Znbit.c H5Zshuffle.c H5Zscaleoffset.c \
- H5Zszip.c H5Ztrans.c
+ H5Zdeflate.c H5Zfletcher32.c H5Znbit.c H5Zshuffle.c \
+ H5Zscaleoffset.c H5Zszip.c H5Ztrans.c \
+ hlog.c
# Only compile parallel sources if necessary
if BUILD_PARALLEL_CONDITIONAL
@@ -137,13 +139,16 @@ if ROS3_VFD_CONDITIONAL
endif
# Public headers
-include_HEADERS = hdf5.h H5api_adpt.h H5overflow.h H5pubconf.h H5public.h H5version.h \
+include_HEADERS = hdf5.h H5api_adpt.h H5overflow.h H5pubconf.h H5public.h \
+ H5queue.h \
+ H5version.h \
H5Apublic.h H5ACpublic.h \
H5Cpublic.h H5Dpublic.h \
H5Epubgen.h H5Epublic.h H5ESpublic.h H5Fpublic.h \
H5FDpublic.h H5FDcore.h H5FDdirect.h H5FDfamily.h H5FDhdfs.h \
H5FDlog.h H5FDmirror.h H5FDmpi.h H5FDmpio.h H5FDmulti.h H5FDros3.h \
H5FDsec2.h H5FDsplitter.h H5FDstdio.h H5FDwindows.h \
+ H5FDvfd_swmr.h \
H5Gpublic.h H5Ipublic.h H5Lpublic.h \
H5Mpublic.h H5MMpublic.h H5Opublic.h H5Ppublic.h \
H5PLextern.h H5PLpublic.h \
diff --git a/src/hdf5.h b/src/hdf5.h
index eaaf8ae..7df67f7 100644
--- a/src/hdf5.h
+++ b/src/hdf5.h
@@ -53,6 +53,7 @@
#include "H5FDsec2.h" /* POSIX unbuffered file I/O */
#include "H5FDsplitter.h" /* Twin-channel (R/W & R/O) I/O passthrough */
#include "H5FDstdio.h" /* Standard C buffered I/O */
+#include "H5FDvfd_swmr.h" /* VFD SWMR reader VFD */
#ifdef H5_HAVE_WINDOWS
#include "H5FDwindows.h" /* Win32 I/O */
#endif
diff --git a/src/hlog.c b/src/hlog.c
new file mode 100644
index 0000000..d2d0e51
--- /dev/null
+++ b/src/hlog.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2004, 2005, 2006, 2007 David Young. All rights reserved.
+ *
+ * See COPYING at the top of the HDF5 distribution for license terms.
+ */
+/*
+ * Copyright (c) 2004 Urbana-Champaign Independent Media Center.
+ * All rights reserved.
+ *
+ * See COPYING at the top of the HDF5 distribution for license terms.
+ */
+#include <err.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h> /* for uintmax_t */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+
+#include "hlog.h"
+#include "H5time_private.h"
+
+TAILQ_HEAD(, hlog_outlet) hlog_outlets = TAILQ_HEAD_INITIALIZER(hlog_outlets);
+
+HLOG_OUTLET_TOP_DEFN(all);
+
+static struct timespec timestamp_zero;
+
+void hlog_init(void) _constructor;
+static void hlog_init_timestamps(void);
+
+void
+hlog_init(void)
+{
+ const char *settings0;
+ char *item, *settings;
+
+ if ((settings0 = getenv("HLOG")) == NULL)
+ return;
+
+ if ((settings = strdup(settings0)) == NULL) {
+ warn("%s: cannot duplicate settings string", __func__);
+ return;
+ }
+
+ while ((item = strsep(&settings, " ,")) != NULL) {
+ hlog_outlet_state_t state;
+ char key[64 + 1], val[4 + 1]; // + 1 for the terminating NUL
+ int nconverted;
+
+ nconverted = sscanf(item, " %64[0-9a-z_] = %4s ", key, val);
+ if (nconverted != 2) {
+ warnx("%s: malformed HLOG item \"%s\"", __func__, item);
+ continue;
+ }
+
+ if (strcmp(val, "on") == 0 || strcmp(val, "yes") == 0)
+ state = HLOG_OUTLET_S_ON;
+ else if (strcmp(val, "off") == 0 || strcmp(val, "no") == 0)
+ state = HLOG_OUTLET_S_OFF;
+ else if (strcmp(val, "pass") == 0)
+ state = HLOG_OUTLET_S_PASS;
+ else {
+ warnx("%s: bad HLOG value \"%s\" in item \"%s\"", __func__,
+ val, item);
+ continue;
+ }
+
+ if (hlog_set_state(key, state, true) == -1) {
+ warn("%s: could not set state for HLOG item \"%s\"", __func__,
+ item);
+ }
+ }
+
+ free(settings);
+}
+
+
+static void
+hlog_init_timestamps(void)
+{
+ static bool initialized = false;
+
+ if (initialized)
+ return;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &timestamp_zero) == -1)
+ err(EXIT_FAILURE, "%s: clock_gettime", __func__);
+
+ initialized = true;
+}
+
+static void
+hlog_print_time(void)
+{
+ struct timespec elapsed, now;
+
+ hlog_init_timestamps();
+
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
+ err(EXIT_FAILURE, "%s: clock_gettime", __func__);
+
+ timespecsub(&now, &timestamp_zero, &elapsed);
+
+ fprintf(stderr, "%ju.%.9ld ", (uintmax_t)elapsed.tv_sec, elapsed.tv_nsec);
+}
+
+void
+vhlog(const char *fmt, va_list ap)
+{
+ hlog_print_time();
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fputc('\n', stderr);
+}
+
+static char *
+message_extend_stderr(const char *fmt0)
+{
+ static const char sep[] = ": ";
+ const char *m;
+ char *fmt;
+ size_t fmtlen;
+
+ m = strerror(errno);
+
+ fmtlen = strlen(fmt0) + strlen(m) + sizeof(sep);
+
+ if ((fmt = malloc(fmtlen)) == NULL) {
+ err(EXIT_FAILURE, "%s: malloc failed", __func__);
+ return NULL;
+ }
+
+ /* Note well the safe strcpy, strcat usage. Thank you. */
+ strcpy(fmt, fmt0);
+ strcat(fmt, sep);
+ strcat(fmt, m);
+
+ return fmt;
+}
+
+static char *
+message_extend(const char *fmt0)
+{
+ return message_extend_stderr(fmt0);
+}
+
+void
+vhlog_err(int status, const char *fmt0, va_list ap)
+{
+ char *fmt;
+
+ if ((fmt = message_extend(fmt0)) == NULL)
+ exit(status);
+ vhlog(fmt, ap);
+ free(fmt);
+
+ exit(status);
+}
+
+void
+vhlog_errx(int status, const char *fmt, va_list ap)
+{
+ vhlog(fmt, ap);
+ exit(status);
+}
+
+void
+vhlog_warn(const char *fmt0, va_list ap)
+{
+ char *fmt;
+
+ if ((fmt = message_extend(fmt0)) == NULL)
+ return;
+ vhlog(fmt, ap);
+ free(fmt);
+}
+
+void
+vhlog_warnx(const char *fmt, va_list ap)
+{
+ vhlog(fmt, ap);
+}
+
+void
+hlog_err(int status, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vhlog_err(status, fmt, ap);
+ va_end(ap);
+}
+
+void
+hlog_errx(int status, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vhlog_errx(status, fmt, ap);
+ va_end(ap);
+}
+
+void
+hlog_warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vhlog_warn(fmt, ap);
+ va_end(ap);
+}
+
+void
+hlog_warnx(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vhlog_warnx(fmt, ap);
+ va_end(ap);
+}
+
+struct hlog_outlet *
+hlog_outlet_find_active(struct hlog_outlet *ls0)
+{
+ struct hlog_outlet *ls;
+
+ HLOG_OUTLET_FOREACH(ls, ls0) {
+ switch (ls->ls_state) {
+ case HLOG_OUTLET_S_PASS:
+ continue;
+ case HLOG_OUTLET_S_OFF:
+ return NULL;
+ case HLOG_OUTLET_S_ON:
+ default:
+ return ls;
+ }
+ }
+ return NULL;
+}
+
+void
+hlog_always(struct hlog_outlet *ls _unused, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vhlog(fmt, ap);
+ va_end(ap);
+}
+
+void
+hlog_impl(struct hlog_outlet *ls0, const char *fmt, ...)
+{
+ struct hlog_outlet *ls;
+ va_list ap;
+
+ if ((ls = hlog_outlet_find_active(ls0)) == NULL) {
+ ls0->ls_resolved = HLOG_OUTLET_S_OFF;
+ return;
+ }
+
+ ls0->ls_resolved = HLOG_OUTLET_S_ON;
+
+ va_start(ap, fmt);
+ vhlog(fmt, ap);
+ va_end(ap);
+}
+
+static void
+hlog_outlet_reset_all(void)
+{
+ struct hlog_outlet *ls;
+
+ TAILQ_FOREACH(ls, &hlog_outlets, ls_next)
+ ls->ls_resolved = HLOG_OUTLET_S_PASS;
+}
+
+struct hlog_outlet *
+hlog_outlet_lookup(const char *name)
+{
+ struct hlog_outlet *ls;
+
+ TAILQ_FOREACH(ls, &hlog_outlets, ls_next) {
+ if (strcmp(ls->ls_name, name) == 0)
+ return ls;
+ }
+ return NULL;
+}
+
+static struct hlog_outlet *
+hlog_outlet_create(const char *name)
+{
+ struct hlog_outlet *ls;
+
+ if ((ls = calloc(1, sizeof(*ls))) == NULL)
+ return NULL;
+ else if ((ls->ls_name0 = strdup(name)) == NULL) {
+ free(ls);
+ return NULL;
+ }
+ ls->ls_name = ls->ls_name0;
+ ls->ls_rendezvous = true;
+ return ls;
+}
+
+static void
+hlog_outlet_destroy(struct hlog_outlet *ls)
+{
+ /*LINTED*/
+ if (ls->ls_name0 != NULL)
+ free(ls->ls_name0);
+ free(ls);
+}
+
+int
+hlog_set_state(const char *name, hlog_outlet_state_t state, bool rendezvous)
+{
+ struct hlog_outlet *ls;
+ errno = 0;
+
+ switch (state) {
+ case HLOG_OUTLET_S_PASS:
+ case HLOG_OUTLET_S_OFF:
+ case HLOG_OUTLET_S_ON:
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ if ((ls = hlog_outlet_lookup(name)) == NULL && !rendezvous) {
+ errno = ESRCH;
+ return -1;
+ } else if (ls == NULL) {
+ if ((ls = hlog_outlet_create(name)) == NULL)
+ return -1;
+ TAILQ_INSERT_TAIL(&hlog_outlets, ls, ls_next);
+ }
+ ls->ls_state = state;
+ hlog_outlet_reset_all();
+ return 0;
+}
+
+void
+hlog_outlet_register(struct hlog_outlet *ls_arg)
+{
+ struct hlog_outlet *ls;
+ if ((ls = hlog_outlet_lookup(ls_arg->ls_name)) == NULL ||
+ ls->ls_rendezvous) {
+ TAILQ_INSERT_TAIL(&hlog_outlets, ls_arg, ls_next);
+ if (ls == NULL)
+ return;
+ warnx("%s: rendezvous with log-outlet '%s'", __func__,
+ ls->ls_name);
+ ls_arg->ls_state = ls->ls_state;
+ TAILQ_REMOVE(&hlog_outlets, ls, ls_next);
+ hlog_outlet_destroy(ls);
+ } else
+ warnx("%s: duplicate log-outlet, '%s'", __func__, ls->ls_name);
+}
diff --git a/src/hlog.h b/src/hlog.h
new file mode 100644
index 0000000..2489a47
--- /dev/null
+++ b/src/hlog.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2004, 2005, 2006, 2007 David Young. All rights reserved.
+ *
+ * See COPYING at the top of the HDF5 distribution for license terms.
+ */
+/*
+ * Copyright (c) 2004 Urbana-Champaign Independent Media Center.
+ * All rights reserved.
+ *
+ * See COPYING at the top of the HDF5 distribution for license terms.
+ */
+#ifndef _HLOG_H
+#define _HLOG_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <syslog.h>
+#include <sys/cdefs.h>
+
+#include "H5queue.h"
+
+#ifndef _unused
+#define _unused __attribute__((unused))
+#endif
+
+#ifndef _constructor
+#define _constructor __attribute__((constructor))
+#endif
+
+#ifndef _noreturn
+#define _noreturn __attribute__((__noreturn__))
+#endif
+
+#ifndef _printflike
+#define _printflike(_fmt, _args) \
+ __attribute__((__format__(__printf__,_fmt,_args)))
+#endif
+
+enum hlog_outlet_state {
+ HLOG_OUTLET_S_PASS = 0
+ , HLOG_OUTLET_S_OFF = 1
+ , HLOG_OUTLET_S_ON = 2
+};
+
+typedef enum hlog_outlet_state hlog_outlet_state_t;
+
+struct hlog_outlet {
+ hlog_outlet_state_t ls_resolved;
+ struct hlog_outlet *ls_parent;
+ hlog_outlet_state_t ls_state;
+ const char *ls_name;
+ char *ls_name0;
+ bool ls_rendezvous;
+ TAILQ_ENTRY(hlog_outlet) ls_next;
+};
+
+typedef struct hlog_outlet hlog_outlet_t;
+
+#define HLOG_CONSTRUCTOR(__sym) \
+void hlog_constructor_##__sym(void) _constructor; \
+void \
+hlog_constructor_##__sym(void) \
+{ \
+ hlog_outlet_register(&__sym); \
+} \
+void hlog_undefined_##__sym(void) _constructor
+
+#define HLOG_OUTLET_FOREACH(__le, __le0) \
+ for ((__le) = (__le0); (__le) != NULL; (__le) = (__le)->ls_parent)
+
+#define HLOG_OUTLET_DECL1(__sym) extern struct hlog_outlet __sym
+
+#define HLOG_JOIN_SYMS(x, y) x ## y
+
+#define HLOG_PREFIX(_sfx) HLOG_JOIN_SYMS(hlog_gbl_, _sfx)
+
+#define HLOG_OUTLET_DECL(__name) HLOG_OUTLET_DECL1(HLOG_PREFIX(__name))
+
+#define HLOG_OUTLET_DEFN(__sym, __name, __parent, __state) \
+ struct hlog_outlet __sym = { \
+ .ls_name = __name \
+ , .ls_parent = (__parent) \
+ , .ls_state = (__state) \
+ }; \
+ HLOG_CONSTRUCTOR(__sym)
+
+#define HLOG_OUTLET_MEDIUM_DEFN(__name, __parent, __state) \
+ HLOG_OUTLET_DEFN(HLOG_PREFIX(__name), #__name, &HLOG_PREFIX(__parent), \
+ __state)
+
+#define HLOG_OUTLET_SHORT_DEFN(__name, __parent) \
+ HLOG_OUTLET_MEDIUM_DEFN(__name, __parent, HLOG_OUTLET_S_PASS)
+
+#define HLOG_OUTLET_TOP_DEFN(__name) \
+ HLOG_OUTLET_DEFN(HLOG_PREFIX(__name), #__name, NULL, HLOG_OUTLET_S_PASS)
+
+HLOG_OUTLET_DECL(all);
+
+#define hlog(_name, _fmt, ...) \
+ hlog_impl(&HLOG_PREFIX(_name), _fmt, __VA_ARGS__)
+
+#define hlog_fast(_name, ...) \
+ do { \
+ hlog_outlet_t *_ls0 = &HLOG_PREFIX(_name); \
+ \
+ if (_ls0->ls_resolved == HLOG_OUTLET_S_OFF) \
+ break; \
+ else if (_ls0->ls_resolved == HLOG_OUTLET_S_ON) \
+ hlog_always(_ls0, __VA_ARGS__); \
+ else \
+ hlog_impl(_ls0, __VA_ARGS__); \
+ } while (/*CONSTCOND*/0)
+
+struct hlog_outlet *hlog_outlet_find_active(struct hlog_outlet *);
+void hlog_outlet_register(struct hlog_outlet *);
+struct hlog_outlet *hlog_outlet_lookup(const char *);
+int hlog_set_state(const char *, hlog_outlet_state_t, bool);
+
+void vhlog(const char *, va_list) _printflike(1,0);
+
+void vhlog_warn(const char *, va_list) _printflike(1,0);
+void vhlog_warnx(const char *, va_list) _printflike(1,0);
+void vhlog_err(int, const char *, va_list) _printflike(2,0) _noreturn;
+void vhlog_errx(int, const char *, va_list) _printflike(2,0) _noreturn;
+
+void hlog_warnx(const char *, ...) _printflike(1,2);
+void hlog_warn(const char *, ...) _printflike(1,2);
+
+void hlog_err(int, const char *, ...) _printflike(2,3) _noreturn;
+void hlog_errx(int, const char *, ...) _printflike(2,3) _noreturn;
+
+void hlog_always(struct hlog_outlet *, const char *, ...)
+ _printflike(2,3);
+
+void hlog_impl(struct hlog_outlet *, const char *, ...)
+ _printflike(2,3);
+
+#endif /* _HLOG_H */
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 311d753..7e01d06 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -16,6 +16,8 @@ set (TEST_LIB_SOURCES
${HDF5_TEST_SOURCE_DIR}/cache_common.c
${HDF5_TEST_SOURCE_DIR}/external_common.c
${HDF5_TEST_SOURCE_DIR}/swmr_common.c
+ ${HDF5_TEST_SOURCE_DIR}/vfd_swmr_common.c
+ ${HDF5_TEST_SOURCE_DIR}/vds_swmr_common.c
)
set (TEST_LIB_HEADERS
@@ -24,6 +26,7 @@ set (TEST_LIB_HEADERS
${HDF5_TEST_SOURCE_DIR}/external_common.h
${HDF5_TEST_SOURCE_DIR}/external_fname.h
${HDF5_TEST_SOURCE_DIR}/swmr_common.h
+ ${HDF5_TEST_SOURCE_DIR}/vfd_swmr_common.h
)
if (NOT ONLY_SHARED_LIBS)
@@ -319,8 +322,8 @@ set (H5_TESTS_MULTIPLE
testhdf5
cache_image
ttsafe
- thread_id # special link
mirror_vfd
+ thread_id # special link
)
# Only build single source tests here
foreach (h5_test ${H5_TESTS})
@@ -500,6 +503,20 @@ foreach (h5_test ${H5_VDS_SWMR_TESTS})
ADD_H5_EXE(${h5_test})
endforeach ()
+set (H5_VFD_SWMR_TESTS
+ vds_swmr_addrem_writer
+ vfd_swmr_generator
+ vfd_swmr_reader
+ vfd_swmr_remove_reader
+ vfd_swmr_remove_writer
+ vfd_swmr_sparse_writer
+ vfd_swmr_writer
+)
+
+foreach (h5_test ${H5_VFD_SWMR_TESTS})
+ ADD_H5_EXE(${h5_test})
+endforeach ()
+
#-- Adding test for accum_swmr_reader
# This has to be copied to the test directory for execve() to find it
# and it can't be renamed (i.e., no <foo>-shared).
diff --git a/test/Makefile.am b/test/Makefile.am
index 7ebeae7..2b1d7d5 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -37,6 +37,7 @@ AM_CPPFLAGS+=-I$(top_srcdir)/src -I$(top_builddir)/src
# test_usecases.sh: use_append_chunk, use_append_mchunks, use_disable_mdc_flushes
TEST_SCRIPT = testerror.sh testlibinfo.sh testcheck_version.sh testlinks_env.sh testexternal_env.sh \
testswmr.sh testvds_env.sh testvdsswmr.sh testflushrefresh.sh test_usecases.sh testabort_fail.sh \
+ testvfdswmr.sh \
test_mirror.sh
SCRIPT_DEPEND = error_test$(EXEEXT) err_compat$(EXEEXT) links_env$(EXEEXT) \
external_env$(EXEEXT) filenotclosed$(EXEEXT) del_many_dense_attrs$(EXEEXT) \
@@ -44,7 +45,16 @@ SCRIPT_DEPEND = error_test$(EXEEXT) err_compat$(EXEEXT) links_env$(EXEEXT) \
swmr_generator$(EXEEXT) swmr_reader$(EXEEXT) swmr_writer$(EXEEXT) \
swmr_remove_reader$(EXEEXT) swmr_remove_writer$(EXEEXT) swmr_addrem_writer$(EXEEXT) \
swmr_sparse_reader$(EXEEXT) swmr_sparse_writer$(EXEEXT) swmr_start_write$(EXEEXT) \
- vds_env$(EXEEXT) vds_swmr_gen$(EXEEXT) vds_swmr_reader$(EXEEXT) vds_swmr_writer$(EXEEXT)
+ vfd_swmr_generator$(EXEEXT) vfd_swmr_reader$(EXEEXT) vfd_swmr_writer$(EXEEXT) \
+ vfd_swmr_remove_reader$(EXEEXT) vfd_swmr_remove_writer$(EXEEXT) \
+ vfd_swmr_addrem_writer$(EXEEXT) vfd_swmr_sparse_reader$(EXEEXT) \
+ vfd_swmr_sparse_writer$(EXEEXT) \
+ vfd_swmr_bigset_reader$(EXEEXT) vfd_swmr_bigset_writer$(EXEEXT) \
+ vfd_swmr_group_reader$(EXEEXT) vfd_swmr_group_writer$(EXEEXT) \
+ vfd_swmr_vlstr_reader$(EXEEXT) vfd_swmr_vlstr_writer$(EXEEXT) \
+ vfd_swmr_zoo_reader$(EXEEXT) vfd_swmr_zoo_writer$(EXEEXT) \
+ vds_env$(EXEEXT) \
+ vds_swmr_gen$(EXEEXT) vds_swmr_reader$(EXEEXT) vds_swmr_writer$(EXEEXT)
if HAVE_SHARED_CONDITIONAL
TEST_SCRIPT += test_filter_plugin.sh test_vol_plugin.sh
SCRIPT_DEPEND += filter_plugin$(EXEEXT) vol_plugin$(EXEEXT)
@@ -59,7 +69,7 @@ check_SCRIPTS = $(TEST_SCRIPT)
TEST_PROG= testhdf5 \
cache cache_api cache_image cache_tagging lheap ohdr \
stab gheap evict_on_close farray earray btree2 fheap \
- pool accum hyperslab istore bittests dt_arith page_buffer \
+ pool accum hyperslab istore bittests dt_arith page_buffer vfd_swmr \
dtypes dsets chunk_info cmpd_dset filter_fail extend direct_chunk \
external efc objcopy objcopy_ref links unlink twriteorder big mtime fillval mount \
flush1 flush2 app_ref enum set_extent ttsafe enc_dec_plist \
@@ -90,6 +100,13 @@ check_PROGRAMS=$(TEST_PROG) error_test err_compat tcheck_version \
use_append_chunk use_append_chunk_mirror use_append_mchunks use_disable_mdc_flushes \
swmr_generator swmr_start_write swmr_reader swmr_writer swmr_remove_reader \
swmr_remove_writer swmr_addrem_writer swmr_sparse_reader swmr_sparse_writer \
+ vfd_swmr_generator vfd_swmr_reader vfd_swmr_writer \
+ vfd_swmr_remove_reader vfd_swmr_remove_writer vfd_swmr_addrem_writer \
+ vfd_swmr_sparse_reader vfd_swmr_sparse_writer \
+ vfd_swmr_bigset_reader vfd_swmr_bigset_writer \
+ vfd_swmr_group_reader vfd_swmr_group_writer \
+ vfd_swmr_vlstr_reader vfd_swmr_vlstr_writer \
+ vfd_swmr_zoo_reader vfd_swmr_zoo_writer \
swmr_check_compat_vfd vds_env vds_swmr_gen vds_swmr_reader vds_swmr_writer \
mirror_vfd
if HAVE_SHARED_CONDITIONAL
@@ -139,7 +156,7 @@ else
noinst_LTLIBRARIES=libh5test.la
endif
-libh5test_la_SOURCES=h5test.c testframe.c cache_common.c swmr_common.c external_common.c
+libh5test_la_SOURCES=h5test.c testframe.c cache_common.c swmr_common.c external_common.c stubs.c vfd_swmr_common.c
# Use libhd5test.la to compile all of the tests
LDADD=libh5test.la $(LIBHDF5)
@@ -150,6 +167,12 @@ ttsafe_SOURCES=ttsafe.c ttsafe_dcreate.c ttsafe_error.c ttsafe_cancel.c \
cache_image_SOURCES=cache_image.c genall5.c
mirror_vfd_SOURCES=mirror_vfd.c genall5.c
+vfd_swmr_zoo_writer_SOURCES=vfd_swmr_zoo_writer.c genall5.c
+vfd_swmr_zoo_reader_SOURCES=vfd_swmr_zoo_writer.c genall5.c
+
+vfd_swmr_bigset_reader_SOURCES=vfd_swmr_bigset_writer.c
+vfd_swmr_group_reader_SOURCES=vfd_swmr_group_writer.c
+
VFD_LIST = sec2 stdio core core_paged split multi family
if DIRECT_VFD_CONDITIONAL
VFD_LIST += direct
diff --git a/test/cache_common.c b/test/cache_common.c
index 1dc13a1..651534d 100644
--- a/test/cache_common.c
+++ b/test/cache_common.c
@@ -269,6 +269,7 @@ static const H5C_class_t pico_class[1] = {{
NULL,
pico_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t nano_class[1] = {{
@@ -286,6 +287,7 @@ static const H5C_class_t nano_class[1] = {{
NULL,
nano_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t micro_class[1] = {{
@@ -303,6 +305,7 @@ static const H5C_class_t micro_class[1] = {{
NULL,
micro_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t tiny_class[1] = {{
@@ -320,6 +323,7 @@ static const H5C_class_t tiny_class[1] = {{
NULL,
tiny_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t small_class[1] = {{
@@ -337,6 +341,7 @@ static const H5C_class_t small_class[1] = {{
NULL,
small_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t medium_class[1] = {{
@@ -354,6 +359,7 @@ static const H5C_class_t medium_class[1] = {{
NULL,
medium_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t large_class[1] = {{
@@ -371,6 +377,7 @@ static const H5C_class_t large_class[1] = {{
NULL,
large_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t huge_class[1] = {{
@@ -388,6 +395,7 @@ static const H5C_class_t huge_class[1] = {{
NULL,
huge_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t monster_class[1] = {{
@@ -405,6 +413,7 @@ static const H5C_class_t monster_class[1] = {{
NULL,
monster_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t variable_class[1] = {{
@@ -422,6 +431,7 @@ static const H5C_class_t variable_class[1] = {{
NULL,
variable_free_icr,
NULL,
+ NULL,
}};
static const H5C_class_t notify_class[1] = {{
@@ -439,6 +449,7 @@ static const H5C_class_t notify_class[1] = {{
notify_notify,
notify_free_icr,
NULL,
+ NULL,
}};
/* callback table declaration */
diff --git a/test/cache_image.c b/test/cache_image.c
index 59689a9..d4002c7 100644
--- a/test/cache_image.c
+++ b/test/cache_image.c
@@ -22,7 +22,6 @@
/* global variable declarations: */
-
const char *FILENAMES[] = {
"cache_image_test",
NULL
@@ -4820,7 +4819,9 @@ cache_image_smoke_check_5(hbool_t single_file_vfd)
/* 3) Construct a "zoo" in the above group, and validate it. */
if ( pass )
- create_zoo(file_id, process_group_name, min_group);
+ pass = create_zoo(file_id, process_group_name,
+ (zoo_config_t){.proc_num = min_group, .skip_varlen = false,
+ .skip_compact = false});
#if H5C_COLLECT_CACHE_STATS
if ( pass ) {
@@ -4891,8 +4892,11 @@ cache_image_smoke_check_5(hbool_t single_file_vfd)
/* 6) Validate the "zoo" created in the previous file open. */
- if ( pass )
- validate_zoo(file_id, process_group_name, max_group);
+ if ( pass ) {
+ pass = validate_zoo(file_id, process_group_name,
+ (zoo_config_t){.proc_num = max_group, .skip_varlen = false,
+ .skip_compact = false});
+ }
#if H5C_COLLECT_CACHE_STATS
if ( pass ) {
@@ -4932,8 +4936,11 @@ cache_image_smoke_check_5(hbool_t single_file_vfd)
/* 8) Construct a "zoo" in the above group, and validate it. */
- if ( pass )
- create_zoo(file_id, process_group_name, max_group);
+ if ( pass ) {
+ pass = create_zoo(file_id, process_group_name,
+ (zoo_config_t){.proc_num = max_group, .skip_varlen = false,
+ .skip_compact = false});
+ }
if ( show_progress )
HDfprintf(stdout, "%s:L4 cp = %d, max_group = %d, pass = %d.\n",
@@ -4994,7 +5001,9 @@ cache_image_smoke_check_5(hbool_t single_file_vfd)
i = min_group;
while(pass && i <= max_group) {
HDsprintf(process_group_name, "/process_%d", i);
- validate_zoo(file_id, process_group_name, i++);
+ pass = validate_zoo(file_id, process_group_name,
+ (zoo_config_t){.proc_num = i++, .skip_varlen = false,
+ .skip_compact = false});
}
#if H5C_COLLECT_CACHE_STATS
@@ -5047,7 +5056,9 @@ cache_image_smoke_check_5(hbool_t single_file_vfd)
while ( ( pass ) && ( i <= max_group ) ) {
HDsprintf(process_group_name, "/process_%d", i);
- validate_zoo(file_id, process_group_name, i++);
+ pass = validate_zoo(file_id, process_group_name,
+ (zoo_config_t){.proc_num = i++, .skip_varlen = false,
+ .skip_compact = false});
}
#if H5C_COLLECT_CACHE_STATS
@@ -5111,7 +5122,9 @@ cache_image_smoke_check_5(hbool_t single_file_vfd)
i = min_group;
while ( ( pass ) && ( i <= max_group ) ) {
HDsprintf(process_group_name, "/process_%d", i);
- validate_zoo(file_id, process_group_name, i++);
+ pass = validate_zoo(file_id, process_group_name,
+ (zoo_config_t){.proc_num = i++, .skip_varlen = false,
+ .skip_compact = false});
}
#if H5C_COLLECT_CACHE_STATS
diff --git a/test/cache_tagging.c b/test/cache_tagging.c
index cf0cd8d..ddd63e4 100644
--- a/test/cache_tagging.c
+++ b/test/cache_tagging.c
@@ -443,7 +443,7 @@ check_file_creation_tags(hid_t fcpl_id, int type)
hid_t fid = -1; /* File Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose test outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag = 0;
haddr_t sbe_tag = 0;
@@ -532,7 +532,7 @@ check_file_open_tags(hid_t fcpl, int type)
hid_t fid = -1; /* File Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag; /* Root Group Tag */
haddr_t sbe_tag; /* Sblock Extension Tag */
@@ -647,7 +647,7 @@ check_group_creation_tags(void)
hid_t gid = -1; /* Group Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag = HADDR_UNDEF; /* Root Group Tag */
haddr_t g_tag; /* Group Tag */
@@ -750,7 +750,7 @@ check_multi_group_creation_tags(void)
hid_t gid = -1; /* Group Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
char gname[16]; /* group name buffer */
int i = 0; /* iterator */
hid_t fapl = -1; /* File access prop list */
@@ -881,7 +881,7 @@ check_link_iteration_tags(void)
hid_t did = -1; /* Group Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
int i = 0; /* iterator */
haddr_t root_tag = 0; /* Root Group Tag Value */
char dsetname[500]; /* Name of dataset */
@@ -1003,7 +1003,7 @@ check_dense_attribute_tags(void)
hid_t dcpl = -1; /* Group Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
int i = 0; /* iterator */
hid_t fapl = -1; /* File access property list */
haddr_t d_tag = 0; /* Dataset tag value */
@@ -1187,7 +1187,7 @@ check_group_open_tags(void)
hid_t gid = -1; /* Group Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file output */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag = HADDR_UNDEF;
haddr_t g_tag;
@@ -1298,7 +1298,7 @@ check_attribute_creation_tags(hid_t fcpl, int type)
hid_t sid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag = 0; /* Root group tag */
haddr_t g_tag = 0;
@@ -1434,7 +1434,7 @@ check_attribute_open_tags(hid_t fcpl, int type)
hid_t sid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag = 0;
haddr_t g_tag = 0;
@@ -1573,7 +1573,7 @@ check_attribute_rename_tags(hid_t fcpl, int type)
hid_t sid = -1; /* Dataset Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
int *data = NULL; /* data buffer */
int i,j,k = 0; /* iterators */
hid_t fapl = -1; /* File access prop list */
@@ -1750,7 +1750,7 @@ check_attribute_delete_tags(hid_t fcpl, int type)
hid_t sid = -1; /* Dataset Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
int *data = NULL; /* data buffer */
int i,j,k = 0; /* iterators */
hid_t fapl = -1; /* File access prop list */
@@ -1918,7 +1918,7 @@ check_dataset_creation_tags(hid_t fcpl, int type)
hid_t sid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t dcpl = -1; /* dataset creation pl */
hsize_t cdims[2] = {1,1}; /* chunk dimensions */
int fillval = 0;
@@ -2053,7 +2053,7 @@ check_dataset_creation_earlyalloc_tags(hid_t fcpl, int type)
hid_t sid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t dcpl = -1; /* dataset creation pl */
hsize_t cdims[2] = {1,1}; /* chunk dimensions */
int fillval = 0;
@@ -2192,7 +2192,7 @@ check_dataset_open_tags(void)
hid_t sid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t dcpl = -1; /* dataset creation pl */
hsize_t cdims[2] = {1,1}; /* chunk dimensions */
int fillval = 0;
@@ -2320,7 +2320,7 @@ check_dataset_write_tags(void)
hid_t sid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t dcpl = -1; /* dataset creation pl */
hsize_t cdims[2] = {1,1}; /* chunk dimensions */
int fillval = 0;
@@ -2464,7 +2464,7 @@ check_attribute_write_tags(hid_t fcpl, int type)
hid_t sid = -1; /* Dataset Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
int *data = NULL; /* data buffer */
int i,j,k = 0; /* iterators */
hid_t fapl = -1; /* File access prop list */
@@ -2619,7 +2619,7 @@ check_dataset_read_tags(void)
hid_t sid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t dcpl = -1; /* dataset creation pl */
hsize_t cdims[2] = {1,1}; /* chunk dimensions */
int fillval = 0;
@@ -2758,7 +2758,7 @@ check_dataset_size_retrieval(void)
hid_t sid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t dcpl = -1; /* dataset creation pl */
hsize_t cdims[2] = {1,1}; /* chunk dimensions */
int fillval = 0;
@@ -2899,7 +2899,7 @@ check_dataset_extend_tags(void)
hid_t sid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t dcpl = -1; /* dataset creation pl */
hsize_t cdims[2] = {1,1}; /* chunk dimensions */
int fillval = 0;
@@ -3038,7 +3038,7 @@ check_object_info_tags(void)
hid_t gid = -1; /* Group Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file output */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag = HADDR_UNDEF;
haddr_t g_tag;
@@ -3152,7 +3152,7 @@ check_object_copy_tags(void)
hid_t gid = -1; /* Group Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file output */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag = HADDR_UNDEF;
haddr_t g_tag;
@@ -3275,7 +3275,7 @@ check_link_removal_tags(hid_t fcpl, int type)
hid_t gid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t dcpl = -1; /* dataset creation pl */
hsize_t cdims[2] = {1,1}; /* chunk dimensions */
int fillval = 0;
@@ -3436,7 +3436,7 @@ check_link_getname_tags(void)
hid_t gid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t dcpl = -1; /* dataset creation pl */
hsize_t cdims[2] = {1,1}; /* chunk dimensions */
int fillval = 0;
@@ -3587,7 +3587,7 @@ check_external_link_creation_tags(void)
hid_t gid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag = 0;
@@ -3695,7 +3695,7 @@ check_external_link_open_tags(void)
hid_t xid = -1; /* Dataspace Identifier */
#ifndef NDEBUG
int verbose = FALSE; /* verbose file outout */
-#endif /* NDEBUG */ /* end debugging functions */
+#endif
H5O_native_info_t ninfo; /* Native object info struct */
hid_t fapl = -1; /* File access prop list */
haddr_t root_tag = 0;
@@ -3900,10 +3900,9 @@ check_invalid_tag_application(void)
#if H5C_DO_TAGGING_SANITY_CHECKS
error:
if(api_ctx_pushed) H5CX_pop();
-
- return 1;
#endif /* H5C_DO_TAGGING_SANITY_CHECKS */
+ return 1;
} /* check_invalid_tag_application */
diff --git a/test/earray.c b/test/earray.c
index 6597afd..82c482d 100644
--- a/test/earray.c
+++ b/test/earray.c
@@ -2106,7 +2106,7 @@ test_set_elmts(hid_t fapl, H5EA_create_t *cparam, earray_test_param_t *tparam,
/*
* Display testing message
*/
- TESTING(test_str);
+ TESTING("%s", test_str);
/* Create file & retrieve pointer to internal file object */
if(create_file(H5F_ACC_TRUNC, fapl, &file, &f) < 0)
@@ -2280,7 +2280,7 @@ test_skip_elmts(hid_t fapl, H5EA_create_t *cparam, earray_test_param_t *tparam,
/*
* Display testing message
*/
- TESTING(test_str);
+ TESTING("%s", test_str);
/* Create file & retrieve pointer to internal file object */
if(create_file(H5F_ACC_TRUNC, fapl, &file, &f) < 0)
diff --git a/test/enc_dec_plist_cross_platform.c b/test/enc_dec_plist_cross_platform.c
index f54f675..c4dec9f 100644
--- a/test/enc_dec_plist_cross_platform.c
+++ b/test/enc_dec_plist_cross_platform.c
@@ -156,73 +156,70 @@ error:
return 1;
}
+static hid_t
+read_and_decode_plist_file(const char *filename_prefix, unsigned config, char *filename, size_t filename_len)
+{
+ int fd;
+ size_t size;
+ const char *testfile;
+ void *buf = NULL;
+ hid_t plist = H5I_INVALID_HID;
+
+ /* Generate filename from prefix and configuration word */
+ if(HDsnprintf(filename, filename_len, "%s%s%s", filename_prefix,
+ config & CONFIG_64 ? "64" : "32",
+ config & CONFIG_LE ? "le" : "be") < 0)
+ TEST_ERROR
+
+ /* Read file 1 */
+ testfile = H5_get_srcdir_filename(filename);
+ if((fd = HDopen(testfile, O_RDONLY)) < 0)
+ TEST_ERROR
+ size = (size_t)HDlseek(fd, 0, SEEK_END);
+ HDlseek(fd, 0, SEEK_SET);
+ buf = HDmalloc(size);
+ if(HDread(fd, buf, size) < 0)
+ TEST_ERROR
+ HDclose(fd);
+
+ /* Decode property lists */
+ if((plist = H5Pdecode(buf)) < 0)
+ FAIL_STACK_ERROR
+error:
+ if (buf != NULL)
+ HDfree(buf);
+ return plist;
+}
+
static int
test_plists(const char *filename_prefix)
{
- unsigned config_1, config_2;
- int fd_1, fd_2;
- size_t size_1 = 0, size_2 = 0;
- void *buf_1 = NULL, *buf_2 = NULL;
- hid_t plist_1, plist_2;
- char filename[1024];
- const char *testfile;
+ int i;
+ unsigned config[2];
+ hid_t plist[2];
+ char filename[2][1024];
/* Iterate over all combinations of configurations */
- for(config_1 = 0; config_1 < (NCONFIG - 1); config_1++)
- for(config_2 = config_1 + 1; config_2 < NCONFIG; config_2++) {
- /* Generate filename for file 1 */
- if(HDsnprintf(filename, sizeof(filename), "%s%s%s", filename_prefix,
- config_1 & CONFIG_64 ? "64" : "32",
- config_1 & CONFIG_LE ? "le" : "be") < 0)
- TEST_ERROR
-
- /* Read file 1 */
- testfile = H5_get_srcdir_filename(filename);
- if((fd_1 = HDopen(testfile, O_RDONLY)) < 0)
- TEST_ERROR
- size_1 = (size_t)HDlseek(fd_1, (HDoff_t)0, SEEK_END);
- HDlseek(fd_1, (HDoff_t)0, SEEK_SET);
- buf_1 = (void *)HDmalloc(size_1);
- if(HDread(fd_1, buf_1, size_1) < 0)
- TEST_ERROR
- HDclose(fd_1);
-
- /* Generate filename for file 2 */
- if(HDsnprintf(filename, sizeof(filename), "%s%s%s", filename_prefix,
- config_2 & CONFIG_64 ? "64" : "32",
- config_2 & CONFIG_LE ? "le" : "be") < 0)
- TEST_ERROR
-
- /* Read file 1 */
- testfile = H5_get_srcdir_filename(filename);
- if((fd_2 = HDopen(testfile, O_RDONLY)) < 0)
- TEST_ERROR
- size_2 = (size_t)HDlseek(fd_2, (HDoff_t)0, SEEK_END);
- HDlseek(fd_2, (HDoff_t)0, SEEK_SET);
- buf_2 = (void *)HDmalloc(size_2);
- if(HDread(fd_2, buf_2, size_2) < 0)
- TEST_ERROR
- HDclose(fd_2);
-
- /* Decode property lists */
- if((plist_1 = H5Pdecode(buf_1)) < 0)
- FAIL_STACK_ERROR
- if((plist_2 = H5Pdecode(buf_2)) < 0)
- FAIL_STACK_ERROR
+ for(config[0] = 0; config[0] < (NCONFIG - 1); config[0]++) {
+ for(config[1] = config[0] + 1; config[1] < NCONFIG; config[1]++) {
+ for (i = 0; i < 2; i++) {
+ plist[i] = read_and_decode_plist_file(filename_prefix,
+ config[i], filename[i], sizeof(filename[i]));
+ if (plist[i] == H5I_INVALID_HID)
+ goto error;
+ }
/* Compare decoded property lists */
- if(!H5Pequal(plist_1, plist_2))
- FAIL_PUTS_ERROR("PLIST encoding/decoding comparison failed\n")
-
- /* Close */
- if((H5Pclose(plist_1)) < 0)
- FAIL_STACK_ERROR
- if((H5Pclose(plist_2)) < 0)
- FAIL_STACK_ERROR
-
- HDfree(buf_1);
- HDfree(buf_2);
- } /* end for */
+ if(!H5Pequal(plist[0], plist[1]))
+ FAIL_PRINTF_ERROR("PLIST encoding/decoding comparison failed, "
+ "%s != %s\n", filename[0], filename[1])
+
+ for (i = 0; i < 2; i++) {
+ if((H5Pclose(plist[i])) < 0)
+ FAIL_STACK_ERROR
+ }
+ }
+ }
return 1;
diff --git a/test/err_compat.c b/test/err_compat.c
index eb86760..bd317bf 100644
--- a/test/err_compat.c
+++ b/test/err_compat.c
@@ -241,7 +241,7 @@ test_error1(void)
herr_t ret;
TESTING("error API H5Eset/get_auto");
- HDfprintf(stderr, "\n");
+ printf("\n");
/* Create the data space */
dims[0] = DIM0;
@@ -338,6 +338,7 @@ test_error1(void)
if(dataset >= 0)
TEST_ERROR;
+ fprintf(stderr, "\n");
return 0;
error:
@@ -370,7 +371,7 @@ test_error2(hid_t file)
const char *FUNC_test_error="test_error2";
TESTING("error API based on data I/O");
- HDfprintf(stderr, "\n");
+ printf("\n");
/* Create the data space */
dims[0] = DIM0;
@@ -504,6 +505,7 @@ main(void)
/* Print out the errors on stack */
dump_error();
+ fprintf(stderr, "\n");
/* Empty error stack */
H5Eclear1();
diff --git a/test/farray.c b/test/farray.c
index b647b51..2cc79a1 100644
--- a/test/farray.c
+++ b/test/farray.c
@@ -1376,7 +1376,7 @@ test_set_elmts(hid_t fapl, H5FA_create_t *cparam, farray_test_param_t *tparam,
/*
* Display testing message
*/
- TESTING(test_str);
+ TESTING("%s", test_str);
/* Create file & retrieve pointer to internal file object */
if(create_file(fapl, &file, &f) < 0)
@@ -1531,7 +1531,7 @@ test_skip_elmts(hid_t fapl, H5FA_create_t *cparam, farray_test_param_t *tparam,
/*
* Display testing message
*/
- TESTING(test_str);
+ TESTING("%s", test_str);
/* Create file & retrieve pointer to internal file object */
if(create_file(fapl, &file, &f) < 0)
diff --git a/test/fheap.c b/test/fheap.c
index b1a0db0..b6d24f7 100644
--- a/test/fheap.c
+++ b/test/fheap.c
@@ -558,11 +558,8 @@ begin_test(fheap_test_param_t *tparam, const char *base_desc,
*/
del_str = get_del_string(tparam);
HDassert(del_str);
- test_desc = (char *)H5MM_malloc(HDstrlen(del_str) + HDstrlen(base_desc));
- HDsprintf(test_desc, base_desc, del_str);
- TESTING(test_desc);
+ TESTING(base_desc, del_str);
H5MM_xfree(del_str);
- H5MM_xfree(test_desc);
/* Initialize the heap ID structure */
HDmemset(keep_ids, 0, sizeof(fheap_heap_ids_t));
diff --git a/test/file_image.c b/test/file_image.c
index 0d4873a..bb16e6d 100644
--- a/test/file_image.c
+++ b/test/file_image.c
@@ -710,7 +710,7 @@ test_get_file_image(const char * test_banner,
hid_t fcpl = -1;
herr_t ret;
- TESTING(test_banner);
+ TESTING("%s", test_banner);
/* set flag if we are dealing with a family file */
driver = H5Pget_driver(fapl);
diff --git a/test/gen_plist.c b/test/gen_plist.c
index b6f5597..7f64aa4 100644
--- a/test/gen_plist.c
+++ b/test/gen_plist.c
@@ -469,7 +469,7 @@ encode_plist(hid_t plist_id, int little_endian, int word_length, const char *fil
if((ret = H5Pencode2(plist_id, NULL, &temp_size, H5P_DEFAULT)) < 0)
HDassert(ret > 0);
- temp_buf = (void *)HDmalloc(temp_size);
+ temp_buf = HDcalloc(1, temp_size);
HDassert(temp_buf);
if((ret = H5Pencode2(plist_id, temp_buf, &temp_size, H5P_DEFAULT)) < 0)
diff --git a/test/genall5.c b/test/genall5.c
index 3f55930..0a64f37 100644
--- a/test/genall5.c
+++ b/test/genall5.c
@@ -19,6 +19,8 @@
* of the same name.
*/
+#include <err.h>
+
#include "cache_common.h"
#include "genall5.h"
@@ -27,6 +29,70 @@
#define DSET_CHUNK_DIMS 1024
#define DSET_COMPACT_DIMS 4096
+typedef enum phase {PHASE_CREATE, PHASE_VALIDATE, PHASE_DELETE,
+ PHASE_VALIDATE_DELETION} phase_t;
+
+static bool rm_ns_grp_0(hid_t, const char *);
+static bool rm_ns_grp_c(hid_t, const char *, unsigned);
+static bool rm_ns_grp_d(hid_t, const char *, unsigned);
+static bool rm_os_grp_0(hid_t, const char *);
+static bool rm_os_grp_n(hid_t, const char *, int, unsigned);
+static bool rm_ds_ctg_i(hid_t, const char *, hbool_t);
+static bool rm_ds_chk_i(hid_t, const char *, hbool_t);
+static bool rm_ds_cpt_i(hid_t, const char *, hbool_t);
+static bool rm_ds_ctg_v(hid_t, const char *, hbool_t);
+
+static bool missing_ns_grp_0(hid_t, const char *);
+static bool missing_ns_grp_c(hid_t, const char *, unsigned);
+static bool missing_ns_grp_d(hid_t, const char *, unsigned);
+static bool missing_os_grp_0(hid_t, const char *);
+static bool missing_os_grp_n(hid_t, const char *, int, unsigned);
+static bool missing_ds_ctg_i(hid_t, const char *, hbool_t);
+static bool missing_ds_chk_i(hid_t, const char *, hbool_t);
+static bool missing_ds_cpt_i(hid_t, const char *, hbool_t);
+static bool missing_ds_ctg_v(hid_t, const char *, hbool_t);
+
+#define FN_ITEM_DEFN(__name, ...) \
+ typedef bool (*__name##fn_t)(__VA_ARGS__); \
+ static const __name##fn_t __name##_fntbl[] = \
+ {__name, vrfy_##__name, rm_##__name, missing_##__name}
+
+FN_ITEM_DEFN(ns_grp_0, hid_t, const char *);
+FN_ITEM_DEFN(ns_grp_c, hid_t, const char *, unsigned);
+FN_ITEM_DEFN(ns_grp_d, hid_t, const char *, unsigned);
+FN_ITEM_DEFN(os_grp_0, hid_t, const char *);
+FN_ITEM_DEFN(os_grp_n, hid_t, const char *, int, unsigned);
+FN_ITEM_DEFN(ds_ctg_i, hid_t, const char *, bool);
+FN_ITEM_DEFN(ds_chk_i, hid_t, const char *, bool);
+FN_ITEM_DEFN(ds_cpt_i, hid_t, const char *, bool);
+FN_ITEM_DEFN(ds_ctg_v, hid_t, const char *, bool);
+
+#undef FN_ITEM_DEFN
+
+static bool
+file_has_no_path(hid_t fid, const char *path)
+{
+ switch (H5Lexists(fid, path, H5P_DEFAULT)) {
+ case FALSE:
+ return true;
+ case TRUE:
+ failure_mssg = "H5Lexists unexpectedly true.";
+ return false;
+ default:
+ failure_mssg = "H5Lexists unexpectedly failed.";
+ return false;
+ }
+}
+
+static bool
+remove_from_file_path(hid_t fid, const char *path)
+{
+ if (H5Ldelete(fid, path, H5P_DEFAULT) < 0) {
+ failure_mssg = "H5Ldelete failed.";
+ return false;
+ }
+ return true;
+}
/*-------------------------------------------------------------------------
* Function: ns_grp_0
@@ -34,12 +100,10 @@
* Purpose: Create an empty "new style" group at the specified location
* in the specified file.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
@@ -47,65 +111,62 @@
*-------------------------------------------------------------------------
*/
-void
-ns_grp_0(hid_t fid, const char *group_name) {
+static bool
+missing_ns_grp_0(hid_t fid, const char *group_name)
+{
+ return file_has_no_path(fid, group_name);
+}
+
+static bool
+rm_ns_grp_0(hid_t fid, const char *group_name)
+{
+ return remove_from_file_path(fid, group_name);
+}
+
+bool
+ns_grp_0(hid_t fid, const char *group_name)
+{
hid_t gid = -1;
hid_t gcpl = -1;
herr_t ret;
- if (pass) {
- gcpl = H5Pcreate(H5P_GROUP_CREATE);
+ gcpl = H5Pcreate(H5P_GROUP_CREATE);
- if (gcpl <= 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_0: H5Pcreate() failed";
- }
- HDassert(gcpl > 0);
+ if (gcpl <= 0) {
+ failure_mssg = "ns_grp_0: H5Pcreate() failed";
+ return false;
}
- if (pass) {
- ret = H5Pset_link_creation_order(gcpl, H5P_CRT_ORDER_TRACKED);
+ ret = H5Pset_link_creation_order(gcpl, H5P_CRT_ORDER_TRACKED);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_0: H5Pset_link_creation_order() failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_0: H5Pset_link_creation_order() failed";
+ return false;
}
- if (pass) {
- gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, gcpl, H5P_DEFAULT);
+ gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, gcpl, H5P_DEFAULT);
- if (gid <= 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_0: H5Gcreate2() failed";
- }
- HDassert(gid > 0);
+ if (gid <= 0) {
+ failure_mssg = "ns_grp_0: H5Gcreate2() failed";
+ return false;
}
- if (pass) {
- ret = H5Pclose(gcpl);
+ ret = H5Pclose(gcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_0: H5Pclose(gcpl) failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_0: H5Pclose(gcpl) failed";
+ return false;
}
- if (pass) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_0: H5Gclose(gid) failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_0: H5Gclose(gid) failed";
+ return false;
}
- return;
-
-} /* ns_grp_0 */
+ return true;
+}
/*-------------------------------------------------------------------------
@@ -114,12 +175,10 @@ ns_grp_0(hid_t fid, const char *group_name) {
* Purpose: verify an empty "new style" group at the specified location
* in the specified file.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
@@ -127,103 +186,76 @@ ns_grp_0(hid_t fid, const char *group_name) {
*-------------------------------------------------------------------------
*/
-void vrfy_ns_grp_0(hid_t fid, const char *group_name) {
+bool
+vrfy_ns_grp_0(hid_t fid, const char *group_name)
+{
hid_t gid = -1;
hid_t gcpl = -1;
H5G_info_t grp_info;
unsigned crt_order_flags = 0;
herr_t ret;
- if (pass) {
- gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
+ gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
- if (gid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_0: H5Gopen2() failed";
- }
- HDassert(gid > 0);
+ if (gid <= 0) {
+ failure_mssg = "vrfy_ns_grp_0: H5Gopen2() failed";
+ return false;
}
- if (pass) {
- gcpl = H5Gget_create_plist(gid);
+ gcpl = H5Gget_create_plist(gid);
- if (gcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_0: H5Gget_create_plist() failed";
- }
- HDassert(gcpl > 0);
+ if (gcpl <= 0) {
+ failure_mssg = "vrfy_ns_grp_0: H5Gget_create_plist() failed";
+ return false;
}
- if (pass) {
- ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
+ ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
- if (gcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_0: H5Pget_link_creation_order() failed";
- }
- else if ( H5P_CRT_ORDER_TRACKED != crt_order_flags) {
- pass = FALSE;
- failure_mssg =
- "vrfy_ns_grp_0: H5P_CRT_ORDER_TRACKED != crt_order_flags";
- }
- HDassert(ret >= 0);
- HDassert(H5P_CRT_ORDER_TRACKED == crt_order_flags);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_0: H5Pget_link_creation_order() failed";
+ return false;
+ } else if ( H5P_CRT_ORDER_TRACKED != crt_order_flags) {
+ failure_mssg =
+ "vrfy_ns_grp_0: H5P_CRT_ORDER_TRACKED != crt_order_flags";
+ return false;
}
- if (pass) {
- ret = H5Pclose(gcpl);
+ ret = H5Pclose(gcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_0: H5Pclose() failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_0: H5Pclose() failed";
+ return false;
}
- if (pass) {
- HDmemset(&grp_info, 0, sizeof(grp_info));
- ret = H5Gget_info(gid, &grp_info);
-
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_0: H5Gget_info() failed";
- }
- else if (H5G_STORAGE_TYPE_COMPACT != grp_info.storage_type) {
- pass = FALSE;
- failure_mssg =
- "vrfy_ns_grp_0: H5G_STORAGE_TYPE_COMPACT != grp_info.storage_type";
- }
- else if (0 != grp_info.nlinks) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_0: 0 != grp_info.nlinks";
- }
- else if (0 != grp_info.max_corder) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_0: 0 != grp_info.max_corder";
- }
- else if ( FALSE != grp_info.mounted) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_0: FALSE != grp_info.mounted";
- }
+ HDmemset(&grp_info, 0, sizeof(grp_info));
+ ret = H5Gget_info(gid, &grp_info);
- HDassert(ret >= 0);
- HDassert(H5G_STORAGE_TYPE_COMPACT == grp_info.storage_type);
- HDassert(0 == grp_info.nlinks);
- HDassert(0 == grp_info.max_corder);
- HDassert(false == grp_info.mounted);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_0: H5Gget_info() failed";
+ return false;
+ } else if (H5G_STORAGE_TYPE_COMPACT != grp_info.storage_type) {
+ failure_mssg =
+ "vrfy_ns_grp_0: H5G_STORAGE_TYPE_COMPACT != grp_info.storage_type";
+ return false;
+ } else if (0 != grp_info.nlinks) {
+ failure_mssg = "vrfy_ns_grp_0: 0 != grp_info.nlinks";
+ return false;
+ } else if (0 != grp_info.max_corder) {
+ failure_mssg = "vrfy_ns_grp_0: 0 != grp_info.max_corder";
+ return false;
+ } else if ( FALSE != grp_info.mounted) {
+ failure_mssg = "vrfy_ns_grp_0: FALSE != grp_info.mounted";
+ return false;
}
- if (pass) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_0: H5Gclose() failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_0: H5Gclose() failed";
+ return false;
}
- return;
+ return true;
} /* vrfy_ns_grp_0() */
@@ -233,12 +265,10 @@ void vrfy_ns_grp_0(hid_t fid, const char *group_name) {
* Purpose: Create a compact "new style" group, with 'nlinks'
* soft/hard/external links in it in the specified file.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
@@ -246,127 +276,111 @@ void vrfy_ns_grp_0(hid_t fid, const char *group_name) {
*-------------------------------------------------------------------------
*/
-void
-ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks) {
+static bool
+missing_ns_grp_c(hid_t fid, const char *group_name,
+ unsigned H5_ATTR_UNUSED nlinks)
+{
+ return file_has_no_path(fid, group_name);
+}
+
+static bool
+rm_ns_grp_c(hid_t fid, const char *group_name, unsigned H5_ATTR_UNUSED nlinks)
+{
+ return remove_from_file_path(fid, group_name);
+}
+
+bool
+ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks)
+{
hid_t gid = -1;
hid_t gcpl = -1;
unsigned max_compact;
unsigned u;
herr_t ret;
- if (pass) {
- gcpl = H5Pcreate(H5P_GROUP_CREATE);
+ gcpl = H5Pcreate(H5P_GROUP_CREATE);
- if (gcpl <= 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_c: H5Pcreate(H5P_GROUP_CREATE) failed";
- }
- HDassert(gcpl > 0);
+ if (gcpl <= 0) {
+ failure_mssg = "ns_grp_c: H5Pcreate(H5P_GROUP_CREATE) failed";
+ return false;
}
- if (pass) {
- ret = H5Pset_link_creation_order(gcpl, H5P_CRT_ORDER_TRACKED);
+ ret = H5Pset_link_creation_order(gcpl, H5P_CRT_ORDER_TRACKED);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_c: H5Pset_link_creation_order() failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_c: H5Pset_link_creation_order() failed";
+ return false;
}
- if (pass) {
- gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, gcpl, H5P_DEFAULT);
+ gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, gcpl, H5P_DEFAULT);
- if (gid <= 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_c: H5Gcreate2() failed";
- }
- HDassert(gid > 0);
+ if (gid <= 0) {
+ failure_mssg = "ns_grp_c: H5Gcreate2() failed";
+ return false;
}
- if (pass) {
- max_compact = 0;
- ret = H5Pget_link_phase_change(gcpl, &max_compact, NULL);
-
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_c: H5Pget_link_phase_change() failed";
- }
- else if (nlinks <= 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_c: nlinks <= 0";
- }
- else if (nlinks >= max_compact) {
- pass = FALSE;
- failure_mssg = "ns_grp_c: nlinks >= max_compact";
- }
+ max_compact = 0;
+ ret = H5Pget_link_phase_change(gcpl, &max_compact, NULL);
- HDassert(ret >= 0);
- HDassert(nlinks > 0);
- HDassert(nlinks < max_compact);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_c: H5Pget_link_phase_change() failed";
+ return false;
+ } else if (nlinks <= 0) {
+ failure_mssg = "ns_grp_c: nlinks <= 0";
+ return false;
+ } else if (nlinks >= max_compact) {
+ failure_mssg = "ns_grp_c: nlinks >= max_compact";
+ return false;
}
- u = 0;
- while ((pass) && (u < nlinks)) {
+ for (u = 0; u < nlinks; u++) {
char linkname[16];
HDsprintf(linkname, "%u", u);
if (0 == (u % 3)) {
- ret = H5Lcreate_soft(group_name, gid, linkname, H5P_DEFAULT, H5P_DEFAULT);
+ ret = H5Lcreate_soft(group_name, gid, linkname, H5P_DEFAULT,
+ H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ns_grp_c: H5Lcreate_soft() failed";
+ return false;
}
- HDassert(ret >= 0);
- } /* end if */
- else if (1 == (u % 3)) {
+ } else if (1 == (u % 3)) {
ret = H5Lcreate_hard(fid, "/", gid, linkname, H5P_DEFAULT,
H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ns_grp_c: H5Lcreate_hard() failed";
+ return false;
}
- HDassert(ret >= 0);
- } /* end else-if */
- else {
+ } else {
HDassert(2 == (u % 3));
ret = H5Lcreate_external("external.h5", "/ext", gid, linkname,
H5P_DEFAULT, H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ns_grp_c: H5Lcreate_external() failed";
+ return false;
}
- HDassert(ret >= 0);
- } /* end else */
-
- u++;
- } /* end while() */
+ }
+ }
- if (pass) {
- ret = H5Pclose(gcpl);
+ ret = H5Pclose(gcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_c: H5Pclose(gcpl) failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_c: H5Pclose(gcpl) failed";
+ return false;
}
- if (pass) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_c: H5Gclose(gid) failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_c: H5Gclose(gid) failed";
+ return false;
}
- return;
+ return true;
} /* ns_grp_c() */
@@ -377,12 +391,10 @@ ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks) {
* Purpose: Verify a compact "new style" group, with 'nlinks'
* soft/hard/external links in it in the specified file.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
@@ -390,8 +402,9 @@ ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks) {
*-------------------------------------------------------------------------
*/
-void
-vrfy_ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks) {
+bool
+vrfy_ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks)
+{
hid_t gid = -1;
hid_t gcpl = -1;
H5G_info_t grp_info;
@@ -399,86 +412,60 @@ vrfy_ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks) {
unsigned u;
herr_t ret;
- if (pass) {
- gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
+ gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
- if (gid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: H5Gopen2() failed";
- }
- HDassert(gid > 0);
+ if (gid <= 0) {
+ failure_mssg = "vrfy_ns_grp_c: H5Gopen2() failed";
+ return false;
}
- if (pass) {
- gcpl = H5Gget_create_plist(gid);
+ gcpl = H5Gget_create_plist(gid);
- if (gcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: H5Gget_create_plist(gid) failed";
- }
- HDassert(gcpl > 0);
+ if (gcpl <= 0) {
+ failure_mssg = "vrfy_ns_grp_c: H5Gget_create_plist(gid) failed";
+ return false;
}
- if (pass) {
- ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
+ ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: H5Pget_link_creation_order() failed";
- }
- else if ( H5P_CRT_ORDER_TRACKED != crt_order_flags) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: H5P_CRT_ORDER_TRACKED != crt_order_flags";
- }
- HDassert(ret >= 0);
- HDassert(H5P_CRT_ORDER_TRACKED == crt_order_flags);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_c: H5Pget_link_creation_order() failed";
+ return false;
+ } else if ( H5P_CRT_ORDER_TRACKED != crt_order_flags) {
+ failure_mssg =
+ "vrfy_ns_grp_c: H5P_CRT_ORDER_TRACKED != crt_order_flags";
+ return false;
}
- if (pass) {
- ret = H5Pclose(gcpl);
+ ret = H5Pclose(gcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: H5Pclose() failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_c: H5Pclose() failed";
+ return false;
}
- if (pass) {
- HDmemset(&grp_info, 0, sizeof(grp_info));
- ret = H5Gget_info(gid, &grp_info);
+ HDmemset(&grp_info, 0, sizeof(grp_info));
+ ret = H5Gget_info(gid, &grp_info);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: H5Gget_info() failed";
- }
- else if (H5G_STORAGE_TYPE_COMPACT != grp_info.storage_type) {
- pass = FALSE;
- failure_mssg =
- "vrfy_ns_grp_c: H5G_STORAGE_TYPE_COMPACT != grp_info.storage_type";
- }
- else if (nlinks != grp_info.nlinks) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: nlinks != grp_info.nlinks";
- }
- else if (nlinks != grp_info.max_corder) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: nlinks != grp_info.max_corder";
- }
- else if ( FALSE != grp_info.mounted) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: FALSE != grp_info.mounted";
- }
-
- HDassert(ret >= 0);
- HDassert(H5G_STORAGE_TYPE_COMPACT == grp_info.storage_type);
- HDassert(nlinks == grp_info.nlinks);
- HDassert(nlinks == grp_info.max_corder);
- HDassert(false == grp_info.mounted);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_c: H5Gget_info() failed";
+ return false;
+ } else if (H5G_STORAGE_TYPE_COMPACT != grp_info.storage_type) {
+ failure_mssg =
+ "vrfy_ns_grp_c: H5G_STORAGE_TYPE_COMPACT != grp_info.storage_type";
+ return false;
+ } else if (nlinks != grp_info.nlinks) {
+ failure_mssg = "vrfy_ns_grp_c: nlinks != grp_info.nlinks";
+ return false;
+ } else if (nlinks != grp_info.max_corder) {
+ failure_mssg = "vrfy_ns_grp_c: nlinks != grp_info.max_corder";
+ return false;
+ } else if ( FALSE != grp_info.mounted) {
+ failure_mssg = "vrfy_ns_grp_c: FALSE != grp_info.mounted";
+ return false;
}
- u = 0;
- while ((pass) && (u < nlinks)) {
+ for (u = 0; u < nlinks; u++) {
H5L_info2_t lnk_info;
char linkname[16];
htri_t link_exists;
@@ -487,104 +474,82 @@ vrfy_ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks) {
link_exists = H5Lexists(gid, linkname, H5P_DEFAULT);
if (link_exists < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: H5Lexists() failed";
+ return false;
}
- HDassert(link_exists >= 0);
HDmemset(&lnk_info, 0, sizeof(grp_info));
ret = H5Lget_info2(gid, linkname, &lnk_info, H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: H5Lget_info() failed";
- }
- else if ( TRUE != lnk_info.corder_valid) {
- pass = FALSE;
+ return false;
+ } else if ( TRUE != lnk_info.corder_valid) {
failure_mssg = "vrfy_ns_grp_c: TRUE != lnk_info.corder_valid";
- }
- else if (u != lnk_info.corder) {
- pass = FALSE;
+ return false;
+ } else if (u != lnk_info.corder) {
failure_mssg = "vrfy_ns_grp_c: u != lnk_info.corder";
- }
- else if (H5T_CSET_ASCII != lnk_info.cset) {
- pass = FALSE;
+ return false;
+ } else if (H5T_CSET_ASCII != lnk_info.cset) {
failure_mssg = "vrfy_ns_grp_c: H5T_CSET_ASCII != lnk_info.cset";
+ return false;
}
- HDassert(ret >= 0);
- HDassert(true == lnk_info.corder_valid);
- HDassert(u == lnk_info.corder);
- HDassert(H5T_CSET_ASCII == lnk_info.cset);
if (0 == (u % 3)) {
char *slinkval;
if (H5L_TYPE_SOFT != lnk_info.type) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: H5L_TYPE_SOFT != lnk_info.type";
+ return false;
}
else if ((HDstrlen(group_name) + 1) != lnk_info.u.val_size) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: (HDstrlen(group_name) + 1) != lnk_info.u.val_size";
+ return false;
}
- HDassert(H5L_TYPE_SOFT == lnk_info.type);
- HDassert((HDstrlen(group_name) + 1) == lnk_info.u.val_size);
- slinkval = (char *) HDmalloc(lnk_info.u.val_size);
+ slinkval = HDmalloc(lnk_info.u.val_size);
if (!slinkval) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: HDmalloc of slinkval failed";
+ return false;
}
- HDassert(slinkval);
ret = H5Lget_val(gid, linkname, slinkval, lnk_info.u.val_size,
H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: H5Lget_val() failed";
- }
- else if (0 != HDstrcmp(slinkval, group_name)) {
- pass = FALSE;
+ HDfree(slinkval);
+ return false;
+ } else if (0 != HDstrcmp(slinkval, group_name)) {
failure_mssg = "vrfy_ns_grp_c: 0 != HDstrcmp(slinkval, group_name)";
+ HDfree(slinkval);
+ return false;
}
- HDassert(ret >= 0);
- HDassert(0 == HDstrcmp(slinkval, group_name));
HDfree(slinkval);
- } /* end if */
- else if (1 == (u % 3)) {
+ } else if (1 == (u % 3)) {
H5O_info2_t root_oinfo;
int token_cmp = 0;
if (H5L_TYPE_HARD != lnk_info.type) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: H5L_TYPE_HARD != lnk_info.type";
+ return false;
}
- HDassert(H5L_TYPE_HARD == lnk_info.type);
HDmemset(&root_oinfo, 0, sizeof(root_oinfo));
ret = H5Oget_info3(fid, &root_oinfo, H5O_INFO_BASIC);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: H5Oget_info() failed.";
+ return false;
+ } else if(H5Otoken_cmp(fid, &root_oinfo.token, &lnk_info.u.token, &token_cmp) < 0) {
+ failure_mssg = "vrfy_ns_grp_c: H5Otoken_cmp() failed.";
+ return false;
+ } else if (token_cmp) {
+ failure_mssg = "vrfy_ns_grp_c: root_oinfo.token != lnk_info.u.token";
+ return false;
}
- else {
- if(H5Otoken_cmp(fid, &root_oinfo.token, &lnk_info.u.token, &token_cmp) < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: H5Otoken_cmp() failed.";
- }
-
- if (token_cmp) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: root_oinfo.token != lnk_info.u.token";
- }
- }
- HDassert(ret >= 0);
- HDassert(!token_cmp);
- } /* end else-if */
- else {
+ } else {
void *elinkval;
const char *file = NULL;
const char *path = NULL;
@@ -592,61 +557,53 @@ vrfy_ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks) {
HDassert(2 == (u % 3));
if (H5L_TYPE_EXTERNAL != lnk_info.type) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: H5L_TYPE_EXTERNAL != lnk_info.type";
+ failure_mssg =
+ "vrfy_ns_grp_c: H5L_TYPE_EXTERNAL != lnk_info.type";
+ return false;
}
- HDassert(H5L_TYPE_EXTERNAL == lnk_info.type);
elinkval = HDmalloc(lnk_info.u.val_size);
if (!elinkval) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: HDmalloc of elinkval failed.";
+ return false;
}
- HDassert(elinkval);
ret = H5Lget_val(gid, linkname, elinkval, lnk_info.u.val_size,
H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: H5Lget_val() failed.";
+ return false;
}
- HDassert(ret >= 0);
- ret = H5Lunpack_elink_val(elinkval, lnk_info.u.val_size, NULL, &file, &path);
+ ret = H5Lunpack_elink_val(elinkval, lnk_info.u.val_size, NULL,
+ &file, &path);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_c: H5Lunpack_elink_val() failed.";
- }
- else if (0 != HDstrcmp(file, "external.h5")) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: 0 != HDstrcmp(file, \"external.h5\")";
- }
- else if (0 != HDstrcmp(path, "/ext")) {
- pass = FALSE;
+ HDfree(elinkval);
+ return false;
+ } else if (0 != HDstrcmp(file, "external.h5")) {
+ failure_mssg =
+ "vrfy_ns_grp_c: 0 != HDstrcmp(file, \"external.h5\")";
+ HDfree(elinkval);
+ return false;
+ } else if (0 != HDstrcmp(path, "/ext")) {
failure_mssg = "vrfy_ns_grp_c: 0 != HDstrcmp(path, \"/ext\")";
+ HDfree(elinkval);
+ return false;
}
- HDassert(ret >= 0);
- HDassert(0 == HDstrcmp(file, "external.h5"));
- HDassert(0 == HDstrcmp(path, "/ext"));
-
HDfree(elinkval);
- } /* end else */
-
- u++;
- } /* end while */
+ }
+ }
- if (pass) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_c: H5Gclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_c: H5Gclose() failed.";
+ return false;
}
- return;
+ return true;
} /* vrfy_ns_grp_c() */
@@ -656,12 +613,10 @@ vrfy_ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks) {
* Purpose: Create a dense "new style" group, with 'nlinks'
* (soft/hard/external) links in it in the specified file.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
@@ -669,62 +624,62 @@ vrfy_ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks) {
*-------------------------------------------------------------------------
*/
-void
-ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
+static bool
+missing_ns_grp_d(hid_t fid, const char *group_name,
+ unsigned H5_ATTR_UNUSED nlinks)
+{
+ return file_has_no_path(fid, group_name);
+}
+
+static bool
+rm_ns_grp_d(hid_t fid, const char *group_name, unsigned H5_ATTR_UNUSED nlinks)
+{
+ return remove_from_file_path(fid, group_name);
+}
+
+bool
+ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks)
+{
hid_t gid = -1;
hid_t gcpl = -1;
unsigned max_compact;
unsigned u;
herr_t ret;
- if (pass) {
- gcpl = H5Pcreate(H5P_GROUP_CREATE);
+ gcpl = H5Pcreate(H5P_GROUP_CREATE);
- if (gcpl <= 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_d: H5Pcreate() failed.";
- }
- HDassert(gcpl > 0);
+ if (gcpl <= 0) {
+ failure_mssg = "ns_grp_d: H5Pcreate() failed.";
+ return false;
}
- if (pass) {
- ret = H5Pset_link_creation_order(gcpl, H5P_CRT_ORDER_TRACKED);
+ ret = H5Pset_link_creation_order(gcpl, H5P_CRT_ORDER_TRACKED);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_d: H5Pset_link_creation_order() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_d: H5Pset_link_creation_order() failed.";
+ return false;
}
- if (pass) {
- gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, gcpl, H5P_DEFAULT);
+ gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, gcpl, H5P_DEFAULT);
- if (gid <= 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_d: H5Gcreate2() failed.";
- }
- HDassert(gid > 0);
+ if (gid <= 0) {
+ failure_mssg = "ns_grp_d: H5Gcreate2() failed.";
+ return false;
}
- if (pass) {
- max_compact = 0;
- ret = H5Pget_link_phase_change(gcpl, &max_compact, NULL);
+ max_compact = 0;
+ ret = H5Pget_link_phase_change(gcpl, &max_compact, NULL);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_d: H5Pget_link_phase_change() failed.";
- }
- else if (nlinks <= max_compact) {
- pass = FALSE;
- failure_mssg = "ns_grp_d: nlinks <= max_compact";
- }
- HDassert(ret >= 0);
- HDassert(nlinks > max_compact);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_d: H5Pget_link_phase_change() failed.";
+ return false;
+ }
+ else if (nlinks <= max_compact) {
+ failure_mssg = "ns_grp_d: nlinks <= max_compact";
+ return false;
}
- u = 0;
- while ((pass) && (u < nlinks)) {
+ for (u = 0; u < nlinks; u++) {
char linkname[16];
HDsprintf(linkname, "%u", u);
@@ -734,58 +689,45 @@ ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
H5P_DEFAULT, H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ns_grp_d: H5Lcreate_soft() failed.";
+ return false;
}
- HDassert(ret >= 0);
- } /* end if */
- else if (1 == (u % 3)) {
+ } else if (1 == (u % 3)) {
ret = H5Lcreate_hard(fid, "/", gid, linkname,
H5P_DEFAULT, H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ns_grp_d: H5Lcreate_hard() failed.";
+ return false;
}
- HDassert(ret >= 0);
- } /* end else-if */
- else {
+ } else {
HDassert(2 == (u % 3));
ret = H5Lcreate_external("external.h5", "/ext", gid, linkname,
H5P_DEFAULT, H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ns_grp_d: H5Lcreate_external() failed.";
+ return false;
}
- HDassert(ret >= 0);
- } /* end else */
-
- u++;
- } /* end while */
+ }
+ }
- if (pass) {
- ret = H5Pclose(gcpl);
+ ret = H5Pclose(gcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_d: H5Pclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_d: H5Pclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ns_grp_d: H5Gclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ns_grp_d: H5Gclose() failed.";
+ return false;
}
- return;
+ return true;
} /* ns_grp_d() */
@@ -795,12 +737,10 @@ ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
* Purpose: Verify a dense "new style" group, with 'nlinks'
* soft/hard/external links in it in the specified file.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
@@ -809,8 +749,9 @@ ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
*/
-void
-vrfy_ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
+bool
+vrfy_ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks)
+{
hid_t gid = -1;
hid_t gcpl = -1;
H5G_info_t grp_info;
@@ -818,84 +759,59 @@ vrfy_ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
unsigned u;
herr_t ret;
- if (pass) {
- gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
+ gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
- if (gid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5Gopen2() failed.";
- }
- HDassert(gid > 0);
+ if (gid <= 0) {
+ failure_mssg = "vrfy_ns_grp_d: H5Gopen2() failed.";
+ return false;
}
- if (pass) {
- gcpl = H5Gget_create_plist(gid);
+ gcpl = H5Gget_create_plist(gid);
- if (gcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5Gget_create_plist() failed.";
- }
- HDassert(gcpl > 0);
+ if (gcpl <= 0) {
+ failure_mssg = "vrfy_ns_grp_d: H5Gget_create_plist() failed.";
+ return false;
}
- if (pass) {
- ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
+ ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5Pget_link_creation_order() failed.";
- }
- else if (H5P_CRT_ORDER_TRACKED != crt_order_flags) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5P_CRT_ORDER_TRACKED != crt_order_flags";
- }
- HDassert(ret >= 0);
- HDassert(H5P_CRT_ORDER_TRACKED == crt_order_flags);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_d: H5Pget_link_creation_order() failed.";
+ return false;
+ } else if (H5P_CRT_ORDER_TRACKED != crt_order_flags) {
+ failure_mssg =
+ "vrfy_ns_grp_d: H5P_CRT_ORDER_TRACKED != crt_order_flags";
+ return false;
}
- if (pass) {
- ret = H5Pclose(gcpl);
+ ret = H5Pclose(gcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5Pclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_d: H5Pclose() failed.";
+ return false;
}
- if (pass) {
- HDmemset(&grp_info, 0, sizeof(grp_info));
- ret = H5Gget_info(gid, &grp_info);
+ HDmemset(&grp_info, 0, sizeof(grp_info));
+ ret = H5Gget_info(gid, &grp_info);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5Gget_info() failed.";
- }
- else if (H5G_STORAGE_TYPE_DENSE != grp_info.storage_type) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5G_STORAGE_TYPE_DENSE != grp_info.storage_type";
- }
- else if (nlinks != grp_info.nlinks) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: nlinks != grp_info.nlinks";
- }
- else if (nlinks != grp_info.max_corder) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: nlinks != grp_info.max_corder";
- }
- else if ( FALSE != grp_info.mounted) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: FALSE != grp_info.mounted";
- }
- HDassert(ret >= 0);
- HDassert(H5G_STORAGE_TYPE_DENSE == grp_info.storage_type);
- HDassert(nlinks == grp_info.nlinks);
- HDassert(nlinks == grp_info.max_corder);
- HDassert(false == grp_info.mounted);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_d: H5Gget_info() failed.";
+ return false;
+ } else if (H5G_STORAGE_TYPE_DENSE != grp_info.storage_type) {
+ failure_mssg = "vrfy_ns_grp_d: H5G_STORAGE_TYPE_DENSE != grp_info.storage_type";
+ return false;
+ } else if (nlinks != grp_info.nlinks) {
+ failure_mssg = "vrfy_ns_grp_d: nlinks != grp_info.nlinks";
+ return false;
+ } else if (nlinks != grp_info.max_corder) {
+ failure_mssg = "vrfy_ns_grp_d: nlinks != grp_info.max_corder";
+ return false;
+ } else if ( FALSE != grp_info.mounted) {
+ failure_mssg = "vrfy_ns_grp_d: FALSE != grp_info.mounted";
+ return false;
}
- u = 0;
- while ((pass) && (u < nlinks)) {
+ for (u = 0; u < nlinks; u++) {
H5L_info2_t lnk_info;
char linkname[16];
htri_t link_exists;
@@ -904,103 +820,80 @@ vrfy_ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
link_exists = H5Lexists(gid, linkname, H5P_DEFAULT);
if (link_exists < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: H5Lexists() failed.";
+ return false;
}
- HDassert(link_exists >= 0);
HDmemset(&lnk_info, 0, sizeof(grp_info));
ret = H5Lget_info2(gid, linkname, &lnk_info, H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: H5Lget_info() failed.";
- }
- else if (TRUE != lnk_info.corder_valid) {
- pass = FALSE;
+ return false;
+ } else if (TRUE != lnk_info.corder_valid) {
failure_mssg = "vrfy_ns_grp_d: TRUE != lnk_info.corder_valid";
- }
- else if (u != lnk_info.corder) {
- pass = FALSE;
+ return false;
+ } else if (u != lnk_info.corder) {
failure_mssg = "vrfy_ns_grp_d: u != lnk_info.corder";
- }
- else if (H5T_CSET_ASCII != lnk_info.cset) {
- pass = FALSE;
+ return false;
+ } else if (H5T_CSET_ASCII != lnk_info.cset) {
failure_mssg = "vrfy_ns_grp_d: H5T_CSET_ASCII != lnk_info.cset";
+ return false;
}
- HDassert(ret >= 0);
- HDassert(true == lnk_info.corder_valid);
- HDassert(u == lnk_info.corder);
- HDassert(H5T_CSET_ASCII == lnk_info.cset);
if (0 == (u % 3)) {
char *slinkval;
if (H5L_TYPE_SOFT != lnk_info.type) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: H5L_TYPE_SOFT != lnk_info.type";
+ return false;
}
else if ((HDstrlen(group_name) + 1) != lnk_info.u.val_size) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: H5L_TYPE_SOFT != lnk_info.type";
+ return false;
}
- HDassert(H5L_TYPE_SOFT == lnk_info.type);
- HDassert((HDstrlen(group_name) + 1) == lnk_info.u.val_size);
- slinkval = (char *) HDmalloc(lnk_info.u.val_size);
+ slinkval = HDmalloc(lnk_info.u.val_size);
if (!slinkval) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: HDmalloc of slinkval failed";
+ return false;
}
- HDassert(slinkval);
ret = H5Lget_val(gid, linkname, slinkval, lnk_info.u.val_size,
- H5P_DEFAULT);
+ H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: H5Lget_val() failed";
- }
- else if (0 != HDstrcmp(slinkval, group_name)) {
- pass = FALSE;
+ HDfree(slinkval);
+ return false;
+ } else if (0 != HDstrcmp(slinkval, group_name)) {
failure_mssg = "vrfy_ns_grp_d: 0 != HDstrcmp(slinkval, group_name)";
+ HDfree(slinkval);
+ return false;
}
- HDassert(ret >= 0);
- HDassert(0 == HDstrcmp(slinkval, group_name));
-
HDfree(slinkval);
- } /* end if */
- else if (1 == (u % 3)) {
+ } else if (1 == (u % 3)) {
H5O_info2_t root_oinfo;
int token_cmp = 0;
if (H5L_TYPE_HARD != lnk_info.type) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: H5L_TYPE_HARD != lnk_info.type";
+ return false;
}
- HDassert(H5L_TYPE_HARD == lnk_info.type);
HDmemset(&root_oinfo, 0, sizeof(root_oinfo));
ret = H5Oget_info3(fid, &root_oinfo, H5O_INFO_BASIC);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: H5Oget_info() failed.";
+ return false;
+ } else if(H5Otoken_cmp(fid, &root_oinfo.token, &lnk_info.u.token, &token_cmp) < 0) {
+ failure_mssg = "vrfy_ns_grp_d: H5Otoken_cmp() failed.";
+ return false;
+ } else if (token_cmp) {
+ failure_mssg = "vrfy_ns_grp_d: root_oinfo.token != lnk_info.u.token";
+ return false;
}
- else {
- if(H5Otoken_cmp(fid, &root_oinfo.token, &lnk_info.u.token, &token_cmp) < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5Otoken_cmp() failed.";
- }
-
- if (token_cmp) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: root_oinfo.token != lnk_info.u.token";
- }
- }
- HDassert(ret >= 0);
- HDassert(!token_cmp);
- } /* end else-if */
- else {
+ } else {
void *elinkval;
const char *file = NULL;
const char *path = NULL;
@@ -1008,61 +901,53 @@ vrfy_ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
HDassert(2 == (u % 3));
if (H5L_TYPE_EXTERNAL != lnk_info.type) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5L_TYPE_EXTERNAL != lnk_info.type";
+ failure_mssg =
+ "vrfy_ns_grp_d: H5L_TYPE_EXTERNAL != lnk_info.type";
+ return false;
}
- HDassert(H5L_TYPE_EXTERNAL == lnk_info.type);
elinkval = HDmalloc(lnk_info.u.val_size);
if (!elinkval) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: HDmalloc of elinkval failed.";
+ return false;
}
- HDassert(elinkval);
ret = H5Lget_val(gid, linkname, elinkval, lnk_info.u.val_size,
- H5P_DEFAULT);
+ H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: H5Lget_val failed.";
+ return false;
}
- HDassert(ret >= 0);
- ret = H5Lunpack_elink_val(elinkval, lnk_info.u.val_size, NULL, &file, &path);
+ ret = H5Lunpack_elink_val(elinkval, lnk_info.u.val_size, NULL,
+ &file, &path);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ns_grp_d: H5Lunpack_elink_val failed.";
- }
- else if (0 != HDstrcmp(file, "external.h5")) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: 0 != HDstrcmp(file, \"external.h5\").";
- }
- else if (0 != HDstrcmp(path, "/ext")) {
- pass = FALSE;
+ HDfree(elinkval);
+ return false;
+ } else if (0 != HDstrcmp(file, "external.h5")) {
+ failure_mssg =
+ "vrfy_ns_grp_d: 0 != HDstrcmp(file, \"external.h5\").";
+ HDfree(elinkval);
+ return false;
+ } else if (0 != HDstrcmp(path, "/ext")) {
failure_mssg = "vrfy_ns_grp_d: 0 != HDstrcmp(path, \"/ext\")";
+ HDfree(elinkval);
+ return false;
}
- HDassert(ret >= 0);
- HDassert(0 == HDstrcmp(file, "external.h5"));
- HDassert(0 == HDstrcmp(path, "/ext"));
-
HDfree(elinkval);
- } /* end else */
-
- u++;
- } /* end while() */
+ }
+ }
- if (pass) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ns_grp_d: H5Gclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ns_grp_d: H5Gclose() failed.";
+ return false;
}
- return;
+ return true;
} /* vrfy_ns_grp_d() */
@@ -1072,12 +957,10 @@ vrfy_ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
*
* Purpose: Create an empty "old style" group.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
@@ -1085,7 +968,19 @@ vrfy_ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks) {
*-------------------------------------------------------------------------
*/
-void
+static bool
+missing_os_grp_0(hid_t fid, const char *group_name)
+{
+ return file_has_no_path(fid, group_name);
+}
+
+static bool
+rm_os_grp_0(hid_t fid, const char *group_name)
+{
+ return remove_from_file_path(fid, group_name);
+}
+
+bool
os_grp_0(hid_t fid, const char *group_name)
{
hid_t gid = -1;
@@ -1094,66 +989,52 @@ os_grp_0(hid_t fid, const char *group_name)
herr_t ret;
- if ( pass ) { /* get the file's file access property list */
- fapl = H5Fget_access_plist(fid);
- if ( fapl <= 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_0: H5Fget_access_plist() failed.";
- }
- HDassert(fapl > 0);
+ /* get the file's file access property list */
+ fapl = H5Fget_access_plist(fid);
+ if ( fapl <= 0 ) {
+ failure_mssg = "os_grp_0: H5Fget_access_plist() failed.";
+ return false;
}
- if ( pass ) { /* get low and high bounds from fapl */
- ret = H5Pget_libver_bounds(fapl, &low, &high);
- if ( ret < 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_0: H5Pget_libver_bounds() failed(1).";
- }
- HDassert(ret >= 0);
+ /* get low and high bounds from fapl */
+ ret = H5Pget_libver_bounds(fapl, &low, &high);
+ if ( ret < 0 ) {
+ failure_mssg = "os_grp_0: H5Pget_libver_bounds() failed(1).";
+ return false;
}
- if ( pass ) { /* turn file format latest off */
- if(low >= H5F_LIBVER_V18) {
- ret = H5Fset_libver_bounds(fid, H5F_LIBVER_EARLIEST, high);
- if ( ret < 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_0: H5Fset_libver_bounds() failed(1).";
- }
- HDassert(ret >= 0);
+ /* turn file format latest off */
+ if(low >= H5F_LIBVER_V18) {
+ ret = H5Fset_libver_bounds(fid, H5F_LIBVER_EARLIEST, high);
+ if ( ret < 0 ) {
+ failure_mssg = "os_grp_0: H5Fset_libver_bounds() failed(1).";
+ return false;
}
}
- if ( pass ) {
- gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
- if ( gid <= 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_0: H5Gcreate2() failed.";
- }
- HDassert(gid > 0);
+ gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if ( gid <= 0 ) {
+ failure_mssg = "os_grp_0: H5Gcreate2() failed.";
+ return false;
}
- if ( pass ) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if ( ret < 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_0: H5Gclose() failed.";
- }
- HDassert(ret >= 0);
+ if ( ret < 0 ) {
+ failure_mssg = "os_grp_0: H5Gclose() failed.";
+ return false;
}
- if ( pass ) { /* restore low and high bounds */
- if(low >= H5F_LIBVER_V18) {
- ret = H5Fset_libver_bounds(fid, low, high);
- if ( ret < 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_0: H5Fset_libver_bounds() failed(1).";
- }
- HDassert(ret >= 0);
+ /* restore low and high bounds */
+ if(low >= H5F_LIBVER_V18) {
+ ret = H5Fset_libver_bounds(fid, low, high);
+ if ( ret < 0 ) {
+ failure_mssg = "os_grp_0: H5Fset_libver_bounds() failed(1).";
+ return false;
}
}
- return;
+ return true;
} /* os_grp_0() */
@@ -1162,12 +1043,10 @@ os_grp_0(hid_t fid, const char *group_name)
*
* Purpose: Validate an empty "old style" group.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
@@ -1175,102 +1054,74 @@ os_grp_0(hid_t fid, const char *group_name)
*-------------------------------------------------------------------------
*/
-void
-vrfy_os_grp_0(hid_t fid, const char *group_name) {
+bool
+vrfy_os_grp_0(hid_t fid, const char *group_name)
+{
hid_t gid = -1;
hid_t gcpl = -1;
H5G_info_t grp_info;
unsigned crt_order_flags = 0;
herr_t ret;
- if (pass) {
- gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
+ gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
- if (gid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: H5Gopen2() failed.";
- }
- HDassert(gid > 0);
+ if (gid <= 0) {
+ failure_mssg = "vrfy_os_grp_0: H5Gopen2() failed.";
+ return false;
}
- if (pass) {
- gcpl = H5Gget_create_plist(gid);
+ gcpl = H5Gget_create_plist(gid);
- if (gcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: H5Gget_create_plist() failed.";
- }
- HDassert(gcpl > 0);
+ if (gcpl <= 0) {
+ failure_mssg = "vrfy_os_grp_0: H5Gget_create_plist() failed.";
+ return false;
}
- if (pass) {
-
- ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
+ ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: H5Pget_link_creation_order() failed";
- }
- else if (0 != crt_order_flags) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: 0 != crt_order_flags";
- }
- HDassert(ret >= 0);
- HDassert(0 == crt_order_flags);
+ if (ret < 0) {
+ failure_mssg = "vrfy_os_grp_0: H5Pget_link_creation_order() failed";
+ return false;
+ } else if (0 != crt_order_flags) {
+ failure_mssg = "vrfy_os_grp_0: 0 != crt_order_flags";
+ return false;
}
- if (pass) {
- ret = H5Pclose(gcpl);
+ ret = H5Pclose(gcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: H5Pclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_os_grp_0: H5Pclose() failed.";
+ return false;
}
- if (pass) {
- HDmemset(&grp_info, 0, sizeof(grp_info));
- ret = H5Gget_info(gid, &grp_info);
+ HDmemset(&grp_info, 0, sizeof(grp_info));
+ ret = H5Gget_info(gid, &grp_info);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: H5Gget_info() failed.";
- }
- else if (H5G_STORAGE_TYPE_SYMBOL_TABLE != grp_info.storage_type) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: H5G_STORAGE_TYPE_SYMBOL_TABLE != grp_info.storage_type";
- }
- else if (0 != grp_info.nlinks) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: 0 != grp_info.nlinks";
- }
- else if (0 != grp_info.max_corder) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: 0 != grp_info.max_corder";
- }
- else if ( FALSE != grp_info.mounted) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: FALSE != grp_info.mounted";
- }
- HDassert(ret >= 0);
- HDassert(H5G_STORAGE_TYPE_SYMBOL_TABLE == grp_info.storage_type);
- HDassert(0 == grp_info.nlinks);
- HDassert(0 == grp_info.max_corder);
- HDassert(false == grp_info.mounted);
+ if (ret < 0) {
+ failure_mssg = "vrfy_os_grp_0: H5Gget_info() failed.";
+ return false;
+ } else if (H5G_STORAGE_TYPE_SYMBOL_TABLE != grp_info.storage_type) {
+ failure_mssg = "vrfy_os_grp_0: H5G_STORAGE_TYPE_SYMBOL_TABLE != grp_info.storage_type";
+ return false;
+ } else if (0 != grp_info.nlinks) {
+ failure_mssg = "vrfy_os_grp_0: 0 != grp_info.nlinks";
+ return false;
+ } else if (0 != grp_info.max_corder) {
+ failure_mssg = "vrfy_os_grp_0: 0 != grp_info.max_corder";
+ return false;
+ } else if ( FALSE != grp_info.mounted) {
+ failure_mssg = "vrfy_os_grp_0: FALSE != grp_info.mounted";
+ return false;
}
- if (pass) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_0: H5Gclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_os_grp_0: H5Gclose() failed.";
+ return false;
}
- return;
+ return true;
} /* vrfy_os_grp_0() */
@@ -1280,12 +1131,10 @@ vrfy_os_grp_0(hid_t fid, const char *group_name) {
* Purpose: Create an "old style" group, with 'nlinks' soft/hard
* links in it.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
@@ -1293,7 +1142,21 @@ vrfy_os_grp_0(hid_t fid, const char *group_name) {
*-------------------------------------------------------------------------
*/
-void
+static bool
+missing_os_grp_n(hid_t fid, const char *group_name, int H5_ATTR_UNUSED proc_num,
+ unsigned H5_ATTR_UNUSED nlinks)
+{
+ return file_has_no_path(fid, group_name);
+}
+
+static bool
+rm_os_grp_n(hid_t fid, const char *group_name, int H5_ATTR_UNUSED proc_num,
+ unsigned H5_ATTR_UNUSED nlinks)
+{
+ return remove_from_file_path(fid, group_name);
+}
+
+bool
os_grp_n(hid_t fid, const char *group_name, int proc_num, unsigned nlinks)
{
hid_t gid = -1;
@@ -1302,48 +1165,38 @@ os_grp_n(hid_t fid, const char *group_name, int proc_num, unsigned nlinks)
H5F_libver_t low, high;
herr_t ret;
- if ( pass ) { /* get the file's file access property list */
- fapl = H5Fget_access_plist(fid);
- if ( fapl <= 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_n: H5Fget_access_plist() failed.";
- }
- HDassert(fapl > 0);
+ /* get the file's file access property list */
+ fapl = H5Fget_access_plist(fid);
+ if ( fapl <= 0 ) {
+ failure_mssg = "os_grp_n: H5Fget_access_plist() failed.";
+ return false;
}
- if ( pass ) { /* get low and high bounds from fapl */
- ret = H5Pget_libver_bounds(fapl, &low, &high);
- if ( ret < 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_0: H5Pget_libver_bounds() failed(1).";
- }
- HDassert(ret >= 0);
+ /* get low and high bounds from fapl */
+ ret = H5Pget_libver_bounds(fapl, &low, &high);
+ if ( ret < 0 ) {
+ failure_mssg = "os_grp_0: H5Pget_libver_bounds() failed(1).";
+ return false;
}
- if ( pass ) { /* turn file format latest off */
- if(low >= H5F_LIBVER_V18) {
- ret = H5Fset_libver_bounds(fid, H5F_LIBVER_EARLIEST, high);
- if ( ret < 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_0: H5Fset_libver_bounds() failed(1).";
- }
- HDassert(ret >= 0);
+ /* turn file format latest off */
+ if(low >= H5F_LIBVER_V18) {
+ ret = H5Fset_libver_bounds(fid, H5F_LIBVER_EARLIEST, high);
+ if ( ret < 0 ) {
+ failure_mssg = "os_grp_0: H5Fset_libver_bounds() failed(1).";
+ return false;
}
}
- if ( pass ) {
- gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
- if ( gid <= 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_n: H5Gcreate2() failed.";
- }
- HDassert(gid > 0);
+ gid = H5Gcreate2(fid, group_name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+ if ( gid <= 0 ) {
+ failure_mssg = "os_grp_n: H5Gcreate2() failed.";
+ return false;
}
HDassert(nlinks > 0);
- u = 0;
- while ( ( pass ) && ( u < nlinks ) ) {
+ for (u = 0; u < nlinks; u++) {
char linkname[32];
HDsprintf(linkname, "ln%d_%u", proc_num, u);
@@ -1351,47 +1204,37 @@ os_grp_n(hid_t fid, const char *group_name, int proc_num, unsigned nlinks)
if(0 == (u % 2)) {
ret = H5Lcreate_soft(group_name, gid, linkname, H5P_DEFAULT, H5P_DEFAULT);
if ( ret < 0 ) {
- pass = FALSE;
failure_mssg = "os_grp_n: H5Lcreate_soft() failed.";
+ return false;
}
- HDassert(ret >= 0);
- } /* end if */
- else {
+ } else {
HDassert(1 == (u % 2));
ret = H5Lcreate_hard(fid, "/", gid, linkname, H5P_DEFAULT, H5P_DEFAULT);
if ( ret < 0 ) {
- pass = FALSE;
failure_mssg = "os_grp_n: H5Lcreate_hard() failed.";
+ return false;
}
- HDassert(ret >= 0);
- } /* end else */
-
- u++;
- } /* end while */
+ }
+ }
- if ( pass ) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if ( ret < 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_n: H5Gclose() failed.";
- }
- HDassert(ret >= 0);
+ if ( ret < 0 ) {
+ failure_mssg = "os_grp_n: H5Gclose() failed.";
+ return false;
}
- if ( pass ) { /* restore low and high bounds */
- if(low >= H5F_LIBVER_V18) {
- ret = H5Fset_libver_bounds(fid, low, high);
- if ( ret < 0 ) {
- pass = FALSE;
- failure_mssg = "os_grp_n: H5Fset_libver_bounds() failed(2).";
- }
- HDassert(ret >= 0);
+ /* restore low and high bounds */
+ if(low >= H5F_LIBVER_V18) {
+ ret = H5Fset_libver_bounds(fid, low, high);
+ if ( ret < 0 ) {
+ failure_mssg = "os_grp_n: H5Fset_libver_bounds() failed(2).";
+ return false;
}
}
- return;
+ return true;
} /* os_grp_n() */
@@ -1401,21 +1244,19 @@ os_grp_n(hid_t fid, const char *group_name, int proc_num, unsigned nlinks)
* Purpose: Validate an "old style" group with 'nlinks' soft/hard
* links in it.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
*
*-------------------------------------------------------------------------
*/
-void
-vrfy_os_grp_n(hid_t fid, const char *group_name, int proc_num,
- unsigned nlinks) {
+bool
+vrfy_os_grp_n(hid_t fid, const char *group_name, int proc_num, unsigned nlinks)
+{
hid_t gid = -1;
hid_t gcpl = -1;
H5G_info_t grp_info;
@@ -1423,85 +1264,59 @@ vrfy_os_grp_n(hid_t fid, const char *group_name, int proc_num,
unsigned u;
herr_t ret;
- if (pass) {
- gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
+ gid = H5Gopen2(fid, group_name, H5P_DEFAULT);
- if (gid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: H5Gopen2() failed";
- }
- HDassert(gid > 0);
+ if (gid <= 0) {
+ failure_mssg = "vrfy_os_grp_n: H5Gopen2() failed";
+ return false;
}
- if (pass) {
- gcpl = H5Gget_create_plist(gid);
+ gcpl = H5Gget_create_plist(gid);
- if (gcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: H5Gget_create_plist() failed";
- }
- HDassert(gcpl > 0);
+ if (gcpl <= 0) {
+ failure_mssg = "vrfy_os_grp_n: H5Gget_create_plist() failed";
+ return false;
}
- if (pass) {
- ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
+ ret = H5Pget_link_creation_order(gcpl, &crt_order_flags);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: H5Pget_link_creation_order";
- }
- else if (0 != crt_order_flags) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: 0 != crt_order_flags";
- }
- HDassert(ret >= 0);
- HDassert(0 == crt_order_flags);
+ if (ret < 0) {
+ failure_mssg = "vrfy_os_grp_n: H5Pget_link_creation_order";
+ return false;
+ } else if (0 != crt_order_flags) {
+ failure_mssg = "vrfy_os_grp_n: 0 != crt_order_flags";
+ return false;
}
- if (pass) {
- ret = H5Pclose(gcpl);
+ ret = H5Pclose(gcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: H5Pclose() failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_os_grp_n: H5Pclose() failed";
+ return false;
}
- if (pass) {
- HDmemset(&grp_info, 0, sizeof(grp_info));
+ HDmemset(&grp_info, 0, sizeof(grp_info));
- ret = H5Gget_info(gid, &grp_info);
+ ret = H5Gget_info(gid, &grp_info);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: H5Gget_info() failed";
- }
- else if (H5G_STORAGE_TYPE_SYMBOL_TABLE != grp_info.storage_type) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: H5G_STORAGE_TYPE_SYMBOL_TABLE != grp_info.storage_type";
- }
- else if (nlinks != grp_info.nlinks) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: nlinks != grp_info.nlinks";
- }
- else if (0 != grp_info.max_corder) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: 0 != grp_info.max_corder";
- }
- else if ( FALSE != grp_info.mounted) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: FALSE != grp_info.mounted";
- }
- HDassert(ret >= 0);
- HDassert(H5G_STORAGE_TYPE_SYMBOL_TABLE == grp_info.storage_type);
- HDassert(nlinks == grp_info.nlinks);
- HDassert(0 == grp_info.max_corder);
- HDassert(false == grp_info.mounted);
+ if (ret < 0) {
+ failure_mssg = "vrfy_os_grp_n: H5Gget_info() failed";
+ return false;
+ } else if (H5G_STORAGE_TYPE_SYMBOL_TABLE != grp_info.storage_type) {
+ failure_mssg = "vrfy_os_grp_n: H5G_STORAGE_TYPE_SYMBOL_TABLE != grp_info.storage_type";
+ return false;
+ } else if (nlinks != grp_info.nlinks) {
+ failure_mssg = "vrfy_os_grp_n: nlinks != grp_info.nlinks";
+ return false;
+ } else if (0 != grp_info.max_corder) {
+ failure_mssg = "vrfy_os_grp_n: 0 != grp_info.max_corder";
+ return false;
+ } else if ( FALSE != grp_info.mounted) {
+ failure_mssg = "vrfy_os_grp_n: FALSE != grp_info.mounted";
+ return false;
}
- u = 0;
- while ((pass) && (u < nlinks)) {
+ for (u = 0; u < nlinks; u++) {
H5L_info2_t lnk_info;
char linkname[32];
htri_t link_exists;
@@ -1510,8 +1325,8 @@ vrfy_os_grp_n(hid_t fid, const char *group_name, int proc_num,
link_exists = H5Lexists(gid, linkname, H5P_DEFAULT);
if (link_exists < 0) {
- pass = FALSE;
failure_mssg = "vrfy_os_grp_n: H5Lexists() failed";
+ return false;
}
HDassert(link_exists >= 0);
@@ -1519,107 +1334,84 @@ vrfy_os_grp_n(hid_t fid, const char *group_name, int proc_num,
ret = H5Lget_info2(gid, linkname, &lnk_info, H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_os_grp_n: H5Lget_info() failed";
+ return false;
}
else if ( FALSE != lnk_info.corder_valid) {
- pass = FALSE;
failure_mssg = "vrfy_os_grp_n: FALSE != lnk_info.corder_valid";
+ return false;
}
else if (H5T_CSET_ASCII != lnk_info.cset) {
- pass = FALSE;
failure_mssg = "vrfy_os_grp_n: H5T_CSET_ASCII != lnk_info.cset";
+ return false;
}
- HDassert(ret >= 0);
- HDassert(false == lnk_info.corder_valid);
- HDassert(H5T_CSET_ASCII == lnk_info.cset);
if (0 == (u % 2)) {
char *slinkval;
if (H5L_TYPE_SOFT != lnk_info.type) {
- pass = FALSE;
failure_mssg = "vrfy_os_grp_n: H5L_TYPE_SOFT != lnk_info.type";
- }
- else if ((HDstrlen(group_name) + 1) != lnk_info.u.val_size) {
- pass = FALSE;
+ return false;
+ } else if ((HDstrlen(group_name) + 1) != lnk_info.u.val_size) {
failure_mssg = "vrfy_os_grp_n: (HDstrlen(group_name) + 1) != lnk_info.u.val_size";
+ return false;
}
- HDassert(H5L_TYPE_SOFT == lnk_info.type);
- HDassert((HDstrlen(group_name) + 1) == lnk_info.u.val_size);
- slinkval = (char *) HDmalloc(lnk_info.u.val_size);
+ slinkval = HDmalloc(lnk_info.u.val_size);
if (!slinkval) {
- pass = FALSE;
failure_mssg = "vrfy_os_grp_n: HDmalloc of slinkval failed";
+ return false;
}
- HDassert(slinkval);
ret = H5Lget_val(gid, linkname, slinkval, lnk_info.u.val_size,
- H5P_DEFAULT);
+ H5P_DEFAULT);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_os_grp_n: H5Lget_val() failed";
- }
- else if (0 != HDstrcmp(slinkval, group_name)) {
- pass = FALSE;
+ HDfree(slinkval);
+ return false;
+ } else if (0 != HDstrcmp(slinkval, group_name)) {
failure_mssg = "vrfy_os_grp_n: 0 != HDstrcmp(slinkval, group_name)";
+ HDfree(slinkval);
+ return false;
}
- HDassert(ret >= 0);
- HDassert(0 == HDstrcmp(slinkval, group_name));
-
HDfree(slinkval);
- } /* end if */
- else {
+ } else {
H5O_info2_t root_oinfo;
int token_cmp = 0;
HDassert(1 == (u % 2));
if (H5L_TYPE_HARD != lnk_info.type) {
- pass = FALSE;
failure_mssg = "vrfy_os_grp_n: H5L_TYPE_HARD != lnk_info.type";
+ return false;
}
- HDassert(H5L_TYPE_HARD == lnk_info.type);
HDmemset(&root_oinfo, 0, sizeof(root_oinfo));
ret = H5Oget_info3(fid, &root_oinfo, H5O_INFO_BASIC);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_os_grp_n: H5Oget_info() failed.";
+ return false;
+ } else if(H5Otoken_cmp(fid, &root_oinfo.token, &lnk_info.u.token, &token_cmp) < 0) {
+ failure_mssg = "vrfy_os_grp_n: H5Otoken_cmp() failed.";
+ return false;
+ } else if (token_cmp) {
+ failure_mssg = "vrfy_os_grp_n: root_oinfo.token != lnk_info.u.token";
+ return false;
}
- else {
- if(H5Otoken_cmp(fid, &root_oinfo.token, &lnk_info.u.token, &token_cmp) < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: H5Otoken_cmp() failed.";
- }
-
- if (token_cmp) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: root_oinfo.token != lnk_info.u.token";
- }
- }
- HDassert(ret >= 0);
- HDassert(!token_cmp);
- } /* end else */
-
- u++;
- } /* end while */
+ }
+ }
- if (pass) {
- ret = H5Gclose(gid);
+ ret = H5Gclose(gid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_os_grp_n: H5Gclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_os_grp_n: H5Gclose() failed.";
+ return false;
}
- return;
+ return true;
} /* vrfy_os_grp_n() */
@@ -1630,20 +1422,32 @@ vrfy_os_grp_n(hid_t fid, const char *group_name, int proc_num,
* to the data set or not as indicated by the write_data
* parameter.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
*
*-------------------------------------------------------------------------
*/
-void
-ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data) {
+static bool
+missing_ds_ctg_i(hid_t fid, const char *dset_name,
+ hbool_t H5_ATTR_UNUSED write_data)
+{
+ return file_has_no_path(fid, dset_name);
+}
+
+static bool
+rm_ds_ctg_i(hid_t fid, const char *dset_name, hbool_t H5_ATTR_UNUSED write_data)
+{
+ return remove_from_file_path(fid, dset_name);
+}
+
+bool
+ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data)
+{
int *wdata = NULL;
unsigned u;
hid_t dsid = -1;
@@ -1651,74 +1455,58 @@ ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data) {
hsize_t dims[1] = { DSET_DIMS };
herr_t ret;
- if (pass) {
- sid = H5Screate_simple(1, dims, NULL);
+ sid = H5Screate_simple(1, dims, NULL);
- if (sid <= 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_i: H5Screate_simple() failed";
- }
- HDassert(sid > 0);
+ if (sid <= 0) {
+ failure_mssg = "ds_ctg_i: H5Screate_simple() failed";
+ return false;
}
- if (pass) {
- dsid = H5Dcreate2(fid, dset_name, H5T_NATIVE_INT, sid, H5P_DEFAULT,
- H5P_DEFAULT, H5P_DEFAULT);
+ dsid = H5Dcreate2(fid, dset_name, H5T_NATIVE_INT, sid, H5P_DEFAULT,
+ H5P_DEFAULT, H5P_DEFAULT);
- if (dsid <= 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_i: H5Dcreate2() failed";
- }
- HDassert(dsid > 0);
+ if (dsid <= 0) {
+ failure_mssg = "ds_ctg_i: H5Dcreate2() failed";
+ return false;
}
- if (pass) {
- ret = H5Sclose(sid);
+ ret = H5Sclose(sid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_i: H5Sclose() failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_ctg_i: H5Sclose() failed";
+ return false;
}
- if ((pass) && (write_data)) {
- wdata = (int *) HDmalloc(sizeof(int) * DSET_DIMS);
+ if (write_data) {
+ wdata = HDmalloc(sizeof(int) * DSET_DIMS);
if (!wdata) {
- pass = FALSE;
failure_mssg = "ds_ctg_i: HDmalloc of wdata failed.";
+ return false;
}
- HDassert(wdata);
- }
- if ((pass) && (write_data)) {
for (u = 0; u < DSET_DIMS; u++)
wdata[u] = (int) u;
- ret = H5Dwrite(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
- H5P_DEFAULT, wdata);
+ ret = H5Dwrite(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ wdata);
+
+ HDfree(wdata);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ds_ctg_i: H5Dwrite() failed.";
+ return false;
}
- HDassert(ret >= 0);
-
- HDfree(wdata);
}
- if (pass) {
- ret = H5Dclose(dsid);
+ ret = H5Dclose(dsid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_i: H5Dclose() failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_ctg_i: H5Dclose() failed";
+ return false;
}
- return;
+ return true;
} /* ds_ctg_i */
@@ -1728,20 +1516,19 @@ ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data) {
* Purpose: Validate a contiguous datasets w/int datatypes. Validate
* data if indicated via the write_data parameter.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
*
*-------------------------------------------------------------------------
*/
-void
-vrfy_ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data) {
+bool
+vrfy_ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data)
+{
int *rdata = NULL;
unsigned u;
hid_t dsid = -1;
@@ -1755,191 +1542,137 @@ vrfy_ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data) {
htri_t type_equal;
herr_t ret;
- if (pass) {
- dsid = H5Dopen2(fid, dset_name, H5P_DEFAULT);
+ dsid = H5Dopen2(fid, dset_name, H5P_DEFAULT);
- if (dsid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Dopen2() failed.";
- }
- HDassert(dsid > 0);
+ if (dsid <= 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Dopen2() failed.";
+ return false;
}
- if (pass) {
- sid = H5Dget_space(dsid);
+ sid = H5Dget_space(dsid);
- if (sid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Dget_space() failed.";
- }
- HDassert(sid > 0);
+ if (sid <= 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Dget_space() failed.";
+ return false;
}
- if (pass) {
- ndims = H5Sget_simple_extent_ndims(sid);
+ ndims = H5Sget_simple_extent_ndims(sid);
- if (1 != ndims) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: 1 != ndims";
- }
- HDassert(1 == ndims);
+ if (1 != ndims) {
+ failure_mssg = "vrfy_ds_ctg_i: 1 != ndims";
+ return false;
}
- if (pass) {
- ret = H5Sget_simple_extent_dims(sid, dims, max_dims);
+ ret = H5Sget_simple_extent_dims(sid, dims, max_dims);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Sget_simple_extent_dims() failed";
- }
- else if ( DSET_DIMS != dims[0]) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: DSET_DIMS != dims[0]";
- }
- else if ( DSET_DIMS != max_dims[0]) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: DSET_DIMS != max_dims[0]";
- }
- HDassert(ret >= 0);
- HDassert(DSET_DIMS == dims[0]);
- HDassert(DSET_DIMS == max_dims[0]);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Sget_simple_extent_dims() failed";
+ return false;
+ } else if ( DSET_DIMS != dims[0]) {
+ failure_mssg = "vrfy_ds_ctg_i: DSET_DIMS != dims[0]";
+ return false;
+ } else if ( DSET_DIMS != max_dims[0]) {
+ failure_mssg = "vrfy_ds_ctg_i: DSET_DIMS != max_dims[0]";
+ return false;
}
- if (pass) {
- ret = H5Sclose(sid);
+ ret = H5Sclose(sid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Sclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Sclose() failed.";
+ return false;
}
- if (pass) {
- tid = H5Dget_type(dsid);
+ tid = H5Dget_type(dsid);
- if (tid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Dget_type() failed.";
- }
- HDassert(tid > 0);
+ if (tid <= 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Dget_type() failed.";
+ return false;
}
- if (pass) {
- type_equal = H5Tequal(tid, H5T_NATIVE_INT);
+ type_equal = H5Tequal(tid, H5T_NATIVE_INT);
- if (1 != type_equal) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: type not H5T_NATIVE_INT";
- }
- HDassert(1 == type_equal);
+ if (1 != type_equal) {
+ failure_mssg = "vrfy_ds_ctg_i: type not H5T_NATIVE_INT";
+ return false;
}
- if (pass) {
- ret = H5Tclose(tid);
+ ret = H5Tclose(tid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Tclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Tclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Dget_space_status(dsid, &allocation);
+ ret = H5Dget_space_status(dsid, &allocation);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Dget_space_status() failed.";
- }
- else if (write_data && (allocation != H5D_SPACE_STATUS_ALLOCATED)) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: write_data && allocation != H5D_SPACE_STATUS_ALLOCATED";
- }
- else if (!write_data
- && (allocation != H5D_SPACE_STATUS_NOT_ALLOCATED)) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: !write_data && allocation != H5D_SPACE_STATUS_NOT_ALLOCATED";
- }
- HDassert(ret >= 0);
- HDassert((write_data && allocation == H5D_SPACE_STATUS_ALLOCATED)
- || (!write_data && allocation == H5D_SPACE_STATUS_NOT_ALLOCATED));
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Dget_space_status() failed.";
+ return false;
+ } else if (write_data && (allocation != H5D_SPACE_STATUS_ALLOCATED)) {
+ failure_mssg = "vrfy_ds_ctg_i: "
+ "write_data && allocation != H5D_SPACE_STATUS_ALLOCATED";
+ return false;
+ } else if (!write_data && (allocation != H5D_SPACE_STATUS_NOT_ALLOCATED)) {
+ failure_mssg = "vrfy_ds_ctg_i: "
+ "!write_data && allocation != H5D_SPACE_STATUS_NOT_ALLOCATED";
+ return false;
}
- if (pass) {
- dcpl = H5Dget_create_plist(dsid);
+ dcpl = H5Dget_create_plist(dsid);
- if (dcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Dget_create_plist() failed.";
- }
- HDassert(dcpl > 0);
+ if (dcpl <= 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Dget_create_plist() failed.";
+ return false;
}
- if (pass) {
- layout = H5Pget_layout(dcpl);
+ layout = H5Pget_layout(dcpl);
- if (H5D_CONTIGUOUS != layout) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5D_CONTIGUOUS != layout";
- }
- HDassert(H5D_CONTIGUOUS == layout);
+ if (H5D_CONTIGUOUS != layout) {
+ failure_mssg = "vrfy_ds_ctg_i: H5D_CONTIGUOUS != layout";
+ return false;
}
- if (pass) {
- ret = H5Pclose(dcpl);
+ ret = H5Pclose(dcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Pclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Pclose() failed.";
+ return false;
}
- if ((pass) && (write_data)) {
- rdata = (int *) HDmalloc(sizeof(int) * DSET_DIMS);
+ if (write_data) {
+ rdata = HDmalloc(sizeof(int) * DSET_DIMS);
if (!rdata) {
- pass = FALSE;
failure_mssg = "vrfy_ds_ctg_i: HDmalloc of rdata failed.";
+ return false;
}
- HDassert(rdata);
- }
- if ((pass) && (write_data)) {
- ret = H5Dread(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
- H5P_DEFAULT, rdata);
+ ret = H5Dread(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ rdata);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ds_ctg_i: H5Dread() failed.";
+ return false;
}
- HDassert(ret >= 0);
- }
- if ((pass) && (write_data)) {
for (u = 0; u < DSET_DIMS; u++) {
if ((int) u != rdata[u]) {
- pass = FALSE;
failure_mssg = "vrfy_ds_ctg_i: u != rdata[u].";
- break;
+ HDfree(rdata);
+ return false;
}
- HDassert((int )u == rdata[u]);
}
-
HDfree(rdata);
- } /* end if */
+ }
- if (pass) {
- ret = H5Dclose(dsid);
+ ret = H5Dclose(dsid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_i: H5Dclose() failed";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_i: H5Dclose() failed";
+ return false;
}
- return;
+ return true;
} /* vrfy_ds_ctg_i() */
@@ -1951,20 +1684,32 @@ vrfy_ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data) {
* to the data set or not as indicated by the write_data
* parameter.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
*
*-------------------------------------------------------------------------
*/
-void
-ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data) {
+static bool
+missing_ds_chk_i(hid_t fid, const char *dset_name,
+ hbool_t H5_ATTR_UNUSED write_data)
+{
+ return file_has_no_path(fid, dset_name);
+}
+
+static bool
+rm_ds_chk_i(hid_t fid, const char *dset_name, hbool_t H5_ATTR_UNUSED write_data)
+{
+ return remove_from_file_path(fid, dset_name);
+}
+
+bool
+ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data)
+{
int *wdata = NULL;
unsigned u;
hid_t dsid = -1;
@@ -1974,102 +1719,77 @@ ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data) {
hsize_t chunk_dims[1] = { DSET_CHUNK_DIMS };
herr_t ret;
- if (pass) {
- sid = H5Screate_simple(1, dims, NULL);
+ sid = H5Screate_simple(1, dims, NULL);
- if (sid <= 0) {
- pass = FALSE;
- failure_mssg = "ds_chk_i: H5Screate_simple() failed.";
- }
- HDassert(sid > 0);
+ if (sid <= 0) {
+ failure_mssg = "ds_chk_i: H5Screate_simple() failed.";
+ return false;
}
- if (pass) {
- dcpl = H5Pcreate(H5P_DATASET_CREATE);
+ dcpl = H5Pcreate(H5P_DATASET_CREATE);
- if (dcpl <= 0) {
- pass = FALSE;
- failure_mssg = "ds_chk_i: H5Pcreate() failed.";
- }
- HDassert(dcpl > 0);
+ if (dcpl <= 0) {
+ failure_mssg = "ds_chk_i: H5Pcreate() failed.";
+ return false;
}
- if (pass) {
- ret = H5Pset_chunk(dcpl, 1, chunk_dims);
+ ret = H5Pset_chunk(dcpl, 1, chunk_dims);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_chk_i: H5Pset_chunk() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_chk_i: H5Pset_chunk() failed.";
+ return false;
}
- if (pass) {
- dsid = H5Dcreate2(fid, dset_name, H5T_NATIVE_INT, sid,
- H5P_DEFAULT, dcpl, H5P_DEFAULT);
+ dsid = H5Dcreate2(fid, dset_name, H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl,
+ H5P_DEFAULT);
- if (dsid <= 0) {
- pass = FALSE;
- failure_mssg = "ds_chk_i: H5Dcreate2() failed";
- }
- HDassert(dsid > 0);
+ if (dsid <= 0) {
+ failure_mssg = "ds_chk_i: H5Dcreate2() failed";
+ return false;
}
- if (pass) {
- ret = H5Pclose(dcpl);
+ ret = H5Pclose(dcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_chk_i: H5Pclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_chk_i: H5Pclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Sclose(sid);
+ ret = H5Sclose(sid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_chk_i: H5Sclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_chk_i: H5Sclose() failed.";
+ return false;
}
- if ((pass) && (write_data)) {
- wdata = (int *) HDmalloc(sizeof(int) * DSET_DIMS);
+ if (write_data) {
+ wdata = HDmalloc(sizeof(int) * DSET_DIMS);
if (!wdata) {
- pass = FALSE;
failure_mssg = "ds_chk_i: HDmalloc of wdata failed.";
+ return false;
}
- HDassert(wdata);
- }
- if ((pass) && (write_data)) {
for (u = 0; u < DSET_DIMS; u++)
wdata[u] = (int) u;
- ret = H5Dwrite(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
- H5P_DEFAULT, wdata);
+ ret = H5Dwrite(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ wdata);
+ HDfree(wdata);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ds_chk_i: H5Dwrite() failed.";
+ return false;
}
- HDassert(ret >= 0);
- HDfree(wdata);
- } /* end if */
+ }
- if (pass) {
- ret = H5Dclose(dsid);
+ ret = H5Dclose(dsid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_chk_i: H5Dclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_chk_i: H5Dclose() failed.";
+ return false;
}
- return;
+ return true;
} /* ds_chk_i */
@@ -2079,20 +1799,19 @@ ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data) {
* Purpose: Validate a chunked datasets w/int datatypes. Validate
* data if indicated via the write_data parameter.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
*
*-------------------------------------------------------------------------
*/
-void
-vrfy_ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data) {
+bool
+vrfy_ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data)
+{
int *rdata = NULL;
unsigned u;
hid_t dsid = -1;
@@ -2106,204 +1825,145 @@ vrfy_ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data) {
htri_t type_equal;
herr_t ret;
- if (pass) {
- dsid = H5Dopen2(fid, dset_name, H5P_DEFAULT);
+ dsid = H5Dopen2(fid, dset_name, H5P_DEFAULT);
- if (dsid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Dopen2() failed.";
- }
- HDassert(dsid > 0);
+ if (dsid <= 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Dopen2() failed.";
+ return false;
}
- if (pass) {
- sid = H5Dget_space(dsid);
+ sid = H5Dget_space(dsid);
- if (sid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Dget_space() failed.";
- }
- HDassert(sid > 0);
+ if (sid <= 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Dget_space() failed.";
+ return false;
}
- if (pass) {
- ndims = H5Sget_simple_extent_ndims(sid);
+ ndims = H5Sget_simple_extent_ndims(sid);
- if (1 != ndims) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: 1 != ndims";
- }
- HDassert(1 == ndims);
+ if (1 != ndims) {
+ failure_mssg = "vrfy_ds_chk_i: 1 != ndims";
+ return false;
}
- if (pass) {
- ret = H5Sget_simple_extent_dims(sid, dims, max_dims);
+ ret = H5Sget_simple_extent_dims(sid, dims, max_dims);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Sget_simple_extent_dims() failed";
- }
- else if ( DSET_DIMS != dims[0]) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: DSET_DIMS != dims[0]";
- }
- else if ( DSET_DIMS != max_dims[0]) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: DSET_DIMS != max_dims[0]";
- }
- HDassert(ret >= 0);
- HDassert(DSET_DIMS == dims[0]);
- HDassert(DSET_DIMS == max_dims[0]);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Sget_simple_extent_dims() failed";
+ return false;
+ } else if ( DSET_DIMS != dims[0]) {
+ failure_mssg = "vrfy_ds_chk_i: DSET_DIMS != dims[0]";
+ return false;
+ } else if ( DSET_DIMS != max_dims[0]) {
+ failure_mssg = "vrfy_ds_chk_i: DSET_DIMS != max_dims[0]";
+ return false;
}
- if (pass) {
- ret = H5Sclose(sid);
+ ret = H5Sclose(sid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Sclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Sclose() failed.";
+ return false;
}
- if (pass) {
- tid = H5Dget_type(dsid);
+ tid = H5Dget_type(dsid);
- if (tid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Dget_type() failed.";
- }
- HDassert(tid > 0);
+ if (tid <= 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Dget_type() failed.";
+ return false;
}
- if (pass) {
- type_equal = H5Tequal(tid, H5T_NATIVE_INT);
+ type_equal = H5Tequal(tid, H5T_NATIVE_INT);
- if (1 != type_equal) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: tid != H5T_NATIVE_INT";
- }
- HDassert(1 == type_equal);
+ if (1 != type_equal) {
+ failure_mssg = "vrfy_ds_chk_i: tid != H5T_NATIVE_INT";
+ return false;
}
- if (pass) {
- ret = H5Tclose(tid);
+ ret = H5Tclose(tid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Tclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Tclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Dget_space_status(dsid, &allocation);
+ ret = H5Dget_space_status(dsid, &allocation);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Dget_space_status() failed.";
- }
- else if (write_data && (allocation != H5D_SPACE_STATUS_ALLOCATED)) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: write_data && allocation != H5D_SPACE_STATUS_ALLOCATED";
- }
- else if (!write_data && (allocation != H5D_SPACE_STATUS_NOT_ALLOCATED)) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: !write_data && allocation != H5D_SPACE_STATUS_NOT_ALLOCATED";
- }
- HDassert(ret >= 0);
- HDassert((write_data && allocation == H5D_SPACE_STATUS_ALLOCATED)
- || (!write_data && allocation == H5D_SPACE_STATUS_NOT_ALLOCATED));
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Dget_space_status() failed.";
+ return false;
+ } else if (write_data && (allocation != H5D_SPACE_STATUS_ALLOCATED)) {
+ failure_mssg = "vrfy_ds_chk_i: write_data && allocation != H5D_SPACE_STATUS_ALLOCATED";
+ return false;
+ } else if (!write_data && (allocation != H5D_SPACE_STATUS_NOT_ALLOCATED)) {
+ failure_mssg = "vrfy_ds_chk_i: !write_data && allocation != H5D_SPACE_STATUS_NOT_ALLOCATED";
+ return false;
}
- if (pass) {
- dcpl = H5Dget_create_plist(dsid);
+ dcpl = H5Dget_create_plist(dsid);
- if (dcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Dget_create_plist() failed.";
- }
- HDassert(dcpl > 0);
+ if (dcpl <= 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Dget_create_plist() failed.";
+ return false;
}
- if (pass) {
- layout = H5Pget_layout(dcpl);
+ layout = H5Pget_layout(dcpl);
- if (H5D_CHUNKED != layout) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5D_CHUNKED != layout";
- }
- HDassert(H5D_CHUNKED == layout);
+ if (H5D_CHUNKED != layout) {
+ failure_mssg = "vrfy_ds_chk_i: H5D_CHUNKED != layout";
+ return false;
}
- if (pass) {
- ret = H5Pget_chunk(dcpl, 1, chunk_dims);
+ ret = H5Pget_chunk(dcpl, 1, chunk_dims);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Pget_chunk";
- }
- else if ( DSET_CHUNK_DIMS != chunk_dims[0]) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: ";
- }
- HDassert(ret >= 0);
- HDassert(DSET_CHUNK_DIMS == chunk_dims[0]);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Pget_chunk";
+ return false;
+ } else if ( DSET_CHUNK_DIMS != chunk_dims[0]) {
+ failure_mssg = "vrfy_ds_chk_i: ";
+ return false;
}
- if (pass) {
- ret = H5Pclose(dcpl);
+ ret = H5Pclose(dcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Pclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Pclose() failed.";
+ return false;
}
- if ((pass) && (write_data)) {
- rdata = (int *) HDmalloc(sizeof(int) * DSET_DIMS);
+ if (write_data) {
+ rdata = HDmalloc(sizeof(int) * DSET_DIMS);
if (!rdata) {
- pass = FALSE;
failure_mssg = "vrfy_ds_chk_i: HDmalloc of rdata failed.";
+ return false;
}
- HDassert(rdata);
- }
- if ((pass) && (write_data)) {
- ret = H5Dread(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata);
+ ret = H5Dread(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ rdata);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ds_chk_i: H5Dread() failed.";
+ return false;
}
- HDassert(ret >= 0);
- }
- if ((pass) && (write_data)) {
for (u = 0; u < DSET_DIMS; u++) {
if ((int) u != rdata[u]) {
- pass = FALSE;
failure_mssg = "vrfy_ds_chk_i: u != rdata[u]";
- break;
+ HDfree(rdata);
+ return false;
}
- HDassert((int )u == rdata[u]);
}
-
HDfree(rdata);
- } /* end if */
+ }
- if (pass) {
- ret = H5Dclose(dsid);
+ ret = H5Dclose(dsid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_chk_i: H5Dclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_chk_i: H5Dclose() failed.";
+ return false;
}
- return;
+ return true;
} /* vrfy_ds_chk_i() */
@@ -2314,20 +1974,32 @@ vrfy_ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data) {
* to the data set or not as indicated by the write_data
* parameter.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
*
*-------------------------------------------------------------------------
*/
-void
-ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data) {
+static bool
+missing_ds_cpt_i(hid_t fid, const char *dset_name,
+ hbool_t H5_ATTR_UNUSED write_data)
+{
+ return file_has_no_path(fid, dset_name);
+}
+
+static bool
+rm_ds_cpt_i(hid_t fid, const char *dset_name, hbool_t H5_ATTR_UNUSED write_data)
+{
+ return remove_from_file_path(fid, dset_name);
+}
+
+bool
+ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data)
+{
int *wdata = NULL;
unsigned u;
hid_t dsid = -1;
@@ -2336,104 +2008,78 @@ ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data) {
hsize_t dims[1] = { DSET_COMPACT_DIMS };
herr_t ret;
- if (pass) {
- sid = H5Screate_simple(1, dims, NULL);
+ sid = H5Screate_simple(1, dims, NULL);
- if (sid <= 0) {
- pass = FALSE;
- failure_mssg = "ds_cpt_i: H5Screate_simple() failed.";
- }
- HDassert(sid > 0);
+ if (sid <= 0) {
+ failure_mssg = "ds_cpt_i: H5Screate_simple() failed.";
+ return false;
}
- if (pass) {
- dcpl = H5Pcreate(H5P_DATASET_CREATE);
+ dcpl = H5Pcreate(H5P_DATASET_CREATE);
- if (dcpl <= 0) {
- pass = FALSE;
- failure_mssg = "ds_cpt_i: H5Pcreate() failed.";
- }
- HDassert(dcpl > 0);
+ if (dcpl <= 0) {
+ failure_mssg = "ds_cpt_i: H5Pcreate() failed.";
+ return false;
}
- if (pass) {
- ret = H5Pset_layout(dcpl, H5D_COMPACT);
+ ret = H5Pset_layout(dcpl, H5D_COMPACT);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_cpt_i: H5Pset_layout() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_cpt_i: H5Pset_layout() failed.";
+ return false;
}
- if (pass) {
- dsid = H5Dcreate2(fid, dset_name, H5T_NATIVE_INT, sid,
- H5P_DEFAULT, dcpl, H5P_DEFAULT);
+ dsid = H5Dcreate2(fid, dset_name, H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl,
+ H5P_DEFAULT);
- if (dsid <= 0) {
- pass = FALSE;
- failure_mssg = "ds_cpt_i: H5Dcreate2() failed.";
- }
- HDassert(dsid > 0);
+ if (dsid <= 0) {
+ failure_mssg = "ds_cpt_i: H5Dcreate2() failed.";
+ return false;
}
- if (pass) {
- ret = H5Pclose(dcpl);
+ ret = H5Pclose(dcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_cpt_i: H5Pclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_cpt_i: H5Pclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Sclose(sid);
+ ret = H5Sclose(sid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_cpt_i: H5Sclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_cpt_i: H5Sclose() failed.";
+ return false;
}
- if ((pass) && (write_data)) {
- wdata = (int *) HDmalloc(sizeof(int) * DSET_COMPACT_DIMS);
+ if (write_data) {
+ wdata = HDmalloc(sizeof(int) * DSET_COMPACT_DIMS);
if (!wdata) {
- pass = FALSE;
failure_mssg = "ds_cpt_i: HDmalloc of wdata failed.";
+ return false;
}
- HDassert(wdata);
- }
- if ((pass) && (write_data)) {
for (u = 0; u < DSET_COMPACT_DIMS; u++)
wdata[u] = (int) u;
- ret = H5Dwrite(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
- H5P_DEFAULT, wdata);
+ ret = H5Dwrite(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ wdata);
+ HDfree(wdata);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ds_cpt_i: H5Dwrite() failed.";
+ return false;
}
- HDassert(ret >= 0);
-
- HDfree(wdata);
- } /* end if */
+ }
- if (pass) {
- ret = H5Dclose(dsid);
+ ret = H5Dclose(dsid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_cpt_i: H5Dclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_cpt_i: H5Dclose() failed.";
+ return false;
}
- return;
+ return true;
} /* ds_cpt_i() */
@@ -2444,20 +2090,19 @@ ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data) {
* Purpose: Validate a compact datasets w/int datatypes. Validate
* data if indicated via the write_data parameter.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
*
*-------------------------------------------------------------------------
*/
-void
-vrfy_ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data) {
+bool
+vrfy_ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data)
+{
int *rdata = NULL;
unsigned u;
hid_t dsid = -1;
@@ -2471,186 +2116,133 @@ vrfy_ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data) {
htri_t type_equal;
herr_t ret;
- if (pass) {
- dsid = H5Dopen2(fid, dset_name, H5P_DEFAULT);
+ dsid = H5Dopen2(fid, dset_name, H5P_DEFAULT);
- if (dsid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Dopen2() failed.";
- }
- HDassert(dsid > 0);
+ if (dsid <= 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Dopen2() failed.";
+ return false;
}
- if (pass) {
- sid = H5Dget_space(dsid);
+ sid = H5Dget_space(dsid);
- if (sid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Dget_space() failed.";
- }
- HDassert(sid > 0);
+ if (sid <= 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Dget_space() failed.";
+ return false;
}
- if (pass) {
- ndims = H5Sget_simple_extent_ndims(sid);
+ ndims = H5Sget_simple_extent_ndims(sid);
- if (1 != ndims) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: 1 != ndims";
- }
- HDassert(1 == ndims);
+ if (1 != ndims) {
+ failure_mssg = "vrfy_ds_cpt_i: 1 != ndims";
+ return false;
}
- if (pass) {
- ret = H5Sget_simple_extent_dims(sid, dims, max_dims);
+ ret = H5Sget_simple_extent_dims(sid, dims, max_dims);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Sget_simple_extent_dims() failed";
- }
- else if ( DSET_COMPACT_DIMS != dims[0]) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: DSET_COMPACT_DIMS != dims[0]";
- }
- else if ( DSET_COMPACT_DIMS != max_dims[0]) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: DSET_COMPACT_DIMS != max_dims[0]";
- }
- HDassert(ret >= 0);
- HDassert(DSET_COMPACT_DIMS == dims[0]);
- HDassert(DSET_COMPACT_DIMS == max_dims[0]);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Sget_simple_extent_dims() failed";
+ return false;
+ } else if ( DSET_COMPACT_DIMS != dims[0]) {
+ failure_mssg = "vrfy_ds_cpt_i: DSET_COMPACT_DIMS != dims[0]";
+ return false;
+ } else if ( DSET_COMPACT_DIMS != max_dims[0]) {
+ failure_mssg = "vrfy_ds_cpt_i: DSET_COMPACT_DIMS != max_dims[0]";
+ return false;
}
- if (pass) {
- ret = H5Sclose(sid);
+ ret = H5Sclose(sid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Sclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Sclose() failed.";
+ return false;
}
- if (pass) {
- tid = H5Dget_type(dsid);
+ tid = H5Dget_type(dsid);
- if (tid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Dget_type() failed.";
- }
- HDassert(tid > 0);
+ if (tid <= 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Dget_type() failed.";
+ return false;
}
- if (pass) {
- type_equal = H5Tequal(tid, H5T_NATIVE_INT);
+ type_equal = H5Tequal(tid, H5T_NATIVE_INT);
- if (1 != type_equal) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: type != H5T_NATIVE_INT";
- }
- HDassert(1 == type_equal);
+ if (1 != type_equal) {
+ failure_mssg = "vrfy_ds_cpt_i: type != H5T_NATIVE_INT";
+ return false;
}
- if (pass) {
- ret = H5Tclose(tid);
+ ret = H5Tclose(tid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Tclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Tclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Dget_space_status(dsid, &allocation);
+ ret = H5Dget_space_status(dsid, &allocation);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Dget_space_status() failed.";
- }
- else if (H5D_SPACE_STATUS_ALLOCATED != allocation) {
- pass = FALSE;
- failure_mssg =
- "vrfy_ds_cpt_i: H5D_SPACE_STATUS_ALLOCATED != allocation";
- }
- HDassert(ret >= 0);
- HDassert(H5D_SPACE_STATUS_ALLOCATED == allocation);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Dget_space_status() failed.";
+ return false;
+ } else if (H5D_SPACE_STATUS_ALLOCATED != allocation) {
+ failure_mssg =
+ "vrfy_ds_cpt_i: H5D_SPACE_STATUS_ALLOCATED != allocation";
+ return false;
}
- if (pass) {
- dcpl = H5Dget_create_plist(dsid);
+ dcpl = H5Dget_create_plist(dsid);
- if (dcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Dget_create_plist() failed.";
- }
- HDassert(dcpl > 0);
+ if (dcpl <= 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Dget_create_plist() failed.";
+ return false;
}
- if (pass) {
- layout = H5Pget_layout(dcpl);
+ layout = H5Pget_layout(dcpl);
- if (H5D_COMPACT != layout) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5D_COMPACT != layout";
- }
- HDassert(H5D_COMPACT == layout);
+ if (H5D_COMPACT != layout) {
+ failure_mssg = "vrfy_ds_cpt_i: H5D_COMPACT != layout";
+ return false;
}
- if (pass) {
- ret = H5Pclose(dcpl);
+ ret = H5Pclose(dcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Pclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Pclose() failed.";
+ return false;
}
- if ((pass) && (write_data)) {
- rdata = (int *) HDmalloc(sizeof(int) * DSET_COMPACT_DIMS);
+ if (write_data) {
+ rdata = HDmalloc(sizeof(int) * DSET_COMPACT_DIMS);
if (!rdata) {
- pass = FALSE;
failure_mssg = "vrfy_ds_cpt_i: HDmalloc of rdata failed.";
+ return false;
}
- HDassert(rdata);
- }
- if ((pass) && (write_data)) {
- ret = H5Dread(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
- H5P_DEFAULT, rdata);
+ ret = H5Dread(dsid, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT,
+ rdata);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ds_cpt_i: H5Dread() failed.";
+ return false;
}
- HDassert(ret >= 0);
- }
- if ((pass) && (write_data)) {
for (u = 0; u < DSET_COMPACT_DIMS; u++) {
if ((int) u != rdata[u]) {
- pass = FALSE;
failure_mssg = "vrfy_ds_cpt_i: (int)u != rdata[u]";
- break;
+ HDfree(rdata);
+ return false;
}
- HDassert((int )u == rdata[u]);
}
-
HDfree(rdata);
- } /* end if */
+ }
- if (pass) {
- ret = H5Dclose(dsid);
+ ret = H5Dclose(dsid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_cpt_i: H5Dclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_cpt_i: H5Dclose() failed.";
+ return false;
}
- return;
+ return true;
} /* vrfy_ds_cpt_i() */
@@ -2661,20 +2253,32 @@ vrfy_ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data) {
* Write data to the data set or not as indicated by the
* write_data parameter.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
*
*-------------------------------------------------------------------------
*/
-void
-ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data) {
+static bool
+missing_ds_ctg_v(hid_t fid, const char *dset_name,
+ hbool_t H5_ATTR_UNUSED write_data)
+{
+ return file_has_no_path(fid, dset_name);
+}
+
+static bool
+rm_ds_ctg_v(hid_t fid, const char *dset_name, hbool_t H5_ATTR_UNUSED write_data)
+{
+ return remove_from_file_path(fid, dset_name);
+}
+
+bool
+ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data)
+{
hid_t dsid = -1;
hid_t sid = -1;
hid_t tid = -1;
@@ -2683,124 +2287,101 @@ ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data) {
hvl_t *wdata = NULL;
unsigned u;
- if (pass) {
- sid = H5Screate_simple(1, dims, NULL);
+ sid = H5Screate_simple(1, dims, NULL);
- if (sid <= 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_v: H5Screate_simple";
- }
- HDassert(sid > 0);
+ if (sid <= 0) {
+ failure_mssg = "ds_ctg_v: H5Screate_simple";
+ return false;
}
- if (pass) {
- tid = H5Tvlen_create(H5T_NATIVE_INT);
+ tid = H5Tvlen_create(H5T_NATIVE_INT);
- if (tid <= 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_v: H5Tvlen_create() failed.";
- }
- HDassert(tid > 0);
+ if (tid <= 0) {
+ failure_mssg = "ds_ctg_v: H5Tvlen_create() failed.";
+ return false;
}
- if (pass) {
- dsid = H5Dcreate2(fid, dset_name, tid, sid, H5P_DEFAULT,
- H5P_DEFAULT, H5P_DEFAULT);
+ dsid = H5Dcreate2(fid, dset_name, tid, sid, H5P_DEFAULT,
+ H5P_DEFAULT, H5P_DEFAULT);
- if (dsid <= 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_v: H5Dcreate2() failed.";
- }
- HDassert(dsid > 0);
+ if (dsid <= 0) {
+ failure_mssg = "ds_ctg_v: H5Dcreate2() failed.";
+ return false;
}
- if ((pass) && (write_data)) {
- wdata = (hvl_t *) HDmalloc(sizeof(hvl_t) * DSET_SMALL_DIMS);
+ if (write_data) {
+ wdata = HDmalloc(sizeof(hvl_t) * DSET_SMALL_DIMS);
if (!wdata) {
- pass = FALSE;
failure_mssg = "ds_ctg_v: HDmalloc of wdata failed.";
+ return false;
}
- HDassert(wdata);
- }
- if ((pass) && (write_data)) {
for (u = 0; u < DSET_SMALL_DIMS; u++) {
int *tdata;
unsigned len;
unsigned v;
len = (u % 10) + 1;
- tdata = (int *) HDmalloc(sizeof(int) * len);
+ tdata = HDmalloc(sizeof(int) * len);
if (!tdata) {
- pass = FALSE;
failure_mssg = "ds_ctg_v: HDmalloc of tdata failed.";
- break;
+ while (u > 0)
+ free(wdata[u--].p);
+ HDfree(wdata);
+ return false;
}
- HDassert(tdata);
for (v = 0; v < len; v++)
tdata[v] = (int) (u + v);
wdata[u].len = len;
wdata[u].p = tdata;
- } /* end for */
- }
+ }
- if ((pass) && (write_data)) {
ret = H5Dwrite(dsid, tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, wdata);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ds_ctg_v: H5Dwrite() failed.";
+ for (u = 0; u < DSET_SMALL_DIMS; u++)
+ free(wdata[u].p);
+ HDfree(wdata);
+ return false;
}
- HDassert(ret >= 0);
- }
- if ((pass) && (write_data)) {
ret = H5Treclaim(tid, sid, H5P_DEFAULT, wdata);
+ HDfree(wdata);
+
if (ret < 0) {
- pass = FALSE;
failure_mssg = "ds_ctg_v: H5Treclaim() failed.";
+ return false;
}
- HDassert(ret >= 0);
-
- HDfree(wdata);
- } /* end if */
+ }
- if (pass) {
- ret = H5Sclose(sid);
+ ret = H5Sclose(sid);
- if (sid < 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_v: H5Sclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_ctg_v: H5Sclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Tclose(tid);
+ ret = H5Tclose(tid);
- if (tid < 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_v: H5Tclose() failed.";
- }
- HDassert(ret >= 0);
+ if (tid < 0) {
+ failure_mssg = "ds_ctg_v: H5Tclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Dclose(dsid);
+ ret = H5Dclose(dsid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "ds_ctg_v: H5Dclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "ds_ctg_v: H5Dclose() failed.";
+ return false;
}
- return;
+ return true;
} /* ds_ctg_v() */
@@ -2810,20 +2391,19 @@ ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data) {
* Purpose: Validate a contiguous datasets w/variable-length datatypes.
* Validate data if indicated via the write_data parameter.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
- * Return: void
+ * Return: true on success, false on failure.
*
* Programmer: John Mainzer
* 9/14/15
*
*-------------------------------------------------------------------------
*/
-void
-vrfy_ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data) {
+bool
+vrfy_ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data)
+{
hid_t dsid = -1;
hid_t sid = -1;
hid_t tid = -1;
@@ -2838,170 +2418,120 @@ vrfy_ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data) {
unsigned u;
herr_t ret;
- if (pass) {
- dsid = H5Dopen2(fid, dset_name, H5P_DEFAULT);
+ dsid = H5Dopen2(fid, dset_name, H5P_DEFAULT);
- if (dsid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Dopen2() failed.";
- }
- HDassert(dsid > 0);
+ if (dsid <= 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Dopen2() failed.";
+ return false;
}
- if (pass) {
- sid = H5Dget_space(dsid);
+ sid = H5Dget_space(dsid);
- if (sid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Dget_space() failed";
- }
- HDassert(sid > 0);
+ if (sid <= 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Dget_space() failed";
+ return false;
}
- if (pass) {
- ndims = H5Sget_simple_extent_ndims(sid);
+ ndims = H5Sget_simple_extent_ndims(sid);
- if (1 != ndims) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: 1 != ndims";
- }
- HDassert(1 == ndims);
+ if (1 != ndims) {
+ failure_mssg = "vrfy_ds_ctg_v: 1 != ndims";
+ return false;
}
- if (pass) {
- ret = H5Sget_simple_extent_dims(sid, dims, max_dims);
+ ret = H5Sget_simple_extent_dims(sid, dims, max_dims);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Sget_simple_extent_dims() failed.";
- }
- else if ( DSET_SMALL_DIMS != dims[0]) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: DSET_SMALL_DIMS != dims[0]";
- }
- else if ( DSET_SMALL_DIMS != max_dims[0]) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: DSET_SMALL_DIMS != max_dims[0]";
- }
- HDassert(ret >= 0);
- HDassert(DSET_SMALL_DIMS == dims[0]);
- HDassert(DSET_SMALL_DIMS == max_dims[0]);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Sget_simple_extent_dims() failed.";
+ return false;
+ } else if ( DSET_SMALL_DIMS != dims[0]) {
+ failure_mssg = "vrfy_ds_ctg_v: DSET_SMALL_DIMS != dims[0]";
+ return false;
+ } else if ( DSET_SMALL_DIMS != max_dims[0]) {
+ failure_mssg = "vrfy_ds_ctg_v: DSET_SMALL_DIMS != max_dims[0]";
+ return false;
}
- if (pass) {
- tid = H5Dget_type(dsid);
+ tid = H5Dget_type(dsid);
- if (tid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Dget_type() failed.";
- }
- HDassert(tid > 0);
+ if (tid <= 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Dget_type() failed.";
+ return false;
}
- if (pass) {
- tmp_tid = H5Tvlen_create(H5T_NATIVE_INT);
+ tmp_tid = H5Tvlen_create(H5T_NATIVE_INT);
- if (tmp_tid <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Tvlen_create() failed.";
- }
- HDassert(tmp_tid > 0);
+ if (tmp_tid <= 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Tvlen_create() failed.";
+ return false;
}
- if (pass) {
- type_equal = H5Tequal(tid, tmp_tid);
+ type_equal = H5Tequal(tid, tmp_tid);
- if (1 != type_equal) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: type != vlen H5T_NATIVE_INT";
- }
- HDassert(1 == type_equal);
+ if (1 != type_equal) {
+ failure_mssg = "vrfy_ds_ctg_v: type != vlen H5T_NATIVE_INT";
+ return false;
}
- if (pass) {
- ret = H5Tclose(tmp_tid);
+ ret = H5Tclose(tmp_tid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Tclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Tclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Dget_space_status(dsid, &allocation);
+ ret = H5Dget_space_status(dsid, &allocation);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Dget_space_status() failed";
- }
- else if (write_data && (allocation != H5D_SPACE_STATUS_ALLOCATED)) {
- pass = FALSE;
- failure_mssg =
- "vrfy_ds_ctg_v: write_data && allocation != H5D_SPACE_STATUS_ALLOCATED";
- }
- else if (!write_data
- && (allocation != H5D_SPACE_STATUS_NOT_ALLOCATED)) {
- pass = FALSE;
- failure_mssg =
- "vrfy_ds_ctg_v: !write_data && allocation != H5D_SPACE_STATUS_NOT_ALLOCATED";
- }
- HDassert(ret >= 0);
- HDassert((write_data && allocation == H5D_SPACE_STATUS_ALLOCATED)
- || (!write_data && allocation == H5D_SPACE_STATUS_NOT_ALLOCATED));
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Dget_space_status() failed";
+ return false;
+ } else if (write_data && (allocation != H5D_SPACE_STATUS_ALLOCATED)) {
+ failure_mssg =
+ "vrfy_ds_ctg_v: write_data && allocation != H5D_SPACE_STATUS_ALLOCATED";
+ return false;
+ } else if (!write_data
+ && (allocation != H5D_SPACE_STATUS_NOT_ALLOCATED)) {
+ failure_mssg =
+ "vrfy_ds_ctg_v: !write_data && allocation != H5D_SPACE_STATUS_NOT_ALLOCATED";
+ return false;
}
- if (pass) {
- dcpl = H5Dget_create_plist(dsid);
+ dcpl = H5Dget_create_plist(dsid);
- if (dcpl <= 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Dget_create_plist() failed.";
- }
- HDassert(dcpl > 0);
+ if (dcpl <= 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Dget_create_plist() failed.";
+ return false;
}
- if (pass) {
- layout = H5Pget_layout(dcpl);
+ layout = H5Pget_layout(dcpl);
- if (H5D_CONTIGUOUS != layout) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5D_CONTIGUOUS != layout";
- }
- HDassert(H5D_CONTIGUOUS == layout);
+ if (H5D_CONTIGUOUS != layout) {
+ failure_mssg = "vrfy_ds_ctg_v: H5D_CONTIGUOUS != layout";
+ return false;
}
- if (pass) {
- ret = H5Pclose(dcpl);
+ ret = H5Pclose(dcpl);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Pclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Pclose() failed.";
+ return false;
}
- if ((pass) && (write_data)) {
- rdata = (hvl_t *) HDmalloc(sizeof(hvl_t) * DSET_SMALL_DIMS);
+ if (write_data) {
+ rdata = HDmalloc(sizeof(hvl_t) * DSET_SMALL_DIMS);
if (!rdata) {
- pass = FALSE;
failure_mssg = "vrfy_ds_ctg_v: HDmalloc of rdata failed.";
+ return false;
}
- HDassert(rdata);
- }
- if ((pass) && (write_data)) {
ret = H5Dread(dsid, tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, rdata);
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ds_ctg_v: H5Dread() failed.";
+ return false;
}
- HDassert(ret >= 0);
- }
- if ((pass) && (write_data)) {
for (u = 0; u < DSET_SMALL_DIMS; u++) {
unsigned len;
unsigned v;
@@ -3011,66 +2541,222 @@ vrfy_ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data) {
int *tdata = (int *) rdata[u].p;
if (!tdata) {
- pass = FALSE;
failure_mssg = "vrfy_ds_ctg_v: !tdata";
- break;
- }
- else if ((int) (u + v) != tdata[v]) {
- pass = FALSE;
+ return false;
+ } else if ((int) (u + v) != tdata[v]) {
failure_mssg = "vrfy_ds_ctg_v: (int)(u + v) != tdata[v]";
- break;
+ return false;
}
- HDassert(tdata);
- HDassert((int )(u + v) == tdata[v]);
- } /* end for */
- } /* end for */
- }
+ }
+ }
- if ((pass) && (write_data)) {
ret = H5Treclaim(tid, sid, H5P_DEFAULT, rdata);
+ HDfree(rdata);
+
if (ret < 0) {
- pass = FALSE;
failure_mssg = "vrfy_ds_ctg_v: H5Treclaim() failed.";
+ return false;
}
- HDassert(ret >= 0);
-
- HDfree(rdata);
- } /* end if */
+ }
- if (pass) {
- ret = H5Sclose(sid);
+ ret = H5Sclose(sid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Sclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Sclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Tclose(tid);
+ ret = H5Tclose(tid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Tclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Tclose() failed.";
+ return false;
}
- if (pass) {
- ret = H5Dclose(dsid);
+ ret = H5Dclose(dsid);
- if (ret < 0) {
- pass = FALSE;
- failure_mssg = "vrfy_ds_ctg_v: H5Dclose() failed.";
- }
- HDassert(ret >= 0);
+ if (ret < 0) {
+ failure_mssg = "vrfy_ds_ctg_v: H5Dclose() failed.";
+ return false;
}
- return;
+ return true;
} /* vrfy_ds_ctg_v() */
+/* Create or, if `validate` is true, validate objects in file `fid` under
+ * group `full_path`. `proc_num` tells the processor number the test runs
+ * on. The set of objects to create/validate is chosen by `selector`.
+ *
+ * The valid selectors are consecutive and they start at 0.
+ *
+ * If the selected objects cannot be created/validated, `*okp` will be set
+ * to `false`, indicating that the selected objects could not be
+ * created/validated, and `failure_mmsg` set to a description of the error
+ * that occurred. If the objects can be created/validated, then `*okp`
+ * will be set to `true`.
+ *
+ * The program may also fail an assert()ion if the selected objects cannot
+ * be created/validated.
+ *
+ * Return `true` if the selector was valid, `false` if it was not.
+ */
+
+static bool
+create_or_validate_selection(hid_t fid, const char *full_path,
+ int selector, zoo_config_t config, phase_t phase, bool *okp)
+{
+ bool ok;
+
+ switch (selector) {
+ case 0: /* Add & verify an empty "new style" group */
+ ok = ns_grp_0_fntbl[phase](fid, full_path);
+ break;
+ case 1: /* Add & verify a compact "new style" group (3 link messages) */
+ ok = ns_grp_c_fntbl[phase](fid, full_path, 3);
+ break;
+ case 2:
+ /* Add & verify a dense "new style" group (w/300 links,
+ * in v2 B-tree & fractal heap)
+ */
+ ok = ns_grp_d_fntbl[phase](fid, full_path, 300);
+ break;
+ case 3: /* Add & verify an empty "old style" group to file */
+ ok = os_grp_0_fntbl[phase](fid, full_path);
+ break;
+ case 4:
+ /* Add & verify an "old style" group (w/300 links, in
+ * v1 B-tree & local heap) to file
+ */
+ ok = os_grp_n_fntbl[phase](fid, full_path, config.proc_num, 300);
+ break;
+ case 5:
+ /* Add & verify a contiguous dataset w/integer datatype (but no data)
+ * to file
+ */
+ ok = ds_ctg_i_fntbl[phase](fid, full_path, false);
+ break;
+ case 6:
+ /* Add & verify a contiguous dataset w/integer datatype (with data)
+ * to file
+ */
+ ok = ds_ctg_i_fntbl[phase](fid, full_path, true);
+ break;
+ case 7:
+ /* Add & verify a chunked dataset w/integer datatype (but no data)
+ * to file
+ */
+ ok = ds_chk_i_fntbl[phase](fid, full_path, false);
+ break;
+ case 8:
+ /* Add & verify a chunked dataset w/integer datatype (and data)
+ * to file
+ */
+ ok = ds_chk_i_fntbl[phase](fid, full_path, true);
+ break;
+ case 9:
+ /* Add & verify a compact dataset w/integer datatype (but no data)
+ * to file
+ */
+ ok = config.skip_compact ||
+ ds_cpt_i_fntbl[phase](fid, full_path, false);
+ break;
+ case 10:
+ /* Add & verify a compact dataset w/integer datatype (and data)
+ * to file
+ */
+ ok = config.skip_compact || ds_cpt_i_fntbl[phase](fid, full_path, true);
+ break;
+ case 11:
+ /* Add & verify a contiguous dataset w/variable-length datatype
+ * (but no data) to file
+ */
+ ok = config.skip_varlen || ds_ctg_v_fntbl[phase](fid, full_path, false);
+ break;
+ case 12:
+ /* Add & verify a contiguous dataset w/variable-length datatype
+ * (and data) to file
+ */
+ ok = config.skip_varlen || ds_ctg_v_fntbl[phase](fid, full_path, true);
+ break;
+ default:
+ return false;
+ }
+ *okp = ok;
+ return true;
+}
+
+/* Sleep for no more than `max_pause_msecs` milliseconds. */
+static void
+random_pause(unsigned int max_pause_msecs)
+{
+ struct timespec delay;
+ const uint64_t nsecs_per_sec = 1000 * 1000 * 1000;
+ uint64_t nsecs_per_msec, nsecs;
+
+ if (max_pause_msecs == 0)
+ return;
+
+ nsecs_per_msec = 1 + (uint64_t)random() % (1000 * 1000);
+ nsecs = max_pause_msecs * nsecs_per_msec;
+
+ delay.tv_sec = (time_t)(nsecs / nsecs_per_sec);
+ delay.tv_nsec = (long)(nsecs % nsecs_per_sec);
+ for (;;) {
+ if (nanosleep(&delay, &delay) == 0)
+ break;
+ if (errno == EINTR)
+ continue;
+ errx(EXIT_FAILURE, "%s: nanosleep", __func__);
+ }
+}
+
+/* Create and validate objects or, if `only_validate` is true, only
+ * validate objects in file `fid` under group `base_path`. `config.proc_num`
+ * tells the processor number the test runs on. If `config.skip_varlen` is
+ * true, do NOT perform tests that use variable-length data.
+ *
+ * Return true if all tests pass, false if any test fails.
+ */
+
+static bool
+tend_zoo(hid_t fid, const char *base_path, zoo_config_t config,
+ const phase_t *phase, size_t nphases)
+{
+ char full_path[1024];
+ int i, nwritten;
+ size_t j;
+ char *leafp;
+ bool ok = true;
+
+ nwritten = snprintf(full_path, sizeof(full_path), "%s/*", base_path);
+ if (nwritten < 0 || (size_t)nwritten >= sizeof(full_path)) {
+ failure_mssg = "tend_zoo: snprintf failed";
+ return false;
+ }
+
+ if ((leafp = strrchr(full_path, '*')) == NULL) {
+ failure_mssg = "tend_zoo: strrchr failed";
+ return false;
+ }
+
+ for (i = 0; ok; i++) {
+ assert('A' + i <= 'Z');
+ *leafp = (char)('A' + i);
+ for (j = 0; j < nphases; j++) {
+ if (!create_or_validate_selection(fid, full_path, i, config,
+ phase[j], &ok))
+ goto out;
+ if (phase[j] == PHASE_CREATE || phase[j] == PHASE_DELETE)
+ zoo_create_hook(fid);
+ }
+ random_pause(config.max_pause_msecs);
+ }
+out:
+ if (!ok)
+ warnx("%s: %s", __func__, failure_mssg);
+ return ok;
+}
/*-------------------------------------------------------------------------
* Function: create_zoo
@@ -3080,9 +2766,7 @@ vrfy_ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data) {
* include instances of all on disk data structures used
* in the HDF5 library.
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
* This function was initially created to assist in testing
@@ -3092,214 +2776,16 @@ vrfy_ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data) {
* the superblock.
*
* Note the associated validate_zoo() function.
- *
- * Return: void
- *
- * Programmer: John Mainzer
- * 9/14/15
- *
*-------------------------------------------------------------------------
*/
-void
-create_zoo(hid_t fid, const char *base_path, int proc_num)
+bool
+create_zoo(hid_t fid, const char *base_path, zoo_config_t config)
{
- char full_path[1024];
-
- HDassert(base_path);
-
- /* Add & verify an empty "new style" group */
- if ( pass ) {
- HDsprintf(full_path, "%s/A", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ns_grp_0(fid, full_path);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/A", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ns_grp_0(fid, full_path);
- }
-
- /* Add & verify a compact "new style" group (3 link messages) */
- if ( pass ) {
- HDsprintf(full_path, "%s/B", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ns_grp_c(fid, full_path, 3);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/B", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ns_grp_c(fid, full_path, 3);
- }
-
- /* Add & verify a dense "new style" group (w/300 links, in v2 B-tree &
- * fractal heap)
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/C", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ns_grp_d(fid, full_path, 300);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/C", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ns_grp_d(fid, full_path, 300);
- }
-
- /* Add & verify an empty "old style" group to file */
- if ( pass ) {
- HDsprintf(full_path, "%s/D", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- os_grp_0(fid, full_path);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/D", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_os_grp_0(fid, full_path);
- }
-
- /* Add & verify an "old style" group (w/300 links, in v1 B-tree &
- * local heap) to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/E", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- os_grp_n(fid, full_path, proc_num, 300);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/E", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_os_grp_n(fid, full_path, proc_num, 300);
- }
-
- /* Add & verify a contiguous dataset w/integer datatype (but no data)
- * to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/F", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ds_ctg_i(fid, full_path, FALSE);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/F", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_ctg_i(fid, full_path, FALSE);
- }
-
- /* Add & verify a contiguous dataset w/integer datatype (with data)
- * to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/G", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ds_ctg_i(fid, full_path, TRUE);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/G", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_ctg_i(fid, full_path, TRUE);
- }
-
- /* Add & verify a chunked dataset w/integer datatype (but no data)
- * to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/H", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ds_chk_i(fid, full_path, FALSE);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/H", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_chk_i(fid, full_path, FALSE);
- }
-
- /* Add & verify a chunked dataset w/integer datatype (and data)
- * to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/I", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ds_chk_i(fid, full_path, TRUE);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/I", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_chk_i(fid, full_path, TRUE);
- }
-
- /* Add & verify a compact dataset w/integer datatype (but no data)
- * to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/J", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ds_cpt_i(fid, full_path, FALSE);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/J", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_cpt_i(fid, full_path, FALSE);
- }
-
- /* Add & verify a compact dataset w/integer datatype (and data)
- * to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/K", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ds_cpt_i(fid, full_path, TRUE);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/K", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_cpt_i(fid, full_path, TRUE);
- }
-
- /* Add & verify a contiguous dataset w/variable-length datatype
- * (but no data) to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/L", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ds_ctg_v(fid, full_path, FALSE);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/L", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_ctg_v(fid, full_path, FALSE);
- }
-
- /* Add & verify a contiguous dataset w/variable-length datatype
- * (and data) to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/M", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- ds_ctg_v(fid, full_path, TRUE);
- }
-
- if ( pass ) {
- HDsprintf(full_path, "%s/M", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_ctg_v(fid, full_path, TRUE);
- }
-
- return;
-} /* create_zoo() */
+ const phase_t phase[] = {PHASE_CREATE, PHASE_VALIDATE};
+ return tend_zoo(fid, base_path, config, phase, NELMTS(phase));
+}
/*-------------------------------------------------------------------------
* Function: validate_zoo
@@ -3307,9 +2793,7 @@ create_zoo(hid_t fid, const char *base_path, int proc_num)
* Purpose: Given the path to a group in which a "zoo" has been
* constructed, validate the objects in the "zoo".
*
- * If pass is false on entry, do nothing.
- *
- * If an error is detected, set pass to FALSE, and set
+ * If an error is detected, return false, and set
* failure_mssg to point to an appropriate error message.
*
* This function was initially created to assist in testing
@@ -3318,134 +2802,31 @@ create_zoo(hid_t fid, const char *base_path, int proc_num)
* on disk structures that can occur with this version of
* the superblock.
*
- * Note the associated validate_zoo() function.
- *
- * Return: void
- *
- * Programmer: John Mainzer
- * 9/14/15
- *
+ * Note the associated create_zoo() function.
*-------------------------------------------------------------------------
*/
-void
-validate_zoo(hid_t fid, const char *base_path, int proc_num)
+bool
+validate_zoo(hid_t fid, const char *base_path, zoo_config_t config)
{
- char full_path[1024];
+ const phase_t phase[] = {PHASE_VALIDATE};
+
+ return tend_zoo(fid, base_path, config, phase, NELMTS(phase));
+}
+
+bool
+delete_zoo(hid_t fid, const char *base_path, zoo_config_t config)
+{
+ const phase_t phase[] = {PHASE_DELETE};
+
+ return tend_zoo(fid, base_path, config, phase, NELMTS(phase));
+}
+
+bool
+validate_deleted_zoo(hid_t fid, const char *base_path, zoo_config_t config)
+{
+ const phase_t phase[] = {PHASE_VALIDATE_DELETION};
- HDassert(base_path);
-
- /* validate an empty "new style" group */
- if ( pass ) {
- HDsprintf(full_path, "%s/A", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ns_grp_0(fid, full_path);
- }
-
- /* validate a compact "new style" group (3 link messages) */
- if ( pass ) {
- HDsprintf(full_path, "%s/B", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ns_grp_c(fid, full_path, 3);
- }
-
- /* validate a dense "new style" group (w/300 links, in v2 B-tree &
- * fractal heap)
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/C", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ns_grp_d(fid, full_path, 300);
- }
-
- /* validate an empty "old style" group in file */
- if ( pass ) {
- HDsprintf(full_path, "%s/D", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_os_grp_0(fid, full_path);
- }
-
- /* validate an "old style" group (w/300 links, in v1 B-tree &
- * local heap)
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/E", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_os_grp_n(fid, full_path, proc_num, 300);
- }
-
- /* validate a contiguous dataset w/integer datatype (but no data)
- * in file.
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/F", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_ctg_i(fid, full_path, FALSE);
- }
-
- /* validate a contiguous dataset w/integer datatype (with data)
- * in file.
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/G", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_ctg_i(fid, full_path, TRUE);
- }
-
- /* validate a chunked dataset w/integer datatype (but no data)
- * in file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/H", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_chk_i(fid, full_path, FALSE);
- }
-
- /* validate a chunked dataset w/integer datatype (and data)
- * in file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/I", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_chk_i(fid, full_path, TRUE);
- }
-
- /* Validate a compact dataset w/integer datatype (but no data)
- * in file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/J", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_cpt_i(fid, full_path, FALSE);
- }
-
- /* validate a compact dataset w/integer datatype (and data)
- * in file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/K", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_cpt_i(fid, full_path, TRUE);
- }
-
- /* validate a contiguous dataset w/variable-length datatype
- * (but no data) to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/L", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_ctg_v(fid, full_path, FALSE);
- }
-
- /* validate a contiguous dataset w/variable-length datatype
- * (and data) to file
- */
- if ( pass ) {
- HDsprintf(full_path, "%s/M", base_path);
- HDassert(HDstrlen(full_path) < 1024);
- vrfy_ds_ctg_v(fid, full_path, TRUE);
- }
-
- return;
-} /* validate_zoo() */
+ return tend_zoo(fid, base_path, config, phase, NELMTS(phase));
+}
diff --git a/test/genall5.h b/test/genall5.h
index 20141de..a03ccce 100644
--- a/test/genall5.h
+++ b/test/genall5.h
@@ -11,41 +11,54 @@
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-/* Programmer: John Mainzer
- * 9/4/15
- *
- * This file contains declarations of all functions defined
- * in genall5.c
+/*
+ * This file contains declarations of all functions defined in genall5.c
*/
-void create_zoo(hid_t fid, const char *base_path, int proc_num);
-void validate_zoo(hid_t fid, const char *base_path, int proc_num);
+typedef struct _zoo_config {
+ int proc_num;
+ bool continue_on_failure;
+ bool skip_compact;
+ bool skip_varlen;
+ unsigned max_pause_msecs;
+} zoo_config_t;
-void ns_grp_0(hid_t fid, const char *group_name);
-void vrfy_ns_grp_0(hid_t fid, const char *group_name);
+bool create_zoo(hid_t, const char *, zoo_config_t);
+bool validate_zoo(hid_t, const char *, zoo_config_t);
+bool delete_zoo(hid_t, const char *, zoo_config_t);
+bool validate_deleted_zoo(hid_t, const char *, zoo_config_t);
-void ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks);
-void vrfy_ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks);
+bool ns_grp_0(hid_t fid, const char *group_name);
+bool vrfy_ns_grp_0(hid_t fid, const char *group_name);
-void ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks);
-void vrfy_ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks);
+bool ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks);
+bool vrfy_ns_grp_c(hid_t fid, const char *group_name, unsigned nlinks);
-void os_grp_0(hid_t fid, const char *group_name);
-void vrfy_os_grp_0(hid_t fid, const char *group_name);
+bool ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks);
+bool vrfy_ns_grp_d(hid_t fid, const char *group_name, unsigned nlinks);
-void os_grp_n(hid_t fid, const char *group_name, int proc_num, unsigned nlinks);
-void vrfy_os_grp_n(hid_t fid, const char *group_name, int proc_num,
+bool os_grp_0(hid_t fid, const char *group_name);
+bool vrfy_os_grp_0(hid_t fid, const char *group_name);
+
+bool os_grp_n(hid_t fid, const char *group_name, int proc_num, unsigned nlinks);
+bool vrfy_os_grp_n(hid_t fid, const char *group_name, int proc_num,
unsigned nlinks);
-void ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data);
-void vrfy_ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data);
+bool ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data);
+bool vrfy_ds_ctg_i(hid_t fid, const char *dset_name, hbool_t write_data);
-void ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data);
-void vrfy_ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data);
+bool ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data);
+bool vrfy_ds_chk_i(hid_t fid, const char *dset_name, hbool_t write_data);
-void ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data);
-void vrfy_ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data);
+bool ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data);
+bool vrfy_ds_cpt_i(hid_t fid, const char *dset_name, hbool_t write_data);
-void ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data);
-void vrfy_ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data);
+bool ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data);
+bool vrfy_ds_ctg_v(hid_t fid, const char *dset_name, hbool_t write_data);
+/* Individual tests can override zoo_create_hook(), which is called
+ * after each step of create_zoo(). The `hid_t` argument identifies
+ * the file where the step was performed. The test library provides a
+ * default implementation of zoo_create_hook() that does nothing.
+ */
+void zoo_create_hook(hid_t);
diff --git a/test/h5test.c b/test/h5test.c
index 1b445dd..a13babc 100644
--- a/test/h5test.c
+++ b/test/h5test.c
@@ -150,7 +150,18 @@ h5_errors(hid_t estack, void H5_ATTR_UNUSED *client_data)
return 0;
}
-
+void
+h5_testing(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[62 + 1]; /* room for 62-char field + NUL */
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ printf("Testing %s", buf);
+ fflush(stdout);
+}
+
/*-------------------------------------------------------------------------
* Function: h5_clean_files
*
@@ -1941,6 +1952,7 @@ static const H5FD_class_t H5FD_dummy_g = {
NULL, /* truncate */
NULL, /* lock */
NULL, /* unlock */
+ NULL, /* dedup */
H5FD_FLMAP_DICHOTOMY /* fl_map */
};
diff --git a/test/h5test.h b/test/h5test.h
index 3eeb1f8..6a7ae85 100644
--- a/test/h5test.h
+++ b/test/h5test.h
@@ -101,7 +101,7 @@ H5TEST_DLLVAR MPI_Info h5_io_info_g; /* MPI INFO object for IO */
* spaces. If the h5_errors() is used for automatic error handling then
* the H5_FAILED() macro is invoked automatically when an API function fails.
*/
-#define TESTING(WHAT) {HDprintf("Testing %-62s",WHAT); HDfflush(stdout);}
+#define TESTING(...) h5_testing(__VA_ARGS__);
#define TESTING_2(WHAT) {HDprintf(" Testing %-60s",WHAT); HDfflush(stdout);}
#define PASSED() do {HDputs(" PASSED");HDfflush(stdout);} while (0)
#define H5_FAILED() {HDputs("*FAILED*");HDfflush(stdout);}
@@ -113,6 +113,7 @@ H5TEST_DLLVAR MPI_Info h5_io_info_g; /* MPI INFO object for IO */
#define FAIL_STACK_ERROR {H5_FAILED(); AT(); H5Eprint2(H5E_DEFAULT, stdout); \
goto error;}
#define FAIL_PUTS_ERROR(s) {H5_FAILED(); AT(); HDputs(s); goto error;}
+#define FAIL_PRINTF_ERROR(fmt, ...) {H5_FAILED(); AT(); HDprintf(fmt, __VA_ARGS__); goto error;}
/*
* Alarm definitions to wait up (terminate) a test that runs too long.
@@ -189,6 +190,7 @@ extern "C" {
#endif
/* Generally useful testing routines */
+H5TEST_DLL void h5_testing(const char *, ...) H5_ATTR_FORMAT(printf, 1, 2);
H5TEST_DLL void h5_clean_files(const char *base_name[], hid_t fapl);
H5TEST_DLL int h5_cleanup(const char *base_name[], hid_t fapl);
H5TEST_DLL char *h5_fixname(const char *base_name, hid_t fapl, char *fullname, size_t size);
diff --git a/test/hyperslab.c b/test/hyperslab.c
index e702023..4c84868 100644
--- a/test/hyperslab.c
+++ b/test/hyperslab.c
@@ -1051,8 +1051,7 @@ test_array_fill(size_t lo, size_t hi)
size_t u, v, w; /* Local index variables */
char s[256];
- HDsprintf(s, "array filling %4lu-%-4lu elements", (unsigned long)lo,(unsigned long)hi);
- TESTING(s);
+ TESTING("array filling %4zu-%-4zu elements", lo, hi);
/* Initialize */
if(NULL == (dst = (int *)HDcalloc(sizeof(int),ARRAY_FILL_SIZE * hi)))
@@ -1116,8 +1115,7 @@ test_array_offset_n_calc(size_t n, size_t x, size_t y, size_t z)
hsize_t new_coords[ARRAY_OFFSET_NDIMS]; /* X, Y & X coordinates of offset */
char s[256];
- HDsprintf(s, "array offset %4lux%4lux%4lu elements", (unsigned long)z,(unsigned long)y,(unsigned long)x);
- TESTING(s);
+ TESTING("array offset %4zux%4zux%4zu elements", z, y, x);
/* Initialize */
if(NULL == (a = (hsize_t *)HDmalloc(sizeof(hsize_t) * x * y *z)))
diff --git a/test/istore.c b/test/istore.c
index c8fe866..be6949e 100644
--- a/test/istore.c
+++ b/test/istore.c
@@ -280,8 +280,7 @@ test_extend(hid_t f, const char *prefix,
(unsigned long) nx, (unsigned long) ny, (unsigned long) nz);
}
- HDsprintf(s, "istore extend: %s", dims);
- TESTING(s);
+ TESTING("istore extend: %s", dims);
buf = (uint8_t *)HDmalloc(nx * ny * nz);
check = (uint8_t *)HDmalloc(nx * ny * nz);
whole = (uint8_t *)HDcalloc((size_t)1, nx * ny * nz);
@@ -493,8 +492,7 @@ test_sparse(hid_t f, const char *prefix, size_t nblocks,
(unsigned long) nx, (unsigned long) ny, (unsigned long) nz);
}
- HDsprintf(s, "istore sparse: %s", dims);
- TESTING(s);
+ TESTING("istore sparse: %s", dims);
if(skip_test){
SKIPPED()
return SUCCEED;
diff --git a/test/page_buffer.c b/test/page_buffer.c
index a508dc9..6b6de02 100644
--- a/test/page_buffer.c
+++ b/test/page_buffer.c
@@ -19,6 +19,9 @@
*
*************************************************************/
+#include <err.h>
+#include <libgen.h>
+
#include "h5test.h"
#include "H5CXprivate.h" /* API Contexts */
@@ -39,30 +42,156 @@
#define FILENAME_LEN 1024
+#ifndef H5_HAVE_PARALLEL
+#define NUM_DSETS 5
+#define NX 100
+#define NY 50
+#endif
+
+/* helper routines */
+#ifndef H5_HAVE_PARALLEL
+static unsigned create_file(char *filename, hid_t fcpl, hid_t fapl);
+static unsigned open_file(char *filename, hid_t fapl, hsize_t page_size, size_t page_buffer_size);
+#endif /* H5_HAVE_PARALLEL */
+
/* test routines */
#ifdef H5_HAVE_PARALLEL
static unsigned verify_page_buffering_disabled(hid_t orig_fapl,
const char *env_h5_drvr);
#else
-#define NUM_DSETS 5
-#define NX 100
-#define NY 50
-
static unsigned test_args(hid_t fapl, const char *env_h5_drvr);
-static unsigned test_raw_data_handling(hid_t orig_fapl, const char *env_h5_drvr);
+static unsigned test_raw_data_handling(hid_t orig_fapl, const char *env_h5_drvr,
+ bool);
static unsigned test_lru_processing(hid_t orig_fapl, const char *env_h5_drvr);
static unsigned test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr);
static unsigned test_stats_collection(hid_t orig_fapl, const char *env_h5_drvr);
-
-/* helper routines */
-static unsigned create_file(char *filename, hid_t fcpl, hid_t fapl);
-static unsigned open_file(char *filename, hid_t fapl, hsize_t page_size, size_t page_buffer_size);
#endif /* H5_HAVE_PARALLEL */
-const char *FILENAME[] = {
- "filepaged",
- NULL
-};
+#define FILENAME "filepaged"
+static const char *namebases[] = {FILENAME, NULL};
+static const char *namebase = FILENAME;
+
+static hid_t
+paging_fcpl_create(const hsize_t pgsz)
+{
+ hid_t fcpl = H5I_INVALID_HID;
+
+ if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ goto error;
+ if (H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
+ goto error;
+ if (H5Pset_file_space_page_size(fcpl, pgsz) < 0)
+ goto error;
+
+ return fcpl;
+error:
+ if (fcpl != H5I_INVALID_HID)
+ H5Pclose(fcpl);
+ return H5I_INVALID_HID;
+}
+
+static struct timespec
+print_elapsed_time(const struct timespec H5_ATTR_UNUSED *lastp,
+ const char H5_ATTR_UNUSED *fn, int H5_ATTR_UNUSED ln)
+{
+#if 0
+ uint64_t elapsed_ns;
+ struct timespec diff, now, last;
+#endif
+ struct timespec now;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
+ err(EXIT_FAILURE, "%s: clock_gettime", __func__);
+
+#if 0
+ last = (lastp == NULL) ? now : *lastp;
+
+ timespecsub(&now, &last, &diff);
+
+ elapsed_ns = (uint64_t)diff.tv_sec * (uint64_t)1000000000 + diff.tv_nsec;
+
+ printf("%5" PRIu64 ".%03" PRIu64 " %s.%d\n",
+ elapsed_ns / 1000000000, (elapsed_ns / 1000000) % 1000, fn, ln);
+#endif
+
+ return now;
+}
+
+static int
+swmr_fapl_augment(hid_t fapl, const char *filename, uint32_t max_lag)
+{
+ H5F_vfd_swmr_config_t config = {
+ .version = H5F__CURR_VFD_SWMR_CONFIG_VERSION
+ , .tick_len = 4
+ , .max_lag = max_lag
+ , .writer = true
+ , .md_pages_reserved = 128
+ };
+ const char *dname;
+ char *tname;
+
+ if ((tname = strdup(filename)) == NULL) {
+ HDfprintf(stderr, "temporary string allocation failed\n");
+ return -1;
+ }
+ dname = dirname(tname);
+ snprintf(config.md_file_path, sizeof(config.md_file_path),
+ "%s/my_md_file", dname);
+ free(tname);
+
+ /* Enable VFD SWMR configuration */
+ if(H5Pset_vfd_swmr_config(fapl, &config) < 0) {
+ HDfprintf(stderr, "H5Pset_vrd_swmr_config failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+static bool
+pgbuf_read_each_equals(H5F_t *f, H5FD_mem_t ty, haddr_t addr, size_t nelts,
+ int *data, int val)
+{
+ size_t i;
+
+ /* Read all elements using the VFD. */
+ if (H5F_block_read(f, ty, addr, sizeof(int) * nelts, data) < 0)
+ FAIL_STACK_ERROR;
+
+ for (i = 0; i < nelts; i++) {
+ if (data[i] != val) {
+ printf("%s: read %d at data[%zu], expected %d\n", __func__,
+ data[i], i, val);
+ return false;
+ }
+ }
+ return true;
+error:
+ return false;
+}
+
+static bool
+vfd_read_each_equals(H5F_t *f, H5FD_mem_t ty, haddr_t addr, size_t nelts,
+ int *data, int val)
+{
+ size_t i;
+
+ /* Read all elements using the VFD. */
+ if (H5FD_read(f->shared->lf, ty, addr, sizeof(int) * nelts, data) < 0)
+ FAIL_STACK_ERROR;
+
+ for (i = 0; i < nelts; i++) {
+ if (data[i] != val) {
+#if 0
+ printf("%s: read %d at data[%d], expected %d\n", __func__,
+ data[i], i, val);
+#endif
+ return false;
+ }
+ }
+ return true;
+error:
+ return false;
+}
#ifndef H5_HAVE_PARALLEL
@@ -204,7 +333,7 @@ error:
HDfree(data);
} H5E_END_TRY;
return(1);
-} /* create_file */
+}
/*-------------------------------------------------------------------------
@@ -248,11 +377,11 @@ open_file(char *filename, hid_t fapl, hsize_t page_size,
if(NULL == (f = (H5F_t *)H5VL_object(file_id)))
FAIL_STACK_ERROR;
- if(f->shared->page_buf == NULL)
+ if(f->shared->pb_ptr == NULL)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->page_size != page_size)
+ if(f->shared->pb_ptr->page_size != page_size)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->max_size != page_buffer_size)
+ if(f->shared->pb_ptr->max_size != page_buffer_size)
FAIL_STACK_ERROR;
if((grp_id = H5Gopen2(file_id, "GROUP", H5P_DEFAULT)) < 0)
@@ -342,7 +471,7 @@ set_multi_split(const char *env_h5_drvr, hid_t fapl, hsize_t pagesize)
/* Set memb_addr aligned */
for(mt = H5FD_MEM_DEFAULT; mt < H5FD_MEM_NTYPES; mt++)
memb_addr[mt] = ((memb_addr[mt] + pagesize - 1) / pagesize) * pagesize;
- } /* end else */
+ }
/* Set multi driver with new FAPLs */
if(H5Pset_fapl_multi(fapl, memb_map, memb_fapl_arr, (const char * const *)memb_name, memb_addr, relax) < 0)
@@ -352,14 +481,14 @@ set_multi_split(const char *env_h5_drvr, hid_t fapl, hsize_t pagesize)
for(mt = H5FD_MEM_DEFAULT; mt < H5FD_MEM_NTYPES; mt++)
free(memb_name[mt]);
- } /* end if */
+ }
return 0;
error:
return 1;
-} /* set_multi_split() */
+}
#ifndef H5_HAVE_PARALLEL
@@ -382,6 +511,10 @@ error:
* Programmer: unknown
* ?? / ?? / ??
*
+ * Changes: Minor updates to adapt to new implementation of page
+ * buffer.
+ * JRM -- 10//26/18
+ *
*-------------------------------------------------------------------------
*/
@@ -396,7 +529,7 @@ test_args(hid_t orig_fapl, const char *env_h5_drvr)
TESTING("Settings for Page Buffering");
- h5_fixname(FILENAME[0], orig_fapl, filename, sizeof(filename));
+ h5_fixname(namebase, orig_fapl, filename, sizeof(filename));
if((fapl = H5Pcopy(orig_fapl)) < 0) TEST_ERROR
@@ -421,7 +554,7 @@ test_args(hid_t orig_fapl, const char *env_h5_drvr)
/* Test setting a page buffer with a size smaller than a single
* page size - should fail
*/
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
TEST_ERROR;
if(H5Pset_file_space_page_size(fcpl, 512) < 0)
@@ -452,7 +585,7 @@ test_args(hid_t orig_fapl, const char *env_h5_drvr)
TEST_ERROR;
/* Test setting a page buffer with a size equal to a single page size */
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
TEST_ERROR;
if(H5Pset_file_space_page_size(fcpl, 512) < 0)
@@ -471,7 +604,7 @@ test_args(hid_t orig_fapl, const char *env_h5_drvr)
/* Test setting a page buffer with a size slightly larger than a
* single page size
*/
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
TEST_ERROR;
if(H5Pset_file_space_page_size(fcpl, 512) < 0)
@@ -491,7 +624,7 @@ test_args(hid_t orig_fapl, const char *env_h5_drvr)
/* Test setting a large page buffer size and page size */
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
TEST_ERROR;
if(H5Pset_file_space_page_size(fcpl, 4194304) < 0)
@@ -511,7 +644,7 @@ test_args(hid_t orig_fapl, const char *env_h5_drvr)
/* Test setting a 512 byte page buffer size and page size */
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
TEST_ERROR;
if(H5Pset_file_space_page_size(fcpl, 512) < 0)
@@ -541,20 +674,495 @@ error:
} /* test_args */
-/*-------------------------------------------------------------------------
+/*
+ * Function: test_mpmde_delay_basic()
+ *
+ * Purpose: Check that a multi-page metadata entry
+ * (MPMDE) is not written immediately to the HDF5 file in
+ * VFD SWMR mode, but it is buffered in the shadow file
+ * until max_lag + 1 ticks have elapsed. Furthermore,
+ * check that it appears *immediately* after max_lag + 1
+ * ticks, since the LRU list does not hold onto MPMDEs.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: David Young
+ * 16 Sep 2019
+ */
+static unsigned
+test_mpmde_delay_basic(hid_t orig_fapl, const char *env_h5_drvr)
+{
+ char filename[FILENAME_LEN]; /* Filename to use */
+ hid_t file_id = -1; /* File ID */
+ hid_t fcpl = -1;
+ hid_t fapl = -1;
+ size_t i, num_elements = 2000;
+ int *data = NULL, *odata = NULL;
+ H5F_t *f;
+ const uint32_t max_lag = 5;
+ hsize_t pgsz = sizeof(int) * 200;
+ haddr_t addr;
+
+ TESTING("Multipage Metadata Delay Handling");
+
+ h5_fixname(namebase, orig_fapl, filename, sizeof(filename));
+
+ if ((fapl = H5Pcopy(orig_fapl)) < 0)
+ TEST_ERROR
+
+ if (set_multi_split(env_h5_drvr, fapl, pgsz) != 0)
+ TEST_ERROR;
+
+ if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ TEST_ERROR;
+ if (H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
+ TEST_ERROR;
+ if (H5Pset_file_space_page_size(fcpl, pgsz) < 0)
+ TEST_ERROR;
+ if (H5Pset_page_buffer_size(fapl, 10 * pgsz, 0, 0) < 0)
+ TEST_ERROR;
+
+ if (swmr_fapl_augment(fapl, filename, max_lag) < 0)
+ TEST_ERROR;
+
+ if ((file_id = H5Fcreate(filename, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get a pointer to the internal file object */
+ if (NULL == (f = (H5F_t *)H5VL_object(file_id)))
+ FAIL_STACK_ERROR;
+
+ addr = H5MF_alloc(f, H5FD_MEM_BTREE, sizeof(int) * num_elements);
+ /* allocate space for 2000 elements */
+ if (HADDR_UNDEF == addr)
+ FAIL_STACK_ERROR;
+
+ if ((odata = (int *)HDcalloc(num_elements, sizeof(int))) == NULL)
+ TEST_ERROR;
+
+ if ((data = (int *)HDcalloc(num_elements, sizeof(int))) == NULL)
+ TEST_ERROR;
+
+ /* initialize all the elements to have a value of -1 */
+ for(i = 0; i < num_elements; i++)
+ odata[i] = -1;
+
+ if (H5F_block_write(f, H5FD_MEM_BTREE, addr, sizeof(int) * num_elements,
+ odata) < 0)
+ FAIL_STACK_ERROR;
+
+ /* H5Fvfd_swmr_end_tick() processes delayed writes before it increases
+ * the tick number, so it takes `max_lag + 1` times through this loop
+ * for a multi-page metadata write to make it to the HDF5 file.
+ */
+ for (i = 0; i < max_lag + 1; i++) {
+ /* All elements read using the VFD should be 0. */
+ if (!vfd_read_each_equals(f, H5FD_MEM_BTREE, addr, num_elements,
+ data, 0))
+ TEST_ERROR;
+ H5Fvfd_swmr_end_tick(file_id);
+ }
+
+ /* It is not necessary to flush the page buffer because delayed
+ * multi-page metadata buffers are flushed *immediately*
+ * when their delay elapses.
+ *
+ * (If we were waiting for a single-page metadata buffer to
+ * appear at the VFD layer, then it may reside in the LRU queue
+ * for a while.)
+ */
+#if 0
+ if (H5PB_flush(f) < 0)
+ FAIL_STACK_ERROR;
+#endif
+
+ /* All elements read using the VFD should be -1. */
+ if (!vfd_read_each_equals(f, H5FD_MEM_BTREE, addr, num_elements, data, -1))
+ TEST_ERROR;
+
+ if (H5Fclose(file_id) < 0)
+ FAIL_STACK_ERROR;
+ if (H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+ if (H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ HDfree(data);
+ HDfree(odata);
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ if (fapl != H5I_INVALID_HID)
+ H5Pclose(fapl);
+ if (fcpl != H5I_INVALID_HID)
+ H5Pclose(fcpl);
+ if (file_id != H5I_INVALID_HID)
+ H5Fclose(file_id);
+ if (data != NULL)
+ HDfree(data);
+ if (odata != NULL)
+ HDfree(odata);
+ } H5E_END_TRY;
+ return 1;
+}
+
+
+/*
+ * Function: test_spmde_lru_evict_basic()
+ *
+ * Purpose: Check that once a single-page metadata entry
+ * (SPMDE) is eligible to be written to the HDF5 file
+ * (because it has resided unchanged in the metadata file
+ * for max_lag + 1 ticks), filling the page buffer to
+ * capacity causes the entry to be flushed.
+ *
+ * Further check that the page was evicted by writing
+ * changes to the VFD layer ("under" the page buffer)
+ * and trying to read the changes back through the page
+ * buffer: stale page-buffer content should not shadow
+ * the changes.
+ *
+ * XXX
+ * XXX reduce duplication with test_spmde_delay_basic!
+ * XXX
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: David Young
+ * 16 Sep 2019
+ */
+static unsigned
+test_spmde_lru_evict_basic(hid_t orig_fapl, const char *env_h5_drvr)
+{
+ char filename[FILENAME_LEN]; /* Filename to use */
+ hid_t file_id = -1; /* File ID */
+ hid_t fcpl = -1;
+ hid_t fapl = -1;
+ size_t i, num_elements = 20;
+ int *data = NULL, *odata = NULL;
+ H5F_t *f;
+ const uint32_t max_lag = 5;
+ const hsize_t pgsz = sizeof(int) * 200;
+ const hsize_t pgbufsz = 10 * pgsz;
+ hsize_t ofs;
+ bool flushed;
+ struct timespec last;
+ haddr_t addr;
+ haddr_t pressure;
+
+ TESTING("Single Page Metadata Flush & Eviction Handling");
+
+ h5_fixname(namebase, orig_fapl, filename, sizeof(filename));
+
+ last = print_elapsed_time(NULL, __func__, __LINE__);
+ if ((fapl = H5Pcopy(orig_fapl)) < 0)
+ TEST_ERROR
+
+ if (set_multi_split(env_h5_drvr, fapl, pgsz) != 0)
+ TEST_ERROR;
+
+ if ((fcpl = paging_fcpl_create(pgsz)) < 0)
+ FAIL_STACK_ERROR;
+
+ if (H5Pset_page_buffer_size(fapl, pgbufsz, 0, 0) < 0)
+ FAIL_STACK_ERROR;
+
+ if (swmr_fapl_augment(fapl, filename, max_lag) < 0)
+ FAIL_STACK_ERROR;
+
+ if ((file_id = H5Fcreate(filename, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get a pointer to the internal file object */
+ if (NULL == (f = (H5F_t *)H5VL_object(file_id)))
+ FAIL_STACK_ERROR;
+
+ last = print_elapsed_time(&last, __func__, __LINE__);
+
+ /* Allocate a region whose pages we can write to force eviction of
+ * least-recently used pages.
+ */
+ pressure = H5MF_alloc(f, H5FD_MEM_BTREE, pgbufsz);
+ if (HADDR_UNDEF == pressure)
+ FAIL_STACK_ERROR;
+
+ /* Allocate a whole page for our 20 elements so that they do
+ * not share a "hot" page with something else.
+ */
+ addr = H5MF_alloc(f, H5FD_MEM_BTREE, pgsz);
+ if (HADDR_UNDEF == addr)
+ FAIL_STACK_ERROR;
+
+ if ((odata = (int *)HDcalloc(num_elements, sizeof(int))) == NULL)
+ TEST_ERROR;
+
+ if ((data = (int *)HDcalloc(num_elements, sizeof(int))) == NULL)
+ TEST_ERROR;
+
+ /* initialize all the elements to have a value of -1 */
+ for(i = 0; i < num_elements; i++)
+ odata[i] = -1;
+
+ if (H5F_block_write(f, H5FD_MEM_BTREE, addr, sizeof(int) * num_elements,
+ odata) < 0)
+ FAIL_STACK_ERROR;
+
+ last = print_elapsed_time(&last, __func__, __LINE__);
+
+ /* H5Fvfd_swmr_end_tick() processes delayed writes before it increases
+ * the tick number, so only after `max_lag + 1` times through this loop
+ * is a metadata write eligible to be written to the HDF5 file.
+ */
+ for (i = 0; i < max_lag + 1; i++) {
+ /* All elements read using the VFD should be 0. */
+ if (!vfd_read_each_equals(f, H5FD_MEM_BTREE, addr, num_elements,
+ data, 0))
+ TEST_ERROR;
+ H5Fvfd_swmr_end_tick(file_id);
+ }
+
+ last = print_elapsed_time(&last, __func__, __LINE__);
+
+ flushed = false;
+ /* We are waiting for a single-page metadata buffer to
+ * appear at the VFD layer, but it may reside in the LRU queue.
+ * Dirty new blocks to apply pressure on the page buffer so that
+ * it empties the LRU queue. Writing to N distinct pages,
+ * for N the number of pages in the page buffer, ought to do
+ * the trick.
+ */
+ for (ofs = 0; ofs < pgbufsz - pgsz * 2; ofs += pgsz) {
+ int tmp = -1;
+ if (H5F_block_write(f, H5FD_MEM_BTREE, pressure + ofs,
+ sizeof(tmp), &tmp) < 0)
+ FAIL_STACK_ERROR;
+ if (!flushed &&
+ vfd_read_each_equals(f, H5FD_MEM_BTREE, addr, num_elements,
+ data, -1)) {
+ flushed = true;
+#if 0
+ printf("Writing page %" PRIuHSIZE " flushed target page.\n",
+ ofs / pgsz);
+#endif
+ }
+ }
+
+ last = print_elapsed_time(&last, __func__, __LINE__);
+
+ if (!vfd_read_each_equals(f, H5FD_MEM_BTREE, addr, num_elements, data, -1))
+ TEST_ERROR;
+
+ /* initialize all the elements to have a value of -2 */
+ for(i = 0; i < num_elements; i++)
+ odata[i] = -2;
+ /* Write -2 to our target page using the VFD. */
+ if (H5FD_write(f->shared->lf, H5FD_MEM_BTREE, addr,
+ sizeof(int) * num_elements, odata) < 0)
+ FAIL_STACK_ERROR;
+
+ last = print_elapsed_time(&last, __func__, __LINE__);
+
+ /* All elements read through the page buffer should be -2. That is,
+ * no page-buffer entry should shadow the page.
+ */
+ if (!pgbuf_read_each_equals(f, H5FD_MEM_BTREE, addr, num_elements, data,
+ -2))
+ TEST_ERROR;
+
+ last = print_elapsed_time(&last, __func__, __LINE__);
+
+ /* Force ticks to occur so that H5Fclose() doesn't pause waiting
+ * for them to elapse.
+ */
+ for (i = 0; i < max_lag + 1; i++)
+ H5Fvfd_swmr_end_tick(file_id);
+
+ if (H5Fclose(file_id) < 0)
+ FAIL_STACK_ERROR;
+ if (H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+ if (H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ HDfree(data);
+ HDfree(odata);
+
+ last = print_elapsed_time(&last, __func__, __LINE__);
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ if (fapl != H5I_INVALID_HID)
+ H5Pclose(fapl);
+ if (fcpl != H5I_INVALID_HID)
+ H5Pclose(fcpl);
+ if (file_id != H5I_INVALID_HID)
+ H5Fclose(file_id);
+ if (data != NULL)
+ HDfree(data);
+ if (odata != NULL)
+ HDfree(odata);
+ } H5E_END_TRY;
+ return 1;
+}
+
+
+/*
+ * Function: test_spmde_delay_basic()
+ *
+ * Purpose: Check that a single-page metadata entry
+ * (SPMDE) is not written immediately to the HDF5 file in
+ * VFD SWMR mode, but it is buffered in the shadow file
+ * until max_lag + 1 ticks have elapsed.
+ *
+ * The LRU list will hold onto SPMDEs, so it's necessary to
+ * flush the page buffer to make sure the buffer is flushed
+ * to the VFD layer.
+ *
+ * XXX
+ * XXX reduce duplication with test_mpmde_delay_basic!
+ * XXX
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: David Young
+ * 16 Sep 2019
+ */
+static unsigned
+test_spmde_delay_basic(hid_t orig_fapl, const char *env_h5_drvr)
+{
+ char filename[FILENAME_LEN]; /* Filename to use */
+ hid_t file_id = -1; /* File ID */
+ hid_t fcpl = -1;
+ hid_t fapl = -1;
+ size_t i, num_elements = 20;
+ int *data = NULL, *odata = NULL;
+ H5F_t *f;
+ const uint32_t max_lag = 5;
+ hsize_t pgsz = sizeof(int) * 200;
+ haddr_t addr;
+
+ TESTING("Single Page Metadata Delay Handling");
+
+ h5_fixname(namebase, orig_fapl, filename, sizeof(filename));
+
+ if ((fapl = H5Pcopy(orig_fapl)) < 0)
+ TEST_ERROR
+
+ if (set_multi_split(env_h5_drvr, fapl, pgsz) != 0)
+ TEST_ERROR;
+
+ if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ TEST_ERROR;
+ if (H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
+ TEST_ERROR;
+ if (H5Pset_file_space_page_size(fcpl, pgsz) < 0)
+ TEST_ERROR;
+ if (H5Pset_page_buffer_size(fapl, 10 * pgsz, 0, 0) < 0)
+ TEST_ERROR;
+
+ if (swmr_fapl_augment(fapl, filename, max_lag) < 0)
+ TEST_ERROR;
+
+ if ((file_id = H5Fcreate(filename, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get a pointer to the internal file object */
+ if (NULL == (f = (H5F_t *)H5VL_object(file_id)))
+ FAIL_STACK_ERROR;
+
+ addr = H5MF_alloc(f, H5FD_MEM_BTREE,
+ sizeof(int) * num_elements);
+ /* allocate space for 2000 elements */
+ if (HADDR_UNDEF == addr)
+ FAIL_STACK_ERROR;
+
+ if ((odata = (int *)HDcalloc(num_elements, sizeof(int))) == NULL)
+ TEST_ERROR;
+
+ if ((data = (int *)HDcalloc(num_elements, sizeof(int))) == NULL)
+ TEST_ERROR;
+
+ /* initialize all the elements to have a value of -1 */
+ for(i = 0; i < num_elements; i++)
+ odata[i] = -1;
+
+ if (H5F_block_write(f, H5FD_MEM_BTREE, addr, sizeof(int) * num_elements,
+ odata) < 0)
+ FAIL_STACK_ERROR;
+
+ /* H5Fvfd_swmr_end_tick() processes delayed writes before it increases
+ * the tick number, so only after `max_lag + 1` times through this loop
+ * is a metadata write eligible to be written to the HDF5 file.
+ */
+ for (i = 0; i < max_lag + 1; i++) {
+ /* All elements read using the VFD should be 0. */
+ if (!vfd_read_each_equals(f, H5FD_MEM_BTREE, addr, num_elements,
+ data, 0))
+ TEST_ERROR;
+ H5Fvfd_swmr_end_tick(file_id);
+ }
+
+ /* We are waiting for a single-page metadata buffer to
+ * appear at the VFD layer, but it may reside in the LRU queue
+ * for a while if we do not flush the page buffer.
+ */
+ if (H5PB_flush(f->shared) < 0)
+ FAIL_STACK_ERROR;
+
+ /* All elements read using the VFD should be -1. */
+ if (!vfd_read_each_equals(f, H5FD_MEM_BTREE, addr, num_elements, data, -1))
+ TEST_ERROR;
+
+ if (H5Fclose(file_id) < 0)
+ FAIL_STACK_ERROR;
+ if (H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+ if (H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ HDfree(data);
+ HDfree(odata);
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ if (fapl != H5I_INVALID_HID)
+ H5Pclose(fapl);
+ if (fcpl != H5I_INVALID_HID)
+ H5Pclose(fcpl);
+ if (file_id != H5I_INVALID_HID)
+ H5Fclose(file_id);
+ if (data != NULL)
+ HDfree(data);
+ if (odata != NULL)
+ HDfree(odata);
+ } H5E_END_TRY;
+ return 1;
+}
+
+
+/*
* Function: test_raw_data_handling()
*
- * Purpose: The purpose of this function appears to be a smoke check
- * of raw data reads and writes via the page buffer.
+ * Purpose: Check that raw data is written to the HDF5
+ * file when expected whether in VFD SWMR mode or not.
*
- * Any data mis-matches or failures reported by the HDF5
- * library result in test failure.
+ * Any data mis-matches or failures reported by the HDF5
+ * library result in test failure.
*
* Return: 0 if test is sucessful
* 1 if test fails
*
- * Programmer: unknown
- * ?? / ?? / ??
+ * Programmer: David Young
+ * 16 Sep 2019
*
* Changes: Added base_page_cnt field as supporting code. This allows
* the test to adjust to the number of page buffer pages
@@ -567,47 +1175,51 @@ error:
*
* JRM -- 2/23/17
*
- *-------------------------------------------------------------------------
+ * Minor changes to adapt to re-implementation of the
+ * page buffer.
+ *
+ * JRM -- 10/26/18
*/
/* Changes due to file space page size has a minimum size of 512 */
static unsigned
-test_raw_data_handling(hid_t orig_fapl, const char *env_h5_drvr)
+test_raw_data_handling(hid_t orig_fapl, const char *env_h5_drvr,
+ bool vfd_swmr_mode)
{
char filename[FILENAME_LEN]; /* Filename to use */
hid_t file_id = -1; /* File ID */
hid_t fcpl = -1;
hid_t fapl = -1;
- size_t base_page_cnt;
- size_t page_count = 0;
+ int64_t base_page_cnt;
+ int64_t page_count = 0;
int i, num_elements = 2000;
haddr_t addr = HADDR_UNDEF;
int *data = NULL;
H5F_t *f = NULL;
+ const uint32_t max_lag = 5;
- TESTING("Raw Data Handling");
+ TESTING("%sRaw Data Handling", vfd_swmr_mode ? "VFD SWMR " : "");
- h5_fixname(FILENAME[0], orig_fapl, filename, sizeof(filename));
+ h5_fixname(namebase, orig_fapl, filename, sizeof(filename));
- if((fapl = H5Pcopy(orig_fapl)) < 0)
+ if ((fapl = H5Pcopy(orig_fapl)) < 0)
TEST_ERROR
- if(set_multi_split(env_h5_drvr, fapl, sizeof(int)*200) != 0)
+ if (set_multi_split(env_h5_drvr, fapl, sizeof(int) * 200) != 0)
TEST_ERROR;
- if((data = (int *)HDcalloc((size_t)num_elements, sizeof(int))) == NULL)
+ if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
TEST_ERROR;
-
- if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ if (H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
TEST_ERROR;
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if (H5Pset_file_space_page_size(fcpl, sizeof(int) * 200) < 0)
TEST_ERROR;
- if(H5Pset_file_space_page_size(fcpl, sizeof(int)*200) < 0)
- TEST_ERROR;
- if(H5Pset_page_buffer_size(fapl, sizeof(int)*2000, 0, 0) < 0)
+ if (H5Pset_page_buffer_size(fapl, sizeof(int) * 2000, 0, 0) < 0)
TEST_ERROR;
- if((file_id = H5Fcreate(filename, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
+ if (vfd_swmr_mode && swmr_fapl_augment(fapl, filename, max_lag) < 0)
+ TEST_ERROR;
+ if ((file_id = H5Fcreate(filename, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
FAIL_STACK_ERROR;
/* Get a pointer to the internal file object */
@@ -616,129 +1228,139 @@ test_raw_data_handling(hid_t orig_fapl, const char *env_h5_drvr)
/* opening the file inserts one or more pages into the page buffer.
* Get the number of pages inserted, and verify that it is the
- * the expected value.
+ * expected value.
*/
- base_page_cnt = H5SL_count(f->shared->page_buf->slist_ptr);
- if(base_page_cnt != 1)
+ base_page_cnt = f->shared->pb_ptr->curr_pages;
+ if (base_page_cnt != 2)
TEST_ERROR;
- /* allocate space for a 2000 elements */
- if(HADDR_UNDEF == (addr = H5MF_alloc(f, H5FD_MEM_DRAW, sizeof(int)*(size_t)num_elements)))
+ /* allocate space for 2000 elements */
+ if (HADDR_UNDEF == (addr = H5MF_alloc(f, H5FD_MEM_DRAW, sizeof(int) * (size_t)num_elements)))
FAIL_STACK_ERROR;
+ if ((data = (int *)HDcalloc((size_t)num_elements, sizeof(int))) == NULL)
+ TEST_ERROR;
+
/* initialize all the elements to have a value of -1 */
for(i=0 ; i<num_elements ; i++)
data[i] = -1;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr, sizeof(int)*(size_t)num_elements, data) < 0)
+ if (H5F_block_write(f, H5FD_MEM_DRAW, addr, sizeof(int) * (size_t)num_elements, data) < 0)
FAIL_STACK_ERROR;
- /* update the first 50 elements to have values 0-49 - this will be
+ /* update the first 100 elements to have values 0-99 - this will be
a page buffer update with 1 page resulting in the page
buffer. */
- /* Changes: 100 */
for(i=0 ; i<100 ; i++)
data[i] = i;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr, sizeof(int)*100, data) < 0)
+ if (H5F_block_write(f, H5FD_MEM_DRAW, addr, sizeof(int) * 100, data) < 0)
FAIL_STACK_ERROR;
page_count ++;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
FAIL_STACK_ERROR;
/* update elements 300 - 450, with values 300 - - this will
bring two more pages into the page buffer. */
for(i=0 ; i<150 ; i++)
data[i] = i+300;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*300), sizeof(int)*150, data) < 0)
+ if (H5F_block_write(f, H5FD_MEM_DRAW, addr + (sizeof(int) * 300), sizeof(int) * 150, data) < 0)
FAIL_STACK_ERROR;
page_count += 2;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
FAIL_STACK_ERROR;
/* update elements 100 - 300, this will go to disk but also update
existing pages in the page buffer. */
for(i=0 ; i<200 ; i++)
data[i] = i+100;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*100), sizeof(int)*200, data) < 0)
+ if (H5F_block_write(f, H5FD_MEM_DRAW, addr + (sizeof(int) * 100), sizeof(int) * 200, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
FAIL_STACK_ERROR;
/* Update elements 225-300 - this will update an existing page in the PB */
/* Changes: 450 - 600; 150 */
for(i=0 ; i<150 ; i++)
data[i] = i+450;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*450), sizeof(int)*150, data) < 0)
+ if (H5F_block_write(f, H5FD_MEM_DRAW, addr + (sizeof(int) * 450), sizeof(int) * 150, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
FAIL_STACK_ERROR;
/* Do a full page write to block 600-800 - should bypass the PB */
for(i=0 ; i<200 ; i++)
data[i] = i+600;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*600), sizeof(int)*200, data) < 0)
+ if (H5F_block_write(f, H5FD_MEM_DRAW, addr + (sizeof(int) * 600), sizeof(int) * 200, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
FAIL_STACK_ERROR;
/* read elements 800 - 1200, this should not affect the PB, and should read -1s */
- if(H5F_block_read(f, H5FD_MEM_DRAW, addr+(sizeof(int)*800), sizeof(int)*400, data) < 0)
+ if (H5F_block_read(f, H5FD_MEM_DRAW, addr + (sizeof(int) * 800), sizeof(int) * 400, data) < 0)
FAIL_STACK_ERROR;
for (i=0; i < 400; i++) {
- if(data[i] != -1) {
+ if (data[i] != -1) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n", i, data[i], -1);
FAIL_STACK_ERROR;
}
}
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
FAIL_STACK_ERROR;
/* read elements 1200 - 1201, this should read -1 and bring in an
* entire page of addr 1200
*/
- if(H5F_block_read(f, H5FD_MEM_DRAW, addr+(sizeof(int)*1200), sizeof(int)*1, data) < 0)
+ if (H5F_block_read(f, H5FD_MEM_DRAW, addr + (sizeof(int) * 1200), sizeof(int) * 1, data) < 0)
FAIL_STACK_ERROR;
for (i=0; i < 1; i++) {
- if(data[i] != -1) {
+ if (data[i] != -1) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n", i, data[i], -1);
TEST_ERROR;
}
}
page_count ++;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
TEST_ERROR;
/* read elements 175 - 225, this should use the PB existing pages */
/* Changes: 350 - 450 */
/* read elements 175 - 225, this should use the PB existing pages */
- if(H5F_block_read(f, H5FD_MEM_DRAW, addr+(sizeof(int)*350), sizeof(int)*100, data) < 0)
+ if (H5F_block_read(f, H5FD_MEM_DRAW, addr + (sizeof(int) * 350), sizeof(int) * 100, data) < 0)
FAIL_STACK_ERROR;
for (i=0; i < 100; i++) {
- if(data[i] != i+350) {
+ if (data[i] != i + 350) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n", i, data[i],
+ i + 350);
TEST_ERROR;
}
}
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
TEST_ERROR;
/* read elements 0 - 800 using the VFD.. this should result in -1s
except for the writes that went through the PB (100-300 & 600-800) */
- if(H5FD_read(f->shared->lf, H5FD_MEM_DRAW, addr, sizeof(int)*800, data) < 0)
+ if (H5FD_read(f->shared->lf, H5FD_MEM_DRAW, addr, sizeof(int) * 800, data) < 0)
FAIL_STACK_ERROR;
i = 0;
while (i < 800) {
- if((i>=100 && i<300) || (i>=600)) {
- if(data[i] != i) {
+ if((i>=100 && i<300) || i >= 600) {
+ if (data[i] != i) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n",
+ i, data[i], i);
TEST_ERROR;
}
}
else {
- if(data[i] != -1) {
+ if (data[i] != -1) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n",
+ i, data[i], -1);
TEST_ERROR;
}
}
@@ -748,13 +1370,15 @@ test_raw_data_handling(hid_t orig_fapl, const char *env_h5_drvr)
/* read elements 0 - 800 using the PB.. this should result in all
* what we have written so far and should get the updates from the PB
*/
- if(H5F_block_read(f, H5FD_MEM_DRAW, addr, sizeof(int)*800, data) < 0)
+ if (H5F_block_read(f, H5FD_MEM_DRAW, addr, sizeof(int) * 800, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
TEST_ERROR;
for (i=0; i < 800; i++) {
- if(data[i] != i) {
+ if (data[i] != i) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n",
+ i, data[i], i);
TEST_ERROR;
}
}
@@ -765,41 +1389,45 @@ test_raw_data_handling(hid_t orig_fapl, const char *env_h5_drvr)
*/
for(i=0 ; i<1000 ; i++)
data[i] = 0;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*400), sizeof(int)*1000, data) < 0)
+ if (H5F_block_write(f, H5FD_MEM_DRAW, addr + (sizeof(int) * 400), sizeof(int) * 1000, data) < 0)
FAIL_STACK_ERROR;
page_count -= 2;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
TEST_ERROR;
/* read elements 0 - 1000.. this should go to disk then update the
* buffer result 200-400 with existing pages
*/
- if(H5F_block_read(f, H5FD_MEM_DRAW, addr, sizeof(int)*1000, data) < 0)
+ if (H5F_block_read(f, H5FD_MEM_DRAW, addr, sizeof(int) * 1000, data) < 0)
FAIL_STACK_ERROR;
i=0;
while (i < 1000) {
if(i<400) {
- if(data[i] != i) {
+ if (data[i] != i) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n",
+ i, data[i], i);
TEST_ERROR;
}
}
else {
- if(data[i] != 0) {
+ if (data[i] != 0) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n",
+ i, data[i], 0);
TEST_ERROR;
}
}
i++;
}
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
+ if (f->shared->pb_ptr->curr_pages != page_count + base_page_cnt)
TEST_ERROR;
- if(H5Fclose(file_id) < 0)
+ if (H5Fclose(file_id) < 0)
FAIL_STACK_ERROR;
- if(H5Pclose(fcpl) < 0)
+ if (H5Pclose(fcpl) < 0)
FAIL_STACK_ERROR;
- if(H5Pclose(fapl) < 0)
+ if (H5Pclose(fapl) < 0)
FAIL_STACK_ERROR;
HDfree(data);
@@ -808,10 +1436,13 @@ test_raw_data_handling(hid_t orig_fapl, const char *env_h5_drvr)
error:
H5E_BEGIN_TRY {
- H5Pclose(fapl);
- H5Pclose(fcpl);
- H5Fclose(file_id);
- if(data)
+ if (fapl != H5I_INVALID_HID)
+ H5Pclose(fapl);
+ if (fcpl != H5I_INVALID_HID)
+ H5Pclose(fcpl);
+ if (file_id != H5I_INVALID_HID)
+ H5Fclose(file_id);
+ if (data != NULL)
HDfree(data);
} H5E_END_TRY;
return 1;
@@ -832,17 +1463,12 @@ error:
*
* Programmer: unknown
* ?? / ?? / ??
+ *
+ * Changes: Reworked for new implementation of page buffer. Major
+ * change was adaption to the new implementation's greater
+ * respect for max_pages.
*
- * Changes: Added base_page_cnt field as supporting code. This allows
- * the test to adjust to the number of page buffer pages
- * accessed during file open / create.
- *
- * The test for the value of base_page_cnt just after file
- * open exists detect changes in library behavior. Assuming
- * any such change is not indicative of other issues, these
- * tests can be modified to reflect the change.
- *
- * JRM -- 2/23/17
+ * JRM -- 10/26/18
*
*
*-------------------------------------------------------------------------
@@ -852,11 +1478,10 @@ static unsigned
test_lru_processing(hid_t orig_fapl, const char *env_h5_drvr)
{
char filename[FILENAME_LEN]; /* Filename to use */
+ hbool_t page_exists;
hid_t file_id = -1; /* File ID */
hid_t fcpl = -1;
hid_t fapl = -1;
- size_t base_page_cnt;
- size_t page_count = 0;
int i;
int num_elements = 2000;
haddr_t addr = HADDR_UNDEF;
@@ -866,7 +1491,7 @@ test_lru_processing(hid_t orig_fapl, const char *env_h5_drvr)
TESTING("LRU Processing");
- h5_fixname(FILENAME[0], orig_fapl, filename, sizeof(filename));
+ h5_fixname(namebase, orig_fapl, filename, sizeof(filename));
if((fapl = H5Pcopy(orig_fapl)) < 0)
FAIL_STACK_ERROR
@@ -880,7 +1505,7 @@ test_lru_processing(hid_t orig_fapl, const char *env_h5_drvr)
if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
FAIL_STACK_ERROR;
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
FAIL_STACK_ERROR;
if(H5Pset_file_space_page_size(fcpl, sizeof(int)*200) < 0)
@@ -897,28 +1522,25 @@ test_lru_processing(hid_t orig_fapl, const char *env_h5_drvr)
if(NULL == (f = (H5F_t *)H5VL_object(file_id)))
FAIL_STACK_ERROR;
- /* opening the file inserts one or more pages into the page buffer.
- * Get the number of pages inserted, and verify that it is the
- * the expected value.
- */
- base_page_cnt = H5SL_count(f->shared->page_buf->slist_ptr);
- if(base_page_cnt != 1)
- TEST_ERROR;
-
- /* allocate space for a 2000 elements */
- if(HADDR_UNDEF == (addr = H5MF_alloc(f, H5FD_MEM_DRAW, sizeof(int)*(size_t)num_elements)))
+ /* allocate space for 2000 elements */
+ if(HADDR_UNDEF == (addr = H5MF_alloc(f, H5FD_MEM_DRAW,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
/* initialize all the elements to have a value of -1 */
for(i=0 ; i<num_elements ; i++)
data[i] = -1;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr, sizeof(int)*(size_t)num_elements, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, addr,
+ sizeof(int)*(size_t)num_elements, data) < 0)
+ FAIL_STACK_ERROR;
+
+ /* there should be no raw data pages in the page buffer -- verify this */
+ if (f->shared->pb_ptr->curr_rd_pages != 0)
FAIL_STACK_ERROR;
/* update the first 100 elements to have values 0-99 - this will be
- * a page buffer update with 1 page resulting in the page
- * buffer.
+ * a page buffer update that loads page addr + 0 into the page buffer.
*/
for(i=0 ; i<100 ; i++)
data[i] = i;
@@ -926,126 +1548,142 @@ test_lru_processing(hid_t orig_fapl, const char *env_h5_drvr)
if(H5F_block_write(f, H5FD_MEM_DRAW, addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- page_count ++;
+ /* verify addr + 0 is the only raw data page in the page buffer */
+ search_addr = addr;
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
+ FAIL_STACK_ERROR;
+ if (f->shared->pb_ptr->curr_rd_pages != 1)
+ FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count + base_page_cnt)
- TEST_ERROR;
/* update elements 300 - 450, with values 300 - 449 - this will
- * bring two pages into the page buffer and evict 0.
+ * bring two pages (addr + 200 & addr + 400) into the page buffer and
+ * evict addr + 0.
*/
for(i=0 ; i<150 ; i++)
data[i] = i+300;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*300), sizeof(int)*150, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*300),
+ sizeof(int)*150, data) < 0)
FAIL_STACK_ERROR;
- page_count = 2;
+ /* verify that addr + 200 and addr + 400 are the only raw data pages in
+ * the page buffer.
+ */
+ search_addr = addr + sizeof(int)*200;
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
+ FAIL_STACK_ERROR;
+ search_addr = addr + sizeof(int)*400;
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
+ FAIL_STACK_ERROR;
+ if (f->shared->pb_ptr->curr_rd_pages != 2)
+ FAIL_STACK_ERROR;
- /* at this point, the page buffer entry created at file open should
- * have been evicted -- thus no further need to consider base_page_cnt.
+ /* at this point, the page buffer entries created at file open should
+ * have been evicted.
*/
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_md_pages != 0)
FAIL_STACK_ERROR;
- /* The two pages should be the ones with address 100 and 200; 0
- should have been evicted */
- /* Changes: 200, 400 */
- search_addr = addr;
- if(NULL != H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
+
+ /* update elements 300-301, this will update page addr + 200 in
+ * page buffer and move it to the top of the LRU.
+ */
+ for(i=0 ; i<1 ; i++)
+ data[i] = i+300;
+ if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*300),
+ sizeof(int)*2, data) < 0)
FAIL_STACK_ERROR;
+
+ /* verify that addr + 200 and addr + 400 are the only raw data pages in
+ * the page buffer.
+ */
search_addr = addr + sizeof(int)*200;
- if(NULL == H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
FAIL_STACK_ERROR;
search_addr = addr + sizeof(int)*400;
- if(NULL == H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
FAIL_STACK_ERROR;
-
- /* update elements 150-151, this will update existing pages in the
- page buffer and move it to the top of the LRU. */
- /* Changes: 300 - 301 */
- for(i=0 ; i<1 ; i++)
- data[i] = i+300;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*300), sizeof(int)*1, data) < 0)
+ if (f->shared->pb_ptr->curr_rd_pages != 2)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 2)
FAIL_STACK_ERROR;
- /* read elements 600 - 601, this should read -1 and bring in an
- entire page of addr 600, and evict page 200 */
- /* Changes: 1200 - 1201; 1200, 400 */
- if(H5F_block_read(f, H5FD_MEM_DRAW, addr+(sizeof(int)*1200), sizeof(int)*1, data) < 0)
+ /* read elements 1200 - 1201, this should read -1, bring in page
+ * addr + 1200, and evict page addr + 400
+ */
+ if(H5F_block_read(f, H5FD_MEM_DRAW, addr+(sizeof(int)*1200),
+ sizeof(int)*1, data) < 0)
FAIL_STACK_ERROR;
for (i=0; i < 1; i++) {
if(data[i] != -1) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n", i, data[i], -1);
TEST_ERROR;
- } /* end if */
- } /* end for */
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
- FAIL_STACK_ERROR;
-
- /* Changes: 400 */
- search_addr = addr + sizeof(int)*400;
- if(NULL != H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
- FAIL_STACK_ERROR;
+ }
+ }
- /* Changes: 200 */
+ /* verify that addr + 200 and addr + 1200 are the only raw data pages in
+ * the page buffer.
+ */
search_addr = addr + sizeof(int)*200;
- if(NULL == H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
FAIL_STACK_ERROR;
-
- /* Changes: 1200 */
search_addr = addr + sizeof(int)*1200;
- if(NULL == H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
+ FAIL_STACK_ERROR;
+ if (f->shared->pb_ptr->curr_rd_pages != 2)
FAIL_STACK_ERROR;
- /* read elements 175 - 225, this should move 100 to the top, evict 600 and bring in 200 */
- /* Changes: 350 - 450; 200, 1200, 400 */
- if(H5F_block_read(f, H5FD_MEM_DRAW, addr+(sizeof(int)*350), sizeof(int)*100, data) < 0)
+ if(f->shared->pb_ptr->curr_pages != 2)
+ FAIL_STACK_ERROR;
+
+ /* read elements 350 - 450, this should load page addr + 400 and move
+ * it to the top of the LRU, and evict page addr + 1200.
+ */
+ if(H5F_block_read(f, H5FD_MEM_DRAW, addr+(sizeof(int)*350),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
for (i=0; i < 100; i++) {
if(data[i] != i+350) {
HDfprintf(stderr, "Read different values than written\n");
+ HDfprintf(stderr, "data[%d] = %d, %d expected.\n", i, data[i],
+ i + 350);
TEST_ERROR;
- } /* end if */
- } /* end for */
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
- FAIL_STACK_ERROR;
-
- /* Changes: 1200 */
- search_addr = addr + sizeof(int)*1200;
- if(NULL != H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
- FAIL_STACK_ERROR;
+ }
+ }
- /* Changes: 200 */
+ /* verify that addr + 200 and addr + 400 are the only raw data pages in
+ * the page buffer.
+ */
search_addr = addr + sizeof(int)*200;
- if(NULL == H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
FAIL_STACK_ERROR;
-
- /* Changes: 400 */
search_addr = addr + sizeof(int)*400;
- if(NULL == H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
+ FAIL_STACK_ERROR;
+ if (f->shared->pb_ptr->curr_rd_pages != 2)
+ FAIL_STACK_ERROR;
+ if(f->shared->pb_ptr->curr_pages != 2)
FAIL_STACK_ERROR;
- /* update elements 200 - 700 to value 0, this will go to disk but
- also discarding existing pages from the PB (page 200). */
- /* Changes: 400 - 1400; 400 */
+
+ /* update elements 400 - 1400 to value 0, this will overwrite and
+ * evict page addr + 400.
+ */
for(i=0 ; i<1000 ; i++)
data[i] = 0;
- if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*400), sizeof(int)*1000, data) < 0)
- FAIL_STACK_ERROR;
- page_count -= 1;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, addr+(sizeof(int)*400),
+ sizeof(int)*1000, data) < 0)
FAIL_STACK_ERROR;
- /* Changes: 200 */
+ /* verify that addr + 200 is the only raw data page in the page buffer.
+ */
search_addr = addr + sizeof(int)*200;
- if(NULL == H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
+ if((H5PB_page_exists(f->shared, search_addr, &page_exists) < 0) || (!page_exists))
FAIL_STACK_ERROR;
-
- /* Changes: 400 */
- search_addr = addr + sizeof(int)*400;
- if(NULL != H5SL_search(f->shared->page_buf->slist_ptr, &(search_addr)))
+ if (f->shared->pb_ptr->curr_rd_pages != 1)
+ FAIL_STACK_ERROR;
+ if(f->shared->pb_ptr->curr_pages != 1)
FAIL_STACK_ERROR;
if(H5Fclose(file_id) < 0)
@@ -1061,10 +1699,13 @@ test_lru_processing(hid_t orig_fapl, const char *env_h5_drvr)
error:
H5E_BEGIN_TRY {
- H5Pclose(fapl);
- H5Pclose(fcpl);
- H5Fclose(file_id);
- if(data)
+ if (fapl != H5I_INVALID_HID)
+ H5Pclose(fapl);
+ if (fcpl != H5I_INVALID_HID)
+ H5Pclose(fcpl);
+ if (file_id != H5I_INVALID_HID)
+ H5Fclose(file_id);
+ if (data != NULL)
HDfree(data);
} H5E_END_TRY;
return 1;
@@ -1099,6 +1740,14 @@ error:
*
* JRM -- 2/23/17
*
+ * Reworked test for new implementatin of the page buffer.
+ * The major change was adapting the test for the new
+ * page buffers refusal to buffer any raw data when
+ * min_md_pages == max_pages, or any metadata pages wwhen
+ * min_rd_pages == max_pages.
+ *
+ * JRM -- 10/27/18
+ *
*-------------------------------------------------------------------------
*/
@@ -1109,12 +1758,11 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
hid_t file_id = -1; /* File ID */
hid_t fcpl = -1;
hid_t fapl = -1;
- size_t base_raw_cnt = 0;
- size_t base_meta_cnt = 0;
- size_t page_count = 0;
+ int64_t base_raw_cnt = 0;
+ int64_t base_meta_cnt = 0;
int i;
int num_elements = 1000;
- H5PB_t *page_buf;
+ H5PB_t *pb_ptr;
haddr_t meta_addr = HADDR_UNDEF;
haddr_t raw_addr = HADDR_UNDEF;
int *data = NULL;
@@ -1122,7 +1770,7 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
TESTING("Minimum Metadata threshold Processing");
HDprintf("\n");
- h5_fixname(FILENAME[0], orig_fapl, filename, sizeof(filename));
+ h5_fixname(namebase, orig_fapl, filename, sizeof(filename));
if((fapl = H5Pcopy(orig_fapl)) < 0)
TEST_ERROR
@@ -1136,7 +1784,7 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
FAIL_STACK_ERROR;
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
FAIL_STACK_ERROR;
if(H5Pset_file_space_page_size(fcpl, sizeof(int)*200) < 0)
@@ -1161,35 +1809,35 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
*/
HDassert(f);
HDassert(f->shared);
- HDassert(f->shared->page_buf);
+ HDassert(f->shared->pb_ptr);
- base_raw_cnt = f->shared->page_buf->raw_count;
- base_meta_cnt = f->shared->page_buf->meta_count;
+ base_raw_cnt = f->shared->pb_ptr->curr_rd_pages;
+ base_meta_cnt = f->shared->pb_ptr->curr_md_pages;
if(base_raw_cnt != 0)
TEST_ERROR;
- if(base_meta_cnt != 1)
+ if(base_meta_cnt != 2)
TEST_ERROR;
- page_buf = f->shared->page_buf;
+ pb_ptr = f->shared->pb_ptr;
- if(page_buf->min_meta_count != 5)
+ if(pb_ptr->min_md_pages != 5)
TEST_ERROR;
- if(page_buf->min_raw_count != 0)
+ if(pb_ptr->min_rd_pages != 0)
TEST_ERROR;
- if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
- if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
- /* write all raw data, this would end up in page buffer since there
- * is no metadata yet
- *
- * Not necessarily -- opening the file may may load a metadata page.
+ /* write all raw data. Since min_md_pages == max_pages, none of it
+ * should end up in the page buffer.
*/
for(i=0 ; i<100 ; i++)
data[i] = i;
@@ -1197,77 +1845,88 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- page_count += 5;
-
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != base_meta_cnt)
FAIL_STACK_ERROR;
- if(page_buf->raw_count != 5 - base_meta_cnt)
+ if(pb_ptr->curr_rd_pages != 0)
TEST_ERROR;
/* write all meta data, this would end up in page buffer */
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr, sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr,
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200), sizeof(int)*50, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200),
+ sizeof(int)*50, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400), sizeof(int)*50, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400),
+ sizeof(int)*50, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600), sizeof(int)*50, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600),
+ sizeof(int)*50, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800), sizeof(int)*50, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800),
+ sizeof(int)*50, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
- if(page_buf->meta_count != 5)
+ if(pb_ptr->curr_md_pages != 5)
TEST_ERROR;
- if(page_buf->raw_count != 0)
+ if(pb_ptr->curr_rd_pages != 0)
TEST_ERROR;
/* write and read more raw data and make sure that they don't end up in
- * page buffer since the minimum metadata is actually the entire
* page buffer
*/
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*350), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*350),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*500), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*500),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*750), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*750),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*900), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*900),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
- if(page_buf->meta_count != 5)
+ if(pb_ptr->curr_md_pages != 5)
TEST_ERROR;
- if(page_buf->raw_count != 0)
+ if(pb_ptr->curr_rd_pages != 0)
TEST_ERROR;
if(H5Fclose(file_id) < 0)
@@ -1276,10 +1935,7 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
HDprintf("\tMinimum raw data threshold = 100%%\n");
- page_count = 0;
-
/* keep 5 pages at max in the page buffer and 5 raw page minimum */
- /* Changes: 1000 */
if(H5Pset_page_buffer_size(fapl, sizeof(int)*1000, 0, 100) < 0)
TEST_ERROR;
@@ -1296,31 +1952,34 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
*/
HDassert(f);
HDassert(f->shared);
- HDassert(f->shared->page_buf);
+ HDassert(f->shared->pb_ptr);
- base_raw_cnt = f->shared->page_buf->raw_count;
- base_meta_cnt = f->shared->page_buf->meta_count;
+ base_raw_cnt = f->shared->pb_ptr->curr_rd_pages;
+ base_meta_cnt = f->shared->pb_ptr->curr_md_pages;
if(base_raw_cnt != 0)
TEST_ERROR;
- if(base_meta_cnt != 1)
+ if(base_meta_cnt != 0)
TEST_ERROR;
- page_buf = f->shared->page_buf;
+ pb_ptr = f->shared->pb_ptr;
- if(page_buf->min_meta_count != 0)
+ if(pb_ptr->min_md_pages != 0)
TEST_ERROR;
- if(page_buf->min_raw_count != 5)
+ if(pb_ptr->min_rd_pages != 5)
FAIL_STACK_ERROR;
- if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
- if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW,
+ sizeof(int)*(size_t)num_elements)))
TEST_ERROR;
- /* write all meta data, this would end up in page buffer since there
+ /* write all meta data, none of this should end up in the page buffer since
+ * min_rd_pages == max_pages
* is no raw data yet
*/
for(i=0 ; i<100 ; i++)
@@ -1329,86 +1988,97 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- page_count += 5;
-
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 0)
FAIL_STACK_ERROR;
- if(page_buf->meta_count != 5 - base_raw_cnt)
+ if(pb_ptr->curr_md_pages != 0)
TEST_ERROR;
/* write/read all raw data, this would end up in page buffer */
if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
- if(page_buf->raw_count != 5)
+ if(pb_ptr->curr_rd_pages != 5)
TEST_ERROR;
- if(page_buf->meta_count != 0)
+ if(pb_ptr->curr_md_pages != 0)
TEST_ERROR;
/* write and read more meta data and make sure that they don't end up in
- * page buffer since the minimum metadata is actually the entire
* page buffer
*/
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*100), sizeof(int)*50, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*100),
+ sizeof(int)*50, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*350), sizeof(int)*50, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*350),
+ sizeof(int)*50, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*500), sizeof(int)*50, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*500),
+ sizeof(int)*50, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*750), sizeof(int)*50, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*750),
+ sizeof(int)*50, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*900), sizeof(int)*50, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*900),
+ sizeof(int)*50, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
- if(page_buf->raw_count != 5)
+ if(pb_ptr->curr_rd_pages != 5)
TEST_ERROR;
- if(page_buf->meta_count != 0)
+ if(pb_ptr->curr_md_pages != 0)
TEST_ERROR;
if(H5Fclose(file_id) < 0)
FAIL_STACK_ERROR;
- HDprintf("\tMinimum metadata threshold = 40%%, Minimum rawdata threshold = 40%%\n");
- page_count = 0;
- /* keep 5 pages at max in the page buffer 2 meta pages, 2 raw pages
- * minimum
+ HDprintf("\tMinimum metadata threshold = 40%%, ");
+ HDprintf("Minimum rawdata threshold = 40%%\n");
+
+ /* keep 5 pages at max in the page buffer 2 meta pages, 2 raw pages
+ * minimum
*/
if(H5Pset_page_buffer_size(fapl, sizeof(int)*1000, 40, 40) < 0)
TEST_ERROR;
@@ -1428,33 +2098,41 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
* metadata entry counts.
*/
+ base_raw_cnt = f->shared->pb_ptr->curr_rd_pages;
+ base_meta_cnt = f->shared->pb_ptr->curr_md_pages;
+
if(base_raw_cnt != 0)
TEST_ERROR;
- if(base_meta_cnt != 1)
+ if(base_meta_cnt != 2)
TEST_ERROR;
- page_buf = f->shared->page_buf;
- if(page_buf->min_meta_count != 2)
+ pb_ptr = f->shared->pb_ptr;
+
+ if(pb_ptr->min_md_pages != 2)
TEST_ERROR;
- if(page_buf->min_raw_count != 2)
+ if(pb_ptr->min_rd_pages != 2)
TEST_ERROR;
- if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
- if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
/* initialize all the elements to have a value of -1 */
for(i=0 ; i<num_elements ; i++)
data[i] = -1;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr, sizeof(int)*(size_t)num_elements, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr,
+ sizeof(int)*(size_t)num_elements, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr, sizeof(int)*(size_t)num_elements, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr,
+ sizeof(int)*(size_t)num_elements, data) < 0)
FAIL_STACK_ERROR;
/* fill the page buffer with raw data */
@@ -1464,124 +2142,169 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- page_count += 5;
-
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
TEST_ERROR;
- if(f->shared->page_buf->raw_count != 5 - base_meta_cnt)
+ if(f->shared->pb_ptr->curr_rd_pages != 5 - base_meta_cnt)
TEST_ERROR;
- /* add 3 meta entries evicting 3 raw entries */
+ /* add 3 meta entries evicting 1 raw entry */
if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->meta_count != 3)
+ if(f->shared->pb_ptr->curr_md_pages != 3)
TEST_ERROR;
- if(f->shared->page_buf->raw_count != 2)
+ if(f->shared->pb_ptr->curr_rd_pages != 2)
TEST_ERROR;
/* adding more meta entires should replace meta entries since raw data
* is at its minimum
*/
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->meta_count != 3)
+ if(f->shared->pb_ptr->curr_md_pages != 3)
TEST_ERROR;
- if(f->shared->page_buf->raw_count != 2)
+ if(f->shared->pb_ptr->curr_rd_pages != 2)
TEST_ERROR;
/* bring existing raw entires up the LRU */
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*750), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*750),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
/* adding 2 raw entries (even with 1 call) should only evict 1 meta
* entry and another raw entry
*/
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*350), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*350),
+ sizeof(int)*100, data) < 0)
+ FAIL_STACK_ERROR;
+
+ if(f->shared->pb_ptr->curr_md_pages != 2)
+ TEST_ERROR;
+
+ if(f->shared->pb_ptr->curr_rd_pages != 3)
+ TEST_ERROR;
+
+ /* read a metadata entry to force the flush of the metadata entries
+ * in the page buffer, and then read some raw data so that the metadata
+ * pages are at the bottom of the LRU.
+ *
+ * When we are done, should still have 2 metadata pages and 3 raw data
+ * pages in the page buffer
+ */
+
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*442),
+ sizeof(int)*100, data) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*150),
+ sizeof(int)*100, data) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*550),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->meta_count != 2)
+ if(f->shared->pb_ptr->curr_md_pages != 2)
TEST_ERROR;
- if(f->shared->page_buf->raw_count != 3)
+ if(f->shared->pb_ptr->curr_rd_pages != 3)
TEST_ERROR;
- /* adding 2 meta entries should replace 2 entires at the bottom of the LRU */
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*98), sizeof(int)*100, data) < 0)
+ /* adding 2 meta entries should replace 2 entires at the bottom
+ * of the LRU
+ */
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*98),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*242), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*242),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->meta_count != 2)
+ if(f->shared->pb_ptr->curr_md_pages != 2)
TEST_ERROR;
- if(f->shared->page_buf->raw_count != 3)
+ if(f->shared->pb_ptr->curr_rd_pages != 3)
TEST_ERROR;
if(H5Fclose(file_id) < 0)
FAIL_STACK_ERROR;
HDprintf("\tMinimum metadata threshold = 20%%\n");
- page_count = 0;
+
/* keep 5 pages at max in the page buffer and 1 meta page minimum */
if(H5Pset_page_buffer_size(fapl, sizeof(int)*1000, 39, 0) < 0)
TEST_ERROR;
+
/* create the file */
if((file_id = H5Fcreate(filename, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
FAIL_STACK_ERROR;
+
/* Get a pointer to the internal file object */
if(NULL == (f = (H5F_t *)H5VL_object(file_id)))
FAIL_STACK_ERROR;
- page_buf = f->shared->page_buf;
- if(page_buf->min_meta_count != 1)
+ pb_ptr = f->shared->pb_ptr;
+
+ if(pb_ptr->min_md_pages != 1)
TEST_ERROR;
- if(page_buf->min_raw_count != 0)
+ if(pb_ptr->min_rd_pages != 0)
TEST_ERROR;
- if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
- if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
/* initialize all the elements to have a value of -1 */
for(i=0 ; i<num_elements ; i++)
data[i] = -1;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr, sizeof(int)*(size_t)num_elements, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr,
+ sizeof(int)*(size_t)num_elements, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr, sizeof(int)*(size_t)num_elements, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr,
+ sizeof(int)*(size_t)num_elements, data) < 0)
FAIL_STACK_ERROR;
/* fill the page buffer with raw data */
@@ -1591,90 +2314,105 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- page_count += 5;
-
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
+ if(f->shared->pb_ptr->curr_md_pages != 1)
+ TEST_ERROR;
+
+ if(f->shared->pb_ptr->curr_rd_pages != 4)
+ TEST_ERROR;
+
/* add 2 meta entries evicting 2 raw entries */
if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->meta_count != 2)
+ if(f->shared->pb_ptr->curr_md_pages != 3)
TEST_ERROR;
- if(f->shared->page_buf->raw_count != 3)
+ if(f->shared->pb_ptr->curr_rd_pages != 2)
TEST_ERROR;
/* bring the rest of the raw entries up the LRU */
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*500), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*500),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*700), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*700),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*900), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*900),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
/* write one more raw entry which replace one meta entry */
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*100), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*100),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->meta_count != 1)
+ if(f->shared->pb_ptr->curr_md_pages != 1)
TEST_ERROR;
- if(f->shared->page_buf->raw_count != 4)
+ if(f->shared->pb_ptr->curr_rd_pages != 4)
TEST_ERROR;
/* write one more raw entry which should replace another raw entry
* keeping min threshold of meta entries
*/
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*300), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*300),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->meta_count != 1)
+ if(f->shared->pb_ptr->curr_md_pages != 1)
TEST_ERROR;
- if(f->shared->page_buf->raw_count != 4)
+ if(f->shared->pb_ptr->curr_rd_pages != 4)
TEST_ERROR;
/* write a metadata entry that should replace the metadata entry
* at the bottom of the LRU
*/
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*500), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*500),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5SL_count(f->shared->page_buf->slist_ptr) != page_count)
+ if(f->shared->pb_ptr->curr_pages != 5)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->meta_count != 1)
+ if(f->shared->pb_ptr->curr_md_pages != 1)
TEST_ERROR;
- if(f->shared->page_buf->raw_count != 4)
+ if(f->shared->pb_ptr->curr_rd_pages != 4)
TEST_ERROR;
if(H5Fclose(file_id) < 0)
@@ -1693,15 +2431,16 @@ test_min_threshold(hid_t orig_fapl, const char *env_h5_drvr)
return 0;
error:
-
H5E_BEGIN_TRY {
- H5Pclose(fapl);
- H5Pclose(fcpl);
- H5Fclose(file_id);
- if(data)
+ if (fapl != H5I_INVALID_HID)
+ H5Pclose(fapl);
+ if (fcpl != H5I_INVALID_HID)
+ H5Pclose(fcpl);
+ if (file_id != H5I_INVALID_HID)
+ H5Fclose(file_id);
+ if (data != NULL)
HDfree(data);
} H5E_END_TRY;
-
return 1;
} /* test_min_threshold */
@@ -1735,6 +2474,10 @@ error:
*
* JRM -- 2/23/17
*
+ * Reworked test for the new page buffer implementation.
+ *
+ * JRM -- 10/28/18
+ *
*-------------------------------------------------------------------------
*/
static unsigned
@@ -1746,8 +2489,8 @@ test_stats_collection(hid_t orig_fapl, const char *env_h5_drvr)
hid_t fapl = -1;
int i;
int num_elements = 1000;
- size_t base_raw_cnt = 0;
- size_t base_meta_cnt = 0;
+ int64_t base_raw_cnt = 0;
+ int64_t base_meta_cnt = 0;
haddr_t meta_addr = HADDR_UNDEF;
haddr_t raw_addr = HADDR_UNDEF;
int *data = NULL;
@@ -1755,7 +2498,7 @@ test_stats_collection(hid_t orig_fapl, const char *env_h5_drvr)
TESTING("Statistics Collection");
- h5_fixname(FILENAME[0], orig_fapl, filename, sizeof(filename));
+ h5_fixname(namebase, orig_fapl, filename, sizeof(filename));
if((fapl = H5Pcopy(orig_fapl)) < 0)
TEST_ERROR
@@ -1769,7 +2512,7 @@ test_stats_collection(hid_t orig_fapl, const char *env_h5_drvr)
if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
TEST_ERROR;
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
TEST_ERROR;
if(H5Pset_file_space_page_size(fcpl, sizeof(int)*200) < 0)
@@ -1792,25 +2535,27 @@ test_stats_collection(hid_t orig_fapl, const char *env_h5_drvr)
*/
HDassert(f);
HDassert(f->shared);
- HDassert(f->shared->page_buf);
+ HDassert(f->shared->pb_ptr);
- base_raw_cnt = f->shared->page_buf->raw_count;
- base_meta_cnt = f->shared->page_buf->meta_count;
+ base_raw_cnt = f->shared->pb_ptr->curr_rd_pages;
+ base_meta_cnt = f->shared->pb_ptr->curr_md_pages;
if(base_raw_cnt != 0)
TEST_ERROR;
- if(base_meta_cnt != 1)
+ if(base_meta_cnt != 2)
TEST_ERROR;
/* reset statistics before we begin the tests */
if(H5Freset_page_buffering_stats(file_id) < 0)
FAIL_STACK_ERROR;
- if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (meta_addr = H5MF_alloc(f, H5FD_MEM_SUPER,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
- if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW, sizeof(int)*(size_t)num_elements)))
+ if(HADDR_UNDEF == (raw_addr = H5MF_alloc(f, H5FD_MEM_DRAW,
+ sizeof(int)*(size_t)num_elements)))
FAIL_STACK_ERROR;
@@ -1818,10 +2563,12 @@ test_stats_collection(hid_t orig_fapl, const char *env_h5_drvr)
for(i=0 ; i<num_elements ; i++)
data[i] = -1;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr, sizeof(int)*(size_t)num_elements, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr,
+ sizeof(int)*(size_t)num_elements, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr, sizeof(int)*(size_t)num_elements, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr,
+ sizeof(int)*(size_t)num_elements, data) < 0)
FAIL_STACK_ERROR;
for(i=0 ; i<200 ; i++)
@@ -1830,137 +2577,217 @@ test_stats_collection(hid_t orig_fapl, const char *env_h5_drvr)
if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*500), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*500),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*700), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*700),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*900), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*900),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400), sizeof(int)*200, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400),
+ sizeof(int)*200, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*100), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*100),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*300), sizeof(int)*100, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*300),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800), sizeof(int)*182, data) < 0)
+ if(H5F_block_write(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800),
+ sizeof(int)*182, data) < 0)
FAIL_STACK_ERROR;
if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*400),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr, sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*200),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*600),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_DRAW, raw_addr+(sizeof(int)*800),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*400),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600), sizeof(int)*200, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*600),
+ sizeof(int)*200, data) < 0)
FAIL_STACK_ERROR;
- if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800), sizeof(int)*100, data) < 0)
+ if(H5F_block_read(f, H5FD_MEM_SUPER, meta_addr+(sizeof(int)*800),
+ sizeof(int)*100, data) < 0)
FAIL_STACK_ERROR;
- if(f->shared->page_buf->accesses[0] != 8)
- TEST_ERROR;
- if(f->shared->page_buf->accesses[1] != 16)
- TEST_ERROR;
+ if ( ( f->shared->pb_ptr->accesses[0] != 9 ) ||
+ ( f->shared->pb_ptr->accesses[1] != 16 ) ||
+ ( f->shared->pb_ptr->accesses[2] != 0 ) ) {
- if(f->shared->page_buf->bypasses[0] != 3)
- TEST_ERROR;
- if(f->shared->page_buf->bypasses[1] != 1)
+ HDfprintf(stderr, "accesses[] = {%d, %d, %d}. {9, 16, 0} expected\n",
+ f->shared->pb_ptr->accesses[0],
+ f->shared->pb_ptr->accesses[1],
+ f->shared->pb_ptr->accesses[2]);
TEST_ERROR;
+ }
- if(f->shared->page_buf->hits[0] != 0)
- TEST_ERROR;
- if(f->shared->page_buf->hits[1] != 4)
- TEST_ERROR;
+ if ( ( f->shared->pb_ptr->bypasses[0] != 2 ) ||
+ ( f->shared->pb_ptr->bypasses[1] != 1 ) ||
+ ( f->shared->pb_ptr->bypasses[2] != 1 ) ) {
- if(f->shared->page_buf->misses[0] != 8)
+ HDfprintf(stderr, "bypasses[] = {%d, %d, %d}. {2, 1, 1} expected\n",
+ f->shared->pb_ptr->bypasses[0],
+ f->shared->pb_ptr->bypasses[1],
+ f->shared->pb_ptr->bypasses[2]);
TEST_ERROR;
- if(f->shared->page_buf->misses[1] != 11)
+ }
+
+ if ( ( f->shared->pb_ptr->hits[0] != 0 ) ||
+ ( f->shared->pb_ptr->hits[1] != 4 ) ||
+ ( f->shared->pb_ptr->hits[2] != 0 ) ) {
+
+ HDfprintf(stderr, "hits[] = {%d, %d, %d}. {0, 4, 0} expected\n",
+ f->shared->pb_ptr->hits[0],
+ f->shared->pb_ptr->hits[1],
+ f->shared->pb_ptr->hits[2]);
TEST_ERROR;
+ }
+
+ if ( ( f->shared->pb_ptr->misses[0] != 9 ) ||
+ ( f->shared->pb_ptr->misses[1] != 16 ) ||
+ ( f->shared->pb_ptr->misses[2] != 0 ) ) {
- if(f->shared->page_buf->evictions[0] != 5 + base_meta_cnt)
+ HDfprintf(stderr, "misses[] = {%d, %d, %d}. {9, 16, 0} expected\n",
+ f->shared->pb_ptr->misses[0],
+ f->shared->pb_ptr->misses[1],
+ f->shared->pb_ptr->misses[2]);
TEST_ERROR;
- if(f->shared->page_buf->evictions[1] != 9 + base_raw_cnt)
+ }
+
+ if ( ( f->shared->pb_ptr->evictions[0] != 7) ||
+ ( f->shared->pb_ptr->evictions[1] != 9) ||
+ ( f->shared->pb_ptr->evictions[2] != 0 ) ) {
+
+ HDfprintf(stderr, "evictions[] = {%d, %d, %d}. {%d, %d, 0} expected\n",
+ f->shared->pb_ptr->evictions[0],
+ f->shared->pb_ptr->evictions[1],
+ f->shared->pb_ptr->evictions[2], 7, 9);
TEST_ERROR;
+ }
{
- unsigned accesses[2];
- unsigned hits[2];
- unsigned misses[2];
- unsigned evictions[2];
- unsigned bypasses[2];
-
- if(H5Fget_page_buffering_stats(file_id, accesses, hits, misses, evictions, bypasses) < 0)
+ unsigned accesses[3];
+ unsigned hits[3];
+ unsigned misses[3];
+ unsigned evictions[3];
+ unsigned bypasses[3];
+
+ if(H5Fget_page_buffering_stats(file_id, accesses, hits, misses,
+ evictions, bypasses) < 0)
FAIL_STACK_ERROR;
- if(accesses[0] != 8)
- TEST_ERROR;
- if(accesses[1] != 16)
- TEST_ERROR;
- if(bypasses[0] != 3)
- TEST_ERROR;
- if(bypasses[1] != 1)
- TEST_ERROR;
- if(hits[0] != 0)
- TEST_ERROR;
- if(hits[1] != 4)
+ if ( ( accesses[0] != 9 ) ||
+ ( accesses[1] != 16 ) ||
+ ( accesses[2] != 0 ) ) {
+
+ HDfprintf(stderr,
+ "accesses[] = {%d, %d, %d}. {9, 16, 0} expected\n",
+ accesses[0], accesses[1], accesses[2]);
TEST_ERROR;
- if(misses[0] != 8)
+ }
+
+ if ( ( bypasses[0] != 2 ) ||
+ ( bypasses[1] != 1 ) ||
+ ( bypasses[2] != 1 ) ) {
+
+ HDfprintf(stderr, "bypasses[] = {%d, %d, %d}. {2, 1, 1} expected\n",
+ bypasses[0], bypasses[1], bypasses[2]);
TEST_ERROR;
- if(misses[1] != 11)
+ }
+
+ if ( ( hits[0] != 0 ) ||
+ ( hits[1] != 4 ) ||
+ ( hits[2] != 0 ) ) {
+
+ HDfprintf(stderr, "hits[] = {%d, %d, %d}. {0, 4, 0} expected\n",
+ hits[0], hits[1], hits[2]);
TEST_ERROR;
- if(evictions[0] != 5 + base_meta_cnt)
+ }
+
+ if ( ( misses[0] != 9 ) ||
+ ( misses[1] != 16 ) ||
+ ( misses[2] != 0 ) ) {
+
+ HDfprintf(stderr, "misses[] = {%d, %d, %d}. {9, 16, 0} expected\n",
+ misses[0], misses[1], misses[2]);
TEST_ERROR;
- if(evictions[1] != 9 + base_raw_cnt)
+ }
+
+ if ( ( evictions[0] != 7 ) ||
+ ( evictions[1] != 9 ) ||
+ ( evictions[2] != 0 ) ) {
+
+ HDfprintf(stderr,
+ "evictions[] = {%d, %d, %d}. {%d, %d, 0} expected\n",
+ evictions[0], evictions[1], evictions[2], 7, 9);
TEST_ERROR;
+ }
if(H5Freset_page_buffering_stats(file_id) < 0)
FAIL_STACK_ERROR;
- if(H5Fget_page_buffering_stats(file_id, accesses, hits, misses, evictions, bypasses) < 0)
+
+ if(H5Fget_page_buffering_stats(file_id, accesses, hits, misses,
+ evictions, bypasses) < 0)
FAIL_STACK_ERROR;
if(accesses[0] != 0)
@@ -1983,7 +2810,7 @@ test_stats_collection(hid_t orig_fapl, const char *env_h5_drvr)
TEST_ERROR;
if(evictions[1] != 0)
TEST_ERROR;
- } /* end block */
+ }
if(H5Fclose(file_id) < 0)
FAIL_STACK_ERROR;
@@ -1999,10 +2826,13 @@ test_stats_collection(hid_t orig_fapl, const char *env_h5_drvr)
error:
H5E_BEGIN_TRY {
- H5Pclose(fapl);
- H5Pclose(fcpl);
- H5Fclose(file_id);
- if(data)
+ if (fapl != H5I_INVALID_HID)
+ H5Pclose(fapl);
+ if (fcpl != H5I_INVALID_HID)
+ H5Pclose(fcpl);
+ if (file_id != H5I_INVALID_HID)
+ H5Fclose(file_id);
+ if (data != NULL)
HDfree(data);
} H5E_END_TRY;
@@ -2041,7 +2871,7 @@ verify_page_buffering_disabled(hid_t orig_fapl, const char *env_h5_drvr)
hid_t fapl = -1;
TESTING("Page Buffering Disabled");
- h5_fixname(FILENAME[0], orig_fapl, filename, sizeof(filename));
+ h5_fixname(namebase, orig_fapl, filename, sizeof(filename));
/* first, try to create a file with page buffering enabled */
@@ -2055,7 +2885,7 @@ verify_page_buffering_disabled(hid_t orig_fapl, const char *env_h5_drvr)
if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
FAIL_STACK_ERROR;
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
FAIL_STACK_ERROR;
if(H5Pset_file_space_page_size(fcpl, 4096) < 0)
@@ -2078,7 +2908,7 @@ verify_page_buffering_disabled(hid_t orig_fapl, const char *env_h5_drvr)
if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
FAIL_STACK_ERROR;
- if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, (hsize_t)1) < 0)
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, 0, 1) < 0)
FAIL_STACK_ERROR;
if(H5Pset_file_space_page_size(fcpl, 4096) < 0)
@@ -2115,14 +2945,17 @@ verify_page_buffering_disabled(hid_t orig_fapl, const char *env_h5_drvr)
error:
H5E_BEGIN_TRY {
- H5Pclose(fapl);
- H5Pclose(fcpl);
- H5Fclose(file_id);
+ if (fapl != H5I_INVALID_HID)
+ H5Pclose(fapl);
+ if (fcpl != H5I_INVALID_HID)
+ H5Pclose(fcpl);
+ if (file_id != H5I_INVALID_HID)
+ H5Fclose(file_id);
} H5E_END_TRY;
return 1;
-} /* verify_page_buffering_disabled() */
+}
#endif /* H5_HAVE_PARALLEL */
@@ -2163,13 +2996,14 @@ main(void)
SKIPPED()
HDputs("Skip page buffering test because paged aggregation is disabled for multi/split drivers");
+ HDputs("Furthermore, VFD SWMR is not (yet) expected to work with multi/split drivers");
HDexit(EXIT_SUCCESS);
- } /* end if */
+ }
if((fapl = h5_fileaccess()) < 0) {
nerrors++;
PUTS_ERROR("Can't get VFD-dependent fapl")
- } /* end if */
+ }
/* Push API context */
if(H5CX_push() < 0) FAIL_STACK_ERROR
@@ -2183,14 +3017,18 @@ main(void)
#else /* H5_HAVE_PARALLEL */
nerrors += test_args(fapl, env_h5_drvr);
- nerrors += test_raw_data_handling(fapl, env_h5_drvr);
+ nerrors += test_raw_data_handling(fapl, env_h5_drvr, false);
+ nerrors += test_raw_data_handling(fapl, env_h5_drvr, true);
+ nerrors += test_spmde_delay_basic(fapl, env_h5_drvr);
+ nerrors += test_mpmde_delay_basic(fapl, env_h5_drvr);
+ nerrors += test_spmde_lru_evict_basic(fapl, env_h5_drvr);
nerrors += test_lru_processing(fapl, env_h5_drvr);
nerrors += test_min_threshold(fapl, env_h5_drvr);
nerrors += test_stats_collection(fapl, env_h5_drvr);
#endif /* H5_HAVE_PARALLEL */
- h5_clean_files(FILENAME, fapl);
+ h5_clean_files(namebases, fapl);
if(nerrors)
goto error;
@@ -2214,5 +3052,4 @@ error:
if(api_ctx_pushed) H5CX_pop();
HDexit(EXIT_FAILURE);
-} /* main() */
-
+}
diff --git a/test/stubs.c b/test/stubs.c
new file mode 100644
index 0000000..c04f1ba
--- /dev/null
+++ b/test/stubs.c
@@ -0,0 +1,25 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 "h5test.h"
+#include "genall5.h"
+
+/* The default, do-nothing implementation of zoo_create_hook(), which
+ * is called after each create_zoo() step.
+ */
+void
+zoo_create_hook(hid_t H5_ATTR_UNUSED fid)
+{
+ return;
+}
+
diff --git a/test/supervise.subr b/test/supervise.subr
new file mode 100644
index 0000000..d2b0395
--- /dev/null
+++ b/test/supervise.subr
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+#
+# catch_out_err_and_rc outbase command [arguments]
+#
+# Run `command` with any `arguments` provided. Redirect `command`'s
+# stderr and stdout to the file `outbase.out`. Record the result code
+# of `command` in `outbase.rc`.
+#
+catch_out_err_and_rc()
+{
+ if [ $# -lt 2 ]; then
+ echo "usage: catch_output_and_rc outbase command [arguments]" \
+ 1>&2
+ exit 1
+ fi
+ outbase=$1
+ shift
+ {
+ "$@" < ${STDIN_PATH:-/dev/stdin} > ${STDOUT_PATH:-/dev/stdout} &
+ echo $! > ${outbase}.pid
+ wait $(cat ${outbase}.pid)
+ echo $? > ${outbase}.rc
+ } 2>&1 | tee ${outbase}.out
+}
+
+#catch_out_err_and_rc xxlsxx ls smiles &
+
+#wait
+#echo result=$(cat xxlsxx.rc)
+
+#exit 0
diff --git a/test/swmr_addrem_writer.c b/test/swmr_addrem_writer.c
index 71e4929..7c79de4 100644
--- a/test/swmr_addrem_writer.c
+++ b/test/swmr_addrem_writer.c
@@ -193,7 +193,7 @@ addrem_records(hid_t fid, unsigned verbose, unsigned long nops, unsigned long fl
hid_t file_sid; /* Dataset's space ID */
/* Get a random dataset, according to the symbol distribution */
- symbol = choose_dataset();
+ symbol = choose_dataset(NULL, NULL);
/* Decide whether to shrink or expand, and by how much */
count[1] = (hsize_t)HDrandom() % (MAX_SIZE_CHANGE * 2) + 1;
diff --git a/test/swmr_common.c b/test/swmr_common.c
index 7ae1fad..0290fe5 100644
--- a/test/swmr_common.c
+++ b/test/swmr_common.c
@@ -109,8 +109,9 @@ char VDS_DSET_NAME[NAME_LEN] = "vds_dset";
*-------------------------------------------------------------------------
*/
symbol_info_t *
-choose_dataset(void)
+choose_dataset(unsigned *levelp, unsigned *offsetp)
{
+ static unsigned ncalls = 0;
unsigned level; /* The level of the dataset */
unsigned offset; /* The "offset" of the dataset at that level */
@@ -120,10 +121,19 @@ choose_dataset(void)
/* Determine the offset of the level */
offset = (unsigned)(HDrandom() % (int)symbol_count[level]);
+ ++ncalls;
+ if ((ncalls % 1000) == 0) {
+ fprintf(stderr, "%s: call %u chose level %u offset %u\n", __func__,
+ ncalls, level, offset);
+ }
+ if (levelp != NULL)
+ *levelp = level;
+ if (offsetp != NULL)
+ *offsetp = offset;
return &symbol_info[level][offset];
} /* end choose_dataset() */
-
+
/*-------------------------------------------------------------------------
* Function: create_symbol_datatype
*
@@ -217,13 +227,12 @@ generate_symbols(void)
unsigned u, v; /* Local index variables */
for(u = 0; u < NLEVELS; u++) {
- symbol_info[u] = (symbol_info_t *)HDmalloc(symbol_count[u] * sizeof(symbol_info_t));
+ symbol_info[u] = HDmalloc(symbol_count[u] * sizeof(symbol_info_t));
for(v = 0; v < symbol_count[u]; v++) {
char name_buf[64];
generate_name(name_buf, u, v);
- symbol_info[u][v].name = (char *)HDmalloc(HDstrlen(name_buf) + 1);
- HDstrcpy(symbol_info[u][v].name, name_buf);
+ symbol_info[u][v].name = HDstrdup(name_buf);
symbol_info[u][v].dsid = -1;
symbol_info[u][v].nrecords = 0;
} /* end for */
diff --git a/test/swmr_common.h b/test/swmr_common.h
index a0bc581..47f96b7 100644
--- a/test/swmr_common.h
+++ b/test/swmr_common.h
@@ -64,7 +64,7 @@ H5TEST_DLLVAR unsigned symbol_count[NLEVELS];
extern "C" {
#endif
-H5TEST_DLL symbol_info_t * choose_dataset(void);
+H5TEST_DLL symbol_info_t * choose_dataset(unsigned *, unsigned *);
H5TEST_DLL hid_t create_symbol_datatype(void);
H5TEST_DLL int generate_name(char *name_buf, unsigned level, unsigned count);
H5TEST_DLL int generate_symbols(void);
diff --git a/test/swmr_reader.c b/test/swmr_reader.c
index ee263e3..28a6597 100644
--- a/test/swmr_reader.c
+++ b/test/swmr_reader.c
@@ -250,7 +250,7 @@ read_records(const char *filename, hbool_t verbose, FILE *verbose_file,
symbol_info_t *sym; /* Symbol to use */
/* Determine the symbol, within all symbols */
- if(NULL == (sym = choose_dataset()))
+ if(NULL == (sym = choose_dataset(NULL, NULL)))
return -1;
sym_rand[v] = sym;
diff --git a/test/swmr_remove_reader.c b/test/swmr_remove_reader.c
index b02d16f..41f8437 100644
--- a/test/swmr_remove_reader.c
+++ b/test/swmr_remove_reader.c
@@ -247,7 +247,7 @@ read_records(const char *filename, unsigned verbose, unsigned long nseconds,
symbol_info_t *sym; /* Symbol to use */
/* Determine the symbol, within all symbols */
- if(NULL == (sym = choose_dataset()))
+ if(NULL == (sym = choose_dataset(NULL, NULL)))
return -1;
sym_rand[v] = sym;
diff --git a/test/swmr_remove_writer.c b/test/swmr_remove_writer.c
index 2bebab9..2db9493 100644
--- a/test/swmr_remove_writer.c
+++ b/test/swmr_remove_writer.c
@@ -179,7 +179,7 @@ remove_records(hid_t fid, unsigned verbose, unsigned long nshrinks, unsigned lon
hsize_t remove_size; /* Size to reduce dataset dimension by */
/* Get a random dataset, according to the symbol distribution */
- symbol = choose_dataset();
+ symbol = choose_dataset(NULL, NULL);
/* Shrink the dataset's dataspace */
remove_size = (hsize_t)HDrandom() % MAX_REMOVE_SIZE + 1;
diff --git a/test/swmr_sparse_reader.c b/test/swmr_sparse_reader.c
index 2d558df..cdf03ba 100644
--- a/test/swmr_sparse_reader.c
+++ b/test/swmr_sparse_reader.c
@@ -29,6 +29,7 @@
/* Headers */
/***********/
+#include <inttypes.h> /* for PRIu64 */
#include "h5test.h"
#include "swmr_common.h"
@@ -118,7 +119,7 @@ check_dataset(hid_t fid, unsigned verbose, const symbol_info_t *symbol, symbol_t
HDfprintf(stderr, "Symbol = '%s', location = %" PRIuMAX ",%" PRIuMAX "\n", symbol->name, (uintmax_t)start[0], (uintmax_t)start[1]);
/* Read record from dataset */
- record->rec_id = (uint64_t)ULLONG_MAX;
+ record->rec_id = UINT64_MAX;
if(H5Dread(dsid, symbol_tid, rec_sid, file_sid, H5P_DEFAULT, record) < 0)
return -1;
@@ -231,7 +232,7 @@ read_records(const char *filename, unsigned verbose, unsigned long nrecords,
unsigned long file_u; /* Attribute sequence number (writer's "u") */
/* Get a random dataset, according to the symbol distribution */
- symbol = choose_dataset();
+ symbol = choose_dataset(NULL, NULL);
/* Fill in "nrecords" field. Note that this depends on the writer
* using the same algorithm and "nrecords" */
diff --git a/test/swmr_sparse_writer.c b/test/swmr_sparse_writer.c
index 5173c71..e33ebd4 100644
--- a/test/swmr_sparse_writer.c
+++ b/test/swmr_sparse_writer.c
@@ -201,7 +201,7 @@ add_records(hid_t fid, unsigned verbose, unsigned long nrecords, unsigned long f
hbool_t corked; /* Whether the dataset was corked */
/* Get a random dataset, according to the symbol distribution */
- symbol = choose_dataset();
+ symbol = choose_dataset(NULL, NULL);
/* If this is the first time the dataset has been opened, extend it and
* add the sequence attribute */
diff --git a/test/swmr_start_write.c b/test/swmr_start_write.c
index fc7e7a5..5522795 100644
--- a/test/swmr_start_write.c
+++ b/test/swmr_start_write.c
@@ -257,7 +257,7 @@ add_records(hid_t fid, hbool_t verbose, FILE *verbose_file,
hid_t file_sid; /* Dataset's space ID */
/* Get a random dataset, according to the symbol distribution */
- symbol = choose_dataset();
+ symbol = choose_dataset(NULL, NULL);
/* Set the record's ID (equal to its position) */
record.rec_id = symbol->nrecords;
diff --git a/test/swmr_writer.c b/test/swmr_writer.c
index d4387aa..5db17ef 100644
--- a/test/swmr_writer.c
+++ b/test/swmr_writer.c
@@ -197,7 +197,7 @@ add_records(hid_t fid, hbool_t verbose, FILE *verbose_file,
hid_t file_sid; /* Dataset's space ID */
/* Get a random dataset, according to the symbol distribution */
- symbol = choose_dataset();
+ symbol = choose_dataset(NULL, NULL);
/* Set the record's ID (equal to its position) */
record.rec_id = symbol->nrecords;
diff --git a/test/testfiles/err_compat_1 b/test/testfiles/err_compat_1
index fc99f77..978c1f4 100644
--- a/test/testfiles/err_compat_1
+++ b/test/testfiles/err_compat_1
@@ -1,4 +1,6 @@
-Testing error API H5Eset/get_auto Testing error API based on data I/O All error API tests passed.
+Testing error API H5Eset/get_auto
+Testing error API based on data I/O
+All error API tests passed.
This program tests the Error API compatible with HDF5 version (number). There are supposed to be some error messages
********* Print error stack in HDF5 default way *********
HDF5-DIAG: Error detected in HDF5 (version (number)) thread (IDs):
diff --git a/test/testfiles/plist_files/dcpl_32be b/test/testfiles/plist_files/dcpl_32be
index 667c67f..c8241a6 100644
--- a/test/testfiles/plist_files/dcpl_32be
+++ b/test/testfiles/plist_files/dcpl_32be
Binary files differ
diff --git a/test/testfiles/plist_files/dcpl_32le b/test/testfiles/plist_files/dcpl_32le
index 667c67f..c8241a6 100644
--- a/test/testfiles/plist_files/dcpl_32le
+++ b/test/testfiles/plist_files/dcpl_32le
Binary files differ
diff --git a/test/testfiles/plist_files/dcpl_64be b/test/testfiles/plist_files/dcpl_64be
index 667c67f..c8241a6 100644
--- a/test/testfiles/plist_files/dcpl_64be
+++ b/test/testfiles/plist_files/dcpl_64be
Binary files differ
diff --git a/test/testfiles/plist_files/dcpl_64le b/test/testfiles/plist_files/dcpl_64le
index 667c67f..c8241a6 100644
--- a/test/testfiles/plist_files/dcpl_64le
+++ b/test/testfiles/plist_files/dcpl_64le
Binary files differ
diff --git a/test/testfiles/plist_files/def_dcpl_32be b/test/testfiles/plist_files/def_dcpl_32be
index 01b233c..0909391 100644
--- a/test/testfiles/plist_files/def_dcpl_32be
+++ b/test/testfiles/plist_files/def_dcpl_32be
Binary files differ
diff --git a/test/testfiles/plist_files/def_dcpl_32le b/test/testfiles/plist_files/def_dcpl_32le
index 01b233c..0909391 100644
--- a/test/testfiles/plist_files/def_dcpl_32le
+++ b/test/testfiles/plist_files/def_dcpl_32le
Binary files differ
diff --git a/test/testfiles/plist_files/def_dcpl_64be b/test/testfiles/plist_files/def_dcpl_64be
index 01b233c..0909391 100644
--- a/test/testfiles/plist_files/def_dcpl_64be
+++ b/test/testfiles/plist_files/def_dcpl_64be
Binary files differ
diff --git a/test/testfiles/plist_files/def_dcpl_64le b/test/testfiles/plist_files/def_dcpl_64le
index 01b233c..0909391 100644
--- a/test/testfiles/plist_files/def_dcpl_64le
+++ b/test/testfiles/plist_files/def_dcpl_64le
Binary files differ
diff --git a/test/testfiles/plist_files/def_dxpl_32be b/test/testfiles/plist_files/def_dxpl_32be
index b13f456..da29037 100644
--- a/test/testfiles/plist_files/def_dxpl_32be
+++ b/test/testfiles/plist_files/def_dxpl_32be
Binary files differ
diff --git a/test/testfiles/plist_files/def_dxpl_32le b/test/testfiles/plist_files/def_dxpl_32le
index b13f456..da29037 100644
--- a/test/testfiles/plist_files/def_dxpl_32le
+++ b/test/testfiles/plist_files/def_dxpl_32le
Binary files differ
diff --git a/test/testfiles/plist_files/def_dxpl_64be b/test/testfiles/plist_files/def_dxpl_64be
index b13f456..da29037 100644
--- a/test/testfiles/plist_files/def_dxpl_64be
+++ b/test/testfiles/plist_files/def_dxpl_64be
Binary files differ
diff --git a/test/testfiles/plist_files/def_dxpl_64le b/test/testfiles/plist_files/def_dxpl_64le
index b13f456..da29037 100644
--- a/test/testfiles/plist_files/def_dxpl_64le
+++ b/test/testfiles/plist_files/def_dxpl_64le
Binary files differ
diff --git a/test/testfiles/plist_files/def_fapl_32be b/test/testfiles/plist_files/def_fapl_32be
index 53ef572..6738cda 100644
--- a/test/testfiles/plist_files/def_fapl_32be
+++ b/test/testfiles/plist_files/def_fapl_32be
Binary files differ
diff --git a/test/testfiles/plist_files/def_fapl_32le b/test/testfiles/plist_files/def_fapl_32le
index 53ef572..6738cda 100644
--- a/test/testfiles/plist_files/def_fapl_32le
+++ b/test/testfiles/plist_files/def_fapl_32le
Binary files differ
diff --git a/test/testfiles/plist_files/def_fapl_64be b/test/testfiles/plist_files/def_fapl_64be
index 53ef572..6738cda 100644
--- a/test/testfiles/plist_files/def_fapl_64be
+++ b/test/testfiles/plist_files/def_fapl_64be
Binary files differ
diff --git a/test/testfiles/plist_files/def_fapl_64le b/test/testfiles/plist_files/def_fapl_64le
index 53ef572..6738cda 100644
--- a/test/testfiles/plist_files/def_fapl_64le
+++ b/test/testfiles/plist_files/def_fapl_64le
Binary files differ
diff --git a/test/testfiles/plist_files/dxpl_32be b/test/testfiles/plist_files/dxpl_32be
index 5ff2ea0..cac7e50 100644
--- a/test/testfiles/plist_files/dxpl_32be
+++ b/test/testfiles/plist_files/dxpl_32be
Binary files differ
diff --git a/test/testfiles/plist_files/dxpl_32le b/test/testfiles/plist_files/dxpl_32le
index 5ff2ea0..cac7e50 100644
--- a/test/testfiles/plist_files/dxpl_32le
+++ b/test/testfiles/plist_files/dxpl_32le
Binary files differ
diff --git a/test/testfiles/plist_files/dxpl_64be b/test/testfiles/plist_files/dxpl_64be
index 5ff2ea0..cac7e50 100644
--- a/test/testfiles/plist_files/dxpl_64be
+++ b/test/testfiles/plist_files/dxpl_64be
Binary files differ
diff --git a/test/testfiles/plist_files/dxpl_64le b/test/testfiles/plist_files/dxpl_64le
index 5ff2ea0..cac7e50 100644
--- a/test/testfiles/plist_files/dxpl_64le
+++ b/test/testfiles/plist_files/dxpl_64le
Binary files differ
diff --git a/test/testfiles/plist_files/fapl_32be b/test/testfiles/plist_files/fapl_32be
index d89a44c..807c04a 100644
--- a/test/testfiles/plist_files/fapl_32be
+++ b/test/testfiles/plist_files/fapl_32be
Binary files differ
diff --git a/test/testfiles/plist_files/fapl_32le b/test/testfiles/plist_files/fapl_32le
index d89a44c..807c04a 100644
--- a/test/testfiles/plist_files/fapl_32le
+++ b/test/testfiles/plist_files/fapl_32le
Binary files differ
diff --git a/test/testfiles/plist_files/fapl_64be b/test/testfiles/plist_files/fapl_64be
index d89a44c..807c04a 100644
--- a/test/testfiles/plist_files/fapl_64be
+++ b/test/testfiles/plist_files/fapl_64be
Binary files differ
diff --git a/test/testfiles/plist_files/fapl_64le b/test/testfiles/plist_files/fapl_64le
index d89a44c..807c04a 100644
--- a/test/testfiles/plist_files/fapl_64le
+++ b/test/testfiles/plist_files/fapl_64le
Binary files differ
diff --git a/test/testfiles/plist_files/lapl_32be b/test/testfiles/plist_files/lapl_32be
index eee238e..3db7163 100644
--- a/test/testfiles/plist_files/lapl_32be
+++ b/test/testfiles/plist_files/lapl_32be
Binary files differ
diff --git a/test/testfiles/plist_files/lapl_32le b/test/testfiles/plist_files/lapl_32le
index eee238e..3db7163 100644
--- a/test/testfiles/plist_files/lapl_32le
+++ b/test/testfiles/plist_files/lapl_32le
Binary files differ
diff --git a/test/testfiles/plist_files/lapl_64be b/test/testfiles/plist_files/lapl_64be
index eee238e..3db7163 100644
--- a/test/testfiles/plist_files/lapl_64be
+++ b/test/testfiles/plist_files/lapl_64be
Binary files differ
diff --git a/test/testfiles/plist_files/lapl_64le b/test/testfiles/plist_files/lapl_64le
index eee238e..3db7163 100644
--- a/test/testfiles/plist_files/lapl_64le
+++ b/test/testfiles/plist_files/lapl_64le
Binary files differ
diff --git a/test/testvfdswmr.sh.in b/test/testvfdswmr.sh.in
new file mode 100644
index 0000000..a355245
--- /dev/null
+++ b/test/testvfdswmr.sh.in
@@ -0,0 +1,746 @@
+#! /bin/bash
+#
+# 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.
+#
+# Tests for the vfd swmr feature.
+#
+###############################################################################
+# VFD SWMR concurrent tests which are modified from existing swmr concurrent tests.
+# This is copied and modified from testswmr.sh.in
+#
+###############################################################################
+
+srcdir=@srcdir@
+
+. ${srcdir}/supervise.subr
+
+###############################################################################
+## test parameters
+###############################################################################
+
+Nreaders=5 # number of readers to launch
+Nrdrs_spa=3 # number of sparse readers to launch
+Nrecords=400000 # number of records to write
+Nrecs_rem=40000 # number of times to shrink
+Nrecs_spa=20000 # number of records to write in the sparse test
+Nsecs_add=5 # number of seconds per read interval
+Nsecs_rem=3 # number of seconds per read interval
+Nsecs_addrem=8 # number of seconds per read interval
+nerrors=0
+nsofterrors=0 # soft errors are expected to occur some of the time
+ # on a couple of nondeterministic tests.
+
+###############################################################################
+## 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
+ # This should be the same as the define in "./swmr_common.h"
+MESSAGE_TIMEOUT=300 # Message timeout length in secs
+ # This should be the same as the define in "./h5test.h"
+
+###############################################################################
+## short hands and function definitions
+###############################################################################
+DPRINT=: # Set to "echo Debug:" for debugging printing,
+ # else ":" for noop.
+IFDEBUG=: # Set to null to turn on debugging, else ":" for noop.
+
+# Print a line-line message left justified in a field of 70 characters
+# beginning with the word "Testing".
+#
+TESTING() {
+ SPACES=" "
+ echo "Testing $* $SPACES" | cut -c1-70 | tr -d '\012'
+}
+
+# To wait for the writer message file or till the maximum # of seconds is reached
+# $1 is the message file to wait for
+# This performs similar function as the routine h5_wait_message() in test/h5test.c
+WAIT_MESSAGE() {
+ message=$1 # Get the name of the message file to wait for
+ t0=`date +%s` # Get current time in seconds
+ difft=0 # Initialize the time difference
+ mexist=0 # Indicate whether the message file is found
+ while [ $difft -lt $MESSAGE_TIMEOUT ] ; # Loop till message times out
+ do
+ t1=`date +%s` # Get current time in seconds
+ difft=`expr $t1 - $t0` # Calculate the time difference
+ if [ -e $message ]; then # If message file is found:
+ mexist=1 # indicate the message file is found
+ rm $message # remove the message file
+ break # get out of the while loop
+ fi
+ done;
+ if test $mexist -eq 0; then
+ # Issue warning that the writer message file is not found, continue with launching the reader(s)
+ echo warning: $WRITER_MESSAGE is not found after waiting $MESSAGE_TIMEOUT seconds
+ else
+ echo $WRITER_MESSAGE is found
+ fi
+}
+
+###############################################################################
+## Main
+##
+## Modifications:
+## Vailin Choi; July 2013
+## Add waiting of message file before launching the reader(s).
+## Due to the implementation of file locking, coordination
+## is needed in file opening for the writer/reader tests
+## to proceed as expected.
+##
+###############################################################################
+# The build (current) directory might be different than the source directory.
+if test -z "$srcdir"; then
+ srcdir=.
+fi
+
+# Check to see if the VFD specified by the HDF5_DRIVER environment variable
+# supports SWMR. ??? DO I NEED TO MODIFY THIS ????
+./swmr_check_compat_vfd
+rc=$?
+if [ $rc -ne 0 ] ; then
+ echo
+ echo "The VFD specified by the HDF5_DRIVER environment variable"
+ echo "does not support VFD SWMR."
+ echo
+ echo "VFD SWMR acceptance tests skipped"
+ echo
+ exit 0
+fi
+
+all_tests="generator expand shrink expand_shrink sparse vlstr_null vlstr_oob zoo groups"
+all_tests="${all_tests} few_big many_small"
+tests=${all_tests}
+
+if [ $# -gt 0 ]; then
+ tests=
+fi
+
+for t; do
+ if ! echo $all_tests | grep -q "\<${t}\>"; then
+ echo "$t: Unknown test, ${t}"
+ exit 1
+ fi
+ tests="${tests} ${t}"
+done
+
+echo tests=${tests}
+for t in ${tests}; do
+ eval do_${t}=yes
+done
+
+# HDF5 has several tests that create and delete signal files to communicate
+# between processes, and it seems that even though the names of the files are
+# different, occasionally the wrong file is deleted, interrupting the flow of
+# the test. Running each of these tests in its own directory should eliminate
+# the problem.
+rm -rf vfd_swmr_test
+mkdir vfd_swmr_test
+
+## With the --disable-shared option, swmr program files are built in the test
+## directory, otherwise they are in test/.libs with a corresponding wrapper
+## script in the test directory. The programs or wrapper scripts in test should
+## always be copied, swmr files in .libs should be copied only if they exists.
+#if [ -f .libs/vfd_swmr ]; then
+# mkdir vfd_swmr_test/.libs
+# for FILE in .libs/vfd_swmr*; do
+# case "$FILE" in
+# *.o) continue ;; ## don't copy the .o files
+# esac
+# cp $FILE vfd_swmr_test/.libs
+# done
+#fi
+
+cd vfd_swmr_test
+
+# Loop over index types
+for index_type in "-i ea" "-i b2"
+do
+ # Try without compression, only; uncomment "-c 5" to try with compression.
+
+ for compress in "" #"-c 5"
+ do
+ echo
+ echo "** Loop testing parameters: $index_type $compress"
+ echo
+ if [ ${do_generator:-no} = yes ]; then
+ echo
+ echo "## Generator test"
+ # Launch the Generator without VFD SWMR write
+ echo launch the vfd_swmr_generator
+ ../vfd_swmr_generator $compress $index_type
+ if test $? -ne 0; then
+ echo generator had error
+ nerrors=`expr $nerrors + 1`
+ fi
+
+ # Launch the Generator with VFD SWMR write
+ echo launch the vfd_swmr_generator with VFD SWMR write
+ ../vfd_swmr_generator -s $compress $index_type
+ if test $? -ne 0; then
+ echo generator had error
+ nerrors=`expr $nerrors + 1`
+ fi
+ fi
+
+ if [ ${do_expand:-no} = yes ]; then
+ echo
+ echo "## Writer test - test expanding the dataset"
+
+ # Launch the Generator
+ echo launch the vfd_swmr_generator with VFD SWMR write
+ ../vfd_swmr_generator -s $compress $index_type
+ if test $? -ne 0; then
+ echo generator had error
+ nerrors=`expr $nerrors + 1`
+ fi
+
+ # Remove any possible writer message file before launching writer
+ rm -f $WRITER_MESSAGE
+ #
+ # Launch the Writer
+ echo launch the vfd_swmr_writer
+ seed="" # Put -r <random seed> command here
+ catch_out_err_and_rc vfd_swmr_writer \
+ ../vfd_swmr_writer -o $Nrecords $seed &
+ pid_writer=$!
+ $DPRINT pid_writer=$pid_writer
+
+ # Wait for message from writer process before starting reader(s)
+ WAIT_MESSAGE $WRITER_MESSAGE
+ #
+ # Launch the Readers
+ #declare -a seeds=(<seed1> <seed2> <seed3> ... )
+ echo launch $Nreaders vfd_swmr_readers
+ pid_readers=""
+ n=0
+ while [ $n -lt $Nreaders ]; do
+ #seed="-r ${seeds[$n]}"
+ seed=""
+ catch_out_err_and_rc vfd_swmr_reader.$n \
+ ../vfd_swmr_reader $Nsecs_add $seed &
+ pid_readers="$pid_readers $!"
+ n=`expr $n + 1`
+ done
+ $DPRINT pid_readers=$pid_readers
+ $IFDEBUG ps
+
+ # Wait for the readers to finish before signalling the
+ # writer to quit: the writer holds the file open so that the
+ # readers will find the shadow file when they reopen
+ # the .h5 file.
+ wait $pid_readers
+ kill -USR1 $(cat vfd_swmr_writer.pid)
+ wait $pid_writer
+
+ # Collect exit codes of the readers
+ n=0
+ while [ $n -lt $Nreaders ]; do
+ if [ $(cat vfd_swmr_reader.$n.rc) -ne 0 ]; then
+ echo reader had error
+ nerrors=$((nerrors + 1))
+ fi
+ n=$((n + 1))
+ done
+
+ # Collect exit code of the writer
+ $DPRINT checked writer $pid_writer
+ if [ $(cat vfd_swmr_writer.rc) -ne 0 ]; then
+ echo writer had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Clean up output files
+ rm -f vfd_swmr_writer.{out,rc}
+ rm -f vfd_swmr_reader.*.{out,rc}
+ fi
+
+ if [ ${do_shrink:-no} = yes ]; then
+ if [ ${do_expand:-no} != yes ]; then
+ echo "Cancelling the 'shrink' test: it depends on the .h5 file left behind by the 'expand' test." 1>&2
+ exit 1
+ fi
+ echo
+ echo "## Remove test - test shrinking the dataset"
+
+ # Remove any possible writer message file before launching writer
+ rm -f $WRITER_MESSAGE
+ # Launch the Remove Writer
+ echo launch the vfd_swmr_remove_writer
+ seed="" # Put -r <random seed> command here
+ catch_out_err_and_rc vfd_swmr_writer \
+ ../vfd_swmr_remove_writer -o $Nrecs_rem $seed &
+ pid_writer=$!
+ $DPRINT pid_writer=$pid_writer
+
+ # Wait for message from writer process before starting reader(s)
+ WAIT_MESSAGE $WRITER_MESSAGE
+ #
+ # Launch the Remove Readers
+ #declare -a seeds=(<seed1> <seed2> <seed3> ... )
+ n=0
+ pid_readers=""
+ echo launch $Nreaders swmr_remove_readers
+ while [ $n -lt $Nreaders ]; do
+ #seed="-r ${seeds[$n]}"
+ seed=""
+ catch_out_err_and_rc vfd_swmr_reader.$n \
+ ../vfd_swmr_remove_reader $Nsecs_rem $seed &
+ pid_readers="$pid_readers $!"
+ n=`expr $n + 1`
+ done
+ $DPRINT pid_readers=$pid_readers
+ $IFDEBUG ps
+
+ # Wait for the readers to finish before signalling the
+ # writer to quit: the writer holds the file open so that the
+ # readers will find the shadow file when they reopen
+ # the .h5 file.
+ wait $pid_readers
+ kill -USR1 $(cat vfd_swmr_writer.pid)
+ wait $pid_writer
+
+ # Collect exit codes of the readers
+ n=0
+ while [ $n -lt $Nreaders ]; do
+ if [ $(cat vfd_swmr_reader.$n.rc) -ne 0 ]; then
+ echo reader had error
+ nerrors=$((nerrors + 1))
+ fi
+ n=$((n + 1))
+ done
+
+ # Collect exit code of the writer
+ $DPRINT checked writer $pid_writer
+ if [ $(cat vfd_swmr_writer.rc) -ne 0 ]; then
+ echo writer had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Clean up output files
+ rm -f vfd_swmr_writer.{out,rc}
+ rm -f vfd_swmr_reader.*.{out,rc}
+ fi
+
+ if [ ${do_expand_shrink:-no} = yes ]; then
+ echo
+ echo "## Expand/shrink test - randomly grow or shrink the dataset"
+
+ # Launch the Generator
+ echo launch the vfd_swmr_generator with VFD SWMR write
+ ../vfd_swmr_generator -s $compress $index_type
+ if test $? -ne 0; then
+ echo generator had error
+ nerrors=`expr $nerrors + 1`
+ fi
+
+ # Launch the Writer (not in parallel - just to rebuild the datasets)
+ echo launch the vfd_swmr_writer
+ seed="" # Put -r <random seed> command here
+ ../vfd_swmr_writer -W $Nrecords $seed
+ if test $? -ne 0; then
+ echo writer had error
+ nerrors=`expr $nerrors + 1`
+ fi
+
+ # Remove any possible writer message file before launching writer
+ rm -f $WRITER_MESSAGE
+ #
+ # Launch the Add/Remove Writer
+ echo launch the vfd_swmr_addrem_writer
+ seed="" # Put -r <random seed> command here
+ catch_out_err_and_rc vfd_swmr_writer \
+ ../vfd_swmr_addrem_writer $Nrecords $seed &
+ pid_writer=$!
+ $DPRINT pid_writer=$pid_writer
+
+ # Wait for message from writer process before starting reader(s)
+ WAIT_MESSAGE $WRITER_MESSAGE
+ #
+ # Launch the Add/Remove Readers
+ #declare -a seeds=(<seed1> <seed2> <seed3> ... )
+ n=0
+ pid_readers=""
+ echo launch $Nreaders vfd_swmr_remove_readers
+ while [ $n -lt $Nreaders ]; do
+ #seed="-r ${seeds[$n]}"
+ seed=""
+ catch_out_err_and_rc vfd_swmr_reader.$n \
+ ../vfd_swmr_remove_reader $Nsecs_addrem $seed &
+ pid_readers="$pid_readers $!"
+ n=`expr $n + 1`
+ done
+ $DPRINT pid_readers=$pid_readers
+ $IFDEBUG ps
+
+ # Wait for the readers to finish before signalling the
+ # writer to quit: the writer holds the file open so that the
+ # readers will find the shadow file when they reopen
+ # the .h5 file.
+ wait $pid_readers
+ kill -USR1 $(cat vfd_swmr_writer.pid)
+ wait $pid_writer
+
+ # Collect exit codes of the readers
+ n=0
+ while [ $n -lt $Nreaders ]; do
+ if [ $(cat vfd_swmr_reader.$n.rc) -ne 0 ]; then
+ echo reader had error
+ nerrors=$((nerrors + 1))
+ fi
+ n=$((n + 1))
+ done
+
+ # Collect exit code of the writer
+ $DPRINT checked writer $pid_writer
+ if [ ! -e vfd_swmr_writer.rc ] ||
+ [ $(cat vfd_swmr_writer.rc) -ne 0 ]; then
+ echo writer had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Clean up output files
+ rm -f vfd_swmr_writer.{out,rc}
+ rm -f vfd_swmr_reader.*.{out,rc}
+ fi
+
+ if [ ${do_sparse:-no} = yes ]; then
+ echo
+ echo "## Sparse writer test - write random dataset locations"
+
+ # Launch the Generator
+ # NOTE: Random seed is shared between readers and writers and is
+ # created by the generator.
+ echo launch the vfd_swmr_generator with VFD SWMR write
+ seed="" # Put -r <random seed> command here
+ ../vfd_swmr_generator -s $compress $index_type $seed
+ if test $? -ne 0; then
+ echo generator had error
+ nerrors=`expr $nerrors + 1`
+ fi
+
+ # Remove any possible writer message file before launching writer
+ rm -f $WRITER_MESSAGE
+ # Launch the Sparse writer
+ echo launch the vfd_swmr_sparse_writer
+ catch_out_err_and_rc vfd_swmr_writer nice -n 20 \
+ ../vfd_swmr_sparse_writer $Nrecs_spa &
+ pid_writer=$!
+ $DPRINT pid_writer=$pid_writer
+
+ # Wait for message from writer process before starting reader(s)
+ WAIT_MESSAGE $WRITER_MESSAGE
+ #
+ # Launch the Sparse readers
+ n=0
+ pid_readers=""
+ echo launch $Nrdrs_spa vfd_swmr_sparse_readers
+ while [ $n -lt $Nrdrs_spa ]; do
+ # The sparse reader spits out a LOT of data so it's set to 'quiet'
+ catch_out_err_and_rc vfd_swmr_reader.$n \
+ ../vfd_swmr_sparse_reader -q $Nrecs_spa &
+ pid_readers="$pid_readers $!"
+ n=`expr $n + 1`
+ done
+ $DPRINT pid_readers=$pid_readers
+ $IFDEBUG ps
+
+ # Wait for the readers and the writer to finish.
+ echo "pid_readers=$pid_readers"
+ echo "pid_writer=$pid_writer"
+
+ # Wait for the readers to finish before signalling the
+ # writer to quit: the writer holds the file open so that the
+ # readers will find the shadow file when they reopen
+ # the .h5 file.
+ wait $pid_readers
+ kill -USR1 $(cat vfd_swmr_writer.pid)
+ wait $pid_writer
+
+ # Collect exit codes of the readers
+ n=0
+ while [ $n -lt $Nrdrs_spa ]; do
+ if [ $(cat vfd_swmr_reader.$n.rc) -ne 0 ]; then
+ echo reader had error
+ nerrors=$((nerrors + 1))
+ fi
+ n=$((n + 1))
+ done
+
+ # Collect exit code of the writer
+ $DPRINT checked writer $pid_writer
+ if [ $(cat vfd_swmr_writer.rc) -ne 0 ]; then
+ echo writer had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Clean up output files
+ rm -f vfd_swmr_writer.{out,rc}
+ rm -f vfd_swmr_reader.*.{out,rc}
+ fi
+ done
+done
+
+#
+# Test variable-length strings, expecting errors.
+#
+for ty in null oob; do
+
+ if [ ${ty} = null ]; then
+ [ ${do_vlstr_null:-no} = no ] && continue
+ echo
+ echo "## VL string 1 - expect to read NULL"
+ else
+ [ ${do_vlstr_oob:-no} = no ] && continue
+ echo
+ echo "## VL string 2 - expect out-of-bounds access"
+ fi
+
+ echo launch vfd_swmr_vlstr_writer
+ catch_out_err_and_rc vfd_swmr_vlstr_writer \
+ ../vfd_swmr_vlstr_writer -n 500 -q -t ${ty} &
+ pid_writer=$!
+
+ # pause?
+
+ catch_out_err_and_rc vfd_swmr_vlstr_reader \
+ ../vfd_swmr_vlstr_reader -n 500 -q -t ${ty} &
+ pid_reader=$!
+
+ # Wait for the reader to finish before signalling the
+ # writer to quit: the writer holds the file open so that the
+ # reader will find the shadow file when it opens
+ # the .h5 file.
+ wait $pid_reader
+ kill -USR1 $(cat vfd_swmr_vlstr_writer.pid)
+ wait $pid_writer
+
+ # Collect exit code of the reader
+ if [ $(cat vfd_swmr_vlstr_reader.rc) -ne 0 ]; then
+ echo reader had error
+ nsofterrors=$((nsofterrors + 1))
+ fi
+
+ # Collect exit code of the writer
+ if [ $(cat vfd_swmr_vlstr_writer.rc) -ne 0 ]; then
+ echo writer had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Clean up output files
+ rm -f vfd_swmr_vlstr_writer.{out,rc}
+ rm -f vfd_swmr_vlstr_reader.*.{out,rc}
+done
+
+#
+# Make sure that a "zoo"---the variety of HDF5 object types---can be
+# read and written by VFD SWMR.
+#
+if [ ${do_zoo:-no} = yes ]; then
+ [ -e ./fifo ] && rm -f ./fifo
+ mkfifo -m 0600 ./fifo
+ rm -f ./shared_tick_num
+ echo launch vfd_swmr_zoo_writer
+ STDIN_PATH="./fifo" catch_out_err_and_rc vfd_swmr_zoo_writer \
+ ../vfd_swmr_zoo_writer -m 1000 -q &
+ pid_writer=$!
+
+ STDOUT_PATH="./fifo" catch_out_err_and_rc vfd_swmr_zoo_reader \
+ ../vfd_swmr_zoo_reader -q -W &
+ pid_reader=$!
+
+ # Wait for the reader to finish before signalling the
+ # writer to quit: the writer holds the file open so that the
+ # reader will find the shadow file when it opens
+ # the .h5 file.
+ wait $pid_reader
+ kill -USR1 $(cat vfd_swmr_zoo_writer.pid)
+ wait $pid_writer
+
+ # Collect exit code of the reader
+ if [ $(cat vfd_swmr_zoo_reader.rc) -ne 0 ]; then
+ echo reader had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Collect exit code of the writer
+ if [ $(cat vfd_swmr_zoo_writer.rc) -ne 0 ]; then
+ echo writer had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Clean up output files
+ rm -f ./fifo
+ rm -f vfd_swmr_zoo_writer.{out,rc}
+ rm -f vfd_swmr_zoo_reader.*.{out,rc}
+fi
+
+#
+# Make sure that we can create 10000 groups while a reader waits
+# for each to appear.
+#
+if [ ${do_groups:-no} = yes ]; then
+ echo launch vfd_swmr_group_writer
+ catch_out_err_and_rc vfd_swmr_group_writer \
+ ../vfd_swmr_group_writer -q -u 10 -n 10000 &
+ pid_writer=$!
+
+ catch_out_err_and_rc vfd_swmr_group_reader \
+ ../vfd_swmr_group_reader -q -u 10 -n 10000 -W &
+ pid_reader=$!
+
+ # Wait for the reader to finish before signalling the
+ # writer to quit: the writer holds the file open so that the
+ # reader will find the shadow file when it opens
+ # the .h5 file.
+ wait $pid_reader
+ kill -USR1 $(cat vfd_swmr_group_writer.pid)
+ wait $pid_writer
+
+ # Collect exit code of the reader
+ if [ $(cat vfd_swmr_group_reader.rc) -ne 0 ]; then
+ echo reader had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Collect exit code of the writer
+ if [ $(cat vfd_swmr_group_writer.rc) -ne 0 ]; then
+ echo writer had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Clean up output files
+ rm -f vfd_swmr_group_writer.{out,rc}
+ rm -f vfd_swmr_group_reader.*.{out,rc}
+fi
+
+for options in "-d 1" "-d 2" "-d 1 -V" "-d 1 -M"; do
+ if [ ${do_many_small:-no} = no ]; then
+ continue
+ fi
+ #
+ # Test many small datasets of one and two dimensions.
+ #
+ # Perform 50 iterations on 1000 extensible datasets configured with
+ # 16x16 chunks of 32-bit unsigned integer elements,
+ # expanding each dataset by a chunk in one dimension (up to 50x1
+ # 16x16 chunks) on each iteration.
+ #
+ # Perform the test again, extending each dataset
+ # in *two* dimensions (up to 50x50 16x16 chunks).
+ #
+ 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 &
+ 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 &
+ pid_reader=$!
+
+ # Wait for the reader to finish before signalling the
+ # writer to quit: the writer holds the file open so that the
+ # reader will find the shadow file when it opens
+ # the .h5 file.
+ wait $pid_reader
+ kill -USR1 $(cat vfd_swmr_bigset_writer.pid)
+ wait $pid_writer
+
+ # Collect exit code of the reader
+ if [ $(cat vfd_swmr_bigset_reader.rc) -ne 0 ]; then
+ echo reader had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Collect exit code of the writer
+ if [ $(cat vfd_swmr_bigset_writer.rc) -ne 0 ]; then
+ echo writer had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Clean up output files
+ rm -f vfd_swmr_bigset_writer.{out,rc}
+ rm -f vfd_swmr_bigset_reader.*.{out,rc}
+done
+
+for options in "-d 1" "-d 2" "-d 1 -V" "-d 1 -M"; do
+ #
+ # Test a few big datasets of one and two dimensions.
+ #
+ # Perform 50 iterations on 5 extensible datasets configured with
+ # 256x256 chunks of 32-bit unsigned integer elements,
+ # expanding each dataset by a chunk in one dimension (up to 50x1
+ # 256x256 chunks) on each iteration.
+ #
+ # Perform the test again, extending each dataset
+ # in *two* dimensions (up to 50x50 256x256 chunks).
+ #
+ if [ ${do_few_big:-no} = no ]; then
+ continue
+ 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 &
+ 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 &
+ pid_reader=$!
+
+ # Wait for the reader to finish before signalling the
+ # writer to quit: the writer holds the file open so that the
+ # reader will find the shadow file when it opens
+ # the .h5 file.
+ wait $pid_reader
+ kill -USR1 $(cat vfd_swmr_bigset_writer.pid)
+ wait $pid_writer
+
+ # Collect exit code of the reader
+ if [ $(cat vfd_swmr_bigset_reader.rc) -ne 0 ]; then
+ echo reader had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Collect exit code of the writer
+ if [ $(cat vfd_swmr_bigset_writer.rc) -ne 0 ]; then
+ echo writer had error
+ nerrors=$((nerrors + 1))
+ fi
+
+ # Clean up output files
+ rm -f vfd_swmr_bigset_writer.{out,rc}
+ rm -f vfd_swmr_bigset_reader.*.{out,rc}
+done
+
+###############################################################################
+## Report and exit
+###############################################################################
+cd ..
+$DPRINT nerrors $nerrors nsofterrors $nsofterrors
+if test $nerrors -eq 0 ; then
+ echo "VFD SWMR tests passed."
+ if test $nsofterrors -ne 0 ; then
+ echo
+ echo "${nsofterrors} soft errors occurred. That's safe to ignore."
+ fi
+ if test -z "$HDF5_NOCLEANUP"; then
+ # delete the test directory
+ rm -rf vfd_swmr_test
+ fi
+ exit 0
+else
+ echo -n "VFD SWMR tests failed with $nerrors hard errors "
+ echo "and $nsofterrors soft errors."
+ exit 1
+fi
+
diff --git a/test/tmisc.c b/test/tmisc.c
index 6eb6872..f35daac 100644
--- a/test/tmisc.c
+++ b/test/tmisc.c
@@ -135,6 +135,7 @@ typedef struct
#ifndef H5_HAVE_PARALLEL
#define MISC8_DSETNAME2 "Dataset2"
#define MISC8_DSETNAME3 "Dataset3"
+#define MISC8_DSETNAME4 "Dataset4"
#define MISC8_DSETNAME6 "Dataset6"
#define MISC8_DSETNAME7 "Dataset7"
#define MISC8_DSETNAME9 "Dataset9"
diff --git a/test/unlink.c b/test/unlink.c
index 48dd79d..16d5d0c 100644
--- a/test/unlink.c
+++ b/test/unlink.c
@@ -1259,7 +1259,7 @@ test_create_unlink(const char *msg, hid_t fapl)
char groupname[1024];
char filename[1024];
- TESTING(msg);
+ TESTING("%s", msg);
/* Create file */
h5_fixname(FILENAME[3], fapl, filename, sizeof filename);
diff --git a/test/vfd_swmr.c b/test/vfd_swmr.c
new file mode 100644
index 0000000..d49629f
--- /dev/null
+++ b/test/vfd_swmr.c
@@ -0,0 +1,3474 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/***********************************************************
+*
+* Test program:
+*
+* Tests the VFD SWMR Feature.
+*
+*************************************************************/
+
+#include "H5queue.h"
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+/*
+ * This file needs to access private information from the H5F package.
+ */
+
+#define H5F_FRIEND /*suppress error about including H5Fpkg */
+#define H5FD_FRIEND /*suppress error about including H5FDpkg */
+#define H5F_TESTING
+#define H5FD_TESTING
+#include "H5FDprivate.h"
+#include "H5Fpkg.h"
+#include "H5FDpkg.h"
+#include "H5Iprivate.h"
+
+#define H5FD_FRIEND /*suppress error about including H5FDpkg */
+#include "H5FDpkg.h"
+
+#define FS_PAGE_SIZE 512
+#define FILENAME "vfd_swmr_file.h5"
+#define MD_FILENAME "vfd_swmr_metadata_file"
+
+#define FILENAME2 "vfd_swmr_file2.h5"
+#define MD_FILENAME2 "vfd_swmr_metadata_file2"
+
+#define FILENAME3 "vfd_swmr_file3.h5"
+#define MD_FILENAME3 "vfd_swmr_metadata_file3"
+
+#define FNAME "non_vfd_swmr_file.h5"
+
+/* test routines for VFD SWMR */
+static unsigned test_fapl(void);
+static unsigned test_file_end_tick(void);
+static unsigned test_file_fapl(void);
+static unsigned test_writer_md(void);
+
+/* helper routines */
+static hid_t
+init_vfd_swmr_config_fapl(H5F_vfd_swmr_config_t *config, uint32_t tick_len, uint32_t max_lag,
+ hbool_t is_writer, uint32_t md_pages_reserved, const char *md_file_path, size_t pbuf_size);
+
+/*-------------------------------------------------------------------------
+ * Function: init_vfd_swmr_config_fapl
+ *
+ * Purpose: Helper routine to initialize the fields for VFD SWMR configuration
+ *
+ * Return: void
+ *
+ *-------------------------------------------------------------------------
+ */
+static hid_t
+init_vfd_swmr_config_fapl(H5F_vfd_swmr_config_t *config, uint32_t tick_len, uint32_t max_lag,
+ hbool_t is_writer, uint32_t md_pages_reserved, const char *md_file_path, size_t pbuf_size)
+{
+ hid_t fapl;
+
+ HDmemset(config, 0, sizeof(*config));
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = tick_len;
+ config->max_lag = max_lag;
+ config->writer = is_writer;
+ config->md_pages_reserved = md_pages_reserved;
+ HDstrcpy(config->md_file_path, md_file_path);
+
+ /* Create a copy of the file access property list */
+ if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+ return H5I_INVALID_HID;
+
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0) {
+ (void)H5Pclose(fapl);
+ return H5I_INVALID_HID;
+ }
+
+ /* Enable page buffering */
+ if(pbuf_size != 0 && H5Pset_page_buffer_size(fapl, pbuf_size, 0, 0) < 0) {
+ (void)H5Pclose(fapl);
+ return H5I_INVALID_HID;
+ }
+
+ return fapl;
+} /* init_vfd_swmr_config_fapl() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_fapl()
+ *
+ * Purpose: A) Verify that invalid info set in the fapl fails
+ * as expected (see the RFC for VFD SWMR):
+ * --version: should be a known version
+ * --tick_len: should be >= 0
+ * --max_lag: should be >= 3
+ * --md_pages_reserved: should be >= 2
+ * --md_file_path: should contain the metadata file path (POSIX)
+ * B) Verify that info set in the fapl is retrieved correctly.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; July 2018
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_fapl(void)
+{
+ hid_t fapl = -1; /* File access property list */
+ H5F_vfd_swmr_config_t *my_config = NULL; /* Configuration for VFD SWMR */
+ herr_t ret; /* Return value */
+
+ TESTING("Configure VFD SWMR with fapl");
+
+ /* Allocate memory for the configuration structure */
+ if((my_config = HDmalloc(sizeof(*my_config))) == NULL)
+ FAIL_STACK_ERROR;
+ HDmemset(my_config, 0, sizeof(*my_config));
+
+ /* Get a copy of the file access property list */
+ if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+ TEST_ERROR;
+
+ /* Should get invalid VFD SWMR config info */
+ if(H5Pget_vfd_swmr_config(fapl, my_config) < 0)
+ TEST_ERROR;
+
+ /* Verify that the version is incorrect */
+ if(my_config->version >= H5F__CURR_VFD_SWMR_CONFIG_VERSION)
+ TEST_ERROR;
+
+ /* Should fail: version is 0 */
+ H5E_BEGIN_TRY {
+ ret = H5Pset_vfd_swmr_config(fapl, my_config);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR;
+
+ /* Set valid version */
+ my_config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+
+ /* Set valid tick_len */
+ my_config->tick_len = 3;
+ /* Should fail: max_lag is 2 */
+ my_config->max_lag = 2;
+ H5E_BEGIN_TRY {
+ ret = H5Pset_vfd_swmr_config(fapl, my_config);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR;
+
+ /* Set valid max_lag */
+ my_config->max_lag = 3;
+ /* Should fail: md_pages_reserved is 0 */
+ H5E_BEGIN_TRY {
+ ret = H5Pset_vfd_swmr_config(fapl, my_config);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR;
+
+ /* Set valid md_pages_reserved */
+ my_config->md_pages_reserved = 2;
+ /* Should fail: empty md_file_path */
+ H5E_BEGIN_TRY {
+ ret = H5Pset_vfd_swmr_config(fapl, my_config);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR;
+
+ /* Set md_file_path */
+ HDstrcpy(my_config->md_file_path, MD_FILENAME);
+ my_config->writer = TRUE;
+
+ /* Should succeed in setting the configuration info */
+ if(H5Pset_vfd_swmr_config(fapl, my_config) < 0)
+ TEST_ERROR;
+
+ /* Clear the configuration structure */
+ HDmemset(my_config, 0, sizeof(H5F_vfd_swmr_config_t));
+
+ /* Retrieve the configuration info just set */
+ if(H5Pget_vfd_swmr_config(fapl, my_config) < 0)
+ TEST_ERROR;
+
+ /* Verify the configuration info */
+ if(my_config->version < H5F__CURR_VFD_SWMR_CONFIG_VERSION)
+ TEST_ERROR;
+ if(my_config->md_pages_reserved != 2)
+ TEST_ERROR;
+ if(HDstrcmp(my_config->md_file_path, MD_FILENAME) != 0)
+ TEST_ERROR;
+
+ /* Close the file access property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+
+ if(my_config)
+ HDfree(my_config);
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ } H5E_END_TRY;
+ if(my_config)
+ HDfree(my_config);
+ return 1;
+} /* test_fapl() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_file_fapl()
+ *
+ * Purpose: A) Verify that page buffering and paged aggregation
+ * have to be enabled for a file to be configured
+ * with VFD SWMR.
+ * B) Verify that the "writer" setting in the fapl's VFD
+ * SWMR configuration should be consistent with the
+ * file access flags.
+ * C) Verify the VFD SWMR configuration set in fapl
+ * used to create/open the file is the same as the
+ * configuration retrieved from the file's fapl.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; July 2018
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_file_fapl(void)
+{
+ hid_t fid = -1; /* File ID */
+ hid_t fid2 = -1; /* File ID */
+ hid_t fcpl = -1; /* File creation property list ID */
+ hid_t fapl1 = -1; /* File access property list ID */
+ hid_t fapl2 = -1; /* File access property list ID */
+ hid_t file_fapl = -1; /* File access property list ID associated with the file */
+ H5F_vfd_swmr_config_t *config1 = NULL; /* Configuration for VFD SWMR */
+ H5F_vfd_swmr_config_t *config2 = NULL; /* Configuration for VFD SWMR */
+ H5F_vfd_swmr_config_t *file_config = NULL; /* Configuration for VFD SWMR */
+
+ TESTING("VFD SWMR configuration for the file and fapl");
+
+ /* Should succeed without VFD SWMR configured */
+ if((fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Allocate memory for the configuration structure */
+ if((config1 = (H5F_vfd_swmr_config_t *)HDmalloc(sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ FAIL_STACK_ERROR;
+ if((config2 = (H5F_vfd_swmr_config_t *)HDmalloc(sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ FAIL_STACK_ERROR;
+ if((file_config = (H5F_vfd_swmr_config_t *)HDmalloc(sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Configured as VFD SWMR reader + no page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 6, FALSE, 2, MD_FILENAME, 0);
+ if(fapl1 == H5I_INVALID_HID)
+ TEST_ERROR
+
+ /* Should fail to create: file access is writer but VFD SWMR config is reader */
+ H5E_BEGIN_TRY {
+ fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, H5P_DEFAULT, fapl1);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR;
+
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Configured as VFD SWMR writer + no page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 6, TRUE, 2, MD_FILENAME, 0);
+ if(fapl1 == H5I_INVALID_HID)
+ TEST_ERROR
+
+ /* Should fail to create: page buffering and paged aggregation not enabled */
+ H5E_BEGIN_TRY {
+ fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, H5P_DEFAULT, fapl1);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR;
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to create: no page buffering */
+ H5E_BEGIN_TRY {
+ fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl1);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR;
+
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Configured as VFD SWMR writer + page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 6, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ TEST_ERROR
+
+ /* Should succeed to create the file: paged aggregation and page buffering enabled */
+ if((fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl1)) < 0)
+ TEST_ERROR;
+
+ /* Get the file's file access property list */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Retrieve the VFD SWMR configuration from file_fapl */
+ if(H5Pget_vfd_swmr_config(file_fapl, file_config) < 0)
+ TEST_ERROR;
+
+ /* Verify the retrieved info is the same as config1 */
+ if(HDmemcmp(config1, file_config, sizeof(H5F_vfd_swmr_config_t)) != 0)
+ TEST_ERROR;
+
+ /* Closing */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to open: file access is reader but VFD SWMR config is writer */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl1);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR;
+
+ /* Should succeed to open: file access and VFD SWMR config are consistent */
+ if((fid = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl1)) < 0)
+ TEST_ERROR;
+
+ /* Get the file's file access property list */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Clear info in file_config */
+ HDmemset(file_config, 0, sizeof(H5F_vfd_swmr_config_t));
+
+ /* Retrieve the VFD SWMR configuration from file_fapl */
+ if(H5Pget_vfd_swmr_config(file_fapl, file_config) < 0)
+ TEST_ERROR;
+
+ /* Verify the retrieved info is the same as config1 */
+ if(HDmemcmp(config1, file_config, sizeof(H5F_vfd_swmr_config_t)) != 0)
+ TEST_ERROR;
+
+ /* Closing */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Set up different VFD SWMR configuration + page_buffering */
+ fapl2 = init_vfd_swmr_config_fapl(config2, 4, 10, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl2 == H5I_INVALID_HID)
+ TEST_ERROR
+
+ /* Should succeed to open the file as VFD SWMR writer */
+ if((fid = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl2)) < 0)
+ TEST_ERROR;
+
+ /* Get the file's file access property list */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Clear info in file_config */
+ HDmemset(file_config, 0, sizeof(H5F_vfd_swmr_config_t));
+
+ /* Retrieve the VFD SWMR configuration from file_fapl */
+ if(H5Pget_vfd_swmr_config(file_fapl, file_config) < 0)
+ TEST_ERROR;
+
+ /* Verify the retrieved info is NOT the same as config1 */
+ if(HDmemcmp(config1, file_config, sizeof(H5F_vfd_swmr_config_t)) == 0)
+ TEST_ERROR;
+
+ /* Verify the retrieved info is the same as config2 */
+ if(HDmemcmp(config2, file_config, sizeof(H5F_vfd_swmr_config_t)) != 0)
+ TEST_ERROR;
+
+ /* The file previously opened as VDF SWMR writer is still open */
+ /* with VFD SWMR configuration in config2 */
+
+ /* Set up as VFD SWMR writer in config1 but different from config2 */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 3, 8, TRUE, 3, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ TEST_ERROR;
+
+ /* Re-open the same file with config1 */
+ /* Should fail to open since config1 is different from config2 setting */
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl1);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR;
+
+ /* Close fapl1 */
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Set up as VFD SWMR reader in config1 which is same as config2 */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 10, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ TEST_ERROR;
+
+ /* Re-open the same file as VFD SWMR writer */
+ /* Should succeed since config1 is same as the setting in config2 */
+ if((fid2 = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl1)) < 0)
+ TEST_ERROR
+
+ /* Close fapl1 */
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+
+ HDmemset(file_config, 0, sizeof(H5F_vfd_swmr_config_t));
+
+ /* Get the file's file access property list */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Retrieve the VFD SWMR configuration from file_fapl */
+ if(H5Pget_vfd_swmr_config(file_fapl, file_config) < 0)
+ TEST_ERROR;
+
+ /* Should be the same as config1 */
+ if(HDmemcmp(config1, file_config, sizeof(H5F_vfd_swmr_config_t)) != 0)
+ TEST_ERROR;
+
+ /* Should be the the same as config2 */
+ if(HDmemcmp(config2, file_config, sizeof(H5F_vfd_swmr_config_t)) != 0)
+ TEST_ERROR;
+
+ /* Closing */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pclose(fapl2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free buffers */
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+ if(file_config)
+ HDfree(file_config);
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl1);
+ H5Pclose(fapl2);
+ H5Pclose(file_fapl);
+ H5Pclose(fcpl);
+ H5Fclose(fid);
+ H5Fclose(fid2);
+ } H5E_END_TRY;
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+ if(file_config)
+ HDfree(file_config);
+ return 1;
+} /* test_file_fapl() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_file_end_tick()
+ *
+ * Purpose: Verify the public routine H5Fvfd_swmr_end_tick() works
+ * as described in the RFC for VFD SWMR.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; June 2020
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_file_end_tick(void)
+{
+ hid_t fid1 = H5I_INVALID_HID; /* File ID */
+ hid_t fid2 = H5I_INVALID_HID; /* File ID */
+ hid_t fid3 = H5I_INVALID_HID; /* File ID */
+ hid_t fcpl = H5I_INVALID_HID; /* File creation property list ID */
+ hid_t fapl1 = H5I_INVALID_HID; /* File access property list ID */
+ hid_t fapl2 = H5I_INVALID_HID; /* File access property list ID */
+ hid_t fapl3 = H5I_INVALID_HID; /* File access property list ID */
+ H5F_vfd_swmr_config_t *config1 = NULL; /* Configuration for VFD SWMR */
+ H5F_vfd_swmr_config_t *config2 = NULL; /* Configuration for VFD SWMR */
+ H5F_vfd_swmr_config_t *config3 = NULL; /* Configuration for VFD SWMR */
+ H5F_t *f1, *f2, *f3; /* File pointer */
+ uint64_t s1 = 0; /* Saved tick_num */
+ uint64_t s2 = 0; /* Saved tick_num */
+ uint64_t s3 = 0; /* Saved tick_num */
+ int ret; /* Return status */
+
+ TESTING("H5Fvfd_swmr_end_tick()");
+
+ /* Create a file without VFD SWMR configured */
+ if((fid1 = H5Fcreate(FILENAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ /* Should fail to trigger EOT */
+ H5E_BEGIN_TRY {
+ ret = H5Fvfd_swmr_end_tick(fid1);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Allocate memory for the configuration structure */
+ if((config1 = HDmalloc(sizeof(*config1))) == NULL)
+ FAIL_STACK_ERROR;
+ if((config2 = HDmalloc(sizeof(*config2))) == NULL)
+ FAIL_STACK_ERROR;
+ if((config3 = HDmalloc(sizeof(*config3))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Configured file 1 as VFD SWMR writer + page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 10, 15, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Configured file 2 as VFD SWMR writer + page buffering */
+ fapl2 = init_vfd_swmr_config_fapl(config2, 5, 6, TRUE, 2, MD_FILENAME2, 4096);
+ if(fapl2 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Configured file 3 as VFD SWMR writer + page buffering */
+ fapl3 = init_vfd_swmr_config_fapl(config3, 3, 6, TRUE, 2, MD_FILENAME3, 4096);
+ if(fapl3 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create file 1 with VFD SWMR writer */
+ if((fid1 = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl1)) < 0)
+ TEST_ERROR;
+ /* Keep file 1 opened */
+
+ /* Create file 2 with VFD SWMR writer */
+ if((fid2 = H5Fcreate(FILENAME2, H5F_ACC_TRUNC, fcpl, fapl2)) < 0)
+ TEST_ERROR;
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create file 3 with VFD SWMR writer */
+ if((fid3 = H5Fcreate(FILENAME3, H5F_ACC_TRUNC, fcpl, fapl3)) < 0)
+ TEST_ERROR;
+ if(H5Fclose(fid3) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Open file 2 as VFD SWMR writer */
+ if((fid2 = H5Fopen(FILENAME2, H5F_ACC_RDWR, fapl2)) < 0)
+ TEST_ERROR;
+
+ /* Open file 3 as VFD SWMR writer */
+ if((fid3 = H5Fopen(FILENAME3, H5F_ACC_RDWR, fapl3)) < 0)
+ TEST_ERROR;
+
+ /* Get file pointer for the 3 files */
+ f1 = H5VL_object(fid1);
+ f2 = H5VL_object(fid2);
+ f3 = H5VL_object(fid3);
+
+ /* Saved tick_num for the 3 files */
+ s1 = f1->shared->tick_num;
+ s2 = f2->shared->tick_num;
+ s3 = f3->shared->tick_num;
+
+ /* Trigger EOT for file 2 */
+ if(H5Fvfd_swmr_end_tick(fid2) < 0)
+ TEST_ERROR;
+
+ /* file 2: tick_num should increase or at least same as previous tick_num */
+ if(f2->shared->tick_num < s2)
+ TEST_ERROR;
+
+ /* Disable EOT for file 2 */
+ if(H5Fvfd_swmr_disable_end_of_tick(fid2) < 0)
+ TEST_ERROR;
+
+ /* Should fail to trigger end of tick processing for file 2 */
+ H5E_BEGIN_TRY {
+ ret = H5Fvfd_swmr_end_tick(fid2);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR;
+
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Trigger EOT for file 1 */
+ if(H5Fvfd_swmr_end_tick(fid1) < 0)
+ TEST_ERROR;
+
+ /* file 1: tick_num should increase or at least same as previous tick_num */
+ if(f1->shared->tick_num < s1)
+ TEST_ERROR;
+
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /* Trigger EOT for file 3 */
+ if(H5Fvfd_swmr_end_tick(fid3) < 0)
+ TEST_ERROR;
+
+ /* file 3: tick_num should increase or at least same as previous tick_num */
+ if(f3->shared->tick_num < s3)
+ TEST_ERROR;
+
+ if(H5Fclose(fid3) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /* Closing */
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl3) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free buffers */
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+ if(config3)
+ HDfree(config3);
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl1);
+ H5Pclose(fapl2);
+ H5Pclose(fapl3);
+ H5Pclose(fcpl);
+ H5Fclose(fid1);
+ H5Fclose(fid2);
+ H5Fclose(fid3);
+ } H5E_END_TRY;
+
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+ if(config3)
+ HDfree(config3);
+
+ return 1;
+} /* test_file_end_tick() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_writer_create_open_flush()
+ *
+ * Purpose: Verify info in the metadata file when:
+ * --creating the HDF5 file
+ * --flushing the HDF5 file
+ * --opening an existing HDF5 file
+ * It will call the internal testing routine
+ * H5F__vfd_swmr_writer_create_open_flush_test() to do the following:
+ * --Open the metadata file
+ * --Verify the file size is as expected (md_pages_reserved)
+ * --For file create:
+ * --No header magic is found
+ * --For file open or file flush:
+ * --Read and decode the header and index in the metadata file
+ * --Verify info in the header and index read from
+ * the metadata file is as expected (empty index)
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; October 2018
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_writer_create_open_flush(void)
+{
+ hid_t fid = -1; /* File ID */
+ hid_t fapl = -1; /* File access property list */
+ hid_t fcpl = -1; /* File creation property list */
+ H5F_vfd_swmr_config_t *my_config = NULL; /* Configuration for VFD SWMR */
+
+ TESTING("Create/Open/Flush an HDF5 file for VFD SWMR");
+
+ /* Allocate memory for the configuration structure */
+ if((my_config = HDmalloc(sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Set up the VFD SWMR configuration + page buffering */
+ fapl = init_vfd_swmr_config_fapl(my_config, 1, 3, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create an HDF5 file with VFD SWMR configured */
+ if((fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Verify info in metadata file when creating the HDF5 file */
+ if(H5F__vfd_swmr_writer_create_open_flush_test(fid, TRUE) < 0)
+ FAIL_STACK_ERROR;
+
+#ifdef LATER /* Will activate the test when flush is implemented */
+ /* Flush the HDF5 file */
+ if(H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify info in metadata file when flushing the HDF5 file */
+ if(H5F__vfd_swmr_writer_create_open_flush_test(fid, FALSE) < 0)
+ FAIL_STACK_ERROR
+#endif
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Re-open the file as VFD SWMR writer */
+ if((fid = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Verify info in metadata file when reopening the HDF5 file */
+ if(H5F__vfd_swmr_writer_create_open_flush_test(fid, FALSE) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Closing */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ if(my_config)
+ HDfree(my_config);
+
+ PASSED();
+ return 0;
+
+error:
+ if(my_config)
+ HDfree(my_config);
+
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Pclose(fcpl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return 1;
+} /* test_writer_create_open_flush() */
+
+/* Sleep for `tenths` tenths of a second.
+ *
+ * This routine may quietly perform a too-short sleep if an error occurs
+ * in nanosleep(2).
+ */
+static void
+decisleep(uint32_t tenths)
+{
+ struct timespec delay = {.tv_sec = tenths / 10,
+ .tv_nsec = tenths * 100 * 1000 * 1000};
+
+ while (nanosleep(&delay, &delay) == -1 && errno == EINTR)
+ ; // do nothing
+}
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_writer_md()
+ *
+ * Purpose: Verify info in the metadata file after updating with the
+ * constructed index: (A), (B), (C), (D)
+ * It will call the internal testing routine
+ * H5F__vfd_swmr_writer_md_test() to do the following:
+ * --Update the metadata file with the input index via the
+ * internal library routine H5F_update_vfd_swmr_metadata_file()
+ * --Verify the entries in the delayed list is as expected:
+ * --num_dl_entries
+ * --Open the metadata file, read and decode the header and index
+ * --Verify header and index info just read from the metadata
+ * file is as expected:
+ * --num_entries and index
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; October 2018
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_writer_md(void)
+{
+ hid_t fid = -1; /* File ID */
+ hid_t fapl = -1; /* File access property list */
+ hid_t fcpl = -1; /* File creation property list */
+ const unsigned num_entries = 10; /* index size */
+ unsigned i = 0; /* Local index variables */
+ uint8_t *buf = NULL; /* Data page from the page buffer */
+ hid_t dcpl = -1; /* Dataset creation property list */
+ hid_t sid = -1; /* Dataspace ID */
+ hid_t did = -1; /* Dataset ID */
+ int *rwbuf = NULL; /* Data buffer for writing */
+ H5O_info_t oinfo; /* Object metadata information */
+ char dname[100]; /* Name of dataset */
+ hsize_t dims[2] = {50, 20}; /* Dataset dimension sizes */
+ hsize_t max_dims[2] = {H5S_UNLIMITED, H5S_UNLIMITED}; /* Dataset maximum dimension sizes */
+ hsize_t chunk_dims[2] = {2, 5}; /* Dataset chunked dimension sizes */
+ H5FD_vfd_swmr_idx_entry_t *index = NULL; /* Pointer to the index entries */
+ H5F_vfd_swmr_config_t *my_config = NULL; /* Configuration for VFD SWMR */
+
+ TESTING("Verify the metadata file for VFD SWMR writer");
+
+ /* Allocate memory for the configuration structure */
+ if((my_config = HDmalloc(sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Set up the VFD SWMR configuration + page buffering */
+ fapl = init_vfd_swmr_config_fapl(my_config, 1, 3, TRUE, 256, MD_FILENAME, FS_PAGE_SIZE);
+ if(fapl == H5I_INVALID_HID)
+ FAIL_STACK_ERROR
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_file_space_page_size(fcpl, FS_PAGE_SIZE) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create an HDF5 file with VFD SWMR configured */
+ if((fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Allocate num_entries for the data buffer */
+ if((buf = HDcalloc(num_entries, FS_PAGE_SIZE)) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Allocate memory for num_entries index */
+ index = HDcalloc(num_entries, sizeof(H5FD_vfd_swmr_idx_entry_t));
+ if(NULL == index)
+ FAIL_STACK_ERROR;
+
+ /* (A) Construct index for updating the metadata file */
+ for(i = 0; i < num_entries; i++) {
+ index[i].hdf5_page_offset = 3 + 7 * i;
+ index[i].md_file_page_offset = 1 + (num_entries - i) * 5;
+ index[i].length = (uint32_t)FS_PAGE_SIZE;
+ index[i].entry_ptr = &buf[i * FS_PAGE_SIZE];
+ }
+
+ /* Update with index and verify info in the metadata file */
+ /* Also verify that 0 entries will be on the delayed list */
+ if(H5F__vfd_swmr_writer_md_test(fid, num_entries, index, 0) < 0)
+ TEST_ERROR
+
+ /* Create dataset creation property list */
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use chunked dataset */
+ if(H5Pset_chunk(dcpl, 2, chunk_dims) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create dataspace */
+ if((sid = H5Screate_simple(2, dims, max_dims)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Perform activities to ensure that max_lag ticks elapse */
+ for(i = 0; i < my_config->max_lag + 1; i++) {
+ decisleep(my_config->tick_len);
+
+ /* Create a chunked dataset */
+ sprintf(dname, "dset %d", i);
+ if((did = H5Dcreate2(fid, dname, H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get dataset object header address */
+ if(H5Oget_info(did, &oinfo, H5O_INFO_BASIC) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ /* (B) Update every other entry in the index */
+ for(i = 0; i < num_entries; i+= 2)
+ index[i].entry_ptr = &buf[i * FS_PAGE_SIZE];
+
+ /* Update with index and verify info in the metadata file */
+ /* Also verify that 5 entries will be on the delayed list */
+ if(H5F__vfd_swmr_writer_md_test(fid, num_entries, index, 5) < 0)
+ TEST_ERROR
+
+ /* Allocate memory for the read/write buffer */
+ if((rwbuf = HDmalloc(sizeof(*rwbuf) * (50 * 20))) == NULL)
+ FAIL_STACK_ERROR;
+ for(i = 0; i < (50 * 20); i++)
+ rwbuf[i] = (int)i;
+
+ /* Perform activities to ensure that max_lag ticks elapse */
+ for(i = 0; i < my_config->max_lag + 1; i++) {
+ decisleep(my_config->tick_len);
+
+ /* Open the dataset */
+ sprintf(dname, "dset %d", i);
+ if((did = H5Dopen2(fid, dname, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Write to the dataset */
+ if(H5Dwrite(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rwbuf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get dataset object info */
+ if(H5Oget_info(did, &oinfo, H5O_INFO_BASIC) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ /* (C) Update every 3 entry in the index */
+ for(i = 0; i < num_entries; i+= 3)
+ index[i].entry_ptr = &buf[i * FS_PAGE_SIZE];
+
+ /* Update with index and verify info in the metadata file */
+ /* Also verify that 4 entries will be on the delayed list */
+ if(H5F__vfd_swmr_writer_md_test(fid, num_entries, index, 4) < 0)
+ TEST_ERROR
+
+ /* Clear the read/write buffer */
+ HDmemset(rwbuf, 0, sizeof(sizeof(int) * (50 * 20)));
+
+ /* Perform activities to ensure that max_lag ticks elapse */
+ for(i = 0; i < my_config->max_lag + 1; i++) {
+ decisleep(my_config->tick_len);
+
+ /* Open the dataset */
+ sprintf(dname, "dset %d", i);
+ if((did = H5Dopen2(fid, dname, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Read from the dataset */
+ if(H5Dread(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rwbuf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get dataset object info */
+ if(H5Oget_info(did, &oinfo, H5O_INFO_BASIC) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ /* (D) Update two entries in the index */
+ index[1].entry_ptr = &buf[1 * FS_PAGE_SIZE];
+ index[5].entry_ptr = &buf[5 * FS_PAGE_SIZE];
+
+ /* Update with index and verify info in the metadata file */
+ /* Also verify that 2 entries will be on the delayed list */
+ if(H5F__vfd_swmr_writer_md_test(fid, num_entries, index, 2) < 0)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free resources */
+ if(my_config)
+ HDfree(my_config);
+ if(buf)
+ HDfree(buf);
+ if(rwbuf)
+ HDfree(rwbuf);
+ if(index)
+ HDfree(index);
+
+ PASSED();
+ return 0;
+
+error:
+ if(my_config)
+ HDfree(my_config);
+ if(buf)
+ HDfree(buf);
+ if(rwbuf)
+ HDfree(rwbuf);
+ if(index)
+ HDfree(index);
+
+ H5E_BEGIN_TRY {
+ H5Dclose(did);
+ H5Sclose(sid);
+ H5Pclose(dcpl);
+ H5Pclose(fapl);
+ H5Pclose(fcpl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return 1;
+} /* test_writer__md() */
+
+
+#if !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID) && defined(H5_HAVE_FLOCK))
+
+static unsigned
+test_reader_md_concur(void)
+{
+ /* Output message about test being performed */
+ TESTING("Verify the metadata file for VFD SWMR reader");
+ SKIPPED();
+ HDputs(" Test skipped due to fork, waitpid, or flock not defined.");
+ return 0;
+
+} /* test_reader_md_concur() */
+
+static unsigned
+test_multiple_file_opens_concur(void)
+{
+ /* Output message about test being performed */
+ TESTING("EOT queue entries when opening files concurrently with VFD SWMR");
+ SKIPPED();
+ HDputs(" Test skipped due to fork, waitpid, or flock not defined.");
+ return 0;
+
+} /* test_multiple_file_opens_concur() */
+
+static unsigned
+test_disable_enable_eot_concur(void)
+
+ /* Output message about test being performed */
+ TESTING("Verify concurrent H5Fvfd_swmr_enable/disable_end_of_tick()");
+ SKIPPED();
+ HDputs(" Test skipped due to fork, waitpid, or flock not defined.");
+ return 0;
+
+} /* test_disable_enble_eot_concur() */
+
+#else /* defined(H5_HAVE_FORK && defined(H5_HAVE_WAITPID) && defined(H5_HAVE_FLOCK) */
+
+/*-------------------------------------------------------------------------
+ * Function: test_reader_md_concur()
+ *
+ * Purpose: Verify metadata file info updated by the writer is
+ * what the reader obtained from the metadata file:
+ * --Cases (A), (B), (C), (D), (E)
+ * NOTE: Changes for page buffering/cache are not in place yet.
+ * Index entries are constructed at the front end by the
+ * writer and verified at the back end by the reader.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; October 2018
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_reader_md_concur(void)
+{
+ unsigned i = 0; /* Local index variables */
+ uint8_t *buf = NULL; /* Data page from the page buffer */
+ hid_t dcpl = -1; /* Dataset creation property list */
+ hid_t sid = -1; /* Dataspace ID */
+ hid_t did = -1; /* Dataset ID */
+ int *rwbuf = NULL; /* Data buffer for writing */
+ H5O_info_t oinfo; /* Object metadata information */
+ char dname[100]; /* Name of dataset */
+ hsize_t dims[2] = {50, 20}; /* Dataset dimension sizes */
+ hsize_t max_dims[2] = /* Dataset maximum dimension sizes */
+ {H5S_UNLIMITED, H5S_UNLIMITED};
+ hsize_t chunk_dims[2] = {2, 5}; /* Dataset chunked dimension sizes */
+ unsigned num_entries = 0 ; /* Number of entries in the index */
+ H5FD_vfd_swmr_idx_entry_t *index = NULL; /* Pointer to the index entries */
+
+ hid_t fcpl = -1; /* File creation property list */
+ hid_t fid_writer = -1; /* File ID for writer */
+ hid_t fapl_writer = -1; /* File access property list for writer */
+ H5F_vfd_swmr_config_t *config_writer = NULL; /* VFD SWMR Configuration for writer */
+ pid_t tmppid; /* Child process ID returned by waitpid */
+ pid_t childpid = 0; /* Child process ID */
+ int child_status; /* Status passed to waitpid */
+ int child_wait_option=0; /* Options passed to waitpid */
+ int child_exit_val; /* Exit status of the child */
+
+ int parent_pfd[2]; /* Pipe for parent process as writer */
+ int child_pfd[2]; /* Pipe for child process as reader */
+ int notify = 0; /* Notification between parent and child */
+ H5F_t *file_writer; /* File pointer for writer */
+
+ TESTING("Verify the metadata file for VFD SWMR reader");
+
+ /* Allocate memory for the configuration structure */
+ if((config_writer = HDmalloc(sizeof(*config_writer))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Set up the VFD SWMR configuration + page buffering */
+ fapl_writer = init_vfd_swmr_config_fapl(config_writer, 1, 3, TRUE, 256, MD_FILENAME, FS_PAGE_SIZE);
+ if(fapl_writer == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pset_file_space_page_size(fcpl, FS_PAGE_SIZE) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create an HDF5 file with VFD SWMR configured */
+ if((fid_writer = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl_writer)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid_writer) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create 2 pipes */
+ if(HDpipe(parent_pfd) < 0)
+ FAIL_STACK_ERROR
+
+ if(HDpipe(child_pfd) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Child process as reader
+ */
+ if(childpid == 0) {
+ int child_notify = 0; /* Notification between child and parent */
+ hid_t fid_reader = -1; /* File ID for reader */
+ hid_t fapl_reader = -1; /* File access property list for reader */
+ H5F_t *file_reader; /* File pointer for reader */
+ H5F_vfd_swmr_config_t *config_reader = NULL; /* VFD SWMR configuration for reader */
+ unsigned child_num_entries = 0; /* Number of entries passed to reader */
+ H5FD_vfd_swmr_idx_entry_t *child_index = NULL; /* Index passed to reader */
+
+ /* Close unused write end for writer pipe */
+ if(HDclose(parent_pfd[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close unused read end for reader pipe */
+ if(HDclose(child_pfd[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /*
+ * Case A: reader
+ * --verify an empty index
+ */
+
+ /* Wait for notification 1 from parent to start verification */
+ while(child_notify != 1) {
+ if(HDread(parent_pfd[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Allocate memory for the configuration structure */
+ if((config_reader = HDmalloc(sizeof(*config_reader))) == NULL)
+ HDexit(EXIT_FAILURE);
+
+ /* Set up the VFD SWMR configuration as reader + page buffering */
+ fapl_reader = init_vfd_swmr_config_fapl(config_reader, 1, 3, FALSE, 256, MD_FILENAME, FS_PAGE_SIZE);
+ if(fapl_reader == H5I_INVALID_HID)
+ HDexit(EXIT_FAILURE);
+
+ /* Open the test file as reader */
+ if((fid_reader = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl_reader)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Get file pointer */
+ file_reader = H5VL_object(fid_reader);
+
+ /* Read and verify header and an empty index in the metadata file */
+ if(H5FD__vfd_swmr_reader_md_test(file_reader->shared->lf, 0, NULL) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Send notification 2 to parent that the verification is complete */
+ child_notify = 2;
+ if(HDwrite(child_pfd[1], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /*
+ * Case B: reader
+ * --verify index as sent from writer
+ */
+
+ /* Wait for notification 3 from parent to start verification */
+ while(child_notify != 3) {
+ if(HDread(parent_pfd[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Read num_entries from writer pipe */
+ if(HDread(parent_pfd[0], &child_num_entries, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Free previous index */
+ if(child_index)
+ HDfree(child_index);
+
+ if(child_num_entries) {
+
+ /* Allocate memory for num_entries index */
+ if((child_index = HDcalloc(child_num_entries,
+ sizeof(*child_index))) == NULL)
+ HDexit(EXIT_FAILURE);
+
+ /* Read index from writer pipe */
+ if(HDread(parent_pfd[0], child_index,
+ child_num_entries * sizeof(*child_index)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Read and verify the expected header and index info in the
+ * metadata file
+ */
+ if(H5FD__vfd_swmr_reader_md_test(file_reader->shared->lf,
+ child_num_entries, child_index) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Send notification 4 to parent that the verification is complete */
+ child_notify = 4;
+ if(HDwrite(child_pfd[1], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /*
+ * Case C: reader
+ * --verify index as sent from writer
+ */
+
+ /* Wait for notification 5 from parent to start verification */
+ while(child_notify != 5) {
+ if(HDread(parent_pfd[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Read num_entries from writer pipe */
+ if(HDread(parent_pfd[0], &child_num_entries, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Free previous index */
+ if(child_index)
+ HDfree(child_index);
+
+ if(child_num_entries) {
+ /* Allocate memory for num_entries index */
+ if((child_index = (H5FD_vfd_swmr_idx_entry_t *)
+ HDcalloc(child_num_entries,
+ sizeof(H5FD_vfd_swmr_idx_entry_t))) == NULL)
+ HDexit(EXIT_FAILURE);
+
+ /* Read index from writer pipe */
+ if(HDread(parent_pfd[0], child_index,
+ child_num_entries * sizeof(H5FD_vfd_swmr_idx_entry_t)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Read and verify the expected header and index info in the
+ * metadata file
+ */
+ if(H5FD__vfd_swmr_reader_md_test(file_reader->shared->lf,
+ child_num_entries, child_index) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Send notification 6 to parent that the verification is complete */
+ child_notify = 6;
+ if(HDwrite(child_pfd[1], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /*
+ * Case D: reader
+ * --verify index as sent from writer
+ */
+
+ /* Wait for notification 7 from parent to start verification */
+ while(child_notify != 7) {
+
+ if(HDread(parent_pfd[0], &child_notify, sizeof(int)) < 0)
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Read num_entries from writer pipe */
+ if(HDread(parent_pfd[0], &child_num_entries, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Free previous index */
+ if(child_index)
+ HDfree(child_index);
+
+ if(child_num_entries) {
+ /* Allocate memory for num_entries index */
+ if((child_index = (H5FD_vfd_swmr_idx_entry_t *)
+ HDcalloc(child_num_entries,
+ sizeof(H5FD_vfd_swmr_idx_entry_t))) == NULL)
+ HDexit(EXIT_FAILURE);
+
+ /* Read index from writer pipe */
+ if(HDread(parent_pfd[0], child_index,
+ child_num_entries * sizeof(H5FD_vfd_swmr_idx_entry_t)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Read and verify the expected header and index info in the
+ * metadata file
+ */
+ if(H5FD__vfd_swmr_reader_md_test(file_reader->shared->lf,
+ child_num_entries, child_index) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Send notification 8 to parent that the verification is complete */
+ child_notify = 8;
+ if(HDwrite(child_pfd[1], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /*
+ * Case E: reader
+ * --verify an empty index
+ */
+
+ /* Wait for notification 9 from parent to start verification */
+ while(child_notify != 9) {
+ if(HDread(parent_pfd[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Read and verify header and an empty index in the metadata file */
+ if(H5FD__vfd_swmr_reader_md_test(file_reader->shared->lf, 0, NULL) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Free resources */
+ if(child_index)
+ HDfree(child_index);
+ if(config_reader)
+ HDfree(config_reader);
+
+ /* Closing */
+ if(H5Fclose(fid_reader) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Pclose(fapl_reader) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the pipes */
+ if(HDclose(parent_pfd[0]) < 0)
+ HDexit(EXIT_FAILURE);
+ if(HDclose(child_pfd[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ } /* end child process */
+
+ /*
+ * Parent process as writer
+ */
+
+ /* Close unused read end for writer pipe */
+ if(HDclose(parent_pfd[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close unused write end for reader pipe */
+ if(HDclose(child_pfd[1]) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case A: writer
+ * --open the file as VFD SWMR writer
+ */
+
+ /* Open as VFD SWMR writer */
+ if((fid_writer = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl_writer)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Send notification 1 to reader to start verfication */
+ notify = 1;
+ if(HDwrite(parent_pfd[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Case B: writer
+ * --create datasets to ensure ticks elapse
+ * --construct 12 entries in the index
+ * --update the metadata file with the index
+ */
+
+ /* Wait for notification 2 from reader that the verifcation is complete */
+ while(notify != 2) {
+ if(HDread(child_pfd[0], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+ }
+
+ /* Create dataset creation property list */
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use chunked dataset */
+ if(H5Pset_chunk(dcpl, 2, chunk_dims) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create dataspace */
+ if((sid = H5Screate_simple(2, dims, max_dims)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Perform activities to ensure that ticks elapse */
+ for(i = 0; i < config_writer->max_lag + 1; i++) {
+ decisleep(config_writer->tick_len);
+
+ /* Create a chunked dataset */
+ sprintf(dname, "dset %d", i);
+ if((did = H5Dcreate2(fid_writer, dname, H5T_NATIVE_INT, sid,
+ H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get dataset object header address */
+ if(H5Oget_info(did, &oinfo, H5O_INFO_BASIC) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ num_entries = 12;
+
+ /* Allocate num_entries for the data buffer */
+ if((buf = HDcalloc(num_entries, FS_PAGE_SIZE)) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Allocate memory for num_entries index */
+ index = HDcalloc(num_entries, sizeof(H5FD_vfd_swmr_idx_entry_t));
+ if(NULL == index)
+ FAIL_STACK_ERROR;
+
+ /* Construct index for updating the metadata file */
+ for(i = 0; i < num_entries; i++) {
+ index[i].hdf5_page_offset = 3 + 7 * i;
+ index[i].md_file_page_offset = 1 + (num_entries - i) * 5;
+ index[i].length = (uint32_t)FS_PAGE_SIZE;
+ index[i].entry_ptr = &buf[i * FS_PAGE_SIZE];
+ }
+
+ /* Get the file pointer */
+ file_writer = H5VL_object(fid_writer);
+
+ /* Update the metadata file with the index */
+ if(H5F_update_vfd_swmr_metadata_file(file_writer, num_entries, index) < 0)
+ TEST_ERROR;
+
+ /* Send notification 3 to child to start verification */
+ notify = 3;
+ if(HDwrite(parent_pfd[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Send num_entries to the reader */
+ if(HDwrite(parent_pfd[1], &num_entries, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Send index to the reader */
+ if(HDwrite(parent_pfd[1], index,
+ num_entries * sizeof(H5FD_vfd_swmr_idx_entry_t)) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Case C: writer
+ * --write to the datasets to ensure ticks elapse
+ * --update 3 entries in the index
+ * --update the metadata file with the index
+ */
+
+ /* Wait for notification 4 from reader that the verifcation is complete */
+ while(notify != 4) {
+ if(HDread(child_pfd[0], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+ }
+
+ /* Allocate memory for the read/write buffer */
+ if((rwbuf = HDmalloc(sizeof(*rwbuf) * (50 * 20))) == NULL)
+ FAIL_STACK_ERROR;
+ for(i = 0; i < (50 * 20); i++)
+ rwbuf[i] = (int)i;
+
+ /* Perform activities to ensure that max_lag ticks elapse */
+ for(i = 0; i < config_writer->max_lag + 1; i++) {
+ decisleep(config_writer->tick_len);
+
+ /* Open the dataset */
+ sprintf(dname, "dset %d", i);
+ if((did = H5Dopen2(fid_writer, dname, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Write to the dataset */
+ if(H5Dwrite(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
+ H5P_DEFAULT, rwbuf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ /* Update 3 entries in the index */
+ num_entries = 3;
+ for(i = 0; i < num_entries; i++)
+ index[i].entry_ptr = &buf[i * FS_PAGE_SIZE];
+
+ /* Update the metadata file with the index */
+ if(H5F_update_vfd_swmr_metadata_file(file_writer, num_entries, index) < 0)
+ TEST_ERROR;
+
+ /* Send notification 5 to reader to start verification */
+ notify = 5;
+ if(HDwrite(parent_pfd[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Send num_entries to the reader */
+ if(HDwrite(parent_pfd[1], &num_entries, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Send index to the reader */
+ if(HDwrite(parent_pfd[1], index,
+ num_entries * sizeof(H5FD_vfd_swmr_idx_entry_t)) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Case D: writer
+ * --read from the datasets to ensure ticks elapse
+ * --update 5 entries in the index
+ * --update the metadata file with the index
+ */
+
+ /* Wait for notification 6 from reader that the verifcation is complete */
+ while(notify != 6) {
+ if(HDread(child_pfd[0], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+ }
+
+ /* Perform activities to ensure that max_lag ticks elapse */
+ for(i = 0; i < config_writer->max_lag + 1; i++) {
+ decisleep(config_writer->tick_len);
+
+ /* Open the dataset */
+ sprintf(dname, "dset %d", i);
+ if((did = H5Dopen2(fid_writer, dname, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Read from the dataset */
+ if(H5Dread(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
+ H5P_DEFAULT, rwbuf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ /* Update 5 entries in the index */
+ num_entries = 5;
+ for(i = 0; i < num_entries; i++)
+ index[i].entry_ptr = &buf[i * FS_PAGE_SIZE];
+
+ /* Update the metadata file with the index */
+ if(H5F_update_vfd_swmr_metadata_file(file_writer, num_entries, index) < 0)
+ TEST_ERROR;
+
+ /* Send notification 7 to reader to start verification */
+ notify = 7;
+ if(HDwrite(parent_pfd[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Send num_entries to the reader */
+ if(HDwrite(parent_pfd[1], &num_entries, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Send index to the reader */
+ if(HDwrite(parent_pfd[1], index,
+ num_entries * sizeof(H5FD_vfd_swmr_idx_entry_t)) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Case E: writer
+ * --write to the datasets again to ensure ticks elapse
+ * --update the metadata file with an empty index
+ */
+
+ /* Wait for notification 8 from reader that the verifcation is complete */
+ while(notify != 8) {
+ if(HDread(child_pfd[0], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+ }
+
+ /* Perform activities to ensure that ticks elapse */
+ for(i = 0; i < config_writer->max_lag + 1; i++) {
+ decisleep(config_writer->tick_len);
+
+ /* Open the dataset */
+ sprintf(dname, "dset %d", i);
+ if((did = H5Dopen2(fid_writer, dname, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Write to the dataset */
+ if(H5Dwrite(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL,
+ H5P_DEFAULT, rwbuf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ /* Update the metadata file with 0 entries and NULL index */
+ if(H5F_update_vfd_swmr_metadata_file(file_writer, 0, NULL) < 0)
+ TEST_ERROR;
+
+ /* Send notification 8 to reader to start verification */
+ notify = 9;
+ if(HDwrite(parent_pfd[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Done
+ */
+
+ /* Close the pipes */
+ if(HDclose(parent_pfd[1]) < 0)
+ FAIL_STACK_ERROR;
+ if(HDclose(child_pfd[0]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if((tmppid = HDwaitpid(childpid, &child_status, child_wait_option)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check exit status of child process */
+ if(WIFEXITED(child_status)) {
+ if((child_exit_val = WEXITSTATUS(child_status)) != 0)
+ TEST_ERROR
+ } else { /* child process terminated abnormally */
+ TEST_ERROR
+ }
+
+ /* Closing */
+ if(H5Fclose(fid_writer) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pclose(fapl_writer) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free resources */
+ if(config_writer)
+ HDfree(config_writer);
+ if(buf)
+ HDfree(buf);
+ if(rwbuf)
+ HDfree(rwbuf);
+ if(index)
+ HDfree(index);
+
+ PASSED();
+ return 0;
+
+error:
+ if(config_writer)
+ HDfree(config_writer);
+ if(buf)
+ HDfree(buf);
+ if(rwbuf)
+ HDfree(rwbuf);
+ if(index)
+ HDfree(index);
+
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl_writer);
+ H5Fclose(fid_writer);
+ H5Pclose(fcpl);
+ } H5E_END_TRY;
+
+ return 1;
+} /* test_reader_md_concur() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_multiple_file_opens_concur()
+ *
+ * Purpose: Verify the entries on the EOT queue when opening files
+ * with and without VFD SWMR configured.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; 11/18/2019
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_multiple_file_opens_concur(void)
+{
+ hid_t fcpl = H5I_INVALID_HID;
+ pid_t tmppid; /* Child process ID returned by waitpid */
+ pid_t childpid = 0; /* Child process ID */
+ int child_status; /* Status passed to waitpid */
+ int child_wait_option=0; /* Options passed to waitpid */
+ int child_exit_val; /* Exit status of the child */
+ int parent_pfd[2]; /* Pipe for parent process as writer */
+ int child_pfd[2]; /* Pipe for child process as reader */
+ int notify = 0; /* Notification between parent and child */
+ hid_t fid1 = H5I_INVALID_HID, fid2 = H5I_INVALID_HID;
+ hid_t fapl1 = H5I_INVALID_HID, fapl2 = H5I_INVALID_HID;
+ H5F_vfd_swmr_config_t *config1 = NULL; /* VFD SWMR configuration */
+ H5F_vfd_swmr_config_t *config2 = NULL; /* VFD SWMR configuration */
+ H5F_t *f1, *f2; /* File pointer */
+ eot_queue_entry_t *curr;
+
+ TESTING("EOT queue entries when opening files concurrently with VFD SWMR");
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pset_file_space_page_size(fcpl, FS_PAGE_SIZE) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create file A */
+ if((fid1 = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create file B */
+ if((fid2 = H5Fcreate(FILENAME2, H5F_ACC_TRUNC, fcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create 2 pipes */
+ if(HDpipe(parent_pfd) < 0)
+ FAIL_STACK_ERROR
+
+ if(HDpipe(child_pfd) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Child process
+ */
+ if(childpid == 0) {
+ int child_notify = 0; /* Notification between child and parent */
+ hid_t fid_writer = -1; /* File ID for writer */
+ hid_t fapl_writer = -1; /* File access property list for writer */
+ H5F_vfd_swmr_config_t *config_writer = NULL; /* VFD SWMR configuration for reader */
+
+ /* Close unused write end for writer pipe */
+ if(HDclose(parent_pfd[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close unused read end for reader pipe */
+ if(HDclose(child_pfd[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /*
+ * Set up and open file B as VFD SWMR writer
+ */
+
+ /* Wait for notification 1 from parent before opening file B */
+ while(child_notify != 1) {
+ if(HDread(parent_pfd[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Allocate memory for VFD SMWR configuration */
+ if((config_writer = HDmalloc(sizeof(*config_writer))) == NULL)
+ HDexit(EXIT_FAILURE);
+
+
+ /* Set the VFD SWMR configuration in fapl_writer + page buffering */
+ fapl_writer = init_vfd_swmr_config_fapl(config_writer, 1, 3, TRUE, 256, MD_FILENAME2, FS_PAGE_SIZE);
+ if(fapl_writer == H5I_INVALID_HID)
+ HDexit(EXIT_FAILURE);
+
+ /* Open file B as VFD SWMR writer */
+ if((fid_writer = H5Fopen(FILENAME2, H5F_ACC_RDWR, fapl_writer)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Send notification 2 to parent that file B is open */
+ child_notify = 2;
+ if(HDwrite(child_pfd[1], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification 3 from parent before closing file B */
+ while(child_notify != 3) {
+ if(HDread(parent_pfd[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ if(config_writer )
+ HDfree(config_writer);
+
+ /* Close the file */
+ if(H5Fclose(fid_writer) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Pclose(fapl_writer) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Send notification 4 to parent that file B is closed */
+ child_notify = 4;
+ if(HDwrite(child_pfd[1], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the pipes */
+ if(HDclose(parent_pfd[0]) < 0)
+ HDexit(EXIT_FAILURE);
+ if(HDclose(child_pfd[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ } /* end child process */
+
+ /*
+ * Parent process
+ */
+
+ /* Close unused read end for writer pipe */
+ if(HDclose(parent_pfd[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close unused write end for reader pipe */
+ if(HDclose(child_pfd[1]) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Set up and open file A as VFD SWMR writer
+ */
+
+ /* Allocate memory for VFD SWMR configuration */
+ if((config1 = HDmalloc(sizeof(*config1))) == NULL)
+ FAIL_STACK_ERROR
+
+ /* Set the VFD SWMR configuration in fapl1 + page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 7, 10, TRUE, 256, MD_FILENAME, FS_PAGE_SIZE);
+ if(fapl1 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR
+
+ /* Open file A as VFD SWMR writer */
+ if((fid1 = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl1)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get a pointer to the internal file object */
+ if(NULL == (f1 = H5VL_object(fid1)))
+ FAIL_STACK_ERROR
+
+ /* Head of EOT queue should be a writer */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) == NULL || !curr->vfd_swmr_writer)
+ TEST_ERROR;
+
+ /* The EOT queue's first entry should be f1 */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) == NULL || curr->vfd_swmr_file != f1)
+ TEST_ERROR;
+
+
+ /* Send notification 1 to child to open file B */
+ notify = 1;
+ if(HDwrite(parent_pfd[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for notification 2 from child that file B is open */
+ while(notify != 2) {
+ if(HDread(child_pfd[0], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+ }
+
+ /* Open file B as VFD SWMR reader */
+
+ /* Allocate memory for VFD SWMR configuration */
+ if((config2 = HDmalloc(sizeof(*config2))) == NULL)
+ FAIL_STACK_ERROR
+
+ /* Set the VFD SWMR configuration in fapl2 + page buffering */
+ fapl2 = init_vfd_swmr_config_fapl(config2, 1, 3, FALSE, 256, MD_FILENAME2, FS_PAGE_SIZE);
+ if(fapl2 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR
+
+ /* Open file B as VFD SWMR reader */
+ if((fid2 = H5Fopen(FILENAME2, H5F_ACC_RDONLY, fapl2)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get a pointer to the internal file object */
+ if(NULL == (f2 = H5VL_object(fid2)))
+ FAIL_STACK_ERROR
+
+ /* Head of EOT queue should NOT be a writer */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) != NULL && curr->vfd_swmr_writer)
+ TEST_ERROR;
+
+ /* The EOT queue's first entry should be f2 */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) == NULL || curr->vfd_swmr_file != f2)
+ TEST_ERROR;
+
+ /* Send notification 3 to child to close file B */
+ notify = 3;
+ if(HDwrite(parent_pfd[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for notification 4 from child that file B is closed */
+ while(notify != 4) {
+ if(HDread(child_pfd[0], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+ }
+
+ /*
+ * Done
+ */
+
+ /* Close the pipes */
+ if(HDclose(parent_pfd[1]) < 0)
+ FAIL_STACK_ERROR;
+ if(HDclose(child_pfd[0]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if((tmppid = HDwaitpid(childpid, &child_status, child_wait_option)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check exit status of child process */
+ if(WIFEXITED(child_status)) {
+ if((child_exit_val = WEXITSTATUS(child_status)) != 0)
+ TEST_ERROR
+ } else { /* child process terminated abnormally */
+ TEST_ERROR
+ }
+
+ /* Closing */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free resources */
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+
+ PASSED();
+ return 0;
+
+error:
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl1);
+ H5Pclose(fapl2);
+ H5Fclose(fid1);
+ H5Fclose(fid2);
+ H5Pclose(fcpl);
+ } H5E_END_TRY;
+
+ return 1;
+} /* test_multiple_file_opens_concur() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_enable_disable_eot_concur()
+ *
+ * Purpose: Verify the public routines:
+ * H5Fvfd_swmr_disable_end_of_tick()
+ * H5Fvfd_swmr_enable_end_of_tick()
+ * enables/disables EOT when the files are opened
+ * concurrently.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; June 2020
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_disable_enable_eot_concur(void)
+{
+ hid_t fcpl = -1; /* File creation property list */
+ hid_t fid_writer = -1; /* File ID for writer */
+ hid_t fapl_writer = -1; /* File access property list for writer */
+ H5F_vfd_swmr_config_t *config_writer = NULL; /* VFD SWMR Configuration for writer */
+ pid_t tmppid; /* Child process ID returned by waitpid */
+ pid_t childpid = 0; /* Child process ID */
+ int child_status; /* Status passed to waitpid */
+ int child_wait_option=0; /* Options passed to waitpid */
+ int child_exit_val; /* Exit status of the child */
+
+ int parent_pfd[2]; /* Pipe for parent process as writer */
+ int child_pfd[2]; /* Pipe for child process as reader */
+ int notify = 0; /* Notification between parent and child */
+
+ TESTING("Verify concurrent H5Fvfd_swmr_enable/disable_end_of_tick()");
+
+ /* Allocate memory for the configuration structure */
+ if((config_writer = HDmalloc(sizeof(*config_writer))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Set up the VFD SWMR configuration + page buffering */
+ fapl_writer = init_vfd_swmr_config_fapl(config_writer, 1, 3, TRUE, 256, MD_FILENAME, FS_PAGE_SIZE);
+ if(fapl_writer == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pset_file_space_page_size(fcpl, FS_PAGE_SIZE) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create an HDF5 file with VFD SWMR configured */
+ if((fid_writer = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl_writer)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid_writer) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create 2 pipes */
+ if(HDpipe(parent_pfd) < 0)
+ FAIL_STACK_ERROR
+
+ if(HDpipe(child_pfd) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Child process as reader
+ */
+ if(childpid == 0) {
+ int child_notify = 0; /* Notification between child and parent */
+ hid_t fid_reader = H5I_INVALID_HID; /* File ID for reader */
+ hid_t fid_reader2 = H5I_INVALID_HID; /* File ID for reader */
+ hid_t fid_reader3 = H5I_INVALID_HID; /* File ID for reader */
+ hid_t fapl_reader = H5I_INVALID_HID; /* File access property list for reader */
+ H5F_vfd_swmr_config_t *config_reader = NULL; /* VFD SWMR configuration */
+ H5F_t *file_reader; /* File pointer */
+ eot_queue_entry_t *curr; /* Pointer to an entry on the EOT queue */
+ unsigned count = 0; /* Counter */
+
+ /* Close unused write end for writer pipe */
+ if(HDclose(parent_pfd[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close unused read end for reader pipe */
+ if(HDclose(child_pfd[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /*
+ * Open the file 3 times as VFD SWMR reader
+ * Enable and disable EOT for a file
+ * Verify the state of the EOT queue
+ */
+
+ /* Wait for notification 1 from parent to start verification */
+ while(child_notify != 1) {
+ if(HDread(parent_pfd[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Allocate memory for the configuration structure */
+ if((config_reader = HDmalloc(sizeof(*config_reader))) == NULL)
+ HDexit(EXIT_FAILURE);
+
+ /* Set up the VFD SWMR configuration as reader + page buffering */
+ fapl_reader = init_vfd_swmr_config_fapl(config_reader, 1, 3, FALSE, 256, MD_FILENAME, FS_PAGE_SIZE);
+ if(fapl_reader == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Open the test file as reader */
+ if((fid_reader = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl_reader)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Open the same test file as reader (a second time) */
+ if((fid_reader2 = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl_reader)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Open the same test file as reader (a third time) */
+ if((fid_reader3 = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl_reader)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Verify the # of files on the EOT queue is 3 */
+ count = 0;
+ TAILQ_FOREACH(curr, &eot_queue_g, link)
+ count++;
+ if(count != 3)
+ HDexit(EXIT_FAILURE);
+
+ /* Disable EOT for the second opened file */
+ if(H5Fvfd_swmr_disable_end_of_tick(fid_reader2) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Verify the # of files on the EOT queue is 2 */
+ count = 0;
+ TAILQ_FOREACH(curr, &eot_queue_g, link)
+ count++;
+ if(count != 2)
+ HDexit(EXIT_FAILURE);
+
+ /* Get file pointer */
+ file_reader = H5VL_object(fid_reader2);
+
+ /* Should not find the second opened file on the EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if(curr->vfd_swmr_file == file_reader)
+ break;
+ }
+ if(curr != NULL && curr->vfd_swmr_file == file_reader)
+ HDexit(EXIT_FAILURE);
+
+ /* Enable EOT for the second opened file again */
+ if(H5Fvfd_swmr_enable_end_of_tick(fid_reader2) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Verify the # of files on the EOT queue is 3 */
+ count = 0;
+ TAILQ_FOREACH(curr, &eot_queue_g, link)
+ count++;
+ if(count != 3)
+ HDexit(EXIT_FAILURE);
+
+ /* Should find the second opened file on the EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if(curr->vfd_swmr_file == file_reader)
+ break;
+ }
+ if(curr == NULL || curr->vfd_swmr_file != file_reader)
+ HDexit(EXIT_FAILURE);
+
+ /* Closing */
+ if(H5Fclose(fid_reader) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Fclose(fid_reader2) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Fclose(fid_reader3) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Pclose(fapl_reader) < 0)
+ HDexit(EXIT_FAILURE);
+ if(config_reader)
+ HDfree(config_reader);
+
+ /* Close the pipes */
+ if(HDclose(parent_pfd[0]) < 0)
+ HDexit(EXIT_FAILURE);
+ if(HDclose(child_pfd[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ } /* end child process */
+
+ /*
+ * Parent process as writer
+ */
+
+ /* Close unused read end for writer pipe */
+ if(HDclose(parent_pfd[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close unused write end for reader pipe */
+ if(HDclose(child_pfd[1]) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Open the file as VFD SWMR writer
+ */
+
+ /* Open as VFD SWMR writer */
+ if((fid_writer = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl_writer)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Send notification 1 to reader to start verfication */
+ notify = 1;
+ if(HDwrite(parent_pfd[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Done
+ */
+
+ /* Close the pipes */
+ if(HDclose(parent_pfd[1]) < 0)
+ FAIL_STACK_ERROR;
+ if(HDclose(child_pfd[0]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if((tmppid = HDwaitpid(childpid, &child_status, child_wait_option)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check exit status of child process */
+ if(WIFEXITED(child_status)) {
+ if((child_exit_val = WEXITSTATUS(child_status)) != 0)
+ TEST_ERROR
+ } else { /* child process terminated abnormally */
+ TEST_ERROR
+ }
+
+ /* Closing */
+ if(H5Fclose(fid_writer) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pclose(fapl_writer) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free resources */
+ if(config_writer)
+ HDfree(config_writer);
+
+ PASSED();
+ return 0;
+
+error:
+ if(config_writer)
+ HDfree(config_writer);
+
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl_writer);
+ H5Fclose(fid_writer);
+ H5Pclose(fcpl);
+ } H5E_END_TRY;
+
+ return 1;
+} /* test_disable_enable_eot_concur() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_file_end_tick_concur()
+ *
+ * Purpose: Verify the public routine H5Fvfd_swmr_end_tick()
+ * triggers end of tick processing when the files
+ * are opened concurrently.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; June 2020
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_file_end_tick_concur(void)
+{
+ hid_t fcpl = -1; /* File creation property list */
+ hid_t fid_writer = -1; /* File ID for writer */
+ hid_t fapl_writer = -1; /* File access property list for writer */
+ H5F_vfd_swmr_config_t *config_writer = NULL; /* VFD SWMR Configuration for writer */
+ pid_t tmppid; /* Child process ID returned by waitpid */
+ pid_t childpid = 0; /* Child process ID */
+ int child_status; /* Status passed to waitpid */
+ int child_wait_option=0; /* Options passed to waitpid */
+ int child_exit_val; /* Exit status of the child */
+
+ int parent_pfd[2]; /* Pipe for parent process as writer */
+ int child_pfd[2]; /* Pipe for child process as reader */
+ int notify = 0; /* Notification between parent and child */
+
+ TESTING("Verify concurrent H5Fvfd_swmr_end_tick()");
+
+ /* Allocate memory for the configuration structure */
+ if((config_writer = HDmalloc(sizeof(*config_writer))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Set up the VFD SWMR configuration + page buffering */
+ fapl_writer = init_vfd_swmr_config_fapl(config_writer, 1, 3, TRUE, 256, MD_FILENAME, FS_PAGE_SIZE);
+ if(fapl_writer == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pset_file_space_page_size(fcpl, FS_PAGE_SIZE) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create an HDF5 file with VFD SWMR configured */
+ if((fid_writer = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl_writer)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid_writer) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create 2 pipes */
+ if(HDpipe(parent_pfd) < 0)
+ FAIL_STACK_ERROR
+
+ if(HDpipe(child_pfd) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Child process as reader
+ */
+ if(childpid == 0) {
+ int child_notify = 0; /* Notification between child and parent */
+ hid_t fid_reader1 = H5I_INVALID_HID; /* File ID for reader */
+ hid_t fid_reader2 = H5I_INVALID_HID; /* File ID for reader */
+ hid_t fid_reader3 = H5I_INVALID_HID; /* File ID for reader */
+ hid_t fapl_reader = H5I_INVALID_HID; /* File access property list for reader */
+ H5F_vfd_swmr_config_t *config_reader = NULL; /* VFD SWMR configuration */
+ H5F_t *f1, *f2, *f3; /* File pointer */
+ uint64_t s1 = 0; /* Saved tick_num */
+ uint64_t s2 = 0; /* Saved tick_num */
+ uint64_t s3 = 0; /* Saved tick_num */
+
+ /* Close unused write end for writer pipe */
+ if(HDclose(parent_pfd[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close unused read end for reader pipe */
+ if(HDclose(child_pfd[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /*
+ * Open the file 3 times as VFD SWMR reader
+ * Trigger EOT for the files
+ */
+
+ /* Wait for notification 1 from parent to start verification */
+ while(child_notify != 1) {
+ if(HDread(parent_pfd[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Allocate memory for the configuration structure */
+ if((config_reader = HDmalloc(sizeof(*config_reader))) == NULL)
+ HDexit(EXIT_FAILURE);
+
+ /* Set up the VFD SWMR configuration as reader + page buffering */
+ fapl_reader = init_vfd_swmr_config_fapl(config_reader, 1, 3, FALSE, 256, MD_FILENAME, FS_PAGE_SIZE);
+ if(fapl_reader == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Open the test file as reader */
+ if((fid_reader1 = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl_reader)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Open the same test file as reader (a second time) */
+ if((fid_reader2 = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl_reader)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Open the same test file as reader (a third time) */
+ if((fid_reader3 = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl_reader)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Get file pointer */
+ f1 = H5VL_object(fid_reader1);
+ f2 = H5VL_object(fid_reader2);
+ f3 = H5VL_object(fid_reader3);
+
+ /* Saved tick_num for the 3 files */
+ s1 = f1->shared->tick_num;
+ s2 = f2->shared->tick_num;
+ s3 = f3->shared->tick_num;
+
+ /* Trigger EOT for the second opened file */
+ if(H5Fvfd_swmr_end_tick(fid_reader2) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Verify tick_num should not be less than the previous tick_num */
+ if(f2->shared->tick_num < s2)
+ HDexit(EXIT_FAILURE);
+
+ if(H5Fclose(fid_reader2) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Trigger EOT for the first opened file */
+ if(H5Fvfd_swmr_end_tick(fid_reader1) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Verify tick_num should not be less than the previous tick_num */
+ if(f1->shared->tick_num < s1)
+ HDexit(EXIT_FAILURE);
+
+ if(H5Fclose(fid_reader1) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Trigger end tick processing for the third opened file */
+ if(H5Fvfd_swmr_end_tick(fid_reader3) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Verify tick_num should not be less than the previous tick_num */
+ if(f3->shared->tick_num < s3)
+ HDexit(EXIT_FAILURE);
+
+ if(H5Fclose(fid_reader3) < 0)
+ HDexit(EXIT_FAILURE);
+
+ if(H5Pclose(fapl_reader) < 0)
+ HDexit(EXIT_FAILURE);
+ if(config_reader)
+ HDfree(config_reader);
+
+ /* Close the pipes */
+ if(HDclose(parent_pfd[0]) < 0)
+ HDexit(EXIT_FAILURE);
+ if(HDclose(child_pfd[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ } /* end child process */
+
+ /*
+ * Parent process as writer
+ */
+
+ /* Close unused read end for writer pipe */
+ if(HDclose(parent_pfd[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close unused write end for reader pipe */
+ if(HDclose(child_pfd[1]) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Open the file as VFD SWMR writer
+ */
+
+ /* Open as VFD SWMR writer */
+ if((fid_writer = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl_writer)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Send notification 1 to reader to start verfication */
+ notify = 1;
+ if(HDwrite(parent_pfd[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Done
+ */
+
+ /* Close the pipes */
+ if(HDclose(parent_pfd[1]) < 0)
+ FAIL_STACK_ERROR;
+ if(HDclose(child_pfd[0]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if((tmppid = HDwaitpid(childpid, &child_status, child_wait_option)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check exit status of child process */
+ if(WIFEXITED(child_status)) {
+ if((child_exit_val = WEXITSTATUS(child_status)) != 0)
+ TEST_ERROR
+ } else { /* child process terminated abnormally */
+ TEST_ERROR
+ }
+
+ /* Closing */
+ if(H5Fclose(fid_writer) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pclose(fapl_writer) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free resources */
+ if(config_writer)
+ HDfree(config_writer);
+
+ PASSED();
+ return 0;
+
+error:
+ if(config_writer)
+ HDfree(config_writer);
+
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl_writer);
+ H5Fclose(fid_writer);
+ H5Pclose(fcpl);
+ } H5E_END_TRY;
+
+ return 1;
+} /* test_file_end_tick_concur() */
+
+#endif /* !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID) && defined(H5_HAVE_FLOCK)) */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_multiple_file_opens()
+ *
+ * Purpose: Verify the entries on the EOT queue when opening files
+ * with and without VFD SWMR configured.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; 11/18/2019
+ *
+ *-------------------------------------------------------------------------
+ */
+
+static unsigned
+test_multiple_file_opens(void)
+{
+ hid_t fid1 = -1; /* File ID */
+ hid_t fid2 = -1; /* File ID */
+ hid_t fid = -1; /* File ID */
+ hid_t fcpl = -1; /* File creation property list ID */
+ hid_t fapl1 = -1; /* File access property list ID */
+ hid_t fapl2 = -1; /* File access property list ID */
+ H5F_t *f1, *f2, *f; /* File pointer */
+ H5F_vfd_swmr_config_t *config1 = NULL; /* Configuration for VFD SWMR */
+ H5F_vfd_swmr_config_t *config2 = NULL; /* Configuration for VFD SWMR */
+ eot_queue_entry_t *curr;
+
+ TESTING("EOT queue entries when opening files with/without VFD SWMR");
+
+ /* Allocate memory for the configuration structure */
+ if((config1 = HDmalloc(sizeof(*config1))) == NULL)
+ FAIL_STACK_ERROR;
+ if((config2 = HDmalloc(sizeof(*config2))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Configured as VFD SWMR writer + page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 6, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Configured as VFD SWMR writer + page buffering */
+ fapl2 = init_vfd_swmr_config_fapl(config2, 4, 6, TRUE, 2, MD_FILENAME2, 4096);
+ if(fapl2 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create a file without VFD SWMR */
+ if((fid = H5Fcreate(FNAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ /* Get a pointer to the internal file object */
+ if(NULL == (f = H5VL_object(fid)))
+ FAIL_STACK_ERROR
+
+ /* Verify the global vfd_swmr_writer_g is not set */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) != NULL && curr->vfd_swmr_writer)
+ TEST_ERROR;
+ /* The EOT queue should be empty */
+ if(!TAILQ_EMPTY(&eot_queue_g))
+ TEST_ERROR;
+
+ /* Create a file with VFD SWMR writer */
+ if((fid1 = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl1)) < 0)
+ TEST_ERROR;
+
+ /* Get a pointer to the internal file object */
+ if(NULL == (f1 = H5VL_object(fid1)))
+ FAIL_STACK_ERROR
+
+ /* Head of EOT queue should be a writer */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) == NULL || !curr->vfd_swmr_writer)
+ TEST_ERROR;
+ /* The EOT queue should be initialized with the first entry equals to f1 */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) == NULL || curr->vfd_swmr_file != f1)
+ TEST_ERROR;
+
+ /* Create another file with VFD SWMR writer */
+ if((fid2 = H5Fcreate(FILENAME2, H5F_ACC_TRUNC, fcpl, fapl2)) < 0)
+ TEST_ERROR;
+
+ /* Get a pointer to the internal file object */
+ if(NULL == (f2 = H5VL_object(fid2)))
+ FAIL_STACK_ERROR
+
+ /* Head of EOT queue should be a writer */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) == NULL || !curr->vfd_swmr_writer)
+ TEST_ERROR;
+ /* The EOT queue's first entry should be f1 */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) == NULL || curr->vfd_swmr_file != f1)
+ TEST_ERROR;
+
+ /* The file without VFD SWMR should not exist on the EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if(curr->vfd_swmr_file == f)
+ TEST_ERROR
+ }
+
+ /* Close the first file with VFD SWMR */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Head of EOT queue should be a writer */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) == NULL || !curr->vfd_swmr_writer)
+ TEST_ERROR;
+ /* The EOT queue's first entry should be f2 */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) == NULL || curr->vfd_swmr_file != f2)
+ TEST_ERROR;
+
+ /* Close the second file with VFD SWMR */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Head of EOT queue should not be a writer */
+ if((curr = TAILQ_FIRST(&eot_queue_g)) != NULL && curr->vfd_swmr_writer)
+ TEST_ERROR;
+ /* The EOT queue should be empty */
+ if(!TAILQ_EMPTY(&eot_queue_g))
+ TEST_ERROR;
+
+ /* Closing */
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free buffers */
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl1);
+ H5Pclose(fapl2);
+ H5Pclose(fcpl);
+ H5Fclose(fid);
+ H5Fclose(fid1);
+ H5Fclose(fid2);
+ } H5E_END_TRY;
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+
+ return 1;
+} /* test_multiple_file_opens() */
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_same_file_opens()
+ *
+ * Purpose: Verify multiple opens of the same file as listed below:
+ *
+ * #1st open#
+ * #2nd open# VW VR W R
+ * ------------------
+ * VW | s f f f |
+ * VR | f f f f |
+ * W | f f s f |
+ * R | f f s s |
+ * ------------------
+ *
+ * Notations:
+ * W: H5F_ACC_RDWR
+ * R: H5F_ACC_RDONLY
+ * VW: VFD SWMR writer
+ * VR: VFD SWMR reader
+ *
+ * f: the open fails
+ * s: the open succeeds
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; October 2019
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_same_file_opens(void)
+{
+ hid_t fid = -1; /* File ID */
+ hid_t fid2 = -1; /* File ID */
+ hid_t fcpl = -1; /* File creation property list ID */
+ hid_t fapl1 = -1; /* File access property list ID */
+ hid_t fapl2 = -1; /* File access property list ID */
+ H5F_vfd_swmr_config_t *config1 = NULL; /* Configuration for VFD SWMR */
+ H5F_vfd_swmr_config_t *config2 = NULL; /* Configuration for VFD SWMR */
+
+ TESTING("Multiple opens of the same file with VFD SWMR configuration");
+
+ /* Should succeed without VFD SWMR configured */
+ if((fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Allocate memory for the configuration structure */
+ if((config1 = HDmalloc(sizeof(*config1))) == NULL)
+ FAIL_STACK_ERROR;
+ if((config2 = HDmalloc(sizeof(*config2))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Tests for first column
+ */
+
+ /* Create the test file */
+ if((fid = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /* Set the VFD SWMR configuration in fapl1 + page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 10, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Open the file as VFD SWMR writer */
+ /* Keep the file open */
+ if((fid = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl1)) < 0)
+ TEST_ERROR;
+
+ /* Open the same file again as VFD SWMR writer */
+ /* Should succeed: 1st open--VFD SWMR writer, 2nd open--VFD SWMR writer */
+ if((fid2 = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl1)) < 0)
+ TEST_ERROR;
+
+ /* Close the second file open */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Set the VFD SWMR configuration in fapl2 + page buffering */
+ fapl2 = init_vfd_swmr_config_fapl(config2, 3, 8, FALSE, 3, MD_FILENAME, 4096);
+ if(fapl2 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Open the same file again as VFD SWMR reader */
+ /* Should fail: 1st open--VFD SWMR writer, 2nd open--VFD SWMR reader */
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl2);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR;
+
+ if(H5Pclose(fapl2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Open the same file again as regular writer */
+ /* Should fail: 1st open--VFD SWMR writer, 2nd open--regular writer */
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(FILENAME, H5F_ACC_RDWR, H5P_DEFAULT);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR;
+
+ /* Open the same file again as regular reader */
+ /* Should fail: 1st open--VFD SWMR writer, 2nd open--regular reader */
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(FILENAME, H5F_ACC_RDONLY, H5P_DEFAULT);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR;
+
+ /* Close the 1st open file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /*
+ * Tests for second column
+ */
+
+ /* Set up as VFD SWMR reader + page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 10, FALSE, 2, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Open the file as VFD SWMR reader */
+ /* Should fail because there is no metadata file */
+ /* Take a while to complete due to retries */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(FILENAME, H5F_ACC_RDONLY, fapl1);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR;
+
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Tests for third column
+ */
+
+ /* Open the file as regular writer */
+ /* Keep the file open */
+ if((fid = H5Fopen(FILENAME, H5F_ACC_RDWR, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ /* Set up as VFD SWMR writer + page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 10, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Open the same file again as VFD SWMR writer */
+ /* Should fail: 1st open--regular writer, 2nd open--VFD SWMR writer */
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl1);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR;
+
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /* Open the same file again as regular writer */
+ /* Should succeed: 1st open--regular writer, 2nd open--regular writer */
+ if((fid2 = H5Fopen(FILENAME, H5F_ACC_RDWR, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /* Open the same file again as regular reader */
+ /* Should succeed: 1st open is writer, 2nd open is the same file as reader */
+ if((fid2 = H5Fopen(FILENAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the 1st open file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Tests for fourth column
+ */
+
+ /* Open the file as regular reader */
+ /* keep the file open */
+ if((fid = H5Fopen(FILENAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ /* Set up as VFD SWMR writer + page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 10, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Open the same file again as VFD SMWR writer */
+ /* Should fail: 1st open--regular reader, 2nd open--VFD SWMR writer */
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(FILENAME, H5F_ACC_RDWR, fapl1);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR;
+
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Open the same file again as regular reader */
+ /* Should succeed: 1st open--regular reader, 2nd open--regular reader */
+ if((fid2 = H5Fopen(FILENAME, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Open the same file again as regular writer */
+ /* Should fail: 1st open--regular reader, 2nd open--regular writer */
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(FILENAME, H5F_ACC_RDWR, H5P_DEFAULT);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR;
+
+ /* Close the 1st open file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free buffers */
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl1);
+ H5Pclose(fapl2);
+ H5Pclose(fcpl);
+ H5Fclose(fid);
+ H5Fclose(fid2);
+ } H5E_END_TRY;
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+ return 1;
+} /* test_same_file_opens() */
+
+#ifndef _arraycount
+#define _arraycount(_a) (sizeof(_a)/sizeof(_a[0]))
+#endif
+
+static unsigned
+test_shadow_index_lookup(void)
+{
+ unsigned nerrors = 0;
+ H5FD_vfd_swmr_idx_entry_t *idx;
+ uint32_t size[] = {0, 1, 2, 3, 4, 0};
+ char vector[8];
+ unsigned seed = 1;
+ unsigned i, j, failj = UINT_MAX;
+ bool have_failj = false;
+ unsigned long tmpl;
+ char *ostate;
+ const char *seedvar = "H5_SHADOW_INDEX_SEED";
+ const char *failvar = "H5_SHADOW_INDEX_FAIL";
+
+ TESTING("Shadow-index lookups");
+
+ /* get seed from environment or else from time(3) */
+ switch (fetch_env_ulong(seedvar, UINT_MAX, &tmpl)) {
+ case -1:
+ nerrors = 1;
+ goto out;
+ case 0:
+ seed = (unsigned int)time(NULL);
+ break;
+ default:
+ seed = (unsigned int)tmpl;
+ break;
+ }
+
+ /* get forced-fail index from environment */
+ switch (fetch_env_ulong(failvar, UINT_MAX, &tmpl)) {
+ case -1:
+ nerrors = 1;
+ goto out;
+ case 0:
+ break;
+ default:
+ failj = (unsigned int)tmpl;
+ have_failj = true;
+ break;
+ }
+
+ ostate = initstate(seed, vector, _arraycount(vector));
+
+ size[5] = (uint32_t)(1024 + random() % (16 * 1024 * 1024 - 1024));
+
+ for (i = 0; i < _arraycount(size); i++) {
+ uint32_t cursize = size[i];
+ const uint64_t modulus = UINT64_MAX / MAX(1, cursize);
+ uint64_t pageno;
+
+ assert(modulus > 1); // so that modulus - 1 > 0, below
+
+ idx = (cursize == 0) ? NULL : calloc(cursize, sizeof(*idx));
+ if (idx == NULL && cursize != 0) {
+ fprintf(stderr, "couldn't allocate %" PRIu32 " indices\n",
+ cursize);
+ exit(EXIT_FAILURE);
+ }
+ for (pageno = (uint64_t)random() % modulus, j = 0;
+ j < cursize;
+ j++, pageno += 1 + (uint64_t)random() % (modulus - 1)) {
+ idx[j].hdf5_page_offset = pageno;
+ }
+ for (j = 0; j < cursize; j++) {
+ H5FD_vfd_swmr_idx_entry_t *found;
+
+ found = vfd_swmr_pageno_to_mdf_idx_entry(idx, cursize,
+ idx[j].hdf5_page_offset, false);
+ if ((have_failj && failj == j) || found != &idx[j])
+ break;
+ }
+ if (j < cursize) {
+ printf("\nshadow-index entry %d lookup, pageno %" PRIu64
+ ", index size %" PRIu32 ", seed %u", j,
+ idx[j].hdf5_page_offset, cursize, seed);
+ nerrors++;
+ }
+ if (idx != NULL)
+ free(idx);
+ }
+ (void)setstate(ostate);
+out:
+ if (nerrors == 0)
+ PASSED();
+ else
+ printf(" FAILED\n");
+ return nerrors;
+}
+
+/*-------------------------------------------------------------------------
+ * Function: test_enable_disable_eot()
+ *
+ * Purpose: Verify the public routines:
+ * H5Fvfd_swmr_enable_end_of_tick()
+ * H5Fvfd_swmr_disable_end_of_tick()
+ * enables/disables EOT for the specified file
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ * Programmer: Vailin Choi; June 2020
+ *
+ *-------------------------------------------------------------------------
+ */
+static unsigned
+test_enable_disable_eot(void)
+{
+ hid_t fid = H5I_INVALID_HID; /* File ID */
+ hid_t fid1 = H5I_INVALID_HID; /* File ID */
+ hid_t fid2 = H5I_INVALID_HID; /* File ID */
+ hid_t fid3 = H5I_INVALID_HID; /* File ID */
+ hid_t fcpl = H5I_INVALID_HID; /* File creation property list ID */
+ hid_t fapl1 = H5I_INVALID_HID; /* File access property list ID */
+ hid_t fapl2 = H5I_INVALID_HID; /* File access property list ID */
+ hid_t fapl3 = H5I_INVALID_HID; /* File access property list ID */
+ H5F_t *f1, *f2, *f3; /* File pointer */
+ H5F_vfd_swmr_config_t *config1 = NULL; /* Configuration for VFD SWMR */
+ H5F_vfd_swmr_config_t *config2 = NULL; /* Configuration for VFD SWMR */
+ H5F_vfd_swmr_config_t *config3 = NULL; /* Configuration for VFD SWMR */
+ eot_queue_entry_t *curr; /* Pointer to an entry on the EOT queue */
+ unsigned count = 0; /* Counter */
+ herr_t ret; /* Return value */
+
+ TESTING("H5Fvfd_swmr_enable/disable_end_of_tick()");
+
+ /* Allocate memory for the configuration structure */
+ if((config1 = HDmalloc(sizeof(*config1))) == NULL)
+ FAIL_STACK_ERROR;
+ if((config2 = HDmalloc(sizeof(*config2))) == NULL)
+ FAIL_STACK_ERROR;
+ if((config3 = HDmalloc(sizeof(*config3))) == NULL)
+ FAIL_STACK_ERROR;
+
+ /* Configured first file as VFD SWMR writer + page buffering */
+ fapl1 = init_vfd_swmr_config_fapl(config1, 4, 6, TRUE, 2, MD_FILENAME, 4096);
+ if(fapl1 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Configured second file as VFD SWMR writer + page buffering */
+ fapl2 = init_vfd_swmr_config_fapl(config2, 4, 6, TRUE, 2, MD_FILENAME2, 4096);
+ if(fapl2 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Configured third file as VFD SWMR writer + page buffering */
+ fapl3 = init_vfd_swmr_config_fapl(config3, 4, 6, TRUE, 2, MD_FILENAME3, 4096);
+ if(fapl3 == H5I_INVALID_HID)
+ FAIL_STACK_ERROR;
+
+ /* Create a copy of the file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set file space strategy */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create a file without VFD SWMR */
+ if((fid = H5Fcreate(FNAME, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ TEST_ERROR;
+
+ /* Should fail to disable the file because VFD SWMR is not configured */
+ H5E_BEGIN_TRY {
+ ret = H5Fvfd_swmr_disable_end_of_tick(fid);
+ } H5E_END_TRY;
+ if(ret >=0)
+ TEST_ERROR
+
+ if(H5Fclose(fid) < 0)
+ TEST_ERROR
+
+ /* Create file 1 with VFD SWMR writer */
+ if((fid1 = H5Fcreate(FILENAME, H5F_ACC_TRUNC, fcpl, fapl1)) < 0)
+ TEST_ERROR;
+
+ /* Create file 2 with VFD SWMR writer */
+ if((fid2 = H5Fcreate(FILENAME2, H5F_ACC_TRUNC, fcpl, fapl2)) < 0)
+ TEST_ERROR;
+
+ /* Create file 3 with VFD SWMR writer */
+ if((fid3 = H5Fcreate(FILENAME3, H5F_ACC_TRUNC, fcpl, fapl3)) < 0)
+ TEST_ERROR;
+
+ /* Should have 3 files on the EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link)
+ count++;
+ if(count != 3)
+ TEST_ERROR;
+
+ /* Disable EOT for file 1 */
+ if(H5Fvfd_swmr_disable_end_of_tick(fid1) < 0)
+ TEST_ERROR
+
+ /* Disable file 1 again should fail because the file has just been disabled */
+ H5E_BEGIN_TRY {
+ ret = H5Fvfd_swmr_disable_end_of_tick(fid1);
+ } H5E_END_TRY;
+ if(ret >=0)
+ TEST_ERROR
+
+ /* Should have 2 files on the EOT queue */
+ count = 0;
+ TAILQ_FOREACH(curr, &eot_queue_g, link)
+ count++;
+ if(count != 2)
+ TEST_ERROR
+
+ /* Get a pointer to the internal file object */
+ if(NULL == (f1 = H5VL_object(fid1)))
+ FAIL_STACK_ERROR
+
+ /* Should not find file 1 on the EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if(curr->vfd_swmr_file == f1)
+ break;
+ }
+ if(curr != NULL && curr->vfd_swmr_file == f1)
+ TEST_ERROR
+
+ /* Enable EOT for file 2 should fail because the file has not been disabled */
+ H5E_BEGIN_TRY {
+ ret = H5Fvfd_swmr_enable_end_of_tick(fid2);
+ } H5E_END_TRY;
+ if(ret >=0)
+ TEST_ERROR
+
+ /* Get a pointer to the internal file object */
+ if(NULL == (f2 = H5VL_object(fid2)))
+ FAIL_STACK_ERROR
+
+ /* File 2 should be on the EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if(curr->vfd_swmr_file == f2)
+ break;
+ }
+ if(curr == NULL || curr->vfd_swmr_file != f2)
+ TEST_ERROR
+
+ /* Close file 3 */
+ if(H5Fclose(fid3) < 0)
+ TEST_ERROR
+
+ /* Open file 3 again without VFD SWMR writer */
+ if((fid3 = H5Fopen(FILENAME3, H5F_ACC_RDWR, H5P_DEFAULT)) < 0)
+ TEST_ERROR
+
+ /* Get a pointer to the internal file object for file 3 */
+ if(NULL == (f3 = H5VL_object(fid3)))
+ FAIL_STACK_ERROR
+
+ /* File 3 should not exist on the EOT queue */
+ TAILQ_FOREACH(curr, &eot_queue_g, link) {
+ if(curr->vfd_swmr_file == f3)
+ break;
+ }
+ if(curr != NULL && curr->vfd_swmr_file == f3)
+ TEST_ERROR
+
+ /* Should have 2 files on the EOT queue */
+ count = 0;
+ TAILQ_FOREACH(curr, &eot_queue_g, link)
+ count++;
+ if(count != 1)
+ TEST_ERROR;
+
+ /* Should fail to enable file 3 */
+ H5E_BEGIN_TRY {
+ ret = H5Fvfd_swmr_enable_end_of_tick(fid3);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Should fail to disable file 3 */
+ H5E_BEGIN_TRY {
+ ret = H5Fvfd_swmr_disable_end_of_tick(fid3);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid3) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl3) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Free buffers */
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+ if(config3)
+ HDfree(config3);
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl1);
+ H5Pclose(fapl2);
+ H5Pclose(fapl3);
+ H5Pclose(fcpl);
+ H5Fclose(fid);
+ H5Fclose(fid1);
+ H5Fclose(fid2);
+ H5Fclose(fid3);
+ } H5E_END_TRY;
+ if(config1)
+ HDfree(config1);
+ if(config2)
+ HDfree(config2);
+ if(config3)
+ HDfree(config3);
+
+ return 1;
+} /* test_enable_disable_eot() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: main()
+ *
+ * Purpose: Main function for VFD SWMR tests.
+ *
+ * Return: 0 if test is sucessful
+ * 1 if test fails
+ *
+ *-------------------------------------------------------------------------
+ */
+int
+main(void)
+{
+ hid_t fapl = -1; /* File access property list for */
+ /* data files */
+ unsigned nerrors = 0; /* Cumulative error count */
+ char *lock_env_var = NULL; /* File locking env var pointer */
+ const char *env_h5_drvr = NULL; /* File Driver value from environment */
+ hbool_t use_file_locking; /* Read from env var */
+
+ /* Check the environment variable that determines if we care
+ * about file locking. File locking should be used unless explicitly
+ * disabled.
+ */
+ lock_env_var = HDgetenv("HDF5_USE_FILE_LOCKING");
+ if(lock_env_var && !HDstrcmp(lock_env_var, "FALSE"))
+ use_file_locking = FALSE;
+ else
+ use_file_locking = TRUE;
+
+ /* Get the VFD to use */
+ env_h5_drvr = HDgetenv("HDF5_DRIVER");
+ if(env_h5_drvr == NULL)
+ env_h5_drvr = "nomatch";
+
+ /* Temporary skip testing with multi/split drivers:
+ * Page buffering depends on paged aggregation which is
+ * currently disabled for multi/split drivers.
+ */
+ if((0 == HDstrcmp(env_h5_drvr, "multi")) ||
+ (0 == HDstrcmp(env_h5_drvr, "split"))) {
+
+ SKIPPED()
+ HDputs("Skip VFD SWMR test because paged aggregation is disabled for multi/split drivers");
+ HDexit(EXIT_SUCCESS);
+ } /* end if */
+
+ /* Set up */
+ h5_reset();
+
+ if((fapl = h5_fileaccess()) < 0) {
+ nerrors++;
+ PUTS_ERROR("Can't get VFD-dependent fapl")
+ } /* end if */
+
+ nerrors += test_fapl();
+
+ if(use_file_locking) {
+ nerrors += test_shadow_index_lookup();
+
+ nerrors += test_file_fapl();
+ nerrors += test_writer_create_open_flush();
+ nerrors += test_writer_md();
+ nerrors += test_reader_md_concur();
+
+ nerrors += test_multiple_file_opens();
+ nerrors += test_multiple_file_opens_concur();
+ nerrors += test_same_file_opens();
+
+ nerrors += test_enable_disable_eot();
+ nerrors += test_disable_enable_eot_concur();
+
+ nerrors += test_file_end_tick();
+ nerrors += test_file_end_tick_concur();
+ }
+
+ if(nerrors)
+ goto error;
+
+ HDputs("All VFD SWMR tests passed.");
+
+ HDexit(EXIT_SUCCESS);
+
+error:
+ HDprintf("***** %d VFD SWMR TEST%s FAILED! *****\n",
+ nerrors, nerrors > 1 ? "S" : "");
+
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ } H5E_END_TRY;
+
+ HDexit(EXIT_FAILURE);
+}
diff --git a/test/vfd_swmr_addrem_writer.c b/test/vfd_swmr_addrem_writer.c
new file mode 100644
index 0000000..a8aefa9
--- /dev/null
+++ b/test/vfd_swmr_addrem_writer.c
@@ -0,0 +1,502 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: vfd_swmr_addrem_writer.c
+ * (copied and modified from swmr_addrem_writer.c)
+ *
+ * Purpose: Adds and removes data to a randomly selected subset of the
+ * datasets in the VFD SWMR test file.
+ *
+ * This program is intended to run concurrently with the
+ * vfd_swmr_reader program. It is also run AFTER a sequential
+ * (not concurrent!) invoking of vfd_swmr_writer so the writer
+ * can dump a bunch of data into the datasets. Otherwise,
+ * there wouldn't be much to shrink :)
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/***********/
+/* Headers */
+/***********/
+
+#include <err.h> /* errx(3) */
+#include <stdlib.h> /* EXIT_FAILURE */
+
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+/****************/
+/* Local Macros */
+/****************/
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static hid_t open_skeleton(const char *filename, unsigned verbose);
+static int addrem_records(hid_t fid, unsigned verbose, unsigned long nops,
+ unsigned long flush_count);
+static void usage(void);
+
+
+/*-------------------------------------------------------------------------
+ * Function: open_skeleton
+ *
+ * Purpose: Opens the SWMR HDF5 file and datasets.
+ *
+ * Parameters: const char *filename
+ * The filename of the SWMR HDF5 file to open
+ *
+ * unsigned verbose
+ * Whether or not to emit verbose console messages
+ *
+ * Return: Success: The file ID of the opened SWMR file
+ * The dataset IDs are stored in a global array
+ *
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static hid_t
+open_skeleton(const char *filename, unsigned verbose)
+{
+ hid_t dapl = H5I_INVALID_HID;
+ hid_t fid = -1; /* File ID for new HDF5 file */
+ hid_t fapl = -1; /* File access property list */
+ hid_t sid = -1; /* Dataspace ID */
+ hsize_t dim[2]; /* Dataspace dimension */
+ unsigned u, v; /* Local index variable */
+ H5F_vfd_swmr_config_t *config = NULL; /* Configuration for VFD SWMR */
+
+ HDassert(filename);
+
+ /* Create file access property list */
+ if((fapl = h5_fileaccess()) < 0)
+ goto error;
+
+ if ((dapl = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+ errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed", __func__, __LINE__);
+
+ if (H5Pset_chunk_cache(dapl, H5D_CHUNK_CACHE_NSLOTS_DEFAULT, 0,
+ H5D_CHUNK_CACHE_W0_DEFAULT) < 0)
+ errx(EXIT_FAILURE, "H5Pset_chunk_cache failed");
+
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ goto error;
+
+ /*
+ * Set up to open the file with VFD SWMR configured.
+ */
+
+ /* Enable page buffering */
+ if(H5Pset_page_buffer_size(fapl, 4096, 0, 0) < 0)
+ goto error;
+
+ /* Allocate memory for the configuration structure */
+ if((config = (H5F_vfd_swmr_config_t *)calloc(1, sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ goto error;
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = 4;
+ config->max_lag = 5;
+ config->writer = TRUE;
+ config->md_pages_reserved = 128;
+ HDstrcpy(config->md_file_path, "./my_md_file");
+
+ /* Enable VFD SWMR configuration */
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0)
+ goto error;
+
+ /* Open the file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ goto error;
+
+ /* Close file access property list */
+ if(H5Pclose(fapl) < 0)
+ goto error;
+
+ if(config)
+ HDfree(config);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Opening datasets\n");
+
+ /* Open the datasets */
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++) {
+ hid_t dsid;
+
+ if((dsid = H5Dopen2(fid, symbol_info[u][v].name, dapl)) < 0)
+ goto error;
+
+ symbol_info[u][v].dsid = dsid;
+
+ if((sid = H5Dget_space(symbol_info[u][v].dsid)) < 0)
+ goto error;
+ if(2 != H5Sget_simple_extent_ndims(sid))
+ goto error;
+ if(H5Sget_simple_extent_dims(sid, dim, NULL) < 0)
+ goto error;
+ symbol_info[u][v].nrecords = dim[1];
+
+ if(H5Sclose(sid) < 0)
+ goto error;
+ } /* end for */
+
+ return fid;
+
+error:
+ if(config)
+ HDfree(config);
+
+ H5E_BEGIN_TRY {
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ H5Dclose(symbol_info[u][v].dsid);
+ H5Sclose(sid);
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ H5Pclose(dapl);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* open_skeleton() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: addrem_records
+ *
+ * Purpose: Adds/removes a specified number of records to random datasets
+ * to the SWMR test file.
+ *
+ * Parameters: hid_t fid
+ * The file ID of the SWMR HDF5 file
+ *
+ * unsigned verbose
+ * Whether or not to emit verbose console messages
+ *
+ * unsigned long nops
+ * # of records to read/write in the datasets
+ *
+ * unsigned long flush_count
+ * # of records to write before flushing the file to disk
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+addrem_records(hid_t fid, unsigned verbose, unsigned long nops, unsigned long flush_count)
+{
+ hid_t tid = -1; /* Datatype ID for records */
+ hid_t mem_sid = -1; /* Memory dataspace ID */
+ hid_t file_sid = -1; /* Dataset's space ID */
+ hsize_t start[2] = {0, 0}, count[2] = {1, 1}; /* Hyperslab selection values */
+ hsize_t dim[2] = {1, 0}; /* Dataspace dimensions */
+ symbol_t buf[MAX_SIZE_CHANGE]; /* Write buffer */
+ unsigned long op_to_flush; /* # of operations before flush */
+ unsigned long u, v; /* Local index variables */
+
+ HDassert(fid > 0);
+
+ /* Reset the buffer */
+ HDmemset(&buf, 0, sizeof(buf));
+
+ /* Create a dataspace for the record to add */
+ if((mem_sid = H5Screate_simple(2, count, NULL)) < 0)
+ goto error;
+
+ /* Create datatype for appending records */
+ if((tid = create_symbol_datatype()) < 0)
+ goto error;
+
+ /* Add and remove records to random datasets, according to frequency
+ * distribution */
+ op_to_flush = flush_count;
+ for(u=0; u<nops; u++) {
+ symbol_info_t *symbol; /* Symbol to write record to */
+
+ /* Get a random dataset, according to the symbol distribution */
+ symbol = choose_dataset(NULL, NULL);
+
+ /* Decide whether to shrink or expand, and by how much */
+ count[1] = (hsize_t)HDrandom() % (MAX_SIZE_CHANGE * 2) + 1;
+
+ if(count[1] > MAX_SIZE_CHANGE) {
+ /* Add records */
+ count[1] -= MAX_SIZE_CHANGE;
+
+ /* Set the buffer's IDs (equal to its position) */
+ for(v=0; v<count[1]; v++)
+ buf[v].rec_id = (uint64_t)symbol->nrecords + (uint64_t)v;
+
+ /* Set the memory space to the correct size */
+ if(H5Sset_extent_simple(mem_sid, 2, count, NULL) < 0)
+ goto error;
+
+ /* Get the coordinates to write */
+ start[1] = symbol->nrecords;
+
+ /* Extend the dataset's dataspace to hold the new record */
+ symbol->nrecords+= count[1];
+ dim[1] = symbol->nrecords;
+ if(H5Dset_extent(symbol->dsid, dim) < 0)
+ goto error;
+
+ /* Get the dataset's dataspace */
+ if((file_sid = H5Dget_space(symbol->dsid)) < 0)
+ goto error;
+
+ /* Choose the last record in the dataset */
+ if(H5Sselect_hyperslab(file_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0)
+ goto error;
+
+ /* Write record to the dataset */
+ if(H5Dwrite(symbol->dsid, tid, mem_sid, file_sid, H5P_DEFAULT, &buf) < 0)
+ goto error;
+
+ if(H5Dflush(symbol->dsid) < 0)
+ goto error;
+
+ /* Close the dataset's dataspace */
+ if(H5Sclose(file_sid) < 0)
+ goto error;
+ } /* end if */
+ else {
+ /* Shrink the dataset's dataspace */
+ if(count[1] > symbol->nrecords)
+ symbol->nrecords = 0;
+ else
+ symbol->nrecords -= count[1];
+ dim[1] = symbol->nrecords;
+ if(H5Dset_extent(symbol->dsid, dim) < 0)
+ goto error;
+ } /* end else */
+
+ /* Check for flushing file */
+ if(flush_count > 0) {
+ /* Decrement count of records to write before flushing */
+ op_to_flush--;
+
+ /* Check for counter being reached */
+ if(0 == op_to_flush) {
+#ifdef TEMP_OUT
+ /* Flush contents of file */
+ if(H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0)
+ return -1;
+#endif /* TEMP_OUT */
+
+ /* Reset flush counter */
+ op_to_flush = flush_count;
+ } /* end if */
+ } /* end if */
+ } /* end for */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Closing datasets\n");
+
+ /* Close the datasets */
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ if(H5Dclose(symbol_info[u][v].dsid) < 0)
+ return -1;
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Sclose(mem_sid);
+ H5Sclose(file_sid);
+ H5Tclose(tid);
+
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ H5Dclose(symbol_info[u][v].dsid);
+
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* addrem_records() */
+
+static void
+usage(void)
+{
+ printf("\n");
+ printf("Usage error!\n");
+ printf("\n");
+ printf("Usage: vfd_swmr_addrem_writer [-q] [-f <# of operations between flushing\n");
+ printf(" file contents>] [-r <random seed>] <# of operations>\n");
+ printf("\n");
+ printf("<# of operations between flushing file contents> should be 0 (for\n");
+ printf("no flushing) or between 1 and (<# of operations> - 1).\n");
+ printf("\n");
+ printf("<# of operations> must be specified.\n");
+ printf("\n");
+ printf("Defaults to verbose (no '-q' given), flushing every 1000 operations\n");
+ printf("('-f 1000'), and will generate a random seed (no -r given).\n");
+ printf("\n");
+ HDexit(1);
+} /* usage() */
+
+int main(int argc, const char *argv[])
+{
+ sigset_t oldset;
+ hid_t fid; /* File ID for file opened */
+ long nops = 0; /* # of times to grow or shrink the dataset */
+ long flush_count = 1000; /* # of records to write between flushing file */
+ unsigned verbose = 1; /* Whether to emit some informational messages */
+ unsigned use_seed = 0; /* Set to 1 if a seed was set on the command line */
+ unsigned random_seed = 0; /* Random # seed */
+ unsigned u; /* Local index variable */
+ int temp;
+
+ block_signals(&oldset);
+
+ /* Parse command line options */
+ if(argc < 2)
+ usage();
+ if(argc > 1) {
+ u = 1;
+ while(u < (unsigned)argc) {
+ if(argv[u][0] == '-') {
+ switch(argv[u][1]) {
+ /* # of records to write between flushing file */
+ case 'f':
+ flush_count = HDatol(argv[u + 1]);
+ if(flush_count < 0)
+ usage();
+ u += 2;
+ break;
+
+ /* Be quiet */
+ case 'q':
+ verbose = 0;
+ u++;
+ break;
+
+ /* Random # seed */
+ case 'r':
+ use_seed = 1;
+ temp = HDatoi(argv[u + 1]);
+ if(temp < 0)
+ usage();
+ else
+ random_seed = (unsigned)temp;
+ u += 2;
+ break;
+
+ default:
+ usage();
+ break;
+ } /* end switch */
+ } /* end if */
+ else {
+ /* Get the number of records to append */
+ nops = HDatol(argv[u]);
+ if(nops <= 0)
+ usage();
+
+ u++;
+ } /* end else */
+ } /* end while */
+ } /* end if */
+ if(nops <= 0)
+ usage();
+ if(flush_count >= nops)
+ usage();
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "WRITER: Parameters:\n");
+ HDfprintf(stderr, "\t# of operations between flushes = %ld\n", flush_count);
+ HDfprintf(stderr, "\t# of operations = %ld\n", nops);
+ } /* end if */
+
+ /* Set the random seed */
+ if(0 == use_seed) {
+ struct timeval t;
+ HDgettimeofday(&t, NULL);
+ random_seed = (unsigned)(t.tv_usec);
+ } /* end if */
+ HDsrandom(random_seed);
+ /* ALWAYS emit the random seed for possible debugging */
+ HDfprintf(stderr, "WRITER: Using writer random seed: %u\n", random_seed);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Generating symbol names\n");
+
+ /* Generate dataset names */
+ if(generate_symbols() < 0)
+ return -1;
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "WRITER: Opening skeleton file: %s\n",
+ COMMON_FILENAME);
+ }
+
+ /* Open file skeleton */
+ if((fid = open_skeleton(COMMON_FILENAME, verbose)) < 0) {
+ HDfprintf(stderr, "WRITER: Error opening skeleton file!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Send a message to indicate "H5Fopen" is complete--releasing the file lock */
+ h5_send_message(WRITER_MESSAGE, NULL, NULL);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Adding and removing records\n");
+
+ /* Grow and shrink datasets */
+ if(addrem_records(fid, verbose, (unsigned long)nops, (unsigned long)flush_count) < 0) {
+ HDfprintf(stderr, "WRITER: Error adding and removing records from datasets!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Releasing symbols\n");
+
+ /* Clean up the symbols */
+ if(shutdown_symbols() < 0) {
+ HDfprintf(stderr, "WRITER: Error releasing symbols!\n");
+ HDexit(1);
+ } /* end if */
+
+ await_signal(fid);
+
+ restore_signals(&oldset);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Closing objects\n");
+
+ /* Close objects opened */
+ if(H5Fclose(fid) < 0) {
+ HDfprintf(stderr, "WRITER: Error closing file!\n");
+ HDexit(1);
+ } /* end if */
+
+ return 0;
+} /* main() */
diff --git a/test/vfd_swmr_bigset_writer.c b/test/vfd_swmr_bigset_writer.c
new file mode 100644
index 0000000..6d65c8b
--- /dev/null
+++ b/test/vfd_swmr_bigset_writer.c
@@ -0,0 +1,1104 @@
+/*
+ * 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 <err.h>
+#include <libgen.h>
+#include <time.h> /* nanosleep(2) */
+#include <unistd.h> /* getopt(3) */
+
+#define H5C_FRIEND /*suppress error about including H5Cpkg */
+#define H5F_FRIEND /*suppress error about including H5Fpkg */
+
+#include "hdf5.h"
+
+#include "H5Cpkg.h"
+#include "H5Fpkg.h"
+// #include "H5Iprivate.h"
+#include "H5HGprivate.h"
+#include "H5VLprivate.h"
+
+#include "testhdf5.h"
+#include "vfd_swmr_common.h"
+
+#define ROWS 256
+#define COLS 512
+#define RANK 2
+
+static const unsigned int hang_back = 3;
+
+typedef struct _base {
+ hsize_t row, col;
+} base_t;
+
+typedef struct _mat {
+ unsigned rows, cols;
+ uint32_t elt[1];
+} mat_t;
+
+typedef struct _quadrant {
+ hsize_t start[RANK];
+ hsize_t stride[RANK];
+ hsize_t block[RANK];
+ hsize_t count[RANK];
+ hid_t space, src_space;
+} quadrant_t;
+
+typedef struct _sources {
+ hid_t ul, ur, bl, br;
+} sources_t;
+
+#define MANY_FILES 4
+
+typedef struct {
+ hid_t *dataset;
+ sources_t *sources;
+ hid_t file[MANY_FILES];
+ hid_t dapl, filetype, memspace, one_by_one_sid, quadrant_dcpl;
+ unsigned ndatasets;
+ const char *filename[MANY_FILES];
+ char progname[PATH_MAX];
+ struct timespec update_interval;
+ struct {
+ quadrant_t ul, ur, bl, br, src;
+ } quadrants;
+ unsigned int cols, rows;
+ unsigned int asteps;
+ unsigned int nsteps;
+ bool two_dee;
+ bool wait_for_signal;
+ enum {vds_off, vds_single, vds_multi} vds;
+ bool use_vfd_swmr;
+ bool writer;
+ hsize_t chunk_dims[RANK];
+ hsize_t one_dee_max_dims[RANK];
+} state_t;
+
+static inline state_t
+state_initializer(void)
+{
+ return (state_t){
+ .memspace = H5I_INVALID_HID
+ , .dapl = H5I_INVALID_HID
+ , .file = {H5I_INVALID_HID, H5I_INVALID_HID,
+ H5I_INVALID_HID, H5I_INVALID_HID}
+ , .filetype = H5T_NATIVE_UINT32
+ , .one_by_one_sid = H5I_INVALID_HID
+ , .quadrant_dcpl = H5I_INVALID_HID
+ , .rows = ROWS
+ , .cols = COLS
+ , .ndatasets = 5
+ , .asteps = 10
+ , .nsteps = 100
+ , .filename = {"", "", "", ""}
+ , .two_dee = false
+ , .wait_for_signal = true
+ , .vds = vds_off
+ , .use_vfd_swmr = true
+ , .writer = true
+ , .one_dee_max_dims = {ROWS, H5S_UNLIMITED}
+ , .chunk_dims = {ROWS, COLS}
+ , .update_interval = (struct timespec){
+ .tv_sec = 0
+ , .tv_nsec = 1000000000UL / 30 /* 1/30 second */}};
+}
+
+static void state_init(state_t *, int, char **);
+
+static const hid_t badhid = H5I_INVALID_HID;
+
+static const hsize_t two_dee_max_dims[RANK] = {H5S_UNLIMITED, H5S_UNLIMITED};
+
+static uint32_t
+matget(const mat_t *mat, unsigned i, unsigned j)
+{
+ assert(i < mat->rows && j < mat->cols);
+
+ return mat->elt[i * mat->cols + j];
+}
+
+static void
+matset(mat_t *mat, unsigned i, unsigned j, uint32_t v)
+{
+ assert(i < mat->rows && j < mat->cols);
+
+ mat->elt[i * mat->cols + j] = v;
+}
+
+static mat_t *
+newmat(unsigned rows, unsigned cols)
+{
+ mat_t *mat;
+
+ mat = malloc(sizeof(*mat) + (rows * cols - 1) * sizeof(mat->elt[0]));
+
+ if (mat == NULL)
+ err(EXIT_FAILURE, "%s: malloc", __func__);
+
+ mat->rows = rows;
+ mat->cols = cols;
+
+ return mat;
+}
+
+static void
+usage(const char *progname)
+{
+ fprintf(stderr, "usage: %s [-S] [-W] [-a steps] [-b] [-c cols]\n"
+ " [-d dims]\n"
+ " [-n iterations] [-r rows] [-s datasets]\n"
+ " [-u milliseconds]\n"
+ "\n"
+ "-M: use virtual datasets and many source\n"
+ " files\n"
+ "-S: do not use VFD SWMR\n"
+ "-V: use virtual datasets and a single\n"
+ " source file\n"
+ "-W: do not wait for a signal before\n"
+ " exiting\n"
+ "-a steps: `steps` between adding attributes\n"
+ "-b: write data in big-endian byte order\n"
+ "-c cols: `cols` columns per chunk\n"
+ "-d 1|one|2|two|both: select dataset expansion in one or\n"
+ " both dimensions\n"
+ "-n iterations: how many times to expand each dataset\n"
+ "-r rows: `rows` rows per chunk\n"
+ "-s datasets: number of datasets to create\n"
+ "-u ms: milliseconds interval between updates\n"
+ " to %s.h5\n"
+ "\n",
+ progname, progname);
+ exit(EXIT_FAILURE);
+}
+
+static void
+make_quadrant_dataspace(state_t *s, quadrant_t *q)
+{
+ q->space = H5Screate_simple(NELMTS(s->chunk_dims), s->chunk_dims,
+ s->two_dee ? two_dee_max_dims : s->one_dee_max_dims);
+
+ if (q->space < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed",
+ __func__, __LINE__);
+ }
+
+ if (H5Sselect_hyperslab(q->space, H5S_SELECT_SET, q->start, q->stride,
+ q->count, q->block) < 0)
+ errx(EXIT_FAILURE, "%s: H5Sselect_hyperslab failed", __func__);
+}
+
+static void
+state_init(state_t *s, int argc, char **argv)
+{
+ unsigned long tmp;
+ int ch;
+ unsigned i;
+ const hsize_t dims = 1;
+ char tfile[PATH_MAX];
+ char *end;
+ unsigned long millis;
+ quadrant_t * const ul = &s->quadrants.ul,
+ * const ur = &s->quadrants.ur,
+ * const bl = &s->quadrants.bl,
+ * const br = &s->quadrants.br,
+ * const src = &s->quadrants.src;
+ const char *personality;
+
+ *s = state_initializer();
+ esnprintf(tfile, sizeof(tfile), "%s", argv[0]);
+ esnprintf(s->progname, sizeof(s->progname), "%s", basename(tfile));
+
+ while ((ch = getopt(argc, argv, "MSVWa:bc:d:n:qr:s:u:")) != -1) {
+ switch (ch) {
+ case 'M':
+ s->vds = vds_multi;
+ break;
+ case 'S':
+ s->use_vfd_swmr = false;
+ break;
+ case 'V':
+ s->vds = vds_single;
+ break;
+ case 'W':
+ s->wait_for_signal = false;
+ break;
+ case 'd':
+ if (strcmp(optarg, "1") == 0 ||
+ strcmp(optarg, "one") == 0)
+ s->two_dee = false;
+ else if (strcmp(optarg, "2") == 0 ||
+ strcmp(optarg, "two") == 0 ||
+ strcmp(optarg, "both") == 0)
+ s->two_dee = true;
+ else {
+ errx(EXIT_FAILURE,
+ "bad -d argument \"%s\"", optarg);
+ }
+ break;
+ case 'a':
+ case 'c':
+ case 'n':
+ case 'r':
+ case 's':
+ errno = 0;
+ tmp = strtoul(optarg, &end, 0);
+ if (end == optarg || *end != '\0') {
+ errx(EXIT_FAILURE, "couldn't parse `-%c` argument `%s`", ch,
+ optarg);
+ } else if (errno != 0) {
+ err(EXIT_FAILURE, "couldn't parse `-%c` argument `%s`", ch,
+ optarg);
+ } else if (tmp > UINT_MAX)
+ errx(EXIT_FAILURE, "`-%c` argument `%lu` too large", ch, tmp);
+
+ if ((ch == 'c' || ch == 'r') && tmp == 0) {
+ errx(EXIT_FAILURE, "`-%c` argument `%lu` must be >= 1", ch,
+ tmp);
+ }
+
+ if (ch == 'a')
+ s->asteps = (unsigned)tmp;
+ else if (ch == 'c')
+ s->cols = (unsigned)tmp;
+ else if (ch == 'n')
+ s->nsteps = (unsigned)tmp;
+ else if (ch == 'r')
+ s->rows = (unsigned)tmp;
+ else
+ s->ndatasets = (unsigned)tmp;
+ break;
+ case 'b':
+ s->filetype = H5T_STD_U32BE;
+ break;
+ case 'q':
+ verbosity = 0;
+ 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);
+ break;
+ case '?':
+ default:
+ usage(s->progname);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ errx(EXIT_FAILURE, "unexpected command-line arguments");
+
+ if (s->vds != vds_off && s->two_dee) {
+ errx(EXIT_FAILURE,
+ "virtual datasets and 2D datasets are mutually exclusive");
+ }
+
+ s->chunk_dims[0] = s->rows;
+ s->chunk_dims[1] = s->cols;
+ s->one_dee_max_dims[0] = s->rows;
+ s->one_dee_max_dims[1] = H5S_UNLIMITED;
+
+ if (s->vds != vds_off) {
+ const hsize_t half_chunk_dims[RANK] = {s->rows / 2, s->cols / 2};
+ const hsize_t half_max_dims[RANK] = {s->rows / 2, H5S_UNLIMITED};
+
+ if ((s->quadrant_dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed",
+ __func__, __LINE__);
+ }
+
+ if (H5Pset_chunk(s->quadrant_dcpl, RANK, half_chunk_dims) < 0)
+ errx(EXIT_FAILURE, "H5Pset_chunk failed");
+
+ *ul = (quadrant_t){
+ .start = {0, 0}
+ , .stride = {s->rows, s->cols}
+ , .block = {s->rows / 2, s->cols / 2}
+ , .count = {1, H5S_UNLIMITED}};
+
+ *ur = (quadrant_t){
+ .start = {s->rows / 2, 0}
+ , .stride = {s->rows, s->cols}
+ , .block = {s->rows / 2, s->cols / 2}
+ , .count = {1, H5S_UNLIMITED}};
+
+ *bl = (quadrant_t){
+ .start = {0, s->cols / 2}
+ , .stride = {s->rows, s->cols}
+ , .block = {s->rows / 2, s->cols / 2}
+ , .count = {1, H5S_UNLIMITED}};
+
+ *br = (quadrant_t){
+ .start = {s->rows / 2, s->cols / 2}
+ , .stride = {s->rows, s->cols}
+ , .block = {s->rows / 2, s->cols / 2}
+ , .count = {1, H5S_UNLIMITED}};
+
+ make_quadrant_dataspace(s, ul);
+ make_quadrant_dataspace(s, ur);
+ make_quadrant_dataspace(s, bl);
+ make_quadrant_dataspace(s, br);
+
+ *src = (quadrant_t){
+ .start = {0, 0}
+ , .stride = {s->rows / 2, s->cols / 2}
+ , .block = {s->rows / 2, s->cols / 2}
+ , .count = {1, H5S_UNLIMITED}};
+
+ src->space = H5Screate_simple(RANK, half_chunk_dims,
+ half_max_dims);
+
+ if (src->space < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed",
+ __func__, __LINE__);
+ }
+
+ if (H5Sselect_hyperslab(src->space, H5S_SELECT_SET, src->start,
+ src->stride, src->count, src->block) < 0)
+ errx(EXIT_FAILURE, "%s: H5Sselect_hyperslab failed", __func__);
+
+ ul->src_space = H5Screate_simple(RANK, half_chunk_dims, half_max_dims);
+
+ if (ul->src_space < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed",
+ __func__, __LINE__);
+ }
+
+ ur->src_space = H5Screate_simple(RANK, half_chunk_dims, half_max_dims);
+
+ if (ur->src_space < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed",
+ __func__, __LINE__);
+ }
+
+ bl->src_space = H5Screate_simple(RANK, half_chunk_dims, half_max_dims);
+
+ if (bl->src_space < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed",
+ __func__, __LINE__);
+ }
+
+ br->src_space = H5Screate_simple(RANK, half_chunk_dims, half_max_dims);
+
+ if (br->src_space < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed",
+ __func__, __LINE__);
+ }
+ }
+
+ /* space for attributes */
+ if ((s->one_by_one_sid = H5Screate_simple(1, &dims, &dims)) < 0)
+ errx(EXIT_FAILURE, "H5Screate_simple failed");
+
+ s->dataset = malloc(sizeof(*s->dataset) * s->ndatasets);
+ if (s->dataset == NULL)
+ err(EXIT_FAILURE, "could not allocate dataset handles");
+
+ s->sources = malloc(sizeof(*s->sources) * s->ndatasets);
+ if (s->sources == NULL)
+ err(EXIT_FAILURE, "could not allocate quadrant dataset handles");
+
+ for (i = 0; i < s->ndatasets; i++) {
+ s->dataset[i] = badhid;
+ s->sources[i].ul = s->sources[i].ur = s->sources[i].bl =
+ s->sources[i].br = badhid;
+ }
+
+ s->memspace = H5Screate_simple(RANK, s->chunk_dims, NULL);
+
+ if (s->memspace < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed",
+ __func__, __LINE__);
+ }
+
+ s->filename[0] = "vfd_swmr_bigset.h5";
+ if (s->vds == vds_multi) {
+ s->filename[1] = "vfd_swmr_bigset-ur.h5";
+ s->filename[2] = "vfd_swmr_bigset-bl.h5";
+ s->filename[3] = "vfd_swmr_bigset-br.h5";
+ } else {
+ s->filename[1] = s->filename[0];
+ s->filename[2] = s->filename[0];
+ s->filename[3] = s->filename[0];
+ }
+
+ personality = strstr(s->progname, "vfd_swmr_bigset_");
+
+ if (personality != NULL &&
+ strcmp(personality, "vfd_swmr_bigset_writer") == 0)
+ s->writer = true;
+ else if (personality != NULL &&
+ strcmp(personality, "vfd_swmr_bigset_reader") == 0)
+ s->writer = false;
+ else {
+ errx(EXIT_FAILURE,
+ "unknown personality, expected vfd_swmr_bigset_{reader,writer}");
+ }
+
+ if ((s->dapl = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+ errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed", __func__, __LINE__);
+
+ if (H5Pset_chunk_cache(s->dapl, 0, 0,
+ H5D_CHUNK_CACHE_W0_DEFAULT) < 0)
+ errx(EXIT_FAILURE, "H5Pset_chunk_cache failed");
+
+ if (s->vds != vds_off &&
+ H5Pset_virtual_view(s->dapl, H5D_VDS_FIRST_MISSING) < 0)
+ errx(EXIT_FAILURE, "H5Pset_virtual_view failed");
+}
+
+static void
+state_destroy(state_t *s)
+{
+ size_t i;
+
+ if (H5Pclose(s->dapl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fapl)");
+
+ s->dapl = badhid;
+
+ if (s->vds != vds_off) {
+ quadrant_t * const ul = &s->quadrants.ul,
+ * const ur = &s->quadrants.ur,
+ * const bl = &s->quadrants.bl,
+ * const br = &s->quadrants.br;
+
+ if (H5Sclose(ul->src_space) < 0 ||
+ H5Sclose(ur->src_space) < 0 ||
+ H5Sclose(bl->src_space) < 0 ||
+ H5Sclose(br->src_space) < 0)
+ errx(EXIT_FAILURE, "H5Sclose failed");
+
+ ul->src_space = ur->src_space = bl->src_space = br->src_space = badhid;
+
+ if (H5Pclose(s->quadrant_dcpl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(dcpl)");
+
+ s->quadrant_dcpl = badhid;
+
+ /* TBD destroy spaces belonging to quadrants */
+ }
+
+ for (i = 0; i < NELMTS(s->file); i++) {
+ hid_t fid = s->file[i];
+
+ s->file[i] = badhid;
+
+ if (s->vds != vds_multi && i > 0)
+ continue;
+
+ if (H5Fclose(fid) < 0)
+ errx(EXIT_FAILURE, "H5Fclose");
+ }
+}
+
+static void
+create_extensible_dset(state_t *s, unsigned int which)
+{
+ quadrant_t * const ul = &s->quadrants.ul,
+ * const ur = &s->quadrants.ur,
+ * const bl = &s->quadrants.bl,
+ * const br = &s->quadrants.br,
+ * const src = &s->quadrants.src;
+ char dname[sizeof("/dataset-9999999999")];
+ char ul_dname[sizeof("/ul-dataset-9999999999")],
+ ur_dname[sizeof("/ur-dataset-9999999999")],
+ bl_dname[sizeof("/bl-dataset-9999999999")],
+ br_dname[sizeof("/br-dataset-9999999999")];
+ hid_t dcpl, ds, filespace;
+
+ assert(which < s->ndatasets);
+ assert(s->dataset[which] == badhid);
+
+ esnprintf(dname, sizeof(dname), "/dataset-%d", which);
+
+ if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed",
+ __func__, __LINE__);
+ }
+
+ if (H5Pset_chunk(dcpl, RANK, s->chunk_dims) < 0)
+ errx(EXIT_FAILURE, "H5Pset_chunk failed");
+
+ if (s->vds != vds_off) {
+ sources_t * const srcs = &s->sources[which];
+
+ esnprintf(ul_dname, sizeof(ul_dname), "/ul-dataset-%d", which);
+ esnprintf(ur_dname, sizeof(ur_dname), "/ur-dataset-%d", which);
+ esnprintf(bl_dname, sizeof(bl_dname), "/bl-dataset-%d", which);
+ esnprintf(br_dname, sizeof(br_dname), "/br-dataset-%d", which);
+
+ srcs->ul = H5Dcreate2(s->file[0], ul_dname, s->filetype, ul->src_space,
+ H5P_DEFAULT, s->quadrant_dcpl, s->dapl);
+
+ if (srcs->ul < 0)
+ errx(EXIT_FAILURE, "H5Dcreate(, \"%s\", ) failed", ul_dname);
+
+ srcs->ur = H5Dcreate2(s->file[1], ur_dname, s->filetype, ur->src_space,
+ H5P_DEFAULT, s->quadrant_dcpl, s->dapl);
+
+ if (srcs->ur < 0)
+ errx(EXIT_FAILURE, "H5Dcreate(, \"%s\", ) failed", ur_dname);
+
+ srcs->bl = H5Dcreate2(s->file[2], bl_dname, s->filetype, bl->src_space,
+ H5P_DEFAULT, s->quadrant_dcpl, s->dapl);
+
+ if (srcs->bl < 0)
+ errx(EXIT_FAILURE, "H5Dcreate(, \"%s\", ) failed", bl_dname);
+
+ srcs->br = H5Dcreate2(s->file[3], br_dname, s->filetype, br->src_space,
+ H5P_DEFAULT, s->quadrant_dcpl, s->dapl);
+
+ if (srcs->br < 0)
+ errx(EXIT_FAILURE, "H5Dcreate(, \"%s\", ) failed", br_dname);
+
+ if (H5Pset_virtual(dcpl, ul->space, s->filename[0], ul_dname,
+ src->space) < 0)
+ errx(EXIT_FAILURE, "%s: H5Pset_virtual failed", __func__);
+
+ if (H5Pset_virtual(dcpl, ur->space, s->filename[1], ur_dname,
+ src->space) < 0)
+ errx(EXIT_FAILURE, "%s: H5Pset_virtual failed", __func__);
+
+ if (H5Pset_virtual(dcpl, bl->space, s->filename[2], bl_dname,
+ src->space) < 0)
+ errx(EXIT_FAILURE, "%s: H5Pset_virtual failed", __func__);
+
+ if (H5Pset_virtual(dcpl, br->space, s->filename[3], br_dname,
+ src->space) < 0)
+ errx(EXIT_FAILURE, "%s: H5Pset_virtual failed", __func__);
+ }
+
+ filespace = H5Screate_simple(NELMTS(s->chunk_dims), s->chunk_dims,
+ s->two_dee ? two_dee_max_dims : s->one_dee_max_dims);
+
+ if (filespace < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Screate_simple failed",
+ __func__, __LINE__);
+ }
+
+ ds = H5Dcreate2(s->file[0], dname, s->filetype, filespace,
+ H5P_DEFAULT, dcpl, s->dapl);
+
+ if (ds < 0)
+ errx(EXIT_FAILURE, "H5Dcreate(, \"%s\", ) failed", dname);
+
+ if (H5Sclose(filespace) < 0)
+ errx(EXIT_FAILURE, "%s: H5Sclose failed", __func__);
+
+ if (H5Pclose(dcpl) < 0)
+ errx(EXIT_FAILURE, "%s: H5Pclose failed", __func__);
+
+ s->dataset[which] = ds;
+}
+
+static void
+close_extensible_dset(state_t *s, unsigned int which)
+{
+ char dname[sizeof("/dataset-9999999999")];
+ hid_t ds;
+
+ assert(which < s->ndatasets);
+
+ esnprintf(dname, sizeof(dname), "/dataset-%d", which);
+
+ ds = s->dataset[which];
+
+ if (H5Dclose(ds) < 0)
+ errx(EXIT_FAILURE, "H5Dclose failed for \"%s\"", dname);
+
+ s->dataset[which] = badhid;
+
+ if (s->vds != vds_off && s->writer) {
+ sources_t * const srcs = &s->sources[which];
+
+ if (H5Dclose(srcs->ul) < 0 || H5Dclose(srcs->ur) < 0 ||
+ H5Dclose(srcs->bl) < 0 || H5Dclose(srcs->br) < 0)
+ errx(EXIT_FAILURE, "H5Dclose failed");
+
+ srcs->ul = srcs->ur = srcs->bl = srcs->br = badhid;
+ }
+}
+
+static void
+open_extensible_dset(state_t *s, unsigned int which)
+{
+ hsize_t dims[RANK], maxdims[RANK];
+ char dname[sizeof("/dataset-9999999999")];
+ hid_t ds, filespace, ty;
+
+ assert(which < s->ndatasets);
+ assert(s->dataset[which] == badhid);
+
+ esnprintf(dname, sizeof(dname), "/dataset-%d", which);
+
+ ds = H5Dopen(s->file[0], dname, s->dapl);
+
+ 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, s->filetype) <= 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 (H5Sclose(filespace) < 0)
+ errx(EXIT_FAILURE, "H5Sclose failed");
+
+ filespace = badhid;
+
+ if (s->two_dee) {
+ if (maxdims[0] != two_dee_max_dims[0] ||
+ maxdims[1] != two_dee_max_dims[1] ||
+ maxdims[0] != maxdims[1]) {
+ errx(EXIT_FAILURE, "Unexpected maximum dimensions %"
+ PRIuHSIZE " x %" PRIuHSIZE, maxdims[0], maxdims[1]);
+ }
+ } else if (maxdims[0] != s->one_dee_max_dims[0] ||
+ maxdims[1] != s->one_dee_max_dims[1] ||
+ dims[0] != s->chunk_dims[0]) {
+ errx(EXIT_FAILURE, "Unexpected maximum dimensions %"
+ PRIuHSIZE " x %" PRIuHSIZE " or columns %" PRIuHSIZE,
+ maxdims[0], maxdims[1], dims[1]);
+ }
+
+ s->dataset[which] = ds;
+}
+
+static void
+set_or_verify_matrix(mat_t *mat, unsigned int which, base_t base, bool do_set)
+{
+ unsigned row, col;
+
+ for (row = 0; row < mat->rows; row++) {
+ for (col = 0; col < mat->cols; col++) {
+ uint32_t v;
+ hsize_t i = base.row + row,
+ j = base.col + col,
+ u;
+
+ if (j <= i)
+ u = (i + 1) * (i + 1) - 1 - j;
+ else
+ u = j * j + i;
+
+ assert(UINT32_MAX - u >= which);
+ v = (uint32_t)(u + which);
+ if (do_set)
+ matset(mat, row, col, v);
+ else if (matget(mat, row, col) != v) {
+ errx(EXIT_FAILURE, "matrix mismatch "
+ "at %" PRIuHSIZE ", %" PRIuHSIZE " (%u, %u), "
+ "read %" PRIu32 " expecting %" PRIu32,
+ i, j, row, col, matget(mat, row, col), v);
+ }
+ }
+ }
+}
+
+static void
+init_matrix(mat_t *mat, unsigned int which, base_t base)
+{
+ set_or_verify_matrix(mat, which, base, true);
+}
+
+static void
+verify_matrix(mat_t *mat, unsigned int which, base_t base)
+{
+ set_or_verify_matrix(mat, which, base, false);
+}
+
+static void
+verify_chunk(state_t *s, hid_t filespace,
+ mat_t *mat, unsigned which, base_t base)
+{
+ hsize_t offset[RANK] = {base.row, base.col};
+ herr_t status;
+ hid_t ds;
+
+ assert(which < s->ndatasets);
+
+ dbgf(1, "verifying chunk %" PRIuHSIZE ", %" PRIuHSIZE "\n",
+ base.row, base.col);
+
+ ds = s->dataset[which];
+
+ status = H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset,
+ NULL, s->chunk_dims, NULL);
+
+ if (status < 0)
+ errx(EXIT_FAILURE, "H5Sselect_hyperslab failed");
+
+ status = H5Dread(ds, H5T_NATIVE_UINT32, s->memspace, filespace,
+ H5P_DEFAULT, mat->elt);
+
+ if (status < 0)
+ errx(EXIT_FAILURE, "H5Dread failed");
+
+ verify_matrix(mat, which, base);
+}
+
+static void
+init_and_write_chunk(state_t *s, hid_t filespace,
+ mat_t *mat, unsigned which, base_t base)
+{
+ hsize_t offset[RANK] = {base.row, base.col};
+ herr_t status;
+ hid_t ds;
+
+ assert(which < s->ndatasets);
+
+ ds = s->dataset[which];
+
+ init_matrix(mat, which, base);
+
+ status = H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset,
+ NULL, s->chunk_dims, NULL);
+
+ if (status < 0)
+ errx(EXIT_FAILURE, "H5Sselect_hyperslab failed");
+
+ status = H5Dwrite(ds, H5T_NATIVE_UINT32, s->memspace, filespace,
+ H5P_DEFAULT, mat->elt);
+
+ if (status < 0)
+ errx(EXIT_FAILURE, "H5Dwrite failed");
+}
+
+static void
+verify_dset_attribute(hid_t ds, unsigned int which, unsigned int step)
+{
+ unsigned int read_step;
+ hid_t aid;
+ char name[sizeof("attr-9999999999")];
+
+ esnprintf(name, sizeof(name), "attr-%u", step);
+
+ dbgf(1, "verifying attribute %s on dataset %u equals %u\n", name, which,
+ step);
+
+ if ((aid = H5Aopen(ds, name, H5P_DEFAULT)) < 0)
+ errx(EXIT_FAILURE, "H5Aopen failed");
+
+ if (H5Aread(aid, H5T_NATIVE_UINT, &read_step) < 0)
+ errx(EXIT_FAILURE, "H5Aread failed");
+
+ if (H5Aclose(aid) < 0)
+ errx(EXIT_FAILURE, "H5Aclose failed");
+
+ if (read_step != step)
+ errx(EXIT_FAILURE, "expected %u read %u", step, read_step);
+}
+
+static void
+verify_extensible_dset(state_t *s, unsigned int which, mat_t *mat,
+ unsigned *stepp)
+{
+ hid_t ds, filespace;
+ hsize_t size[RANK];
+ base_t base, last;
+ unsigned int ncols, last_step, step;
+
+ assert(which < s->ndatasets);
+
+ ds = s->dataset[which];
+
+ if (H5Drefresh(ds) < 0)
+ errx(EXIT_FAILURE, "H5Drefresh failed");
+
+ filespace = H5Dget_space(ds);
+
+ if (filespace == badhid)
+ errx(EXIT_FAILURE, "H5Dget_space failed");
+
+ if (H5Sget_simple_extent_dims(filespace, size, NULL) < 0)
+ errx(EXIT_FAILURE, "H5Sget_simple_extent_dims failed");
+
+ ncols = (unsigned)(size[1] / s->chunk_dims[1]);
+ if (ncols < hang_back)
+ goto out;
+
+ last_step = ncols - hang_back;
+
+ for (step = *stepp; step <= last_step; step++) {
+ const unsigned ofs = step % 2;
+
+ dbgf(1, "%s: which %u step %u\n", __func__, which, step);
+
+ if (s->two_dee) {
+ size[0] = s->chunk_dims[0] * (1 + step);
+ size[1] = s->chunk_dims[1] * (1 + step);
+ last.row = s->chunk_dims[0] * step + ofs;
+ last.col = s->chunk_dims[1] * step + ofs;
+ } else {
+ size[0] = s->chunk_dims[0];
+ size[1] = s->chunk_dims[1] * (1 + step);
+ last.row = 0;
+ last.col = s->chunk_dims[1] * step + ofs;
+ }
+
+ dbgf(1, "new size %" PRIuHSIZE ", %" PRIuHSIZE "\n", size[0], size[1]);
+ dbgf(1, "last row %" PRIuHSIZE " col %" PRIuHSIZE "\n", last.row,
+ last.col);
+
+ if (s->two_dee) {
+
+ /* Down the right side, intersecting the bottom row. */
+ base.col = last.col;
+ for (base.row = ofs; base.row <= last.row;
+ base.row += s->chunk_dims[0]) {
+ verify_chunk(s, filespace, mat, which, base);
+ }
+
+ /* Across the bottom, stopping before the last column to
+ * avoid re-writing the bottom-right chunk.
+ */
+ base.row = last.row;
+ for (base.col = ofs; base.col < last.col;
+ base.col += s->chunk_dims[1]) {
+ verify_chunk(s, filespace, mat, which, base);
+ }
+ } else {
+ verify_chunk(s, filespace, mat, which, last);
+ }
+ if (s->asteps != 0 && step % s->asteps == 0)
+ verify_dset_attribute(ds, which, step);
+ }
+
+ *stepp = last_step;
+
+out:
+ if (H5Sclose(filespace) < 0)
+ errx(EXIT_FAILURE, "H5Sclose failed");
+}
+
+static void
+add_dset_attribute(const state_t *s, hid_t ds, hid_t sid, unsigned int which,
+ unsigned int step)
+{
+ hid_t aid;
+ char name[sizeof("attr-9999999999")];
+
+ esnprintf(name, sizeof(name), "attr-%u", step);
+
+ dbgf(1, "setting attribute %s on dataset %u to %u\n", name, which, step);
+
+ if ((aid = H5Acreate2(ds, name, s->filetype, sid, H5P_DEFAULT,
+ H5P_DEFAULT)) < 0)
+ errx(EXIT_FAILURE, "H5Acreate2 failed");
+
+ if (H5Awrite(aid, H5T_NATIVE_UINT, &step) < 0)
+ errx(EXIT_FAILURE, "H5Awrite failed");
+ if (H5Aclose(aid) < 0)
+ errx(EXIT_FAILURE, "H5Aclose failed");
+}
+
+static void
+write_extensible_dset(state_t *s, unsigned int which, unsigned int step,
+ mat_t *mat)
+{
+ hid_t ds, filespace;
+ hsize_t size[RANK];
+ base_t base, last;
+
+ dbgf(1, "%s: which %u step %u\n", __func__, which, step);
+
+ assert(which < s->ndatasets);
+
+ ds = s->dataset[which];
+
+ if (s->asteps != 0 && step % s->asteps == 0)
+ add_dset_attribute(s, ds, s->one_by_one_sid, which, step);
+
+ if (s->two_dee) {
+ size[0] = s->chunk_dims[0] * (1 + step);
+ size[1] = s->chunk_dims[1] * (1 + step);
+ last.row = s->chunk_dims[0] * step;
+ last.col = s->chunk_dims[1] * step;
+ } else {
+ size[0] = s->chunk_dims[0];
+ size[1] = s->chunk_dims[1] * (1 + step);
+ last.row = 0;
+ last.col = s->chunk_dims[1] * step;
+ }
+
+ dbgf(1, "new size %" PRIuHSIZE ", %" PRIuHSIZE "\n", size[0], size[1]);
+
+ if (s->vds != vds_off) {
+ const hsize_t half_size[RANK] = {size[0] / 2, size[1] / 2};
+ sources_t * const srcs = &s->sources[which];
+
+ if (H5Dset_extent(srcs->ul, half_size) < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Dset_extent failed",
+ __func__, __LINE__);
+ }
+ if (H5Dset_extent(srcs->ur, half_size) < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Dset_extent failed",
+ __func__, __LINE__);
+ }
+ if (H5Dset_extent(srcs->bl, half_size) < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Dset_extent failed",
+ __func__, __LINE__);
+ }
+ if (H5Dset_extent(srcs->br, half_size) < 0) {
+ errx(EXIT_FAILURE, "%s.%d: H5Dset_extent failed",
+ __func__, __LINE__);
+ }
+
+ } else if (H5Dset_extent(ds, size) < 0)
+ errx(EXIT_FAILURE, "H5Dset_extent failed");
+
+ filespace = H5Dget_space(ds);
+
+ if (filespace == badhid)
+ errx(EXIT_FAILURE, "H5Dget_space failed");
+
+ if (s->two_dee) {
+ base.col = last.col;
+ for (base.row = 0; base.row <= last.row; base.row += s->chunk_dims[0]) {
+ dbgf(1, "writing chunk %" PRIuHSIZE ", %" PRIuHSIZE "\n",
+ base.row, base.col);
+ init_and_write_chunk(s, filespace, mat, which, base);
+ }
+
+ base.row = last.row;
+ for (base.col = 0; base.col < last.col; base.col += s->chunk_dims[1]) {
+ dbgf(1, "writing chunk %" PRIuHSIZE ", %" PRIuHSIZE "\n",
+ base.row, base.col);
+ init_and_write_chunk(s, filespace, mat, which, base);
+ }
+ } else {
+ init_and_write_chunk(s, filespace, mat, which, last);
+ }
+
+ if (H5Sclose(filespace) < 0)
+ errx(EXIT_FAILURE, "H5Sclose failed");
+}
+
+int
+main(int argc, char **argv)
+{
+ mat_t *mat;
+ hid_t fcpl;
+ sigset_t oldsigs;
+ herr_t ret;
+ unsigned step, which;
+ state_t s;
+ size_t i;
+
+ state_init(&s, argc, argv);
+
+ if ((mat = newmat(s.rows, s.cols)) == NULL)
+ err(EXIT_FAILURE, "%s: could not allocate matrix", __func__);
+
+ if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ errx(EXIT_FAILURE, "H5Pcreate");
+
+ ret = H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, false, 1);
+ if (ret < 0)
+ errx(EXIT_FAILURE, "H5Pset_file_space_strategy");
+
+ for (i = 0; i < NELMTS(s.file); i++) {
+ hid_t fapl;
+
+ if (s.vds != vds_multi && i > 0) {
+ s.file[i] = s.file[0];
+ continue;
+ }
+
+ fapl = vfd_swmr_create_fapl(s.writer, true, s.use_vfd_swmr,
+ "./metadata-%zu", i);
+
+ if (fapl < 0)
+ errx(EXIT_FAILURE, "vfd_swmr_create_fapl");
+
+ s.file[i] =
+ s.writer ? H5Fcreate(s.filename[i], H5F_ACC_TRUNC, fcpl, fapl)
+ : H5Fopen(s.filename[i], H5F_ACC_RDONLY, fapl);
+
+ if (s.file[i] == badhid)
+ errx(EXIT_FAILURE, s.writer ? "H5Fcreate" : "H5Fopen");
+
+ if (H5Pclose(fapl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fapl)");
+ }
+
+ block_signals(&oldsigs);
+
+ if (s.writer) {
+ for (which = 0; which < s.ndatasets; which++)
+ create_extensible_dset(&s, which);
+
+ for (step = 0; step < s.nsteps; step++) {
+ for (which = 0; which < s.ndatasets; which++) {
+ dbgf(2, "step %d which %d\n", step, which);
+ write_extensible_dset(&s, which, step, mat);
+ if (s.ndatasets <= s.nsteps)
+ nanosleep(&s.update_interval, NULL);
+ }
+ if (s.ndatasets > s.nsteps)
+ nanosleep(&s.update_interval, NULL);
+ }
+ } else {
+ for (which = 0; which < s.ndatasets; which++)
+ open_extensible_dset(&s, which);
+
+ for (step = 0; hang_back + step < s.nsteps;) {
+ for (which = s.ndatasets; which-- > 0; ) {
+ dbgf(2, "step %d which %d\n", step, which);
+ verify_extensible_dset(&s, which, mat, &step);
+ if (s.ndatasets <= s.nsteps)
+ nanosleep(&s.update_interval, NULL);
+ }
+ if (s.ndatasets > s.nsteps)
+ nanosleep(&s.update_interval, NULL);
+ }
+ }
+
+ for (which = 0; which < s.ndatasets; which++)
+ close_extensible_dset(&s, which);
+
+ if (s.use_vfd_swmr && s.wait_for_signal)
+ await_signal(s.file[0]);
+
+ restore_signals(&oldsigs);
+
+ if (H5Pclose(fcpl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fcpl)");
+
+ state_destroy(&s);
+
+ free(mat);
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/vfd_swmr_common.c b/test/vfd_swmr_common.c
new file mode 100644
index 0000000..e4f4889
--- /dev/null
+++ b/test/vfd_swmr_common.c
@@ -0,0 +1,262 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: swmr_common.c
+ *
+ * Purpose: Utility functions for the SWMR test code.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/***********/
+/* Headers */
+/***********/
+
+#include <err.h> /* for err(3) */
+
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+static const hid_t badhid = H5I_INVALID_HID;
+
+int verbosity = 2;
+
+void
+evsnprintf(char *buf, size_t bufsz, const char *fmt, va_list ap)
+{
+ int rc;
+
+ rc = vsnprintf(buf, bufsz, fmt, ap);
+
+ if (rc < 0)
+ err(EXIT_FAILURE, "%s: vsnprintf", __func__);
+ else if ((size_t)rc >= bufsz)
+ errx(EXIT_FAILURE, "%s: buffer too small", __func__);
+}
+
+void
+esnprintf(char *buf, size_t bufsz, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ evsnprintf(buf, bufsz, fmt, ap);
+ va_end(ap);
+}
+
+void
+dbgf(int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (verbosity < level)
+ return;
+
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+estack_state_t
+disable_estack(void)
+{
+ estack_state_t es = estack_get_state();
+
+ (void)H5Eset_auto(H5E_DEFAULT, NULL, NULL);
+
+ return es;
+}
+
+estack_state_t
+estack_get_state(void)
+{
+ estack_state_t es;
+
+ (void)H5Eget_auto(H5E_DEFAULT, &es.efunc, &es.edata);
+
+ return es;
+}
+
+void
+restore_estack(estack_state_t es)
+{
+ (void)H5Eset_auto(H5E_DEFAULT, es.efunc, es.edata);
+}
+
+void
+block_signals(sigset_t *oldset)
+{
+ sigset_t fullset;
+
+ if (sigfillset(&fullset) == -1) {
+ err(EXIT_FAILURE, "%s.%d: could not initialize signal masks",
+ __func__, __LINE__);
+ }
+
+ if (sigprocmask(SIG_BLOCK, &fullset, oldset) == -1)
+ err(EXIT_FAILURE, "%s.%d: sigprocmask", __func__, __LINE__);
+}
+
+void
+restore_signals(sigset_t *oldset)
+{
+ if (sigprocmask(SIG_SETMASK, oldset, NULL) == -1)
+ err(EXIT_FAILURE, "%s.%d: sigprocmask", __func__, __LINE__);
+}
+
+#if 0
+static const char *
+strsignal(int signum)
+{
+ switch (signum) {
+ case SIGUSR1:
+ return "SIGUSR1";
+ case SIGINT:
+ return "SIGINT";
+ case SIGPIPE:
+ return "SIGPIPE";
+ default:
+ return "<unknown>";
+ }
+}
+#endif
+
+void
+await_signal(hid_t fid)
+{
+ sigset_t sleepset;
+ struct timespec tick = {.tv_sec = 0, .tv_nsec = 1000000000 / 100};
+
+ if (sigfillset(&sleepset) == -1) {
+ err(EXIT_FAILURE, "%s.%d: could not initialize signal mask",
+ __func__, __LINE__);
+ }
+
+ /* Avoid deadlock: flush the file before waiting for the reader's
+ * message.
+ */
+ if (H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0)
+ errx(EXIT_FAILURE, "%s: H5Fflush failed", __func__);
+
+ dbgf(1, "waiting for signal\n");
+
+ for (;;) {
+ const int rc = sigtimedwait(&sleepset, NULL, &tick);
+
+ if (rc != -1) {
+ fprintf(stderr, "Received %s, wrapping things up.\n",
+ strsignal(rc));
+ break;
+ } else if (rc == -1 && errno == EAGAIN) {
+ estack_state_t es;
+
+ /* Avoid deadlock with peer: periodically enter the API so that
+ * tick processing occurs and data is flushed so that the peer
+ * can see it.
+ *
+ * The call we make will fail, but that's ok,
+ * so squelch errors.
+ */
+ es = disable_estack();
+ (void)H5Aexists_by_name(fid, "nonexistent", "nonexistent",
+ H5P_DEFAULT);
+ restore_estack(es);
+ } else if (rc == -1)
+ err(EXIT_FAILURE, "%s: sigtimedwait", __func__);
+ }
+}
+
+hid_t
+vfd_swmr_create_fapl(bool writer, bool only_meta_pages, bool use_vfd_swmr,
+ const char *mdfile_fmtstr, ...)
+{
+ H5F_vfd_swmr_config_t config;
+ hid_t fapl;
+ va_list ap;
+
+ /* Create file access property list */
+ if((fapl = h5_fileaccess()) < 0) {
+ warnx("h5_fileaccess");
+ return badhid;
+ }
+
+ /* FOR NOW: set to use latest format, the "old" parameter is not used */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) {
+ warnx("H5Pset_libver_bounds");
+ return badhid;
+ }
+
+ /*
+ * Set up to open the file with VFD SWMR configured.
+ */
+
+ /* Enable page buffering */
+ if(H5Pset_page_buffer_size(fapl, 4096, only_meta_pages ? 100 : 0, 0) < 0) {
+ warnx("H5Pset_page_buffer_size");
+ return badhid;
+ }
+
+ memset(&config, 0, sizeof(config));
+
+ config.version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config.tick_len = 4;
+ config.max_lag = 7;
+ config.writer = writer;
+ config.md_pages_reserved = 128;
+ va_start(ap, mdfile_fmtstr);
+ evsnprintf(config.md_file_path, sizeof(config.md_file_path),
+ mdfile_fmtstr, ap);
+ va_end(ap);
+
+ /* Enable VFD SWMR configuration */
+ if(use_vfd_swmr && H5Pset_vfd_swmr_config(fapl, &config) < 0) {
+ warnx("H5Pset_vfd_swmr_config");
+ return badhid;
+ }
+ return fapl;
+}
+
+/* Fetch a variable from the environment and parse it for unsigned long
+ * content. Return 0 if the variable is not present, -1 if it is present
+ * but it does not parse and compare less than `limit`, 1 if it's present,
+ * parses, and is in-bounds.
+ */
+int
+fetch_env_ulong(const char *varname, unsigned long limit, unsigned long *valp)
+{
+ char *end;
+ unsigned long ul;
+ char *tmp;
+
+ if ((tmp = getenv(varname)) == NULL)
+ return 0;
+
+ errno = 0;
+ ul = strtoul(tmp, &end, 0);
+ if (ul == ULONG_MAX && errno != 0) {
+ fprintf(stderr, "could not parse %s: %s\n", varname, strerror(errno));
+ return -1;
+ }
+ if (end == tmp || *end != '\0') {
+ fprintf(stderr, "could not parse %s\n", varname);
+ return -1;
+ }
+ if (ul > limit) {
+ fprintf(stderr, "%s (%lu) out of range\n", varname, ul);
+ return -1;
+ }
+ *valp = ul;
+ return 1;
+}
diff --git a/test/vfd_swmr_common.h b/test/vfd_swmr_common.h
new file mode 100644
index 0000000..8e1f877
--- /dev/null
+++ b/test/vfd_swmr_common.h
@@ -0,0 +1,114 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef _VFD_SWMR_COMMON_H
+#define _VFD_SWMR_COMMON_H
+
+/***********/
+/* Headers */
+/***********/
+
+#include <stdarg.h>
+#include "h5test.h"
+
+/**********/
+/* Macros */
+/**********/
+
+/* The maximum # of records to add/remove from the dataset in one step,
+ * used by vfd_swmr_addrem_writer and vfd_swmr_remove_reader.
+ */
+#define MAX_SIZE_CHANGE 10
+
+#define NLEVELS 5 /* # of datasets in the SWMR test file */
+
+#define NMAPPING 9
+
+#define COMMON_FILENAME "vfd_swmr_data.h5" /* SWMR test file name */
+#define DTYPE_SIZE 150 /* Data size in opaque type */
+
+/* The message sent by writer that the file open is done--releasing the file lock */
+#define WRITER_MESSAGE "VFD_SWMR_WRITER_MESSAGE"
+
+/************/
+/* Typedefs */
+/************/
+
+typedef struct _estack_state {
+ H5E_auto_t efunc;
+ void *edata;
+} estack_state_t;
+
+/* Information about a symbol/dataset */
+typedef struct {
+ char *name; /* Dataset name for symbol */
+ hid_t dsid; /* Dataset ID for symbol */
+ hsize_t nrecords; /* Number of records for the symbol */
+} symbol_info_t;
+
+/* A symbol's record */
+typedef struct {
+ uint64_t rec_id; /* ID for this record (unique in symbol) */
+ uint8_t info[DTYPE_SIZE]; /* "Other" information for this record */
+} symbol_t;
+
+typedef enum _testsel {
+ TEST_NONE = 0
+, TEST_NULL
+, TEST_OOB
+} testsel_t;
+
+/********************/
+/* Global Variables */
+/********************/
+H5TEST_DLLVAR symbol_info_t *symbol_info[NLEVELS];
+H5TEST_DLLVAR unsigned symbol_count[NLEVELS];
+
+/**************/
+/* Prototypes */
+/**************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+H5TEST_DLL estack_state_t estack_get_state(void);
+H5TEST_DLL estack_state_t disable_estack(void);
+H5TEST_DLL void restore_estack(estack_state_t);
+
+H5TEST_DLL symbol_info_t * choose_dataset(unsigned *, unsigned *);
+H5TEST_DLL hid_t create_symbol_datatype(void);
+H5TEST_DLL int generate_name(char *name_buf, unsigned level, unsigned count);
+H5TEST_DLL int generate_symbols(void);
+H5TEST_DLL int shutdown_symbols(void);
+H5TEST_DLL int print_metadata_retries_info(hid_t fid);
+
+H5TEST_DLL void block_signals(sigset_t *);
+H5TEST_DLL void restore_signals(sigset_t *);
+H5TEST_DLL void await_signal(hid_t);
+H5TEST_DLL hid_t vfd_swmr_create_fapl(bool, bool, bool, const char *, ...)
+ H5_ATTR_FORMAT(printf, 4, 5);
+
+H5TEST_DLL void dbgf(int, const char *, ...) H5_ATTR_FORMAT(printf, 2, 3);
+H5TEST_DLL void evsnprintf(char *, size_t, const char *, va_list);
+H5TEST_DLL void esnprintf(char *, size_t, const char *, ...)
+ H5_ATTR_FORMAT(printf, 3, 4);
+
+H5TEST_DLL int fetch_env_ulong(const char *, unsigned long, unsigned long *);
+
+#ifdef __cplusplus
+}
+#endif
+
+extern int verbosity;
+
+#endif /* _SWMR_COMMON_H */
diff --git a/test/vfd_swmr_generator.c b/test/vfd_swmr_generator.c
new file mode 100644
index 0000000..13fc941
--- /dev/null
+++ b/test/vfd_swmr_generator.c
@@ -0,0 +1,399 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: vfd_swmr_generator.c
+ * (copied and modified from swmr_generator.c)
+ *
+ * Purpose: Functions for building and setting up the VFD SWMR test file
+ * and datasets.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/***********/
+/* Headers */
+/***********/
+
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+/*
+ * This file needs to access testing codefrom the H5O package.
+ */
+#define H5O_FRIEND /*suppress error about including H5Opkg */
+#define H5O_TESTING
+#include "H5Opkg.h" /* Object headers */
+
+
+/****************/
+/* Local Macros */
+/****************/
+
+#define CHUNK_SIZE 50 /* Chunk size for created datasets */
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static int gen_skeleton(const char *filename, hbool_t verbose,
+ hbool_t vfd_swmr_write, int comp_level, const char *index_type,
+ unsigned random_seed);
+static void usage(void);
+
+
+/*-------------------------------------------------------------------------
+ * Function: gen_skeleton
+ *
+ * Purpose: Creates the HDF5 file and datasets which will be used in
+ * the SWMR testing.
+ *
+ * Parameters: const char *filename
+ * The SWMR test file's name.
+ *
+ * hbool_t verbose
+ * Whether verbose console output is desired.
+ *
+ * hbool_t vfd_swmr_write
+ * Whether to create the file with VFD SWMR writing enabled
+ *
+ * int comp_level
+ * The zlib compression level to use. -1 = no compression.
+ *
+ * const char *index_type
+ * The chunk index type (b1 | b2 | ea | fa)
+ *
+ * unsigned random_seed
+ * The random seed to store in the file. The sparse tests use
+ * this value.
+ *
+ * Return: Success: 0
+ * Failure: Can't fail
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+gen_skeleton(const char *filename, hbool_t verbose, hbool_t vfd_swmr_write,
+ int comp_level, const char *index_type, unsigned random_seed)
+{
+ hid_t fid; /* File ID for new HDF5 file */
+ hid_t fcpl; /* File creation property list */
+ hid_t fapl; /* File access property list */
+ hid_t dcpl; /* Dataset creation property list */
+ hid_t tid; /* Datatype for dataset elements */
+ hid_t sid; /* Dataspace ID */
+ hid_t aid; /* Attribute ID */
+ hsize_t dims[2] = {1, 0}; /* Dataset starting dimensions */
+ hsize_t max_dims[2] = {1, H5S_UNLIMITED}; /* Dataset maximum dimensions */
+ hsize_t chunk_dims[2] = {1, CHUNK_SIZE}; /* Chunk dimensions */
+#ifdef FILLVAL_WORKS
+ symbol_t fillval; /* Dataset fill value */
+#endif /* FILLVAL_WORKS */
+ unsigned u, v; /* Local index variable */
+ H5F_vfd_swmr_config_t *config = NULL; /* Configuration for VFD SWMR */
+
+ HDassert(filename);
+ HDassert(index_type);
+
+ /* Create file access property list */
+ if((fapl = h5_fileaccess()) < 0)
+ return -1;
+
+ /* Set to use the latest format with the latest chunk indexing */
+ /* FOR NOW: the parameter vfd_swmr_write is not used here as in swmr_generator.c */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ return -1;
+
+ /* There are two chunk indexes tested here.
+ * With one unlimited dimension, we get the extensible array index
+ * type, with two unlimited dimensions, we get a v2 B-tree.
+ */
+ if(!HDstrcmp(index_type, "b2"))
+ max_dims[0] = H5S_UNLIMITED;
+
+ /* Create file creation property list */
+ if((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ return -1;
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "Creating file\n");
+
+ /*
+ * Set up to create the file with VFD SWMR write configured.
+ */
+
+ if(vfd_swmr_write) {
+ /* Set file space strategy to paged aggregation in fcpl */
+ if(H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, FALSE, 1) < 0)
+ return -1;
+
+ /* Enable page buffering in fapl */
+ if(H5Pset_page_buffer_size(fapl, 4096, 0, 0) < 0)
+ return -1;
+
+ /* Allocate memory for the VFD SWMR configuration structure */
+ if((config = (H5F_vfd_swmr_config_t *)HDcalloc(1, sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ return -1;
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = 4;
+ config->max_lag = 10;
+ config->writer = TRUE;
+ config->md_pages_reserved = 128;
+ HDstrcpy(config->md_file_path, "my_md_file");
+
+ /* Enable VFD SWMR configuration in fapl */
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0)
+ return -1;
+ }
+
+ /* Create the file with VFD SWMR write configured */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, fcpl, fapl)) < 0)
+ return -1;
+
+ /* Close file creation property list */
+ if(H5Pclose(fcpl) < 0)
+ return -1;
+
+ /* Close file access property list */
+ if(H5Pclose(fapl) < 0)
+ return -1;
+
+ /* Create attribute with (shared) random number seed - for sparse test */
+ if((sid = H5Screate(H5S_SCALAR)) < 0)
+ return -1;
+ if((aid = H5Acreate2(fid, "seed", H5T_NATIVE_UINT, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ return -1;
+ if(H5Awrite(aid, H5T_NATIVE_UINT, &random_seed) < 0)
+ return -1;
+ if(H5Aclose(aid) < 0)
+ return -1;
+ if(H5Sclose(sid) < 0)
+ return -1;
+
+ /* Create datatype for creating datasets */
+ if((tid = create_symbol_datatype()) < 0)
+ return -1;
+
+ /* Create dataspace for creating datasets */
+ if((sid = H5Screate_simple(2, dims, max_dims)) < 0)
+ return -1;
+
+ /* Create dataset creation property list */
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ return -1;
+ if(H5Pset_chunk(dcpl, 2, chunk_dims) < 0)
+ return -1;
+ if(comp_level >= 0) {
+ if(H5Pset_deflate(dcpl, (unsigned)comp_level) < 0)
+ return -1;
+ } /* end if */
+#ifdef FILLVAL_WORKS
+ /* Currently fill values do not work because they can bump the dataspace
+ * message to the second object header chunk. We should enable the fillval
+ * here when this is fixed. -NAF 8/11/11 */
+ HDmemset(&fillval, 0, sizeof(fillval));
+ fillval.rec_id = (uint64_t)ULLONG_MAX;
+ if(H5Pset_fill_value(dcpl, tid, &fillval) < 0)
+ return -1;
+#endif /* FILLVAL_WORKS */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "Creating datasets\n");
+
+#if 0 /* delete this once the race condiditon bug is fixed */ /* JRM */
+ sleep(1);
+#endif /* JRM */
+
+ /* Create the datasets */
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++) {
+ hid_t dsid; /* Dataset ID */
+ char name_buf[64];
+ hbool_t move_dataspace_message = FALSE; /* Whether to move the dataspace message out of object header chunk #0 */
+
+ generate_name(name_buf, u, v);
+ if((dsid = H5Dcreate2(fid, name_buf, tid, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ return -1;
+
+ /* Determine if the dataspace message for this dataset should be
+ * moved out of chunk #0 of the object header
+ * (Set to TRUE for every fourth dataset)
+ */
+ move_dataspace_message = !(HDrandom() % 4);
+ if(move_dataspace_message) {
+ unsigned chunk_num; /* Object header chunk # for dataspace message */
+
+ /* Move the dataspace message to a new object header chunk */
+ if(H5O__msg_move_to_new_chunk_test(dsid, H5O_SDSPACE_ID) < 0)
+ return -1;
+
+ /* Retrieve the chunk # for the dataspace message */
+ chunk_num = UINT_MAX;
+ if(H5O__msg_get_chunkno_test(dsid, H5O_SDSPACE_ID, &chunk_num) < 0)
+ return -1;
+ /* Should not be in chunk #0 for now */
+ if(0 == chunk_num)
+ return -1;
+ } /* end if */
+
+ if(H5Dclose(dsid) < 0)
+ return -1;
+ } /* end for */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "Closing objects\n");
+
+ /* Close everythign */
+ if(H5Pclose(dcpl) < 0)
+ return -1;
+ if(H5Sclose(sid) < 0)
+ return -1;
+ if(H5Tclose(tid) < 0)
+ return -1;
+
+ if(verbose)
+ HDfprintf(stderr, "Closing file\n");
+
+ if(H5Fclose(fid) < 0)
+ return -1;
+
+ /* Free the config structure */
+ if(config)
+ HDfree(config);
+ return 0;
+} /* end gen_skeleton() */
+
+static void
+usage(void)
+{
+ printf("\n");
+ printf("Usage error!\n");
+ printf("\n");
+ printf("Usage: swmr_generator [-q] [-s] [-c <deflate compression level>]\n");
+ printf(" [-i <index type>] [-r <random seed>]\n");
+ printf("\n");
+ printf("NOTE: The random seed option is only used by the sparse test. Other\n");
+ printf(" tests specify the random seed as a reader/writer option.\n");
+ printf("\n");
+ printf("<deflate compression level> should be -1 (for no compression) or 0-9\n");
+ printf("\n");
+ printf("<index type> should be b2 or ea\n");
+ printf("\n");
+ printf("Defaults to verbose (no '-q' given), no VFD_SWMR_WRITE mode (no '-s' given) no\n");
+ printf("compression ('-c -1'), v1 b-tree indexing (-i b1), and will generate a random\n");
+ printf("seed (no -r given).\n");
+ printf("\n");
+ HDexit(1);
+} /* end usage() */
+
+int main(int argc, const char *argv[])
+{
+ int comp_level = -1; /* Compression level (-1 is no compression) */
+ hbool_t verbose = TRUE; /* Whether to emit some informational messages */
+ hbool_t vfd_swmr_write = FALSE; /* Whether to create file with VFD SWMR access */
+ const char *index_type = "b1"; /* Chunk index type */
+ hbool_t use_seed = FALSE; /* Set to TRUE if a seed was set on the command line */
+ unsigned random_seed = 0; /* Random # seed */
+ unsigned u; /* Local index variables */
+ int temp;
+
+ /* Parse command line options */
+ if(argc > 1) {
+ u = 1;
+ while(u < (unsigned)argc) {
+ if(argv[u][0] == '-') {
+ switch(argv[u][1]) {
+ /* Compress dataset chunks */
+ case 'c':
+ comp_level = HDatoi(argv[u + 1]);
+ if(comp_level < -1 || comp_level > 9)
+ usage();
+ u += 2;
+ break;
+
+ /* Chunk index type */
+ case 'i':
+ index_type = argv[u + 1];
+ if(HDstrcmp(index_type, "ea")
+ && HDstrcmp(index_type, "b2"))
+ usage();
+ u += 2;
+ break;
+
+ /* Random # seed */
+ case 'r':
+ use_seed = TRUE;
+ temp = HDatoi(argv[u + 1]);
+ if(temp < 0)
+ usage();
+ else
+ random_seed = (unsigned)temp;
+ u += 2;
+ break;
+
+ /* Be quiet */
+ case 'q':
+ verbose = FALSE;
+ u++;
+ break;
+
+ /* Run with SWMR_WRITE */
+ case 's':
+ vfd_swmr_write = TRUE;
+ u++;
+ break;
+
+ default:
+ usage();
+ break;
+ } /* end switch */
+ } /* end if */
+ } /* end while */
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "Parameters:\n");
+ HDfprintf(stderr, "\tswmr writes %s\n", vfd_swmr_write ? "on" : "off");
+ HDfprintf(stderr, "\tcompression level = %d\n", comp_level);
+ HDfprintf(stderr, "\tindex type = %s\n", index_type);
+ } /* end if */
+
+ /* Set the random seed */
+ if(!use_seed) {
+ struct timeval t;
+
+ HDgettimeofday(&t, NULL);
+ random_seed = (unsigned)(t.tv_usec);
+ } /* end if */
+ HDsrandom(random_seed);
+ /* ALWAYS emit the random seed for possible debugging */
+ HDfprintf(stderr, "Using generator random seed (used in sparse test only): %u\n", random_seed);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "Generating skeleton file: %s\n", COMMON_FILENAME);
+
+ /* Generate file skeleton */
+ if(gen_skeleton(COMMON_FILENAME, verbose, vfd_swmr_write, comp_level, index_type, random_seed) < 0) {
+ HDfprintf(stderr, "Error generating skeleton file!\n");
+ HDexit(1);
+ } /* end if */
+
+ return 0;
+} /* main() */
diff --git a/test/vfd_swmr_group_writer.c b/test/vfd_swmr_group_writer.c
new file mode 100644
index 0000000..a1bea61
--- /dev/null
+++ b/test/vfd_swmr_group_writer.c
@@ -0,0 +1,343 @@
+/*
+ * 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 <err.h>
+#include <libgen.h>
+#include <time.h> /* nanosleep(2) */
+#include <unistd.h> /* getopt(3) */
+
+#define H5F_FRIEND /*suppress error about including H5Fpkg */
+
+#include "hdf5.h"
+
+#include "H5Fpkg.h"
+// #include "H5Iprivate.h"
+#include "H5HGprivate.h"
+#include "H5VLprivate.h"
+
+#include "testhdf5.h"
+#include "vfd_swmr_common.h"
+
+typedef struct {
+ hid_t file, filetype, one_by_one_sid;
+ char filename[PATH_MAX];
+ char progname[PATH_MAX];
+ struct timespec update_interval;
+ unsigned int asteps;
+ unsigned int nsteps;
+ bool wait_for_signal;
+ bool use_vfd_swmr;
+} state_t;
+
+#define ALL_HID_INITIALIZER (state_t){ \
+ .file = H5I_INVALID_HID \
+ , .one_by_one_sid = H5I_INVALID_HID \
+ , .filename = "" \
+ , .filetype = H5T_NATIVE_UINT32 \
+ , .asteps = 10 \
+ , .nsteps = 100 \
+ , .wait_for_signal = true \
+ , .use_vfd_swmr = true \
+ , .update_interval = (struct timespec){ \
+ .tv_sec = 0 \
+ , .tv_nsec = 1000000000UL / 30 /* 1/30 second */}}
+
+static void state_init(state_t *, int, char **);
+
+static const hid_t badhid = H5I_INVALID_HID;
+
+static void
+usage(const char *progname)
+{
+ fprintf(stderr, "usage: %s [-S] [-W] [-a steps] [-b]\n"
+ " [-n iterations] [-u milliseconds]\n"
+ "\n"
+ "-S: do not use VFD SWMR\n"
+ "-W: do not wait for a signal before\n"
+ " exiting\n"
+ "-a steps: `steps` between adding attributes\n"
+ "-b: write data in big-endian byte order\n"
+ "-n iterations: how many times to expand each dataset\n"
+ "-u ms: milliseconds interval between updates\n"
+ " to %s.h5\n"
+ "\n",
+ progname, progname);
+ exit(EXIT_FAILURE);
+}
+
+static void
+state_init(state_t *s, int argc, char **argv)
+{
+ unsigned long tmp;
+ int ch;
+ const hsize_t dims = 1;
+ char tfile[PATH_MAX];
+ char *end;
+ unsigned long millis;
+
+ *s = ALL_HID_INITIALIZER;
+ esnprintf(tfile, sizeof(tfile), "%s", argv[0]);
+ esnprintf(s->progname, sizeof(s->progname), "%s", basename(tfile));
+
+ while ((ch = getopt(argc, argv, "SWa:bn:qu:")) != -1) {
+ switch (ch) {
+ case 'S':
+ s->use_vfd_swmr = false;
+ break;
+ case 'W':
+ s->wait_for_signal = false;
+ break;
+ case 'a':
+ case 'n':
+ errno = 0;
+ tmp = strtoul(optarg, &end, 0);
+ if (end == optarg || *end != '\0') {
+ errx(EXIT_FAILURE, "couldn't parse `-%c` argument `%s`", ch,
+ optarg);
+ } else if (errno != 0) {
+ err(EXIT_FAILURE, "couldn't parse `-%c` argument `%s`", ch,
+ optarg);
+ } else if (tmp > UINT_MAX)
+ errx(EXIT_FAILURE, "`-%c` argument `%lu` too large", ch, tmp);
+
+ if (ch == 'a')
+ s->asteps = (unsigned)tmp;
+ else
+ s->nsteps = (unsigned)tmp;
+ break;
+ case 'b':
+ s->filetype = H5T_STD_U32BE;
+ break;
+ case 'q':
+ verbosity = 0;
+ 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);
+ dbgf(1, "%lu milliseconds between updates\n", millis);
+ break;
+ case '?':
+ default:
+ usage(s->progname);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* space for attributes */
+ if ((s->one_by_one_sid = H5Screate_simple(1, &dims, &dims)) < 0)
+ errx(EXIT_FAILURE, "H5Screate_simple failed");
+
+ if (argc > 0)
+ errx(EXIT_FAILURE, "unexpected command-line arguments");
+
+ esnprintf(s->filename, sizeof(s->filename), "vfd_swmr_group.h5");
+}
+
+static void
+add_group_attribute(const state_t *s, hid_t g, hid_t sid, unsigned int which)
+{
+ hid_t aid;
+ char name[sizeof("attr-9999999999")];
+
+ esnprintf(name, sizeof(name), "attr-%u", which);
+
+ dbgf(1, "setting attribute %s on group %u to %u\n", name, which, which);
+
+ if ((aid = H5Acreate2(g, name, s->filetype, sid, H5P_DEFAULT,
+ H5P_DEFAULT)) < 0)
+ errx(EXIT_FAILURE, "H5Acreate2 failed");
+
+ if (H5Awrite(aid, H5T_NATIVE_UINT, &which) < 0)
+ errx(EXIT_FAILURE, "H5Awrite failed");
+ if (H5Aclose(aid) < 0)
+ errx(EXIT_FAILURE, "H5Aclose failed");
+}
+
+
+static void
+write_group(state_t *s, unsigned int which)
+{
+ char name[sizeof("/group-9999999999")];
+ hid_t g;
+
+ assert(which < s->nsteps);
+
+ esnprintf(name, sizeof(name), "/group-%d", which);
+
+ g = H5Gcreate2(s->file, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+
+ if (g < 0)
+ errx(EXIT_FAILURE, "H5Gcreate(, \"%s\", ) failed", name);
+
+ if (s->asteps != 0 && which % s->asteps == 0)
+ add_group_attribute(s, g, s->one_by_one_sid, which);
+
+ if (H5Gclose(g) < 0)
+ errx(EXIT_FAILURE, "H5Gclose failed");
+}
+
+static bool
+verify_group_attribute(hid_t g, unsigned int which)
+{
+ estack_state_t es;
+ unsigned int read_which;
+ hid_t aid;
+ char name[sizeof("attr-9999999999")];
+
+ esnprintf(name, sizeof(name), "attr-%u", which);
+
+ dbgf(1, "verifying attribute %s on group %u equals %u\n", name, which,
+ which);
+
+ es = disable_estack();
+ if ((aid = H5Aopen(g, name, H5P_DEFAULT)) < 0) {
+ restore_estack(es);
+ return false;
+ }
+
+ if (H5Aread(aid, H5T_NATIVE_UINT, &read_which) < 0) {
+ restore_estack(es);
+ if (H5Aclose(aid) < 0)
+ errx(EXIT_FAILURE, "H5Aclose failed");
+ return false;
+ }
+
+ restore_estack(es);
+
+ if (H5Aclose(aid) < 0)
+ errx(EXIT_FAILURE, "H5Aclose failed");
+
+ return read_which == which;
+}
+
+static bool
+verify_group(state_t *s, unsigned int which)
+{
+ char name[sizeof("/group-9999999999")];
+ hid_t g;
+ estack_state_t es;
+ bool result;
+
+ assert(which < s->nsteps);
+
+ esnprintf(name, sizeof(name), "/group-%d", which);
+
+ es = disable_estack();
+ g = H5Gopen(s->file, name, H5P_DEFAULT);
+ restore_estack(es);
+
+ if (g < 0)
+ return false;
+
+ if (s->asteps != 0 && which % s->asteps == 0)
+ result = verify_group_attribute(g, which);
+ else
+ result = true;
+
+ if (H5Gclose(g) < 0)
+ errx(EXIT_FAILURE, "H5Gclose failed");
+
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ hid_t fapl, fcpl;
+ sigset_t oldsigs;
+ herr_t ret;
+ unsigned step;
+ bool writer;
+ state_t s;
+ const char *personality;
+
+ state_init(&s, argc, argv);
+
+ personality = strstr(s.progname, "vfd_swmr_group_");
+
+ if (personality != NULL &&
+ strcmp(personality, "vfd_swmr_group_writer") == 0)
+ writer = true;
+ else if (personality != NULL &&
+ strcmp(personality, "vfd_swmr_group_reader") == 0)
+ writer = false;
+ else {
+ errx(EXIT_FAILURE,
+ "unknown personality, expected vfd_swmr_group_{reader,writer}");
+ }
+
+ fapl = vfd_swmr_create_fapl(writer, true, s.use_vfd_swmr, "./shadow");
+
+ if (fapl < 0)
+ errx(EXIT_FAILURE, "vfd_swmr_create_fapl");
+
+ if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ errx(EXIT_FAILURE, "H5Pcreate");
+
+ ret = H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, false, 1);
+ if (ret < 0)
+ errx(EXIT_FAILURE, "H5Pset_file_space_strategy");
+
+ if (writer)
+ s.file = H5Fcreate(s.filename, H5F_ACC_TRUNC, fcpl, fapl);
+ else
+ s.file = H5Fopen(s.filename, H5F_ACC_RDONLY, fapl);
+
+ if (s.file == badhid)
+ errx(EXIT_FAILURE, writer ? "H5Fcreate" : "H5Fopen");
+
+ block_signals(&oldsigs);
+
+ if (writer) {
+ for (step = 0; step < s.nsteps; step++) {
+ dbgf(2, "step %d\n", step);
+ write_group(&s, step);
+ nanosleep(&s.update_interval, NULL);
+ }
+ } else {
+ for (step = 0; step < s.nsteps;) {
+ dbgf(2, "step %d\n", step);
+ if (verify_group(&s, step))
+ step++;
+ nanosleep(&s.update_interval, NULL);
+ }
+ }
+
+ if (s.use_vfd_swmr && s.wait_for_signal)
+ await_signal(s.file);
+
+ restore_signals(&oldsigs);
+
+ if (H5Pclose(fapl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fapl)");
+
+ if (H5Pclose(fcpl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fcpl)");
+
+ if (H5Fclose(s.file) < 0)
+ errx(EXIT_FAILURE, "H5Fclose");
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/vfd_swmr_reader.c b/test/vfd_swmr_reader.c
new file mode 100644
index 0000000..706c894
--- /dev/null
+++ b/test/vfd_swmr_reader.c
@@ -0,0 +1,677 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: vfd_swmr_reader.c
+ * (copied and modified from swmr_reader.c)
+ *
+ * Purpose: Reads data from a randomly selected subset of the datasets
+ * in the VFD SWMR test file.
+ *
+ * This program is intended to run concurrently with the
+ * vfd_swmr_writer program.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/***********/
+/* Headers */
+/***********/
+
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static int check_dataset(hid_t fid, hbool_t verbose, FILE *verbose_file,
+ const char *sym_name, symbol_t *record, hid_t rec_sid);
+static int read_records(const char *filename, hbool_t verbose, FILE *verbose_file,
+ unsigned random_seed, unsigned long nseconds, unsigned poll_time,
+ unsigned ncommon, unsigned nrandom);
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+static hid_t symbol_tid = -1; /* The type ID for the SWMR datasets */
+
+
+/*-------------------------------------------------------------------------
+ * Function: check_dataset
+ *
+ * Purpose: For a given dataset, checks to make sure that the stated
+ * and actual sizes are the same. If they are not, then
+ * we have an inconsistent dataset due to a SWMR error.
+ *
+ * Parameters: hid_t fid
+ * The SWMR test file's ID.
+ *
+ * hbool_t verbose
+ * Whether verbose console output is desired.
+ *
+ * FILE *verbose_file
+ * File handle for verbose output
+ *
+ * const char *sym_name
+ * The name of the dataset from which to read.
+ *
+ * symbol_t *record
+ * Memory for the record. Must be pre-allocated.
+ *
+ * hid_t rec_sid
+ * The memory dataspace for access. It's always the same so
+ * there is no need to re-create it every time this function
+ * is called.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+check_dataset(hid_t fid, hbool_t verbose, FILE *verbose_file,
+ const char *sym_name, symbol_t *record, hid_t rec_sid)
+{
+ int fill_count = 0; /* # of times fill value (0) was read
+ * instead of the expected value.
+ */
+ hid_t dsid; /* Dataset ID */
+ hid_t file_sid; /* Dataset's space ID */
+ hssize_t snpoints; /* Number of elements in dataset */
+ hsize_t start[2] = {0, 0}, count[2] = {1, 1}; /* Hyperslab selection values */
+
+ HDassert(fid >= 0);
+ HDassert(sym_name);
+ HDassert(record);
+ HDassert(rec_sid >= 0);
+
+ /* Open dataset for symbol */
+ if((dsid = H5Dopen2(fid, sym_name, H5P_DEFAULT)) < 0) {
+ fprintf(stderr, "%s.%d: H5Dopen2 failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Get the dataset's dataspace */
+ if((file_sid = H5Dget_space(dsid)) < 0) {
+ fprintf(stderr, "%s.%d: H5Dget_space failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Get the number of elements (= records, for 1-D datasets) */
+ if((snpoints = H5Sget_simple_extent_npoints(file_sid)) < 0) {
+ fprintf(stderr, "%s.%d: H5Sget_simple_extent_npoints failed\n",
+ __func__, __LINE__);
+ goto error;
+ }
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Symbol = '%s', # of records = %lld\n", sym_name, (long long)snpoints);
+
+ /* Check if there are records for symbol */
+ if(snpoints > 0) {
+ /* Choose the last record in the dataset */
+ start[1] = (hsize_t)(snpoints - 1);
+ if(H5Sselect_hyperslab(file_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0) {
+ fprintf(stderr, "%s.%d: H5Sselect_hyperslab failed\n",
+ __func__, __LINE__);
+ goto error;
+ }
+
+ /* Read record from dataset */
+ record->rec_id = UINT64_MAX;
+ if(H5Dread(dsid, symbol_tid, rec_sid, file_sid, H5P_DEFAULT, record) < 0) {
+ fprintf(stderr, "%s.%d: H5Dread failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Verify record value */
+ if (record->rec_id != start[1] && record->rec_id == 0)
+ fill_count++;
+ else if (record->rec_id != start[1]) {
+ struct timeval tv;
+
+ HDgettimeofday(&tv, NULL);
+
+ if(verbose) {
+ HDfprintf(verbose_file, "*** READER ERROR ***\n");
+ HDfprintf(verbose_file, "Incorrect record value!\n");
+ HDfprintf(verbose_file,
+ "Time = %jd.%06jd, Symbol = '%s'"
+ ", # of records = %" PRIdHSIZE
+ ", record->rec_id = %" PRIu64 "\n",
+ (intmax_t)tv.tv_sec, (intmax_t)tv.tv_usec,
+ sym_name, snpoints, record->rec_id);
+ } /* end if */
+ fprintf(stderr,
+ "%s.%d: record value %" PRIu64 " != %" PRIuHSIZE "\n",
+ __func__, __LINE__, record->rec_id, start[1]);
+ goto error;
+ } /* end if */
+ } /* end if */
+
+ /* Close the dataset's dataspace */
+ if(H5Sclose(file_sid) < 0) {
+ fprintf(stderr, "%s.%d: H5Sclose failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Close dataset for symbol */
+ if(H5Dclose(dsid) < 0) {
+ fprintf(stderr, "%s.%d: H5Dclose failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ return fill_count;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Sclose(file_sid);
+ H5Dclose(dsid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* end check_dataset() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: read_records
+ *
+ * Purpose: For a given dataset, checks to make sure that the stated
+ * and actual sizes are the same. If they are not, then
+ * we have an inconsistent dataset due to a SWMR error.
+ *
+ * The "common" datasets are a random selection from among
+ * the level 0 datasets. The "random" datasets are a random
+ * selection from among all the file's datasets. This scheme
+ * ensures that the level 0 datasets are interrogated vigorously.
+ *
+ * Parameters: const char *filename
+ * The SWMR test file's name.
+ *
+ * hbool_t verbose
+ * Whether verbose console output is desired.
+ *
+ * FILE *verbose_file
+ * File handle for verbose output
+ *
+ * unsigned random_seed
+ * Random seed for the file (used for verbose logging)
+ *
+ * unsigned long nseconds
+ * The amount of time to read records (ns).
+ *
+ * unsigned poll_time
+ * The amount of time to sleep (s).
+ *
+ * unsigned ncommon
+ * The number of common/non-random datasets that will be opened.
+ *
+ * unsigned nrandom
+ * The number of random datasets that will be opened.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+read_records(const char *filename, hbool_t verbose, FILE *verbose_file,
+ unsigned random_seed, unsigned long nseconds, unsigned poll_time,
+ unsigned ncommon, unsigned nrandom)
+{
+ time_t start_time; /* Starting time */
+ time_t curr_time; /* Current time */
+ symbol_info_t **sym_com = NULL; /* Pointers to array of common dataset IDs */
+ symbol_info_t **sym_rand = NULL; /* Pointers to array of random dataset IDs */
+ hid_t mem_sid; /* Memory dataspace ID */
+ hid_t fid; /* SWMR test file ID */
+ hid_t fapl; /* file access property list */
+ symbol_t record; /* The record to read from the dataset */
+ unsigned v; /* Local index variable */
+ hbool_t use_log_vfd = FALSE; /* Use the log VFD (set this manually) */
+ H5F_vfd_swmr_config_t *config = NULL; /* Configuration for VFD SWMR */
+
+ HDassert(filename);
+ HDassert(nseconds != 0);
+ HDassert(poll_time != 0);
+
+ /* Reset the record */
+ /* (record's 'info' field might need to change for each record read, also) */
+ HDmemset(&record, 0, sizeof(record));
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Choosing datasets\n");
+
+ /* Allocate space for 'common' datasets, if any */
+ if(ncommon > 0) {
+ /* Allocate array to hold pointers to symbols for common datasets */
+ if(NULL == (sym_com = (symbol_info_t **)HDmalloc(sizeof(symbol_info_t *) * ncommon))) {
+ fprintf(stderr, "%s.%d: malloc failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Open the common datasets */
+ for(v = 0; v < ncommon; v++) {
+ unsigned offset; /* Offset of symbol to use */
+
+ /* Determine the offset of the symbol, within level 0 symbols */
+ /* (level 0 symbols are the most common symbols) */
+ offset = (unsigned)((unsigned)HDrandom() % symbol_count[0]);
+ sym_com[v] = &symbol_info[0][offset];
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Common symbol #%u = '%s'\n", v, symbol_info[0][offset].name);
+ } /* end for */
+ } /* end if */
+
+ /* Allocate space for 'random' datasets, if any */
+ if(nrandom > 0) {
+ /* Allocate array to hold pointers to symbols for random datasets */
+ if(NULL == (sym_rand = (symbol_info_t **)HDmalloc(sizeof(symbol_info_t *) * nrandom))) {
+ fprintf(stderr, "%s.%d: malloc failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Determine the random datasets */
+ for(v = 0; v < nrandom; v++) {
+ symbol_info_t *sym; /* Symbol to use */
+
+ /* Determine the symbol, within all symbols */
+ if(NULL == (sym = choose_dataset(NULL, NULL)))
+ return -1;
+ sym_rand[v] = sym;
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Random symbol #%u = '%s'\n", v, sym->name);
+ } /* end for */
+ } /* end if */
+
+ /* Create a dataspace for the record to read */
+ if((mem_sid = H5Screate(H5S_SCALAR)) < 0) {
+ fprintf(stderr, "%s.%d: H5Screate failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Going to open file and read records\n");
+
+ /* Get the starting time */
+ start_time = HDtime(NULL);
+ curr_time = start_time;
+
+ /* Create file access property list */
+ if((fapl = h5_fileaccess()) < 0) {
+ fprintf(stderr, "%s.%d: h5_fileaccess failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Log I/O when verbose output it enbabled */
+ if(use_log_vfd) {
+ char verbose_name[1024];
+
+ HDsnprintf(verbose_name, sizeof(verbose_name), "vfd_swmr_reader.log.%u", random_seed);
+
+ H5Pset_fapl_log(fapl, verbose_name, H5FD_LOG_ALL, (size_t)(512 * 1024 * 1024));
+ } /* end if */
+
+ /*
+ * Set up to open the file with VFD SWMR configured.
+ */
+ /* Enable page buffering */
+ if(H5Pset_page_buffer_size(fapl, 4096, 0, 0) < 0) {
+ fprintf(stderr, "%s.%d: H5Pset_page_buffer_size failed\n",
+ __func__, __LINE__);
+ goto error;
+ }
+
+ /* Allocate memory for the configuration structure */
+ if((config = (H5F_vfd_swmr_config_t *)HDcalloc(1, sizeof(H5F_vfd_swmr_config_t))) == NULL) {
+ fprintf(stderr, "%s.%d: malloc failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = 4;
+ config->max_lag = 5;
+ config->writer = FALSE;
+ config->md_pages_reserved = 128;
+ HDstrcpy(config->md_file_path, "./my_md_file");
+
+ /* Enable VFD SWMR configuration */
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0) {
+ fprintf(stderr, "%s.%d: H5Pset_vfd_swmr_config failed\n",
+ __func__, __LINE__);
+ goto error;
+ }
+
+ /* Loop over reading records until [at least] the correct # of seconds have passed */
+ while(curr_time < (time_t)(start_time + (time_t)nseconds)) {
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Opening file: %s\n", filename);
+
+ /* Open the file with VFD SWMR configured */
+ /* Remove H5E_BEGIN_TRY/END_TRY if you want to see the error stack */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl);
+ } H5E_END_TRY;
+ if(fid < 0) {
+ HDfprintf(stderr, "READER: Error in opening the file: %s\n", filename);
+ goto error;
+ }
+
+ /* Check 'common' datasets, if any */
+ if(ncommon > 0) {
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Checking common symbols after FILE OPEN\n");
+
+ /* Iterate over common datasets */
+ for(v = 0; v < ncommon; v++) {
+ /* Check common dataset */
+ const int fill_count =
+ check_dataset(fid, verbose, verbose_file, sym_com[v]->name,
+ &record, mem_sid);
+ if(fill_count < 0) {
+ fprintf(stderr, "%s.%d: check_dataset failed\n",
+ __func__, __LINE__);
+ goto error;
+ }
+ HDmemset(&record, 0, sizeof(record));
+ if (fill_count > 0) {
+ fprintf(stderr, "common dataset: read fill at %d records\n",
+ fill_count);
+ }
+ } /* end for */
+ } /* end if */
+
+ /* Check 'random' datasets, if any */
+ if(nrandom > 0) {
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Checking random symbols after FILE OPEN\n");
+
+ /* Iterate over random datasets */
+ for(v = 0; v < nrandom; v++) {
+ /* Check random dataset */
+ const int fill_count = check_dataset(fid, verbose, verbose_file,
+ sym_rand[v]->name, &record, mem_sid);
+ if(fill_count < 0) {
+ fprintf(stderr, "%s.%d: check_dataset failed\n",
+ __func__, __LINE__);
+ goto error;
+ }
+ HDmemset(&record, 0, sizeof(record));
+ if (fill_count > 0) {
+ fprintf(stderr, "random dataset: read fill at %d records\n",
+ fill_count);
+ }
+ } /* end for */
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Closing file\n");
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0) {
+ fprintf(stderr, "%s.%d: H5Fclose failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Sleep for the appropriate # of seconds */
+ HDsleep(poll_time);
+
+ /* Retrieve the current time */
+ curr_time = HDtime(NULL);
+ } /* end while */
+
+ /* Close the memory dataspace */
+ if(H5Sclose(mem_sid) < 0) {
+ fprintf(stderr, "%s.%d: H5Sclose failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* Close the fapl */
+ if(H5Pclose(fapl) < 0) {
+ fprintf(stderr, "%s.%d: H5Pclose failed\n", __func__, __LINE__);
+ goto error;
+ }
+
+ if(config)
+ HDfree(config);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Closing datasets\n");
+
+ /* Close 'random' datasets, if any */
+ if(nrandom > 0) {
+ /* Release array holding dataset ID's for random datasets */
+ HDfree(sym_rand);
+ } /* end if */
+
+ /* Close 'common' datasets, if any */
+ if(ncommon > 0) {
+ /* Release array holding dataset ID's for common datasets */
+ HDfree(sym_com);
+ } /* end if */
+
+ return 0;
+
+error:
+ if(config)
+ HDfree(config);
+
+ if(sym_rand)
+ HDfree(sym_rand);
+
+ if(sym_com)
+ HDfree(sym_com);
+
+ H5E_BEGIN_TRY {
+ H5Sclose(mem_sid);
+ H5Fclose(fid);
+ H5Pclose(fapl);
+ } H5E_END_TRY;
+
+ return -1;
+} /* end read_records() */
+
+static void
+usage(void)
+{
+ printf("\n");
+ printf("Usage error!\n");
+ printf("\n");
+ printf("Usage: vfd_swmr_reader [-q] [-s <# of seconds to sleep between polling>]\n");
+ printf(" [-h <# of common symbols to poll>] [-l <# of random symbols to poll>]\n");
+ printf(" [-r <random seed>] <# of seconds to test>\n");
+ printf("\n");
+ printf("<# of seconds to test> must be specified.\n");
+ printf("\n");
+ printf("Defaults to verbose (no '-q' given), 1 second between polling ('-s 1'),\n");
+ printf("5 common symbols to poll ('-h 5'), 10 random symbols to poll ('-l 10'),\n");
+ printf("and will generate a random seed (no -r given).\n");
+ printf("\n");
+ HDexit(1);
+}
+
+int main(int argc, const char *argv[])
+{
+ long nseconds = 0; /* # of seconds to test */
+ int poll_time = 1; /* # of seconds between polling */
+ int ncommon = 5; /* # of common symbols to poll */
+ int nrandom = 10; /* # of random symbols to poll */
+ hbool_t verbose = TRUE; /* Whether to emit some informational messages */
+ FILE *verbose_file = NULL; /* File handle for verbose output */
+ hbool_t use_seed = FALSE; /* Set to 1 if a seed was set on the command line */
+ unsigned random_seed = 0; /* Random # seed */
+ unsigned u; /* Local index variables */
+ int temp;
+
+ /* Parse command line options */
+ if(argc < 2)
+ usage();
+ if(argc > 1) {
+ u = 1;
+ while(u < (unsigned)argc) {
+ if(argv[u][0] == '-') {
+ switch(argv[u][1]) {
+ /* # of common symbols to poll */
+ case 'h':
+ ncommon = HDatoi(argv[u + 1]);
+ if(ncommon < 0)
+ usage();
+ u += 2;
+ break;
+
+ /* # of random symbols to poll */
+ case 'l':
+ nrandom = HDatoi(argv[u + 1]);
+ if(nrandom < 0)
+ usage();
+ u += 2;
+ break;
+
+ /* Be quiet */
+ case 'q':
+ verbose = FALSE;
+ u++;
+ break;
+
+ /* Random # seed */
+ case 'r':
+ use_seed = TRUE;
+ temp = HDatoi(argv[u + 1]);
+ if(temp < 0)
+ usage();
+ else
+ random_seed = (unsigned)temp;
+ u += 2;
+ break;
+
+ /* # of seconds between polling */
+ case 's':
+ poll_time = HDatoi(argv[u + 1]);
+ if(poll_time < 0)
+ usage();
+ u += 2;
+ break;
+
+ default:
+ usage();
+ break;
+ } /* end switch */
+ } /* end if */
+ else {
+ /* Get the number of records to append */
+ nseconds = HDatol(argv[u]);
+ if(nseconds <= 0)
+ usage();
+
+ u++;
+ } /* end else */
+ } /* end while */
+ } /* end if */
+ if(nseconds <= 0)
+ usage();
+ if(poll_time >= nseconds)
+ usage();
+
+ /* Set the random seed */
+ if(!use_seed) {
+ struct timeval t;
+
+ HDgettimeofday(&t, NULL);
+ random_seed = (unsigned)(t.tv_usec);
+ } /* end if */
+ HDsrandom(random_seed);
+
+ /* Open output file */
+ if(verbose) {
+ char verbose_name[1024];
+
+ HDsnprintf(verbose_name, sizeof(verbose_name), "vfd_swmr_reader.out.%u", random_seed);
+ if(NULL == (verbose_file = HDfopen(verbose_name, "w"))) {
+ HDfprintf(stderr, "READER: Can't open verbose output file!\n");
+ HDexit(1);
+ }
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(verbose_file, "READER: Parameters:\n");
+ HDfprintf(verbose_file, "\t# of seconds between polling = %d\n", poll_time);
+ HDfprintf(verbose_file, "\t# of common symbols to poll = %d\n", ncommon);
+ HDfprintf(verbose_file, "\t# of random symbols to poll = %d\n", nrandom);
+ HDfprintf(verbose_file, "\t# of seconds to test = %ld\n", nseconds);
+ } /* end if */
+
+ /* ALWAYS emit the random seed for possible debugging */
+ HDfprintf(stdout, "READER: Using reader random seed: %u\n", random_seed);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Generating symbol names\n");
+
+ /* Generate dataset names */
+ if(generate_symbols() < 0) {
+ HDfprintf(stderr, "READER: Error generating symbol names!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Create datatype for creating datasets */
+ if((symbol_tid = create_symbol_datatype()) < 0) {
+ HDfprintf(stderr, "READER: Error creating symbol datatype!\n");
+ HDexit(1);
+ }
+
+ /* Reading records from datasets */
+ if(read_records(COMMON_FILENAME, verbose, verbose_file, random_seed, (unsigned long)nseconds, (unsigned)poll_time, (unsigned)ncommon, (unsigned)nrandom) < 0) {
+ HDfprintf(stderr, "READER: Error reading records from datasets (random_seed = %u)!\n", random_seed);
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Releasing symbols\n");
+
+ /* Clean up the symbols */
+ if(shutdown_symbols() < 0) {
+ HDfprintf(stderr, "READER: Error releasing symbols!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "READER: Closing objects\n");
+
+ /* Close objects created */
+ if(H5Tclose(symbol_tid) < 0) {
+ HDfprintf(stderr, "READER: Error closing symbol datatype!\n");
+ HDexit(1);
+ } /* end if */
+
+ return 0;
+}
+
diff --git a/test/vfd_swmr_remove_reader.c b/test/vfd_swmr_remove_reader.c
new file mode 100644
index 0000000..3515987
--- /dev/null
+++ b/test/vfd_swmr_remove_reader.c
@@ -0,0 +1,596 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: vfd_swmr_remove_reader.c
+ * (copied and modified from swmr_remove_reader.c)
+ *
+ * Purpose: Reads data from a randomly selected subset of the datasets
+ * in the VFD SWMR test file. Unlike the regular reader, these
+ * datasets will be shrinking.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/***********/
+/* Headers */
+/***********/
+
+#include <err.h> /* errx(3) */
+#include <stdlib.h> /* EXIT_FAILURE */
+
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+static hid_t symbol_tid = -1;
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static int check_dataset(hid_t, hid_t, unsigned, const char *,
+ symbol_t *, hid_t);
+static int read_records(const char *, unsigned, unsigned long,
+ unsigned, unsigned, unsigned);
+static void usage(void);
+
+
+/*-------------------------------------------------------------------------
+ * Function: check_dataset
+ *
+ * Purpose: For a given dataset, checks to make sure that the stated
+ * and actual sizes are the same. If they are not, then
+ * we have an inconsistent dataset due to a SWMR error.
+ *
+ * Parameters: hid_t fid
+ * The SWMR test file's ID.
+ *
+ * unsigned verbose
+ * Whether verbose console output is desired.
+ *
+ * const char *sym_name
+ * The name of the dataset from which to read.
+ *
+ * symbol_t *record
+ * Memory for the record. Must be pre-allocated.
+ *
+ * hid_t rec_sid
+ * The memory dataspace for access. It's always the same so
+ * there is no need to re-create it every time this function
+ * is called.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+check_dataset(hid_t fid, hid_t dapl, unsigned verbose, const char *sym_name, symbol_t *record,
+ hid_t rec_sid)
+{
+ hid_t dsid; /* Dataset ID */
+ hid_t file_sid; /* Dataset's space ID */
+ hssize_t snpoints; /* Number of elements in dataset */
+ hsize_t start[2] = {0, 0}, count[2] = {1, 1}; /* Hyperslab selection values */
+
+ HDassert(fid >= 0);
+ HDassert(sym_name);
+ HDassert(record);
+ HDassert(rec_sid >= 0);
+
+ /* Open dataset for symbol */
+ if((dsid = H5Dopen2(fid, sym_name, dapl)) < 0)
+ goto error;
+
+ /* Get the dataset's dataspace */
+ if((file_sid = H5Dget_space(dsid)) < 0)
+ goto error;
+
+ /* Get the number of elements (= records, for 1-D datasets) */
+ if((snpoints = H5Sget_simple_extent_npoints(file_sid)) < 0)
+ goto error;
+
+ /* Back off by one: it's possible that the metadata indicating
+ * `snpoints` available is new, but the data is stale, because
+ * a tick occurred on the writer between H5Dset_extent() and H5Dwrite().
+ */
+ snpoints -= MAX_SIZE_CHANGE;
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "READER: Symbol = '%s'"
+ ", # of records = %" PRIdHSIZE "\n", sym_name, snpoints);
+ }
+
+ /* Check if there are records for symbol */
+ if(snpoints > 0) {
+ /* Choose a random record in the dataset, choosing the last record half
+ * the time */
+ start[1] = (hsize_t)(HDrandom() % (snpoints * 2));
+ if(start[1] > (hsize_t)(snpoints - 1))
+ start[1] = (hsize_t)(snpoints - 1);
+ if(H5Sselect_hyperslab(file_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0)
+ goto error;
+
+ /* Read record from dataset */
+#ifdef FILLVAL_WORKS
+ /* When shrinking the dataset, we cannot guarantee that the buffer will
+ * even be touched, unless there is a fill value. Since fill values do
+ * not work with SWMR currently (see note in swmr_generator.c), we
+ * simply initialize rec_id to 0. */
+ record->rec_id = (uint64_t)ULLONG_MAX - 1;
+#else /* FILLVAL_WORKS */
+ record->rec_id = (uint64_t)0;
+#endif /* FILLVAL_WORKS */
+ if(H5Dread(dsid, symbol_tid, rec_sid, file_sid, H5P_DEFAULT, record) < 0)
+ goto error;
+
+ /* Verify record value - note that it may be the fill value, because the
+ * chunk may be deleted before the object header has the updated
+ * dimensions */
+ if(record->rec_id != start[1] && record->rec_id != 0) {
+ HDfprintf(stderr, "*** READER: ERROR ***\n");
+ HDfprintf(stderr, "Incorrect record value!\n");
+ HDfprintf(stderr, "Symbol = '%s', # of records = %" PRIdHSIZE
+ ", record->rec_id = %" PRIx64 ", expected %" PRIxHSIZE "\n",
+ sym_name, snpoints, record->rec_id, start[1]);
+ return -1;
+ } /* end if */
+ } /* end if */
+
+ /* Close the dataset's dataspace */
+ if(H5Sclose(file_sid) < 0)
+ goto error;
+
+ /* Close dataset for symbol */
+ if(H5Dclose(dsid) < 0)
+ goto error;
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Sclose(file_sid);
+ H5Dclose(dsid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* end check_dataset() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: read_records
+ *
+ * Purpose: For a given dataset, checks to make sure that the stated
+ * and actual sizes are the same. If they are not, then
+ * we have an inconsistent dataset due to a SWMR error.
+ *
+ * The "common" datasets are a random selection from among
+ * the level 0 datasets. The "random" datasets are a random
+ * selection from among all the file's datasets. This scheme
+ * ensures that the level 0 datasets are interrogated vigorously.
+ *
+ * Parameters: const char *filename
+ * The SWMR test file's name.
+ *
+ * unsigned verbose
+ * Whether verbose console output is desired.
+ *
+ * unsigned long nseconds
+ * The amount of time to read records (ns).
+ *
+ * unsigned poll_time
+ * The amount of time to sleep (s).
+ *
+ * unsigned ncommon
+ * The number of common/non-random datasets that will be opened.
+ *
+ * unsigned nrandom
+ * The number of random datasets that will be opened.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+read_records(const char *filename, unsigned verbose, unsigned long nseconds,
+ unsigned poll_time, unsigned ncommon, unsigned nrandom)
+{
+ time_t start_time; /* Starting time */
+ time_t curr_time; /* Current time */
+ symbol_info_t **sym_com = NULL; /* Pointers to array of common dataset IDs */
+ symbol_info_t **sym_rand = NULL; /* Pointers to array of random dataset IDs */
+ hid_t dapl;
+ hid_t mem_sid; /* Memory dataspace ID */
+ hid_t fid; /* SWMR test file ID */
+ hid_t fapl; /* File access property list */
+ symbol_t record; /* The record to add to the dataset */
+ unsigned v; /* Local index variable */
+ H5F_vfd_swmr_config_t *config = NULL; /* Configuration for VFD SWMR */
+
+ HDassert(filename);
+ HDassert(nseconds != 0);
+ HDassert(poll_time != 0);
+
+ /* Reset the record */
+ /* (record's 'info' field might need to change for each record written, also) */
+ HDmemset(&record, 0, sizeof(record));
+
+ if ((dapl = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+ errx(EXIT_FAILURE, "%s.%d: H5Pcreate failed", __func__, __LINE__);
+
+ if (H5Pset_chunk_cache(dapl, H5D_CHUNK_CACHE_NSLOTS_DEFAULT, 0,
+ H5D_CHUNK_CACHE_W0_DEFAULT) < 0)
+ errx(EXIT_FAILURE, "H5Pset_chunk_cache failed");
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Choosing datasets\n");
+
+ /* Allocate space for 'common' datasets, if any */
+ if(ncommon > 0) {
+ /* Allocate array to hold pointers to symbols for common datasets */
+ if(NULL == (sym_com = (symbol_info_t **)HDmalloc(sizeof(symbol_info_t *) * ncommon)))
+ goto error;
+
+ /* Open the common datasets */
+ for(v = 0; v < ncommon; v++) {
+ unsigned offset; /* Offset of symbol to use */
+
+ /* Determine the offset of the symbol, within level 0 symbols */
+ /* (level 0 symbols are the most common symbols) */
+ offset = (unsigned)HDrandom() % symbol_count[0];
+ sym_com[v] = &symbol_info[0][offset];
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Common symbol #%u = '%s'\n", v, symbol_info[0][offset].name);
+ } /* end for */
+ } /* end if */
+
+ /* Allocate space for 'random' datasets, if any */
+ if(nrandom > 0) {
+ /* Allocate array to hold pointers to symbols for random datasets */
+ if(NULL == (sym_rand = (symbol_info_t **)HDmalloc(sizeof(symbol_info_t *) * nrandom)))
+ goto error;
+
+ /* Determine the random datasets */
+ for(v = 0; v < nrandom; v++) {
+ symbol_info_t *sym; /* Symbol to use */
+
+ /* Determine the symbol, within all symbols */
+ if(NULL == (sym = choose_dataset(NULL, NULL)))
+ goto error;
+ sym_rand[v] = sym;
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Random symbol #%u = '%s'\n", v, sym->name);
+ } /* end for */
+ } /* end if */
+
+ /* Create a dataspace for the record to read */
+ if((mem_sid = H5Screate(H5S_SCALAR)) < 0)
+ goto error;
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Reading records\n");
+
+ /* Get the starting time */
+ start_time = HDtime(NULL);
+ curr_time = start_time;
+
+ /* Create file access property list */
+ if((fapl = h5_fileaccess()) < 0)
+ goto error;
+
+ /*
+ * Set up to open the file with VFD SWMR configured.
+ */
+ /* Enable page buffering */
+ if(H5Pset_page_buffer_size(fapl, 4096, 0, 0) < 0)
+ goto error;
+
+ /* Allocate memory for the configuration structure */
+ if((config = (H5F_vfd_swmr_config_t *)HDcalloc(1, sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ goto error;
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = 4;
+ config->max_lag = 5;
+ config->writer = FALSE;
+ config->md_pages_reserved = 128;
+ HDstrcpy(config->md_file_path, "./my_md_file");
+
+ /* Enable VFD SWMR configuration */
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0)
+ goto error;
+
+ /* Loop over reading records until [at least] the correct # of seconds have passed */
+ while(curr_time < (time_t)(start_time + (time_t)nseconds)) {
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Opening file: %s\n", filename);
+
+ /* Open the file */
+ /* Remove H5E_BEGIN_TRY/END_TRY if you want to see the error stack */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl);
+ } H5E_END_TRY;
+ if(fid < 0) {
+ HDfprintf(stderr, "READER: Error in opening the file: %s\n", filename);
+ goto error;
+ }
+
+ /* Check 'common' datasets, if any */
+ if(ncommon > 0) {
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Checking common symbols\n");
+
+ /* Iterate over common datasets */
+ for(v = 0; v < ncommon; v++) {
+ /* Check common dataset */
+ if(check_dataset(fid, dapl, verbose, sym_com[v]->name, &record, mem_sid) < 0)
+ goto error;
+ HDmemset(&record, 0, sizeof(record));
+ } /* end for */
+ } /* end if */
+
+ /* Check 'random' datasets, if any */
+ if(nrandom > 0) {
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Checking random symbols\n");
+
+ /* Iterate over random datasets */
+ for(v = 0; v < nrandom; v++) {
+ /* Check random dataset */
+ if(check_dataset(fid, dapl, verbose, sym_rand[v]->name, &record, mem_sid) < 0)
+ goto error;
+ HDmemset(&record, 0, sizeof(record));
+ } /* end for */
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Closing file\n");
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ goto error;
+
+ /* Sleep for the appropriate # of seconds */
+ HDsleep(poll_time);
+
+ /* Retrieve the current time */
+ curr_time = HDtime(NULL);
+ } /* end while */
+
+ /* Close the fapl */
+ if(H5Pclose(fapl) < 0)
+ goto error;
+
+ if(config)
+ HDfree(config);
+
+ /* Close the memory dataspace */
+ if(H5Sclose(mem_sid) < 0)
+ goto error;
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Closing datasets\n");
+
+ /* Close 'random' datasets, if any */
+ if(nrandom > 0) {
+ /* Release array holding dataset ID's for random datasets */
+ HDfree(sym_rand);
+ } /* end if */
+
+ /* Close 'common' datasets, if any */
+ if(ncommon > 0) {
+ /* Release array holding dataset ID's for common datasets */
+ HDfree(sym_com);
+ } /* end if */
+
+ return 0;
+
+error:
+ if(config)
+ HDfree(config);
+
+ if(sym_rand)
+ HDfree(sym_rand);
+
+ if(sym_com)
+ HDfree(sym_com);
+
+ H5E_BEGIN_TRY {
+ H5Sclose(mem_sid);
+ H5Fclose(fid);
+ H5Pclose(fapl);
+ H5Pclose(dapl);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* end read_records() */
+
+static void
+usage(void)
+{
+ printf("\n");
+ printf("Usage error!\n");
+ printf("\n");
+ printf("Usage: vfd_swmr_remove_reader [-q] [-s <# of seconds to sleep between\n");
+ printf(" polling>] [-h <# of common symbols to poll>] [-l <# of random symbols\n");
+ printf(" to poll>] [-r <random seed>] <# of seconds to test>\n");
+ printf("\n");
+ printf("Defaults to verbose (no '-q' given), 1 second between polling ('-s 1'),\n");
+ printf("5 common symbols to poll ('-h 5'), 10 random symbols to poll ('-l 10'),\n");
+ printf("and will generate a random seed (no -r given).\n");
+ printf("\n");
+ HDexit(1);
+}
+
+int main(int argc, const char *argv[])
+{
+ long nseconds = 0; /* # of seconds to test */
+ int poll_time = 1; /* # of seconds between polling */
+ int ncommon = 5; /* # of common symbols to poll */
+ int nrandom = 10; /* # of random symbols to poll */
+ unsigned verbose = 1; /* Whether to emit some informational messages */
+ unsigned use_seed = 0; /* Set to 1 if a seed was set on the command line */
+ unsigned random_seed = 0; /* Random # seed */
+ unsigned u; /* Local index variables */
+ int temp;
+
+ /* Parse command line options */
+ if(argc < 2)
+ usage();
+ if(argc > 1) {
+ u = 1;
+ while(u < (unsigned)argc) {
+ if(argv[u][0] == '-') {
+ switch(argv[u][1]) {
+ /* # of common symbols to poll */
+ case 'h':
+ ncommon = HDatoi(argv[u + 1]);
+ if(ncommon < 0)
+ usage();
+ u += 2;
+ break;
+
+ /* # of random symbols to poll */
+ case 'l':
+ nrandom = HDatoi(argv[u + 1]);
+ if(nrandom < 0)
+ usage();
+ u += 2;
+ break;
+
+ /* Be quiet */
+ case 'q':
+ verbose = 0;
+ u++;
+ break;
+
+ /* Random # seed */
+ case 'r':
+ use_seed = 1;
+ temp = HDatoi(argv[u + 1]);
+ if(temp < 0)
+ usage();
+ else
+ random_seed = (unsigned)temp;
+ u += 2;
+ break;
+
+ /* # of seconds between polling */
+ case 's':
+ poll_time = HDatoi(argv[u + 1]);
+ if(poll_time < 0)
+ usage();
+ u += 2;
+ break;
+
+ default:
+ usage();
+ break;
+ } /* end switch */
+ } /* end if */
+ else {
+ /* Get the number of records to append */
+ nseconds = HDatol(argv[u]);
+ if(nseconds <= 0)
+ usage();
+
+ u++;
+ } /* end else */
+ } /* end while */
+ } /* end if */
+ if(nseconds <= 0)
+ usage();
+ if(poll_time >= nseconds)
+ usage();
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "READER: Parameters:\n");
+ HDfprintf(stderr, "\t# of seconds between polling = %d\n", poll_time);
+ HDfprintf(stderr, "\t# of common symbols to poll = %d\n", ncommon);
+ HDfprintf(stderr, "\t# of random symbols to poll = %d\n", nrandom);
+ HDfprintf(stderr, "\t# of seconds to test = %ld\n", nseconds);
+ } /* end if */
+
+ /* Set the random seed */
+ if(0 == use_seed) {
+ struct timeval t;
+ HDgettimeofday(&t, NULL);
+ random_seed = (unsigned)(t.tv_usec);
+ } /* end if */
+ HDsrandom(random_seed);
+ /* ALWAYS emit the random seed for possible debugging */
+ HDfprintf(stderr, "READER: Using reader random seed: %u\n", random_seed);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Generating symbol names\n");
+
+ /* Generate dataset names */
+ if(generate_symbols() < 0) {
+ HDfprintf(stderr, "READER: Error generating symbol names!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Create datatype for creating datasets */
+ if((symbol_tid = create_symbol_datatype()) < 0)
+ return -1;
+
+ /* Reading records from datasets */
+ if(read_records(COMMON_FILENAME, verbose, (unsigned long)nseconds, (unsigned)poll_time, (unsigned)ncommon, (unsigned)nrandom) < 0) {
+ HDfprintf(stderr, "READER: Error reading records from datasets!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Releasing symbols\n");
+
+ /* Clean up the symbols */
+ if(shutdown_symbols() < 0) {
+ HDfprintf(stderr, "READER: Error releasing symbols!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Closing objects\n");
+
+ /* Close objects created */
+ if(H5Tclose(symbol_tid) < 0) {
+ HDfprintf(stderr, "READER: Error closing symbol datatype!\n");
+ HDexit(1);
+ } /* end if */
+
+ return 0;
+}
diff --git a/test/vfd_swmr_remove_writer.c b/test/vfd_swmr_remove_writer.c
new file mode 100644
index 0000000..2ebe96f
--- /dev/null
+++ b/test/vfd_swmr_remove_writer.c
@@ -0,0 +1,426 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: vfd_swmr_remove_writer.c
+ * (copied and modified from swmr_remove_writer.c)
+ *
+ * Purpose: Removes data from a randomly selected subset of the datasets
+ * in the VFD SWMR test file.
+ *
+ * This program is intended to run concurrently with the
+ * vfd_swmr_remove_reader program. It is also run AFTER a sequential
+ * (not concurrent!) invoking of vfd_swmr_writer so the writer
+ * can dump a bunch of data into the datasets. Otherwise,
+ * there wouldn't be much to shrink :)
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/***********/
+/* Headers */
+/***********/
+
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+/****************/
+/* Local Macros */
+/****************/
+
+/* The maximum number of records to remove in one step */
+#define MAX_REMOVE_SIZE 10
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static hid_t open_skeleton(const char *filename, unsigned verbose, unsigned old);
+static int remove_records(hid_t fid, unsigned verbose, unsigned long nshrinks,
+ unsigned long flush_count);
+static void usage(void);
+
+
+/*-------------------------------------------------------------------------
+ * Function: open_skeleton
+ *
+ * Purpose: Opens the SWMR HDF5 file and datasets.
+ *
+ * Parameters: const char *filename
+ * The filename of the SWMR HDF5 file to open
+ *
+ * unsigned verbose
+ * Whether or not to emit verbose console messages
+ *
+ * Return: Success: The file ID of the opened SWMR file
+ * The dataset IDs are stored in a global array
+ *
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static hid_t
+open_skeleton(const char *filename, unsigned verbose,
+ unsigned old H5_ATTR_UNUSED)
+{
+ hid_t fid = -1; /* File ID for new HDF5 file */
+ hid_t fapl = -1; /* File access property list */
+ hid_t sid = -1; /* Dataspace ID */
+ hsize_t dim[2]; /* Dataspace dimensions */
+ unsigned u, v; /* Local index variable */
+ H5F_vfd_swmr_config_t *config = NULL; /* Configuration for VFD SWMR */
+
+ HDassert(filename);
+
+ /* Create file access property list */
+ if((fapl = h5_fileaccess()) < 0)
+ goto error;
+
+ /* FOR NOW: set to use latest format, the "old" parameter is not used */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ goto error;
+
+ /*
+ * Set up to open the file with VFD SWMR configured.
+ */
+
+ /* Enable page buffering */
+ if(H5Pset_page_buffer_size(fapl, 4096, 0, 0) < 0)
+ goto error;
+
+ /* Allocate memory for the configuration structure */
+ if((config = (H5F_vfd_swmr_config_t *)HDcalloc(1, sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ goto error;
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = 4;
+ config->max_lag = 5;
+ config->writer = TRUE;
+ config->md_pages_reserved = 128;
+ HDstrcpy(config->md_file_path, "./my_md_file");
+
+ /* Enable VFD SWMR configuration */
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0)
+ goto error;
+
+ /* Open the file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ goto error;
+
+ /* Close file access property list */
+ if(H5Pclose(fapl) < 0)
+ goto error;
+
+ if(config)
+ HDfree(config);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Opening datasets\n");
+
+ /* Open the datasets */
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++) {
+ if((symbol_info[u][v].dsid = H5Dopen2(fid, symbol_info[u][v].name, H5P_DEFAULT)) < 0)
+ goto error;
+ if((sid = H5Dget_space(symbol_info[u][v].dsid)) < 0)
+ goto error;
+ if(2 != H5Sget_simple_extent_ndims(sid))
+ goto error;
+ if(H5Sget_simple_extent_dims(sid, dim, NULL) < 0)
+ goto error;
+ symbol_info[u][v].nrecords = dim[1];
+
+ if(H5Sclose(sid) < 0)
+ goto error;
+ } /* end for */
+
+ return fid;
+
+error:
+ if(config)
+ HDfree(config);
+
+ H5E_BEGIN_TRY {
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ H5Dclose(symbol_info[u][v].dsid);
+ H5Sclose(sid);
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* open_skeleton() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: remove_records
+ *
+ * Purpose: Removes a specified number of records from random datasets in
+ * the SWMR test file.
+ *
+ * Parameters: hid_t fid
+ * The file ID of the SWMR HDF5 file
+ *
+ * unsigned verbose
+ * Whether or not to emit verbose console messages
+ *
+ * unsigned long nshrinks
+ * # of records to remove from the datasets
+ *
+ * unsigned long flush_count
+ * # of records to write before flushing the file to disk
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+remove_records(hid_t fid, unsigned verbose, unsigned long nshrinks, unsigned long flush_count)
+{
+ unsigned long shrink_to_flush; /* # of removals before flush */
+ hsize_t dim[2] = {1,0}; /* Dataspace dimensions */
+ unsigned long u, v; /* Local index variables */
+
+ HDassert(fid >= 0);
+
+ /* Remove records from random datasets, according to frequency distribution */
+ shrink_to_flush = flush_count;
+ for(u = 0; u < nshrinks; u++) {
+ symbol_info_t *symbol; /* Symbol to remove record from */
+ hsize_t remove_size; /* Size to reduce dataset dimension by */
+
+ /* Get a random dataset, according to the symbol distribution */
+ symbol = choose_dataset(NULL, NULL);
+
+ /* Shrink the dataset's dataspace */
+ remove_size = (hsize_t)HDrandom() % MAX_REMOVE_SIZE + 1;
+ if(remove_size > symbol->nrecords)
+ symbol->nrecords = 0;
+ else
+ symbol->nrecords -= remove_size;
+ dim[1] = symbol->nrecords;
+ if(H5Dset_extent(symbol->dsid, dim) < 0)
+ goto error;
+
+ /* Check for flushing file */
+ if(flush_count > 0) {
+ /* Decrement count of records to write before flushing */
+ shrink_to_flush--;
+
+ /* Check for counter being reached */
+ if(0 == shrink_to_flush) {
+#ifdef TEMP_OUT
+ /* Flush contents of file */
+ if(H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0)
+ return -1;
+#endif
+
+ /* Reset flush counter */
+ shrink_to_flush = flush_count;
+ } /* end if */
+ } /* end if */
+ } /* end for */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Closing datasets\n");
+
+ /* Close the datasets */
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ if(H5Dclose(symbol_info[u][v].dsid) < 0)
+ goto error;
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ H5Dclose(symbol_info[u][v].dsid);
+
+ } H5E_END_TRY;
+
+ return -1;
+} /* remove_records() */
+
+static void
+usage(void)
+{
+ printf("\n");
+ printf("Usage error!\n");
+ printf("\n");
+ printf("Usage: swmr_remove_writer [-q] [-o] [-f <# of shrinks between flushing\n");
+ printf(" file contents>] [-r <random seed>] <# of shrinks>\n");
+ printf("\n");
+ printf("<# of shrinks between flushing file contents> should be 0 (for no\n");
+ printf("flushing) or between 1 and (<# of shrinks> - 1)\n");
+ printf("\n");
+ printf("Defaults to verbose (no '-q' given), latest format when opening file (no '-o' given),\n");
+ printf("flushing every 1000 shrinks ('-f 1000'), and will generate a random seed (no -r given).\n");
+ printf("\n");
+ HDexit(1);
+} /* usage() */
+
+int main(int argc, const char *argv[])
+{
+ sigset_t oldset;
+ hid_t fid; /* File ID for file opened */
+ long nshrinks = 0; /* # of times to shrink the dataset */
+ long flush_count = 1000; /* # of records to write between flushing file */
+ unsigned verbose = 1; /* Whether to emit some informational messages */
+ unsigned old = 0; /* Whether to use non-latest-format when opening file */
+ unsigned use_seed = 0; /* Set to 1 if a seed was set on the command line */
+ unsigned random_seed = 0; /* Random # seed */
+ unsigned u; /* Local index variable */
+ int temp;
+
+ block_signals(&oldset);
+
+ /* Parse command line options */
+ if(argc < 2)
+ usage();
+ if(argc > 1) {
+ u = 1;
+ while(u < (unsigned)argc) {
+ if(argv[u][0] == '-') {
+ switch(argv[u][1]) {
+ /* # of records to write between flushing file */
+ case 'f':
+ flush_count = HDatol(argv[u + 1]);
+ if(flush_count < 0)
+ usage();
+ u += 2;
+ break;
+
+ /* Be quiet */
+ case 'q':
+ verbose = 0;
+ u++;
+ break;
+
+ /* Random # seed */
+ case 'r':
+ use_seed = 1;
+ temp = HDatoi(argv[u + 1]);
+ random_seed = (unsigned)temp;
+ u += 2;
+ break;
+
+ /* Use non-latest-format when opening file */
+ case 'o':
+ old = 1;
+ u++;
+ break;
+
+ default:
+ usage();
+ break;
+ } /* end switch */
+ } /* end if */
+ else {
+ /* Get the number of records to append */
+ nshrinks = HDatol(argv[u]);
+ if(nshrinks <= 0)
+ usage();
+
+ u++;
+ } /* end else */
+ } /* end while */
+ } /* end if */
+ if(nshrinks <= 0)
+ usage();
+ if(flush_count >= nshrinks)
+ usage();
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "WRITER: Parameters:\n");
+ HDfprintf(stderr, "\t# of shrinks between flushes = %ld\n", flush_count);
+ HDfprintf(stderr, "\t# of shrinks = %ld\n", nshrinks);
+ } /* end if */
+
+ /* Set the random seed */
+ if(0 == use_seed) {
+ struct timeval t;
+ HDgettimeofday(&t, NULL);
+ random_seed = (unsigned)(t.tv_usec);
+ } /* end if */
+ HDsrandom(random_seed);
+ /* ALWAYS emit the random seed for possible debugging */
+ HDfprintf(stderr, "WRITER: Using writer random seed: %u\n", random_seed);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Generating symbol names\n");
+
+ /* Generate dataset names */
+ if(generate_symbols() < 0)
+ return -1;
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "WRITER: Opening skeleton file: %s\n",
+ COMMON_FILENAME);
+ }
+
+ /* Open file skeleton */
+ if((fid = open_skeleton(COMMON_FILENAME, verbose, old)) < 0) {
+ HDfprintf(stderr, "WRITER: Error opening skeleton file!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Send a message to indicate "H5Fopen" is complete--releasing the file lock */
+ h5_send_message(WRITER_MESSAGE, NULL, NULL);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Removing records\n");
+
+ /* Remove records from datasets */
+ if(remove_records(fid, verbose, (unsigned long)nshrinks, (unsigned long)flush_count) < 0) {
+ HDfprintf(stderr, "WRITER: Error removing records from datasets!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Releasing symbols\n");
+
+ /* Clean up the symbols */
+ if(shutdown_symbols() < 0) {
+ HDfprintf(stderr, "WRITER: Error releasing symbols!\n");
+ HDexit(1);
+ } /* end if */
+
+ await_signal(fid);
+
+ restore_signals(&oldset);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Closing objects\n");
+
+ /* Close objects opened */
+ if(H5Fclose(fid) < 0) {
+ HDfprintf(stderr, "WRITER: Error closing file!\n");
+ HDexit(1);
+ } /* end if */
+
+ return 0;
+} /* main() */
diff --git a/test/vfd_swmr_sparse_reader.c b/test/vfd_swmr_sparse_reader.c
new file mode 100644
index 0000000..83fb886
--- /dev/null
+++ b/test/vfd_swmr_sparse_reader.c
@@ -0,0 +1,535 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: vfd_swmr_sparse_reader.c
+ * (copied and modified from swmr_sparse_reader.c)
+ *
+ * Purpose: Reads data from a randomly selected subset of the datasets
+ * in the VFD SWMR test file. Unlike the regular reader, these
+ * datasets will be shrinking.
+ *
+ * This program is intended to run concurrently with the
+ * vfd_swmr_sparse_writer program.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/***********/
+/* Headers */
+/***********/
+
+#include <inttypes.h> /* for PRIu64 */
+
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+/****************/
+/* Local Macros */
+/****************/
+
+#define TIMEOUT 30
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+static hid_t symbol_tid = (-1);
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static int check_dataset(hid_t fid, unsigned verbose, const symbol_info_t *symbol,
+ symbol_t *record, hid_t rec_sid);
+static int read_records(const char *filename, unsigned verbose, unsigned long nrecords,
+ unsigned poll_time, unsigned reopen_count);
+static void usage(void);
+
+
+/*-------------------------------------------------------------------------
+ * Function: check_dataset
+ *
+ * Purpose: For a given dataset, checks to make sure that the stated
+ * and actual sizes are the same. If they are not, then
+ * we have an inconsistent dataset due to a SWMR error.
+ *
+ * Parameters: hid_t fid
+ * The SWMR test file's ID.
+ *
+ * unsigned verbose
+ * Whether verbose console output is desired.
+ *
+ * const symbol_info_t *symbol
+ * The dataset from which to read (the ID is in the struct).
+ * Must be pre-allocated.
+ *
+ * symbol_t *record
+ * Memory for the record. Must be pre-allocated.
+ *
+ * hid_t rec_sid
+ * The memory dataspace for access. It's always the same so
+ * there is no need to re-create it every time this function
+ * is called.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+check_dataset(hid_t fid, unsigned verbose, const symbol_info_t *symbol, symbol_t *record,
+ hid_t rec_sid)
+{
+ hid_t dsid = -1; /* Dataset ID */
+ hid_t file_sid = -1; /* Dataset's space ID */
+ hsize_t start[2] = {0, 0}; /* Hyperslab selection values */
+ hsize_t count[2] = {1, 1}; /* Hyperslab selection values */
+
+ HDassert(fid >= 0);
+ HDassert(symbol);
+ HDassert(record);
+ HDassert(rec_sid >= 0);
+
+ /* Open dataset for symbol */
+ if((dsid = H5Dopen2(fid, symbol->name, H5P_DEFAULT)) < 0)
+ goto error;
+
+ /* Get the dataset's dataspace */
+ if((file_sid = H5Dget_space(dsid)) < 0)
+ goto error;
+
+ /* Choose the random record in the dataset (will be the same as chosen by
+ * the writer) */
+ start[1] = (hsize_t)HDrandom() % symbol->nrecords;
+ if(H5Sselect_hyperslab(file_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0)
+ goto error;
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Symbol = '%s', nrecords = %Hu, name = %s, location = %Hu, %Hu\n", \
+ symbol->name, symbol->nrecords, symbol->name, start[0], start[1]);
+
+ /* Read record from dataset */
+ record->rec_id = UINT64_MAX;
+ if(H5Dread(dsid, symbol_tid, rec_sid, file_sid, H5P_DEFAULT, record) < 0)
+ goto error;
+
+ /* Verify record value */
+ if(record->rec_id != start[1]) {
+ HDfprintf(stderr, "*** READER: ERROR ***\n");
+ HDfprintf(stderr, "Incorrect record value!\n");
+ HDfprintf(stderr, "Symbol = '%s', location = %Hu, %Hu, record->rec_id = %" PRIu64 "\n", symbol->name, start[0], start[1], record->rec_id);
+ goto error;
+ } /* end if */
+
+ /* Close the dataset's dataspace */
+ if(H5Sclose(file_sid) < 0)
+ goto error;
+
+ /* Close dataset for symbol */
+ if(H5Dclose(dsid) < 0)
+ goto error;
+
+ return 0;
+
+error:
+
+ H5E_BEGIN_TRY {
+ H5Sclose(file_sid);
+ H5Dclose(dsid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* end check_dataset() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: read_records
+ *
+ * Purpose: For a given dataset, checks to make sure that the stated
+ * and actual sizes are the same. If they are not, then
+ * we have an inconsistent dataset due to a SWMR error.
+ *
+ * Parameters: const char *filename
+ * The SWMR test file's name.
+ *
+ * unsigned verbose
+ * Whether verbose console output is desired.
+ *
+ * unsigned long nrecords
+ * The total number of records to read.
+ *
+ * unsigned poll_time
+ * The amount of time to sleep (s).
+ *
+ * unsigned reopen_count
+ *
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+read_records(const char *filename, unsigned verbose, unsigned long nrecords,
+ unsigned poll_time, unsigned reopen_count)
+{
+ hid_t fid = H5I_INVALID_HID;
+ hid_t aid = H5I_INVALID_HID;
+ time_t start_time; /* Starting time */
+ hid_t mem_sid = H5I_INVALID_HID;
+ symbol_t record; /* The record to add to the dataset */
+ unsigned seed; /* Seed for random number generator */
+ unsigned iter_to_reopen = reopen_count; /* # of iterations until reopen */
+ unsigned long u; /* Local index variable */
+ hid_t fapl = H5I_INVALID_HID;
+ H5F_vfd_swmr_config_t *config = NULL; /* Configuration for VFD SWMR */
+
+ HDassert(filename);
+ HDassert(poll_time != 0);
+
+ /* Create file access property list */
+ if((fapl = h5_fileaccess()) < 0)
+ goto error;
+
+ if(H5Pset_fclose_degree(fapl, H5F_CLOSE_SEMI) < 0)
+ goto error;
+
+ /*
+ * Set up to open the file with VFD SWMR configured.
+ */
+ /* Enable page buffering */
+ if(H5Pset_page_buffer_size(fapl, 4096, 0, 0) < 0)
+ goto error;
+
+ /* Allocate memory for the configuration structure */
+ if((config = HDcalloc(1, sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ goto error;
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = 4;
+ config->max_lag = 5;
+ config->writer = FALSE;
+ config->md_pages_reserved = 128;
+ HDstrcpy(config->md_file_path, "./my_md_file");
+
+ /* Enable VFD SWMR configuration */
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0)
+ goto error;
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Opening file: %s\n", filename);
+
+ /* Open the file */
+ /* Remove H5E_BEGIN_TRY/END_TRY to see the error stack if error */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl);
+ } H5E_END_TRY;
+ if(fid < 0) {
+ HDfprintf(stderr, "READER: Error in opening the file: %s\n", filename);
+ goto error;
+ } else
+ HDfprintf(stderr, "READER: SUCCESS in opening the file: %s\n", filename);
+
+ /* Seed the random number generator with the attribute in the file */
+ if((aid = H5Aopen(fid, "seed", H5P_DEFAULT)) < 0)
+ goto error;
+ if(H5Aread(aid, H5T_NATIVE_UINT, &seed) < 0)
+ goto error;
+ if(H5Aclose(aid) < 0)
+ goto error;
+ HDsrandom(seed);
+
+ /* Reset the record */
+ /* (record's 'info' field might need to change for each record written, also) */
+ HDmemset(&record, 0, sizeof(record));
+
+ /* Create a dataspace for the record to read */
+ if((mem_sid = H5Screate(H5S_SCALAR)) < 0)
+ goto error;
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Reading records\n");
+
+ /* Read records */
+ for(u = 0; u < nrecords; u++) {
+ unsigned level, offset;
+ symbol_info_t *symbol = NULL; /* Symbol (dataset) */
+ htri_t attr_exists; /* Whether the sequence number attribute exists */
+ unsigned long file_u; /* Attribute sequence number (writer's "u") */
+
+ /* Get a random dataset, according to the symbol distribution */
+ symbol = choose_dataset(&level, &offset);
+
+ /* Fill in "nrecords" field. Note that this depends on the writer
+ * using the same algorithm and "nrecords" */
+ symbol->nrecords = nrecords / 5;
+
+ /* Get the starting time */
+ if ((start_time = HDtime(NULL)) == (time_t)-1) {
+ fprintf(stderr, "READER: could not read time.\n");
+ goto error;
+ }
+
+ /* Wait until we can read the dataset */
+ for (;;) {
+ if((attr_exists = H5Aexists_by_name(fid, symbol->name, "seq", H5P_DEFAULT)) < 0)
+ goto error;
+
+ if(attr_exists) {
+ if((aid = H5Aopen_by_name(fid, symbol->name, "seq",
+ H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ goto error;
+ if(H5Aread(aid, H5T_NATIVE_ULONG, &file_u) < 0)
+ goto error;
+ if(H5Aclose(aid) < 0)
+ goto error;
+
+ if(file_u >= u)
+ break;
+ }
+
+ if(HDtime(NULL) >= (time_t)(start_time + (time_t)TIMEOUT)) {
+ HDfprintf(stderr,
+ "READER: Reader timed at record %lu level %u offset %u",
+ u, level, offset);
+ if (attr_exists) {
+ HDfprintf(stderr, ", read sequence %lu\n", file_u);
+ } else {
+ HDfprintf(stderr, ", read no sequence\n");
+ HDfprintf(stderr, ", read no sequence\n");
+ }
+ goto error;
+ }
+
+ HDsleep(poll_time);
+
+ if(verbose)
+ HDfprintf(stderr, "READER: Reopening file (do while loop): %s\n", filename);
+
+ if(print_metadata_retries_info(fid) < 0)
+ HDfprintf(stderr, "READER: Warning: could not obtain metadata retries info\n");
+
+ if(H5Fclose(fid) < 0)
+ goto error;
+
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl);
+ } H5E_END_TRY;
+ if(fid < 0) {
+ HDfprintf(stderr, "READER: Error in reopening the file (do while loop): %s\n", filename);
+ goto error;
+ }
+ iter_to_reopen = reopen_count;
+ }
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Checking dataset %lu\n", u);
+
+ /* Check dataset */
+ if(check_dataset(fid, verbose, symbol, &record, mem_sid) < 0)
+ goto error;
+ HDmemset(&record, 0, sizeof(record));
+
+ /* Check for reopen */
+ iter_to_reopen--;
+ if(iter_to_reopen == 0) {
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Reopening file (iter_to_reopen): %s\n", filename);
+
+ /* Retrieve and print the collection of metadata read retries */
+ if(print_metadata_retries_info(fid) < 0)
+ HDfprintf(stderr, "READER: Warning: could not obtain metadata retries info\n");
+
+ /* Reopen the file */
+ if(H5Fclose(fid) < 0)
+ goto error;
+
+ /* Remove H5E_BEGIN_TRY/END_TRY to see the error stack if error */
+// H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl);
+ // } H5E_END_TRY;
+ if(fid < 0) {
+ HDfprintf(stderr, "READER: Error in reopening the file (iter_to_reopen): %s\n", filename);
+ goto error;
+ }
+
+ iter_to_reopen = reopen_count;
+ } /* end if */
+ } /* end while */
+
+ /* Retrieve and print the collection of metadata read retries */
+ if(print_metadata_retries_info(fid) < 0)
+ HDfprintf(stderr, "READER: Warning: could not obtain metadata retries info\n");
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ goto error;
+
+ /* Close the memory dataspace */
+ if(H5Sclose(mem_sid) < 0)
+ goto error;
+
+ /* Close the file access property list */
+ if(H5Pclose(fapl) < 0)
+ goto error;
+
+ if(config)
+ HDfree(config);
+
+ return 0;
+
+error:
+ if(config)
+ HDfree(config);
+
+ H5E_BEGIN_TRY {
+ H5Aclose(aid);
+ H5Sclose(mem_sid);
+ H5Fclose(fid);
+ H5Pclose(fapl);
+ } H5E_END_TRY;
+
+ return -1;
+} /* end read_records() */
+
+static void
+usage(void)
+{
+ printf("\n");
+ printf("Usage error!\n");
+ printf("\n");
+ printf("Usage: vfd_swmr_sparse_reader [-q] [-s <# of seconds to wait for writer>]\n");
+ printf(" [-n <# of reads between reopens>] <# of records>\n");
+ printf("\n");
+ printf("Defaults to verbose (no '-q' given), 1 second wait ('-s 1') and 1 read\n");
+ printf("between reopens ('-r 1')\n");
+ printf("\n");
+ printf("Note that the # of records *must* be the same as that supplied to\n");
+ printf("vfd_swmr_sparse_writer\n");
+ printf("\n");
+ HDexit(1);
+} /* end usage() */
+
+int main(int argc, const char *argv[])
+{
+ long nrecords = 0; /* # of records to read */
+ int poll_time = 1; /* # of seconds to sleep when waiting for writer */
+ int reopen_count = 1; /* # of reads between reopens */
+ unsigned verbose = 1; /* Whether to emit some informational messages */
+ unsigned u; /* Local index variables */
+
+ /* Parse command line options */
+ if(argc < 2)
+ usage();
+ if(argc > 1) {
+ u = 1;
+ while(u < (unsigned)argc) {
+ if(argv[u][0] == '-') {
+ switch(argv[u][1]) {
+ /* # of reads between reopens */
+ case 'n':
+ reopen_count = HDatoi(argv[u + 1]);
+ if(reopen_count < 0)
+ usage();
+ u += 2;
+ break;
+
+ /* Be quiet */
+ case 'q':
+ verbose = 0;
+ u++;
+ break;
+
+ /* # of seconds between polling */
+ case 's':
+ poll_time = HDatoi(argv[u + 1]);
+ if(poll_time < 0)
+ usage();
+ u += 2;
+ break;
+
+ default:
+ usage();
+ break;
+ } /* end switch */
+ } /* end if */
+ else {
+ /* Get the number of records to read */
+ nrecords = HDatol(argv[u]);
+ if(nrecords <= 0)
+ usage();
+
+ u++;
+ } /* end else */
+ } /* end while */
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "READER: Parameters:\n");
+ HDfprintf(stderr, "\t# of seconds between polling = %d\n", poll_time);
+ HDfprintf(stderr, "\t# of reads between reopens = %d\n", reopen_count);
+ HDfprintf(stderr, "\t# of records to read = %ld\n", nrecords);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Generating symbol names\n");
+
+ /* Generate dataset names */
+ if(generate_symbols() < 0) {
+ HDfprintf(stderr, "READER: Error generating symbol names!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Create datatype for creating datasets */
+ if((symbol_tid = create_symbol_datatype()) < 0) {
+ HDfprintf(stderr, "READER: Error creating symbol datatype!\n");
+ HDexit(1);
+ }
+
+ /* Reading records from datasets */
+ if(read_records(COMMON_FILENAME, verbose, (unsigned long) nrecords, (unsigned)poll_time, (unsigned)reopen_count) < 0) {
+ HDfprintf(stderr, "READER: Error reading records from datasets!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Releasing symbols\n");
+
+ /* Clean up the symbols */
+ if(shutdown_symbols() < 0) {
+ HDfprintf(stderr, "READER: Error releasing symbols!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "READER: Closing objects\n");
+
+ /* Close objects created */
+ if(H5Tclose(symbol_tid) < 0) {
+ HDfprintf(stderr, "READER: Error closing symbol datatype!\n");
+ HDexit(1);
+ } /* end if */
+
+ return 0;
+} /* main() */
diff --git a/test/vfd_swmr_sparse_writer.c b/test/vfd_swmr_sparse_writer.c
new file mode 100644
index 0000000..5722558
--- /dev/null
+++ b/test/vfd_swmr_sparse_writer.c
@@ -0,0 +1,488 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+ /*-------------------------------------------------------------------------
+ *
+ * Created: vfd_swmr_sparse_writer.c
+ * (copied and modified from swmr_sparse_writer.c)
+ *
+ * Purpose: Writes data to a randomly selected subset of the datasets
+ * in the VFD_SWMR test file.
+ *
+ * This program is intended to run concurrently with the
+ * vfd_swmr_sparse_reader program.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/***********/
+/* Headers */
+/***********/
+#include <err.h>
+#include <signal.h>
+
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+/****************/
+/* Local Macros */
+/****************/
+
+#ifdef OUT
+#define BUSY_WAIT 100000
+#endif /* OUT */
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static hid_t open_skeleton(const char *filename, unsigned verbose);
+static int add_records(hid_t fid, unsigned verbose, unsigned long nrecords,
+ unsigned long flush_count);
+static void usage(void);
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: open_skeleton
+ *
+ * Purpose: Opens the SWMR HDF5 file and datasets.
+ *
+ * Parameters: const char *filename
+ * The filename of the SWMR HDF5 file to open
+ *
+ * unsigned verbose
+ * Whether or not to emit verbose console messages
+ *
+ * Return: Success: The file ID of the opened SWMR file
+ * The dataset IDs are stored in a global array
+ *
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static hid_t
+open_skeleton(const char *filename, unsigned verbose)
+{
+ hid_t fid = -1; /* File ID for new HDF5 file */
+ hid_t fapl = -1; /* File access property list */
+ hid_t aid = -1; /* Attribute ID */
+ unsigned seed; /* Seed for random number generator */
+ unsigned u, v; /* Local index variable */
+ H5F_vfd_swmr_config_t *config = NULL; /* Configuration for VFD SWMR */
+
+ HDassert(filename);
+
+ /* Create file access property list */
+ if((fapl = h5_fileaccess()) < 0)
+ goto error;
+
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ goto error;
+
+ /*
+ * Set up to open the file with VFD SWMR configured.
+ */
+
+ /* Enable page buffering */
+ if(H5Pset_page_buffer_size(fapl, 4096, 0, 0) < 0)
+ goto error;
+
+ /* Allocate memory for the configuration structure */
+ if((config = (H5F_vfd_swmr_config_t *)HDcalloc(1, sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ goto error;
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = 4;
+ config->max_lag = 5;
+ config->writer = TRUE;
+ config->md_pages_reserved = 128;
+ HDstrcpy(config->md_file_path, "./my_md_file");
+
+ /* Enable VFD SWMR configuration */
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0)
+ goto error;
+
+ /* Open the file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ goto error;
+
+ /* Close file access property list */
+ if(H5Pclose(fapl) < 0)
+ goto error;
+
+ if(config)
+ HDfree(config);
+
+ /* Emit informational message */
+ if(verbose)
+ fprintf(stderr, "WRITER: Opening datasets\n");
+
+ /* Seed the random number generator with the attribute in the file */
+ if((aid = H5Aopen(fid, "seed", H5P_DEFAULT)) < 0)
+ goto error;
+ if(H5Aread(aid, H5T_NATIVE_UINT, &seed) < 0)
+ goto error;
+ if(H5Aclose(aid) < 0)
+ goto error;
+ HDsrandom(seed);
+
+ /* Open the datasets */
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++) {
+ if((symbol_info[u][v].dsid = H5Dopen2(fid, symbol_info[u][v].name, H5P_DEFAULT)) < 0)
+ return(-1);
+ symbol_info[u][v].nrecords = 0;
+ } /* end for */
+
+ return fid;
+
+error:
+ if(config)
+ HDfree(config);
+
+ H5E_BEGIN_TRY {
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ H5Dclose(symbol_info[u][v].dsid);
+ H5Aclose(aid);
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* open_skeleton() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: add_records
+ *
+ * Purpose: Writes a specified number of records to random datasets in
+ * the SWMR test file.
+ *
+ * Parameters: hid_t fid
+ * The file ID of the SWMR HDF5 file
+ *
+ * unsigned verbose
+ * Whether or not to emit verbose console messages
+ *
+ * unsigned long nrecords
+ * # of records to write to the datasets
+ *
+ * unsigned long flush_count
+ * # of records to write before flushing the file to disk
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+add_records(hid_t fid, unsigned verbose, unsigned long nrecords, unsigned long flush_count)
+{
+ hid_t tid = -1; /* Datatype ID for records */
+ hid_t mem_sid = -1; /* Memory dataspace ID */
+ hid_t file_sid = -1; /* Dataset's space ID */
+ hid_t aid = -1; /* Attribute ID */
+ hsize_t start[2] = {0, 0}; /* Hyperslab selection values */
+ hsize_t count[2] = {1, 1}; /* Hyperslab selection values */
+ symbol_t record; /* The record to add to the dataset */
+ unsigned long rec_to_flush; /* # of records left to write before flush */
+#ifdef OUT
+ volatile int dummy; /* Dummy varialbe for busy sleep */
+#endif /* OUT */
+ hsize_t dim[2] = {1,0}; /* Dataspace dimensions */
+ unsigned long u, v; /* Local index variables */
+
+ HDassert(fid >= 0);
+
+ /* Reset the record */
+ /* (record's 'info' field might need to change for each record written, also) */
+ HDmemset(&record, 0, sizeof(record));
+
+ /* Create a dataspace for the record to add */
+ if((mem_sid = H5Screate(H5S_SCALAR)) < 0)
+ goto error;
+
+ /* Create datatype for appending records */
+ if((tid = create_symbol_datatype()) < 0)
+ goto error;
+
+ /* Add records to random datasets, according to frequency distribution */
+ rec_to_flush = flush_count;
+ for(u = 0; u < nrecords; u++) {
+ symbol_info_t *symbol; /* Symbol to write record to */
+
+ /* Get a random dataset, according to the symbol distribution */
+ symbol = choose_dataset(NULL, NULL);
+
+ /* If this is the first time the dataset has been opened, extend it and
+ * add the sequence attribute */
+ if(symbol->nrecords == 0) {
+ symbol->nrecords = nrecords / 5;
+ dim[1] = symbol->nrecords;
+
+ if(H5Dset_extent(symbol->dsid, dim) < 0)
+ goto error;
+
+ if((file_sid = H5Screate(H5S_SCALAR)) < 0)
+ goto error;
+ if((aid = H5Acreate2(symbol->dsid, "seq", H5T_NATIVE_ULONG, file_sid, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ goto error;
+ if(H5Sclose(file_sid) < 0)
+ goto error;
+ } else if ((aid = H5Aopen(symbol->dsid, "seq", H5P_DEFAULT)) < 0)
+ goto error;
+
+ /* Get the coordinate to write */
+ start[1] = (hsize_t)HDrandom() % symbol->nrecords;
+
+ /* Set the record's ID (equal to its position) */
+ record.rec_id = start[1];
+
+ /* Get the dataset's dataspace */
+ if((file_sid = H5Dget_space(symbol->dsid)) < 0)
+ goto error;
+
+ /* Choose a random record in the dataset */
+ if(H5Sselect_hyperslab(file_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0)
+ goto error;
+
+ /* Write record to the dataset */
+ if(H5Dwrite(symbol->dsid, tid, mem_sid, file_sid, H5P_DEFAULT, &record) < 0)
+ goto error;
+
+ /* Write the sequence number attribute. Since we synchronize the random
+ * number seed, the readers will always generate the same sequence of
+ * randomly chosen datasets and offsets. Therefore, and because of the
+ * flush dependencies on the object header, the reader will be
+ * guaranteed to see the written data if the sequence attribute is >=u.
+ */
+ if(H5Awrite(aid, H5T_NATIVE_ULONG, &u) < 0)
+ goto error;
+
+ /* Close the attribute */
+ if(H5Aclose(aid) < 0)
+ goto error;
+
+ /* Close the dataset's dataspace */
+ if(H5Sclose(file_sid) < 0)
+ goto error;
+
+ /* Check for flushing file */
+ if(flush_count > 0) {
+ /* Decrement count of records to write before flushing */
+ rec_to_flush--;
+
+ /* Check for counter being reached */
+ if(0 == rec_to_flush) {
+#ifdef TEMP_OUT
+ /* Flush contents of file */
+ if(H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0)
+ return -1;
+#endif
+
+ /* Reset flush counter */
+ rec_to_flush = flush_count;
+ } /* end if */
+ } /* end if */
+
+#ifdef OUT
+ /* Busy wait, to let readers catch up */
+ /* If this is removed, also remove the BUSY_WAIT symbol
+ * at the top of the file.
+ */
+ dummy = 0;
+ for(v=0; v<BUSY_WAIT; v++)
+ dummy++;
+ if((unsigned long)dummy != v)
+ return -1;
+#endif /* OUT */
+
+ } /* end for */
+
+ /* Close the memory dataspace */
+ if(H5Sclose(mem_sid) < 0)
+ goto error;
+
+ /* Close the datatype */
+ if(H5Tclose(tid) < 0)
+ goto error;
+
+ /* Emit informational message */
+ if(verbose)
+ fprintf(stderr, "WRITER: Closing datasets\n");
+
+ /* Close the datasets */
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ if(H5Dclose(symbol_info[u][v].dsid) < 0)
+ goto error;
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Sclose(mem_sid);
+ H5Sclose(file_sid);
+ H5Tclose(tid);
+ H5Aclose(aid);
+
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ H5Dclose(symbol_info[u][v].dsid);
+
+ } H5E_END_TRY;
+
+ return -1;
+} /* add_records() */
+
+static void
+usage(void)
+{
+ printf("\n");
+ printf("Usage error!\n");
+ printf("\n");
+ printf("Usage: vfd_swmr_sparse_writer [-q] [-f <# of records to write between\n");
+ printf(" flushing file contents>] <# of records>\n");
+ printf("\n");
+ printf("<# of records to write between flushing file contents> should be 0\n");
+ printf("(for no flushing) or between 1 and (<# of records> - 1)\n");
+ printf("\n");
+ printf("Defaults to verbose (no '-q' given) and flushing every 1000 records\n");
+ printf("('-f 1000')\n");
+ printf("\n");
+ HDexit(1);
+} /* usage() */
+
+int main(int argc, const char *argv[])
+{
+ sigset_t oldset;
+ hid_t fid; /* File ID for file opened */
+ long nrecords = 0; /* # of records to append */
+ long flush_count = 1000; /* # of records to write between flushing file */
+ unsigned verbose = 1; /* Whether to emit some informational messages */
+ unsigned u; /* Local index variable */
+
+ block_signals(&oldset);
+
+ /* Parse command line options */
+ if(argc < 2)
+ usage();
+ if(argc > 1) {
+ u = 1;
+ while(u < (unsigned)argc) {
+ if(argv[u][0] == '-') {
+ switch(argv[u][1]) {
+ /* # of records to write between flushing file */
+ case 'f':
+ flush_count = HDatol(argv[u + 1]);
+ if(flush_count < 0)
+ usage();
+ u += 2;
+ break;
+
+ /* Be quiet */
+ case 'q':
+ verbose = 0;
+ u++;
+ break;
+
+ default:
+ usage();
+ break;
+ } /* end switch */
+ } /* end if */
+ else {
+ /* Get the number of records to append */
+ nrecords = HDatol(argv[u]);
+ if(nrecords <= 0)
+ usage();
+
+ u++;
+ } /* end else */
+ } /* end while */
+ } /* end if */
+ if(nrecords <= 0)
+ usage();
+ if(flush_count >= nrecords)
+ usage();
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "WRITER: Parameters:\n");
+ HDfprintf(stderr, "\t# of records between flushes = %ld\n", flush_count);
+ HDfprintf(stderr, "\t# of records to write = %ld\n", nrecords);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Generating symbol names\n");
+
+ /* Generate dataset names */
+ if(generate_symbols() < 0)
+ return -1;
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(stderr, "WRITER: Opening skeleton file: %s\n",
+ COMMON_FILENAME);
+ }
+
+ /* Open file skeleton */
+ if((fid = open_skeleton(COMMON_FILENAME, verbose)) < 0) {
+ HDfprintf(stderr, "WRITER: Error opening skeleton file!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Send a message to indicate "H5Fopen" is complete--releasing the file lock */
+ h5_send_message(WRITER_MESSAGE, NULL, NULL);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Adding records\n");
+
+ /* Append records to datasets */
+ if(add_records(fid, verbose, (unsigned long)nrecords, (unsigned long)flush_count) < 0) {
+ HDfprintf(stderr, "WRITER: Error appending records to datasets!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Releasing symbols\n");
+
+ /* Clean up the symbols */
+ if(shutdown_symbols() < 0) {
+ HDfprintf(stderr, "WRITER: Error releasing symbols!\n");
+ HDexit(1);
+ } /* end if */
+
+ await_signal(fid);
+
+ restore_signals(&oldset);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(stderr, "WRITER: Closing objects/file\n");
+
+ /* Close objects opened */
+ if(H5Fclose(fid) < 0) {
+ HDfprintf(stderr, "WRITER: Error closing file!\n");
+ HDexit(1);
+ } /* end if */
+
+ return 0;
+} /* main() */
diff --git a/test/vfd_swmr_vlstr_reader.c b/test/vfd_swmr_vlstr_reader.c
new file mode 100644
index 0000000..df9037729
--- /dev/null
+++ b/test/vfd_swmr_vlstr_reader.c
@@ -0,0 +1,232 @@
+/*
+ * 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 <err.h>
+#include <time.h> /* nanosleep(2) */
+#include <unistd.h> /* getopt(3) */
+
+#define H5C_FRIEND /*suppress error about including H5Cpkg */
+#define H5F_FRIEND /*suppress error about including H5Fpkg */
+
+#include "hdf5.h"
+
+#include "H5Cpkg.h"
+#include "H5Fpkg.h"
+// #include "H5Iprivate.h"
+#include "H5HGprivate.h"
+#include "H5VLprivate.h"
+
+#include "testhdf5.h"
+#include "vfd_swmr_common.h"
+
+typedef enum _step {
+ CREATE = 0
+, LENGTHEN
+, SHORTEN
+, DELETE
+, NSTEPS
+} step_t;
+
+static const hid_t badhid = H5I_INVALID_HID; // abbreviate
+static bool caught_out_of_bounds = false;
+static bool read_null = false;
+
+static bool
+read_vl_dset(hid_t dset, hid_t type, char **data)
+{
+ bool success;
+ estack_state_t es;
+
+ es = disable_estack();
+ success = H5Dread(dset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, data) >= 0;
+ if (*data == NULL) {
+ read_null = true;
+ return false;
+ }
+ restore_estack(es);
+
+ return success;
+}
+
+static void
+usage(const char *progname)
+{
+ fprintf(stderr, "usage: %s [-W] [-V] [-t (oob|null)] \n", progname);
+ fprintf(stderr, "\n -S: do not use VFD SWMR\n");
+ fprintf(stderr, " -n: number of test steps to perform\n");
+ fprintf(stderr, " -q: be quiet: few/no progress messages\n");
+ fprintf(stderr, " -t (oob|null): select out-of-bounds or NULL test\n");
+ exit(EXIT_FAILURE);
+}
+
+bool
+H5HG_trap(const char *reason)
+{
+ if (strcmp(reason, "out of bounds") == 0) {
+ caught_out_of_bounds = true;
+ return true;
+ }
+ return false;
+}
+
+int
+main(int argc, char **argv)
+{
+ hid_t fapl, fid, space, type;
+ hid_t dset[2];
+ char *content[2];
+ char name[2][96];
+ int ch, i, ntimes = 100;
+ unsigned long tmp;
+ bool use_vfd_swmr = true;
+ char *end;
+ const long millisec_in_nanosecs = 1000 * 1000;
+ const struct timespec delay =
+ {.tv_sec = 0, .tv_nsec = millisec_in_nanosecs * 11 / 10};
+ testsel_t sel = TEST_NONE;
+
+ assert(H5T_C_S1 != badhid);
+
+ while ((ch = getopt(argc, argv, "Sn:qt:")) != -1) {
+ switch(ch) {
+ case 'S':
+ use_vfd_swmr = false;
+ break;
+ case 'n':
+ errno = 0;
+ tmp = strtoul(optarg, &end, 0);
+ if (end == optarg || *end != '\0')
+ errx(EXIT_FAILURE, "couldn't parse `-n` argument `%s`", optarg);
+ else if (errno != 0)
+ err(EXIT_FAILURE, "couldn't parse `-n` argument `%s`", optarg);
+ else if (tmp > INT_MAX)
+ errx(EXIT_FAILURE, "`-n` argument `%lu` too large", tmp);
+ ntimes = (int)tmp;
+ break;
+ case 'q':
+ verbosity = 1;
+ break;
+ case 't':
+ if (strcmp(optarg, "oob") == 0)
+ sel = TEST_OOB;
+ else if (strcmp(optarg, "null") == 0)
+ sel = TEST_NULL;
+ else
+ usage(argv[0]);
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc > 0)
+ errx(EXIT_FAILURE, "unexpected command-line arguments");
+
+ fapl = vfd_swmr_create_fapl(false, sel == TEST_OOB, use_vfd_swmr,
+ "./shadow");
+ if (fapl < 0)
+ errx(EXIT_FAILURE, "vfd_swmr_create_fapl");
+
+ fid = H5Fopen("vfd_swmr_vlstr.h5", H5F_ACC_RDONLY, fapl);
+
+ /* Create the VL string datatype and a scalar dataspace, or a
+ * fixed-length string datatype and a simple dataspace.
+ */
+ if ((type = H5Tcopy(H5T_C_S1)) == badhid)
+ errx(EXIT_FAILURE, "H5Tcopy");
+
+ /* Create the VL string datatype and a scalar dataspace */
+ if ((type = H5Tcopy(H5T_C_S1)) == badhid)
+ errx(EXIT_FAILURE, "H5Tcopy");
+
+ if (H5Tset_size(type, H5T_VARIABLE) < 0)
+ errx(EXIT_FAILURE, "H5Tset_size");
+ space = H5Screate(H5S_SCALAR);
+
+ if (space == badhid)
+ errx(EXIT_FAILURE, "H5Screate");
+
+ if (fid == badhid)
+ errx(EXIT_FAILURE, "H5Fcreate");
+
+ /* content 1 seq 1 short
+ * content 1 seq 1 long long long long long long long long
+ * content 1 seq 1 medium medium medium
+ */
+ for (i = 0;
+ !caught_out_of_bounds && i < ntimes;
+ (i % 2 == 0) ? nanosleep(&delay, NULL) : 0, i++) {
+ estack_state_t es;
+ const int ndsets = 2;
+ const int which = i % ndsets;
+ int nconverted;
+ struct {
+ int which;
+ int seq;
+ char tail[96];
+ } scanned_content;
+
+ dbgf(2, "iteration %d which %d", i, which);
+ (void)snprintf(name[which], sizeof(name[which]),
+ "dset-%d", which);
+ es = disable_estack();
+ dset[which] = H5Dopen(fid, name[which], H5P_DEFAULT);
+ restore_estack(es);
+ if (caught_out_of_bounds || dset[which] == badhid) {
+ dbgf(2, ": couldn't open\n");
+ continue;
+ }
+ if (!read_vl_dset(dset[which], type, &content[which])) {
+ H5Dclose(dset[which]);
+ dbgf(2, ": couldn't read\n");
+ continue;
+ }
+ nconverted = sscanf(content[which], "content %d seq %d %96s",
+ &scanned_content.which, &scanned_content.seq, scanned_content.tail);
+ if (nconverted != 3) {
+ dbgf(2, ": couldn't scan\n");
+ continue;
+ }
+ dbgf(2, ": read which %d seq %d tail %s\n",
+ scanned_content.which, scanned_content.seq, scanned_content.tail);
+ H5Dclose(dset[which]);
+ }
+
+ if (caught_out_of_bounds)
+ fprintf(stderr, "caught out of bounds\n");
+
+ if (read_null)
+ fprintf(stderr, "read NULL\n");
+
+ if (H5Pclose(fapl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fapl)");
+
+ if (H5Tclose(type) < 0)
+ errx(EXIT_FAILURE, "H5Tclose");
+
+ if (H5Sclose(space) < 0)
+ errx(EXIT_FAILURE, "H5Sclose");
+
+ if (H5Fclose(fid) < 0)
+ errx(EXIT_FAILURE, "H5Fclose");
+
+ if (sel == TEST_OOB)
+ return caught_out_of_bounds ? EXIT_SUCCESS : EXIT_FAILURE;
+ else if (sel == TEST_NULL)
+ return read_null ? EXIT_SUCCESS : EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/vfd_swmr_vlstr_writer.c b/test/vfd_swmr_vlstr_writer.c
new file mode 100644
index 0000000..193e03c
--- /dev/null
+++ b/test/vfd_swmr_vlstr_writer.c
@@ -0,0 +1,322 @@
+/*
+ * 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 <err.h>
+#include <time.h> /* nanosleep(2) */
+#include <unistd.h> /* getopt(3) */
+
+#define H5C_FRIEND /*suppress error about including H5Cpkg */
+#define H5F_FRIEND /*suppress error about including H5Fpkg */
+
+#include "hdf5.h"
+
+#include "H5Cpkg.h"
+#include "H5Fpkg.h"
+// #include "H5Iprivate.h"
+#include "H5HGprivate.h"
+#include "H5VLprivate.h"
+
+#include "testhdf5.h"
+#include "vfd_swmr_common.h"
+
+enum _step {
+ CREATE = 0
+, LENGTHEN
+, SHORTEN
+, DELETE
+, NSTEPS
+} step_t;
+
+static const hid_t badhid = H5I_INVALID_HID; // abbreviate
+static bool caught_out_of_bounds = false;
+
+static void
+write_vl_dset(hid_t dset, hid_t type, hid_t space, char *data)
+{
+ if (H5Dwrite(dset, type, space, space, H5P_DEFAULT, &data) < 0)
+ errx(EXIT_FAILURE, "%s: H5Dwrite", __func__);
+ if (H5Dflush(dset) < 0)
+ errx(EXIT_FAILURE, "%s: H5Dflush", __func__);
+}
+
+#if 0
+static hid_t
+initialize_dset(hid_t file, hid_t type, hid_t space, const char *name,
+ void *data)
+{
+ hid_t dset;
+
+ dset = H5Dcreate2(file, name, type, space, H5P_DEFAULT, H5P_DEFAULT,
+ H5P_DEFAULT);
+
+ if (dset == badhid)
+ errx(EXIT_FAILURE, "H5Dcreate2");
+
+ if (H5Dwrite(dset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, data) < 0)
+ errx(EXIT_FAILURE, "H5Dwrite");
+
+ if (H5Dflush(dset) < 0)
+ errx(EXIT_FAILURE, "%s: H5Dflush", __func__);
+
+ return dset;
+}
+
+static void
+rewrite_dset(hid_t dset, hid_t type, char *data)
+{
+ if (H5Dwrite(dset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, data) < 0)
+ errx(EXIT_FAILURE, "%s: H5Dwrite", __func__);
+ if (H5Dflush(dset) < 0)
+ errx(EXIT_FAILURE, "%s: H5Dflush", __func__);
+}
+#endif
+
+static hid_t
+create_vl_dset(hid_t file, hid_t type, hid_t space, const char *name)
+{
+ hid_t dset;
+
+ dset = H5Dcreate2(file, name, type, space, H5P_DEFAULT, H5P_DEFAULT,
+ H5P_DEFAULT);
+
+ if (dset == badhid)
+ errx(EXIT_FAILURE, "H5Dcreate2");
+
+ return dset;
+}
+
+static void
+print_cache_hits(H5C_t *cache)
+{
+ int i;
+
+ for (i = 0; i < H5AC_NTYPES; i++) {
+ dbgf(3, "type-%d cache hits %" PRId64 "%s\n",
+ i, cache->hits[i], (i == H5AC_GHEAP_ID) ? " *" : "");
+ }
+ dbgf(3, "\n");
+}
+
+static void
+usage(const char *progname)
+{
+ fprintf(stderr, "usage: %s [-W] [-V]\n", progname);
+ fprintf(stderr, "\n -W: do not wait for SIGINT or SIGUSR1\n");
+ fprintf(stderr, "\n -S: do not use VFD SWMR\n");
+ fprintf(stderr, " -f: use fixed-length string\n");
+ fprintf(stderr, " (default: variable-length string)\n");
+ fprintf(stderr, " -n: number of test steps to perform\n");
+ fprintf(stderr, " -q: be quiet: few/no progress messages\n");
+ fprintf(stderr, " -t (oob|null): select out-of-bounds or NULL test\n");
+ exit(EXIT_FAILURE);
+}
+
+bool
+H5HG_trap(const char *reason)
+{
+ if (strcmp(reason, "out of bounds") == 0) {
+ caught_out_of_bounds = true;
+ return false;
+ }
+ return true;
+}
+
+int
+main(int argc, char **argv)
+{
+ hid_t fapl, fcpl, fid, space, type;
+ hid_t dset[2];
+ char content[2][96];
+ char name[2][96];
+ H5F_t *f;
+ H5C_t *cache;
+ sigset_t oldsigs;
+ herr_t ret;
+ bool variable = true, wait_for_signal = true;
+ const hsize_t dims = 1;
+ int ch, i, ntimes = 100;
+ unsigned long tmp;
+ char *end;
+ bool use_vfd_swmr = true;
+ const struct timespec delay =
+ {.tv_sec = 0, .tv_nsec = 1000 * 1000 * 1000 / 10};
+ testsel_t sel = TEST_NONE;
+
+ assert(H5T_C_S1 != badhid);
+
+ while ((ch = getopt(argc, argv, "SWfn:qt:")) != -1) {
+ switch(ch) {
+ case 'S':
+ use_vfd_swmr = false;
+ break;
+ case 'W':
+ wait_for_signal = false;
+ break;
+ case 'f':
+ variable = false;
+ break;
+ case 'n':
+ errno = 0;
+ tmp = strtoul(optarg, &end, 0);
+ if (end == optarg || *end != '\0')
+ errx(EXIT_FAILURE, "couldn't parse `-n` argument `%s`", optarg);
+ else if (errno != 0)
+ err(EXIT_FAILURE, "couldn't parse `-n` argument `%s`", optarg);
+ else if (tmp > INT_MAX)
+ errx(EXIT_FAILURE, "`-n` argument `%lu` too large", tmp);
+ ntimes = (int)tmp;
+ break;
+ case 'q':
+ verbosity = 1;
+ break;
+ case 't':
+ if (strcmp(optarg, "oob") == 0)
+ sel = TEST_OOB;
+ else if (strcmp(optarg, "null") == 0)
+ sel = TEST_NULL;
+ else
+ usage(argv[0]);
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc > 0)
+ errx(EXIT_FAILURE, "unexpected command-line arguments");
+
+ fapl = vfd_swmr_create_fapl(true, sel == TEST_OOB, use_vfd_swmr,
+ "./shadow");
+ if (fapl < 0)
+ errx(EXIT_FAILURE, "vfd_swmr_create_fapl");
+
+ if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ errx(EXIT_FAILURE, "H5Pcreate");
+
+ ret = H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, false, 1);
+ if (ret < 0)
+ errx(EXIT_FAILURE, "H5Pset_file_space_strategy");
+
+ fid = H5Fcreate("vfd_swmr_vlstr.h5", H5F_ACC_TRUNC, fcpl, fapl);
+
+ /* Create the VL string datatype and a scalar dataspace, or a
+ * fixed-length string datatype and a simple dataspace.
+ */
+ if ((type = H5Tcopy(H5T_C_S1)) == badhid)
+ errx(EXIT_FAILURE, "H5Tcopy");
+
+ /* Create the VL string datatype and a scalar dataspace */
+ if ((type = H5Tcopy(H5T_C_S1)) == badhid)
+ errx(EXIT_FAILURE, "H5Tcopy");
+
+ if (!variable) {
+ if (H5Tset_size(type, 32) < 0)
+ errx(EXIT_FAILURE, "H5Tset_size");
+ space = H5Screate_simple(1, &dims, NULL);
+ } else {
+ if (H5Tset_size(type, H5T_VARIABLE) < 0)
+ errx(EXIT_FAILURE, "H5Tset_size");
+ space = H5Screate(H5S_SCALAR);
+ }
+
+ if (space == badhid)
+ errx(EXIT_FAILURE, "H5Screate");
+
+ if ((f = H5VL_object_verify(fid, H5I_FILE)) == NULL)
+ errx(EXIT_FAILURE, "H5VL_object_verify");
+
+ cache = f->shared->cache;
+
+ if (fid == badhid)
+ errx(EXIT_FAILURE, "H5Fcreate");
+
+ block_signals(&oldsigs);
+
+ print_cache_hits(cache);
+
+ /* content 1 seq 1 short
+ * content 1 seq 1 long long long long long long long long
+ * content 1 seq 1 medium medium medium
+ */
+ for (i = 0; i < ntimes; i++) {
+ const int ndsets = 2;
+ const int step = i % NSTEPS;
+ const int which = (i / NSTEPS) % ndsets;
+ const int seq = i / (ndsets * NSTEPS);
+ dbgf(2, "iteration %d which %d step %d seq %d\n",
+ i, which, step, seq);
+ switch (step) {
+ case CREATE:
+ (void)snprintf(name[which], sizeof(name[which]),
+ "dset-%d", which);
+ (void)snprintf(content[which], sizeof(content[which]),
+ "content %d seq %d short", which, seq);
+ dset[which] =
+ create_vl_dset(fid, type, space, name[which]);
+ write_vl_dset(dset[which], type, space, content[which]);
+ break;
+ case LENGTHEN:
+ (void)snprintf(content[which], sizeof(content[which]),
+ "content %d seq %d long long long long long long long long",
+ which, seq);
+ write_vl_dset(dset[which], type, space, content[which]);
+ break;
+ case SHORTEN:
+ (void)snprintf(content[which], sizeof(content[which]),
+ "content %d seq %d medium medium medium",
+ which, seq);
+ write_vl_dset(dset[which], type, space, content[which]);
+ break;
+ case DELETE:
+ if (H5Dclose(dset[which]) < 0)
+ errx(EXIT_FAILURE, "H5Dclose");
+ if (H5Ldelete(fid, name[which], H5P_DEFAULT) < 0) {
+ errx(EXIT_FAILURE, "%s: H5Ldelete(, \"%s\", ) failed",
+ __func__, name[which]);
+ }
+ break;
+ default:
+ errx(EXIT_FAILURE, "%s: unknown step %d", __func__, step);
+ }
+ if (caught_out_of_bounds) {
+ fprintf(stderr, "caught out of bounds\n");
+ break;
+ }
+ nanosleep(&delay, NULL);
+ }
+
+ if (use_vfd_swmr && wait_for_signal)
+ await_signal(fid);
+
+ restore_signals(&oldsigs);
+
+ if (H5Pclose(fapl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fapl)");
+
+ if (H5Pclose(fcpl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fcpl)");
+
+ if (H5Tclose(type) < 0)
+ errx(EXIT_FAILURE, "H5Tclose");
+
+ if (H5Sclose(space) < 0)
+ errx(EXIT_FAILURE, "H5Sclose");
+
+ if (H5Fclose(fid) < 0)
+ errx(EXIT_FAILURE, "H5Fclose");
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/vfd_swmr_writer.c b/test/vfd_swmr_writer.c
new file mode 100644
index 0000000..1d73dd6
--- /dev/null
+++ b/test/vfd_swmr_writer.c
@@ -0,0 +1,454 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: vfd_swmr_writer.c
+ * (copied and modified from swmr_writer.c)
+ *
+ * Purpose: Writes data to a randomly selected subset of the datasets
+ * in the VFD SWMR test file.
+ *
+ * This program is intended to run concurrently with the
+ * vfd_swmr_reader program.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/***********/
+/* Headers */
+/***********/
+
+#include <unistd.h> /* getopt(3) */
+
+#include "h5test.h"
+#include "vfd_swmr_common.h"
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+static hid_t open_skeleton(const char *filename, hbool_t verbose, FILE *verbose_file,
+ unsigned random_seed, hbool_t old);
+static int add_records(hid_t fid, hbool_t verbose, FILE *verbose_file,
+ unsigned long nrecords, unsigned long flush_count);
+static void usage(void);
+
+
+/*-------------------------------------------------------------------------
+ * Function: open_skeleton
+ *
+ * Purpose: Opens the SWMR HDF5 file and datasets.
+ *
+ * Parameters: const char *filename
+ * The filename of the SWMR HDF5 file to open
+ *
+ * hbool_t verbose
+ * Whether or not to emit verbose console messages
+ *
+ * FILE *verbose_file
+ * File handle for verbose output
+ *
+ * unsigned random_seed
+ * Random seed for the file (used for verbose logging)
+ *
+ * hbool_t old
+ * Whether to write in "old" file format
+ *
+ * Return: Success: The file ID of the opened SWMR file
+ * The dataset IDs are stored in a global array
+ *
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static hid_t
+open_skeleton(const char *filename, hbool_t verbose, FILE *verbose_file,
+ unsigned random_seed, hbool_t old H5_ATTR_UNUSED)
+{
+ hid_t fid; /* File ID for new HDF5 file */
+ hid_t fapl; /* File access property list */
+ unsigned u, v; /* Local index variable */
+ hbool_t use_log_vfd = FALSE; /* Use the log VFD (set this manually) */
+ H5F_vfd_swmr_config_t *config = NULL; /* Configuration for VFD SWMR */
+
+ HDassert(filename);
+
+ /* Create file access property list */
+ if((fapl = h5_fileaccess()) < 0)
+ return -1;
+
+ /* FOR NOW: set to use latest format, the "old" parameter is not used */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ return -1;
+
+ if(use_log_vfd) {
+ char verbose_name[1024];
+
+ HDsnprintf(verbose_name, sizeof(verbose_name), "vfd_swmr_writer.log.%u", random_seed);
+
+ H5Pset_fapl_log(fapl, verbose_name, H5FD_LOG_ALL, (size_t)(512 * 1024 * 1024));
+ } /* end if */
+
+ /*
+ * Set up to open the file with VFD SWMR configured.
+ */
+
+ /* Enable page buffering */
+ if(H5Pset_page_buffer_size(fapl, 4096, 0, 0) < 0)
+ return -1;
+
+ /* Allocate memory for the configuration structure */
+ if((config = (H5F_vfd_swmr_config_t *)HDcalloc(1, sizeof(H5F_vfd_swmr_config_t))) == NULL)
+ return -1;
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = 4;
+ config->max_lag = 5;
+ config->writer = TRUE;
+ config->md_pages_reserved = 128;
+ HDstrcpy(config->md_file_path, "./my_md_file");
+
+ /* Enable VFD SWMR configuration */
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0)
+ return -1;
+
+ /* Open the file with VFD SWMR configured */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ return -1;
+
+ /* Close file access property list */
+ if(H5Pclose(fapl) < 0)
+ return -1;
+
+ if(config)
+ HDfree(config);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "WRITER: Opening datasets\n");
+
+ /* Open the datasets */
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++) {
+ if((symbol_info[u][v].dsid = H5Dopen2(fid, symbol_info[u][v].name, H5P_DEFAULT)) < 0)
+ return -1;
+ symbol_info[u][v].nrecords = 0;
+ } /* end for */
+
+ return fid;
+} /* open_skeleton() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: add_records
+ *
+ * Purpose: Writes a specified number of records to random datasets in
+ * the SWMR test file.
+ *
+ * Parameters: hid_t fid
+ * The file ID of the SWMR HDF5 file
+ *
+ * hbool_t verbose
+ * Whether or not to emit verbose console messages
+ *
+ * FILE *verbose_file
+ * File handle for verbose output
+ *
+ * unsigned long nrecords
+ * # of records to write to the datasets
+ *
+ * unsigned long flush_count
+ * # of records to write before flushing the file to disk
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+add_records(hid_t fid, hbool_t verbose, FILE *verbose_file,
+ unsigned long nrecords, unsigned long flush_count)
+{
+ hid_t tid; /* Datatype ID for records */
+ hid_t mem_sid; /* Memory dataspace ID */
+ hsize_t start[2] = {0, 0}, count[2] = {1, 1}; /* Hyperslab selection values */
+ hsize_t dim[2] = {1, 0}; /* Dataspace dimensions */
+ symbol_t record; /* The record to add to the dataset */
+ unsigned long rec_to_flush; /* # of records left to write before flush */
+ unsigned long u, v; /* Local index variables */
+
+ HDassert(fid >= 0);
+
+ /* Reset the record */
+ /* (record's 'info' field might need to change for each record written, also) */
+ HDmemset(&record, 0, sizeof(record));
+
+ /* Create a dataspace for the record to add */
+ if((mem_sid = H5Screate(H5S_SCALAR)) < 0)
+ return -1;
+
+ /* Create datatype for appending records */
+ if((tid = create_symbol_datatype()) < 0)
+ return -1;
+
+ /* Add records to random datasets, according to frequency distribution */
+ rec_to_flush = flush_count;
+ for(u = 0; u < nrecords; u++) {
+ symbol_info_t *symbol; /* Symbol to write record to */
+ hid_t file_sid; /* Dataset's space ID */
+
+ /* Get a random dataset, according to the symbol distribution */
+ symbol = choose_dataset(NULL, NULL);
+
+ /* Set the record's ID (equal to its position) */
+ record.rec_id = symbol->nrecords;
+
+ /* Get the coordinate to write */
+ start[1] = symbol->nrecords;
+
+ /* Extend the dataset's dataspace to hold the new record */
+ symbol->nrecords++;
+ dim[1] = symbol->nrecords;
+ if(H5Dset_extent(symbol->dsid, dim) < 0)
+ return -1;
+
+ /* Get the dataset's dataspace */
+ if((file_sid = H5Dget_space(symbol->dsid)) < 0)
+ return -1;
+
+ /* Choose the last record in the dataset */
+ if(H5Sselect_hyperslab(file_sid, H5S_SELECT_SET, start, NULL, count, NULL) < 0)
+ return -1;
+
+ /* Write record to the dataset */
+ if(H5Dwrite(symbol->dsid, tid, mem_sid, file_sid, H5P_DEFAULT, &record) < 0)
+ return -1;
+
+ /* Close the dataset's dataspace */
+ if(H5Sclose(file_sid) < 0)
+ return -1;
+
+ /* Check for flushing file */
+ if(flush_count > 0) {
+ /* Decrement count of records to write before flushing */
+ rec_to_flush--;
+
+ /* Check for counter being reached */
+ if(0 == rec_to_flush) {
+#ifdef TEMP_OUT
+ /* Flush contents of file */
+ if(H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0)
+ return -1;
+#endif /* TEMP_OUT */
+
+ /* Reset flush counter */
+ rec_to_flush = flush_count;
+ } /* end if */
+ } /* end if */
+ } /* end for */
+
+ /* Close the memory dataspace */
+ if(H5Sclose(mem_sid) < 0)
+ return -1;
+
+ /* Close the datatype */
+ if(H5Tclose(tid) < 0)
+ return -1;
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "WRITER: Closing datasets\n");
+
+ /* Close the datasets */
+ for(u = 0; u < NLEVELS; u++)
+ for(v = 0; v < symbol_count[u]; v++)
+ if(H5Dclose(symbol_info[u][v].dsid) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void
+usage(void)
+{
+ printf("\n");
+ printf("Usage error!\n");
+ printf("\n");
+ printf("Usage: vfd_swmr_writer [-q] [-o] [-f <# of records to write between flushing\n");
+ printf(" file contents>] [-r <random seed>] <# of records>\n");
+ printf("\n");
+ printf("<# of records to write between flushing file contents> should be 0\n");
+ printf("(for no flushing) or between 1 and (<# of records> - 1).\n");
+ printf("\n");
+ printf("<# of records> must be specified.\n");
+ printf("\n");
+ printf("Defaults to verbose (no '-q' given), latest format when opening file (no '-o' given),\n");
+ printf("flushing every 10000 records ('-f 10000'), and will generate a random seed (no -r given).\n");
+ printf("\n");
+ HDexit(1);
+}
+
+int
+main(int argc, char * const *argv)
+{
+ sigset_t oldset;
+ hid_t fid; /* File ID for file opened */
+ long nrecords = 0; /* # of records to append */
+ long flush_count = 10000; /* # of records to write between flushing file */
+ hbool_t verbose = TRUE; /* Whether to emit some informational messages */
+ FILE *verbose_file = NULL; /* File handle for verbose output */
+ hbool_t old = FALSE; /* Whether to use non-latest-format when opening file */
+ hbool_t use_seed = FALSE; /* Set to TRUE if a seed was set on the command line */
+ hbool_t wait_for_signal = TRUE;
+ unsigned random_seed = 0; /* Random # seed */
+ int ch, temp;
+
+ block_signals(&oldset);
+
+ while ((ch = getopt(argc, argv, "Wf:qr:o")) != -1) {
+ switch(ch) {
+ /* # of records to write between flushing file */
+ case 'f':
+ flush_count = HDatol(optarg);
+ if(flush_count < 0)
+ usage();
+ break;
+
+ /* Be quiet */
+ case 'q':
+ verbose = FALSE;
+ break;
+
+ /* Random # seed */
+ case 'r':
+ use_seed = TRUE;
+ temp = HDatoi(optarg);
+ random_seed = (unsigned)temp;
+ break;
+
+ case 'W':
+ wait_for_signal = FALSE;
+ break;
+
+ /* Use non-latest-format when opening file */
+ case 'o':
+ old = TRUE;
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+ argv += optind;
+ argc -= optind;
+ /* Parse command line options */
+ if(argc < 1)
+ usage();
+ /* Get the number of records to append */
+ nrecords = HDatol(argv[0]);
+ if(nrecords <= 0 || flush_count >= nrecords)
+ usage();
+
+ /* Set the random seed */
+ if(!use_seed) {
+ struct timeval t;
+
+ HDgettimeofday(&t, NULL);
+ random_seed = (unsigned)(t.tv_usec);
+ } /* end if */
+ HDsrandom(random_seed);
+
+ /* Open output file */
+ if(verbose) {
+ char verbose_name[1024];
+
+ HDsnprintf(verbose_name, sizeof(verbose_name), "vfd_swmr_writer.out.%u", random_seed);
+ if(NULL == (verbose_file = HDfopen(verbose_name, "w"))) {
+ HDfprintf(stderr, "WRITER: Can't open verbose output file!\n");
+ HDexit(1);
+ }
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(verbose_file, "WRITER: Parameters:\n");
+ HDfprintf(verbose_file, "\t# of records between flushes = %ld\n", flush_count);
+ HDfprintf(verbose_file, "\t# of records to write = %ld\n", nrecords);
+ } /* end if */
+
+ /* ALWAYS emit the random seed for possible debugging */
+ HDfprintf(stdout, "Using writer random seed: %u\n", random_seed);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "WRITER: Generating symbol names\n");
+
+ /* Generate dataset names */
+ if(generate_symbols() < 0)
+ return -1;
+
+ /* Emit informational message */
+ if(verbose) {
+ HDfprintf(verbose_file, "WRITER: Opening skeleton file: %s\n",
+ COMMON_FILENAME);
+ }
+
+ /* Open file skeleton */
+ if((fid = open_skeleton(COMMON_FILENAME, verbose, verbose_file, random_seed, old)) < 0) {
+ HDfprintf(stderr, "WRITER: Error opening skeleton file!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Send a message to indicate "H5Fopen" is complete--releasing the file lock */
+ h5_send_message(WRITER_MESSAGE, NULL, NULL);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "WRITER: Adding records\n");
+
+ /* Append records to datasets */
+ if(add_records(fid, verbose, verbose_file, (unsigned long)nrecords, (unsigned long)flush_count) < 0) {
+ HDfprintf(stderr, "WRITER: Error appending records to datasets!\n");
+ HDexit(1);
+ } /* end if */
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "WRITER: Releasing symbols\n");
+
+ /* Clean up the symbols */
+ if(shutdown_symbols() < 0) {
+ HDfprintf(stderr, "WRITER: Error releasing symbols!\n");
+ HDexit(1);
+ } /* end if */
+
+ if (wait_for_signal)
+ await_signal(fid);
+
+ restore_signals(&oldset);
+
+ /* Emit informational message */
+ if(verbose)
+ HDfprintf(verbose_file, "WRITER: Closing objects/file\n");
+
+ /* Close objects opened */
+ if(H5Fclose(fid) < 0) {
+ HDfprintf(stderr, "WRITER: Error closing file!\n");
+ HDexit(1);
+ } /* end if */
+
+ return 0;
+}
+
diff --git a/test/vfd_swmr_zoo_writer.c b/test/vfd_swmr_zoo_writer.c
new file mode 100644
index 0000000..e2892ec
--- /dev/null
+++ b/test/vfd_swmr_zoo_writer.c
@@ -0,0 +1,412 @@
+/*
+ * 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 <err.h>
+#include <libgen.h> /* basename(3) */
+#include <time.h> /* nanosleep(2) */
+#include <unistd.h> /* getopt(3) */
+
+#define H5C_FRIEND /* suppress error about including H5Cpkg */
+#define H5F_FRIEND /* suppress error about including H5Fpkg */
+
+#include "hdf5.h"
+
+#include "H5private.h"
+#include "H5retry_private.h"
+#include "H5Cpkg.h"
+#include "H5Fpkg.h"
+// #include "H5Iprivate.h"
+#include "H5HGprivate.h"
+#include "H5VLprivate.h"
+
+#include "testhdf5.h"
+#include "genall5.h"
+#include "vfd_swmr_common.h"
+
+#ifndef _arraycount
+#define _arraycount(_a) (sizeof(_a)/sizeof(_a[0]))
+#endif
+
+typedef struct _shared_ticks {
+ uint64_t reader_tick;
+} shared_ticks_t;
+
+typedef struct _tick_stats {
+ uint64_t writer_tried_increase;
+ uint64_t writer_aborted_increase;
+ uint64_t writer_read_shared_file;
+ uint64_t reader_tick_was_zero; // writer read reader tick equal to 0
+ uint64_t reader_tick_lead_writer; // writer read reader tick greater than
+ // proposed writer tick
+ uint64_t writer_lead_reader_by[1]; // proposed writer tick lead reader
+ // tick by `lead` ticks
+ // `writer_lead_reader_by[lead]`
+ // times, for `0 <= lead <= max_lag - 1`
+} tick_stats_t;
+
+static H5F_vfd_swmr_config_t swmr_config;
+static tick_stats_t *tick_stats = NULL;
+static const hid_t badhid = H5I_INVALID_HID;
+static bool writer;
+
+static void
+print_cache_hits(H5C_t *cache)
+{
+ int i;
+
+ for (i = 0; i < H5AC_NTYPES; i++) {
+ dbgf(3, "type-%d cache hits %" PRId64 "%s\n",
+ i, cache->hits[i], (i == H5AC_GHEAP_ID) ? " *" : "");
+ }
+ dbgf(3, "\n");
+}
+
+void
+zoo_create_hook(hid_t fid)
+{
+ dbgf(3, "%s: enter\n", __func__);
+ if (writer)
+ H5Fvfd_swmr_end_tick(fid);
+}
+
+static void
+usage(const char *progname)
+{
+ fprintf(stderr, "usage: %s [-C] [-S] [-W] [-a] [-e] [-m] [-q] [-v]\n",
+ progname);
+ fprintf(stderr, "\n -C: skip compact dataset tests\n");
+ fprintf(stderr, " -S: do not use VFD SWMR\n");
+ fprintf(stderr, " -W: do not wait for SIGINT or SIGUSR1\n");
+ fprintf(stderr, " -a: run all tests, including variable-length data\n");
+ fprintf(stderr, " -e: print error stacks\n");
+ fprintf(stderr, " -m ms: maximum `ms` milliseconds pause between\n");
+ fprintf(stderr, " each create/delete step\n");
+ fprintf(stderr, " -q: be quiet: few/no progress messages\n");
+ fprintf(stderr, " -v: be verbose: most progress messages\n");
+ exit(EXIT_FAILURE);
+}
+
+bool
+vfd_swmr_writer_may_increase_tick_to(uint64_t new_tick, bool wait_for_reader)
+{
+ static int fd = -1;
+ shared_ticks_t shared;
+ ssize_t nread;
+ h5_retry_t retry;
+ bool do_try;
+
+ dbgf(3, "%s: enter\n", __func__);
+
+ if (fd == -1) {
+ fd = open("./shared_tick_num", O_RDONLY);
+ if (fd == -1) {
+ warn("%s: open", __func__); // TBD ratelimit/silence this warning
+ return true;
+ }
+ assert(tick_stats == NULL);
+ tick_stats = calloc(1, sizeof(*tick_stats) +
+ (swmr_config.max_lag - 1) *
+ sizeof(tick_stats->writer_lead_reader_by[0]));
+ if (tick_stats == NULL)
+ err(EXIT_FAILURE, "%s: calloc", __func__);
+ }
+
+ tick_stats->writer_tried_increase++;
+
+ for (do_try = h5_retry_init(&retry, 14, 10 * 1000 * 1000,
+ 100 * 1000 * 1000);
+ do_try;
+ do_try = wait_for_reader && h5_retry_next(&retry)) {
+
+ tick_stats->writer_read_shared_file++;
+
+ if ((nread = pread(fd, &shared, sizeof(shared), 0)) == -1)
+ err(EXIT_FAILURE, "%s: pread", __func__);
+
+ if (nread != sizeof(shared))
+ errx(EXIT_FAILURE, "%s: pread", __func__);
+
+ // TBD convert endianness
+
+ if (shared.reader_tick == 0) {
+ tick_stats->reader_tick_was_zero++;
+ return true;
+ }
+
+ if (new_tick < shared.reader_tick) {
+ tick_stats->reader_tick_lead_writer++;
+ return true;
+ }
+ if (new_tick <= shared.reader_tick + swmr_config.max_lag - 1) {
+ uint64_t lead = new_tick - shared.reader_tick;
+ assert(lead <= swmr_config.max_lag - 1);
+ tick_stats->writer_lead_reader_by[lead]++;
+ return true;
+ }
+ }
+ if (wait_for_reader && !do_try)
+ errx(EXIT_FAILURE, "%s: timed out waiting for reader", __func__);
+
+ tick_stats->writer_aborted_increase++;
+
+ return false;
+}
+
+void
+vfd_swmr_reader_did_increase_tick_to(uint64_t new_tick)
+{
+ static int fd = -1;
+ shared_ticks_t shared;
+ ssize_t nwritten;
+
+ dbgf(3, "%s: enter\n", __func__);
+
+ if (fd == -1) {
+ // TBD create a temporary file, here, and move it to its final path
+ // after writing it.
+ fd = open("./shared_tick_num", O_RDWR|O_CREAT, 0600);
+ if (fd == -1)
+ err(EXIT_FAILURE, "%s: open", __func__);
+ }
+
+ shared.reader_tick = new_tick;
+
+ // TBD convert endianness
+
+ if ((nwritten = pwrite(fd, &shared, sizeof(shared), 0)) == -1)
+ errx(EXIT_FAILURE, "%s: pwrite", __func__);
+
+ if (nwritten != sizeof(shared))
+ errx(EXIT_FAILURE, "%s: pwrite", __func__);
+
+ if (new_tick == 0) {
+ if (unlink("./shared_tick_num") == -1)
+ warn("%s: unlink", __func__);
+ if (close(fd) == -1)
+ err(EXIT_FAILURE, "%s: close", __func__);
+ fd = -1;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ hid_t fapl, fcpl, fid;
+ H5F_t *f;
+ H5C_t *cache;
+ sigset_t oldsigs;
+ herr_t ret;
+ zoo_config_t config = {
+ .proc_num = 0
+ , .skip_compact = false
+ , .skip_varlen = true
+ , .max_pause_msecs = 0
+ };
+ bool wait_for_signal;
+ int ch;
+ char vector[8];
+ unsigned seed;
+ unsigned long tmpl;
+ char *end, *ostate;
+ const char *seedvar = "H5_ZOO_STEP_SEED";
+ bool use_vfd_swmr = true;
+ bool print_estack = false;
+ const char *progname = basename(argv[0]);
+ const char *personality = strstr(progname, "vfd_swmr_zoo_");
+ estack_state_t es;
+ char step = 'b';
+
+ if (personality != NULL && strcmp(personality, "vfd_swmr_zoo_writer") == 0)
+ writer = wait_for_signal = true;
+ else if (personality != NULL &&
+ strcmp(personality, "vfd_swmr_zoo_reader") == 0)
+ writer = false;
+ else {
+ errx(EXIT_FAILURE,
+ "unknown personality, expected vfd_swmr_zoo_{reader,writer}");
+ }
+
+ if (writer)
+ config.max_pause_msecs = 50;
+
+ while ((ch = getopt(argc, argv, "CSWaem:qv")) != -1) {
+ switch(ch) {
+ case 'C':
+ config.skip_compact = true;
+ break;
+ case 'S':
+ use_vfd_swmr = false;
+ break;
+ case 'W':
+ wait_for_signal = false;
+ break;
+ case 'a':
+ config.skip_varlen = false;
+ break;
+ case 'e':
+ print_estack = true;
+ break;
+ case 'm':
+ errno = 0;
+ tmpl = strtoul(optarg, &end, 0);
+ if (end == optarg || *end != '\0')
+ errx(EXIT_FAILURE, "couldn't parse `-m` argument `%s`", optarg);
+ else if (errno != 0)
+ err(EXIT_FAILURE, "couldn't parse `-m` argument `%s`", optarg);
+ else if (tmpl > UINT_MAX)
+ errx(EXIT_FAILURE, "`-m` argument `%lu` too large", tmpl);
+ config.max_pause_msecs = (unsigned)tmpl;
+ break;
+ case 'q':
+ verbosity = 1;
+ break;
+ case 'v':
+ verbosity = 3;
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc > 0)
+ errx(EXIT_FAILURE, "unexpected command-line arguments");
+
+ fapl = vfd_swmr_create_fapl(writer, true, use_vfd_swmr, "./shadow");
+
+ if (use_vfd_swmr && H5Pget_vfd_swmr_config(fapl, &swmr_config) < 0)
+ errx(EXIT_FAILURE, "H5Pget_vfd_swmr_config");
+
+ if (fapl < 0)
+ errx(EXIT_FAILURE, "vfd_swmr_create_fapl");
+
+ if (H5Pset_libver_bounds(fapl, H5F_LIBVER_EARLIEST, H5F_LIBVER_LATEST) < 0){
+ errx(EXIT_FAILURE, "%s.%d: H5Pset_libver_bounds", __func__, __LINE__);
+ }
+
+ if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
+ errx(EXIT_FAILURE, "H5Pcreate");
+
+ ret = H5Pset_file_space_strategy(fcpl, H5F_FSPACE_STRATEGY_PAGE, false, 1);
+ if (ret < 0)
+ errx(EXIT_FAILURE, "H5Pset_file_space_strategy");
+
+ if (writer)
+ fid = H5Fcreate("vfd_swmr_zoo.h5", H5F_ACC_TRUNC, fcpl, fapl);
+ else
+ fid = H5Fopen("vfd_swmr_zoo.h5", H5F_ACC_RDONLY, fapl);
+
+ if (fid == badhid)
+ errx(EXIT_FAILURE, writer ? "H5Fcreate" : "H5Fopen");
+
+ if ((f = H5VL_object_verify(fid, H5I_FILE)) == NULL)
+ errx(EXIT_FAILURE, "H5VL_object_verify");
+
+ cache = f->shared->cache;
+
+ if (wait_for_signal)
+ block_signals(&oldsigs);
+
+ print_cache_hits(cache);
+
+ es = print_estack ? estack_get_state() : disable_estack();
+ if (writer) {
+
+ dbgf(2, "Writing zoo...\n");
+
+ /* get seed from environment or else from time(3) */
+ switch (fetch_env_ulong(seedvar, UINT_MAX, &tmpl)) {
+ case -1:
+ errx(EXIT_FAILURE, "%s: fetch_env_ulong", __func__);
+ case 0:
+ seed = (unsigned int)time(NULL);
+ break;
+ default:
+ seed = (unsigned int)tmpl;
+ break;
+ }
+
+ dbgf(1, "To reproduce, set seed (%s) to %u.\n", seedvar, seed);
+
+ ostate = initstate(seed, vector, _arraycount(vector));
+
+ if (!create_zoo(fid, ".", config))
+ errx(EXIT_FAILURE, "create_zoo didn't pass self-check");
+
+ /* Avoid deadlock: flush the file before waiting for the reader's
+ * message.
+ */
+ if (H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0)
+ errx(EXIT_FAILURE, "%s: H5Fflush failed", __func__);
+
+ if (read(STDIN_FILENO, &step, sizeof(step)) == -1)
+ err(EXIT_FAILURE, "read");
+
+ if (step != 'b')
+ errx(EXIT_FAILURE, "expected 'b' read '%c'", step);
+
+ if (!delete_zoo(fid, ".", config))
+ errx(EXIT_FAILURE, "delete_zoo failed");
+ (void)setstate(ostate);
+ } else {
+ dbgf(2, "Reading zoo...\n");
+
+ while (!validate_zoo(fid, ".", config))
+ ;
+
+ if (write(STDOUT_FILENO, &step, sizeof(step)) == -1)
+ err(EXIT_FAILURE, "write");
+ while (!validate_deleted_zoo(fid, ".", config))
+ ;
+ }
+ restore_estack(es);
+
+ if (use_vfd_swmr && wait_for_signal)
+ await_signal(fid);
+
+ if (writer && tick_stats != NULL) {
+ uint64_t lead;
+
+ dbgf(2, "writer tried tick increase %" PRIu64 "\n",
+ tick_stats->writer_tried_increase);
+ dbgf(2, "writer aborted tick increase %" PRIu64 "\n",
+ tick_stats->writer_aborted_increase);
+ dbgf(2, "writer read shared file %" PRIu64 "\n",
+ tick_stats->writer_read_shared_file);
+ dbgf(2, "writer read reader tick equal to 0 %" PRIu64 "\n",
+ tick_stats->reader_tick_was_zero);
+ dbgf(2, "writer read reader tick leading writer %" PRIu64 "\n",
+ tick_stats->reader_tick_lead_writer);
+
+ for (lead = 0; lead < swmr_config.max_lag; lead++) {
+ dbgf(2, "writer tick lead writer by %" PRIu64 " %" PRIu64 "\n",
+ lead, tick_stats->writer_lead_reader_by[lead]);
+ }
+ }
+
+ if (H5Pclose(fapl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fapl)");
+
+ if (H5Pclose(fcpl) < 0)
+ errx(EXIT_FAILURE, "H5Pclose(fcpl)");
+
+ if (H5Fclose(fid) < 0)
+ errx(EXIT_FAILURE, "H5Fclose");
+
+ if (wait_for_signal)
+ restore_signals(&oldsigs);
+
+ return EXIT_SUCCESS;
+}
diff --git a/testpar/t_2Gio.c b/testpar/t_2Gio.c
index 54ea546..ee19b5f 100644
--- a/testpar/t_2Gio.c
+++ b/testpar/t_2Gio.c
@@ -3907,6 +3907,8 @@ actual_io_mode_tests(void) {
* Programmer: Jonathan Kim
* Date: Aug, 2012
*/
+#define DSET_NOCOLCAUSE "nocolcause"
+#define NELM 2
#define FILE_EXTERNAL "nocolcause_extern.data"
static void
test_no_collective_cause_mode(int selection_mode)
diff --git a/testpar/t_cache.c b/testpar/t_cache.c
index 954071d..efa17f8 100644
--- a/testpar/t_cache.c
+++ b/testpar/t_cache.c
@@ -2379,7 +2379,7 @@ datum_get_initial_load_size(void *udata_ptr, size_t *image_len_ptr)
*-------------------------------------------------------------------------
*/
static void *
-datum_deserialize(const void H5_ATTR_NDEBUG_UNUSED *image_ptr,
+datum_deserialize(const void H5_ATTR_SANITY_CHECK *image_ptr,
H5_ATTR_UNUSED size_t len,
void * udata_ptr,
hbool_t * dirty_ptr)
@@ -2492,7 +2492,7 @@ datum_image_len(const void *thing, size_t *image_len)
*/
static herr_t
datum_serialize(const H5F_t *f,
- void H5_ATTR_NDEBUG_UNUSED *image_ptr,
+ void H5_ATTR_SANITY_CHECK *image_ptr,
size_t len,
void *thing_ptr)
{
diff --git a/tools/lib/h5tools.c b/tools/lib/h5tools.c
index 55c69a7..e6df7c8 100644
--- a/tools/lib/h5tools.c
+++ b/tools/lib/h5tools.c
@@ -16,6 +16,8 @@
* amongst the various HDF5 tools.
*/
+#include <libgen.h>
+
#include "h5tools.h"
#include "h5tools_dump.h"
#include "h5tools_ref.h"
@@ -81,19 +83,20 @@ const char *volnames[] = {
* somehow otherwise not enabled.
*
*/
-const char *drivernames[] = {
- "sec2",
- "direct",
- "log",
- "windows",
- "stdio",
- "core",
- "family",
- "split",
- "multi",
- "mpio",
- "ros3",
- "hdfs",
+const char *drivernames[]={
+ [SEC2_VFD_IDX] = "sec2",
+ [DIRECT_VFD_IDX] = "direct",
+ [LOG_VFD_IDX] = "log",
+ [WINDOWS_VFD_IDX] = "windows",
+ [STDIO_VFD_IDX] = "stdio",
+ [CORE_VFD_IDX] = "core",
+ [FAMILY_VFD_IDX] = "family",
+ [SPLIT_VFD_IDX] = "split",
+ [MULTI_VFD_IDX] = "multi",
+ [MPIO_VFD_IDX] = "mpio",
+ [ROS3_VFD_IDX] = "ros3",
+ [HDFS_VFD_IDX] = "hdfs",
+ [SWMR_VFD_IDX] = "swmr",
};
#define NUM_VOLS (sizeof(volnames) / sizeof(volnames[0]))
@@ -468,6 +471,52 @@ h5tools_set_error_file(const char *fname, int is_bin)
return retvalue;
}
+static hid_t
+swmr_fapl_augment(hid_t fapl, const char *fname)
+{
+ H5F_vfd_swmr_config_t *config = NULL; /* Configuration for VFD SWMR */
+ const char *dname;
+ char *tname;
+
+ /*
+ * Set up to open the file with VFD SWMR configured.
+ */
+ /* Enable page buffering */
+ if(H5Pset_page_buffer_size(fapl, 4096, 100, 0) < 0) {
+ HDfprintf(rawerrorstream, "H5Pset_page_buffer_size failed\n");
+ return H5I_INVALID_HID;
+ }
+
+ /* Allocate memory for the configuration structure */
+ config = (H5F_vfd_swmr_config_t *)HDcalloc(1, sizeof(*config));
+ if(config == NULL) {
+ HDfprintf(rawerrorstream, "VFD SWMR config allocation failed\n");
+ return H5I_INVALID_HID;
+ }
+
+ config->version = H5F__CURR_VFD_SWMR_CONFIG_VERSION;
+ config->tick_len = 4;
+ config->max_lag = 5;
+ config->writer = FALSE;
+ config->md_pages_reserved = 128;
+
+ if ((tname = strdup(fname)) == NULL) {
+ HDfprintf(rawerrorstream, "temporary string allocation failed\n");
+ return H5I_INVALID_HID;
+ }
+ dname = dirname(tname);
+ snprintf(config->md_file_path, sizeof(config->md_file_path),
+ "%s/my_md_file", dname);
+ free(tname);
+
+ /* Enable VFD SWMR configuration */
+ if(H5Pset_vfd_swmr_config(fapl, config) < 0) {
+ HDfprintf(rawerrorstream, "H5Pset_vrd_swmr_config failed\n");
+ return H5I_INVALID_HID;
+ }
+ return fapl;
+}
+
/*-------------------------------------------------------------------------
* Function: h5tools_set_fapl_vfd
*
@@ -484,7 +533,11 @@ h5tools_set_fapl_vfd(hid_t fapl_id, h5tools_vfd_info_t *vfd_info)
herr_t ret_value = SUCCEED;
/* Determine which driver the user wants to open the file with */
- if (!HDstrcmp(vfd_info->name, drivernames[SEC2_VFD_IDX])) {
+ if (!HDstrcmp(vfd_info->name, drivernames[SWMR_VFD_IDX])) {
+ /* SWMR driver */
+ if (swmr_fapl_augment(fapl_id, vfd_info->fname) < 0)
+ H5TOOLS_GOTO_ERROR(FAIL, "swmr_fapl_augment failed");
+ } else if (!HDstrcmp(vfd_info->name, drivernames[SEC2_VFD_IDX])) {
/* SEC2 Driver */
if (H5Pset_fapl_sec2(fapl_id) < 0)
H5TOOLS_GOTO_ERROR(FAIL, "H5Pset_fapl_sec2 failed");
@@ -948,7 +1001,15 @@ h5tools_fopen(const char *fname, unsigned flags, hid_t fapl_id, hbool_t use_spec
if (drivernum == LOG_VFD_IDX)
continue;
+ /* Skip the SWMR VFD, since it will start to wait to
+ * rendezvous with a writer, and that's not usually
+ * desired.
+ */
+ if (drivernum == SWMR_VFD_IDX)
+ continue;
+
vfd_info.info = NULL;
+ vfd_info.fname = fname;
vfd_info.name = drivernames[drivernum];
/* Get a fapl reflecting the selected VOL connector and VFD */
diff --git a/tools/lib/h5tools.h b/tools/lib/h5tools.h
index 152ec1a..14a5361 100644
--- a/tools/lib/h5tools.h
+++ b/tools/lib/h5tools.h
@@ -568,6 +568,9 @@ typedef struct h5tools_vfd_info_t {
/* Name of the VFD */
const char *name;
+
+ /* Name of the file to open with the VFD */
+ const char *fname;
} h5tools_vfd_info_t;
/* This enum should match the entries in the above 'volnames'
@@ -592,6 +595,7 @@ typedef enum {
MPIO_VFD_IDX,
ROS3_VFD_IDX,
HDFS_VFD_IDX,
+ SWMR_VFD_IDX,
} driver_idx;
/* The following include, h5tools_str.h, must be after the
diff --git a/tools/lib/h5tools_dump.c b/tools/lib/h5tools_dump.c
index b1be577..4ba89fa 100644
--- a/tools/lib/h5tools_dump.c
+++ b/tools/lib/h5tools_dump.c
@@ -81,7 +81,7 @@ h5tool_format_t h5tools_dataformat = {
1, /*skip_first */
1, /*obj_hidefileno */
- " "H5_PRINTF_HADDR_FMT, /*obj_format */
+ " %" PRIuHADDR, /*obj_format */
1, /*dset_hidefileno */
"DATASET %s ", /*dset_format */
@@ -3292,7 +3292,7 @@ h5tools_dump_dcpl(FILE *stream, const h5tool_format_t *info,
if (HADDR_UNDEF == ioffset)
h5tools_str_append(&buffer, "OFFSET HADDR_UNDEF");
else
- h5tools_str_append(&buffer, "OFFSET "H5_PRINTF_HADDR_FMT, ioffset);
+ h5tools_str_append(&buffer, "OFFSET %" PRIuHADDR, ioffset);
h5tools_render_element(stream, info, ctx, &buffer, &curr_pos, (size_t) ncols, (hsize_t) 0, (hsize_t) 0);
}
}
diff --git a/tools/libtest/h5tools_test_utils.c b/tools/libtest/h5tools_test_utils.c
index 12360a7..4b50c7c 100644
--- a/tools/libtest/h5tools_test_utils.c
+++ b/tools/libtest/h5tools_test_utils.c
@@ -1173,6 +1173,7 @@ test_set_configured_fapl(void)
/* test */
vfd_info.info = C.conf_fa;
vfd_info.name = C.vfdname;
+ vfd_info.fname = "ignore";
result = h5tools_get_fapl(H5P_DEFAULT, NULL, &vfd_info);
if (C.expected == 0)
JSVERIFY( result, H5I_INVALID_HID, C.message)
diff --git a/tools/src/h5dump/h5dump_xml.c b/tools/src/h5dump/h5dump_xml.c
index bb47f77..696841d 100644
--- a/tools/src/h5dump/h5dump_xml.c
+++ b/tools/src/h5dump/h5dump_xml.c
@@ -93,7 +93,7 @@ static h5tool_format_t xml_dataformat = {
1, /*skip_first */
1, /*obj_hidefileno */
- " "H5_PRINTF_HADDR_FMT, /*obj_format */
+ " %" PRIuHADDR, /*obj_format */
1, /*dset_hidefileno */
"DATASET %s ", /*dset_format */
diff --git a/tools/src/h5ls/h5ls.c b/tools/src/h5ls/h5ls.c
index e239cbc..bbfbc79 100644
--- a/tools/src/h5ls/h5ls.c
+++ b/tools/src/h5ls/h5ls.c
@@ -96,7 +96,7 @@ static h5tool_format_t ls_dataformat = {
0, /*skip_first */
0, /*obj_hidefileno */
- "-%lu:"H5_PRINTF_HADDR_FMT, /*obj_format */
+ "-%lu:%" PRIuHADDR, /*obj_format */
0, /*dset_hidefileno */
"DSET-%s ", /*dset_format */
@@ -121,6 +121,7 @@ typedef struct {
/* Command-line switches */
static int verbose_g = 0; /* lots of extra output */
static int width_g = 80; /* output width in characters */
+static hbool_t vfd_swmr_poll_g = FALSE; /* poll for changes using VFD SWMR */
static hbool_t address_g = FALSE; /* print raw data addresses */
static hbool_t data_g = FALSE; /* display dataset values? */
static hbool_t label_g = FALSE; /* label compound values? */
@@ -171,6 +172,7 @@ usage (void)
{
FLUSHSTREAM(rawoutstream);
PRINTVALSTREAM(rawoutstream, "usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]\n");
+ PRINTVALSTREAM(rawoutstream, " h5ls [OPTIONS] --poll file\n");
PRINTVALSTREAM(rawoutstream, " OPTIONS\n");
PRINTVALSTREAM(rawoutstream, " -h, -?, --help Print a usage message and exit\n");
PRINTVALSTREAM(rawoutstream, " -a, --address Print raw data address. If dataset is contiguous, address\n");
@@ -201,6 +203,7 @@ usage (void)
PRINTVALSTREAM(rawoutstream, " -f, --full Print full path names instead of base names\n");
PRINTVALSTREAM(rawoutstream, " -g, --group Show information about a group, not its contents\n");
PRINTVALSTREAM(rawoutstream, " -l, --label Label members of compound datasets\n");
+ PRINTVALSTREAM(rawoutstream, " -p, --poll Continuously re-read and re-display the input file using VFD SWMR\n");
PRINTVALSTREAM(rawoutstream, " -r, --recursive List all groups recursively, avoiding cycles\n");
PRINTVALSTREAM(rawoutstream, " -s, --string Print 1-byte integer datasets as ASCII\n");
PRINTVALSTREAM(rawoutstream, " -S, --simple Use a machine-readable output format\n");
@@ -1418,6 +1421,8 @@ dump_dataset_values(hid_t dset)
ctx.need_prefix = TRUE;
ctx.cur_column = (size_t)curr_pos;
+ if (vfd_swmr_poll_g)
+ H5Drefresh(dset);
if (H5Tget_class(f_type) == H5T_REFERENCE) {
H5TOOLS_DEBUG("reference class type");
if (!H5Tequal(f_type, H5T_STD_REF) && !H5Tequal(f_type, H5T_STD_REF_DSETREG) && !H5Tequal(f_type, H5T_STD_REF_OBJ)) {
@@ -2620,11 +2625,12 @@ main(int argc, const char *argv[])
char *fname = NULL, *oname = NULL, *x;
const char *s = NULL;
char *rest;
- int argno;
+ int argno, times;
static char root_name[] = "/";
char drivername[50];
const char *preferred_driver = NULL;
int err_exit = 0;
+ uint64_t poll_nanosecs = 1000;
hid_t fapl_id = H5P_DEFAULT;
hbool_t custom_vol_fapl = FALSE;
h5tools_vol_info_t vol_info;
@@ -2704,8 +2710,20 @@ main(int argc, const char *argv[])
}
else if (!HDstrcmp(argv[argno], "--full")) {
fullname_g = TRUE;
- }
- else if (!HDstrcmp(argv[argno], "--group")) {
+ } else if(!HDstrncmp(argv[argno], "--poll=", strlen("--poll="))) {
+ int nscanned = 0, rc;
+ uint64_t poll_millisecs;
+ rc = sscanf(argv[argno], "--poll=%" SCNu64 "%n", &poll_millisecs,
+ &nscanned);
+ if (rc != 1 || argv[argno][nscanned] != '\0') {
+ usage();
+ leave(EXIT_FAILURE);
+ }
+ poll_nanosecs = poll_millisecs * 1000000;
+ vfd_swmr_poll_g = TRUE;
+ } else if(!HDstrcmp(argv[argno], "--poll")) {
+ vfd_swmr_poll_g = TRUE;
+ } else if (!HDstrcmp(argv[argno], "--group")) {
grp_literal_g = TRUE;
}
else if (!HDstrcmp(argv[argno], "--label")) {
@@ -2877,6 +2895,10 @@ main(int argc, const char *argv[])
label_g = TRUE;
break;
+ case 'p': /* --poll */
+ vfd_swmr_poll_g = TRUE;
+ break;
+
case 'r': /* --recursive */
recursive_g = TRUE;
fullname_g = TRUE;
@@ -2926,6 +2948,10 @@ main(int argc, const char *argv[])
leave(EXIT_FAILURE);
} /* end if */
+ if (vfd_swmr_poll_g && argc > 1 + argno) {
+ HDfprintf(rawerrorstream, "Error: -p / --poll is limited to only one file[/OBJECT]\n\n");
+ leave(EXIT_FAILURE);
+ }
/* Check for conflicting arguments */
if (!is_valid_args()) {
usage();
@@ -2988,6 +3014,10 @@ main(int argc, const char *argv[])
symlink_trav_t symlink_list;
size_t u;
+ if (vfd_swmr_poll_g) {
+ preferred_driver = "swmr";
+ }
+
fname = HDstrdup(argv[argno++]);
oname = NULL;
file_id = H5I_INVALID_HID;
@@ -3000,8 +3030,8 @@ main(int argc, const char *argv[])
if (verbose_g)
PRINTSTREAM(rawoutstream, "Opened \"%s\" with %s driver.\n", fname, drivername);
break; /*success*/
- } /* end if */
-
+ } else if (vfd_swmr_poll_g)
+ break;
/* Shorten the file name; lengthen the object name */
x = oname;
oname = HDstrrchr(fname, '/');
@@ -3077,17 +3107,27 @@ main(int argc, const char *argv[])
else
li.type = H5L_TYPE_HARD;
- /* Open the object and display it's information */
- if (li.type == H5L_TYPE_HARD) {
- if (visit_obj(file_id, oname, &iter) < 0) {
- leave(EXIT_FAILURE);
+ for (times = 0; vfd_swmr_poll_g || times < 1; times++) {
+ if (vfd_swmr_poll_g) {
+ int i;
+ for (i = 0; i < 3; i++)
+ printf("\n");
}
- } /* end if(li.type == H5L_TYPE_HARD) */
- else {
- /* Specified name is not for object -- list that link */
- /* Use file_id ID for root group ID */
- iter.gid = file_id;
- list_lnk(oname, &li, &iter);
+ /* Open the object and display it's information */
+ if (li.type == H5L_TYPE_HARD) {
+ if (visit_obj(file_id, oname, &iter) < 0) {
+ leave(EXIT_FAILURE);
+ }
+ } /* end if(li.type == H5L_TYPE_HARD) */
+ else {
+ /* Specified name is not for object -- list that link */
+ /* Use file_id ID for root group ID */
+ iter.gid = file_id;
+ list_lnk(oname, &li, &iter);
+ }
+#if 1
+ H5_nanosleep(poll_nanosecs);
+#endif
}
H5Fclose(file_id);
HDfree(fname);
diff --git a/tools/src/h5stat/h5stat.c b/tools/src/h5stat/h5stat.c
index ec4b5ab..1d587e1 100644
--- a/tools/src/h5stat/h5stat.c
+++ b/tools/src/h5stat/h5stat.c
@@ -1800,6 +1800,8 @@ main(int argc, const char *argv[])
if(parse_command_line(argc, argv, &hand) < 0)
goto done;
+ fname = argv[opt_ind];
+
/* enable error reporting if command line option */
h5tools_error_report();
@@ -1808,6 +1810,7 @@ main(int argc, const char *argv[])
vfd_info.info = NULL;
vfd_info.name = drivername;
+ vfd_info.fname = fname;
if (!HDstrcmp(drivername, drivernames[ROS3_VFD_IDX])) {
#ifdef H5_HAVE_ROS3_VFD
@@ -1832,8 +1835,6 @@ main(int argc, const char *argv[])
}
}
- fname = argv[opt_ind];
-
/* Check for filename given */
if(fname) {
hid_t fcpl;
diff --git a/tools/testfiles/help-1.ls b/tools/testfiles/help-1.ls
index 0926c4c..2167e00 100644
--- a/tools/testfiles/help-1.ls
+++ b/tools/testfiles/help-1.ls
@@ -1,4 +1,5 @@
usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
+ h5ls [OPTIONS] --poll file
OPTIONS
-h, -?, --help Print a usage message and exit
-a, --address Print raw data address. If dataset is contiguous, address
@@ -29,6 +30,7 @@ usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
-f, --full Print full path names instead of base names
-g, --group Show information about a group, not its contents
-l, --label Label members of compound datasets
+ -p, --poll Continuously re-read and re-display the input file using VFD SWMR
-r, --recursive List all groups recursively, avoiding cycles
-s, --string Print 1-byte integer datasets as ASCII
-S, --simple Use a machine-readable output format
diff --git a/tools/testfiles/help-2.ls b/tools/testfiles/help-2.ls
index 0926c4c..2167e00 100644
--- a/tools/testfiles/help-2.ls
+++ b/tools/testfiles/help-2.ls
@@ -1,4 +1,5 @@
usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
+ h5ls [OPTIONS] --poll file
OPTIONS
-h, -?, --help Print a usage message and exit
-a, --address Print raw data address. If dataset is contiguous, address
@@ -29,6 +30,7 @@ usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
-f, --full Print full path names instead of base names
-g, --group Show information about a group, not its contents
-l, --label Label members of compound datasets
+ -p, --poll Continuously re-read and re-display the input file using VFD SWMR
-r, --recursive List all groups recursively, avoiding cycles
-s, --string Print 1-byte integer datasets as ASCII
-S, --simple Use a machine-readable output format
diff --git a/tools/testfiles/help-3.ls b/tools/testfiles/help-3.ls
index 0926c4c..2167e00 100644
--- a/tools/testfiles/help-3.ls
+++ b/tools/testfiles/help-3.ls
@@ -1,4 +1,5 @@
usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
+ h5ls [OPTIONS] --poll file
OPTIONS
-h, -?, --help Print a usage message and exit
-a, --address Print raw data address. If dataset is contiguous, address
@@ -29,6 +30,7 @@ usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
-f, --full Print full path names instead of base names
-g, --group Show information about a group, not its contents
-l, --label Label members of compound datasets
+ -p, --poll Continuously re-read and re-display the input file using VFD SWMR
-r, --recursive List all groups recursively, avoiding cycles
-s, --string Print 1-byte integer datasets as ASCII
-S, --simple Use a machine-readable output format
diff --git a/tools/testfiles/textlinksrc-nodangle-1.ls b/tools/testfiles/textlinksrc-nodangle-1.ls
index 0926c4c..2167e00 100644
--- a/tools/testfiles/textlinksrc-nodangle-1.ls
+++ b/tools/testfiles/textlinksrc-nodangle-1.ls
@@ -1,4 +1,5 @@
usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
+ h5ls [OPTIONS] --poll file
OPTIONS
-h, -?, --help Print a usage message and exit
-a, --address Print raw data address. If dataset is contiguous, address
@@ -29,6 +30,7 @@ usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
-f, --full Print full path names instead of base names
-g, --group Show information about a group, not its contents
-l, --label Label members of compound datasets
+ -p, --poll Continuously re-read and re-display the input file using VFD SWMR
-r, --recursive List all groups recursively, avoiding cycles
-s, --string Print 1-byte integer datasets as ASCII
-S, --simple Use a machine-readable output format
diff --git a/tools/testfiles/tgroup-1.ls b/tools/testfiles/tgroup-1.ls
index 0926c4c..2167e00 100644
--- a/tools/testfiles/tgroup-1.ls
+++ b/tools/testfiles/tgroup-1.ls
@@ -1,4 +1,5 @@
usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
+ h5ls [OPTIONS] --poll file
OPTIONS
-h, -?, --help Print a usage message and exit
-a, --address Print raw data address. If dataset is contiguous, address
@@ -29,6 +30,7 @@ usage: h5ls [OPTIONS] file[/OBJECT] [file[/[OBJECT]...]
-f, --full Print full path names instead of base names
-g, --group Show information about a group, not its contents
-l, --label Label members of compound datasets
+ -p, --poll Continuously re-read and re-display the input file using VFD SWMR
-r, --recursive List all groups recursively, avoiding cycles
-s, --string Print 1-byte integer datasets as ASCII
-S, --simple Use a machine-readable output format