diff options
206 files changed, 32165 insertions, 4598 deletions
@@ -1,5 +1,11 @@ # .gitignore file for HDF5 +# ctags +**/tags + +# vim swap files +**/.*.swp + # Makefile.in files **/Makefile.in @@ -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 @@ -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 @@ -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 Binary files differnew file mode 100644 index 0000000..384628f --- /dev/null +++ b/doc/VFD_SWMR_RFC_190902.docx diff --git a/doc/VFD_SWMR_RFC_200107.docx b/doc/VFD_SWMR_RFC_200107.docx Binary files differnew file mode 100644 index 0000000..4f38846 --- /dev/null +++ b/doc/VFD_SWMR_RFC_200107.docx diff --git a/doc/VFD_SWMR_RFC_2020-02-03.docx b/doc/VFD_SWMR_RFC_2020-02-03.docx Binary files differnew file mode 100644 index 0000000..ef9a9db --- /dev/null +++ b/doc/VFD_SWMR_RFC_2020-02-03.docx 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 @@ -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 */ /*******************/ @@ -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 */ }}; /*******************/ @@ -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 */ }}; @@ -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 */ }}; @@ -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 */ }}; @@ -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 */ }}; @@ -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); @@ -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 = §ion[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 = §ion[0], *middle = §ion[1], + *tail = §ion[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 = §ion[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 = §ion[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 = §ion[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 \ @@ -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, ×tamp_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, ×tamp_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 Binary files differindex 667c67f..c8241a6 100644 --- a/test/testfiles/plist_files/dcpl_32be +++ b/test/testfiles/plist_files/dcpl_32be diff --git a/test/testfiles/plist_files/dcpl_32le b/test/testfiles/plist_files/dcpl_32le Binary files differindex 667c67f..c8241a6 100644 --- a/test/testfiles/plist_files/dcpl_32le +++ b/test/testfiles/plist_files/dcpl_32le diff --git a/test/testfiles/plist_files/dcpl_64be b/test/testfiles/plist_files/dcpl_64be Binary files differindex 667c67f..c8241a6 100644 --- a/test/testfiles/plist_files/dcpl_64be +++ b/test/testfiles/plist_files/dcpl_64be diff --git a/test/testfiles/plist_files/dcpl_64le b/test/testfiles/plist_files/dcpl_64le Binary files differindex 667c67f..c8241a6 100644 --- a/test/testfiles/plist_files/dcpl_64le +++ b/test/testfiles/plist_files/dcpl_64le diff --git a/test/testfiles/plist_files/def_dcpl_32be b/test/testfiles/plist_files/def_dcpl_32be Binary files differindex 01b233c..0909391 100644 --- a/test/testfiles/plist_files/def_dcpl_32be +++ b/test/testfiles/plist_files/def_dcpl_32be diff --git a/test/testfiles/plist_files/def_dcpl_32le b/test/testfiles/plist_files/def_dcpl_32le Binary files differindex 01b233c..0909391 100644 --- a/test/testfiles/plist_files/def_dcpl_32le +++ b/test/testfiles/plist_files/def_dcpl_32le diff --git a/test/testfiles/plist_files/def_dcpl_64be b/test/testfiles/plist_files/def_dcpl_64be Binary files differindex 01b233c..0909391 100644 --- a/test/testfiles/plist_files/def_dcpl_64be +++ b/test/testfiles/plist_files/def_dcpl_64be diff --git a/test/testfiles/plist_files/def_dcpl_64le b/test/testfiles/plist_files/def_dcpl_64le Binary files differindex 01b233c..0909391 100644 --- a/test/testfiles/plist_files/def_dcpl_64le +++ b/test/testfiles/plist_files/def_dcpl_64le diff --git a/test/testfiles/plist_files/def_dxpl_32be b/test/testfiles/plist_files/def_dxpl_32be Binary files differindex b13f456..da29037 100644 --- a/test/testfiles/plist_files/def_dxpl_32be +++ b/test/testfiles/plist_files/def_dxpl_32be diff --git a/test/testfiles/plist_files/def_dxpl_32le b/test/testfiles/plist_files/def_dxpl_32le Binary files differindex b13f456..da29037 100644 --- a/test/testfiles/plist_files/def_dxpl_32le +++ b/test/testfiles/plist_files/def_dxpl_32le diff --git a/test/testfiles/plist_files/def_dxpl_64be b/test/testfiles/plist_files/def_dxpl_64be Binary files differindex b13f456..da29037 100644 --- a/test/testfiles/plist_files/def_dxpl_64be +++ b/test/testfiles/plist_files/def_dxpl_64be diff --git a/test/testfiles/plist_files/def_dxpl_64le b/test/testfiles/plist_files/def_dxpl_64le Binary files differindex b13f456..da29037 100644 --- a/test/testfiles/plist_files/def_dxpl_64le +++ b/test/testfiles/plist_files/def_dxpl_64le diff --git a/test/testfiles/plist_files/def_fapl_32be b/test/testfiles/plist_files/def_fapl_32be Binary files differindex 53ef572..6738cda 100644 --- a/test/testfiles/plist_files/def_fapl_32be +++ b/test/testfiles/plist_files/def_fapl_32be diff --git a/test/testfiles/plist_files/def_fapl_32le b/test/testfiles/plist_files/def_fapl_32le Binary files differindex 53ef572..6738cda 100644 --- a/test/testfiles/plist_files/def_fapl_32le +++ b/test/testfiles/plist_files/def_fapl_32le diff --git a/test/testfiles/plist_files/def_fapl_64be b/test/testfiles/plist_files/def_fapl_64be Binary files differindex 53ef572..6738cda 100644 --- a/test/testfiles/plist_files/def_fapl_64be +++ b/test/testfiles/plist_files/def_fapl_64be diff --git a/test/testfiles/plist_files/def_fapl_64le b/test/testfiles/plist_files/def_fapl_64le Binary files differindex 53ef572..6738cda 100644 --- a/test/testfiles/plist_files/def_fapl_64le +++ b/test/testfiles/plist_files/def_fapl_64le diff --git a/test/testfiles/plist_files/dxpl_32be b/test/testfiles/plist_files/dxpl_32be Binary files differindex 5ff2ea0..cac7e50 100644 --- a/test/testfiles/plist_files/dxpl_32be +++ b/test/testfiles/plist_files/dxpl_32be diff --git a/test/testfiles/plist_files/dxpl_32le b/test/testfiles/plist_files/dxpl_32le Binary files differindex 5ff2ea0..cac7e50 100644 --- a/test/testfiles/plist_files/dxpl_32le +++ b/test/testfiles/plist_files/dxpl_32le diff --git a/test/testfiles/plist_files/dxpl_64be b/test/testfiles/plist_files/dxpl_64be Binary files differindex 5ff2ea0..cac7e50 100644 --- a/test/testfiles/plist_files/dxpl_64be +++ b/test/testfiles/plist_files/dxpl_64be diff --git a/test/testfiles/plist_files/dxpl_64le b/test/testfiles/plist_files/dxpl_64le Binary files differindex 5ff2ea0..cac7e50 100644 --- a/test/testfiles/plist_files/dxpl_64le +++ b/test/testfiles/plist_files/dxpl_64le diff --git a/test/testfiles/plist_files/fapl_32be b/test/testfiles/plist_files/fapl_32be Binary files differindex d89a44c..807c04a 100644 --- a/test/testfiles/plist_files/fapl_32be +++ b/test/testfiles/plist_files/fapl_32be diff --git a/test/testfiles/plist_files/fapl_32le b/test/testfiles/plist_files/fapl_32le Binary files differindex d89a44c..807c04a 100644 --- a/test/testfiles/plist_files/fapl_32le +++ b/test/testfiles/plist_files/fapl_32le diff --git a/test/testfiles/plist_files/fapl_64be b/test/testfiles/plist_files/fapl_64be Binary files differindex d89a44c..807c04a 100644 --- a/test/testfiles/plist_files/fapl_64be +++ b/test/testfiles/plist_files/fapl_64be diff --git a/test/testfiles/plist_files/fapl_64le b/test/testfiles/plist_files/fapl_64le Binary files differindex d89a44c..807c04a 100644 --- a/test/testfiles/plist_files/fapl_64le +++ b/test/testfiles/plist_files/fapl_64le diff --git a/test/testfiles/plist_files/lapl_32be b/test/testfiles/plist_files/lapl_32be Binary files differindex eee238e..3db7163 100644 --- a/test/testfiles/plist_files/lapl_32be +++ b/test/testfiles/plist_files/lapl_32be diff --git a/test/testfiles/plist_files/lapl_32le b/test/testfiles/plist_files/lapl_32le Binary files differindex eee238e..3db7163 100644 --- a/test/testfiles/plist_files/lapl_32le +++ b/test/testfiles/plist_files/lapl_32le diff --git a/test/testfiles/plist_files/lapl_64be b/test/testfiles/plist_files/lapl_64be Binary files differindex eee238e..3db7163 100644 --- a/test/testfiles/plist_files/lapl_64be +++ b/test/testfiles/plist_files/lapl_64be diff --git a/test/testfiles/plist_files/lapl_64le b/test/testfiles/plist_files/lapl_64le Binary files differindex eee238e..3db7163 100644 --- a/test/testfiles/plist_files/lapl_64le +++ b/test/testfiles/plist_files/lapl_64le 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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], ¬ify, 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 |