summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQuincey Koziol <koziol@hdfgroup.org>2016-12-02 16:07:04 (GMT)
committerQuincey Koziol <koziol@hdfgroup.org>2016-12-02 16:07:04 (GMT)
commit64a339183f0e4532597744351548308203e993c8 (patch)
treeddfff2f15c0f3a01782f191a847665c6eceadd42
parent5d7d029b97b36d1c380cef82d637342921bf3a1d (diff)
downloadhdf5-64a339183f0e4532597744351548308203e993c8.zip
hdf5-64a339183f0e4532597744351548308203e993c8.tar.gz
hdf5-64a339183f0e4532597744351548308203e993c8.tar.bz2
Bring SWMR support in to the main development branch. (Finally!) More tests
and the tool and API wrappers will be coming in over the weekend.
-rw-r--r--MANIFEST7
-rw-r--r--c++/test/tfile.cpp2
-rw-r--r--hl/test/test_file_image.c36
-rw-r--r--src/CMakeLists.txt6
-rw-r--r--src/H5AC.c189
-rw-r--r--src/H5AClog.c140
-rw-r--r--src/H5ACmpio.c57
-rw-r--r--src/H5ACpkg.h8
-rw-r--r--src/H5ACprivate.h34
-rw-r--r--src/H5ACproxy_entry.c796
-rw-r--r--src/H5B2.c229
-rw-r--r--src/H5B2cache.c254
-rw-r--r--src/H5B2dbg.c11
-rw-r--r--src/H5B2hdr.c76
-rw-r--r--src/H5B2int.c2423
-rw-r--r--src/H5B2internal.c1443
-rw-r--r--src/H5B2leaf.c1065
-rw-r--r--src/H5B2pkg.h118
-rw-r--r--src/H5B2private.h4
-rw-r--r--src/H5B2stat.c2
-rw-r--r--src/H5B2test.c35
-rw-r--r--src/H5Bdbg.c3
-rw-r--r--src/H5C.c276
-rw-r--r--src/H5Cpkg.h8
-rw-r--r--src/H5Cprivate.h8
-rw-r--r--src/H5Ctag.c6
-rw-r--r--src/H5Dbtree.c1
-rw-r--r--src/H5Dbtree2.c83
-rw-r--r--src/H5Dchunk.c9
-rw-r--r--src/H5Dearray.c94
-rw-r--r--src/H5Dfarray.c94
-rw-r--r--src/H5Dnone.c1
-rw-r--r--src/H5Dpkg.h1
-rw-r--r--src/H5Dsingle.c1
-rw-r--r--src/H5Dvirtual.c2
-rw-r--r--src/H5EA.c24
-rw-r--r--src/H5EAcache.c105
-rw-r--r--src/H5EAdblkpage.c37
-rw-r--r--src/H5EAdblock.c40
-rw-r--r--src/H5EAhdr.c44
-rw-r--r--src/H5EAiblock.c39
-rw-r--r--src/H5EApkg.h34
-rw-r--r--src/H5EAprivate.h2
-rw-r--r--src/H5EAsblock.c37
-rw-r--r--src/H5F.c240
-rw-r--r--src/H5FA.c50
-rw-r--r--src/H5FAcache.c212
-rw-r--r--src/H5FAdblkpage.c38
-rw-r--r--src/H5FAdblock.c37
-rw-r--r--src/H5FAhdr.c44
-rw-r--r--src/H5FAint.c139
-rw-r--r--src/H5FApkg.h34
-rw-r--r--src/H5FAprivate.h2
-rw-r--r--src/H5FD.c29
-rw-r--r--src/H5FDint.c13
-rw-r--r--src/H5FDlog.c1
-rw-r--r--src/H5FDpkg.h1
-rw-r--r--src/H5FDprivate.h1
-rw-r--r--src/H5FDpublic.h6
-rw-r--r--src/H5FDsec2.c1
-rw-r--r--src/H5FDstdio.c6
-rw-r--r--src/H5FDtest.c119
-rw-r--r--src/H5FS.c1
-rw-r--r--src/H5FScache.c65
-rw-r--r--src/H5FSint.c145
-rw-r--r--src/H5FSpkg.h7
-rw-r--r--src/H5Faccum.c6
-rw-r--r--src/H5Fint.c156
-rw-r--r--src/H5Fio.c64
-rw-r--r--src/H5Fpkg.h26
-rw-r--r--src/H5Fprivate.h5
-rw-r--r--src/H5Fpublic.h18
-rw-r--r--src/H5Fsuper.c26
-rw-r--r--src/H5Gent.c2
-rw-r--r--src/H5Gtest.c2
-rw-r--r--src/H5Lexternal.c2
-rw-r--r--src/H5O.c125
-rw-r--r--src/H5Oalloc.c90
-rw-r--r--src/H5Oattribute.c10
-rw-r--r--src/H5Ocache.c99
-rw-r--r--src/H5Ochunk.c4
-rw-r--r--src/H5Ocopy.c12
-rw-r--r--src/H5Odbg.c2
-rw-r--r--src/H5Oflush.c11
-rw-r--r--src/H5Omessage.c8
-rw-r--r--src/H5Opkg.h26
-rw-r--r--src/H5Oprivate.h7
-rw-r--r--src/H5Otest.c176
-rw-r--r--src/H5Pfapl.c9
-rw-r--r--src/H5Plapl.c4
-rw-r--r--src/H5SM.c2
-rw-r--r--src/H5err.txt2
-rw-r--r--src/Makefile.am12
-rw-r--r--test/CMakeLists.txt1
-rw-r--r--test/CMakeTests.cmake1
-rw-r--r--test/Makefile.am9
-rw-r--r--test/dsets.c660
-rw-r--r--test/swmr.c7131
-rw-r--r--test/tfile.c141
-rw-r--r--testpar/t_cache.c82
100 files changed, 15534 insertions, 2472 deletions
diff --git a/MANIFEST b/MANIFEST
index 7baa899..7ffef1d 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -478,6 +478,7 @@
./src/H5ACpkg.h
./src/H5ACprivate.h
./src/H5ACpublic.h
+./src/H5ACproxy_entry.c
./src/H5B.c
./src/H5Bcache.c
./src/H5Bdbg.c
@@ -490,6 +491,8 @@
./src/H5B2dbg.c
./src/H5B2hdr.c
./src/H5B2int.c
+./src/H5B2internal.c
+./src/H5B2leaf.c
./src/H5B2module.h
./src/H5B2pkg.h
./src/H5B2private.h
@@ -584,6 +587,7 @@
./src/H5FAdblkpage.c
./src/H5FAdblock.c
./src/H5FAhdr.c
+./src/H5FAint.c
./src/H5FAmodule.h
./src/H5FApkg.h
./src/H5FAprivate.h
@@ -615,6 +619,7 @@
./src/H5FDspace.c
./src/H5FDstdio.c
./src/H5FDstdio.h
+./src/H5FDtest.c
./src/H5FDwindows.c
./src/H5FDwindows.h
./src/H5FL.c
@@ -625,6 +630,7 @@
./src/H5FS.c
./src/H5FScache.c
./src/H5FSdbg.c
+./src/H5FSint.c
./src/H5FSmodule.h
./src/H5FSpkg.h
./src/H5FSprivate.h
@@ -990,6 +996,7 @@
# ====end distribute this for now. See HDFFV-8236====
./test/specmetaread.h5
./test/stab.c
+./test/swmr.c
./test/tarray.c
./test/tarrold.h5
./test/tattr.c
diff --git a/c++/test/tfile.cpp b/c++/test/tfile.cpp
index dba0980..47b9a85 100644
--- a/c++/test/tfile.cpp
+++ b/c++/test/tfile.cpp
@@ -727,7 +727,7 @@ static void test_libver_bounds()
/* Run the tests */
test_libver_bounds_real(H5F_LIBVER_EARLIEST, H5O_VERSION_1, H5F_LIBVER_LATEST, H5O_VERSION_2);
- test_libver_bounds_real(H5F_LIBVER_LATEST, H5O_VERSION_2, H5F_LIBVER_EARLIEST, H5O_VERSION_1);
+ test_libver_bounds_real(H5F_LIBVER_LATEST, H5O_VERSION_2, H5F_LIBVER_EARLIEST, H5O_VERSION_2);
PASSED();
} /* end test_libver_bounds() */
diff --git a/hl/test/test_file_image.c b/hl/test/test_file_image.c
index 9b18539..6ff5bf4 100644
--- a/hl/test/test_file_image.c
+++ b/hl/test/test_file_image.c
@@ -20,6 +20,12 @@
#define RANK 2
+/* For superblock version 0, 1: the offset to "file consistency flags" is 20 with size of 4 bytes */
+/* The file consistency flags is the "status_flags" field in H5F_super_t */
+/* Note: the offset and size will be different when using superblock version 2 for the test file */
+#define SUPER_STATUS_FLAGS_OFF_V0_V1 20
+#define SUPER_STATUS_FLAGS_SIZE_V0_V1 4
+
/* Test of file image operations.
The following code provides a means to thoroughly test the file image
@@ -214,10 +220,32 @@ test_file_image(size_t open_images, size_t nflags, unsigned *flags)
else
VERIFY(*core_buf_ptr_ptr != buf_ptr[i], "vfd buffer and user buffer should be different");
- /* test whether the contents of the user buffer and driver buffer */
- /* are equal. */
- if (HDmemcmp(*core_buf_ptr_ptr, buf_ptr[i], (size_t)buf_size[i]) != 0)
- FAIL_PUTS_ERROR("comparison of vfd and user buffer failed");
+ /*
+ * When the vfd and user buffers are different and H5LT_FILE_IMAGE_OPEN_RW is enabled,
+ * status_flags in the superblock needs to be cleared in the vfd buffer for
+ * the comparison to proceed as expected. The user buffer as returned from H5Fget_file_image()
+ * has already cleared status_flags. The superblock's status_flags is used for the
+ * implementation of file locking.
+ */
+ if(input_flags[i] & H5LT_FILE_IMAGE_OPEN_RW && !(input_flags[i] & H5LT_FILE_IMAGE_DONT_COPY)) {
+
+ void *tmp_ptr = HDmalloc((size_t)buf_size[i]);
+ /* Copy vfd buffer to a temporary buffer */
+ HDmemcpy(tmp_ptr, (void *)*core_buf_ptr_ptr, (size_t)buf_size[i]);
+ /* Clear status_flags in the superblock for the vfd buffer: file locking is using status_flags */
+ HDmemset((uint8_t *)tmp_ptr + SUPER_STATUS_FLAGS_OFF_V0_V1, (int)0, (size_t)SUPER_STATUS_FLAGS_SIZE_V0_V1);
+ /* Does the comparision */
+ if(HDmemcmp(tmp_ptr, buf_ptr[i], (size_t)buf_size[i]) != 0)
+ FAIL_PUTS_ERROR("comparison of TMP vfd and user buffer failed");
+ /* Free the temporary buffer */
+ if(tmp_ptr) HDfree(tmp_ptr);
+ } else {
+
+ /* test whether the contents of the user buffer and driver buffer */
+ /* are equal. */
+ if (HDmemcmp(*core_buf_ptr_ptr, buf_ptr[i], (size_t)buf_size[i]) != 0)
+ FAIL_PUTS_ERROR("comparison of vfd and user buffer failed");
+ }
} /* end else */
} /* end for */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a4c6367..55de5ea 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -47,6 +47,7 @@ set (H5AC_SOURCES
${HDF5_SRC_DIR}/H5ACdbg.c
${HDF5_SRC_DIR}/H5AClog.c
${HDF5_SRC_DIR}/H5ACmpio.c
+ ${HDF5_SRC_DIR}/H5ACproxy_entry.c
)
set (H5AC_HDRS
@@ -73,6 +74,8 @@ set (H5B2_SOURCES
${HDF5_SRC_DIR}/H5B2dbg.c
${HDF5_SRC_DIR}/H5B2hdr.c
${HDF5_SRC_DIR}/H5B2int.c
+ ${HDF5_SRC_DIR}/H5B2internal.c
+ ${HDF5_SRC_DIR}/H5B2leaf.c
${HDF5_SRC_DIR}/H5B2stat.c
${HDF5_SRC_DIR}/H5B2test.c
)
@@ -209,6 +212,7 @@ set (H5FA_SOURCES
${HDF5_SRC_DIR}/H5FAdblkpage.c
${HDF5_SRC_DIR}/H5FAdblock.c
${HDF5_SRC_DIR}/H5FAhdr.c
+ ${HDF5_SRC_DIR}/H5FAint.c
${HDF5_SRC_DIR}/H5FAstat.c
${HDF5_SRC_DIR}/H5FAtest.c
)
@@ -231,6 +235,7 @@ set (H5FD_SOURCES
${HDF5_SRC_DIR}/H5FDsec2.c
${HDF5_SRC_DIR}/H5FDspace.c
${HDF5_SRC_DIR}/H5FDstdio.c
+ ${HDF5_SRC_DIR}/H5FDtest.c
${HDF5_SRC_DIR}/H5FDwindows.c
)
@@ -271,6 +276,7 @@ set (H5FS_SOURCES
${HDF5_SRC_DIR}/H5FS.c
${HDF5_SRC_DIR}/H5FScache.c
${HDF5_SRC_DIR}/H5FSdbg.c
+ ${HDF5_SRC_DIR}/H5FSint.c
${HDF5_SRC_DIR}/H5FSsection.c
${HDF5_SRC_DIR}/H5FSstat.c
${HDF5_SRC_DIR}/H5FStest.c
diff --git a/src/H5AC.c b/src/H5AC.c
index 764dfcd..561f10c 100644
--- a/src/H5AC.c
+++ b/src/H5AC.c
@@ -135,6 +135,7 @@ static const char *H5AC_entry_type_names[H5AC_NTYPES] =
"fixed array data block pages",
"superblock",
"driver info",
+ "proxy entry",
"test entry" /* for testing only -- not used for actual files */
};
@@ -604,6 +605,52 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5AC_evict
+ *
+ * Purpose: Evict all entries except the pinned entries
+ * in the cache.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Vailin Choi
+ * Dec 2013
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_evict(H5F_t *f, hid_t dxpl_id)
+{
+ hbool_t log_enabled; /* TRUE if logging was set up */
+ hbool_t curr_logging; /* TRUE if currently logging */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(f->shared->cache);
+
+ /* Check if log messages are being emitted */
+ if(H5C_get_logging_status(f->shared->cache, &log_enabled, &curr_logging) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unable to get logging status")
+
+ /* Evict all entries in the cache except the pinned superblock entry */
+ if(H5C_evict(f, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "can't evict cache")
+
+done:
+
+ /* If currently logging, generate a message */
+ if(curr_logging)
+ if(H5AC__write_evict_cache_log_msg(f->shared->cache, ret_value) < 0)
+ HDONE_ERROR(H5E_CACHE, H5E_LOGFAIL, FAIL, "unable to emit log message")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC_evict() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5AC_expunge_entry
*
* Purpose: Expunge the target entry from the cache without writing it
@@ -1004,6 +1051,84 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5AC_mark_entry_clean
+ *
+ * Purpose: Mark a pinned entry as dirty. The target
+ * entry MUST be pinned.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * 7/23/16
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_mark_entry_clean(void *thing)
+{
+#if H5AC__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC__TRACE_FILE_ENABLED */
+ hbool_t log_enabled; /* TRUE if logging was set up */
+ hbool_t curr_logging; /* TRUE if currently logging */
+ H5AC_info_t *entry_ptr = NULL; /* Pointer to the cache entry */
+ H5C_t *cache_ptr = NULL; /* Pointer to the entry's associated metadata cache */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity check */
+ HDassert(thing);
+
+#if H5AC__TRACE_FILE_ENABLED
+ /* For the mark pinned or protected entry clean call, only the addr
+ * is really necessary in the trace file. Write the result to catch
+ * occult errors.
+ */
+ if(NULL != (trace_file_ptr = H5C_get_trace_file_ptr_from_entry(thing)))
+ sprintf(trace, "%s 0x%lx", FUNC,
+ (unsigned long)(((H5C_cache_entry_t *)thing)->addr));
+#endif /* H5AC__TRACE_FILE_ENABLED */
+
+ entry_ptr = (H5AC_info_t *)thing;
+ cache_ptr = entry_ptr->cache_ptr;
+
+ /* Check if log messages are being emitted */
+ if(H5C_get_logging_status(cache_ptr, &log_enabled, &curr_logging) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unable to get logging status")
+
+#ifdef H5_HAVE_PARALLEL
+{
+ H5AC_aux_t *aux_ptr;
+
+ aux_ptr = (H5AC_aux_t *)H5C_get_aux_ptr(cache_ptr);
+ if((!entry_ptr->is_dirty) && (!entry_ptr->is_protected) &&
+ (entry_ptr->is_pinned) && (NULL != aux_ptr))
+ if(H5AC__log_cleaned_entry(entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKCLEAN, FAIL, "can't log cleaned entry")
+}
+#endif /* H5_HAVE_PARALLEL */
+
+ if(H5C_mark_entry_clean(thing) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKCLEAN, FAIL, "can't mark pinned or protected entry clean")
+
+done:
+#if H5AC__TRACE_FILE_ENABLED
+ if(trace_file_ptr)
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+#endif /* H5AC__TRACE_FILE_ENABLED */
+
+ /* If currently logging, generate a message */
+ if(curr_logging)
+ if(H5AC__write_mark_clean_entry_log_msg(cache_ptr, entry_ptr, ret_value) < 0)
+ HDONE_ERROR(H5E_CACHE, H5E_LOGFAIL, FAIL, "unable to emit log message")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC_mark_entry_clean() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5AC_move_entry
*
* Purpose: Use this function to notify the cache that an object's
@@ -2619,3 +2744,67 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5AC_reset_ring() */
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_remove_entry()
+ *
+ * Purpose: Remove an entry from the cache. Must be not protected, pinned,
+ * dirty, involved in flush dependencies, etc.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_remove_entry(void *_entry)
+{
+ H5AC_info_t *entry = (H5AC_info_t *)_entry; /* Entry to remove */
+ H5C_t *cache = NULL; /* Pointer to the entry's associated metadata cache */
+#if H5AC__TRACE_FILE_ENABLED
+ char trace[128] = "";
+ FILE * trace_file_ptr = NULL;
+#endif /* H5AC__TRACE_FILE_ENABLED */
+ hbool_t log_enabled; /* TRUE if logging was set up */
+ hbool_t curr_logging; /* TRUE if currently logging */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(entry);
+ cache = entry->cache_ptr;
+ HDassert(cache);
+
+#if H5AC__TRACE_FILE_ENABLED
+ /* For the remove entry call, only the addr is really necessary
+ * in the trace file. Also write the result to catch occult errors.
+ */
+ if(NULL != (trace_file_ptr = H5C_get_trace_file_ptr_from_entry(entry)))
+ sprintf(trace, "%s 0x%lx", FUNC, (unsigned long)(entry->addr));
+#endif /* H5AC__TRACE_FILE_ENABLED */
+
+ /* Check if log messages are being emitted */
+ if(H5C_get_logging_status(cache, &log_enabled, &curr_logging) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, FAIL, "unable to get logging status")
+
+ /* Remove the entry from the cache*/
+ if(H5C_remove_entry(entry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry")
+
+done:
+#if H5AC__TRACE_FILE_ENABLED
+ if(trace_file_ptr)
+ HDfprintf(trace_file_ptr, "%s %d\n", trace, (int)ret_value);
+#endif /* H5AC__TRACE_FILE_ENABLED */
+
+ /* If currently logging, generate a message */
+ if(curr_logging)
+ if(H5AC__write_remove_entry_log_msg(cache, entry, ret_value) < 0)
+ HDONE_ERROR(H5E_CACHE, H5E_LOGFAIL, FAIL, "unable to emit log message")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC_remove_entry() */
+
diff --git a/src/H5AClog.c b/src/H5AClog.c
index 980b105..1cdaa00 100644
--- a/src/H5AClog.c
+++ b/src/H5AClog.c
@@ -206,6 +206,51 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5AC__write_evict_cache_log_msg
+ *
+ * Purpose: Write a log message for eviction of cache entries.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Dana Robinson
+ * Sunday, March 16, 2014
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC__write_evict_cache_log_msg(const H5AC_t *cache,
+ herr_t fxn_ret_value)
+{
+ char msg[MSG_SIZE];
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(cache);
+
+ /* Create the log message string */
+ HDsnprintf(msg, MSG_SIZE,
+"\
+{\
+\"timestamp\":%lld,\
+\"action\":\"evict\",\
+\"returned\":%d\
+},\n\
+"
+ , (long long)HDtime(NULL), (int)fxn_ret_value);
+
+ /* Write the log message to the file */
+ if(H5C_write_log_message(cache, msg) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unable to emit log message")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC__write_evict_cache_log_msg() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5AC__write_expunge_entry_log_msg
*
* Purpose: Write a log message for expunge of cache entries.
@@ -404,6 +449,53 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5AC__write_mark_clean_entry_log_msg
+ *
+ * Purpose: Write a log message for marking cache entries as clean.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Quincey Koziol
+ * Saturday, July 23, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC__write_mark_clean_entry_log_msg(const H5AC_t *cache, const H5AC_info_t *entry,
+ herr_t fxn_ret_value)
+{
+ char msg[MSG_SIZE]; /* Log message buffer */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(cache);
+ HDassert(entry);
+
+ /* Create the log message string */
+ HDsnprintf(msg, MSG_SIZE,
+"\
+{\
+\"timestamp\":%lld,\
+\"action\":\"clean\",\
+\"address\":0x%lx,\
+\"returned\":%d\
+},\n\
+"
+ , (long long)HDtime(NULL), (unsigned long)entry->addr, (int)fxn_ret_value);
+
+ /* Write the log message to the file */
+ if(H5C_write_log_message(cache, msg) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unable to emit log message")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC__write_mark_clean_entry_log_msg() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5AC__write_move_entry_log_msg
*
* Purpose: Write a log message for moving a cache entry.
@@ -870,3 +962,51 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5AC__write_set_cache_config_log_msg() */
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC__write_remove_entry_log_msg
+ *
+ * Purpose: Write a log message for removing a cache entry.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC__write_remove_entry_log_msg(const H5AC_t *cache, const H5AC_info_t *entry,
+ herr_t fxn_ret_value)
+{
+ char msg[MSG_SIZE];
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(cache);
+ HDassert(entry);
+
+ /* Create the log message string */
+ HDsnprintf(msg, MSG_SIZE,
+"\
+{\
+\"timestamp\":%lld,\
+\"action\":\"remove\",\
+\"address\":0x%lx,\
+\"returned\":%d\
+},\n\
+"
+ , (long long)HDtime(NULL), (unsigned long)entry->addr,
+ (int)fxn_ret_value);
+
+ /* Write the log message to the file */
+ if(H5C_write_log_message(cache, msg) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unable to emit log message")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC__write_remove_entry_log_msg() */
+
diff --git a/src/H5ACmpio.c b/src/H5ACmpio.c
index 89a8f9d..570783a 100644
--- a/src/H5ACmpio.c
+++ b/src/H5ACmpio.c
@@ -805,6 +805,63 @@ done:
/*-------------------------------------------------------------------------
*
+ * Function: H5AC__log_cleaned_entry()
+ *
+ * Purpose: Treat this operation as a 'clear' and remove the entry
+ * from both the cleaned and dirtied lists if it is present.
+ * Reduces the dirty_bytes count by the size of the entry.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: Quincey Koziol
+ * 7/23/16
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC__log_cleaned_entry(const H5AC_info_t *entry_ptr)
+{
+ H5AC_t * cache_ptr;
+ H5AC_aux_t * aux_ptr;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Sanity check */
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->is_dirty == FALSE);
+ cache_ptr = entry_ptr->cache_ptr;
+ HDassert(cache_ptr != NULL);
+ aux_ptr = (H5AC_aux_t *)H5C_get_aux_ptr(cache_ptr);
+ HDassert(aux_ptr != NULL);
+ HDassert(aux_ptr->magic == H5AC__H5AC_AUX_T_MAGIC);
+
+ if(aux_ptr->mpi_rank == 0) {
+ H5AC_slist_entry_t *slist_entry_ptr;
+ haddr_t addr = entry_ptr->addr;
+
+ /* Sanity checks */
+ HDassert(aux_ptr->d_slist_ptr != NULL);
+ HDassert(aux_ptr->c_slist_ptr != NULL);
+
+ /* Remove it from both the cleaned list and the dirtied list. */
+ if(NULL != (slist_entry_ptr = (H5AC_slist_entry_t *)H5SL_remove(aux_ptr->c_slist_ptr, (void *)(&addr))))
+ slist_entry_ptr = H5FL_FREE(H5AC_slist_entry_t, slist_entry_ptr);
+ if(NULL != (slist_entry_ptr = (H5AC_slist_entry_t *)H5SL_remove(aux_ptr->d_slist_ptr, (void *)(&addr))))
+ slist_entry_ptr = H5FL_FREE(H5AC_slist_entry_t, slist_entry_ptr);
+
+ } /* end if */
+
+ /* Decrement the dirty byte count */
+ aux_ptr->dirty_bytes -= entry_ptr->size;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC__log_cleaned_entry() */
+
+
+/*-------------------------------------------------------------------------
+ *
* Function: H5AC__log_flushed_entry()
*
* Purpose: Update the clean entry slist for the flush of an entry --
diff --git a/src/H5ACpkg.h b/src/H5ACpkg.h
index a298f85..3900ff1 100644
--- a/src/H5ACpkg.h
+++ b/src/H5ACpkg.h
@@ -412,6 +412,7 @@ typedef struct H5AC_aux_t
/* Parallel I/O routines */
H5_DLL herr_t H5AC__log_deleted_entry(const H5AC_info_t *entry_ptr);
H5_DLL herr_t H5AC__log_dirtied_entry(const H5AC_info_t *entry_ptr);
+H5_DLL herr_t H5AC__log_cleaned_entry(const H5AC_info_t *entry_ptr);
H5_DLL herr_t H5AC__log_flushed_entry(H5C_t *cache_ptr, haddr_t addr,
hbool_t was_dirty, unsigned flags);
H5_DLL herr_t H5AC__log_inserted_entry(const H5AC_info_t *entry_ptr);
@@ -432,6 +433,8 @@ H5_DLL herr_t H5AC__open_trace_file(H5AC_t *cache_ptr, const char *trace_file_na
/* Cache logging routines */
H5_DLL herr_t H5AC__write_create_cache_log_msg(H5AC_t *cache);
H5_DLL herr_t H5AC__write_destroy_cache_log_msg(H5AC_t *cache);
+H5_DLL herr_t H5AC__write_evict_cache_log_msg(const H5AC_t *cache,
+ herr_t fxn_ret_value);
H5_DLL herr_t H5AC__write_expunge_entry_log_msg(const H5AC_t *cache,
haddr_t address,
int type_id,
@@ -447,6 +450,8 @@ H5_DLL herr_t H5AC__write_insert_entry_log_msg(const H5AC_t *cache,
H5_DLL herr_t H5AC__write_mark_dirty_entry_log_msg(const H5AC_t *cache,
const H5AC_info_t *entry,
herr_t fxn_ret_value);
+H5_DLL herr_t H5AC__write_mark_clean_entry_log_msg(const H5AC_t *cache,
+ const H5AC_info_t *entry, herr_t fxn_ret_value);
H5_DLL herr_t H5AC__write_move_entry_log_msg(const H5AC_t *cache,
haddr_t old_addr,
haddr_t new_addr,
@@ -482,6 +487,9 @@ H5_DLL herr_t H5AC__write_unprotect_entry_log_msg(const H5AC_t *cache,
H5_DLL herr_t H5AC__write_set_cache_config_log_msg(const H5AC_t *cache,
const H5AC_cache_config_t *config,
herr_t fxn_ret_value);
+H5_DLL herr_t H5AC__write_remove_entry_log_msg(const H5AC_t *cache,
+ const H5AC_info_t *entry,
+ herr_t fxn_ret_value);
#endif /* _H5ACpkg_H */
diff --git a/src/H5ACprivate.h b/src/H5ACprivate.h
index 2251af4..76013a3 100644
--- a/src/H5ACprivate.h
+++ b/src/H5ACprivate.h
@@ -86,7 +86,8 @@ typedef enum {
H5AC_FARRAY_DBLK_PAGE_ID, /* (24) fixed array data block page */
H5AC_SUPERBLOCK_ID, /* (25) file superblock */
H5AC_DRVRINFO_ID, /* (26) driver info block (supplements superblock)*/
- H5AC_TEST_ID, /* (27) test entry -- not used for actual files */
+ H5AC_PROXY_ENTRY_ID, /* (27) cache entry proxy */
+ H5AC_TEST_ID, /* (28) test entry -- not used for actual files */
H5AC_NTYPES /* Number of types, must be last */
} H5AC_type_t;
@@ -199,6 +200,25 @@ typedef H5C_cache_entry_t H5AC_info_t;
/* Typedef for metadata cache (defined in H5Cpkg.h) */
typedef H5C_t H5AC_t;
+/* Metadata cache proxy entry type */
+typedef struct H5AC_proxy_entry_t {
+ H5AC_info_t cache_info; /* Information for H5AC cache functions */
+ /* (MUST be first field in structure) */
+
+ /* General fields */
+ haddr_t addr; /* Address of the entry in the file */
+ /* (Should be in 'temporary' address space) */
+
+ /* Parent fields */
+ H5SL_t *parents; /* Skip list to track parent addresses */
+
+ /* Child fields */
+ size_t nchildren; /* Number of children */
+ size_t ndirty_children; /* Number of dirty children */
+ /* (Note that this currently duplicates some cache functionality) */
+} H5AC_proxy_entry_t;
+
+
#define H5AC_RING_NAME "H5AC_ring_type"
/* Dataset transfer property lists for metadata calls */
@@ -353,11 +373,14 @@ H5_DLL herr_t H5AC_unprotect(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type,
haddr_t addr, void *thing, unsigned flags);
H5_DLL herr_t H5AC_flush(H5F_t *f, hid_t dxpl_id);
H5_DLL herr_t H5AC_mark_entry_dirty(void *thing);
+H5_DLL herr_t H5AC_mark_entry_clean(void *thing);
H5_DLL herr_t H5AC_move_entry(H5F_t *f, const H5AC_class_t *type,
haddr_t old_addr, haddr_t new_addr, hid_t dxpl_id);
H5_DLL herr_t H5AC_dest(H5F_t *f, hid_t dxpl_id);
+H5_DLL herr_t H5AC_evict(H5F_t *f, hid_t dxpl_id);
H5_DLL herr_t H5AC_expunge_entry(H5F_t *f, hid_t dxpl_id,
const H5AC_class_t *type, haddr_t addr, unsigned flags);
+H5_DLL herr_t H5AC_remove_entry(void *entry);
H5_DLL herr_t H5AC_get_cache_auto_resize_config(const H5AC_t * cache_ptr,
H5AC_cache_config_t *config_ptr);
H5_DLL herr_t H5AC_get_cache_size(H5AC_t *cache_ptr, size_t *max_size_ptr,
@@ -381,6 +404,15 @@ H5_DLL herr_t H5AC_set_ring(hid_t dxpl_id, H5AC_ring_t ring, H5P_genplist_t **dx
H5_DLL herr_t H5AC_reset_ring(H5P_genplist_t *dxpl, H5AC_ring_t orig_ring);
H5_DLL herr_t H5AC_expunge_tag_type_metadata(H5F_t *f, hid_t dxpl_id, haddr_t tag, int type_id, unsigned flags);
+/* Virtual entry routines */
+H5_DLL H5AC_proxy_entry_t *H5AC_proxy_entry_create(void);
+H5_DLL herr_t H5AC_proxy_entry_add_parent(H5AC_proxy_entry_t *pentry, void *parent);
+H5_DLL herr_t H5AC_proxy_entry_remove_parent(H5AC_proxy_entry_t *pentry, void *parent);
+H5_DLL herr_t H5AC_proxy_entry_add_child(H5AC_proxy_entry_t *pentry, H5F_t *f,
+ hid_t dxpl_id, void *child);
+H5_DLL herr_t H5AC_proxy_entry_remove_child(H5AC_proxy_entry_t *pentry, void *child);
+H5_DLL herr_t H5AC_proxy_entry_dest(H5AC_proxy_entry_t *pentry);
+
#ifdef H5_HAVE_PARALLEL
H5_DLL herr_t H5AC_add_candidate(H5AC_t * cache_ptr, haddr_t addr);
#endif /* H5_HAVE_PARALLEL */
diff --git a/src/H5ACproxy_entry.c b/src/H5ACproxy_entry.c
new file mode 100644
index 0000000..ab5edb9
--- /dev/null
+++ b/src/H5ACproxy_entry.c
@@ -0,0 +1,796 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5ACproxy_entry.c
+ *
+ * Purpose: Functions and a cache client for a "proxy" cache entry.
+ * A proxy cache entry is used as a placeholder for entire
+ * data structures to attach flush dependencies, etc.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/****************/
+/* Module Setup */
+/****************/
+#include "H5ACmodule.h" /* This source code file is part of the H5AC module */
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5ACpkg.h" /* Metadata cache */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5MFprivate.h" /* File memory management */
+
+
+/****************/
+/* Local Macros */
+/****************/
+
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+
+/********************/
+/* Package Typedefs */
+/********************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+/* Metadata cache (H5AC) callbacks */
+static herr_t H5AC__proxy_entry_image_len(const void *thing, size_t *image_len);
+static herr_t H5AC__proxy_entry_serialize(const H5F_t *f, void *image_ptr,
+ size_t len, void *thing);
+static herr_t H5AC__proxy_entry_notify(H5AC_notify_action_t action, void *thing);
+static herr_t H5AC__proxy_entry_free_icr(void *thing);
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+/* H5AC proxy entries inherit cache-like properties from H5AC */
+const H5AC_class_t H5AC_PROXY_ENTRY[1] = {{
+ H5AC_PROXY_ENTRY_ID, /* Metadata client ID */
+ "Proxy entry", /* Metadata client name (for debugging) */
+ H5FD_MEM_SUPER, /* File space memory type for client */
+ 0, /* Client class behavior flags */
+ NULL, /* 'get_initial_load_size' callback */
+ NULL, /* 'get_final_load_size' callback */
+ NULL, /* 'verify_chksum' callback */
+ NULL, /* 'deserialize' callback */
+ H5AC__proxy_entry_image_len, /* 'image_len' callback */
+ NULL, /* 'pre_serialize' callback */
+ H5AC__proxy_entry_serialize, /* 'serialize' callback */
+ H5AC__proxy_entry_notify, /* 'notify' callback */
+ H5AC__proxy_entry_free_icr, /* 'free_icr' callback */
+ NULL, /* 'fsf_size' callback */
+}};
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+/* Declare a free list to manage H5AC_proxy_entry_t objects */
+H5FL_DEFINE_STATIC(H5AC_proxy_entry_t);
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_proxy_entry_create
+ *
+ * Purpose: Create a new proxy entry
+ *
+ * Return: Success: Pointer to the new proxy entry object.
+ * Failure: NULL
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+H5AC_proxy_entry_t *
+H5AC_proxy_entry_create(void)
+{
+ H5AC_proxy_entry_t *pentry = NULL; /* Pointer to new proxy entry */
+ H5AC_proxy_entry_t *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_NOAPI(NULL)
+
+ /* Allocate new proxy entry */
+ if(NULL == (pentry = H5FL_CALLOC(H5AC_proxy_entry_t)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "can't allocate proxy entry")
+
+ /* Set non-zero fields */
+ pentry->addr = HADDR_UNDEF;
+
+ /* Set return value */
+ ret_value = pentry;
+
+done:
+ /* Release resources on error */
+ if(!ret_value)
+ if(pentry)
+ pentry = H5FL_FREE(H5AC_proxy_entry_t, pentry);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_proxy_entry_create() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_proxy_entry_add_parent
+ *
+ * Purpose: Add a parent to a proxy entry
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_proxy_entry_add_parent(H5AC_proxy_entry_t *pentry, void *_parent)
+{
+ H5AC_info_t *parent = (H5AC_info_t *)_parent; /* Parent entry's cache info */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(parent);
+ HDassert(pentry);
+
+ /* Add parent to the list of parents */
+ if(NULL == pentry->parents)
+ if(NULL == (pentry->parents = H5SL_create(H5SL_TYPE_HADDR, NULL)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "unable to create skip list for parents of proxy entry")
+
+ /* Insert parent address into skip list */
+ if(H5SL_insert(pentry->parents, parent, &parent->addr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINSERT, FAIL, "unable to insert parent into proxy's skip list")
+
+ /* Add flush dependency on parent */
+ if(pentry->nchildren > 0) {
+ /* Sanity check */
+ HDassert(H5F_addr_defined(pentry->addr));
+
+ if(H5AC_create_flush_dependency(parent, pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDEPEND, FAIL, "unable to set flush dependency on proxy entry")
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_proxy_entry_add_parent() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_proxy_entry_remove_parent
+ *
+ * Purpose: Removes a parent from a proxy entry
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_proxy_entry_remove_parent(H5AC_proxy_entry_t *pentry, void *_parent)
+{
+ H5AC_info_t *parent = (H5AC_info_t *)_parent; /* Pointer to the parent entry */
+ H5AC_info_t *rem_parent; /* Pointer to the removed parent entry */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(pentry);
+ HDassert(pentry->parents);
+ HDassert(parent);
+
+ /* Remove parent from skip list */
+ if(NULL == (rem_parent = (H5AC_info_t *)H5SL_remove(pentry->parents, &parent->addr)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "unable to remove proxy entry parent from skip list")
+ if(!H5F_addr_eq(rem_parent->addr, parent->addr))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "removed proxy entry parent not the same as real parent")
+
+ /* Shut down the skip list, if this is the last parent */
+ if(0 == H5SL_count(pentry->parents)) {
+ /* Sanity check */
+ HDassert(0 == pentry->nchildren);
+
+ if(H5SL_close(pentry->parents) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CLOSEERROR, FAIL, "can't close proxy parent skip list")
+ pentry->parents = NULL;
+ } /* end if */
+
+ /* Remove flush dependency between the proxy entry and a parent */
+ if(pentry->nchildren > 0)
+ if(H5AC_destroy_flush_dependency(parent, pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "unable to remove flush dependency on proxy entry")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_proxy_entry_remove_parent() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC__proxy_entry_add_child_cb
+ *
+ * Purpose: Callback routine for adding an entry as a flush dependency for
+ * a proxy entry.
+ *
+ * Return: Success: Non-negative on success
+ * Failure: Negative
+ *
+ * Programmer: Quincey Koziol
+ * Thursday, September 22, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+H5AC__proxy_entry_add_child_cb(void *_item, void H5_ATTR_UNUSED *_key, void *_udata)
+{
+ H5AC_info_t *parent = (H5AC_info_t *)_item; /* Pointer to the parent entry */
+ H5AC_proxy_entry_t *pentry = (H5AC_proxy_entry_t *)_udata; /* Pointer to the proxy entry */
+ int ret_value = H5_ITER_CONT; /* Callback return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Add flush dependency on parent for proxy entry */
+ if(H5AC_create_flush_dependency(parent, pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDEPEND, H5_ITER_ERROR, "unable to set flush dependency for virtual entry")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC__proxy_entry_add_child_cb() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_proxy_entry_add_child
+ *
+ * Purpose: Add a child a proxy entry
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_proxy_entry_add_child(H5AC_proxy_entry_t *pentry, H5F_t *f, hid_t dxpl_id,
+ void *child)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(pentry);
+ HDassert(child);
+
+ /* Check for first child */
+ if(0 == pentry->nchildren) {
+ /* Get an address, if the proxy doesn't already have one */
+ if(!H5F_addr_defined(pentry->addr))
+ if(HADDR_UNDEF == (pentry->addr = H5MF_alloc_tmp(f, 1)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "temporary file space allocation failed for proxy entry")
+
+ /* Insert the proxy entry into the cache */
+ if(H5AC_insert_entry(f, dxpl_id, H5AC_PROXY_ENTRY, pentry->addr, pentry, H5AC__PIN_ENTRY_FLAG) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINSERT, FAIL, "unable to cache proxy entry")
+
+ /* Proxies start out clean (insertions are automatically marked dirty) */
+ if(H5AC_mark_entry_clean(pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCLEAN, FAIL, "can't mark proxy entry clean")
+
+ /* If there are currently parents, iterate over the list of parents, creating flush dependency on them */
+ if(pentry->parents)
+ if(H5SL_iterate(pentry->parents, H5AC__proxy_entry_add_child_cb, pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADITER, FAIL, "can't visit parents")
+ } /* end if */
+
+ /* Add flush dependency on proxy entry */
+ if(H5AC_create_flush_dependency(pentry, child) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDEPEND, FAIL, "unable to set flush dependency on proxy entry")
+
+ /* Increment count of children */
+ pentry->nchildren++;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_proxy_entry_add_child() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC__proxy_entry_remove_child_cb
+ *
+ * Purpose: Callback routine for removing an entry as a flush dependency for
+ * proxy entry.
+ *
+ * Return: Success: Non-negative on success
+ * Failure: Negative
+ *
+ * Programmer: Quincey Koziol
+ * Thursday, September 22, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+H5AC__proxy_entry_remove_child_cb(void *_item, void H5_ATTR_UNUSED *_key, void *_udata)
+{
+ H5AC_info_t *parent = (H5AC_info_t *)_item; /* Pointer to the parent entry */
+ H5AC_proxy_entry_t *pentry = (H5AC_proxy_entry_t *)_udata; /* Pointer to the proxy entry */
+ int ret_value = H5_ITER_CONT; /* Callback return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Remove flush dependency on parent for proxy entry */
+ if(H5AC_destroy_flush_dependency(parent, pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, H5_ITER_ERROR, "unable to remove flush dependency for proxy entry")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC__proxy_entry_remove_child_cb() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_proxy_entry_remove_child
+ *
+ * Purpose: Remove a child a proxy entry
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_proxy_entry_remove_child(H5AC_proxy_entry_t *pentry, void *child)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(pentry);
+ HDassert(child);
+
+ /* Remove flush dependency on proxy entry */
+ if(H5AC_destroy_flush_dependency(pentry, child) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "unable to remove flush dependency on proxy entry")
+
+ /* Decrement count of children */
+ pentry->nchildren--;
+
+ /* Check for last child */
+ if(0 == pentry->nchildren) {
+ /* Check for flush dependencies on proxy's parents */
+ if(pentry->parents)
+ /* Iterate over the list of parents, removing flush dependency on them */
+ if(H5SL_iterate(pentry->parents, H5AC__proxy_entry_remove_child_cb, pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADITER, FAIL, "can't visit parents")
+
+ /* Unpin proxy */
+ if(H5AC_unpin_entry(pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "can't unpin proxy entry")
+
+ /* Remove proxy entry from cache */
+ if(H5AC_remove_entry(pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "unable to remove proxy entry")
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_proxy_entry_remove_child() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_proxy_entry_dest
+ *
+ * Purpose: Destroys a proxy entry in memory.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_proxy_entry_dest(H5AC_proxy_entry_t *pentry)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(pentry);
+ HDassert(NULL == pentry->parents);
+ HDassert(0 == pentry->nchildren);
+ HDassert(0 == pentry->ndirty_children);
+
+ /* Free the proxy entry object */
+ pentry = H5FL_FREE(H5AC_proxy_entry_t, pentry);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_proxy_entry_dest() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC__proxy_entry_image_len
+ *
+ * Purpose: Compute the size of the data structure on disk.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5AC__proxy_entry_image_len(const void H5_ATTR_UNUSED *thing, size_t *image_len)
+{
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Check arguments */
+ HDassert(image_len);
+
+ /* Set the image length size to 1 byte */
+ *image_len = 1;
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5AC__proxy_entry_image_len() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC__proxy_entry_serialize
+ *
+ * Purpose: Serializes a data structure for writing to disk.
+ *
+ * Note: Should never be invoked.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5AC__proxy_entry_serialize(const H5F_t H5_ATTR_UNUSED *f, void H5_ATTR_UNUSED *image,
+ size_t H5_ATTR_UNUSED len, void H5_ATTR_UNUSED *thing)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
+
+ /* Should never be invoked */
+ HDassert(0 && "Invalid callback?!?");
+
+ HERROR(H5E_CACHE, H5E_CANTSERIALIZE, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5AC__proxy_entry_serialize() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC__proxy_entry_notify
+ *
+ * Purpose: Handle cache action notifications
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5AC__proxy_entry_notify(H5AC_notify_action_t action, void *_thing)
+{
+ H5AC_proxy_entry_t *pentry = (H5AC_proxy_entry_t *)_thing;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity check */
+ HDassert(pentry);
+
+ switch(action) {
+ case H5AC_NOTIFY_ACTION_AFTER_INSERT:
+ break;
+
+ case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+#ifdef NDEBUG
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid notify action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Invalid action?!?");
+#endif /* NDEBUG */
+ break;
+
+ case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
+#ifdef NDEBUG
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid notify action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Invalid action?!?");
+#endif /* NDEBUG */
+ break;
+
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ /* Sanity checks */
+ HDassert(0 == pentry->ndirty_children);
+
+ /* No action */
+ break;
+
+ case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
+ /* Sanity checks */
+ HDassert(pentry->ndirty_children > 0);
+
+ /* No action */
+ break;
+
+ case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
+ /* Sanity checks */
+ HDassert(0 == pentry->ndirty_children);
+
+ /* No action */
+ break;
+
+ case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
+ /* Increment # of dirty children */
+ pentry->ndirty_children++;
+
+ /* Check for first dirty child */
+ if(1 == pentry->ndirty_children)
+ if(H5AC_mark_entry_dirty(pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDIRTY, FAIL, "can't mark proxy entry dirty")
+ break;
+
+ case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
+ /* Sanity check */
+ HDassert(pentry->ndirty_children > 0);
+
+ /* Decrement # of dirty children */
+ pentry->ndirty_children--;
+
+ /* Check for last dirty child */
+ if(0 == pentry->ndirty_children)
+ if(H5AC_mark_entry_clean(pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCLEAN, FAIL, "can't mark proxy entry clean")
+ break;
+
+ default:
+#ifdef NDEBUG
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "unknown notify action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Unknown action?!?");
+#endif /* NDEBUG */
+ } /* end switch */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC__proxy_entry_notify() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC__proxy_entry_free_icr
+ *
+ * Purpose: Destroy/release an "in core representation" of a data
+ * structure
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5AC__proxy_entry_free_icr(void *_thing)
+{
+ H5AC_proxy_entry_t *pentry = (H5AC_proxy_entry_t *)_thing;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Destroy the proxy entry */
+ if(H5AC_proxy_entry_dest(pentry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to destroy proxy entry")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC__proxy_entry_free_icr() */
+
+#ifdef OLD_CODE
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_virt_entry_dirty_parent
+ *
+ * Purpose: Indicate that a virtual entry's parent became dirty
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * July 23, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_virt_entry_dirty_parent(H5AC_virt_entry_t *ventry)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity check */
+ HDassert(ventry);
+ HDassert(ventry->track_parents);
+ HDassert(ventry->nparents > 0);
+
+ /* If this is the first dirty parent or child, mark the virtual entry dirty */
+ if(ventry->in_cache && 0 == ventry->ndirty_parents && 0 == ventry->ndirty_children)
+ if(H5AC_mark_entry_dirty(ventry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDIRTY, FAIL, "can't mark virtual entry dirty")
+
+ /* Increment the number of dirty parents */
+ ventry->ndirty_parents++;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_virt_entry_dirty_parent() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_virt_entry_clean_parent
+ *
+ * Purpose: Indicate that a virtual entry's parent became clean
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * July 23, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_virt_entry_clean_parent(H5AC_virt_entry_t *ventry)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity check */
+ HDassert(ventry);
+ HDassert(ventry->track_parents);
+// HDassert(ventry->nparents > 0);
+ HDassert(ventry->ndirty_parents > 0);
+
+ /* Decrement the number of dirty parents */
+ ventry->ndirty_parents--;
+
+ /* If this is the last dirty parent or child, mark the virtual entry clean */
+ if(ventry->in_cache && 0 == ventry->ndirty_parents && 0 == ventry->ndirty_children)
+ if(H5AC_mark_entry_clean(ventry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCLEAN, FAIL, "can't mark virtual entry clean")
+
+ /* Destroy the skip list, if no more parents */
+ if(0 == ventry->nparents && 0 == ventry->ndirty_parents) {
+// /* Sanity check */
+// HDassert(0 == ventry->ndirty_parents);
+
+ if(H5SL_close(ventry->parents) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CLOSEERROR, FAIL, "can't close parent list")
+ ventry->parents = NULL;
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_virt_entry_clean_parent() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_virt_entry_dirty_child
+ *
+ * Purpose: Indicate that a virtual entry's child became dirty
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * July 24, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_virt_entry_dirty_child(H5AC_virt_entry_t *ventry)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity check */
+ HDassert(ventry);
+ HDassert(ventry->nchildren > 0);
+
+ /* If this is the first dirty parent or child, mark the virtual entry dirty */
+ if(ventry->in_cache && 0 == ventry->ndirty_parents && 0 == ventry->ndirty_children)
+ if(H5AC_mark_entry_dirty(ventry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDIRTY, FAIL, "can't mark virtual entry dirty")
+
+ /* Increment the number of dirty children */
+ ventry->ndirty_children++;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_virt_entry_dirty_child() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_virt_entry_clean_child
+ *
+ * Purpose: Indicate that a virtual entry's child became clean
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * July 24, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_virt_entry_clean_child(H5AC_virt_entry_t *ventry)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity check */
+ HDassert(ventry);
+ HDassert(ventry->nchildren > 0);
+ HDassert(ventry->ndirty_children > 0);
+
+ /* Decrement the number of dirty children */
+ ventry->ndirty_children--;
+
+ /* If this is the last dirty parent or child, mark the virtual entry clean */
+ if(ventry->in_cache && 0 == ventry->ndirty_parents && 0 == ventry->ndirty_children)
+ if(H5AC_mark_entry_clean(ventry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCLEAN, FAIL, "can't mark virtual entry clean")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5AC_virt_entry_clean_child() */
+
+#endif /* OLD_CODE */
+
+
diff --git a/src/H5B2.c b/src/H5B2.c
index af5fae7..e9f6150 100644
--- a/src/H5B2.c
+++ b/src/H5B2.c
@@ -290,7 +290,7 @@ H5B2_insert(H5B2_t *bt2, hid_t dxpl_id, void *udata)
hdr = bt2->hdr;
/* Insert the record */
- if(H5B2__insert_hdr(hdr, dxpl_id, udata) < 0)
+ if(H5B2__insert(hdr, dxpl_id, udata) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree")
done:
@@ -336,17 +336,17 @@ H5B2_update(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_modify_t op, void *op_
/* Check if the root node is allocated yet */
if(!H5F_addr_defined(hdr->root.addr)) {
/* Create root node as leaf node in B-tree */
- if(H5B2__create_leaf(hdr, dxpl_id, &(hdr->root)) < 0)
+ if(H5B2__create_leaf(hdr, dxpl_id, hdr, &(hdr->root)) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "unable to create root node")
} /* end if */
/* Attempt to insert record into B-tree */
if(hdr->depth > 0) {
- if(H5B2__update_internal(hdr, dxpl_id, hdr->depth, NULL, &hdr->root, &status, H5B2_POS_ROOT, udata, op, op_data) < 0)
+ if(H5B2__update_internal(hdr, dxpl_id, hdr->depth, NULL, &hdr->root, &status, H5B2_POS_ROOT, hdr, udata, op, op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update record in B-tree internal node")
} /* end if */
else {
- if(H5B2__update_leaf(hdr, dxpl_id, &hdr->root, &status, H5B2_POS_ROOT, udata, op, op_data) < 0)
+ if(H5B2__update_leaf(hdr, dxpl_id, &hdr->root, &status, H5B2_POS_ROOT, hdr, udata, op, op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update record in B-tree leaf node")
} /* end else */
@@ -354,9 +354,19 @@ H5B2_update(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_modify_t op, void *op_
HDassert(H5B2_UPDATE_UNKNOWN != status);
/* Use insert algorithm if nodes to leaf full */
- if(H5B2_UPDATE_INSERT_CHILD_FULL == status)
- if(H5B2__insert_hdr(hdr, dxpl_id, udata) < 0)
+ if(H5B2_UPDATE_INSERT_CHILD_FULL == status) {
+ if(H5B2__insert(hdr, dxpl_id, udata) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree")
+ } /* end if */
+ else if(H5B2_UPDATE_SHADOW_DONE == status || H5B2_UPDATE_INSERT_DONE == status) {
+ /* Mark B-tree header as dirty */
+ if(H5B2__hdr_dirty(hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTMARKDIRTY, FAIL, "unable to mark B-tree header dirty")
+ } /* end else-if */
+ else {
+ /* Sanity check */
+ HDassert(H5B2_UPDATE_MODIFY_DONE == status);
+ } /* end else */
done:
FUNC_LEAVE_NOAPI(ret_value)
@@ -432,7 +442,7 @@ H5B2_iterate(H5B2_t *bt2, hid_t dxpl_id, H5B2_operator_t op, void *op_data)
/* Iterate through records */
if(hdr->root.node_nrec > 0) {
/* Iterate through nodes */
- if((ret_value = H5B2__iterate_node(hdr, dxpl_id, hdr->depth, &hdr->root, op, op_data)) < 0)
+ if((ret_value = H5B2__iterate_node(hdr, dxpl_id, hdr->depth, &hdr->root, hdr, op, op_data)) < 0)
HERROR(H5E_BTREE, H5E_CANTLIST, "node iteration failed");
} /* end if */
@@ -469,6 +479,7 @@ H5B2_find(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_found_t op,
{
H5B2_hdr_t *hdr; /* Pointer to the B-tree header */
H5B2_node_ptr_t curr_node_ptr; /* Node pointer info for current node */
+ void *parent = NULL; /* Parent of current node */
uint16_t depth; /* Current depth of the tree */
int cmp; /* Comparison value of records */
unsigned idx; /* Location of record which matches key */
@@ -522,6 +533,10 @@ H5B2_find(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_found_t op,
/* Current depth of the tree */
depth = hdr->depth;
+ /* Set initial parent, if doing swmr writes */
+ if(hdr->swmr_write)
+ parent = hdr;
+
/* Walk down B-tree to find record or leaf node where record is located */
cmp = -1;
curr_pos = H5B2_POS_ROOT;
@@ -530,12 +545,18 @@ H5B2_find(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_found_t op,
H5B2_node_ptr_t next_node_ptr; /* Node pointer info for next node */
/* Lock B-tree current node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node_ptr.addr, curr_node_ptr.node_nrec, depth, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, &curr_node_ptr, depth, FALSE, H5AC__READ_ONLY_FLAG)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to load B-tree internal node")
+ /* Unpin parent if necessary */
+ if(parent) {
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ parent = NULL;
+ } /* end if */
+
/* Locate node pointer for child */
- if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native,
- udata, &idx, &cmp) < 0) {
+ if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) < 0) {
/* Unlock current node before failing */
H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET);
HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
@@ -566,9 +587,13 @@ H5B2_find(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_found_t op,
} /* end if */
/* Unlock current node */
- if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET) < 0)
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, (unsigned)(hdr->swmr_write ? H5AC__PIN_ENTRY_FLAG : H5AC__NO_FLAGS_SET)) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node")
+ /* Keep track of parent if necessary */
+ if(hdr->swmr_write)
+ parent = internal;
+
/* Set pointer to next node to load */
curr_node_ptr = next_node_ptr;
} /* end if */
@@ -598,16 +623,22 @@ H5B2_find(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_found_t op,
H5B2_leaf_t *leaf; /* Pointer to leaf node in B-tree */
/* Lock B-tree leaf node */
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, curr_node_ptr.addr, curr_node_ptr.node_nrec, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, &curr_node_ptr, FALSE, H5AC__READ_ONLY_FLAG)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ /* Unpin parent if necessary */
+ if(parent) {
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ parent = NULL;
+ } /* end if */
+
/* Locate record */
- if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native,
- udata, &idx, &cmp) < 0) {
- /* unlock current node before failing */
+ if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0) {
+ /* Unlock current node before failing */
H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr.addr, leaf, H5AC__NO_FLAGS_SET);
HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- }
+ } /* end if */
if(cmp != 0) {
/* Unlock leaf node */
@@ -655,6 +686,12 @@ H5B2_find(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_found_t op,
} /* end block */
done:
+ if(parent) {
+ HDassert(ret_value < 0);
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ } /* end if */
+
FUNC_LEAVE_NOAPI(ret_value)
} /* H5B2_find() */
@@ -683,6 +720,7 @@ H5B2_index(H5B2_t *bt2, hid_t dxpl_id, H5_iter_order_t order, hsize_t idx,
{
H5B2_hdr_t *hdr; /* Pointer to the B-tree header */
H5B2_node_ptr_t curr_node_ptr; /* Node pointer info for current node */
+ void *parent = NULL; /* Parent of current node */
uint16_t depth; /* Current depth of the tree */
herr_t ret_value = SUCCEED; /* Return value */
@@ -712,6 +750,10 @@ H5B2_index(H5B2_t *bt2, hid_t dxpl_id, H5_iter_order_t order, hsize_t idx,
/* Current depth of the tree */
depth = hdr->depth;
+ /* Set initial parent, if doing swmr writes */
+ if(hdr->swmr_write)
+ parent = hdr;
+
/* Check for reverse indexing and map requested index to appropriate forward index */
if(order == H5_ITER_DEC)
idx = curr_node_ptr.all_nrec - (idx + 1);
@@ -723,9 +765,16 @@ H5B2_index(H5B2_t *bt2, hid_t dxpl_id, H5_iter_order_t order, hsize_t idx,
unsigned u; /* Local index variable */
/* Lock B-tree current node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node_ptr.addr, curr_node_ptr.node_nrec, depth, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, &curr_node_ptr, depth, FALSE, H5AC__READ_ONLY_FLAG)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to load B-tree internal node")
+ /* Unpin parent if necessary */
+ if(parent) {
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ parent = NULL;
+ } /* end if */
+
/* Search for record with correct index */
for(u = 0; u < internal->nrec; u++) {
/* Check if record is in child node */
@@ -734,9 +783,13 @@ H5B2_index(H5B2_t *bt2, hid_t dxpl_id, H5_iter_order_t order, hsize_t idx,
next_node_ptr = internal->node_ptrs[u];
/* Unlock current node */
- if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET) < 0)
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, (unsigned)(hdr->swmr_write ? H5AC__PIN_ENTRY_FLAG : H5AC__NO_FLAGS_SET)) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node")
+ /* Keep track of parent if necessary */
+ if(hdr->swmr_write)
+ parent = internal;
+
/* Set pointer to next node to load */
curr_node_ptr = next_node_ptr;
@@ -776,9 +829,13 @@ H5B2_index(H5B2_t *bt2, hid_t dxpl_id, H5_iter_order_t order, hsize_t idx,
next_node_ptr = internal->node_ptrs[u];
/* Unlock current node */
- if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET) < 0)
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, (unsigned)(hdr->swmr_write ? H5AC__PIN_ENTRY_FLAG : H5AC__NO_FLAGS_SET)) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node")
+ /* Keep track of parent if necessary */
+ if(hdr->swmr_write)
+ parent = internal;
+
/* Set pointer to next node to load */
curr_node_ptr = next_node_ptr;
} /* end if */
@@ -795,9 +852,16 @@ H5B2_index(H5B2_t *bt2, hid_t dxpl_id, H5_iter_order_t order, hsize_t idx,
H5B2_leaf_t *leaf; /* Pointer to leaf node in B-tree */
/* Lock B-tree leaf node */
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, curr_node_ptr.addr, curr_node_ptr.node_nrec, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, &curr_node_ptr, FALSE, H5AC__READ_ONLY_FLAG)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ /* Unpin parent if necessary */
+ if(parent) {
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ parent = NULL;
+ } /* end if */
+
/* Sanity check index */
HDassert(idx < leaf->nrec);
@@ -816,6 +880,12 @@ H5B2_index(H5B2_t *bt2, hid_t dxpl_id, H5_iter_order_t order, hsize_t idx,
} /* end block */
done:
+ if(parent) {
+ HDassert(ret_value < 0);
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ } /* end if */
+
FUNC_LEAVE_NOAPI(ret_value)
} /* H5B2_index() */
@@ -859,8 +929,9 @@ H5B2_remove(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_remove_t op,
if(hdr->depth > 0) {
hbool_t depth_decreased = FALSE; /* Flag to indicate whether the depth of the B-tree decreased */
- if(H5B2__remove_internal(hdr, dxpl_id, &depth_decreased, NULL, hdr->depth,
- &(hdr->cache_info), NULL, H5B2_POS_ROOT, &hdr->root, udata, op, op_data) < 0)
+ if(H5B2__remove_internal(hdr, dxpl_id, &depth_decreased, NULL, NULL,
+ hdr->depth, &(hdr->cache_info), NULL, H5B2_POS_ROOT, &hdr->root,
+ udata, op, op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree internal node")
/* Check for decreasing the depth of the B-tree */
@@ -878,7 +949,7 @@ H5B2_remove(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_remove_t op,
} /* end for */
} /* end if */
else {
- if(H5B2__remove_leaf(hdr, dxpl_id, &hdr->root, H5B2_POS_ROOT, udata, op, op_data) < 0)
+ if(H5B2__remove_leaf(hdr, dxpl_id, &hdr->root, H5B2_POS_ROOT, hdr, udata, op, op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree leaf node")
} /* end else */
@@ -941,8 +1012,9 @@ H5B2_remove_by_idx(H5B2_t *bt2, hid_t dxpl_id, H5_iter_order_t order,
if(hdr->depth > 0) {
hbool_t depth_decreased = FALSE; /* Flag to indicate whether the depth of the B-tree decreased */
- if(H5B2__remove_internal_by_idx(hdr, dxpl_id, &depth_decreased, NULL, hdr->depth,
- &(hdr->cache_info), NULL, &hdr->root, H5B2_POS_ROOT, idx, op, op_data) < 0)
+ if(H5B2__remove_internal_by_idx(hdr, dxpl_id, &depth_decreased, NULL, NULL,
+ hdr->depth, &(hdr->cache_info), NULL, &hdr->root, H5B2_POS_ROOT,
+ idx, op, op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree internal node")
/* Check for decreasing the depth of the B-tree */
@@ -960,7 +1032,7 @@ H5B2_remove_by_idx(H5B2_t *bt2, hid_t dxpl_id, H5_iter_order_t order,
} /* end for */
} /* end if */
else {
- if(H5B2__remove_leaf_by_idx(hdr, dxpl_id, &hdr->root, H5B2_POS_ROOT, (unsigned)idx, op, op_data) < 0)
+ if(H5B2__remove_leaf_by_idx(hdr, dxpl_id, &hdr->root, H5B2_POS_ROOT, hdr, (unsigned)idx, op, op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree leaf node")
} /* end else */
@@ -1055,11 +1127,11 @@ H5B2_neighbor(H5B2_t *bt2, hid_t dxpl_id, H5B2_compare_t range, void *udata,
/* Attempt to find neighbor record in B-tree */
if(hdr->depth > 0) {
- if(H5B2__neighbor_internal(hdr, dxpl_id, hdr->depth, &hdr->root, NULL, range, udata, op, op_data) < 0)
+ if(H5B2__neighbor_internal(hdr, dxpl_id, hdr->depth, &hdr->root, NULL, range, hdr, udata, op, op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "unable to find neighbor record in B-tree internal node")
} /* end if */
else {
- if(H5B2__neighbor_leaf(hdr, dxpl_id, &hdr->root, NULL, range, udata, op, op_data) < 0)
+ if(H5B2__neighbor_leaf(hdr, dxpl_id, &hdr->root, NULL, range, hdr, udata, op, op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "unable to find neighbor record in B-tree leaf node")
} /* end else */
@@ -1094,6 +1166,7 @@ H5B2_modify(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_modify_t op,
{
H5B2_hdr_t *hdr; /* Pointer to the B-tree header */
H5B2_node_ptr_t curr_node_ptr; /* Node pointer info for current node */
+ void *parent = NULL; /* Parent of current node */
H5B2_nodepos_t curr_pos; /* Position of current node */
uint16_t depth; /* Current depth of the tree */
int cmp; /* Comparison value of records */
@@ -1122,6 +1195,10 @@ H5B2_modify(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_modify_t op,
/* Current depth of the tree */
depth = hdr->depth;
+ /* Set initial parent, if doing swmr writes */
+ if(hdr->swmr_write)
+ parent = hdr;
+
/* Walk down B-tree to find record or leaf node where record is located */
cmp = -1;
curr_pos = H5B2_POS_ROOT;
@@ -1131,16 +1208,22 @@ H5B2_modify(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_modify_t op,
H5B2_node_ptr_t next_node_ptr; /* Node pointer info for next node */
/* Lock B-tree current node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node_ptr.addr, curr_node_ptr.node_nrec, depth, H5AC__NO_FLAGS_SET)))
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, &curr_node_ptr, depth, FALSE, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to load B-tree internal node")
+ /* Unpin parent if necessary */
+ if(parent) {
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ parent = NULL;
+ } /* end if */
+
/* Locate node pointer for child */
- if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native,
- udata, &idx, &cmp) < 0) {
- /* Unlock current node */
+ if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) < 0) {
+ /* Unlock current node before failing */
H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET);
HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- }
+ } /* end if */
if(cmp > 0)
idx++;
@@ -1168,9 +1251,13 @@ H5B2_modify(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_modify_t op,
} /* end if */
/* Unlock current node */
- if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET) < 0)
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, (unsigned)(hdr->swmr_write ? H5AC__PIN_ENTRY_FLAG : H5AC__NO_FLAGS_SET)) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node")
+ /* Keep track of parent if necessary */
+ if(hdr->swmr_write)
+ parent = internal;
+
/* Set pointer to next node to load */
curr_node_ptr = next_node_ptr;
} /* end if */
@@ -1209,15 +1296,22 @@ H5B2_modify(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_modify_t op,
hbool_t changed = FALSE;/* Whether the 'modify' callback changed the record */
/* Lock B-tree leaf node */
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, curr_node_ptr.addr, curr_node_ptr.node_nrec, H5AC__NO_FLAGS_SET)))
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, &curr_node_ptr, FALSE, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ /* Unpin parent if necessary */
+ if(parent) {
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ parent = NULL;
+ } /* end if */
+
/* Locate record */
- if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native,
- udata, &idx, &cmp) < 0) {
+ if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0) {
+ /* Unlock current node before failing */
H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr.addr, leaf, H5AC__NO_FLAGS_SET);
HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- }
+ } /* end if */
if(cmp != 0) {
/* Unlock leaf node */
@@ -1278,6 +1372,12 @@ H5B2_modify(H5B2_t *bt2, hid_t dxpl_id, void *udata, H5B2_modify_t op,
} /* end block */
done:
+ if(parent) {
+ HDassert(ret_value < 0);
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ } /* end if */
+
FUNC_LEAVE_NOAPI(ret_value)
} /* H5B2_modify() */
@@ -1451,6 +1551,59 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5B2_depend
+ *
+ * Purpose: Make a child flush dependency between the v2 B-tree's
+ * header and another piece of metadata in the file.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: Dana Robinson
+ * Fall 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2_depend(H5B2_t *bt2, hid_t dxpl_id, H5AC_proxy_entry_t *parent)
+{
+ /* Local variables */
+ H5B2_hdr_t *hdr = bt2->hdr; /* Header for B-tree */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(SUCCEED)
+
+ /*
+ * Check arguments.
+ */
+ HDassert(bt2);
+ HDassert(hdr);
+ HDassert(parent);
+ HDassert(hdr->parent == NULL || hdr->parent == parent);
+
+ /*
+ * Check to see if the flush dependency between the parent
+ * and the v2 B-tree header has already been setup. If it hasn't, then
+ * set it up.
+ */
+ if(NULL == hdr->parent) {
+ /* Sanity check */
+ HDassert(hdr->top_proxy);
+
+ /* Set the shared v2 B-tree header's file context for this operation */
+ hdr->f = bt2->f;
+
+ /* Add the v2 B-tree as a child of the parent (proxy) */
+ if(H5AC_proxy_entry_add_child(parent, hdr->f, dxpl_id, hdr->top_proxy) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSET, FAIL, "unable to add v2 B-tree as child of proxy")
+ hdr->parent = parent;
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2_depend() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5B2_patch_file
*
* Purpose: Patch the top-level file pointer contained in bt2
diff --git a/src/H5B2cache.c b/src/H5B2cache.c
index 51ad32b..6954e6c 100644
--- a/src/H5B2cache.c
+++ b/src/H5B2cache.c
@@ -72,6 +72,7 @@ static void *H5B2__cache_hdr_deserialize(const void *image, size_t len,
static herr_t H5B2__cache_hdr_image_len(const void *thing, size_t *image_len);
static herr_t H5B2__cache_hdr_serialize(const H5F_t *f, void *image, size_t len,
void *thing);
+static herr_t H5B2__cache_hdr_notify(H5AC_notify_action_t action, void *thing);
static herr_t H5B2__cache_hdr_free_icr(void *thing);
static herr_t H5B2__cache_int_get_initial_load_size(void *udata, size_t *image_len);
@@ -81,6 +82,7 @@ static void *H5B2__cache_int_deserialize(const void *image, size_t len,
static herr_t H5B2__cache_int_image_len(const void *thing, size_t *image_len);
static herr_t H5B2__cache_int_serialize(const H5F_t *f, void *image, size_t len,
void *thing);
+static herr_t H5B2__cache_int_notify(H5AC_notify_action_t action, void *thing);
static herr_t H5B2__cache_int_free_icr(void *thing);
static herr_t H5B2__cache_leaf_get_initial_load_size(void *udata, size_t *image_len);
@@ -90,6 +92,7 @@ static void *H5B2__cache_leaf_deserialize(const void *image, size_t len,
static herr_t H5B2__cache_leaf_image_len(const void *thing, size_t *image_len);
static herr_t H5B2__cache_leaf_serialize(const H5F_t *f, void *image, size_t len,
void *thing);
+static herr_t H5B2__cache_leaf_notify(H5AC_notify_action_t action, void *thing);
static herr_t H5B2__cache_leaf_free_icr(void *thing);
/*********************/
@@ -109,7 +112,7 @@ const H5AC_class_t H5AC_BT2_HDR[1] = {{
H5B2__cache_hdr_image_len, /* 'image_len' callback */
NULL, /* 'pre_serialize' callback */
H5B2__cache_hdr_serialize, /* 'serialize' callback */
- NULL, /* 'notify' callback */
+ H5B2__cache_hdr_notify, /* 'notify' callback */
H5B2__cache_hdr_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
}};
@@ -127,7 +130,7 @@ const H5AC_class_t H5AC_BT2_INT[1] = {{
H5B2__cache_int_image_len, /* 'image_len' callback */
NULL, /* 'pre_serialize' callback */
H5B2__cache_int_serialize, /* 'serialize' callback */
- NULL, /* 'notify' callback */
+ H5B2__cache_int_notify, /* 'notify' callback */
H5B2__cache_int_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
}};
@@ -145,7 +148,7 @@ const H5AC_class_t H5AC_BT2_LEAF[1] = {{
H5B2__cache_leaf_image_len, /* 'image_len' callback */
NULL, /* 'pre_serialize' callback */
H5B2__cache_leaf_serialize, /* 'serialize' callback */
- NULL, /* 'notify' callback */
+ H5B2__cache_leaf_notify, /* 'notify' callback */
H5B2__cache_leaf_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
}};
@@ -433,6 +436,91 @@ H5B2__cache_hdr_serialize(const H5F_t *f, void *_image, size_t H5_ATTR_UNUSED le
/*-------------------------------------------------------------------------
+ * Function: H5B2__cache_hdr_notify
+ *
+ * Purpose: Handle cache action notifications
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Neil Fortner
+ * nfortne2@hdfgroup.org
+ * Apr 24 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5B2__cache_hdr_notify(H5AC_notify_action_t action, void *_thing)
+{
+ H5B2_hdr_t *hdr = (H5B2_hdr_t *)_thing;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ /*
+ * Check arguments.
+ */
+ HDassert(hdr);
+
+ /* Check if the file was opened with SWMR-write access */
+ if(hdr->swmr_write) {
+ switch(action) {
+ case H5AC_NOTIFY_ACTION_AFTER_INSERT:
+ case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ /* do nothing */
+ break;
+
+ case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
+ /* Increment the shadow epoch, forcing new modifications to
+ * internal and leaf nodes to create new shadow copies */
+ hdr->shadow_epoch++;
+ break;
+
+ case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
+ case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
+ case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
+ case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
+ /* do nothing */
+ break;
+
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ /* If hdr->parent != NULL, hdr->parent is used to destroy
+ * the flush dependency before the header is evicted.
+ */
+ if(hdr->parent) {
+ /* Sanity check */
+ HDassert(hdr->top_proxy);
+
+ /* Destroy flush dependency on object header proxy */
+ if(H5AC_proxy_entry_remove_child((H5AC_proxy_entry_t *)hdr->parent, (void *)hdr->top_proxy) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency between v2 B-tree and proxy")
+ hdr->parent = NULL;
+ } /* end if */
+
+ /* Detach from 'top' proxy for extensible array */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(hdr->top_proxy, hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency between header and v2 B-tree 'top' proxy")
+ /* Don't reset hdr->top_proxy here, it's destroyed when the header is freed -QAK */
+ } /* end if */
+ break;
+
+ default:
+#ifdef NDEBUG
+ HGOTO_ERROR(H5E_BTREE, H5E_BADVALUE, FAIL, "unknown action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Unknown action?!?");
+#endif /* NDEBUG */
+ } /* end switch */
+ } /* end if */
+ else
+ HDassert(NULL == hdr->parent);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__cache_hdr_notify() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5B2__cache_hdr_free_icr
*
* Purpose: Destroy/release an "in core representation" of a data
@@ -573,9 +661,8 @@ H5B2__cache_int_deserialize(const void *_image, size_t H5_ATTR_UNUSED len,
HDassert(udata);
/* Allocate new internal node and reset cache info */
- if(NULL == (internal = H5FL_MALLOC(H5B2_internal_t)))
+ if(NULL == (internal = H5FL_CALLOC(H5B2_internal_t)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed")
- HDmemset(&internal->cache_info, 0, sizeof(H5AC_info_t));
/* Increment ref. count on B-tree header */
if(H5B2__hdr_incr(udata->hdr) < 0)
@@ -583,6 +670,8 @@ H5B2__cache_int_deserialize(const void *_image, size_t H5_ATTR_UNUSED len,
/* Share B-tree information */
internal->hdr = udata->hdr;
+ internal->parent = udata->parent;
+ internal->shadow_epoch = udata->hdr->shadow_epoch;
/* Magic number */
if(HDmemcmp(image, H5B2_INT_MAGIC, (size_t)H5_SIZEOF_MAGIC))
@@ -775,6 +864,80 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5B2__cache_int_notify
+ *
+ * Purpose: Handle cache action notifications
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Neil Fortner
+ * nfortne2@hdfgroup.org
+ * Apr 25 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5B2__cache_int_notify(H5AC_notify_action_t action, void *_thing)
+{
+ H5B2_internal_t *internal = (H5B2_internal_t *)_thing;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /*
+ * Check arguments.
+ */
+ HDassert(internal);
+ HDassert(internal->hdr);
+
+ /* Check if the file was opened with SWMR-write access */
+ if(internal->hdr->swmr_write) {
+ switch(action) {
+ case H5AC_NOTIFY_ACTION_AFTER_INSERT:
+ case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ /* Create flush dependency on parent */
+ if(H5B2__create_flush_depend((H5AC_info_t *)internal->parent, (H5AC_info_t *)internal) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDEPEND, FAIL, "unable to create flush dependency")
+ break;
+
+ case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
+ case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
+ case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
+ case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
+ case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
+ /* do nothing */
+ break;
+
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ /* Destroy flush dependency on parent */
+ if(H5B2__destroy_flush_depend((H5AC_info_t *)internal->parent, (H5AC_info_t *)internal) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency")
+
+ /* Detach from 'top' proxy for v2 B-tree */
+ if(internal->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(internal->top_proxy, internal) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency between internal node and v2 B-tree 'top' proxy")
+ internal->top_proxy = NULL;
+ } /* end if */
+ break;
+
+ default:
+#ifdef NDEBUG
+ HGOTO_ERROR(H5E_BTREE, H5E_BADVALUE, FAIL, "unknown action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Unknown action?!?");
+#endif /* NDEBUG */
+ } /* end switch */
+ } /* end if */
+ else
+ HDassert(NULL == internal->top_proxy);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__cache_int_notify() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5B2__cache_int_free_icr
*
* Purpose: Destroy/release an "in core representation" of a data
@@ -915,9 +1078,8 @@ H5B2__cache_leaf_deserialize(const void *_image, size_t H5_ATTR_UNUSED len,
HDassert(udata);
/* Allocate new leaf node and reset cache info */
- if(NULL == (leaf = H5FL_MALLOC(H5B2_leaf_t)))
+ if(NULL == (leaf = H5FL_CALLOC(H5B2_leaf_t)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, NULL, "memory allocation failed")
- HDmemset(&leaf->cache_info, 0, sizeof(H5AC_info_t));
/* Increment ref. count on B-tree header */
if(H5B2__hdr_incr(udata->hdr) < 0)
@@ -925,6 +1087,8 @@ H5B2__cache_leaf_deserialize(const void *_image, size_t H5_ATTR_UNUSED len,
/* Share B-tree header information */
leaf->hdr = udata->hdr;
+ leaf->parent = udata->parent;
+ leaf->shadow_epoch = udata->hdr->shadow_epoch;
/* Magic number */
if(HDmemcmp(image, H5B2_LEAF_MAGIC, (size_t)H5_SIZEOF_MAGIC))
@@ -1027,7 +1191,7 @@ H5B2__cache_leaf_image_len(const void *_thing, size_t *image_len)
*-------------------------------------------------------------------------
*/
static herr_t
-H5B2__cache_leaf_serialize(const H5F_t *f, void *_image, size_t H5_ATTR_UNUSED len,
+H5B2__cache_leaf_serialize(const H5F_t H5_ATTR_UNUSED *f, void *_image, size_t H5_ATTR_UNUSED len,
void *_thing)
{
H5B2_leaf_t *leaf = (H5B2_leaf_t *)_thing; /* Pointer to the B-tree leaf node */
@@ -1086,6 +1250,80 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5B2__cache_leaf_notify
+ *
+ * Purpose: Handle cache action notifications
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Neil Fortner
+ * nfortne2@hdfgroup.org
+ * Apr 25 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5B2__cache_leaf_notify(H5AC_notify_action_t action, void *_thing)
+{
+ H5B2_leaf_t *leaf = (H5B2_leaf_t *)_thing;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /*
+ * Check arguments.
+ */
+ HDassert(leaf);
+ HDassert(leaf->hdr);
+
+ /* Check if the file was opened with SWMR-write access */
+ if(leaf->hdr->swmr_write) {
+ switch(action) {
+ case H5AC_NOTIFY_ACTION_AFTER_INSERT:
+ case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ /* Create flush dependency on parent */
+ if(H5B2__create_flush_depend((H5AC_info_t *)leaf->parent, (H5AC_info_t *)leaf) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDEPEND, FAIL, "unable to create flush dependency")
+ break;
+
+ case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
+ case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
+ case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
+ case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
+ case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
+ /* do nothing */
+ break;
+
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ /* Destroy flush dependency on parent */
+ if(H5B2__destroy_flush_depend((H5AC_info_t *)leaf->parent, (H5AC_info_t *)leaf) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency")
+
+ /* Detach from 'top' proxy for v2 B-tree */
+ if(leaf->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(leaf->top_proxy, leaf) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency between leaf node and v2 B-tree 'top' proxy")
+ leaf->top_proxy = NULL;
+ } /* end if */
+ break;
+
+ default:
+#ifdef NDEBUG
+ HGOTO_ERROR(H5E_BTREE, H5E_BADVALUE, FAIL, "unknown action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Unknown action?!?");
+#endif /* NDEBUG */
+ } /* end switch */
+ } /* end if */
+ else
+ HDassert(NULL == leaf->top_proxy);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__cache_leaf_notify() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5B2__cache_leaf_free_icr
*
* Purpose: Destroy/release an "in core representation" of a data
diff --git a/src/H5B2dbg.c b/src/H5B2dbg.c
index ddff9e9..19ca89a 100644
--- a/src/H5B2dbg.c
+++ b/src/H5B2dbg.c
@@ -188,6 +188,7 @@ H5B2__int_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent,
{
H5B2_hdr_t *hdr = NULL; /* B-tree header */
H5B2_internal_t *internal = NULL; /* B-tree internal node */
+ H5B2_node_ptr_t node_ptr; /* Fake node pointer for protect */
unsigned u; /* Local index variable */
char temp_str[128]; /* Temporary string, for formatting */
herr_t ret_value=SUCCEED; /* Return value */
@@ -217,9 +218,10 @@ H5B2__int_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent,
/*
* Load the B-tree internal node
*/
- H5_CHECK_OVERFLOW(nrec, unsigned, uint16_t);
H5_CHECK_OVERFLOW(depth, unsigned, uint16_t);
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, addr, (uint16_t)nrec, (uint16_t)depth, H5AC__READ_ONLY_FLAG)))
+ node_ptr.addr = addr;
+ H5_CHECKED_ASSIGN(node_ptr.node_nrec, unsigned, nrec, uint16_t)
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, NULL, &node_ptr, (uint16_t)depth, FALSE, H5AC__READ_ONLY_FLAG)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTLOAD, FAIL, "unable to load B-tree internal node")
/* Print opening message */
@@ -298,6 +300,7 @@ H5B2__leaf_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent
{
H5B2_hdr_t *hdr = NULL; /* B-tree header */
H5B2_leaf_t *leaf = NULL; /* B-tree leaf node */
+ H5B2_node_ptr_t node_ptr; /* Fake node pointer for protect */
unsigned u; /* Local index variable */
char temp_str[128]; /* Temporary string, for formatting */
herr_t ret_value = SUCCEED; /* Return value */
@@ -328,7 +331,9 @@ H5B2__leaf_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent
* Load the B-tree leaf node
*/
H5_CHECK_OVERFLOW(nrec, unsigned, uint16_t);
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, addr, (uint16_t)nrec, H5AC__READ_ONLY_FLAG)))
+ node_ptr.addr = addr;
+ H5_CHECKED_ASSIGN(node_ptr.node_nrec, unsigned, nrec, uint16_t)
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, NULL, &node_ptr, FALSE, H5AC__READ_ONLY_FLAG)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
/* Print opening message */
diff --git a/src/H5B2hdr.c b/src/H5B2hdr.c
index 49ffb5b..251a33d 100644
--- a/src/H5B2hdr.c
+++ b/src/H5B2hdr.c
@@ -133,10 +133,6 @@ H5B2__hdr_init(H5B2_hdr_t *hdr, const H5B2_create_t *cparam, void *ctx_udata,
HDassert(cparam->split_percent > 0 && cparam->split_percent <= 100);
HDassert(cparam->merge_percent < (cparam->split_percent / 2));
- /* Initialize basic information */
- hdr->rc = 0;
- hdr->pending_delete = FALSE;
-
/* Assign dynamic information */
hdr->depth = depth;
@@ -207,6 +203,13 @@ H5B2__hdr_init(H5B2_hdr_t *hdr, const H5B2_create_t *cparam, void *ctx_udata,
} /* end for */
} /* end if */
+ /* Determine if we are doing SWMR writes. Only enable for data chunks for now. */
+ hdr->swmr_write = (H5F_INTENT(hdr->f) & H5F_ACC_SWMR_WRITE) > 0
+ && (hdr->cls->id == H5B2_CDSET_ID || hdr->cls->id == H5B2_CDSET_FILT_ID);
+
+ /* Reset the shadow epoch */
+ hdr->shadow_epoch = 0;
+
/* Create the callback context, if the callback exists */
if(hdr->cls->crt_context)
if(NULL == (hdr->cb_ctx = (*hdr->cls->crt_context)(ctx_udata)))
@@ -285,6 +288,7 @@ H5B2__hdr_create(H5F_t *f, hid_t dxpl_id, const H5B2_create_t *cparam,
void *ctx_udata)
{
H5B2_hdr_t *hdr = NULL; /* The new v2 B-tree header information */
+ hbool_t inserted = FALSE; /* Whether the header was inserted into cache */
haddr_t ret_value = HADDR_UNDEF; /* Return value */
FUNC_ENTER_PACKAGE
@@ -307,17 +311,40 @@ H5B2__hdr_create(H5F_t *f, hid_t dxpl_id, const H5B2_create_t *cparam,
if(HADDR_UNDEF == (hdr->addr = H5MF_alloc(f, H5FD_MEM_BTREE, dxpl_id, (hsize_t)hdr->hdr_size)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, HADDR_UNDEF, "file allocation failed for B-tree header")
+ /* Create 'top' proxy for extensible array entries */
+ if(hdr->swmr_write)
+ if(NULL == (hdr->top_proxy = H5AC_proxy_entry_create()))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCREATE, HADDR_UNDEF, "can't create v2 B-tree proxy")
+
/* Cache the new B-tree node */
if(H5AC_insert_entry(f, dxpl_id, H5AC_BT2_HDR, hdr->addr, hdr, H5AC__NO_FLAGS_SET) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, HADDR_UNDEF, "can't add B-tree header to cache")
+ inserted = TRUE;
+
+ /* Add header as child of 'top' proxy */
+ if(hdr->top_proxy)
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, f, dxpl_id, hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSET, HADDR_UNDEF, "unable to add v2 B-tree header as child of array proxy")
/* Set address of v2 B-tree header to return */
ret_value = hdr->addr;
done:
- if(!H5F_addr_defined(ret_value) && hdr)
- if(H5B2__hdr_free(hdr) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTRELEASE, HADDR_UNDEF, "unable to release v2 B-tree header")
+ if(!H5F_addr_defined(ret_value))
+ if(hdr) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(hdr) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTREMOVE, HADDR_UNDEF, "unable to remove v2 B-tree header from cache")
+
+ /* Release header's disk space */
+ if(H5F_addr_defined(hdr->addr) && H5MF_xfree(f, H5FD_MEM_BTREE, dxpl_id, hdr->addr, (hsize_t)hdr->hdr_size) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, HADDR_UNDEF, "unable to free v2 B-tree header")
+
+ /* Destroy header */
+ if(H5B2__hdr_free(hdr) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTRELEASE, HADDR_UNDEF, "unable to release v2 B-tree header")
+ } /* end if */
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5B2__hdr_create() */
@@ -503,6 +530,7 @@ H5B2__hdr_protect(H5F_t *f, hid_t dxpl_id, haddr_t hdr_addr, void *ctx_udata,
unsigned flags)
{
H5B2_hdr_cache_ud_t udata; /* User data for cache callbacks */
+ H5B2_hdr_t *hdr = NULL; /* v2 B-tree header */
H5B2_hdr_t *ret_value = NULL; /* Return value */
FUNC_ENTER_PACKAGE
@@ -520,11 +548,32 @@ H5B2__hdr_protect(H5F_t *f, hid_t dxpl_id, haddr_t hdr_addr, void *ctx_udata,
udata.ctx_udata = ctx_udata;
/* Protect the header */
- if(NULL == (ret_value = (H5B2_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_BT2_HDR, hdr_addr, &udata, flags)))
+ if(NULL == (hdr = (H5B2_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_BT2_HDR, hdr_addr, &udata, flags)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, NULL, "unable to load v2 B-tree header, address = %llu", (unsigned long long)hdr_addr)
- ret_value->f = f; /* (Must be set again here, in case the header was already in the cache -QAK) */
+ hdr->f = f; /* (Must be set again here, in case the header was already in the cache -QAK) */
+
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->swmr_write && NULL == hdr->top_proxy) {
+ /* Create 'top' proxy for v2 B-tree entries */
+ if(NULL == (hdr->top_proxy = H5AC_proxy_entry_create()))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCREATE, NULL, "can't create v2 B-tree proxy")
+
+ /* Add header as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, f, dxpl_id, hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSET, NULL, "unable to add v2 B-tree header as child of proxy")
+ } /* end if */
+
+ /* Set return value */
+ ret_value = hdr;
done:
+ /* Clean up on error */
+ if(!ret_value) {
+ /* Release the header, if it was protected */
+ if(hdr && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_HDR, hdr_addr, hdr, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, NULL, "unable to unprotect v2 B-tree header, address = %llu", (unsigned long long)hdr_addr)
+ } /* end if */
+
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5B2__hdr_protect() */
@@ -623,6 +672,13 @@ H5B2__hdr_free(H5B2_hdr_t *hdr)
if(hdr->max_native_rec)
hdr->max_native_rec = H5MM_xfree(hdr->max_native_rec);
+ /* Destroy the 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_dest(hdr->top_proxy) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTRELEASE, FAIL, "unable to destroy v2 B-tree 'top' proxy")
+ hdr->top_proxy = NULL;
+ } /* end if */
+
/* Free B-tree header info */
hdr = H5FL_FREE(H5B2_hdr_t, hdr);
@@ -671,7 +727,7 @@ H5B2__hdr_delete(H5B2_hdr_t *hdr, hid_t dxpl_id)
/* Delete all nodes in B-tree */
if(H5F_addr_defined(hdr->root.addr))
- if(H5B2__delete_node(hdr, dxpl_id, hdr->depth, &hdr->root, hdr->remove_op, hdr->remove_op_data) < 0)
+ if(H5B2__delete_node(hdr, dxpl_id, hdr->depth, &hdr->root, hdr, hdr->remove_op, hdr->remove_op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to delete B-tree nodes")
/* Indicate that the heap header should be deleted & file space freed */
diff --git a/src/H5B2int.c b/src/H5B2int.c
index a8fa59f..47100fd 100644
--- a/src/H5B2int.c
+++ b/src/H5B2int.c
@@ -37,16 +37,13 @@
#include "H5private.h" /* Generic Functions */
#include "H5B2pkg.h" /* v2 B-trees */
#include "H5Eprivate.h" /* Error handling */
-#include "H5MFprivate.h" /* File memory management */
-#include "H5MMprivate.h" /* Memory management */
#include "H5VMprivate.h" /* Vectors and arrays */
+
/****************/
/* Local Macros */
/****************/
-/* Uncomment this macro to enable extra sanity checking */
-/* #define H5B2_DEBUG */
/******************/
/* Local Typedefs */
@@ -61,44 +58,15 @@
/********************/
/* Local Prototypes */
/********************/
+static herr_t H5B2__update_child_flush_depends(H5B2_hdr_t *hdr, hid_t dxpl_id,
+ unsigned depth, const H5B2_node_ptr_t *node_ptrs, unsigned start_idx,
+ unsigned end_idx, void *old_parent, void *new_parent);
-/* Helper functions */
-static herr_t H5B2__split1(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- H5B2_node_ptr_t *curr_node_ptr, unsigned *parent_cache_info_flags_ptr,
- H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx);
-static herr_t H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- H5B2_internal_t *internal, unsigned idx);
-static herr_t H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx);
-static herr_t H5B2__merge2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- H5B2_node_ptr_t *curr_node_ptr, unsigned *parent_cache_info_flags_ptr,
- H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx);
-static herr_t H5B2__merge3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- H5B2_node_ptr_t *curr_node_ptr, unsigned *parent_cache_info_flags_ptr,
- H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx);
-static herr_t H5B2__swap_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx,
- void *swap_loc);
-static herr_t H5B2__create_internal(H5B2_hdr_t *hdr, hid_t dxpl_id,
- H5B2_node_ptr_t *node_ptr, uint16_t depth);
-#ifdef H5B2_DEBUG
-/* Don't label these with H5_ATTR_PURE or you'll get even more warnings... */
-static herr_t H5B2__assert_leaf(const H5B2_hdr_t *hdr, const H5B2_leaf_t *leaf);
-static herr_t H5B2__assert_leaf2(const H5B2_hdr_t *hdr, const H5B2_leaf_t *leaf, const H5B2_leaf_t *leaf2);
-static herr_t H5B2__assert_internal(hsize_t parent_all_nrec, const H5B2_hdr_t *hdr, const H5B2_internal_t *internal);
-static herr_t H5B2__assert_internal2(hsize_t parent_all_nrec, const H5B2_hdr_t *hdr, const H5B2_internal_t *internal, const H5B2_internal_t *internal2);
-#endif /* H5B2_DEBUG */
/*********************/
/* Package Variables */
/*********************/
-/* Declare a free list to manage the H5B2_internal_t struct */
-H5FL_DEFINE(H5B2_internal_t);
-
-/* Declare a free list to manage the H5B2_leaf_t struct */
-H5FL_DEFINE(H5B2_leaf_t);
-
/*****************************/
/* Library Private Variables */
@@ -137,7 +105,7 @@ H5FL_SEQ_EXTERN(H5B2_node_info_t);
*/
herr_t
H5B2__locate_record(const H5B2_class_t *type, unsigned nrec, size_t *rec_off,
- const uint8_t *native, const void *udata, unsigned *idx, int *cmp)
+ const uint8_t *native, const void *udata, unsigned *idx, int *cmp)
{
unsigned lo = 0, hi; /* Low & high index values */
unsigned my_idx = 0; /* Final index value */
@@ -179,7 +147,7 @@ done:
*
*-------------------------------------------------------------------------
*/
-static herr_t
+herr_t
H5B2__split1(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
H5B2_node_ptr_t *curr_node_ptr, unsigned *parent_cache_info_flags_ptr,
H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx)
@@ -195,7 +163,7 @@ H5B2__split1(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
unsigned left_child_flags = H5AC__NO_FLAGS_SET, right_child_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting child nodes */
herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_PACKAGE
/* Check arguments. */
HDassert(hdr);
@@ -214,19 +182,20 @@ H5B2__split1(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Create new internal node */
internal->node_ptrs[idx + 1].all_nrec = internal->node_ptrs[idx + 1].node_nrec = 0;
- if(H5B2__create_internal(hdr, dxpl_id, &(internal->node_ptrs[idx + 1]), (uint16_t)(depth - 1)) < 0)
+ if(H5B2__create_internal(hdr, dxpl_id, internal, &(internal->node_ptrs[idx + 1]), (uint16_t)(depth - 1)) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "unable to create new internal node")
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_INT;
- left_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Protect both leaves */
- if(NULL == (left_int = H5B2__protect_internal(hdr, dxpl_id, left_addr, internal->node_ptrs[idx].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ /* (Shadow left node if doing SWMR writes) */
+ if(NULL == (left_int = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx], (uint16_t)(depth - 1), hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
- if(NULL == (right_int = H5B2__protect_internal(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_int = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], (uint16_t)(depth - 1), FALSE, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for child nodes */
left_child = left_int;
@@ -243,19 +212,20 @@ H5B2__split1(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Create new leaf node */
internal->node_ptrs[idx + 1].all_nrec = internal->node_ptrs[idx + 1].node_nrec = 0;
- if(H5B2__create_leaf(hdr, dxpl_id, &(internal->node_ptrs[idx + 1])) < 0)
+ if(H5B2__create_leaf(hdr, dxpl_id, internal, &(internal->node_ptrs[idx + 1])) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "unable to create new leaf node")
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_LEAF;
- left_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Protect both leaves */
- if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, left_addr, internal->node_ptrs[idx].node_nrec, H5AC__NO_FLAGS_SET)))
+ /* (Shadow the left node if doing SWMR writes) */
+ if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx], hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
- if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], FALSE, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for child nodes */
left_child = left_leaf;
@@ -295,7 +265,7 @@ H5B2__split1(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Determine total number of records in new child nodes */
if(depth > 1) {
- unsigned u; /* Local index variable */
+ unsigned u; /* Local index variable */
hsize_t new_left_all_nrec; /* New total number of records in left child */
hsize_t new_right_all_nrec; /* New total number of records in right child */
@@ -329,6 +299,12 @@ H5B2__split1(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
if(parent_cache_info_flags_ptr)
*parent_cache_info_flags_ptr |= H5AC__DIRTIED_FLAG;
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, right_node_ptrs,
+ 0, (unsigned)(*right_nrec + 1), left_child, right_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
#ifdef H5B2_DEBUG
H5B2__assert_internal((hsize_t)0, hdr, internal);
if(depth > 1) {
@@ -407,11 +383,11 @@ H5B2__split_root(H5B2_hdr_t *hdr, hid_t dxpl_id)
/* Create new internal node to use as root */
hdr->root.node_nrec = 0;
- if(H5B2__create_internal(hdr, dxpl_id, &(hdr->root), hdr->depth) < 0)
+ if(H5B2__create_internal(hdr, dxpl_id, hdr, &(hdr->root), hdr->depth) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "unable to create new internal node")
/* Protect new root node */
- if(NULL == (new_root = H5B2__protect_internal(hdr, dxpl_id, hdr->root.addr, hdr->root.node_nrec, hdr->depth, H5AC__NO_FLAGS_SET)))
+ if(NULL == (new_root = H5B2__protect_internal(hdr, dxpl_id, hdr, &hdr->root, hdr->depth, FALSE, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
/* Set first node pointer in root node to old root node pointer info */
@@ -444,7 +420,7 @@ done:
*
*-------------------------------------------------------------------------
*/
-static herr_t
+herr_t
H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
H5B2_internal_t *internal, unsigned idx)
{
@@ -458,7 +434,7 @@ H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
unsigned left_child_flags = H5AC__NO_FLAGS_SET, right_child_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting child nodes */
herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_PACKAGE
/* Check arguments. */
HDassert(hdr);
@@ -471,14 +447,15 @@ H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_INT;
- left_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Lock left & right B-tree child nodes */
- if(NULL == (left_internal = H5B2__protect_internal(hdr, dxpl_id, left_addr, internal->node_ptrs[idx].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ /* (Shadow both nodes if doing SWMR writes) */
+ if(NULL == (left_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx], (uint16_t)(depth - 1), hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
- if(NULL == (right_internal = H5B2__protect_internal(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], (uint16_t)(depth - 1), hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for child nodes */
left_child = left_internal;
@@ -496,14 +473,15 @@ H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_LEAF;
- left_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Lock left & right B-tree child nodes */
- if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, left_addr, internal->node_ptrs[idx].node_nrec, H5AC__NO_FLAGS_SET)))
+ /* (Shadow both nodes if doing SWMR writes) */
+ if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx], hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
- if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for child nodes */
left_child = left_leaf;
@@ -549,7 +527,7 @@ H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Handle node pointers, if we have an internal node */
if(depth > 1) {
hsize_t moved_nrec = move_nrec; /* Total number of records moved, for internal redistrib */
- unsigned u; /* Local index variable */
+ unsigned u; /* Local index variable */
/* Count the number of records being moved */
for(u = 0; u < move_nrec; u++)
@@ -564,6 +542,12 @@ H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
HDmemmove(&(right_node_ptrs[0]), &(right_node_ptrs[move_nrec]), sizeof(H5B2_node_ptr_t) * (new_right_nrec + (unsigned)1));
} /* end if */
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, left_node_ptrs,
+ (unsigned)(*left_nrec + 1), (unsigned)(*left_nrec + move_nrec + 1), right_child, left_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
/* Update number of records in child nodes */
*left_nrec = (uint16_t)(*left_nrec + move_nrec);
*right_nrec = new_right_nrec;
@@ -599,7 +583,7 @@ H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Handle node pointers, if we have an internal node */
if(depth > 1) {
hsize_t moved_nrec = move_nrec; /* Total number of records moved, for internal redistrib */
- unsigned u; /* Local index variable */
+ unsigned u; /* Local index variable */
/* Slide node pointers in right node up */
HDmemmove(&(right_node_ptrs[move_nrec]), &(right_node_ptrs[0]), sizeof(H5B2_node_ptr_t) * (size_t)(*right_nrec + 1));
@@ -614,6 +598,12 @@ H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
H5_CHECKED_ASSIGN(right_moved_nrec, hssize_t, moved_nrec, hsize_t)
} /* end if */
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, right_node_ptrs,
+ 0, (unsigned)move_nrec, left_child, right_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
/* Update number of records in child nodes */
*left_nrec = new_left_nrec;
*right_nrec = (uint16_t)(*right_nrec + move_nrec);
@@ -674,7 +664,7 @@ done:
*
*-------------------------------------------------------------------------
*/
-static herr_t
+herr_t
H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx)
{
@@ -695,7 +685,7 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
unsigned middle_child_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting child nodes */
herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_PACKAGE
/* Check arguments. */
HDassert(hdr);
@@ -710,17 +700,18 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_INT;
- left_addr = internal->node_ptrs[idx - 1].addr;
- middle_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Lock B-tree child nodes */
- if(NULL == (left_internal = H5B2__protect_internal(hdr, dxpl_id, left_addr, internal->node_ptrs[idx - 1].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ /* (Shadow all nodes if doing SWMR writes) */
+ if(NULL == (left_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx - 1], (uint16_t)(depth - 1), hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
- if(NULL == (middle_internal = H5B2__protect_internal(hdr, dxpl_id, middle_addr, internal->node_ptrs[idx].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx - 1].addr;
+ if(NULL == (middle_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx], (uint16_t)(depth - 1), hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
- if(NULL == (right_internal = H5B2__protect_internal(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ middle_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], (uint16_t)(depth - 1), hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for child nodes */
left_child = left_internal;
@@ -743,17 +734,18 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_LEAF;
- left_addr = internal->node_ptrs[idx - 1].addr;
- middle_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Lock B-tree child nodes */
- if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, left_addr, internal->node_ptrs[idx - 1].node_nrec, H5AC__NO_FLAGS_SET)))
+ /* (Shadow all nodes if doing SWMR writes) */
+ if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx - 1], hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
- if(NULL == (middle_leaf = H5B2__protect_leaf(hdr, dxpl_id, middle_addr, internal->node_ptrs[idx].node_nrec, H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx - 1].addr;
+ if(NULL == (middle_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx], hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
- if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, H5AC__NO_FLAGS_SET)))
+ middle_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for child nodes */
left_child = left_leaf;
@@ -802,7 +794,7 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Move node pointers also if this is an internal node */
if(depth > 1) {
- hsize_t moved_nrec; /* Total number of records moved, for internal redistrib */
+ hsize_t moved_nrec; /* Total number of records moved, for internal redistrib */
unsigned move_nptrs; /* Number of node pointers to move */
unsigned u; /* Local index variable */
@@ -820,6 +812,12 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
HDmemmove(&(middle_node_ptrs[0]), &(middle_node_ptrs[move_nptrs]), sizeof(H5B2_node_ptr_t) * ((*middle_nrec - move_nptrs) + 1));
} /* end if */
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, left_node_ptrs,
+ (unsigned)(*left_nrec + 1), (unsigned)(*left_nrec + moved_middle_nrec + 1), middle_child, left_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
/* Update the current number of records in middle node */
curr_middle_nrec = (uint16_t)(curr_middle_nrec - moved_middle_nrec);
@@ -847,7 +845,7 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Move node pointers also if this is an internal node */
if(depth > 1) {
- hsize_t moved_nrec; /* Total number of records moved, for internal redistrib */
+ hsize_t moved_nrec; /* Total number of records moved, for internal redistrib */
unsigned u; /* Local index variable */
/* Slide the node pointers in right node up */
@@ -863,6 +861,12 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
middle_moved_nrec -= (hssize_t)(moved_nrec + right_nrec_move);
} /* end if */
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, right_node_ptrs,
+ 0, (unsigned)right_nrec_move, middle_child, right_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
/* Update the current number of records in middle node */
curr_middle_nrec = (uint16_t)(curr_middle_nrec - right_nrec_move);
@@ -890,7 +894,7 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Move node pointers also if this is an internal node */
if(depth > 1) {
- hsize_t moved_nrec; /* Total number of records moved, for internal redistrib */
+ hsize_t moved_nrec; /* Total number of records moved, for internal redistrib */
unsigned u; /* Local index variable */
/* Slide the node pointers in middle node up */
@@ -906,6 +910,12 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
middle_moved_nrec += (hssize_t)(moved_nrec + left_nrec_move);
} /* end if */
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, middle_node_ptrs,
+ 0, (unsigned)left_nrec_move, left_child, middle_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
/* Update the current number of records in middle node */
curr_middle_nrec = (uint16_t)(curr_middle_nrec + left_nrec_move);
@@ -932,7 +942,7 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Move node pointers also if this is an internal node */
if(depth > 1) {
- hsize_t moved_nrec; /* Total number of records moved, for internal redistrib */
+ hsize_t moved_nrec; /* Total number of records moved, for internal redistrib */
unsigned u; /* Local index variable */
/* Move right node pointers into middle node */
@@ -948,6 +958,12 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
HDmemmove(&(right_node_ptrs[0]), &(right_node_ptrs[right_nrec_move]), sizeof(H5B2_node_ptr_t) * (size_t)(new_right_nrec + 1));
} /* end if */
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, middle_node_ptrs,
+ (unsigned)(curr_middle_nrec + 1), (unsigned)(curr_middle_nrec + right_nrec_move + 1), right_child, middle_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
/* Mark nodes as dirty */
middle_child_flags |= H5AC__DIRTIED_FLAG;
right_child_flags |= H5AC__DIRTIED_FLAG;
@@ -979,46 +995,6 @@ H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Mark parent as dirty */
*internal_flags_ptr |= H5AC__DIRTIED_FLAG;
-#ifdef QAK
-{
- unsigned u;
-
- HDfprintf(stderr, "%s: Internal records:\n", FUNC);
- for(u = 0; u < internal->nrec; u++) {
- HDfprintf(stderr, "%s: u = %u\n", FUNC, u);
- (hdr->cls->debug)(stderr, hdr->f, dxpl_id, 3, 4, H5B2_INT_NREC(internal, hdr, u), NULL);
- } /* end for */
-
- HDfprintf(stderr, "%s: Left Child records:\n", FUNC);
- for(u = 0; u < *left_nrec; u++) {
- HDfprintf(stderr, "%s: u = %u\n", FUNC, u);
- (hdr->cls->debug)(stderr, hdr->f, dxpl_id, 3, 4, H5B2_NAT_NREC(left_native, hdr, u), NULL);
- } /* end for */
-
- HDfprintf(stderr, "%s: Middle Child records:\n", FUNC);
- for(u = 0; u < *middle_nrec; u++) {
- HDfprintf(stderr, "%s: u = %u\n", FUNC, u);
- (hdr->cls->debug)(stderr, hdr->f, dxpl_id, 3, 4, H5B2_NAT_NREC(middle_native, hdr, u), NULL);
- } /* end for */
-
- HDfprintf(stderr, "%s: Right Child records:\n", FUNC);
- for(u = 0; u < *right_nrec; u++) {
- HDfprintf(stderr, "%s: u = %u\n", FUNC, u);
- (hdr->cls->debug)(stderr, hdr->f, dxpl_id, 3, 4, H5B2_NAT_NREC(right_native, hdr, u), NULL);
- } /* end for */
-
- for(u = 0; u < internal->nrec + 1; u++)
- HDfprintf(stderr, "%s: internal->node_ptrs[%u] = (%Hu/%u/%a)\n", FUNC, u, internal->node_ptrs[u].all_nrec, internal->node_ptrs[u].node_nrec, internal->node_ptrs[u].addr);
- if(depth > 1) {
- for(u = 0; u < *left_nrec + 1; u++)
- HDfprintf(stderr, "%s: left_node_ptr[%u] = (%Hu/%u/%a)\n", FUNC, u, left_node_ptrs[u].all_nrec, left_node_ptrs[u].node_nrec, left_node_ptrs[u].addr);
- for(u = 0; u < *middle_nrec + 1; u++)
- HDfprintf(stderr, "%s: middle_node_ptr[%u] = (%Hu/%u/%a)\n", FUNC, u, middle_node_ptrs[u].all_nrec, middle_node_ptrs[u].node_nrec, middle_node_ptrs[u].addr);
- for(u = 0; u < *right_nrec + 1; u++)
- HDfprintf(stderr, "%s: right_node_ptr[%u] = (%Hu/%u/%a)\n", FUNC, u, right_node_ptrs[u].all_nrec, right_node_ptrs[u].node_nrec, right_node_ptrs[u].addr);
- } /* end if */
-}
-#endif /* QAK */
#ifdef H5B2_DEBUG
H5B2__assert_internal((hsize_t)0, hdr, internal);
if(depth > 1) {
@@ -1062,7 +1038,7 @@ done:
*
*-------------------------------------------------------------------------
*/
-static herr_t
+herr_t
H5B2__merge2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
H5B2_node_ptr_t *curr_node_ptr, unsigned *parent_cache_info_flags_ptr,
H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx)
@@ -1076,7 +1052,7 @@ H5B2__merge2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
unsigned left_child_flags = H5AC__NO_FLAGS_SET, right_child_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting child nodes */
herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_PACKAGE
/* Check arguments. */
HDassert(hdr);
@@ -1091,14 +1067,15 @@ H5B2__merge2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_INT;
- left_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Lock left & right B-tree child nodes */
- if(NULL == (left_internal = H5B2__protect_internal(hdr, dxpl_id, left_addr, internal->node_ptrs[idx].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ /* (Shadow the left node if doing SWMR writes) */
+ if(NULL == (left_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx], (uint16_t)(depth - 1), hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
- if(NULL == (right_internal = H5B2__protect_internal(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], (uint16_t)(depth - 1), FALSE, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for accessing child node information */
left_child = left_internal;
@@ -1116,14 +1093,15 @@ H5B2__merge2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_LEAF;
- left_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Lock left & right B-tree child nodes */
- if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, left_addr, internal->node_ptrs[idx].node_nrec, H5AC__NO_FLAGS_SET)))
+ /* (Shadow the left node if doing SWMR writes) */
+ if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx], hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
- if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], FALSE, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for accessing child node information */
left_child = left_leaf;
@@ -1146,12 +1124,20 @@ H5B2__merge2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
if(depth > 1)
HDmemcpy(&(left_node_ptrs[*left_nrec + 1]), &(right_node_ptrs[0]), sizeof(H5B2_node_ptr_t) * (size_t)(*right_nrec + 1));
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, left_node_ptrs,
+ (unsigned)(*left_nrec + 1), (unsigned)(*left_nrec + *right_nrec + 2), right_child, left_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
/* Update # of records in left node */
*left_nrec = (uint16_t)(*left_nrec + *right_nrec + 1);
/* Mark nodes as dirty */
left_child_flags |= H5AC__DIRTIED_FLAG;
- right_child_flags |= H5AC__DIRTIED_FLAG | H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
+ right_child_flags |= H5AC__DELETED_FLAG;
+ if(!(hdr->swmr_write))
+ right_child_flags |= H5AC__DIRTIED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
} /* end block */
/* Update # of records in child nodes */
@@ -1215,7 +1201,7 @@ done:
*
*-------------------------------------------------------------------------
*/
-static herr_t
+herr_t
H5B2__merge3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
H5B2_node_ptr_t *curr_node_ptr, unsigned *parent_cache_info_flags_ptr,
H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx)
@@ -1236,7 +1222,7 @@ H5B2__merge3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
unsigned middle_child_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting child nodes */
herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_PACKAGE
/* Check arguments. */
HDassert(hdr);
@@ -1252,17 +1238,18 @@ H5B2__merge3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_INT;
- left_addr = internal->node_ptrs[idx - 1].addr;
- middle_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Lock B-tree child nodes */
- if(NULL == (left_internal = H5B2__protect_internal(hdr, dxpl_id, left_addr, internal->node_ptrs[idx - 1].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ /* (Shadow left and middle nodes if doing SWMR writes) */
+ if(NULL == (left_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx - 1], (uint16_t)(depth - 1), hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
- if(NULL == (middle_internal = H5B2__protect_internal(hdr, dxpl_id, middle_addr, internal->node_ptrs[idx].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx - 1].addr;
+ if(NULL == (middle_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx], (uint16_t)(depth - 1), hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
- if(NULL == (right_internal = H5B2__protect_internal(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
+ middle_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], (uint16_t)(depth - 1), FALSE, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for accessing child node information */
left_child = left_internal;
@@ -1285,17 +1272,18 @@ H5B2__merge3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Setup information for unlocking child nodes */
child_class = H5AC_BT2_LEAF;
- left_addr = internal->node_ptrs[idx - 1].addr;
- middle_addr = internal->node_ptrs[idx].addr;
- right_addr = internal->node_ptrs[idx + 1].addr;
/* Lock B-tree child nodes */
- if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, left_addr, internal->node_ptrs[idx - 1].node_nrec, H5AC__NO_FLAGS_SET)))
+ /* (Shadow left and middle nodes if doing SWMR writes) */
+ if(NULL == (left_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx - 1], hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
- if(NULL == (middle_leaf = H5B2__protect_leaf(hdr, dxpl_id, middle_addr, internal->node_ptrs[idx].node_nrec, H5AC__NO_FLAGS_SET)))
+ left_addr = internal->node_ptrs[idx - 1].addr;
+ if(NULL == (middle_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx], hdr->swmr_write, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
- if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, right_addr, internal->node_ptrs[idx + 1].node_nrec, H5AC__NO_FLAGS_SET)))
+ middle_addr = internal->node_ptrs[idx].addr;
+ if(NULL == (right_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx + 1], FALSE, H5AC__NO_FLAGS_SET)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ right_addr = internal->node_ptrs[idx + 1].addr;
/* More setup for accessing child node information */
left_child = left_leaf;
@@ -1344,6 +1332,12 @@ H5B2__merge3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
HDmemmove(&(middle_node_ptrs[0]), &(middle_node_ptrs[middle_nrec_move]), sizeof(H5B2_node_ptr_t) * (size_t)((unsigned)(*middle_nrec + 1) - middle_nrec_move));
} /* end if */
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, left_node_ptrs,
+ (unsigned)(*left_nrec + 1), (unsigned)(*left_nrec + middle_nrec_move + 1), middle_child, left_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
/* Update # of records in left & middle nodes */
*left_nrec = (uint16_t)(*left_nrec + middle_nrec_move);
*middle_nrec = (uint16_t)(*middle_nrec - middle_nrec_move);
@@ -1366,12 +1360,20 @@ H5B2__merge3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Copy node pointers from right node into middle node */
HDmemcpy(&(middle_node_ptrs[*middle_nrec + 1]), &(right_node_ptrs[0]), sizeof(H5B2_node_ptr_t) * (size_t)(*right_nrec + 1));
+ /* Update flush dependencies for grandchildren, if using SWMR */
+ if(hdr->swmr_write && depth > 1)
+ if(H5B2__update_child_flush_depends(hdr, dxpl_id, depth, middle_node_ptrs,
+ (unsigned)(*middle_nrec + 1), (unsigned)(*middle_nrec + *right_nrec + 2), right_child, middle_child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child nodes to new parent")
+
/* Update # of records in middle node */
*middle_nrec = (uint16_t)(*middle_nrec + (*right_nrec + 1));
/* Mark nodes as dirty */
middle_child_flags |= H5AC__DIRTIED_FLAG;
- right_child_flags |= H5AC__DIRTIED_FLAG | H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
+ right_child_flags |= H5AC__DELETED_FLAG;
+ if(!(hdr->swmr_write))
+ right_child_flags |= H5AC__DIRTIED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
} /* end block */
/* Update # of records in child nodes */
@@ -1429,98 +1431,7 @@ done:
/*-------------------------------------------------------------------------
- * Function: H5B2__swap_leaf
- *
- * Purpose: Swap a record in a node with a record in a leaf node
- *
- * Return: Success: Non-negative
- *
- * Failure: Negative
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Mar 4 2005
- *
- *-------------------------------------------------------------------------
- */
-static herr_t
-H5B2__swap_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- H5B2_internal_t *internal, unsigned *internal_flags_ptr,
- unsigned idx, void *swap_loc)
-{
- const H5AC_class_t *child_class; /* Pointer to child node's class info */
- haddr_t child_addr; /* Address of child node */
- void *child = NULL; /* Pointer to child node */
- uint8_t *child_native; /* Pointer to child's native records */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_STATIC
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(internal);
- HDassert(internal_flags_ptr);
- HDassert(idx <= internal->nrec);
-
- /* Check for the kind of B-tree node to swap */
- if(depth > 1) {
- H5B2_internal_t *child_internal; /* Pointer to internal node */
-
- /* Setup information for unlocking child node */
- child_class = H5AC_BT2_INT;
- child_addr = internal->node_ptrs[idx].addr;
-
- /* Lock B-tree child nodes */
- if(NULL == (child_internal = H5B2__protect_internal(hdr, dxpl_id, child_addr, internal->node_ptrs[idx].node_nrec, (uint16_t)(depth - 1), H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
-
- /* More setup for accessing child node information */
- child = child_internal;
- child_native = child_internal->int_native;
- } /* end if */
- else {
- H5B2_leaf_t *child_leaf; /* Pointer to leaf node */
-
- /* Setup information for unlocking child nodes */
- child_class = H5AC_BT2_LEAF;
- child_addr = internal->node_ptrs[idx].addr;
-
- /* Lock B-tree child node */
- if(NULL == (child_leaf = H5B2__protect_leaf(hdr, dxpl_id, child_addr, internal->node_ptrs[idx].node_nrec, H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
-
- /* More setup for accessing child node information */
- child = child_leaf;
- child_native = child_leaf->leaf_native;
- } /* end else */
-
- /* Swap records (use disk page as temporary buffer) */
- HDmemcpy(hdr->page, H5B2_NAT_NREC(child_native, hdr, 0), hdr->cls->nrec_size);
- HDmemcpy(H5B2_NAT_NREC(child_native, hdr, 0), swap_loc, hdr->cls->nrec_size);
- HDmemcpy(swap_loc, hdr->page, hdr->cls->nrec_size);
-
- /* Mark parent as dirty */
- *internal_flags_ptr |= H5AC__DIRTIED_FLAG;
-
-#ifdef H5B2_DEBUG
- H5B2__assert_internal((hsize_t)0, hdr, internal);
- if(depth > 1)
- H5B2__assert_internal(internal->node_ptrs[idx].all_nrec, hdr, (H5B2_internal_t *)child);
- else
- H5B2__assert_leaf(hdr, (H5B2_leaf_t *)child);
-#endif /* H5B2_DEBUG */
-
-done:
- /* Unlock child node */
- if(child && H5AC_unprotect(hdr->f, dxpl_id, child_class, child_addr, child, H5AC__DIRTIED_FLAG) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree child node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* end H5B2__swap_leaf() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__insert_hdr
+ * Function: H5B2__insert
*
* Purpose: Adds a new record to the B-tree.
*
@@ -1533,7 +1444,7 @@ done:
*-------------------------------------------------------------------------
*/
herr_t
-H5B2__insert_hdr(H5B2_hdr_t *hdr, hid_t dxpl_id, void *udata)
+H5B2__insert(H5B2_hdr_t *hdr, hid_t dxpl_id, void *udata)
{
herr_t ret_value = SUCCEED; /* Return value */
@@ -1546,7 +1457,7 @@ H5B2__insert_hdr(H5B2_hdr_t *hdr, hid_t dxpl_id, void *udata)
/* Check if the root node is allocated yet */
if(!H5F_addr_defined(hdr->root.addr)) {
/* Create root node as leaf node in B-tree */
- if(H5B2__create_leaf(hdr, dxpl_id, &(hdr->root)) < 0)
+ if(H5B2__create_leaf(hdr, dxpl_id, hdr, &(hdr->root)) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "unable to create root node")
} /* end if */
/* Check if we need to split the root node (equiv. to a 1->2 node split) */
@@ -1558,11 +1469,11 @@ H5B2__insert_hdr(H5B2_hdr_t *hdr, hid_t dxpl_id, void *udata)
/* Attempt to insert record into B-tree */
if(hdr->depth > 0) {
- if(H5B2__insert_internal(hdr, dxpl_id, hdr->depth, NULL, &hdr->root, H5B2_POS_ROOT, udata) < 0)
+ if(H5B2__insert_internal(hdr, dxpl_id, hdr->depth, NULL, &hdr->root, H5B2_POS_ROOT, hdr, udata) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree internal node")
} /* end if */
else {
- if(H5B2__insert_leaf(hdr, dxpl_id, &hdr->root, H5B2_POS_ROOT, udata) < 0)
+ if(H5B2__insert_leaf(hdr, dxpl_id, &hdr->root, H5B2_POS_ROOT, hdr, udata) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree leaf node")
} /* end else */
@@ -1572,834 +1483,7 @@ H5B2__insert_hdr(H5B2_hdr_t *hdr, hid_t dxpl_id, void *udata)
done:
FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__insert_hdr() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__insert_leaf
- *
- * Purpose: Adds a new record to a B-tree leaf node.
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Mar 3 2005
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__insert_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *curr_node_ptr,
- H5B2_nodepos_t curr_pos, void *udata)
-{
- H5B2_leaf_t *leaf; /* Pointer to leaf node */
- int cmp; /* Comparison value of records */
- unsigned idx; /* Location of record which matches key */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
-
- /* Lock current B-tree node */
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, curr_node_ptr->addr, curr_node_ptr->node_nrec, H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
-
- /* Must have a leaf node with enough space to insert a record now */
- HDassert(curr_node_ptr->node_nrec < hdr->node_info[0].max_nrec);
-
- /* Sanity check number of records */
- HDassert(curr_node_ptr->all_nrec == curr_node_ptr->node_nrec);
- HDassert(leaf->nrec == curr_node_ptr->node_nrec);
-
- /* Check for inserting into empty leaf */
- if(leaf->nrec == 0)
- idx = 0;
- else {
- /* Find correct location to insert this record */
- if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- if(cmp == 0)
- HGOTO_ERROR(H5E_BTREE, H5E_EXISTS, FAIL, "record is already in B-tree")
- if(cmp > 0)
- idx++;
-
- /* Make room for new record */
- if(idx < leaf->nrec)
- HDmemmove(H5B2_LEAF_NREC(leaf, hdr, idx + 1), H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size * (leaf->nrec - idx));
- } /* end else */
-
- /* Make callback to store record in native form */
- if((hdr->cls->store)(H5B2_LEAF_NREC(leaf, hdr, idx), udata) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into leaf node")
-
- /* Update record count for node pointer to current node */
- curr_node_ptr->all_nrec++;
- curr_node_ptr->node_nrec++;
-
- /* Update record count for current node */
- leaf->nrec++;
-
- /* Check for new record being the min or max for the tree */
- /* (Don't use 'else' for the idx check, to allow for root leaf node) */
- if(H5B2_POS_MIDDLE != curr_pos) {
- if(idx == 0) {
- if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos) {
- if(hdr->min_native_rec == NULL)
- if(NULL == (hdr->min_native_rec = H5MM_malloc(hdr->cls->nrec_size)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "memory allocation failed for v2 B-tree min record info")
- HDmemcpy(hdr->min_native_rec, H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size);
- } /* end if */
- } /* end if */
- if(idx == (unsigned)(leaf->nrec - 1)) {
- if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos) {
- if(hdr->max_native_rec == NULL)
- if(NULL == (hdr->max_native_rec = H5MM_malloc(hdr->cls->nrec_size)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "memory allocation failed for v2 B-tree max record info")
- HDmemcpy(hdr->max_native_rec, H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size);
- } /* end if */
- } /* end if */
- } /* end if */
-
-done:
- /* Release the B-tree leaf node (marked as dirty) */
- if(leaf && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr->addr, leaf, H5AC__DIRTIED_FLAG) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release leaf B-tree node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__insert_leaf() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__insert_internal
- *
- * Purpose: Adds a new record to a B-tree node.
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Mar 2 2005
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__insert_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- unsigned *parent_cache_info_flags_ptr, H5B2_node_ptr_t *curr_node_ptr,
- H5B2_nodepos_t curr_pos, void *udata)
-{
- H5B2_internal_t *internal = NULL; /* Pointer to internal node */
- unsigned internal_flags = H5AC__NO_FLAGS_SET;
- unsigned idx; /* Location of record which matches key */
- H5B2_nodepos_t next_pos = H5B2_POS_MIDDLE; /* Position of node */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(depth > 0);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
-
- /* Lock current B-tree node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node_ptr->addr, curr_node_ptr->node_nrec, depth, H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
-
- /* Sanity check number of records */
- HDassert(internal->nrec == curr_node_ptr->node_nrec);
-
- /* Split or redistribute child node pointers, if necessary */
- {
- int cmp; /* Comparison value of records */
- unsigned retries; /* Number of times to attempt redistribution */
- size_t split_nrec; /* Number of records to split node at */
-
- /* Locate node pointer for child */
- if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native,
- udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- if(cmp == 0)
- HGOTO_ERROR(H5E_BTREE, H5E_EXISTS, FAIL, "record is already in B-tree")
- if(cmp > 0)
- idx++;
-
- /* Set the number of redistribution retries */
- /* This takes care of the case where a B-tree node needs to be
- * redistributed, but redistributing the node causes the index
- * for insertion to move to another node, which also needs to be
- * redistributed. Now, we loop trying to redistribute and then
- * eventually force a split */
- retries = 2;
-
- /* Determine the correct number of records to split child node at */
- split_nrec = hdr->node_info[depth - 1].split_nrec;
-
- /* Preemptively split/redistribute a node we will enter */
- while(internal->node_ptrs[idx].node_nrec == split_nrec) {
- /* Attempt to redistribute records among children */
- if(idx == 0) { /* Left-most child */
- if(retries > 0 && (internal->node_ptrs[idx + 1].node_nrec < split_nrec)) {
- if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
- } /* end if */
- else {
- if(H5B2__split1(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to split child node")
- } /* end else */
- } /* end if */
- else if(idx == internal->nrec) { /* Right-most child */
- if(retries > 0 && (internal->node_ptrs[idx - 1].node_nrec < split_nrec)) {
- if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, (idx - 1)) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
- } /* end if */
- else {
- if(H5B2__split1(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to split child node")
- } /* end else */
- } /* end if */
- else { /* Middle child */
- if(retries > 0 && ((internal->node_ptrs[idx + 1].node_nrec < split_nrec) ||
- (internal->node_ptrs[idx - 1].node_nrec < split_nrec))) {
- if(H5B2__redistribute3(hdr, dxpl_id, depth, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
- } /* end if */
- else {
- if(H5B2__split1(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to split child node")
- } /* end else */
- } /* end else */
-
- /* Locate node pointer for child (after split/redistribute) */
- /* Actually, this can be easily updated (for 2-node redistrib.) and shouldn't require re-searching */
- if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native,
- udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- if(cmp == 0)
- HGOTO_ERROR(H5E_BTREE, H5E_EXISTS, FAIL, "record is already in B-tree")
- if(cmp > 0)
- idx++;
-
- /* Decrement the number of redistribution retries left */
- retries--;
- } /* end while */
- } /* end block */
-
- /* Check if this node is left/right-most */
- if(H5B2_POS_MIDDLE != curr_pos) {
- if(idx == 0) {
- if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
- next_pos = H5B2_POS_LEFT;
- } /* end if */
- else if(idx == internal->nrec) {
- if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
- next_pos = H5B2_POS_RIGHT;
- } /* end else */
- } /* end if */
-
- /* Attempt to insert node */
- if(depth > 1) {
- if(H5B2__insert_internal(hdr, dxpl_id, (uint16_t)(depth - 1), &internal_flags, &internal->node_ptrs[idx], next_pos, udata) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree internal node")
- } /* end if */
- else {
- if(H5B2__insert_leaf(hdr, dxpl_id, &internal->node_ptrs[idx], next_pos, udata) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree leaf node")
- } /* end else */
-
- /* Update record count for node pointer to current node */
- curr_node_ptr->all_nrec++;
-
- /* Mark node as dirty */
- internal_flags |= H5AC__DIRTIED_FLAG;
-
-done:
- /* Release the B-tree internal node */
- if(internal && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__insert_internal() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__update_leaf
- *
- * Purpose: Insert or modify a record in a B-tree leaf node.
- * If the record exists already, it is modified as if H5B2_modify
- * was called). If it doesn't exist, it is inserted as if
- * H5B2_insert was called.
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@hdfgroup.org
- * Dec 23 2015
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__update_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *curr_node_ptr,
- H5B2_update_status_t *status, H5B2_nodepos_t curr_pos, void *udata,
- H5B2_modify_t op, void *op_data)
-{
- H5B2_leaf_t *leaf; /* Pointer to leaf node */
- unsigned leaf_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting the leaf node */
- int cmp = -1; /* Comparison value of records */
- unsigned idx; /* Location of record which matches key */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
-
- /* Lock current B-tree node */
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, curr_node_ptr->addr, curr_node_ptr->node_nrec, H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
-
- /* Sanity check number of records */
- HDassert(curr_node_ptr->all_nrec == curr_node_ptr->node_nrec);
- HDassert(leaf->nrec == curr_node_ptr->node_nrec);
-
- /* Check for inserting into empty leaf */
- if(leaf->nrec == 0)
- idx = 0;
- else {
- /* Find correct location to insert this record */
- if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
-
- /* Check for inserting a record */
- if(0 != cmp) {
- /* Check if the leaf node is full */
- if(curr_node_ptr->node_nrec == hdr->node_info[0].split_nrec) {
- /* Indicate that the leaf is full, but we need to insert */
- *status = H5B2_UPDATE_INSERT_CHILD_FULL;
-
- /* Let calling routine handle insertion */
- HGOTO_DONE(SUCCEED)
- } /* end if */
-
- /* Adjust index to leave room for record to insert */
- if(cmp > 0)
- idx++;
-
- /* Make room for new record */
- if(idx < leaf->nrec)
- HDmemmove(H5B2_LEAF_NREC(leaf, hdr, idx + 1), H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size * (leaf->nrec - idx));
- } /* end if */
- } /* end else */
-
- /* Check for modifying existing record */
- if(0 == cmp) {
- hbool_t changed = FALSE; /* Whether the 'modify' callback changed the record */
-
- /* Make callback for current record */
- if((op)(H5B2_LEAF_NREC(leaf, hdr, idx), op_data, &changed) < 0) {
- /* Make certain that the callback didn't modify the value if it failed */
- HDassert(changed == FALSE);
-
- HGOTO_ERROR(H5E_BTREE, H5E_CANTMODIFY, FAIL, "'modify' callback failed for B-tree update operation")
- } /* end if */
-
- /* Mark the node as dirty if it changed */
- leaf_flags |= (changed ? H5AC__DIRTIED_FLAG : 0);
-
- /* Indicate that the record was modified */
- *status = H5B2_UPDATE_MODIFY_DONE;
- } /* end if */
- else {
- /* Must have a leaf node with enough space to insert a record now */
- HDassert(curr_node_ptr->node_nrec < hdr->node_info[0].max_nrec);
-
- /* Make callback to store record in native form */
- if((hdr->cls->store)(H5B2_LEAF_NREC(leaf, hdr, idx), udata) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into leaf node")
-
- /* Mark the node as dirty */
- leaf_flags |= H5AC__DIRTIED_FLAG;
-
- /* Indicate that the record was inserted */
- *status = H5B2_UPDATE_INSERT_DONE;
-
- /* Update record count for node pointer to current node */
- curr_node_ptr->all_nrec++;
- curr_node_ptr->node_nrec++;
-
- /* Update record count for current node */
- leaf->nrec++;
- } /* end else */
-
- /* Check for new record being the min or max for the tree */
- /* (Don't use 'else' for the idx check, to allow for root leaf node) */
- if(H5B2_POS_MIDDLE != curr_pos) {
- if(idx == 0) {
- if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos) {
- if(hdr->min_native_rec == NULL)
- if(NULL == (hdr->min_native_rec = H5MM_malloc(hdr->cls->nrec_size)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "memory allocation failed for v2 B-tree min record info")
- HDmemcpy(hdr->min_native_rec, H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size);
- } /* end if */
- } /* end if */
- if(idx == (unsigned)(leaf->nrec - 1)) {
- if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos) {
- if(hdr->max_native_rec == NULL)
- if(NULL == (hdr->max_native_rec = H5MM_malloc(hdr->cls->nrec_size)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "memory allocation failed for v2 B-tree max record info")
- HDmemcpy(hdr->max_native_rec, H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size);
- } /* end if */
- } /* end if */
- } /* end if */
-
-done:
- /* Release the B-tree leaf node */
- if(leaf && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr->addr, leaf, leaf_flags) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release leaf B-tree node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__update_leaf() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__update_internal
- *
- * Purpose: Insert or modify a record in a B-tree leaf node.
- * If the record exists already, it is modified as if H5B2_modify
- * was called). If it doesn't exist, it is inserted as if
- * H5B2_insert was called.
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@hdfgroup.org
- * Dec 24 2015
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__update_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- unsigned *parent_cache_info_flags_ptr, H5B2_node_ptr_t *curr_node_ptr,
- H5B2_update_status_t *status, H5B2_nodepos_t curr_pos, void *udata,
- H5B2_modify_t op, void *op_data)
-{
- H5B2_internal_t *internal = NULL; /* Pointer to internal node */
- unsigned internal_flags = H5AC__NO_FLAGS_SET;
- int cmp; /* Comparison value of records */
- unsigned idx; /* Location of record which matches key */
- H5B2_nodepos_t next_pos = H5B2_POS_MIDDLE; /* Position of node */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(depth > 0);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
-
- /* Lock current B-tree node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node_ptr->addr, curr_node_ptr->node_nrec, depth, H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
-
- /* Sanity check number of records */
- HDassert(internal->nrec == curr_node_ptr->node_nrec);
-
- /* Locate node pointer for child */
- if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
-
- /* Check for modifying existing record */
- if(0 == cmp) {
- hbool_t changed = FALSE; /* Whether the 'modify' callback changed the record */
-
- /* Make callback for current record */
- if((op)(H5B2_INT_NREC(internal, hdr, idx), op_data, &changed) < 0) {
- /* Make certain that the callback didn't modify the value if it failed */
- HDassert(changed == FALSE);
-
- HGOTO_ERROR(H5E_BTREE, H5E_CANTMODIFY, FAIL, "'modify' callback failed for B-tree update operation")
- } /* end if */
-
- /* Mark the node as dirty if it changed */
- internal_flags |= (changed ? H5AC__DIRTIED_FLAG : 0);
-
- /* Indicate that the record was modified */
- *status = H5B2_UPDATE_MODIFY_DONE;
- } /* end if */
- else {
- /* Adjust index to leave room for node to insert */
- if(cmp > 0)
- idx++;
-
- /* Check if this node is left/right-most */
- if(H5B2_POS_MIDDLE != curr_pos) {
- if(idx == 0) {
- if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
- next_pos = H5B2_POS_LEFT;
- } /* end if */
- else if(idx == internal->nrec) {
- if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
- next_pos = H5B2_POS_RIGHT;
- } /* end else */
- } /* end if */
-
- /* Attempt to update record in child */
- if(depth > 1) {
- if(H5B2__update_internal(hdr, dxpl_id, (uint16_t)(depth - 1), &internal_flags, &internal->node_ptrs[idx], status, next_pos, udata, op, op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update record in internal B-tree node")
- } /* end if */
- else {
- if(H5B2__update_leaf(hdr, dxpl_id, &internal->node_ptrs[idx], status, next_pos, udata, op, op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update record in leaf B-tree node")
- } /* end else */
-
- /* Take actions based on child's status report */
- switch(*status) {
- case H5B2_UPDATE_MODIFY_DONE:
- /* No action */
- break;
-
- case H5B2_UPDATE_INSERT_DONE:
- /* Mark node as dirty */
- internal_flags |= H5AC__DIRTIED_FLAG;
-
- /* Update total record count for node pointer to current node */
- curr_node_ptr->all_nrec++;
- break;
-
- case H5B2_UPDATE_INSERT_CHILD_FULL:
- /* Split/redistribute this node */
- if(internal->nrec == hdr->node_info[depth].split_nrec) {
- hbool_t could_split = FALSE; /* Whether the child node could split */
-
-#ifdef QAK
-HDfprintf(stderr, "%s: idx = %u, internal->nrec = %u\n", FUNC, idx, internal->nrec);
-HDfprintf(stderr, "%s: hdr->node_info[%u].split_nrec = %u\n", FUNC, (unsigned)depth, (unsigned)hdr->node_info[depth].split_nrec);
-HDfprintf(stderr, "%s: hdr->node_info[%u].split_nrec = %u\n", FUNC, (unsigned)(depth - 1), (unsigned)hdr->node_info[depth - 1].split_nrec);
-#endif /* QAK */
- if(idx == 0) { /* Left-most child */
-#ifdef QAK
-HDfprintf(stderr, "%s: Left-most child\n", FUNC);
-HDfprintf(stderr, "%s: internal->node_ptrs[%u].node_nrec = %u\n", FUNC, (unsigned)idx, (unsigned)internal->node_ptrs[idx].node_nrec);
-HDfprintf(stderr, "%s: internal->node_ptrs[%u].node_nrec = %u\n", FUNC, (unsigned)(idx + 1), (unsigned)internal->node_ptrs[idx + 1].node_nrec);
-#endif /* QAK */
- /* Check for left-most child and its neighbor being close to full */
- if((internal->node_ptrs[idx].node_nrec + internal->node_ptrs[idx + 1].node_nrec)
- >= ((hdr->node_info[depth - 1].split_nrec * 2) - 1))
- could_split = TRUE;
- } /* end if */
- else if(idx == internal->nrec) { /* Right-most child */
-#ifdef QAK
-HDfprintf(stderr, "%s: Right-most child\n", FUNC);
-HDfprintf(stderr, "%s: internal->node_ptrs[%u].node_nrec = %u\n", FUNC, (unsigned)(idx - 1), (unsigned)internal->node_ptrs[idx - 1].node_nrec);
-HDfprintf(stderr, "%s: internal->node_ptrs[%u].node_nrec = %u\n", FUNC, (unsigned)idx, (unsigned)internal->node_ptrs[idx].node_nrec);
-#endif /* QAK */
- /* Check for right-most child and its neighbor being close to full */
- if((internal->node_ptrs[idx - 1].node_nrec + internal->node_ptrs[idx].node_nrec)
- >= ((hdr->node_info[depth - 1].split_nrec * 2) - 1))
- could_split = TRUE;
- } /* end else-if */
- else { /* Middle child */
-#ifdef QAK
-HDfprintf(stderr, "%s: Middle child\n", FUNC);
-HDfprintf(stderr, "%s: internal->node_ptrs[%u].node_nrec = %u\n", FUNC, (unsigned)(idx - 1), (unsigned)internal->node_ptrs[idx - 1].node_nrec);
-HDfprintf(stderr, "%s: internal->node_ptrs[%u].node_nrec = %u\n", FUNC, (unsigned)idx, (unsigned)internal->node_ptrs[idx].node_nrec);
-HDfprintf(stderr, "%s: internal->node_ptrs[%u].node_nrec = %u\n", FUNC, (unsigned)(idx + 1), (unsigned)internal->node_ptrs[idx + 1].node_nrec);
-#endif /* QAK */
- /* Check for middle child and its left neighbor being close to full */
- if((internal->node_ptrs[idx - 1].node_nrec + internal->node_ptrs[idx].node_nrec)
- >= ((hdr->node_info[depth - 1].split_nrec * 2) - 1))
- could_split = TRUE;
- /* Check for middle child and its right neighbor being close to full */
- else if((internal->node_ptrs[idx].node_nrec + internal->node_ptrs[idx + 1].node_nrec)
- >= ((hdr->node_info[depth - 1].split_nrec * 2) - 1))
- could_split = TRUE;
- } /* end if */
-
- /* If this node is full and the child node insertion could
- * cause a split, punt back up to caller, leaving the
- * "insert child full" status.
- */
- if(could_split) {
- /* Release the internal B-tree node */
- if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
- internal = NULL;
-
-#ifdef QAK
-HDfprintf(stderr, "%s: Punting back to caller\n", FUNC);
-#endif /* QAK */
- /* Punt back to caller */
- HGOTO_DONE(SUCCEED);
- } /* end if */
- } /* end if */
-
- /* Release the internal B-tree node */
- if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
- internal = NULL;
-
- /* Indicate that the record was inserted */
- *status = H5B2_UPDATE_INSERT_DONE;
-
- /* Dodge sideways into inserting a record into this node */
- if(H5B2__insert_internal(hdr, dxpl_id, depth, parent_cache_info_flags_ptr, curr_node_ptr, curr_pos, udata) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into internal B-tree node")
- break;
-
- case H5B2_UPDATE_UNKNOWN:
- default:
- HDassert(0 && "Invalid update status");
- HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "invalid update status")
- } /* end switch */
- } /* end else */
-
-done:
- /* Release the internal B-tree node */
- if(internal && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__update_internal() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__create_leaf
- *
- * Purpose: Creates empty leaf node of a B-tree and update node pointer
- * to point to it.
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Feb 2 2005
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__create_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *node_ptr)
-{
- H5B2_leaf_t *leaf = NULL; /* Pointer to new leaf node created */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(node_ptr);
-
- /* Allocate memory for leaf information */
- if(NULL == (leaf = H5FL_MALLOC(H5B2_leaf_t)))
- HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree leaf info")
-
- /* Set metadata cache info */
- HDmemset(&leaf->cache_info, 0, sizeof(H5AC_info_t));
-
- /* Increment ref. count on B-tree header */
- if(H5B2__hdr_incr(hdr) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTINC, FAIL, "can't increment ref. count on B-tree header")
-
- /* Share B-tree header information */
- leaf->hdr = hdr;
-
- /* Allocate space for the native keys in memory */
- if(NULL == (leaf->leaf_native = (uint8_t *)H5FL_FAC_MALLOC(hdr->node_info[0].nat_rec_fac)))
- HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree leaf native keys")
- HDmemset(leaf->leaf_native, 0, hdr->cls->nrec_size * hdr->node_info[0].max_nrec);
-
- /* Set number of records */
- leaf->nrec = 0;
-
- /* Allocate space on disk for the leaf */
- if(HADDR_UNDEF == (node_ptr->addr = H5MF_alloc(hdr->f, H5FD_MEM_BTREE, dxpl_id, (hsize_t)hdr->node_size)))
- HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for B-tree leaf node")
-
- /* Cache the new B-tree node */
- if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_BT2_LEAF, node_ptr->addr, leaf, H5AC__NO_FLAGS_SET) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "can't add B-tree leaf to cache")
-
-done:
- if(ret_value < 0) {
- if(leaf)
- if(H5B2__leaf_free(leaf) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, FAIL, "unable to release v2 B-tree leaf node")
- } /* end if */
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__create_leaf() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__protect_leaf
- *
- * Purpose: "Protect" an leaf node in the metadata cache
- *
- * Return: Pointer to leaf node on success/NULL on failure
- *
- * Programmer: Quincey Koziol
- * koziol@hdfgroup.org
- * May 5 2010
- *
- *-------------------------------------------------------------------------
- */
-H5B2_leaf_t *
-H5B2__protect_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, haddr_t addr, uint16_t nrec,
- unsigned flags)
-{
- H5B2_leaf_cache_ud_t udata; /* User-data for callback */
- H5B2_leaf_t *ret_value = NULL; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(H5F_addr_defined(addr));
-
- /* only H5AC__READ_ONLY_FLAG may appear in flags */
- HDassert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0);
-
- /* Set up user data for callback */
- udata.f = hdr->f;
- udata.hdr = hdr;
- H5_CHECKED_ASSIGN(udata.nrec, uint16_t, nrec, unsigned)
-
- /* Protect the leaf node */
- if(NULL == (ret_value = (H5B2_leaf_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_BT2_LEAF, addr, &udata, flags)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, NULL, "unable to protect B-tree leaf node")
-
-done:
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__protect_leaf() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__create_internal
- *
- * Purpose: Creates empty internal node of a B-tree and update node pointer
- * to point to it.
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Feb 3 2005
- *
- *-------------------------------------------------------------------------
- */
-static herr_t
-H5B2__create_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *node_ptr,
- uint16_t depth)
-{
- H5B2_internal_t *internal = NULL; /* Pointer to new internal node created */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_STATIC
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(node_ptr);
- HDassert(depth > 0);
-
- /* Allocate memory for internal node information */
- if(NULL == (internal = H5FL_MALLOC(H5B2_internal_t)))
- HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree internal info")
-
- /* Set metadata cache info */
- HDmemset(&internal->cache_info, 0, sizeof(H5AC_info_t));
-
- /* Increment ref. count on B-tree header */
- if(H5B2__hdr_incr(hdr) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTINC, FAIL, "can't increment ref. count on B-tree header")
-
- /* Share B-tree header information */
- internal->hdr = hdr;
-
- /* Allocate space for the native keys in memory */
- if(NULL == (internal->int_native = (uint8_t *)H5FL_FAC_MALLOC(hdr->node_info[depth].nat_rec_fac)))
- HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree internal native keys")
- HDmemset(internal->int_native, 0, hdr->cls->nrec_size * hdr->node_info[depth].max_nrec);
-
- /* Allocate space for the node pointers in memory */
- if(NULL == (internal->node_ptrs = (H5B2_node_ptr_t *)H5FL_FAC_MALLOC(hdr->node_info[depth].node_ptr_fac)))
- HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree internal node pointers")
- HDmemset(internal->node_ptrs, 0, sizeof(H5B2_node_ptr_t) * (hdr->node_info[depth].max_nrec + 1));
-
- /* Set number of records & depth of the node */
- internal->nrec = 0;
- internal->depth = depth;
-
- /* Allocate space on disk for the internal node */
- if(HADDR_UNDEF == (node_ptr->addr = H5MF_alloc(hdr->f, H5FD_MEM_BTREE, dxpl_id, (hsize_t)hdr->node_size)))
- HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for B-tree internal node")
-
- /* Cache the new B-tree node */
- if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_BT2_INT, node_ptr->addr, internal, H5AC__NO_FLAGS_SET) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "can't add B-tree internal node to cache")
-
-done:
- if(ret_value < 0) {
- if(internal)
- if(H5B2__internal_free(internal) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, FAIL, "unable to release v2 B-tree internal node")
- } /* end if */
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__create_internal() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__protect_internal
- *
- * Purpose: "Protect" an internal node in the metadata cache
- *
- * Return: Pointer to internal node on success/NULL on failure
- *
- * Programmer: Quincey Koziol
- * koziol@hdfgroup.org
- * Aug 25 2006
- *
- *-------------------------------------------------------------------------
- */
-H5B2_internal_t *
-H5B2__protect_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, haddr_t addr,
- uint16_t nrec, uint16_t depth, unsigned flags)
-{
- H5B2_internal_cache_ud_t udata; /* User data to pass through to cache 'deserialize' callback */
- H5B2_internal_t *ret_value = NULL; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(H5F_addr_defined(addr));
- HDassert(depth > 0);
-
- /* only H5AC__READ_ONLY_FLAG may appear in flags */
- HDassert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0);
-
- /* Set up user data for callback */
- udata.f = hdr->f;
- udata.hdr = hdr;
- udata.nrec = nrec;
- udata.depth = depth;
-
- /* Protect the internal node */
- if(NULL == (ret_value = (H5B2_internal_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_BT2_INT, addr, &udata, flags)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, NULL, "unable to protect B-tree internal node")
-
-done:
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__protect_internal() */
+} /* H5B2__insert() */
/*-------------------------------------------------------------------------
@@ -2421,13 +1505,15 @@ done:
*/
herr_t
H5B2__iterate_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- const H5B2_node_ptr_t *curr_node, H5B2_operator_t op, void *op_data)
+ const H5B2_node_ptr_t *curr_node, void *parent, H5B2_operator_t op,
+ void *op_data)
{
const H5AC_class_t *curr_node_class = NULL; /* Pointer to current node's class info */
void *node = NULL; /* Pointers to current node */
uint8_t *node_native; /* Pointers to node's native records */
uint8_t *native = NULL; /* Pointers to copy of node's native records */
H5B2_node_ptr_t *node_ptrs = NULL; /* Pointers to node's node pointers */
+ hbool_t node_pinned = FALSE; /* Whether node is pinned */
unsigned u; /* Local index */
herr_t ret_value = H5_ITER_CONT; /* Iterator return value */
@@ -2443,7 +1529,7 @@ H5B2__iterate_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
H5B2_internal_t *internal; /* Pointer to internal node */
/* Lock the current B-tree node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node->addr, curr_node->node_nrec, depth, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, (H5B2_node_ptr_t *)curr_node, depth, FALSE, H5AC__READ_ONLY_FLAG))) /* Casting away const OK -QAK */
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
/* Set up information about current node */
@@ -2462,7 +1548,7 @@ H5B2__iterate_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
H5B2_leaf_t *leaf; /* Pointer to leaf node */
/* Lock the current B-tree node */
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, curr_node->addr, curr_node->node_nrec, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, (H5B2_node_ptr_t *)curr_node, FALSE, H5AC__READ_ONLY_FLAG))) /* Casting away const OK -QAK */
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
/* Set up information about current node */
@@ -2479,15 +1565,18 @@ H5B2__iterate_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
HDmemcpy(native, node_native, (hdr->cls->nrec_size * curr_node->node_nrec));
/* Unlock the node */
- if(H5AC_unprotect(hdr->f, dxpl_id, curr_node_class, curr_node->addr, node, H5AC__NO_FLAGS_SET) < 0)
+ if(H5AC_unprotect(hdr->f, dxpl_id, curr_node_class, curr_node->addr, node, (unsigned)(hdr->swmr_write ? H5AC__PIN_ENTRY_FLAG : H5AC__NO_FLAGS_SET)) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node")
- node = NULL;
+ if(hdr->swmr_write)
+ node_pinned = TRUE;
+ else
+ node = NULL;
/* Iterate through records, in order */
for(u = 0; u < curr_node->node_nrec && !ret_value; u++) {
/* Descend into child node, if current node is an internal node */
if(depth > 0) {
- if((ret_value = H5B2__iterate_node(hdr, dxpl_id, (uint16_t)(depth - 1), &(node_ptrs[u]), op, op_data)) < 0)
+ if((ret_value = H5B2__iterate_node(hdr, dxpl_id, (uint16_t)(depth - 1), &(node_ptrs[u]), node, op, op_data)) < 0)
HERROR(H5E_BTREE, H5E_CANTLIST, "node iteration failed");
} /* end if */
@@ -2500,11 +1589,15 @@ H5B2__iterate_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Descend into last child node, if current node is an internal node */
if(!ret_value && depth > 0) {
- if((ret_value = H5B2__iterate_node(hdr, dxpl_id, (uint16_t)(depth - 1), &(node_ptrs[u]), op, op_data)) < 0)
+ if((ret_value = H5B2__iterate_node(hdr, dxpl_id, (uint16_t)(depth - 1), &(node_ptrs[u]), node, op, op_data)) < 0)
HERROR(H5E_BTREE, H5E_CANTLIST, "node iteration failed");
} /* end if */
done:
+ /* Unpin the node if it was pinned */
+ if(node_pinned && H5AC_unpin_entry(node) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "can't unpin node")
+
/* Release the node pointers & native records, if they were copied */
if(node_ptrs)
node_ptrs = (H5B2_node_ptr_t *)H5FL_FAC_FREE(hdr->node_info[depth].node_ptr_fac, node_ptrs);
@@ -2516,867 +1609,6 @@ done:
/*-------------------------------------------------------------------------
- * Function: H5B2__remove_leaf
- *
- * Purpose: Removes a record from a B-tree leaf node.
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Mar 3 2005
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__remove_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *curr_node_ptr,
- H5B2_nodepos_t curr_pos, void *udata, H5B2_remove_t op, void *op_data)
-{
- H5B2_leaf_t *leaf; /* Pointer to leaf node */
- haddr_t leaf_addr = HADDR_UNDEF; /* Leaf address on disk */
- unsigned leaf_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting leaf node */
- unsigned idx; /* Location of record which matches key */
- int cmp; /* Comparison value of records */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
-
- /* Lock current B-tree node */
- leaf_addr = curr_node_ptr->addr;
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, leaf_addr, curr_node_ptr->node_nrec, H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
-
- /* Sanity check number of records */
- HDassert(curr_node_ptr->all_nrec == curr_node_ptr->node_nrec);
- HDassert(leaf->nrec == curr_node_ptr->node_nrec);
-
- /* Find correct location to remove this record */
- if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- if(cmp != 0)
- HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "record is not in B-tree")
-
- /* Check for invalidating the min/max record for the tree */
- if(H5B2_POS_MIDDLE != curr_pos) {
- /* (Don't use 'else' for the idx check, to allow for root leaf node) */
- if(idx == 0) {
- if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos) {
- if(hdr->min_native_rec)
- hdr->min_native_rec = H5MM_xfree(hdr->min_native_rec);
- } /* end if */
- } /* end if */
- if(idx == (unsigned)(leaf->nrec - 1)) {
- if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos) {
- if(hdr->max_native_rec)
- hdr->max_native_rec = H5MM_xfree(hdr->max_native_rec);
- } /* end if */
- } /* end if */
- } /* end if */
-
- /* Make 'remove' callback if there is one */
- if(op)
- if((op)(H5B2_LEAF_NREC(leaf, hdr, idx), op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record into leaf node")
-
- /* Update number of records in node */
- leaf->nrec--;
-
- /* Mark leaf node as dirty also */
- leaf_flags |= H5AC__DIRTIED_FLAG;
-
- if(leaf->nrec > 0) {
- /* Pack record out of leaf */
- if(idx < leaf->nrec)
- HDmemmove(H5B2_LEAF_NREC(leaf, hdr, idx), H5B2_LEAF_NREC(leaf, hdr, (idx + 1)), hdr->cls->nrec_size * (leaf->nrec - idx));
- } /* end if */
- else {
- /* Let the cache know that the object is deleted */
- leaf_flags |= H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
-
- /* Reset address of parent node pointer */
- curr_node_ptr->addr = HADDR_UNDEF;
- } /* end else */
-
- /* Update record count for parent of leaf node */
- curr_node_ptr->node_nrec--;
-
-done:
- /* Release the B-tree leaf node */
- if(leaf && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, leaf_addr, leaf, leaf_flags) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release leaf B-tree node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__remove_leaf() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__remove_internal
- *
- * Purpose: Removes a record from a B-tree node.
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Mar 3 2005
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__remove_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, hbool_t *depth_decreased,
- void *swap_loc, uint16_t depth, H5AC_info_t *parent_cache_info,
- unsigned *parent_cache_info_flags_ptr, H5B2_nodepos_t curr_pos,
- H5B2_node_ptr_t *curr_node_ptr, void *udata, H5B2_remove_t op, void *op_data)
-{
- H5AC_info_t *new_cache_info; /* Pointer to new cache info */
- unsigned *new_cache_info_flags_ptr = NULL;
- H5B2_node_ptr_t *new_node_ptr; /* Pointer to new node pointer */
- H5B2_internal_t *internal; /* Pointer to internal node */
- H5B2_nodepos_t next_pos = H5B2_POS_MIDDLE; /* Position of next node */
- unsigned internal_flags = H5AC__NO_FLAGS_SET;
- haddr_t internal_addr; /* Address of internal node */
- size_t merge_nrec; /* Number of records to merge node at */
- hbool_t collapsed_root = FALSE; /* Whether the root was collapsed */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(depth > 0);
- HDassert(parent_cache_info);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
-
- /* Lock current B-tree node */
- internal_addr = curr_node_ptr->addr;
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, internal_addr, curr_node_ptr->node_nrec, depth, H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
-
- /* Determine the correct number of records to merge at */
- merge_nrec = hdr->node_info[depth - 1].merge_nrec;
-
- /* Check for needing to collapse the root node */
- /* (The root node is the only internal node allowed to have 1 record) */
- if(internal->nrec == 1 &&
- ((internal->node_ptrs[0].node_nrec + internal->node_ptrs[1].node_nrec) <= ((merge_nrec * 2) + 1))) {
-
- /* Merge children of root node */
- if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, 0) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
-
- /* Let the cache know that the object is deleted */
- internal_flags |= H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
-
- /* Reset information in header's root node pointer */
- curr_node_ptr->addr = internal->node_ptrs[0].addr;
- curr_node_ptr->node_nrec = internal->node_ptrs[0].node_nrec;
-
- /* Indicate that the level of the B-tree decreased */
- *depth_decreased = TRUE;
-
- /* Set pointers for advancing to child node */
- new_cache_info = parent_cache_info;
- new_cache_info_flags_ptr = parent_cache_info_flags_ptr;
- new_node_ptr = curr_node_ptr;
-
- /* Set flag to indicate root was collapsed */
- collapsed_root = TRUE;
-
- /* Indicate position of next node */
- next_pos = H5B2_POS_ROOT;
- } /* end if */
- /* Merge or redistribute child node pointers, if necessary */
- else {
- unsigned idx; /* Location of record which matches key */
- int cmp = 0; /* Comparison value of records */
- unsigned retries; /* Number of times to attempt redistribution */
-
- /* Locate node pointer for child */
- if(swap_loc)
- idx = 0;
- else {
- if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native,
- udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- if(cmp >= 0)
- idx++;
- } /* end else */
-
- /* Set the number of redistribution retries */
- /* This takes care of the case where a B-tree node needs to be
- * redistributed, but redistributing the node causes the index
- * for removal to move to another node, which also needs to be
- * redistributed. Now, we loop trying to redistribute and then
- * eventually force a merge */
- retries = 2;
-
- /* Preemptively merge/redistribute a node we will enter */
- while(internal->node_ptrs[idx].node_nrec == merge_nrec) {
- /* Attempt to redistribute records among children */
- /* (NOTE: These 2-node redistributions should actually get the
- * record to promote from the node with more records. - QAK)
- */
- /* (NOTE: This code is the same in both H5B2__remove_internal() and
- * H5B2__remove_internal_by_idx(), fix bugs in both places! - QAK)
- */
- if(idx == 0) { /* Left-most child */
- if(retries > 0 && (internal->node_ptrs[idx + 1].node_nrec > merge_nrec)) {
- if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
- } /* end if */
- else {
- if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
- } /* end else */
- } /* end if */
- else if(idx == internal->nrec) { /* Right-most child */
- if(retries > 0 && (internal->node_ptrs[idx - 1].node_nrec > merge_nrec)) {
- if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, (idx - 1)) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
- } /* end if */
- else {
- if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, (idx - 1)) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
- } /* end else */
- } /* end if */
- else { /* Middle child */
- if(retries > 0 && ((internal->node_ptrs[idx + 1].node_nrec > merge_nrec) ||
- (internal->node_ptrs[idx - 1].node_nrec > merge_nrec))) {
- if(H5B2__redistribute3(hdr, dxpl_id, depth, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
- } /* end if */
- else {
- if(H5B2__merge3(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
- } /* end else */
- } /* end else */
-
- /* Locate node pointer for child (after merge/redistribute) */
- if(swap_loc)
- idx = 0;
- else {
-/* Actually, this can be easily updated (for 2-node redistrib.) and shouldn't require re-searching */
- if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- if(cmp >= 0)
- idx++;
- } /* end else */
-
- /* Decrement the number of redistribution retries left */
- retries--;
- } /* end while */
-
- /* Handle deleting a record from an internal node */
- if(!swap_loc && cmp == 0)
- swap_loc = H5B2_INT_NREC(internal, hdr, idx - 1);
-
- /* Swap record to delete with record from leaf, if we are the last internal node */
- if(swap_loc && depth == 1)
- if(H5B2__swap_leaf(hdr, dxpl_id, depth, internal, &internal_flags, idx, swap_loc) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSWAP, FAIL, "Can't swap records in B-tree")
-
- /* Set pointers for advancing to child node */
- new_cache_info_flags_ptr = &internal_flags;
- new_cache_info = &internal->cache_info;
- new_node_ptr = &internal->node_ptrs[idx];
-
- /* Indicate position of next node */
- if(H5B2_POS_MIDDLE != curr_pos) {
- if(idx == 0) {
- if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
- next_pos = H5B2_POS_LEFT;
- } /* end if */
- else if(idx == internal->nrec) {
- if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
- next_pos = H5B2_POS_RIGHT;
- } /* end if */
- } /* end if */
- } /* end else */
-
- /* Attempt to remove record from child node */
- if(depth > 1) {
- if(H5B2__remove_internal(hdr, dxpl_id, depth_decreased, swap_loc, (uint16_t)(depth - 1),
- new_cache_info, new_cache_info_flags_ptr, next_pos, new_node_ptr, udata, op, op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree internal node")
- } /* end if */
- else {
- if(H5B2__remove_leaf(hdr, dxpl_id, new_node_ptr, next_pos, udata, op, op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree leaf node")
- } /* end else */
-
- /* Update record count for node pointer to current node */
- if(!collapsed_root)
- new_node_ptr->all_nrec--;
-
- /* Mark node as dirty */
- internal_flags |= H5AC__DIRTIED_FLAG;
-
-#ifdef H5B2_DEBUG
- H5B2__assert_internal((!collapsed_root ? (curr_node_ptr->all_nrec - 1) : new_node_ptr->all_nrec), hdr, internal);
-#endif /* H5B2_DEBUG */
-
-done:
- /* Release the B-tree internal node */
- if(internal && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, internal_addr, internal, internal_flags) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__remove_internal() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__remove_leaf_by_idx
- *
- * Purpose: Removes a record from a B-tree leaf node, according to the
- * offset in the B-tree records.
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@hdfgroup.org
- * Nov 14 2006
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__remove_leaf_by_idx(H5B2_hdr_t *hdr, hid_t dxpl_id,
- H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos,
- unsigned idx, H5B2_remove_t op, void *op_data)
-{
- H5B2_leaf_t *leaf; /* Pointer to leaf node */
- haddr_t leaf_addr = HADDR_UNDEF; /* Leaf address on disk */
- unsigned leaf_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting leaf node */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
-
- /* Lock B-tree leaf node */
- leaf_addr = curr_node_ptr->addr;
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, leaf_addr, curr_node_ptr->node_nrec, H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
-
- /* Sanity check number of records */
- HDassert(curr_node_ptr->all_nrec == curr_node_ptr->node_nrec);
- HDassert(leaf->nrec == curr_node_ptr->node_nrec);
- HDassert(idx < leaf->nrec);
-
- /* Check for invalidating the min/max record for the tree */
- if(H5B2_POS_MIDDLE != curr_pos) {
- /* (Don't use 'else' for the idx check, to allow for root leaf node) */
- if(idx == 0) {
- if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos) {
- if(hdr->min_native_rec)
- hdr->min_native_rec = H5MM_xfree(hdr->min_native_rec);
- } /* end if */
- } /* end if */
- if(idx == (unsigned)(leaf->nrec - 1)) {
- if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos) {
- if(hdr->max_native_rec)
- hdr->max_native_rec = H5MM_xfree(hdr->max_native_rec);
- } /* end if */
- } /* end if */
- } /* end if */
-
- /* Make 'remove' callback if there is one */
- if(op)
- if((op)(H5B2_LEAF_NREC(leaf, hdr, idx), op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record into leaf node")
-
- /* Update number of records in node */
- leaf->nrec--;
-
- /* Mark leaf node as dirty also */
- leaf_flags |= H5AC__DIRTIED_FLAG;
-
- if(leaf->nrec > 0) {
- /* Pack record out of leaf */
- if(idx < leaf->nrec)
- HDmemmove(H5B2_LEAF_NREC(leaf, hdr, idx), H5B2_LEAF_NREC(leaf, hdr, (idx + 1)), hdr->cls->nrec_size * (leaf->nrec - idx));
- } /* end if */
- else {
- /* Let the cache know that the object is deleted */
- leaf_flags |= H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
-
- /* Reset address of parent node pointer */
- curr_node_ptr->addr = HADDR_UNDEF;
- } /* end else */
-
- /* Update record count for parent of leaf node */
- curr_node_ptr->node_nrec--;
-
-done:
- /* Release the B-tree leaf node */
- if(leaf && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, leaf_addr, leaf, leaf_flags) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release leaf B-tree node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__remove_leaf_by_idx() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__remove_internal_by_idx
- *
- * Purpose: Removes a record from a B-tree node, according to the offset
- * in the B-tree records
- *
- * Return: Non-negative on success/Negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@hdfgroup.org
- * Nov 14 2006
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__remove_internal_by_idx(H5B2_hdr_t *hdr, hid_t dxpl_id,
- hbool_t *depth_decreased, void *swap_loc, uint16_t depth,
- H5AC_info_t *parent_cache_info, unsigned *parent_cache_info_flags_ptr,
- H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, hsize_t n,
- H5B2_remove_t op, void *op_data)
-{
- H5AC_info_t *new_cache_info; /* Pointer to new cache info */
- unsigned *new_cache_info_flags_ptr = NULL;
- H5B2_node_ptr_t *new_node_ptr; /* Pointer to new node pointer */
- H5B2_internal_t *internal; /* Pointer to internal node */
- H5B2_nodepos_t next_pos = H5B2_POS_MIDDLE; /* Position of next node */
- unsigned internal_flags = H5AC__NO_FLAGS_SET;
- haddr_t internal_addr; /* Address of internal node */
- size_t merge_nrec; /* Number of records to merge node at */
- hbool_t collapsed_root = FALSE; /* Whether the root was collapsed */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(depth > 0);
- HDassert(parent_cache_info);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
-
- /* Lock current B-tree node */
- internal_addr = curr_node_ptr->addr;
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, internal_addr, curr_node_ptr->node_nrec, depth, H5AC__NO_FLAGS_SET)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
- HDassert(internal->nrec == curr_node_ptr->node_nrec);
- HDassert(depth == hdr->depth || internal->nrec > 1);
-
- /* Determine the correct number of records to merge at */
- merge_nrec = hdr->node_info[depth - 1].merge_nrec;
-
- /* Check for needing to collapse the root node */
- /* (The root node is the only internal node allowed to have 1 record) */
- if(internal->nrec == 1 &&
- ((internal->node_ptrs[0].node_nrec + internal->node_ptrs[1].node_nrec) <= ((merge_nrec * 2) + 1))) {
- HDassert(depth == hdr->depth);
-
- /* Merge children of root node */
- if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, 0) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
-
- /* Let the cache know that the object is deleted */
- internal_flags |= H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
-
- /* Reset information in header's root node pointer */
- curr_node_ptr->addr = internal->node_ptrs[0].addr;
- curr_node_ptr->node_nrec = internal->node_ptrs[0].node_nrec;
-
- /* Indicate that the level of the B-tree decreased */
- *depth_decreased = TRUE;
-
- /* Set pointers for advancing to child node */
- new_cache_info = parent_cache_info;
- new_cache_info_flags_ptr = parent_cache_info_flags_ptr;
- new_node_ptr = curr_node_ptr;
-
- /* Set flag to indicate root was collapsed */
- collapsed_root = TRUE;
-
- /* Indicate position of next node */
- next_pos = H5B2_POS_ROOT;
- } /* end if */
- /* Merge or redistribute child node pointers, if necessary */
- else {
- hsize_t orig_n = n; /* Original index looked for */
- unsigned idx; /* Location of record which matches key */
- hbool_t found = FALSE; /* Comparison value of records */
- unsigned retries; /* Number of times to attempt redistribution */
-
- /* Locate node pointer for child */
- if(swap_loc)
- idx = 0;
- else {
- /* Search for record with correct index */
- for(idx = 0; idx < internal->nrec; idx++) {
- /* Check which child node contains indexed record */
- if(internal->node_ptrs[idx].all_nrec >= n) {
- /* Check if record is in this node */
- if(internal->node_ptrs[idx].all_nrec == n) {
- /* Indicate the record was found and that the index
- * in child nodes is zero from now on
- */
- found = TRUE;
- n = 0;
-
- /* Increment to next record */
- idx++;
- } /* end if */
-
- /* Break out of loop early */
- break;
- } /* end if */
-
- /* Decrement index we are looking for to account for the node we
- * just advanced past.
- */
- n -= (internal->node_ptrs[idx].all_nrec + 1);
- } /* end for */
- } /* end else */
-
- /* Set the number of redistribution retries */
- /* This takes care of the case where a B-tree node needs to be
- * redistributed, but redistributing the node causes the index
- * for removal to move to another node, which also needs to be
- * redistributed. Now, we loop trying to redistribute and then
- * eventually force a merge */
- retries = 2;
-
- /* Preemptively merge/redistribute a node we will enter */
- while(internal->node_ptrs[idx].node_nrec == merge_nrec) {
- /* Attempt to redistribute records among children */
- /* (NOTE: These 2-node redistributions should actually get the
- * record to promote from the node with more records. - QAK)
- */
- /* (NOTE: This code is the same in both H5B2__remove_internal() and
- * H5B2__remove_internal_by_idx(), fix bugs in both places! - QAK)
- */
- if(idx == 0) { /* Left-most child */
- if(retries > 0 && (internal->node_ptrs[idx + 1].node_nrec > merge_nrec)) {
- if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
- } /* end if */
- else {
- if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
- } /* end else */
- } /* end if */
- else if(idx == internal->nrec) { /* Right-most child */
- if(retries > 0 && (internal->node_ptrs[idx - 1].node_nrec > merge_nrec)) {
- if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, (idx - 1)) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
- } /* end if */
- else {
- if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, (idx - 1)) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
- } /* end else */
- } /* end if */
- else { /* Middle child */
- if(retries > 0 && ((internal->node_ptrs[idx + 1].node_nrec > merge_nrec) ||
- (internal->node_ptrs[idx - 1].node_nrec > merge_nrec))) {
- if(H5B2__redistribute3(hdr, dxpl_id, depth, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
- } /* end if */
- else {
- if(H5B2__merge3(hdr, dxpl_id, depth, curr_node_ptr,
- parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
- } /* end else */
- } /* end else */
-
- /* Locate node pointer for child (after merge/redistribute) */
- if(swap_loc)
- idx = 0;
- else {
- /* Count from the orginal index value again */
- n = orig_n;
-
- /* Reset "found" flag - the record may have shifted during the
- * redistribute/merge
- */
- found = FALSE;
-
- /* Search for record with correct index */
- for(idx = 0; idx < internal->nrec; idx++) {
- /* Check which child node contains indexed record */
- if(internal->node_ptrs[idx].all_nrec >= n) {
- /* Check if record is in this node */
- if(internal->node_ptrs[idx].all_nrec == n) {
- /* Indicate the record was found and that the index
- * in child nodes is zero from now on
- */
- found = TRUE;
- n = 0;
-
- /* Increment to next record */
- idx++;
- } /* end if */
-
- /* Break out of loop early */
- break;
- } /* end if */
-
- /* Decrement index we are looking for to account for the node we
- * just advanced past.
- */
- n -= (internal->node_ptrs[idx].all_nrec + 1);
- } /* end for */
- } /* end else */
-
- /* Decrement the number of redistribution retries left */
- retries--;
- } /* end while */
-
- /* Handle deleting a record from an internal node */
- if(!swap_loc && found)
- swap_loc = H5B2_INT_NREC(internal, hdr, idx - 1);
-
- /* Swap record to delete with record from leaf, if we are the last internal node */
- if(swap_loc && depth == 1)
- if(H5B2__swap_leaf(hdr, dxpl_id, depth, internal, &internal_flags, idx, swap_loc) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTSWAP, FAIL, "can't swap records in B-tree")
-
- /* Set pointers for advancing to child node */
- new_cache_info_flags_ptr = &internal_flags;
- new_cache_info = &internal->cache_info;
- new_node_ptr = &internal->node_ptrs[idx];
-
- /* Indicate position of next node */
- if(H5B2_POS_MIDDLE != curr_pos) {
- if(idx == 0) {
- if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
- next_pos = H5B2_POS_LEFT;
- } /* end if */
- else if(idx == internal->nrec) {
- if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
- next_pos = H5B2_POS_RIGHT;
- } /* end if */
- } /* end if */
- } /* end else */
-
- /* Attempt to remove record from child node */
- if(depth > 1) {
- if(H5B2__remove_internal_by_idx(hdr, dxpl_id, depth_decreased, swap_loc, (uint16_t)(depth - 1),
- new_cache_info, new_cache_info_flags_ptr, new_node_ptr, next_pos, n, op, op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree internal node")
- } /* end if */
- else {
- if(H5B2__remove_leaf_by_idx(hdr, dxpl_id, new_node_ptr, next_pos, (unsigned)n, op, op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree leaf node")
- } /* end else */
-
- /* Update record count for node pointer to child node */
- if(!collapsed_root)
- new_node_ptr->all_nrec--;
-
- /* Mark node as dirty */
- internal_flags |= H5AC__DIRTIED_FLAG;
-
-#ifdef H5B2_DEBUG
- H5B2__assert_internal((!collapsed_root ? (curr_node_ptr->all_nrec - 1) : new_node_ptr->all_nrec), hdr, internal);
-#endif /* H5B2_DEBUG */
-
-done:
- /* Release the B-tree internal node */
- if(internal && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, internal_addr, internal, internal_flags) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__remove_internal_by_idx() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__neighbor_leaf
- *
- * Purpose: Locate a record relative to the specified information in a
- * B-tree leaf node and return that information by filling in
- * fields of the
- * caller-supplied UDATA pointer depending on the type of leaf node
- * requested. The UDATA can point to additional data passed
- * to the key comparison function.
- *
- * The 'OP' routine is called with the record found and the
- * OP_DATA pointer, to allow caller to return information about
- * the record.
- *
- * The RANGE indicates whether to search for records less than or
- * equal to, or greater than or equal to the information passed
- * in with UDATA.
- *
- * Return: Non-negative on success, negative on failure.
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Mar 9 2005
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__neighbor_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *curr_node_ptr,
- void *neighbor_loc, H5B2_compare_t comp, void *udata, H5B2_found_t op,
- void *op_data)
-{
- H5B2_leaf_t *leaf; /* Pointer to leaf node */
- unsigned idx; /* Location of record which matches key */
- int cmp = 0; /* Comparison value of records */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
- HDassert(op);
-
- /* Lock current B-tree node */
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, curr_node_ptr->addr, curr_node_ptr->node_nrec, H5AC__READ_ONLY_FLAG)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
-
- /* Locate node pointer for child */
- if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- if(cmp > 0)
- idx++;
- else
- if(cmp == 0 && comp == H5B2_COMPARE_GREATER)
- idx++;
-
- /* Set the neighbor location, if appropriate */
- if(comp == H5B2_COMPARE_LESS) {
- if(idx > 0)
- neighbor_loc = H5B2_LEAF_NREC(leaf, hdr, idx - 1);
- } /* end if */
- else {
- HDassert(comp == H5B2_COMPARE_GREATER);
-
- if(idx < leaf->nrec)
- neighbor_loc = H5B2_LEAF_NREC(leaf, hdr, idx);
- } /* end else */
-
- /* Make callback if neighbor record has been found */
- if(neighbor_loc) {
- /* Make callback for current record */
- if((op)(neighbor_loc, op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "'found' callback failed for B-tree neighbor operation")
- } /* end if */
- else
- HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "unable to find neighbor record in B-tree")
-
-done:
- /* Release the B-tree internal node */
- if(leaf && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr->addr, leaf, H5AC__NO_FLAGS_SET) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree leaf node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__neighbor_leaf() */
-
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__neighbor_internal
- *
- * Purpose: Locate a record relative to the specified information in a
- * B-tree internal node and return that information by filling in
- * fields of the
- * caller-supplied UDATA pointer depending on the type of leaf node
- * requested. The UDATA can point to additional data passed
- * to the key comparison function.
- *
- * The 'OP' routine is called with the record found and the
- * OP_DATA pointer, to allow caller to return information about
- * the record.
- *
- * The RANGE indicates whether to search for records less than or
- * equal to, or greater than or equal to the information passed
- * in with UDATA.
- *
- * Return: Non-negative on success, negative on failure.
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Mar 9 2005
- *
- *-------------------------------------------------------------------------
- */
-herr_t
-H5B2__neighbor_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- H5B2_node_ptr_t *curr_node_ptr, void *neighbor_loc, H5B2_compare_t comp,
- void *udata, H5B2_found_t op, void *op_data)
-{
- H5B2_internal_t *internal; /* Pointer to internal node */
- unsigned idx; /* Location of record which matches key */
- int cmp = 0; /* Comparison value of records */
- herr_t ret_value = SUCCEED; /* Return value */
-
- FUNC_ENTER_PACKAGE
-
- /* Check arguments. */
- HDassert(hdr);
- HDassert(depth > 0);
- HDassert(curr_node_ptr);
- HDassert(H5F_addr_defined(curr_node_ptr->addr));
- HDassert(op);
-
- /* Lock current B-tree node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node_ptr->addr, curr_node_ptr->node_nrec, depth, H5AC__READ_ONLY_FLAG)))
- HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
-
- /* Locate node pointer for child */
- if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native,
- udata, &idx, &cmp) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
- if(cmp > 0)
- idx++;
-
- /* Set the neighbor location, if appropriate */
- if(comp == H5B2_COMPARE_LESS) {
- if(idx > 0)
- neighbor_loc = H5B2_INT_NREC(internal, hdr, idx - 1);
- } /* end if */
- else {
- HDassert(comp == H5B2_COMPARE_GREATER);
-
- if(idx < internal->nrec)
- neighbor_loc = H5B2_INT_NREC(internal, hdr, idx);
- } /* end else */
-
- /* Attempt to find neighboring record */
- if(depth > 1) {
- if(H5B2__neighbor_internal(hdr, dxpl_id, (uint16_t)(depth - 1), &internal->node_ptrs[idx], neighbor_loc, comp, udata, op, op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "unable to find neighbor record in B-tree internal node")
- } /* end if */
- else {
- if(H5B2__neighbor_leaf(hdr, dxpl_id, &internal->node_ptrs[idx], neighbor_loc, comp, udata, op, op_data) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "unable to find neighbor record in B-tree leaf node")
- } /* end else */
-
-done:
- /* Release the B-tree internal node */
- if(internal && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, H5AC__NO_FLAGS_SET) < 0)
- HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
-
- FUNC_LEAVE_NOAPI(ret_value)
-} /* H5B2__neighbor_internal() */
-
-
-/*-------------------------------------------------------------------------
* Function: H5B2__delete_node
*
* Purpose: Iterate over all the nodes in a B-tree node deleting them
@@ -3392,7 +1624,8 @@ done:
*/
herr_t
H5B2__delete_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- const H5B2_node_ptr_t *curr_node, H5B2_remove_t op, void *op_data)
+ const H5B2_node_ptr_t *curr_node, void *parent, H5B2_remove_t op,
+ void *op_data)
{
const H5AC_class_t *curr_node_class = NULL; /* Pointer to current node's class info */
void *node = NULL; /* Pointers to current node */
@@ -3410,7 +1643,7 @@ H5B2__delete_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
unsigned u; /* Local index */
/* Lock the current B-tree node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node->addr, curr_node->node_nrec, depth, H5AC__NO_FLAGS_SET)))
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, (H5B2_node_ptr_t *)curr_node, depth, FALSE, H5AC__NO_FLAGS_SET))) /* Casting away const OK -QAK */
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
/* Set up information about current node */
@@ -3420,14 +1653,14 @@ H5B2__delete_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Descend into children */
for(u = 0; u < internal->nrec + (unsigned)1; u++)
- if(H5B2__delete_node(hdr, dxpl_id, (uint16_t)(depth - 1), &(internal->node_ptrs[u]), op, op_data) < 0)
+ if(H5B2__delete_node(hdr, dxpl_id, (uint16_t)(depth - 1), &(internal->node_ptrs[u]), internal, op, op_data) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTLIST, FAIL, "node descent failed")
} /* end if */
else {
H5B2_leaf_t *leaf; /* Pointer to leaf node */
/* Lock the current B-tree node */
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, curr_node->addr, curr_node->node_nrec, H5AC__NO_FLAGS_SET)))
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, (H5B2_node_ptr_t *)curr_node, FALSE, H5AC__NO_FLAGS_SET))) /* Casting away const OK -QAK */
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
/* Set up information about current node */
@@ -3450,7 +1683,7 @@ H5B2__delete_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
done:
/* Unlock & delete current node */
- if(node && H5AC_unprotect(hdr->f, dxpl_id, curr_node_class, curr_node->addr, node, H5AC__DELETED_FLAG | H5AC__FREE_FILE_SPACE_FLAG) < 0)
+ if(node && H5AC_unprotect(hdr->f, dxpl_id, curr_node_class, curr_node->addr, node, (unsigned)(H5AC__DELETED_FLAG | (hdr->swmr_write ? 0 : H5AC__FREE_FILE_SPACE_FLAG))) < 0)
HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node")
FUNC_LEAVE_NOAPI(ret_value)
@@ -3472,7 +1705,7 @@ done:
*/
herr_t
H5B2__node_size(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- const H5B2_node_ptr_t *curr_node, hsize_t *btree_size)
+ const H5B2_node_ptr_t *curr_node, void *parent, hsize_t *btree_size)
{
H5B2_internal_t *internal = NULL; /* Pointer to internal node */
herr_t ret_value = SUCCEED; /* Iterator return value */
@@ -3486,7 +1719,7 @@ H5B2__node_size(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
HDassert(depth > 0);
/* Lock the current B-tree node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node->addr, curr_node->node_nrec, depth, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, (H5B2_node_ptr_t *)curr_node, depth, FALSE, H5AC__READ_ONLY_FLAG))) /* Casting away const OK -QAK */
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
/* Recursively descend into child nodes, if we are above the "twig" level in the B-tree */
@@ -3495,7 +1728,7 @@ H5B2__node_size(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
/* Descend into children */
for(u = 0; u < internal->nrec + (unsigned)1; u++)
- if(H5B2__node_size(hdr, dxpl_id, (uint16_t)(depth - 1), &(internal->node_ptrs[u]), btree_size) < 0)
+ if(H5B2__node_size(hdr, dxpl_id, (uint16_t)(depth - 1), &(internal->node_ptrs[u]), internal, btree_size) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTLIST, FAIL, "node iteration failed")
} /* end if */
else /* depth is 1: count all the leaf nodes from this node */
@@ -3513,219 +1746,205 @@ done:
/*-------------------------------------------------------------------------
- * Function: H5B2__internal_free
+ * Function: H5B2__create_flush_depend
*
- * Purpose: Destroys a B-tree internal node in memory.
+ * Purpose: Create a flush dependency between two data structure components
*
- * Return: Non-negative on success/Negative on failure
+ * Return: SUCCEED/FAIL
*
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Feb 2 2005
+ * Programmer: Dana Robinson
+ * Fall 2012
*
*-------------------------------------------------------------------------
*/
herr_t
-H5B2__internal_free(H5B2_internal_t *internal)
+H5B2__create_flush_depend(H5AC_info_t *parent_entry, H5AC_info_t *child_entry)
{
herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_PACKAGE
-
- /*
- * Check arguments.
- */
- HDassert(internal);
-
- /* Release internal node's native key buffer */
- if(internal->int_native)
- internal->int_native = (uint8_t *)H5FL_FAC_FREE(internal->hdr->node_info[internal->depth].nat_rec_fac, internal->int_native);
-
- /* Release internal node's node pointer buffer */
- if(internal->node_ptrs)
- internal->node_ptrs = (H5B2_node_ptr_t *)H5FL_FAC_FREE(internal->hdr->node_info[internal->depth].node_ptr_fac, internal->node_ptrs);
-
- /* Decrement ref. count on B-tree header */
- if(H5B2__hdr_decr(internal->hdr) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTDEC, FAIL, "can't decrement ref. count on B-tree header")
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* Sanity check */
+ HDassert(parent_entry);
+ HDassert(child_entry);
- /* Free B-tree internal node info */
- internal = H5FL_FREE(H5B2_internal_t, internal);
+ /* Create a flush dependency between parent and child entry */
+ if(H5AC_create_flush_dependency(parent_entry, child_entry) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDEPEND, FAIL, "unable to create flush dependency")
done:
FUNC_LEAVE_NOAPI(ret_value)
-} /* end H5B2__internal_free() */
+} /* end H5B2__create_flush_depend() */
/*-------------------------------------------------------------------------
- * Function: H5B2__leaf_free
+ * Function: H5B2__update_flush_depend
*
- * Purpose: Destroys a B-tree leaf node in memory.
+ * Purpose: Update flush dependencies for children of a node
*
- * Return: Non-negative on success/Negative on failure
+ * Return: SUCCEED/FAIL
*
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Feb 2 2005
+ * Programmer: Quincey Koziol
+ * koziol@lbl.gov
+ * Dec 1 2016
*
*-------------------------------------------------------------------------
*/
herr_t
-H5B2__leaf_free(H5B2_leaf_t *leaf)
+H5B2__update_flush_depend(H5B2_hdr_t *hdr, hid_t dxpl_id, unsigned depth,
+ const H5B2_node_ptr_t *node_ptr, void *old_parent, void *new_parent)
{
+ const H5AC_class_t *child_class; /* Pointer to child node's class info */
+ void *child = NULL; /* Pointer to child node */
+ unsigned node_status = 0; /* Node's status in the metadata cache */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_PACKAGE
+
+ /* Sanity checks */
+ HDassert(hdr);
+ HDassert(depth > 0);
+ HDassert(node_ptr);
+ HDassert(old_parent);
+ HDassert(new_parent);
- /*
- * Check arguments.
- */
- HDassert(leaf);
+ /* Check the node's entry status in the metadata cache */
+ if(H5AC_get_entry_status(hdr->f, node_ptr->addr, &node_status) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTGET, FAIL, "unable to check status of B-tree node")
- /* Release leaf's native key buffer */
- if(leaf->leaf_native)
- leaf->leaf_native = (uint8_t *)H5FL_FAC_FREE(leaf->hdr->node_info[0].nat_rec_fac, leaf->leaf_native);
+ /* If the node is in the cache, check for retargeting its parent */
+ if(node_status & H5AC_ES__IN_CACHE) {
+ void **parent_ptr; /* Pointer to child node's parent */
+ hbool_t update_deps = FALSE; /* Whether to update flush dependencies */
- /* Decrement ref. count on B-tree header */
- if(H5B2__hdr_decr(leaf->hdr) < 0)
- HGOTO_ERROR(H5E_BTREE, H5E_CANTDEC, FAIL, "can't decrement ref. count on B-tree header")
+ /* Get child node pointer */
+ if(depth > 1) {
+ H5B2_internal_t *child_int;
- /* Free B-tree leaf node info */
- leaf = H5FL_FREE(H5B2_leaf_t, leaf);
+ /* Protect child */
+ if(NULL == (child_int = H5B2__protect_internal(hdr, dxpl_id, new_parent, (H5B2_node_ptr_t *)node_ptr, (uint16_t)(depth - 1), FALSE, H5AC__NO_FLAGS_SET))) /* Casting away const OK -QAK */
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+ child_class = H5AC_BT2_INT;
+ child = child_int;
-done:
- FUNC_LEAVE_NOAPI(ret_value)
-} /* end H5B2__leaf_free() */
+ if(child_int->parent == old_parent) {
+ parent_ptr = &child_int->parent;
+ update_deps = TRUE;
+ } /* end if */
+ else
+ HDassert(child_int->parent == new_parent);
+ } /* end if */
+ else {
+ H5B2_leaf_t *child_leaf;
-#ifdef H5B2_DEBUG
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__assert_leaf
- *
- * Purpose: Verify than a leaf node is mostly sane
- *
- * Return: Non-negative on success, negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Feb 19 2005
- *
- *-------------------------------------------------------------------------
- */
-H5_ATTR_PURE static herr_t
-H5B2__assert_leaf(const H5B2_hdr_t *hdr, const H5B2_leaf_t *leaf)
-{
- /* General sanity checking on node */
- HDassert(leaf->nrec <= hdr->node_info->split_nrec);
+ /* Protect child */
+ if(NULL == (child_leaf = H5B2__protect_leaf(hdr, dxpl_id, new_parent, (H5B2_node_ptr_t *)node_ptr, FALSE, H5AC__NO_FLAGS_SET))) /* Casting away const OK -QAK */
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ child_class = H5AC_BT2_LEAF;
+ child = child_leaf;
- return(0);
-} /* end H5B2__assert_leaf() */
+ if(child_leaf->parent == old_parent) {
+ parent_ptr = &child_leaf->parent;
+ update_deps = TRUE;
+ } /* end if */
+ else
+ HDassert(child_leaf->parent == new_parent);
+ } /* end else */
-
-/*-------------------------------------------------------------------------
- * Function: H5B2__assert_leaf2
- *
- * Purpose: Verify than a leaf node is mostly sane
- *
- * Return: Non-negative on success, negative on failure
- *
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Feb 19 2005
- *
- *-------------------------------------------------------------------------
- */
-H5_ATTR_PURE static herr_t
-H5B2__assert_leaf2(const H5B2_hdr_t *hdr, const H5B2_leaf_t *leaf, const H5B2_leaf_t H5_ATTR_UNUSED *leaf2)
-{
- /* General sanity checking on node */
- HDassert(leaf->nrec <= hdr->node_info->split_nrec);
+ /* Update flush dependencies if necessary */
+ if(update_deps) {
+ /* Sanity check */
+ HDassert(parent_ptr);
+
+ /* Switch the flush dependency for the node */
+ if(H5B2__destroy_flush_depend((H5AC_info_t *)old_parent, (H5AC_info_t *)child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency")
+ *parent_ptr = new_parent;
+ if(H5B2__create_flush_depend((H5AC_info_t *)new_parent, (H5AC_info_t *)child) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDEPEND, FAIL, "unable to create flush dependency")
+ } /* end if */
+ } /* end if */
- return(0);
-} /* end H5B2__assert_leaf2() */
+done:
+ /* Unprotect the child */
+ if(child)
+ if(H5AC_unprotect(hdr->f, dxpl_id, child_class, node_ptr->addr, child, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__update_flush_depend() */
/*-------------------------------------------------------------------------
- * Function: H5B2__assert_internal
+ * Function: H5B2__update_child_flush_depends
*
- * Purpose: Verify than an internal node is mostly sane
+ * Purpose: Update flush dependencies for children of a node
*
- * Return: Non-negative on success, negative on failure
+ * Return: SUCCEED/FAIL
*
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Feb 19 2005
+ * Programmer: Quincey Koziol
+ * koziol@lbl.gov
+ * Dec 1 2016
*
*-------------------------------------------------------------------------
*/
-H5_ATTR_PURE static herr_t
-H5B2__assert_internal(hsize_t parent_all_nrec, const H5B2_hdr_t *hdr, const H5B2_internal_t *internal)
+static herr_t
+H5B2__update_child_flush_depends(H5B2_hdr_t *hdr, hid_t dxpl_id, unsigned depth,
+ const H5B2_node_ptr_t *node_ptrs, unsigned start_idx, unsigned end_idx,
+ void *old_parent, void *new_parent)
{
- hsize_t tot_all_nrec; /* Total number of records at or below this node */
- uint16_t u, v; /* Local index variables */
-
- /* General sanity checking on node */
- HDassert(internal->nrec <= hdr->node_info->split_nrec);
-
- /* Sanity checking on node pointers */
- tot_all_nrec = internal->nrec;
- for(u = 0; u < internal->nrec + 1; u++) {
- tot_all_nrec += internal->node_ptrs[u].all_nrec;
-
- HDassert(H5F_addr_defined(internal->node_ptrs[u].addr));
- HDassert(internal->node_ptrs[u].addr > 0);
- for(v = 0; v < u; v++)
- HDassert(internal->node_ptrs[u].addr != internal->node_ptrs[v].addr);
- } /* end for */
+ unsigned u; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
- /* Sanity check all_nrec total in parent */
- if(parent_all_nrec > 0)
- HDassert(tot_all_nrec == parent_all_nrec);
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(hdr);
+ HDassert(depth > 1);
+ HDassert(node_ptrs);
+ HDassert(start_idx <= end_idx);
+ HDassert(old_parent);
+ HDassert(new_parent);
+
+ /* Loop over children */
+ for(u = start_idx; u < end_idx; u++)
+ /* Update parent for children */
+ if(H5B2__update_flush_depend(hdr, dxpl_id, depth - 1, &node_ptrs[u], old_parent, new_parent) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child node to new parent")
- return(0);
-} /* end H5B2__assert_internal() */
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__update_child_flush_depends() */
/*-------------------------------------------------------------------------
- * Function: H5B2__assert_internal2
+ * Function: H5B2__destroy_flush_depend
*
- * Purpose: Verify than internal nodes are mostly sane
+ * Purpose: Destroy a flush dependency between two data structure components
*
- * Return: Non-negative on success, negative on failure
+ * Return: SUCCEED/FAIL
*
- * Programmer: Quincey Koziol
- * koziol@ncsa.uiuc.edu
- * Feb 19 2005
+ * Programmer: Dana Robinson
+ * Fall 2012
*
*-------------------------------------------------------------------------
*/
-H5_ATTR_PURE static herr_t
-H5B2__assert_internal2(hsize_t parent_all_nrec, const H5B2_hdr_t *hdr, const H5B2_internal_t *internal, const H5B2_internal_t *internal2)
+herr_t
+H5B2__destroy_flush_depend(H5AC_info_t *parent_entry, H5AC_info_t *child_entry)
{
- hsize_t tot_all_nrec; /* Total number of records at or below this node */
- uint16_t u, v; /* Local index variables */
-
- /* General sanity checking on node */
- HDassert(internal->nrec <= hdr->node_info->split_nrec);
-
- /* Sanity checking on node pointers */
- tot_all_nrec =internal->nrec;
- for(u =0; u < internal->nrec + 1; u++) {
- tot_all_nrec += internal->node_ptrs[u].all_nrec;
-
- HDassert(H5F_addr_defined(internal->node_ptrs[u].addr));
- HDassert(internal->node_ptrs[u].addr > 0);
- for(v = 0; v < u; v++)
- HDassert(internal->node_ptrs[u].addr != internal->node_ptrs[v].addr);
- for(v = 0; v < internal2->nrec + 1; v++)
- HDassert(internal->node_ptrs[u].addr != internal2->node_ptrs[v].addr);
- } /* end for */
+ herr_t ret_value = SUCCEED; /* Return value */
- /* Sanity check all_nrec total in parent */
- if(parent_all_nrec > 0)
- HDassert(tot_all_nrec == parent_all_nrec);
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* Sanity check */
+ HDassert(parent_entry);
+ HDassert(child_entry);
- return(0);
-} /* end H5B2__assert_internal2() */
-#endif /* H5B2_DEBUG */
+ /* Destroy a flush dependency between parent and child entry */
+ if(H5AC_destroy_flush_dependency(parent_entry, child_entry) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__destroy_flush_depend() */
diff --git a/src/H5B2internal.c b/src/H5B2internal.c
new file mode 100644
index 0000000..e74ae59
--- /dev/null
+++ b/src/H5B2internal.c
@@ -0,0 +1,1443 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5B2internal.c
+ * Dec 01 2016
+ * Quincey Koziol <koziol@lbl.gov>
+ *
+ * Purpose: Routines for managing v2 B-tree internal ndoes.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/****************/
+/* Module Setup */
+/****************/
+
+#include "H5B2module.h" /* This source code file is part of the H5B2 module */
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5B2pkg.h" /* v2 B-trees */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5MFprivate.h" /* File memory management */
+
+
+/****************/
+/* Local Macros */
+/****************/
+
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+
+/********************/
+/* Package Typedefs */
+/********************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+static herr_t H5B2__shadow_internal(H5B2_internal_t *internal, hid_t dxpl_id,
+ H5B2_node_ptr_t *curr_node_ptr);
+
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+/* Declare a free list to manage the H5B2_internal_t struct */
+H5FL_DEFINE(H5B2_internal_t);
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__create_internal
+ *
+ * Purpose: Creates empty internal node of a B-tree and update node pointer
+ * to point to it.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Feb 3 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__create_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, void *parent,
+ H5B2_node_ptr_t *node_ptr, uint16_t depth)
+{
+ H5B2_internal_t *internal = NULL; /* Pointer to new internal node created */
+ hbool_t inserted = FALSE; /* Whether the internal node was inserted into cache */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(node_ptr);
+ HDassert(depth > 0);
+
+ /* Allocate memory for internal node information */
+ if(NULL == (internal = H5FL_CALLOC(H5B2_internal_t)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree internal info")
+
+ /* Increment ref. count on B-tree header */
+ if(H5B2__hdr_incr(hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTINC, FAIL, "can't increment ref. count on B-tree header")
+
+ /* Share B-tree header information */
+ internal->hdr = hdr;
+
+ /* Allocate space for the native keys in memory */
+ if(NULL == (internal->int_native = (uint8_t *)H5FL_FAC_MALLOC(hdr->node_info[depth].nat_rec_fac)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree internal native keys")
+ HDmemset(internal->int_native, 0, hdr->cls->nrec_size * hdr->node_info[depth].max_nrec);
+
+ /* Allocate space for the node pointers in memory */
+ if(NULL == (internal->node_ptrs = (H5B2_node_ptr_t *)H5FL_FAC_MALLOC(hdr->node_info[depth].node_ptr_fac)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree internal node pointers")
+ HDmemset(internal->node_ptrs, 0, sizeof(H5B2_node_ptr_t) * (hdr->node_info[depth].max_nrec + 1));
+
+ /* Set depth of the node */
+ internal->depth = depth;
+
+ /* Set parent */
+ internal->parent = parent;
+
+ /* Set shadowed epoch to header's epoch */
+ internal->shadow_epoch = hdr->shadow_epoch;
+
+ /* Allocate space on disk for the internal node */
+ if(HADDR_UNDEF == (node_ptr->addr = H5MF_alloc(hdr->f, H5FD_MEM_BTREE, dxpl_id, (hsize_t)hdr->node_size)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for B-tree internal node")
+
+ /* Cache the new B-tree node */
+ if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_BT2_INT, node_ptr->addr, internal, H5AC__NO_FLAGS_SET) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "can't add B-tree internal node to cache")
+ inserted = TRUE;
+
+ /* Add internal node as child of 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, internal) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSET, FAIL, "unable to add v2 B-tree node as child of proxy")
+ internal->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+done:
+ if(ret_value < 0) {
+ if(internal) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(internal) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTREMOVE, FAIL, "unable to remove v2 B-tree internal node from cache")
+
+ /* Release internal node's disk space */
+ if(H5F_addr_defined(node_ptr->addr) && H5MF_xfree(hdr->f, H5FD_MEM_BTREE, dxpl_id, node_ptr->addr, (hsize_t)hdr->node_size) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, FAIL, "unable to release file space for v2 B-tree internal node")
+
+ /* Destroy internal node */
+ if(H5B2__internal_free(internal) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, FAIL, "unable to release v2 B-tree internal node")
+ } /* end if */
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__create_internal() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__protect_internal
+ *
+ * Purpose: "Protect" an internal node in the metadata cache
+ *
+ * Return: Pointer to internal node on success/NULL on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@hdfgroup.org
+ * Aug 25 2006
+ *
+ *-------------------------------------------------------------------------
+ */
+H5B2_internal_t *
+H5B2__protect_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, void *parent,
+ H5B2_node_ptr_t *node_ptr, uint16_t depth, hbool_t shadow, unsigned flags)
+{
+ H5B2_internal_cache_ud_t udata; /* User data to pass through to cache 'deserialize' callback */
+ H5B2_internal_t *internal = NULL; /* v2 B-tree internal node */
+ H5B2_internal_t *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(node_ptr);
+ HDassert(H5F_addr_defined(node_ptr->addr));
+ HDassert(depth > 0);
+
+ /* only H5AC__READ_ONLY_FLAG may appear in flags */
+ HDassert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0);
+
+ /* Set up user data for callback */
+ udata.f = hdr->f;
+ udata.hdr = hdr;
+ udata.parent = parent;
+ udata.nrec = node_ptr->node_nrec;
+ udata.depth = depth;
+
+ /* Protect the internal node */
+ if(NULL == (internal = (H5B2_internal_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_BT2_INT, node_ptr->addr, &udata, flags)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, NULL, "unable to protect B-tree internal node")
+
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->top_proxy && NULL == internal->top_proxy) {
+ /* Add internal node as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, internal) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSET, NULL, "unable to add v2 B-tree internal node as child of proxy")
+ internal->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+ /* Shadow the node, if requested */
+ if(shadow)
+ if(H5B2__shadow_internal(internal, dxpl_id, node_ptr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOPY, NULL, "unable to shadow internal node")
+
+ /* Set return value */
+ ret_value = internal;
+
+done:
+ /* Clean up on error */
+ if(!ret_value) {
+ /* Release the internal node, if it was protected */
+ if(internal) {
+ /* Remove from v2 B-tree's proxy, if added */
+ if(internal->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(internal->top_proxy, internal) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, NULL, "unable to destroy flush dependency between internal node and v2 B-tree 'top' proxy")
+ internal->top_proxy = NULL;
+ } /* end if */
+
+ /* Unprotect internal node */
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, node_ptr->addr, internal, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, NULL, "unable to unprotect v2 B-tree internal node, address = %llu", (unsigned long long)node_ptr->addr)
+ } /* end if */
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__protect_internal() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__neighbor_internal
+ *
+ * Purpose: Locate a record relative to the specified information in a
+ * B-tree internal node and return that information by filling in
+ * fields of the
+ * caller-supplied UDATA pointer depending on the type of leaf node
+ * requested. The UDATA can point to additional data passed
+ * to the key comparison function.
+ *
+ * The 'OP' routine is called with the record found and the
+ * OP_DATA pointer, to allow caller to return information about
+ * the record.
+ *
+ * The RANGE indicates whether to search for records less than or
+ * equal to, or greater than or equal to the information passed
+ * in with UDATA.
+ *
+ * Return: Non-negative on success, negative on failure.
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Mar 9 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__neighbor_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ H5B2_node_ptr_t *curr_node_ptr, void *neighbor_loc, H5B2_compare_t comp,
+ void *parent, void *udata, H5B2_found_t op, void *op_data)
+{
+ H5B2_internal_t *internal; /* Pointer to internal node */
+ unsigned idx = 0; /* Location of record which matches key */
+ int cmp = 0; /* Comparison value of records */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(depth > 0);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+ HDassert(op);
+
+ /* Lock current B-tree node */
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, curr_node_ptr, depth, FALSE, H5AC__READ_ONLY_FLAG)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+
+ /* Locate node pointer for child */
+ if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+ if(cmp > 0)
+ idx++;
+
+ /* Set the neighbor location, if appropriate */
+ if(comp == H5B2_COMPARE_LESS) {
+ if(idx > 0)
+ neighbor_loc = H5B2_INT_NREC(internal, hdr, idx - 1);
+ } /* end if */
+ else {
+ HDassert(comp == H5B2_COMPARE_GREATER);
+
+ if(idx < internal->nrec)
+ neighbor_loc = H5B2_INT_NREC(internal, hdr, idx);
+ } /* end else */
+
+ /* Attempt to find neighboring record */
+ if(depth > 1) {
+ if(H5B2__neighbor_internal(hdr, dxpl_id, (uint16_t)(depth - 1), &internal->node_ptrs[idx], neighbor_loc, comp, internal, udata, op, op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "unable to find neighbor record in B-tree internal node")
+ } /* end if */
+ else {
+ if(H5B2__neighbor_leaf(hdr, dxpl_id, &internal->node_ptrs[idx], neighbor_loc, comp, internal, udata, op, op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "unable to find neighbor record in B-tree leaf node")
+ } /* end else */
+
+done:
+ /* Release the B-tree internal node */
+ if(internal && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__neighbor_internal() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__insert_internal
+ *
+ * Purpose: Adds a new record to a B-tree node.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Mar 2 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__insert_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ unsigned *parent_cache_info_flags_ptr, H5B2_node_ptr_t *curr_node_ptr,
+ H5B2_nodepos_t curr_pos, void *parent, void *udata)
+{
+ H5B2_internal_t *internal = NULL; /* Pointer to internal node */
+ unsigned internal_flags = H5AC__NO_FLAGS_SET;
+ unsigned idx = 0; /* Location of record which matches key */
+ H5B2_nodepos_t next_pos = H5B2_POS_MIDDLE; /* Position of node */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(depth > 0);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+
+ /* Lock current B-tree node */
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, curr_node_ptr, depth, FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+
+ /* Sanity check number of records */
+ HDassert(internal->nrec == curr_node_ptr->node_nrec);
+
+ /* Split or redistribute child node pointers, if necessary */
+ {
+ int cmp; /* Comparison value of records */
+ unsigned retries; /* Number of times to attempt redistribution */
+ size_t split_nrec; /* Number of records to split node at */
+
+ /* Locate node pointer for child */
+ if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native,
+ udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+ if(cmp == 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_EXISTS, FAIL, "record is already in B-tree")
+ if(cmp > 0)
+ idx++;
+
+ /* Set the number of redistribution retries */
+ /* This takes care of the case where a B-tree node needs to be
+ * redistributed, but redistributing the node causes the index
+ * for insertion to move to another node, which also needs to be
+ * redistributed. Now, we loop trying to redistribute and then
+ * eventually force a split */
+ retries = 2;
+
+ /* Determine the correct number of records to split child node at */
+ split_nrec = hdr->node_info[depth - 1].split_nrec;
+
+ /* Preemptively split/redistribute a node we will enter */
+ while(internal->node_ptrs[idx].node_nrec == split_nrec) {
+ /* Attempt to redistribute records among children */
+ if(idx == 0) { /* Left-most child */
+ if(retries > 0 && (internal->node_ptrs[idx + 1].node_nrec < split_nrec)) {
+ if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
+ } /* end if */
+ else {
+ if(H5B2__split1(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to split child node")
+ } /* end else */
+ } /* end if */
+ else if(idx == internal->nrec) { /* Right-most child */
+ if(retries > 0 && (internal->node_ptrs[idx - 1].node_nrec < split_nrec)) {
+ if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, (idx - 1)) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
+ } /* end if */
+ else {
+ if(H5B2__split1(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to split child node")
+ } /* end else */
+ } /* end if */
+ else { /* Middle child */
+ if(retries > 0 && ((internal->node_ptrs[idx + 1].node_nrec < split_nrec) ||
+ (internal->node_ptrs[idx - 1].node_nrec < split_nrec))) {
+ if(H5B2__redistribute3(hdr, dxpl_id, depth, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
+ } /* end if */
+ else {
+ if(H5B2__split1(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to split child node")
+ } /* end else */
+ } /* end else */
+
+ /* Locate node pointer for child (after split/redistribute) */
+ /* Actually, this can be easily updated (for 2-node redistrib.) and shouldn't require re-searching */
+ if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native,
+ udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+ if(cmp == 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_EXISTS, FAIL, "record is already in B-tree")
+ if(cmp > 0)
+ idx++;
+
+ /* Decrement the number of redistribution retries left */
+ retries--;
+ } /* end while */
+ } /* end block */
+
+ /* Check if this node is left/right-most */
+ if(H5B2_POS_MIDDLE != curr_pos) {
+ if(idx == 0) {
+ if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
+ next_pos = H5B2_POS_LEFT;
+ } /* end if */
+ else if(idx == internal->nrec) {
+ if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
+ next_pos = H5B2_POS_RIGHT;
+ } /* end else */
+ } /* end if */
+
+ /* Attempt to insert node */
+ if(depth > 1) {
+ if(H5B2__insert_internal(hdr, dxpl_id, (uint16_t)(depth - 1), &internal_flags, &internal->node_ptrs[idx], next_pos, internal, udata) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree internal node")
+ } /* end if */
+ else {
+ if(H5B2__insert_leaf(hdr, dxpl_id, &internal->node_ptrs[idx], next_pos, internal, udata) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into B-tree leaf node")
+ } /* end else */
+
+ /* Update record count for node pointer to current node */
+ curr_node_ptr->all_nrec++;
+
+ /* Mark node as dirty */
+ internal_flags |= H5AC__DIRTIED_FLAG;
+
+done:
+ /* Release the B-tree internal node */
+ if(internal) {
+ /* Shadow the node if doing SWMR writes */
+ if(hdr->swmr_write && (internal_flags & H5AC__DIRTIED_FLAG))
+ if(H5B2__shadow_internal(internal, dxpl_id, curr_node_ptr) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow internal B-tree node")
+
+ /* Unprotect node */
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__insert_internal() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__update_internal
+ *
+ * Purpose: Insert or modify a record in a B-tree internal node.
+ * If the record exists already, it is modified as if H5B2_modify
+ * was called). If it doesn't exist, it is inserted as if
+ * H5B2_insert was called.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@hdfgroup.org
+ * Dec 24 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__update_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ unsigned *parent_cache_info_flags_ptr, H5B2_node_ptr_t *curr_node_ptr,
+ H5B2_update_status_t *status, H5B2_nodepos_t curr_pos, void *parent,
+ void *udata, H5B2_modify_t op, void *op_data)
+{
+ H5B2_internal_t *internal = NULL; /* Pointer to internal node */
+ unsigned internal_flags = H5AC__NO_FLAGS_SET;
+ int cmp; /* Comparison value of records */
+ unsigned idx = 0; /* Location of record which matches key */
+ H5B2_nodepos_t next_pos = H5B2_POS_MIDDLE; /* Position of node */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(depth > 0);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+
+ /* Lock current B-tree node */
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, curr_node_ptr, depth, FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+
+ /* Sanity check number of records */
+ HDassert(internal->nrec == curr_node_ptr->node_nrec);
+
+ /* Locate node pointer for child */
+ if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+
+ /* Check for modifying existing record */
+ if(0 == cmp) {
+ hbool_t changed = FALSE; /* Whether the 'modify' callback changed the record */
+
+ /* Make callback for current record */
+ if((op)(H5B2_INT_NREC(internal, hdr, idx), op_data, &changed) < 0) {
+ /* Make certain that the callback didn't modify the value if it failed */
+ HDassert(changed == FALSE);
+
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTMODIFY, FAIL, "'modify' callback failed for B-tree update operation")
+ } /* end if */
+
+ /* Mark the node as dirty if it changed */
+ internal_flags |= (changed ? H5AC__DIRTIED_FLAG : 0);
+
+ /* Indicate that the record was modified */
+ *status = H5B2_UPDATE_MODIFY_DONE;
+ } /* end if */
+ else {
+ /* Adjust index to leave room for node to insert */
+ if(cmp > 0)
+ idx++;
+
+ /* Check if this node is left/right-most */
+ if(H5B2_POS_MIDDLE != curr_pos) {
+ if(idx == 0) {
+ if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
+ next_pos = H5B2_POS_LEFT;
+ } /* end if */
+ else if(idx == internal->nrec) {
+ if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
+ next_pos = H5B2_POS_RIGHT;
+ } /* end else */
+ } /* end if */
+
+ /* Attempt to update record in child */
+ if(depth > 1) {
+ if(H5B2__update_internal(hdr, dxpl_id, (uint16_t)(depth - 1), &internal_flags, &internal->node_ptrs[idx], status, next_pos, internal, udata, op, op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update record in internal B-tree node")
+ } /* end if */
+ else {
+ if(H5B2__update_leaf(hdr, dxpl_id, &internal->node_ptrs[idx], status, next_pos, internal, udata, op, op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update record in leaf B-tree node")
+ } /* end else */
+
+ /* Take actions based on child's status report */
+ switch(*status) {
+ case H5B2_UPDATE_MODIFY_DONE:
+ /* No action */
+ break;
+
+ case H5B2_UPDATE_SHADOW_DONE:
+ /* If child node was shadowed (if SWMR is enabled), mark this node dirty */
+ if(hdr->swmr_write)
+ internal_flags |= H5AC__DIRTIED_FLAG;
+
+ /* No further modifications up the tree are necessary though, so downgrade to merely "modified" */
+ *status = H5B2_UPDATE_MODIFY_DONE;
+ break;
+
+ case H5B2_UPDATE_INSERT_DONE:
+ /* Mark node as dirty */
+ internal_flags |= H5AC__DIRTIED_FLAG;
+
+ /* Update total record count for node pointer to current node */
+ curr_node_ptr->all_nrec++;
+ break;
+
+ case H5B2_UPDATE_INSERT_CHILD_FULL:
+ /* Split/redistribute this node */
+ if(internal->nrec == hdr->node_info[depth].split_nrec) {
+ hbool_t could_split = FALSE; /* Whether the child node could split */
+
+ if(idx == 0) { /* Left-most child */
+ /* Check for left-most child and its neighbor being close to full */
+ if((internal->node_ptrs[idx].node_nrec + internal->node_ptrs[idx + 1].node_nrec)
+ >= ((hdr->node_info[depth - 1].split_nrec * 2) - 1))
+ could_split = TRUE;
+ } /* end if */
+ else if(idx == internal->nrec) { /* Right-most child */
+ /* Check for right-most child and its neighbor being close to full */
+ if((internal->node_ptrs[idx - 1].node_nrec + internal->node_ptrs[idx].node_nrec)
+ >= ((hdr->node_info[depth - 1].split_nrec * 2) - 1))
+ could_split = TRUE;
+ } /* end else-if */
+ else { /* Middle child */
+ /* Check for middle child and its left neighbor being close to full */
+ if((internal->node_ptrs[idx - 1].node_nrec + internal->node_ptrs[idx].node_nrec)
+ >= ((hdr->node_info[depth - 1].split_nrec * 2) - 1))
+ could_split = TRUE;
+ /* Check for middle child and its right neighbor being close to full */
+ else if((internal->node_ptrs[idx].node_nrec + internal->node_ptrs[idx + 1].node_nrec)
+ >= ((hdr->node_info[depth - 1].split_nrec * 2) - 1))
+ could_split = TRUE;
+ } /* end if */
+
+ /* If this node is full and the child node insertion could
+ * cause a split, punt back up to caller, leaving the
+ * "insert child full" status.
+ */
+ if(could_split) {
+ /* Release the internal B-tree node */
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
+ internal = NULL;
+
+ /* Punt back to caller */
+ HGOTO_DONE(SUCCEED);
+ } /* end if */
+ } /* end if */
+
+ /* Release the internal B-tree node */
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
+ internal = NULL;
+
+ /* Indicate that the record was inserted */
+ *status = H5B2_UPDATE_INSERT_DONE;
+
+ /* Dodge sideways into inserting a record into this node */
+ if(H5B2__insert_internal(hdr, dxpl_id, depth, parent_cache_info_flags_ptr, curr_node_ptr, curr_pos, parent, udata) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into internal B-tree node")
+ break;
+
+ case H5B2_UPDATE_UNKNOWN:
+ default:
+ HDassert(0 && "Invalid update status");
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "invalid update status")
+ } /* end switch */
+ } /* end else */
+
+done:
+ /* Release the internal B-tree node */
+ if(internal) {
+ /* Check if we should shadow this node */
+ if(hdr->swmr_write && (internal_flags & H5AC__DIRTIED_FLAG)) {
+ /* Attempt to shadow the node if doing SWMR writes */
+ if(H5B2__shadow_internal(internal, dxpl_id, curr_node_ptr) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow internal B-tree node")
+
+ /* Change the state to "shadowed" if only modified currently */
+ /* (Triggers parent to be marked dirty) */
+ if(*status == H5B2_UPDATE_MODIFY_DONE)
+ *status = H5B2_UPDATE_SHADOW_DONE;
+ } /* end if */
+
+ /* Unprotect node */
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr->addr, internal, internal_flags) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__update_internal() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__shadow_internal
+ *
+ * Purpose: "Shadow" an internal node - copy it to a new location,
+ * leaving the data in the old location intact (for now).
+ * This is done when writing in SWMR mode to ensure that
+ * readers do not see nodes that are out of date with
+ * respect to each other and thereby inconsistent.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Neil Fortner
+ * Apr 27 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5B2__shadow_internal(H5B2_internal_t *internal, hid_t dxpl_id,
+ H5B2_node_ptr_t *curr_node_ptr)
+{
+ H5B2_hdr_t *hdr; /* B-tree header */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /*
+ * Check arguments.
+ */
+ HDassert(internal);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+ hdr = internal->hdr;
+ HDassert(hdr);
+ HDassert(hdr->swmr_write);
+
+ /* We only need to shadow the node if it has not been shadowed since the
+ * last time the header was flushed, as otherwise it will be unreachable by
+ * the readers so there will be no need to shadow. To check if it has been
+ * shadowed, compare the epoch of this node and the header. If this node's
+ * epoch is <= to the header's, it hasn't been shadowed yet. */
+ if(internal->shadow_epoch <= hdr->shadow_epoch) {
+ haddr_t new_node_addr; /* Address to move node to */
+
+ /*
+ * We must clone the old node so readers with an out-of-date version of
+ * the parent can still see the correct number of children, via the
+ * shadowed node. Remove it from cache but do not mark it free on disk.
+ */
+ /* Allocate space for the cloned node */
+ if(HADDR_UNDEF == (new_node_addr = H5MF_alloc(hdr->f, H5FD_MEM_BTREE, dxpl_id, (hsize_t)hdr->node_size)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "unable to allocate file space to move B-tree node")
+
+ /* Move the location of the node on the disk */
+ if(H5AC_move_entry(hdr->f, H5AC_BT2_INT, curr_node_ptr->addr, new_node_addr, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTMOVE, FAIL, "unable to move B-tree node")
+ curr_node_ptr->addr = new_node_addr;
+
+ /* Should free the space in the file, but this is not supported by
+ * SWMR_WRITE code yet - QAK, 2016/12/01
+ */
+
+ /* Set shadow epoch for node ahead of header */
+ internal->shadow_epoch = hdr->shadow_epoch + 1;
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__shadow_internal() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__remove_internal
+ *
+ * Purpose: Removes a record from a B-tree node.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Mar 3 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__remove_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, hbool_t *depth_decreased,
+ void *swap_loc, void *swap_parent, uint16_t depth, H5AC_info_t *parent_cache_info,
+ unsigned *parent_cache_info_flags_ptr, H5B2_nodepos_t curr_pos,
+ H5B2_node_ptr_t *curr_node_ptr, void *udata, H5B2_remove_t op, void *op_data)
+{
+ H5AC_info_t *new_cache_info; /* Pointer to new cache info */
+ unsigned *new_cache_info_flags_ptr = NULL;
+ H5B2_node_ptr_t *new_node_ptr; /* Pointer to new node pointer */
+ H5B2_internal_t *internal; /* Pointer to internal node */
+ H5B2_nodepos_t next_pos = H5B2_POS_MIDDLE; /* Position of next node */
+ unsigned internal_flags = H5AC__NO_FLAGS_SET;
+ haddr_t internal_addr = HADDR_UNDEF; /* Address of internal node */
+ size_t merge_nrec; /* Number of records to merge node at */
+ hbool_t collapsed_root = FALSE; /* Whether the root was collapsed */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(depth > 0);
+ HDassert(parent_cache_info);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+
+ /* Lock current B-tree node */
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent_cache_info, curr_node_ptr, depth, FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+ internal_addr = curr_node_ptr->addr;
+
+ /* Determine the correct number of records to merge at */
+ merge_nrec = hdr->node_info[depth - 1].merge_nrec;
+
+ /* Check for needing to collapse the root node */
+ /* (The root node is the only internal node allowed to have 1 record) */
+ if(internal->nrec == 1 &&
+ ((internal->node_ptrs[0].node_nrec + internal->node_ptrs[1].node_nrec) <= ((merge_nrec * 2) + 1))) {
+
+ /* Merge children of root node */
+ if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, 0) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
+
+ /* Let the cache know that the object is deleted */
+ internal_flags |= H5AC__DELETED_FLAG;
+ if(!hdr->swmr_write)
+ internal_flags |= H5AC__FREE_FILE_SPACE_FLAG;
+
+ /* Reset information in header's root node pointer */
+ curr_node_ptr->addr = internal->node_ptrs[0].addr;
+ curr_node_ptr->node_nrec = internal->node_ptrs[0].node_nrec;
+
+ /* Update flush dependency for child, if using SWMR */
+ if(hdr->swmr_write)
+ if(H5B2__update_flush_depend(hdr, dxpl_id, depth, curr_node_ptr, internal, hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child node to new parent")
+
+ /* Indicate that the level of the B-tree decreased */
+ *depth_decreased = TRUE;
+
+ /* Set pointers for advancing to child node */
+ new_cache_info = parent_cache_info;
+ new_cache_info_flags_ptr = parent_cache_info_flags_ptr;
+ new_node_ptr = curr_node_ptr;
+
+ /* Set flag to indicate root was collapsed */
+ collapsed_root = TRUE;
+
+ /* Indicate position of next node */
+ next_pos = H5B2_POS_ROOT;
+ } /* end if */
+ /* Merge or redistribute child node pointers, if necessary */
+ else {
+ unsigned idx = 0; /* Location of record which matches key */
+ int cmp = 0; /* Comparison value of records */
+ unsigned retries; /* Number of times to attempt redistribution */
+
+ /* Shadow the node if doing SWMR writes */
+ if(hdr->swmr_write) {
+ if(H5B2__shadow_internal(internal, dxpl_id, curr_node_ptr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow internal node")
+ internal_addr = curr_node_ptr->addr;
+ } /* end if */
+
+ /* Locate node pointer for child */
+ if(swap_loc)
+ idx = 0;
+ else {
+ if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+ if(cmp >= 0)
+ idx++;
+ } /* end else */
+
+ /* Set the number of redistribution retries */
+ /* This takes care of the case where a B-tree node needs to be
+ * redistributed, but redistributing the node causes the index
+ * for removal to move to another node, which also needs to be
+ * redistributed. Now, we loop trying to redistribute and then
+ * eventually force a merge */
+ retries = 2;
+
+ /* Preemptively merge/redistribute a node we will enter */
+ while(internal->node_ptrs[idx].node_nrec == merge_nrec) {
+ /* Attempt to redistribute records among children */
+ /* (NOTE: These 2-node redistributions should actually get the
+ * record to promote from the node with more records. - QAK)
+ */
+ /* (NOTE: This code is the same in both H5B2__remove_internal() and
+ * H5B2__remove_internal_by_idx(), fix bugs in both places! - QAK)
+ */
+ if(idx == 0) { /* Left-most child */
+ if(retries > 0 && (internal->node_ptrs[idx + 1].node_nrec > merge_nrec)) {
+ if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
+ } /* end if */
+ else {
+ if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
+ } /* end else */
+ } /* end if */
+ else if(idx == internal->nrec) { /* Right-most child */
+ if(retries > 0 && (internal->node_ptrs[idx - 1].node_nrec > merge_nrec)) {
+ if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, (idx - 1)) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
+ } /* end if */
+ else {
+ if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, (idx - 1)) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
+ } /* end else */
+ } /* end if */
+ else { /* Middle child */
+ if(retries > 0 && ((internal->node_ptrs[idx + 1].node_nrec > merge_nrec) ||
+ (internal->node_ptrs[idx - 1].node_nrec > merge_nrec))) {
+ if(H5B2__redistribute3(hdr, dxpl_id, depth, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
+ } /* end if */
+ else {
+ if(H5B2__merge3(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
+ } /* end else */
+ } /* end else */
+
+ /* Locate node pointer for child (after merge/redistribute) */
+ if(swap_loc)
+ idx = 0;
+ else {
+/* Actually, this can be easily updated (for 2-node redistrib.) and shouldn't require re-searching */
+ if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native, udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+ if(cmp >= 0)
+ idx++;
+ } /* end else */
+
+ /* Decrement the number of redistribution retries left */
+ retries--;
+ } /* end while */
+
+ /* Handle deleting a record from an internal node */
+ if(!swap_loc && cmp == 0) {
+ swap_loc = H5B2_INT_NREC(internal, hdr, idx - 1);
+ swap_parent = internal;
+ } /* end if */
+
+ /* Swap record to delete with record from leaf, if we are the last internal node */
+ if(swap_loc && depth == 1)
+ if(H5B2__swap_leaf(hdr, dxpl_id, depth, internal, &internal_flags, idx, swap_loc) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSWAP, FAIL, "Can't swap records in B-tree")
+
+ /* Set pointers for advancing to child node */
+ new_cache_info_flags_ptr = &internal_flags;
+ new_cache_info = &internal->cache_info;
+ new_node_ptr = &internal->node_ptrs[idx];
+
+ /* Indicate position of next node */
+ if(H5B2_POS_MIDDLE != curr_pos) {
+ if(idx == 0) {
+ if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
+ next_pos = H5B2_POS_LEFT;
+ } /* end if */
+ else if(idx == internal->nrec) {
+ if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
+ next_pos = H5B2_POS_RIGHT;
+ } /* end if */
+ } /* end if */
+ } /* end else */
+
+ /* Attempt to remove record from child node */
+ if(depth > 1) {
+ if(H5B2__remove_internal(hdr, dxpl_id, depth_decreased, swap_loc, swap_parent, (uint16_t)(depth - 1),
+ new_cache_info, new_cache_info_flags_ptr, next_pos, new_node_ptr, udata, op, op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree internal node")
+ } /* end if */
+ else {
+ if(H5B2__remove_leaf(hdr, dxpl_id, new_node_ptr, next_pos, new_cache_info, udata, op, op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree leaf node")
+ } /* end else */
+
+ /* Update record count for node pointer to current node */
+ if(!collapsed_root)
+ new_node_ptr->all_nrec--;
+
+ /* Mark node as dirty */
+ if(!(hdr->swmr_write && collapsed_root))
+ internal_flags |= H5AC__DIRTIED_FLAG;
+
+#ifdef H5B2_DEBUG
+ H5B2__assert_internal((!collapsed_root ? (curr_node_ptr->all_nrec - 1) : new_node_ptr->all_nrec), hdr, internal);
+#endif /* H5B2_DEBUG */
+
+done:
+ /* Release the B-tree internal node */
+ if(internal && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, internal_addr, internal, internal_flags) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__remove_internal() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__remove_internal_by_idx
+ *
+ * Purpose: Removes a record from a B-tree node, according to the offset
+ * in the B-tree records
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@hdfgroup.org
+ * Nov 14 2006
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__remove_internal_by_idx(H5B2_hdr_t *hdr, hid_t dxpl_id,
+ hbool_t *depth_decreased, void *swap_loc, void *swap_parent, uint16_t depth,
+ H5AC_info_t *parent_cache_info, unsigned *parent_cache_info_flags_ptr,
+ H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, hsize_t n,
+ H5B2_remove_t op, void *op_data)
+{
+ H5AC_info_t *new_cache_info; /* Pointer to new cache info */
+ unsigned *new_cache_info_flags_ptr = NULL;
+ H5B2_node_ptr_t *new_node_ptr; /* Pointer to new node pointer */
+ H5B2_internal_t *internal; /* Pointer to internal node */
+ H5B2_nodepos_t next_pos = H5B2_POS_MIDDLE; /* Position of next node */
+ unsigned internal_flags = H5AC__NO_FLAGS_SET;
+ haddr_t internal_addr = HADDR_UNDEF; /* Address of internal node */
+ size_t merge_nrec; /* Number of records to merge node at */
+ hbool_t collapsed_root = FALSE; /* Whether the root was collapsed */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(depth > 0);
+ HDassert(parent_cache_info);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+
+ /* Lock current B-tree node */
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent_cache_info, curr_node_ptr, depth, FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+ internal_addr = curr_node_ptr->addr;
+ HDassert(internal->nrec == curr_node_ptr->node_nrec);
+ HDassert(depth == hdr->depth || internal->nrec > 1);
+
+ /* Determine the correct number of records to merge at */
+ merge_nrec = hdr->node_info[depth - 1].merge_nrec;
+
+ /* Check for needing to collapse the root node */
+ /* (The root node is the only internal node allowed to have 1 record) */
+ if(internal->nrec == 1 &&
+ ((internal->node_ptrs[0].node_nrec + internal->node_ptrs[1].node_nrec) <= ((merge_nrec * 2) + 1))) {
+ HDassert(depth == hdr->depth);
+
+ /* Merge children of root node */
+ if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, 0) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
+
+ /* Let the cache know that the object is deleted */
+ internal_flags |= H5AC__DELETED_FLAG;
+ if(!hdr->swmr_write)
+ internal_flags |= H5AC__FREE_FILE_SPACE_FLAG;
+
+ /* Reset information in header's root node pointer */
+ curr_node_ptr->addr = internal->node_ptrs[0].addr;
+ curr_node_ptr->node_nrec = internal->node_ptrs[0].node_nrec;
+
+ /* Update flush dependency for child, if using SWMR */
+ if(hdr->swmr_write)
+ if(H5B2__update_flush_depend(hdr, dxpl_id, depth, curr_node_ptr, internal, hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUPDATE, FAIL, "unable to update child node to new parent")
+
+ /* Indicate that the level of the B-tree decreased */
+ *depth_decreased = TRUE;
+
+ /* Set pointers for advancing to child node */
+ new_cache_info = parent_cache_info;
+ new_cache_info_flags_ptr = parent_cache_info_flags_ptr;
+ new_node_ptr = curr_node_ptr;
+
+ /* Set flag to indicate root was collapsed */
+ collapsed_root = TRUE;
+
+ /* Indicate position of next node */
+ next_pos = H5B2_POS_ROOT;
+ } /* end if */
+ /* Merge or redistribute child node pointers, if necessary */
+ else {
+ hsize_t orig_n = n; /* Original index looked for */
+ unsigned idx; /* Location of record which matches key */
+ hbool_t found = FALSE; /* Comparison value of records */
+ unsigned retries; /* Number of times to attempt redistribution */
+
+ /* Shadow the node if doing SWMR writes */
+ if(hdr->swmr_write) {
+ if(H5B2__shadow_internal(internal, dxpl_id, curr_node_ptr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow internal node")
+ internal_addr = curr_node_ptr->addr;
+ } /* end if */
+
+ /* Locate node pointer for child */
+ if(swap_loc)
+ idx = 0;
+ else {
+ /* Search for record with correct index */
+ for(idx = 0; idx < internal->nrec; idx++) {
+ /* Check which child node contains indexed record */
+ if(internal->node_ptrs[idx].all_nrec >= n) {
+ /* Check if record is in this node */
+ if(internal->node_ptrs[idx].all_nrec == n) {
+ /* Indicate the record was found and that the index
+ * in child nodes is zero from now on
+ */
+ found = TRUE;
+ n = 0;
+
+ /* Increment to next record */
+ idx++;
+ } /* end if */
+
+ /* Break out of loop early */
+ break;
+ } /* end if */
+
+ /* Decrement index we are looking for to account for the node we
+ * just advanced past.
+ */
+ n -= (internal->node_ptrs[idx].all_nrec + 1);
+ } /* end for */
+ } /* end else */
+
+ /* Set the number of redistribution retries */
+ /* This takes care of the case where a B-tree node needs to be
+ * redistributed, but redistributing the node causes the index
+ * for removal to move to another node, which also needs to be
+ * redistributed. Now, we loop trying to redistribute and then
+ * eventually force a merge */
+ retries = 2;
+
+ /* Preemptively merge/redistribute a node we will enter */
+ while(internal->node_ptrs[idx].node_nrec == merge_nrec) {
+ /* Attempt to redistribute records among children */
+ /* (NOTE: These 2-node redistributions should actually get the
+ * record to promote from the node with more records. - QAK)
+ */
+ /* (NOTE: This code is the same in both H5B2__remove_internal() and
+ * H5B2__remove_internal_by_idx(), fix bugs in both places! - QAK)
+ */
+ if(idx == 0) { /* Left-most child */
+ if(retries > 0 && (internal->node_ptrs[idx + 1].node_nrec > merge_nrec)) {
+ if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
+ } /* end if */
+ else {
+ if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
+ } /* end else */
+ } /* end if */
+ else if(idx == internal->nrec) { /* Right-most child */
+ if(retries > 0 && (internal->node_ptrs[idx - 1].node_nrec > merge_nrec)) {
+ if(H5B2__redistribute2(hdr, dxpl_id, depth, internal, (idx - 1)) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
+ } /* end if */
+ else {
+ if(H5B2__merge2(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, (idx - 1)) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
+ } /* end else */
+ } /* end if */
+ else { /* Middle child */
+ if(retries > 0 && ((internal->node_ptrs[idx + 1].node_nrec > merge_nrec) ||
+ (internal->node_ptrs[idx - 1].node_nrec > merge_nrec))) {
+ if(H5B2__redistribute3(hdr, dxpl_id, depth, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTREDISTRIBUTE, FAIL, "unable to redistribute child node records")
+ } /* end if */
+ else {
+ if(H5B2__merge3(hdr, dxpl_id, depth, curr_node_ptr,
+ parent_cache_info_flags_ptr, internal, &internal_flags, idx) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSPLIT, FAIL, "unable to merge child node")
+ } /* end else */
+ } /* end else */
+
+ /* Locate node pointer for child (after merge/redistribute) */
+ if(swap_loc)
+ idx = 0;
+ else {
+ /* Count from the orginal index value again */
+ n = orig_n;
+
+ /* Reset "found" flag - the record may have shifted during the
+ * redistribute/merge
+ */
+ found = FALSE;
+
+ /* Search for record with correct index */
+ for(idx = 0; idx < internal->nrec; idx++) {
+ /* Check which child node contains indexed record */
+ if(internal->node_ptrs[idx].all_nrec >= n) {
+ /* Check if record is in this node */
+ if(internal->node_ptrs[idx].all_nrec == n) {
+ /* Indicate the record was found and that the index
+ * in child nodes is zero from now on
+ */
+ found = TRUE;
+ n = 0;
+
+ /* Increment to next record */
+ idx++;
+ } /* end if */
+
+ /* Break out of loop early */
+ break;
+ } /* end if */
+
+ /* Decrement index we are looking for to account for the node we
+ * just advanced past.
+ */
+ n -= (internal->node_ptrs[idx].all_nrec + 1);
+ } /* end for */
+ } /* end else */
+
+ /* Decrement the number of redistribution retries left */
+ retries--;
+ } /* end while */
+
+ /* Handle deleting a record from an internal node */
+ if(!swap_loc && found) {
+ swap_loc = H5B2_INT_NREC(internal, hdr, idx - 1);
+ swap_parent = internal;
+ } /* end if */
+
+ /* Swap record to delete with record from leaf, if we are the last internal node */
+ if(swap_loc && depth == 1)
+ if(H5B2__swap_leaf(hdr, dxpl_id, depth, internal, &internal_flags, idx, swap_loc) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSWAP, FAIL, "can't swap records in B-tree")
+
+ /* Set pointers for advancing to child node */
+ new_cache_info_flags_ptr = &internal_flags;
+ new_cache_info = &internal->cache_info;
+ new_node_ptr = &internal->node_ptrs[idx];
+
+ /* Indicate position of next node */
+ if(H5B2_POS_MIDDLE != curr_pos) {
+ if(idx == 0) {
+ if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos)
+ next_pos = H5B2_POS_LEFT;
+ } /* end if */
+ else if(idx == internal->nrec) {
+ if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos)
+ next_pos = H5B2_POS_RIGHT;
+ } /* end if */
+ } /* end if */
+ } /* end else */
+
+ /* Attempt to remove record from child node */
+ if(depth > 1) {
+ if(H5B2__remove_internal_by_idx(hdr, dxpl_id, depth_decreased, swap_loc, swap_parent, (uint16_t)(depth - 1),
+ new_cache_info, new_cache_info_flags_ptr, new_node_ptr, next_pos, n, op, op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree internal node")
+ } /* end if */
+ else {
+ if(H5B2__remove_leaf_by_idx(hdr, dxpl_id, new_node_ptr, next_pos, new_cache_info, (unsigned)n, op, op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record from B-tree leaf node")
+ } /* end else */
+
+ /* Update record count for node pointer to child node */
+ if(!collapsed_root)
+ new_node_ptr->all_nrec--;
+
+ /* Mark node as dirty */
+ if(!(hdr->swmr_write && collapsed_root))
+ internal_flags |= H5AC__DIRTIED_FLAG;
+
+#ifdef H5B2_DEBUG
+ H5B2__assert_internal((!collapsed_root ? (curr_node_ptr->all_nrec - 1) : new_node_ptr->all_nrec), hdr, internal);
+#endif /* H5B2_DEBUG */
+
+done:
+ /* Release the B-tree internal node */
+ if(internal && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, internal_addr, internal, internal_flags) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release internal B-tree node")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__remove_internal_by_idx() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__internal_free
+ *
+ * Purpose: Destroys a B-tree internal node in memory.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Feb 2 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__internal_free(H5B2_internal_t *internal)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /*
+ * Check arguments.
+ */
+ HDassert(internal);
+
+ /* Release internal node's native key buffer */
+ if(internal->int_native)
+ internal->int_native = (uint8_t *)H5FL_FAC_FREE(internal->hdr->node_info[internal->depth].nat_rec_fac, internal->int_native);
+
+ /* Release internal node's node pointer buffer */
+ if(internal->node_ptrs)
+ internal->node_ptrs = (H5B2_node_ptr_t *)H5FL_FAC_FREE(internal->hdr->node_info[internal->depth].node_ptr_fac, internal->node_ptrs);
+
+ /* Decrement ref. count on B-tree header */
+ if(H5B2__hdr_decr(internal->hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDEC, FAIL, "can't decrement ref. count on B-tree header")
+
+ /* Sanity check */
+ HDassert(NULL == internal->top_proxy);
+
+ /* Free B-tree internal node info */
+ internal = H5FL_FREE(H5B2_internal_t, internal);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__internal_free() */
+
+#ifdef H5B2_DEBUG
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__assert_internal
+ *
+ * Purpose: Verify than an internal node is mostly sane
+ *
+ * Return: Non-negative on success, negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Feb 19 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+H5_ATTR_PURE herr_t
+H5B2__assert_internal(hsize_t parent_all_nrec, const H5B2_hdr_t *hdr, const H5B2_internal_t *internal)
+{
+ hsize_t tot_all_nrec; /* Total number of records at or below this node */
+ uint16_t u, v; /* Local index variables */
+
+ /* General sanity checking on node */
+ HDassert(internal->nrec <= hdr->node_info->split_nrec);
+
+ /* Sanity checking on node pointers */
+ tot_all_nrec = internal->nrec;
+ for(u = 0; u < internal->nrec + 1; u++) {
+ tot_all_nrec += internal->node_ptrs[u].all_nrec;
+
+ HDassert(H5F_addr_defined(internal->node_ptrs[u].addr));
+ HDassert(internal->node_ptrs[u].addr > 0);
+ for(v = 0; v < u; v++)
+ HDassert(internal->node_ptrs[u].addr != internal->node_ptrs[v].addr);
+ } /* end for */
+
+ /* Sanity check all_nrec total in parent */
+ if(parent_all_nrec > 0)
+ HDassert(tot_all_nrec == parent_all_nrec);
+
+ return(0);
+} /* end H5B2__assert_internal() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__assert_internal2
+ *
+ * Purpose: Verify than internal nodes are mostly sane
+ *
+ * Return: Non-negative on success, negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Feb 19 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+H5_ATTR_PURE herr_t
+H5B2__assert_internal2(hsize_t parent_all_nrec, const H5B2_hdr_t *hdr, const H5B2_internal_t *internal, const H5B2_internal_t *internal2)
+{
+ hsize_t tot_all_nrec; /* Total number of records at or below this node */
+ uint16_t u, v; /* Local index variables */
+
+ /* General sanity checking on node */
+ HDassert(internal->nrec <= hdr->node_info->split_nrec);
+
+ /* Sanity checking on node pointers */
+ tot_all_nrec =internal->nrec;
+ for(u =0; u < internal->nrec + 1; u++) {
+ tot_all_nrec += internal->node_ptrs[u].all_nrec;
+
+ HDassert(H5F_addr_defined(internal->node_ptrs[u].addr));
+ HDassert(internal->node_ptrs[u].addr > 0);
+ for(v = 0; v < u; v++)
+ HDassert(internal->node_ptrs[u].addr != internal->node_ptrs[v].addr);
+ for(v = 0; v < internal2->nrec + 1; v++)
+ HDassert(internal->node_ptrs[u].addr != internal2->node_ptrs[v].addr);
+ } /* end for */
+
+ /* Sanity check all_nrec total in parent */
+ if(parent_all_nrec > 0)
+ HDassert(tot_all_nrec == parent_all_nrec);
+
+ return(0);
+} /* end H5B2__assert_internal2() */
+#endif /* H5B2_DEBUG */
+
diff --git a/src/H5B2leaf.c b/src/H5B2leaf.c
new file mode 100644
index 0000000..4f8b8e6
--- /dev/null
+++ b/src/H5B2leaf.c
@@ -0,0 +1,1065 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5B2leaf.c
+ * Dec 01 2016
+ * Quincey Koziol <koziol@lbl.gov>
+ *
+ * Purpose: Routines for managing v2 B-tree leaf ndoes.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/****************/
+/* Module Setup */
+/****************/
+
+#include "H5B2module.h" /* This source code file is part of the H5B2 module */
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5B2pkg.h" /* v2 B-trees */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5MFprivate.h" /* File memory management */
+#include "H5MMprivate.h" /* Memory management */
+
+
+/****************/
+/* Local Macros */
+/****************/
+
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+
+/********************/
+/* Package Typedefs */
+/********************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+static herr_t H5B2__shadow_leaf(H5B2_leaf_t *leaf, hid_t dxpl_id,
+ H5B2_node_ptr_t *curr_node_ptr);
+
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+/* Declare a free list to manage the H5B2_leaf_t struct */
+H5FL_DEFINE(H5B2_leaf_t);
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__create_leaf
+ *
+ * Purpose: Creates empty leaf node of a B-tree and update node pointer
+ * to point to it.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Feb 2 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__create_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, void *parent, H5B2_node_ptr_t *node_ptr)
+{
+ H5B2_leaf_t *leaf = NULL; /* Pointer to new leaf node created */
+ hbool_t inserted = FALSE; /* Whether the leaf node was inserted into cache */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(node_ptr);
+
+ /* Allocate memory for leaf information */
+ if(NULL == (leaf = H5FL_CALLOC(H5B2_leaf_t)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree leaf info")
+
+ /* Increment ref. count on B-tree header */
+ if(H5B2__hdr_incr(hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTINC, FAIL, "can't increment ref. count on B-tree header")
+
+ /* Share B-tree header information */
+ leaf->hdr = hdr;
+
+ /* Allocate space for the native keys in memory */
+ if(NULL == (leaf->leaf_native = (uint8_t *)H5FL_FAC_MALLOC(hdr->node_info[0].nat_rec_fac)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for B-tree leaf native keys")
+ HDmemset(leaf->leaf_native, 0, hdr->cls->nrec_size * hdr->node_info[0].max_nrec);
+
+ /* Set parent */
+ leaf->parent = parent;
+
+ /* Set shadowed epoch to header's epoch */
+ leaf->shadow_epoch = hdr->shadow_epoch;
+
+ /* Allocate space on disk for the leaf */
+ if(HADDR_UNDEF == (node_ptr->addr = H5MF_alloc(hdr->f, H5FD_MEM_BTREE, dxpl_id, (hsize_t)hdr->node_size)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for B-tree leaf node")
+
+ /* Cache the new B-tree node */
+ if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_BT2_LEAF, node_ptr->addr, leaf, H5AC__NO_FLAGS_SET) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTINIT, FAIL, "can't add B-tree leaf to cache")
+ inserted = TRUE;
+
+ /* Add leaf node as child of 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, leaf) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSET, FAIL, "unable to add v2 B-tree node as child of proxy")
+ leaf->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+done:
+ if(ret_value < 0) {
+ if(leaf) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(leaf) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTREMOVE, FAIL, "unable to remove v2 B-tree leaf node from cache")
+
+ /* Release leaf node's disk space */
+ if(H5F_addr_defined(node_ptr->addr) && H5MF_xfree(hdr->f, H5FD_MEM_BTREE, dxpl_id, node_ptr->addr, (hsize_t)hdr->node_size) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, FAIL, "unable to release file space for v2 B-tree leaf node")
+
+ /* Destroy leaf node */
+ if(H5B2__leaf_free(leaf) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTFREE, FAIL, "unable to release v2 B-tree leaf node")
+ } /* end if */
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__create_leaf() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__protect_leaf
+ *
+ * Purpose: "Protect" an leaf node in the metadata cache
+ *
+ * Return: Pointer to leaf node on success/NULL on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@hdfgroup.org
+ * May 5 2010
+ *
+ *-------------------------------------------------------------------------
+ */
+H5B2_leaf_t *
+H5B2__protect_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, void *parent,
+ H5B2_node_ptr_t *node_ptr, hbool_t shadow, unsigned flags)
+{
+ H5B2_leaf_cache_ud_t udata; /* User-data for callback */
+ H5B2_leaf_t *leaf; /* v2 B-tree leaf node */
+ H5B2_leaf_t *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(node_ptr);
+ HDassert(H5F_addr_defined(node_ptr->addr));
+
+ /* only H5AC__READ_ONLY_FLAG may appear in flags */
+ HDassert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0);
+
+ /* Set up user data for callback */
+ udata.f = hdr->f;
+ udata.hdr = hdr;
+ udata.parent = parent;
+ udata.nrec = node_ptr->node_nrec;
+
+ /* Protect the leaf node */
+ if(NULL == (leaf = (H5B2_leaf_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_BT2_LEAF, node_ptr->addr, &udata, flags)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, NULL, "unable to protect B-tree leaf node")
+
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->top_proxy && NULL == leaf->top_proxy) {
+ /* Add leaf node as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, leaf) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTSET, NULL, "unable to add v2 B-tree leaf node as child of proxy")
+ leaf->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+ /* Shadow the node, if requested */
+ if(shadow)
+ if(H5B2__shadow_leaf(leaf, dxpl_id, node_ptr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOPY, NULL, "unable to shadow leaf node")
+
+ /* Set return value */
+ ret_value = leaf;
+
+done:
+ /* Clean up on error */
+ if(!ret_value) {
+ /* Release the leaf node, if it was protected */
+ if(leaf) {
+ /* Remove from v2 B-tree's proxy, if added */
+ if(leaf->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(leaf->top_proxy, leaf) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNDEPEND, NULL, "unable to destroy flush dependency between leaf node and v2 B-tree 'top' proxy")
+ leaf->top_proxy = NULL;
+ } /* end if */
+
+ /* Unprotect leaf node */
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, node_ptr->addr, leaf, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, NULL, "unable to unprotect v2 B-tree leaf node, address = %llu", (unsigned long long)node_ptr->addr)
+ } /* end if */
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__protect_leaf() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__neighbor_leaf
+ *
+ * Purpose: Locate a record relative to the specified information in a
+ * B-tree leaf node and return that information by filling in
+ * fields of the
+ * caller-supplied UDATA pointer depending on the type of leaf node
+ * requested. The UDATA can point to additional data passed
+ * to the key comparison function.
+ *
+ * The 'OP' routine is called with the record found and the
+ * OP_DATA pointer, to allow caller to return information about
+ * the record.
+ *
+ * The RANGE indicates whether to search for records less than or
+ * equal to, or greater than or equal to the information passed
+ * in with UDATA.
+ *
+ * Return: Non-negative on success, negative on failure.
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Mar 9 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__neighbor_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *curr_node_ptr,
+ void *neighbor_loc, H5B2_compare_t comp, void *parent, void *udata, H5B2_found_t op,
+ void *op_data)
+{
+ H5B2_leaf_t *leaf; /* Pointer to leaf node */
+ unsigned idx = 0; /* Location of record which matches key */
+ int cmp = 0; /* Comparison value of records */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+ HDassert(op);
+
+ /* Lock current B-tree node */
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, curr_node_ptr, FALSE, H5AC__READ_ONLY_FLAG)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+
+ /* Locate node pointer for child */
+ if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+ if(cmp > 0)
+ idx++;
+ else
+ if(cmp == 0 && comp == H5B2_COMPARE_GREATER)
+ idx++;
+
+ /* Set the neighbor location, if appropriate */
+ if(comp == H5B2_COMPARE_LESS) {
+ if(idx > 0)
+ neighbor_loc = H5B2_LEAF_NREC(leaf, hdr, idx - 1);
+ } /* end if */
+ else {
+ HDassert(comp == H5B2_COMPARE_GREATER);
+
+ if(idx < leaf->nrec)
+ neighbor_loc = H5B2_LEAF_NREC(leaf, hdr, idx);
+ } /* end else */
+
+ /* Make callback if neighbor record has been found */
+ if(neighbor_loc) {
+ /* Make callback for current record */
+ if((op)(neighbor_loc, op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "'found' callback failed for B-tree neighbor operation")
+ } /* end if */
+ else
+ HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "unable to find neighbor record in B-tree")
+
+done:
+ /* Release the B-tree leaf node */
+ if(leaf && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr->addr, leaf, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree leaf node")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__neighbor_leaf() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__insert_leaf
+ *
+ * Purpose: Adds a new record to a B-tree leaf node.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Mar 3 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__insert_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *curr_node_ptr,
+ H5B2_nodepos_t curr_pos, void *parent, void *udata)
+{
+ H5B2_leaf_t *leaf; /* Pointer to leaf node */
+ unsigned leaf_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting the leaf node */
+ int cmp; /* Comparison value of records */
+ unsigned idx = 0; /* Location of record which matches key */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+
+ /* Lock current B-tree node */
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, curr_node_ptr, FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+
+ /* Must have a leaf node with enough space to insert a record now */
+ HDassert(curr_node_ptr->node_nrec < hdr->node_info[0].max_nrec);
+
+ /* Sanity check number of records */
+ HDassert(curr_node_ptr->all_nrec == curr_node_ptr->node_nrec);
+ HDassert(leaf->nrec == curr_node_ptr->node_nrec);
+
+ /* Check for inserting into empty leaf */
+ if(leaf->nrec == 0)
+ idx = 0;
+ else {
+ /* Find correct location to insert this record */
+ if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+ if(cmp == 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_EXISTS, FAIL, "record is already in B-tree")
+ if(cmp > 0)
+ idx++;
+
+ /* Make room for new record */
+ if(idx < leaf->nrec)
+ HDmemmove(H5B2_LEAF_NREC(leaf, hdr, idx + 1), H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size * (leaf->nrec - idx));
+ } /* end else */
+
+ /* Make callback to store record in native form */
+ if((hdr->cls->store)(H5B2_LEAF_NREC(leaf, hdr, idx), udata) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into leaf node")
+
+ /* Mark the node as dirty */
+ leaf_flags |= H5AC__DIRTIED_FLAG;
+
+ /* Update record count for node pointer to current node */
+ curr_node_ptr->all_nrec++;
+ curr_node_ptr->node_nrec++;
+
+ /* Update record count for current node */
+ leaf->nrec++;
+
+ /* Check for new record being the min or max for the tree */
+ /* (Don't use 'else' for the idx check, to allow for root leaf node) */
+ if(H5B2_POS_MIDDLE != curr_pos) {
+ if(idx == 0) {
+ if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos) {
+ if(hdr->min_native_rec == NULL)
+ if(NULL == (hdr->min_native_rec = H5MM_malloc(hdr->cls->nrec_size)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "memory allocation failed for v2 B-tree min record info")
+ HDmemcpy(hdr->min_native_rec, H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size);
+ } /* end if */
+ } /* end if */
+ if(idx == (unsigned)(leaf->nrec - 1)) {
+ if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos) {
+ if(hdr->max_native_rec == NULL)
+ if(NULL == (hdr->max_native_rec = H5MM_malloc(hdr->cls->nrec_size)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "memory allocation failed for v2 B-tree max record info")
+ HDmemcpy(hdr->max_native_rec, H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size);
+ } /* end if */
+ } /* end if */
+ } /* end if */
+
+done:
+ /* Release the B-tree leaf node (marked as dirty) */
+ if(leaf) {
+ /* Shadow the node if doing SWMR writes */
+ if(hdr->swmr_write && (leaf_flags & H5AC__DIRTIED_FLAG))
+ if(H5B2__shadow_leaf(leaf, dxpl_id, curr_node_ptr) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow leaf B-tree node")
+
+ /* Unprotect leaf node */
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr->addr, leaf, leaf_flags) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release leaf B-tree node")
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__insert_leaf() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__update_leaf
+ *
+ * Purpose: Insert or modify a record in a B-tree leaf node.
+ * If the record exists already, it is modified as if H5B2_modify
+ * was called). If it doesn't exist, it is inserted as if
+ * H5B2_insert was called.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@hdfgroup.org
+ * Dec 23 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__update_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *curr_node_ptr,
+ H5B2_update_status_t *status, H5B2_nodepos_t curr_pos, void *parent,
+ void *udata, H5B2_modify_t op, void *op_data)
+{
+ H5B2_leaf_t *leaf; /* Pointer to leaf node */
+ unsigned leaf_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting the leaf node */
+ int cmp = -1; /* Comparison value of records */
+ unsigned idx = 0; /* Location of record which matches key */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+
+ /* Lock current B-tree node */
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, curr_node_ptr, FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+
+ /* Sanity check number of records */
+ HDassert(curr_node_ptr->all_nrec == curr_node_ptr->node_nrec);
+ HDassert(leaf->nrec == curr_node_ptr->node_nrec);
+
+ /* Check for inserting into empty leaf */
+ if(leaf->nrec == 0)
+ idx = 0;
+ else {
+ /* Find correct location to insert this record */
+ if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+
+ /* Check for inserting a record */
+ if(0 != cmp) {
+ /* Check if the leaf node is full */
+ if(curr_node_ptr->node_nrec == hdr->node_info[0].split_nrec) {
+ /* Indicate that the leaf is full, but we need to insert */
+ *status = H5B2_UPDATE_INSERT_CHILD_FULL;
+
+ /* Let calling routine handle insertion */
+ HGOTO_DONE(SUCCEED)
+ } /* end if */
+
+ /* Adjust index to leave room for record to insert */
+ if(cmp > 0)
+ idx++;
+
+ /* Make room for new record */
+ if(idx < leaf->nrec)
+ HDmemmove(H5B2_LEAF_NREC(leaf, hdr, idx + 1), H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size * (leaf->nrec - idx));
+ } /* end if */
+ } /* end else */
+
+ /* Check for modifying existing record */
+ if(0 == cmp) {
+ hbool_t changed = FALSE; /* Whether the 'modify' callback changed the record */
+
+ /* Make callback for current record */
+ if((op)(H5B2_LEAF_NREC(leaf, hdr, idx), op_data, &changed) < 0) {
+ /* Make certain that the callback didn't modify the value if it failed */
+ HDassert(changed == FALSE);
+
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTMODIFY, FAIL, "'modify' callback failed for B-tree update operation")
+ } /* end if */
+
+ /* Mark the node as dirty if it changed */
+ leaf_flags |= (changed ? H5AC__DIRTIED_FLAG : 0);
+
+ /* Indicate that the record was modified */
+ *status = H5B2_UPDATE_MODIFY_DONE;
+ } /* end if */
+ else {
+ /* Must have a leaf node with enough space to insert a record now */
+ HDassert(curr_node_ptr->node_nrec < hdr->node_info[0].max_nrec);
+
+ /* Make callback to store record in native form */
+ if((hdr->cls->store)(H5B2_LEAF_NREC(leaf, hdr, idx), udata) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTINSERT, FAIL, "unable to insert record into leaf node")
+
+ /* Mark the node as dirty */
+ leaf_flags |= H5AC__DIRTIED_FLAG;
+
+ /* Indicate that the record was inserted */
+ *status = H5B2_UPDATE_INSERT_DONE;
+
+ /* Update record count for node pointer to current node */
+ curr_node_ptr->all_nrec++;
+ curr_node_ptr->node_nrec++;
+
+ /* Update record count for current node */
+ leaf->nrec++;
+ } /* end else */
+
+ /* Check for new record being the min or max for the tree */
+ /* (Don't use 'else' for the idx check, to allow for root leaf node) */
+ if(H5B2_POS_MIDDLE != curr_pos) {
+ if(idx == 0) {
+ if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos) {
+ if(hdr->min_native_rec == NULL)
+ if(NULL == (hdr->min_native_rec = H5MM_malloc(hdr->cls->nrec_size)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "memory allocation failed for v2 B-tree min record info")
+ HDmemcpy(hdr->min_native_rec, H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size);
+ } /* end if */
+ } /* end if */
+ if(idx == (unsigned)(leaf->nrec - 1)) {
+ if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos) {
+ if(hdr->max_native_rec == NULL)
+ if(NULL == (hdr->max_native_rec = H5MM_malloc(hdr->cls->nrec_size)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "memory allocation failed for v2 B-tree max record info")
+ HDmemcpy(hdr->max_native_rec, H5B2_LEAF_NREC(leaf, hdr, idx), hdr->cls->nrec_size);
+ } /* end if */
+ } /* end if */
+ } /* end if */
+
+done:
+ /* Release the B-tree leaf node */
+ if(leaf) {
+ /* Check if we should shadow this node */
+ if(hdr->swmr_write && (leaf_flags & H5AC__DIRTIED_FLAG)) {
+ /* Attempt to shadow the node if doing SWMR writes */
+ if(H5B2__shadow_leaf(leaf, dxpl_id, curr_node_ptr) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow leaf B-tree node")
+
+ /* Change the state to "shadowed" if only modified currently */
+ /* (Triggers parent to be marked dirty) */
+ if(*status == H5B2_UPDATE_MODIFY_DONE)
+ *status = H5B2_UPDATE_SHADOW_DONE;
+ } /* end if */
+
+ /* Unprotect leaf node */
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, curr_node_ptr->addr, leaf, leaf_flags) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release leaf B-tree node")
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__update_leaf() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__swap_leaf
+ *
+ * Purpose: Swap a record in a node with a record in a leaf node
+ *
+ * Return: Success: Non-negative
+ *
+ * Failure: Negative
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Mar 4 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__swap_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ H5B2_internal_t *internal, unsigned *internal_flags_ptr,
+ unsigned idx, void *swap_loc)
+{
+ const H5AC_class_t *child_class; /* Pointer to child node's class info */
+ haddr_t child_addr; /* Address of child node */
+ void *child = NULL; /* Pointer to child node */
+ uint8_t *child_native; /* Pointer to child's native records */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(internal);
+ HDassert(internal_flags_ptr);
+ HDassert(idx <= internal->nrec);
+
+ /* Check for the kind of B-tree node to swap */
+ if(depth > 1) {
+ H5B2_internal_t *child_internal; /* Pointer to internal node */
+
+ /* Setup information for unlocking child node */
+ child_class = H5AC_BT2_INT;
+
+ /* Lock B-tree child nodes */
+ if(NULL == (child_internal = H5B2__protect_internal(hdr, dxpl_id, internal, &internal->node_ptrs[idx], (uint16_t)(depth - 1), FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree internal node")
+ child_addr = internal->node_ptrs[idx].addr;
+
+ /* More setup for accessing child node information */
+ child = child_internal;
+ child_native = child_internal->int_native;
+ } /* end if */
+ else {
+ H5B2_leaf_t *child_leaf; /* Pointer to leaf node */
+
+ /* Setup information for unlocking child nodes */
+ child_class = H5AC_BT2_LEAF;
+
+ /* Lock B-tree child node */
+ if(NULL == (child_leaf = H5B2__protect_leaf(hdr, dxpl_id, internal, &internal->node_ptrs[idx], FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ child_addr = internal->node_ptrs[idx].addr;
+
+ /* More setup for accessing child node information */
+ child = child_leaf;
+ child_native = child_leaf->leaf_native;
+ } /* end else */
+
+ /* Swap records (use disk page as temporary buffer) */
+ HDmemcpy(hdr->page, H5B2_NAT_NREC(child_native, hdr, 0), hdr->cls->nrec_size);
+ HDmemcpy(H5B2_NAT_NREC(child_native, hdr, 0), swap_loc, hdr->cls->nrec_size);
+ HDmemcpy(swap_loc, hdr->page, hdr->cls->nrec_size);
+
+ /* Mark parent as dirty */
+ *internal_flags_ptr |= H5AC__DIRTIED_FLAG;
+
+#ifdef H5B2_DEBUG
+ H5B2__assert_internal((hsize_t)0, hdr, internal);
+ if(depth > 1)
+ H5B2__assert_internal(internal->node_ptrs[idx].all_nrec, hdr, (H5B2_internal_t *)child);
+ else
+ H5B2__assert_leaf(hdr, (H5B2_leaf_t *)child);
+#endif /* H5B2_DEBUG */
+
+done:
+ /* Unlock child node */
+ if(child && H5AC_unprotect(hdr->f, dxpl_id, child_class, child_addr, child, H5AC__DIRTIED_FLAG) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree child node")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__swap_leaf() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__shadow_leaf
+ *
+ * Purpose: "Shadow" a leaf node - copy it to a new location, leaving
+ * the data in the old location intact (for now). This is
+ * done when writing in SWMR mode to ensure that readers do
+ * not see nodes that are out of date with respect to each
+ * other and thereby inconsistent.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Neil Fortner
+ * Apr 27 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5B2__shadow_leaf(H5B2_leaf_t *leaf, hid_t dxpl_id, H5B2_node_ptr_t *curr_node_ptr)
+{
+ H5B2_hdr_t *hdr; /* B-tree header */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /*
+ * Check arguments.
+ */
+ HDassert(leaf);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+ hdr = leaf->hdr;
+ HDassert(hdr);
+ HDassert(hdr->swmr_write);
+
+ /* We only need to shadow the node if it has not been shadowed since the
+ * last time the header was flushed, as otherwise it will be unreachable by
+ * the readers so there will be no need to shadow. To check if it has been
+ * shadowed, compare the epoch of this node and the header. If this node's
+ * epoch is <= to the header's, it hasn't been shadowed yet. */
+ if(leaf->shadow_epoch <= hdr->shadow_epoch) {
+ haddr_t new_node_addr; /* Address to move node to */
+
+ /*
+ * We must clone the old node so readers with an out-of-date version of
+ * the parent can still see the correct number of children, via the
+ * shadowed node. Remove it from cache but do not mark it free on disk.
+ */
+ /* Allocate space for the cloned node */
+ if(HADDR_UNDEF == (new_node_addr = H5MF_alloc(hdr->f, H5FD_MEM_BTREE, dxpl_id, (hsize_t)hdr->node_size)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTALLOC, FAIL, "unable to allocate file space to move B-tree node")
+
+ /* Move the location of the old child on the disk */
+ if(H5AC_move_entry(hdr->f, H5AC_BT2_LEAF, curr_node_ptr->addr, new_node_addr, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTMOVE, FAIL, "unable to move B-tree node")
+ curr_node_ptr->addr = new_node_addr;
+
+ /* Should free the space in the file, but this is not supported by
+ * SWMR_WRITE code yet - QAK, 2016/12/01
+ */
+
+ /* Set shadow epoch for node ahead of header */
+ leaf->shadow_epoch = hdr->shadow_epoch + 1;
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__shadow_leaf() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__remove_leaf
+ *
+ * Purpose: Removes a record from a B-tree leaf node.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Mar 3 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__remove_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, H5B2_node_ptr_t *curr_node_ptr,
+ H5B2_nodepos_t curr_pos, void *parent, void *udata, H5B2_remove_t op, void *op_data)
+{
+ H5B2_leaf_t *leaf; /* Pointer to leaf node */
+ haddr_t leaf_addr = HADDR_UNDEF; /* Leaf address on disk */
+ unsigned leaf_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting leaf node */
+ unsigned idx = 0; /* Location of record which matches key */
+ int cmp; /* Comparison value of records */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+
+ /* Lock current B-tree node */
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, curr_node_ptr, FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ leaf_addr = curr_node_ptr->addr;
+
+ /* Sanity check number of records */
+ HDassert(curr_node_ptr->all_nrec == curr_node_ptr->node_nrec);
+ HDassert(leaf->nrec == curr_node_ptr->node_nrec);
+
+ /* Find correct location to remove this record */
+ if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native, udata, &idx, &cmp) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOMPARE, FAIL, "can't compare btree2 records")
+ if(cmp != 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_NOTFOUND, FAIL, "record is not in B-tree")
+
+ /* Check for invalidating the min/max record for the tree */
+ if(H5B2_POS_MIDDLE != curr_pos) {
+ /* (Don't use 'else' for the idx check, to allow for root leaf node) */
+ if(idx == 0) {
+ if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos) {
+ if(hdr->min_native_rec)
+ hdr->min_native_rec = H5MM_xfree(hdr->min_native_rec);
+ } /* end if */
+ } /* end if */
+ if(idx == (unsigned)(leaf->nrec - 1)) {
+ if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos) {
+ if(hdr->max_native_rec)
+ hdr->max_native_rec = H5MM_xfree(hdr->max_native_rec);
+ } /* end if */
+ } /* end if */
+ } /* end if */
+
+ /* Make 'remove' callback if there is one */
+ if(op)
+ if((op)(H5B2_LEAF_NREC(leaf, hdr, idx), op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record into leaf node")
+
+ /* Update number of records in node */
+ leaf->nrec--;
+
+ if(leaf->nrec > 0) {
+ /* Shadow the node if doing SWMR writes */
+ if(hdr->swmr_write) {
+ if(H5B2__shadow_leaf(leaf, dxpl_id, curr_node_ptr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow leaf node")
+ leaf_addr = curr_node_ptr->addr;
+ } /* end if */
+
+ /* Pack record out of leaf */
+ if(idx < leaf->nrec)
+ HDmemmove(H5B2_LEAF_NREC(leaf, hdr, idx), H5B2_LEAF_NREC(leaf, hdr, (idx + 1)), hdr->cls->nrec_size * (leaf->nrec - idx));
+
+ /* Mark leaf node as dirty also */
+ leaf_flags |= H5AC__DIRTIED_FLAG;
+ } /* end if */
+ else {
+ /* Let the cache know that the object is deleted */
+ leaf_flags |= H5AC__DELETED_FLAG;
+ if(!hdr->swmr_write)
+ leaf_flags |= H5AC__DIRTIED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
+
+ /* Reset address of parent node pointer */
+ curr_node_ptr->addr = HADDR_UNDEF;
+ } /* end else */
+
+ /* Update record count for parent of leaf node */
+ curr_node_ptr->node_nrec--;
+
+done:
+ /* Release the B-tree leaf node */
+ if(leaf && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, leaf_addr, leaf, leaf_flags) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release leaf B-tree node")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__remove_leaf() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__remove_leaf_by_idx
+ *
+ * Purpose: Removes a record from a B-tree leaf node, according to the
+ * offset in the B-tree records.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@hdfgroup.org
+ * Nov 14 2006
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__remove_leaf_by_idx(H5B2_hdr_t *hdr, hid_t dxpl_id,
+ H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, void *parent,
+ unsigned idx, H5B2_remove_t op, void *op_data)
+{
+ H5B2_leaf_t *leaf; /* Pointer to leaf node */
+ haddr_t leaf_addr = HADDR_UNDEF; /* Leaf address on disk */
+ unsigned leaf_flags = H5AC__NO_FLAGS_SET; /* Flags for unprotecting leaf node */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Check arguments. */
+ HDassert(hdr);
+ HDassert(curr_node_ptr);
+ HDassert(H5F_addr_defined(curr_node_ptr->addr));
+
+ /* Lock B-tree leaf node */
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, curr_node_ptr, FALSE, H5AC__NO_FLAGS_SET)))
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ leaf_addr = curr_node_ptr->addr;
+
+ /* Sanity check number of records */
+ HDassert(curr_node_ptr->all_nrec == curr_node_ptr->node_nrec);
+ HDassert(leaf->nrec == curr_node_ptr->node_nrec);
+ HDassert(idx < leaf->nrec);
+
+ /* Check for invalidating the min/max record for the tree */
+ if(H5B2_POS_MIDDLE != curr_pos) {
+ /* (Don't use 'else' for the idx check, to allow for root leaf node) */
+ if(idx == 0) {
+ if(H5B2_POS_LEFT == curr_pos || H5B2_POS_ROOT == curr_pos) {
+ if(hdr->min_native_rec)
+ hdr->min_native_rec = H5MM_xfree(hdr->min_native_rec);
+ } /* end if */
+ } /* end if */
+ if(idx == (unsigned)(leaf->nrec - 1)) {
+ if(H5B2_POS_RIGHT == curr_pos || H5B2_POS_ROOT == curr_pos) {
+ if(hdr->max_native_rec)
+ hdr->max_native_rec = H5MM_xfree(hdr->max_native_rec);
+ } /* end if */
+ } /* end if */
+ } /* end if */
+
+ /* Make 'remove' callback if there is one */
+ if(op)
+ if((op)(H5B2_LEAF_NREC(leaf, hdr, idx), op_data) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDELETE, FAIL, "unable to remove record into leaf node")
+
+ /* Update number of records in node */
+ leaf->nrec--;
+
+ if(leaf->nrec > 0) {
+ /* Shadow the node if doing SWMR writes */
+ if(hdr->swmr_write) {
+ if(H5B2__shadow_leaf(leaf, dxpl_id, curr_node_ptr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTCOPY, FAIL, "unable to shadow leaf node")
+ leaf_addr = curr_node_ptr->addr;
+ } /* end if */
+
+ /* Pack record out of leaf */
+ if(idx < leaf->nrec)
+ HDmemmove(H5B2_LEAF_NREC(leaf, hdr, idx), H5B2_LEAF_NREC(leaf, hdr, (idx + 1)), hdr->cls->nrec_size * (leaf->nrec - idx));
+
+ /* Mark leaf node as dirty also */
+ leaf_flags |= H5AC__DIRTIED_FLAG;
+ } /* end if */
+ else {
+ /* Let the cache know that the object is deleted */
+ leaf_flags |= H5AC__DELETED_FLAG;
+ if(!hdr->swmr_write)
+ leaf_flags |= H5AC__DIRTIED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
+
+ /* Reset address of parent node pointer */
+ curr_node_ptr->addr = HADDR_UNDEF;
+ } /* end else */
+
+ /* Update record count for parent of leaf node */
+ curr_node_ptr->node_nrec--;
+
+done:
+ /* Release the B-tree leaf node */
+ if(leaf && H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_LEAF, leaf_addr, leaf, leaf_flags) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release leaf B-tree node")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5B2__remove_leaf_by_idx() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__leaf_free
+ *
+ * Purpose: Destroys a B-tree leaf node in memory.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Feb 2 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5B2__leaf_free(H5B2_leaf_t *leaf)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /*
+ * Check arguments.
+ */
+ HDassert(leaf);
+
+ /* Release leaf's native key buffer */
+ if(leaf->leaf_native)
+ leaf->leaf_native = (uint8_t *)H5FL_FAC_FREE(leaf->hdr->node_info[0].nat_rec_fac, leaf->leaf_native);
+
+ /* Decrement ref. count on B-tree header */
+ if(H5B2__hdr_decr(leaf->hdr) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTDEC, FAIL, "can't decrement ref. count on B-tree header")
+
+ /* Sanity check */
+ HDassert(NULL == leaf->top_proxy);
+
+ /* Free B-tree leaf node info */
+ leaf = H5FL_FREE(H5B2_leaf_t, leaf);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5B2__leaf_free() */
+
+#ifdef H5B2_DEBUG
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__assert_leaf
+ *
+ * Purpose: Verify than a leaf node is mostly sane
+ *
+ * Return: Non-negative on success, negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Feb 19 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+H5_ATTR_PURE herr_t
+H5B2__assert_leaf(const H5B2_hdr_t *hdr, const H5B2_leaf_t *leaf)
+{
+ /* General sanity checking on node */
+ HDassert(leaf->nrec <= hdr->node_info->split_nrec);
+
+ return(0);
+} /* end H5B2__assert_leaf() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5B2__assert_leaf2
+ *
+ * Purpose: Verify than a leaf node is mostly sane
+ *
+ * Return: Non-negative on success, negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Feb 19 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+H5_ATTR_PURE herr_t
+H5B2__assert_leaf2(const H5B2_hdr_t *hdr, const H5B2_leaf_t *leaf, const H5B2_leaf_t H5_ATTR_UNUSED *leaf2)
+{
+ /* General sanity checking on node */
+ HDassert(leaf->nrec <= hdr->node_info->split_nrec);
+
+ return(0);
+} /* end H5B2__assert_leaf2() */
+#endif /* H5B2_DEBUG */
+
diff --git a/src/H5B2pkg.h b/src/H5B2pkg.h
index 71700fe..7b1ec4d 100644
--- a/src/H5B2pkg.h
+++ b/src/H5B2pkg.h
@@ -125,6 +125,9 @@
#define H5B2_NUM_INT_REC(h, d) \
(((h)->node_size - (H5B2_INT_PREFIX_SIZE + H5B2_INT_POINTER_SIZE(h, d))) / ((h)->rrec_size + H5B2_INT_POINTER_SIZE(h, d)))
+/* Uncomment this macro to enable extra sanity checking */
+/* #define H5B2_DEBUG */
+
/****************************/
/* Package Private Typedefs */
@@ -185,6 +188,26 @@ typedef struct H5B2_hdr_t {
void *min_native_rec; /* Pointer to minimum native record */
void *max_native_rec; /* Pointer to maximum native record */
+ /* SWMR / Flush dependency information (not stored) */
+ hbool_t swmr_write; /* Whether we are doing SWMR writes */
+ H5AC_proxy_entry_t *top_proxy; /* 'Top' proxy cache entry for all B-tree entries */
+ void *parent; /* Pointer to 'top' proxy flush dependency
+ * parent, if it exists, otherwise NULL.
+ * If the v2 B-tree is being used to index a
+ * chunked dataset and the dataset metadata is
+ * modified by a SWMR writer, this field will
+ * be set equal to the object header proxy
+ * that is the flush dependency parent
+ * of the v2 B-tree header.
+ *
+ * The field is used to avoid duplicate setups
+ * of the flush dependency relationship, and to
+ * allow the v2 B-tree header to destroy the
+ * flush dependency on receipt of an eviction
+ * notification from the metadata cache.
+ */
+ uint64_t shadow_epoch; /* Epoch of header, for making shadow copies */
+
/* Client information (not stored) */
const H5B2_class_t *cls; /* Class of B-tree client */
void *cb_ctx; /* Client callback context */
@@ -199,6 +222,11 @@ typedef struct H5B2_leaf_t {
H5B2_hdr_t *hdr; /* Pointer to the [pinned] v2 B-tree header */
uint8_t *leaf_native; /* Pointer to native records */
uint16_t nrec; /* Number of records in node */
+
+ /* SWMR / Flush dependency information (not stored) */
+ H5AC_proxy_entry_t *top_proxy; /* 'Top' proxy cache entry for all B-tree entries */
+ void *parent; /* Flush dependency parent for leaf */
+ uint64_t shadow_epoch; /* Epoch of node, for making shadow copies */
} H5B2_leaf_t;
/* B-tree internal node information */
@@ -212,6 +240,11 @@ typedef struct H5B2_internal_t {
H5B2_node_ptr_t *node_ptrs; /* Pointer to node pointers */
uint16_t nrec; /* Number of records in node */
uint16_t depth; /* Depth of this node in the B-tree */
+
+ /* SWMR / Flush dependency information (not stored) */
+ H5AC_proxy_entry_t *top_proxy; /* 'Top' proxy cache entry for all B-tree entries */
+ void *parent; /* Flush dependency parent for internal node */
+ uint64_t shadow_epoch; /* Epoch of node, for making shadow copies */
} H5B2_internal_t;
/* v2 B-tree */
@@ -232,6 +265,7 @@ typedef enum H5B2_nodepos_t {
typedef enum H5B2_update_status_t {
H5B2_UPDATE_UNKNOWN, /* Unknown update status (initial state) */
H5B2_UPDATE_MODIFY_DONE, /* Update successfully modified existing record */
+ H5B2_UPDATE_SHADOW_DONE, /* Update modified existing record and modified node was shadowed */
H5B2_UPDATE_INSERT_DONE, /* Update inserted record successfully */
H5B2_UPDATE_INSERT_CHILD_FULL /* Update will insert record, but child is full */
} H5B2_update_status_t;
@@ -247,6 +281,7 @@ typedef struct H5B2_hdr_cache_ud_t {
typedef struct H5B2_internal_cache_ud_t {
H5F_t *f; /* File that v2 b-tree header is within */
H5B2_hdr_t *hdr; /* v2 B-tree header */
+ void *parent; /* Flush dependency parent */
uint16_t nrec; /* Number of records in node to load */
uint16_t depth; /* Depth of node to load */
} H5B2_internal_cache_ud_t;
@@ -255,6 +290,7 @@ typedef struct H5B2_internal_cache_ud_t {
typedef struct H5B2_leaf_cache_ud_t {
H5F_t *f; /* File that v2 b-tree header is within */
H5B2_hdr_t *hdr; /* v2 B-tree header */
+ void *parent; /* Flush dependency parent */
uint16_t nrec; /* Number of records in node to load */
} H5B2_leaf_cache_ud_t;
@@ -306,6 +342,30 @@ extern const H5B2_class_t *const H5B2_client_class_g[H5B2_NUM_BTREE_ID];
/* Package Private Prototypes */
/******************************/
+/* Generic routines */
+H5_DLL herr_t H5B2__create_flush_depend(H5AC_info_t *parent_entry,
+ H5AC_info_t *child_entry);
+H5_DLL herr_t H5B2__update_flush_depend(H5B2_hdr_t *hdr, hid_t dxpl_id,
+ unsigned depth, const H5B2_node_ptr_t *node_ptr, void *old_parent,
+ void *new_parent);
+H5_DLL herr_t H5B2__destroy_flush_depend(H5AC_info_t *parent_entry,
+ H5AC_info_t *child_entry);
+
+/* Internal node management routines */
+H5_DLL herr_t H5B2__split1(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ H5B2_node_ptr_t *curr_node_ptr, unsigned *parent_cache_info_flags_ptr,
+ H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx);
+H5_DLL herr_t H5B2__redistribute2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ H5B2_internal_t *internal, unsigned idx);
+H5_DLL herr_t H5B2__redistribute3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx);
+H5_DLL herr_t H5B2__merge2(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ H5B2_node_ptr_t *curr_node_ptr, unsigned *parent_cache_info_flags_ptr,
+ H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx);
+H5_DLL herr_t H5B2__merge3(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ H5B2_node_ptr_t *curr_node_ptr, unsigned *parent_cache_info_flags_ptr,
+ H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx);
+
/* Routines for managing B-tree header info */
H5_DLL H5B2_hdr_t *H5B2__hdr_alloc(H5F_t *f);
H5_DLL haddr_t H5B2__hdr_create(H5F_t *f, hid_t dxpl_id,
@@ -324,17 +384,23 @@ H5_DLL herr_t H5B2__hdr_unprotect(H5B2_hdr_t *hdr, hid_t dxpl_id,
H5_DLL herr_t H5B2__hdr_delete(H5B2_hdr_t *hdr, hid_t dxpl_id);
/* Routines for operating on leaf nodes */
-H5B2_leaf_t *H5B2__protect_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, haddr_t addr,
- uint16_t nrec, unsigned flags);
+H5_DLL H5B2_leaf_t * H5B2__protect_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id,
+ void *parent, H5B2_node_ptr_t *node_ptr, hbool_t shadow, unsigned flags);
+H5_DLL herr_t H5B2__swap_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
+ H5B2_internal_t *internal, unsigned *internal_flags_ptr, unsigned idx,
+ void *swap_loc);
/* Routines for operating on internal nodes */
H5_DLL H5B2_internal_t *H5B2__protect_internal(H5B2_hdr_t *hdr, hid_t dxpl_id,
- haddr_t addr, uint16_t nrec, uint16_t depth, unsigned flags);
+ void *parent, H5B2_node_ptr_t *node_ptr, uint16_t depth, hbool_t shadow,
+ unsigned flags);
/* Routines for allocating nodes */
H5_DLL herr_t H5B2__split_root(H5B2_hdr_t *hdr, hid_t dxpl_id);
-H5_DLL herr_t H5B2__create_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id,
+H5_DLL herr_t H5B2__create_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id, void *parent,
H5B2_node_ptr_t *node_ptr);
+H5_DLL herr_t H5B2__create_internal(H5B2_hdr_t *hdr, hid_t dxpl_id, void *parent,
+ H5B2_node_ptr_t *node_ptr, uint16_t depth);
/* Routines for releasing structures */
H5_DLL herr_t H5B2__hdr_free(H5B2_hdr_t *hdr);
@@ -342,59 +408,64 @@ H5_DLL herr_t H5B2__leaf_free(H5B2_leaf_t *l);
H5_DLL herr_t H5B2__internal_free(H5B2_internal_t *i);
/* Routines for inserting records */
-H5_DLL herr_t H5B2__insert_hdr(H5B2_hdr_t *hdr, hid_t dxpl_id, void *udata);
+H5_DLL herr_t H5B2__insert(H5B2_hdr_t *hdr, hid_t dxpl_id, void *udata);
H5_DLL herr_t H5B2__insert_internal(H5B2_hdr_t *hdr, hid_t dxpl_id,
uint16_t depth, unsigned *parent_cache_info_flags_ptr,
- H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, void *udata);
+ H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, void *parent, void *udata);
H5_DLL herr_t H5B2__insert_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id,
- H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, void *udata);
+ H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, void *parent, void *udata);
/* Routines for update records */
H5_DLL herr_t H5B2__update_internal(H5B2_hdr_t *hdr, hid_t dxpl_id,
uint16_t depth, unsigned *parent_cache_info_flags_ptr,
H5B2_node_ptr_t *curr_node_ptr, H5B2_update_status_t *status,
- H5B2_nodepos_t curr_pos, void *udata, H5B2_modify_t op, void *op_data);
+ H5B2_nodepos_t curr_pos, void *parent, void *udata, H5B2_modify_t op,
+ void *op_data);
H5_DLL herr_t H5B2__update_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id,
H5B2_node_ptr_t *curr_node_ptr, H5B2_update_status_t *status,
- H5B2_nodepos_t curr_pos, void *udata, H5B2_modify_t op, void *op_data);
+ H5B2_nodepos_t curr_pos, void *parent, void *udata, H5B2_modify_t op,
+ void *op_data);
/* Routines for iterating over nodes/records */
H5_DLL herr_t H5B2__iterate_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- const H5B2_node_ptr_t *curr_node, H5B2_operator_t op, void *op_data);
+ const H5B2_node_ptr_t *curr_node, void *parent, H5B2_operator_t op, void *op_data);
H5_DLL herr_t H5B2__node_size(H5B2_hdr_t *hdr, hid_t dxpl_id,
- uint16_t depth, const H5B2_node_ptr_t *curr_node, hsize_t *op_data);
+ uint16_t depth, const H5B2_node_ptr_t *curr_node, void *parent,
+ hsize_t *op_data);
/* Routines for locating records */
H5_DLL herr_t H5B2__locate_record(const H5B2_class_t *type, unsigned nrec,
size_t *rec_off, const uint8_t *native, const void *udata, unsigned *idx, int *result);
H5_DLL herr_t H5B2__neighbor_internal(H5B2_hdr_t *hdr, hid_t dxpl_id,
uint16_t depth, H5B2_node_ptr_t *curr_node_ptr, void *neighbor_loc,
- H5B2_compare_t comp, void *udata, H5B2_found_t op, void *op_data);
+ H5B2_compare_t comp, void *parent, void *udata, H5B2_found_t op,
+ void *op_data);
H5_DLL herr_t H5B2__neighbor_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id,
- H5B2_node_ptr_t *curr_node_ptr, void *neighbor_loc,
- H5B2_compare_t comp, void *udata, H5B2_found_t op, void *op_data);
+ H5B2_node_ptr_t *curr_node_ptr, void *neighbor_loc, H5B2_compare_t comp,
+ void *parent, void *udata, H5B2_found_t op, void *op_data);
/* Routines for removing records */
H5_DLL herr_t H5B2__remove_internal(H5B2_hdr_t *hdr, hid_t dxpl_id,
- hbool_t *depth_decreased, void *swap_loc, uint16_t depth,
+ hbool_t *depth_decreased, void *swap_loc, void *swap_parent, uint16_t depth,
H5AC_info_t *parent_cache_info, unsigned *parent_cache_info_flags_ptr,
H5B2_nodepos_t curr_pos, H5B2_node_ptr_t *curr_node_ptr, void *udata,
H5B2_remove_t op, void *op_data);
H5_DLL herr_t H5B2__remove_leaf(H5B2_hdr_t *hdr, hid_t dxpl_id,
- H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos,
+ H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, void *parent,
void *udata, H5B2_remove_t op, void *op_data);
H5_DLL herr_t H5B2__remove_internal_by_idx(H5B2_hdr_t *hdr, hid_t dxpl_id,
- hbool_t *depth_decreased, void *swap_loc, uint16_t depth,
+ hbool_t *depth_decreased, void *swap_loc, void *swap_parent, uint16_t depth,
H5AC_info_t *parent_cache_info, unsigned *parent_cache_info_flags_ptr,
H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, hsize_t n,
H5B2_remove_t op, void *op_data);
H5_DLL herr_t H5B2__remove_leaf_by_idx(H5B2_hdr_t *hdr, hid_t dxpl_id,
- H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos,
+ H5B2_node_ptr_t *curr_node_ptr, H5B2_nodepos_t curr_pos, void *parent,
unsigned idx, H5B2_remove_t op, void *op_data);
/* Routines for deleting nodes */
H5_DLL herr_t H5B2__delete_node(H5B2_hdr_t *hdr, hid_t dxpl_id, uint16_t depth,
- const H5B2_node_ptr_t *curr_node, H5B2_remove_t op, void *op_data);
+ const H5B2_node_ptr_t *curr_node, void *parent, H5B2_remove_t op,
+ void *op_data);
/* Debugging routines for dumping file structures */
H5_DLL herr_t H5B2__hdr_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr,
@@ -406,6 +477,15 @@ H5_DLL herr_t H5B2__leaf_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr,
FILE *stream, int indent, int fwidth, const H5B2_class_t *type,
haddr_t hdr_addr, unsigned nrec, haddr_t obj_addr);
+/* Sanity checking routines */
+#ifdef H5B2_DEBUG
+/* Don't label these with H5_ATTR_PURE or you'll get even more warnings... */
+H5_DLL herr_t H5B2__assert_internal(hsize_t parent_all_nrec, const H5B2_hdr_t *hdr, const H5B2_internal_t *internal);
+H5_DLL herr_t H5B2__assert_internal2(hsize_t parent_all_nrec, const H5B2_hdr_t *hdr, const H5B2_internal_t *internal, const H5B2_internal_t *internal2);
+H5_DLL herr_t H5B2__assert_leaf(const H5B2_hdr_t *hdr, const H5B2_leaf_t *leaf);
+H5_DLL herr_t H5B2__assert_leaf2(const H5B2_hdr_t *hdr, const H5B2_leaf_t *leaf, const H5B2_leaf_t *leaf2);
+#endif /* H5B2_DEBUG */
+
/* Testing routines */
#ifdef H5B2_TESTING
H5_DLL herr_t H5B2_get_root_addr_test(H5B2_t *bt2, haddr_t *root_addr);
diff --git a/src/H5B2private.h b/src/H5B2private.h
index 3caf41f..161e25e 100644
--- a/src/H5B2private.h
+++ b/src/H5B2private.h
@@ -31,7 +31,8 @@
#include "H5B2public.h"
/* Private headers needed by this file */
-#include "H5Fprivate.h" /* File access */
+#include "H5ACprivate.h" /* Metadata cache */
+#include "H5Fprivate.h" /* File access */
/**************************/
/* Library Private Macros */
@@ -153,6 +154,7 @@ H5_DLL herr_t H5B2_size(H5B2_t *bt2, hid_t dxpl_id,
H5_DLL herr_t H5B2_close(H5B2_t *bt2, hid_t dxpl_id);
H5_DLL herr_t H5B2_delete(H5F_t *f, hid_t dxpl_id, haddr_t addr,
void *ctx_udata, H5B2_remove_t op, void *op_data);
+H5_DLL herr_t H5B2_depend(H5B2_t *bt2, hid_t dxpl_id, H5AC_proxy_entry_t *parent);
H5_DLL herr_t H5B2_patch_file(H5B2_t *fa, H5F_t *f);
/* Statistics routines */
diff --git a/src/H5B2stat.c b/src/H5B2stat.c
index 10c692e..da721c6 100644
--- a/src/H5B2stat.c
+++ b/src/H5B2stat.c
@@ -139,7 +139,7 @@ H5B2_size(H5B2_t *bt2, hid_t dxpl_id, hsize_t *btree_size)
*btree_size += hdr->node_size;
else
/* Iterate through nodes */
- if(H5B2__node_size(hdr, dxpl_id, hdr->depth, &hdr->root, btree_size) < 0)
+ if(H5B2__node_size(hdr, dxpl_id, hdr->depth, &hdr->root, hdr, btree_size) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTLIST, FAIL, "node iteration failed")
} /* end if */
diff --git a/src/H5B2test.c b/src/H5B2test.c
index 24163e0..aec2aba 100644
--- a/src/H5B2test.c
+++ b/src/H5B2test.c
@@ -518,6 +518,7 @@ H5B2_get_node_info_test(H5B2_t *bt2, hid_t dxpl_id, void *udata,
{
H5B2_hdr_t *hdr; /* Pointer to the B-tree header */
H5B2_node_ptr_t curr_node_ptr; /* Node pointer info for current node */
+ void *parent = NULL; /* Parent of current node */
uint16_t depth; /* Current depth of the tree */
int cmp; /* Comparison value of records */
unsigned idx; /* Location of record which matches key */
@@ -537,6 +538,10 @@ H5B2_get_node_info_test(H5B2_t *bt2, hid_t dxpl_id, void *udata,
/* Make copy of the root node pointer to start search with */
curr_node_ptr = hdr->root;
+ /* Set initial parent, if doing swmr writes */
+ if(hdr->swmr_write)
+ parent = hdr;
+
/* Current depth of the tree */
depth = hdr->depth;
@@ -551,9 +556,16 @@ H5B2_get_node_info_test(H5B2_t *bt2, hid_t dxpl_id, void *udata,
H5B2_node_ptr_t next_node_ptr; /* Node pointer info for next node */
/* Lock B-tree current node */
- if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, curr_node_ptr.addr, curr_node_ptr.node_nrec, depth, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (internal = H5B2__protect_internal(hdr, dxpl_id, parent, &curr_node_ptr, depth, FALSE, H5AC__READ_ONLY_FLAG)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to load B-tree internal node")
+ /* Unpin parent if necessary */
+ if(parent) {
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ parent = NULL;
+ } /* end if */
+
/* Locate node pointer for child */
if(H5B2__locate_record(hdr->cls, internal->nrec, hdr->nat_off, internal->int_native,
udata, &idx, &cmp) < 0)
@@ -567,9 +579,13 @@ H5B2_get_node_info_test(H5B2_t *bt2, hid_t dxpl_id, void *udata,
next_node_ptr = internal->node_ptrs[idx];
/* Unlock current node */
- if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, H5AC__NO_FLAGS_SET) < 0)
+ if(H5AC_unprotect(hdr->f, dxpl_id, H5AC_BT2_INT, curr_node_ptr.addr, internal, (unsigned)(hdr->swmr_write ? H5AC__PIN_ENTRY_FLAG : H5AC__NO_FLAGS_SET)) < 0)
HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPROTECT, FAIL, "unable to release B-tree node")
+ /* Keep track of parent if necessary */
+ if(hdr->swmr_write)
+ parent = internal;
+
/* Set pointer to next node to load */
curr_node_ptr = next_node_ptr;
} /* end if */
@@ -594,9 +610,16 @@ H5B2_get_node_info_test(H5B2_t *bt2, hid_t dxpl_id, void *udata,
H5B2_leaf_t *leaf; /* Pointer to leaf node in B-tree */
/* Lock B-tree leaf node */
- if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, curr_node_ptr.addr, curr_node_ptr.node_nrec, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (leaf = H5B2__protect_leaf(hdr, dxpl_id, parent, &curr_node_ptr, FALSE, H5AC__READ_ONLY_FLAG)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTPROTECT, FAIL, "unable to protect B-tree leaf node")
+ /* Unpin parent if necessary */
+ if(parent) {
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HGOTO_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ parent = NULL;
+ } /* end if */
+
/* Locate record */
if(H5B2__locate_record(hdr->cls, leaf->nrec, hdr->nat_off, leaf->leaf_native,
udata, &idx, &cmp) < 0)
@@ -616,6 +639,12 @@ H5B2_get_node_info_test(H5B2_t *bt2, hid_t dxpl_id, void *udata,
ninfo->nrec = curr_node_ptr.node_nrec;
done:
+ if(parent) {
+ HDassert(ret_value < 0);
+ if(parent != hdr && H5AC_unpin_entry(parent) < 0)
+ HDONE_ERROR(H5E_BTREE, H5E_CANTUNPIN, FAIL, "unable to unpin parent entry")
+ } /* end if */
+
FUNC_LEAVE_NOAPI(ret_value)
} /* H5B2_get_node_info_test() */
diff --git a/src/H5Bdbg.c b/src/H5Bdbg.c
index d92a24b..b22264d 100644
--- a/src/H5Bdbg.c
+++ b/src/H5Bdbg.c
@@ -77,6 +77,9 @@ H5B_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent, int f
HDassert(fwidth >= 0);
HDassert(type);
+ /* Currently does not support SWMR access */
+ HDassert(!(H5F_INTENT(f) & H5F_ACC_SWMR_WRITE));
+
/* Get shared info for B-tree */
if(NULL == (rc_shared = (type->get_shared)(f, udata)))
HGOTO_ERROR(H5E_BTREE, H5E_CANTGET, FAIL, "can't retrieve B-tree's shared ref. count object")
diff --git a/src/H5C.c b/src/H5C.c
index e57dc81..9a60049 100644
--- a/src/H5C.c
+++ b/src/H5C.c
@@ -742,7 +742,39 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5C_evict
*
+ * Purpose: Evict all except pinned entries in the cache
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Vailin Choi
+ * Dec 2013
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_evict(H5F_t * f, hid_t dxpl_id)
+{
+ H5C_t *cache_ptr = f->shared->cache;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity check */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ /* Flush and invalidate all cache entries except the pinned entries */
+ if(H5C_flush_invalidate_cache(f, dxpl_id, H5C__EVICT_ALLOW_LAST_PINS_FLAG) < 0 )
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to evict entries in the cache")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_evict() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5C_expunge_entry
*
* Purpose: Use this function to tell the cache to expunge an entry
@@ -1543,6 +1575,85 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5C_mark_entry_clean
+ *
+ * Purpose: Mark a pinned entry as clean. The target entry MUST be pinned.
+ *
+ * If the entry is not
+ * already clean, the function places function marks the entry
+ * clean and removes it from the skip list.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * 7/23/16
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_mark_entry_clean(void *_thing)
+{
+ H5C_t * cache_ptr;
+ H5C_cache_entry_t * entry_ptr = (H5C_cache_entry_t *)_thing;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(entry_ptr);
+ HDassert(H5F_addr_defined(entry_ptr->addr));
+ cache_ptr = entry_ptr->cache_ptr;
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ /* Operate on pinned entry */
+ if(entry_ptr->is_protected)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKCLEAN, FAIL, "entry is protected")
+ else if(entry_ptr->is_pinned) {
+ hbool_t was_dirty; /* Whether the entry was previously dirty */
+
+ /* Remember previous dirty status */
+ was_dirty = entry_ptr->is_dirty;
+
+ /* Mark the entry as clean if it isn't already */
+ entry_ptr->is_dirty = FALSE;
+
+ /* Also reset the 'flush_marker' flag, since the entry shouldn't be flushed now */
+ entry_ptr->flush_marker = FALSE;
+
+ /* Modify cache data structures */
+ if(was_dirty)
+ H5C__UPDATE_INDEX_FOR_ENTRY_CLEAN(cache_ptr, entry_ptr)
+ if(entry_ptr->in_slist)
+ H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE)
+
+ /* Update stats for entry being marked clean */
+ H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr)
+
+ /* Check for entry changing status and do notifications, etc. */
+ if(was_dirty) {
+ /* If the entry's type has a 'notify' callback send a 'entry cleaned'
+ * notice now that the entry is fully integrated into the cache.
+ */
+ if(entry_ptr->type->notify &&
+ (entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_CLEANED, entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag cleared")
+
+ /* Propagate the clean up the flush dependency chain, if appropriate */
+ if(entry_ptr->flush_dep_nparents > 0)
+ if(H5C__mark_flush_dep_clean(entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKCLEAN, FAIL, "Can't propagate flush dep clean")
+ } /* end if */
+ } /* end if */
+ else
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKCLEAN, FAIL, "Entry is not pinned??")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_mark_entry_clean() */
+
+
+/*-------------------------------------------------------------------------
*
* Function: H5C_move_entry
*
@@ -1591,9 +1702,6 @@ H5C_move_entry(H5C_t * cache_ptr,
HDassert(entry_ptr->addr == old_addr);
HDassert(entry_ptr->type == type);
- if(entry_ptr->is_protected)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTMOVE, FAIL, "target entry is protected.")
-
H5C__SEARCH_INDEX(cache_ptr, new_addr, test_entry_ptr, FAIL)
if(test_entry_ptr != NULL) { /* we are hosed */
@@ -4955,17 +5063,19 @@ H5C_flush_invalidate_cache(const H5F_t * f, hid_t dxpl_id, unsigned flags)
} /* end while */
/* Invariants, after destroying all entries in the hash table */
- HDassert(cache_ptr->index_size == 0);
- HDassert(cache_ptr->clean_index_size == 0);
- HDassert(cache_ptr->dirty_index_size == 0);
- HDassert(cache_ptr->slist_len == 0);
- HDassert(cache_ptr->slist_size == 0);
- HDassert(cache_ptr->pel_len == 0);
- HDassert(cache_ptr->pel_size == 0);
- HDassert(cache_ptr->pl_len == 0);
- HDassert(cache_ptr->pl_size == 0);
- HDassert(cache_ptr->LRU_list_len == 0);
- HDassert(cache_ptr->LRU_list_size == 0);
+ if(!(flags & H5C__EVICT_ALLOW_LAST_PINS_FLAG)) {
+ HDassert(cache_ptr->index_size == 0);
+ HDassert(cache_ptr->clean_index_size == 0);
+ HDassert(cache_ptr->dirty_index_size == 0);
+ HDassert(cache_ptr->slist_len == 0);
+ HDassert(cache_ptr->slist_size == 0);
+ HDassert(cache_ptr->pel_len == 0);
+ HDassert(cache_ptr->pel_size == 0);
+ HDassert(cache_ptr->pl_len == 0);
+ HDassert(cache_ptr->pl_size == 0);
+ HDassert(cache_ptr->LRU_list_len == 0);
+ HDassert(cache_ptr->LRU_list_size == 0);
+ } /* end if */
done:
FUNC_LEAVE_NOAPI(ret_value)
@@ -5307,12 +5417,16 @@ H5C_flush_invalidate_ring(const H5F_t * f, hid_t dxpl_id, H5C_ring_t ring,
* into the cache, or moved to a new location
* in the file as a side effect of the flush.
*
- * If this happens, and one of the target
- * entries happens to be the next entry in
- * the hash bucket, we could find ourselves
- * either find ourselves either scanning a
- * non-existant entry, scanning through a
- * different bucket, or skipping an entry.
+ * It's also possible that removing a clean
+ * entry will remove the last child of a proxy
+ * entry, allowing it to be removed also and
+ * invalidating the next_entry_ptr.
+ *
+ * If either of these happen, and one of the target
+ * or proxy entries happens to be the next entry in
+ * the hash bucket, we could either find ourselves
+ * either scanning a non-existant entry, scanning
+ * through a different bucket, or skipping an entry.
*
* Neither of these are good, so restart the
* the scan at the head of the hash bucket
@@ -5889,6 +6003,11 @@ H5C__flush_single_entry(const H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_
H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr)
} /* end else if */
+ /* Note that the algorithm below is (very) similar to the set of operations
+ * in H5C_remove_entry() and should be kept in sync with changes
+ * to that code. - QAK, 2016/11/30
+ */
+
/* Update the cache internal data structures. */
if(destroy) {
/* Sanity checks */
@@ -7724,3 +7843,120 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__generate_image */
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C_remove_entry
+ *
+ * Purpose: Remove an entry from the cache. Must be not protected, pinned,
+ * dirty, involved in flush dependencies, etc.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * September 17, 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_remove_entry(void *_entry)
+{
+ H5C_cache_entry_t *entry = (H5C_cache_entry_t *)_entry; /* Entry to remove */
+ H5C_t *cache; /* Cache for file */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(entry);
+ HDassert(entry->ring != H5C_RING_UNDEFINED);
+ cache = entry->cache_ptr;
+ HDassert(cache);
+ HDassert(cache->magic == H5C__H5C_T_MAGIC);
+
+ /* Check for error conditions */
+ if(entry->is_dirty)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove dirty entry from cache")
+ if(entry->is_protected)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove protected entry from cache")
+ if(entry->is_pinned)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove pinned entry from cache")
+ if(entry->flush_dep_nparents > 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry with flush dependency parents from cache")
+ if(entry->flush_dep_nchildren > 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry with flush dependency children from cache")
+
+ /* Additional internal cache consistency checks */
+ HDassert(!entry->in_slist);
+ HDassert(!entry->flush_marker);
+ HDassert(!entry->flush_in_progress);
+
+ /* Note that the algorithm below is (very) similar to the set of operations
+ * in H5C__flush_single_entry() and should be kept in sync with changes
+ * to that code. - QAK, 2016/11/30
+ */
+
+ /* Update stats, as if we are "destroying" and taking ownership of the entry */
+ H5C__UPDATE_STATS_FOR_EVICTION(cache, entry, TRUE)
+
+ /* If the entry's type has a 'notify' callback, send a 'before eviction'
+ * notice while the entry is still fully integrated in the cache.
+ */
+ if(entry->type->notify && (entry->type->notify)(H5C_NOTIFY_ACTION_BEFORE_EVICT, entry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry to evict")
+
+ /* Update the cache internal data structures as appropriate for a destroy.
+ * Specifically:
+ * 1) Delete it from the index
+ * 2) Update the replacement policy for eviction
+ * 3) Remove it from the tag list for this object
+ */
+
+ H5C__DELETE_FROM_INDEX(cache, entry)
+
+ H5C__UPDATE_RP_FOR_EVICTION(cache, entry, FAIL)
+
+ /* Remove entry from tag list */
+ if(H5C__untag_entry(cache, entry) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry from tag list")
+
+ /* Increment entries_removed_counter and set last_entry_removed_ptr.
+ * As we me be about to free the entry, recall that last_entry_removed_ptr
+ * must NEVER be dereferenced.
+ *
+ * Recall that these fields are maintained to allow functions that perform
+ * scans of lists of entries to detect the unexpected removal of entries
+ * (via expunge, eviction, or take ownership at present), so that they can
+ * re-start their scans if necessary.
+ *
+ * Also check if the entry we are watching for removal is being
+ * removed (usually the 'next' entry for an iteration) and reset
+ * it to indicate that it was removed.
+ */
+ cache->entries_removed_counter++;
+ cache->last_entry_removed_ptr = entry;
+ if(entry == cache->entry_watched_for_removal)
+ cache->entry_watched_for_removal = NULL;
+
+ /* Internal cache data structures should now be up to date, and
+ * consistant with the status of the entry.
+ *
+ * Now clean up internal cache fields if appropriate.
+ */
+
+ /* Free the buffer for the on disk image */
+ if(entry->image_ptr != NULL)
+ entry->image_ptr = H5MM_xfree(entry->image_ptr);
+
+ /* Reset the pointer to the cache the entry is within */
+ entry->cache_ptr = NULL;
+
+ /* Client is taking ownership of the entry. Set bad magic here so the
+ * cache will choke unless the entry is re-inserted properly
+ */
+ entry->magic = H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__remove_entry() */
+
diff --git a/src/H5Cpkg.h b/src/H5Cpkg.h
index 16efb5c..fae4d74 100644
--- a/src/H5Cpkg.h
+++ b/src/H5Cpkg.h
@@ -1508,7 +1508,6 @@ if ( ( (cache_ptr)->index_size != \
HDassert( (cache_ptr) ); \
HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \
HDassert( (entry_ptr) ); \
- HDassert( !((entry_ptr)->is_protected) ); \
HDassert( !((entry_ptr)->is_read_only) ); \
HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
HDassert( (entry_ptr)->size > 0 ); \
@@ -1548,7 +1547,6 @@ if ( ( (cache_ptr)->index_size != \
HDassert( (cache_ptr) ); \
HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \
HDassert( (entry_ptr) ); \
- HDassert( !((entry_ptr)->is_protected) ); \
HDassert( !((entry_ptr)->is_read_only) ); \
HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
HDassert( (entry_ptr)->in_slist ); \
@@ -2398,12 +2396,11 @@ if ( ( (cache_ptr)->index_size != \
HDassert( (cache_ptr) ); \
HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \
HDassert( (entry_ptr) ); \
- HDassert( !((entry_ptr)->is_protected) ); \
HDassert( !((entry_ptr)->is_read_only) ); \
HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
HDassert( (entry_ptr)->size > 0 ); \
\
- if ( ! ( (entry_ptr)->is_pinned ) ) { \
+ if ( ! ( (entry_ptr)->is_pinned ) && ! ( (entry_ptr->is_protected ) ) ) { \
\
/* modified LRU specific code */ \
\
@@ -2476,12 +2473,11 @@ if ( ( (cache_ptr)->index_size != \
HDassert( (cache_ptr) ); \
HDassert( (cache_ptr)->magic == H5C__H5C_T_MAGIC ); \
HDassert( (entry_ptr) ); \
- HDassert( !((entry_ptr)->is_protected) ); \
HDassert( !((entry_ptr)->is_read_only) ); \
HDassert( ((entry_ptr)->ro_ref_count) == 0 ); \
HDassert( (entry_ptr)->size > 0 ); \
\
- if ( ! ( (entry_ptr)->is_pinned ) ) { \
+ if ( ! ( (entry_ptr)->is_pinned ) && ! ( (entry_ptr->is_protected ) ) ) { \
\
/* modified LRU specific code */ \
\
diff --git a/src/H5Cprivate.h b/src/H5Cprivate.h
index 923083f..baa80a2 100644
--- a/src/H5Cprivate.h
+++ b/src/H5Cprivate.h
@@ -41,7 +41,7 @@
/**************************/
/* Cache configuration settings */
-#define H5C__MAX_NUM_TYPE_IDS 28
+#define H5C__MAX_NUM_TYPE_IDS 29
#define H5C__PREFIX_LEN 32
/* This sanity checking constant was picked out of the air. Increase
@@ -169,6 +169,9 @@
* These flags apply to H5C_expunge_entry():
* H5C__FREE_FILE_SPACE_FLAG
*
+ * These flags apply to H5C_evict():
+ * H5C__EVICT_ALLOW_LAST_PINS_FLAG
+ *
* These flags apply to H5C_flush_cache():
* H5C__FLUSH_INVALIDATE_FLAG
* H5C__FLUSH_CLEAR_ONLY_FLAG
@@ -1716,6 +1719,7 @@ H5_DLL void H5C_def_auto_resize_rpt_fcn(H5C_t *cache_ptr, int32_t version,
size_t old_max_cache_size, size_t new_max_cache_size,
size_t old_min_clean_size, size_t new_min_clean_size);
H5_DLL herr_t H5C_dest(H5F_t *f, hid_t dxpl_id);
+H5_DLL herr_t H5C_evict(H5F_t *f, hid_t dxpl_id);
H5_DLL herr_t H5C_expunge_entry(H5F_t *f, hid_t dxpl_id,
const H5C_class_t *type, haddr_t addr, unsigned flags);
H5_DLL herr_t H5C_flush_cache(H5F_t *f, hid_t dxpl_id, unsigned flags);
@@ -1743,6 +1747,7 @@ H5_DLL FILE *H5C_get_trace_file_ptr_from_entry(const H5C_cache_entry_t *entry_pt
H5_DLL herr_t H5C_insert_entry(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type,
haddr_t addr, void *thing, unsigned int flags);
H5_DLL herr_t H5C_mark_entry_dirty(void *thing);
+H5_DLL herr_t H5C_mark_entry_clean(void *thing);
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);
@@ -1770,6 +1775,7 @@ H5_DLL hbool_t H5C_get_ignore_tags(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_remove_entry(void * thing);
#ifdef H5_HAVE_PARALLEL
H5_DLL herr_t H5C_apply_candidate_list(H5F_t *f, hid_t dxpl_id,
diff --git a/src/H5Ctag.c b/src/H5Ctag.c
index 6d5b454..6b47cc7 100644
--- a/src/H5Ctag.c
+++ b/src/H5Ctag.c
@@ -627,8 +627,10 @@ H5C_verify_tag(int id, haddr_t tag)
* constraints are met. */
if(tag == H5AC__IGNORE_TAG)
HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "cannot ignore a tag while doing verification.")
- else if(tag == H5AC__INVALID_TAG)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "no metadata tag provided")
+ else if(tag == H5AC__INVALID_TAG) {
+ if(id != H5AC_PROXY_ENTRY_ID)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "no metadata tag provided")
+ } /* end else-if */
else {
/* Perform some sanity checks on tag value. Certain entry
* types require certain tag values, so check that these
diff --git a/src/H5Dbtree.c b/src/H5Dbtree.c
index 85238da..8ef14b9 100644
--- a/src/H5Dbtree.c
+++ b/src/H5Dbtree.c
@@ -153,6 +153,7 @@ static herr_t H5D__btree_idx_dest(const H5D_chk_idx_info_t *idx_info);
/* v1 B-tree indexed chunk I/O ops */
const H5D_chunk_ops_t H5D_COPS_BTREE[1] = {{
+ FALSE, /* v1 B-tree indices does not support SWMR access */
H5D__btree_idx_init, /* insert */
H5D__btree_idx_create, /* create */
H5D__btree_idx_is_space_alloc, /* is_space_alloc */
diff --git a/src/H5Dbtree2.c b/src/H5Dbtree2.c
index 3b9b803..f687a5d 100644
--- a/src/H5Dbtree2.c
+++ b/src/H5Dbtree2.c
@@ -105,6 +105,7 @@ static herr_t H5D__bt2_filt_debug(FILE *stream, int indent, int fwidth,
/* Helper routine */
static herr_t H5D__bt2_idx_open(const H5D_chk_idx_info_t *idx_info);
+static herr_t H5D__btree2_idx_depend(const H5D_chk_idx_info_t *idx_info);
/* Callback for H5B2_iterate() which is called in H5D__bt2_idx_iterate() */
static int H5D__bt2_idx_iterate_cb(const void *_record, void *_udata);
@@ -152,6 +153,7 @@ static herr_t H5D__bt2_idx_dest(const H5D_chk_idx_info_t *idx_info);
/* Chunked dataset I/O ops for v2 B-tree indexing */
const H5D_chunk_ops_t H5D_COPS_BT2[1] = {{
+ TRUE, /* Fixed array indices support SWMR access */
H5D__bt2_idx_init, /* init */
H5D__bt2_idx_create, /* create */
H5D__bt2_idx_is_space_alloc, /* is_space_alloc */
@@ -623,6 +625,68 @@ H5D__bt2_idx_init(const H5D_chk_idx_info_t H5_ATTR_UNUSED *idx_info,
/*-------------------------------------------------------------------------
+ * Function: H5D__btree2_idx_depend
+ *
+ * Purpose: Create flush dependency between v2 B-tree and dataset's
+ * object header.
+ *
+ * Return: Success: non-negative
+ * Failure: negative
+ *
+ * Programmer: Quincey Koziol
+ * Friday, December 18, 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5D__btree2_idx_depend(const H5D_chk_idx_info_t *idx_info)
+{
+ H5O_t *oh = NULL; /* Object header */
+ H5O_loc_t oloc; /* Temporary object header location for dataset */
+ H5AC_proxy_entry_t *oh_proxy; /* Dataset's object header proxy */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Check args */
+ HDassert(idx_info);
+ HDassert(idx_info->f);
+ HDassert(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE);
+ HDassert(idx_info->pline);
+ HDassert(idx_info->layout);
+ HDassert(H5D_CHUNK_IDX_BT2 == idx_info->layout->idx_type);
+ HDassert(idx_info->storage);
+ HDassert(H5D_CHUNK_IDX_BT2 == idx_info->storage->idx_type);
+ HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
+ HDassert(idx_info->storage->u.btree2.bt2);
+
+ /* Set up object header location for dataset */
+ H5O_loc_reset(&oloc);
+ oloc.file = idx_info->f;
+ oloc.addr = idx_info->storage->u.btree.dset_ohdr_addr;
+
+ /* Get header */
+ if(NULL == (oh = H5O_protect(&oloc, idx_info->dxpl_id, H5AC__READ_ONLY_FLAG, TRUE)))
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTPROTECT, FAIL, "unable to protect object header")
+
+ /* Retrieve the dataset's object header proxy */
+ if(NULL == (oh_proxy = H5O_get_proxy(oh)))
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to get dataset object header proxy")
+
+ /* Make the v2 B-tree a child flush dependency of the dataset's object header proxy */
+ if(H5B2_depend(idx_info->storage->u.btree2.bt2, idx_info->dxpl_id, oh_proxy) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header proxy")
+
+done:
+ /* Release the object header from the cache */
+ if(oh && H5O_unprotect(&oloc, idx_info->dxpl_id, oh, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_DATASET, H5E_CANTUNPROTECT, FAIL, "unable to release object header")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5D__btree2_idx_depend() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5D__bt2_idx_open()
*
* Purpose: Opens an existing v2 B-tree.
@@ -667,6 +731,11 @@ H5D__bt2_idx_open(const H5D_chk_idx_info_t *idx_info)
if(NULL == (idx_info->storage->u.btree2.bt2 = H5B2_open(idx_info->f, idx_info->dxpl_id, idx_info->storage->idx_addr, &u_ctx)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "can't open v2 B-tree for tracking chunked dataset")
+ /* Check for SWMR writes to the file */
+ if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
+ if(H5D__btree2_idx_depend(idx_info) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header")
+
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__bt2_idx_open() */
@@ -738,6 +807,11 @@ H5D__bt2_idx_create(const H5D_chk_idx_info_t *idx_info)
if(H5B2_get_addr(idx_info->storage->u.btree2.bt2, &(idx_info->storage->idx_addr)) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get v2 B-tree address for tracking chunked dataset")
+ /* Check for SWMR writes to the file */
+ if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
+ if(H5D__btree2_idx_depend(idx_info) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header")
+
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__bt2_idx_create() */
@@ -1186,7 +1260,7 @@ H5D__bt2_idx_remove(const H5D_chk_idx_info_t *idx_info, H5D_chunk_common_ud_t *u
/* Remove the record for the "dataset chunk" object from the v2 B-tree */
/* (space in the file for the object is freed in the 'remove' callback) */
- if(H5B2_remove(bt2, idx_info->dxpl_id, &bt2_udata, H5D__bt2_remove_cb, &remove_udata) < 0)
+ if(H5B2_remove(bt2, idx_info->dxpl_id, &bt2_udata, (H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE) ? NULL : H5D__bt2_remove_cb, &remove_udata) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTREMOVE, FAIL, "can't remove object from B-tree")
done:
@@ -1243,8 +1317,11 @@ H5D__bt2_idx_delete(const H5D_chk_idx_info_t *idx_info)
remove_udata.f = idx_info->f;
remove_udata.dxpl_id = idx_info->dxpl_id;
- /* Set remove operation. */
- remove_op = H5D__bt2_remove_cb;
+ /* Set remove operation. Do not remove chunks in SWMR_WRITE mode */
+ if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
+ remove_op = NULL;
+ else
+ remove_op = H5D__bt2_remove_cb;
/* Delete the v2 B-tree */
/*(space in the file for each object is freed in the 'remove' callback) */
diff --git a/src/H5Dchunk.c b/src/H5Dchunk.c
index 7637339..7a646af 100644
--- a/src/H5Dchunk.c
+++ b/src/H5Dchunk.c
@@ -6292,8 +6292,13 @@ H5D__chunk_file_alloc(const H5D_chk_idx_info_t *idx_info, const H5F_block_t *old
/* Check for chunk being same size */
if(new_chunk->length != old_chunk->length) {
/* Release previous chunk */
- if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, old_chunk->offset, old_chunk->length) < 0)
- HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
+ /* Only free the old location if not doing SWMR writes - otherwise
+ * we must keep the old chunk around in case a reader has an
+ * outdated version of the B-tree node
+ */
+ if(!(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE))
+ if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, old_chunk->offset, old_chunk->length) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
alloc_chunk = TRUE;
} /* end if */
else {
diff --git a/src/H5Dearray.c b/src/H5Dearray.c
index 3f12e97..e9dbd0d 100644
--- a/src/H5Dearray.c
+++ b/src/H5Dearray.c
@@ -148,6 +148,7 @@ static herr_t H5D__earray_idx_dest(const H5D_chk_idx_info_t *idx_info);
/* Generic extensible array routines */
static herr_t H5D__earray_idx_open(const H5D_chk_idx_info_t *idx_info);
+static herr_t H5D__earray_idx_depend(const H5D_chk_idx_info_t *idx_info);
/*********************/
@@ -156,6 +157,7 @@ static herr_t H5D__earray_idx_open(const H5D_chk_idx_info_t *idx_info);
/* Extensible array indexed chunk I/O ops */
const H5D_chunk_ops_t H5D_COPS_EARRAY[1] = {{
+ TRUE, /* Extensible array indices support SWMR access */
H5D__earray_idx_init, /* init */
H5D__earray_idx_create, /* create */
H5D__earray_idx_is_space_alloc, /* is_space_alloc */
@@ -716,6 +718,68 @@ H5D__earray_dst_dbg_context(void *_dbg_ctx)
/*-------------------------------------------------------------------------
+ * Function: H5D__earray_idx_depend
+ *
+ * Purpose: Create flush dependency between extensible array and dataset's
+ * object header.
+ *
+ * Return: Success: non-negative
+ * Failure: negative
+ *
+ * Programmer: Quincey Koziol
+ * Tuesday, June 2, 2009
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5D__earray_idx_depend(const H5D_chk_idx_info_t *idx_info)
+{
+ H5O_t *oh = NULL; /* Object header */
+ H5O_loc_t oloc; /* Temporary object header location for dataset */
+ H5AC_proxy_entry_t *oh_proxy; /* Dataset's object header proxy */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Check args */
+ HDassert(idx_info);
+ HDassert(idx_info->f);
+ HDassert(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE);
+ HDassert(idx_info->pline);
+ HDassert(idx_info->layout);
+ HDassert(H5D_CHUNK_IDX_EARRAY == idx_info->layout->idx_type);
+ HDassert(idx_info->storage);
+ HDassert(H5D_CHUNK_IDX_EARRAY == idx_info->storage->idx_type);
+ HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
+ HDassert(idx_info->storage->u.earray.ea);
+
+ /* Set up object header location for dataset */
+ H5O_loc_reset(&oloc);
+ oloc.file = idx_info->f;
+ oloc.addr = idx_info->storage->u.earray.dset_ohdr_addr;
+
+ /* Get header */
+ if(NULL == (oh = H5O_protect(&oloc, idx_info->dxpl_id, H5AC__READ_ONLY_FLAG, TRUE)))
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTPROTECT, FAIL, "unable to protect object header")
+
+ /* Retrieve the dataset's object header proxy */
+ if(NULL == (oh_proxy = H5O_get_proxy(oh)))
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to get dataset object header proxy")
+
+ /* Make the extensible array a child flush dependency of the dataset's object header */
+ if(H5EA_depend(idx_info->storage->u.earray.ea, idx_info->dxpl_id, oh_proxy) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header proxy")
+
+done:
+ /* Release the object header from the cache */
+ if(oh && H5O_unprotect(&oloc, idx_info->dxpl_id, oh, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_DATASET, H5E_CANTUNPROTECT, FAIL, "unable to release object header")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5D__earray_idx_depend() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5D__earray_idx_open
*
* Purpose: Opens an existing extensible array.
@@ -760,6 +824,11 @@ H5D__earray_idx_open(const H5D_chk_idx_info_t *idx_info)
if(NULL == (idx_info->storage->u.earray.ea = H5EA_open(idx_info->f, idx_info->dxpl_id, idx_info->storage->idx_addr, &udata)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "can't open extensible array")
+ /* Check for SWMR writes to the file */
+ if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
+ if(H5D__earray_idx_depend(idx_info) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header")
+
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__earray_idx_open() */
@@ -910,6 +979,11 @@ H5D__earray_idx_create(const H5D_chk_idx_info_t *idx_info)
if(H5EA_get_addr(idx_info->storage->u.earray.ea, &(idx_info->storage->idx_addr)) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't query extensible array address")
+ /* Check for SWMR writes to the file */
+ if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
+ if(H5D__earray_idx_depend(idx_info) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header")
+
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__earray_idx_create() */
@@ -1355,11 +1429,13 @@ H5D__earray_idx_remove(const H5D_chk_idx_info_t *idx_info, H5D_chunk_common_ud_t
if(H5EA_get(ea, idx_info->dxpl_id, idx, &elmt) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get chunk info")
- /* Remove raw data chunk from file */
+ /* Remove raw data chunk from file if not doing SWMR writes */
HDassert(H5F_addr_defined(elmt.addr));
- H5_CHECK_OVERFLOW(elmt.nbytes, /*From: */uint32_t, /*To: */hsize_t);
- if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, elmt.addr, (hsize_t)elmt.nbytes) < 0)
- HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
+ if(!(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)) {
+ H5_CHECK_OVERFLOW(elmt.nbytes, /*From: */uint32_t, /*To: */hsize_t);
+ if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, elmt.addr, (hsize_t)elmt.nbytes) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
+ } /* end if */
/* Reset the info about the chunk for the index */
elmt.addr = HADDR_UNDEF;
@@ -1375,11 +1451,13 @@ H5D__earray_idx_remove(const H5D_chk_idx_info_t *idx_info, H5D_chunk_common_ud_t
if(H5EA_get(ea, idx_info->dxpl_id, idx, &addr) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get chunk address")
- /* Remove raw data chunk from file */
+ /* Remove raw data chunk from file if not doing SWMR writes */
HDassert(H5F_addr_defined(addr));
- H5_CHECK_OVERFLOW(idx_info->layout->size, /*From: */uint32_t, /*To: */hsize_t);
- if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, addr, (hsize_t)idx_info->layout->size) < 0)
- HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
+ if(!(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)) {
+ H5_CHECK_OVERFLOW(idx_info->layout->size, /*From: */uint32_t, /*To: */hsize_t);
+ if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, addr, (hsize_t)idx_info->layout->size) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
+ } /* end if */
/* Reset the address of the chunk for the index */
addr = HADDR_UNDEF;
diff --git a/src/H5Dfarray.c b/src/H5Dfarray.c
index 212f1f1..6b95e12 100644
--- a/src/H5Dfarray.c
+++ b/src/H5Dfarray.c
@@ -147,6 +147,7 @@ static herr_t H5D__farray_idx_dest(const H5D_chk_idx_info_t *idx_info);
/* Generic fixed array routines */
static herr_t H5D__farray_idx_open(const H5D_chk_idx_info_t *idx_info);
+static herr_t H5D__farray_idx_depend(const H5D_chk_idx_info_t *idx_info);
/*********************/
/* Package Variables */
@@ -154,6 +155,7 @@ static herr_t H5D__farray_idx_open(const H5D_chk_idx_info_t *idx_info);
/* Fixed array indexed chunk I/O ops */
const H5D_chunk_ops_t H5D_COPS_FARRAY[1] = {{
+ TRUE, /* Fixed array indices support SWMR access */
H5D__farray_idx_init, /* init */
H5D__farray_idx_create, /* create */
H5D__farray_idx_is_space_alloc, /* is_space_alloc */
@@ -716,6 +718,68 @@ H5D__farray_filt_debug(FILE *stream, int indent, int fwidth, hsize_t idx,
/*-------------------------------------------------------------------------
+ * Function: H5D__farray_idx_depend
+ *
+ * Purpose: Create flush dependency between fixed array and dataset's
+ * object header.
+ *
+ * Return: Success: non-negative
+ * Failure: negative
+ *
+ * Programmer: Vailin Choi
+ * Thursday, April 30, 2009
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5D__farray_idx_depend(const H5D_chk_idx_info_t *idx_info)
+{
+ H5O_t *oh = NULL; /* Object header */
+ H5O_loc_t oloc; /* Temporary object header location for dataset */
+ H5AC_proxy_entry_t *oh_proxy; /* Dataset's object header proxy */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Check args */
+ HDassert(idx_info);
+ HDassert(idx_info->f);
+ HDassert(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE);
+ HDassert(idx_info->pline);
+ HDassert(idx_info->layout);
+ HDassert(H5D_CHUNK_IDX_FARRAY == idx_info->layout->idx_type);
+ HDassert(idx_info->storage);
+ HDassert(H5D_CHUNK_IDX_FARRAY == idx_info->storage->idx_type);
+ HDassert(H5F_addr_defined(idx_info->storage->idx_addr));
+ HDassert(idx_info->storage->u.farray.fa);
+
+ /* Set up object header location for dataset */
+ H5O_loc_reset(&oloc);
+ oloc.file = idx_info->f;
+ oloc.addr = idx_info->storage->u.farray.dset_ohdr_addr;
+
+ /* Get header */
+ if(NULL == (oh = H5O_protect(&oloc, idx_info->dxpl_id, H5AC__READ_ONLY_FLAG, TRUE)))
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTPROTECT, FAIL, "unable to protect object header")
+
+ /* Retrieve the dataset's object header proxy */
+ if(NULL == (oh_proxy = H5O_get_proxy(oh)))
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to get dataset object header proxy")
+
+ /* Make the fixed array a child flush dependency of the dataset's object header proxy */
+ if(H5FA_depend(idx_info->storage->u.farray.fa, idx_info->dxpl_id, oh_proxy) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header proxy")
+
+done:
+ /* Release the object header from the cache */
+ if(oh && H5O_unprotect(&oloc, idx_info->dxpl_id, oh, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_DATASET, H5E_CANTUNPROTECT, FAIL, "unable to release object header")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5D__farray_idx_depend() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5D__farray_idx_init
*
* Purpose: Initialize the indexing information for a dataset.
@@ -784,6 +848,11 @@ H5D__farray_idx_open(const H5D_chk_idx_info_t *idx_info)
if(NULL == (idx_info->storage->u.farray.fa = H5FA_open(idx_info->f, idx_info->dxpl_id, idx_info->storage->idx_addr, &udata)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "can't open fixed array")
+ /* Check for SWMR writes to the file */
+ if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
+ if(H5D__farray_idx_depend(idx_info) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header")
+
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__farray_idx_open() */
@@ -860,6 +929,11 @@ H5D__farray_idx_create(const H5D_chk_idx_info_t *idx_info)
if(H5FA_get_addr(idx_info->storage->u.farray.fa, &(idx_info->storage->idx_addr)) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't query fixed array address")
+ /* Check for SWMR writes to the file */
+ if(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)
+ if(H5D__farray_idx_depend(idx_info) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTDEPEND, FAIL, "unable to create flush dependency on object header")
+
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__farray_idx_create() */
@@ -1224,11 +1298,13 @@ H5D__farray_idx_remove(const H5D_chk_idx_info_t *idx_info, H5D_chunk_common_ud_t
if(H5FA_get(fa, idx_info->dxpl_id, idx, &elmt) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get chunk info")
- /* Remove raw data chunk from file */
+ /* Remove raw data chunk from file if not doing SWMR writes */
HDassert(H5F_addr_defined(elmt.addr));
- H5_CHECK_OVERFLOW(elmt.nbytes, /*From: */uint32_t, /*To: */hsize_t);
- if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, elmt.addr, (hsize_t)elmt.nbytes) < 0)
- HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
+ if(!(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)) {
+ H5_CHECK_OVERFLOW(elmt.nbytes, /*From: */uint32_t, /*To: */hsize_t);
+ if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, elmt.addr, (hsize_t)elmt.nbytes) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
+ } /* end if */
/* Reset the info about the chunk for the index */
elmt.addr = HADDR_UNDEF;
@@ -1244,11 +1320,13 @@ H5D__farray_idx_remove(const H5D_chk_idx_info_t *idx_info, H5D_chunk_common_ud_t
if(H5FA_get(fa, idx_info->dxpl_id, idx, &addr) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get chunk address")
- /* Remove raw data chunk from file */
+ /* Remove raw data chunk from file if not doing SWMR writes */
HDassert(H5F_addr_defined(addr));
- H5_CHECK_OVERFLOW(idx_info->layout->size, /*From: */uint32_t, /*To: */hsize_t);
- if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, addr, (hsize_t)idx_info->layout->size) < 0)
- HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
+ if(!(H5F_INTENT(idx_info->f) & H5F_ACC_SWMR_WRITE)) {
+ H5_CHECK_OVERFLOW(idx_info->layout->size, /*From: */uint32_t, /*To: */hsize_t);
+ if(H5MF_xfree(idx_info->f, H5FD_MEM_DRAW, idx_info->dxpl_id, addr, (hsize_t)idx_info->layout->size) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTFREE, FAIL, "unable to free chunk")
+ } /* end if */
/* Reset the address of the chunk for the index */
addr = HADDR_UNDEF;
diff --git a/src/H5Dnone.c b/src/H5Dnone.c
index 677ca67..0cadac2 100644
--- a/src/H5Dnone.c
+++ b/src/H5Dnone.c
@@ -82,6 +82,7 @@ static herr_t H5D__none_idx_dump(const H5O_storage_chunk_t *storage, FILE *strea
/* Non Index chunk I/O ops */
const H5D_chunk_ops_t H5D_COPS_NONE[1] = {{
+ FALSE, /* Non-indexed chunking don't current support SWMR access */
NULL, /* init */
H5D__none_idx_create, /* create */
H5D__none_idx_is_space_alloc, /* is_space_alloc */
diff --git a/src/H5Dpkg.h b/src/H5Dpkg.h
index 12966f2..f54a9f2 100644
--- a/src/H5Dpkg.h
+++ b/src/H5Dpkg.h
@@ -325,6 +325,7 @@ typedef herr_t (*H5D_chunk_dest_func_t)(const H5D_chk_idx_info_t *idx_info);
/* Typedef for grouping chunk I/O routines */
typedef struct H5D_chunk_ops_t {
+ hbool_t can_swim; /* Flag to indicate that the index supports SWMR access */
H5D_chunk_init_func_t init; /* Routine to initialize indexing information in memory */
H5D_chunk_create_func_t create; /* Routine to create chunk index */
H5D_chunk_is_space_alloc_func_t is_space_alloc; /* Query routine to determine if storage/index is allocated */
diff --git a/src/H5Dsingle.c b/src/H5Dsingle.c
index e09a5cf..04b8971 100644
--- a/src/H5Dsingle.c
+++ b/src/H5Dsingle.c
@@ -84,6 +84,7 @@ static herr_t H5D__single_idx_dump(const H5O_storage_chunk_t *storage, FILE *str
/* Non Index chunk I/O ops */
const H5D_chunk_ops_t H5D_COPS_SINGLE[1] = {{
+ FALSE, /* Single Chunk indexing doesn't current support SWMR access */
H5D__single_idx_init, /* init */
H5D__single_idx_create, /* create */
H5D__single_idx_is_space_alloc, /* is_space_alloc */
diff --git a/src/H5Dvirtual.c b/src/H5Dvirtual.c
index 3780b88..c85b1e9 100644
--- a/src/H5Dvirtual.c
+++ b/src/H5Dvirtual.c
@@ -768,7 +768,7 @@ H5D__virtual_open_source_dset(const H5D_t *vdset,
/* Check if we need to open the source file */
if(HDstrcmp(source_dset->file_name, ".")) {
/* Open the source file */
- if(NULL == (src_file = H5F_open(source_dset->file_name, H5F_INTENT(vdset->oloc.file) & H5F_ACC_RDWR, H5P_FILE_CREATE_DEFAULT, vdset->shared->layout.storage.u.virt.source_fapl, dxpl_id)))
+ if(NULL == (src_file = H5F_open(source_dset->file_name, H5F_INTENT(vdset->oloc.file) & (H5F_ACC_RDWR | H5F_ACC_SWMR_WRITE | H5F_ACC_SWMR_READ), H5P_FILE_CREATE_DEFAULT, vdset->shared->layout.storage.u.virt.source_fapl, dxpl_id)))
H5E_clear_stack(NULL); /* Quick hack until proper support for H5Fopen with missing file is implemented */
else
src_file_open = TRUE;
diff --git a/src/H5EA.c b/src/H5EA.c
index 9c3f3e7..e35a4ed 100644
--- a/src/H5EA.c
+++ b/src/H5EA.c
@@ -795,7 +795,7 @@ END_FUNC(PRIV) /* end H5EA_get() */
*/
BEGIN_FUNC(PRIV, ERR,
herr_t, SUCCEED, FAIL,
-H5EA_depend(H5AC_info_t *parent_entry, H5EA_t *ea))
+H5EA_depend(H5EA_t *ea, hid_t dxpl_id, H5AC_proxy_entry_t *parent))
/* Local variables */
H5EA_hdr_t *hdr = ea->hdr; /* Header for EA */
@@ -805,13 +805,25 @@ H5EA_depend(H5AC_info_t *parent_entry, H5EA_t *ea))
*/
HDassert(ea);
HDassert(hdr);
+ HDassert(parent);
- /* Set the shared array header's file context for this operation */
- hdr->f = ea->f;
+ /*
+ * Check to see if a flush dependency between the extensible array
+ * and another data structure in the file has already been set up.
+ * If it hasn't, do so now.
+ */
+ if(NULL == hdr->parent) {
+ /* Sanity check */
+ HDassert(hdr->top_proxy);
- /* Set up flush dependency between parent entry and extensible array header */
- if(H5EA__create_flush_depend(parent_entry, (H5AC_info_t *)hdr) < 0)
- H5E_THROW(H5E_CANTDEPEND, "unable to create flush dependency on file metadata")
+ /* Set the shared array header's file context for this operation */
+ hdr->f = ea->f;
+
+ /* Add the extensible array as a child of the parent (proxy) */
+ if(H5AC_proxy_entry_add_child(parent, hdr->f, dxpl_id, hdr->top_proxy) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array as child of proxy")
+ hdr->parent = parent;
+ } /* end if */
CATCH
diff --git a/src/H5EAcache.c b/src/H5EAcache.c
index b7846e9..1119e39 100644
--- a/src/H5EAcache.c
+++ b/src/H5EAcache.c
@@ -80,6 +80,7 @@ static void *H5EA__cache_hdr_deserialize(const void *image, size_t len,
static herr_t H5EA__cache_hdr_image_len(const void *thing, size_t *image_len);
static herr_t H5EA__cache_hdr_serialize(const H5F_t *f, void *image, size_t len,
void *thing);
+static herr_t H5EA__cache_hdr_notify(H5AC_notify_action_t action, void *thing);
static herr_t H5EA__cache_hdr_free_icr(void *thing);
static herr_t H5EA__cache_iblock_get_initial_load_size(void *udata, size_t *image_len);
@@ -142,7 +143,7 @@ const H5AC_class_t H5AC_EARRAY_HDR[1] = {{
H5EA__cache_hdr_image_len, /* 'image_len' callback */
NULL, /* 'pre_serialize' callback */
H5EA__cache_hdr_serialize, /* 'serialize' callback */
- NULL, /* 'notify' callback */
+ H5EA__cache_hdr_notify, /* 'notify' callback */
H5EA__cache_hdr_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
}};
@@ -521,6 +522,80 @@ END_FUNC(STATIC) /* end H5EA__cache_hdr_serialize() */
/*-------------------------------------------------------------------------
+ * Function: H5EA__cache_hdr_notify
+ *
+ * Purpose: Handle cache action notifications
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 11/30/15
+ *
+ *-------------------------------------------------------------------------
+ */
+BEGIN_FUNC(STATIC, ERR,
+herr_t, SUCCEED, FAIL,
+H5EA__cache_hdr_notify(H5AC_notify_action_t action, void *_thing))
+
+ /* Local variables */
+ H5EA_hdr_t *hdr = (H5EA_hdr_t *)_thing; /* Pointer to the object */
+
+ /* Sanity check */
+ HDassert(hdr);
+
+ /* Check if the file was opened with SWMR-write access */
+ if(hdr->swmr_write) {
+ /* Determine which action to take */
+ switch(action) {
+ case H5AC_NOTIFY_ACTION_AFTER_INSERT:
+ case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
+ case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
+ case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
+ case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
+ case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
+ /* do nothing */
+ break;
+
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ /* If hdr->parent != NULL, hdr->parent is used to destroy
+ * the flush dependency before the header is evicted.
+ */
+ if(hdr->parent) {
+ /* Sanity check */
+ HDassert(hdr->top_proxy);
+
+ /* Destroy flush dependency on object header proxy */
+ if(H5AC_proxy_entry_remove_child((H5AC_proxy_entry_t *)hdr->parent, (void *)hdr->top_proxy) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between extensible array and proxy")
+ hdr->parent = NULL;
+ } /* end if */
+
+ /* Detach from 'top' proxy for extensible array */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(hdr->top_proxy, hdr) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between header and extensible array 'top' proxy")
+ /* Don't reset hdr->top_proxy here, it's destroyed when the header is freed -QAK */
+ } /* end if */
+ break;
+
+ default:
+#ifdef NDEBUG
+ H5E_THROW(H5E_BADVALUE, "unknown action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Unknown action?!?");
+#endif /* NDEBUG */
+ } /* end switch */
+ } /* end if */
+ else
+ HDassert(NULL == hdr->parent);
+
+CATCH
+
+END_FUNC(STATIC) /* end H5EA__cache_hdr_notify() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5EA__cache_hdr_free_icr
*
* Purpose: Destroy/release an "in core representation" of a data
@@ -890,6 +965,13 @@ H5EA__cache_iblock_notify(H5AC_notify_action_t action, void *_thing))
/* Destroy flush dependency on extensible array header */
if(H5EA__destroy_flush_depend((H5AC_info_t *)iblock->hdr, (H5AC_info_t *)iblock) < 0)
H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between index block and header, address = %llu", (unsigned long long)iblock->addr)
+
+ /* Detach from 'top' proxy for extensible array */
+ if(iblock->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(iblock->top_proxy, iblock) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between index block and extensible array 'top' proxy")
+ iblock->top_proxy = NULL;
+ } /* end if */
break;
default:
@@ -1286,6 +1368,13 @@ H5EA__cache_sblock_notify(H5AC_notify_action_t action, void *_thing))
H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between super block and header, address = %llu", (unsigned long long)sblock->addr)
sblock->has_hdr_depend = FALSE;
} /* end if */
+
+ /* Detach from 'top' proxy for extensible array */
+ if(sblock->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(sblock->top_proxy, sblock) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between super block and extensible array 'top' proxy")
+ sblock->top_proxy = NULL;
+ } /* end if */
break;
case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
@@ -1690,6 +1779,13 @@ H5EA__cache_dblock_notify(H5AC_notify_action_t action, void *_thing))
H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between data block and header, address = %llu", (unsigned long long)dblock->addr)
dblock->has_hdr_depend = FALSE;
} /* end if */
+
+ /* Detach from 'top' proxy for extensible array */
+ if(dblock->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(dblock->top_proxy, dblock) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between data block and extensible array 'top' proxy")
+ dblock->top_proxy = NULL;
+ } /* end if */
break;
case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
@@ -2062,6 +2158,13 @@ H5EA__cache_dblk_page_notify(H5AC_notify_action_t action, void *_thing))
H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between data block page and header, address = %llu", (unsigned long long)dblk_page->addr)
dblk_page->has_hdr_depend = FALSE;
} /* end if */
+
+ /* Detach from 'top' proxy for extensible array */
+ if(dblk_page->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(dblk_page->top_proxy, dblk_page) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between data block page and extensible array 'top' proxy")
+ dblk_page->top_proxy = NULL;
+ } /* end if */
break;
case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
diff --git a/src/H5EAdblkpage.c b/src/H5EAdblkpage.c
index b903a8a..927dcb6 100644
--- a/src/H5EAdblkpage.c
+++ b/src/H5EAdblkpage.c
@@ -155,6 +155,7 @@ H5EA__dblk_page_create(H5EA_hdr_t *hdr, hid_t dxpl_id, H5EA_sblock_t *parent,
/* Local variables */
H5EA_dblk_page_t *dblk_page = NULL; /* Extensible array data block page */
+ hbool_t inserted = FALSE; /* Whether the header was inserted into cache */
/* Sanity check */
HDassert(hdr);
@@ -174,10 +175,23 @@ H5EA__dblk_page_create(H5EA_hdr_t *hdr, hid_t dxpl_id, H5EA_sblock_t *parent,
/* Cache the new extensible array data block page */
if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_EARRAY_DBLK_PAGE, dblk_page->addr, dblk_page, H5AC__NO_FLAGS_SET) < 0)
H5E_THROW(H5E_CANTINSERT, "can't add extensible array data block page to cache")
+ inserted = TRUE;
+
+ /* Add data block page as child of 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, dblk_page) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
+ dblk_page->top_proxy = hdr->top_proxy;
+ } /* end if */
CATCH
if(ret_value < 0)
if(dblk_page) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(dblk_page) < 0)
+ H5E_THROW(H5E_CANTREMOVE, "unable to remove extensible array data block page from cache")
+
/* Destroy data block page */
if(H5EA__dblk_page_dest(dblk_page) < 0)
H5E_THROW(H5E_CANTFREE, "unable to destroy extensible array data block page")
@@ -206,6 +220,7 @@ H5EA__dblk_page_protect(H5EA_hdr_t *hdr, hid_t dxpl_id, H5EA_sblock_t *parent,
haddr_t dblk_page_addr, unsigned flags))
/* Local variables */
+ H5EA_dblk_page_t *dblk_page = NULL; /* Extensible array data block page */
H5EA_dblk_page_cache_ud_t udata; /* Information needed for loading data block page */
/* Sanity check */
@@ -221,10 +236,27 @@ H5EA__dblk_page_protect(H5EA_hdr_t *hdr, hid_t dxpl_id, H5EA_sblock_t *parent,
udata.dblk_page_addr = dblk_page_addr;
/* Protect the data block page */
- if(NULL == (ret_value = (H5EA_dblk_page_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_EARRAY_DBLK_PAGE, dblk_page_addr, &udata, flags)))
+ if(NULL == (dblk_page = (H5EA_dblk_page_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_EARRAY_DBLK_PAGE, dblk_page_addr, &udata, flags)))
H5E_THROW(H5E_CANTPROTECT, "unable to protect extensible array data block page, address = %llu", (unsigned long long)dblk_page_addr)
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->top_proxy && NULL == dblk_page->top_proxy) {
+ /* Add data block page as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, dblk_page) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
+ dblk_page->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+ /* Set return value */
+ ret_value = dblk_page;
+
CATCH
+ /* Clean up on error */
+ if(!ret_value) {
+ /* Release the data block page, if it was protected */
+ if(dblk_page && H5AC_unprotect(hdr->f, dxpl_id, H5AC_EARRAY_DBLK_PAGE, dblk_page->addr, dblk_page, H5AC__NO_FLAGS_SET) < 0)
+ H5E_THROW(H5E_CANTUNPROTECT, "unable to unprotect extensible array data block page, address = %llu", (unsigned long long)dblk_page->addr)
+ } /* end if */
END_FUNC(PKG) /* end H5EA__dblk_page_protect() */
@@ -299,6 +331,9 @@ H5EA__dblk_page_dest(H5EA_dblk_page_t *dblk_page))
dblk_page->hdr = NULL;
} /* end if */
+ /* Sanity check */
+ HDassert(NULL == dblk_page->top_proxy);
+
/* Free the data block page itself */
dblk_page = H5FL_FREE(H5EA_dblk_page_t, dblk_page);
diff --git a/src/H5EAdblock.c b/src/H5EAdblock.c
index 9511a9d..c288c69 100644
--- a/src/H5EAdblock.c
+++ b/src/H5EAdblock.c
@@ -168,6 +168,7 @@ H5EA__dblock_create(H5EA_hdr_t *hdr, hid_t dxpl_id, void *parent,
/* Local variables */
H5EA_dblock_t *dblock = NULL; /* Extensible array data block */
haddr_t dblock_addr; /* Extensible array data block address */
+ hbool_t inserted = FALSE; /* Whether the header was inserted into cache */
/* Sanity check */
HDassert(hdr);
@@ -198,6 +199,14 @@ H5EA__dblock_create(H5EA_hdr_t *hdr, hid_t dxpl_id, void *parent,
/* Cache the new extensible array data block */
if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_EARRAY_DBLOCK, dblock_addr, dblock, H5AC__NO_FLAGS_SET) < 0)
H5E_THROW(H5E_CANTINSERT, "can't add extensible array data block to cache")
+ inserted = TRUE;
+
+ /* Add data block as child of 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, dblock) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
+ dblock->top_proxy = hdr->top_proxy;
+ } /* end if */
/* Update extensible array data block statistics */
hdr->stats.stored.ndata_blks++;
@@ -215,6 +224,11 @@ H5EA__dblock_create(H5EA_hdr_t *hdr, hid_t dxpl_id, void *parent,
CATCH
if(!H5F_addr_defined(ret_value))
if(dblock) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(dblock) < 0)
+ H5E_THROW(H5E_CANTREMOVE, "unable to remove extensible array data block from cache")
+
/* Release data block's disk space */
if(H5F_addr_defined(dblock->addr) && H5MF_xfree(hdr->f, H5FD_MEM_EARRAY_DBLOCK, dxpl_id, dblock->addr, (hsize_t)dblock->size) < 0)
H5E_THROW(H5E_CANTFREE, "unable to release extensible array data block")
@@ -284,7 +298,8 @@ H5EA__dblock_protect(H5EA_hdr_t *hdr, hid_t dxpl_id, void *parent,
haddr_t dblk_addr, size_t dblk_nelmts, unsigned flags))
/* Local variables */
- H5EA_dblock_cache_ud_t udata; /* Information needed for loading data block */
+ H5EA_dblock_t *dblock; /* Extensible array data block */
+ H5EA_dblock_cache_ud_t udata; /* Information needed for loading data block */
/* Sanity check */
HDassert(hdr);
@@ -301,11 +316,29 @@ H5EA__dblock_protect(H5EA_hdr_t *hdr, hid_t dxpl_id, void *parent,
udata.dblk_addr = dblk_addr;
/* Protect the data block */
- if(NULL == (ret_value = (H5EA_dblock_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_EARRAY_DBLOCK, dblk_addr, &udata, flags)))
+ if(NULL == (dblock = (H5EA_dblock_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_EARRAY_DBLOCK, dblk_addr, &udata, flags)))
H5E_THROW(H5E_CANTPROTECT, "unable to protect extensible array data block, address = %llu", (unsigned long long)dblk_addr)
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->top_proxy && NULL == dblock->top_proxy) {
+ /* Add data block as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, dblock) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
+ dblock->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+ /* Set return value */
+ ret_value = dblock;
+
CATCH
+ /* Clean up on error */
+ if(!ret_value) {
+ /* Release the data block, if it was protected */
+ if(dblock && H5AC_unprotect(hdr->f, dxpl_id, H5AC_EARRAY_DBLOCK, dblock->addr, dblock, H5AC__NO_FLAGS_SET) < 0)
+ H5E_THROW(H5E_CANTUNPROTECT, "unable to unprotect extensible array data block, address = %llu", (unsigned long long)dblock->addr)
+ } /* end if */
+
END_FUNC(PKG) /* end H5EA__dblock_protect() */
@@ -442,6 +475,9 @@ H5EA__dblock_dest(H5EA_dblock_t *dblock))
dblock->hdr = NULL;
} /* end if */
+ /* Sanity check */
+ HDassert(NULL == dblock->top_proxy);
+
/* Free the data block itself */
dblock = H5FL_FREE(H5EA_dblock_t, dblock);
diff --git a/src/H5EAhdr.c b/src/H5EAhdr.c
index d8be3cb..76d8733 100644
--- a/src/H5EAhdr.c
+++ b/src/H5EAhdr.c
@@ -135,6 +135,7 @@ H5EA__hdr_alloc(H5F_t *f))
/* Set the internal parameters for the array */
hdr->f = f;
+ hdr->swmr_write = (H5F_INTENT(f) & H5F_ACC_SWMR_WRITE) > 0;
hdr->sizeof_addr = H5F_SIZEOF_ADDR(f);
hdr->sizeof_size = H5F_SIZEOF_SIZE(f);
@@ -361,6 +362,7 @@ H5EA__hdr_create(H5F_t *f, hid_t dxpl_id, const H5EA_create_t *cparam,
/* Local variables */
H5EA_hdr_t *hdr = NULL; /* Extensible array header */
+ hbool_t inserted = FALSE; /* Whether the header was inserted into cache */
/* Check arguments */
HDassert(f);
@@ -418,9 +420,20 @@ H5EA__hdr_create(H5F_t *f, hid_t dxpl_id, const H5EA_create_t *cparam,
if(HADDR_UNDEF == (hdr->addr = H5MF_alloc(f, H5FD_MEM_EARRAY_HDR, dxpl_id, (hsize_t)hdr->size)))
H5E_THROW(H5E_CANTALLOC, "file allocation failed for extensible array header")
+ /* Create 'top' proxy for extensible array entries */
+ if(hdr->swmr_write)
+ if(NULL == (hdr->top_proxy = H5AC_proxy_entry_create()))
+ H5E_THROW(H5E_CANTCREATE, "can't create extensible array entry proxy")
+
/* Cache the new extensible array header */
if(H5AC_insert_entry(f, dxpl_id, H5AC_EARRAY_HDR, hdr->addr, hdr, H5AC__NO_FLAGS_SET) < 0)
H5E_THROW(H5E_CANTINSERT, "can't add extensible array header to cache")
+ inserted = TRUE;
+
+ /* Add header as child of 'top' proxy */
+ if(hdr->top_proxy)
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, f, dxpl_id, hdr) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
/* Set address of array header to return */
ret_value = hdr->addr;
@@ -428,6 +441,11 @@ H5EA__hdr_create(H5F_t *f, hid_t dxpl_id, const H5EA_create_t *cparam,
CATCH
if(!H5F_addr_defined(ret_value))
if(hdr) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(hdr) < 0)
+ H5E_THROW(H5E_CANTREMOVE, "unable to remove extensible array header from cache")
+
/* Release header's disk space */
if(H5F_addr_defined(hdr->addr) && H5MF_xfree(f, H5FD_MEM_EARRAY_HDR, dxpl_id, hdr->addr, (hsize_t)hdr->size) < 0)
H5E_THROW(H5E_CANTFREE, "unable to free extensible array header")
@@ -614,6 +632,7 @@ H5EA__hdr_protect(H5F_t *f, hid_t dxpl_id, haddr_t ea_addr, void *ctx_udata,
unsigned flags))
/* Local variables */
+ H5EA_hdr_t *hdr; /* Extensible array header */
H5EA_hdr_cache_ud_t udata; /* User data for cache callbacks */
/* Sanity check */
@@ -629,9 +648,23 @@ H5EA__hdr_protect(H5F_t *f, hid_t dxpl_id, haddr_t ea_addr, void *ctx_udata,
udata.ctx_udata = ctx_udata;
/* Protect the header */
- if(NULL == (ret_value = (H5EA_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_EARRAY_HDR, ea_addr, &udata, flags)))
+ if(NULL == (hdr = (H5EA_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_EARRAY_HDR, ea_addr, &udata, flags)))
H5E_THROW(H5E_CANTPROTECT, "unable to protect extensible array header, address = %llu", (unsigned long long)ea_addr)
- ret_value->f = f; /* (Must be set again here, in case the header was already in the cache -QAK) */
+ hdr->f = f; /* (Must be set again here, in case the header was already in the cache -QAK) */
+
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->swmr_write && NULL == hdr->top_proxy) {
+ /* Create 'top' proxy for extensible array entries */
+ if(NULL == (hdr->top_proxy = H5AC_proxy_entry_create()))
+ H5E_THROW(H5E_CANTCREATE, "can't create extensible array entry proxy")
+
+ /* Add header as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, f, dxpl_id, hdr) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
+ } /* end if */
+
+ /* Set return value */
+ ret_value = hdr;
CATCH
@@ -779,6 +812,13 @@ H5EA__hdr_dest(H5EA_hdr_t *hdr))
if(hdr->sblk_info)
hdr->sblk_info = (H5EA_sblk_info_t *)H5FL_SEQ_FREE(H5EA_sblk_info_t, hdr->sblk_info);
+ /* Destroy the 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_dest(hdr->top_proxy) < 0)
+ H5E_THROW(H5E_CANTRELEASE, "unable to destroy extensible array 'top' proxy")
+ hdr->top_proxy = NULL;
+ } /* end if */
+
/* Free the shared info itself */
hdr = H5FL_FREE(H5EA_hdr_t, hdr);
diff --git a/src/H5EAiblock.c b/src/H5EAiblock.c
index 48b52a4..a3723c5 100644
--- a/src/H5EAiblock.c
+++ b/src/H5EAiblock.c
@@ -183,6 +183,7 @@ H5EA__iblock_create(H5EA_hdr_t *hdr, hid_t dxpl_id, hbool_t *stats_changed))
/* Local variables */
H5EA_iblock_t *iblock = NULL; /* Extensible array index block */
haddr_t iblock_addr; /* Extensible array index block address */
+ hbool_t inserted = FALSE; /* Whether the header was inserted into cache */
#ifdef QAK
HDfprintf(stderr, "%s: Called\n", FUNC);
@@ -233,6 +234,14 @@ HDfprintf(stderr, "%s: iblock->size = %Zu\n", FUNC, iblock->size);
/* Cache the new extensible array index block */
if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_EARRAY_IBLOCK, iblock_addr, iblock, H5AC__NO_FLAGS_SET) < 0)
H5E_THROW(H5E_CANTINSERT, "can't add extensible array index block to cache")
+ inserted = TRUE;
+
+ /* Add index block as child of 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, iblock) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
+ iblock->top_proxy = hdr->top_proxy;
+ } /* end if */
/* Update extensible array index block statistics */
HDassert(0 == hdr->stats.computed.nindex_blks);
@@ -252,6 +261,11 @@ HDfprintf(stderr, "%s: iblock->size = %Zu\n", FUNC, iblock->size);
CATCH
if(!H5F_addr_defined(ret_value))
if(iblock) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(iblock) < 0)
+ H5E_THROW(H5E_CANTREMOVE, "unable to remove extensible array index block from cache")
+
/* Release index block's disk space */
if(H5F_addr_defined(iblock->addr) && H5MF_xfree(hdr->f, H5FD_MEM_EARRAY_IBLOCK, dxpl_id, iblock->addr, (hsize_t)iblock->size) < 0)
H5E_THROW(H5E_CANTFREE, "unable to release file space for extensible array index block")
@@ -281,6 +295,9 @@ BEGIN_FUNC(PKG, ERR,
H5EA_iblock_t *, NULL, NULL,
H5EA__iblock_protect(H5EA_hdr_t *hdr, hid_t dxpl_id, unsigned flags))
+ /* Local variables */
+ H5EA_iblock_t *iblock = NULL; /* Pointer to index block */
+
#ifdef QAK
HDfprintf(stderr, "%s: Called\n", FUNC);
#endif /* QAK */
@@ -292,10 +309,27 @@ HDfprintf(stderr, "%s: Called\n", FUNC);
HDassert((flags & (unsigned)(~H5AC__READ_ONLY_FLAG)) == 0);
/* Protect the index block */
- if(NULL == (ret_value = (H5EA_iblock_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_EARRAY_IBLOCK, hdr->idx_blk_addr, hdr, flags)))
+ if(NULL == (iblock = (H5EA_iblock_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_EARRAY_IBLOCK, hdr->idx_blk_addr, hdr, flags)))
H5E_THROW(H5E_CANTPROTECT, "unable to protect extensible array index block, address = %llu", (unsigned long long)hdr->idx_blk_addr)
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->top_proxy && NULL == iblock->top_proxy) {
+ /* Add index block as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, iblock) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
+ iblock->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+ /* Set return value */
+ ret_value = iblock;
+
CATCH
+ /* Clean up on error */
+ if(!ret_value) {
+ /* Release the index block, if it was protected */
+ if(iblock && H5AC_unprotect(hdr->f, dxpl_id, H5AC_EARRAY_IBLOCK, iblock->addr, iblock, H5AC__NO_FLAGS_SET) < 0)
+ H5E_THROW(H5E_CANTUNPROTECT, "unable to unprotect extensible array index block, address = %llu", (unsigned long long)iblock->addr)
+ } /* end if */
END_FUNC(PKG) /* end H5EA__iblock_protect() */
@@ -470,6 +504,9 @@ H5EA__iblock_dest(H5EA_iblock_t *iblock))
iblock->hdr = NULL;
} /* end if */
+ /* Sanity check */
+ HDassert(NULL == iblock->top_proxy);
+
/* Free the index block itself */
iblock = H5FL_FREE(H5EA_iblock_t, iblock);
diff --git a/src/H5EApkg.h b/src/H5EApkg.h
index 70e6a55..093403c 100644
--- a/src/H5EApkg.h
+++ b/src/H5EApkg.h
@@ -206,6 +206,28 @@ typedef struct H5EA_hdr_t {
/* Client information (not stored) */
void *cb_ctx; /* Callback context */
+
+ /* SWMR / Flush dependency information (not stored) */
+ hbool_t swmr_write; /* Flag indicating the file is opened with SWMR-write access */
+ H5AC_proxy_entry_t *top_proxy; /* 'Top' proxy cache entry for all array entries */
+ void *parent; /* Pointer to 'top' proxy flush dependency
+ * parent, if it exists, otherwise NULL.
+ * If the extensible array is being used
+ * to index a chunked dataset and the
+ * dataset metadata is modified by a
+ * SWMR writer, this field will be set
+ * equal to the object header proxy
+ * that is the flush dependency parent
+ * of the extensible array header.
+ *
+ * The field is used to avoid duplicate
+ * setups of the flush dependency
+ * relationship, and to allow the
+ * extensible array header to destroy
+ * the flush dependency on receipt of
+ * an eviction notification from the
+ * metadata cache.
+ */
} H5EA_hdr_t;
/* The extensible array index block information */
@@ -223,6 +245,9 @@ typedef struct H5EA_iblock_t {
haddr_t addr; /* Address of this index block on disk */
size_t size; /* Size of index block on disk */
+ /* SWMR / Flush dependency information (not stored) */
+ H5AC_proxy_entry_t *top_proxy; /* "Top" proxy cache entry for all array entries */
+
/* Computed/cached values (not stored) */
size_t nsblks; /* # of super blocks whose data block addresses are in index block */
size_t ndblk_addrs; /* Number of pointers to data blocks in index block */
@@ -244,8 +269,9 @@ typedef struct H5EA_sblock_t {
haddr_t addr; /* Address of this index block on disk */
size_t size; /* Size of index block on disk */
- /* Flush dependency information (not stored) */
+ /* SWMR / Flush dependency information (not stored) */
hbool_t has_hdr_depend; /* Whether this object has a flush dependency on the header */
+ H5AC_proxy_entry_t *top_proxy; /* "Top" proxy cache entry for all array entries */
H5EA_iblock_t *parent; /* Parent object for super block (index block) */
/* Computed/cached values (not stored) */
@@ -271,8 +297,9 @@ typedef struct H5EA_dblock_t {
haddr_t addr; /* Address of this data block on disk */
size_t size; /* Size of data block on disk */
- /* Flush dependency information (not stored) */
+ /* SWMR / Flush dependency information (not stored) */
hbool_t has_hdr_depend; /* Whether this object has a flush dependency on the header */
+ H5AC_proxy_entry_t *top_proxy; /* 'Top' proxy cache entry for all array entries */
void *parent; /* Parent object for data block (index or super block) */
/* Computed/cached values (not stored) */
@@ -293,8 +320,9 @@ typedef struct H5EA_dbk_page_t {
haddr_t addr; /* Address of this data block page on disk */
size_t size; /* Size of data block page on disk */
- /* Flush dependency information (not stored) */
+ /* SWMR / Flush dependency information (not stored) */
hbool_t has_hdr_depend; /* Whether this object has a flush dependency on the header */
+ H5AC_proxy_entry_t *top_proxy; /* "Top" proxy cache entry for all array entries */
H5EA_sblock_t *parent; /* Parent object for data block page (super block) */
/* Computed/cached values (not stored) */
diff --git a/src/H5EAprivate.h b/src/H5EAprivate.h
index 0a1b945..66d88e0 100644
--- a/src/H5EAprivate.h
+++ b/src/H5EAprivate.h
@@ -142,7 +142,7 @@ H5_DLL herr_t H5EA_get_nelmts(const H5EA_t *ea, hsize_t *nelmts);
H5_DLL herr_t H5EA_get_addr(const H5EA_t *ea, haddr_t *addr);
H5_DLL herr_t H5EA_set(const H5EA_t *ea, hid_t dxpl_id, hsize_t idx, const void *elmt);
H5_DLL herr_t H5EA_get(const H5EA_t *ea, hid_t dxpl_id, hsize_t idx, void *elmt);
-H5_DLL herr_t H5EA_depend(H5AC_info_t *parent_entry, H5EA_t *ea);
+H5_DLL herr_t H5EA_depend(H5EA_t *ea, hid_t dxpl_id, H5AC_proxy_entry_t *parent);
H5_DLL herr_t H5EA_iterate(H5EA_t *fa, hid_t dxpl_id, H5EA_operator_t op, void *udata);
H5_DLL herr_t H5EA_close(H5EA_t *ea, hid_t dxpl_id);
H5_DLL herr_t H5EA_delete(H5F_t *f, hid_t dxpl_id, haddr_t ea_addr, void *ctx_udata);
diff --git a/src/H5EAsblock.c b/src/H5EAsblock.c
index 4f153cd..2fa87e0 100644
--- a/src/H5EAsblock.c
+++ b/src/H5EAsblock.c
@@ -195,6 +195,7 @@ H5EA__sblock_create(H5EA_hdr_t *hdr, hid_t dxpl_id, H5EA_iblock_t *parent,
H5EA_sblock_t *sblock = NULL; /* Extensible array super block */
haddr_t sblock_addr; /* Extensible array super block address */
haddr_t tmp_addr = HADDR_UNDEF; /* Address value to fill data block addresses with */
+ hbool_t inserted = FALSE; /* Whether the header was inserted into cache */
/* Sanity check */
HDassert(hdr);
@@ -221,6 +222,14 @@ H5EA__sblock_create(H5EA_hdr_t *hdr, hid_t dxpl_id, H5EA_iblock_t *parent,
/* Cache the new extensible array super block */
if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_EARRAY_SBLOCK, sblock_addr, sblock, H5AC__NO_FLAGS_SET) < 0)
H5E_THROW(H5E_CANTINSERT, "can't add extensible array super block to cache")
+ inserted = TRUE;
+
+ /* Add super block as child of 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, sblock) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
+ sblock->top_proxy = hdr->top_proxy;
+ } /* end if */
/* Update extensible array super block statistics */
hdr->stats.stored.nsuper_blks++;
@@ -235,6 +244,11 @@ H5EA__sblock_create(H5EA_hdr_t *hdr, hid_t dxpl_id, H5EA_iblock_t *parent,
CATCH
if(!H5F_addr_defined(ret_value))
if(sblock) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(sblock) < 0)
+ H5E_THROW(H5E_CANTREMOVE, "unable to remove extensible array super block from cache")
+
/* Release super block's disk space */
if(H5F_addr_defined(sblock->addr) && H5MF_xfree(hdr->f, H5FD_MEM_EARRAY_SBLOCK, dxpl_id, sblock->addr, (hsize_t)sblock->size) < 0)
H5E_THROW(H5E_CANTFREE, "unable to release extensible array super block")
@@ -266,6 +280,7 @@ H5EA__sblock_protect(H5EA_hdr_t *hdr, hid_t dxpl_id, H5EA_iblock_t *parent,
haddr_t sblk_addr, unsigned sblk_idx, unsigned flags))
/* Local variables */
+ H5EA_sblock_t *sblock = NULL; /* Pointer to super block */
H5EA_sblock_cache_ud_t udata; /* Information needed for loading super block */
/* Sanity check */
@@ -282,10 +297,27 @@ H5EA__sblock_protect(H5EA_hdr_t *hdr, hid_t dxpl_id, H5EA_iblock_t *parent,
udata.sblk_addr = sblk_addr;
/* Protect the super block */
- if(NULL == (ret_value = (H5EA_sblock_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_EARRAY_SBLOCK, sblk_addr, &udata, flags)))
+ if(NULL == (sblock = (H5EA_sblock_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_EARRAY_SBLOCK, sblk_addr, &udata, flags)))
H5E_THROW(H5E_CANTPROTECT, "unable to protect extensible array super block, address = %llu", (unsigned long long)sblk_addr)
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->top_proxy && NULL == sblock->top_proxy) {
+ /* Add super block as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, sblock) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add extensible array entry as child of array proxy")
+ sblock->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+ /* Set return value */
+ ret_value = sblock;
+
CATCH
+ /* Clean up on error */
+ if(!ret_value) {
+ /* Release the super block, if it was protected */
+ if(sblock && H5AC_unprotect(hdr->f, dxpl_id, H5AC_EARRAY_SBLOCK, sblock->addr, sblock, H5AC__NO_FLAGS_SET) < 0)
+ H5E_THROW(H5E_CANTUNPROTECT, "unable to unprotect extensible array super block, address = %llu", (unsigned long long)sblock->addr)
+ } /* end if */
END_FUNC(PKG) /* end H5EA__sblock_protect() */
@@ -409,6 +441,9 @@ H5EA__sblock_dest(H5EA_sblock_t *sblock))
sblock->hdr = NULL;
} /* end if */
+ /* Sanity check */
+ HDassert(NULL == sblock->top_proxy);
+
/* Free the super block itself */
sblock = H5FL_FREE(H5EA_sblock_t, sblock);
diff --git a/src/H5F.c b/src/H5F.c
index 6b087ce..a43009b 100644
--- a/src/H5F.c
+++ b/src/H5F.c
@@ -456,9 +456,9 @@ H5Fcreate(const char *filename, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
if(!filename || !*filename)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file name")
/* In this routine, we only accept the following flags:
- * H5F_ACC_EXCL and H5F_ACC_TRUNC
+ * H5F_ACC_EXCL, H5F_ACC_TRUNC and H5F_ACC_SWMR_WRITE
*/
- if(flags & ~(H5F_ACC_EXCL | H5F_ACC_TRUNC))
+ if(flags & ~(H5F_ACC_EXCL | H5F_ACC_TRUNC | H5F_ACC_SWMR_WRITE))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid flags")
/* The H5F_ACC_EXCL and H5F_ACC_TRUNC flags are mutually exclusive */
if((flags & H5F_ACC_EXCL) && (flags & H5F_ACC_TRUNC))
@@ -563,6 +563,12 @@ H5Fopen(const char *filename, unsigned flags, hid_t fapl_id)
if((flags & ~H5F_ACC_PUBLIC_FLAGS) ||
(flags & H5F_ACC_TRUNC) || (flags & H5F_ACC_EXCL))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file open flags")
+ /* Asking for SWMR write access on a read-only file is invalid */
+ if((flags & H5F_ACC_SWMR_WRITE) && 0 == (flags & H5F_ACC_RDWR))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "SWMR write access on a file open for read-only access is not allowed")
+ /* Asking for SWMR read access on a non-read-only file is invalid */
+ if((flags & H5F_ACC_SWMR_READ) && (flags & H5F_ACC_RDWR))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "SWMR read access on a file open for read-write access is not allowed")
/* Verify access property list and get correct dxpl */
if(H5P_verify_apl_and_dxpl(&fapl_id, H5P_CLS_FACC, &dxpl_id, H5I_INVALID_HID, TRUE) < 0)
@@ -857,10 +863,20 @@ H5Fget_intent(hid_t file_id, unsigned *intent_flags)
* Simplify things for them so that they only get either H5F_ACC_RDWR
* or H5F_ACC_RDONLY.
*/
- if(H5F_INTENT(file) & H5F_ACC_RDWR)
+ if(H5F_INTENT(file) & H5F_ACC_RDWR) {
*intent_flags = H5F_ACC_RDWR;
- else
+
+ /* Check for SWMR write access on the file */
+ if(H5F_INTENT(file) & H5F_ACC_SWMR_WRITE)
+ *intent_flags |= H5F_ACC_SWMR_WRITE;
+ } /* end if */
+ else {
*intent_flags = H5F_ACC_RDONLY;
+
+ /* Check for SWMR read access on the file */
+ if(H5F_INTENT(file) & H5F_ACC_SWMR_READ)
+ *intent_flags |= H5F_ACC_SWMR_READ;
+ } /* end else */
} /* end if */
done:
@@ -1541,6 +1557,222 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5Fstart_swmr_write
+ *
+ * Purpose: To enable SWMR writing mode for the file
+ * 1) Refresh opened objects: part 1
+ * 2) Flush & reset accumulator
+ * 3) Mark the file in SWMR writing mode
+ * 4) Set metadata read attempts and retries info
+ * 5) Disable accumulator
+ * 6) Evict all cache entries except the superblock
+ * 7) Refresh opened objects (part 2)
+ * 8) Unlock the file
+ *
+ * Pre-conditions:
+ * 1) The file being opened has v3 superblock
+ * 2) The file is opened with H5F_ACC_RDWR
+ * 3) The file is not already marked for SWMR writing
+ * 4) Current implementaion for opened objects:
+ * --only allow datasets and groups without attributes
+ * --disallow named datatype with/without attributes
+ * --disallow opened attributes attached to objects
+ * NOTE: Currently, only opened groups and datasets are allowed
+ * when enabling SWMR via H5Fstart_swmr_write().
+ * Will later implement a different approach--
+ * set up flush dependency/proxy even for file opened without
+ * SWMR to resolve issues with opened objects.
+ *
+ * Return: Non-negative on success/negative on failure
+ *
+ * Programmer:
+ * Vailin Choi; Feb 2014
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Fstart_swmr_write(hid_t file_id)
+{
+ H5F_t *file = NULL; /* File info */
+ size_t grp_dset_count=0; /* # of open objects: groups & datasets */
+ size_t nt_attr_count=0; /* # of opened named datatypes + opened attributes */
+ hid_t *obj_ids=NULL; /* List of ids */
+ H5G_loc_t *obj_glocs=NULL; /* Group location of the object */
+ H5O_loc_t *obj_olocs=NULL; /* Object location */
+ H5G_name_t *obj_paths=NULL; /* Group hierarchy path */
+ size_t u; /* Local index variable */
+ hbool_t setup = FALSE; /* Boolean flag to indicate whether SWMR setting is enabled */
+ H5F_io_info_t fio_info; /* I/O info for operation */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE1("e", "i", file_id);
+
+ /* check args */
+ if(NULL == (file = (H5F_t *)H5I_object_verify(file_id, H5I_FILE)))
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file")
+
+ /* Should have write permission */
+ if((H5F_INTENT(file) & H5F_ACC_RDWR) == 0)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "no write intent on file")
+
+ if(file->shared->sblock->super_vers < HDF5_SUPERBLOCK_VERSION_3)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "file superblock version should be at least 3")
+ HDassert(file->shared->latest_flags == H5F_LATEST_ALL_FLAGS);
+
+ /* Should not be marked for SWMR writing mode already */
+ if(file->shared->sblock->status_flags & H5F_SUPER_SWMR_WRITE_ACCESS)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "file already in SWMR writing mode")
+
+ HDassert(file->shared->sblock->status_flags & H5F_SUPER_WRITE_ACCESS);
+
+ /* Flush data buffers */
+ if(H5F_flush(file, H5AC_ind_read_dxpl_id, FALSE) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL, "unable to flush file's cached information")
+
+ /* Get the # of opened named datatypes and attributes */
+ if(H5F_get_obj_count(file, H5F_OBJ_DATATYPE|H5F_OBJ_ATTR, FALSE, &nt_attr_count) < 0)
+ HGOTO_ERROR(H5E_INTERNAL, H5E_BADITER, FAIL, "H5F_get_obj_count failed")
+ if(nt_attr_count)
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "named datatypes and/or attributes opened in the file")
+
+ /* Get the # of opened datasets and groups */
+ if(H5F_get_obj_count(file, H5F_OBJ_GROUP|H5F_OBJ_DATASET, FALSE, &grp_dset_count) < 0)
+ HGOTO_ERROR(H5E_INTERNAL, H5E_BADITER, FAIL, "H5F_get_obj_count failed")
+
+ if(grp_dset_count) {
+ /* Allocate space for group and object locations */
+ if((obj_ids = (hid_t *) H5MM_malloc(grp_dset_count * sizeof(hid_t))) == NULL)
+ HGOTO_ERROR(H5E_FILE, H5E_NOSPACE, FAIL, "can't allocate buffer for hid_t")
+ if((obj_glocs = (H5G_loc_t *) H5MM_malloc(grp_dset_count * sizeof(H5G_loc_t))) == NULL)
+ HGOTO_ERROR(H5E_FILE, H5E_NOSPACE, FAIL, "can't allocate buffer for H5G_loc_t")
+ if((obj_olocs = (H5O_loc_t *) H5MM_malloc(grp_dset_count * sizeof(H5O_loc_t))) == NULL)
+ HGOTO_ERROR(H5E_FILE, H5E_NOSPACE, FAIL, "can't allocate buffer for H5O_loc_t")
+ if((obj_paths = (H5G_name_t *) H5MM_malloc(grp_dset_count * sizeof(H5G_name_t))) == NULL)
+ HGOTO_ERROR(H5E_FILE, H5E_NOSPACE, FAIL, "can't allocate buffer for H5G_name_t")
+
+ /* Get the list of opened object ids (groups & datasets) */
+ if(H5F_get_obj_ids(file, H5F_OBJ_GROUP|H5F_OBJ_DATASET, grp_dset_count, obj_ids, FALSE, &grp_dset_count) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "H5F_get_obj_ids failed")
+
+ /* Refresh opened objects (groups, datasets) in the file */
+ for(u = 0; u < grp_dset_count; u++) {
+ H5O_loc_t *oloc; /* object location */
+ H5G_loc_t tmp_loc;
+
+ /* Set up the id's group location */
+ obj_glocs[u].oloc = &obj_olocs[u];
+ obj_glocs[u].path = &obj_paths[u];
+ H5G_loc_reset(&obj_glocs[u]);
+
+ /* get the id's object location */
+ if((oloc = H5O_get_loc(obj_ids[u])) == NULL)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not an object")
+
+ /* Make deep local copy of object's location information */
+ H5G_loc(obj_ids[u], &tmp_loc);
+ H5G_loc_copy(&obj_glocs[u], &tmp_loc, H5_COPY_DEEP);
+
+ /* Close the object */
+ if(H5I_dec_ref(obj_ids[u]) < 0)
+ HGOTO_ERROR(H5E_ATOM, H5E_CANTCLOSEOBJ, FAIL, "decrementing object ID failed")
+ } /* end for */
+ } /* end if */
+
+ /* Set up I/O info for operation */
+ fio_info.f = file;
+ if(NULL == (fio_info.dxpl = (H5P_genplist_t *)H5I_object(H5AC_ind_read_dxpl_id)))
+ HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "can't get property list")
+
+ /* Flush and reset the accumulator */
+ if(H5F__accum_reset(&fio_info, TRUE) < 0)
+ HGOTO_ERROR(H5E_IO, H5E_CANTRESET, FAIL, "can't reset accumulator")
+
+ /* Turn on SWMR write in shared file open flags */
+ file->shared->flags |= H5F_ACC_SWMR_WRITE;
+
+ /* Mark the file in SWMR writing mode */
+ file->shared->sblock->status_flags |= H5F_SUPER_SWMR_WRITE_ACCESS;
+
+ /* Set up metadata read attempts */
+ file->shared->read_attempts = H5F_SWMR_METADATA_READ_ATTEMPTS;
+
+ /* Initialize "retries" and "retries_nbins" */
+ if(H5F_set_retries(file) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "can't set retries and retries_nbins")
+
+ /* Turn off usage of accumulator */
+ file->shared->feature_flags &= ~(unsigned)H5FD_FEAT_ACCUMULATE_METADATA;
+ if(H5FD_set_feature_flags(file->shared->lf, file->shared->feature_flags) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "can't set feature_flags in VFD")
+
+ setup = TRUE;
+
+ /* Mark superblock as dirty */
+ if(H5F_super_dirty(file) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTMARKDIRTY, FAIL, "unable to mark superblock as dirty")
+
+ /* Flush the superblock */
+ if(H5F_flush_tagged_metadata(file, H5AC__SUPERBLOCK_TAG, H5AC_ind_read_dxpl_id) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL, "unable to flush superblock")
+
+ /* Evict all flushed entries in the cache except the pinned superblock */
+ if(H5F__evict_cache_entries(file, H5AC_ind_read_dxpl_id) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL, "unable to evict file's cached information")
+
+ /* 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], H5AC_ind_read_dxpl_id, TRUE) < 0)
+ HGOTO_ERROR(H5E_ATOM, H5E_CLOSEERROR, FAIL, "can't refresh-close object")
+
+ /* Unlock the file */
+ if(H5FD_unlock(file->shared->lf) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "unable to unlock the file")
+
+done:
+ if(ret_value < 0 && setup) {
+ HDassert(file);
+
+ /* Re-enable accumulator */
+ file->shared->feature_flags |= (unsigned)H5FD_FEAT_ACCUMULATE_METADATA;
+ if(H5FD_set_feature_flags(file->shared->lf, file->shared->feature_flags) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTSET, FAIL, "can't set feature_flags in VFD")
+
+ /* Reset the # of read attempts */
+ file->shared->read_attempts = H5F_METADATA_READ_ATTEMPTS;
+ if(H5F_set_retries(file) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTINIT, FAIL, "can't set retries and retries_nbins")
+
+ /* Un-set H5F_ACC_SWMR_WRITE in shared open flags */
+ file->shared->flags &= ~H5F_ACC_SWMR_WRITE;
+
+ /* Unmark the file: not in SWMR writing mode */
+ file->shared->sblock->status_flags &= (uint8_t)(~H5F_SUPER_SWMR_WRITE_ACCESS);
+
+ /* Mark superblock as dirty */
+ if(H5F_super_dirty(file) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTMARKDIRTY, FAIL, "unable to mark superblock as dirty")
+
+ /* Flush the superblock */
+ if(H5F_flush_tagged_metadata(file, H5AC__SUPERBLOCK_TAG, H5AC_ind_read_dxpl_id) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL, "unable to flush superblock")
+ } /* end if */
+
+ /* Free memory */
+ if(obj_ids)
+ H5MM_xfree(obj_ids);
+ if(obj_glocs)
+ H5MM_xfree(obj_glocs);
+ if(obj_olocs)
+ H5MM_xfree(obj_olocs);
+ if(obj_paths)
+ H5MM_xfree(obj_paths);
+
+ FUNC_LEAVE_API(ret_value)
+} /* end H5Fstart_swmr_write() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5Fstart_mdc_logging
*
* Purpose: Start metadata cache logging operations for a file.
diff --git a/src/H5FA.c b/src/H5FA.c
index c6c2e1b..90144e7 100644
--- a/src/H5FA.c
+++ b/src/H5FA.c
@@ -737,6 +737,56 @@ END_FUNC(PRIV) /* end H5FA_iterate() */
/*-------------------------------------------------------------------------
+ * Function: H5FA_depend
+ *
+ * Purpose: Make a child flush dependency between the fixed array
+ * and another piece of metadata in the file.
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: Dana Robinson
+ * Fall 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+BEGIN_FUNC(PRIV, ERR,
+herr_t, SUCCEED, FAIL,
+H5FA_depend(H5FA_t *fa, hid_t dxpl_id, H5AC_proxy_entry_t *parent))
+
+ /* Local variables */
+ H5FA_hdr_t *hdr = fa->hdr; /* Header for FA */
+
+ /*
+ * Check arguments.
+ */
+ HDassert(fa);
+ HDassert(hdr);
+ HDassert(parent);
+
+ /*
+ * Check to see if a flush dependency between the fixed array
+ * and another data structure in the file has already been set up.
+ * If it hasn't, do so now.
+ */
+ if(NULL == hdr->parent) {
+ /* Sanity check */
+ HDassert(hdr->top_proxy);
+
+ /* Set the shared array header's file context for this operation */
+ hdr->f = fa->f;
+
+ /* Add the fixed array as a child of the parent (proxy) */
+ if(H5AC_proxy_entry_add_child(parent, hdr->f, dxpl_id, hdr->top_proxy) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add fixed array as child of proxy")
+ hdr->parent = parent;
+ } /* end if */
+
+CATCH
+
+END_FUNC(PRIV) /* end H5FA_depend() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5FA_patch_file
*
* Purpose: Patch the top-level file pointer contained in fa
diff --git a/src/H5FAcache.c b/src/H5FAcache.c
index 82b87dd..11e8571 100644
--- a/src/H5FAcache.c
+++ b/src/H5FAcache.c
@@ -78,6 +78,7 @@ static void *H5FA__cache_hdr_deserialize(const void *image, size_t len,
static herr_t H5FA__cache_hdr_image_len(const void *thing, size_t *image_len);
static herr_t H5FA__cache_hdr_serialize(const H5F_t *f, void *image, size_t len,
void *thing);
+static herr_t H5FA__cache_hdr_notify(H5AC_notify_action_t action, void *thing);
static herr_t H5FA__cache_hdr_free_icr(void *thing);
static herr_t H5FA__cache_dblock_get_initial_load_size(void *udata, size_t *image_len);
@@ -87,6 +88,7 @@ static void *H5FA__cache_dblock_deserialize(const void *image, size_t len,
static herr_t H5FA__cache_dblock_image_len(const void *thing, size_t *image_len);
static herr_t H5FA__cache_dblock_serialize(const H5F_t *f, void *image, size_t len,
void *thing);
+static herr_t H5FA__cache_dblock_notify(H5AC_notify_action_t action, void *thing);
static herr_t H5FA__cache_dblock_free_icr(void *thing);
static herr_t H5FA__cache_dblock_fsf_size(const void *thing, size_t *fsf_size);
@@ -97,6 +99,7 @@ static void *H5FA__cache_dblk_page_deserialize(const void *image, size_t len,
static herr_t H5FA__cache_dblk_page_image_len(const void *thing, size_t *image_len);
static herr_t H5FA__cache_dblk_page_serialize(const H5F_t *f, void *image, size_t len,
void *thing);
+static herr_t H5FA__cache_dblk_page_notify(H5AC_notify_action_t action, void *thing);
static herr_t H5FA__cache_dblk_page_free_icr(void *thing);
@@ -117,7 +120,7 @@ const H5AC_class_t H5AC_FARRAY_HDR[1] = {{
H5FA__cache_hdr_image_len, /* 'image_len' callback */
NULL, /* 'pre_serialize' callback */
H5FA__cache_hdr_serialize, /* 'serialize' callback */
- NULL, /* 'notify' callback */
+ H5FA__cache_hdr_notify, /* 'notify' callback */
H5FA__cache_hdr_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
}};
@@ -135,7 +138,7 @@ const H5AC_class_t H5AC_FARRAY_DBLOCK[1] = {{
H5FA__cache_dblock_image_len, /* 'image_len' callback */
NULL, /* 'pre_serialize' callback */
H5FA__cache_dblock_serialize, /* 'serialize' callback */
- NULL, /* 'notify' callback */
+ H5FA__cache_dblock_notify, /* 'notify' callback */
H5FA__cache_dblock_free_icr, /* 'free_icr' callback */
H5FA__cache_dblock_fsf_size, /* 'fsf_size' callback */
}};
@@ -153,7 +156,7 @@ const H5AC_class_t H5AC_FARRAY_DBLK_PAGE[1] = {{
H5FA__cache_dblk_page_image_len, /* 'image_len' callback */
NULL, /* 'pre_serialize' callback */
H5FA__cache_dblk_page_serialize, /* 'serialize' callback */
- NULL, /* 'notify' callback */
+ H5FA__cache_dblk_page_notify, /* 'notify' callback */
H5FA__cache_dblk_page_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
}};
@@ -440,6 +443,80 @@ END_FUNC(STATIC) /* end H5FA__cache_hdr_serialize() */
/*-------------------------------------------------------------------------
+ * Function: H5FA__cache_hdr_notify
+ *
+ * Purpose: Handle cache action notifications
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Dana Robinson
+ * December 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+BEGIN_FUNC(STATIC, ERR,
+herr_t, SUCCEED, FAIL,
+H5FA__cache_hdr_notify(H5AC_notify_action_t action, void *_thing))
+
+ /* Local variables */
+ H5FA_hdr_t *hdr = (H5FA_hdr_t *)_thing; /* Pointer to the object */
+
+ /* Sanity check */
+ HDassert(hdr);
+
+ /* Check if the file was opened with SWMR-write access */
+ if(hdr->swmr_write) {
+ /* Determine which action to take */
+ switch(action) {
+ case H5AC_NOTIFY_ACTION_AFTER_INSERT:
+ case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
+ case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
+ case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
+ case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
+ case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
+ /* do nothing */
+ break;
+
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ /* If hdr->parent != NULL, hdr->parent is used to destroy
+ * the flush dependency before the header is evicted.
+ */
+ if(hdr->parent) {
+ /* Sanity check */
+ HDassert(hdr->top_proxy);
+
+ /* Destroy flush dependency on object header proxy */
+ if(H5AC_proxy_entry_remove_child((H5AC_proxy_entry_t *)hdr->parent, (void *)hdr->top_proxy) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between fixed array and proxy")
+ hdr->parent = NULL;
+ } /* end if */
+
+ /* Detach from 'top' proxy for fixed array */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(hdr->top_proxy, hdr) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between header and fixed array 'top' proxy")
+ /* Don't reset hdr->top_proxy here, it's destroyed when the header is freed -QAK */
+ } /* end if */
+ break;
+
+ default:
+#ifdef NDEBUG
+ H5E_THROW(H5E_BADVALUE, "unknown action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Unknown action?!?");
+#endif /* NDEBUG */
+ } /* end switch */
+ } /* end if */
+ else
+ HDassert(NULL == hdr->parent);
+
+CATCH
+
+END_FUNC(STATIC) /* end H5FA__cache_hdr_notify() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5FA__cache_hdr_free_icr
*
* Purpose: Destroy/release an "in core representation" of a data
@@ -765,6 +842,75 @@ END_FUNC(STATIC) /* end H5FA__cache_dblock_serialize() */
/*-------------------------------------------------------------------------
+ * Function: H5FA__cache_dblock_notify
+ *
+ * Purpose: Handle cache action notifications
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: Dana Robinson
+ * Fall 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+BEGIN_FUNC(STATIC, ERR,
+herr_t, SUCCEED, FAIL,
+H5FA__cache_dblock_notify(H5AC_notify_action_t action, void *_thing))
+
+ /* Local variables */
+ H5FA_dblock_t *dblock = (H5FA_dblock_t *)_thing;
+
+ /* Sanity check */
+ HDassert(dblock);
+
+ /* Check if the file was opened with SWMR-write access */
+ if(dblock->hdr->swmr_write) {
+ /* Determine which action to take */
+ switch(action) {
+ case H5AC_NOTIFY_ACTION_AFTER_INSERT:
+ case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ /* Create flush dependency on parent */
+ if(H5FA__create_flush_depend((H5AC_info_t *)dblock->hdr, (H5AC_info_t *)dblock) < 0)
+ H5E_THROW(H5E_CANTDEPEND, "unable to create flush dependency between data block and header, address = %llu", (unsigned long long)dblock->addr)
+ break;
+
+ case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
+ case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
+ case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
+ case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
+ case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
+ /* do nothing */
+ break;
+
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ /* Destroy flush dependency on parent */
+ if(H5FA__destroy_flush_depend((H5AC_info_t *)dblock->hdr, (H5AC_info_t *)dblock) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency")
+
+ /* Detach from 'top' proxy for fixed array */
+ if(dblock->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(dblock->top_proxy, dblock) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between data block and fixed array 'top' proxy")
+ dblock->top_proxy = NULL;
+ } /* end if */
+ break;
+
+ default:
+#ifdef NDEBUG
+ H5E_THROW(H5E_BADVALUE, "unknown action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Unknown action?!?");
+#endif /* NDEBUG */
+ } /* end switch */
+ } /* end if */
+
+CATCH
+
+END_FUNC(STATIC) /* end H5FA__cache_dblock_notify() */
+
+
+
+/*-------------------------------------------------------------------------
* Function: H5FA__cache_dblock_free_icr
*
* Purpose: Destroy/release an "in core representation" of a data
@@ -1065,6 +1211,66 @@ END_FUNC(STATIC) /* end H5FA__cache_dblk_page_serialize() */
/*-------------------------------------------------------------------------
+ * Function: H5FA__cache_dblk_page_notify
+ *
+ * Purpose: Handle cache action notifications
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * koziol@lbl.gov
+ * Oct 17 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+BEGIN_FUNC(STATIC, ERR,
+herr_t, SUCCEED, FAIL,
+H5FA__cache_dblk_page_notify(H5AC_notify_action_t action, void *_thing))
+
+ /* Local variables */
+ H5FA_dblk_page_t *dblk_page = (H5FA_dblk_page_t *)_thing; /* Pointer to the object */
+
+ /* Sanity check */
+ HDassert(dblk_page);
+
+ /* Determine which action to take */
+ switch(action) {
+ case H5AC_NOTIFY_ACTION_AFTER_INSERT:
+ case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
+ /* do nothing */
+ break;
+
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ /* Detach from 'top' proxy for fixed array */
+ if(dblk_page->top_proxy) {
+ if(H5AC_proxy_entry_remove_child(dblk_page->top_proxy, dblk_page) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency between data block page and fixed array 'top' proxy")
+ dblk_page->top_proxy = NULL;
+ } /* end if */
+ break;
+
+ case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
+ case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
+ case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
+ case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
+ /* do nothing */
+ break;
+
+ default:
+#ifdef NDEBUG
+ H5E_THROW(H5E_BADVALUE, "unknown action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Unknown action?!?");
+#endif /* NDEBUG */
+ } /* end switch */
+
+CATCH
+
+END_FUNC(STATIC) /* end H5FA__cache_dblk_page_notify() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5FA__cache_dblk_page_free_icr
*
* Purpose: Destroy/release an "in core representation" of a data
diff --git a/src/H5FAdblkpage.c b/src/H5FAdblkpage.c
index 054ffb9..09278f8 100644
--- a/src/H5FAdblkpage.c
+++ b/src/H5FAdblkpage.c
@@ -153,6 +153,7 @@ H5FA__dblk_page_create(H5FA_hdr_t *hdr, hid_t dxpl_id, haddr_t addr, size_t nelm
/* Local variables */
H5FA_dblk_page_t *dblk_page = NULL; /* Fixed array data block page */
+ hbool_t inserted = FALSE; /* Whether the header was inserted into cache */
#ifdef H5FA_DEBUG
HDfprintf(stderr, "%s: Called, addr = %a\n", FUNC, addr);
@@ -179,10 +180,23 @@ HDfprintf(stderr, "%s: dblk_page->size = %Zu\n", FUNC, dblk_page->size);
/* Cache the new fixed array data block page */
if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_FARRAY_DBLK_PAGE, dblk_page->addr, dblk_page, H5AC__NO_FLAGS_SET) < 0)
H5E_THROW(H5E_CANTINSERT, "can't add fixed array data block page to cache")
+ inserted = TRUE;
+
+ /* Add data block page as child of 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, dblk_page) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add fixed array entry as child of array proxy")
+ dblk_page->top_proxy = hdr->top_proxy;
+ } /* end if */
CATCH
if(ret_value < 0)
if(dblk_page) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(dblk_page) < 0)
+ H5E_THROW(H5E_CANTREMOVE, "unable to remove fixed array data block page from cache")
+
/* Destroy data block page */
if(H5FA__dblk_page_dest(dblk_page) < 0)
H5E_THROW(H5E_CANTFREE, "unable to destroy fixed array data block page")
@@ -210,6 +224,7 @@ H5FA__dblk_page_protect(H5FA_hdr_t *hdr, hid_t dxpl_id, haddr_t dblk_page_addr,
size_t dblk_page_nelmts, unsigned flags))
/* Local variables */
+ H5FA_dblk_page_t *dblk_page = NULL; /* Fixed array data block page */
H5FA_dblk_page_cache_ud_t udata; /* Information needed for loading data block page */
#ifdef H5FA_DEBUG
@@ -229,11 +244,29 @@ HDfprintf(stderr, "%s: Called\n", FUNC);
udata.dblk_page_addr = dblk_page_addr;
/* Protect the data block page */
- if(NULL == (ret_value = (H5FA_dblk_page_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_FARRAY_DBLK_PAGE, dblk_page_addr, &udata, flags)))
+ if(NULL == (dblk_page = (H5FA_dblk_page_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_FARRAY_DBLK_PAGE, dblk_page_addr, &udata, flags)))
H5E_THROW(H5E_CANTPROTECT, "unable to protect fixed array data block page, address = %llu", (unsigned long long)dblk_page_addr)
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->top_proxy && NULL == dblk_page->top_proxy) {
+ /* Add data block page as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, dblk_page) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add fixed array entry as child of array proxy")
+ dblk_page->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+ /* Set return value */
+ ret_value = dblk_page;
+
CATCH
+ /* Clean up on error */
+ if(!ret_value) {
+ /* Release the data block page, if it was protected */
+ if(dblk_page && H5AC_unprotect(hdr->f, dxpl_id, H5AC_FARRAY_DBLK_PAGE, dblk_page->addr, dblk_page, H5AC__NO_FLAGS_SET) < 0)
+ H5E_THROW(H5E_CANTUNPROTECT, "unable to unprotect fixed array data block page, address = %llu", (unsigned long long)dblk_page->addr)
+ } /* end if */
+
END_FUNC(PKG) /* end H5FA__dblk_page_protect() */
@@ -306,6 +339,9 @@ H5FA__dblk_page_dest(H5FA_dblk_page_t *dblk_page))
dblk_page->hdr = NULL;
} /* end if */
+ /* Sanity check */
+ HDassert(NULL == dblk_page->top_proxy);
+
/* Free the data block page itself */
dblk_page = H5FL_FREE(H5FA_dblk_page_t, dblk_page);
diff --git a/src/H5FAdblock.c b/src/H5FAdblock.c
index f597c8e..440447d 100644
--- a/src/H5FAdblock.c
+++ b/src/H5FAdblock.c
@@ -191,6 +191,7 @@ H5FA__dblock_create(H5FA_hdr_t *hdr, hid_t dxpl_id, hbool_t *hdr_dirty))
/* Local variables */
H5FA_dblock_t *dblock = NULL; /* Fixed array data block */
haddr_t dblock_addr; /* Fixed array data block address */
+ hbool_t inserted = FALSE; /* Whether the header was inserted into cache */
/* Sanity check */
HDassert(hdr);
@@ -217,6 +218,14 @@ H5FA__dblock_create(H5FA_hdr_t *hdr, hid_t dxpl_id, hbool_t *hdr_dirty))
/* Cache the new fixed array data block */
if(H5AC_insert_entry(hdr->f, dxpl_id, H5AC_FARRAY_DBLOCK, dblock_addr, dblock, H5AC__NO_FLAGS_SET) < 0)
H5E_THROW(H5E_CANTINSERT, "can't add fixed array data block to cache")
+ inserted = TRUE;
+
+ /* Add data block as child of 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, dblock) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add fixed array entry as child of array proxy")
+ dblock->top_proxy = hdr->top_proxy;
+ } /* end if */
/* Mark the header dirty (for updating statistics) */
*hdr_dirty = TRUE;
@@ -228,6 +237,11 @@ CATCH
if(!H5F_addr_defined(ret_value))
if(dblock) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(dblock) < 0)
+ H5E_THROW(H5E_CANTREMOVE, "unable to remove fixed array data block from cache")
+
/* Release data block's disk space */
if(H5F_addr_defined(dblock->addr) && H5MF_xfree(hdr->f, H5FD_MEM_FARRAY_DBLOCK, dxpl_id, dblock->addr, (hsize_t)dblock->size) < 0)
H5E_THROW(H5E_CANTFREE, "unable to release fixed array data block")
@@ -258,6 +272,7 @@ H5FA__dblock_protect(H5FA_hdr_t *hdr, hid_t dxpl_id, haddr_t dblk_addr,
unsigned flags))
/* Local variables */
+ H5FA_dblock_t *dblock; /* Fixed array data block */
H5FA_dblock_cache_ud_t udata; /* Information needed for loading data block */
/* Sanity check */
@@ -272,11 +287,28 @@ H5FA__dblock_protect(H5FA_hdr_t *hdr, hid_t dxpl_id, haddr_t dblk_addr,
udata.dblk_addr = dblk_addr;
/* Protect the data block */
- if(NULL == (ret_value = (H5FA_dblock_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_FARRAY_DBLOCK, dblk_addr, &udata, flags)))
+ if(NULL == (dblock = (H5FA_dblock_t *)H5AC_protect(hdr->f, dxpl_id, H5AC_FARRAY_DBLOCK, dblk_addr, &udata, flags)))
H5E_THROW(H5E_CANTPROTECT, "unable to protect fixed array data block, address = %llu", (unsigned long long)dblk_addr)
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->top_proxy && NULL == dblock->top_proxy) {
+ /* Add data block as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, hdr->f, dxpl_id, dblock) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add fixed array entry as child of array proxy")
+ dblock->top_proxy = hdr->top_proxy;
+ } /* end if */
+
+ /* Set return value */
+ ret_value = dblock;
+
CATCH
+ /* Clean up on error */
+ if(!ret_value)
+ /* Release the data block, if it was protected */
+ if(dblock && H5AC_unprotect(hdr->f, dxpl_id, H5AC_FARRAY_DBLOCK, dblock->addr, dblock, H5AC__NO_FLAGS_SET) < 0)
+ H5E_THROW(H5E_CANTUNPROTECT, "unable to unprotect fixed array data block, address = %llu", (unsigned long long)dblock->addr)
+
END_FUNC(PKG) /* end H5FA__dblock_protect() */
@@ -408,6 +440,9 @@ H5FA__dblock_dest(H5FA_dblock_t *dblock))
dblock->hdr = NULL;
} /* end if */
+ /* Sanity check */
+ HDassert(NULL == dblock->top_proxy);
+
/* Free the data block itself */
dblock = H5FL_FREE(H5FA_dblock_t, dblock);
diff --git a/src/H5FAhdr.c b/src/H5FAhdr.c
index d6e32dc..52b90d1 100644
--- a/src/H5FAhdr.c
+++ b/src/H5FAhdr.c
@@ -112,6 +112,7 @@ H5FA__hdr_alloc(H5F_t *f))
/* Set the internal parameters for the array */
hdr->f = f;
+ hdr->swmr_write = (H5F_INTENT(f) & H5F_ACC_SWMR_WRITE) > 0;
hdr->sizeof_addr = H5F_SIZEOF_ADDR(f);
hdr->sizeof_size = H5F_SIZEOF_SIZE(f);
@@ -184,6 +185,7 @@ H5FA__hdr_create(H5F_t *f, hid_t dxpl_id, const H5FA_create_t *cparam,
/* Local variables */
H5FA_hdr_t *hdr = NULL; /* Fixed array header */
+ hbool_t inserted = FALSE; /* Whether the header was inserted into cache */
/* Check arguments */
HDassert(f);
@@ -218,9 +220,20 @@ H5FA__hdr_create(H5F_t *f, hid_t dxpl_id, const H5FA_create_t *cparam,
if(HADDR_UNDEF == (hdr->addr = H5MF_alloc(f, H5FD_MEM_FARRAY_HDR, dxpl_id, (hsize_t)hdr->size)))
H5E_THROW(H5E_CANTALLOC, "file allocation failed for Fixed Array header")
+ /* Create 'top' proxy for extensible array entries */
+ if(hdr->swmr_write)
+ if(NULL == (hdr->top_proxy = H5AC_proxy_entry_create()))
+ H5E_THROW(H5E_CANTCREATE, "can't create fixed array entry proxy")
+
/* Cache the new Fixed Array header */
if(H5AC_insert_entry(f, dxpl_id, H5AC_FARRAY_HDR, hdr->addr, hdr, H5AC__NO_FLAGS_SET) < 0)
H5E_THROW(H5E_CANTINSERT, "can't add fixed array header to cache")
+ inserted = TRUE;
+
+ /* Add header as child of 'top' proxy */
+ if(hdr->top_proxy)
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, f, dxpl_id, hdr) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add fixed array entry as child of array proxy")
/* Set address of array header to return */
ret_value = hdr->addr;
@@ -229,6 +242,11 @@ CATCH
if(!H5F_addr_defined(ret_value))
if(hdr) {
+ /* Remove from cache, if inserted */
+ if(inserted)
+ if(H5AC_remove_entry(hdr) < 0)
+ H5E_THROW(H5E_CANTREMOVE, "unable to remove fixed array header from cache")
+
/* Release header's disk space */
if(H5F_addr_defined(hdr->addr) && H5MF_xfree(f, H5FD_MEM_FARRAY_HDR, dxpl_id, hdr->addr, (hsize_t)hdr->size) < 0)
H5E_THROW(H5E_CANTFREE, "unable to free Fixed Array header")
@@ -409,6 +427,7 @@ H5FA__hdr_protect(H5F_t *f, hid_t dxpl_id, haddr_t fa_addr, void *ctx_udata,
unsigned flags))
/* Local variables */
+ H5FA_hdr_t *hdr; /* Fixed array header */
H5FA_hdr_cache_ud_t udata; /* User data for cache callbacks */
/* Sanity check */
@@ -424,9 +443,23 @@ H5FA__hdr_protect(H5F_t *f, hid_t dxpl_id, haddr_t fa_addr, void *ctx_udata,
udata.ctx_udata = ctx_udata;
/* Protect the header */
- if(NULL == (ret_value = (H5FA_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_FARRAY_HDR, fa_addr, &udata, flags)))
+ if(NULL == (hdr = (H5FA_hdr_t *)H5AC_protect(f, dxpl_id, H5AC_FARRAY_HDR, fa_addr, &udata, flags)))
H5E_THROW(H5E_CANTPROTECT, "unable to protect fixed array header, address = %llu", (unsigned long long)fa_addr)
- ret_value->f = f; /* (Must be set again here, in case the header was already in the cache -QAK) */
+ hdr->f = f; /* (Must be set again here, in case the header was already in the cache -QAK) */
+
+ /* Create top proxy, if it doesn't exist */
+ if(hdr->swmr_write && NULL == hdr->top_proxy) {
+ /* Create 'top' proxy for fixed array entries */
+ if(NULL == (hdr->top_proxy = H5AC_proxy_entry_create()))
+ H5E_THROW(H5E_CANTCREATE, "can't create fixed array entry proxy")
+
+ /* Add header as child of 'top' proxy */
+ if(H5AC_proxy_entry_add_child(hdr->top_proxy, f, dxpl_id, hdr) < 0)
+ H5E_THROW(H5E_CANTSET, "unable to add fixed array entry as child of array proxy")
+ } /* end if */
+
+ /* Set return value */
+ ret_value = hdr;
CATCH
@@ -547,6 +580,13 @@ H5FA__hdr_dest(H5FA_hdr_t *hdr))
} /* end if */
hdr->cb_ctx = NULL;
+ /* Destroy the 'top' proxy */
+ if(hdr->top_proxy) {
+ if(H5AC_proxy_entry_dest(hdr->top_proxy) < 0)
+ H5E_THROW(H5E_CANTRELEASE, "unable to destroy fixed array 'top' proxy")
+ hdr->top_proxy = NULL;
+ } /* end if */
+
/* Free the shared info itself */
hdr = H5FL_FREE(H5FA_hdr_t, hdr);
diff --git a/src/H5FAint.c b/src/H5FAint.c
new file mode 100644
index 0000000..331227b
--- /dev/null
+++ b/src/H5FAint.c
@@ -0,0 +1,139 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5FAint.c
+ * Fall 2012
+ * Dana Robinson <derobins@hdfgroup.org>
+ *
+ * Purpose: Internal routines for fixed arrays.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/**********************/
+/* Module Declaration */
+/**********************/
+
+#include "H5FAmodule.h" /* This source code file is part of the H5FA module */
+
+
+/***********************/
+/* Other Packages Used */
+/***********************/
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error Handling */
+#include "H5FApkg.h" /* Fixed Arrays */
+
+
+/****************/
+/* Local Macros */
+/****************/
+
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+
+/********************/
+/* Package Typedefs */
+/********************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FA__create_flush_depend
+ *
+ * Purpose: Create a flush dependency between two data structure components
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: Dana Robinson
+ * Fall 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+BEGIN_FUNC(PKG, ERR,
+herr_t, SUCCEED, FAIL,
+H5FA__create_flush_depend(H5AC_info_t *parent_entry, H5AC_info_t *child_entry))
+
+ /* Sanity check */
+ HDassert(parent_entry);
+ HDassert(child_entry);
+
+ /* Create a flush dependency between parent and child entry */
+ if(H5AC_create_flush_dependency(parent_entry, child_entry) < 0)
+ H5E_THROW(H5E_CANTDEPEND, "unable to create flush dependency")
+
+CATCH
+
+END_FUNC(PKG) /* end H5FA__create_flush_depend() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FA__destroy_flush_depend
+ *
+ * Purpose: Destroy a flush dependency between two data structure components
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: Dana Robinson
+ * Fall 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+BEGIN_FUNC(PKG, ERR,
+herr_t, SUCCEED, FAIL,
+H5FA__destroy_flush_depend(H5AC_info_t *parent_entry, H5AC_info_t *child_entry))
+
+ /* Sanity check */
+ HDassert(parent_entry);
+ HDassert(child_entry);
+
+ /* Destroy a flush dependency between parent and child entry */
+ if(H5AC_destroy_flush_dependency(parent_entry, child_entry) < 0)
+ H5E_THROW(H5E_CANTUNDEPEND, "unable to destroy flush dependency")
+
+CATCH
+
+END_FUNC(PKG) /* end H5FA__destroy_flush_depend() */
+
diff --git a/src/H5FApkg.h b/src/H5FApkg.h
index c29322a..ccef562 100644
--- a/src/H5FApkg.h
+++ b/src/H5FApkg.h
@@ -144,6 +144,28 @@ typedef struct H5FA_hdr_t {
/* Client information (not stored) */
void *cb_ctx; /* Callback context */
+
+ /* SWMR / Flush dependency information (not stored) */
+ hbool_t swmr_write; /* Flag indicating the file is opened with SWMR-write access */
+ H5AC_proxy_entry_t *top_proxy; /* 'Top' proxy cache entry for all array entries */
+ void *parent; /* Pointer to 'top' proxy flush dependency
+ * parent, if it exists, otherwise NULL.
+ * If the fixed array is being used
+ * to index a chunked dataset and the
+ * dataset metadata is modified by a
+ * SWMR writer, this field will be set
+ * equal to the object header proxy
+ * that is the flush dependency parent
+ * of the fixed array header.
+ *
+ * The field is used to avoid duplicate
+ * setups of the flush dependency
+ * relationship, and to allow the
+ * fixed array header to destroy
+ * the flush dependency on receipt of
+ * an eviction notification from the
+ * metadata cache.
+ */
} H5FA_hdr_t;
/* The fixed array data block information */
@@ -158,6 +180,9 @@ typedef struct H5FA_dblock_t {
/* Internal array information (not stored) */
H5FA_hdr_t *hdr; /* Shared array header info */
+ /* SWMR / Flush dependency information (not stored) */
+ H5AC_proxy_entry_t *top_proxy; /* 'Top' proxy cache entry for all array entries */
+
/* Computed/cached values (not stored) */
haddr_t addr; /* Address of this data block on disk */
hsize_t size; /* Size of data block on disk */
@@ -181,6 +206,9 @@ typedef struct H5FA_dbk_page_t {
/* Internal array information (not stored) */
H5FA_hdr_t *hdr; /* Shared array header info */
+ /* SWMR / Flush dependency information (not stored) */
+ H5AC_proxy_entry_t *top_proxy; /* 'Top' proxy cache entry for all array entries */
+
/* Computed/cached values (not stored) */
haddr_t addr; /* Address of this data block page on disk */
size_t size; /* Size of data block page on disk */
@@ -241,6 +269,12 @@ H5_DLLVAR const H5FA_class_t *const H5FA_client_class_g[H5FA_NUM_CLS_ID];
/* Package Private Prototypes */
/******************************/
+/* Generic routines */
+H5_DLL herr_t H5FA__create_flush_depend(H5AC_info_t *parent_entry,
+ H5AC_info_t *child_entry);
+H5_DLL herr_t H5FA__destroy_flush_depend(H5AC_info_t *parent_entry,
+ H5AC_info_t *child_entry);
+
/* Header routines */
H5_DLL H5FA_hdr_t *H5FA__hdr_alloc(H5F_t *f);
H5_DLL herr_t H5FA__hdr_init(H5FA_hdr_t *hdr, void *ctx_udata);
diff --git a/src/H5FAprivate.h b/src/H5FAprivate.h
index 1e76468..612f3a2 100644
--- a/src/H5FAprivate.h
+++ b/src/H5FAprivate.h
@@ -32,6 +32,7 @@
#endif /* NOT_YET */
/* Private headers needed by this file */
+#include "H5ACprivate.h" /* Metadata cache */
#include "H5Fprivate.h" /* File access */
@@ -126,6 +127,7 @@ H5_DLL herr_t H5FA_get_nelmts(const H5FA_t *fa, hsize_t *nelmts);
H5_DLL herr_t H5FA_get_addr(const H5FA_t *fa, haddr_t *addr);
H5_DLL herr_t H5FA_set(const H5FA_t *fa, hid_t dxpl_id, hsize_t idx, const void *elmt);
H5_DLL herr_t H5FA_get(const H5FA_t *fa, hid_t dxpl_id, hsize_t idx, void *elmt);
+H5_DLL herr_t H5FA_depend(H5FA_t *fa, hid_t dxpl_id, H5AC_proxy_entry_t *parent);
H5_DLL herr_t H5FA_iterate(H5FA_t *fa, hid_t dxpl_id, H5FA_operator_t op, void *udata);
H5_DLL herr_t H5FA_close(H5FA_t *fa, hid_t dxpl_id);
H5_DLL herr_t H5FA_delete(H5F_t *f, hid_t dxpl_id, haddr_t fa_addr, void *ctx_udata);
diff --git a/src/H5FD.c b/src/H5FD.c
index 57905b1..99a9288 100644
--- a/src/H5FD.c
+++ b/src/H5FD.c
@@ -811,6 +811,9 @@ H5FD_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr)
if(NULL == (file = (driver->open)(name, flags, fapl_id, maxaddr)))
HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, NULL, "open failed")
+ /* Set the file access flags */
+ file->access_flags = flags;
+
/*
* Fill in public fields. We must increment the reference count on the
* driver ID to prevent it from being freed while this file is open.
@@ -1441,6 +1444,32 @@ H5FD_get_feature_flags(const H5FD_t *file, unsigned long *feature_flags)
/*-------------------------------------------------------------------------
+ * Function: H5FD_set_feature_flags
+ *
+ * Purpose: Set the feature flags for the VFD
+ *
+ * Return: Success: Non-negative
+ * Failure: Negative
+ *
+ * Programmer: Vailin Choi; Oct 2013
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5FD_set_feature_flags(H5FD_t *file, unsigned long feature_flags)
+{
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ HDassert(file);
+
+ /* Set the file's feature flags */
+ file->feature_flags = feature_flags;
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5FD_set_feature_flags() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5FD_get_fs_type_map
*
* Purpose: Retrieve the free space type mapping for the VFD
diff --git a/src/H5FDint.c b/src/H5FDint.c
index cd48197..744c3d1 100644
--- a/src/H5FDint.c
+++ b/src/H5FDint.c
@@ -207,9 +207,16 @@ H5P_genplist_t *dxpl, H5FD_mem_t type, haddr_t addr,
if(HADDR_UNDEF == (eoa = (file->cls->get_eoa)(file, type)))
HGOTO_ERROR(H5E_VFL, H5E_CANTINIT, FAIL, "driver get_eoa request failed")
- 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)
+
+ /*
+ * If the file is open for SWMR read access, allow access to data past
+ * the end of the allocated space (the 'eoa'). This is done because the
+ * eoa stored in the file's superblock might be out of sync with the
+ * objects being written within the file by the application performing
+ * SWMR write operations.
+ */
+ if(!(file->access_flags & H5F_ACC_SWMR_READ) && ((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)
/* Dispatch to driver */
if((file->cls->read)(file, type, H5P_PLIST_ID(dxpl), addr + file->base_addr, size, buf) < 0)
diff --git a/src/H5FDlog.c b/src/H5FDlog.c
index a2891cb..03228d2 100644
--- a/src/H5FDlog.c
+++ b/src/H5FDlog.c
@@ -898,6 +898,7 @@ H5FD_log_query(const H5FD_t *_file, unsigned long *flags /* out */)
*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; /* VFD handle is POSIX I/O call compatible */
+ *flags |= H5FD_FEAT_SUPPORTS_SWMR_IO; /* VFD supports the single-writer/multiple-readers (SWMR) pattern */
/* Check for flags that are set by h5repart */
if(file && file->fam_to_sec2)
diff --git a/src/H5FDpkg.h b/src/H5FDpkg.h
index a0c1b3a..45bcfd8 100644
--- a/src/H5FDpkg.h
+++ b/src/H5FDpkg.h
@@ -59,6 +59,7 @@ H5_DLL herr_t H5FD_free_real(H5FD_t *file, hid_t dxpl_id, H5FD_mem_t type,
/* Testing functions */
#ifdef H5FD_TESTING
+H5_DLL hbool_t H5FD_supports_swmr_test(const char *vfd_name);
#endif /* H5FD_TESTING */
#endif /* _H5FDpkg_H */
diff --git a/src/H5FDprivate.h b/src/H5FDprivate.h
index c40dc54..427b42c 100644
--- a/src/H5FDprivate.h
+++ b/src/H5FDprivate.h
@@ -157,6 +157,7 @@ H5_DLL herr_t H5FD_set_eoa(H5FD_t *file, H5FD_mem_t type, haddr_t addr);
H5_DLL haddr_t H5FD_get_eof(const H5FD_t *file, H5FD_mem_t type);
H5_DLL haddr_t H5FD_get_maxaddr(const H5FD_t *file);
H5_DLL herr_t H5FD_get_feature_flags(const H5FD_t *file, unsigned long *feature_flags);
+H5_DLL herr_t H5FD_set_feature_flags(H5FD_t *file, unsigned long feature_flags);
H5_DLL herr_t H5FD_get_fs_type_map(const H5FD_t *file, H5FD_mem_t *type_map);
H5_DLL herr_t H5FD_read(H5FD_t *file,
#ifndef H5_DEBUG_BUILD
diff --git a/src/H5FDpublic.h b/src/H5FDpublic.h
index 4931e0f..436be10 100644
--- a/src/H5FDpublic.h
+++ b/src/H5FDpublic.h
@@ -234,6 +234,11 @@ typedef enum H5F_mem_t H5FD_mem_t;
* image to store in memory.
*/
#define H5FD_FEAT_CAN_USE_FILE_IMAGE_CALLBACKS 0x00000800
+ /*
+ * Defining H5FD_FEAT_SUPPORTS_SWMR_IO for a VFL driver means that the
+ * driver supports the single-writer/multiple-readers I/O pattern.
+ */
+#define H5FD_FEAT_SUPPORTS_SWMR_IO 0x00001000
/* Forward declaration */
typedef struct H5FD_t H5FD_t;
@@ -294,6 +299,7 @@ struct H5FD_t {
hid_t driver_id; /*driver ID for this file */
const H5FD_class_t *cls; /*constant class info */
unsigned long fileno; /* File 'serial' number */
+ unsigned access_flags; /* File access flags (from create or open) */
unsigned long feature_flags; /* VFL Driver feature Flags */
haddr_t maxaddr; /* For this file, overrides class */
haddr_t base_addr; /* Base address for HDF5 data w/in file */
diff --git a/src/H5FDsec2.c b/src/H5FDsec2.c
index 04a0790..0ca5efb 100644
--- a/src/H5FDsec2.c
+++ b/src/H5FDsec2.c
@@ -530,6 +530,7 @@ H5FD_sec2_query(const H5FD_t *_file, unsigned long *flags /* out */)
*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; /* VFD handle is POSIX I/O call compatible */
+ *flags |= H5FD_FEAT_SUPPORTS_SWMR_IO; /* VFD supports the single-writer/multiple-readers (SWMR) pattern */
/* Check for flags that are set by h5repart */
if(file && file->fam_to_sec2)
diff --git a/src/H5FDstdio.c b/src/H5FDstdio.c
index 7056f7f..4c62bcc 100644
--- a/src/H5FDstdio.c
+++ b/src/H5FDstdio.c
@@ -550,7 +550,11 @@ H5FD_stdio_query(const H5FD_t *_f, unsigned long /*OUT*/ *flags)
/* Quiet the compiler */
_f=_f;
- /* Set the VFL feature flags that this driver supports */
+ /* Set the VFL feature flags that this driver supports.
+ *
+ * Note that this VFD does not support SWMR due to the unpredictable
+ * nature of the buffering layer.
+ */
if(flags) {
*flags = 0;
*flags|=H5FD_FEAT_AGGREGATE_METADATA; /* OK to aggregate metadata allocations */
diff --git a/src/H5FDtest.c b/src/H5FDtest.c
new file mode 100644
index 0000000..9d2f2e9
--- /dev/null
+++ b/src/H5FDtest.c
@@ -0,0 +1,119 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5FDtest.c
+ * Fall 2014
+ *
+ * Purpose: File driver testing routines.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/****************/
+/* Module Setup */
+/****************/
+
+#include "H5FDmodule.h" /* This source code file is part of the H5FD module */
+#define H5FD_TESTING /* Suppress warning about H5FD testing funcs */
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5FDpkg.h" /* File Drivers */
+
+/****************/
+/* Local Macros */
+/****************/
+
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+
+/********************/
+/* Package Typedefs */
+/********************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FD_supports_swmr_test()
+ *
+ * Purpose: Determines if a VFD supports SWMR.
+ *
+ * The function determines SWMR support by inspecting the
+ * HDF5_DRIVER environment variable, not by checking the
+ * VFD feature flags (which do not exist until the driver
+ * is instantiated).
+ *
+ * See test/Makefile.am for a list of the VFD strings.
+ *
+ * This function is only intended for use in the test code.
+ *
+ * Return: TRUE (1) if the VFD supports SWMR I/O or vfd_name is
+ * NULL or the empty string (which implies the default VFD).
+ *
+ * FALSE (0) if it does not
+ *
+ * This function cannot fail at this time so there is no
+ * error return value.
+ *
+ * Programmer: Dana Robinson
+ * Fall 2014
+ *
+ *-------------------------------------------------------------------------
+ */
+hbool_t
+H5FD_supports_swmr_test(const char *vfd_name)
+{
+ hbool_t ret_value = FALSE;
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ if(!vfd_name || !HDstrcmp(vfd_name, ""))
+ ret_value = TRUE;
+ else
+ ret_value = !HDstrcmp(vfd_name, "direct")
+ || !HDstrcmp(vfd_name, "log")
+ || !HDstrcmp(vfd_name, "sec2");
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+} /* end H5FD_supports_swmr_test() */
+
diff --git a/src/H5FS.c b/src/H5FS.c
index c3b45d2..8211404 100644
--- a/src/H5FS.c
+++ b/src/H5FS.c
@@ -132,6 +132,7 @@ HDfprintf(stderr, "%s: Creating free space manager, nclasses = %Zu\n", FUNC, ncl
fspace->expand_percent = fs_create->expand_percent;
fspace->max_sect_addr = fs_create->max_sect_addr;
fspace->max_sect_size = fs_create->max_sect_size;
+ fspace->swmr_write = (H5F_INTENT(f) & H5F_ACC_SWMR_WRITE) > 0;
fspace->alignment = alignment;
fspace->threshold = threshold;
diff --git a/src/H5FScache.c b/src/H5FScache.c
index c1a9e0a..f43676d 100644
--- a/src/H5FScache.c
+++ b/src/H5FScache.c
@@ -100,6 +100,7 @@ static herr_t H5FS__cache_sinfo_pre_serialize(const H5F_t *f, hid_t dxpl_id,
unsigned *flags);
static herr_t H5FS__cache_sinfo_serialize(const H5F_t *f, void *image,
size_t len, void *thing);
+static herr_t H5FS__cache_sinfo_notify(H5AC_notify_action_t action, void *thing);
static herr_t H5FS__cache_sinfo_free_icr(void *thing);
@@ -138,7 +139,7 @@ const H5AC_class_t H5AC_FSPACE_SINFO[1] = {{
H5FS__cache_sinfo_image_len, /* 'image_len' callback */
H5FS__cache_sinfo_pre_serialize, /* 'pre_serialize' callback */
H5FS__cache_sinfo_serialize, /* 'serialize' callback */
- NULL, /* 'notify' callback */
+ H5FS__cache_sinfo_notify, /* 'notify' callback */
H5FS__cache_sinfo_free_icr, /* 'free_icr' callback */
NULL, /* 'fsf_size' callback */
}};
@@ -1235,6 +1236,68 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5FS__cache_sinfo_notify
+ *
+ * Purpose: Handle cache action notifications
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: Dana Robinson
+ * Fall 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5FS__cache_sinfo_notify(H5AC_notify_action_t action, void *_thing)
+{
+ H5FS_sinfo_t *sinfo = (H5FS_sinfo_t *)_thing;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* Sanity check */
+ HDassert(sinfo);
+
+ /* Check if the file was opened with SWMR-write access */
+ if(sinfo->fspace->swmr_write) {
+ /* Determine which action to take */
+ switch(action) {
+ case H5AC_NOTIFY_ACTION_AFTER_INSERT:
+ case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ /* Create flush dependency on parent */
+ if(H5FS__create_flush_depend((H5AC_info_t *)sinfo->fspace, (H5AC_info_t *)sinfo) < 0)
+ HGOTO_ERROR(H5E_FSPACE, H5E_CANTDEPEND, FAIL, "unable to create flush dependency between data block and header, address = %llu", (unsigned long long)sinfo->fspace->sect_addr)
+ break;
+
+ case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
+ case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
+ case H5AC_NOTIFY_ACTION_ENTRY_CLEANED:
+ case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
+ case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
+ /* do nothing */
+ break;
+
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ /* Destroy flush dependency on parent */
+ if(H5FS__destroy_flush_depend((H5AC_info_t *)sinfo->fspace, (H5AC_info_t *)sinfo) < 0)
+ HGOTO_ERROR(H5E_FSPACE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency")
+ break;
+
+ default:
+#ifdef NDEBUG
+ HGOTO_ERROR(H5E_FSPACE, H5E_BADVALUE, FAIL, "unknown action from metadata cache")
+#else /* NDEBUG */
+ HDassert(0 && "Unknown action?!?");
+#endif /* NDEBUG */
+ } /* end switch */
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FS__cache_sinfo_notify() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5FS__cache_sinfo_free_icr
*
* Purpose: Free the memory used for the in core representation of the
diff --git a/src/H5FSint.c b/src/H5FSint.c
new file mode 100644
index 0000000..60cedd5
--- /dev/null
+++ b/src/H5FSint.c
@@ -0,0 +1,145 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5FSint.c
+ * Fall 2012
+ * Dana Robinson <derobins@hdfgroup.org>
+ *
+ * Purpose: Internal routines for free space managers.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/**********************/
+/* Module Declaration */
+/**********************/
+
+#include "H5FSmodule.h" /* This source code file is part of the H5FS module */
+
+
+/***********************/
+/* Other Packages Used */
+/***********************/
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error Handling */
+#include "H5FSpkg.h" /* Free Space Managers */
+
+
+/****************/
+/* Local Macros */
+/****************/
+
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+
+/********************/
+/* Package Typedefs */
+/********************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FS__create_flush_depend
+ *
+ * Purpose: Create a flush dependency between two data structure components
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: Dana Robinson
+ * Fall 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5FS__create_flush_depend(H5AC_info_t *parent_entry, H5AC_info_t *child_entry)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* Sanity check */
+ HDassert(parent_entry);
+ HDassert(child_entry);
+
+ /* Create a flush dependency between parent and child entry */
+ if(H5AC_create_flush_dependency(parent_entry, child_entry) < 0)
+ HGOTO_ERROR(H5E_FSPACE, H5E_CANTDEPEND, FAIL, "unable to create flush dependency")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FS__create_flush_depend() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5FS__destroy_flush_depend
+ *
+ * Purpose: Destroy a flush dependency between two data structure components
+ *
+ * Return: SUCCEED/FAIL
+ *
+ * Programmer: Dana Robinson
+ * Fall 2012
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5FS__destroy_flush_depend(H5AC_info_t *parent_entry, H5AC_info_t *child_entry)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* Sanity check */
+ HDassert(parent_entry);
+ HDassert(child_entry);
+
+ /* Destroy a flush dependency between parent and child entry */
+ if(H5AC_destroy_flush_dependency(parent_entry, child_entry) < 0)
+ HGOTO_ERROR(H5E_FSPACE, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5FS__destroy_flush_depend() */
+
diff --git a/src/H5FSpkg.h b/src/H5FSpkg.h
index 7e52b5f..b3b1548 100644
--- a/src/H5FSpkg.h
+++ b/src/H5FSpkg.h
@@ -179,6 +179,7 @@ struct H5FS_t {
haddr_t addr; /* Address of free space header on disk */
size_t hdr_size; /* Size of free space header on disk */
H5FS_sinfo_t *sinfo; /* Section information */
+ hbool_t swmr_write; /* Flag indicating the file is opened with SWMR-write access */
unsigned sinfo_lock_count; /* # of times the section info has been locked */
hbool_t sinfo_protected; /* Whether the section info was protected when locked */
hbool_t sinfo_modified; /* Whether the section info has been modified while locked */
@@ -222,6 +223,12 @@ H5FL_EXTERN(H5FS_t);
/* Package Private Prototypes */
/******************************/
+/* Generic routines */
+H5_DLL herr_t H5FS__create_flush_depend(H5AC_info_t *parent_entry,
+ H5AC_info_t *child_entry);
+H5_DLL herr_t H5FS__destroy_flush_depend(H5AC_info_t *parent_entry,
+ H5AC_info_t *child_entry);
+
/* Free space manager header routines */
H5_DLL H5FS_t *H5FS__new(const H5F_t *f, uint16_t nclasses,
const H5FS_section_class_t *classes[], void *cls_init_udata);
diff --git a/src/H5Faccum.c b/src/H5Faccum.c
index 3fac184..48f9bdd 100644
--- a/src/H5Faccum.c
+++ b/src/H5Faccum.c
@@ -726,6 +726,12 @@ H5F__accum_write(const H5F_io_info_t *fio_info, H5FD_mem_t map_type, haddr_t add
} /* end else */
} /* end if */
else {
+ /* Make certain that data in accumulator is visible before new write */
+ if((H5F_INTENT(fio_info->f) & H5F_ACC_SWMR_WRITE) > 0)
+ /* Flush if dirty and reset accumulator */
+ if(H5F__accum_reset(fio_info, TRUE) < 0)
+ HGOTO_ERROR(H5E_IO, H5E_CANTRESET, FAIL, "can't reset accumulator")
+
/* Write the data */
if(H5FD_write(fio_info->f->shared->lf, fio_info->dxpl, map_type, addr, size, buf) < 0)
HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "file write failed")
diff --git a/src/H5Fint.c b/src/H5Fint.c
index 9cf129c..363812d 100644
--- a/src/H5Fint.c
+++ b/src/H5Fint.c
@@ -641,8 +641,8 @@ H5F_new(H5F_file_t *shared, unsigned flags, hid_t fcpl_id, hid_t fapl_id, H5FD_t
HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get sieve buffer size")
if(H5P_get(plist, H5F_ACS_LATEST_FORMAT_NAME, &latest_format) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get 'latest format' flag")
- /* For latest format, activate all latest version support */
- if(latest_format)
+ /* For latest format or SWMR_WRITE, activate all latest version support */
+ if(latest_format || (H5F_INTENT(f) & H5F_ACC_SWMR_WRITE))
f->shared->latest_flags |= H5F_LATEST_ALL_FLAGS;
if(H5P_get(plist, H5F_ACS_USE_MDC_LOGGING_NAME, &(f->shared->use_mdc_logging)) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get 'use mdc logging' flag")
@@ -672,6 +672,15 @@ H5F_new(H5F_file_t *shared, unsigned flags, hid_t fcpl_id, hid_t fapl_id, H5FD_t
HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, NULL, "bad maximum address from VFD")
if(H5FD_get_feature_flags(lf, &f->shared->feature_flags) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "can't get feature flags from VFD")
+
+ /* Require the SWMR feature flag if SWMR I/O is desired */
+ if(!H5F_HAS_FEATURE(f, H5FD_FEAT_SUPPORTS_SWMR_IO) && (H5F_INTENT(f) & (H5F_ACC_SWMR_WRITE | H5F_ACC_SWMR_READ)))
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, NULL, "must use a SWMR-compatible VFD when SWMR is specified")
+
+ /* Require a POSIX compatible VFD to use SWMR feature */
+ /* (It's reasonable to try to expand this to other VFDs eventually -QAK) */
+ if(!H5F_HAS_FEATURE(f, H5FD_FEAT_POSIX_COMPAT_HANDLE) && (H5F_INTENT(f) & (H5F_ACC_SWMR_WRITE | H5F_ACC_SWMR_READ)))
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, NULL, "must use POSIX compatible VFD with SWMR write access")
if(H5FD_get_fs_type_map(lf, f->shared->fs_type_map) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "can't get free space type mapping from VFD")
if(H5MF_init_merge_flags(f) < 0)
@@ -692,9 +701,23 @@ H5F_new(H5F_file_t *shared, unsigned flags, hid_t fcpl_id, hid_t fapl_id, H5FD_t
if(H5P_get(plist, H5F_ACS_METADATA_READ_ATTEMPTS_NAME, &f->shared->read_attempts) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get the # of read attempts")
- /* If no value for read attempts has been set, use the default */
- if(!f->shared->read_attempts)
- f->shared->read_attempts = H5F_METADATA_READ_ATTEMPTS;
+ /* When opening file with SWMR access, the # of read attempts is H5F_SWMR_METADATA_READ_ATTEMPTS if not set */
+ /* When opening file without SWMR access, the # of read attempts is always H5F_METADATA_READ_ATTEMPTS (set or not set) */
+ if(H5F_INTENT(f) & (H5F_ACC_SWMR_READ | H5F_ACC_SWMR_WRITE)) {
+ /* If no value for read attempts has been set, use the default */
+ if(!f->shared->read_attempts)
+ f->shared->read_attempts = H5F_SWMR_METADATA_READ_ATTEMPTS;
+
+ /* Turn off accumulator with SWMR */
+ f->shared->feature_flags &= ~(unsigned)H5FD_FEAT_ACCUMULATE_METADATA;
+ if(H5FD_set_feature_flags(f->shared->lf, f->shared->feature_flags) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTSET, NULL, "can't set feature_flags in VFD")
+ } /* end if */
+ else {
+ /* If no value for read attempts has been set, use the default */
+ if(!f->shared->read_attempts)
+ f->shared->read_attempts = H5F_METADATA_READ_ATTEMPTS;
+ } /* end else */
/* Determine the # of bins for metdata read retries */
if(H5F_set_retries(f) < 0)
@@ -831,10 +854,19 @@ H5F_dest(H5F_t *f, hid_t dxpl_id, hbool_t flush)
/* Flush the file again (if requested), as shutting down the
* free space manager may dirty some data structures again.
*/
- if(flush)
+ if(flush) {
+ /* Clear status_flags */
+ f->shared->sblock->status_flags &= (uint8_t)(~H5F_SUPER_WRITE_ACCESS);
+ f->shared->sblock->status_flags &= (uint8_t)(~H5F_SUPER_SWMR_WRITE_ACCESS);
+ /* Mark superblock dirty in cache, so change will get encoded */
+ /* Push error, but keep going*/
+ if(H5F_super_dirty(f) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTMARKDIRTY, FAIL, "unable to mark superblock as dirty")
+
if(H5F_flush(f, dxpl_id, TRUE) < 0)
/* Push error, but keep going*/
HDONE_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache")
+ } /* end if */
} /* end if */
/* if it exists, unpin the driver information block cache entry,
@@ -979,6 +1011,37 @@ H5F_dest(H5F_t *f, hid_t dxpl_id, hbool_t flush)
* The ACCESS_PARMS argument is optional. A null pointer will
* cause the default file access parameters to be used.
*
+ * The following two tables show results of file opens for single and concurrent access:
+ *
+ * SINGLE PROCESS ACCESS CONCURRENT ACCESS
+ *
+ * #1st open# #1st open#
+ * -- SR SR -- -- SR SR -- -- SR SR -- -- SR SR --
+ * -- -- SW SW SW SW -- -- -- -- SW SW SW SW -- --
+ * W W W W R R R R W W W W R R R R
+ * #2nd open# #2nd open#
+ * -------------------------- --------------------------
+ * -- -- W | s x x s x x f f | -- -- W | f x x f x x f f |
+ * SR -- W | x x x x x x x x | SR -- W | x x x x x x x x |
+ * SR SW W | x x x x x x x x | SR SW W | x x x x x x x x |
+ * -- SW W | f x x s x x f f | -- SW W | f x x f x x f f |
+ * -- SW R | x x x x x x x x | -- SW R | x x x x x x x x |
+ * SR SW R | x x x x x x x x | SR SW R | x x x x x x x x |
+ * SR -- R | s x x s x x s f | SR -- R | f x x s x x s s |
+ * -- -- R | s x x s x x s s | -- -- R | f x x f x x s s |
+ * -------------------------- --------------------------
+ *
+ * Notations:
+ * W: H5F_ACC_RDWR
+ * R: H5F_ACC_RDONLY
+ * SW: H5F_ACC_SWMR_WRITE
+ * SR: H5F_ACC_SWMR_READ
+ *
+ * x: the first open or second open itself fails due to invalid flags combination
+ * f: the open fails with flags combination from both the first and second opens
+ * s: the open succeeds with flags combination from both the first and second opens
+ *
+ *
* Return: Success: A new file pointer.
* Failure: NULL
*
@@ -998,6 +1061,8 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id,
H5FD_class_t *drvr; /*file driver class info */
H5P_genplist_t *a_plist; /*file access property list */
H5F_close_degree_t fc_degree; /*file close degree */
+ hbool_t set_flag = FALSE; /*set the status_flags in the superblock */
+ hbool_t clear = FALSE; /*clear the status_flags */
hbool_t evict_on_close; /* evict on close value from plist */
H5F_t *ret_value = NULL; /*actual return value */
char *lock_env_var = NULL;/*env var pointer */
@@ -1074,7 +1139,8 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id,
* 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
* exists), or if the new request adds write access (since the
- * readers don't expect the file to change under them).
+ * 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")
@@ -1085,6 +1151,11 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id,
if((flags & H5F_ACC_RDWR) && 0 == (shared->flags & H5F_ACC_RDWR))
HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "file is already open for read-only")
+ if((flags & H5F_ACC_SWMR_WRITE) && 0 == (shared->flags & H5F_ACC_SWMR_WRITE))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "SWMR write access flag not the same for file that is already open")
+ if((flags & H5F_ACC_SWMR_READ) && !((shared->flags & H5F_ACC_SWMR_WRITE) || (shared->flags & H5F_ACC_SWMR_READ) || (shared->flags & H5F_ACC_RDWR)))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "SWMR read access flag not the same for file that is already open")
+
/* Allocate new "high-level" file struct */
if((file = H5F_new(shared, flags, fcpl_id, fapl_id, NULL)) == NULL)
HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to create new file object")
@@ -1116,6 +1187,10 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id,
/* Create the 'top' file structure */
if(NULL == (file = H5F_new(NULL, flags, fcpl_id, fapl_id, lf)))
HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to initialize file structure")
+
+ /* Need to set status_flags in the superblock if the driver has a 'lock' method */
+ if(drvr->lock)
+ set_flag = TRUE;
} /* end else */
/* Retain the name the file was opened with */
@@ -1171,6 +1246,15 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id,
if(H5P_get(a_plist, H5F_ACS_CLOSE_DEGREE_NAME, &fc_degree) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get file close degree")
+ /* This is a private property to clear the status_flags in the super block */
+ /* Use by h5clear and a routine in test/flush2.c to clear the test file's status_flags */
+ if(H5P_exist_plist(a_plist, H5F_ACS_CLEAR_STATUS_FLAGS_NAME) > 0) {
+ if(H5P_get(a_plist, H5F_ACS_CLEAR_STATUS_FLAGS_NAME, &clear) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get clearance for status_flags")
+ else if(clear)
+ file->shared->sblock->status_flags = 0;
+ } /* end if */
+
if(shared->nrefs == 1) {
if(fc_degree == H5F_CLOSE_DEFAULT)
shared->fc_degree = lf->cls->fc_degree;
@@ -1207,6 +1291,53 @@ H5F_open(const char *name, unsigned flags, hid_t fcpl_id, hid_t fapl_id,
if(H5F_build_actual_name(file, a_plist, name, &file->actual_name) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "unable to build actual name")
+ if(set_flag) {
+ if(H5F_INTENT(file) & H5F_ACC_RDWR) { /* Set and 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(file->shared->sblock->status_flags & H5F_SUPER_WRITE_ACCESS ||
+ file->shared->sblock->status_flags & H5F_SUPER_SWMR_WRITE_ACCESS)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "file is already open for write/SWMR write (may use <h5clear file> to clear file consistency flags)")
+ } /* version 3 superblock */
+
+ file->shared->sblock->status_flags |= H5F_SUPER_WRITE_ACCESS;
+ if(H5F_INTENT(file) & H5F_ACC_SWMR_WRITE)
+ file->shared->sblock->status_flags |= H5F_SUPER_SWMR_WRITE_ACCESS;
+
+ /* Flush the superblock */
+ if(H5F_super_dirty(file) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTMARKDIRTY, NULL, "unable to mark superblock as dirty")
+ if(H5F_flush_tagged_metadata(file, H5AC__SUPERBLOCK_TAG, H5AC_ind_read_dxpl_id) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, NULL, "unable to flush superblock")
+
+ /* Remove the file lock for SWMR_WRITE */
+ if(use_file_locking && (H5F_INTENT(file) & H5F_ACC_SWMR_WRITE)) {
+ if(H5FD_unlock(file->shared->lf) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "unable to unlock the file")
+ } /* end if */
+ } /* end if */
+ 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((file->shared->sblock->status_flags & H5F_SUPER_WRITE_ACCESS &&
+ !(file->shared->sblock->status_flags & H5F_SUPER_SWMR_WRITE_ACCESS))
+ ||
+ (!(file->shared->sblock->status_flags & H5F_SUPER_WRITE_ACCESS) &&
+ file->shared->sblock->status_flags & H5F_SUPER_SWMR_WRITE_ACCESS))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "file is not already open for SWMR writing")
+
+ } /* end if */
+ else if((file->shared->sblock->status_flags & H5F_SUPER_WRITE_ACCESS) ||
+ (file->shared->sblock->status_flags & H5F_SUPER_SWMR_WRITE_ACCESS))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, NULL, "file is already open for write (may use <h5clear file> to clear file consistency flags)")
+
+ } /* version 3 superblock */
+ } /* end else */
+ } /* end if set_flag */
+
/* Success */
ret_value = file;
@@ -2149,6 +2280,8 @@ H5F_get_file_image(H5F_t *file, void *buf_ptr, size_t buf_len, hid_t dxpl_id)
/* test to see if a buffer was provided -- if not, we are done */
if(buf_ptr != NULL) {
size_t space_needed; /* size of file image */
+ hsize_t tmp;
+ size_t tmp_size;
H5P_genplist_t *xfer_plist= NULL; /* Dataset transfer property list object */
/* Check for buffer too small */
@@ -2165,6 +2298,15 @@ H5F_get_file_image(H5F_t *file, void *buf_ptr, size_t buf_len, hid_t dxpl_id)
/* (Note compensation for base address addition in internal routine) */
if(H5FD_read(fd_ptr, xfer_plist, H5FD_MEM_DEFAULT, 0, space_needed, buf_ptr) < 0)
HGOTO_ERROR(H5E_FILE, H5E_READERROR, FAIL, "file image read request failed")
+
+ /* Offset to "status_flags" in the superblock */
+ tmp = H5F_SUPER_STATUS_FLAGS_OFF(file->shared->sblock->super_vers);
+ /* Size of "status_flags" depends on the superblock version */
+ tmp_size = H5F_SUPER_STATUS_FLAGS_SIZE(file->shared->sblock->super_vers);
+
+ /* Clear "status_flags" */
+ HDmemset((uint8_t *)(buf_ptr) + tmp, 0, tmp_size);
+
} /* end if */
done:
diff --git a/src/H5Fio.c b/src/H5Fio.c
index 273ee74..e215666 100644
--- a/src/H5Fio.c
+++ b/src/H5Fio.c
@@ -264,26 +264,70 @@ H5F_evict_tagged_metadata(H5F_t * f, haddr_t tag, hid_t dxpl_id)
FUNC_ENTER_NOAPI(FAIL)
- /* Unpin the superblock, as this will be marked for eviction and it can't
- be pinned. */
- if(H5AC_unpin_entry(f->shared->sblock) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "unable to unpin superblock")
- f->shared->sblock = NULL;
-
/* Evict the object's metadata */
if(H5AC_evict_tagged_metadata(f, tag, TRUE, dxpl_id) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, "unable to evict tagged metadata")
- /* Re-read the superblock. */
- if(H5F__super_read(f, dxpl_id, FALSE) < 0)
- HGOTO_ERROR(H5E_FILE, H5E_READERROR, FAIL, "unable to read superblock")
-
done:
FUNC_LEAVE_NOAPI(ret_value);
} /* end H5F_evict_tagged_metadata */
/*-------------------------------------------------------------------------
+ * Function: H5F__evict_cache_entries
+ *
+ * Purpose: To evict all cache entries except the pinned superblock entry
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Vailin Choi
+ * Dec 2013
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F__evict_cache_entries(H5F_t *f, hid_t dxpl_id)
+{
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_PACKAGE
+
+ HDassert(f);
+ HDassert(f->shared);
+
+ /* Evict all except pinned entries in the cache */
+ if(H5AC_evict(f, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, "unable to evict all except pinned entries")
+
+#ifndef NDEBUG
+{
+ unsigned status = 0;
+ int32_t cur_num_entries;
+
+ /* Retrieve status of the superblock */
+ if(H5AC_get_entry_status(f, (haddr_t)0, &status) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "unable to get entry status")
+
+ /* Verify status of the superblock entry in the cache */
+ if(!(status & H5AC_ES__IN_CACHE) || !(status & H5AC_ES__IS_PINNED))
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "unable to get entry status")
+
+ /* Get the number of cache entries */
+ if(H5AC_get_cache_size(f->shared->cache, NULL, NULL, NULL, &cur_num_entries) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5AC_get_cache_size() failed.")
+
+ /* Should be the only one left in the cache (the superblock) */
+ if(cur_num_entries != 1)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "number of cache entries is not correct")
+}
+#endif /* NDEBUG */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value);
+} /* end H5F__evict_cache_entries() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5F_get_checksums
*
* Purpose: Decode checksum stored in the buffer
diff --git a/src/H5Fpkg.h b/src/H5Fpkg.h
index e9c45f3..1adf74b 100644
--- a/src/H5Fpkg.h
+++ b/src/H5Fpkg.h
@@ -56,10 +56,11 @@
/* Superblock status flags */
#define H5F_SUPER_WRITE_ACCESS 0x01
#define H5F_SUPER_FILE_OK 0x02
-#define H5F_SUPER_ALL_FLAGS (H5F_SUPER_WRITE_ACCESS | H5F_SUPER_FILE_OK)
+#define H5F_SUPER_SWMR_WRITE_ACCESS 0x04
+#define H5F_SUPER_ALL_FLAGS (H5F_SUPER_WRITE_ACCESS | H5F_SUPER_FILE_OK | H5F_SUPER_SWMR_WRITE_ACCESS)
/* Mask for removing private file access flags */
-#define H5F_ACC_PUBLIC_FLAGS 0x001fu
+#define H5F_ACC_PUBLIC_FLAGS 0x007fu
/* Free space section+aggregator merge flags */
#define H5F_FS_MERGE_METADATA 0x01 /* Section can merge with metadata aggregator */
@@ -131,12 +132,28 @@
#define H5F_SUPERBLOCK_VARLEN_SIZE(v, sizeof_addr, sizeof_size) ( \
(v == 0 ? H5F_SUPERBLOCK_VARLEN_SIZE_V0(sizeof_addr, sizeof_size) : 0) \
+ (v == 1 ? H5F_SUPERBLOCK_VARLEN_SIZE_V1(sizeof_addr, sizeof_size) : 0) \
- + (v == 2 ? H5F_SUPERBLOCK_VARLEN_SIZE_V2(sizeof_addr) : 0))
+ + (v >= 2 ? H5F_SUPERBLOCK_VARLEN_SIZE_V2(sizeof_addr) : 0))
/* Total size of superblock, depends on superblock version */
#define H5F_SUPERBLOCK_SIZE(s) ( H5F_SUPERBLOCK_FIXED_SIZE \
+ H5F_SUPERBLOCK_VARLEN_SIZE((s)->super_vers, (s)->sizeof_addr, (s)->sizeof_size))
+/* For superblock version 0 & 1:
+ Offset to the file consistency flags (status_flags) in the superblock (excluding H5F_SUPERBLOCK_FIXED_SIZE) */
+#define H5F_SUPER_STATUS_OFF_V01 \
+ (2 /* freespace, and root group versions */ \
+ + 1 /* reserved */ \
+ + 3 /* shared header vers, size of address, size of lengths */ \
+ + 1 /* reserved */ \
+ + 4) /* group leaf k, group internal k */
+
+#define H5F_SUPER_STATUS_OFF(v) (v >= 2 ? 2 : H5F_SUPER_STATUS_OFF_V01)
+
+/* Offset to the file consistency flags (status_flags) in the superblock */
+#define H5F_SUPER_STATUS_FLAGS_OFF(v) (H5F_SUPERBLOCK_FIXED_SIZE + H5F_SUPER_STATUS_OFF(v))
+
+/* Size of file consistency flags (status_flags) in the superblock */
+#define H5F_SUPER_STATUS_FLAGS_SIZE(v) (v >= 2 ? 1 : 4)
/* Forward declaration external file cache struct used below (defined in
* H5Fefc.c) */
@@ -406,6 +423,9 @@ H5_DLL herr_t H5F_efc_try_close(H5F_t *f);
H5_DLL herr_t H5F__set_eoa(const H5F_t *f, H5F_mem_t type, haddr_t addr);
H5_DLL herr_t H5F__set_base_addr(const H5F_t *f, haddr_t addr);
+/* Functions that flush or evict */
+H5_DLL herr_t H5F__evict_cache_entries(H5F_t *f, hid_t dxpl_id);
+
/* Testing functions */
#ifdef H5F_TESTING
H5_DLL herr_t H5F_get_sohm_mesg_count_test(hid_t fid, unsigned type_id,
diff --git a/src/H5Fprivate.h b/src/H5Fprivate.h
index 50cd763..a6d1c4a 100644
--- a/src/H5Fprivate.h
+++ b/src/H5Fprivate.h
@@ -473,6 +473,7 @@
#define H5F_ACS_OBJECT_FLUSH_CB_NAME "object_flush_cb" /* Object flush callback */
#define H5F_ACS_EFC_SIZE_NAME "efc_size" /* Size of external file cache */
#define H5F_ACS_FILE_IMAGE_INFO_NAME "file_image_info" /* struct containing initial file image and callback info */
+#define H5F_ACS_CLEAR_STATUS_FLAGS_NAME "clear_status_flags" /* Whether to clear superblock status_flags (private property only used by h5clear) */
#define H5F_ACS_USE_MDC_LOGGING_NAME "use_mdc_logging" /* Whether to use metadata cache logging */
#define H5F_ACS_MDC_LOG_LOCATION_NAME "mdc_log_location" /* Name of metadata cache log location */
#define H5F_ACS_START_MDC_LOG_ON_ACCESS_NAME "start_mdc_log_on_access" /* Whether logging starts on file create/open */
@@ -498,7 +499,8 @@
#define HDF5_SUPERBLOCK_VERSION_DEF 0 /* The default super block format */
#define HDF5_SUPERBLOCK_VERSION_1 1 /* Version with non-default B-tree 'K' value */
#define HDF5_SUPERBLOCK_VERSION_2 2 /* Revised version with superblock extension and checksum */
-#define HDF5_SUPERBLOCK_VERSION_LATEST HDF5_SUPERBLOCK_VERSION_2 /* The maximum super block format */
+#define HDF5_SUPERBLOCK_VERSION_3 3 /* With file locking and consistency flags (at least this version for SWMR support) */
+#define HDF5_SUPERBLOCK_VERSION_LATEST HDF5_SUPERBLOCK_VERSION_3 /* The maximum super block format */
#define HDF5_SUPERBLOCK_VERSION_V18_LATEST HDF5_SUPERBLOCK_VERSION_2 /* The latest superblock version for v18 */
#define HDF5_FREESPACE_VERSION 0 /* of the Free-Space Info */
#define HDF5_OBJECTDIR_VERSION 0 /* of the Object Directory format */
@@ -523,6 +525,7 @@
/* 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 */
/* Macros to define signatures of all objects in the file */
diff --git a/src/H5Fpublic.h b/src/H5Fpublic.h
index 018618e..a79da75 100644
--- a/src/H5Fpublic.h
+++ b/src/H5Fpublic.h
@@ -55,6 +55,23 @@
#define H5F_ACC_EXCL (H5CHECK H5OPEN 0x0004u) /*fail if file already exists*/
/* NOTE: 0x0008u was H5F_ACC_DEBUG, now deprecated */
#define H5F_ACC_CREAT (H5CHECK H5OPEN 0x0010u) /*create non-existing files */
+#define H5F_ACC_SWMR_WRITE (H5CHECK 0x0020u) /*indicate that this file is
+ * open for writing in a
+ * single-writer/multi-reader (SWMR)
+ * scenario. Note that the
+ * process(es) opening the file
+ * for reading must open the file
+ * with RDONLY access, and use
+ * the special "SWMR_READ" access
+ * flag. */
+#define H5F_ACC_SWMR_READ (H5CHECK 0x0040u) /*indicate that this file is
+ * open for reading in a
+ * single-writer/multi-reader (SWMR)
+ * scenario. Note that the
+ * process(es) opening the file
+ * for SWMR reading must also
+ * open the file with the RDONLY
+ * flag. */
/* Value passed to H5Pset_elink_acc_flags to cause flags to be taken from the
* parent file. */
@@ -225,6 +242,7 @@ H5_DLL herr_t H5Freset_mdc_hit_rate_stats(hid_t file_id);
H5_DLL ssize_t H5Fget_name(hid_t obj_id, char *name, size_t size);
H5_DLL herr_t H5Fget_info2(hid_t obj_id, H5F_info2_t *finfo);
H5_DLL herr_t H5Fget_metadata_read_retry_info(hid_t file_id, H5F_retry_info_t *info);
+H5_DLL herr_t H5Fstart_swmr_write(hid_t file_id);
H5_DLL ssize_t H5Fget_free_sections(hid_t file_id, H5F_mem_t type,
size_t nsects, H5F_sect_info_t *sect_info/*out*/);
H5_DLL herr_t H5Fclear_elink_file_cache(hid_t file_id);
diff --git a/src/H5Fsuper.c b/src/H5Fsuper.c
index 489a149..2a82618 100644
--- a/src/H5Fsuper.c
+++ b/src/H5Fsuper.c
@@ -250,6 +250,7 @@ H5F__super_read(H5F_t *f, hid_t dxpl_id, hbool_t initial_read)
haddr_t super_addr; /* Absolute address of superblock */
haddr_t eof; /* End of file address */
unsigned rw_flags; /* Read/write permissions for file */
+ hbool_t skip_eof_check = FALSE; /* Whether to skip checking the EOF value */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_PACKAGE_TAG(dxpl_id, H5AC__SUPERBLOCK_TAG, FAIL)
@@ -317,6 +318,14 @@ H5F__super_read(H5F_t *f, hid_t dxpl_id, hbool_t initial_read)
if(NULL == (sblock = (H5F_super_t *)H5AC_protect(f, dxpl_id, H5AC_SUPERBLOCK, (haddr_t)0, &udata, rw_flags)))
HGOTO_ERROR(H5E_FILE, H5E_CANTPROTECT, FAIL, "unable to load superblock")
+ if(H5F_INTENT(f) & H5F_ACC_SWMR_WRITE)
+ if(sblock->super_vers < HDF5_SUPERBLOCK_VERSION_3)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTPROTECT, FAIL, "invalid superblock version for SWMR_WRITE")
+
+ /* Enable all latest version support when file has v3 superblock */
+ if(sblock->super_vers >= HDF5_SUPERBLOCK_VERSION_3)
+ f->shared->latest_flags |= H5F_LATEST_ALL_FLAGS;
+
/* Pin the superblock in the cache */
if(H5AC_pin_protected_entry(sblock) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTPIN, FAIL, "unable to pin superblock")
@@ -410,7 +419,22 @@ H5F__super_read(H5F_t *f, hid_t dxpl_id, hbool_t initial_read)
* Note: the aggregator is changed again after being reset
* earlier before H5AC_flush due to allocation of tmp addresses.
*/
- if(initial_read) {
+ /* The EOF check must be skipped when the file is opened for SWMR read,
+ * as the file can appear truncated if only part of it has been
+ * been flushed to disk by the SWMR writer process.
+ */
+ if(H5F_INTENT(f) & H5F_ACC_SWMR_READ) {
+ /*
+ * When the file is opened for SWMR read access, skip the check if:
+ * --the file is already marked for SWMR writing and
+ * --the file has version 3 superblock for SWMR support
+ */
+ if((sblock->status_flags & H5F_SUPER_SWMR_WRITE_ACCESS) &&
+ (sblock->status_flags & H5F_SUPER_WRITE_ACCESS) &&
+ sblock->super_vers >= HDF5_SUPERBLOCK_VERSION_3)
+ skip_eof_check = TRUE;
+ } /* end if */
+ if(!skip_eof_check && initial_read) {
if(HADDR_UNDEF == (eof = H5FD_get_eof(f->shared->lf, H5FD_MEM_DEFAULT)))
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to determine file size")
diff --git a/src/H5Gent.c b/src/H5Gent.c
index 6020028..f64eaf0 100644
--- a/src/H5Gent.c
+++ b/src/H5Gent.c
@@ -460,7 +460,7 @@ H5G__ent_convert(H5F_t *f, hid_t dxpl_id, H5HL_t *heap, const char *name,
targ_oloc.addr = lnk->u.hard.addr;
/* Get the object header */
- if(NULL == (oh = H5O_protect(&targ_oloc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(&targ_oloc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_SYM, H5E_CANTPROTECT, FAIL, "unable to protect target object header")
/* Check if a symbol table message exists */
diff --git a/src/H5Gtest.c b/src/H5Gtest.c
index d69a804..0e0a897 100644
--- a/src/H5Gtest.c
+++ b/src/H5Gtest.c
@@ -701,7 +701,7 @@ H5G_verify_cached_stabs_test_cb(H5F_t *f, hid_t dxpl_id,
targ_oloc.addr = sn->entry[i].header;
/* Load target object header */
- if(NULL == (targ_oh = H5O_protect(&targ_oloc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (targ_oh = H5O_protect(&targ_oloc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_SYM, H5E_CANTPROTECT, H5_ITER_ERROR, "unable to protect target object header")
/* Check if a symbol table message exists */
diff --git a/src/H5Lexternal.c b/src/H5Lexternal.c
index 12cce73..7f59e50 100644
--- a/src/H5Lexternal.c
+++ b/src/H5Lexternal.c
@@ -323,7 +323,7 @@ H5L_extern_traverse(const char H5_ATTR_UNUSED *link_name, hid_t cur_group,
*/
/* Simplify intent flags for open calls */
- intent = ((intent & H5F_ACC_RDWR) ? H5F_ACC_RDWR : H5F_ACC_RDONLY);
+ intent &= (H5F_ACC_RDWR | H5F_ACC_SWMR_WRITE | H5F_ACC_SWMR_READ);
/* Copy the file name to use */
if(NULL == (temp_file_name = H5MM_strdup(file_name)))
diff --git a/src/H5O.c b/src/H5O.c
index b6f399d..0a4e4eb 100644
--- a/src/H5O.c
+++ b/src/H5O.c
@@ -1281,6 +1281,7 @@ H5O_create(H5F_t *f, hid_t dxpl_id, size_t size_hint, size_t initial_rc,
oh->version = H5O_VERSION_1;
oh->sizeof_size = H5F_SIZEOF_SIZE(f);
oh->sizeof_addr = H5F_SIZEOF_ADDR(f);
+ oh->swmr_write = !!(H5F_INTENT(f) & H5F_ACC_SWMR_WRITE);
#ifdef H5O_ENABLE_BAD_MESG_COUNT
/* Check whether the "bad message count" property is set */
if(H5P_exist_plist(oc_plist, H5O_BAD_MESG_COUNT_NAME) > 0) {
@@ -1290,6 +1291,15 @@ H5O_create(H5F_t *f, hid_t dxpl_id, size_t size_hint, size_t initial_rc,
} /* end if */
#endif /* H5O_ENABLE_BAD_MESG_COUNT */
+ /* Create object header proxy if doing SWMR writes */
+ if(oh->swmr_write) {
+ /* Create virtual entry, for use as proxy */
+ if(NULL == (oh->proxy = H5AC_proxy_entry_create()))
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTCREATE, FAIL, "can't create object header proxy")
+ } /* end if */
+ else
+ oh->proxy = NULL;
+
/* Set initial status flags */
oh->flags = oh_flags;
@@ -1356,6 +1366,7 @@ H5O_create(H5F_t *f, hid_t dxpl_id, size_t size_hint, size_t initial_rc,
/* (including space for serializing the object header prefix */
if(NULL == (oh->chunk[0].image = H5FL_BLK_CALLOC(chunk_image, oh_size)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed")
+ oh->chunk[0].chunk_proxy = NULL;
/* Put magic # for object header in first chunk */
if(oh->version > H5O_VERSION_1)
@@ -1791,7 +1802,8 @@ done:
*-------------------------------------------------------------------------
*/
H5O_t *
-H5O_protect(const H5O_loc_t *loc, hid_t dxpl_id, unsigned prot_flags)
+H5O_protect(const H5O_loc_t *loc, hid_t dxpl_id, unsigned prot_flags,
+ hbool_t pin_all_chunks)
{
H5O_t *oh = NULL; /* Object header protected */
H5O_cache_ud_t udata; /* User data for protecting object header */
@@ -1915,6 +1927,39 @@ H5O_protect(const H5O_loc_t *loc, hid_t dxpl_id, unsigned prot_flags)
H5O_assert(oh);
#endif /* H5O_DEBUG */
+ /* Pin the other chunks also when requested, so that the object header
+ * proxy can be set up.
+ */
+ if(pin_all_chunks && oh->nchunks > 1) {
+ unsigned u; /* Local index variable */
+
+ /* Sanity check */
+ HDassert(oh->swmr_write);
+
+ /* Iterate over chunks > 0 */
+ for(u = 1; u < oh->nchunks; u++) {
+ H5O_chunk_proxy_t *chk_proxy; /* Chunk proxy */
+
+ /* Protect chunk */
+ if(NULL == (chk_proxy = H5O_chunk_protect(loc->file, dxpl_id, oh, u)))
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, NULL, "unable to protect object header chunk")
+
+ /* Pin chunk proxy*/
+ if(H5AC_pin_protected_entry(chk_proxy) < 0 )
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTPIN, NULL, "unable to pin object header chunk")
+
+ /* Unprotect chunk */
+ if(H5O_chunk_unprotect(loc->file, dxpl_id, chk_proxy, FALSE) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, NULL, "unable to unprotect object header chunk")
+
+ /* Preserve chunk proxy pointer for later */
+ oh->chunk[u].chunk_proxy = chk_proxy;
+ } /* end for */
+
+ /* Set the flag for the unprotect */
+ oh->chunks_pinned = TRUE;
+ } /* end if */
+
/* Set return value */
ret_value = oh;
@@ -1956,7 +2001,7 @@ H5O_pin(const H5O_loc_t *loc, hid_t dxpl_id)
HDassert(loc);
/* Get header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__NO_FLAGS_SET)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__NO_FLAGS_SET, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, NULL, "unable to protect object header")
/* Increment the reference count on the object header */
@@ -2038,6 +2083,27 @@ H5O_unprotect(const H5O_loc_t *loc, hid_t dxpl_id, H5O_t *oh, unsigned oh_flags)
HDassert(loc);
HDassert(oh);
+ /* Unpin the other chunks */
+ if(oh->chunks_pinned && oh->nchunks > 1) {
+ unsigned u; /* Local index variable */
+
+ /* Sanity check */
+ HDassert(oh->swmr_write);
+
+ /* Iterate over chunks > 0 */
+ for(u = 1; u < oh->nchunks; u++) {
+ if(NULL != oh->chunk[u].chunk_proxy) {
+ /* Release chunk proxy */
+ if(H5AC_unpin_entry(oh->chunk[u].chunk_proxy) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPIN, FAIL, "unable to unpin object header chunk")
+ oh->chunk[u].chunk_proxy = NULL;
+ } /* end if */
+ } /* end for */
+
+ /* Reet the flag from the unprotect */
+ oh->chunks_pinned = FALSE;
+ } /* end if */
+
/* Unprotect the object header */
if(H5AC_unprotect(loc->file, dxpl_id, H5AC_OHDR, oh->chunk[0].addr, oh, oh_flags) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to release object header")
@@ -2169,7 +2235,7 @@ H5O_touch(const H5O_loc_t *loc, hbool_t force, hid_t dxpl_id)
HDassert(loc);
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__NO_FLAGS_SET)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__NO_FLAGS_SET, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Create/Update the modification time message */
@@ -2295,7 +2361,7 @@ H5O_delete(H5F_t *f, hid_t dxpl_id, haddr_t addr)
loc.holding_file = FALSE;
/* Get the object header information */
- if(NULL == (oh = H5O_protect(&loc, dxpl_id, H5AC__NO_FLAGS_SET)))
+ if(NULL == (oh = H5O_protect(&loc, dxpl_id, H5AC__NO_FLAGS_SET, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Delete object */
@@ -2386,7 +2452,7 @@ H5O_obj_type(const H5O_loc_t *loc, H5O_type_t *obj_type, hid_t dxpl_id)
FUNC_ENTER_NOAPI_TAG(dxpl_id, loc->addr, FAIL)
/* Load the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Retrieve the type of the object */
@@ -2464,7 +2530,7 @@ H5O_obj_class(const H5O_loc_t *loc, hid_t dxpl_id)
FUNC_ENTER_NOAPI_NOINIT_TAG(dxpl_id, loc->addr, NULL)
/* Load the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, NULL, "unable to load object header")
/* Test whether entry qualifies as a particular type of object */
@@ -2761,7 +2827,7 @@ H5O_get_hdr_info(const H5O_loc_t *loc, hid_t dxpl_id, H5O_hdr_info_t *hdr)
HDmemset(hdr, 0, sizeof(*hdr));
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to load object header")
/* Get the information for the object header */
@@ -2885,7 +2951,7 @@ H5O_get_info(const H5O_loc_t *loc, hid_t dxpl_id, hbool_t want_ih_info,
HDassert(oinfo);
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Reset the object info structure */
@@ -3006,7 +3072,7 @@ H5O_get_create_plist(const H5O_loc_t *loc, hid_t dxpl_id, H5P_genplist_t *oc_pli
HDassert(oc_plist);
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Set property values, if they were used for the object */
@@ -3061,7 +3127,7 @@ H5O_get_nlinks(const H5O_loc_t *loc, hid_t dxpl_id, hsize_t *nlinks)
HDassert(nlinks);
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Retrieve the # of link messages seen when the object header was loaded */
@@ -3178,7 +3244,7 @@ H5O_get_rc_and_type(const H5O_loc_t *loc, hid_t dxpl_id, unsigned *rc, H5O_type_
HDassert(loc);
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Set the object's reference count */
@@ -3551,7 +3617,7 @@ H5O_dec_rc_by_loc(const H5O_loc_t *loc, hid_t dxpl_id)
HDassert(loc);
/* Get header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header")
/* Decrement the reference count on the object header */
@@ -3569,6 +3635,30 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5O_get_proxy
+ *
+ * Purpose: Retrieve the proxy for the object header.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * July 24 2016
+ *
+ *-------------------------------------------------------------------------
+ */
+H5AC_proxy_entry_t *
+H5O_get_proxy(const H5O_t *oh)
+{
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Check args */
+ HDassert(oh);
+
+ FUNC_LEAVE_NOAPI(oh->proxy)
+} /* end H5O_get_proxy() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5O__free
*
* Purpose: Destroys an object header.
@@ -3585,8 +3675,9 @@ herr_t
H5O__free(H5O_t *oh)
{
unsigned u; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_PACKAGE_NOERR
+ FUNC_ENTER_PACKAGE
/* check args */
HDassert(oh);
@@ -3621,9 +3712,15 @@ H5O__free(H5O_t *oh)
oh->mesg = (H5O_mesg_t *)H5FL_SEQ_FREE(H5O_mesg_t, oh->mesg);
} /* end if */
+ /* Destroy the proxy */
+ if(oh->proxy)
+ if(H5AC_proxy_entry_dest(oh->proxy) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to destroy virtual entry used for proxy")
+
/* destroy object header */
oh = H5FL_FREE(H5O_t, oh);
- FUNC_LEAVE_NOAPI(SUCCEED)
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
} /* end H5O__free() */
diff --git a/src/H5Oalloc.c b/src/H5Oalloc.c
index d22fd92..4f98cfa 100644
--- a/src/H5Oalloc.c
+++ b/src/H5Oalloc.c
@@ -745,9 +745,8 @@ H5O__alloc_find_best_nonnull(const H5F_t *f, const H5O_t *oh, size_t *size,
size_t cont_size; /* Continuation message size */
size_t multi_size; /* Size of all the messages in the last chunk */
unsigned u; /* Local index variable */
- herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_STATIC_NOERR
/* Check args */
HDassert(f);
@@ -865,8 +864,7 @@ H5O__alloc_find_best_nonnull(const H5F_t *f, const H5O_t *oh, size_t *size,
else
*size += (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + oh->mesg[found_msg->msgno].raw_size;
-done:
- FUNC_LEAVE_NOAPI(ret_value)
+ FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5O__alloc_find_best_nonnull() */
@@ -949,6 +947,7 @@ H5O__alloc_chunk(H5F_t *f, hid_t dxpl_id, H5O_t *oh, size_t size,
oh->chunk[chunkno].gap = 0;
if(NULL == (oh->chunk[chunkno].image = p = H5FL_BLK_CALLOC(chunk_image, size)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "can't allocate image for chunk, size = %zu", size)
+ oh->chunk[chunkno].chunk_proxy = NULL;
/* If this is a later version of the object header format, put the magic
* # at the beginning of the chunk image.
@@ -1221,10 +1220,9 @@ static herr_t
H5O__alloc_find_best_null(const H5O_t *oh, size_t size, size_t *mesg_idx)
{
size_t idx; /* Index of message which fits allocation */
- int found_null; /* Best fit null message */
- herr_t ret_value = SUCCEED; /* Return value */
+ ssize_t found_null; /* Best fit null message */
- FUNC_ENTER_STATIC
+ FUNC_ENTER_STATIC_NOERR
/* check args */
HDassert(oh);
@@ -1239,11 +1237,11 @@ H5O__alloc_find_best_null(const H5O_t *oh, size_t size, size_t *mesg_idx)
if(oh->mesg[idx].raw_size == size) {
/* Keep first exact fit */
if(found_null < 0)
- found_null = idx;
+ found_null = (ssize_t)idx;
else
/* If we've got more than one exact fit, choose the one in the earliest chunk */
if(oh->mesg[idx].chunkno < oh->mesg[found_null].chunkno) {
- found_null = idx;
+ found_null = (ssize_t)idx;
/* If we found an exact fit in object header chunk #0, we can get out */
/* (Could extend this to look for earliest message in
@@ -1257,29 +1255,26 @@ H5O__alloc_find_best_null(const H5O_t *oh, size_t size, size_t *mesg_idx)
else if(oh->mesg[idx].raw_size > size) {
/* Keep first one found */
if(found_null < 0)
- found_null = idx;
+ found_null = (ssize_t)idx;
else
/* Check for better fit */
if(oh->mesg[idx].raw_size < oh->mesg[found_null].raw_size)
- found_null = idx;
+ found_null = (ssize_t)idx;
else {
/* If they are the same size, choose the one in the earliest chunk */
if(oh->mesg[idx].raw_size == oh->mesg[found_null].raw_size) {
if(oh->mesg[idx].chunkno < oh->mesg[found_null].chunkno)
- found_null = idx;
+ found_null = (ssize_t)idx;
} /* end if */
} /* end else */
} /* end else-if */
- /* Ignore too-small null messages */
- else
- ;
+ /* else: Ignore too-small null messages */
} /* end if */
} /* end for */
if(found_null >= 0)
- *mesg_idx = found_null;
+ *mesg_idx = (size_t)found_null;
-done:
- FUNC_LEAVE_NOAPI(ret_value)
+ FUNC_LEAVE_NOAPI(SUCCEED)
} /* H5O__alloc_find_best_null() */
@@ -1627,6 +1622,7 @@ H5O_move_msgs_forward(H5F_t *f, hid_t dxpl_id, H5O_t *oh)
{
H5O_chunk_proxy_t *null_chk_proxy = NULL; /* Chunk that null message is in */
H5O_chunk_proxy_t *curr_chk_proxy = NULL; /* Chunk that message is in */
+ H5O_chunk_proxy_t *cont_targ_chk_proxy = NULL; /* Chunk that continuation message points to */
hbool_t null_chk_dirtied = FALSE; /* Flags for unprotecting null chunk */
hbool_t curr_chk_dirtied = FALSE; /* Flags for unprotecting curr chunk */
hbool_t packed_msg; /* Flag to indicate that messages were packed */
@@ -1741,6 +1737,60 @@ H5O_move_msgs_forward(H5F_t *f, hid_t dxpl_id, H5O_t *oh)
if(NULL == (curr_chk_proxy = H5O_chunk_protect(f, dxpl_id, oh, curr_msg->chunkno)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk")
+ /* If the message being moved is a continuation
+ * message and we are doing SWMR writes, we must
+ * update the flush dependencies */
+ if(oh->swmr_write && (H5O_CONT_ID == curr_msg->type->id)) {
+ void *null_chk_mdc_obj; /* The metadata cache object for the null_msg chunk */
+
+ /* Point to the metadata cache object for the
+ * null message chunk, oh if in chunk 0 or the
+ * proxy otherwise */
+ null_chk_mdc_obj = (null_msg->chunkno == 0
+ ? (void *)oh
+ : (void *)null_chk_proxy);
+
+ /* The other chunks involved should never be
+ * chunk 0 */
+ HDassert(curr_msg->chunkno > 0);
+ HDassert(((H5O_cont_t *)(curr_msg->native))->chunkno > 0);
+
+ /* Protect continuation message target chunk */
+ if(NULL == (cont_targ_chk_proxy = H5O_chunk_protect(f, dxpl_id, oh, ((H5O_cont_t *)(curr_msg->native))->chunkno)))
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk")
+
+ /* Remove flush dependency on old continuation
+ * message chunk */
+ HDassert(cont_targ_chk_proxy);
+ HDassert(cont_targ_chk_proxy->parent);
+ HDassert(curr_chk_proxy);
+ HDassert((void *)curr_chk_proxy == cont_targ_chk_proxy->parent);
+
+ if(H5AC_destroy_flush_dependency(curr_chk_proxy, cont_targ_chk_proxy) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency")
+
+ cont_targ_chk_proxy->parent = NULL;
+
+ /* Create flush dependency on new continuation
+ * message chunk */
+ if(H5AC_create_flush_dependency(null_chk_mdc_obj, cont_targ_chk_proxy) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTDEPEND, FAIL, "unable to create flush dependency")
+
+ HDassert(null_chk_mdc_obj);
+ HDassert(((H5C_cache_entry_t *)null_chk_mdc_obj)->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(((H5C_cache_entry_t *)null_chk_mdc_obj)->type);
+ HDassert((((H5C_cache_entry_t *)null_chk_mdc_obj)->type->id == H5AC_OHDR_ID) ||
+ (((H5C_cache_entry_t *)null_chk_mdc_obj)->type->id == H5AC_OHDR_CHK_ID));
+
+ cont_targ_chk_proxy->parent = null_chk_mdc_obj;
+
+ /* Unprotect continuation message target chunk
+ */
+ if(H5O_chunk_unprotect(f, dxpl_id, cont_targ_chk_proxy, FALSE) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect object header chunk")
+ cont_targ_chk_proxy = NULL;
+ } /* end if */
+
/* Copy raw data for non-null message to new chunk */
HDmemcpy(null_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh), curr_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh), curr_msg->raw_size + (size_t)H5O_SIZEOF_MSGHDR_OH(oh));
@@ -1889,9 +1939,11 @@ done:
HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect null object header chunk")
if(curr_chk_proxy && H5O_chunk_unprotect(f, dxpl_id, curr_chk_proxy, curr_chk_dirtied) < 0)
HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect current object header chunk")
+ if(cont_targ_chk_proxy && H5O_chunk_unprotect(f, dxpl_id, cont_targ_chk_proxy, FALSE) < 0)
+ HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to unprotect continuation message target object header chunk")
} /* end if */
else
- HDassert(!null_chk_proxy && !curr_chk_proxy);
+ HDassert(!null_chk_proxy && !curr_chk_proxy && !cont_targ_chk_proxy);
FUNC_LEAVE_NOAPI(ret_value)
} /* H5O_move_msgs_forward() */
diff --git a/src/H5Oattribute.c b/src/H5Oattribute.c
index 9b494a1..71f54eb 100644
--- a/src/H5Oattribute.c
+++ b/src/H5Oattribute.c
@@ -484,7 +484,7 @@ H5O_attr_open_by_name(const H5O_loc_t *loc, const char *name, hid_t dxpl_id)
HDassert(name);
/* Protect the object header to iterate over */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_ATTR, H5E_CANTPROTECT, NULL, "unable to load object header")
/* Check for attribute info stored */
@@ -633,7 +633,7 @@ H5O_attr_open_by_idx(const H5O_loc_t *loc, H5_index_t idx_type,
HGOTO_ERROR(H5E_ATTR, H5E_BADITER, NULL, "can't locate attribute")
/* Protect the object header to iterate over */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_ATTR, H5E_CANTPROTECT, NULL, "unable to load object header")
/* Find out whether it has already been opened. If it has, close the object
@@ -1284,7 +1284,7 @@ H5O_attr_iterate_real(hid_t loc_id, const H5O_loc_t *loc, hid_t dxpl_id,
HDassert(attr_op);
/* Protect the object header to iterate over */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_ATTR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Check for attribute info stored */
@@ -1847,7 +1847,7 @@ H5O_attr_exists(const H5O_loc_t *loc, const char *name, hid_t dxpl_id)
HDassert(name);
/* Protect the object header to iterate over */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_ATTR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Check for attribute info stored */
@@ -2001,7 +2001,7 @@ H5O_attr_count(const H5O_loc_t *loc, hid_t dxpl_id)
HDassert(loc);
/* Protect the object header to iterate over */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_ATTR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Retrieve # of attributes on object */
diff --git a/src/H5Ocache.c b/src/H5Ocache.c
index 88a4d85..94498ee 100644
--- a/src/H5Ocache.c
+++ b/src/H5Ocache.c
@@ -441,6 +441,18 @@ H5O__cache_deserialize(const void *_image, size_t len, void *_udata,
/* Retrieve partially deserialized object header from user data */
oh = udata->oh;
+ /* Set SWMR flag, if appropriate */
+ oh->swmr_write = !!(H5F_INTENT(udata->common.f) & H5F_ACC_SWMR_WRITE);
+
+ /* Create object header proxy if doing SWMR writes */
+ if(oh->swmr_write) {
+ /* Create virtual entry, for use as proxy */
+ if(NULL == (oh->proxy = H5AC_proxy_entry_create()))
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTCREATE, NULL, "can't create object header proxy")
+ } /* end if */
+ else
+ oh->proxy = NULL;
+
/* Parse the first chunk */
if(H5O__chunk_deserialize(oh, udata->common.addr, udata->chunk0_size, (const uint8_t *)_image, &(udata->common), dirty) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, NULL, "can't deserialize first object header chunk")
@@ -667,6 +679,16 @@ H5O__cache_notify(H5AC_notify_action_t action, void *_thing)
switch(action) {
case H5AC_NOTIFY_ACTION_AFTER_INSERT:
case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ if(oh->swmr_write) {
+ /* Sanity check */
+ HDassert(oh->proxy);
+
+ /* Register the object header as a parent of the virtual entry */
+ if(H5AC_proxy_entry_add_parent(oh->proxy, oh) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTSET, FAIL, "can't add object header as parent of proxy")
+ } /* end if */
+ break;
+
case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
/* do nothing */
@@ -689,10 +711,17 @@ H5O__cache_notify(H5AC_notify_action_t action, void *_thing)
case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
- case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
/* do nothing */
break;
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ if(oh->swmr_write) {
+ /* Unregister the object header as a parent of the virtual entry */
+ if(H5AC_proxy_entry_remove_parent(oh->proxy, oh) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTSET, FAIL, "can't remove object header as parent of proxy")
+ } /* end if */
+ break;
+
default:
#ifdef NDEBUG
HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, FAIL, "unknown action from metadata cache")
@@ -998,7 +1027,6 @@ static herr_t
H5O__cache_chk_notify(H5AC_notify_action_t action, void *_thing)
{
H5O_chunk_proxy_t *chk_proxy = (H5O_chunk_proxy_t *)_thing;
- void *parent = NULL; /* Chunk containing continuation message that points to this chunk */
H5O_chunk_proxy_t *cont_chk_proxy = NULL; /* Proxy for chunk containing continuation message that points to this chunk, if not chunk 0 */
herr_t ret_value = SUCCEED; /* Return value */
@@ -1013,6 +1041,51 @@ H5O__cache_chk_notify(H5AC_notify_action_t action, void *_thing)
switch(action) {
case H5AC_NOTIFY_ACTION_AFTER_INSERT:
case H5AC_NOTIFY_ACTION_AFTER_LOAD:
+ if(chk_proxy->oh->swmr_write) {
+ /* Add flush dependency on chunk parent */
+ {
+ void *parent; /* Chunk containing continuation message that points to this chunk */
+
+ /* Determine the parent of the chunk */
+ if(chk_proxy->cont_chunkno == 0)
+ parent = chk_proxy->oh;
+ else {
+ if(NULL == (cont_chk_proxy = H5O_chunk_protect(chk_proxy->f, H5AC_ind_read_dxpl_id, chk_proxy->oh, chk_proxy->cont_chunkno)))
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header chunk")
+ parent = cont_chk_proxy;
+ } /* end else */
+
+ /* Sanity checks */
+ HDassert(parent);
+ HDassert(((H5C_cache_entry_t *)parent)->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(((H5C_cache_entry_t *)parent)->type);
+ HDassert((((H5C_cache_entry_t *)(parent))->type->id == H5AC_OHDR_ID)
+ || (((H5C_cache_entry_t *)(parent))->type->id == H5AC_OHDR_CHK_ID));
+
+ /* Add flush dependency from chunk containing the continuation message
+ * that points to this chunk (either oh or another chunk proxy object)
+ */
+ if(H5AC_create_flush_dependency(parent, chk_proxy) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTDEPEND, FAIL, "unable to create flush dependency")
+
+ /* Make note of the address and pointer of the flush dependency
+ * parent so we can take the dependency down on eviction.
+ */
+ chk_proxy->parent = parent;
+ }
+
+ /* Add flush dependency on object header proxy, if proxy exists */
+ {
+ /* Sanity check */
+ HDassert(chk_proxy->oh->proxy);
+
+ /* Register the object header chunk as a parent of the virtual entry */
+ if(H5AC_proxy_entry_add_parent(chk_proxy->oh->proxy, chk_proxy) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTSET, FAIL, "can't add object header chunk as parent of proxy")
+ }
+ } /* end if */
+ break;
+
case H5AC_NOTIFY_ACTION_AFTER_FLUSH:
case H5AC_NOTIFY_ACTION_ENTRY_DIRTIED:
/* do nothing */
@@ -1031,10 +1104,29 @@ H5O__cache_chk_notify(H5AC_notify_action_t action, void *_thing)
case H5AC_NOTIFY_ACTION_CHILD_DIRTIED:
case H5AC_NOTIFY_ACTION_CHILD_CLEANED:
- case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
/* do nothing */
break;
+ case H5AC_NOTIFY_ACTION_BEFORE_EVICT:
+ if(chk_proxy->oh->swmr_write) {
+ /* Remove flush dependency on parent object header chunk */
+ {
+ /* Sanity checks */
+ HDassert(chk_proxy->parent != NULL);
+ HDassert(((H5C_cache_entry_t *)(chk_proxy->parent))->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(((H5C_cache_entry_t *)(chk_proxy->parent))->type);
+ HDassert((((H5C_cache_entry_t *)(chk_proxy->parent))->type->id == H5AC_OHDR_ID) || (((H5C_cache_entry_t *)(chk_proxy->parent))->type->id == H5AC_OHDR_CHK_ID));
+
+ if(H5AC_destroy_flush_dependency(chk_proxy->parent, chk_proxy) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTUNDEPEND, FAIL, "unable to destroy flush dependency")
+ }
+
+ /* Unregister the object header chunk as a parent of the virtual entry */
+ if(H5AC_proxy_entry_remove_parent(chk_proxy->oh->proxy, chk_proxy) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTSET, FAIL, "can't remove object header chunk as parent of proxy")
+ } /* end if */
+ break;
+
default:
#ifdef NDEBUG
HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, FAIL, "unknown action from metadata cache")
@@ -1200,6 +1292,7 @@ H5O__chunk_deserialize(H5O_t *oh, haddr_t addr, size_t len, const uint8_t *image
oh->chunk[chunkno].size = len;
if(NULL == (oh->chunk[chunkno].image = H5FL_BLK_MALLOC(chunk_image, oh->chunk[chunkno].size)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "memory allocation failed")
+ oh->chunk[chunkno].chunk_proxy = NULL;
/* Copy disk image into chunk's image */
HDmemcpy(oh->chunk[chunkno].image, image, oh->chunk[chunkno].size);
diff --git a/src/H5Ochunk.c b/src/H5Ochunk.c
index f6eb135..50be171 100644
--- a/src/H5Ochunk.c
+++ b/src/H5Ochunk.c
@@ -403,7 +403,9 @@ H5O_chunk_delete(H5F_t *f, hid_t dxpl_id, H5O_t *oh, unsigned idx)
HDassert(chk_proxy->oh == oh);
HDassert(chk_proxy->chunkno == idx);
- cache_flags |= H5AC__DIRTIED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
+ /* Only free file space if not doing SWMR writes */
+ if(!oh->swmr_write)
+ cache_flags |= H5AC__DIRTIED_FLAG | H5AC__FREE_FILE_SPACE_FLAG;
/* Release the chunk proxy from the cache, marking it deleted */
if(H5AC_unprotect(f, dxpl_id, H5AC_OHDR_CHK, oh->chunk[idx].addr, chk_proxy, cache_flags) < 0)
diff --git a/src/H5Ocopy.c b/src/H5Ocopy.c
index c1c72cf..ddf375c 100644
--- a/src/H5Ocopy.c
+++ b/src/H5Ocopy.c
@@ -381,7 +381,7 @@ H5O_copy_header_real(const H5O_loc_t *oloc_src, H5O_loc_t *oloc_dst /*out*/,
} /* end if */
/* Get source object header */
- if(NULL == (oh_src = H5O_protect(oloc_src, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh_src = H5O_protect(oloc_src, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Retrieve user data for particular type of object to copy */
@@ -450,6 +450,7 @@ H5O_copy_header_real(const H5O_loc_t *oloc_src, H5O_loc_t *oloc_dst /*out*/,
oh_dst->attr_msgs_seen = oh_src->attr_msgs_seen;
oh_dst->sizeof_size = H5F_SIZEOF_SIZE(oloc_dst->file);
oh_dst->sizeof_addr = H5F_SIZEOF_ADDR(oloc_dst->file);
+ oh_dst->swmr_write = !!(H5F_INTENT(oloc_dst->file) & H5F_ACC_SWMR_WRITE);
/* Copy time fields */
oh_dst->atime = oh_src->atime;
@@ -461,6 +462,15 @@ H5O_copy_header_real(const H5O_loc_t *oloc_src, H5O_loc_t *oloc_dst /*out*/,
oh_dst->max_compact = oh_src->max_compact;
oh_dst->min_dense = oh_src->min_dense;
+ /* Create object header proxy if doing SWMR writes */
+ if(oh_dst->swmr_write) {
+ /* Create virtual entry, for use as proxy */
+ if(NULL == (oh_dst->proxy = H5AC_proxy_entry_create()))
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTCREATE, FAIL, "can't create object header proxy")
+ } /* end if */
+ else
+ oh_dst->proxy = NULL;
+
/* Initialize size of chunk array. Start off with zero chunks so this field
* is consistent with the current state of the chunk array. This is
* important if an error occurs.
diff --git a/src/H5Odbg.c b/src/H5Odbg.c
index d9d4b8b..827f40c 100644
--- a/src/H5Odbg.c
+++ b/src/H5Odbg.c
@@ -578,7 +578,7 @@ H5O_debug(H5F_t *f, hid_t dxpl_id, haddr_t addr, FILE *stream, int indent, int f
loc.addr = addr;
loc.holding_file = FALSE;
- if(NULL == (oh = H5O_protect(&loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(&loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* debug */
diff --git a/src/H5Oflush.c b/src/H5Oflush.c
index 4fc9c8e..e399c6c 100644
--- a/src/H5Oflush.c
+++ b/src/H5Oflush.c
@@ -162,7 +162,7 @@ H5O_oh_tag(const H5O_loc_t *oloc, hid_t dxpl_id, haddr_t *tag)
HDassert(oloc);
/* Get object header for object */
- if(NULL == (oh = H5O_protect(oloc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(oloc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object's object header")
/* Get object header's address (i.e. the tag value for this object) */
@@ -262,7 +262,7 @@ H5O_refresh_metadata(hid_t oid, H5O_loc_t oloc, hid_t dxpl_id)
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, dxpl_id)) < 0)
+ if((H5O_refresh_metadata_reopen(oid, &obj_loc, dxpl_id, FALSE)) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, FAIL, "unable to refresh object")
} /* end if */
@@ -360,7 +360,7 @@ done:
*-------------------------------------------------------------------------
*/
herr_t
-H5O_refresh_metadata_reopen(hid_t oid, H5G_loc_t *obj_loc, hid_t dxpl_id)
+H5O_refresh_metadata_reopen(hid_t oid, H5G_loc_t *obj_loc, hid_t dxpl_id, hbool_t start_swmr)
{
void *object = NULL; /* Dataset for this operation */
H5I_type_t type; /* Type of object for the ID */
@@ -388,8 +388,9 @@ H5O_refresh_metadata_reopen(hid_t oid, H5G_loc_t *obj_loc, hid_t dxpl_id)
/* Re-open the dataset */
if(NULL == (object = H5D_open(obj_loc, H5P_DATASET_ACCESS_DEFAULT, dxpl_id)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "unable to open dataset")
- if(H5D_mult_refresh_reopen((H5D_t *)object, dxpl_id) < 0)
- HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "unable to finish refresh for dataset")
+ if(!start_swmr) /* No need to handle multiple opens when H5Fstart_swmr_write() */
+ if(H5D_mult_refresh_reopen((H5D_t *)object, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "unable to finish refresh for dataset")
break;
case(H5I_UNINIT):
diff --git a/src/H5Omessage.c b/src/H5Omessage.c
index 7063ef6..ee21e49 100644
--- a/src/H5Omessage.c
+++ b/src/H5Omessage.c
@@ -477,7 +477,7 @@ H5O_msg_read(const H5O_loc_t *loc, unsigned type_id, void *mesg,
HDassert(type_id < NELMTS(H5O_msg_class_g));
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, NULL, "unable to protect object header")
/* Call the "real" read routine */
@@ -803,7 +803,7 @@ H5O_msg_count(const H5O_loc_t *loc, unsigned type_id, hid_t dxpl_id)
HDassert(type);
/* Load the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header")
/* Count the messages of the correct type */
@@ -885,7 +885,7 @@ H5O_msg_exists(const H5O_loc_t *loc, unsigned type_id, hid_t dxpl_id)
HDassert(type_id < NELMTS(H5O_msg_class_g));
/* Load the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header")
/* Call the "real" exists routine */
@@ -1225,7 +1225,7 @@ H5O_msg_iterate(const H5O_loc_t *loc, unsigned type_id,
HDassert(op);
/* Protect the object header to iterate over */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header")
/* Call the "real" iterate routine */
diff --git a/src/H5Opkg.h b/src/H5Opkg.h
index b80f736..e778f90 100644
--- a/src/H5Opkg.h
+++ b/src/H5Opkg.h
@@ -269,6 +269,7 @@ typedef struct H5O_chunk_t {
size_t size; /*chunk size */
size_t gap; /*space at end of chunk too small for null message */
uint8_t *image; /*image of file */
+ struct H5O_chunk_proxy_t *chunk_proxy; /* Pointer to a chunk's proxy when chunk protected */
} H5O_chunk_t;
struct H5O_t {
@@ -278,6 +279,7 @@ struct H5O_t {
/* File-specific information (not stored) */
size_t sizeof_size; /* Size of file sizes */
size_t sizeof_addr; /* Size of file addresses */
+ hbool_t swmr_write; /* Whether we are doing SWMR writes */
/* Debugging information (not stored) */
#ifdef H5O_ENABLE_BAD_MESG_COUNT
@@ -320,6 +322,10 @@ struct H5O_t {
size_t nchunks; /*number of chunks */
size_t alloc_nchunks; /*chunks allocated */
H5O_chunk_t *chunk; /*array of chunks */
+ hbool_t chunks_pinned; /* Whether chunks are pinned from ohdr protect */
+
+ /* Object header proxy information (not stored) */
+ H5AC_proxy_entry_t *proxy; /* Proxy cache entry for all ohdr entries */
};
/* Class for types of objects in file */
@@ -381,6 +387,20 @@ typedef struct H5O_chunk_proxy_t {
H5O_t *oh; /* Object header for this chunk */
unsigned chunkno; /* Chunk number for this chunk */
unsigned cont_chunkno; /* Chunk number for the chunk containing the continuation message that points to this chunk */
+
+ /* Flush depencency parent information (not stored)
+ *
+ * The following field is used to store a pointer
+ * to the in-core representation of the chunk proxy's flush dependency
+ * parent -- if it exists. If it does not exist, this field will
+ * contain NULL.
+ *
+ * If the file is opened in SWMR write mode, the flush dependency
+ * parent of the chunk proxy will be either its object header
+ * (if cont_chunkno == 0) or the chunk proxy indicated by the
+ * cont_chunkno field (if cont_chunkno > 0).
+ */
+ void *parent; /* Pointer to flush dependency parent */
} H5O_chunk_proxy_t;
/* Callback information for loading object header chunk from disk */
@@ -621,9 +641,6 @@ H5_DLL herr_t H5O_attr_link(H5F_t *f, hid_t dxpl_id, H5O_t *open_oh, void *_mesg
H5_DLL herr_t H5O_attr_count_real(H5F_t *f, hid_t dxpl_id, H5O_t *oh,
hsize_t *nattrs);
-/* These functions operate on object locations */
-H5_DLL H5O_loc_t *H5O_get_loc(hid_t id);
-
/* Testing functions */
#ifdef H5O_TESTING
H5_DLL htri_t H5O_is_attr_empty_test(hid_t oid);
@@ -633,6 +650,9 @@ H5_DLL herr_t H5O_attr_dense_info_test(hid_t oid, hsize_t *name_count, hsize_t *
H5_DLL herr_t H5O_check_msg_marked_test(hid_t oid, hbool_t flag_val);
H5_DLL herr_t H5O_expunge_chunks_test(const H5O_loc_t *oloc, hid_t dxpl_id);
H5_DLL herr_t H5O_get_rc(const H5O_loc_t *oloc, hid_t dxpl_id, unsigned *rc);
+H5_DLL herr_t H5O_msg_get_chunkno_test(hid_t oid, unsigned msg_type,
+ unsigned *chunk_num);
+H5_DLL herr_t H5O_msg_move_to_new_chunk_test(hid_t oid, unsigned msg_type);
#endif /* H5O_TESTING */
/* Object header debugging routines */
diff --git a/src/H5Oprivate.h b/src/H5Oprivate.h
index 2169906..e430b1f 100644
--- a/src/H5Oprivate.h
+++ b/src/H5Oprivate.h
@@ -829,7 +829,8 @@ H5_DLL herr_t H5O_create(H5F_t *f, hid_t dxpl_id, size_t size_hint,
H5_DLL herr_t H5O_open(H5O_loc_t *loc);
H5_DLL herr_t H5O_close(H5O_loc_t *loc, hbool_t *file_closed/*out*/);
H5_DLL int H5O_link(const H5O_loc_t *loc, int adjust, hid_t dxpl_id);
-H5_DLL H5O_t *H5O_protect(const H5O_loc_t *loc, hid_t dxpl_id, unsigned prot_flags);
+H5_DLL H5O_t *H5O_protect(const H5O_loc_t *loc, hid_t dxpl_id,
+ unsigned prot_flags, hbool_t pin_all_chunks);
H5_DLL H5O_t *H5O_pin(const H5O_loc_t *loc, hid_t dxpl_id);
H5_DLL herr_t H5O_unpin(H5O_t *oh);
H5_DLL herr_t H5O_dec_rc_by_loc(const H5O_loc_t *loc, hid_t dxpl_id);
@@ -852,6 +853,7 @@ H5_DLL herr_t H5O_get_nlinks(const H5O_loc_t *loc, hid_t dxpl_id, hsize_t *nlink
H5_DLL void *H5O_obj_create(H5F_t *f, H5O_type_t obj_type, void *crt_info, H5G_loc_t *obj_loc, hid_t dxpl_id);
H5_DLL haddr_t H5O_get_oh_addr(const H5O_t *oh);
H5_DLL herr_t H5O_get_rc_and_type(const H5O_loc_t *oloc, hid_t dxpl_id, unsigned *rc, H5O_type_t *otype);
+H5_DLL H5AC_proxy_entry_t *H5O_get_proxy(const H5O_t *oh);
/* Object header message routines */
H5_DLL herr_t H5O_msg_create(const H5O_loc_t *loc, unsigned type_id, unsigned mesg_flags,
@@ -903,7 +905,7 @@ H5_DLL herr_t H5O_msg_delete(H5F_t *f, hid_t dxpl_id, H5O_t *open_oh,
H5_DLL herr_t H5O_flush_common(H5O_loc_t *oloc, hid_t obj_id, hid_t dxpl_id);
H5_DLL herr_t H5O_refresh_metadata(hid_t oid, H5O_loc_t oloc, hid_t dxpl_id);
H5_DLL herr_t H5O_refresh_metadata_close(hid_t oid, H5O_loc_t oloc, H5G_loc_t *obj_loc, hid_t dxpl_id);
-H5_DLL herr_t H5O_refresh_metadata_reopen(hid_t oid, H5G_loc_t *obj_loc, hid_t dxpl_id);
+H5_DLL herr_t H5O_refresh_metadata_reopen(hid_t oid, H5G_loc_t *obj_loc, hid_t dxpl_id, hbool_t start_swmr);
/* Object copying routines */
H5_DLL herr_t H5O_copy_header_map(const H5O_loc_t *oloc_src, H5O_loc_t *oloc_dst /*out */,
@@ -923,6 +925,7 @@ H5_DLL herr_t H5O_loc_reset(H5O_loc_t *loc);
H5_DLL herr_t H5O_loc_copy(H5O_loc_t *dst, H5O_loc_t *src, H5_copy_depth_t depth);
H5_DLL herr_t H5O_loc_hold_file(H5O_loc_t *loc);
H5_DLL herr_t H5O_loc_free(H5O_loc_t *loc);
+H5_DLL H5O_loc_t *H5O_get_loc(hid_t id);
/* EFL operators */
H5_DLL hsize_t H5O_efl_total_size(H5O_efl_t *efl);
diff --git a/src/H5Otest.c b/src/H5Otest.c
index c3e487d..8f8980a 100644
--- a/src/H5Otest.c
+++ b/src/H5Otest.c
@@ -108,7 +108,7 @@ H5O_is_attr_dense_test(hid_t oid)
HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "object not found")
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Check for attribute info stored */
@@ -173,7 +173,7 @@ H5O_is_attr_empty_test(hid_t oid)
HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "object not found")
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Check for attribute info stored */
@@ -266,7 +266,7 @@ H5O_num_attrs_test(hid_t oid, hsize_t *nattrs)
HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "object not found")
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Check for attribute info stored */
@@ -361,7 +361,7 @@ H5O_attr_dense_info_test(hid_t oid, hsize_t *name_count, hsize_t *corder_count)
H5_BEGIN_TAG(H5AC_ind_read_dxpl_id, loc->addr, FAIL);
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR_TAG(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Check for attribute info stored */
@@ -452,7 +452,7 @@ H5O_check_msg_marked_test(hid_t oid, hbool_t flag_val)
HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "object not found")
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
/* Locate "unknown" message */
@@ -511,7 +511,7 @@ H5O_expunge_chunks_test(const H5O_loc_t *loc, hid_t dxpl_id)
FUNC_ENTER_NOAPI(FAIL)
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__NO_FLAGS_SET)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__NO_FLAGS_SET, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header")
/* Safety check */
@@ -571,7 +571,7 @@ H5O_get_rc(const H5O_loc_t *loc, hid_t dxpl_id, unsigned *rc)
HDassert(rc);
/* Get the object header */
- if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(loc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to protect object header")
/* Save the refcount for the object header */
@@ -585,3 +585,165 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5O_expunge_chunks_test() */
+
+/*--------------------------------------------------------------------------
+ NAME
+ H5O_msg_get_chunkno_test
+ PURPOSE
+ Retrieve the chunk number for an object header message of a given type.
+ USAGE
+ herr_t H5O_check_msg_marked_test(oid, chunk_num)
+ hid_t oid; IN: Object to check
+ unsigned msg_type; IN: Object header message type to check
+ unsigned *chunk_num; OUT: Object header chunk that the message is in
+ RETURNS
+ Non-negative on success, negative on failure
+ DESCRIPTION
+ Retrieves the chunk number for the first object header message of a given
+ type found in an object's header.
+ GLOBAL VARIABLES
+ COMMENTS, BUGS, ASSUMPTIONS
+ DO NOT USE THIS FUNCTION FOR ANYTHING EXCEPT TESTING
+ EXAMPLES
+ REVISION LOG
+--------------------------------------------------------------------------*/
+herr_t
+H5O_msg_get_chunkno_test(hid_t oid, unsigned msg_type, unsigned *chunk_num)
+{
+ H5O_t *oh = NULL; /* Object header */
+ H5O_loc_t *loc; /* Pointer to object's location */
+ H5O_mesg_t *idx_msg; /* Pointer to message */
+ unsigned idx; /* Index of message */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Get object location for object */
+ if(NULL == (loc = H5O_get_loc(oid)))
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "object not found")
+
+ /* Get the object header */
+ if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
+
+ /* Locate first message of given type */
+ for(idx = 0, idx_msg = &oh->mesg[0]; idx < oh->nmesgs; idx++, idx_msg++)
+ if(idx_msg->type->id == msg_type) {
+ /* Set the chunk number for the message */
+ *chunk_num = idx_msg->chunkno;
+
+ /* Break out of loop, to indicate that the message was found */
+ break;
+ } /* end if */
+
+ /* Check for not finding a message of the given type*/
+ if(idx == oh->nmesgs)
+ HGOTO_ERROR(H5E_OHDR, H5E_NOTFOUND, FAIL, "message of type not found")
+
+done:
+ if(oh && H5O_unprotect(loc, H5AC_ind_read_dxpl_id, oh, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to release object header")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5O_msg_get_chunkno_test() */
+
+
+/*--------------------------------------------------------------------------
+ NAME
+ H5O_msg_move_to_new_chunk_test
+ PURPOSE
+ Move a message into a new chunk
+ USAGE
+ herr_t H5O_msg_move_to_new_chunk_test(oid, msg_type)
+ hid_t oid; IN: Object to check
+ unsigned msg_type; IN: Object header message type to check
+ RETURNS
+ Non-negative on success, negative on failure
+ DESCRIPTION
+ Moves the first message of the given type to a new object header chunk.
+ GLOBAL VARIABLES
+ COMMENTS, BUGS, ASSUMPTIONS
+ DO NOT USE THIS FUNCTION FOR ANYTHING EXCEPT TESTING
+ EXAMPLES
+ REVISION LOG
+--------------------------------------------------------------------------*/
+herr_t
+H5O_msg_move_to_new_chunk_test(hid_t oid, unsigned msg_type)
+{
+ H5O_t *oh = NULL; /* Object header */
+ H5O_loc_t *loc; /* Pointer to object's location */
+ H5O_mesg_t *curr_msg; /* Pointer to current message */
+ unsigned idx; /* Index of message */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Get object location for object */
+ if(NULL == (loc = H5O_get_loc(oid)))
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "object not found")
+
+ /* Get the object header */
+ if(NULL == (oh = H5O_protect(loc, H5AC_ind_read_dxpl_id, H5AC__NO_FLAGS_SET, FALSE)))
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header")
+
+ /* Locate first message of given type */
+ for(idx = 0, curr_msg = &oh->mesg[0]; idx < oh->nmesgs; idx++, curr_msg++)
+ if(curr_msg->type->id == msg_type) {
+ H5O_msg_alloc_info_t found_msg; /* Information about message to move */
+ unsigned msg_chunkno = curr_msg->chunkno; /* Chunk that the message is in */
+ uint8_t *end_chunk_data = (oh->chunk[msg_chunkno].image + oh->chunk[msg_chunkno].size) - (H5O_SIZEOF_CHKSUM_OH(oh) + oh->chunk[msg_chunkno].gap); /* End of message data in chunk */
+ uint8_t *end_msg = curr_msg->raw + curr_msg->raw_size; /* End of current message */
+ size_t gap_size = 0; /* Size of gap after current message */
+ size_t null_size = 0; /* Size of NULL message after current message */
+ unsigned null_msgno = 0; /* Index of NULL message after current message */
+ size_t total_size; /* Total size of available space "around" current message */
+ size_t new_idx; /* Index of new null message */
+
+ /* Check if the message is the last one in the chunk */
+ if(end_msg == end_chunk_data)
+ gap_size = oh->chunk[msg_chunkno].gap;
+ else {
+ H5O_mesg_t *tmp_msg; /* Temp. pointer to message to operate on */
+ unsigned v; /* Local index variable */
+
+ /* Check for null message after this message, in same chunk */
+ for(v = 0, tmp_msg = &oh->mesg[0]; v < oh->nmesgs; v++, tmp_msg++) {
+ if(tmp_msg->type->id == H5O_NULL_ID && (tmp_msg->raw - H5O_SIZEOF_MSGHDR_OH(oh)) == end_msg) {
+ null_msgno = v;
+ null_size = (size_t)H5O_SIZEOF_MSGHDR_OH(oh) + tmp_msg->raw_size;
+ break;
+ } /* end if */
+
+ /* XXX: Should also check for NULL message in front of current message... */
+
+ } /* end for */
+ } /* end else */
+
+ /* Add up current message's total available space */
+ total_size = curr_msg->raw_size + gap_size + null_size;
+
+ /* Set up "found message" info for moving the message */
+ found_msg.msgno = (int)idx;
+ found_msg.id = curr_msg->type->id;
+ found_msg.chunkno = msg_chunkno;
+ found_msg.gap_size = gap_size;
+ found_msg.null_size = null_size;
+ found_msg.total_size = total_size;
+ found_msg.null_msgno = null_msgno;
+
+ /* Allocate and initialize new chunk in the file, moving the found message */
+ /* (*new_idx returned from this routine is unused here) */
+ if(H5O__alloc_chunk(loc->file, H5AC_ind_read_dxpl_id, oh, 40, oh->nmesgs, &found_msg, &new_idx) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "can't allocate new object header chunk")
+
+ /* Break out of loop, the message was found */
+ break;
+ } /* end if */
+
+done:
+ if(oh && H5O_unprotect(loc, H5AC_ind_read_dxpl_id, oh, H5AC__NO_FLAGS_SET) < 0)
+ HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to release object header")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5O_msg_get_chunkno_test() */
+
diff --git a/src/H5Pfapl.c b/src/H5Pfapl.c
index 81cd260..91a0e03 100644
--- a/src/H5Pfapl.c
+++ b/src/H5Pfapl.c
@@ -186,6 +186,9 @@
/* Definition for object flush callback */
#define H5F_ACS_OBJECT_FLUSH_CB_SIZE sizeof(H5F_object_flush_t)
#define H5F_ACS_OBJECT_FLUSH_CB_DEF {NULL, NULL}
+/* Definition for status_flags in the superblock */
+#define H5F_ACS_CLEAR_STATUS_FLAGS_SIZE sizeof(hbool_t)
+#define H5F_ACS_CLEAR_STATUS_FLAGS_DEF FALSE
/* Definition for 'use metadata cache logging' flag */
#define H5F_ACS_USE_MDC_LOGGING_SIZE sizeof(hbool_t)
#define H5F_ACS_USE_MDC_LOGGING_DEF FALSE
@@ -334,6 +337,7 @@ static const hbool_t H5F_def_core_write_tracking_flag_g = H5F_ACS_CORE_WRITE_TRA
static const size_t H5F_def_core_write_tracking_page_size_g = H5F_ACS_CORE_WRITE_TRACKING_PAGE_SIZE_DEF; /* Default core VFD write tracking page size */
static const unsigned H5F_def_metadata_read_attempts_g = H5F_ACS_METADATA_READ_ATTEMPTS_DEF; /* Default setting for the # of metadata read attempts */
static const H5F_object_flush_t H5F_def_object_flush_cb_g = H5F_ACS_OBJECT_FLUSH_CB_DEF; /* Default setting for object flush callback */
+static const hbool_t H5F_def_clear_status_flags_g = H5F_ACS_CLEAR_STATUS_FLAGS_DEF; /* Default to clear the superblock status_flags */
static const hbool_t H5F_def_use_mdc_logging_g = H5F_ACS_USE_MDC_LOGGING_DEF; /* Default metadata cache logging flag */
static const char *H5F_def_mdc_log_location_g = H5F_ACS_MDC_LOG_LOCATION_DEF; /* Default mdc log location */
static const hbool_t H5F_def_start_mdc_log_on_access_g = H5F_ACS_START_MDC_LOG_ON_ACCESS_DEF; /* Default mdc log start on access flag */
@@ -510,6 +514,11 @@ H5P__facc_reg_prop(H5P_genclass_t *pclass)
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class")
+ /* Register the private property of whether to clear the superblock status_flags. It's used by h5clear only. */
+ if(H5P_register_real(pclass, H5F_ACS_CLEAR_STATUS_FLAGS_NAME, H5F_ACS_CLEAR_STATUS_FLAGS_SIZE, &H5F_def_clear_status_flags_g,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class")
+
/* Register the metadata cache logging flag. */
if(H5P_register_real(pclass, H5F_ACS_USE_MDC_LOGGING_NAME, H5F_ACS_USE_MDC_LOGGING_SIZE, &H5F_def_use_mdc_logging_g,
NULL, NULL, NULL, H5F_ACS_USE_MDC_LOGGING_ENC, H5F_ACS_USE_MDC_LOGGING_DEC, NULL, NULL, NULL, NULL) < 0)
diff --git a/src/H5Plapl.c b/src/H5Plapl.c
index 266dc63..a0ec7f1 100644
--- a/src/H5Plapl.c
+++ b/src/H5Plapl.c
@@ -1168,7 +1168,9 @@ H5Pset_elink_acc_flags(hid_t lapl_id, unsigned flags)
H5TRACE2("e", "iIu", lapl_id, flags);
/* Check that flags are valid */
- if((flags != H5F_ACC_RDWR) && (flags != H5F_ACC_RDONLY) && (flags != H5F_ACC_DEFAULT))
+ if(( flags != H5F_ACC_RDWR) && (flags != (H5F_ACC_RDWR | H5F_ACC_SWMR_WRITE))
+ && (flags != H5F_ACC_RDONLY) && (flags != (H5F_ACC_RDONLY | H5F_ACC_SWMR_READ))
+ && (flags != H5F_ACC_DEFAULT))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file open flags")
/* Get the plist structure */
diff --git a/src/H5SM.c b/src/H5SM.c
index 642bf7c..e557c0e 100644
--- a/src/H5SM.c
+++ b/src/H5SM.c
@@ -2436,7 +2436,7 @@ H5SM_read_mesg(H5F_t *f, const H5SM_sohm_t *mesg, H5HF_t *fheap,
HGOTO_ERROR(H5E_SOHM, H5E_CANTLOAD, FAIL, "unable to open object header")
/* Load the object header from the cache */
- if(NULL == (oh = H5O_protect(&oloc, dxpl_id, H5AC__READ_ONLY_FLAG)))
+ if(NULL == (oh = H5O_protect(&oloc, dxpl_id, H5AC__READ_ONLY_FLAG, FALSE)))
HGOTO_ERROR(H5E_SOHM, H5E_CANTPROTECT, FAIL, "unable to load object header")
} /* end if */
else
diff --git a/src/H5err.txt b/src/H5err.txt
index a156316..9531df9 100644
--- a/src/H5err.txt
+++ b/src/H5err.txt
@@ -169,7 +169,9 @@ MINOR, CACHE, H5E_CANTUNPROTECT, Unable to unprotect metadata
MINOR, CACHE, H5E_CANTPIN, Unable to pin cache entry
MINOR, CACHE, H5E_CANTUNPIN, Unable to un-pin cache entry
MINOR, CACHE, H5E_CANTMARKDIRTY, Unable to mark a pinned entry as dirty
+MINOR, CACHE, H5E_CANTMARKCLEAN, Unable to mark a pinned entry as clean
MINOR, CACHE, H5E_CANTDIRTY, Unable to mark metadata as dirty
+MINOR, CACHE, H5E_CANTCLEAN, Unable to mark metadata as clean
MINOR, CACHE, H5E_CANTEXPUNGE, Unable to expunge a metadata cache entry
MINOR, CACHE, H5E_CANTRESIZE, Unable to resize a metadata cache entry
MINOR, CACHE, H5E_CANTDEPEND, Unable to create a flush dependency
diff --git a/src/Makefile.am b/src/Makefile.am
index ff845d1..939e151 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -42,9 +42,10 @@ DISTCLEANFILES=H5pubconf.h
# library sources
libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5system.c H5timer.c H5trace.c \
H5A.c H5Abtree2.c H5Adense.c H5Adeprec.c H5Aint.c H5Atest.c \
- H5AC.c H5ACdbg.c H5AClog.c \
+ H5AC.c H5ACdbg.c H5AClog.c H5ACproxy_entry.c \
H5B.c H5Bcache.c H5Bdbg.c \
- H5B2.c H5B2cache.c H5B2dbg.c H5B2hdr.c H5B2int.c H5B2stat.c H5B2test.c \
+ H5B2.c H5B2cache.c H5B2dbg.c H5B2hdr.c H5B2int.c H5B2internal.c \
+ H5B2leaf.c H5B2stat.c H5B2test.c \
H5C.c H5Cdbg.c H5Cepoch.c H5Clog.c H5Cquery.c H5Ctag.c H5Ctest.c \
H5CS.c \
H5D.c H5Dbtree.c H5Dbtree2.c H5Dchunk.c H5Dcompact.c H5Dcontig.c H5Ddbg.c \
@@ -60,11 +61,12 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5system.c H5timer.c H5trace.c \
H5Fmount.c H5Fquery.c \
H5Fsfile.c H5Fsuper.c H5Fsuper_cache.c H5Ftest.c \
H5FA.c H5FAcache.c H5FAdbg.c H5FAdblock.c H5FAdblkpage.c H5FAhdr.c \
- H5FAstat.c H5FAtest.c \
+ H5FAint.c H5FAstat.c H5FAtest.c \
H5FD.c H5FDcore.c \
H5FDfamily.c H5FDint.c H5FDlog.c \
- H5FDmulti.c H5FDsec2.c H5FDspace.c H5FDstdio.c \
- H5FL.c H5FO.c H5FS.c H5FScache.c H5FSdbg.c H5FSsection.c H5FSstat.c H5FStest.c \
+ H5FDmulti.c H5FDsec2.c H5FDspace.c H5FDstdio.c H5FDtest.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 H5Gent.c \
H5Gint.c H5Glink.c \
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 22774cd..5a55616 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -251,6 +251,7 @@ set (H5_TESTS
unregister
cache_logging
cork
+ swmr
)
foreach (test ${H5_TESTS})
diff --git a/test/CMakeTests.cmake b/test/CMakeTests.cmake
index 5cca2d2..cd2e989 100644
--- a/test/CMakeTests.cmake
+++ b/test/CMakeTests.cmake
@@ -562,6 +562,7 @@ set (H5TEST_TESTS
unregister
cache_logging
cork
+ swmr
)
foreach (test ${H5TEST_TESTS})
diff --git a/test/Makefile.am b/test/Makefile.am
index b6be746..380043a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -48,7 +48,7 @@ TEST_PROG= testhdf5 cache cache_api cache_tagging lheap ohdr stab gheap \
big mtime fillval mount flush1 flush2 app_ref enum \
set_extent ttsafe enc_dec_plist enc_dec_plist_cross_platform\
getname vfd ntypes dangle dtransform reserved cross_read \
- freespace mf vds file_image unregister cache_logging cork
+ freespace mf vds file_image unregister cache_logging cork swmr
# List programs to be built when testing here.
# error_test and err_compat are built at the same time as the other tests, but executed by testerror.sh.
@@ -136,7 +136,8 @@ CHECK_CLEANFILES+=accum.h5 cmpd_dset.h5 compact_dataset.h5 dataset.h5 dset_offse
max_compact_dataset.h5 simple.h5 set_local.h5 random_chunks.h5 \
huge_chunks.h5 chunk_cache.h5 big_chunk.h5 chunk_fast.h5 chunk_expand.h5 \
chunk_fixed.h5 copy_dcpl_newfile.h5 partial_chunks.h5 layout_extend.h5 \
- zero_chunk.h5 chunk_single.h5 \
+ zero_chunk.h5 chunk_single.h5 swmr_non_latest.h5 \
+ earray_hdr_fd.h5 farray_hdr_fd.h5 bt2_hdr_fd.h5 \
storage_size.h5 dls_01_strings.h5 \
extend.h5 istore.h5 extlinks*.h5 frspace.h5 links*.h5 \
sys_file1 tfile[1-7].h5 th5s[1-4].h5 lheap.h5 fheap.h5 ohdr.h5 \
@@ -161,8 +162,8 @@ CHECK_CLEANFILES+=accum.h5 cmpd_dset.h5 compact_dataset.h5 dataset.h5 dset_offse
split_get_file_image_test-m.h5 split_get_file_image_test-r.h5 \
file_image_core_test.h5.copy unregister_filter_1.h5 unregister_filter_2.h5 \
vds_virt.h5 vds_dapl.h5 vds_src_[0-1].h5 \
- cache_logging.h5 cache_logging.out
-
+ cache_logging.h5 cache_logging.out \
+ swmr[0-2].h5
# Sources for testhdf5 executable
testhdf5_SOURCES=testhdf5.c tarray.c tattr.c tchecksum.c tconfig.c tfile.c \
tgenprop.c th5o.c th5s.c tcoords.c theap.c tid.c titerate.c tmeta.c tmisc.c \
diff --git a/test/dsets.c b/test/dsets.c
index 7d9f331..31ef88a 100644
--- a/test/dsets.c
+++ b/test/dsets.c
@@ -23,11 +23,16 @@
#define H5D_FRIEND /*suppress error about including H5Dpkg */
#define H5D_TESTING
+#define H5FD_FRIEND /*suppress error about including H5FDpkg */
+#define H5FD_TESTING
+
#define H5Z_FRIEND /*suppress error about including H5Zpkg */
#include "h5test.h"
#include "H5srcdir.h"
#include "H5Dpkg.h"
+#include "H5FDpkg.h"
+#include "H5VMprivate.h"
#include "H5Zpkg.h"
#ifdef H5_HAVE_SZLIB_H
# include "szlib.h"
@@ -52,8 +57,12 @@ const char *FILENAME[] = {
"layout_extend", /* 15 */
"zero_chunk", /* 16 */
"chunk_single", /* 17 */
- "storage_size", /* 18 */
- "dls_01_strings", /* 19 */
+ "swmr_non_latest", /* 18 */
+ "earray_hdr_fd", /* 19 */
+ "farray_hdr_fd", /* 20 */
+ "bt2_hdr_fd", /* 21 */
+ "storage_size", /* 22 */
+ "dls_01_strings", /* 23 */
NULL
};
#define FILENAME_BUF_SIZE 1024
@@ -127,6 +136,11 @@ const char *FILENAME[] = {
#define POINTS 72
#define POINTS_BIG 2500
+/* Dataset names used for testing header flush dependencies */
+#define DSET_EARRAY_HDR_FD "earray_hdr_fd"
+#define DSET_FARRAY_HDR_FD "farray_hdr_fd"
+#define DSET_BT2_HDR_FD "bt2_hdr_fd"
+
/* Dataset names for testing Implicit Indexing */
#define DSET_SINGLE_MAX "DSET_SINGLE_MAX"
#define DSET_SINGLE_NOMAX "DSET_SINGLE_NOMAX"
@@ -8219,7 +8233,7 @@ error:
*-------------------------------------------------------------------------
*/
static herr_t
-test_chunk_fast(hid_t fapl)
+test_chunk_fast(const char *env_h5_driver, hid_t fapl)
{
char filename[FILENAME_BUF_SIZE];
hid_t fid = -1; /* File ID */
@@ -8232,6 +8246,7 @@ test_chunk_fast(hid_t fapl)
hsize_t hs_size[EARRAY_MAX_RANK]; /* Hyperslab size */
hsize_t chunk_dim[EARRAY_MAX_RANK]; /* Chunk dimensions */
H5F_libver_t low; /* File format low bound */
+ unsigned swmr; /* Whether file should be written with SWMR access enabled */
TESTING("datasets w/extensible array as chunk index");
@@ -8266,9 +8281,19 @@ test_chunk_fast(hid_t fapl)
fill = 1;
H5VM_array_fill(hs_size, &fill, sizeof(fill), EARRAY_MAX_RANK);
- {
+ /* Loop over using SWMR access to write */
+ for(swmr = 0; swmr <= 1; swmr++) {
int compress; /* Whether chunks should be compressed */
+ /* SWMR is now supported with/without latest format: */
+ /* (1) write+latest-format (2) SWMR-write+non-latest-format */
+
+ /* Skip this iteration if SWMR I/O is not supported for the VFD specified
+ * by the environment variable.
+ */
+ if(swmr && !H5FD_supports_swmr_test(env_h5_driver))
+ continue;
+
#ifdef H5_HAVE_FILTER_DEFLATE
/* Loop over compressing chunks */
for(compress = 0; compress <= 1; compress++)
@@ -8318,7 +8343,7 @@ test_chunk_fast(hid_t fapl)
hsize_t u; /* Local index variable */
/* Create file */
- if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, my_fapl)) < 0) FAIL_STACK_ERROR
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC | (swmr ? H5F_ACC_SWMR_WRITE : 0), H5P_DEFAULT, my_fapl)) < 0) FAIL_STACK_ERROR
/* Create n-D dataspace */
fill = EARRAY_DSET_DIM;
@@ -8345,7 +8370,7 @@ test_chunk_fast(hid_t fapl)
if(H5D__layout_idx_type_test(dsid, &idx_type) < 0) FAIL_STACK_ERROR
/* Chunk index type expected depends on whether we are using the latest version of the format */
- if(low == H5F_LIBVER_LATEST) {
+ if(low == H5F_LIBVER_LATEST || swmr) {
/* Verify index type */
if(idx_type != H5D_CHUNK_IDX_EARRAY) FAIL_PUTS_ERROR("should be using extensible array as index");
} /* end if */
@@ -8445,7 +8470,7 @@ test_chunk_fast(hid_t fapl)
/* Re-open file & dataset */
- if((fid = H5Fopen(filename, H5F_ACC_RDONLY, my_fapl)) < 0) FAIL_STACK_ERROR
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY | (swmr ? H5F_ACC_SWMR_READ : 0), my_fapl)) < 0) FAIL_STACK_ERROR
/* Open dataset */
if((dsid = H5Dopen2(fid, "dset", H5P_DEFAULT)) < 0) FAIL_STACK_ERROR
@@ -8454,7 +8479,7 @@ test_chunk_fast(hid_t fapl)
if(H5D__layout_idx_type_test(dsid, &idx_type) < 0) FAIL_STACK_ERROR
/* Chunk index tyepe expected depends on whether we are using the latest version of the format */
- if(low == H5F_LIBVER_LATEST) {
+ if(low == H5F_LIBVER_LATEST || swmr) {
/* Verify index type */
if(idx_type != H5D_CHUNK_IDX_EARRAY) FAIL_PUTS_ERROR("should be using extensible array as index");
} /* end if */
@@ -10316,6 +10341,615 @@ error:
/*-------------------------------------------------------------------------
+ * Function: test_swmr_non_latest
+ *
+ * Purpose: Checks that a file created with either:
+ * (a) SWMR-write + non-latest-format
+ * (b) write + latest format
+ * will generate datset with latest chunk indexing type.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+test_swmr_non_latest(const char *env_h5_driver, hid_t fapl)
+{
+ char filename[FILENAME_BUF_SIZE];
+ hid_t fid = -1; /* File ID */
+ hid_t gid = -1; /* Group ID */
+ hid_t dcpl = -1; /* Dataset creation property list ID */
+ hid_t sid = -1; /* Dataspace ID */
+ hid_t did = -1; /* Dataset ID */
+ hsize_t dim[1], dims2[2]; /* Size of dataset */
+ hsize_t max_dim[1], max_dims2[2]; /* Maximum size of dataset */
+ hsize_t chunk_dim[1], chunk_dims2[2]; /* Chunk dimensions */
+ H5D_chunk_index_t idx_type; /* Chunk index type */
+ int data; /* Data to be written to the dataset */
+ H5F_libver_t low; /* File format low bound */
+
+ TESTING("File created with write+latest-format/SWMR-write+non-latest-format: dataset with latest chunk index");
+
+ /* Skip this test if SWMR I/O is not supported for the VFD specified
+ * by the environment variable.
+ */
+ if(!H5FD_supports_swmr_test(env_h5_driver)) {
+ SKIPPED();
+ HDputs(" Test skipped due to VFD not supporting SWMR I/O.");
+ return 0;
+ } /* end if */
+
+ /* Check if we are using the latest version of the format */
+ if(H5Pget_libver_bounds(fapl, &low, NULL) < 0)
+ FAIL_STACK_ERROR
+
+ h5_fixname(FILENAME[18], fapl, filename, sizeof filename);
+
+ if(low == H5F_LIBVER_LATEST) {
+ /* Create file with write+latest-format */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+ } else {
+ /* Create file with SWMR-write+non-latest-format */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+ } /* end else */
+
+ /* Create a chunked dataset: this will use extensible array chunk indexing */
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ chunk_dim[0] = 6;
+ if(H5Pset_chunk(dcpl, 1, chunk_dim) < 0)
+ FAIL_STACK_ERROR
+
+ dim[0] = 1;
+ max_dim[0] = H5S_UNLIMITED;
+ if((sid = H5Screate_simple(1, dim, max_dim)) < 0)
+ FAIL_STACK_ERROR
+
+ if((did = H5Dcreate2(fid, DSET_CHUNKED_NAME, H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Write to the dataset */
+ data = 100;
+ if(H5Dwrite(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &data) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify the dataset's indexing type */
+ if(H5D__layout_idx_type_test(did, &idx_type) < 0)
+ FAIL_STACK_ERROR
+ if(idx_type != H5D_CHUNK_IDX_EARRAY)
+ FAIL_PUTS_ERROR("created dataset not indexed by extensible array")
+
+ /* Closing */
+ if(H5Dclose(did) < 0) FAIL_STACK_ERROR
+ if(H5Sclose(sid) < 0) FAIL_STACK_ERROR
+ if(H5Pclose(dcpl) < 0) FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0) FAIL_STACK_ERROR
+
+ /* Open the file again */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset in the file */
+ if((did = H5Dopen2(fid, DSET_CHUNKED_NAME, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify the dataset's indexing type */
+ if(H5D__layout_idx_type_test(did, &idx_type) < 0)
+ FAIL_STACK_ERROR
+ if(idx_type != H5D_CHUNK_IDX_EARRAY)
+ FAIL_PUTS_ERROR("created dataset not indexed by extensible array")
+
+ /* Read from the dataset and verify data read is correct */
+ if(H5Dread(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &data) < 0)
+ FAIL_STACK_ERROR
+ if(data != 100)
+ TEST_ERROR
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0) FAIL_STACK_ERROR
+
+ /* Create a group in the file */
+ if((gid = H5Gcreate2(fid, "group", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a chunked dataset in the group: this will use v2 B-tree chunk indexing */
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+
+ chunk_dims2[0] = chunk_dims2[1] = 10;
+ if(H5Pset_chunk(dcpl, 2, chunk_dims2) < 0)
+ FAIL_STACK_ERROR
+
+ dims2[0] = dims2[1] = 1;
+ max_dims2[0] = max_dims2[1] = H5S_UNLIMITED;
+ if((sid = H5Screate_simple(2, dims2, max_dims2)) < 0)
+ FAIL_STACK_ERROR
+
+ if((did = H5Dcreate2(gid, DSET_CHUNKED_NAME, H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify the dataset's indexing type */
+ if(H5D__layout_idx_type_test(did, &idx_type) < 0)
+ FAIL_STACK_ERROR
+ if(idx_type != H5D_CHUNK_IDX_BT2)
+ FAIL_PUTS_ERROR("created dataset not indexed by v2 B-tree")
+
+ /* Closing */
+ if(H5Dclose(did) < 0) FAIL_STACK_ERROR
+ if(H5Sclose(sid) < 0) FAIL_STACK_ERROR
+ if(H5Gclose(gid) < 0) FAIL_STACK_ERROR
+ if(H5Pclose(dcpl) < 0) FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0) FAIL_STACK_ERROR
+
+ /* Open the file again */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the group */
+ if((gid = H5Gopen2(fid, "group", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset in the group */
+ if((did = H5Dopen2(gid, DSET_CHUNKED_NAME, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify the dataset's indexing type */
+ if(H5D__layout_idx_type_test(did, &idx_type) < 0)
+ FAIL_STACK_ERROR
+ if(idx_type != H5D_CHUNK_IDX_BT2)
+ FAIL_PUTS_ERROR("created dataset not indexed by v2 B-tree")
+
+ /* Closing */
+ if(H5Dclose(did) < 0) FAIL_STACK_ERROR
+ if(H5Gclose(gid) < 0) FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0) FAIL_STACK_ERROR
+
+
+ /* Reopen the file with SWMR-write */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR | H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset in the file */
+ if((did = H5Dopen2(fid, DSET_CHUNKED_NAME, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify the dataset's indexing type */
+ if(H5D__layout_idx_type_test(did, &idx_type) < 0)
+ FAIL_STACK_ERROR
+ if(idx_type != H5D_CHUNK_IDX_EARRAY)
+ FAIL_PUTS_ERROR("created dataset not indexed by extensible array")
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0) FAIL_STACK_ERROR
+
+ /* Open the group */
+ if((gid = H5Gopen2(fid, "group", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset in the group */
+ if((did = H5Dopen2(gid, DSET_CHUNKED_NAME, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify the dataset's indexing type */
+ if(H5D__layout_idx_type_test(did, &idx_type) < 0)
+ FAIL_STACK_ERROR
+ if(idx_type != H5D_CHUNK_IDX_BT2)
+ FAIL_PUTS_ERROR("created dataset not indexed by v2 B-tree")
+
+ /* Write to the dataset in the group */
+ data = 99;
+ if(H5Dwrite(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &data) < 0)
+ FAIL_STACK_ERROR
+
+ /* Closing */
+ if(H5Dclose(did) < 0) FAIL_STACK_ERROR
+ if(H5Gclose(gid) < 0) FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0) FAIL_STACK_ERROR
+
+ /* Open the file again with SWMR read access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY | H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ if((gid = H5Gopen2(fid, "group", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset */
+ if((did = H5Dopen2(gid, DSET_CHUNKED_NAME, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Read from the dataset and verify data read is correct */
+ data = 0;
+ if(H5Dread(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &data) < 0)
+ FAIL_STACK_ERROR
+ if(data != 99)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Dclose(did) < 0) FAIL_STACK_ERROR
+ if(H5Gclose(gid) < 0) FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0) FAIL_STACK_ERROR
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(dcpl);
+ H5Dclose(did);
+ H5Sclose(sid);
+ H5Gclose(gid);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+ return -1;
+} /* test_swmr_non_latest() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_earray_hdr_fd
+ *
+ * Purpose: Tests that extensible array header flush dependencies
+ * are created and torn down correctly when used as a
+ * chunk index.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+test_earray_hdr_fd(const char *env_h5_driver, hid_t fapl)
+{
+ char filename[FILENAME_BUF_SIZE];
+ hid_t fid = -1;
+ hid_t sid = -1;
+ hid_t did = -1;
+ hid_t tid = -1;
+ hid_t dcpl = -1;
+ hid_t msid = -1;
+ H5D_chunk_index_t idx_type;
+ const hsize_t shape[1] = { 8 };
+ const hsize_t maxshape[1] = { H5S_UNLIMITED };
+ const hsize_t chunk[1] = { 8 };
+ const int buffer[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+ H5O_info_t info;
+
+ TESTING("Extensible array chunk index header flush dependencies handled correctly");
+
+ /* Skip this test if SWMR I/O is not supported for the VFD specified
+ * by the environment variable.
+ */
+ if(!H5FD_supports_swmr_test(env_h5_driver)) {
+ SKIPPED();
+ HDputs(" Test skipped due to VFD not supporting SWMR I/O.");
+ return 0;
+ } /* end if */
+
+ if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR;
+
+ h5_fixname(FILENAME[19], fapl, filename, sizeof(filename));
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create a dataset with one unlimited dimension */
+ if((sid = H5Screate_simple(1, shape, maxshape)) < 0)
+ FAIL_STACK_ERROR;
+ if((tid = H5Tcopy(H5T_NATIVE_INT32)) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_chunk(dcpl, 1, chunk) < 0)
+ FAIL_STACK_ERROR;
+ if((did = H5Dcreate2(fid, DSET_EARRAY_HDR_FD, tid, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT )) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Verify the chunk index type */
+ if(H5D__layout_idx_type_test(did, &idx_type) < 0)
+ FAIL_STACK_ERROR;
+ if(idx_type != H5D_CHUNK_IDX_EARRAY)
+ FAIL_PUTS_ERROR("should be using extensible array as index");
+
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Fstart_swmr_write(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Write data to the dataset */
+ if((did = H5Dopen2(fid, DSET_EARRAY_HDR_FD, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((tid = H5Dget_type(did)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dwrite(did, tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* The second call triggered a bug in the library (JIRA issue: SWMR-95) */
+ if(H5Oget_info_by_name(fid, DSET_EARRAY_HDR_FD, &info, H5P_DEFAULT) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Oget_info_by_name(fid, DSET_EARRAY_HDR_FD, &info, H5P_DEFAULT) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ TEST_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ H5Dclose(did);
+ H5Pclose(dcpl);
+ H5Tclose(tid);
+ H5Sclose(sid);
+ H5Sclose(msid);
+ } H5E_END_TRY;
+ return -1;
+} /* test_earray_hdr_fd() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_farray_hdr_fd
+ *
+ * Purpose: Tests that fixed array header flush dependencies
+ * are created and torn down correctly when used as a
+ * chunk index.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+test_farray_hdr_fd(const char *env_h5_driver, hid_t fapl)
+{
+ char filename[FILENAME_BUF_SIZE];
+ hid_t fid = -1;
+ hid_t sid = -1;
+ hid_t did = -1;
+ hid_t tid = -1;
+ hid_t dcpl = -1;
+ hid_t msid = -1;
+ H5D_chunk_index_t idx_type;
+ const hsize_t shape[1] = { 8 };
+ const hsize_t maxshape[1] = { 64 };
+ const hsize_t chunk[1] = { 8 };
+ const int buffer[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+ H5O_info_t info;
+
+ TESTING("Fixed array chunk index header flush dependencies handled correctly");
+
+ /* Skip this test if SWMR I/O is not supported for the VFD specified
+ * by the environment variable.
+ */
+ if(!H5FD_supports_swmr_test(env_h5_driver)) {
+ SKIPPED();
+ HDputs(" Test skipped due to VFD not supporting SWMR I/O.");
+ return 0;
+ } /* end if */
+
+ if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR;
+
+ h5_fixname(FILENAME[20], fapl, filename, sizeof(filename));
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create a chunked dataset with fixed dimensions */
+ if((sid = H5Screate_simple(1, shape, maxshape)) < 0)
+ FAIL_STACK_ERROR;
+ if((tid = H5Tcopy(H5T_NATIVE_INT32)) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_chunk(dcpl, 1, chunk) < 0)
+ FAIL_STACK_ERROR;
+ if((did = H5Dcreate2(fid, DSET_FARRAY_HDR_FD, tid, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT )) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Verify the chunk index type */
+ if(H5D__layout_idx_type_test(did, &idx_type) < 0)
+ FAIL_STACK_ERROR;
+ if(idx_type != H5D_CHUNK_IDX_FARRAY)
+ FAIL_PUTS_ERROR("should be using fixed array as index");
+
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Fstart_swmr_write(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Write data to the dataset */
+ if((did = H5Dopen2(fid, DSET_FARRAY_HDR_FD, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((tid = H5Dget_type(did)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dwrite(did, tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* The second call triggered a bug in the library (JIRA issue: SWMR-95) */
+ if(H5Oget_info_by_name(fid, DSET_FARRAY_HDR_FD, &info, H5P_DEFAULT) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Oget_info_by_name(fid, DSET_FARRAY_HDR_FD, &info, H5P_DEFAULT) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ TEST_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ H5Dclose(did);
+ H5Pclose(dcpl);
+ H5Tclose(tid);
+ H5Sclose(sid);
+ H5Sclose(msid);
+ } H5E_END_TRY;
+ return -1;
+} /* test_farray_hdr_fd() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: test_bt2_hdr_fd
+ *
+ * Purpose: Tests that version 2 B-tree header flush dependencies
+ * are created and torn down correctly when used as a
+ * chunk index.
+ *
+ * Return: Success: 0
+ * Failure: -1
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+test_bt2_hdr_fd(const char *env_h5_driver, hid_t fapl)
+{
+ char filename[FILENAME_BUF_SIZE];
+ hid_t fid = -1;
+ hid_t sid = -1;
+ hid_t did = -1;
+ hid_t tid = -1;
+ hid_t dcpl = -1;
+ hid_t msid = -1;
+ H5D_chunk_index_t idx_type;
+ const hsize_t shape[2] = { 8, 8 };
+ const hsize_t maxshape[2] = { H5S_UNLIMITED, H5S_UNLIMITED };
+ const hsize_t chunk[2] = { 8, 8 };
+ const int buffer[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+ H5O_info_t info;
+
+ TESTING("Version 2 B-tree chunk index header flush dependencies handled correctly");
+
+ /* Skip this test if SWMR I/O is not supported for the VFD specified
+ * by the environment variable.
+ */
+ if(!H5FD_supports_swmr_test(env_h5_driver)) {
+ SKIPPED();
+ HDputs(" Test skipped due to VFD not supporting SWMR I/O.");
+ return 0;
+ } /* end if */
+
+ if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR;
+
+ h5_fixname(FILENAME[21], fapl, filename, sizeof(filename));
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create a chunked dataset with fixed dimensions */
+ if((sid = H5Screate_simple(2, shape, maxshape)) < 0)
+ FAIL_STACK_ERROR;
+ if((tid = H5Tcopy(H5T_NATIVE_INT32)) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_chunk(dcpl, 2, chunk) < 0)
+ FAIL_STACK_ERROR;
+ if((did = H5Dcreate2(fid, DSET_BT2_HDR_FD, tid, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT )) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Verify the chunk index type */
+ if(H5D__layout_idx_type_test(did, &idx_type) < 0)
+ FAIL_STACK_ERROR;
+ if(idx_type != H5D_CHUNK_IDX_BT2)
+ FAIL_PUTS_ERROR("should be using fixed array as index");
+
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Fstart_swmr_write(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Write data to the dataset */
+ if((did = H5Dopen2(fid, DSET_BT2_HDR_FD, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((tid = H5Dget_type(did)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dwrite(did, tid, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* The second call triggered a bug in the library (JIRA issue: SWMR-95) */
+ if(H5Oget_info_by_name(fid, DSET_BT2_HDR_FD, &info, H5P_DEFAULT) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Oget_info_by_name(fid, DSET_BT2_HDR_FD, &info, H5P_DEFAULT) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ TEST_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ H5Dclose(did);
+ H5Pclose(dcpl);
+ H5Tclose(tid);
+ H5Sclose(sid);
+ H5Sclose(msid);
+ } H5E_END_TRY;
+ return -1;
+} /* test_bt2_hdr_fd() */
+
+
+/*-------------------------------------------------------------------------
* Function: test_storage_size
*
* Purpose: Tests results from querying the storage size of a dataset,
@@ -10345,7 +10979,7 @@ test_storage_size(hid_t fapl)
TESTING("querying storage size");
- h5_fixname(FILENAME[18], fapl, filename, sizeof filename);
+ h5_fixname(FILENAME[22], fapl, filename, sizeof filename);
/* Create file */
if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0) FAIL_STACK_ERROR
@@ -11943,7 +12577,7 @@ dls_01_main( void ) {
TESTING("Testing DLS bugfix 1");
- if ( NULL == h5_fixname(FILENAME[19], H5P_DEFAULT, filename,
+ if ( NULL == h5_fixname(FILENAME[23], H5P_DEFAULT, filename,
sizeof(filename)) )
TEST_ERROR
@@ -12127,7 +12761,7 @@ main(void)
nerrors += (test_huge_chunks(my_fapl) < 0 ? 1 : 0);
nerrors += (test_chunk_cache(my_fapl) < 0 ? 1 : 0);
nerrors += (test_big_chunks_bypass_cache(my_fapl) < 0 ? 1 : 0);
- nerrors += (test_chunk_fast(my_fapl) < 0 ? 1 : 0);
+ nerrors += (test_chunk_fast(envval, my_fapl) < 0 ? 1 : 0);
nerrors += (test_reopen_chunk_fast(my_fapl) < 0 ? 1 : 0);
nerrors += (test_chunk_fast_bug1(my_fapl) < 0 ? 1 : 0);
nerrors += (test_chunk_expand(my_fapl) < 0 ? 1 : 0);
@@ -12138,6 +12772,10 @@ main(void)
nerrors += (test_single_chunk(my_fapl) < 0 ? 1 : 0);
nerrors += (test_large_chunk_shrink(my_fapl) < 0 ? 1 : 0);
nerrors += (test_zero_dim_dset(my_fapl) < 0 ? 1 : 0);
+ nerrors += (test_swmr_non_latest(envval, my_fapl) < 0 ? 1 : 0);
+ nerrors += (test_earray_hdr_fd(envval, my_fapl) < 0 ? 1 : 0);
+ nerrors += (test_farray_hdr_fd(envval, my_fapl) < 0 ? 1 : 0);
+ nerrors += (test_bt2_hdr_fd(envval, my_fapl) < 0 ? 1 : 0);
nerrors += (test_storage_size(my_fapl) < 0 ? 1 : 0);
if(H5Fclose(file) < 0)
diff --git a/test/swmr.c b/test/swmr.c
new file mode 100644
index 0000000..780eb0f
--- /dev/null
+++ b/test/swmr.c
@@ -0,0 +1,7131 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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 files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdfgroup.org/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from help@hdfgroup.org. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/***********************************************************
+*
+* Test program: swmr
+*
+* To test new public routines from SWMR project:
+* H5Pget/set_metadata_read_attempts()
+* H5Fget_metadata_read_retry_info()
+* H5Fstart_swmr_write()
+* H5Pget/set_object_flush_cb()
+* H5Pget/set_append_flush()
+*
+*************************************************************/
+
+#include "hdf5.h"
+#include "h5test.h"
+#include "H5Iprivate.h"
+
+/*
+ * This file needs to access private information from the H5F package.
+ * This file also needs to access the file, file driver, dataset,
+ * and object header testing code.
+ */
+#define H5F_FRIEND /*suppress error about including H5Fpkg */
+#define H5F_TESTING
+#include "H5Fpkg.h" /* File access */
+
+#define H5D_FRIEND /*suppress error about including H5Dpkg */
+#define H5D_TESTING
+#include "H5Dpkg.h" /* Datasets */
+
+#define H5FD_FRIEND /*suppress error about including H5FDpkg */
+#define H5FD_TESTING
+#include "H5FDpkg.h" /* File drivers */
+
+#define H5O_FRIEND /*suppress error about including H5Opkg */
+#define H5O_TESTING
+#include "H5Opkg.h" /* Object headers */
+
+
+const char *FILENAME[] = {
+ "swmr0", /* 0 */
+ "swmr1", /* 1 */
+ "swmr2", /* 2 */
+ NULL
+};
+
+
+#define NAME_BUF_SIZE 1024 /* Length of file name */
+
+/* Tests for H5Pget/set_metadata_read_attempts(), H5Fget_metadata_read_retry_info */
+static int test_metadata_read_attempts(hid_t in_fapl);
+static int test_metadata_read_retry_info(hid_t in_fapl);
+
+/* Tests for H5Fstart_swmr_write() */
+static int test_start_swmr_write(hid_t in_fapl, hbool_t new_format);
+static int test_err_start_swmr_write(hid_t in_fapl, hbool_t new_format);
+static int test_start_swmr_write_concur(hid_t in_fapl, hbool_t new_format);
+static int test_start_swmr_write_stress_ohdr(hid_t in_fapl);
+
+/* Tests for H5Pget/set_object_flush_cb() */
+static herr_t flush_cb(hid_t obj_id, void *_udata);
+static int test_object_flush_cb(hid_t in_fapl);
+
+/* Tests for H5Pget/set_append_flush() */
+static herr_t append_cb(hid_t dset_id, hsize_t *cur_dims, void *_udata);
+static herr_t append_cb2(hid_t dset_id, hsize_t *cur_dims, void *_udata);
+static int test_append_flush_generic(void);
+static int test_append_flush_dataset_chunked(hid_t in_fapl);
+static int test_append_flush_dataset_fixed(hid_t in_fapl);
+static int test_append_flush_dataset_multiple(hid_t in_fapl);
+
+/* Tests for file open flags/SWMR flags: single process access */
+static int test_file_lock_same(hid_t fapl);
+static int test_file_lock_swmr_same(hid_t fapl);
+
+/* Tests for file open flags/SWMR flags: concurrent process access */
+static int test_file_lock_concur(hid_t fapl);
+static int test_file_lock_swmr_concur(hid_t fapl);
+
+/* Test file lock environment variable */
+static int test_file_lock_env_var(hid_t fapl);
+
+/* Tests for SWMR VFD flag */
+static int test_swmr_vfd_flag(void);
+
+/* Tests for H5Drefresh: concurrent access */
+static int test_refresh_concur(hid_t in_fapl, hbool_t new_format);
+
+/* Tests for multiple opens of files and datasets with H5Drefresh() & H5Fstart_swmr_write(): same process */
+static int test_multiple_same(hid_t in_fapl, hbool_t new_format);
+
+/*
+ * Tests for H5Pget/set_metadata_read_attemps(), H5Fget_metadata_read_retry_info()
+ */
+
+/*
+ * test_metadata_read_attempts():
+ *
+ * Checks the following two public routines work as specified:
+ * H5Pset_metadata_read_attempts()
+ * H5Pget_metadata_read_attempts()
+ */
+static int
+test_metadata_read_attempts(hid_t in_fapl)
+{
+ hid_t fapl; /* File access property list */
+ hid_t file_fapl; /* The file's access property list */
+ hid_t fid, fid1, fid2; /* File IDs */
+ unsigned attempts; /* The # of read attempts */
+ char filename[NAME_BUF_SIZE]; /* File name */
+ herr_t ret; /* Generic return value */
+
+ /* Output message about test being performed */
+ TESTING("H5Pget/set_metadata_read_attempts()");
+
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ /*
+ * Set A:
+ * Tests on verifying the # of read attempts when:
+ * --setting/getting read attempts from a
+ * file access property list.
+ */
+ /* Get # of read attempts -- should be the default: 1 */
+ if(H5Pget_metadata_read_attempts(fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != 1)
+ TEST_ERROR
+
+ /* Set the # of read attempts to 0--should fail */
+ H5E_BEGIN_TRY {
+ ret = H5Pset_metadata_read_attempts(fapl, 0);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Set the # of read attempts to a # > 0--should succeed */
+ if(H5Pset_metadata_read_attempts(fapl, 9) < 0)
+ TEST_ERROR
+
+ /* Retrieve the # of read attempts -- should be 9 */
+ if(H5Pget_metadata_read_attempts(fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != 9)
+ TEST_ERROR
+
+ /* Set the # of read attempts to the default for non-SWMR access: H5F_METADATA_READ_ATTEMPTS --should succeed */
+ if(H5Pset_metadata_read_attempts(fapl, H5F_METADATA_READ_ATTEMPTS) < 0)
+ TEST_ERROR
+
+ /* Retrieve the # of read attempts -- should be H5F_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Set the # of read attempts to the default for SWMR access: H5F_SWMR_METADATA_READ_ATEMPTS --should succeed */
+ if(H5Pset_metadata_read_attempts(fapl, H5F_SWMR_METADATA_READ_ATTEMPTS) < 0)
+ TEST_ERROR
+
+ /* Retrieve the # of read attempts -- should be H5F_SWMR_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_SWMR_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Set B:
+ * Tests on verifying read attempts when:
+ * --create a file with non-SWMR access
+ * --opening files with SWMR access
+ * --using default or non-default file access property list
+ */
+ /* Test 1 */
+ /* Create a file with non-SWMR access and default fapl */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file's fapl -- should be H5F_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Test 2 */
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with SWMR access and default read attempts */
+ if((fid = H5Fopen(filename, (H5F_ACC_RDONLY | H5F_ACC_SWMR_READ), fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file's fapl -- should be H5F_SWMR_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_SWMR_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Test 3 */
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the # of read attempts */
+ if(H5Pset_metadata_read_attempts(fapl, 9) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with SWMR access and fapl (non-default & set to 9) */
+ if((fid = H5Fopen(filename, (H5F_ACC_RDONLY | H5F_ACC_SWMR_READ), fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file's fapl -- should be 9 */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != 9)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Test 4 */
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the # of read attempts */
+ if(H5Pset_metadata_read_attempts(fapl, 1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with SWMR access and fapl (non-default & set to 1) */
+ if((fid = H5Fopen(filename, (H5F_ACC_RDONLY | H5F_ACC_SWMR_READ), fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file fapl -- should be 1 */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != 1)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Test 5 */
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with SWMR_READ and fapl (non-default read attempts but unset) */
+ if((fid = H5Fopen(filename, (H5F_ACC_RDONLY | H5F_ACC_SWMR_READ), fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file's fapl -- should be H5F_SWMR_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_SWMR_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Set C:
+ * Tests on verifying read attempts when:
+ * --create a file with SWMR access
+ * --opening files with non-SWMR access
+ * --using default or non-default file access property list
+ */
+ /* Test 1 */
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a file with non-SWMR access and default read attempts */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file's fapl -- should be H5F_SWMR_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_SWMR_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Test 2 */
+ /* Open the file with non-SWMR access and default fapl */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file's fapl -- should be H5F_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Test 3 */
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the # of read attempts */
+ if(H5Pset_metadata_read_attempts(fapl, 9) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with non-SWMR access and fapl (non-default & set to 9) */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file's fapl -- should be 9 */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != 9)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Test 4 */
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the # of read attempts */
+ if(H5Pset_metadata_read_attempts(fapl, 1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with non-SWMR access and fapl (non-default & set to 1) */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file fapl -- should be 1 */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != 1)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Test 5 */
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with non-SWMR_READ and fapl (non-default but unset) */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file's fapl -- should be H5F_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the # of read attempts */
+ if(H5Pset_metadata_read_attempts(fapl, 9) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open file again with non-SWMR access and default fapl */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file fapl -- should be H5F_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open file again with SWMR access and default read attempts */
+ if((fid = H5Fopen(filename, H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file fapl -- should be H5F_SWMR_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_SWMR_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Set D:
+ * Tests on verifying read attempts when:
+ * --create with non-SWMR access
+ * --opening files with SWMR access
+ * --H5reopen the files
+ */
+
+ /* Create a file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open file again with SWMR access and default read attempts */
+ if((fid1 = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the # of read attempts */
+ if(H5Pset_metadata_read_attempts(fapl, 9) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open file again with SWMR access and fapl (non-default & set to 9) */
+ if((fid2 = H5Fopen(filename, (H5F_ACC_RDONLY | H5F_ACC_SWMR_READ), fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Re-open fid1 */
+ if((fid = H5Freopen(fid1)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file fapl -- should be H5F_SWMR_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_SWMR_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Re-open fid2 */
+ if((fid = H5Freopen(fid2)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file fapl -- should be H5F_SWMR_METADATA_READ_ATTEMPTS, not 9 */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_SWMR_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close all the files */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Set E:
+ * Tests on verifying read attempts when:
+ * --create with SWMR access
+ * --opening files with non-SWMR access
+ * --H5reopen the files
+ */
+
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open file again with non-SWMR access and default fapl */
+ if((fid1 = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a copy of the parameter fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the # of read attempts */
+ if(H5Pset_metadata_read_attempts(fapl, 9) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open file again with non-SWMR access and fapl (non-default & set to 9) */
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close fapl */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Re-open fid1 */
+ if((fid = H5Freopen(fid1)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file fapl -- should be H5F_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Re-open fid2 */
+ if((fid = H5Freopen(fid2)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get file's fapl */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from file fapl -- should be H5F_METADATA_READ_ATTEMPTS */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+ if(attempts != H5F_METADATA_READ_ATTEMPTS)
+ TEST_ERROR
+
+ /* Close the file's fapl */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close all the files */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Pclose(file_fapl);
+ H5Fclose(fid);
+ H5Fclose(fid1);
+ H5Fclose(fid2);
+ } H5E_END_TRY;
+
+ return -1;
+
+
+} /* test_metadata_read_attempts() */
+
+/*
+ * test_metadata_read_retry_info():
+ *
+ * Checks whether the public routine H5Fget_metadata_read_retry_info
+ * works as specified.
+ *
+ */
+static int
+test_metadata_read_retry_info(hid_t in_fapl)
+{
+ hid_t fapl, new_fapl; /* File access property list */
+ hid_t fid, fid1; /* File IDs */
+ H5F_retry_info_t info, info1; /* The collection of metadata retries */
+ H5F_t *f = NULL, *f1 = NULL; /* Internal file object pointers */
+ unsigned i, j, n; /* Local index variables */
+ hid_t did1, did2; /* Dataset IDs */
+ hid_t sid; /* Dataspace ID */
+ hid_t dcpl; /* Dataset creation property list */
+ hsize_t dims[2] = {6, 10}; /* Dataset dimensions */
+ char filename[NAME_BUF_SIZE]; /* File name */
+ int buf[6][10], chkbuf1[6][10], chkbuf2[6][10]; /* Buffers for data */
+ hsize_t max_dims_1un[2] = {H5S_UNLIMITED, H5S_UNLIMITED}; /* Dataset maximum dimensions */
+ hsize_t max_dims_2un[2] = {500, H5S_UNLIMITED}; /* Dataset maximum dimensions */
+ hsize_t chunk_dims[2] = {2, 2}; /* Chunk dimensions */
+
+ /* Output message about test being performed */
+ TESTING("H5Fset_metadata_read_retry_info()");
+
+ /* Get a copy of the parameter in_fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a file without SWMR access */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a chunked dataset with 1 unlimited dimension: extensible array indexing will be used */
+ if((sid = H5Screate_simple(2, dims, max_dims_1un)) < 0)
+ FAIL_STACK_ERROR
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 2, chunk_dims) < 0)
+ FAIL_STACK_ERROR
+ if((did1 = H5Dcreate2(fid, "DSET_1UNLIM", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a chunked dataset with 2 unlimited dimension: v2 Btree indexing will be used */
+ if((sid = H5Screate_simple(2, dims, max_dims_2un)) < 0)
+ FAIL_STACK_ERROR
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 2, chunk_dims) < 0)
+ FAIL_STACK_ERROR
+ if((did2 = H5Dcreate2(fid, "DSET_2UNLIM", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Initialize buffer data */
+ for(i = n = 0; i < 6; i++)
+ for(j = 0; j < 10; j++)
+ buf[i][j] = (int)n++;
+
+ /* Write to the 2 datasets */
+ if(H5Dwrite(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf) < 0)
+ FAIL_STACK_ERROR
+ if(H5Dwrite(did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Closing */
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 1: tests on nbins
+ */
+ /*
+ * Open a file without SWMR access, default # of read attempts--
+ * info.nbins should be 0
+ * info.retries should all be NULL
+ */
+ /* Open the file without SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset */
+ if((did1 = H5Dopen2(fid, "DSET_1UNLIM", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ if(H5Dread(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, chkbuf1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset */
+ if((did2 = H5Dopen2(fid, "DSET_2UNLIM", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Dread(did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, chkbuf2) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve retries information */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 0 */
+ if(info.nbins != 0)
+ TEST_ERROR
+
+ /* Should be all NULL */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++)
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+
+ /*
+ * Open a file with SWMR access, default # of read attempts--
+ * info.nbins should be 2
+ * info.retries should all be NULL
+ */
+ /* Open the file with SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve retries information */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 2 */
+ if(info.nbins != 2)
+ TEST_ERROR
+
+ /* Should be all NULL */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++)
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Open a file with SWMR access, # of read_attempts is 10:
+ * info.nbins should be 1
+ * info.retries should all be NULL
+ */
+ if((new_fapl = H5Pcopy(fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ if(H5Pset_metadata_read_attempts(new_fapl, 10) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, new_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve retry information */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 1 */
+ if(info.nbins != 1)
+ TEST_ERROR
+
+ /* Should be all NULL */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++)
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Pclose(new_fapl) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Open a file with SWMR access, # of read attempts is 101:
+ * info.nbins should be 3
+ * info.retries should all be NULL
+ */
+ if((new_fapl = H5Pcopy(fapl)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_metadata_read_attempts(new_fapl, 101) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, new_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve retry information */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 3 */
+ if(info.nbins != 3)
+ TEST_ERROR
+
+ /* Should be all NULL */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++)
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Pclose(new_fapl) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Open a file with SWMR access, # of read_attempts is 10000:
+ * info.nbins should be 4
+ * info.retries should all be NULL
+ */
+ if((new_fapl = H5Pcopy(fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ if(H5Pset_metadata_read_attempts(new_fapl, 10000) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, new_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve retry information */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 4 */
+ if(info.nbins != 4)
+ TEST_ERROR
+
+ /* Should be all NULL */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++)
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Pclose(new_fapl) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Open a file with SWMR access, # of read_attempts is 1:
+ * info.nbins should be 0
+ * info.retries should all be NULL
+ */
+ if((new_fapl = H5Pcopy(fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ if(H5Pset_metadata_read_attempts(new_fapl, 1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, new_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve retry information */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 0 */
+ if(info.nbins != 0)
+ TEST_ERROR
+
+ /* Should be all NULL */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++)
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Pclose(new_fapl) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+
+ /*
+ * Case 2: tests on retries info
+ */
+
+ /* Open the file with SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset */
+ if((did1 = H5Dopen2(fid, "DSET_1UNLIM", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Read data from the dataset */
+ if(H5Dread(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, chkbuf1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset */
+ if((did2 = H5Dopen2(fid, "DSET_2UNLIM", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Read data from the dataset */
+ if(H5Dread(did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, chkbuf2) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve retry information */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 2 */
+ if(info.nbins != 2)
+ TEST_ERROR
+
+ /* Should be all NULL */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++)
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+
+ /* Get a pointer to the internal file object */
+ if((f = (H5F_t *)H5I_object(fid)) == NULL)
+ FAIL_STACK_ERROR
+
+ /*
+ * Increment 1st set of retries for metadata items:
+ * a) v2 B-tree leaf node--retries[4][1]
+ * b) Extensive array data block--retries[15][1]
+ * c) File's superblock--retries[20][0]
+ */
+
+ /* v2 B-tree leaf node: log retry 99 for 500 times */
+ for(i = 0; i < 500; i++) {
+ if(H5F_track_metadata_read_retries(f, H5AC_BT2_LEAF_ID, 99) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ /* Extensive array data block: log retry 10 for 1000 times */
+ for(i = 0; i < 1000; i++)
+ if(H5F_track_metadata_read_retries(f, H5AC_EARRAY_DBLOCK_ID, 10) < 0)
+ FAIL_STACK_ERROR
+
+ /* File's superblock: log retry 1 for 1 time */
+ if(H5F_track_metadata_read_retries(f, H5AC_SUPERBLOCK_ID, 1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the collection of metadata read retries */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify retries for v2 B-tree leaf node */
+ if(info.retries[4][0] != 0)
+ TEST_ERROR
+ if(info.retries[4][1] != 500)
+ TEST_ERROR
+
+ /* Verify retries for extensive array data block */
+ if(info.retries[15][0] != 0)
+ TEST_ERROR
+ if(info.retries[15][1] != 1000)
+ TEST_ERROR
+
+ /* Verify retries for file's superblock */
+ if(info.retries[20][0] != 1)
+ TEST_ERROR
+ if(info.retries[20][1] != 0)
+ TEST_ERROR
+
+ /* Free memory for info.retries */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++) {
+ if(info.retries[i] != NULL)
+ H5free_memory(info.retries[i]);
+ }
+
+ /*
+ * Increment 2nd set of retries for metadata items:
+ * a) Object header--retries[0][0]
+ * b) Extensive array datablock--retries[15][0]
+ * c) Fixed array header--retries[17][1]
+ * d) File's superblock--retries[20][0]
+ */
+
+ /* Object header: log retry 5 for 5 times */
+ for(i = 0; i < 5; i++) {
+ if(H5F_track_metadata_read_retries(f, H5AC_OHDR_ID, 5) < 0)
+ TEST_ERROR
+ }
+
+ /* Extensive array data block: log retry 4 for 1 time */
+ if(H5F_track_metadata_read_retries(f, H5AC_EARRAY_DBLOCK_ID, 4) < 0)
+ TEST_ERROR
+
+ /* Fixed array header : log retry 50 for 10000 times */
+ for(i = 0; i < 10000; i++) {
+ if(H5F_track_metadata_read_retries(f, H5AC_FARRAY_HDR_ID, 50) < 0)
+ TEST_ERROR
+ }
+
+ /* File's superblock: log retry 1 for 1 more time */
+ if(H5F_track_metadata_read_retries(f, H5AC_SUPERBLOCK_ID, 1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the collection of metadata read retries */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Verify info has both previous + current retries information:
+ */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++) {
+ switch(i) {
+ case 0: /* Object header */
+ if(info.retries[i][0] != 5)
+ TEST_ERROR
+ if(info.retries[i][1] != 0)
+ TEST_ERROR
+ break;
+
+ case 4: /* v2 B-tree leaf node */
+ if(info.retries[i][0] != 0)
+ TEST_ERROR
+ if(info.retries[i][1] != 500)
+ TEST_ERROR
+ break;
+
+ case 15: /* Extensive array data block */
+ if(info.retries[i][0] != 1)
+ TEST_ERROR
+ if(info.retries[i][1] != 1000)
+ TEST_ERROR
+ break;
+
+ case 17: /* Fixed array header */
+ if(info.retries[i][0] != 0)
+ TEST_ERROR
+ if(info.retries[i][1] != 10000)
+ TEST_ERROR
+ break;
+
+ case 20: /* File's superblock */
+ if(info.retries[i][0] != 2)
+ TEST_ERROR
+ if(info.retries[i][1] != 0)
+ TEST_ERROR
+ break;
+
+ default:
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+ break;
+ }
+ }
+
+ /* Free memory for info.retries */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++)
+ if(info.retries[i] != NULL)
+ H5free_memory(info.retries[i]);
+
+ /* Closing */
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a copy of the file access property list */
+ if((new_fapl = H5Pcopy(fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the number of metadata read attempts to 101 */
+ if(H5Pset_metadata_read_attempts(new_fapl, 101) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the file with SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, new_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a pointer to the internal file object */
+ if((f = (H5F_t *)H5I_object(fid)) == NULL)
+ FAIL_STACK_ERROR
+
+ /* File's superblock: log retry 1 for 1 time */
+ if(H5F_track_metadata_read_retries(f, H5AC_SUPERBLOCK_ID, 1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the collection of metadata read retries */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 3 */
+ if(info.nbins != 3)
+ TEST_ERROR
+
+ /* Verify retries info */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++) {
+ switch(i) {
+ case 20: /* File's superblock */
+ if(info.retries[i][0] != 1)
+ TEST_ERROR
+ if(info.retries[i][1] != 0)
+ TEST_ERROR
+ if(info.retries[i][2] != 0)
+ TEST_ERROR
+ break;
+
+ default:
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+ break;
+ }
+ }
+
+ /* Free memory */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++)
+ if(info.retries[i] != NULL)
+ H5free_memory(info.retries[i]);
+
+ /* Closing */
+ if(H5Pclose(new_fapl) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 3: Tests on retrieving the collection of retries
+ * when H5Fopen and H5Freopen the same file.
+ */
+
+ /*
+ * Open a file without SWMR access, default # of read attempts--
+ * H5Freopen the same file--
+ * Both files should:
+ * nbins should be 0
+ * retries should all be NULL
+ */
+ /* Open the file without SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Re-open fid */
+ if((fid1 = H5Freopen(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve retries information for fid */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve retries information for fid1*/
+ if(H5Fget_metadata_read_retry_info(fid1, &info1)< 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 0 */
+ if(info.nbins != 0)
+ TEST_ERROR
+ if(info1.nbins != 0)
+ TEST_ERROR
+
+ /* Should be all NULL */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++) {
+ if(info.retries[i] != NULL)
+ TEST_ERROR
+ if(info1.retries[i] != NULL)
+ TEST_ERROR
+ }
+
+ /* Closing */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Open a file with SWMR access, default # of read attempts:
+ * --increment retries for metadata item: fixed array data block page (retries[19][0])
+ * H5Freopen the same file:
+ * --increment retries for metadata item: free-space sections (retries[9][1])--
+ */
+ /* Open the file with SWMR access */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a pointer to the internal file object for fid */
+ if((f = (H5F_t *)H5I_object(fid)) == NULL)
+ FAIL_STACK_ERROR
+
+ /* Re-open fid */
+ if((fid1 = H5Freopen(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a pointer to the internal file object for fid1 */
+ if((f1 = (H5F_t *)H5I_object(fid1)) == NULL)
+ FAIL_STACK_ERROR
+
+ /* For fid: fixed array data block page--log retry 9 for 500 times */
+ for(i = 0; i < 500; i++) {
+ if(H5F_track_metadata_read_retries(f, H5AC_FARRAY_DBLK_PAGE_ID, 9) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ /* For fid1: free-space sections--log retry 99 for 1000 times */
+ for(i = 0; i < 1000; i++) {
+ if(H5F_track_metadata_read_retries(f1, H5AC_FSPACE_SINFO_ID, 99) < 0)
+ FAIL_STACK_ERROR
+ }
+
+ /* Retrieve the collection of metadata read retries for fid */
+ if(H5Fget_metadata_read_retry_info(fid, &info) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the collection of metadata read retries for fid1 */
+ if(H5Fget_metadata_read_retry_info(fid1, &info1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify nbins for fid & fid1: should be 2 */
+ if(info.nbins != 2)
+ TEST_ERROR
+ if(info1.nbins != 2)
+ TEST_ERROR
+
+ /* Verify retries for fid: fixed array data block page */
+ if(info.retries[19][0] != 500)
+ TEST_ERROR
+ if(info.retries[19][1] != 0)
+ TEST_ERROR
+
+ /* Verify retries for fid: free-space sections */
+ /* (Since file was re-opened) */
+ if(info.retries[9][0] != 0)
+ TEST_ERROR
+ if(info.retries[9][1] != 1000)
+ TEST_ERROR
+
+ /* Verify retries for fid1: free-space sections */
+ if(info1.retries[9][0] != 0)
+ TEST_ERROR
+ if(info1.retries[9][1] != 1000)
+ TEST_ERROR
+
+ /* Verify retries for fid1: fixed array data block page */
+ /* (Since file was re-opened) */
+ if(info1.retries[19][0] != 500)
+ TEST_ERROR
+ if(info1.retries[19][1] != 0)
+ TEST_ERROR
+
+ /* Free memory for info.retries and info1.retries */
+ for(i = 0; i < H5F_NUM_METADATA_READ_RETRY_TYPES; i++) {
+ if(info.retries[i] != NULL)
+ H5free_memory(info.retries[i]);
+ if(info1.retries[i] != NULL)
+ H5free_memory(info1.retries[i]);
+ } /* end for */
+
+ /* Closing */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR
+
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Pclose(new_fapl);
+ H5Dclose(did1);
+ H5Dclose(did2);
+ H5Sclose(sid);
+ H5Pclose(dcpl);
+ H5Fclose(fid);
+ H5Fclose(fid1);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* test_metadata_read_retry_info() */
+
+
+
+/*
+ * Tests for H5Fstart_swmr_write()
+ */
+
+/*
+ * test_start_swmr_write():
+ *
+ * Verify SWMR writing is enabled via H5Fstart_swmr_write():
+ * Mainly test for file created with SWMR_WRITE + with/without latest format:
+ * --file will have v3 superblock and all latest version support enabled
+ *
+ * (A) Creating a file
+ * Create a file with SWMR_WRITE + non-latest-format
+ * Create a chunked dataset "dataset1" in the file -- should be using latest chunk indexing
+ * Should fail to enable SWMR as the file is already in SWMR writing mode
+ * Close the file
+ *
+ * (B) Opening a file
+ * Open the file with write + non-latest-format
+ * --file has v3 superblock and all latest version support enabled
+ * Open dataset "dataset1" 3 times--keep it open
+ * Write to "dataset1"
+ * Create a group in the file
+ * Create a chunked dataset "dataset2" in the group--should be using latest chunk indexing--keep it open
+ * Should succeed in enabling SWMR
+ * Should succeed in reading from multiple opens of "dataset1"
+ * Close multiple opens of "dataset1"
+ * Close "dataset2"
+ * Create "dataset3"--should be using latest chunk indexing
+ * Close "dataset3"
+ * Close the group and file
+ */
+static int
+test_start_swmr_write(hid_t in_fapl, hbool_t new_format)
+{
+ hid_t fid = -1; /* File ID */
+ hid_t fapl = -1; /* File access property */
+ hid_t gid = -1; /* Group ID */
+ hid_t dcpl = -1; /* Dataset creation property */
+ hid_t file_fapl = -1; /* File access property for the file */
+ hid_t did1 = -1, did2 = -1, did3 = -1; /* Dataset IDs */
+ hid_t did1_a = -1, did1_b = -1;
+ hid_t sid1 = -1, sid2 = -1, sid3 = -1; /* Dataspace IDs */
+ hsize_t dim[1] = {1}; /* Dimension sizes */
+ hsize_t max_dim[1] = {H5S_UNLIMITED}; /* Maximum dimension sizes */
+ hsize_t chunk_dim[1] = {2}; /* Chunk dimension sizes */
+ hsize_t dim2[2] = {5, 10}; /* Dimension sizes */
+ hsize_t max_dim2[2] = {H5S_UNLIMITED, H5S_UNLIMITED}; /* Maximum dimension sizes */
+ hsize_t chunk_dim2[2] = {2, 7}; /* Chunk dimension sizes */
+ H5D_chunk_index_t idx_type; /* Dataset chunk index type */
+ int wdata = 99; /* Data to write */
+ int rdata; /* Data read */
+ unsigned attempts; /* The retrieved # of read attempts */
+ char filename[NAME_BUF_SIZE]; /* File name */
+ herr_t ret; /* Return value */
+
+
+ /* Get a copy of the parameter fapl (non-latest-format) */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ if(new_format) {
+ TESTING("H5Fstart_swmr_write() when creating/opening a file with latest format");
+
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+ } else {
+ TESTING("H5Fstart_swmr_write() when creating/opening a file without latest format");
+ } /* end if */
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ /*
+ * Case A: when creating a file
+ */
+
+ /* Create the file with SWMR write + non-latest-format */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC | (new_format ? 0 : H5F_ACC_SWMR_WRITE), H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get the file's access_property list */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts from the file's fapl */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 100 */
+ if(attempts != (new_format ? H5F_METADATA_READ_ATTEMPTS : H5F_SWMR_METADATA_READ_ATTEMPTS))
+ TEST_ERROR;
+
+ /* Close the property list */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create "dataset1" */
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 1, chunk_dim) < 0)
+ FAIL_STACK_ERROR
+ if((sid1 = H5Screate_simple(1, dim, max_dim)) < 0)
+ FAIL_STACK_ERROR;
+ if((did1 = H5Dcreate2(fid, "dataset1", H5T_NATIVE_INT, sid1, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the chunk index type */
+ if(H5D__layout_idx_type_test(did1, &idx_type) < 0)
+ FAIL_STACK_ERROR;
+ if(idx_type != H5D_CHUNK_IDX_EARRAY)
+ FAIL_PUTS_ERROR("should be using extensible array as index");
+
+ /* Write to the dataset */
+ if(H5Dwrite(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wdata) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to enable SWMR for non-latest-format */
+ /* Should succeed in enabling SWMR for latest format */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(new_format) {
+ if(ret < 0) TEST_ERROR
+ } else if(ret >= 0)
+ TEST_ERROR
+
+ /* Read from the dataset */
+ if(H5Dread(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Verify the data is correct */
+ if(wdata != rdata)
+ TEST_ERROR
+
+ /* Close "dataset1", dataspace, dataset creation property list */
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the file's access_property list */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 100 */
+ if(attempts != H5F_SWMR_METADATA_READ_ATTEMPTS)
+ TEST_ERROR;
+
+ /* Close the file access property list */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Case B: when opening a file
+ */
+
+ /* Open the file again with write + non-latest-format */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the file's access_property list */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 1 */
+ if(attempts != H5F_METADATA_READ_ATTEMPTS)
+ TEST_ERROR;
+
+ /* Close the property list */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* open "dataset1", keep it open */
+ if((did1 = H5Dopen2(fid, "dataset1", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* open "dataset1" second time */
+ if((did1_a = H5Dopen2(fid, "dataset1", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* open "dataset1" third time */
+ if((did1_b = H5Dopen2(fid, "dataset1", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Write to "dataset1" */
+ wdata = 88;
+ if(H5Dwrite(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wdata) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create a group */
+ if((gid = H5Gcreate2(fid, "group", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create "dataset2" in the group, keep it open */
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 2, chunk_dim2) < 0)
+ FAIL_STACK_ERROR
+ if((sid2 = H5Screate_simple(2, dim2, max_dim2)) < 0)
+ FAIL_STACK_ERROR;
+ if((did2 = H5Dcreate2(gid, "dataset2", H5T_NATIVE_INT, sid2, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the chunk index type for "dataset2" */
+ if(H5D__layout_idx_type_test(did2, &idx_type) < 0)
+ FAIL_STACK_ERROR;
+ if(idx_type != H5D_CHUNK_IDX_BT2)
+ FAIL_PUTS_ERROR("should be using v2 B-tree chunk indexing");
+
+ /* Should succeed in enabling SWMR writing */
+ if(H5Fstart_swmr_write(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the file's access_property list */
+ if((file_fapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the # of read attempts */
+ if(H5Pget_metadata_read_attempts(file_fapl, &attempts) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should be 100 */
+ if(attempts != H5F_SWMR_METADATA_READ_ATTEMPTS)
+ TEST_ERROR;
+
+ /* Close the property list */
+ if(H5Pclose(file_fapl) < 0)
+ FAIL_STACK_ERROR;
+
+ rdata = 0;
+ /* Read from "dataset1" via did1_b (multiple opens) */
+ if(H5Dread(did1_b, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
+ FAIL_STACK_ERROR;
+ if(wdata != rdata)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did1_b) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Read from "dataset1" */
+ rdata = 0;
+ if(H5Dread(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
+ FAIL_STACK_ERROR;
+ if(wdata != rdata)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+
+ rdata = 0;
+ /* Read from "dataset1" via did1_a (multiple opens) */
+ if(H5Dread(did1_a, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
+ FAIL_STACK_ERROR;
+ if(wdata != rdata)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did1_a) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close "dataset2", dataspace, dataset creation property list */
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create "dataset3" */
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 2, chunk_dim2) < 0)
+ FAIL_STACK_ERROR
+ if((sid3 = H5Screate_simple(2, dim2, max_dim2)) < 0)
+ FAIL_STACK_ERROR;
+ if((did3 = H5Dcreate2(fid, "dataset3", H5T_NATIVE_INT, sid3, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the chunk index type for "dataset3" */
+ if(H5D__layout_idx_type_test(did3, &idx_type) < 0)
+ FAIL_STACK_ERROR;
+ if(idx_type != H5D_CHUNK_IDX_BT2)
+ FAIL_PUTS_ERROR("should be using v2 B-tree as index");
+
+ /* Close "dataset3", dataspace, dataset creation property list */
+ if(H5Dclose(did3) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid3) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the group */
+ if(H5Gclose(gid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file access property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Fclose(fid);
+ H5Pclose(fapl);
+ H5Pclose(file_fapl);
+ H5Gclose(gid);
+ H5Pclose(dcpl);
+ H5Dclose(did1);
+ H5Dclose(did1_a);
+ H5Dclose(did1_b);
+ H5Dclose(did2);
+ H5Dclose(did3);
+ H5Sclose(sid1);
+ H5Sclose(sid2);
+ H5Sclose(sid3);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_start_swmr_write() */
+
+/*
+ * test_err_start_swmr_write():
+ *
+ * Verify failure conditions in enabling SWMR writing mode via H5Fstart_swmr_write():
+ * (A) When creating a file:
+ * (1) Create a file with SWMR write + with/without latest format
+ * --fail to enable SWMR because the file is already in SWMR writing mode
+ * If (latest-format):
+ * (2a) Create a file with write + latest format and with opened named datatype
+ * --fail to enable SWMR because there are opened datatype
+ * If !(latest-format):
+ * (2b) Create a file with write + non-latest-format
+ * --fail to enable SWMR because superblock version is not at least 3
+ *
+ * (B) When opening a file which is created with write + latest format:
+ * (1) Open the file with SWMR write + with/without latest format
+ * --fail to enable SWMR because the file is already in SWMR writing mode
+ * (2) Open the file with read only + with/without latest format
+ * --fail to enable SWMR because the file is not opened with write
+ * (3) Open the file with SWMR read only + with/without latest format
+ * --fail to enable SWMR because the file is not opened with write
+ * (4) Open the file with write + with/without latest format and with opened named datatype/attribute
+ * --fail to enable SWMR because there are opened datatype/attribute
+ *
+ * (C) When doing multiple opens for a file:
+ * Create a file with (a) write + latest format or (b) SMWR write + non-latest-format
+ * Close the file
+ * (1) --Open the file with write + with/without latest format
+ * --Enable SWMR writing mode twice
+ * --First time succeed, second time fail
+ * --Close the file
+ (2) --Open the file with write + with/without latest format
+ * --Succeed to enable SWMR writing mode
+ * --Reopen the same file
+ * --fail to enable SWMR writing mode for the reopened file
+ * --Close the file
+ (3) --Open the file with write + with/without latest format
+ * --Open the same file again
+ * --succeed to enable SWMR for the first opened file
+ * --fail to enable SWMR for the second opened file
+ * --Close the file
+ *
+ * (D) (!new_format): When opening a file which is created with write + non-latest-format:
+ * (1) Open the file with SWMR write+latest format
+ * --fail to open due to superblock version not 3
+ * (2) Open the file with SWMR write+non-latest-format
+ * --fail to open due to superblock version not 3
+
+ * (3) Open the file with write+latest format
+ * --fail to enable SWMR due to superblock version not 3
+ * (4) Open the file with write+non-latest-format
+ * --fail to enable SWMR due to superblock version not 3
+ */
+static int
+test_err_start_swmr_write(hid_t in_fapl, hbool_t new_format)
+{
+ hid_t fid = -1; /* File ID */
+ hid_t fid2 = -1; /* File ID */
+ hid_t fapl = -1; /* A copy of file access property */
+ hid_t new_fapl = -1; /* A copy of file access property */
+ hid_t gid = -1; /* Group ID */
+ hid_t did = -1; /* Dataset ID */
+ hid_t sid = -1; /* Dataspace ID */
+ hid_t aid = -1; /* Attribute ID */
+ hid_t tid = -1; /* Datatype ID */
+ hid_t bad_fid = -1; /* Test fid (should never represent a real ID) */
+ herr_t ret; /* Return value */
+ char filename[NAME_BUF_SIZE]; /* File name */
+
+ /* Create a copy of the input parameter in_fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ if((new_fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(new_fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ if(new_format) {
+ TESTING("H5Fstart_swmr_write() on failure conditions for latest format");
+
+ if((fapl = H5Pcopy(new_fapl)) < 0)
+ FAIL_STACK_ERROR
+ } else {
+ TESTING("H5Fstart_swmr_write() on failure conditions for without latest format");
+ }
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+
+ /*
+ * (A) When creating a file:
+ */
+
+ /* Case 1 */
+
+ /* Create the file with SWMR_WRITE + with/without latest format */
+ fid = H5Fcreate(filename, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl);
+
+ /* Should fail to enable SWMR writing when the file is already in SWMR writing mode */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Case 2 */
+
+ if(new_format) {
+
+ /* Create the file with write + latest format */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create and commit a named datatype */
+ if((tid = H5Tcopy(H5T_NATIVE_INT)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tcommit2(fid, "TID", tid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to enable SWMR writing when there is an opened named datatype */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Close the datatype */
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should succeed in enabling SWMR writing */
+ if(H5Fstart_swmr_write(fid) < 0)
+ TEST_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ } else {
+
+ /* Create a file with write + non-latest-format */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ TEST_ERROR
+
+ /* Should fail to enable SWMR writing because the file's superblock version is not at least 3 */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+ } /* end if */
+
+ /*
+ * (B) When opening a file which is created with the latest format
+ */
+
+ /* Create a file with write + latest format */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, new_fapl)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Case 1 */
+
+ /* Open the file with SWMR write + with/without latest format */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to enable SWMR writing when the file is already in SWMR writing mode */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Case 2 */
+
+ /* Open the file with read only access + with/without latest format */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to enable SWMR writing when the file is opened with read only access */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Case 3 */
+
+ /* Open the file file with SWMR read access + with/without latest format */
+ if((fid = H5Fopen(filename, H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to enable SWMR writing when the file is opened with SWMR read access only */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Case 4 */
+
+ /* Open the file with write + with/without latest format */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create and commit a named datatype */
+ if((tid = H5Tcopy(H5T_NATIVE_INT)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tcommit2(fid, "TID", tid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create dataspace */
+ if((sid = H5Screate(H5S_SCALAR)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Attach an attribute to the named datatype */
+ if((aid = H5Acreate2(tid, "attr", H5T_NATIVE_INT, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to enable SWMR writing when there are opened named datatype and attribute */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Close the datatype */
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Still fail to enable SWMR writing when the attribute is still opened */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Close the attribute */
+ if(H5Aclose(aid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should succeed in enabling SWMR writing */
+ if(H5Fstart_swmr_write(fid) < 0)
+ TEST_ERROR;
+
+ /* Close the dataspace */
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /*
+ * (C) Failure cases for multiple opens
+ */
+
+ /* Case 1 */
+
+ /* Create a file with write + with/without latest format */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC | (new_format ? 0 : H5F_ACC_SWMR_WRITE), H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Open the file with write + with/without latest format */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should succeed in enabling SWMR writing mode */
+ if(H5Fstart_swmr_write(fid) < 0)
+ TEST_ERROR
+
+ /* Should fail for a second call to enable SWMR writing mode */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /* Case 2 */
+
+ /* Open the file with write + with/without latest format */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should succeed in enabling SWMR writing mode */
+ if(H5Fstart_swmr_write(fid) < 0)
+ TEST_ERROR
+
+ /* Re-open the same file */
+ if((fid2 = H5Freopen(fid)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to enable SWMR writing mode for fid2 */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid2);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Close the files */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Case 3 */
+
+ /* Open the file with write + with/without latest format */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Open the same file */
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should succeed in enabling SWMR writing for fid */
+ if(H5Fstart_swmr_write(fid) < 0)
+ TEST_ERROR
+
+ /* Should fail to enable SWMR writing for fid2 */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid2);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Close the files */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+
+ if(!new_format) {
+
+ /*
+ * (D) When opening a file which is created without the latest format:
+ */
+
+ /* Create a file with write + without latest format */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Case 1 */
+
+ /* Should fail to open the file with SWMR write + latest format due to superblock version not at least 3 */
+ H5E_BEGIN_TRY {
+ bad_fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, new_fapl);
+ } H5E_END_TRY;
+ if(bad_fid >= 0)
+ TEST_ERROR
+
+ /* Case 2 */
+
+ /* Should fail to open the file with SWMR write + non-latest-format due to superblock version not at least 3 */
+ H5E_BEGIN_TRY {
+ bad_fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+ if(bad_fid >= 0)
+ TEST_ERROR
+
+ /* Case 3 */
+
+ /* Open the file with write + latest format */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, new_fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to enable SWMR writing due to superblock version not at least 3 */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /* Case 4 */
+
+ /* Open the file with write + non-latest-format */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to enable SWMR writing because the file's superblock version is not at least 3 */
+ H5E_BEGIN_TRY {
+ ret = H5Fstart_swmr_write(fid);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ } /* not new */
+
+ /* Close the file access property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(new_fapl) < 0)
+ FAIL_STACK_ERROR;
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Sclose(sid);
+ H5Gclose(gid);
+ H5Dclose(did);
+ H5Fclose(fid);
+ H5Fclose(fid2);
+ H5Pclose(fapl);
+ H5Pclose(new_fapl);
+ /* bad_fid should only represent a read ID in the error case.
+ * It never needs to be closed in the normal case.
+ */
+ H5Fclose(bad_fid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_err_start_swmr_write() */
+
+/*
+ * test_start_swmr_write_concur():
+ *
+ * The "new_format" parameter indicates whether to create the file with latest format or not.
+ * To have SWMR support, can use either one of the following in creating a file:
+ * (a) Create the file with write + latest format:
+ * --result in v3 superblock with latest chunk indexing types
+ * (b) Create the file with SWMR write + non-latest-format:
+ * --result in v3 superblock with latest chunk indexing types
+ * Create a chunked dataset with 1 extendible dimension in the file
+ *
+ * Verify concurrent access for H5Fstart_swmr_write()--
+ * (1) Parent: open a file with write access
+ * Child: concurrent open of the file with read & SWMR read (fail)
+ * (2) Parent: open a file with write access; enable SWMR writing mode
+ * Child: open the file 2 times with read & SWMR read (succeed)
+ * open the dataset 2 times with the 2 file opens
+ * verify data read from multiple opens of the dataset is correct
+ * (3) Parent: open a file with write access; enable SWMR writing mode
+ * Child: Concurrent open of the file with read only (fail)
+ * (4) Parent: open a file with write access; enable SWMR writing mode
+ * Child: concurrent open of the file with write access (fail)
+ * (5) Parent: open a file with write access; enable SWMR writing mode
+ * Child: concurrent open of the file with write and SWMR write access (fail)
+ */
+#if !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID))
+
+static int
+test_start_swmr_write_concur(hid_t H5_ATTR_UNUSED in_fapl, hbool_t H5_ATTR_UNUSED new_format)
+{
+ SKIPPED();
+ HDputs(" Test skipped due to fork or waitpid not defined.");
+ return 0;
+} /* test_start_swmr_write_concur() */
+
+#else /* defined(H5_HAVE_FORK && defined(H5_HAVE_WAITPID) */
+
+static int
+test_start_swmr_write_concur(hid_t in_fapl, hbool_t new_format)
+{
+ hid_t fid = -1, fid1 = -1, fid2 = -1; /* File IDs */
+ hid_t fapl; /* File access property list */
+ pid_t childpid=0; /* Child process ID */
+ pid_t tmppid; /* Child process ID returned by waitpid */
+ 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 */
+ char filename[NAME_BUF_SIZE]; /* File name */
+
+ hid_t did = -1, did1 = -1, did2 = -1, did3 = -1;
+ hid_t sid = -1;
+ hid_t dcpl = -1;
+ hsize_t chunk_dims[1] = {1};
+ hsize_t maxdims[1] = {H5S_UNLIMITED};
+ hsize_t dims[1] = {1};
+ int wdata = 0;
+
+ int out_pdf[2];
+ int in_pdf[2];
+ int notify = 0;
+
+ /* Output message about test being performed */
+ if(new_format) {
+ TESTING("H5Fstart_swmr_write()--concurrent access for latest format");
+ } else {
+ TESTING("H5Fstart_swmr_write()--concurrent access for non-latest-format");
+ } /* end if */
+
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ if(new_format) {
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create the test file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+ } else {
+ /* Create the test file without latest format but with SWMR write */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+ } /* end if */
+
+ /* Create a chunked dataset with 1 extendible dimension */
+ if((sid = H5Screate_simple(1, dims, maxdims)) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 1, chunk_dims) < 0)
+ FAIL_STACK_ERROR;
+ if((did = H5Dcreate2(fid, "dataset", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case (1):
+ * Verify concurrent file open with H5F_ACC_RDONLY|H5F_ACC_SWMR_READ
+ * will fail without H5Fstart_swmr_write()
+ */
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Should fail */
+ H5E_BEGIN_TRY {
+ /* Open the test file */
+ fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 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
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case (2):
+ * Verify concurrent file open with H5F_ACC_RDONLY|H5F_ACC_SWMR_READ
+ * will succeed with H5Fstart_swmr_write()
+ */
+
+ /* Create 2 pipes */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+ if(HDpipe(in_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid1 = -1, child_fid2; /* File IDs */
+ hid_t child_did1 = -1, child_did2 = -1; /* Dataset IDs */
+ int child_notify = 0;
+ int rdata = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+ /* close unused read end for in_pdf */
+ if(HDclose(in_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Should succeed in opening the test file 2 times */
+ if((child_fid1 = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ HDexit(EXIT_FAILURE);
+ if((child_fid2 = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* open "dataset" 2 times */
+ if((child_did1 = H5Dopen2(child_fid1, "dataset", H5P_DEFAULT)) < 0)
+ HDexit(EXIT_FAILURE);
+ if((child_did2 = H5Dopen2(child_fid2, "dataset", H5P_DEFAULT)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Read from "dataset" via child_did1 */
+ rdata = 0;
+ if(H5Dread(child_did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
+ HDexit(EXIT_FAILURE);
+ if(rdata != 88)
+ HDexit(EXIT_FAILURE);
+
+ /* Notify parent process */
+ child_notify = 2;
+ if(HDwrite(in_pdf[1], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 3) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Refresh "dataset" via child_did2 */
+ if(H5Drefresh(child_did2) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Read from "dataset" child_did2 */
+ rdata = 0;
+ if(H5Dread(child_did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
+ HDexit(EXIT_FAILURE);
+ if(rdata != 99)
+ HDexit(EXIT_FAILURE);
+
+ /* Read from "dataset" child_did1 */
+ rdata = 0;
+ if(H5Dread(child_did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rdata) < 0)
+ HDexit(EXIT_FAILURE);
+ if(rdata != 99)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the dataset */
+ if(H5Dclose(child_did1))
+ HDexit(EXIT_FAILURE);
+ if(H5Dclose(child_did2))
+ HDexit(EXIT_FAILURE);
+
+ /* Close the file */
+ if(H5Fclose(child_fid1) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Fclose(child_fid2) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+ if(HDclose(in_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+ /* Close unused write end for in_pdf */
+ if(HDclose(in_pdf[1]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid1 = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* open "dataset", keep it open */
+ if((did1 = H5Dopen2(fid1, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((did2 = H5Dopen2(fid2, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((did3 = H5Dopen2(fid1, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Write to "dataset" */
+ wdata = 88;
+ if(H5Dwrite(did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wdata) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Flush to disk */
+ if(H5Fflush(fid1, H5F_SCOPE_LOCAL) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Enable SWMR writing mode */
+ if(H5Fstart_swmr_write(fid1) < 0)
+ TEST_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for notification from child process */
+ while(notify != 2) {
+ if(HDread(in_pdf[0], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+ }
+
+ /* Write to "dataset" */
+ wdata = 99;
+ if(H5Dwrite(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wdata) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Flush to disk */
+ if(H5Fflush(fid1, H5F_SCOPE_LOCAL) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Notify child process */
+ notify = 3;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the dataset */
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR
+ if(H5Dclose(did3) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the pipes */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+ if(HDclose(in_pdf[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
+
+ /* Close the file */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+
+ /*
+ * Case (3):
+ * Verify concurrent file open with H5F_ACC_RDONLY
+ * will fail with H5Fstart_swmr_write()
+ */
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Should fail in opening the test file */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ } /* end if */
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Enable SWMR writing mode */
+ if(H5Fstart_swmr_write(fid) < 0)
+ TEST_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 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
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case (4):
+ * Verify concurrent file open with H5F_ACC_RDWR
+ * will fail with H5Fstart_swmr_write()
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Should fail in opening the test file */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDWR, fapl);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ } /* end if */
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Enable SWMR writing mode */
+ if(H5Fstart_swmr_write(fid) < 0)
+ TEST_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 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
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case (5):
+ * Verify concurrent file open with H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE
+ * will fail with H5Fstart_swmr_write()
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Should fail in opening the test file */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Enable SWMR writing mode */
+ if(H5Fstart_swmr_write(fid) < 0)
+ TEST_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 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
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Sclose(sid);
+ H5Pclose(dcpl);
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* test_start_swmr_write_concur() */
+#endif /* !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID)) */
+
+/*
+ * test_start_swmr_write_stress_ohdr():
+ *
+ * Verify that H5Fswmr_start_write() works correctly when the dataspace header
+ * message is not located in chunk #0 of the object header.
+ *
+ */
+static int
+test_start_swmr_write_stress_ohdr(hid_t in_fapl)
+{
+ hid_t fid = -1; /* File IDs */
+ hid_t fapl; /* File access property list */
+ char filename[NAME_BUF_SIZE]; /* File name */
+ hid_t did = -1, did2 = -1; /* Dataset IDs */
+ hid_t sid = -1; /* Dataspace ID */
+ hid_t tid = -1; /* Datatype ID */
+ hid_t dcpl = -1; /* Dataset creation property list ID */
+ hid_t aid = -1; /* Attribute ID */
+ hsize_t chunk_dims[2] = {10, 10};
+ hsize_t maxdims[2] = {H5S_UNLIMITED, H5S_UNLIMITED};
+ char fill[256]; /* Fill value for dataset */
+ char attr_data[32]; /* Data value for attribute */
+ hsize_t dims[2] = {1,1};
+ unsigned chunk_num; /* Object header chunk # for dataspace message */
+
+ /* Output message about test being performed */
+ TESTING("H5Fstart_swmr_write()--stress object header messages");
+
+ /* Initialize buffers */
+ HDmemset(fill, 0, sizeof(fill));
+ HDmemset(attr_data, 0, sizeof(attr_data));
+
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create the test file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a chunked dataset with 2 extendible dimensions */
+ if((sid = H5Screate_simple(1, dims, maxdims)) < 0)
+ FAIL_STACK_ERROR;
+ if((tid = H5Tcopy(H5T_C_S1)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tset_size(tid, 256) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 1, chunk_dims) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_fill_value(dcpl, tid, &fill) < 0)
+ FAIL_STACK_ERROR;
+ if((did = H5Dcreate2(fid, "dataset", tid, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Retrieve the chunk # for the dataspace message */
+ chunk_num = UINT_MAX;
+ if(H5O_msg_get_chunkno_test(did, H5O_SDSPACE_ID, &chunk_num) < 0)
+ FAIL_STACK_ERROR;
+ /* Should be in chunk #0 for now */
+ if(0 != chunk_num)
+ TEST_ERROR;
+
+ /* Create a second chunked dataset with 2 extendible dimensions */
+ /* (So that the original dataset's object header can't be extended) */
+ if((sid = H5Screate_simple(1, dims, maxdims)) < 0)
+ FAIL_STACK_ERROR;
+ if((tid = H5Tcopy(H5T_C_S1)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tset_size(tid, 256) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 1, chunk_dims) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_fill_value(dcpl, tid, &fill) < 0)
+ FAIL_STACK_ERROR;
+ if((did2 = H5Dcreate2(fid, "dataset2", tid, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the second dataset */
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the objects for the dataset creation */
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create attribute on original dataset, to push dataspace header message out of header chunk #0 */
+ if((sid = H5Screate(H5S_SCALAR)) < 0)
+ FAIL_STACK_ERROR;
+ if((tid = H5Tcopy(H5T_C_S1)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tset_size(tid, 32) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Tset_strpad(tid, H5T_STR_NULLTERM) < 0)
+ FAIL_STACK_ERROR;
+ if((aid = H5Acreate2(did, "attr", tid, sid, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Awrite(aid, tid, attr_data) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Tclose(tid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Aclose(aid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the chunk # for the dataspace message */
+ chunk_num = UINT_MAX;
+ if(H5O_msg_get_chunkno_test(did, H5O_SDSPACE_ID, &chunk_num) < 0)
+ FAIL_STACK_ERROR;
+ /* Should be in chunk #0 for now */
+ if(1 != chunk_num)
+ TEST_ERROR;
+
+ /* Enable SWMR write */
+ if(H5Fstart_swmr_write(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the FAPL */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Sclose(aid);
+ H5Sclose(tid);
+ H5Sclose(sid);
+ H5Sclose(did);
+ H5Sclose(did2);
+ H5Pclose(dcpl);
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_start_swmr_write_stress_ohdr() */
+
+/*
+ * Tests for H5Pset/get_object_flush_cb()
+ */
+
+/* The callback function for object flush property */
+static herr_t
+flush_cb(hid_t H5_ATTR_UNUSED obj_id, void *_udata)
+{
+ unsigned *flush_ct = (unsigned*)_udata;
+ ++(*flush_ct);
+ return 0;
+}
+
+/*
+ * test_object_flush_cb()
+ *
+ * Verify the public routines H5Pget/set_object_flush_cb() work as specified:
+ * 1) To verify the failure condition in setting object flush property
+ * 2) To verify the object flush property values retrieved from a default
+ * file access property list.
+ * 3) To verify the object flush property values retrieved from a non-default
+ * file access property list.
+ * 4) To verify the object flush property values retrieved from a default
+ * file access property list of a file
+ * 5) To verify the object flush property values retrieved from a non-default
+ * file access property list of a file
+ * To verify the object flush callback is invoked when doing H5Oflush(),
+ * H5Dflush(), H5Gflush() and H5Tflush().
+ */
+static int
+test_object_flush_cb(hid_t in_fapl)
+{
+ hid_t fapl = -1; /* A copy of file access property list */
+ hid_t ffapl = -1; /* A file's file access property list */
+ hid_t fid = -1; /* File ID */
+ hid_t gid = -1; /* Group ID */
+ hid_t did1 = -1, did2 = -1; /* Dataset IDs */
+ hid_t sid = -1; /* Dataspace ID */
+ hsize_t dims[2] = {5, 10}; /* Dataset dimension sizes */
+ int buf[50]; /* Data buffer */
+ H5F_flush_cb_t ret_cb; /* The callback function set in object flush property */
+ void *ret_ct; /* The user data set in object flush property */
+ unsigned flush_ct = 0; /* The user data for object flush property */
+ char filename[NAME_BUF_SIZE]; /* File name */
+ int i; /* Local index variable */
+ herr_t ret; /* Generic return value */
+
+ TESTING("H5Pget/set_obj_flush_cb()");
+
+ /*
+ * Case (1)
+ * To verify the failure condition in setting object flush property
+ */
+ /* Should fail if the callback function is not defined but user data is defined */
+ H5E_BEGIN_TRY {
+ ret = H5Pset_object_flush_cb(fapl, NULL, &flush_ct);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /*
+ * Case (2)
+ * To verify the object flush property values retrieved from a
+ * default file access property list.
+ */
+
+ /* Create a copy of file access property list */
+ if((fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve object flush property values for the default file access property list */
+ if(H5Pget_object_flush_cb(fapl, &ret_cb, &ret_ct) < 0)
+ TEST_ERROR
+ /* Should be null */
+ if(ret_cb != NULL || ret_ct != NULL)
+ TEST_ERROR
+
+ /*
+ * Case (3)
+ * To verify the object flush property values retrieved from a
+ * non-default file access property list.
+ */
+ /* Set the object flush property */
+ if(H5Pset_object_flush_cb(fapl, flush_cb, &flush_ct) < 0)
+ TEST_ERROR
+
+ /* Increment the counter */
+ ++flush_ct;
+
+ /* Retrieve object flush property values for the non-default file access property list */
+ if(H5Pget_object_flush_cb(fapl, &ret_cb, &ret_ct) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_cb != flush_cb || *(unsigned *)ret_ct != 1)
+ TEST_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Case (4)
+ * To verify the object flush property values retrieved from a
+ * default file access property list of a file
+ */
+
+ /* Reset values */
+ flush_ct = 0;
+ ret_cb = NULL;
+ ret_ct = NULL;
+
+ /* Make a copy of the input parameter in_fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ /* Create the test file: without setting object flush property in fapl */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get the file's file access property list */
+ if((ffapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Retrieve the object flush property values */
+ if(H5Pget_object_flush_cb(ffapl, &ret_cb, &ret_ct) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_cb != NULL || ret_ct != NULL)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Pclose(ffapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Cases (5)
+ * To verify the object flush property values retrieved from a non-default
+ * file access property list of a file.
+ * To verify the object flush callback is invoked when doing H5Oflush(),
+ * H5Dflush(), H5Gflush() and H5Tflush().
+ */
+ /* Reset values */
+ flush_ct = 0;
+ ret_cb = NULL;
+ ret_ct = NULL;
+
+ /* Set the object flush property */
+ if(H5Pset_object_flush_cb(fapl, flush_cb, &flush_ct) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file: with object flush property setting in fapl */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create a dataset */
+ if((sid = H5Screate_simple(2, dims, dims)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Create a dataset */
+ if((did1 = H5Dcreate2(fid, "dataset1", H5T_NATIVE_INT, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Initialize data buffer */
+ for(i = 0; i < 50; i++)
+ buf[i] = i + 1;
+
+ /* Write to the dataset */
+ if(H5Dwrite(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, buf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Flush the dataset object */
+ if(H5Oflush(did1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the file's file access property list */
+ if((ffapl = H5Fget_access_plist(fid)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Retrieve the object flush property values */
+ if(H5Pget_object_flush_cb(ffapl, &ret_cb, &ret_ct) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_cb != flush_cb || *(unsigned *)ret_ct != 1)
+ TEST_ERROR
+
+ /* Create a group */
+ if((gid = H5Gcreate2(fid, "group", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Flush the group */
+ if(H5Gflush(gid) < 0)
+ TEST_ERROR
+
+ /* Retrieve the object flush property values */
+ if(H5Pget_object_flush_cb(ffapl, &ret_cb, &ret_ct) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_cb != flush_cb || *(unsigned *)ret_ct != 2)
+ TEST_ERROR
+
+ /* Create a dataset */
+ if((did2 = H5Dcreate2(gid, "dataset2", H5T_NATIVE_INT, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Flush the dataset */
+ if(H5Dflush(did2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Retrieve the object flush property values */
+ if(H5Pget_object_flush_cb(ffapl, &ret_cb, &ret_ct) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_cb != flush_cb || *(unsigned *)ret_ct != 3)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Gclose(gid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(ffapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Pclose(ffapl);
+ H5Sclose(sid);
+ H5Dclose(did1);
+ H5Dclose(did2);
+ H5Gclose(gid);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_object_flush_cb() */
+
+/*
+ * Tests for H5Pset/get_append_flush()
+ */
+
+
+/* The callback function for append flush property */
+static herr_t
+append_cb(hid_t H5_ATTR_UNUSED dset_id, hsize_t H5_ATTR_UNUSED *cur_dims, void *_udata)
+{
+ unsigned *count = (unsigned *)_udata;
+ ++(*count++);
+ return 0;
+} /* append_cb() */
+
+
+/* The callback function for append flush property */
+static herr_t
+append_cb2(hid_t H5_ATTR_UNUSED dset_id, hsize_t H5_ATTR_UNUSED *cur_dims, void *_udata)
+{
+ unsigned *count = (unsigned *)_udata;
+ ++(*count++);
+ return 0;
+} /* append_cb2() */
+
+
+
+/*
+ * test_append_flush_generic()
+ *
+ * Verify H5Pget/set_append_flush() work as specified for a generic dataset
+ * access property list:
+ * 1) To verify the append flush property values retrieved from a default
+ * access property list.
+ * -- zero boundary, null callback function, null user data
+ * 2) To verify the failure conditions in setting append flush property:
+ * -- an invalid dataset rank: <= 0, > H5S_MAX_RANK
+ * -- undefined callback but defined user data
+ * -- no boundary specified
+ * -- invalid boundary size: H5S_UNLIMITED, negative value
+ * 3) To verify the append flush property values retrieved from a non-default
+ * access property list.
+ * -- the set callback function, the set user data
+ * -- the # of boundary sizes retrieved does not exceed MIN(input ndims, the ndims set)
+ */
+static int
+test_append_flush_generic(void)
+{
+ hid_t dapl = -1; /* A copy of dataset access property */
+ hsize_t boundary[3]; /* The boundary for append flush property */
+ unsigned count = 0; /* The user data for append flush property */
+ hsize_t ret_boundary[3]; /* The boundary set in append flush property */
+ H5D_append_cb_t ret_cb; /* The callback function set in append flush property */
+ unsigned *ret_count; /* The user data set in append flush property */
+ herr_t ret; /* The return value */
+
+ TESTING("H5Fget/set_append_flush() for a generic dataset access property list");
+
+
+ /*
+ * Case (1)
+ * To verify the retrieved append flush property values:
+ * -- zero boundary, null callback function, null user data
+ */
+
+ /* Create a copy of dataset access property list */
+ if((dapl = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(dapl, 2, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ FAIL_STACK_ERROR
+
+ /* Verify expected values */
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 0)
+ TEST_ERROR;
+ if(ret_cb != NULL || ret_count != NULL)
+ TEST_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(dapl) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Case (2)
+ * To verify the failure conditions in setting append flush property:
+ * -- an invalid dataset rank: <= 0, > H5S_MAX_RANK
+ * -- no boundary specified
+ * -- undefined callback but defined user data
+ * -- invalid boundary size: H5S_UNLIMITED, negative value
+ */
+
+ /* Create a copy of dataset access property list */
+ if((dapl = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Invalid dataset rank: zero value */
+ H5E_BEGIN_TRY {
+ ret = H5Pset_append_flush(dapl, 0, NULL, NULL, &count);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Invalid dataset rank: > H5S_MAX_RANK */
+ H5E_BEGIN_TRY {
+ ret = H5Pset_append_flush(dapl, H5S_MAX_RANK+1, NULL, NULL, &count);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* No boundary specified */
+ H5E_BEGIN_TRY {
+ ret = H5Pset_append_flush(dapl, 2, NULL, NULL, &count);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Set up a valid boundary */
+ boundary[0] = 1;
+ boundary[1] = 1;
+
+ /* Undefined callback function but defined user data */
+ H5E_BEGIN_TRY {
+ ret = H5Pset_append_flush(dapl, 2, boundary, NULL, &count);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Invalid boundary size: negative value */
+ boundary[0] = (hsize_t)-1;
+ boundary[1] = 1;
+ H5E_BEGIN_TRY {
+ ret = H5Pset_append_flush(dapl, 2, boundary, append_cb, &count);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /* Invalid boundary size: H5S_UNLIMITED */
+ boundary[0] = 1;
+ boundary[1] = H5S_UNLIMITED;
+ H5E_BEGIN_TRY {
+ ret = H5Pset_append_flush(dapl, 2, boundary, append_cb, &count);
+ } H5E_END_TRY;
+ if(ret >= 0)
+ TEST_ERROR
+
+ /*
+ * Case (3)
+ * To verify the append flush property values retrieved from a non-default
+ * access property list:
+ * -- the set callback function, the set user data
+ * -- the # of boundary sizes retrieved does not exceed MIN(input ndims, the ndims set)
+ */
+ boundary[0] = boundary[1] = 1;
+ boundary[2] = 0;
+ count = 1;
+ if(H5Pset_append_flush(dapl, 2, boundary, append_cb, &count) < 0)
+ FAIL_STACK_ERROR;
+ ++count;
+
+ /* Verify expected values: with boundary rank > set boundary rank */
+ if(H5Pget_append_flush(dapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+ if(ret_boundary[0] != 1 || ret_boundary[1] != 1 || boundary[2] != 0)
+ TEST_ERROR;
+ if(ret_cb == NULL || ret_count == NULL || *ret_count != 2)
+ TEST_ERROR
+
+ /* Verify expected values: with boundary rank < set boundary rank */
+ HDmemset(ret_boundary, 0, sizeof(ret_boundary));
+ if(H5Pget_append_flush(dapl, 1, ret_boundary, NULL, NULL) < 0)
+ TEST_ERROR
+ if(ret_boundary[0] != 1 || ret_boundary[1] != 0 || boundary[2] != 0)
+ TEST_ERROR;
+
+ /* Closing */
+ if(H5Pclose(dapl) < 0)
+ FAIL_STACK_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(dapl);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_append_flush_generic() */
+
+/*
+ * test_append_flush_dataset_chunked()
+ *
+ * Verify H5Pget/set_append_flush() work as specified for a chunked dataset's
+ * access property list:
+ * 1) To verify the append flush property values retrieved from a default
+ * access property list:
+ * -- zero boundary, null callback function, null user data
+ * 2) To verify failure in creating dataset when:
+ * -- the rank set in append flush property is not the same as the dataset's rank
+ * -- boundary (non-zero) is set for a non-extendible dimension
+ * 3) To verify the append flush property values retrieved from a non-default
+ * access property list:
+ * -- the set callback function, the set user data
+ * -- the # of boundary sizes retrieved does not exceed MIN(input ndims, the ndims set)
+ */
+static int
+test_append_flush_dataset_chunked(hid_t in_fapl)
+{
+ hid_t fid = -1; /* file ID */
+ hid_t fapl = -1; /* A copy of file access property */
+ hid_t did1 = -1, did2 = -1; /* The datset ID */
+ hid_t sid = -1; /* The dataspace ID */
+ hid_t dcpl = -1; /* A copy of dataset creation property */
+ hid_t dapl = -1; /* A copy of dataset access property */
+ hid_t ddapl = -1; /* The dataset access property of the opened dataset */
+
+ hsize_t boundary[3]; /* Boundary size */
+ unsigned count = 0; /* User data */
+
+ hsize_t ret_boundary[3]; /* Boundary size set in the append flush property */
+ H5D_append_cb_t ret_cb; /* The callback function set in the append flush property */
+ unsigned *ret_count; /* The user data set in the append flush property */
+
+ char filename[NAME_BUF_SIZE]; /* file name */
+
+ hsize_t dims[2] = {100, 0}; /* The dataset dimension sizes */
+ hsize_t maxdims[2] = {100, H5S_UNLIMITED}; /* The dataset maximum dimension sizes */
+ hsize_t chunk_dims[2] = {5,2}; /* The chunk dimesion sizes */
+
+ TESTING("H5Fget/set_append_flush() for a chunked dataset's access property list");
+
+ /*
+ * Case (1)--
+ * For a chunked dataset's access property list:
+ * --to verify the append flush property values retrieved from a default access
+ * a default access property list is:
+ * zero rank, zero boundary, null callback function, null user data
+ */
+
+ /* Get a copy of the input parameter in_fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ /* Create the test file to work on */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a chunked dataset with 1 extendible dimension */
+ if((sid = H5Screate_simple(2, dims, maxdims)) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 2, chunk_dims) < 0)
+ FAIL_STACK_ERROR;
+ if((did1 = H5Dcreate2(fid, "dataset1", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ TEST_ERROR
+
+ /* Get the dataset's access property list */
+ if((ddapl = H5Dget_access_plist(did1)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 0 || ret_boundary[2] != 0)
+ TEST_ERROR;
+ if(ret_cb != NULL || ret_count != NULL)
+ TEST_ERROR
+
+ /* Close the dataset's access property list */
+ if(H5Pclose(ddapl) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Case (2)--
+ * For a chunked dataset's access property list:
+ * --to verify failure in creating the dataset when:
+ * --the rank set in append flush property is not the same as the dataset's rank
+ * -- boundary (non-zero) is set for a non-extendible dimension
+ * --to verify failure in opening the dataset
+ * -- boundary (non-zero) is set for a non-extendible dimension
+ */
+ /* Create a copy of dataset access property list */
+ if((dapl = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set boundary dimension rank > the rank of dataset to be created */
+ HDmemset(boundary, 0, sizeof(boundary));
+ if(H5Pset_append_flush(dapl, 3, boundary, NULL, NULL) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should fail to Create the dataset */
+ H5E_BEGIN_TRY {
+ did2 = H5Dcreate2(fid, "dataset2", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, dapl);
+ } H5E_END_TRY;
+ if(did2 >= 0)
+ TEST_ERROR
+
+ /* Set boundary for a non-extendible dimension */
+ boundary[0] = boundary[1] = 1;
+ if(H5Pset_append_flush(dapl, 2, boundary, NULL, NULL) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should fail to create the dataset */
+ H5E_BEGIN_TRY {
+ did2 = H5Dcreate2(fid, "dataset2", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, dapl);
+ } H5E_END_TRY;
+ if(did2 >= 0)
+ TEST_ERROR
+
+ /* Create and close the dataset */
+ if((did2 = H5Dcreate2(fid, "dataset2", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should fail to open the dataset */
+ H5E_BEGIN_TRY {
+ did2 = H5Dopen2(fid, "dataset2", dapl);
+ } H5E_END_TRY;
+ if(did2 >= 0)
+ TEST_ERROR
+
+ /*
+ * Case (3)--
+ * For a chunked dataset's access property list:
+ * --To verify the append flush property values retrieved from a non-default
+ * access property list:
+ * -- the set callback function, the set user data
+ * -- the # of boundary sizes retrieved does not exceed MIN(input ndims, the ndims set)
+ */
+
+ boundary[0] = 0;
+ boundary[1] = 1;
+ if(H5Pset_append_flush(dapl, 2, boundary, append_cb, &count) < 0)
+ FAIL_STACK_ERROR
+ if((did2 = H5Dopen2(fid, "dataset2", dapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get the dataset's access property list */
+ if((ddapl = H5Dget_access_plist(did2)) < 0)
+ FAIL_STACK_ERROR
+
+ HDmemset(ret_boundary, 0, sizeof(ret_boundary));
+ ret_cb = NULL;
+ ret_count = NULL;
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_cb != append_cb || ret_count != &count)
+ TEST_ERROR
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 1 || ret_boundary[2] != 0)
+ TEST_ERROR
+
+ HDmemset(ret_boundary, 0, sizeof(ret_boundary));
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 1, ret_boundary, NULL, NULL) < 0)
+ TEST_ERROR
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 0 || ret_boundary[2] != 0)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Pclose(ddapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR;
+
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(dcpl);
+ H5Pclose(dapl);
+ H5Pclose(ddapl);
+ H5Dclose(did1);
+ H5Dclose(did2);
+ H5Pclose(fapl);
+ H5Sclose(sid);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_append_flush_dataset_chunked() */
+
+/*
+ * test_append_flush_dataset_fixed():
+ *
+ * Verify H5Pget/set_append_flush() work as specified for a
+ * non-chunked (fixed size) dataset's access property list:
+ * (1) To verify success in creating the dataset--whatever is set for the append flush property setting
+ * (2) To verify that default append flush property values are retrieved for both
+ * default or non-default access property list:
+ * -- zero boundary, null callback function, null user data
+ */
+static int
+test_append_flush_dataset_fixed(hid_t in_fapl)
+{
+ hid_t fid = -1; /* file ID */
+ hid_t fapl = -1; /* A copy of file access property */
+ hid_t did1 = -1, did2 = -1; /* The datset ID */
+ hid_t sid = -1; /* The dataspace ID */
+ hid_t dapl = -1; /* A copy of dataset access property */
+ hid_t ddapl = -1; /* The dataset access property of the opened dataset */
+
+ hsize_t boundary[3]; /* Boundary size */
+ unsigned count = 0; /* User data */
+
+ hsize_t ret_boundary[3]; /* Boundary size set in the append flush property */
+ H5D_append_cb_t ret_cb; /* The callback function set in the append flush property */
+ unsigned *ret_count; /* The user data set in the append flush property */
+
+ char filename[NAME_BUF_SIZE]; /* file name */
+
+ hsize_t dims[1] = {100};
+
+ TESTING("H5Fget/set_append_flush() for a non-chunked dataset's access property list");
+
+ /*
+ * Case (1)--
+ * For a non-chunked dataset's access property list:
+ * --to verify the append flush property values retrieved from
+ * a default access property list is:
+ * zero boundary, null callback function, null user data
+ */
+
+ /* Get a copy of the input parameter in_fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ /* Create the test file to work on */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a dataset */
+ if((sid = H5Screate_simple(1, dims, dims)) < 0)
+ FAIL_STACK_ERROR;
+ if((did1 = H5Dcreate2(fid, "dataset1", H5T_NATIVE_INT, sid, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ TEST_ERROR
+
+ /* Get the dataset's access property list */
+ if((ddapl = H5Dget_access_plist(did1)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 0 || ret_boundary[2] != 0)
+ TEST_ERROR;
+ if(ret_cb != NULL || ret_count != NULL)
+ TEST_ERROR
+
+ /* Close the dataset's access property list */
+ if(H5Pclose(ddapl) < 0)
+ FAIL_STACK_ERROR;
+
+ /*
+ * Case (2)--
+ * For a non-chunked dataset's access property list:
+ * --to verify success in creating and opening the dataset even when append flush property
+ * is setup with error conditions:
+ * --the rank set in append flush property is not the same as the dataset's rank
+ * --boundary is set
+ * --to verify the append flush property values are:
+ * zero boundary, null callback function, null user data
+ */
+ /* Create a copy of dataset access property list */
+ if((dapl = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+ FAIL_STACK_ERROR
+
+ boundary[0] = 1;
+ boundary[1] = boundary[2] = 0;
+ if(H5Pset_append_flush(dapl, 3, boundary, append_cb, &count) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should succeed to create the dataset: append flush property has no effect */
+ if((did2 = H5Dcreate2(fid, "dataset2", H5T_NATIVE_INT, sid, H5P_DEFAULT, H5P_DEFAULT, dapl)) < 0)
+ TEST_ERROR
+
+ /* Get the dataset's access property list */
+ if((ddapl = H5Dget_access_plist(did2)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_cb != NULL || ret_count != NULL)
+ TEST_ERROR
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 0 || ret_boundary[2] != 0)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Pclose(ddapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Should succeed in opening the dataset: append flush property has no effect */
+ if((did2 = H5Dopen2(fid, "dataset2", dapl)) < 0)
+ TEST_ERROR
+
+ /* Get the dataset's access property list */
+ if((ddapl = H5Dget_access_plist(did2)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_cb != NULL || ret_count != NULL)
+ TEST_ERROR
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 0 || ret_boundary[2] != 0)
+ TEST_ERROR
+
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR
+ /*
+ * Case (3)--
+ * For a non-chunked dataset's access property list:
+ * --To verify the append flush property values retrieved from a non-default
+ * access property list:
+ * zero boundary, null callback function, null user data
+ */
+
+ HDmemset(boundary, 0, sizeof(boundary));
+ if(H5Pset_append_flush(dapl, 1, boundary, append_cb, &count) < 0)
+ FAIL_STACK_ERROR
+ if((did2 = H5Dopen2(fid, "dataset2", dapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get the dataset's access property list */
+ if((ddapl = H5Dget_access_plist(did2)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 1, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values */
+ if(ret_cb != NULL || ret_count != NULL)
+ TEST_ERROR
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 0 || ret_boundary[2] != 0)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Pclose(ddapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(dapl);
+ H5Pclose(ddapl);
+ H5Dclose(did1);
+ H5Dclose(did2);
+ H5Pclose(fapl);
+ H5Sclose(sid);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_append_flush_dataset_fixed() */
+
+/*
+ * test_append_flush_multiple()
+ *
+ * Verify H5Pget/set_append_flush() work as specified for multiple opens
+ * of a dataset:
+ * (1) did1 = H5Dcreate(...dapl1...)
+ * did2 = H5Dopen2(...dapl2)
+ * H5Pget_append_flush(did1...)
+ * H5Pget_append_flush(did2...)
+ * -- should return append flush property values set in dapl1
+ * (2) H5Dcreate(...H5P_DEFAULT...)
+ * H5Dclose()
+ * did1 = H5Dopen2(...dapl1)
+ * did2 = H5Dopen2(..dapl2)
+ * H5Pget_append_flush(did1, ...)
+ * H5Pget_append_flush(did2, ...)
+ * -- should return append flush property values set in dapl1
+ * NOTE:
+ * FOR NOW: return the append flush property values of the create or the very first open
+ * LATER ON: should REJECT subsequent dataset open if append flush property values differ
+ */
+static int
+test_append_flush_dataset_multiple(hid_t in_fapl)
+{
+ hid_t fid = -1; /* file ID */
+ hid_t fapl = -1; /* A copy of file access property */
+ hid_t did1 = -1, did2 = -1; /* The datset ID */
+ hid_t sid = -1; /* The dataspace ID */
+ hid_t dcpl = -1; /* A copy of dataset creation property */
+ hid_t dapl1 = -1; /* A copy of dataset access property */
+ hid_t dapl2 = -1; /* A copy of dataset access property */
+ hid_t ddapl = -1; /* The dataset access property of the opened dataset */
+
+ hsize_t boundary1[3]; /* Boundary size */
+ hsize_t boundary2[3]; /* Boundary size */
+ unsigned count1 = 0; /* User data */
+ unsigned count2 = 0; /* User data */
+
+ hsize_t ret_boundary[3]; /* Boundary size set in the append flush property */
+ H5D_append_cb_t ret_cb; /* The callback function set in the append flush property */
+ unsigned *ret_count; /* The user data set in the append flush property */
+
+ char filename[NAME_BUF_SIZE]; /* file name */
+
+ hsize_t dims[2] = {0, 0}; /* The dataset dimension sizes */
+ hsize_t maxdims[2] = {H5S_UNLIMITED, H5S_UNLIMITED}; /* The dataset maximum dimension sizes */
+ hsize_t chunk_dims[2] = {5,2}; /* The chunk dimesion sizes */
+
+ TESTING("H5Fget/set_append_flush() for multiple opens of a chunked dataset");
+
+ /*
+ * Case (1)
+ * For a chunked dataset's access property list:
+ * did1 = H5Dcreate(...dapl1...)
+ * did2 = H5Dopen2(...dapl2)
+ * H5Pget_append_flush(did1...)
+ * H5Pget_append_flush(did2...)
+ * -- should return append flush property values set in dapl1
+ */
+
+ /* Create a copy of dataset access property list */
+ if((dapl1 = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+ FAIL_STACK_ERROR
+ if((dapl2 = H5Pcreate(H5P_DATASET_ACCESS)) < 0)
+ FAIL_STACK_ERROR
+
+ boundary1[0] = 0;
+ boundary1[1] = 1;
+ count1 = 0;
+ if(H5Pset_append_flush(dapl1, 2, boundary1, append_cb, &count1) < 0)
+ FAIL_STACK_ERROR
+ boundary2[0] = 1;
+ boundary2[1] = 0;
+ count2 = 0;
+ if(H5Pset_append_flush(dapl2, 2, boundary2, append_cb2, &count2) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a copy of the input parameter in_fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ /* Create the test file to work on */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a chunked dataset with 2 extendible dimensions */
+ if((sid = H5Screate_simple(2, dims, maxdims)) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 2, chunk_dims) < 0)
+ FAIL_STACK_ERROR;
+ if((did1 = H5Dcreate2(fid, "dataset1", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, dapl1)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Open the dataset */
+ if((did2 = H5Dopen2(fid, "dataset1", dapl2)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the dataset's access property list for did1 */
+ if((ddapl = H5Dget_access_plist(did1)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values: should be the setting in dapl1 */
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 1 || ret_boundary[2] != 0)
+ TEST_ERROR;
+ if(ret_cb != append_cb || ret_count != &count1)
+ TEST_ERROR
+
+ /* Close the dataset's access property list */
+ if(H5Pclose(ddapl) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the dataset's access property list for did2 */
+ if((ddapl = H5Dget_access_plist(did2)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values: should be the setting in dapl1 */
+ if(ret_boundary[0] != 0 || ret_boundary[1] != 1 || ret_boundary[2] != 0)
+ TEST_ERROR;
+ if(ret_cb != append_cb || ret_count != &count1)
+ TEST_ERROR
+
+ /* Close the dataset's access property list */
+ if(H5Pclose(ddapl) < 0)
+ FAIL_STACK_ERROR;
+ H5Dclose(did1);
+ H5Dclose(did2);
+
+ /*
+ * Case (2)
+ * For a chunked dataset's access property list:
+ * H5Dcreate(...H5P_DEFAULT...)
+ * H5Dclose()
+ * did1 = H5Dopen2(...dapl1)
+ * did2 = H5Dopen2(..dapl2)
+ * H5Pget_append_flush(did1, ...)
+ * H5Pget_append_flush(did2, ...)
+ * -- should return append flush property values set in dapl1
+ */
+ if((did1 = H5Dcreate2(fid, "dataset2", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Open the dataset with append flush setting in dapl2 */
+ if((did1 = H5Dopen2(fid, "dataset2", dapl2)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Open the dataset with append flush setting in dapl1 */
+ if((did2 = H5Dopen2(fid, "dataset2", dapl1)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Get the dataset's access property list for did1 */
+ if((ddapl = H5Dget_access_plist(did1)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values: should be the setting in dapl2 */
+ if(ret_boundary[0] != 1 || ret_boundary[1] != 0 || ret_boundary[2] != 0)
+ TEST_ERROR;
+ if(ret_cb != append_cb2 || ret_count != &count2)
+ TEST_ERROR
+
+ /* Close the access property list */
+ if(H5Pclose(ddapl) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /* Get the dataset's access property list for did2 */
+ if((ddapl = H5Dget_access_plist(did2)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Retrieve the append flush property values */
+ if(H5Pget_append_flush(ddapl, 3, ret_boundary, &ret_cb, (void **)&ret_count) < 0)
+ TEST_ERROR
+
+ /* Verify expected values: should be the setting in dapl2 */
+ if(ret_boundary[0] != 1 || ret_boundary[1] != 0 || ret_boundary[2] != 0)
+ TEST_ERROR;
+ if(ret_cb != append_cb2 || ret_count != &count2)
+ TEST_ERROR
+
+ /* Closing */
+ if(H5Pclose(ddapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dapl2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dapl1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(dcpl);
+ H5Pclose(dapl1);
+ H5Pclose(dapl2);
+ H5Pclose(ddapl);
+ H5Dclose(did1);
+ H5Dclose(did2);
+ H5Pclose(fapl);
+ H5Sclose(sid);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_append_flush_dataset_multiple() */
+
+
+
+/****************************************************************
+**
+** test_file_lock_same():
+** With the implementation of file locking, this test checks file
+** open with different combinations of flags.
+** This is for single process access.
+**
+*****************************************************************/
+static int
+test_file_lock_same(hid_t in_fapl)
+{
+ hid_t fid = -1, fid2 = -1; /* File IDs */
+ hid_t fapl = -1; /* File access property list */
+ unsigned intent; /* File access flags */
+ char filename[NAME_BUF_SIZE]; /* file name */
+
+ /* Output message about test being performed */
+ TESTING("File open with different combinations of flags--single process access");
+
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[1], fapl, filename, sizeof(filename));
+
+ /*
+ * Case 1: 1) RDWR 2) RDWR : should succeed
+ */
+ /* Create file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get and check file intent */
+ if(H5Fget_intent(fid, &intent) < 0)
+ FAIL_STACK_ERROR
+
+ if(intent != H5F_ACC_RDWR)
+ TEST_ERROR
+
+ /* Open the same file with RDWR */
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get and check the intent */
+ if(H5Fget_intent(fid2, &intent) < 0)
+ FAIL_STACK_ERROR
+ if(intent != H5F_ACC_RDWR)
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 2: 1) RDWR 2) RDONLY : should succeed
+ */
+ /* Open file with RDWR */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get and check the intent */
+ if(H5Fget_intent(fid, &intent) < 0)
+ FAIL_STACK_ERROR
+ if(intent != H5F_ACC_RDWR)
+ TEST_ERROR
+
+ /* Open file with RDONLY */
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get and check the intent: should get intent from 1st open */
+ if(H5Fget_intent(fid2, &intent) < 0)
+ FAIL_STACK_ERROR
+ if(intent != H5F_ACC_RDWR)
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 3: 1) RDONLY 2) RDWR : should fail
+ */
+ /* Open file with RDONLY */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get and check the intent */
+ if(H5Fget_intent(fid, &intent) < 0)
+ FAIL_STACK_ERROR
+ if(intent != H5F_ACC_RDONLY)
+ TEST_ERROR
+
+ /* Open file with RDWR should fail */
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(filename, H5F_ACC_RDWR, fapl);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR
+
+ /* Close first file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 4: 1) RDONLY 2) RDONLY : should succeed
+ */
+ /* Open file with RDONLY */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get and check the intent */
+ if(H5Fget_intent(fid, &intent) < 0)
+ FAIL_STACK_ERROR
+ if(intent != H5F_ACC_RDONLY)
+ TEST_ERROR
+
+ /* Open file with RDONLY */
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get and check the intent */
+ if(H5Fget_intent(fid2, &intent) < 0)
+ FAIL_STACK_ERROR
+ if(intent != H5F_ACC_RDONLY)
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ H5Fclose(fid2);
+ } H5E_END_TRY;
+
+ return -1;
+} /* end test_file_lock_same() */
+
+/****************************************************************
+**
+** test_file_lock_swmr_same():
+** With the implementation of file locking, this test checks file
+** open with different combinations of flags + SWMR flags.
+** This is for single process access.
+**
+*****************************************************************/
+static int
+test_file_lock_swmr_same(hid_t in_fapl)
+{
+ hid_t fid, fid2; /* File IDs */
+ hid_t fapl; /* File access property list */
+ char filename[NAME_BUF_SIZE]; /* file name */
+
+ /* Output message about test being performed */
+ TESTING("File open with different combinations of flags + SWMR flags--single process access");
+
+ /* Get a copy of the parameter in_fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[1], fapl, filename, sizeof(filename));
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create a file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Cases a, b, c, d: H5Fopen failure cases
+ */
+
+ /*
+ * Case a: RDWR|SWRM_READ : should fail
+ */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_READ, fapl);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR
+
+ /*
+ * Case b: RDWR|SWMM_WRTE|SWMR_READ : should fail
+ */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE|H5F_ACC_SWMR_READ, fapl);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR
+
+ /*
+ * Case c: RDONLY|SWMM_WRITE : should fail
+ */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR
+
+ /*
+ * Case d: RDONLY|SWMM_WRITE|SWMR_READ : should fail
+ */
+ H5E_BEGIN_TRY {
+ fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_WRITE|H5F_ACC_SWMR_READ, fapl);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR
+
+ /*
+ * Cases 1 - 12: combinations of different flags for 1st and 2nd opens
+ */
+
+ /*
+ * Case 1: 1) RDWR 2) RDWR|SWMR_WRITE : should fail
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 2: 1) RDWR 2) RDONLY|SWMR_READ : should succeed
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT)) < 0)
+ TEST_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 3: 1) RDWR|SWMR_WRITE 2)RDWR : should succeed
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 4: 1) RDWR|SWMR_WRITE 2) RDWR|SWMR_WRITE : should succeed
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 5: 1) RDWR|SWMR_WRITE 2) RDONLY|SWMR_READ : should succeed
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 6: 1) RDWR|SWMR_WRITE 2) RDONLY : should succeed
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 7: 1) RDONLY|SWMR_READ 2)RDWR : should fail
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 8: 1) RDONLY|SWMR_READ 2) RDWR|SWMR_WRITE : should fail
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 9: 1) RDONLY|SWMR_READ 2) RDONLY|SWMR_READ : should succeed
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 10: 1) RDONLY|SWMR_READ 2) RDONLY : should succeed
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ TEST_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 11: 1) RDONLY 2) RDWR|SWMR_WRITE: should fail
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+ if(fid2 >= 0)
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 12: 1) RDONLY 2) RDONLY|SWMR_READ : should fail
+ */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ H5E_BEGIN_TRY {
+ fid2 = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl);
+ } H5E_END_TRY;
+ if(fid2 >=0 )
+ TEST_ERROR
+
+ /* Close file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ H5Fclose(fid2);
+ } H5E_END_TRY;
+
+ return -1;
+} /* end test_file_lock_swmr_same() */
+
+
+/****************************************************************
+**
+** test_file_lock_concur():
+** With the implementation of file locking, this test checks file
+** open with different combinations of flags.
+** This is for concurrent access.
+**
+*****************************************************************/
+#if !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID) && defined(H5_HAVE_FLOCK))
+
+static int
+test_file_lock_concur(hid_t H5_ATTR_UNUSED in_fapl)
+{
+ /* Output message about test being performed */
+ TESTING("File open with different combinations of flags--concurrent access");
+ SKIPPED();
+ HDputs(" Test skipped due to fork, waitpid, or flock not defined.");
+ return 0;
+
+} /* end test_file_lock_concur() */
+
+#else
+
+static int
+test_file_lock_concur(hid_t in_fapl)
+{
+ hid_t fid = -1; /* File ID */
+ hid_t fapl = -1; /* File access property list */
+ char filename[NAME_BUF_SIZE]; /* file name */
+ pid_t childpid=0; /* Child process ID */
+ int child_status; /* Status passed to waitpid */
+ int child_wait_option=0; /* Options passed to waitpid */
+ int out_pdf[2];
+ int notify = 0;
+
+ /* Output message about test being performed */
+ TESTING("File open with different combinations of flags--concurrent access");
+
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[1], fapl, filename, sizeof(filename));
+
+ /* Create the test file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 1: 1) RDWR 2) RDWR : should fail
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDWR, fapl);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 2: 1) RDWR 2) RDONLY : should fail
+ */
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Opens the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Opens the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 3: 1) RDONLY 2) RDWR : should fail
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Opens the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDWR, fapl);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ } /* end if */
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Opens the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 4: 1) RDONLY 2) RDONLY : should succeed
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Opens the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl);
+ } H5E_END_TRY;
+
+ /* Should succeed */
+ if(child_fid >= 0) {
+ /* Close the file */
+ if(H5Fclose(child_fid) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ } /* end if */
+
+ HDexit(EXIT_FAILURE);
+ } /* end if */
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* end test_file_lock_concur() */
+
+#endif /* !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID) && defined(H5_HAVE_FLOCK)) */
+
+/****************************************************************
+**
+** test_file_lock_swmr_concur(): low-level file test routine.
+** With the implementation of file locking, this test checks file
+** open with different combinations of flags + SWMR flags.
+** This is for concurrent access.
+**
+*****************************************************************/
+#if !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID))
+
+static int
+test_file_lock_swmr_concur(hid_t H5_ATTR_UNUSED in_fapl)
+{
+ /* Output message about test being performed */
+ TESTING("File open with different combintations of flags + SWMR flags--concurrent access");
+ SKIPPED();
+ HDputs(" Test skipped due to fork or waitpid not defined.");
+ return 0;
+
+} /* end test_file_lock_swmr_concur() */
+
+#else
+
+static int
+test_file_lock_swmr_concur(hid_t in_fapl)
+{
+ hid_t fid; /* File ID */
+ hid_t fapl; /* File access property list */
+ char filename[NAME_BUF_SIZE]; /* file name */
+ pid_t childpid=0; /* Child process ID */
+ int child_status; /* Status passed to waitpid */
+ int child_wait_option=0; /* Options passed to waitpid */
+ int out_pdf[2];
+ int notify = 0;
+
+ /* Output message about test being performed */
+ TESTING("File open with different combintations of flags + SWMR flags--concurrent access");
+
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[2], fapl, filename, sizeof(filename));
+
+ /* Set to use latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create the test file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 1: 1) RDWR 2) RDWR|SWMR_WRITE : should fail
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 2: 1) RDWR 2) RDONLY|SWMR_READ: should fail
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 3: 1) RDWR|SWMR_WRITE 2) RDWR : should fail
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 4: 1) RDWR|SWMR_WRITE 2) RDWR|SWMR_WRITE : should fail
+ */
+
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 5: 1) RDWR|SWMR_WRITE 2) RDONLY|SWMR_READ : should succeed
+ */
+
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl);
+ } H5E_END_TRY;
+
+ /* Should succeed */
+ if(child_fid >= 0) {
+ if(H5Fclose(child_fid) < 0)
+ FAIL_STACK_ERROR
+ HDexit(EXIT_SUCCESS);
+ }
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 6: 1) RDWR|SWMR_WRITE 2) RDONLY : should fail
+ */
+
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 7: 1) RDONLY|SWMR_READ 2) RDWR : should fail
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDWR, H5P_DEFAULT);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 8: 1) RDONLY|SWMR_READ 2) RDWR|SWMR_WRITE : should fail
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 9: 1) RDONLY|SWMR_READ 2) RDONLY|SWMR_READ : should succeed
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl);
+ } H5E_END_TRY;
+
+ /* Should succeed */
+ if(child_fid >= 0) {
+ if(H5Fclose(child_fid) < 0)
+ FAIL_STACK_ERROR
+ HDexit(EXIT_SUCCESS);
+ }
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 10: 1) RDONLY|SWMR_READ 2) RDONLY : should succeed
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ if((child_fid = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Should succeed */
+ if(child_fid >= 0) {
+ if(H5Fclose(child_fid) < 0)
+ FAIL_STACK_ERROR
+ HDexit(EXIT_SUCCESS);
+ }
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 11: 1) RDONLY 2) RDWR|SWMR_WRITE : should fail
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl);
+ } H5E_END_TRY;
+
+ /* Should fail */
+ if(child_fid == FAIL)
+ HDexit(EXIT_SUCCESS);
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /*
+ * Case 12: 1) RDONLY 2) RDONLY|SWMR_READ : should succeed
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the test file */
+ H5E_BEGIN_TRY {
+ child_fid = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl);
+ } H5E_END_TRY;
+
+ /* Should succeed */
+ if(child_fid >= 0) {
+ if(H5Fclose(child_fid) < 0)
+ FAIL_STACK_ERROR
+ HDexit(EXIT_SUCCESS);
+ }
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ FAIL_STACK_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } else
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* end test_file_lock_swmr_concur() */
+
+
+
+#endif /* !(defined(H5_HAVE_FORK && defined(H5_HAVE_WAITPID)) */
+
+/****************************************************************
+**
+** test_file_lock_swmr_concur(): low-level file test routine.
+** With the implementation of file locking, this test checks file
+** open with different combinations of flags + SWMR flags.
+** This is for concurrent access.
+**
+*****************************************************************/
+static int
+test_file_lock_env_var(hid_t in_fapl)
+{
+#if !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID))
+ SKIPPED();
+ HDputs(" Test skipped due to fork or waitpid not defined.");
+ return 0;
+#else
+ hid_t fid = -1; /* File ID */
+ hid_t fapl = -1; /* File access property list */
+ char filename[NAME_BUF_SIZE]; /* file name */
+ pid_t childpid=0; /* Child process ID */
+ int child_status; /* Status passed to waitpid */
+ int child_wait_option=0; /* Options passed to waitpid */
+ int out_pdf[2];
+ int notify = 0;
+
+
+ TESTING("File locking environment variable");
+
+
+ /* Set the environment variable */
+ if(HDsetenv("HDF5_USE_FILE_LOCKING", "FALSE", TRUE) < 0)
+ TEST_ERROR
+
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ TEST_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[1], fapl, filename, sizeof(filename));
+
+ /* Create the test file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ TEST_ERROR
+
+ /* Open a file for read-only and then read-write. This would
+ * normally fail due to the file locking scheme but should
+ * pass when the environment variable is set to disable file
+ * locking.
+ */
+
+ /* Create 1 pipe */
+ if(HDpipe(out_pdf) < 0)
+ TEST_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ TEST_ERROR
+
+ if(childpid == 0) {
+
+ /* Child process */
+
+ hid_t child_fid; /* File ID */
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ } /* end while */
+
+ /* Open the test file */
+ if((child_fid = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ TEST_ERROR
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ } /* end if */
+
+ /* close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ TEST_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ TEST_ERROR
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ TEST_ERROR;
+
+ /* Close the pipe */
+ if(HDclose(out_pdf[1]) < 0)
+ TEST_ERROR;
+
+ /* Wait for child process to complete */
+ if(HDwaitpid(childpid, &child_status, child_wait_option) < 0)
+ TEST_ERROR
+
+ /* Check if child terminated normally */
+ if(WIFEXITED(child_status)) {
+ /* Check exit status of the child */
+ if(WEXITSTATUS(child_status) != 0)
+ TEST_ERROR
+ } /* end if */
+ else
+ TEST_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ TEST_ERROR
+
+ /* Close the copied property list */
+ if(H5Pclose(fapl) < 0)
+ TEST_ERROR
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+
+
+#endif /* !(defined(H5_HAVE_FORK && defined(H5_HAVE_WAITPID)) */
+
+} /* end test_file_lock_env_var() */
+
+
+static int
+test_swmr_vfd_flag(void)
+{
+ hid_t fid = -1; /* file ID */
+ hid_t sec2_fapl = -1; /* fapl ID of a VFD that supports SWMR writes (sec2) */
+ hid_t bad_fapl = -1; /* fapl ID of a VFD that does not support SWMR writes (stdio) */
+ char filename[NAME_BUF_SIZE]; /* file name */
+
+ TESTING("SWMR-enabled VFD flag functionality");
+
+ /* Attempt to open a file using a SWMR-compatible VFD. */
+
+ if((sec2_fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_fapl_sec2(sec2_fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_libver_bounds(sec2_fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ h5_fixname(FILENAME[0], sec2_fapl, filename, sizeof(filename));
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC | H5F_ACC_SWMR_WRITE, H5P_DEFAULT, sec2_fapl)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Attempt to open a file using a non-SWMR-compatible VFD. */
+
+ if((bad_fapl = H5Pcreate(H5P_FILE_ACCESS)) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_fapl_stdio(bad_fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pset_libver_bounds(bad_fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ fid = -1;
+ h5_fixname(FILENAME[0], bad_fapl, filename, sizeof(filename));
+ H5E_BEGIN_TRY {
+ fid = H5Fcreate(filename, H5F_ACC_TRUNC | H5F_ACC_SWMR_WRITE, H5P_DEFAULT, bad_fapl);
+ } H5E_END_TRY;
+ if(fid >= 0)
+ TEST_ERROR;
+
+ if(H5Pclose(sec2_fapl) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Pclose(bad_fapl) < 0)
+ FAIL_STACK_ERROR;
+
+ PASSED();
+
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Pclose(sec2_fapl);
+ H5Pclose(bad_fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_swmr_vfd_flag() */
+
+#ifdef OUT
+/*
+ * This exposes a bug for H5Orefresh while handling opened objects for H5Fstart_swmr_write().
+ * The boolean to skip file truncation test when reading in superblock will fix the problem.
+ * Will work to move that to test/flushrefresh.c later.
+ */
+static int
+test_bug_refresh(hid_t in_fapl)
+{
+ hid_t fid = -1; /* File ID */
+ hid_t fapl;
+ H5F_t *f;
+ hid_t gid1, gid2, gid3, gid4, gid5, gid6, gid7, gid8, gid9;
+ char filename[NAME_BUF_SIZE]; /* File name */
+
+ /* Create a copy of the input parameter in_fapl */
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ TESTING("H5Orefresh failure conditions");
+
+ /* Create a file with the latest format */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Get a pointer to the internal file object */
+ if(NULL == (f = (H5F_t *)H5I_object(fid)))
+ FAIL_STACK_ERROR
+
+ /* Create groups: compact to dense storage */
+ if((gid1 = H5Gcreate2(fid, "group1", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((gid2 = H5Gcreate2(fid, "group2", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((gid3 = H5Gcreate2(fid, "group3", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((gid4 = H5Gcreate2(fid, "group4", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((gid5 = H5Gcreate2(fid, "group5", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((gid6 = H5Gcreate2(fid, "group6", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((gid7 = H5Gcreate2(fid, "group7", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((gid8 = H5Gcreate2(fid, "group8", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+ if((gid9 = H5Gcreate2(fid, "group9", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ if (H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0)
+ TEST_ERROR
+
+ if(H5Grefresh(gid1) < 0) TEST_ERROR
+ if(H5Grefresh(gid2) < 0) TEST_ERROR
+ if(H5Grefresh(gid3) < 0) TEST_ERROR
+ if(H5Grefresh(gid4) < 0) TEST_ERROR
+ if(H5Grefresh(gid5) < 0) TEST_ERROR
+ if(H5Grefresh(gid6) < 0) TEST_ERROR
+ if(H5Grefresh(gid7) < 0) TEST_ERROR
+ if(H5Grefresh(gid8) < 0) TEST_ERROR
+ if(H5Grefresh(gid9) < 0) TEST_ERROR
+
+ H5Gclose(gid1);
+ H5Gclose(gid2);
+ H5Gclose(gid3);
+ H5Gclose(gid4);
+ H5Gclose(gid5);
+ H5Gclose(gid6);
+ H5Gclose(gid7);
+ H5Gclose(gid8);
+ H5Gclose(gid9);
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Gclose(gid1);
+ H5Gclose(gid2);
+ H5Gclose(gid3);
+ H5Gclose(gid4);
+ H5Gclose(gid5);
+ H5Gclose(gid6);
+ H5Gclose(gid7);
+ H5Gclose(gid8);
+ H5Gclose(gid9);
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+} /* test_bug_refresh() */
+#endif /* OUT */
+
+/*
+ * test_refresh_concur():
+ *
+ * The "new_format" parameter indicates whether to create the file with latest format or not.
+ * To have SWMR support, can use either one of the following in creating a file:
+ * (a) Create the file with write + latest format:
+ * --result in v3 superblock with latest chunk indexing types
+ * (b) Create the file with SWMR write + non-latest-format:
+ * --result in v3 superblock with latest chunk indexing types
+ *
+ * Verify H5Drefresh() works correctly with concurrent access:
+ * Parent process:
+ * (1) Open the test file, write to the dataset
+ * (2) Notify child process #A
+ * (3) Wait for notification from child process #B
+ * (4) Extend the dataset, write to the dataset, flush the file
+ * (5) Notify child process #C
+ * Child process:
+ * (1) Wait for notification from parent process #A
+ * (2) Open the file 2 times
+ * (3) Open the dataset 2 times with the 2 files
+ * (4) Verify the dataset's dimension and data read are correct
+ * (5) Notify parent process #B
+ * (6) Wait for notification from parent process #C
+ * (7) Refresh the dataset
+ * (8) Verify the dataset's dimension and data are correct
+ */
+#if !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID))
+
+static int
+test_refresh_concur(hid_t H5_ATTR_UNUSED in_fapl, hbool_t H5_ATTR_UNUSED new_format)
+{
+ SKIPPED();
+ HDputs(" Test skipped due to fork or waitpid not defined.");
+ return 0;
+} /* test_refresh_concur() */
+
+#else /* defined(H5_HAVE_FORK && defined(H5_HAVE_WAITPID) */
+
+static int
+test_refresh_concur(hid_t in_fapl, hbool_t new_format)
+{
+ hid_t fid; /* File ID */
+ hid_t fapl; /* File access property list */
+ pid_t childpid=0; /* Child process ID */
+ pid_t tmppid; /* Child process ID returned by waitpid */
+ 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 */
+ char filename[NAME_BUF_SIZE]; /* File name */
+
+ hid_t did = -1;
+ hid_t sid = -1;
+ hid_t dcpl = -1;
+ hsize_t chunk_dims[1] = {1};
+ hsize_t maxdims[1] = {H5S_UNLIMITED};
+ hsize_t dims[1] = { 1 };
+ hsize_t new_dims[1] = {2};
+
+ int out_pdf[2];
+ int in_pdf[2];
+ int notify = 0;
+ int wbuf[2];
+
+ /* Output message about test being performed */
+ if(new_format) {
+ TESTING("H5Drefresh()--concurrent access for latest format");
+ } else {
+ TESTING("H5Drefresh()--concurrent access for non-latest-format");
+ } /* end if */
+
+
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ if(new_format) {
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create the test file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+ } else {
+ /* Create the test file without latest format but with SWMR write */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+ } /* end if */
+
+ /* Create a chunked dataset with 1 extendible dimension */
+ if((sid = H5Screate_simple(1, dims, maxdims)) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 1, chunk_dims) < 0)
+ FAIL_STACK_ERROR;
+ if((did = H5Dcreate2(fid, "dataset", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Closing */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create 2 pipes */
+ if(HDpipe(out_pdf) < 0)
+ FAIL_STACK_ERROR
+ if(HDpipe(in_pdf) < 0)
+ FAIL_STACK_ERROR
+
+ /* Fork child process */
+ if((childpid = HDfork()) < 0)
+ FAIL_STACK_ERROR
+
+ if(childpid == 0) { /* Child process */
+ hid_t child_fid1 = -1; /* File ID */
+ hid_t child_fid2 = -1; /* File ID */
+ hid_t child_did1 = -1, child_did2 = -1;
+ hid_t child_sid = -1;
+ hsize_t tdims[1];
+ int rbuf[2] = {0, 0};
+ int child_notify = 0;
+
+ /* Close unused write end for out_pdf */
+ if(HDclose(out_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* close unused read end for in_pdf */
+ if(HDclose(in_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 1) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Open the file 2 times */
+ if((child_fid1 = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ if((child_fid2 = H5Fopen(filename, H5F_ACC_RDONLY|H5F_ACC_SWMR_READ, fapl)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Open the dataset 2 times */
+ if((child_did1 = H5Dopen2(child_fid1, "dataset", H5P_DEFAULT)) < 0)
+ HDexit(EXIT_FAILURE);
+ if((child_did2 = H5Dopen2(child_fid2, "dataset", H5P_DEFAULT)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Get the dataset's dataspace via did1 */
+ if((child_sid = H5Dget_space(child_did1)) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Sget_simple_extent_dims(child_sid, tdims, NULL) < 0)
+ HDexit(EXIT_FAILURE);
+ if(tdims[0] != 1)
+ HDexit(EXIT_FAILURE);
+
+ /* Read from the dataset via did2 */
+ if(H5Dread(child_did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rbuf) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Verify the data is correct */
+ if(rbuf[0] != 99)
+ HDexit(EXIT_FAILURE);
+
+ /* Notify parent process */
+ child_notify = 2;
+ if(HDwrite(in_pdf[1], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Wait for notification from parent process */
+ while(child_notify != 3) {
+ if(HDread(out_pdf[0], &child_notify, sizeof(int)) < 0)
+ HDexit(EXIT_FAILURE);
+ }
+
+ /* Refresh dataset via did1 */
+ if(H5Drefresh(child_did1) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Get the dataset's dataspace and verify */
+ if((child_sid = H5Dget_space(child_did1)) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Sget_simple_extent_dims(child_sid, tdims, NULL) < 0)
+ HDexit(EXIT_FAILURE);
+
+ if(tdims[0] != 2)
+ HDexit(EXIT_FAILURE);
+
+ /* Read from the dataset */
+ if(H5Dread(child_did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rbuf) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Verify the data is correct */
+ if(rbuf[0] != 100 || rbuf[1] != 100)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the 2 datasets */
+ if(H5Dclose(child_did1) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Dclose(child_did2) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the 2 files */
+ if(H5Fclose(child_fid1) < 0)
+ HDexit(EXIT_FAILURE);
+ if(H5Fclose(child_fid2) < 0)
+ HDexit(EXIT_FAILURE);
+
+ /* Close the pipes */
+ if(HDclose(out_pdf[0]) < 0)
+ HDexit(EXIT_FAILURE);
+ if(HDclose(in_pdf[1]) < 0)
+ HDexit(EXIT_FAILURE);
+
+ HDexit(EXIT_SUCCESS);
+ }
+
+ /* Close unused read end for out_pdf */
+ if(HDclose(out_pdf[0]) < 0)
+ FAIL_STACK_ERROR
+ /* Close unused write end for in_pdf */
+ if(HDclose(in_pdf[1]) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the test file */
+ if((fid = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset */
+ if((did = H5Dopen2(fid, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Write to the dataset */
+ wbuf[0] = wbuf[1] = 99;
+ if(H5Dwrite(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wbuf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Flush to disk */
+ if(H5Fflush(fid, H5F_SCOPE_LOCAL) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Notify child process */
+ notify = 1;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+
+ /* Wait for notification from child process */
+ while(notify != 2) {
+ if(HDread(in_pdf[0], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+ }
+
+ /* Cork the metadata cache, to prevent the object header from being
+ * flushed before the data has been written */
+ if(H5Odisable_mdc_flushes(did) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Extend the dataset */
+ if(H5Dset_extent(did, new_dims) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Write to the dataset */
+ wbuf[0] = wbuf[1] = 100;
+ if(H5Dwrite(did, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wbuf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Uncork the metadata cache */
+ if(H5Oenable_mdc_flushes(did) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Flush to disk */
+ if(H5Fflush(fid, H5F_SCOPE_LOCAL) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Notify child process */
+ notify = 3;
+ if(HDwrite(out_pdf[1], &notify, sizeof(int)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close the pipes */
+ if(HDclose(out_pdf[1]) < 0)
+ FAIL_STACK_ERROR;
+ if(HDclose(in_pdf[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
+
+ /* Close the dataset */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Dclose(did);
+ H5Sclose(sid);
+ H5Pclose(dcpl);
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* test_refresh_concur() */
+#endif /* !(defined(H5_HAVE_FORK) && defined(H5_HAVE_WAITPID)) */
+
+/*
+ * test_multiple_same():
+ *
+ * The "new_format" parameter indicates whether to create the file with latest format or not.
+ * To have SWMR support, can use either one of the following in creating a file:
+ * (a) Create the file with write + latest format:
+ * --result in v3 superblock with latest chunk indexing types
+ * (b) Create the file with SWMR write + non-latest-format:
+ * --result in v3 superblock with latest chunk indexing types
+ *
+ * Verify that H5Drefresh() and H5Fstart_swmr_write() work properly with multiple
+ * opens of files and datasets.
+ */
+static int
+test_multiple_same(hid_t in_fapl, hbool_t new_format)
+{
+ hid_t fid = -1, fid1 = -1, fid2 = -1, fid3 = -1; /* File IDs */
+ hid_t fapl; /* File access property list */
+ char filename[NAME_BUF_SIZE]; /* File name */
+ hid_t did = -1, did1 = -1, did2 = -1, did3 = -1;
+ hid_t sid = -1;
+ hid_t dcpl = -1;
+ hsize_t chunk_dims[2] = {1, 2};
+ hsize_t maxdims[2] = {H5S_UNLIMITED, H5S_UNLIMITED};
+ hsize_t dims[2] = {1, 1};
+ int rbuf = 0;
+ int wbuf = 0;
+
+ /* Output message about test being performed */
+ if(new_format) {
+ TESTING("multiple--single process access for latest format");
+ } else {
+ TESTING("multiple--single process access for non-latest-format");
+ } /* end if */
+
+
+ if((fapl = H5Pcopy(in_fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Set the filename to use for this test (dependent on fapl) */
+ h5_fixname(FILENAME[0], fapl, filename, sizeof(filename));
+
+ if(new_format) {
+ /* Set to use the latest library format */
+ if(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
+ FAIL_STACK_ERROR
+
+ /* Create the test file */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+ } else {
+ /* Create the test file without latest format but with SWMR write */
+ if((fid = H5Fcreate(filename, H5F_ACC_TRUNC|H5F_ACC_SWMR_WRITE, H5P_DEFAULT, fapl)) < 0)
+ FAIL_STACK_ERROR
+ } /* end if */
+
+ /* Create a chunked dataset with 1 extendible dimension */
+ if((sid = H5Screate_simple(2, dims, maxdims)) < 0)
+ FAIL_STACK_ERROR;
+ if((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pset_chunk(dcpl, 2, chunk_dims) < 0)
+ FAIL_STACK_ERROR;
+ if((did = H5Dcreate2(fid, "dataset", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Closing */
+ if(H5Dclose(did) < 0)
+ FAIL_STACK_ERROR
+ if(H5Sclose(sid) < 0)
+ FAIL_STACK_ERROR
+ if(H5Pclose(dcpl) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the file */
+ if(H5Fclose(fid) < 0)
+ FAIL_STACK_ERROR
+
+ /* Case 1 */
+
+ /* Open the file 3 times: SWMR-write, read-write, read-only */
+ if((fid1 = H5Fopen(filename, H5F_ACC_RDWR|H5F_ACC_SWMR_WRITE, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid3 = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset 3 times with fid1, fid2, fid3 */
+ if((did1 = H5Dopen2(fid1, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+ if((did2 = H5Dopen2(fid2, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+ if((did3 = H5Dopen2(fid3, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+
+ /* Write to the dataset via did1 */
+ wbuf = 88;
+ if(H5Dwrite(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wbuf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Refresh via did2 */
+ if(H5Drefresh(did2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Read from the dataset via did2 */
+ rbuf = 0;
+ if(H5Dread(did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rbuf) < 0)
+ FAIL_STACK_ERROR;
+ /* Verify the data is correct */
+ if(rbuf != 88)
+ FAIL_STACK_ERROR;
+
+ /* Write to the dataset via did3 */
+ wbuf = 99;
+ if(H5Dwrite(did3, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wbuf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Refresh via did1 */
+ if(H5Drefresh(did1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Read from the dataset via did1 */
+ rbuf = 0;
+ if(H5Dread(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rbuf) < 0)
+ FAIL_STACK_ERROR;
+ /* Verify the data is correct */
+ if(rbuf != 99)
+ FAIL_STACK_ERROR;
+
+ /* Close datasets */
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did3) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close files */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid3) < 0)
+ FAIL_STACK_ERROR
+
+ /* Case 2 */
+
+ /* Open the file 3 times: read-write, read-only, read-write */
+ if((fid1 = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid3 = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset 3 times with fid1, fid2, fid3 */
+ if((did1 = H5Dopen2(fid1, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+ if((did2 = H5Dopen2(fid2, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+ if((did3 = H5Dopen2(fid3, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Write to the dataset via did1 */
+ wbuf = 88;
+ if(H5Dwrite(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wbuf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Refresh via did2 */
+ if(H5Drefresh(did2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Read from dataset via did2 */
+ rbuf = 0;
+ if(H5Dread(did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rbuf) < 0)
+ FAIL_STACK_ERROR;
+ if(rbuf != wbuf)
+ FAIL_STACK_ERROR;
+
+ /* Write to dataset via did3 */
+ wbuf = 99;
+ if(H5Dwrite(did3, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wbuf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Enable SWMR write */
+ if(H5Fstart_swmr_write(fid1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Read from dataset via did1 and verify data is correct */
+ rbuf = 0;
+ if(H5Dread(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rbuf) < 0)
+ FAIL_STACK_ERROR;
+ if(rbuf != wbuf)
+ FAIL_STACK_ERROR;
+
+ /* Write to dataset via did2 */
+ wbuf = 100;
+ if(H5Dwrite(did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wbuf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Refresh dataset via did3 */
+ if(H5Drefresh(did3) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Read from dataset via did3 and verify data is correct */
+ rbuf = 0;
+ if(H5Dread(did3, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rbuf) < 0)
+ FAIL_STACK_ERROR;
+ if(rbuf != wbuf)
+ FAIL_STACK_ERROR;
+
+ /* Close datasets */
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Dclose(did3) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close files */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR;
+ if(H5Fclose(fid3) < 0)
+ FAIL_STACK_ERROR
+
+ /* Case 3 */
+
+ /* Open the file 3 times: read-write, read-only, read-only */
+ if((fid1 = H5Fopen(filename, H5F_ACC_RDWR, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid2 = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+ if((fid3 = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Open the dataset 3 times with fid1, fid2, fid3 */
+ if((did1 = H5Dopen2(fid1, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+ if((did2 = H5Dopen2(fid2, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+ if((did3 = H5Dopen2(fid3, "dataset", H5P_DEFAULT)) < 0)
+ FAIL_STACK_ERROR
+
+ /* Write to the dataset via did1 */
+ wbuf = 88;
+ if(H5Dwrite(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wbuf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Refresh dataset via did2 */
+ if(H5Drefresh(did2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Read from dataset via did2 and verify data is correct */
+ rbuf = 0;
+ if(H5Dread(did2, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rbuf) < 0)
+ FAIL_STACK_ERROR;
+ if(rbuf != wbuf)
+ FAIL_STACK_ERROR;
+
+ /* Close dataset via did2 */
+ if(H5Dclose(did2) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close file via fid2 */
+ if(H5Fclose(fid2) < 0)
+ FAIL_STACK_ERROR
+
+ /* Write to dataset via did3 */
+ wbuf = 99;
+ if(H5Dwrite(did3, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wbuf) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close dataset via did3 */
+ if(H5Dclose(did3) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close file via fid3 */
+ if(H5Fclose(fid3) < 0)
+ FAIL_STACK_ERROR
+
+ /* Enable SWMR writing */
+ if(H5Fstart_swmr_write(fid1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Read from dataset via did1 and verify data is correct */
+ rbuf = 0;
+ if(H5Dread(did1, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &rbuf) < 0)
+ FAIL_STACK_ERROR;
+ if(rbuf != wbuf)
+ FAIL_STACK_ERROR;
+
+ /* Close dataset via did1 */
+ if(H5Dclose(did1) < 0)
+ FAIL_STACK_ERROR;
+
+ /* Close file via fid1 */
+ if(H5Fclose(fid1) < 0)
+ FAIL_STACK_ERROR
+
+ /* Close the property list */
+ if(H5Pclose(fapl) < 0)
+ FAIL_STACK_ERROR
+
+ PASSED();
+ return 0;
+
+error:
+ H5E_BEGIN_TRY {
+ H5Dclose(did);
+ H5Dclose(did1);
+ H5Dclose(did2);
+ H5Dclose(did3);
+ H5Sclose(sid);
+ H5Pclose(dcpl);
+ H5Pclose(fapl);
+ H5Fclose(fid);
+ H5Fclose(fid1);
+ H5Fclose(fid2);
+ H5Fclose(fid3);
+ } H5E_END_TRY;
+
+ return -1;
+
+} /* test_multiple_same() */
+
+/****************************************************************
+**
+** Tests for new public routines introduced from the SWMR project.
+**
+****************************************************************/
+int
+main(void)
+{
+ int nerrors = 0; /* The # of errors */
+ hid_t fapl = -1; /* File access property list ID */
+ char *driver = NULL; /* VFD string (from env variable) */
+ char *lock_env_var = NULL; /* file locking env var pointer */
+ hbool_t use_file_locking; /* read from env var */
+
+ /* Skip this test if SWMR I/O is not supported for the VFD specified
+ * by the environment variable.
+ */
+ driver = HDgetenv("HDF5_DRIVER");
+ if(!H5FD_supports_swmr_test(driver)) {
+ printf("This VFD does not support SWMR I/O\n");
+ return EXIT_SUCCESS;
+ } /* end if */
+
+ /* 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;
+
+ /* Set up */
+ h5_reset();
+
+ /* Get file access property list */
+ fapl = h5_fileaccess();
+
+#ifdef OUT
+ nerrors += test_bug_refresh(fapl);
+#endif
+ nerrors += test_refresh_concur(fapl, TRUE);
+ nerrors += test_refresh_concur(fapl, FALSE);
+ nerrors += test_multiple_same(fapl, TRUE);
+ nerrors += test_multiple_same(fapl, FALSE);
+
+ /* Tests on H5Pget/set_metadata_read_attempts() and H5Fget_metadata_read_retry_info() */
+ nerrors += test_metadata_read_attempts(fapl);
+ nerrors += test_metadata_read_retry_info(fapl);
+
+ /* Tests on H5Fstart_swmr_write() */
+ /*
+ * Modify the following routines to test for files:
+ * H5Fcreate(write, latest format) or H5Fcreate(SWMR write, non-latest-format)
+ * --both result in v3 superblock and latest version suppport
+ */
+ nerrors += test_start_swmr_write(fapl, TRUE);
+ nerrors += test_start_swmr_write(fapl, FALSE);
+ nerrors += test_err_start_swmr_write(fapl, TRUE);
+ nerrors += test_err_start_swmr_write(fapl, FALSE);
+ nerrors += test_start_swmr_write_concur(fapl, TRUE);
+ nerrors += test_start_swmr_write_concur(fapl, FALSE);
+ nerrors += test_start_swmr_write_stress_ohdr(fapl);
+
+ /* Tests for H5Pget/set_object_flush_cb() */
+ nerrors += test_object_flush_cb(fapl);
+
+ /* Tests on H5Pget/set_append_flush() */
+ nerrors += test_append_flush_generic();
+ nerrors += test_append_flush_dataset_chunked(fapl);
+ nerrors += test_append_flush_dataset_fixed(fapl);
+ nerrors += test_append_flush_dataset_multiple(fapl);
+
+ if(use_file_locking) {
+ /*
+ * Tests for:
+ * file open flags--single process access
+ * file open flags--concurrent access
+ */
+ nerrors += test_file_lock_same(fapl);
+ nerrors += test_file_lock_concur(fapl);
+ /*
+ * Tests for:
+ * file open flags+SWMR flags--single process access
+ * file open flags+SWMR flags--concurrent access
+ *
+ * Modify the following 2 routines to test for files:
+ * H5Fcreate(write, latest format) or H5Fcreate(SWMR write, non-latest-format)
+ * --both result in v3 superblock and latest version suppport
+ */
+ nerrors += test_file_lock_swmr_same(fapl);
+ nerrors += test_file_lock_swmr_concur(fapl);
+ } /* end if */
+
+ /* Tests SWMR VFD compatibility flag.
+ * Only needs to run when the VFD is the default (sec2).
+ */
+ if(NULL == driver || !HDstrcmp(driver, "") || !HDstrcmp(driver, "sec2"))
+ nerrors += test_swmr_vfd_flag();
+
+ /* This test changes the HDF5_USE_FILE_LOCKING environment variable
+ * so it should be run last.
+ */
+ nerrors += test_file_lock_env_var(fapl);
+
+ if(nerrors)
+ goto error;
+
+ printf("All tests passed.\n");
+
+ h5_cleanup(FILENAME, fapl);
+
+ return EXIT_SUCCESS;
+
+error:
+ nerrors = MAX(1, nerrors);
+ printf("***** %d SWMR TEST%s FAILED! *****\n",
+ nerrors, 1 == nerrors ? "" : "S");
+ return EXIT_FAILURE;
+
+} /* end main() */
+
diff --git a/test/tfile.c b/test/tfile.c
index d6cb427..82905d5 100644
--- a/test/tfile.c
+++ b/test/tfile.c
@@ -146,6 +146,11 @@ test_obj_count_and_id(hid_t, hid_t, hid_t, hid_t, hid_t, hid_t);
static void
check_file_id(hid_t, hid_t);
+/* Helper routine used by test_rw_noupdate() */
+static int cal_chksum(const char *file, uint32_t *chksum);
+
+static void test_rw_noupdate(void);
+
/****************************************************************
**
** test_file_create(): Low-level file creation I/O test routine.
@@ -2731,93 +2736,101 @@ test_cached_stab_info(void)
CHECK(ret, FAIL, "H5Fclose");
} /* end test_cached_stab_info() */
+/*
+ * To calculate the checksum for a file.
+ * This is a helper routine for test_rw_noupdate().
+ */
+static int
+cal_chksum(const char *file, uint32_t *chksum)
+{
+ int curr_num_errs = GetTestNumErrs(); /* Retrieve the current # of errors */
+ int fdes = -1; /* File descriptor */
+ void *file_data = NULL; /* Copy of file data */
+ ssize_t bytes_read; /* # of bytes read */
+ h5_stat_t sb; /* Stat buffer for file */
+ herr_t ret; /* Generic return value */
+
+ /* Open the file */
+ fdes = HDopen(file, O_RDONLY, 0);
+ CHECK(fdes, FAIL, "HDopen");
+
+ /* Retrieve the file's size */
+ ret = HDfstat(fdes, &sb);
+ CHECK(fdes, FAIL, "HDfstat");
+
+ /* Allocate space for the file data */
+ file_data = HDmalloc((size_t)sb.st_size);
+ CHECK(file_data, NULL, "HDmalloc");
+
+ if(file_data) {
+ /* Read file's data into memory */
+ bytes_read = HDread(fdes, file_data, (size_t)sb.st_size);
+ CHECK(bytes_read == sb.st_size, FALSE, "HDmalloc");
+
+ /* Calculate checksum */
+ *chksum = H5_checksum_lookup3(file_data, sizeof(file_data), 0);
+
+ /* Free memory */
+ HDfree(file_data);
+ }
+
+ /* Close the file */
+ ret = HDclose(fdes);
+ CHECK(ret, FAIL, "HDclose");
+
+ return((GetTestNumErrs() == curr_num_errs) ? 0 : -1);
+} /* cal_chksum() */
+
/****************************************************************
**
** test_rw_noupdate(): low-level file test routine.
** This test checks to ensure that opening and closing a file
** with read/write permissions does not write anything to the
** file if the file does not change.
+** Due to the implementation of file locking (status_flags in
+** the superblock is used), this test is changed to use checksum
+** instead of timestamp to verify the file is not changed.
**
-** Programmer: Mike McGreevy
-** mamcgree@hdfgroup.org
-** June 29, 2009
+** Programmer: Vailin Choi; July 2013
**
*****************************************************************/
static void
test_rw_noupdate(void)
{
- int fd; /* File Descriptor */
- h5_stat_t sb1, sb2; /* Info from 'stat' call */
- double diff; /* Difference in modification times */
herr_t ret; /* Generic return value */
+ hid_t fid; /* File ID */
+ uint32_t chksum1, chksum2; /* Checksum value */
/* Output message about test being performed */
MESSAGE(5, ("Testing to verify that nothing is written if nothing is changed.\n"));
- /* First make sure the stat function behaves as we expect - the modification time
- * is the time that the file was modified last time. */
- fd = HDopen(SFILE1, O_RDWR | O_CREAT | O_TRUNC, 0666);
- CHECK(fd, FAIL, "HDopen");
- ret = HDclose(fd);
- CHECK(ret, FAIL, "HDclose");
-
- /* Determine File's Initial Timestamp */
- ret = HDstat(SFILE1, &sb1);
- VERIFY(ret, 0, "HDstat");
-
- /* Wait for 2 seconds */
- /* (This ensures a system time difference between the two file accesses) */
- HDsleep(2);
-
- fd = HDopen(SFILE1, O_RDWR, 0666);
- CHECK(fd, FAIL, "HDopen");
- ret = HDclose(fd);
- CHECK(ret, FAIL, "HDclose");
-
- /* Determine File's New Timestamp */
- ret = HDstat(SFILE1, &sb2);
- VERIFY(ret, 0, "HDstat");
-
- /* Get difference between timestamps */
- diff = HDdifftime(sb2.st_mtime, sb1.st_mtime);
+ /* Create and Close a HDF5 File */
+ fid = H5Fcreate(FILE1, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
+ CHECK(fid, FAIL, "H5Fcreate");
- /* Check That Timestamps Are Equal */
- if(diff > (double)0.0F) {
- /* Output message about test being performed */
- MESSAGE(1, ("Testing to verify that nothing is written if nothing is changed: This test is skipped on this system because the modification time from stat is the same as the last access time.\n"));
- } /* end if */
- else {
- hid_t file_id; /* HDF5 File ID */
+ /* Close the file */
+ ret = H5Fclose(fid);
+ CHECK(ret, FAIL, "H5Fclose");
- /* Create and Close a HDF5 File */
- file_id = H5Fcreate(FILE1, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
- CHECK(file_id, FAIL, "H5Fcreate");
- ret = H5Fclose(file_id);
- CHECK(ret, FAIL, "H5Fclose");
+ /* Calculate checksum for the file */
+ ret = cal_chksum(FILE1, &chksum1);
+ CHECK(ret, FAIL, "HDopen");
- /* Determine File's Initial Timestamp */
- ret = HDstat(FILE1, &sb1);
- VERIFY(ret, 0, "HDfstat");
+ /* Open and close File With Read/Write Permission */
+ fid = H5Fopen(FILE1, H5F_ACC_RDWR, H5P_DEFAULT);
+ CHECK(fid, FAIL, "H5Fopen");
- /* Wait for 2 seconds */
- /* (This ensures a system time difference between the two file accesses) */
- HDsleep(2);
+ /* Close the file */
+ ret = H5Fclose(fid);
+ CHECK(ret, FAIL, "H5Fclose");
- /* Open and Close File With Read/Write Permission */
- file_id = H5Fopen(FILE1, H5F_ACC_RDWR, H5P_DEFAULT);
- CHECK(file_id, FAIL, "H5Fopen");
- ret = H5Fclose(file_id);
- CHECK(ret, FAIL, "H5Fclose");
+ /* Calculate checksum for the file */
+ ret = cal_chksum(FILE1, &chksum2);
+ CHECK(ret, FAIL, "HDopen");
- /* Determine File's New Timestamp */
- ret = HDstat(FILE1, &sb2);
- VERIFY(ret, 0, "HDstat");
+ /* The two checksums are the same, i.e. the file is not changed */
+ VERIFY(chksum1, chksum2, "Checksum");
- /* Ensure That Timestamps Are Equal */
- diff = HDdifftime(sb2.st_mtime, sb1.st_mtime);
- ret = (diff > (double)0.0F);
- VERIFY(ret, 0, "Timestamp");
- } /* end else */
} /* end test_rw_noupdate() */
/****************************************************************
@@ -3809,7 +3822,7 @@ test_libver_bounds(void)
/* Run the tests */
test_libver_bounds_real(H5F_LIBVER_EARLIEST, 1, H5F_LIBVER_LATEST, 2);
- test_libver_bounds_real(H5F_LIBVER_LATEST, 2, H5F_LIBVER_EARLIEST, 1);
+ test_libver_bounds_real(H5F_LIBVER_LATEST, 2, H5F_LIBVER_EARLIEST, 2);
} /* end test_libver_bounds() */
/****************************************************************
diff --git a/testpar/t_cache.c b/testpar/t_cache.c
index ef89c8d..94a6e7c 100644
--- a/testpar/t_cache.c
+++ b/testpar/t_cache.c
@@ -480,7 +480,7 @@ static hbool_t setup_cache_for_test(hid_t * fid_ptr,
H5C_t ** cache_ptr_ptr,
int metadata_write_strategy);
static void setup_rand(void);
-static hbool_t take_down_cache(hid_t fid);
+static hbool_t take_down_cache(hid_t fid, H5C_t * cache_ptr);
static hbool_t verify_entry_reads(haddr_t addr, int expected_entry_reads);
static hbool_t verify_entry_writes(haddr_t addr, int expected_entry_writes);
static hbool_t verify_total_reads(int expected_total_reads);
@@ -4491,35 +4491,77 @@ setup_rand(void)
*
*****************************************************************************/
static hbool_t
-take_down_cache(hid_t fid)
+take_down_cache(hid_t fid, H5C_t * cache_ptr)
{
- hbool_t success = FALSE; /* will set to TRUE if appropriate. */
+ hbool_t success = TRUE; /* will set to FALSE if appropriate. */
- /* close the file and delete it */
- if ( H5Fclose(fid) < 0 ) {
+ /* flush the file -- this should write out any remaining test
+ * entries in the cache.
+ */
+ if ( ( success ) && ( H5Fflush(fid, H5F_SCOPE_GLOBAL) < 0 ) ) {
+ success = FALSE;
nerrors++;
if ( verbose ) {
- HDfprintf(stdout, "%d:%s: H5Fclose() failed.\n",
+ HDfprintf(stdout, "%d:%s: H5Fflush() failed.\n",
world_mpi_rank, FUNC);
}
+ }
- } else if ( world_mpi_rank == world_server_mpi_rank ) {
+ /* Now reset the sync point done callback. Must do this as with
+ * the SWMR mods, the cache will do additional I/O on file close
+ * un-related to the test entries, and thereby corrupt our counts
+ * of entry writes.
+ */
+ if ( success ) {
- if ( HDremove(filenames[0]) < 0 ) {
+ if ( H5AC__set_sync_point_done_callback(cache_ptr, NULL) != SUCCEED ) {
+ success = FALSE;
nerrors++;
if ( verbose ) {
- HDfprintf(stdout, "%d:%s: HDremove() failed.\n",
+ HDfprintf(stdout,
+ "%d:%s: H5AC__set_sync_point_done_callback failed.\n",
world_mpi_rank, FUNC);
}
- } else {
+ }
+
+
+ }
- success = TRUE;
+ /* close the file */
+ if ( ( success ) && ( H5Fclose(fid) < 0 ) ) {
+
+ success = FALSE;
+ nerrors++;
+ if ( verbose ) {
+ HDfprintf(stdout, "%d:%s: H5Fclose() failed.\n",
+ world_mpi_rank, FUNC);
}
- } else {
- success = TRUE;
+ }
+
+ if ( success ) {
+
+ if ( world_mpi_rank == world_server_mpi_rank ) {
+
+ if ( HDremove(filenames[0]) < 0 ) {
+
+ success = FALSE;
+ nerrors++;
+ if ( verbose ) {
+ HDfprintf(stdout, "%d:%s: HDremove() failed.\n",
+ world_mpi_rank, FUNC);
+ }
+ }
+ } else {
+
+ /* verify that there have been no further writes of test
+ * entries during the close
+ */
+ success = verify_total_writes(0);
+
+ }
}
return(success);
@@ -5576,7 +5618,7 @@ smoke_check_1(int metadata_write_strategy)
if ( fid >= 0 ) {
- if ( ! take_down_cache(fid) ) {
+ if ( ! take_down_cache(fid, cache_ptr) ) {
nerrors++;
if ( verbose ) {
@@ -5798,7 +5840,7 @@ smoke_check_2(int metadata_write_strategy)
if ( fid >= 0 ) {
- if ( ! take_down_cache(fid) ) {
+ if ( ! take_down_cache(fid, cache_ptr) ) {
nerrors++;
if ( verbose ) {
@@ -6121,7 +6163,7 @@ smoke_check_3(int metadata_write_strategy)
if ( fid >= 0 ) {
- if ( ! take_down_cache(fid) ) {
+ if ( ! take_down_cache(fid, cache_ptr) ) {
nerrors++;
if ( verbose ) {
@@ -6438,7 +6480,7 @@ smoke_check_4(int metadata_write_strategy)
if ( fid >= 0 ) {
- if ( ! take_down_cache(fid) ) {
+ if ( ! take_down_cache(fid, cache_ptr) ) {
nerrors++;
if ( verbose ) {
@@ -6648,7 +6690,7 @@ smoke_check_5(int metadata_write_strategy)
if ( fid >= 0 ) {
- if ( ! take_down_cache(fid) ) {
+ if ( ! take_down_cache(fid, cache_ptr) ) {
nerrors++;
if ( verbose ) {
@@ -6997,7 +7039,7 @@ trace_file_check(int metadata_write_strategy)
if ( fid >= 0 ) {
- if ( ! take_down_cache(fid) ) {
+ if ( ! take_down_cache(fid, cache_ptr) ) {
nerrors++;
if ( verbose ) {
@@ -7325,7 +7367,7 @@ smoke_check_6(int metadata_write_strategy)
if ( fid >= 0 ) {
- if ( ! take_down_cache(fid) ) {
+ if ( ! take_down_cache(fid, cache_ptr) ) {
nerrors++;
if ( verbose ) {