summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/H5AC.c194
-rw-r--r--src/H5ACdbg.c201
-rw-r--r--src/H5ACpkg.h9
-rw-r--r--src/H5ACprivate.h69
-rw-r--r--src/H5ACpublic.h61
-rw-r--r--src/H5B2pkg.h9
-rw-r--r--src/H5Bpkg.h3
-rw-r--r--src/H5C.c532
-rw-r--r--src/H5Cdbg.c443
-rw-r--r--src/H5Cepoch.c5
-rw-r--r--src/H5Cimage.c4100
-rw-r--r--src/H5Cpkg.h411
-rw-r--r--src/H5Cprefetched.c352
-rw-r--r--src/H5Cprivate.h476
-rw-r--r--src/H5Cquery.c2
-rw-r--r--src/H5EApkg.h15
-rw-r--r--src/H5F.c94
-rw-r--r--src/H5FApkg.h9
-rw-r--r--src/H5FSpkg.h6
-rw-r--r--src/H5Fint.c36
-rw-r--r--src/H5Fio.c2
-rw-r--r--src/H5Fpkg.h12
-rw-r--r--src/H5Fprivate.h4
-rw-r--r--src/H5Fpublic.h1
-rw-r--r--src/H5Fsuper.c39
-rw-r--r--src/H5Gpkg.h3
-rw-r--r--src/H5HFcache.c684
-rw-r--r--src/H5HFpkg.h9
-rw-r--r--src/H5HGpkg.h3
-rw-r--r--src/H5HLpkg.h6
-rw-r--r--src/H5MF.c4
-rw-r--r--src/H5O.c7
-rw-r--r--src/H5Ocache_image.c337
-rw-r--r--src/H5Opkg.h13
-rw-r--r--src/H5Oprivate.h16
-rw-r--r--src/H5Pfapl.c255
-rw-r--r--src/H5Ppublic.h2
-rw-r--r--src/H5SMpkg.h2
-rw-r--r--src/Makefile.am6
40 files changed, 7934 insertions, 501 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 510db1e..b7cfeb2 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -90,8 +90,10 @@ set (H5C_SOURCES
${HDF5_SRC_DIR}/H5C.c
${HDF5_SRC_DIR}/H5Cdbg.c
${HDF5_SRC_DIR}/H5Cepoch.c
+ ${HDF5_SRC_DIR}/H5Cimage.c
${HDF5_SRC_DIR}/H5Clog.c
${HDF5_SRC_DIR}/H5Cmpio.c
+ ${HDF5_SRC_DIR}/H5Cprefetched.c
${HDF5_SRC_DIR}/H5Cquery.c
${HDF5_SRC_DIR}/H5Ctag.c
${HDF5_SRC_DIR}/H5Ctest.c
@@ -439,6 +441,7 @@ set (H5O_SOURCES
${HDF5_SRC_DIR}/H5Obogus.c
${HDF5_SRC_DIR}/H5Obtreek.c
${HDF5_SRC_DIR}/H5Ocache.c
+ ${HDF5_SRC_DIR}/H5Ocache_image.c
${HDF5_SRC_DIR}/H5Ochunk.c
${HDF5_SRC_DIR}/H5Ocont.c
${HDF5_SRC_DIR}/H5Ocopy.c
diff --git a/src/H5AC.c b/src/H5AC.c
index 117e662..6a38657 100644
--- a/src/H5AC.c
+++ b/src/H5AC.c
@@ -107,38 +107,43 @@ hbool_t H5_coll_api_sanity_check_g = false;
/* Local Variables */
/*******************/
-static const char *H5AC_entry_type_names[H5AC_NTYPES] =
-{
- "B-tree nodes",
- "symbol table nodes",
- "local heap prefixes",
- "local heap data blocks",
- "global heaps",
- "object headers",
- "object header chunks",
- "v2 B-tree headers",
- "v2 B-tree internal nodes",
- "v2 B-tree leaf nodes",
- "fractal heap headers",
- "fractal heap direct blocks",
- "fractal heap indirect blocks",
- "free space headers",
- "free space sections",
- "shared OH message master table",
- "shared OH message index",
- "extensible array headers",
- "extensible array index blocks",
- "extensible array super blocks",
- "extensible array data blocks",
- "extensible array data block pages",
- "fixed array headers",
- "fixed array data block",
- "fixed array data block pages",
- "superblock",
- "driver info",
- "epoch marker", /* internal to cache only */
- "proxy entry",
- "test entry" /* for testing only -- not used for actual files */
+/* Metadata entry class list */
+
+/* Remember to add new type ID to the H5AC_type_t enum in H5ACprivate.h when
+ * adding a new class.
+ */
+
+static const H5AC_class_t *const H5AC_class_s[] = {
+ H5AC_BT, /* ( 0) B-tree nodes */
+ H5AC_SNODE, /* ( 1) symbol table nodes */
+ H5AC_LHEAP_PRFX, /* ( 2) local heap prefix */
+ H5AC_LHEAP_DBLK, /* ( 3) local heap data block */
+ H5AC_GHEAP, /* ( 4) global heap */
+ H5AC_OHDR, /* ( 5) object header */
+ H5AC_OHDR_CHK, /* ( 6) object header chunk */
+ H5AC_BT2_HDR, /* ( 7) v2 B-tree header */
+ H5AC_BT2_INT, /* ( 8) v2 B-tree internal node */
+ H5AC_BT2_LEAF, /* ( 9) v2 B-tree leaf node */
+ H5AC_FHEAP_HDR, /* (10) fractal heap header */
+ H5AC_FHEAP_DBLOCK, /* (11) fractal heap direct block */
+ H5AC_FHEAP_IBLOCK, /* (12) fractal heap indirect block */
+ H5AC_FSPACE_HDR, /* (13) free space header */
+ H5AC_FSPACE_SINFO, /* (14) free space sections */
+ H5AC_SOHM_TABLE, /* (15) shared object header message master table */
+ H5AC_SOHM_LIST, /* (16) shared message index stored as a list */
+ H5AC_EARRAY_HDR, /* (17) extensible array header */
+ H5AC_EARRAY_IBLOCK, /* (18) extensible array index block */
+ H5AC_EARRAY_SBLOCK, /* (19) extensible array super block */
+ H5AC_EARRAY_DBLOCK, /* (20) extensible array data block */
+ H5AC_EARRAY_DBLK_PAGE, /* (21) extensible array data block page */
+ H5AC_FARRAY_HDR, /* (22) fixed array header */
+ H5AC_FARRAY_DBLOCK, /* (23) fixed array data block */
+ H5AC_FARRAY_DBLK_PAGE, /* (24) fixed array data block page */
+ H5AC_SUPERBLOCK, /* (25) file superblock */
+ H5AC_DRVRINFO, /* (26) driver info block (supplements superblock) */
+ H5AC_EPOCH_MARKER, /* (27) epoch marker - always internal to cache */
+ H5AC_PROXY_ENTRY, /* (28) cache entry proxy */
+ H5AC_PREFETCHED_ENTRY /* (29) prefetched entry - always internal to cache */
};
@@ -366,12 +371,13 @@ H5AC_term_package(void)
*-------------------------------------------------------------------------
*/
herr_t
-H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr)
+H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr, H5AC_cache_image_config_t * image_config_ptr)
{
#ifdef H5_HAVE_PARALLEL
char prefix[H5C__PREFIX_LEN] = "";
H5AC_aux_t * aux_ptr = NULL;
#endif /* H5_HAVE_PARALLEL */
+ struct H5C_cache_image_ctl_t int_ci_config = H5C__DEFAULT_CACHE_IMAGE_CTL;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
@@ -380,11 +386,16 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr)
HDassert(f);
HDassert(NULL == f->shared->cache);
HDassert(config_ptr != NULL) ;
- HDcompile_assert(NELMTS(H5AC_entry_type_names) == H5AC_NTYPES);
+ HDassert(image_config_ptr != NULL) ;
+ HDassert(image_config_ptr->version == H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION);
+ HDcompile_assert(NELMTS(H5AC_class_s) == H5AC_NTYPES);
HDcompile_assert(H5C__MAX_NUM_TYPE_IDS == H5AC_NTYPES);
+ /* Validate configurations */
if(H5AC_validate_config(config_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad cache configuration")
+ if(H5AC_validate_cache_image_config(image_config_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad cache image configuration")
#ifdef H5_HAVE_PARALLEL
if(H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI)) {
@@ -426,6 +437,7 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr)
aux_ptr->candidate_slist_ptr = NULL;
aux_ptr->write_done = NULL;
aux_ptr->sync_point_done = NULL;
+ aux_ptr->p0_image_len = 0;
sprintf(prefix, "%d:", mpi_rank);
@@ -448,19 +460,19 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr)
if(aux_ptr->mpi_rank == 0)
f->shared->cache = H5C_create(H5AC__DEFAULT_MAX_CACHE_SIZE,
H5AC__DEFAULT_MIN_CLEAN_SIZE, (H5AC_NTYPES - 1),
- (const char **)H5AC_entry_type_names,
+ H5AC_class_s,
H5AC__check_if_write_permitted, TRUE, H5AC__log_flushed_entry,
(void *)aux_ptr);
else
f->shared->cache = H5C_create(H5AC__DEFAULT_MAX_CACHE_SIZE,
H5AC__DEFAULT_MIN_CLEAN_SIZE, (H5AC_NTYPES - 1),
- (const char **)H5AC_entry_type_names,
+ H5AC_class_s,
H5AC__check_if_write_permitted, TRUE, NULL,
(void *)aux_ptr);
else
f->shared->cache = H5C_create(H5AC__DEFAULT_MAX_CACHE_SIZE,
H5AC__DEFAULT_MIN_CLEAN_SIZE, (H5AC_NTYPES - 1),
- (const char **)H5AC_entry_type_names,
+ H5AC_class_s,
H5AC__check_if_write_permitted, TRUE, NULL, NULL);
} /* end if */
else {
@@ -471,7 +483,7 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr)
*/
f->shared->cache = H5C_create(H5AC__DEFAULT_MAX_CACHE_SIZE,
H5AC__DEFAULT_MIN_CLEAN_SIZE, (H5AC_NTYPES - 1),
- (const char **)H5AC_entry_type_names,
+ H5AC_class_s,
H5AC__check_if_write_permitted, TRUE, NULL, NULL);
#ifdef H5_HAVE_PARALLEL
} /* end else */
@@ -500,6 +512,20 @@ H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr)
if(H5AC_set_cache_auto_resize_config(f->shared->cache, config_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "auto resize configuration failed")
+ /* don't need to get the current H5C image config here since the
+ * cache has just been created, and thus f->shared->cache->image_ctl
+ * must still set to its initial value (H5C__DEFAULT_CACHE_IMAGE_CTL).
+ * Note that this not true as soon as control returns to the application
+ * program, as some test code modifies f->shared->cache->image_ctl.
+ */
+ int_ci_config.version = image_config_ptr->version;
+ int_ci_config.generate_image = image_config_ptr->generate_image;
+ int_ci_config.save_resize_status = image_config_ptr->save_resize_status;
+ int_ci_config.entry_ageout = image_config_ptr->entry_ageout;
+
+ if(H5C_set_cache_image_config(f, f->shared->cache, &int_ci_config) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "auto resize configuration failed")
+
done:
#ifdef H5_HAVE_PARALLEL
/* if there is a failure, try to tidy up the auxilary structure */
@@ -978,6 +1004,41 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5AC_load_cache_image_on_next_protect
+ *
+ * Purpose: Load the cache image block at the specified location,
+ * decode it, and insert its contents into the metadata
+ * cache.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 7/6/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_load_cache_image_on_next_protect(H5F_t * f, haddr_t addr, hsize_t len,
+ hbool_t rw)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(f->shared->cache);
+
+ if(H5C_load_cache_image_on_next_protect(f, addr, len, rw) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, FAIL, "call to H5C_load_cache_image_on_next_protect failed")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC_load_cache_image_on_next_protect() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5AC_mark_entry_dirty
*
* Purpose: Mark a pinned or protected entry as dirty. The target
@@ -2094,7 +2155,7 @@ done:
*/
herr_t
H5AC_get_cache_size(H5AC_t *cache_ptr, size_t *max_size_ptr, size_t *min_clean_size_ptr,
- size_t *cur_size_ptr, int32_t *cur_num_entries_ptr)
+ size_t *cur_size_ptr, uint32_t *cur_num_entries_ptr)
{
herr_t ret_value = SUCCEED; /* Return value */
@@ -2391,6 +2452,61 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5AC_validate_cache_image_config()
+ *
+ * Purpose: Run a sanity check on the contents of the supplied
+ * instance of H5AC_cache_image_config_t.
+ *
+ * Do nothing and return SUCCEED if no errors are detected,
+ * and flag an error and return FAIL otherwise.
+ *
+ * At present, this function operates by packing the data
+ * from the instance of H5AC_cache_image_config_t into an
+ * instance of H5C_cache_image_ctl_t, and then calling
+ * H5C_validate_cache_image_config(). If and when
+ * H5AC_cache_image_config_t and H5C_cache_image_ctl_t
+ * diverge, we may have to change this.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/25/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5AC_validate_cache_image_config(H5AC_cache_image_config_t *config_ptr)
+{
+ H5C_cache_image_ctl_t internal_config = H5C__DEFAULT_CACHE_IMAGE_CTL;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Check args */
+ if(config_ptr == NULL)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "NULL config_ptr on entry")
+
+ if(config_ptr->version != H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Unknown image config version")
+
+ /* don't need to get the current H5C image config here since the
+ * default values of fields not in the H5AC config will always be
+ * valid.
+ */
+ internal_config.version = config_ptr->version;
+ internal_config.generate_image = config_ptr->generate_image;
+ internal_config.save_resize_status = config_ptr->save_resize_status;
+ internal_config.entry_ageout = config_ptr->entry_ageout;
+
+ if(H5C_validate_cache_image_config(&internal_config) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "error(s) in new cache image config")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC_validate_cache_image_config() */
+
+
+/*-------------------------------------------------------------------------
*
* Function: H5AC__check_if_write_permitted
*
diff --git a/src/H5ACdbg.c b/src/H5ACdbg.c
index 8d99c6f..e2f7b13 100644
--- a/src/H5ACdbg.c
+++ b/src/H5ACdbg.c
@@ -252,6 +252,207 @@ done:
/*-------------------------------------------------------------------------
*
+ * Function: H5AC_get_entry_ptr_from_addr()
+ *
+ * Purpose: Debugging function that attempts to look up an entry in the
+ * cache by its file address, and if found, returns a pointer
+ * to the entry in *entry_ptr_ptr. If the entry is not in the
+ * cache, *entry_ptr_ptr is set to NULL.
+ *
+ * WARNING: This call should be used only in debugging
+ * routines, and it should be avoided when
+ * possible.
+ *
+ * Further, if we ever multi-thread the cache,
+ * this routine will have to be either discarded
+ * or heavily re-worked.
+ *
+ * Finally, keep in mind that the entry whose
+ * pointer is obtained in this fashion may not
+ * be in a stable state.
+ *
+ * Note that this function is only defined if NDEBUG
+ * is not defined.
+ *
+ * As heavy use of this function is almost certainly a
+ * bad idea, the metadata cache tracks the number of
+ * successful calls to this function, and (if
+ * H5C_DO_SANITY_CHECKS is defined) displays any
+ * non-zero count on cache shutdown.
+ *
+ * This function is just a wrapper that calls the H5C
+ * version of the function.
+ *
+ * Return: FAIL if error is detected, SUCCEED otherwise.
+ *
+ * Programmer: John Mainzer, 5/30/14
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+herr_t
+H5AC_get_entry_ptr_from_addr(const H5F_t *f, haddr_t addr, void **entry_ptr_ptr)
+{
+ H5C_t *cache_ptr; /* Ptr to cache */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ cache_ptr = f->shared->cache;
+
+ if(H5C_get_entry_ptr_from_addr(cache_ptr, addr, entry_ptr_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_get_entry_ptr_from_addr() failed")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC_get_entry_ptr_from_addr() */
+#endif /* NDEBUG */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_flush_dependency_exists()
+ *
+ * Purpose: Test to see if a flush dependency relationship exists
+ * between the supplied parent and child. Both parties
+ * are indicated by addresses so as to avoid the necessity
+ * of protect / unprotect calls prior to this call.
+ *
+ * If either the parent or the child is not in the metadata
+ * cache, the function sets *fd_exists_ptr to FALSE.
+ *
+ * If both are in the cache, the childs list of parents is
+ * searched for the proposed parent. If the proposed parent
+ * is found in the childs parent list, the function sets
+ * *fd_exists_ptr to TRUE. In all other non-error cases,
+ * the function sets *fd_exists_ptr FALSE.
+ *
+ * Return: SUCCEED on success/FAIL on failure. Note that
+ * *fd_exists_ptr is undefined on failure.
+ *
+ * Programmer: John Mainzer
+ * 9/28/16
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+herr_t
+H5AC_flush_dependency_exists(H5F_t *f, haddr_t parent_addr, haddr_t child_addr,
+ hbool_t *fd_exists_ptr)
+{
+ H5C_t *cache_ptr; /* Ptr to cache */
+ herr_t ret_value = FAIL; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ cache_ptr = f->shared->cache;
+
+ ret_value = H5C_flush_dependency_exists(cache_ptr, parent_addr, child_addr, fd_exists_ptr);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC_flush_dependency_exists() */
+#endif /* NDEBUG */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5AC_verify_entry_type()
+ *
+ * Purpose: Debugging function that attempts to look up an entry in the
+ * cache by its file address, and if found, test to see if its
+ * type field contains the expected value.
+ *
+ * If the specified entry is in cache, *in_cache_ptr is set
+ * to TRUE, and *type_ok_ptr is set to TRUE or FALSE depending
+ * on whether the entries type field matches the
+ * expected_type parameter
+ *
+ * If the target entry is not in cache, *in_cache_ptr is
+ * set to FALSE, and *type_ok_ptr is undefined.
+ *
+ * Note that this function is only defined if NDEBUG
+ * is not defined.
+ *
+ * This function is just a wrapper that calls the H5C
+ * version of the function.
+ *
+ * Return: FAIL if error is detected, SUCCEED otherwise.
+ *
+ * Programmer: John Mainzer, 5/30/14
+ *
+ * Changes: None.
+ *
+ * JRM -- 9/17/16
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+herr_t
+H5AC_verify_entry_type(const H5F_t *f, haddr_t addr,
+ const H5AC_class_t *expected_type, hbool_t *in_cache_ptr,
+ hbool_t *type_ok_ptr)
+{
+ H5C_t * cache_ptr;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ cache_ptr = f->shared->cache;
+
+ if(H5C_verify_entry_type(cache_ptr, addr, expected_type, in_cache_ptr, type_ok_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_verify_entry_type() failed")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC_verify_entry_type() */
+#endif /* NDEBUG */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5AC_get_serialization_in_progress
+ *
+ * Purpose: Return the current value of
+ * cache_ptr->serialization_in_progress.
+ *
+ * Return: Current value of cache_ptr->serialization_in_progress.
+ *
+ * Programmer: John Mainzer
+ * 8/24/15
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+hbool_t
+H5AC_get_serialization_in_progress(H5F_t *f)
+{
+ H5C_t * cache_ptr;
+ hbool_t ret_value = FALSE; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(f->shared);
+ cache_ptr = f->shared->cache;
+
+ /* Set return value */
+ ret_value = H5C_get_serialization_in_progress(cache_ptr);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5AC_get_serialization_in_progress() */
+#endif /* NDEBUG */
+
+
+/*-------------------------------------------------------------------------
+ *
* Function: H5AC_cache_is_clean()
*
* Purpose: Debugging function that verifies that all rings in the
diff --git a/src/H5ACpkg.h b/src/H5ACpkg.h
index dbbd8a0..816f1d5 100644
--- a/src/H5ACpkg.h
+++ b/src/H5ACpkg.h
@@ -351,6 +351,12 @@ H5FL_EXTERN(H5AC_aux_t);
* this verification. The field is set to NULL when the
* callback is not needed.
*
+ * The following field supports the metadata cache image feature.
+ *
+ * p0_image_len: unsiged integer containing the length of the metadata cache
+ * image constructed by MPI process 0. This field should be 0
+ * if the value is unknown, or if cache image is not enabled.
+ *
****************************************************************************/
#ifdef H5_HAVE_PARALLEL
@@ -400,6 +406,9 @@ typedef struct H5AC_aux_t
void (* sync_point_done)(int num_writes,
haddr_t * written_entries_tbl);
+
+ unsigned p0_image_len;
+
} H5AC_aux_t; /* struct H5AC_aux_t */
#endif /* H5_HAVE_PARALLEL */
diff --git a/src/H5ACprivate.h b/src/H5ACprivate.h
index 3dd6079..9f28b18 100644
--- a/src/H5ACprivate.h
+++ b/src/H5ACprivate.h
@@ -88,7 +88,7 @@ typedef enum {
H5AC_DRVRINFO_ID, /* (26) driver info block (supplements superblock) */
H5AC_EPOCH_MARKER_ID, /* (27) epoch marker - always internal to cache */
H5AC_PROXY_ENTRY_ID, /* (28) cache entry proxy */
- H5AC_TEST_ID, /* (29) test entry -- not used for actual files */
+ H5AC_PREFETCHED_ENTRY_ID, /* (29) prefetched entry - always internal to cache */
H5AC_NTYPES /* Number of types, must be last */
} H5AC_type_t;
@@ -111,14 +111,22 @@ typedef enum {
* use the dump_stats parameter to takedown_cache(), or call
* H5C_stats() directly.
* JRM -- 4/12/15
+ *
+ * Added the H5AC_DUMP_IMAGE_STATS_ON_CLOSE #define, which works much
+ * the same way as H5AC_DUMP_STATS_ON_CLOSE. However, the set of stats
+ * displayed is much smaller, and directed purely at the cache image feature.
+ *
+ * JRM -- 11/1/15
*/
#if H5C_COLLECT_CACHE_STATS
#define H5AC_DUMP_STATS_ON_CLOSE 0
+#define H5AC_DUMP_IMAGE_STATS_ON_CLOSE 0
#else /* H5C_COLLECT_CACHE_STATS */
#define H5AC_DUMP_STATS_ON_CLOSE 0
+#define H5AC_DUMP_IMAGE_STATS_ON_CLOSE 0
#endif /* H5C_COLLECT_CACHE_STATS */
@@ -319,7 +327,13 @@ H5_DLLVAR hid_t H5AC_rawdata_dxpl_id;
}
#endif /* H5_HAVE_PARALLEL */
-
+#define H5AC__DEFAULT_CACHE_IMAGE_CONFIG \
+{ \
+ /* int32_t version = */ H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION, \
+ /* hbool_t generate_image = */ FALSE, \
+ /* hbool_t save_resize_status = */ FALSE, \
+ /* int32_t entry_ageout = */ H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE \
+}
/*
* Library prototypes.
*/
@@ -344,7 +358,6 @@ H5_DLLVAR hid_t H5AC_rawdata_dxpl_id;
#define H5AC__TAKE_OWNERSHIP_FLAG H5C__TAKE_OWNERSHIP_FLAG
#define H5AC__FLUSH_LAST_FLAG H5C__FLUSH_LAST_FLAG
#define H5AC__FLUSH_COLLECTIVELY_FLAG H5C__FLUSH_COLLECTIVELY_FLAG
-#define H5AC__EVICT_ALLOW_LAST_PINS_FLAG H5C__EVICT_ALLOW_LAST_PINS_FLAG
/* #defines of flags used to report entry status in the
@@ -360,11 +373,44 @@ H5_DLLVAR hid_t H5AC_rawdata_dxpl_id;
#define H5AC_ES__IS_CORKED 0x0040
#define H5AC_ES__IMAGE_IS_UP_TO_DATE 0x0080
+/* Metadata entry class declarations */
+H5_DLLVAR const H5AC_class_t H5AC_BT[1];
+H5_DLLVAR const H5AC_class_t H5AC_SNODE[1];
+H5_DLLVAR const H5AC_class_t H5AC_LHEAP_PRFX[1];
+H5_DLLVAR const H5AC_class_t H5AC_LHEAP_DBLK[1];
+H5_DLLVAR const H5AC_class_t H5AC_GHEAP[1];
+H5_DLLVAR const H5AC_class_t H5AC_OHDR[1];
+H5_DLLVAR const H5AC_class_t H5AC_OHDR_CHK[1];
+H5_DLLVAR const H5AC_class_t H5AC_BT2_HDR[1];
+H5_DLLVAR const H5AC_class_t H5AC_BT2_INT[1];
+H5_DLLVAR const H5AC_class_t H5AC_BT2_LEAF[1];
+H5_DLLVAR const H5AC_class_t H5AC_FHEAP_HDR[1];
+H5_DLLVAR const H5AC_class_t H5AC_FHEAP_DBLOCK[1];
+H5_DLLVAR const H5AC_class_t H5AC_FHEAP_IBLOCK[1];
+H5_DLLVAR const H5AC_class_t H5AC_FSPACE_HDR[1];
+H5_DLLVAR const H5AC_class_t H5AC_FSPACE_SINFO[1];
+H5_DLLVAR const H5AC_class_t H5AC_SOHM_TABLE[1];
+H5_DLLVAR const H5AC_class_t H5AC_SOHM_LIST[1];
+H5_DLLVAR const H5AC_class_t H5AC_EARRAY_HDR[1];
+H5_DLLVAR const H5AC_class_t H5AC_EARRAY_IBLOCK[1];
+H5_DLLVAR const H5AC_class_t H5AC_EARRAY_SBLOCK[1];
+H5_DLLVAR const H5AC_class_t H5AC_EARRAY_DBLOCK[1];
+H5_DLLVAR const H5AC_class_t H5AC_EARRAY_DBLK_PAGE[1];
+H5_DLLVAR const H5AC_class_t H5AC_FARRAY_HDR[1];
+H5_DLLVAR const H5AC_class_t H5AC_FARRAY_DBLOCK[1];
+H5_DLLVAR const H5AC_class_t H5AC_FARRAY_DBLK_PAGE[1];
+H5_DLLVAR const H5AC_class_t H5AC_SUPERBLOCK[1];
+H5_DLLVAR const H5AC_class_t H5AC_DRVRINFO[1];
+H5_DLLVAR const H5AC_class_t H5AC_EPOCH_MARKER[1];
+H5_DLLVAR const H5AC_class_t H5AC_PROXY_ENTRY[1];
+H5_DLLVAR const H5AC_class_t H5AC_PREFETCHED_ENTRY[1];
+
/* external function declarations: */
H5_DLL herr_t H5AC_init(void);
-H5_DLL herr_t H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr);
+H5_DLL herr_t H5AC_create(const H5F_t *f, H5AC_cache_config_t *config_ptr,
+ H5AC_cache_image_config_t * image_config_ptr);
H5_DLL herr_t H5AC_get_entry_status(const H5F_t *f, haddr_t addr,
unsigned *status_ptr);
H5_DLL herr_t H5AC_insert_entry(H5F_t *f, hid_t dxpl_id, const H5AC_class_t *type,
@@ -394,13 +440,18 @@ 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,
- size_t *min_clean_size_ptr, size_t *cur_size_ptr, int32_t *cur_num_entries_ptr);
+ size_t *min_clean_size_ptr, size_t *cur_size_ptr, uint32_t *cur_num_entries_ptr);
H5_DLL herr_t H5AC_get_cache_hit_rate(H5AC_t *cache_ptr, double *hit_rate_ptr);
H5_DLL herr_t H5AC_reset_cache_hit_rate_stats(H5AC_t *cache_ptr);
H5_DLL herr_t H5AC_set_cache_auto_resize_config(H5AC_t *cache_ptr,
H5AC_cache_config_t *config_ptr);
H5_DLL herr_t H5AC_validate_config(H5AC_cache_config_t *config_ptr);
+/* Cache image routines */
+H5_DLL herr_t H5AC_load_cache_image_on_next_protect(H5F_t *f, haddr_t addr,
+ hsize_t len, hbool_t rw);
+H5_DLL herr_t H5AC_validate_cache_image_config(H5AC_cache_image_config_t *config_ptr);
+
/* Tag & Ring routines */
H5_DLL herr_t H5AC_tag(hid_t dxpl_id, haddr_t metadata_tag, haddr_t *prev_tag);
H5_DLL herr_t H5AC_flush_tagged_metadata(H5F_t * f, haddr_t metadata_tag, hid_t dxpl_id);
@@ -433,6 +484,14 @@ H5_DLL herr_t H5AC_add_candidate(H5AC_t * cache_ptr, haddr_t addr);
H5_DLL herr_t H5AC_stats(const H5F_t *f);
H5_DLL herr_t H5AC_dump_cache(const H5F_t *f);
#ifndef NDEBUG
+H5_DLL herr_t H5AC_get_entry_ptr_from_addr(const H5F_t *f, haddr_t addr,
+ void **entry_ptr_ptr);
+H5_DLL herr_t H5AC_flush_dependency_exists(H5F_t *f, haddr_t parent_addr,
+ haddr_t child_addr, hbool_t *fd_exists_ptr);
+H5_DLL herr_t H5AC_verify_entry_type(const H5F_t *f, haddr_t addr,
+ const H5AC_class_t *expected_type, hbool_t *in_cache_ptr,
+ hbool_t *type_ok_ptr);
+H5_DLL hbool_t H5AC_get_serialization_in_progress(H5F_t *f);
H5_DLL hbool_t H5AC_cache_is_clean(const H5F_t *f, H5AC_ring_t inner_ring);
#endif /* NDEBUG */ /* end debugging functions */
diff --git a/src/H5ACpublic.h b/src/H5ACpublic.h
index dd16764..5fdb3f4 100644
--- a/src/H5ACpublic.h
+++ b/src/H5ACpublic.h
@@ -508,6 +508,67 @@ typedef struct H5AC_cache_config_t
} H5AC_cache_config_t;
+/****************************************************************************
+ *
+ * structure H5AC_cache_image_config_t
+ *
+ * H5AC_cache_image_ctl_t is a public structure intended for use in public
+ * APIs. At least in its initial incarnation, it is a copy of struct
+ * H5C_cache_image_ctl_t.
+ *
+ * The fields of the structure are discussed individually below:
+ *
+ * version: Integer field containing the version number of this version
+ * of the H5C_image_ctl_t structure. Any instance of
+ * H5C_image_ctl_t passed to the cache must have a known
+ * version number, or an error will be flagged.
+ *
+ * generate_image: Boolean flag indicating whether a cache image should
+ * be created on file close.
+ *
+ * save_resize_status: Boolean flag indicating whether the cache image
+ * should include the adaptive cache resize configuration and status.
+ * Note that this field is ignored at present.
+ *
+ * entry_ageout: Integer field indicating the maximum number of
+ * times a prefetched entry can appear in subsequent cache images.
+ * This field exists to allow the user to avoid the buildup of
+ * infrequently used entries in long sequences of cache images.
+ *
+ * The value of this field must lie in the range
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE (-1) to
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX (100).
+ *
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE means that no limit
+ * is imposed on number of times a prefeteched entry can appear
+ * in subsequent cache images.
+ *
+ * A value of 0 prevents prefetched entries from being included
+ * in cache images.
+ *
+ * Positive integers restrict prefetched entries to the specified
+ * number of appearances.
+ *
+ * Note that the number of subsequent cache images that a prefetched
+ * entry has appeared in is tracked in an 8 bit field. Thus, while
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX can be increased from its
+ * current value, any value in excess of 255 will be the functional
+ * equivalent of H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE.
+ *
+ ****************************************************************************/
+
+#define H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION 1
+
+#define H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE -1
+#define H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX 100
+
+typedef struct H5AC_cache_image_config_t {
+ int32_t version;
+ hbool_t generate_image;
+ hbool_t save_resize_status;
+ int32_t entry_ageout;
+} H5AC_cache_image_config_t;
+
#ifdef __cplusplus
}
#endif
diff --git a/src/H5B2pkg.h b/src/H5B2pkg.h
index 7b1ec4d..71fdfde 100644
--- a/src/H5B2pkg.h
+++ b/src/H5B2pkg.h
@@ -307,15 +307,6 @@ typedef struct H5B2_node_info_test_t {
/* Package Private Variables */
/*****************************/
-/* H5B2 header inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_BT2_HDR[1];
-
-/* H5B2 internal node inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_BT2_INT[1];
-
-/* H5B2 leaf node inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_BT2_LEAF[1];
-
/* Declare a free list to manage the H5B2_internal_t struct */
H5FL_EXTERN(H5B2_internal_t);
diff --git a/src/H5Bpkg.h b/src/H5Bpkg.h
index 41e0951..fb93b8a 100644
--- a/src/H5Bpkg.h
+++ b/src/H5Bpkg.h
@@ -73,9 +73,6 @@ typedef struct H5B_cache_ud_t {
/* Package Private Variables */
/*****************************/
-/* H5B header inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_BT[1];
-
/* Declare a free list to manage the haddr_t sequence information */
H5FL_SEQ_EXTERN(haddr_t);
diff --git a/src/H5C.c b/src/H5C.c
index 22cb0df..73806ad 100644
--- a/src/H5C.c
+++ b/src/H5C.c
@@ -46,9 +46,9 @@
* - Change protect/unprotect to lock/unlock.
*
* - Flush entries in increasing address order in
- * H5C_make_space_in_cache().
+ * H5C__make_space_in_cache().
*
- * - Also in H5C_make_space_in_cache(), use high and low water marks
+ * - Also in H5C__make_space_in_cache(), use high and low water marks
* to reduce the number of I/O calls.
*
* - When flushing, attempt to combine contiguous entries to reduce
@@ -155,11 +155,6 @@ static void * H5C_load_entry(H5F_t * f,
haddr_t addr,
void * udata);
-static herr_t H5C_make_space_in_cache(H5F_t * f,
- hid_t dxpl_id,
- size_t space_needed,
- hbool_t write_permitted);
-
static herr_t H5C__mark_flush_dep_dirty(H5C_cache_entry_t * entry);
static herr_t H5C__mark_flush_dep_clean(H5C_cache_entry_t * entry);
@@ -243,7 +238,7 @@ H5C_t *
H5C_create(size_t max_cache_size,
size_t min_clean_size,
int max_type_id,
- const char * (* type_name_table_ptr),
+ const H5C_class_t * const * class_table_ptr,
H5C_write_permitted_func_t check_write_permitted,
hbool_t write_permitted,
H5C_log_flush_func_t log_flush,
@@ -261,11 +256,11 @@ H5C_create(size_t max_cache_size,
HDassert( max_type_id >= 0 );
HDassert( max_type_id < H5C__MAX_NUM_TYPE_IDS );
- HDassert( type_name_table_ptr );
+ HDassert( class_table_ptr );
for ( i = 0; i <= max_type_id; i++ ) {
- HDassert( (type_name_table_ptr)[i] );
- HDassert( HDstrlen(( type_name_table_ptr)[i]) > 0 );
+ HDassert( (class_table_ptr)[i] );
+ HDassert(HDstrlen((class_table_ptr)[i]->name) > 0);
} /* end for */
if(NULL == (cache_ptr = H5FL_CALLOC(H5C_t)))
@@ -297,7 +292,7 @@ H5C_create(size_t max_cache_size,
cache_ptr->max_type_id = max_type_id;
- cache_ptr->type_name_table_ptr = type_name_table_ptr;
+ cache_ptr->class_table_ptr = class_table_ptr;
cache_ptr->max_cache_size = max_cache_size;
cache_ptr->min_clean_size = min_clean_size;
@@ -389,6 +384,7 @@ H5C_create(size_t max_cache_size,
cache_ptr->resize_enabled = FALSE;
cache_ptr->cache_full = FALSE;
cache_ptr->size_decreased = FALSE;
+ cache_ptr->resize_in_progress = FALSE;
(cache_ptr->resize_ctl).version = H5C__CURR_AUTO_SIZE_CTL_VER;
(cache_ptr->resize_ctl).rpt_fcn = NULL;
@@ -434,12 +430,35 @@ H5C_create(size_t max_cache_size,
((cache_ptr->epoch_markers)[i]).magic =
H5C__H5C_CACHE_ENTRY_T_MAGIC;
((cache_ptr->epoch_markers)[i]).addr = (haddr_t)i;
- ((cache_ptr->epoch_markers)[i]).type = &H5C__epoch_marker_class;
+ ((cache_ptr->epoch_markers)[i]).type = H5AC_EPOCH_MARKER;
}
+ /* Initialize cache image generation on file close related fields.
+ * Initial value of image_ctl must match H5C__DEFAULT_CACHE_IMAGE_CTL
+ * in H5Cprivate.h.
+ */
+ cache_ptr->image_ctl.version = H5C__CURR_CACHE_IMAGE_CTL_VER;
+ cache_ptr->image_ctl.generate_image = FALSE;
+ cache_ptr->image_ctl.save_resize_status = FALSE;
+ cache_ptr->image_ctl.entry_ageout = -1;
+ cache_ptr->image_ctl.flags = H5C_CI__ALL_FLAGS;
+
+ cache_ptr->serialization_in_progress= FALSE;
+ cache_ptr->load_image = FALSE;
+ cache_ptr->image_loaded = FALSE;
+ cache_ptr->delete_image = FALSE;
+ cache_ptr->image_addr = HADDR_UNDEF;
+ cache_ptr->image_len = 0;
+ cache_ptr->image_data_len = 0;
+
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
+ cache_ptr->entry_fd_height_change_counter = 0;
+
+ cache_ptr->num_entries_in_image = 0;
+ cache_ptr->image_entries = NULL;
+ cache_ptr->image_buffer = NULL;
/* initialize free space manager related fields: */
cache_ptr->rdfsm_settled = FALSE;
@@ -456,6 +475,10 @@ H5C_create(size_t max_cache_size,
cache_ptr->prefix[0] = '\0'; /* empty string */
+#ifndef NDEBUG
+ cache_ptr->get_entry_ptr_from_addr_counter = 0;
+#endif /* NDEBUG */
+
/* Set return value */
ret_value = cache_ptr;
@@ -733,6 +756,21 @@ H5C_prep_for_file_close(H5F_t *f, hid_t dxpl_id)
/* Make certain there aren't any protected entries */
HDassert(cache_ptr->pl_len == 0);
+ /* If the file is opened and closed without any access to
+ * any group or dataset, it is possible that the cache image (if
+ * it exists) has not been read yet. Do this now if required.
+ */
+ if(cache_ptr->load_image) {
+ cache_ptr->load_image = FALSE;
+ if(H5C__load_cache_image(f, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, FAIL, "Can't load cache image")
+ } /* end if */
+
+ /* Generate the cache image, if requested */
+ if(cache_ptr->image_ctl.generate_image)
+ if(H5C__prep_image_for_file_close(f, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "Can't create cache image")
+
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_prep_for_file_close() */
@@ -775,10 +813,20 @@ H5C_dest(H5F_t * f, hid_t dxpl_id)
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr->close_warning_received);
+#if H5AC_DUMP_IMAGE_STATS_ON_CLOSE
+ if(H5C_image_stats(cache_ptr, TRUE) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't display cache image stats")
+#endif /* H5AC_DUMP_IMAGE_STATS_ON_CLOSE */
+
/* Flush and invalidate all cache entries */
if(H5C_flush_invalidate_cache(f, dxpl_id, H5C__NO_FLAGS_SET) < 0 )
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache")
+ /* Generate & write cache image if requested */
+ if(cache_ptr->image_ctl.generate_image)
+ if(H5C__generate_cache_image(f, dxpl_id, cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "Can't generate metadata cache image")
+
if(cache_ptr->slist_ptr != NULL) {
H5SL_close(cache_ptr->slist_ptr);
cache_ptr->slist_ptr = NULL;
@@ -790,6 +838,12 @@ H5C_dest(H5F_t * f, hid_t dxpl_id)
} /* end if */
#ifndef NDEBUG
+#if H5C_DO_SANITY_CHECKS
+ if(cache_ptr->get_entry_ptr_from_addr_counter > 0)
+ HDfprintf(stdout, "*** %ld calls to H5C_get_entry_ptr_from_add(). ***\n",
+ cache_ptr->get_entry_ptr_from_addr_counter);
+#endif /* H5C_DO_SANITY_CHECKS */
+
cache_ptr->magic = 0;
#endif /* NDEBUG */
@@ -868,7 +922,7 @@ H5C_expunge_entry(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type,
#if H5C_DO_EXTREME_SANITY_CHECKS
if(H5C_validate_lru_list(cache_ptr) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on entry.\n")
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/* Look for entry in cache */
@@ -909,7 +963,7 @@ H5C_expunge_entry(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type,
done:
#if H5C_DO_EXTREME_SANITY_CHECKS
if(H5C_validate_lru_list(cache_ptr) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on exit.\n")
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
@@ -968,12 +1022,12 @@ H5C_flush_cache(H5F_t *f, hid_t dxpl_id, unsigned flags)
{
#if H5C_DO_SANITY_CHECKS
int i;
- int32_t index_len = 0;
+ uint32_t index_len = 0;
size_t index_size = (size_t)0;
size_t clean_index_size = (size_t)0;
size_t dirty_index_size = (size_t)0;
size_t slist_size = (size_t)0;
- int32_t slist_len = 0;
+ uint32_t slist_len = 0;
#endif /* H5C_DO_SANITY_CHECKS */
H5C_ring_t ring;
H5C_t * cache_ptr;
@@ -1020,7 +1074,7 @@ H5C_flush_cache(H5F_t *f, hid_t dxpl_id, unsigned flags)
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry.\n")
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
ignore_protected = ( (flags & H5C__FLUSH_IGNORE_PROTECTED_FLAG) != 0 );
@@ -1173,16 +1227,8 @@ H5C_flush_to_min_clean(H5F_t * f,
"cache write is not permitted!?!\n");
}
#if 1 /* original code */
- result = H5C_make_space_in_cache(f,
- dxpl_id,
- (size_t)0,
- write_permitted);
-
- if ( result < 0 ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "H5C_make_space_in_cache failed.")
- }
+ if(H5C__make_space_in_cache(f, dxpl_id, (size_t)0, write_permitted) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C__make_space_in_cache failed")
#else /* modified code -- commented out for now */
if ( cache_ptr->max_cache_size > cache_ptr->index_size ) {
@@ -1336,8 +1382,9 @@ H5C_insert_entry(H5F_t * f,
hbool_t set_flush_marker;
hbool_t write_permitted = TRUE;
size_t empty_space;
- H5C_cache_entry_t *entry_ptr;
+ H5C_cache_entry_t *entry_ptr = NULL;
H5C_cache_entry_t *test_entry_ptr;
+ hbool_t entry_tagged = FALSE;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
@@ -1350,6 +1397,7 @@ H5C_insert_entry(H5F_t * f,
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( type );
+ HDassert( type->mem_type == cache_ptr->class_table_ptr[type->id]->mem_type );
HDassert( type->image_len );
HDassert( H5F_addr_defined(addr) );
HDassert( thing );
@@ -1357,14 +1405,10 @@ H5C_insert_entry(H5F_t * f,
#if H5C_DO_EXTREME_SANITY_CHECKS
/* no need to verify that entry is not already in the index as */
/* we already make that check below. */
-
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "an extreme sanity check failed on entry.\n");
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
set_flush_marker = ( (flags & H5C__SET_FLUSH_MARKER_FLAG) != 0 );
@@ -1453,6 +1497,22 @@ H5C_insert_entry(H5F_t * f,
entry_ptr->aux_next = NULL;
entry_ptr->aux_prev = NULL;
+ /* initialize cache image related fields */
+ entry_ptr->include_in_image = FALSE;
+ entry_ptr->lru_rank = 0;
+ entry_ptr->image_dirty = FALSE;
+ entry_ptr->fd_parent_count = 0;
+ entry_ptr->fd_parent_addrs = NULL;
+ entry_ptr->fd_child_count = 0;
+ entry_ptr->fd_dirty_child_count = 0;
+ entry_ptr->image_fd_height = 0;
+ entry_ptr->prefetched = FALSE;
+ entry_ptr->prefetch_type_id = 0;
+ entry_ptr->age = 0;
+#ifndef NDEBUG /* debugging field */
+ entry_ptr->serialization_count = 0;
+#endif /* NDEBUG */
+
#ifdef H5_HAVE_PARALLEL
entry_ptr->coll_next = NULL;
entry_ptr->coll_prev = NULL;
@@ -1461,6 +1521,7 @@ H5C_insert_entry(H5F_t * f,
/* Apply tag to newly inserted entry */
if(H5C__tag_entry(cache_ptr, entry_ptr, dxpl_id) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "Cannot tag metadata entry")
+ entry_tagged = TRUE;
H5C__RESET_CACHE_ENTRY_STATS(entry_ptr)
@@ -1497,7 +1558,7 @@ H5C_insert_entry(H5F_t * f,
/* Note that space_needed is just the amount of space that
* needed to insert the new entry without exceeding the cache
- * size limit. The subsequent call to H5C_make_space_in_cache()
+ * size limit. The subsequent call to H5C__make_space_in_cache()
* may evict the entries required to free more or less space
* depending on conditions. It MAY be less if the cache is
* currently undersized, or more if the cache is oversized.
@@ -1520,8 +1581,8 @@ H5C_insert_entry(H5F_t * f,
* no point in worrying about the third.
*/
- if(H5C_make_space_in_cache(f, dxpl_id, space_needed, write_permitted) < 0)
- HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C_make_space_in_cache failed.")
+ if(H5C__make_space_in_cache(f, dxpl_id, space_needed, write_permitted) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C__make_space_in_cache failed.")
}
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL)
@@ -1533,10 +1594,10 @@ H5C_insert_entry(H5F_t * f,
H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, FAIL)
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) )
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed just before done.\n")
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed just before done")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/* If the entry's type has a 'notify' callback send a 'after insertion'
@@ -1588,16 +1649,15 @@ H5C_insert_entry(H5F_t * f,
done:
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) )
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit.\n")
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
- if(ret_value < 0 && entry_ptr->tag_info) {
+ if(ret_value < 0 && entry_tagged)
if(H5C__untag_entry(cache_ptr, entry_ptr) < 0)
HDONE_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry from tag list")
- }
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_insert_entry() */
@@ -1920,7 +1980,7 @@ H5C_move_entry(H5C_t * cache_ptr,
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry.\n")
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
H5C__SEARCH_INDEX(cache_ptr, old_addr, entry_ptr, FAIL)
@@ -2024,7 +2084,7 @@ done:
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit.\n")
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
@@ -2070,12 +2130,9 @@ H5C_resize_entry(void *thing, size_t new_size)
HGOTO_ERROR(H5E_CACHE, H5E_BADTYPE, FAIL, "Entry isn't pinned or protected??")
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "an extreme sanity check failed on entry.\n");
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/* update for change in entry size if necessary */
@@ -2172,14 +2229,10 @@ H5C_resize_entry(void *thing, size_t new_size)
} /* end if */
done:
-
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "an extreme sanity check failed on exit.\n");
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0))
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
@@ -2275,13 +2328,10 @@ H5C_pin_protected_entry(void *thing)
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "an extreme sanity check failed on entry.\n");
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
@@ -2294,15 +2344,11 @@ H5C_pin_protected_entry(void *thing)
HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "Can't pin entry by client")
done:
-
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "an extreme sanity check failed on exit.\n");
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
@@ -2329,32 +2375,6 @@ done:
*
* Programmer: John Mainzer - 6/2/04
*
- * JRM -- 11/13/08
- * Modified function to call H5C_make_space_in_cache() when
- * the min_clean_size is violated, not just when there isn't
- * enough space for and entry that has just been loaded.
- *
- * The purpose of this modification is to avoid "metadata
- * blizzards" in the write only case. In such instances,
- * the cache was allowed to fill with dirty metadata. When
- * we finally needed to evict an entry to make space, we had
- * to flush out a whole cache full of metadata -- which has
- * interesting performance effects. We hope to avoid (or
- * perhaps more accurately hide) this effect by maintaining
- * the min_clean_size, which should force us to start flushing
- * entries long before we actually have to evict something
- * to make space.
- *
- * JRM -- 9/1/14
- * Replace the old rw parameter with the flags parameter.
- * This allows H5C_protect to accept flags other than
- * H5C__READ_ONLY_FLAG.
- *
- * Added support for the H5C__FLUSH_LAST_FLAG.
- * At present, this flag is only applied if the entry is
- * not in cache, and is loaded into the cache as a result of
- * this call.
- *
*-------------------------------------------------------------------------
*/
void *
@@ -2393,17 +2413,23 @@ H5C_protect(H5F_t * f,
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( type );
+ HDassert( type->mem_type == cache_ptr->class_table_ptr[type->id]->mem_type );
HDassert( H5F_addr_defined(addr) );
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on entry.\n")
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
+ /* Load the cache image, if requested */
+ if(cache_ptr->load_image) {
+ cache_ptr->load_image = FALSE;
+ if(H5C__load_cache_image(f, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "Can't load cache image")
+ } /* end if */
+
read_only = ( (flags & H5C__READ_ONLY_FLAG) != 0 );
flush_last = ( (flags & H5C__FLUSH_LAST_FLAG) != 0 );
@@ -2437,6 +2463,21 @@ H5C_protect(H5F_t * f,
if(entry_ptr->ring != ring)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "ring type mismatch occured for cache entry\n")
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ if(entry_ptr->prefetched) {
+ /* This call removes the prefetched entry from the cache,
+ * and replaces it with an entry deserialized from the
+ * image of the prefetched entry.
+ */
+ if(H5C__deserialize_prefetched_entry(f, dxpl_id, cache_ptr, &entry_ptr, type, addr, udata) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't deserialize prefetched entry.")
+
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(!entry_ptr->prefetched);
+ HDassert(entry_ptr->addr == addr);
+ } /* end if */
+
/* Check for trying to load the wrong type of entry from an address */
if(entry_ptr->type != type)
HGOTO_ERROR(H5E_CACHE, H5E_BADTYPE, NULL, "incorrect cache entry type")
@@ -2554,7 +2595,7 @@ H5C_protect(H5F_t * f,
empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
/* try to free up if necceary and if evictions are permitted. Note
- * that if evictions are enabled, we will call H5C_make_space_in_cache()
+ * that if evictions are enabled, we will call H5C__make_space_in_cache()
* regardless if the min_free_space requirement is not met.
*/
if ( ( cache_ptr->evictions_enabled ) &&
@@ -2589,7 +2630,7 @@ H5C_protect(H5F_t * f,
/* Note that space_needed is just the amount of space that
* needed to insert the new entry without exceeding the cache
- * size limit. The subsequent call to H5C_make_space_in_cache()
+ * size limit. The subsequent call to H5C__make_space_in_cache()
* may evict the entries required to free more or less space
* depending on conditions. It MAY be less if the cache is
* currently undersized, or more if the cache is oversized.
@@ -2616,8 +2657,8 @@ H5C_protect(H5F_t * f,
* see no point in worrying about the fourth.
*/
- if(H5C_make_space_in_cache(f, dxpl_id, space_needed, write_permitted) < 0 )
- HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C_make_space_in_cache failed 1.")
+ if(H5C__make_space_in_cache(f, dxpl_id, space_needed, write_permitted) < 0 )
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C__make_space_in_cache failed 1.")
}
/* Insert the entry in the hash table. It can't be dirty yet, so
@@ -2737,7 +2778,7 @@ H5C_protect(H5F_t * f,
* bring the cache size down to the current maximum cache size.
*
* Also, if the min_clean_size requirement is not met, we
- * should also call H5C_make_space_in_cache() to bring us
+ * should also call H5C__make_space_in_cache() to bring us
* into complience.
*/
@@ -2754,8 +2795,8 @@ H5C_protect(H5F_t * f,
if(cache_ptr->index_size > cache_ptr->max_cache_size)
cache_ptr->cache_full = TRUE;
- if(H5C_make_space_in_cache(f, dxpl_id, (size_t)0, write_permitted) < 0 )
- HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C_make_space_in_cache failed 2.")
+ if(H5C__make_space_in_cache(f, dxpl_id, (size_t)0, write_permitted) < 0 )
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C__make_space_in_cache failed 2.")
}
}
}
@@ -2794,10 +2835,10 @@ H5C_protect(H5F_t * f,
done:
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) )
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on exit.\n")
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
@@ -3166,13 +3207,10 @@ H5C_unpin_entry(void *_entry_ptr)
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "an extreme sanity check failed on entry.\n");
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
@@ -3181,19 +3219,14 @@ H5C_unpin_entry(void *_entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "Can't unpin entry from client")
done:
-
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "an extreme sanity check failed on exit.\n");
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
-
} /* H5C_unpin_entry() */
@@ -3280,13 +3313,10 @@ H5C_unprotect(H5F_t * f,
was_clean = ! ( entry_ptr->is_dirty );
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "an extreme sanity check failed on entry.\n");
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/* if the entry has multiple read only protects, just decrement
@@ -3481,19 +3511,14 @@ H5C_unprotect(H5F_t * f,
H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr)
done:
-
#if H5C_DO_EXTREME_SANITY_CHECKS
- if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) ||
- ( H5C_validate_lru_list(cache_ptr) < 0 ) ) {
-
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \
- "an extreme sanity check failed on exit.\n");
- }
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0)) {
+ HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
-
} /* H5C_unprotect() */
@@ -3884,6 +3909,7 @@ H5C_create_flush_dependency(void * parent_thing, void * child_thing)
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for flush dependency parent list")
child_entry->flush_dep_parent_nalloc *= 2;
} /* end else */
+ cache_ptr->entry_fd_height_change_counter++;
} /* end if */
/* Add the dependency to the child's parent array */
@@ -4092,6 +4118,7 @@ H5C__auto_adjust_cache_size(H5F_t * f,
{
H5C_t * cache_ptr = f->shared->cache;
herr_t result;
+ hbool_t reentrant_call = FALSE;
hbool_t inserted_epoch_marker = FALSE;
size_t new_max_cache_size = 0;
size_t old_max_cache_size = 0;
@@ -4111,6 +4138,18 @@ H5C__auto_adjust_cache_size(H5F_t * f,
HDassert( (double)0.0f <= (cache_ptr->resize_ctl).min_clean_fraction );
HDassert( (cache_ptr->resize_ctl).min_clean_fraction <= (double)100.0f );
+ /* check to see if cache_ptr->resize_in_progress is TRUE. If it, this
+ * is a re-entrant call via a client callback called in the resize
+ * process. To avoid an infinite recursion, set reentrant_call to
+ * TRUE, and goto done.
+ */
+ if(cache_ptr->resize_in_progress) {
+ reentrant_call = TRUE;
+ HGOTO_DONE(SUCCEED);
+ } /* end if */
+
+ cache_ptr->resize_in_progress = TRUE;
+
if ( !cache_ptr->resize_enabled ) {
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Auto cache resize disabled.")
@@ -4409,6 +4448,12 @@ H5C__auto_adjust_cache_size(H5F_t * f,
}
done:
+ /* Sanity checks */
+ HDassert(cache_ptr->resize_in_progress);
+ if(!reentrant_call)
+ cache_ptr->resize_in_progress = FALSE;
+ HDassert((!reentrant_call) || (cache_ptr->resize_in_progress));
+
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__auto_adjust_cache_size() */
@@ -5352,8 +5397,8 @@ H5C_flush_invalidate_cache(H5F_t *f, hid_t dxpl_id, unsigned flags)
#if H5C_DO_SANITY_CHECKS
{
int32_t i;
- int32_t index_len = 0;
- int32_t slist_len = 0;
+ uint32_t index_len = 0;
+ uint32_t slist_len = 0;
size_t index_size = (size_t)0;
size_t clean_index_size = (size_t)0;
size_t dirty_index_size = (size_t)0;
@@ -5484,7 +5529,7 @@ H5C_flush_invalidate_ring(H5F_t * f, hid_t dxpl_id, H5C_ring_t ring,
{
H5C_t *cache_ptr;
hbool_t restart_slist_scan;
- int32_t protected_entries = 0;
+ uint32_t protected_entries = 0;
int32_t i;
int32_t cur_ring_pel_len;
int32_t old_ring_pel_len;
@@ -5925,7 +5970,7 @@ H5C_flush_ring(H5F_t *f, hid_t dxpl_id, H5C_ring_t ring, unsigned flags)
hbool_t ignore_protected;
hbool_t tried_to_flush_protected_entry = FALSE;
hbool_t restart_slist_scan;
- int32_t protected_entries = 0;
+ uint32_t protected_entries = 0;
H5SL_node_t * node_ptr = NULL;
H5C_cache_entry_t * entry_ptr = NULL;
H5C_cache_entry_t * next_entry_ptr = NULL;
@@ -5949,7 +5994,7 @@ H5C_flush_ring(H5F_t *f, hid_t dxpl_id, H5C_ring_t ring, unsigned flags)
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
- HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry.\n")
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
ignore_protected = ( (flags & H5C__FLUSH_IGNORE_PROTECTED_FLAG) != 0 );
@@ -6188,34 +6233,6 @@ done:
*
* Programmer: John Mainzer, 5/5/04
*
- * Changes: Refactored function to remove the type_ptr parameter.
- *
- * JRM -- 8/7/14
- *
- * Added code to check for slist changes in pre_serialize and
- * serialize calls, and set
- * cache_ptr->slist_change_in_pre_serialize and
- * cache_ptr->slist_change_in_serialize as appropriate.
- *
- * JRM -- 12/13/14
- *
- * Refactored function to delay all modifications of the
- * metadata cache data structures until after any calls
- * to the pre-serialize or serialize callbacks.
- *
- * Need to do this, as some pre-serialize or serialize
- * calls result in calls to the metadata cache and
- * modifications to its data structures. Thus, at the
- * time of any such call, the target entry flags and
- * the metadata cache must all be consistant.
- *
- * Also added the entry_size_change_ptr parameter, which
- * allows the function to report back any change in the size
- * of the entry during the flush. Such size changes may
- * occur during the pre-serialize callback.
- *
- * JRM -- 12/24/14
- *
*-------------------------------------------------------------------------
*/
herr_t
@@ -6233,6 +6250,8 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr,
hbool_t destroy_entry; /* internal flag */
hbool_t generate_image; /* internal flag */
hbool_t was_dirty;
+ hbool_t suppress_image_entry_writes = FALSE;
+ hbool_t suppress_image_entry_frees = FALSE;
haddr_t entry_addr = HADDR_UNDEF;
herr_t ret_value = SUCCEED; /* Return value */
@@ -6275,6 +6294,28 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr,
else
write_entry = FALSE;
+ /* if we have received close warning, and we have been instructed to
+ * generate a metadata cache image, and we have actually constructed
+ * the entry images, set suppress_image_entry_frees to TRUE.
+ *
+ * Set suppress_image_entry_writes to TRUE if indicated by the
+ * image_ctl flags.
+ */
+ if(cache_ptr->close_warning_received && cache_ptr->image_ctl.generate_image
+ && cache_ptr->num_entries_in_image > 0 && cache_ptr->image_entries) {
+ /* Sanity checks */
+ HDassert(entry_ptr->image_up_to_date || !(entry_ptr->include_in_image));
+ HDassert(entry_ptr->image_ptr || !(entry_ptr->include_in_image));
+ HDassert((!clear_only) || !(entry_ptr->include_in_image));
+ HDassert((!take_ownership) || !(entry_ptr->include_in_image));
+ HDassert((!free_file_space) || !(entry_ptr->include_in_image));
+
+ suppress_image_entry_frees = TRUE;
+
+ if(cache_ptr->image_ctl.flags & H5C_CI__SUPRESS_ENTRY_WRITES)
+ suppress_image_entry_writes = TRUE;
+ } /* end if */
+
/* run initial sanity checks */
#if H5C_DO_SANITY_CHECKS
if(entry_ptr->in_slist) {
@@ -6329,6 +6370,9 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr,
} /* end if */
if(!(entry_ptr->image_up_to_date)) {
+ /* Sanity check */
+ HDassert(!entry_ptr->prefetched);
+
/* Generate the entry's image */
if(H5C__generate_image(f, cache_ptr, entry_ptr, dxpl_id) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, FAIL, "can't generate entry's image")
@@ -6349,7 +6393,17 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr,
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Write when writes are always forbidden!?!?!")
#endif /* H5C_DO_SANITY_CHECKS */
- if(((entry_ptr->type->flags) & H5C__CLASS_SKIP_WRITES) == 0) {
+ /* Write the image to disk unless the write is suppressed.
+ *
+ * This happens if both suppress_image_entry_writes and
+ * entry_ptr->include_in_image are TRUE, or if the
+ * H5AC__CLASS_SKIP_WRITES is set in the entry's type. This
+ * flag should only be used in test code
+ */
+ if((!suppress_image_entry_writes || !entry_ptr->include_in_image)
+ && (((entry_ptr->type->flags) & H5C__CLASS_SKIP_WRITES) == 0)) {
+ H5FD_mem_t mem_type = H5FD_MEM_DEFAULT;
+
#ifdef H5_HAVE_PARALLEL
if(cache_ptr->coll_write_list) {
if(H5SL_insert(cache_ptr->coll_write_list, entry_ptr, &entry_ptr->addr) < 0)
@@ -6357,7 +6411,15 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr,
} /* end if */
else
#endif /* H5_HAVE_PARALLEL */
- if(H5F_block_write(f, entry_ptr->type->mem_type, entry_ptr->addr, entry_ptr->size, dxpl_id, entry_ptr->image_ptr) < 0)
+
+ if(entry_ptr->prefetched) {
+ HDassert(entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID);
+ mem_type = cache_ptr->class_table_ptr[entry_ptr->prefetch_type_id]->mem_type;
+ } /* end if */
+ else
+ mem_type = entry_ptr->type->mem_type;
+
+ if(H5F_block_write(f, mem_type, entry_ptr->addr, entry_ptr->size, dxpl_id, entry_ptr->image_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't write image to file.")
} /* end if */
@@ -6502,10 +6564,29 @@ H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id, H5C_cache_entry_t *entry_ptr,
/* Sanity check */
HDassert(0 == entry_ptr->flush_dep_nparents);
- /* Start by freeing the buffer for the on disk image */
- if(entry_ptr->image_ptr != NULL)
+ /* if both suppress_image_entry_frees and entry_ptr->include_in_image
+ * are true, simply set entry_ptr->image_ptr to NULL, as we have
+ * another pointer to the buffer in an instance of H5C_image_entry_t
+ * in cache_ptr->image_entries.
+ *
+ * Otherwise, free the buffer if it exists.
+ */
+ if(suppress_image_entry_frees && entry_ptr->include_in_image)
+ entry_ptr->image_ptr = NULL;
+ else if(entry_ptr->image_ptr != NULL)
entry_ptr->image_ptr = H5MM_xfree(entry_ptr->image_ptr);
+ /* If the entry is not a prefetched entry, verify that the flush
+ * dependency parents addresses array has been transfered.
+ *
+ * If the entry is prefetched, the free_isr routine will dispose of
+ * the flush dependency parents adresses array if necessary.
+ */
+ if(!entry_ptr->prefetched) {
+ HDassert(0 == entry_ptr->fd_parent_count);
+ HDassert(NULL == entry_ptr->fd_parent_addrs);
+ } /* end if */
+
/* Check whether we should free the space in the file that
* the entry occupies
*/
@@ -6981,6 +7062,22 @@ H5C_load_entry(H5F_t * f,
entry->coll_prev = NULL;
#endif /* H5_HAVE_PARALLEL */
+ /* initialize cache image related fields */
+ entry->include_in_image = FALSE;
+ entry->lru_rank = 0;
+ entry->image_dirty = FALSE;
+ entry->fd_parent_count = 0;
+ entry->fd_parent_addrs = NULL;
+ entry->fd_child_count = 0;
+ entry->fd_dirty_child_count = 0;
+ entry->image_fd_height = 0;
+ entry->prefetched = FALSE;
+ entry->prefetch_type_id = 0;
+ entry->age = 0;
+#ifndef NDEBUG /* debugging field */
+ entry->serialization_count = 0;
+#endif /* NDEBUG */
+
H5C__RESET_CACHE_ENTRY_STATS(entry);
ret_value = thing;
@@ -7001,7 +7098,7 @@ done:
/*-------------------------------------------------------------------------
*
- * Function: H5C_make_space_in_cache
+ * Function: H5C__make_space_in_cache
*
* Purpose: Attempt to evict cache entries until the index_size
* is at least needed_space below max_cache_size.
@@ -7066,19 +7163,17 @@ done:
*
*-------------------------------------------------------------------------
*/
-static herr_t
-H5C_make_space_in_cache(H5F_t * f,
- hid_t dxpl_id,
- size_t space_needed,
- hbool_t write_permitted)
+herr_t
+H5C__make_space_in_cache(H5F_t *f, hid_t dxpl_id, size_t space_needed,
+ hbool_t write_permitted)
{
H5C_t * cache_ptr = f->shared->cache;
#if H5C_COLLECT_CACHE_STATS
int32_t clean_entries_skipped = 0;
int32_t total_entries_scanned = 0;
#endif /* H5C_COLLECT_CACHE_STATS */
- int32_t entries_examined = 0;
- int32_t initial_list_len;
+ uint32_t entries_examined = 0;
+ uint32_t initial_list_len;
size_t empty_space;
hbool_t prev_is_dirty = FALSE;
hbool_t didnt_flush_entry = FALSE;
@@ -7086,16 +7181,16 @@ H5C_make_space_in_cache(H5F_t * f,
H5C_cache_entry_t * entry_ptr;
H5C_cache_entry_t * prev_ptr;
H5C_cache_entry_t * next_ptr;
- int32_t num_corked_entries = 0;
+ uint32_t num_corked_entries = 0;
herr_t ret_value = SUCCEED; /* Return value */
- FUNC_ENTER_NOAPI_NOINIT
+ FUNC_ENTER_PACKAGE
- HDassert( f );
- HDassert( cache_ptr );
- HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
- HDassert( cache_ptr->index_size ==
- (cache_ptr->clean_index_size + cache_ptr->dirty_index_size) );
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->index_size == (cache_ptr->clean_index_size + cache_ptr->dirty_index_size));
if ( write_permitted ) {
@@ -7358,10 +7453,8 @@ H5C_make_space_in_cache(H5F_t * f,
}
done:
-
FUNC_LEAVE_NOAPI(ret_value)
-
-} /* H5C_make_space_in_cache() */
+} /* H5C__make_space_in_cache() */
/*-------------------------------------------------------------------------
@@ -8184,6 +8277,10 @@ H5C__assert_flush_dep_nocycle(const H5C_cache_entry_t * entry,
* are about to delete the entry from the cache (i.e. on a
* flush destroy).
*
+ * Note: This routine is very similar to H5C__serialize_single_entry
+ * and changes to one should probably be reflected in the other.
+ * Ideally, one should be eliminated.
+ *
* Return: Non-negative on success/Negative on failure
*
* Programmer: Mohamad Chaarawi
@@ -8218,8 +8315,7 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr,
/* Check for any flags set in the pre-serialize callback */
if(serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET) {
/* Check for unexpected flags from serialize callback */
- if(serialize_flags & ~(H5C__SERIALIZE_RESIZED_FLAG |
- H5C__SERIALIZE_MOVED_FLAG))
+ if(serialize_flags & ~(H5C__SERIALIZE_RESIZED_FLAG | H5C__SERIALIZE_MOVED_FLAG))
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unknown serialize flag(s)")
#ifdef H5_HAVE_PARALLEL
@@ -8266,9 +8362,8 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr,
/* Update statistics for resizing the entry */
H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_len);
- /* update the hash table for the size change */
- H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, \
- new_len, entry_ptr, !(entry_ptr->is_dirty));
+ /* Update the hash table for the size change */
+ H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len, entry_ptr, !(entry_ptr->is_dirty));
/* The entry can't be protected since we are in the process of
* flushing it. Thus we must update the replacement policy data
@@ -8281,10 +8376,11 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr,
* for the flush or flush destroy yet, the entry should
* be in the slist. Thus update it for the size change.
*/
+ HDassert(entry_ptr->is_dirty);
HDassert(entry_ptr->in_slist);
H5C__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len);
- /* finally, update the entry for its new size */
+ /* Finally, update the entry for its new size */
entry_ptr->size = new_len;
} /* end if */
@@ -8301,10 +8397,10 @@ H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr,
H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr, FAIL);
H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE);
- /* update the entry for its new address */
+ /* Update the entry for its new address */
entry_ptr->addr = new_addr;
- /* and then reinsert in the index and slist */
+ /* And then reinsert in the index and slist */
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL);
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL);
} /* end if */
diff --git a/src/H5Cdbg.c b/src/H5Cdbg.c
index 5697bff..7f4c8e0 100644
--- a/src/H5Cdbg.c
+++ b/src/H5Cdbg.c
@@ -35,6 +35,7 @@
/* Headers */
/***********/
#include "H5private.h" /* Generic Functions */
+#include "H5ACprivate.h" /* Metadata Cache */
#include "H5Cpkg.h" /* Cache */
#include "H5Eprivate.h" /* Error Handling */
@@ -385,6 +386,7 @@ H5C_stats(H5C_t * cache_ptr,
size_t aggregate_max_size = 0;
int32_t aggregate_max_pins = 0;
double hit_rate;
+ double prefetch_use_rate;
double average_successful_search_depth = 0.0f;
double average_failed_search_depth = 0.0f;
double average_entries_skipped_per_calls_to_msic = 0.0f;
@@ -490,12 +492,12 @@ H5C_stats(H5C_t * cache_ptr,
average_failed_search_depth);
HDfprintf(stdout,
- "%s current (max) index size / length = %ld (%ld) / %ld (%ld)\n",
+ "%s current (max) index size / length = %ld (%ld) / %lu (%lu)\n",
cache_ptr->prefix,
(long)(cache_ptr->index_size),
(long)(cache_ptr->max_index_size),
- (long)(cache_ptr->index_len),
- (long)(cache_ptr->max_index_len));
+ (unsigned long)(cache_ptr->index_len),
+ (unsigned long)(cache_ptr->max_index_len));
HDfprintf(stdout,
"%s current (max) clean/dirty idx size = %ld (%ld) / %ld (%ld)\n",
@@ -506,46 +508,46 @@ H5C_stats(H5C_t * cache_ptr,
(long)(cache_ptr->max_dirty_index_size));
HDfprintf(stdout,
- "%s current (max) slist size / length = %ld (%ld) / %ld (%ld)\n",
+ "%s current (max) slist size / length = %ld (%ld) / %lu (%lu)\n",
cache_ptr->prefix,
(long)(cache_ptr->slist_size),
(long)(cache_ptr->max_slist_size),
- (long)(cache_ptr->slist_len),
- (long)(cache_ptr->max_slist_len));
+ (unsigned long)(cache_ptr->slist_len),
+ (unsigned long)(cache_ptr->max_slist_len));
HDfprintf(stdout,
- "%s current (max) PL size / length = %ld (%ld) / %ld (%ld)\n",
+ "%s current (max) PL size / length = %ld (%ld) / %lu (%lu)\n",
cache_ptr->prefix,
(long)(cache_ptr->pl_size),
(long)(cache_ptr->max_pl_size),
- (long)(cache_ptr->pl_len),
- (long)(cache_ptr->max_pl_len));
+ (unsigned long)(cache_ptr->pl_len),
+ (unsigned long)(cache_ptr->max_pl_len));
HDfprintf(stdout,
- "%s current (max) PEL size / length = %ld (%ld) / %ld (%ld)\n",
+ "%s current (max) PEL size / length = %ld (%ld) / %lu (%lu)\n",
cache_ptr->prefix,
(long)(cache_ptr->pel_size),
(long)(cache_ptr->max_pel_size),
- (long)(cache_ptr->pel_len),
- (long)(cache_ptr->max_pel_len));
+ (unsigned long)(cache_ptr->pel_len),
+ (unsigned long)(cache_ptr->max_pel_len));
HDfprintf(stdout,
- "%s current LRU list size / length = %ld / %ld\n",
+ "%s current LRU list size / length = %ld / %lu\n",
cache_ptr->prefix,
(long)(cache_ptr->LRU_list_size),
- (long)(cache_ptr->LRU_list_len));
+ (unsigned long)(cache_ptr->LRU_list_len));
HDfprintf(stdout,
- "%s current clean LRU size / length = %ld / %ld\n",
+ "%s current clean LRU size / length = %ld / %lu\n",
cache_ptr->prefix,
(long)(cache_ptr->cLRU_list_size),
- (long)(cache_ptr->cLRU_list_len));
+ (unsigned long)(cache_ptr->cLRU_list_len));
HDfprintf(stdout,
- "%s current dirty LRU size / length = %ld / %ld\n",
+ "%s current dirty LRU size / length = %ld / %lu\n",
cache_ptr->prefix,
(long)(cache_ptr->dLRU_list_size),
- (long)(cache_ptr->dLRU_list_len));
+ (unsigned long)(cache_ptr->dLRU_list_len));
HDfprintf(stdout,
"%s Total hits / misses / hit_rate = %ld / %ld / %f\n",
@@ -648,6 +650,38 @@ H5C_stats(H5C_t * cache_ptr,
(long long)(cache_ptr->LRU_scan_restarts),
(long long)(cache_ptr->index_scan_restarts));
+ HDfprintf(stdout,
+ "%s cache image creations/loads/size = %d / %d / %lld\n",
+ cache_ptr->prefix,
+ cache_ptr->images_created,
+ cache_ptr->images_loaded,
+ (long long)cache_ptr->last_image_size);
+
+ HDfprintf(stdout,
+ "%s prefetches / dirty prefetches = %lld / %lld\n",
+ cache_ptr->prefix,
+ (long long)(cache_ptr->prefetches),
+ (long long)(cache_ptr->dirty_prefetches));
+
+ HDfprintf(stdout,
+ "%s prefetch hits/flushes/evictions = %lld / %lld / %lld\n",
+ cache_ptr->prefix,
+ (long long)(cache_ptr->prefetch_hits),
+ (long long)(cache_ptr->flushes[H5AC_PREFETCHED_ENTRY_ID]),
+ (long long)(cache_ptr->evictions[H5AC_PREFETCHED_ENTRY_ID]));
+
+ if(cache_ptr->prefetches > 0)
+ prefetch_use_rate =
+ (double)100.0f * ((double)(cache_ptr->prefetch_hits)) /
+ ((double)(cache_ptr->prefetches));
+ else
+ prefetch_use_rate = 0.0f;
+
+ HDfprintf(stdout,
+ "%s prefetched entry use rate = %lf\n",
+ cache_ptr->prefix,
+ prefetch_use_rate);
+
#if H5C_COLLECT_CACHE_ENTRY_STATS
HDfprintf(stdout, "%s aggregate max / min accesses = %d / %d\n",
@@ -673,7 +707,7 @@ H5C_stats(H5C_t * cache_ptr,
HDfprintf(stdout, "%s Stats on %s:\n",
cache_ptr->prefix,
- ((cache_ptr->type_name_table_ptr))[i]);
+ ((cache_ptr->class_table_ptr))[i]->name);
if((cache_ptr->hits[i] > 0) || (cache_ptr->misses[i] > 0))
hit_rate = (double)100.0f * ((double)(cache_ptr->hits[i])) /
@@ -869,6 +903,14 @@ H5C_stats__reset(H5C_t H5_ATTR_UNUSED * cache_ptr)
cache_ptr->LRU_scan_restarts = 0;
cache_ptr->index_scan_restarts = 0;
+ cache_ptr->images_created = 0;
+ cache_ptr->images_loaded = 0;
+ cache_ptr->last_image_size = (size_t)0;
+
+ cache_ptr->prefetches = 0;
+ cache_ptr->dirty_prefetches = 0;
+ cache_ptr->prefetch_hits = 0;
+
#if H5C_COLLECT_CACHE_ENTRY_STATS
for(i = 0; i <= cache_ptr->max_type_id; i++) {
cache_ptr->max_accesses[i] = 0;
@@ -952,6 +994,303 @@ H5C__dump_entry(H5C_t *cache_ptr, const H5C_cache_entry_t *entry_ptr,
if(entry_ptr->flush_dep_nchildren)
H5C__dump_children(cache_ptr, entry_ptr, FALSE, "Child", indent);
} /* end H5C__dump_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C_flush_dependency_exists()
+ *
+ * Purpose: Test to see if a flush dependency relationship exists
+ * between the supplied parent and child. Both parties
+ * are indicated by addresses so as to avoid the necessity
+ * of protect / unprotect calls prior to this call.
+ *
+ * If either the parent or the child is not in the metadata
+ * cache, the function sets *fd_exists_ptr to FALSE.
+ *
+ * If both are in the cache, the childs list of parents is
+ * searched for the proposed parent. If the proposed parent
+ * is found in the childs parent list, the function sets
+ * *fd_exists_ptr to TRUE. In all other non-error cases,
+ * the function sets *fd_exists_ptr FALSE.
+ *
+ * Return: SUCCEED on success/FAIL on failure. Note that
+ * *fd_exists_ptr is undefined on failure.
+ *
+ * Programmer: John Mainzer
+ * 9/28/16
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+herr_t
+H5C_flush_dependency_exists(H5C_t *cache_ptr, haddr_t parent_addr, haddr_t child_addr,
+ hbool_t *fd_exists_ptr)
+{
+ hbool_t fd_exists = FALSE; /* whether flush dependency exists */
+ H5C_cache_entry_t * parent_ptr; /* Ptr to parent entry */
+ H5C_cache_entry_t * child_ptr; /* Ptr to child entry */
+ hbool_t ret_value = FALSE; /* Return value */
+
+ FUNC_ENTER_NOAPI(NULL)
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(H5F_addr_defined(parent_addr));
+ HDassert(H5F_addr_defined(child_addr));
+ HDassert(fd_exists_ptr);
+
+ H5C__SEARCH_INDEX(cache_ptr, parent_addr, parent_ptr, FAIL)
+ H5C__SEARCH_INDEX(cache_ptr, child_addr, child_ptr, FAIL)
+
+ if(parent_ptr && child_ptr) {
+ HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(child_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ if(child_ptr->flush_dep_nparents > 0) {
+ unsigned u; /* Local index variable */
+
+ HDassert(child_ptr->flush_dep_parent);
+ HDassert(child_ptr->flush_dep_parent_nalloc >= child_ptr->flush_dep_nparents);
+
+ for(u = 0; u < child_ptr->flush_dep_nparents; u++) {
+ if(child_ptr->flush_dep_parent[u] == parent_ptr) {
+ fd_exists = TRUE;
+ HDassert(parent_ptr->flush_dep_nchildren > 0);
+ break;
+ } /* end if */
+ } /* end for */
+ } /* end if */
+ } /* end if */
+
+ *fd_exists_ptr = fd_exists;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_flush_dependency_exists() */
+#endif /* NDEBUG */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C_validate_index_list
+ *
+ * Purpose: Debugging function that scans the index list for errors.
+ *
+ * If an error is detected, the function generates a
+ * diagnostic and returns FAIL. If no error is detected,
+ * the function returns SUCCEED.
+ *
+ * Return: FAIL if error is detected, SUCCEED otherwise.
+ *
+ * Programmer: John Mainzer, 9/16/16
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+herr_t
+H5C_validate_index_list(H5C_t *cache_ptr)
+{
+ H5C_cache_entry_t * entry_ptr = NULL;
+ uint32_t len = 0;
+ int32_t index_ring_len[H5C_RING_NTYPES];
+ size_t size = 0;
+ size_t clean_size = 0;
+ size_t dirty_size = 0;
+ size_t index_ring_size[H5C_RING_NTYPES];
+ size_t clean_index_ring_size[H5C_RING_NTYPES];
+ size_t dirty_index_ring_size[H5C_RING_NTYPES];
+ int i;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ for(i = 0; i < H5C_RING_NTYPES; i++) {
+ index_ring_len[i] = 0;
+ index_ring_size[i] = 0;
+ clean_index_ring_size[i] = 0;
+ dirty_index_ring_size[i] = 0;
+ } /* end if */
+
+ if(((cache_ptr->il_head == NULL) || (cache_ptr->il_tail == NULL))
+ && (cache_ptr->il_head != cache_ptr->il_tail))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 1 failed")
+
+ if((cache_ptr->index_len == 1) && ((cache_ptr->il_head != cache_ptr->il_tail)
+ || (cache_ptr->il_head == NULL) || (cache_ptr->il_head->size != cache_ptr->index_size)))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 2 failed")
+
+ if((cache_ptr->index_len >= 1)
+ && ((cache_ptr->il_head == NULL)
+ || (cache_ptr->il_head->il_prev != NULL)
+ || (cache_ptr->il_tail == NULL)
+ || (cache_ptr->il_tail->il_next != NULL)))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 3 failed")
+
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL) {
+ if((entry_ptr != cache_ptr->il_head)
+ && ((entry_ptr->il_prev == NULL) || (entry_ptr->il_prev->il_next != entry_ptr)))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 4 failed")
+
+ if((entry_ptr != cache_ptr->il_tail)
+ && ((entry_ptr->il_next == NULL) || (entry_ptr->il_next->il_prev != entry_ptr)))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 5 failed")
+
+ HDassert(entry_ptr->ring > 0);
+ HDassert(entry_ptr->ring < H5C_RING_NTYPES);
+
+ len++;
+ index_ring_len[entry_ptr->ring] += 1;
+
+ size += entry_ptr->size;
+ index_ring_size[entry_ptr->ring] += entry_ptr->size;
+
+ if(entry_ptr->is_dirty) {
+ dirty_size += entry_ptr->size;
+ dirty_index_ring_size[entry_ptr->ring] += entry_ptr->size;
+ } /* end if */
+ else {
+ clean_size += entry_ptr->size;
+ clean_index_ring_size[entry_ptr->ring] += entry_ptr->size;
+ } /* end else */
+
+ entry_ptr = entry_ptr->il_next;
+ } /* end while */
+
+ if((cache_ptr->index_len != len) || (cache_ptr->il_len != len)
+ || (cache_ptr->index_size != size) || (cache_ptr->il_size != size)
+ || (cache_ptr->clean_index_size != clean_size)
+ || (cache_ptr->dirty_index_size != dirty_size)
+ || (clean_size + dirty_size != size))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 6 failed")
+
+ size = 0;
+ clean_size = 0;
+ dirty_size = 0;
+ for(i = 0; i < H5C_RING_NTYPES; i++) {
+ size += clean_index_ring_size[i] + dirty_index_ring_size[i];
+ clean_size += clean_index_ring_size[i];
+ dirty_size += dirty_index_ring_size[i];
+ } /* end for */
+
+ if((cache_ptr->index_size != size)
+ || (cache_ptr->clean_index_size != clean_size)
+ || (cache_ptr->dirty_index_size != dirty_size))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 7 failed")
+
+done:
+ if(ret_value != SUCCEED)
+ HDassert(0);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_validate_index_list() */
+#endif /* NDEBUG */
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C_get_entry_ptr_from_addr()
+ *
+ * Purpose: Debugging function that attempts to look up an entry in the
+ * cache by its file address, and if found, returns a pointer
+ * to the entry in *entry_ptr_ptr. If the entry is not in the
+ * cache, *entry_ptr_ptr is set to NULL.
+ *
+ * WARNING: This call should be used only in debugging
+ * routines, and it should be avoided when
+ * possible.
+ *
+ * Further, if we ever multi-thread the cache,
+ * this routine will have to be either discarded
+ * or heavily re-worked.
+ *
+ * Finally, keep in mind that the entry whose
+ * pointer is obtained in this fashion may not
+ * be in a stable state.
+ *
+ * Note that this function is only defined if NDEBUG
+ * is not defined.
+ *
+ * As heavy use of this function is almost certainly a
+ * bad idea, the metadata cache tracks the number of
+ * successful calls to this function, and (if
+ * H5C_DO_SANITY_CHECKS is defined) displays any
+ * non-zero count on cache shutdown.
+ *
+ * Return: FAIL if error is detected, SUCCEED otherwise.
+ *
+ * Programmer: John Mainzer, 5/30/14
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+herr_t
+H5C_get_entry_ptr_from_addr(H5C_t *cache_ptr, haddr_t addr, void **entry_ptr_ptr)
+{
+ H5C_cache_entry_t * entry_ptr = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(H5F_addr_defined(addr));
+ HDassert(entry_ptr_ptr);
+
+ H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL)
+
+ if(entry_ptr == NULL)
+ /* the entry doesn't exist in the cache -- report this
+ * and quit.
+ */
+ *entry_ptr_ptr = NULL;
+ else {
+ *entry_ptr_ptr = entry_ptr;
+
+ /* increment call counter */
+ (cache_ptr->get_entry_ptr_from_addr_counter)++;
+ } /* end else */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_get_entry_ptr_from_addr() */
+#endif /* NDEBUG */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C_get_serialization_in_progress
+ *
+ * Purpose: Return the current value of
+ * cache_ptr->serialization_in_progress.
+ *
+ * Return: Current value of cache_ptr->serialization_in_progress.
+ *
+ * Programmer: John Mainzer
+ * 8/24/15
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+hbool_t
+H5C_get_serialization_in_progress(const H5C_t *cache_ptr)
+{
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Sanity check */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ FUNC_LEAVE_NOAPI(cache_ptr->serialization_in_progress)
+} /* H5C_get_serialization_in_progress() */
+#endif /* NDEBUG */
+
+
/*-------------------------------------------------------------------------
*
* Function: H5C_cache_is_clean()
@@ -995,3 +1334,69 @@ H5C_cache_is_clean(const H5C_t *cache_ptr, H5C_ring_t inner_ring)
} /* H5C_cache_is_clean() */
#endif /* NDEBUG */
+
+/*-------------------------------------------------------------------------
+ *
+ * Function: H5C_verify_entry_type()
+ *
+ * Purpose: Debugging function that attempts to look up an entry in the
+ * cache by its file address, and if found, test to see if its
+ * type field contains the expted value.
+ *
+ * If the specified entry is in cache, *in_cache_ptr is set
+ * to TRUE, and *type_ok_ptr is set to TRUE or FALSE depending
+ * on whether the entries type field matches the expected_type
+ * parameter.
+ *
+ * If the target entry is not in cache, *in_cache_ptr is
+ * set to FALSE, and *type_ok_ptr is undefined.
+ *
+ * Note that this function is only defined if NDEBUG
+ * is not defined.
+ *
+ * Return: FAIL if error is detected, SUCCEED otherwise.
+ *
+ * Programmer: John Mainzer, 5/30/14
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NDEBUG
+herr_t
+H5C_verify_entry_type(H5C_t *cache_ptr, haddr_t addr,
+ const H5C_class_t *expected_type, hbool_t *in_cache_ptr,
+ hbool_t *type_ok_ptr)
+{
+ H5C_cache_entry_t * entry_ptr = NULL;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(H5F_addr_defined(addr));
+ HDassert(expected_type);
+ HDassert(in_cache_ptr);
+ HDassert(type_ok_ptr);
+
+ H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL)
+
+ if(entry_ptr == NULL)
+ /* the entry doesn't exist in the cache -- report this
+ * and quit.
+ */
+ *in_cache_ptr = FALSE;
+ else {
+ *in_cache_ptr = TRUE;
+
+ if(entry_ptr->prefetched)
+ *type_ok_ptr = (expected_type->id == entry_ptr->prefetch_type_id);
+ else
+ *type_ok_ptr = (expected_type == entry_ptr->type);
+ } /* end else */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_verify_entry_type() */
+#endif /* NDEBUG */
+
diff --git a/src/H5Cepoch.c b/src/H5Cepoch.c
index e576028..655d795 100644
--- a/src/H5Cepoch.c
+++ b/src/H5Cepoch.c
@@ -92,8 +92,7 @@ static herr_t H5C__epoch_marker_fsf_size(const void H5_ATTR_UNUSED * thing,
/*******************/
-const H5C_class_t H5C__epoch_marker_class =
-{
+const H5AC_class_t H5AC_EPOCH_MARKER[1] = {{
/* id = */ H5AC_EPOCH_MARKER_ID,
/* name = */ "epoch marker",
/* mem_type = */ H5FD_MEM_DEFAULT, /* value doesn't matter */
@@ -108,7 +107,7 @@ const H5C_class_t H5C__epoch_marker_class =
/* notify = */ H5C__epoch_marker_notify,
/* free_icr = */ H5C__epoch_marker_free_icr,
/* fsf_size = */ H5C__epoch_marker_fsf_size,
-};
+}};
/***************************************************************************
diff --git a/src/H5Cimage.c b/src/H5Cimage.c
new file mode 100644
index 0000000..dec8283
--- /dev/null
+++ b/src/H5Cimage.c
@@ -0,0 +1,4100 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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: H5Cimage.c
+ * July 20, 2015
+ * John Mainzer
+ *
+ * Purpose: Functions in this file are specific to the implementation
+ * of the metadata cache image feature.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/****************/
+/* Module Setup */
+/****************/
+
+#include "H5Cmodule.h" /* This source code file is part of the H5C module */
+#define H5F_FRIEND /*suppress error about including H5Fpkg */
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#ifdef H5_HAVE_PARALLEL
+#define H5AC_FRIEND /*suppress error about including H5ACpkg */
+#include "H5ACpkg.h" /* Metadata cache */
+#endif /* H5_HAVE_PARALLEL */
+#include "H5Cpkg.h" /* Cache */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5Fpkg.h" /* Files */
+#include "H5FDprivate.h" /* File drivers */
+#include "H5FLprivate.h" /* Free Lists */
+#include "H5MFprivate.h" /* File memory management */
+#include "H5MMprivate.h" /* Memory management */
+
+
+/****************/
+/* Local Macros */
+/****************/
+#if H5C_DO_MEMORY_SANITY_CHECKS
+#define H5C_IMAGE_EXTRA_SPACE 8
+#define H5C_IMAGE_SANITY_VALUE "DeadBeef"
+#else /* H5C_DO_MEMORY_SANITY_CHECKS */
+#define H5C_IMAGE_EXTRA_SPACE 0
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
+/* Cache image buffer components, on disk */
+#define H5C__MDCI_BLOCK_SIGNATURE "MDCI"
+#define H5C__MDCI_BLOCK_SIGNATURE_LEN 4
+#define H5C__MDCI_BLOCK_VERSION_0 0
+
+/* Metadata cache image header flags -- max 8 bits */
+#define H5C__MDCI_HEADER_HAVE_RESIZE_STATUS 0x01
+
+/* Metadata cache image entry flags -- max 8 bits */
+#define H5C__MDCI_ENTRY_DIRTY_FLAG 0x01
+#define H5C__MDCI_ENTRY_IN_LRU_FLAG 0x02
+#define H5C__MDCI_ENTRY_IS_FD_PARENT_FLAG 0x04
+#define H5C__MDCI_ENTRY_IS_FD_CHILD_FLAG 0x08
+
+/* Limits on flush dependency values, stored in 16-bit values on disk */
+#define H5C__MDCI_MAX_FD_CHILDREN USHRT_MAX
+#define H5C__MDCI_MAX_FD_PARENTS USHRT_MAX
+
+/* Values for image entry magic field */
+#define H5C_IMAGE_ENTRY_T_MAGIC 0x005CAC08
+#define H5C_IMAGE_ENTRY_T_BAD_MAGIC 0xBeefDead
+
+/* Maximum ring allowed in image */
+#define H5C_MAX_RING_IN_IMAGE 3
+
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+/* Helper routines */
+static size_t H5C__cache_image_block_entry_header_size(const H5F_t *f);
+static size_t H5C__cache_image_block_header_size(const H5F_t *f);
+static herr_t H5C__decode_cache_image_buffer(const H5F_t *f, H5C_t *cache_ptr);
+static herr_t H5C__decode_cache_image_header(const H5F_t *f,
+ H5C_t *cache_ptr, const uint8_t **buf);
+static herr_t H5C__decode_cache_image_entry(const H5F_t *f,
+ const H5C_t *cache_ptr, const uint8_t **buf, unsigned entry_num);
+static herr_t H5C__destroy_pf_entry_child_flush_deps(H5C_t *cache_ptr,
+ H5C_cache_entry_t *pf_entry_ptr, H5C_cache_entry_t **fd_children);
+static herr_t H5C__encode_cache_image_header(const H5F_t *f,
+ const H5C_t *cache_ptr, uint8_t **buf);
+static herr_t H5C__encode_cache_image_entry(H5F_t *f, H5C_t *cache_ptr,
+ uint8_t **buf, unsigned entry_num);
+static herr_t H5C__prep_for_file_close__compute_fd_heights(const H5C_t *cache_ptr);
+static void H5C__prep_for_file_close__compute_fd_heights_real(
+ H5C_cache_entry_t *entry_ptr, uint32_t fd_height);
+static herr_t H5C__prep_for_file_close__setup_image_entries_array(H5C_t *cache_ptr);
+static herr_t H5C__prep_for_file_close__scan_entries(const H5F_t *f,
+ H5C_t *cache_ptr);
+static herr_t H5C__reconstruct_cache_contents(H5F_t *f, hid_t dxpl_id,
+ H5C_t *cache_ptr);
+static H5C_cache_entry_t *H5C__reconstruct_cache_entry(H5C_t *cache_ptr,
+ unsigned index);
+static herr_t H5C__serialize_cache(H5F_t *f, hid_t dxpl_id);
+static herr_t H5C__serialize_ring(H5F_t *f, hid_t dxpl_id,
+ H5C_ring_t ring);
+static herr_t H5C__serialize_single_entry(H5F_t *f, hid_t dxpl_id,
+ H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr,
+ hbool_t *restart_list_scan_ptr);
+static herr_t H5C__write_cache_image_superblock_msg(H5F_t *f, hid_t dxpl_id,
+ hbool_t create);
+static herr_t H5C__read_cache_image(H5F_t * f, hid_t dxpl_id, const H5C_t *cache_ptr);
+static herr_t H5C__write_cache_image(H5F_t *f, hid_t dxpl_id, const H5C_t *cache_ptr);
+static herr_t H5C__construct_cache_image_buffer(H5F_t *f, H5C_t *cache_ptr);
+static herr_t H5C__free_image_entries_array(H5C_t *cache_ptr);
+
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+/* Declare a free list to manage H5C_cache_entry_t objects */
+H5FL_DEFINE(H5C_cache_entry_t);
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C_cache_image_status()
+ *
+ * Purpose: Examine the metadata cache associated with the supplied
+ * instance of H5F_t to determine whether the load of a
+ * cache image has either been queued ir executed, and if
+ * construction of a cache image has been requested.
+ *
+ * This done, it set *load_ci_ptr to TRUE if a cache image
+ * has either been loaded or a load has been requested, and
+ * to FALSE otherwise.
+ *
+ * Similarly, set *write_ci_ptr to TRUE if construction of
+ * a cache image has been requested, and to FALSE otherwise.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 12/29/16
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_cache_image_status(H5F_t * f, hbool_t *load_ci_ptr, hbool_t *write_ci_ptr)
+{
+ H5C_t * cache_ptr;
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ cache_ptr = f->shared->cache;
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(load_ci_ptr);
+ HDassert(write_ci_ptr);
+
+ *load_ci_ptr = cache_ptr->load_image || cache_ptr->image_loaded;
+ *write_ci_ptr = cache_ptr->image_ctl.generate_image;
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* H5C_cache_image_status() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__construct_cache_image_buffer()
+ *
+ * Purpose: Allocate a buffer of size cache_ptr->image_len, and
+ * load it with an image of the metadata cache image block.
+ *
+ * Note that by the time this function is called, the cache
+ * should have removed all entries from its data structures.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 8/5/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__construct_cache_image_buffer(H5F_t * f, H5C_t *cache_ptr)
+{
+ uint8_t * p; /* Pointer into image buffer */
+ uint32_t chksum;
+ unsigned u; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(cache_ptr == f->shared->cache);
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->close_warning_received);
+ HDassert(cache_ptr->image_ctl.generate_image);
+ HDassert(cache_ptr->num_entries_in_image > 0);
+ HDassert(cache_ptr->index_len == 0);
+ HDassert(cache_ptr->image_data_len > 0);
+ HDassert(cache_ptr->image_data_len <= cache_ptr->image_len);
+
+ /* Allocate the buffer in which to construct the cache image block */
+ if(NULL == (cache_ptr->image_buffer = H5MM_malloc(cache_ptr->image_len + 1)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for cache image buffer")
+
+ /* Construct the cache image block header image */
+ p = (uint8_t *)cache_ptr->image_buffer;
+ if(H5C__encode_cache_image_header(f, cache_ptr, &p) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTENCODE, FAIL, "header image construction failed")
+ HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) < cache_ptr->image_data_len);
+
+ /* Construct the cache entry images */
+ for(u = 0; u < cache_ptr->num_entries_in_image; u++)
+ if(H5C__encode_cache_image_entry(f, cache_ptr, &p, u) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTENCODE, FAIL, "entry image construction failed")
+ HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) < cache_ptr->image_data_len);
+
+ /* Construct the adaptive resize status image -- not yet */
+
+ /* Compute the checksum and encode */
+ chksum = H5_checksum_metadata(cache_ptr->image_buffer, (size_t)(cache_ptr->image_data_len - H5F_SIZEOF_CHKSUM), 0);
+ UINT32ENCODE(p, chksum);
+ HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) == cache_ptr->image_data_len);
+ HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) <= cache_ptr->image_len);
+
+#ifndef NDEBUG
+ /* validate the metadata cache image we just constructed by decoding it
+ * and comparing the result with the original data.
+ */
+ {
+ uint32_t old_chksum;
+ const uint8_t * q;
+ H5C_t * fake_cache_ptr = NULL;
+ unsigned v;
+ herr_t status; /* Status from decoding */
+
+ fake_cache_ptr = (H5C_t *)H5MM_malloc(sizeof(H5C_t));
+ HDassert(fake_cache_ptr);
+ fake_cache_ptr->magic = H5C__H5C_T_MAGIC;
+
+ /* needed for sanity checks */
+ fake_cache_ptr->image_len = cache_ptr->image_len;
+ q = (const uint8_t *)cache_ptr->image_buffer;
+ status = H5C__decode_cache_image_header(f, fake_cache_ptr, &q);
+ HDassert(status >= 0);
+
+ HDassert(NULL != p);
+ HDassert(fake_cache_ptr->num_entries_in_image == cache_ptr->num_entries_in_image);
+
+ fake_cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_malloc(sizeof(H5C_image_entry_t) *
+ (size_t)(fake_cache_ptr->num_entries_in_image + 1));
+ HDassert(fake_cache_ptr->image_entries);
+
+ for(u = 0; u < fake_cache_ptr->num_entries_in_image; u++) {
+ (fake_cache_ptr->image_entries)[u].magic = H5C_IMAGE_ENTRY_T_MAGIC;
+ (fake_cache_ptr->image_entries)[u].image_ptr = NULL;
+
+ /* touch up f->shared->cache to satisfy sanity checks... */
+ f->shared->cache = fake_cache_ptr;
+ status = H5C__decode_cache_image_entry(f, fake_cache_ptr, &q, u);
+ HDassert(status >= 0);
+
+ /* ...and then return f->shared->cache to its correct value */
+ f->shared->cache = cache_ptr;
+
+ /* verify expected contents */
+ HDassert((cache_ptr->image_entries)[u].addr == (fake_cache_ptr->image_entries)[u].addr);
+ HDassert((cache_ptr->image_entries)[u].size == (fake_cache_ptr->image_entries)[u].size);
+ HDassert((cache_ptr->image_entries)[u].type_id == (fake_cache_ptr->image_entries)[u].type_id);
+ HDassert((cache_ptr->image_entries)[u].lru_rank == (fake_cache_ptr->image_entries)[u].lru_rank);
+ HDassert((cache_ptr->image_entries)[u].is_dirty == (fake_cache_ptr->image_entries)[u].is_dirty);
+ /* don't check image_fd_height as it is not stored in
+ * the metadata cache image block.
+ */
+ HDassert((cache_ptr->image_entries)[u].fd_child_count == (fake_cache_ptr->image_entries)[u].fd_child_count);
+ HDassert((cache_ptr->image_entries)[u].fd_dirty_child_count == (fake_cache_ptr->image_entries)[u].fd_dirty_child_count);
+ HDassert((cache_ptr->image_entries)[u].fd_parent_count == (fake_cache_ptr->image_entries)[u].fd_parent_count);
+
+ for(v = 0; v < (cache_ptr->image_entries)[u].fd_parent_count; v++)
+ HDassert((cache_ptr->image_entries)[u].fd_parent_addrs[v] == (fake_cache_ptr->image_entries)[u].fd_parent_addrs[v]);
+
+ /* free the fd_parent_addrs array if it exists */
+ if((fake_cache_ptr->image_entries)[u].fd_parent_addrs) {
+ HDassert((fake_cache_ptr->image_entries)[u].fd_parent_count > 0);
+ (fake_cache_ptr->image_entries)[u].fd_parent_addrs = (haddr_t *)H5MM_xfree((fake_cache_ptr->image_entries)[u].fd_parent_addrs);
+ (fake_cache_ptr->image_entries)[u].fd_parent_count = 0;
+ } /* end if */
+ else
+ HDassert((fake_cache_ptr->image_entries)[u].fd_parent_count == 0);
+
+ HDassert((cache_ptr->image_entries)[u].image_ptr);
+ HDassert((fake_cache_ptr->image_entries)[u].image_ptr);
+ HDassert(!HDmemcmp((cache_ptr->image_entries)[u].image_ptr,
+ (fake_cache_ptr->image_entries)[u].image_ptr,
+ (cache_ptr->image_entries)[u].size));
+
+ (fake_cache_ptr->image_entries)[u].image_ptr = H5MM_xfree((fake_cache_ptr->image_entries)[u].image_ptr);
+ } /* end for */
+
+ HDassert((size_t)(q - (const uint8_t *)cache_ptr->image_buffer) == cache_ptr->image_data_len - H5F_SIZEOF_CHKSUM);
+
+ /* compute the checksum */
+ old_chksum = chksum;
+ chksum = H5_checksum_metadata(cache_ptr->image_buffer, (size_t)(cache_ptr->image_data_len - H5F_SIZEOF_CHKSUM), 0);
+ HDassert(chksum == old_chksum);
+
+ fake_cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_xfree(fake_cache_ptr->image_entries);
+ fake_cache_ptr = (H5C_t *)H5MM_xfree(fake_cache_ptr);
+ } /* end block */
+#endif /* NDEBUG */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__construct_cache_image_buffer() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__generate_cache_image()
+ *
+ * Purpose: Generate the cache image and write it to the file, if
+ * directed.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: Quincey Koziol
+ * 1/26/17
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C__generate_cache_image(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(cache_ptr == f->shared->cache);
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ /* Construct cache image */
+ if(H5C__construct_cache_image_buffer(f, cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't create metadata cache image")
+
+ /* Free image entries array */
+ if(H5C__free_image_entries_array(cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't free image entries array")
+
+ /* Write cache image block if so configured */
+ if(cache_ptr->image_ctl.flags & H5C_CI__GEN_MDC_IMAGE_BLK) {
+ if(H5C__write_cache_image(f, dxpl_id, cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't write metadata cache image block to file")
+
+ H5C__UPDATE_STATS_FOR_CACHE_IMAGE_CREATE(cache_ptr);
+ } /* end if */
+
+ /* Free cache image buffer */
+ HDassert(cache_ptr->image_buffer);
+ cache_ptr->image_buffer = H5MM_xfree(cache_ptr->image_buffer);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__generate_cache_image() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__deserialize_prefetched_entry()
+ *
+ * Purpose: Deserialize the supplied prefetched entry entry, and return
+ * a pointer to the deserialized entry in *entry_ptr_ptr.
+ * If successful, remove the prefetched entry from the cache,
+ * and free it. Insert the deserialized entry into the cache.
+ *
+ * Note that the on disk image of the entry is not freed --
+ * a pointer to it is stored in the deserialized entries'
+ * image_ptr field, and its image_up_to_date field is set to
+ * TRUE unless the entry is dirtied by the deserialize call.
+ *
+ * If the prefetched entry is a flush dependency child,
+ * destroy that flush dependency prior to calling the
+ * deserialize callback. If appropriate, the flush dependency
+ * relationship will be recreated by the cache client.
+ *
+ * If the prefetched entry is a flush dependency parent,
+ * destroy the flush dependency relationship with all its
+ * children. As all these children must be prefetched entries,
+ * recreate these flush dependency relationships with
+ * deserialized entry after it is inserted in the cache.
+ *
+ * Since deserializing a prefetched entry is semantically
+ * equivalent to a load, issue an entry loaded nofification
+ * if the notify callback is defined.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Note that *entry_ptr_ptr is undefined on failure.
+ *
+ * Programmer: John Mainzer, 8/10/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C__deserialize_prefetched_entry(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr,
+ H5C_cache_entry_t **entry_ptr_ptr, const H5C_class_t *type,
+ haddr_t addr, void *udata)
+{
+ hbool_t dirty = FALSE; /* Flag indicating whether thing was
+ * dirtied during deserialize
+ */
+ size_t len; /* Size of image in file */
+ void * thing = NULL; /* Pointer to thing loaded */
+ H5C_cache_entry_t * pf_entry_ptr; /* pointer to the prefetched entry */
+ /* supplied in *entry_ptr_ptr. */
+ H5C_cache_entry_t * ds_entry_ptr; /* Alias for thing loaded, as cache
+ * entry
+ */
+ H5C_cache_entry_t** fd_children = NULL; /* Pointer to a dynamically */
+ /* allocated array of pointers to */
+ /* the flush dependency children of */
+ /* the prefetched entry, or NULL if */
+ /* that array does not exist. */
+ unsigned flush_flags = (H5C__FLUSH_INVALIDATE_FLAG |
+ H5C__FLUSH_CLEAR_ONLY_FLAG);
+ int i;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(f->shared->cache);
+ HDassert(f->shared->cache == cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(entry_ptr_ptr);
+ HDassert(*entry_ptr_ptr);
+ pf_entry_ptr = *entry_ptr_ptr;
+ HDassert(pf_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(pf_entry_ptr->type);
+ HDassert(pf_entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID);
+ HDassert(pf_entry_ptr->prefetched);
+ HDassert(pf_entry_ptr->image_up_to_date);
+ HDassert(pf_entry_ptr->image_ptr);
+ HDassert(pf_entry_ptr->size > 0);
+ HDassert(pf_entry_ptr->addr == addr);
+ HDassert(type);
+ HDassert(type->id == pf_entry_ptr->prefetch_type_id);
+ HDassert(type->mem_type == cache_ptr->class_table_ptr[type->id]->mem_type);
+
+ /* verify absence of prohibited or unsupported type flag combinations */
+ HDassert(!(type->flags & H5C__CLASS_SKIP_READS));
+
+ /* Can't see how skip reads could be usefully combined with
+ * either the speculative read flag. Hence disallow.
+ */
+ HDassert(!((type->flags & H5C__CLASS_SKIP_READS) &&
+ (type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG)));
+
+ HDassert(H5F_addr_defined(addr));
+ HDassert(type->get_initial_load_size);
+ HDassert(type->deserialize);
+
+ /* if *pf_entry_ptr is a flush dependency child, destroy all such
+ * relationships now. The client will restore the relationship(s) with
+ * the deserialized entry if appropriate.
+ */
+ for(i = (int)(pf_entry_ptr->fd_parent_count) - 1; i >= 0; i--) {
+ HDassert(pf_entry_ptr->flush_dep_parent);
+ HDassert(pf_entry_ptr->flush_dep_parent[i]);
+ HDassert(pf_entry_ptr->flush_dep_parent[i]->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(pf_entry_ptr->flush_dep_parent[i]->flush_dep_nchildren > 0);
+ HDassert(pf_entry_ptr->fd_parent_addrs);
+ HDassert(pf_entry_ptr->flush_dep_parent[i]->addr == pf_entry_ptr->fd_parent_addrs[i]);
+
+ if(H5C_destroy_flush_dependency(pf_entry_ptr->flush_dep_parent[i], pf_entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "can't destroy pf entry parent flush dependency")
+
+ pf_entry_ptr->fd_parent_addrs[i] = HADDR_UNDEF;
+ } /* end for */
+ HDassert(pf_entry_ptr->flush_dep_nparents == 0);
+
+ /* If *pf_entry_ptr is a flush dependency parent, destroy its flush
+ * dependency relationships with all its children (which must be
+ * prefetched entries as well).
+ *
+ * These flush dependency relationships will have to be restored
+ * after the deserialized entry is inserted into the cache in order
+ * to transfer these relationships to the new entry. Hence save the
+ * pointers to the flush dependency children of *pf_enty_ptr for later
+ * use.
+ */
+ if(pf_entry_ptr->fd_child_count > 0) {
+ if(NULL == (fd_children = (H5C_cache_entry_t **)H5MM_calloc(sizeof(H5C_cache_entry_t **) * (size_t)(pf_entry_ptr->fd_child_count + 1))))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for fd child ptr array")
+
+ if(H5C__destroy_pf_entry_child_flush_deps(cache_ptr, pf_entry_ptr, fd_children) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "can't destroy pf entry child flush dependency(s).")
+ } /* end if */
+
+ /* Since the size of the on disk image is known exactly, there is
+ * no need for either a call to the get_initial_load_size() callback,
+ * or retries if the H5C__CLASS_SPECULATIVE_LOAD_FLAG flag is set.
+ * Similarly, there is no need to clamp possible reads beyond
+ * EOF.
+ */
+ len = pf_entry_ptr->size;
+
+ /* Deserialize the prefetched on-disk image of the entry into the
+ * native memory form
+ */
+ if(NULL == (thing = type->deserialize(pf_entry_ptr->image_ptr, len, udata, &dirty)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, FAIL, "Can't deserialize image")
+
+ ds_entry_ptr = (H5C_cache_entry_t *)thing;
+
+ /* In general, an entry should be clean just after it is loaded.
+ *
+ * However, when this code is used in the metadata cache, it is
+ * possible that object headers will be dirty at this point, as
+ * the deserialize function will alter object headers if necessary to
+ * fix an old bug.
+ *
+ * In the following assert:
+ *
+ * HDassert( ( dirty == FALSE ) || ( type->id == 5 || type->id == 6 ) );
+ *
+ * note that type ids 5 & 6 are associated with object headers in the
+ * metadata cache.
+ *
+ * When we get to using H5C for other purposes, we may wish to
+ * tighten up the assert so that the loophole only applies to the
+ * metadata cache.
+ *
+ * Note that at present, dirty can't be set to true with prefetched
+ * entries. However this may change, so include this functionality
+ * against that posibility.
+ *
+ * Also, note that it is possible for a prefetched entry to be dirty --
+ * hence the value assigned to ds_entry_ptr->is_dirty below.
+ */
+
+ HDassert( ( dirty == FALSE ) || ( type->id == 5 || type->id == 6) );
+
+ ds_entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC;
+ ds_entry_ptr->cache_ptr = f->shared->cache;
+ ds_entry_ptr->addr = addr;
+ ds_entry_ptr->size = len;
+ HDassert(ds_entry_ptr->size < H5C_MAX_ENTRY_SIZE);
+ ds_entry_ptr->image_ptr = pf_entry_ptr->image_ptr;
+ ds_entry_ptr->image_up_to_date = !dirty;
+ ds_entry_ptr->type = type;
+ ds_entry_ptr->is_dirty = dirty |
+ pf_entry_ptr->is_dirty;
+ ds_entry_ptr->dirtied = FALSE;
+ ds_entry_ptr->is_protected = FALSE;
+ ds_entry_ptr->is_read_only = FALSE;
+ ds_entry_ptr->ro_ref_count = 0;
+ ds_entry_ptr->is_pinned = FALSE;
+ ds_entry_ptr->in_slist = FALSE;
+ ds_entry_ptr->flush_marker = FALSE;
+#ifdef H5_HAVE_PARALLEL
+ ds_entry_ptr->clear_on_unprotect = FALSE;
+ ds_entry_ptr->flush_immediately = FALSE;
+ ds_entry_ptr->coll_access = FALSE;
+#endif /* H5_HAVE_PARALLEL */
+ ds_entry_ptr->flush_in_progress = FALSE;
+ ds_entry_ptr->destroy_in_progress = FALSE;
+
+ ds_entry_ptr->ring = pf_entry_ptr->ring;
+
+ /* Initialize flush dependency height fields */
+ ds_entry_ptr->flush_dep_parent = NULL;
+ ds_entry_ptr->flush_dep_nparents = 0;
+ ds_entry_ptr->flush_dep_parent_nalloc = 0;
+ ds_entry_ptr->flush_dep_nchildren = 0;
+ ds_entry_ptr->flush_dep_ndirty_children = 0;
+ ds_entry_ptr->flush_dep_nunser_children = 0;
+
+ /* Initialize fields supporting the hash table: */
+ ds_entry_ptr->ht_next = NULL;
+ ds_entry_ptr->ht_prev = NULL;
+ ds_entry_ptr->il_next = NULL;
+ ds_entry_ptr->il_prev = NULL;
+
+ /* Initialize fields supporting replacement policies: */
+ ds_entry_ptr->next = NULL;
+ ds_entry_ptr->prev = NULL;
+ ds_entry_ptr->aux_next = NULL;
+ ds_entry_ptr->aux_prev = NULL;
+#ifdef H5_HAVE_PARALLEL
+ pf_entry_ptr->coll_next = NULL;
+ pf_entry_ptr->coll_prev = NULL;
+#endif /* H5_HAVE_PARALLEL */
+
+ /* initialize cache image related fields */
+ ds_entry_ptr->include_in_image = FALSE;
+ ds_entry_ptr->lru_rank = 0;
+ ds_entry_ptr->image_dirty = FALSE;
+ ds_entry_ptr->fd_parent_count = 0;
+ ds_entry_ptr->fd_parent_addrs = NULL;
+ ds_entry_ptr->fd_child_count = pf_entry_ptr->fd_child_count;
+ ds_entry_ptr->fd_dirty_child_count = 0;
+ ds_entry_ptr->image_fd_height = 0;
+ ds_entry_ptr->prefetched = FALSE;
+ ds_entry_ptr->prefetch_type_id = 0;
+ ds_entry_ptr->age = 0;
+
+ H5C__RESET_CACHE_ENTRY_STATS(ds_entry_ptr);
+
+ /* Apply to to the newly deserialized entry */
+ if(H5C__tag_entry(cache_ptr, ds_entry_ptr, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "Cannot tag metadata entry")
+
+ /* We have successfully deserialized the prefetched entry.
+ *
+ * Before we return a pointer to the deserialized entry, we must remove
+ * the prefetched entry from the cache, discard it, and replace it with
+ * the deserialized entry. Note that we do not free the prefetched
+ * entries image, as that has been transferred to the deserialized
+ * entry.
+ *
+ * Also note that we have not yet restored any flush dependencies. This
+ * must wait until the deserialized entry is inserted in the cache.
+ *
+ * To delete the prefetched entry from the cache:
+ *
+ * 1) Set pf_entry_ptr->image_ptr to NULL. Since we have already
+ * transferred the buffer containing the image to *ds_entry_ptr,
+ * this is not a memory leak.
+ *
+ * 2) Call H5C__flush_single_entry() with the H5C__FLUSH_INVALIDATE_FLAG
+ * and H5C__FLUSH_CLEAR_ONLY_FLAG flags set.
+ */
+ pf_entry_ptr->image_ptr = NULL;
+ if ( pf_entry_ptr->is_dirty ) {
+ HDassert(pf_entry_ptr->in_slist);
+ flush_flags |= H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG;
+ } /* end if */
+
+ if(H5C__flush_single_entry(f, dxpl_id, pf_entry_ptr, flush_flags) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, "can't expunge prefetched entry")
+
+#ifndef NDEGUG /* verify deletion */
+ H5C__SEARCH_INDEX(cache_ptr, addr, pf_entry_ptr, FAIL);
+
+ HDassert(NULL == pf_entry_ptr);
+#endif /* NDEBUG */
+
+ /* Insert the deserialized entry into the cache. */
+ H5C__INSERT_IN_INDEX(cache_ptr, ds_entry_ptr, FAIL)
+
+ HDassert(!ds_entry_ptr->in_slist);
+
+ if(ds_entry_ptr->is_dirty)
+ H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, ds_entry_ptr, FAIL)
+
+ H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, ds_entry_ptr, FAIL)
+
+ /* Deserializing a prefetched entry is the conceptual equivalent of
+ * loading it from file. If the deserialized entry has a notify callback,
+ * send an "after load" notice now that the deserialized entry is fully
+ * integrated into the cache.
+ */
+ if(ds_entry_ptr->type->notify &&
+ (ds_entry_ptr->type->notify)(H5C_NOTIFY_ACTION_AFTER_LOAD, ds_entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry loaded into cache")
+
+ /* restore flush dependencies with the flush dependency children of
+ * of the prefetched entry. Note that we must protect *ds_entry_ptr
+ * before the call to avoid triggering sanity check failures, and
+ * then unprotect it afterwards.
+ */
+ i = 0;
+ if(fd_children != NULL) {
+ int j;
+ hbool_t found;
+
+ H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, ds_entry_ptr, FAIL)
+ ds_entry_ptr->is_protected = TRUE;
+ while(fd_children[i] != NULL) {
+ /* Sanity checks */
+ HDassert((fd_children[i])->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert((fd_children[i])->prefetched);
+ HDassert((fd_children[i])->fd_parent_count > 0);
+ HDassert((fd_children[i])->fd_parent_addrs);
+
+ j = 0;
+ found = FALSE;
+ while((j < (int)((fd_children[i])->fd_parent_count)) && (!found)) {
+ if((fd_children[i])->fd_parent_addrs[j] == ds_entry_ptr->addr)
+ found = TRUE;
+
+ j++;
+ } /* end while */
+ HDassert(found);
+
+ if(H5C_create_flush_dependency(ds_entry_ptr, fd_children[i]) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDEPEND, FAIL, "Can't restore child flush dependency.")
+
+ i++;
+ } /* end while */
+
+ H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, ds_entry_ptr, FAIL);
+
+ ds_entry_ptr->is_protected = FALSE;
+ } /* end if ( fd_children != NULL ) */
+ HDassert((unsigned)i == ds_entry_ptr->fd_child_count);
+
+ ds_entry_ptr->fd_child_count = 0;
+ H5C__UPDATE_STATS_FOR_PREFETCH_HIT(cache_ptr)
+
+ /* finally, pass ds_entry_ptr back to the caller */
+ *entry_ptr_ptr = ds_entry_ptr;
+
+done:
+ if(fd_children)
+ fd_children = (H5C_cache_entry_t **)H5MM_xfree((void *)fd_children);
+
+ /* Release resources on error */
+ if(FAIL == ret_value)
+ if(thing && type->free_icr(thing) < 0)
+ HDONE_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "free_icr callback failed")
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__deserialize_prefetched_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__free_image_entries_array
+ *
+ * Purpose: If the image entries array exists, free the image
+ * associated with each entry, and then free the image
+ * entries array proper.
+ *
+ * Note that by the time this function is called, the cache
+ * should have removed all entries from its data structures.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 8/4/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__free_image_entries_array(H5C_t * cache_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->close_warning_received);
+ HDassert(cache_ptr->image_ctl.generate_image);
+ HDassert(cache_ptr->index_len == 0);
+
+ /* Check for entries to free */
+ if(cache_ptr->image_entries != NULL) {
+ unsigned u; /* Local index variable */
+
+ for(u = 0; u < cache_ptr->num_entries_in_image; u++) {
+ H5C_image_entry_t *ie_ptr; /* Image entry to release */
+
+ /* Get pointer to image entry */
+ ie_ptr = &((cache_ptr->image_entries)[u]);
+
+ /* Sanity checks */
+ HDassert(ie_ptr);
+ HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC);
+ HDassert(ie_ptr->image_ptr);
+
+ /* Free the parent addrs array if appropriate */
+ if(ie_ptr->fd_parent_addrs) {
+ HDassert(ie_ptr->fd_parent_count > 0);
+
+ ie_ptr->fd_parent_addrs = (haddr_t *)H5MM_xfree(ie_ptr->fd_parent_addrs);
+ } /* end if */
+ else
+ HDassert(ie_ptr->fd_parent_count == 0);
+
+ /* Free the image */
+ ie_ptr->image_ptr = H5MM_xfree(ie_ptr->image_ptr);
+
+ /* Set magic field to bad magic so we can detect freed entries */
+ ie_ptr->magic = H5C_IMAGE_ENTRY_T_BAD_MAGIC;
+ } /* end for */
+
+ /* Free the image entries array */
+ cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_xfree(cache_ptr->image_entries);
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* H5C__free_image_entries_array() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C_get_cache_image_config
+ *
+ * Purpose: Copy the current configuration for cache image generation
+ * on file close into the instance of H5C_cache_image_ctl_t
+ * pointed to by config_ptr.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 7/3/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_get_cache_image_config(const H5C_t * cache_ptr,
+ H5C_cache_image_ctl_t *config_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ if((cache_ptr == NULL) || (cache_ptr->magic != H5C__H5C_T_MAGIC))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad cache_ptr on entry")
+ if(config_ptr == NULL)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad config_ptr on entry")
+
+ *config_ptr = cache_ptr->image_ctl;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_get_cache_image_config() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C_image_stats
+ *
+ * Purpose: Prints statistics specific to the cache image.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 10/26/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+#if H5C_COLLECT_CACHE_STATS
+H5C_image_stats(H5C_t * cache_ptr, hbool_t print_header)
+#else /* H5C_COLLECT_CACHE_STATS */
+H5C_image_stats(H5C_t * cache_ptr, hbool_t H5_ATTR_UNUSED print_header)
+#endif /* H5C_COLLECT_CACHE_STATS */
+{
+#if H5C_COLLECT_CACHE_STATS
+ int i;
+ int64_t total_hits = 0;
+ int64_t total_misses = 0;
+ double hit_rate;
+ double prefetch_use_rate;
+#endif /* H5C_COLLECT_CACHE_STATS */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ if(!cache_ptr || cache_ptr->magic != H5C__H5C_T_MAGIC)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr")
+
+#if H5C_COLLECT_CACHE_STATS
+ for(i = 0; i <= cache_ptr->max_type_id; i++) {
+ total_hits += cache_ptr->hits[i];
+ total_misses += cache_ptr->misses[i];
+ } /* end for */
+
+ if((total_hits > 0) || (total_misses > 0))
+ hit_rate = (double)100.0f * ((double)(total_hits)) / ((double)(total_hits + total_misses));
+ else
+ hit_rate = 0.0f;
+
+ if(cache_ptr->prefetches > 0)
+ prefetch_use_rate = (double)100.0f * ((double)(cache_ptr->prefetch_hits)) /
+ ((double)(cache_ptr->prefetches));
+ else
+ prefetch_use_rate = 0.0f;
+
+ if(print_header) {
+ HDfprintf(stdout,
+ "\nhit prefetches prefetch image pf hit\n");
+ HDfprintf(stdout,
+ "rate: total: dirty: hits: flshs: evct: size: rate:\n");
+ } /* end if */
+
+ HDfprintf(stdout,
+ "%3.1lf %5lld %5lld %5lld %5lld %5lld %5lld %3.1lf\n",
+ hit_rate,
+ (long long)(cache_ptr->prefetches),
+ (long long)(cache_ptr->dirty_prefetches),
+ (long long)(cache_ptr->prefetch_hits),
+ (long long)(cache_ptr->flushes[H5AC_PREFETCHED_ENTRY_ID]),
+ (long long)(cache_ptr->evictions[H5AC_PREFETCHED_ENTRY_ID]),
+ (long long)(cache_ptr->last_image_size),
+ prefetch_use_rate);
+#endif /* H5C_COLLECT_CACHE_STATS */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_image_stats() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__read_cache_image
+ *
+ * Purpose: Load the metadata cache image from the specified location
+ * in the file, and return it in the supplied buffer.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 8/16/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C__read_cache_image(H5F_t *f, hid_t dxpl_id, const H5C_t *cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(cache_ptr);
+ HDassert(H5F_addr_defined(cache_ptr->image_addr));
+ HDassert(cache_ptr->image_len > 0);
+ HDassert(cache_ptr->image_buffer);
+
+#ifdef H5_HAVE_PARALLEL
+{
+ H5AC_aux_t *aux_ptr = (H5AC_aux_t *)cache_ptr->aux_ptr;
+ int mpi_result;
+
+ if((NULL == aux_ptr) || (aux_ptr->mpi_rank == 0)) {
+ HDassert((NULL == aux_ptr) || (aux_ptr->magic == H5AC__H5AC_AUX_T_MAGIC));
+#endif /* H5_HAVE_PARALLEL */
+
+ /* Read the buffer (if serial access, or rank 0 of parallel access) */
+ if(H5F_block_read(f, H5FD_MEM_SUPER, cache_ptr->image_addr, cache_ptr->image_len, dxpl_id, cache_ptr->image_buffer) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_READERROR, FAIL, "Can't read metadata cache image block")
+
+#ifdef H5_HAVE_PARALLEL
+ if(aux_ptr) {
+ /* Broadcast cache image */
+ if(MPI_SUCCESS != (mpi_result = MPI_Bcast(cache_ptr->image_buffer, (int)cache_ptr->image_len, MPI_BYTE, 0, aux_ptr->mpi_comm)))
+ HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed", mpi_result)
+ } /* end if */
+ } /* end if */
+ else if(aux_ptr) {
+ /* Retrieve the contents of the metadata cache image from process 0 */
+ if(MPI_SUCCESS != (mpi_result = MPI_Bcast(cache_ptr->image_buffer, (int)cache_ptr->image_len, MPI_BYTE, 0, aux_ptr->mpi_comm)))
+ HMPI_GOTO_ERROR(FAIL, "can't receive cache image MPI_Bcast", mpi_result)
+ } /* end else-if */
+} /* end block */
+#endif /* H5_HAVE_PARALLEL */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__read_cache_image() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__load_cache_image
+ *
+ * Purpose: Read the cache image superblock extension message and
+ * delete it if so directed.
+ *
+ * Then load the cache image block at the specified location,
+ * decode it, and insert its contents into the metadata
+ * cache.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 7/6/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C__load_cache_image(H5F_t *f, hid_t dxpl_id)
+{
+ H5C_t * cache_ptr;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ cache_ptr = f->shared->cache;
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ /* If the image address is defined, load the image, decode it,
+ * and insert its contents into the metadata cache.
+ *
+ * Note that under normal operating conditions, it is an error if the
+ * image address is HADDR_UNDEF. However, to facilitate testing,
+ * we allow this special value of the image address which means that
+ * no image exists, and that the load operation should be skipped
+ * silently.
+ */
+ if(H5F_addr_defined(cache_ptr->image_addr)) {
+ /* Sanity checks */
+ HDassert(cache_ptr->image_len > 0);
+ HDassert(cache_ptr->image_buffer == NULL);
+
+ /* Allocate space for the image */
+ if(NULL == (cache_ptr->image_buffer = H5MM_malloc(cache_ptr->image_len + 1)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for cache image buffer")
+
+ /* Load the image from file */
+ if(H5C__read_cache_image(f, dxpl_id, cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_READERROR, FAIL, "Can't read metadata cache image block")
+
+ /* Decode metadata cache image */
+ if(H5C__decode_cache_image_buffer(f, cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDECODE, FAIL, "Can't decode metadata cache image block")
+
+ /* At this point, the image_data_len should be known */
+ HDassert(cache_ptr->image_data_len > 0);
+ HDassert(cache_ptr->image_data_len <= cache_ptr->image_len);
+
+ /* Insert image contents into cache */
+ if(H5C__reconstruct_cache_contents(f, dxpl_id, cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDECODE, FAIL, "Can't reconstruct cache contents from image block")
+
+ /* Free the image buffer */
+ cache_ptr->image_buffer = H5MM_xfree(cache_ptr->image_buffer);
+
+ /* Update stats -- must do this now, as we are about
+ * to discard the size of the cache image.
+ */
+ H5C__UPDATE_STATS_FOR_CACHE_IMAGE_LOAD(cache_ptr)
+
+ /* Free the image entries array. Note that all on disk image
+ * image buffers and fd parent address arrays have been transferred
+ * to their respective prefetched entries so we can just free the
+ * array of H5C_image_entry_t.
+ */
+#ifndef NDEBUG
+ {
+ unsigned u;
+
+ for(u = 0; u < cache_ptr->num_entries_in_image; u++) {
+ H5C_image_entry_t * ie_ptr;
+
+ ie_ptr = &((cache_ptr->image_entries)[u]);
+
+ HDassert(ie_ptr);
+ HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC);
+ HDassert(ie_ptr->fd_parent_addrs == NULL);
+ HDassert(ie_ptr->image_ptr == NULL);
+ } /* end for */
+ } /* end block */
+#endif /* NDEBUG */
+
+ cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_xfree(cache_ptr->image_entries);
+ cache_ptr->num_entries_in_image = 0;
+
+ cache_ptr->image_loaded = TRUE;
+ } /* end if */
+
+ /* If directed, free the on disk metadata cache image */
+ if(cache_ptr->delete_image) {
+ if(H5F_super_ext_remove_msg(f, dxpl_id, H5O_MDCI_MSG_ID) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove metadata cache image message from superblock extension")
+
+ /* Reset image block values */
+ cache_ptr->image_len = 0;
+ cache_ptr->image_data_len = 0;
+ cache_ptr->image_addr = HADDR_UNDEF;
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__load_cache_image() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C_load_cache_image_on_next_protect()
+ *
+ * Purpose: Note the fact that a metadata cache image superblock
+ * extension message exists, along with the base address
+ * and length of the metadata cache image block.
+ *
+ * Once this notification is received the metadata cache
+ * image block must be read, decoded, and loaded into the
+ * cache on the next call to H5C_protect().
+ *
+ * Further, if the file is opened R/W, the metadata cache
+ * image superblock extension message must be deleted from
+ * the superblock extension and the image block freed
+ *
+ * Contrawise, if the file is openened R/O, the metadata
+ * cache image superblock extension message and image block
+ * must be left as is. Further, any dirty entries in the
+ * cache image block must be marked as clean to avoid
+ * attempts to write them on file close.
+ *
+ * Return: SUCCEED
+ *
+ * Programmer: John Mainzer
+ * 7/6/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_load_cache_image_on_next_protect(H5F_t *f, haddr_t addr, hsize_t len,
+ hbool_t rw)
+{
+ H5C_t *cache_ptr;
+
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ cache_ptr = f->shared->cache;
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ /* Set information needed to load cache image */
+ cache_ptr->image_addr = addr,
+ cache_ptr->image_len = len;
+ cache_ptr->load_image = TRUE;
+ cache_ptr->delete_image = rw;
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* H5C_load_cache_image_on_next_protect() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__image_entry_cmp
+ *
+ * Purpose: Comparison callback for qsort(3) on image entries.
+ * Entries are sorted first by flush dependency height,
+ * and then by LRU rank.
+ *
+ * Note: Entries with a _greater_ flush dependency height should
+ * be sorted earlier than entries with lower heights, since
+ * leafs in the flush dependency graph are at height 0, and their
+ * parents need to be earlier in the image, so that they can
+ * construct their flush dependencies when decoded.
+ *
+ * Return: An integer less than, equal to, or greater than zero if the
+ * first entry is considered to be respectively less than,
+ * equal to, or greater than the second.
+ *
+ * Programmer: Quincey Koziol
+ * 1/20/16
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+H5C__image_entry_cmp(const void *_entry1, const void *_entry2)
+{
+ const H5C_image_entry_t *entry1 = (const H5C_image_entry_t *)_entry1; /* Pointer to first image entry to compare */
+ const H5C_image_entry_t *entry2 = (const H5C_image_entry_t *)_entry2; /* Pointer to second image entry to compare */
+ int ret_value = 0; /* Return value */
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Sanity checks */
+ HDassert(entry1);
+ HDassert(entry2);
+
+ if(entry1->image_fd_height > entry2->image_fd_height)
+ ret_value = -1;
+ else if(entry1->image_fd_height < entry2->image_fd_height)
+ ret_value = 1;
+ else {
+ /* Sanity check */
+ HDassert(entry1->lru_rank >= -1);
+ HDassert(entry2->lru_rank >= -1);
+
+ if(entry1->lru_rank < entry2->lru_rank)
+ ret_value = -1;
+ else if(entry1->lru_rank > entry2->lru_rank)
+ ret_value = 1;
+ } /* end else */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__image_entry_cmp() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__prep_image_for_file_close
+ *
+ * Purpose: The objective of the call is to allow the metadata cache
+ * to do any preparatory work prior to generation of a
+ * cache image.
+ *
+ * In particular, the cache must
+ *
+ * 1) serialize all its entries,
+ *
+ * 2) compute the size of the metadata cache image,
+ *
+ * 3) allocate space for the metadata cache image, and
+ *
+ * 4) setup the metadata cache image superblock extension
+ * message with the address and size of the metadata
+ * cache image.
+ *
+ * The parallel case is complicated by the fact that
+ * while all metadata caches must contain the same set of
+ * dirty entries, there is no such requirement for clean
+ * entries or the order that entries appear in the LRU.
+ *
+ * Thus, there is no requirement that different processes
+ * will construct cache images of the same size.
+ *
+ * This is not a major issue as long as all processes include
+ * the same set of dirty entries in the cache -- as they
+ * currently do (note that this will change when we implement
+ * the ageout feature). Since only the process zero cache
+ * writes the cache image, all that is necessary is to
+ * broadcast the process zero cache size for use in the
+ * superblock extension messages and cache image block
+ * allocations.
+ *
+ * Note: At present, cache image is disabled in the
+ * parallel case as the new collective metadata write
+ * code must be modified to support cache image.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 7/3/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C__prep_image_for_file_close(H5F_t *f, hid_t dxpl_id)
+{
+ H5C_t * cache_ptr = NULL;
+ haddr_t eoa_frag_addr = HADDR_UNDEF;
+ hsize_t eoa_frag_size = 0;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_PACKAGE
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(f->shared->cache);
+ cache_ptr = f->shared->cache;
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ /* Create the cache image super block extension message.
+ *
+ * Note that the base address and length of the metadata cache
+ * image are undefined at this point, and thus will have to be
+ * updated later.
+ *
+ * Create the super block extension message now so that space
+ * is allocated for it (if necessary) before we allocate space
+ * for the cache image block.
+ *
+ * To simplify testing, do this only if the
+ * H5C_CI__GEN_MDCI_SBE_MESG bit is set in
+ * cache_ptr->image_ctl.flags.
+ */
+ if(cache_ptr->image_ctl.flags & H5C_CI__GEN_MDCI_SBE_MESG)
+ if(H5C__write_cache_image_superblock_msg(f, dxpl_id, TRUE) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "creation of cache image SB mesg failed.")
+
+ /* Serialize the cache */
+ if(H5C__serialize_cache(f, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "serialization of the cache failed")
+
+ /* Scan the cache and record data needed to construct the
+ * cache image. In particular, for each entry we must record:
+ *
+ * 1) rank in LRU (if entry is in LRU)
+ *
+ * 2) Whether the entry is dirty prior to flush of
+ * cache just prior to close.
+ *
+ * 3) Addresses of flush dependency parents (if any).
+ *
+ * 4) Number of flush dependency children (if any).
+ *
+ * In passing, also compute the size of the metadata cache
+ * image. With the recent modifications of the free space
+ * manager code, this size should be correct.
+ */
+ if(H5C__prep_for_file_close__scan_entries(f, cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C__prep_for_file_close__scan_entries failed")
+ HDassert(HADDR_UNDEF == cache_ptr->image_addr);
+
+#ifdef H5_HAVE_PARALLEL
+ /* In the parallel case, overwrite the image_len with the
+ * value computed by process 0.
+ */
+ if(cache_ptr->aux_ptr) { /* we have multiple processes */
+ int mpi_result;
+ unsigned p0_image_len;
+ H5AC_aux_t * aux_ptr;
+
+ aux_ptr = (H5AC_aux_t *)cache_ptr->aux_ptr;
+ if(aux_ptr->mpi_rank == 0) {
+ aux_ptr->p0_image_len = (unsigned)cache_ptr->image_data_len;
+ p0_image_len = aux_ptr->p0_image_len;
+
+ if(MPI_SUCCESS != (mpi_result = MPI_Bcast(&p0_image_len, 1, MPI_UNSIGNED, 0, aux_ptr->mpi_comm)))
+ HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed", mpi_result)
+
+ HDassert(p0_image_len == aux_ptr->p0_image_len);
+ } /* end if */
+ else {
+ if(MPI_SUCCESS != (mpi_result = MPI_Bcast(&p0_image_len, 1, MPI_UNSIGNED, 0, aux_ptr->mpi_comm)))
+ HMPI_GOTO_ERROR(FAIL, "MPI_Bcast failed", mpi_result)
+
+ aux_ptr->p0_image_len = p0_image_len;
+ } /* end else */
+
+ /* Allocate space for a cache image of size equal to that
+ * computed by the process 0. This may be different from
+ * cache_ptr->image_data_len if mpi_rank != 0. However, since
+ * cache image write is suppressed on all processes other than
+ * process 0, this doesn't matter.
+ *
+ * Note that we allocate the cache image directly from the file
+ * driver so as to avoid unsettling the free space managers.
+ */
+ if(HADDR_UNDEF == (cache_ptr->image_addr = H5FD_alloc(f->shared->lf, dxpl_id, H5FD_MEM_SUPER, f,
+ (hsize_t)p0_image_len, &eoa_frag_addr, &eoa_frag_size)))
+ HGOTO_ERROR(H5E_CACHE, H5E_NOSPACE, FAIL, "can't allocate file space for metadata cache image")
+ } /* end if */
+ else
+#endif /* H5_HAVE_PARALLEL */
+ /* Allocate the cache image block. Note that we allocate this
+ * this space directly from the file driver so as to avoid
+ * unsettling the free space managers.
+ */
+ if(HADDR_UNDEF == (cache_ptr->image_addr = H5FD_alloc(f->shared->lf, dxpl_id, H5FD_MEM_SUPER, f,
+ (hsize_t)(cache_ptr->image_data_len), &eoa_frag_addr, &eoa_frag_size)))
+ HGOTO_ERROR(H5E_CACHE, H5E_NOSPACE, FAIL, "can't allocate file space for metadata cache image")
+
+ /* For now, drop any fragment left over from the allocation of the
+ * image block on the ground. A fragment should only be returned
+ * if the underlying file alignment is greater than 1.
+ *
+ * Clean this up eventually by extending the size of the cache
+ * image block to the next alignement boundary, and then setting
+ * the image_data_len to the actual size of the cache_image.
+ *
+ * On the off chance that there is some other way to get a
+ * a fragment on a cache image allocation, leave the following
+ * assertion in the code so we will find out.
+ */
+ HDassert((eoa_frag_size == 0) || (f->shared->alignment != 1));
+
+ /* Eventually it will be possible for the length of the cache image
+ * block on file to be greater than the size of the data it
+ * contains. However, for now they must be the same. Set
+ * cache_ptr->image_len accordingly.
+ */
+ cache_ptr->image_len = cache_ptr->image_data_len;
+
+ /* update the metadata cache image superblock extension
+ * message with the new cache image block base address and
+ * length.
+ *
+ * to simplify testing, do this only if the
+ * H5C_CI__GEN_MDC_IMAGE_BLK bit is set in
+ * cache_ptr->image_ctl.flags.
+ */
+ if(cache_ptr->image_ctl.flags & H5C_CI__GEN_MDC_IMAGE_BLK)
+ if(H5C__write_cache_image_superblock_msg(f, dxpl_id, FALSE) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "update of cache image SB mesg failed.")
+
+ /* At this point:
+ *
+ * 1) space in the file for the metadata cache image
+ * is allocated,
+ *
+ * 2) the metadata cache image superblock extension
+ * message exists and (if so configured) contains
+ * the correct data,
+ *
+ * 3) All entries in the cache that will appear in the
+ * cache image are serialized with up to date images.
+ *
+ * Since we just updated the cache image message,
+ * the super block extension message is dirty. However,
+ * since the superblock and the superblock extension
+ * can't be included in the cache image, this is a non-
+ * issue.
+ *
+ * 4) All entries in the cache that will be include in
+ * the cache are marked as such, and we have a count
+ * of same.
+ *
+ * 5) Flush dependency heights are calculated for all
+ * entries that will be included in the cache image.
+ *
+ * If there are any entries to be included in the metadata cache
+ * image, allocate, populate, and sort the image_entries array.
+ *
+ * If the metadata cache image will be empty, delete the
+ * metadata cache image superblock extension message, set
+ * cache_ptr->image_ctl.generate_image to FALSE. This will
+ * allow the file close to continue normally without the
+ * unecessary generation of the metadata cache image.
+ */
+ if(cache_ptr->num_entries_in_image > 0) {
+ if(H5C__prep_for_file_close__setup_image_entries_array(cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTINIT, FAIL, "can't setup image entries array.")
+
+ /* Sort the entries */
+ HDqsort(cache_ptr->image_entries, (size_t)cache_ptr->num_entries_in_image,
+ sizeof(H5C_image_entry_t), H5C__image_entry_cmp);
+ } /* end if */
+ else { /* cancel creation of metadata cache iamge */
+ HDassert(cache_ptr->image_entries == NULL);
+
+ /* To avoid breaking the control flow tests, only delete
+ * the mdci superblock extension message if the
+ * H5C_CI__GEN_MDC_IMAGE_BLK flag is set in
+ * cache_ptr->image_ctl.flags.
+ */
+ if(cache_ptr->image_ctl.flags & H5C_CI__GEN_MDC_IMAGE_BLK)
+ if(H5F_super_ext_remove_msg(f, dxpl_id, H5O_MDCI_MSG_ID) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove MDC image msg from superblock ext.")
+
+ cache_ptr->image_ctl.generate_image = FALSE;
+ } /* end else */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__prep_image_for_file_close() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C_set_cache_image_config
+ *
+ * Purpose: If *config_ptr contains valid data, copy it into the
+ * image_ctl field of *cache_ptr. Make adjustments for
+ * changes in configuration as required.
+ *
+ * Fail if the new configuration is invalid.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 7/3/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_set_cache_image_config(const H5F_t *f, H5C_t *cache_ptr,
+ H5C_cache_image_ctl_t *config_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(f->shared->cache == f->shared->cache);
+
+ /* Check arguments */
+ if((cache_ptr == NULL) || (cache_ptr->magic != H5C__H5C_T_MAGIC))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad cache_ptr on entry")
+ if(config_ptr == NULL)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "NULL config_ptr on entry")
+ if(config_ptr->version != H5C__CURR_CACHE_IMAGE_CTL_VER)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Unknown config version")
+
+ /* check general configuration section of the config: */
+ if(H5C_validate_cache_image_config(config_ptr) < 0)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "invalid cache image configuration")
+
+ if(H5F_INTENT(f) & H5F_ACC_RDWR) /* file has been opened R/W */
+ cache_ptr->image_ctl = *config_ptr;
+ else { /* file opened R/O -- suppress cache image silently */
+ H5C_cache_image_ctl_t default_image_ctl = H5C__DEFAULT_CACHE_IMAGE_CTL;
+
+ cache_ptr->image_ctl = default_image_ctl;
+ HDassert(!(cache_ptr->image_ctl.generate_image));
+ } /* end else */
+
+#ifdef H5_HAVE_PARALLEL
+ /* the collective metadata write code is not currently compatible
+ * with cache image. Until this is fixed, suppress cache image silently
+ * if there is more than one process.
+ * JRM -- 11/8/16
+ */
+ if(cache_ptr->aux_ptr) {
+ H5C_cache_image_ctl_t default_image_ctl = H5C__DEFAULT_CACHE_IMAGE_CTL;
+
+ cache_ptr->image_ctl = default_image_ctl;
+ HDassert(!(cache_ptr->image_ctl.generate_image));
+ } /* end if */
+#endif /* H5_HAVE_PARALLEL */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_set_cache_image_config() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C_validate_cache_image_config()
+ *
+ * Purpose: Run a sanity check on the provided instance of struct
+ * H5AC_cache_image_config_t.
+ *
+ * Do nothing and return SUCCEED if no errors are detected,
+ * and flag an error and return FAIL otherwise.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/15/15
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5C_validate_cache_image_config(H5C_cache_image_ctl_t * ctl_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(FAIL)
+
+ if(ctl_ptr == NULL)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "NULL ctl_ptr on entry")
+ if(ctl_ptr->version != H5C__CURR_CACHE_IMAGE_CTL_VER)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown cache image control version")
+
+ /* At present, we do not support inclusion of the adaptive resize
+ * configuration in the cache image. Thus the save_resize_status
+ * field must be FALSE.
+ */
+ if(ctl_ptr->save_resize_status != FALSE)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "unexpected value in save_resize_status field")
+
+ /* At present, we do not support prefetched entry ageouts. Thus
+ * the entry_ageout field must be set to
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE.
+ */
+ if(ctl_ptr->entry_ageout != H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "unexpected value in entry_ageout field")
+
+ if((ctl_ptr->flags & ~H5C_CI__ALL_FLAGS) != 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "unknown flag set")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C_validate_cache_image_config() */
+
+
+/*************************************************************************/
+/**************************** Private Functions: *************************/
+/*************************************************************************/
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__cache_image_block_entry_header_size
+ *
+ * Purpose: Compute the size of the header of the metadata cache
+ * image block, and return the value.
+ *
+ * Return: Size of the header section of the metadata cache image
+ * block in bytes.
+ *
+ * Programmer: John Mainzer
+ * 7/27/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static size_t
+H5C__cache_image_block_entry_header_size(const H5F_t * f)
+{
+ size_t ret_value = 0; /* Return value */
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Set return value */
+ ret_value = (size_t)( 1 + /* type */
+ 1 + /* flags */
+ 1 + /* ring */
+ 1 + /* age */
+ 2 + /* dependency child count */
+ 2 + /* dirty dep child count */
+ 2 + /* dependency parent count */
+ 4 + /* index in LRU */
+ H5F_SIZEOF_ADDR(f) + /* entry offset */
+ H5F_SIZEOF_SIZE(f) ); /* entry length */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__cache_image_block_entry_header_size() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__cache_image_block_header_size
+ *
+ * Purpose: Compute the size of the header of the metadata cache
+ * image block, and return the value.
+ *
+ * Return: Size of the header section of the metadata cache image
+ * block in bytes.
+ *
+ * Programmer: John Mainzer
+ * 7/27/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static size_t
+H5C__cache_image_block_header_size(const H5F_t * f)
+{
+ size_t ret_value = 0; /* Return value */
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Set return value */
+ ret_value = (size_t)( 4 + /* signature */
+ 1 + /* version */
+ 1 + /* flags */
+ H5F_SIZEOF_SIZE(f) + /* image data length */
+ 4 ); /* num_entries */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__cache_image_block_header_size() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__decode_cache_image_buffer()
+ *
+ * Purpose: Allocate a suitably size array of instances of
+ * H5C_image_entry_t and and set cache_ptr->image_entries
+ * to point to this array. Set cache_ptr->num_entries_in_image
+ * equal to the number of entries in this array.
+ *
+ * Decode the contents of cache_ptr->image_buffer into the
+ * array.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 8/9/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__decode_cache_image_buffer(const H5F_t *f, H5C_t *cache_ptr)
+{
+ uint32_t read_chksum;
+ uint32_t computed_chksum;
+ const uint8_t * p;
+ unsigned u; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(cache_ptr == f->shared->cache);
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->image_buffer);
+ HDassert(cache_ptr->image_len > 0);
+ HDassert(cache_ptr->image_data_len == 0);
+ HDassert(cache_ptr->image_entries == NULL);
+ HDassert(cache_ptr->num_entries_in_image == 0);
+
+ /* Decode metadata cache image header */
+ p = (uint8_t *)cache_ptr->image_buffer;
+ if(H5C__decode_cache_image_header(f, cache_ptr, &p) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDECODE, FAIL, "cache image header decode failed")
+ HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) < cache_ptr->image_len);
+
+ /* cache_ptr->image_data_len should be defined now */
+ HDassert(cache_ptr->image_data_len > 0);
+ HDassert(cache_ptr->image_data_len <= cache_ptr->image_len);
+
+ /* We should now have cache_ptr->num_entries_in_image -- allocate the
+ * image entries array.
+ */
+ HDassert(cache_ptr->num_entries_in_image > 0);
+ if(NULL == (cache_ptr->image_entries = (H5C_image_entry_t *)H5MM_malloc(sizeof(H5C_image_entry_t) * (size_t)(cache_ptr->num_entries_in_image + 1))))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed image entries array")
+
+ /* Load the image entries */
+ for(u = 0; u < cache_ptr->num_entries_in_image; u++) {
+ (cache_ptr->image_entries)[u].magic = H5C_IMAGE_ENTRY_T_MAGIC;
+ (cache_ptr->image_entries)[u].image_fd_height = 0;
+ (cache_ptr->image_entries)[u].image_ptr = NULL;
+
+ if(H5C__decode_cache_image_entry(f, cache_ptr, &p, u) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDECODE, FAIL, "entry image decode failed")
+ } /* end for */
+ HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) < cache_ptr->image_len);
+
+ /* (Load the adaptive cache resize status -- not yet) */
+
+ /* Verify the checksum */
+ UINT32DECODE(p, read_chksum);
+ HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) == cache_ptr->image_data_len);
+ HDassert((size_t)(p - (uint8_t *)cache_ptr->image_buffer) <= cache_ptr->image_len);
+ computed_chksum = H5_checksum_metadata(cache_ptr->image_buffer, (size_t)(cache_ptr->image_data_len - H5F_SIZEOF_CHKSUM), 0);
+ if(read_chksum != computed_chksum)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "bad checksum on metadata cache image block")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__decode_cache_image_buffer() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__decode_cache_image_header()
+ *
+ * Purpose: Decode the metadata cache image buffer header from the
+ * supplied buffer and load the data into the supplied instance
+ * of H5C_t. Advances the buffer pointer to the first byte
+ * after the header image, or unchanged on failure.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 8/6/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__decode_cache_image_header(const H5F_t *f, H5C_t *cache_ptr,
+ const uint8_t **buf)
+{
+ uint8_t version;
+ uint8_t flags;
+ hbool_t have_resize_status = FALSE;
+ size_t actual_header_len;
+ size_t expected_header_len;
+ const uint8_t * p;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(buf);
+ HDassert(*buf);
+
+ /* Point to buffer to decode */
+ p = *buf;
+
+ /* Check signature */
+ if(HDmemcmp(p, H5C__MDCI_BLOCK_SIGNATURE, (size_t)H5C__MDCI_BLOCK_SIGNATURE_LEN))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad metadata cache image header signature")
+ p += H5C__MDCI_BLOCK_SIGNATURE_LEN;
+
+ /* Check version */
+ version = *p++;
+ if(version != (uint8_t)H5C__MDCI_BLOCK_VERSION_0)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad metadata cache image version")
+
+ /* Decode flags */
+ flags = *p++;
+ if(flags & H5C__MDCI_HEADER_HAVE_RESIZE_STATUS)
+ have_resize_status = TRUE;
+ if(have_resize_status)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "MDC resize status not yet supported")
+
+ /* Read image data length */
+ H5F_DECODE_LENGTH(f, p, cache_ptr->image_data_len);
+
+ /* For now -- will become <= eventually */
+ if(cache_ptr->image_data_len != cache_ptr->image_len)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad metadata cache image data length")
+
+ /* Read num entries */
+ UINT32DECODE(p, cache_ptr->num_entries_in_image);
+ if(cache_ptr->num_entries_in_image == 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad metadata cache entry count")
+
+ /* Verify expected length of header */
+ actual_header_len = (size_t)(p - *buf);
+ expected_header_len = H5C__cache_image_block_header_size(f);
+ if(actual_header_len != expected_header_len)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad header image len.")
+
+ /* Update buffer pointer */
+ *buf = p;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__decode_cache_image_header() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__decode_cache_image_entry()
+ *
+ * Purpose: Decode the metadata cache image entry from the supplied
+ * buffer into the supplied instance of H5C_image_entry_t.
+ * This includes allocating a buffer for the entry image,
+ * loading it, and seting ie_ptr->image_ptr to point to
+ * the buffer.
+ *
+ * Advances the buffer pointer to the first byte
+ * after the entry, or unchanged on failure.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 8/6/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__decode_cache_image_entry(const H5F_t *f, const H5C_t *cache_ptr,
+ const uint8_t **buf, unsigned entry_num)
+{
+ hbool_t is_dirty = FALSE;
+#ifndef NDEBUG /* only used in assertions */
+ hbool_t in_lru = FALSE;
+ hbool_t is_fd_parent = FALSE;
+ hbool_t is_fd_child = FALSE;
+#endif /* NDEBUG */ /* only used in assertions */
+ haddr_t addr;
+ hsize_t size = 0;
+ void * image_ptr;
+ uint8_t flags = 0;
+ uint8_t type_id;
+ uint8_t ring;
+ uint8_t age;
+ uint16_t fd_child_count;
+ uint16_t fd_dirty_child_count;
+ uint16_t fd_parent_count;
+ haddr_t * fd_parent_addrs = NULL;
+ int32_t lru_rank;
+ H5C_image_entry_t * ie_ptr = NULL;
+ const uint8_t * p;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(cache_ptr == f->shared->cache);
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(buf);
+ HDassert(*buf);
+ HDassert(entry_num < cache_ptr->num_entries_in_image);
+ ie_ptr = &((cache_ptr->image_entries)[entry_num]);
+ HDassert(ie_ptr);
+ HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC);
+
+ /* Get pointer to buffer */
+ p = *buf;
+
+ /* Decode type id */
+ type_id = *p++;
+
+ /* Decode flags */
+ flags = *p++;
+ if(flags & H5C__MDCI_ENTRY_DIRTY_FLAG)
+ is_dirty = TRUE;
+#ifndef NDEBUG /* only used in assertions */
+ if(flags & H5C__MDCI_ENTRY_IN_LRU_FLAG)
+ in_lru = TRUE;
+ if(flags & H5C__MDCI_ENTRY_IS_FD_PARENT_FLAG)
+ is_fd_parent = TRUE;
+ if(flags & H5C__MDCI_ENTRY_IS_FD_CHILD_FLAG)
+ is_fd_child = TRUE;
+#endif /* NDEBUG */ /* only used in assertions */
+
+ /* Decode ring */
+ ring = *p++;
+ HDassert(ring > (uint8_t)(H5C_RING_UNDEFINED));
+ HDassert(ring < (uint8_t)(H5C_RING_NTYPES));
+
+ /* Decode age */
+ age = *p++;
+
+ /* Decode dependency child count */
+ UINT16DECODE(p, fd_child_count);
+ HDassert((is_fd_parent && fd_child_count > 0) || (!is_fd_parent && fd_child_count == 0));
+
+ /* Decode dirty dependency child count */
+ UINT16DECODE(p, fd_dirty_child_count);
+ if(fd_dirty_child_count > fd_child_count)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid dirty flush dependency child count")
+
+ /* Decode dependency parent count */
+ UINT16DECODE(p, fd_parent_count);
+ HDassert((is_fd_child && fd_parent_count > 0) || (!is_fd_child && fd_parent_count == 0));
+
+ /* Decode index in LRU */
+ INT32DECODE(p, lru_rank);
+ HDassert((in_lru && lru_rank >= 0) || (!in_lru && lru_rank == -1));
+
+ /* Decode entry offset */
+ H5F_addr_decode(f, &p, &addr);
+ if(!H5F_addr_defined(addr))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid entry offset")
+
+ /* Decode entry length */
+ H5F_DECODE_LENGTH(f, p, size);
+ if(size == 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid entry size")
+
+ /* Verify expected length of entry image */
+ if((size_t)(p - *buf) != H5C__cache_image_block_entry_header_size(f))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADSIZE, FAIL, "Bad entry image len")
+
+ /* If parent count greater than zero, allocate array for parent
+ * addresses, and decode addresses into the array.
+ */
+ if(fd_parent_count > 0) {
+ int i; /* Local index variable */
+
+ if(NULL == (fd_parent_addrs = (haddr_t *)H5MM_malloc((size_t)(fd_parent_count) * H5F_SIZEOF_ADDR(f))))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for fd parent addrs buffer")
+
+ for(i = 0; i < fd_parent_count; i++) {
+ H5F_addr_decode(f, &p, &(fd_parent_addrs[i]));
+ if(!H5F_addr_defined(fd_parent_addrs[i]))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid flush dependency parent offset")
+ } /* end for */
+ } /* end if */
+
+ /* Allocate buffer for entry image */
+ if(NULL == (image_ptr = H5MM_malloc(size + H5C_IMAGE_EXTRA_SPACE)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer")
+
+#if H5C_DO_MEMORY_SANITY_CHECKS
+ HDmemcpy(((uint8_t *)image_ptr) + size, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
+ /* Copy the entry image from the cache image block */
+ HDmemcpy(image_ptr, p, size);
+ p += size;
+
+ /* Copy data into target */
+ ie_ptr->addr = addr;
+ ie_ptr->size = size;
+ ie_ptr->ring = (H5C_ring_t)ring;
+ ie_ptr->age = (int32_t)age;
+ ie_ptr->type_id = (int32_t)type_id;
+ ie_ptr->lru_rank = lru_rank;
+ ie_ptr->is_dirty = is_dirty;
+ ie_ptr->fd_child_count = (uint64_t)fd_child_count;
+ ie_ptr->fd_dirty_child_count = (uint64_t)fd_dirty_child_count;
+ ie_ptr->fd_parent_count = (uint64_t)fd_parent_count;
+ ie_ptr->fd_parent_addrs = fd_parent_addrs;
+ ie_ptr->image_ptr = image_ptr;
+
+ /* Update buffer pointer */
+ *buf = p;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__decode_cache_image_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__destroy_pf_entry_child_flush_deps()
+ *
+ * Purpose: Destroy all flush dependencies in this the supplied
+ * prefetched entry is the parent. Note that the children
+ * in these flush dependencies must be prefetched entries as
+ * well.
+ *
+ * As this action is part of the process of transferring all
+ * such flush dependencies to the deserialized version of the
+ * prefetched entry, ensure that the data necessary to complete
+ * the transfer is retained.
+ *
+ * Note: The current implementation of this function is
+ * quite inefficient -- mostly due to the current
+ * implementation of flush dependencies. This should
+ * be fixed at some point.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 8/11/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__destroy_pf_entry_child_flush_deps(H5C_t *cache_ptr,
+ H5C_cache_entry_t *pf_entry_ptr, H5C_cache_entry_t **fd_children)
+{
+ H5C_cache_entry_t * entry_ptr;
+ unsigned entries_visited = 0;
+ int fd_children_found = 0;
+ hbool_t found;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(pf_entry_ptr);
+ HDassert(pf_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(pf_entry_ptr->type);
+ HDassert(pf_entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID);
+ HDassert(pf_entry_ptr->prefetched);
+ HDassert(pf_entry_ptr->fd_child_count > 0);
+ HDassert(fd_children);
+
+ /* Scan each entry on the index list */
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL) {
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ /* Here we look at entry_ptr->flush_dep_nparents and not
+ * entry_ptr->fd_parent_count as it is possible that some
+ * or all of the prefetched flush dependency child relationships
+ * have already been destroyed.
+ */
+ if(entry_ptr->prefetched && (entry_ptr->flush_dep_nparents > 0)) {
+ unsigned u; /* Local index variable */
+
+ /* Re-init */
+ u = 0;
+ found = FALSE;
+
+ /* Sanity checks */
+ HDassert(entry_ptr->type);
+ HDassert(entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID);
+ HDassert(entry_ptr->fd_parent_count >= entry_ptr->flush_dep_nparents);
+ HDassert(entry_ptr->fd_parent_addrs);
+ HDassert(entry_ptr->flush_dep_parent);
+
+ /* Look for correct entry */
+ while(!found && (u < entry_ptr->fd_parent_count)) {
+ /* Sanity check entry */
+ HDassert(entry_ptr->flush_dep_parent[u]);
+ HDassert(entry_ptr->flush_dep_parent[u]->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ /* Correct entry? */
+ if(pf_entry_ptr == entry_ptr->flush_dep_parent[u])
+ found = TRUE;
+
+ u++;
+ } /* end while */
+
+ if(found) {
+ HDassert(NULL == fd_children[fd_children_found]);
+
+ /* Remove flush dependency */
+ fd_children[fd_children_found] = entry_ptr;
+ fd_children_found++;
+ if(H5C_destroy_flush_dependency(pf_entry_ptr, entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "can't destroy pf entry child flush dependency")
+
+#ifndef NDEBUG
+ /* Sanity check -- verify that the address of the parent
+ * appears in entry_ptr->fd_parent_addrs. Must do a search,
+ * as with flush dependency creates and destroys,
+ * entry_ptr->fd_parent_addrs and entry_ptr->flush_dep_parent
+ * can list parents in different order.
+ */
+ found = FALSE;
+ u = 0;
+ while(!found && u < entry_ptr->fd_parent_count) {
+ if(pf_entry_ptr->addr == entry_ptr->fd_parent_addrs[u])
+ found = TRUE;
+ u++;
+ } /* end while */
+ HDassert(found);
+#endif /* NDEBUG */
+ } /* end if */
+ } /* end if */
+
+ entries_visited++;
+ entry_ptr = entry_ptr->il_next;
+ } /* end while */
+
+ /* Post-op sanity checks */
+ HDassert(NULL == fd_children[fd_children_found]);
+ HDassert((unsigned)fd_children_found == pf_entry_ptr->fd_child_count);
+ HDassert(entries_visited == cache_ptr->index_len);
+ HDassert(!pf_entry_ptr->is_pinned);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__destroy_pf_entry_child_flush_deps() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__encode_cache_image_header()
+ *
+ * Purpose: Encode the metadata cache image buffer header in the
+ * supplied buffer. Updates buffer pointer to the first byte
+ * after the header image in the buffer, or unchanged on failure.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 8/6/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__encode_cache_image_header(const H5F_t *f, const H5C_t *cache_ptr,
+ uint8_t **buf)
+{
+ size_t actual_header_len;
+ size_t expected_header_len;
+ uint8_t flags = 0;
+ uint8_t * p; /* Pointer into cache image buffer */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->close_warning_received);
+ HDassert(cache_ptr->image_ctl.generate_image);
+ HDassert(cache_ptr->index_len == 0);
+ HDassert(cache_ptr->image_data_len > 0);
+ HDassert(cache_ptr->image_data_len <= cache_ptr->image_len);
+ HDassert(buf);
+ HDassert(*buf);
+
+ /* Set pointer into buffer */
+ p = *buf;
+
+ /* write signature */
+ HDmemcpy(p, H5C__MDCI_BLOCK_SIGNATURE, (size_t)H5C__MDCI_BLOCK_SIGNATURE_LEN);
+ p += H5C__MDCI_BLOCK_SIGNATURE_LEN;
+
+ /* write version */
+ *p++ = (uint8_t)H5C__MDCI_BLOCK_VERSION_0;
+
+ /* setup and write flags */
+
+ /* at present we don't support saving resize status */
+ HDassert(!cache_ptr->image_ctl.save_resize_status);
+ if(cache_ptr->image_ctl.save_resize_status)
+ flags |= H5C__MDCI_HEADER_HAVE_RESIZE_STATUS;
+
+ *p++ = flags;
+
+ /* Encode image data length */
+ /* this must be true at present */
+ HDassert(cache_ptr->image_len == cache_ptr->image_data_len);
+ H5F_ENCODE_LENGTH(f, p, cache_ptr->image_data_len);
+
+ /* write num entries */
+ UINT32ENCODE(p, cache_ptr->num_entries_in_image);
+
+ /* verify expected length of header */
+ actual_header_len = (size_t)(p - *buf);
+ expected_header_len = H5C__cache_image_block_header_size(f);
+ if(actual_header_len != expected_header_len)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad header image len")
+
+ /* Update buffer pointer */
+ *buf = p;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__encode_cache_image_header() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__encode_cache_image_entry()
+ *
+ * Purpose: Encode the metadata cache image buffer header in the
+ * supplied buffer. Updates buffer pointer to the first byte
+ * after the entry in the buffer, or unchanged on failure.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 8/6/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__encode_cache_image_entry(H5F_t *f, H5C_t *cache_ptr, uint8_t **buf,
+ unsigned entry_num)
+{
+ H5C_image_entry_t * ie_ptr; /* Pointer to entry to encode */
+ uint8_t flags = 0; /* Flags for entry */
+ uint8_t * p; /* Pointer into cache image buffer */
+ unsigned u; /* Local index value */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(cache_ptr == f->shared->cache);
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->close_warning_received);
+ HDassert(cache_ptr->image_ctl.generate_image);
+ HDassert(cache_ptr->index_len == 0);
+ HDassert(buf);
+ HDassert(*buf);
+ HDassert(entry_num < cache_ptr->num_entries_in_image);
+ ie_ptr = &((cache_ptr->image_entries)[entry_num]);
+ HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC);
+
+ /* Get pointer to buffer to encode into */
+ p = *buf;
+
+ /* Encode type */
+ if((ie_ptr->type_id < 0) || (ie_ptr->type_id > 255))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADRANGE, FAIL, "type_id out of range.")
+ *p++ = (uint8_t)(ie_ptr->type_id);
+
+ /* Compose and encode flags */
+ if(ie_ptr->is_dirty)
+ flags |= H5C__MDCI_ENTRY_DIRTY_FLAG;
+ if(ie_ptr->lru_rank > 0)
+ flags |= H5C__MDCI_ENTRY_IN_LRU_FLAG;
+ if(ie_ptr->fd_child_count > 0)
+ flags |= H5C__MDCI_ENTRY_IS_FD_PARENT_FLAG;
+ if(ie_ptr->fd_parent_count > 0)
+ flags |= H5C__MDCI_ENTRY_IS_FD_CHILD_FLAG;
+ *p++ = flags;
+
+ /* Encode ring */
+ *p++ = (uint8_t)(ie_ptr->ring);
+
+ /* Encode age */
+ *p++ = (uint8_t)(ie_ptr->age);
+
+ /* Validate and encode dependency child count */
+ if(ie_ptr->fd_child_count > H5C__MDCI_MAX_FD_CHILDREN)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADRANGE, FAIL, "fd_child_count out of range")
+ UINT16ENCODE(p, (uint16_t)(ie_ptr->fd_child_count));
+
+ /* Validate and encode dirty dependency child count */
+ if(ie_ptr->fd_dirty_child_count > H5C__MDCI_MAX_FD_CHILDREN)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADRANGE, FAIL, "fd_dirty_child_count out of range")
+ UINT16ENCODE(p, (uint16_t)(ie_ptr->fd_dirty_child_count));
+
+ /* Validate and encode dependency parent count */
+ if(ie_ptr->fd_parent_count > H5C__MDCI_MAX_FD_PARENTS)
+ HGOTO_ERROR(H5E_CACHE, H5E_BADRANGE, FAIL, "fd_parent_count out of rang.")
+ UINT16ENCODE(p, (uint16_t)(ie_ptr->fd_parent_count));
+
+ /* Encode index in LRU */
+ INT32ENCODE(p, ie_ptr->lru_rank);
+
+ /* Encode entry offset */
+ H5F_addr_encode(f, &p, ie_ptr->addr);
+
+ /* Encode entry length */
+ H5F_ENCODE_LENGTH(f, p, ie_ptr->size);
+
+ /* Verify expected length of entry image */
+ if((size_t)(p - *buf) != H5C__cache_image_block_entry_header_size(f))
+ HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "Bad entry image len")
+
+ /* Encode dependency parent offsets -- if any */
+ for(u = 0; u < ie_ptr->fd_parent_count; u++)
+ H5F_addr_encode(f, &p, ie_ptr->fd_parent_addrs[u]);
+
+ /* Copy entry image */
+ HDmemcpy(p, ie_ptr->image_ptr, ie_ptr->size);
+ p += ie_ptr->size;
+
+ /* Update buffer pointer */
+ *buf = p;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__encode_cache_image_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__prep_for_file_close__compute_fd_heights
+ *
+ * Purpose: Recent modifications to flush dependency support in the
+ * metadata cache have removed the notion of flush dependency
+ * height. This is a problem for the cache image feature,
+ * as flush dependency height is used to order entries in the
+ * cache image so that flush dependency parents appear before
+ * flush dependency children. (Recall that the flush dependency
+ * height of an entry in a flush dependency relationship is the
+ * length of the longest path from the entry to a leaf entry --
+ * that is an entry with flush dependency parents, but no
+ * flush dependency children. With the introduction of the
+ * possibility of multiple flush dependency parents, we have
+ * a flush partial dependency latice, not a flush dependency
+ * tree. But since the partial latice is acyclic, the concept
+ * of flush dependency height still makes sense.
+ *
+ * The purpose of this function is to compute the flush
+ * dependency height of all entries that appear in the cache
+ * image.
+ *
+ * At present, entries are included or excluded from the
+ * cache image depending upon the ring in which they reside.
+ * Thus there is no chance that one side of a flush dependency
+ * will be in the cache image, and the other side not.
+ *
+ * However, once we start placing a limit on the size of the
+ * cache image, or start excluding prefetched entries from
+ * the cache image if they haven't been accessed in some
+ * number of file close / open cycles, this will no longer
+ * be the case.
+ *
+ * In particular, if a flush dependency child is dirty, and
+ * one of its flush dependency parents is dirty and not in
+ * the cache image, then the flush dependency child cannot
+ * be in the cache image without violating flush ordering.
+ *
+ * Observe that a clean flush dependency child can be either
+ * in or out of the cache image without effect on flush
+ * dependencies.
+ *
+ * Similarly, a flush dependency parent can always be part
+ * of a cache image, regardless of whether it is clean or
+ * dirty -- but remember that a flush dependency parent can
+ * also be a flush dependency child.
+ *
+ * Finally, note that for purposes of the cache image, flush
+ * dependency height ends when a flush dependecy relation
+ * passes off the cache image.
+ *
+ * On exit, the flush dependency height of each entry in the
+ * cache image should be calculated and stored in the cache
+ * entry. Entries will be removed from the cache image if
+ * necessary to maintain flush ordering.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 9/6/16
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__prep_for_file_close__compute_fd_heights(const H5C_t *cache_ptr)
+{
+ H5C_cache_entry_t * entry_ptr;
+ H5C_cache_entry_t * parent_ptr;
+ unsigned entries_removed_from_image = 0;
+ unsigned external_parent_fd_refs_removed = 0;
+ unsigned external_child_fd_refs_removed = 0;
+ hbool_t done = FALSE;
+ unsigned u; /* Local index variable */
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ /* sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+
+ /* Remove from the cache image all dirty entries that are
+ * flush dependency children of dirty entries that are not in the
+ * cache image. Must do this, as if we fail to do so, the parent
+ * will be written to file before the child. Since it is possible
+ * that the child will have dirty children of its own, this may take
+ * multiple passes through the index list.
+ */
+ done = FALSE;
+ while(!done) {
+ done = TRUE;
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL) {
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ /* Should this entry be in the image */
+ if(entry_ptr->image_dirty && entry_ptr->include_in_image &&
+ (entry_ptr->fd_parent_count > 0)) {
+ HDassert(entry_ptr->flush_dep_parent != NULL);
+ for(u = 0; u < entry_ptr->flush_dep_nparents; u++ ) {
+ parent_ptr = entry_ptr->flush_dep_parent[u];
+
+ /* Sanity check parent */
+ HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->ring == parent_ptr->ring);
+
+ if(parent_ptr->is_dirty && !parent_ptr->include_in_image &&
+ entry_ptr->include_in_image) {
+
+ /* Must remove child from image -- only do this once */
+ entries_removed_from_image++;
+ entry_ptr->include_in_image = FALSE;
+ } /* end if */
+ } /* for */
+ } /* end if */
+
+ entry_ptr = entry_ptr->il_next;
+ } /* while ( entry_ptr != NULL ) */
+ } /* while ( ! done ) */
+
+ /* at present, entries are included in the cache image if they reside
+ * in a specified set of rings. Thus it should be impossible for
+ * entries_removed_from_image to be positive. Assert that this is
+ * so. Note that this will change when we start aging entries out
+ * of the cache image.
+ */
+ HDassert(entries_removed_from_image == 0);
+
+ /* Next, remove from entries in the cache image, references to
+ * flush dependency parents or children that are not in the cache image.
+ */
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL) {
+ if(!entry_ptr->include_in_image && entry_ptr->flush_dep_nparents > 0) {
+ HDassert(entry_ptr->flush_dep_parent != NULL);
+
+ for(u = 0; u < entry_ptr->flush_dep_nparents; u++ ) {
+ parent_ptr = entry_ptr->flush_dep_parent[u];
+
+ /* Sanity check parent */
+ HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->ring == parent_ptr->ring);
+
+ if(parent_ptr->include_in_image) {
+ /* Must remove reference to child */
+ HDassert(parent_ptr->fd_child_count > 0);
+ parent_ptr->fd_child_count--;
+
+ if(entry_ptr->is_dirty) {
+ HDassert(parent_ptr->fd_dirty_child_count > 0);
+ parent_ptr->fd_dirty_child_count--;
+ } /* end if */
+
+ external_child_fd_refs_removed++;
+ } /* end if */
+ } /* for */
+ } /* end if */
+ else if(entry_ptr->include_in_image && entry_ptr->flush_dep_nparents > 0) {
+ /* Sanity checks */
+ HDassert(entry_ptr->flush_dep_parent != NULL);
+ HDassert(entry_ptr->flush_dep_nparents == entry_ptr->fd_parent_count);
+ HDassert(entry_ptr->fd_parent_addrs);
+
+ for(u = 0; u < entry_ptr->flush_dep_nparents; u++ ) {
+ parent_ptr = entry_ptr->flush_dep_parent[u];
+
+ /* Sanity check parent */
+ HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->ring == parent_ptr->ring);
+
+ if(!parent_ptr->include_in_image) {
+ /* Must remove reference to parent */
+ HDassert(entry_ptr->fd_parent_count > 0);
+ parent_ptr->fd_child_count--;
+
+ HDassert(parent_ptr->addr == entry_ptr->fd_parent_addrs[u]);
+
+ entry_ptr->fd_parent_addrs[u] = HADDR_UNDEF;
+ external_parent_fd_refs_removed++;
+ } /* end if */
+ } /* for */
+
+ /* Touch up fd_parent_addrs array if necessary */
+ if(entry_ptr->fd_parent_count == 0) {
+ H5MM_xfree(entry_ptr->fd_parent_addrs);
+ entry_ptr->fd_parent_addrs = NULL;
+ } /* end if */
+ else if(entry_ptr->flush_dep_nparents > entry_ptr->fd_parent_count) {
+ haddr_t * old_fd_parent_addrs = entry_ptr->fd_parent_addrs;
+ unsigned v;
+
+ if(NULL == (entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_calloc(sizeof(haddr_t) * (size_t)(entry_ptr->fd_parent_addrs))))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for fd parent addr array")
+
+ v = 0;
+ for(u = 0; u < entry_ptr->flush_dep_nparents; u++) {
+ if(old_fd_parent_addrs[u] != HADDR_UNDEF) {
+ entry_ptr->fd_parent_addrs[v] = old_fd_parent_addrs[u];
+ v++;
+ } /* end if */
+ } /* end for */
+
+ HDassert(v == entry_ptr->fd_parent_count);
+ } /* end else-if */
+ } /* end else-if */
+
+ entry_ptr = entry_ptr->il_next;
+ } /* while (entry_ptr != NULL) */
+
+ /* At present, no extenal parent or child flush dependency links
+ * should exist -- hence the following assertions. This will change
+ * if we support ageout of entries in the cache image.
+ */
+ HDassert(external_child_fd_refs_removed == 0);
+ HDassert(external_parent_fd_refs_removed == 0);
+
+ /* At this point we should have removed all flush dependencies that
+ * cross cache image boundaries. Now compute the flush dependency
+ * heights for all entries in the image.
+ *
+ * Until I can think of a better way, do this via a depth first
+ * search implemented via a recursive function call.
+ *
+ * Note that entry_ptr->image_fd_height has already been initialized to 0
+ * for all entries that may appear in the cache image.
+ */
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL ) {
+ if(entry_ptr->include_in_image && entry_ptr->fd_child_count == 0 &&
+ entry_ptr->fd_parent_count > 0) {
+ for(u = 0; u < entry_ptr->fd_parent_count; u++ ) {
+ parent_ptr = entry_ptr->flush_dep_parent[u];
+
+ HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ if(parent_ptr->include_in_image && parent_ptr->image_fd_height <= 0)
+ H5C__prep_for_file_close__compute_fd_heights_real(parent_ptr, 1);
+ } /* end for */
+ } /* end if */
+
+ entry_ptr = entry_ptr->il_next;
+ } /* while (entry_ptr != NULL) */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__prep_for_file_close__compute_fd_heights() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__prep_for_file_close__compute_fd_heights_real
+ *
+ * Purpose: H5C__prep_for_file_close__compute_fd_heights() prepares
+ * for the computation of flush dependency heights of all
+ * entries in the cache image, this function actually does
+ * it.
+ *
+ * The basic observation behind this function is as follows:
+ *
+ * Suppose you have an entry E with a flush dependency
+ * height of X. Then the parents of E must all have
+ * flush dependency X + 1 or greater.
+ *
+ * Use this observation to compute flush dependency height
+ * of all entries in the cache image via the following
+ * recursive algorithm:
+ *
+ * 1) On entry, set the flush dependency height of the
+ * supplied cache entry to the supplied value.
+ *
+ * 2) Examine all the flush dependency parents of the
+ * supplied entry.
+ *
+ * If the parent is in the cache image, and has flush
+ * dependency height less than or equal to the flush
+ * dependency height of the current entry, call the
+ * recursive routine on the parent with flush dependency
+ * height equal to the flush dependency height of the
+ * child plus 1.
+ *
+ * Otherwise do nothing.
+ *
+ * Observe that if the flush dependency height of all entries
+ * in the image is initialized to zero, and if this recursive
+ * function is called with flush dependency height 0 on all
+ * entries in the cache image with FD parents in the image,
+ * but without FD children in the image, the correct flush
+ * dependency height should be set for all entries in the
+ * cache image.
+ *
+ * Return: void
+ *
+ * Programmer: John Mainzer
+ * 9/6/16
+ *
+ *-------------------------------------------------------------------------
+ */
+static void
+H5C__prep_for_file_close__compute_fd_heights_real(H5C_cache_entry_t *entry_ptr,
+ uint32_t fd_height)
+{
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Sanity checks */
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->include_in_image);
+ HDassert((entry_ptr->image_fd_height == 0) || (entry_ptr->image_fd_height < fd_height));
+ HDassert(((fd_height == 0) && (entry_ptr->fd_child_count == 0)) || ((fd_height > 0) && (entry_ptr->fd_child_count > 0)));
+
+ entry_ptr->image_fd_height = fd_height;
+ if(entry_ptr->flush_dep_nparents > 0) {
+ unsigned u;
+
+ HDassert(entry_ptr->flush_dep_parent);
+ for(u = 0; u < entry_ptr->fd_parent_count; u++) {
+ H5C_cache_entry_t *parent_ptr;
+
+ parent_ptr = entry_ptr->flush_dep_parent[u];
+ HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ if(parent_ptr->include_in_image && parent_ptr->image_fd_height <= fd_height)
+ H5C__prep_for_file_close__compute_fd_heights_real(parent_ptr, fd_height + 1);
+ } /* end for */
+ } /* end if */
+
+ FUNC_LEAVE_NOAPI_VOID
+} /* H5C__prep_for_file_close__compute_fd_heights_real() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__prep_for_file_close__setup_image_entries_array
+ *
+ * Purpose: Allocate space for the image_entries array, and load
+ * each instance of H5C_image_entry_t in the array with
+ * the data necessary to construct the metadata cache image.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 8/4/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__prep_for_file_close__setup_image_entries_array(H5C_t *cache_ptr)
+{
+ H5C_cache_entry_t * entry_ptr;
+ H5C_image_entry_t * image_entries = NULL;
+ uint32_t entries_visited = 0;
+ unsigned u; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->close_warning_received);
+ HDassert(cache_ptr->pl_len == 0);
+ HDassert(cache_ptr->num_entries_in_image > 0);
+ HDassert(cache_ptr->image_entries == NULL);
+
+ /* Allocate and initialize image_entries array */
+ if(NULL == (image_entries = (H5C_image_entry_t *)H5MM_malloc(sizeof(H5C_image_entry_t) * (size_t)(cache_ptr->num_entries_in_image + 1))))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for image_entries")
+
+ for(u = 0; u <= cache_ptr->num_entries_in_image; u++) {
+ image_entries[u].magic = H5C_IMAGE_ENTRY_T_MAGIC;
+ image_entries[u].addr = HADDR_UNDEF;
+ image_entries[u].size = 0;
+ image_entries[u].ring = H5C_RING_UNDEFINED;
+ image_entries[u].age = 0;
+ image_entries[u].type_id = -1;
+ image_entries[u].lru_rank = 0;
+ image_entries[u].is_dirty = FALSE;
+ image_entries[u].image_fd_height = 0;
+ image_entries[u].fd_parent_count = 0;
+ image_entries[u].fd_parent_addrs = NULL;
+ image_entries[u].fd_child_count = 0;
+ image_entries[u].fd_dirty_child_count = 0;
+ image_entries[u].image_ptr = NULL;
+ } /* end for */
+
+ /* Scan each entry on the index list and populate the image_entries array */
+ u = 0;
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL) {
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ if(entry_ptr->include_in_image) {
+ /* Since we have already serialized the cache, the following
+ * should hold.
+ */
+ HDassert(entry_ptr->image_up_to_date);
+ HDassert(entry_ptr->image_ptr);
+ HDassert(entry_ptr->type);
+
+ image_entries[u].addr = entry_ptr->addr;
+ image_entries[u].size = entry_ptr->size;
+ image_entries[u].ring = entry_ptr->ring;
+
+ /* When a prefetched entry is included in the image, store
+ * its underlying type id in the image entry, not
+ * H5AC_PREFETCHED_ENTRY_ID. In passing, also increment
+ * the age (up to H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX).
+ */
+ if(entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID) {
+ image_entries[u].type_id = entry_ptr->prefetch_type_id;
+ image_entries[u].age = entry_ptr->age + 1;
+
+ if(image_entries[u].age > H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX)
+ image_entries[u].age = H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX;
+ } /* end if */
+ else {
+ image_entries[u].type_id = entry_ptr->type->id;
+ image_entries[u].age = 0;
+ } /* end else */
+
+ image_entries[u].lru_rank = entry_ptr->lru_rank;
+ image_entries[u].is_dirty = entry_ptr->is_dirty;
+ image_entries[u].image_fd_height = entry_ptr->image_fd_height;
+ image_entries[u].fd_parent_count = entry_ptr->fd_parent_count;
+ image_entries[u].fd_parent_addrs = entry_ptr->fd_parent_addrs;
+ image_entries[u].fd_child_count = entry_ptr->fd_child_count;
+ image_entries[u].fd_dirty_child_count =
+ entry_ptr->fd_dirty_child_count;
+ image_entries[u].image_ptr = entry_ptr->image_ptr;
+
+ /* Null out entry_ptr->fd_parent_addrs and set
+ * entry_ptr->fd_parent_count to zero so that ownership of the
+ * flush dependency parents address array is transferred to the
+ * image entry.
+ */
+ entry_ptr->fd_parent_count = 0;
+ entry_ptr->fd_parent_addrs = NULL;
+
+ u++;
+
+ HDassert(u <= cache_ptr->num_entries_in_image);
+ } /* end if */
+
+ entries_visited++;
+
+ entry_ptr = entry_ptr->il_next;
+ } /* end while */
+
+ /* Sanity checks */
+ HDassert(entries_visited == cache_ptr->index_len);
+ HDassert(u == cache_ptr->num_entries_in_image);
+
+ HDassert(image_entries[u].fd_parent_addrs == NULL);
+ HDassert(image_entries[u].image_ptr == NULL);
+
+ cache_ptr->image_entries = image_entries;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__prep_for_file_close__setup_image_entries_array() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__prep_for_file_close__scan_entries
+ *
+ * Purpose: Scan all entries in the metadata cache, and store all
+ * entry specific data required for construction of the
+ * metadata cache image block and likely to be discarded
+ * or modified during the cache flush on file close.
+ *
+ * In particular, make note of:
+ * entry rank in LRU
+ * whether the entry is dirty
+ * base address of entry flush dependency parent,
+ * if it exists.
+ * number of flush dependency children, if any.
+ *
+ * Also, determine which entries are to be included in the
+ * metadata cache image. At present, all entries other than
+ * the superblock, the superblock extension object header and
+ * its associated chunks (if any) are included.
+ *
+ * Finally, compute the size of the metadata cache image
+ * block.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 7/21/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__prep_for_file_close__scan_entries(const H5F_t *f, H5C_t *cache_ptr)
+{
+ H5C_cache_entry_t * entry_ptr;
+ hbool_t include_in_image;
+ unsigned entries_visited = 0;
+ int lru_rank = 1;
+ uint32_t num_entries_tentatively_in_image = 0;
+ uint32_t num_entries_in_image = 0;
+ size_t image_len;
+ size_t entry_header_len;
+ size_t fd_parents_list_len;
+ int i;
+ unsigned j;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(f->shared->sblock);
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->close_warning_received);
+ HDassert(cache_ptr->pl_len == 0);
+
+ /* Initialize image len to the size of the metadata cache image block
+ * header.
+ */
+ image_len = H5C__cache_image_block_header_size(f);
+ entry_header_len = H5C__cache_image_block_entry_header_size(f);
+
+ /* Scan each entry on the index list */
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL) {
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ /* Since we have already serialized the cache, the following
+ * should hold.
+ */
+ HDassert(entry_ptr->image_up_to_date);
+ HDassert(entry_ptr->image_ptr);
+
+ /* Initially, we mark all entries in the rings included
+ * in the cache image as being included in the in the
+ * image. Depending on circumstances, we may exclude some
+ * of these entries later.
+ */
+ if(entry_ptr->ring > H5C_MAX_RING_IN_IMAGE)
+ include_in_image = FALSE;
+ else
+ include_in_image = TRUE;
+ entry_ptr->include_in_image = include_in_image;
+
+ if(include_in_image) {
+ entry_ptr->lru_rank = -1;
+ entry_ptr->image_dirty = entry_ptr->is_dirty;
+ entry_ptr->image_fd_height = 0; /* will compute this later */
+
+ /* Initially, include all flush dependency parents in the
+ * the list of flush dependencies to be stored in the
+ * image. We may remove some or all of these later.
+ */
+ if(entry_ptr->flush_dep_nparents > 0) {
+ /* The parents addresses array may already exist -- reallocate
+ * as needed.
+ */
+ if(entry_ptr->flush_dep_nparents == entry_ptr->fd_parent_count ) {
+ /* parent addresses array should already be allocated
+ * and of the correct size.
+ */
+ HDassert(entry_ptr->fd_parent_addrs);
+ } /* end if */
+ else if(entry_ptr->fd_parent_count > 0) {
+ HDassert(entry_ptr->fd_parent_addrs);
+ entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_xfree(entry_ptr->fd_parent_addrs);
+ } /* end else-if */
+ else {
+ HDassert(entry_ptr->fd_parent_count == 0);
+ HDassert(entry_ptr->fd_parent_addrs == NULL);
+ } /* end else */
+
+ entry_ptr->fd_parent_count = entry_ptr->flush_dep_nparents;
+ if(NULL == entry_ptr->fd_parent_addrs)
+ if(NULL == (entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_malloc(sizeof(haddr_t) * (size_t)(entry_ptr->fd_parent_count))))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for fd parent addrs buffer")
+
+ for(i = 0; i < (int)(entry_ptr->fd_parent_count); i++) {
+ entry_ptr->fd_parent_addrs[i] = entry_ptr->flush_dep_parent[i]->addr;
+ HDassert(H5F_addr_defined(entry_ptr->fd_parent_addrs[i]));
+ } /* end for */
+ } /* end if */
+ else if(entry_ptr->fd_parent_count > 0) {
+ HDassert(entry_ptr->fd_parent_addrs);
+ entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_xfree(entry_ptr->fd_parent_addrs);
+ } /* end else-if */
+ else
+ HDassert(entry_ptr->fd_parent_addrs == NULL);
+
+ /* Initially, all flush dependency children are included int
+ * the count of flush dependency child relationships to be
+ * represented in the cache image. Some or all of these
+ * may be dropped from the image later.
+ */
+ if(entry_ptr->flush_dep_nchildren > 0) {
+ if(!entry_ptr->is_pinned)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "encountered unpinned fd parent?!?")
+
+ entry_ptr->fd_child_count = entry_ptr->flush_dep_nchildren;
+ entry_ptr->fd_dirty_child_count = entry_ptr->flush_dep_ndirty_children;
+ } /* end if */
+
+ num_entries_tentatively_in_image++;
+ } /* end if */
+
+ entries_visited++;
+ entry_ptr = entry_ptr->il_next;
+ } /* end while */
+ HDassert(entries_visited == cache_ptr->index_len);
+
+ /* Now compute the flush dependency heights of all flush dependency
+ * relationships to be represented in the image.
+ *
+ * If all entries in the target rings are included in the
+ * image, the flush dependency heights are simply the heights
+ * of all flush dependencies in the target rings.
+ *
+ * However, if we restrict appearance in the cache image either
+ * by number of entries in the image, restrictions on the number
+ * of times a prefetched entry can appear in an image, or image
+ * size, it is possible that flush dependency parents or children
+ * of entries that are in the image may not be included in the
+ * the image. In this case, we must prune all flush dependency
+ * relationships that cross the image boundary, and all exclude
+ * from the image all dirty flush dependency children that have
+ * a dirty flush dependency parent that is not in the image.
+ * This is necessary to preserve the required flush ordering.
+ *
+ * These details are tended to by the following call to
+ * H5C__prep_for_file_close__compute_fd_heights(). Because the
+ * exact contents of the image cannot be known until after this
+ * call, computation of the image size is delayed.
+ */
+ if(H5C__prep_for_file_close__compute_fd_heights(cache_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "computation of flush dependency heights failed?!?")
+
+ /* At this point, all entries that will appear in the cache
+ * image should be marked correctly. Compute the size of the
+ * cache image.
+ */
+ entries_visited = 0;
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL) {
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ if(entry_ptr->include_in_image) {
+ if(entry_ptr->fd_parent_count > 0)
+ fd_parents_list_len = (size_t)(H5F_SIZEOF_ADDR(f) * entry_ptr->fd_parent_count);
+ else
+ fd_parents_list_len = (size_t)0;
+
+ image_len += entry_header_len + fd_parents_list_len + entry_ptr->size;
+ num_entries_in_image++;
+ } /* end if */
+
+ entries_visited++;
+ entry_ptr = entry_ptr->il_next;
+ } /* end while */
+ HDassert(entries_visited == cache_ptr->index_len);
+ HDassert(num_entries_in_image <= num_entries_tentatively_in_image);
+
+ j = 0;
+ for(i = H5C_MAX_RING_IN_IMAGE + 1; i <= H5C_RING_SB; i++)
+ j += cache_ptr->index_ring_len[i];
+
+ /* This will change */
+ HDassert(entries_visited == (num_entries_tentatively_in_image + j));
+
+ cache_ptr->num_entries_in_image = num_entries_in_image;
+ entries_visited = 0;
+
+ /* Now scan the LRU list to set the lru_rank fields of all entries
+ * on the LRU.
+ *
+ * Note that we start with rank 1, and increment by 1 with each
+ * entry on the LRU.
+ *
+ * Note that manually pinned entryies will have lru_rank -1,
+ * and no flush dependency. Putting these entries at the head of
+ * the reconstructed LRU should be appropriate.
+ */
+ entry_ptr = cache_ptr->LRU_head_ptr;
+ while(entry_ptr != NULL) {
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->type != NULL);
+
+ /* to avoid confusion, don't set lru_rank on epoch markers.
+ * Note that we still increment the lru_rank, so that the holes
+ * in the sequence of entries on the LRU will indicate the
+ * locations of epoch markers (if any) when we reconstruct
+ * the LRU.
+ *
+ * Do not set lru_rank or increment lru_rank for entries
+ * that will not be included in the cache image.
+ */
+ if(entry_ptr->type->id == H5AC_EPOCH_MARKER_ID)
+ lru_rank++;
+ else if(entry_ptr->include_in_image) {
+ entry_ptr->lru_rank = lru_rank;
+ lru_rank++;
+ } /* end else-if */
+
+ entries_visited++;
+ entry_ptr = entry_ptr->next;
+ } /* end while */
+ HDassert(entries_visited == cache_ptr->LRU_list_len);
+
+ image_len += H5F_SIZEOF_CHKSUM;
+ cache_ptr->image_data_len = image_len;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__prep_for_file_close__scan_entries() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__reconstruct_cache_contents()
+ *
+ * Purpose: Scan the image_entries array, and create a prefetched
+ * cache entry for every entry in the array. Insert the
+ * prefetched entries in the index and the LRU, and
+ * reconstruct any flush dependencies. Order the entries
+ * in the LRU as indicated by the stored lru_ranks.
+ *
+ * Return: SUCCEED on success, and FAIL on failure.
+ *
+ * Programmer: John Mainzer
+ * 8/14/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__reconstruct_cache_contents(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr)
+{
+ H5C_cache_entry_t * pf_entry_ptr; /* Pointer to prefetched entry */
+ H5C_cache_entry_t * parent_ptr; /* Pointer to parent of prefetched entry */
+ unsigned u, v; /* Local index variable */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(cache_ptr == f->shared->cache);
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->image_buffer);
+ HDassert(cache_ptr->image_len > 0);
+ HDassert(cache_ptr->image_entries != NULL);
+ HDassert(cache_ptr->num_entries_in_image > 0);
+
+ /* Reconstruct entries in image */
+ for(u = 0; u < cache_ptr->num_entries_in_image; u++) {
+ /* Create the prefetched entry described by the ith
+ * entry in cache_ptr->image_entrise.
+ */
+ if(NULL == (pf_entry_ptr = H5C__reconstruct_cache_entry(cache_ptr, u)))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "reconstruction of cache entry failed")
+
+ /* Note that we make no checks on available cache space before
+ * inserting the reconstructed entry into the metadata cache.
+ *
+ * This is OK since the cache must be almost empty at the beginning
+ * of the process, and since we check cache size at the end of the
+ * reconstruction process.
+ */
+
+ /* Insert the prefetched entry in the index */
+ H5C__INSERT_IN_INDEX(cache_ptr, pf_entry_ptr, FAIL)
+
+ /* If dirty, insert the entry into the slist. */
+ if(pf_entry_ptr->is_dirty)
+ H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, pf_entry_ptr, FAIL)
+
+ /* Append the entry to the LRU */
+ H5C__UPDATE_RP_FOR_INSERT_APPEND(cache_ptr, pf_entry_ptr, FAIL)
+
+ H5C__UPDATE_STATS_FOR_PREFETCH(cache_ptr, pf_entry_ptr->is_dirty)
+
+ /* If the prefetched entry is the child in one or more flush
+ * dependency relationships, recreate those flush dependencies.
+ */
+ for(v = 0; v < pf_entry_ptr->fd_parent_count; v++) {
+ /* Sanity checks */
+ HDassert(pf_entry_ptr->fd_parent_addrs);
+ HDassert(H5F_addr_defined(pf_entry_ptr->fd_parent_addrs[v]));
+
+ /* Find the parent entry */
+ parent_ptr = NULL;
+ H5C__SEARCH_INDEX(cache_ptr, pf_entry_ptr->fd_parent_addrs[v], parent_ptr, FAIL)
+ if(parent_ptr == NULL)
+ HGOTO_ERROR(H5E_CACHE, H5E_NOTFOUND, FAIL, "fd parent not in cache?!?")
+
+ /* Sanity checks */
+ HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(parent_ptr->addr == pf_entry_ptr->fd_parent_addrs[v]);
+ HDassert(parent_ptr->lru_rank == -1);
+
+ /* Must protect parent entry to set up a flush dependency.
+ * Do this now, and then uprotect when done.
+ */
+ H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, parent_ptr, FAIL)
+ parent_ptr->is_protected = TRUE;
+
+ /* Setup the flush dependency */
+ if(H5C_create_flush_dependency(parent_ptr, pf_entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTDEPEND, FAIL, "Can't restore flush dependency")
+
+ /* And now unprotect */
+ H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, parent_ptr, FAIL)
+ parent_ptr->is_protected = FALSE;
+ } /* end for */
+ } /* end for */
+
+#ifndef NDEBUG
+ /* Scan the image_entries array, and verify that each entry has
+ * the expected flush dependency status.
+ */
+ for(u = 0; u < cache_ptr->num_entries_in_image; u++) {
+ H5C_image_entry_t * ie_ptr;
+
+ ie_ptr = &(cache_ptr->image_entries[u]);
+ HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC);
+
+ /* Find the prefetched entry */
+ pf_entry_ptr = NULL;
+ H5C__SEARCH_INDEX(cache_ptr, ie_ptr->addr, pf_entry_ptr, FAIL);
+
+ HDassert(pf_entry_ptr);
+ HDassert(pf_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(pf_entry_ptr->prefetched);
+ HDassert(ie_ptr->fd_parent_count == pf_entry_ptr->fd_parent_count);
+ HDassert(pf_entry_ptr->fd_parent_count == pf_entry_ptr->flush_dep_nparents);
+ HDassert(ie_ptr->lru_rank == pf_entry_ptr->lru_rank);
+
+ for(v = 0; v < pf_entry_ptr->fd_parent_count; v++) {
+ parent_ptr = pf_entry_ptr->flush_dep_parent[v];
+ HDassert(parent_ptr);
+ HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(pf_entry_ptr->fd_parent_addrs);
+ HDassert(pf_entry_ptr->fd_parent_addrs[v] == parent_ptr->addr);
+ HDassert(parent_ptr->flush_dep_nchildren > 0);
+ } /* end for */
+
+ HDassert(ie_ptr->fd_child_count == pf_entry_ptr->fd_child_count);
+ HDassert(pf_entry_ptr->fd_child_count == pf_entry_ptr->flush_dep_nchildren);
+ HDassert(pf_entry_ptr->fd_dirty_child_count == pf_entry_ptr->flush_dep_ndirty_children);
+ } /* end for */
+
+ /* Scan the LRU, and verify the expected ordering of the
+ * prefetched entries.
+ */
+ {
+ int lru_rank_holes = 0;
+ H5C_cache_entry_t *entry_ptr;
+ int i; /* Local index variable */
+
+ i = -1;
+ entry_ptr = cache_ptr->LRU_head_ptr;
+ while(entry_ptr != NULL) {
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->type != NULL);
+
+ if(entry_ptr->prefetched) {
+ HDassert(i <= entry_ptr->lru_rank);
+ HDassert((entry_ptr->lru_rank <= 2) ||
+ (entry_ptr->lru_rank == i + 1) ||
+ (entry_ptr->lru_rank == i + 2));
+
+ if((entry_ptr->lru_rank <= 2) && (entry_ptr->lru_rank == i + 2))
+ lru_rank_holes++;
+
+ i = entry_ptr->lru_rank;
+ } /* end if */
+
+ entry_ptr = entry_ptr->next;
+ } /* end while */
+
+ /* Holes of size 1 appear in the LRU ranking due to epoch
+ * markers. They are left in to allow re-insertion of the
+ * epoch markers on reconstruction of the cache -- thus
+ * the following sanity check will have to be revised when
+ * we add code to store and restore adaptive resize status.
+ */
+ HDassert(lru_rank_holes <= H5C__MAX_EPOCH_MARKERS);
+ } /* end block */
+#endif /* NDEBUG */
+
+ /* Check to see if the cache is oversize, and evict entries as
+ * necessary to remain within limits.
+ */
+ if(cache_ptr->index_size >= cache_ptr->max_cache_size) {
+ /* cache is oversized -- call H5C__make_space_in_cache() with zero
+ * space needed to repair the situation if possible.
+ */
+ hbool_t write_permitted = FALSE;
+
+ if(cache_ptr->check_write_permitted != NULL) {
+ if((cache_ptr->check_write_permitted)(f, &write_permitted) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, FAIL, "Can't get write_permitted")
+ } /* end if */
+ else
+ write_permitted = cache_ptr->write_permitted;
+
+ if(H5C__make_space_in_cache(f, dxpl_id, 0, write_permitted) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, FAIL, "H5C__make_space_in_cache failed")
+ } /* end if */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__reconstruct_cache_contents() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__reconstruct_cache_entry()
+ *
+ * Purpose: Allocate a prefetched metadata cache entry and initialize
+ * it from the indicated entry in the image_entries array.
+ *
+ * Return a pointer to the newly allocated cache entry,
+ * or NULL on failure.
+ *
+ * Return: Pointer to the new instance of H5C_cache_entry on success,
+ * or NULL on failure.
+ *
+ * Programmer: John Mainzer
+ * 8/14/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static H5C_cache_entry_t *
+H5C__reconstruct_cache_entry(H5C_t *cache_ptr, unsigned index)
+{
+ H5C_cache_entry_t *pf_entry_ptr = NULL; /* Reconstructed cache entry */
+ H5C_image_entry_t *ie_ptr;
+ H5C_cache_entry_t *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->image_entries != NULL);
+ HDassert(cache_ptr->num_entries_in_image > 0);
+ HDassert(index < cache_ptr->num_entries_in_image);
+ ie_ptr = &((cache_ptr->image_entries)[index]);
+ HDassert(ie_ptr->magic == H5C_IMAGE_ENTRY_T_MAGIC);
+ HDassert(H5F_addr_defined(ie_ptr->addr));
+ HDassert(ie_ptr->size > 0);
+ HDassert(ie_ptr->image_ptr);
+
+ /* Allocate space for the prefetched cache entry */
+ if(NULL == (pf_entry_ptr = H5FL_CALLOC(H5C_cache_entry_t)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "memory allocation failed for prefetched cache entry")
+
+ /* Initialize the prefetched entry from the entry image */
+ /* (Only need to set non-zero/NULL/FALSE fields, due to calloc() above) */
+ pf_entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC;
+ pf_entry_ptr->cache_ptr = cache_ptr;
+ pf_entry_ptr->addr = ie_ptr->addr;
+ pf_entry_ptr->size = ie_ptr->size;
+ pf_entry_ptr->ring = ie_ptr->ring;
+ pf_entry_ptr->age = ie_ptr->age;
+ pf_entry_ptr->image_ptr = ie_ptr->image_ptr;
+ pf_entry_ptr->image_up_to_date = TRUE;
+ pf_entry_ptr->type = H5AC_PREFETCHED_ENTRY;
+
+ /* Force dirty entries to clean if the file read only -- must do
+ * this as otherwise the cache will attempt to write them on file
+ * close. Since the file is R/O, the metadata cache image superblock
+ * extension message and the cache image block will not be removed.
+ * Hence no danger in this.
+ */
+ pf_entry_ptr->is_dirty = ie_ptr->is_dirty && cache_ptr->delete_image;
+
+ /* Initialize cache image related fields */
+ pf_entry_ptr->lru_rank = ie_ptr->lru_rank;
+ pf_entry_ptr->fd_parent_count = ie_ptr->fd_parent_count;
+ pf_entry_ptr->fd_parent_addrs = ie_ptr->fd_parent_addrs;
+ pf_entry_ptr->fd_child_count = ie_ptr->fd_child_count;
+ pf_entry_ptr->fd_dirty_child_count = ie_ptr->fd_dirty_child_count;
+ pf_entry_ptr->prefetched = TRUE;
+ pf_entry_ptr->prefetch_type_id = ie_ptr->type_id;
+ pf_entry_ptr->age = ie_ptr->age;
+
+ /* Array of addresses of flush dependency parents is now transferred to
+ * the prefetched entry. Thus set ie_ptr->fd_parent_addrs to NULL.
+ */
+ if(pf_entry_ptr->fd_parent_count > 0) {
+ HDassert(ie_ptr->fd_parent_addrs);
+ ie_ptr->fd_parent_addrs = NULL;
+ } /* end if */
+ else
+ HDassert(ie_ptr->fd_parent_addrs == NULL);
+
+ /* On disk image of entry is now transferred to the prefetched entry.
+ * Thus set ie_ptr->image_ptr to NULL.
+ */
+ ie_ptr->image_ptr = NULL;
+
+ /* Sanity checks */
+ HDassert(pf_entry_ptr->size > 0 && pf_entry_ptr->size < H5C_MAX_ENTRY_SIZE);
+
+ ret_value = pf_entry_ptr;
+
+done:
+ if(NULL == ret_value && pf_entry_ptr)
+ pf_entry_ptr = H5FL_FREE(H5C_cache_entry_t, pf_entry_ptr);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__reconstruct_cache_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__serialize_cache
+ *
+ * Purpose: Serialize (i.e. construct an on disk image) for all entries
+ * in the metadata cache including clean entries.
+ *
+ * Note that flush dependencies and "flush me last" flags
+ * must be observed in the serialization process.
+ *
+ * Note also that entries may be loaded, flushed, evicted,
+ * expunged, relocated, resized, or removed from the cache
+ * during this process, just as these actions may occur during
+ * a regular flush.
+ *
+ * However, we are given that the cache will contain no protected
+ * entries on entry to this routine (although entries may be
+ * briefly protected and then unprotected during the serialize
+ * process).
+ *
+ * The objective of this routine is serialize all entries and
+ * to force all entries into their actual locations on disk.
+ *
+ * The initial need for this routine is to settle all entries
+ * in the cache prior to construction of the metadata cache
+ * image so that the size of the cache image can be calculated.
+ * However, I gather that other uses for the routine are
+ * under consideration.
+ *
+ * Return: Non-negative on success/Negative on failure or if there was
+ * a request to flush all items and something was protected.
+ *
+ * Programmer: John Mainzer
+ * 7/22/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__serialize_cache(H5F_t *f, hid_t dxpl_id)
+{
+#if H5C_DO_SANITY_CHECKS
+ int i;
+ uint32_t index_len = 0;
+ size_t index_size = (size_t)0;
+ size_t clean_index_size = (size_t)0;
+ size_t dirty_index_size = (size_t)0;
+ size_t slist_size = (size_t)0;
+ uint32_t slist_len = 0;
+#endif /* H5C_DO_SANITY_CHECKS */
+ H5C_ring_t ring;
+ H5C_t * cache_ptr;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ cache_ptr = f->shared->cache;
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->slist_ptr);
+
+#if H5C_DO_SANITY_CHECKS
+ HDassert(cache_ptr->index_ring_len[H5C_RING_UNDEFINED] == 0);
+ HDassert(cache_ptr->index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
+ HDassert(cache_ptr->clean_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
+ HDassert(cache_ptr->dirty_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
+ HDassert(cache_ptr->slist_ring_len[H5C_RING_UNDEFINED] == 0);
+ HDassert(cache_ptr->slist_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
+
+ for(i = H5C_RING_USER; i < H5C_RING_NTYPES; i++) {
+ index_len += cache_ptr->index_ring_len[i];
+ index_size += cache_ptr->index_ring_size[i];
+ clean_index_size += cache_ptr->clean_index_ring_size[i];
+ dirty_index_size += cache_ptr->dirty_index_ring_size[i];
+
+ slist_len += cache_ptr->slist_ring_len[i];
+ slist_size += cache_ptr->slist_ring_size[i];
+ } /* end for */
+
+ HDassert(cache_ptr->index_len == index_len);
+ HDassert(cache_ptr->index_size == index_size);
+ HDassert(cache_ptr->clean_index_size == clean_index_size);
+ HDassert(cache_ptr->dirty_index_size == dirty_index_size);
+ HDassert(cache_ptr->slist_len == slist_len);
+ HDassert(cache_ptr->slist_size == slist_size);
+#endif /* H5C_DO_SANITY_CHECKS */
+
+#if H5C_DO_EXTREME_SANITY_CHECKS
+ if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
+ (H5C_validate_lru_list(cache_ptr) < 0))
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry.\n")
+#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
+
+#ifndef NDEBUG
+ /* if this is a debug build, set the serialization_count field of
+ * each entry in the cache to zero before we start the serialization.
+ * This allows us to detect the case in which any entry is serialized
+ * more than once (a performance issues), and more importantly, the
+ * case is which any flush depencency parent is serializes more than
+ * once (a correctness issue).
+ */
+ {
+ H5C_cache_entry_t * scan_ptr = NULL;
+
+ scan_ptr = cache_ptr->il_head;
+ while(scan_ptr != NULL) {
+ HDassert(scan_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ scan_ptr->serialization_count = 0;
+ scan_ptr = scan_ptr->il_next;
+ } /* end while */
+ } /* end block */
+#endif /* NDEBUG */
+
+ /* set cache_ptr->serialization_in_progress to TRUE, and back
+ * to FALSE at the end of the function. Must maintain this flag
+ * to support H5C_get_serialization_in_progress(), which is in
+ * turn required to support sanity checking in some cache
+ * clients.
+ */
+ HDassert(!cache_ptr->serialization_in_progress);
+ cache_ptr->serialization_in_progress = TRUE;
+
+ /* Serialize each ring, starting from the outermost ring and
+ * working inward.
+ */
+ ring = H5C_RING_USER;
+ while(ring < H5C_RING_NTYPES) {
+ HDassert(cache_ptr->close_warning_received);
+ switch(ring) {
+ case H5C_RING_USER:
+ break;
+
+ case H5C_RING_RDFSM:
+ if(!cache_ptr->rdfsm_settled) {
+ hbool_t fsm_settled = FALSE; /* Whether the FSM was actually settled */
+
+ /* Settle raw data FSM */
+ if(H5MF_settle_raw_data_fsm(f, dxpl_id, &fsm_settled) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "RD FSM settle failed")
+
+ /* Only set the flag if the FSM was actually settled */
+ if(fsm_settled)
+ cache_ptr->rdfsm_settled = TRUE;
+ } /* end if */
+ break;
+
+ case H5C_RING_MDFSM:
+ if(!cache_ptr->mdfsm_settled) {
+ hbool_t fsm_settled = FALSE; /* Whether the FSM was actually settled */
+
+ /* Settle metadata FSM */
+ if(H5MF_settle_meta_data_fsm(f, dxpl_id, &fsm_settled) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "MD FSM settle failed")
+
+ /* Only set the flag if the FSM was actually settled */
+ if(fsm_settled)
+ cache_ptr->mdfsm_settled = TRUE;
+ } /* end if */
+ break;
+
+ case H5C_RING_SBE:
+ case H5C_RING_SB:
+ break;
+
+ default:
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown ring?!?!")
+ break;
+ } /* end switch */
+
+ if(H5C__serialize_ring(f, dxpl_id, ring) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "serialize ring failed")
+
+ ring++;
+ } /* end while */
+
+#ifndef NDEBUG
+ /* Verify that no entry has been serialized more than once.
+ * FD parents with multiple serializations should have been caught
+ * elsewhere, so no specific check for them here.
+ */
+ {
+ H5C_cache_entry_t * scan_ptr = NULL;
+
+ scan_ptr = cache_ptr->il_head;
+ while(scan_ptr != NULL) {
+ HDassert(scan_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(scan_ptr->serialization_count <= 1);
+
+ scan_ptr = scan_ptr->il_next;
+ } /* end while */
+ } /* end block */
+#endif /* NDEBUG */
+
+done:
+ cache_ptr->serialization_in_progress = FALSE;
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__serialize_cache() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__serialize_ring
+ *
+ * Purpose: Serialize the entries contained in the specified cache and
+ * ring. All entries in rings outside the specified ring
+ * must have been serialized on entry.
+ *
+ * If the cache contains protected entries in the specified
+ * ring, the function will fail, as protected entries cannot
+ * be serialized. However all unprotected entries in the
+ * target ring should be serialized before the function
+ * returns failure.
+ *
+ * If flush dependencies appear in the target ring, the
+ * function makes repeated passes through the index list
+ * serializing entries in flush dependency order.
+ *
+ * All entries outside the H5C_RING_SBE are marked for
+ * inclusion in the cache image. Entries in H5C_RING_SBE
+ * and below are marked for exclusion from the image.
+ *
+ * Return: Non-negative on success/Negative on failure or if there was
+ * a request to flush all items and something was protected.
+ *
+ * Programmer: John Mainzer
+ * 9/11/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__serialize_ring(H5F_t *f, hid_t dxpl_id, H5C_ring_t ring)
+{
+ hbool_t done = FALSE;
+ hbool_t restart_list_scan = FALSE;
+ H5C_t * cache_ptr;
+ H5C_cache_entry_t * entry_ptr;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ cache_ptr = f->shared->cache;
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(ring > H5C_RING_UNDEFINED);
+ HDassert(ring < H5C_RING_NTYPES);
+
+ HDassert(cache_ptr->serialization_in_progress);
+
+ /* The objective here is to serialize all entries in the cache ring
+ * in flush dependency order.
+ *
+ * The basic algorithm is to scan the cache index list looking for
+ * unserialized entries that are either not in a flush dependency
+ * relationship, or which have no unserialized children. Any such
+ * entry is serialized and its flush dependency parents (if any) are
+ * informed -- allowing them to decrement their userialized child counts.
+ *
+ * However, this algorithm is complicated by the ability
+ * of client serialization callbacks to perform operations on
+ * on the cache which can result in the insertion, deletion,
+ * relocation, resize, dirty, flush, eviction, or removal (via the
+ * take ownership flag) of entries. Changes in the flush dependency
+ * structure are also possible.
+ *
+ * On the other hand, the algorithm is simplified by the fact that
+ * we are serializing, not flushing. Thus, as long as all entries
+ * are serialized correctly, it doesn't matter if we have to go back
+ * and serialize an entry a second time.
+ *
+ * These possible actions result in the following modfications to
+ * tha basic algorithm:
+ *
+ * 1) In the event of an entry expunge, eviction or removal, we must
+ * restart the scan as it is possible that the next entry in our
+ * scan is no longer in the cache. Were we to examine this entry,
+ * we would be accessing deallocated memory.
+ *
+ * 2) A resize, dirty, or insertion of an entry may result in the
+ * the increment of a flush dependency parent's dirty and/or
+ * unserialized child count. In the context of serializing the
+ * the cache, this is a non-issue, as even if we have already
+ * serialized the parent, it will be marked dirty and its image
+ * marked out of date if appropriate when the child is serialized.
+ *
+ * However, this is a major issue for a flush, as were this to happen
+ * in a flush, it would violate the invarient that the flush dependency
+ * feature is intended to enforce. As the metadata cache has no
+ * control over the behavior of cache clients, it has no way of
+ * preventing this behaviour. However, it should detect it if at all
+ * possible.
+ *
+ * Do this by maintaining a count of the number of times each entry is
+ * serialized during a cache serialization. If any flush dependency
+ * parent is serialized more than once, throw an assertion failure.
+ *
+ * 3) An entry relocation will typically change the location of the
+ * entry in the index list. This shouldn't cause problems as we
+ * will scan the index list until we make a complete pass without
+ * finding anything to serialize -- making relocations of either
+ * the current or next entries irrelevant.
+ *
+ * Note that since a relocation may result in our skipping part of
+ * the index list, we must always do at least one more pass through
+ * the index list after an entry relocation.
+ *
+ * 4) Changes in the flush dependency structure are possible on
+ * entry insertion, load, expunge, evict, or remove. Destruction
+ * of a flush dependency has no effect, as it can only relax the
+ * flush dependencies. Creation of a flush dependency can create
+ * an unserialized child of a flush dependency parent where all
+ * flush dependency children were previously serialized. Should
+ * this child dirty the flush dependency parent when it is serialized,
+ * the parent will be re-serialized.
+ *
+ * Per the discussion of 2) above, this is a non issue for cache
+ * serialization, and a major problem for cache flush. Using the
+ * same detection mechanism, throw an assertion failure if this
+ * condition appears.
+ *
+ * Observe that either eviction or removal of entries as a result of
+ * a serialization is not a problem as long as the flush depencency
+ * tree does not change beyond the removal of a leaf.
+ */
+ while(!done) {
+ done = TRUE; /* set to FALSE if any activity in inner loop */
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL) {
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+
+ /* Verify that either the entry is already serialized, or
+ * that it is assigned to either the target or an inner
+ * ring.
+ */
+ HDassert((entry_ptr->ring >= ring) || (entry_ptr->image_up_to_date));
+
+ /* Skip flush me last entries or inner ring entries */
+ if(!entry_ptr->flush_me_last && entry_ptr->ring == ring) {
+
+ /* if we encounter an unserialized entry in the current
+ * ring that is not marked flush me last, we are not done.
+ */
+ if(!entry_ptr->image_up_to_date)
+ done = FALSE;
+
+ /* Serialize the entry if its image is not up to date
+ * and it has no unserialized flush dependency children.
+ */
+ if(!entry_ptr->image_up_to_date && entry_ptr->flush_dep_nunser_children == 0) {
+ HDassert(entry_ptr->serialization_count == 0);
+
+ /* Serialize the entry */
+ if(H5C__serialize_single_entry(f, dxpl_id, cache_ptr, entry_ptr, &restart_list_scan) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "entry serialization failed")
+
+ HDassert(entry_ptr->flush_dep_nunser_children == 0);
+ HDassert(entry_ptr->serialization_count == 0);
+
+#ifndef NDEBUG
+ entry_ptr->serialization_count++;
+#endif /* NDEBUG */
+ } /* end if */
+
+#if H5C_COLLECT_CACHE_STATS
+ if(restart_list_scan)
+ H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr);
+#endif /* H5C_COLLECT_CACHE_STATS */
+ } /* end if */
+
+ if(restart_list_scan) {
+ restart_list_scan = FALSE;
+ entry_ptr = cache_ptr->il_head;
+ } /* end if */
+ else
+ entry_ptr = entry_ptr->il_next;
+ } /* while ( entry_ptr != NULL ) */
+ } /* while ( ! done ) */
+
+
+ /* At this point, all entries not marked "flush me last" and in
+ * the current ring or outside it should be serialized and have up
+ * to date images. Scan the index list again to serialize the
+ * "flush me last" entries (if they are in the current ring) and to
+ * verify that all other entries have up to date images.
+ */
+ entry_ptr = cache_ptr->il_head;
+ while(entry_ptr != NULL) {
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->ring > H5C_RING_UNDEFINED);
+ HDassert(entry_ptr->ring < H5C_RING_NTYPES);
+ HDassert((entry_ptr->ring >= ring) || (entry_ptr->image_up_to_date));
+
+ if(entry_ptr->ring == ring) {
+ if(entry_ptr->flush_me_last) {
+ if(!entry_ptr->image_up_to_date) {
+ HDassert(entry_ptr->serialization_count == 0);
+ HDassert(entry_ptr->flush_dep_nunser_children == 0);
+
+ /* Serialize the entry */
+ if(H5C__serialize_single_entry(f, dxpl_id, cache_ptr, entry_ptr, &restart_list_scan) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "entry serialization failed")
+ else if(restart_list_scan)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "flush_me_last entry serialization triggered restart")
+
+ HDassert(entry_ptr->flush_dep_nunser_children == 0);
+ HDassert(entry_ptr->serialization_count == 0);
+#ifndef NDEBUG
+ entry_ptr->serialization_count++;
+#endif /* NDEBUG */
+ } /* end if */
+ } /* end if */
+ else {
+ HDassert(entry_ptr->image_up_to_date);
+ HDassert(entry_ptr->serialization_count <= 1);
+ HDassert(entry_ptr->flush_dep_nunser_children == 0);
+ } /* end else */
+ } /* if ( entry_ptr->ring == ring ) */
+
+ entry_ptr = entry_ptr->il_next;
+ } /* while ( entry_ptr != NULL ) */
+
+done:
+ HDassert(cache_ptr->serialization_in_progress);
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__serialize_ring() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__serialize_single_entry
+ *
+ * Purpose: Serialize the cache entry pointed to by the entry_ptr
+ * parameter.
+ *
+ * Note: This routine is very similar to H5C__generate_image
+ * and changes to one should probably be reflected in the other.
+ * Ideally, one should be eliminated.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer, 7/24/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__serialize_single_entry(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr,
+ H5C_cache_entry_t *entry_ptr, hbool_t *restart_list_scan_ptr)
+{
+ unsigned serialize_flags = H5C__SERIALIZE_NO_FLAGS_SET;
+ haddr_t new_addr = HADDR_UNDEF;
+ haddr_t old_addr = HADDR_UNDEF;
+ size_t new_len = 0;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(!entry_ptr->prefetched);
+ HDassert(!entry_ptr->image_up_to_date);
+ HDassert(entry_ptr->is_dirty);
+ HDassert(!entry_ptr->is_protected);
+ HDassert(!entry_ptr->flush_in_progress);
+ HDassert(entry_ptr->type);
+ HDassert(restart_list_scan_ptr);
+ HDassert(*restart_list_scan_ptr == FALSE);
+
+ /* Set entry_ptr->flush_in_progress to TRUE so the the target entry
+ * will not be evicted out from under us. Must set it back to FALSE
+ * when we are done.
+ */
+ entry_ptr->flush_in_progress = TRUE;
+
+ /* Allocate buffer for the entry image if required. */
+ if(NULL == entry_ptr->image_ptr) {
+ HDassert(entry_ptr->size > 0);
+ if(NULL == (entry_ptr->image_ptr = H5MM_malloc(entry_ptr->size + H5C_IMAGE_EXTRA_SPACE)) )
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer")
+#if H5C_DO_MEMORY_SANITY_CHECKS
+ HDmemcpy(((uint8_t *)entry_ptr->image_ptr) + image_size, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+ } /* end if */
+
+ /* Serialize the entry. Note that the entry need not be dirty. */
+
+ /* Reset cache_ptr->slist_changed so we can detect slist
+ * modifications in the pre_serialize call.
+ */
+ cache_ptr->slist_changed = FALSE;
+
+ /* Make note of the entry's current address */
+ old_addr = entry_ptr->addr;
+
+ /* Reset the counters so that we can detect insertions, loads,
+ * moves, and flush dependency height changes caused by the pre_serialize
+ * and serialize calls.
+ */
+ cache_ptr->entries_loaded_counter = 0;
+ cache_ptr->entries_inserted_counter = 0;
+ cache_ptr->entries_relocated_counter = 0;
+
+ /* Call client's pre-serialize callback, if there's one */
+ if(entry_ptr->type->pre_serialize &&
+ (entry_ptr->type->pre_serialize)(f, dxpl_id, (void *)entry_ptr, entry_ptr->addr, entry_ptr->size, &new_addr, &new_len, &serialize_flags) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to pre-serialize entry")
+
+ /* Check for any flags set in the pre-serialize callback */
+ if(serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET) {
+
+ /* Check for unexpected flags from serialize callback */
+ if(serialize_flags & ~(H5C__SERIALIZE_RESIZED_FLAG | H5C__SERIALIZE_MOVED_FLAG))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unknown serialize flag(s)")
+
+#ifdef H5_HAVE_PARALLEL
+ /* In the parallel case, resizes and moves in the serialize
+ * operation can cause problems. If they occur, scream and die.
+ *
+ * At present, in the parallel case, the aux_ptr will only be
+ * set if there is more than one process. Thus we can use this
+ * to detect the parallel case.
+ *
+ * This works for now, but if we start using the aux_ptr for
+ * other purposes, we will have to change this test accordingly.
+ *
+ * NB: While this test detects entryies that attempt
+ * to resize or move themselves during a flush
+ * in the parallel case, it will not detect an
+ * entry that dirties, resizes, and/or moves
+ * other entries during its flush.
+ *
+ * From what Quincey tells me, this test is
+ * sufficient for now, as any flush routine that
+ * does the latter will also do the former.
+ *
+ * If that ceases to be the case, further
+ * tests will be necessary.
+ */
+ if(cache_ptr->aux_ptr != NULL)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "resize/move in serialize occured in parallel case.")
+#endif /* H5_HAVE_PARALLEL */
+
+ /* Resize the buffer if required */
+ if(serialize_flags & H5C__SERIALIZE_RESIZED_FLAG) {
+ /* Sanity check */
+ HDassert(new_len > 0);
+
+ /* Allocate a new image buffer */
+ if(NULL == (entry_ptr->image_ptr = H5MM_realloc(entry_ptr->image_ptr, new_len + H5C_IMAGE_EXTRA_SPACE)))
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer")
+#if H5C_DO_MEMORY_SANITY_CHECKS
+ HDmemcpy(((uint8_t *)entry_ptr->image_ptr) + new_len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+
+ /* Update the entry and the cache data structures for a resize. */
+ H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_len)
+
+ /* Update the hash table for the size change */
+ H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len, entry_ptr, !(entry_ptr->is_dirty));
+
+ /* The entry can't be protected since we are
+ * in the process of serializing the cache. Thus we must
+ * update the replacement policy data structures for the
+ * size change. The macro deals with the pinned case.
+ */
+ H5C__UPDATE_RP_FOR_SIZE_CHANGE(cache_ptr, entry_ptr, new_len);
+
+ /* It should be in the skip list, update the skip list for the
+ * size change.
+ */
+ HDassert(entry_ptr->is_dirty);
+ HDassert(entry_ptr->in_slist);
+ H5C__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len)
+
+ /* Finally, update the entry for its new size */
+ entry_ptr->size = new_len;
+ } /* end if */
+
+ /* If required, update the entry and the cache data structures
+ * for a move
+ */
+ if(serialize_flags & H5C__SERIALIZE_MOVED_FLAG) {
+ /* Since the entry has moved, it is probably no longer in
+ * the same place in its list. Thus at a minimum, we must set
+ * *restart_list_scan_ptr to TRUE.
+ */
+ *restart_list_scan_ptr = TRUE;
+
+ /* Update stats and the entries relocated counter */
+ H5C__UPDATE_STATS_FOR_MOVE(cache_ptr, entry_ptr)
+
+ /* We must update cache data structures for the change in address */
+ if(entry_ptr->addr == old_addr) {
+ /* Delete the entry from the hash table and the slist */
+ H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr, FAIL)
+ H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE)
+
+ /* Update the entry for its new address */
+ entry_ptr->addr = new_addr;
+
+ /* And then reinsert in the index and slist (if appropriate) */
+ H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL)
+ H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
+ } /* end if */
+ else /* Move is already done for us -- just do sanity checks */
+ HDassert(entry_ptr->addr == new_addr);
+ } /* end if */
+ } /* end if ( serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET ) */
+
+ /* Reset cache_ptr->slist_changed so we can detect slist
+ * modifications in the serialize call.
+ */
+ cache_ptr->slist_changed = FALSE;
+
+ /* Serialize object into buffer */
+ if(entry_ptr->type->serialize(f, entry_ptr->image_ptr, entry_ptr->size, (void *)entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to serialize entry")
+#if H5C_DO_MEMORY_SANITY_CHECKS
+ HDassert(0 == HDmemcmp(((uint8_t *)entry_ptr->image_ptr) + image_len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE));
+#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
+ entry_ptr->image_up_to_date = TRUE;
+
+ /* Propagate the fact that the entry is serialized up the
+ * flush dependency chain if appropriate. Since the image must
+ * have been out of date for this function to have been called
+ * (see assertion on entry), no need to check that -- only check
+ * for flush dependency parents.
+ */
+ HDassert(entry_ptr->flush_dep_nunser_children == 0);
+ if(entry_ptr->flush_dep_nparents > 0)
+ if(H5C__mark_flush_dep_serialized(entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate flush dep serialized flag")
+
+ /* Reset the flush_in progress flag */
+ entry_ptr->flush_in_progress = FALSE;
+
+ /* Set *restart_fd_scan_ptr to TRUE if appropriate, and if we
+ * haven't already done so.
+ */
+ if(!(*restart_list_scan_ptr))
+ if((cache_ptr->entries_loaded_counter > 0) || (cache_ptr->entries_inserted_counter > 0) ||
+ (cache_ptr->entries_relocated_counter > 0))
+ *restart_list_scan_ptr = TRUE;
+
+done:
+ HDassert((ret_value != SUCCEED) || (!entry_ptr->flush_in_progress));
+ HDassert((ret_value != SUCCEED) || (entry_ptr->image_up_to_date));
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__serialize_single_entry() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__write_cache_image_superblock_msg
+ *
+ * Purpose: Write the cache image superblock extension message,
+ * creating if specified.
+ *
+ * In general, the size and location of the cache image block
+ * will be unknow at the time that the cache image superblock
+ * message is created. A subsequent call to this routine will
+ * be used to write the correct data.
+ *
+ * Return: Non-negative on success/Negative on failure.
+ *
+ * Programmer: John Mainzer, 7/4/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__write_cache_image_superblock_msg(H5F_t *f, hid_t dxpl_id, hbool_t create)
+{
+ H5C_t * cache_ptr;
+ H5O_mdci_t mdci_msg; /* metadata cache image message */
+ /* to insert in the superblock */
+ /* extension. */
+ unsigned mesg_flags = H5O_MSG_FLAG_FAIL_IF_UNKNOWN_ALWAYS;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(f->shared->cache);
+ cache_ptr = f->shared->cache;
+ HDassert(cache_ptr);
+ HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
+ HDassert(cache_ptr->close_warning_received);
+
+ /* Write data into the metadata cache image superblock extension message.
+ * Note that this data will be bogus when we first create the message.
+ * We will overwrite this data later in a second call to this function.
+ */
+ mdci_msg.addr = cache_ptr->image_addr;
+#ifdef H5_HAVE_PARALLEL
+ if(cache_ptr->aux_ptr) { /* we have multiple processes */
+ H5AC_aux_t * aux_ptr;
+
+ aux_ptr = (H5AC_aux_t *)cache_ptr->aux_ptr;
+ HDassert(aux_ptr->magic == H5AC__H5AC_AUX_T_MAGIC);
+ mdci_msg.size = aux_ptr->p0_image_len;
+ } /* end if */
+ else
+#endif /* H5_HAVE_PARALLEL */
+ mdci_msg.size = cache_ptr->image_len;
+
+ /* Write metadata cache image message to superblock extension */
+ if(H5F_super_ext_write_msg(f, dxpl_id, H5O_MDCI_MSG_ID, &mdci_msg, create, mesg_flags) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_WRITEERROR, FAIL, "can't write metadata cache image message to superblock extension")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__write_cache_image_superblock_msg() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__write_cache_image
+ *
+ * Purpose: Write the supplied metadata cache image to the specified
+ * location in file.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 8/26/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__write_cache_image(H5F_t *f, hid_t dxpl_id, const H5C_t *cache_ptr)
+{
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(f);
+ HDassert(cache_ptr);
+ HDassert(H5F_addr_defined(cache_ptr->image_addr));
+ HDassert(cache_ptr->image_len > 0);
+ HDassert(cache_ptr->image_buffer);
+
+#ifdef H5_HAVE_PARALLEL
+{
+ H5AC_aux_t *aux_ptr = (H5AC_aux_t *)cache_ptr->aux_ptr;
+
+ if((NULL == aux_ptr) || (aux_ptr->mpi_rank == 0)) {
+ HDassert((NULL == aux_ptr) || (aux_ptr->magic == H5AC__H5AC_AUX_T_MAGIC));
+#endif /* H5_HAVE_PARALLEL */
+
+ /* Write the buffer (if serial access, or rank 0 for parallel access) */
+ if(H5F_block_write(f, H5FD_MEM_SUPER, cache_ptr->image_addr, cache_ptr->image_len, dxpl_id, cache_ptr->image_buffer) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't write metadata cache image block to file.")
+#ifdef H5_HAVE_PARALLEL
+ } /* end if */
+} /* end block */
+#endif /* H5_HAVE_PARALLEL */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* H5C__write_cache_image() */
+
diff --git a/src/H5Cpkg.h b/src/H5Cpkg.h
index 6e37bca..1656a32 100644
--- a/src/H5Cpkg.h
+++ b/src/H5Cpkg.h
@@ -209,7 +209,6 @@ if ( ( (entry_ptr) == NULL ) || \
( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
( (head_ptr) != (tail_ptr) ) \
) || \
- ( (len) < 0 ) || \
( ( (len) == 1 ) && \
( ( (head_ptr) != (tail_ptr) ) || \
( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \
@@ -494,7 +493,6 @@ if ( ( (entry_ptr) == NULL ) || \
( ( ( (hd_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
( (hd_ptr) != (tail_ptr) ) \
) || \
- ( (len) < 0 ) || \
( ( (len) == 1 ) && \
( ( (hd_ptr) != (tail_ptr) ) || ( (Size) <= 0 ) || \
( (hd_ptr) == NULL ) || ( (hd_ptr)->size != (Size) ) \
@@ -514,7 +512,6 @@ if ( ( (entry_ptr) == NULL ) || \
if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
( (head_ptr) != (tail_ptr) ) \
) || \
- ( (len) < 0 ) || \
( ( (len) == 1 ) && \
( ( (head_ptr) != (tail_ptr) ) || \
( (head_ptr) == NULL ) || ( (head_ptr)->size != (Size) ) \
@@ -599,23 +596,6 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
* H5C__UPDATE_CACHE_HIT_RATE_STATS(), which is always active as
* the cache hit rate stats are always collected and available.
*
- * Changes:
- *
- * JRM -- 3/21/06
- * Added / updated macros for pinned entry related stats.
- *
- * JRM -- 8/9/06
- * More pinned entry stats related updates.
- *
- * JRM -- 3/31/07
- * Updated H5C__UPDATE_STATS_FOR_PROTECT() to keep stats on
- * read and write protects.
- *
- * MAM -- 1/15/09
- * Created H5C__UPDATE_MAX_INDEX_SIZE_STATS to contain
- * common code within macros that update the maximum
- * index, clean_index, and dirty_index statistics fields.
- *
***********************************************************************/
#define H5C__UPDATE_CACHE_HIT_RATE_STATS(cache_ptr, hit) \
@@ -702,6 +682,31 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
#define H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr) \
((cache_ptr)->index_scan_restarts)++;
+#define H5C__UPDATE_STATS_FOR_CACHE_IMAGE_CREATE(cache_ptr) \
+{ \
+ (cache_ptr)->images_created++; \
+}
+
+#define H5C__UPDATE_STATS_FOR_CACHE_IMAGE_LOAD(cache_ptr) \
+{ \
+ /* make sure image len is still good */ \
+ HDassert((cache_ptr)->image_len > 0); \
+ (cache_ptr)->images_loaded++; \
+ (cache_ptr)->last_image_size = (cache_ptr)->image_len; \
+}
+
+#define H5C__UPDATE_STATS_FOR_PREFETCH(cache_ptr, dirty) \
+{ \
+ (cache_ptr)->prefetches++; \
+ if ( dirty ) \
+ (cache_ptr)->dirty_prefetches++; \
+}
+
+#define H5C__UPDATE_STATS_FOR_PREFETCH_HIT(cache_ptr) \
+{ \
+ (cache_ptr)->prefetch_hits++; \
+}
+
#if H5C_COLLECT_CACHE_ENTRY_STATS
#define H5C__RESET_CACHE_ENTRY_STATS(entry_ptr) \
@@ -926,6 +931,10 @@ if ( ( ( ( (head_ptr) == NULL ) || ( (tail_ptr) == NULL ) ) && \
#define H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr)
#define H5C__UPDATE_STATS_FOR_LRU_SCAN_RESTART(cache_ptr)
#define H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr)
+#define H5C__UPDATE_STATS_FOR_CACHE_IMAGE_CREATE(cache_ptr)
+#define H5C__UPDATE_STATS_FOR_CACHE_IMAGE_LOAD(cache_ptr)
+#define H5C__UPDATE_STATS_FOR_PREFETCH(cache_ptr, dirty)
+#define H5C__UPDATE_STATS_FOR_PREFETCH_HIT(cache_ptr)
#endif /* H5C_COLLECT_CACHE_STATS */
@@ -2239,6 +2248,120 @@ if ( ( (cache_ptr)->index_size != \
/*-------------------------------------------------------------------------
*
+ * Macro: H5C__UPDATE_RP_FOR_INSERT_APPEND
+ *
+ * Purpose: Update the replacement policy data structures for an
+ * insertion of the specified cache entry.
+ *
+ * Unlike H5C__UPDATE_RP_FOR_INSERTION below, mark the
+ * new entry as the LEAST recently used entry, not the
+ * most recently used.
+ *
+ * For now at least, this macro should only be used in
+ * the reconstruction of the metadata cache from a cache
+ * image block.
+ *
+ * At present, we only support the modified LRU policy, so
+ * this function deals with that case unconditionally. If
+ * we ever support other replacement policies, the function
+ * should switch on the current policy and act accordingly.
+ *
+ * Return: N/A
+ *
+ * Programmer: John Mainzer, 8/15/15
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
+
+#define H5C__UPDATE_RP_FOR_INSERT_APPEND(cache_ptr, entry_ptr, fail_val) \
+{ \
+ 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 ) { \
+ \
+ H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, \
+ (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the tail of the LRU list. */ \
+ \
+ H5C__DLL_APPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* insert the entry at the tail of the clean or dirty LRU list as \
+ * appropriate. \
+ */ \
+ \
+ if ( entry_ptr->is_dirty ) { \
+ H5C__AUX_DLL_APPEND((entry_ptr), (cache_ptr)->dLRU_head_ptr, \
+ (cache_ptr)->dLRU_tail_ptr, \
+ (cache_ptr)->dLRU_list_len, \
+ (cache_ptr)->dLRU_list_size, (fail_val)) \
+ } else { \
+ H5C__AUX_DLL_APPEND((entry_ptr), (cache_ptr)->cLRU_head_ptr, \
+ (cache_ptr)->cLRU_tail_ptr, \
+ (cache_ptr)->cLRU_list_len, \
+ (cache_ptr)->cLRU_list_size, (fail_val)) \
+ } \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+}
+
+#else /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+#define H5C__UPDATE_RP_FOR_INSERT_APPEND(cache_ptr, entry_ptr, fail_val) \
+{ \
+ 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 ) { \
+ \
+ H5C__DLL_PREPEND((entry_ptr), (cache_ptr)->pel_head_ptr, \
+ (cache_ptr)->pel_tail_ptr, \
+ (cache_ptr)->pel_len, \
+ (cache_ptr)->pel_size, (fail_val)) \
+ \
+ } else { \
+ \
+ /* modified LRU specific code */ \
+ \
+ /* insert the entry at the tail of the LRU list. */ \
+ \
+ H5C__DLL_APPEND((entry_ptr), (cache_ptr)->LRU_head_ptr, \
+ (cache_ptr)->LRU_tail_ptr, \
+ (cache_ptr)->LRU_list_len, \
+ (cache_ptr)->LRU_list_size, (fail_val)) \
+ \
+ /* End modified LRU specific code. */ \
+ } \
+}
+
+#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
+
+
+/*-------------------------------------------------------------------------
+ *
* Macro: H5C__UPDATE_RP_FOR_INSERTION
*
* Purpose: Update the replacement policy data structures for an
@@ -2500,7 +2623,6 @@ if ( ( (cache_ptr)->index_size != \
(cache_ptr)->pel_tail_ptr, \
(cache_ptr)->pel_len, \
(cache_ptr)->pel_size, (fail_val)) \
- HDassert( (cache_ptr)->pel_len >= 0 ); \
\
} else { \
\
@@ -2901,7 +3023,6 @@ if ( ( (cache_ptr)->index_size != \
H5C__DLL_REMOVE((entry_ptr), (cache_ptr)->pel_head_ptr, \
(cache_ptr)->pel_tail_ptr, (cache_ptr)->pel_len, \
(cache_ptr)->pel_size, (fail_val)) \
- HDassert( (cache_ptr)->pel_len >= 0 ); \
\
/* modified LRU specific code */ \
\
@@ -3433,10 +3554,8 @@ typedef struct H5C_tag_info_t {
* types are stored in the type_name_table discussed below, and
* indexed by the ids.
*
- * type_name_table_ptr: Pointer to an array of pointer to char of length
- * max_type_id + 1. The strings pointed to by the entries
- * in the array are the names of the entry types associated
- * with the indexing type IDs.
+ * class_table_ptr: Pointer to an array of H5C_class_t of length
+ * max_type_id + 1. Entry classes for the cache.
*
* max_cache_size: Nominal maximum number of bytes that may be stored in the
* cache. This value should be viewed as a soft limit, as the
@@ -3986,7 +4105,22 @@ typedef struct H5C_tag_info_t {
*
* size_decreased: Boolean flag set to TRUE whenever the maximum cache
* size is decreased. The flag triggers a call to
- * H5C_make_space_in_cache() on the next call to H5C_protect().
+ * H5C__make_space_in_cache() on the next call to H5C_protect().
+ *
+ * resize_in_progress: As the metadata cache has become re-entrant, it is
+ * possible that a protect may trigger a call to
+ * H5C__auto_adjust_cache_size(), which may trigger a flush,
+ * which may trigger a protect, which will result in another
+ * call to H5C__auto_adjust_cache_size().
+ *
+ * The resize_in_progress boolean flag is used to detect this,
+ * and to prevent the infinite recursion that would otherwise
+ * occur.
+ *
+ * Note that this issue is not hypothetical -- this field
+ * was added 12/29/15 to fix a bug exposed in the testing
+ * of changes to the file driver info superblock extension
+ * management code needed to support rings.
*
* resize_ctl: Instance of H5C_auto_size_ctl_t containing configuration
* data for automatic cache resizing.
@@ -4063,6 +4197,77 @@ typedef struct H5C_tag_info_t {
* this field will be reset every automatic resize epoch.
*
*
+ * Metadata cache image management related fields.
+ *
+ * image_ctl: Instance of H5C_cache_image_ctl_t containing configuration
+ * data for generation of a cache image on file close.
+ *
+ * serialization_in_progress: Boolean field that is set to TRUE iff
+ * the cache is in the process of being serialized. This
+ * field is needed to support the H5C_serialization_in_progress()
+ * call, which is in turn required for sanity checks in some
+ * cache clients.
+ *
+ * load_image: Boolean flag indicating that the metadata cache image
+ * superblock extension message exists and should be
+ * read, and the image block read and decoded on the next
+ * call to H5C_protect().
+ *
+ * image_loaded: Boolean flag indicating that the metadata cache has
+ * loaded the metadata cache image as directed by the
+ * MDC cache image superblock extension message.
+ *
+ * delete_image: Boolean flag indicating whether the metadata cache image
+ * superblock message should be deleted and the cache image
+ * file space freed after they have been read and decoded.
+ *
+ * This flag should be set to TRUE iff the file is opened
+ * R/W and there is a cache image to be read.
+ *
+ * image_addr: haddr_t containing the base address of the on disk
+ * metadata cache image, or HADDR_UNDEF if that value is
+ * undefined. Note that this field is used both in the
+ * construction and write, and the read and decode of
+ * metadata cache image blocks.
+ *
+ * image_len: hsize_t containing the size of the on disk metadata cache
+ * image, or zero if that value is undefined. Note that this
+ * field is used both in the construction and write, and the
+ * read and decode of metadata cache image blocks.
+ *
+ * image_data_len: size_t containing the number of bytes of data in the
+ * on disk metadata cache image, or zero if that value is
+ * undefined.
+ *
+ * In most cases, this value is the same as the image_len
+ * above. It exists to allow for metadata cache image blocks
+ * that are larger than the actual image. Thus in all
+ * cases image_data_len <= image_len.
+ *
+ * To create the metadata cache image, we must first serialize all the
+ * entries in the metadata cache. This is done by a scan of the index.
+ * As entries must be serialized in increasing flush dependency height
+ * order, we scan the index repeatedly, once for each flush dependency
+ * height in increasing order.
+ *
+ * This operation is complicated by the fact that entries other the the
+ * target may be inserted, loaded, relocated, or removed from the cache
+ * (either by eviction or the take ownership flag) as the result of a
+ * pre_serialize or serialize callback. While entry removals are not
+ * a problem for the scan of the index, insertions, loads, and relocations
+ * are. Hence the entries loaded, inserted, and relocated counters
+ * listed below have been implemented to allow these conditions to be
+ * detected and dealt with by restarting the scan.
+ *
+ * The serialization operation is further complicated by the fact that
+ * the flush dependency height of a given entry may increase (as the
+ * result of an entry load or insert) or decrease (as the result of an
+ * entry removal -- via either eviction or the take ownership flag). The
+ * entry_fd_height_change_counter field is maintained to allow detection
+ * of this condition, and a restart of the scan when it occurs.
+ *
+ * Note that all these new fields would work just as well as booleans.
+ *
* entries_loaded_counter: Number of entries loaded into the cache
* since the last time this field was reset.
*
@@ -4072,6 +4277,29 @@ typedef struct H5C_tag_info_t {
* entries relocated_counter: Number of entries whose base address has
* been changed since the last time this field was reset.
*
+ * entry_fd_height_change_counter: Number of entries whose flush dependency
+ * height has changed since the last time this field was reset.
+ *
+ * The following fields are used assemble the cache image prior to
+ * writing it to disk.
+ *
+ * num_entries_in_image: Unsigned integer field containing the number of entries
+ * to be copied into the metadata cache image. Note that
+ * this value will be less than the number of entries in
+ * the cache, and the superblock and its related entries
+ * are not written to the metadata cache image.
+ *
+ * image_entries: Pointer to a dynamically allocated array of instance of
+ * H5C_image_entry_t of length num_entries_in_image, or NULL
+ * if that array does not exist. This array is used to
+ * assemble entry data to be included in the image, and to
+ * sort them by flush dependency height and LRU rank.
+ *
+ * image_buffer: Pointer to the dynamically allocated buffer of length
+ * image_len in which the metadata cache image is assembled,
+ * or NULL if that buffer does not exist.
+ *
+ *
* Free Space Manager Related fields:
*
* The free space managers must be informed when we are about to close
@@ -4285,23 +4513,63 @@ typedef struct H5C_tag_info_t {
* max_pel_size: Largest value attained by the pel_size field in the
* current epoch.
*
- * calls_to_msic: Total number of calls to H5C_make_space_in_cache
+ * calls_to_msic: Total number of calls to H5C__make_space_in_cache
*
* total_entries_skipped_in_msic: Number of clean entries skipped while
- * enforcing the min_clean_fraction in H5C_make_space_in_cache().
+ * enforcing the min_clean_fraction in H5C__make_space_in_cache().
*
* total_entries_scanned_in_msic: Number of clean entries skipped while
- * enforcing the min_clean_fraction in H5C_make_space_in_cache().
+ * enforcing the min_clean_fraction in H5C__make_space_in_cache().
*
* max_entries_skipped_in_msic: Maximum number of clean entries skipped
- * in any one call to H5C_make_space_in_cache().
+ * in any one call to H5C__make_space_in_cache().
*
* max_entries_scanned_in_msic: Maximum number of entries scanned over
- * in any one call to H5C_make_space_in_cache().
+ * in any one call to H5C__make_space_in_cache().
*
* entries_scanned_to_make_space: Number of entries scanned only when looking
* for entries to evict in order to make space in cache.
*
+ *
+ * The following fields track statistics on cache images.
+ *
+ * images_created: Integer field containing the number of cache images
+ * created since the last time statistics were reset.
+ *
+ * At present, this field must always be either 0 or 1.
+ * Further, since cache images are only created at file
+ * close, this field should only be set at that time.
+ *
+ * images_loaded: Integer field containing the number of cache images
+ * loaded since the last time statistics were reset.
+ *
+ * At present, this field must always be either 0 or 1.
+ * Further, since cache images are only loaded at the
+ * time of the first protect or on file close, this value
+ * should only change on those events.
+ *
+ * last_image_size: Size of the most recently loaded metadata cache image
+ * loaded into the cache, or zero if no image has been
+ * loaded.
+ *
+ * At present, at most one cache image can be loaded into
+ * the metadata cache for any given file, and this image
+ * will be loaded either on the first protect, or on file
+ * close if no entry is protected before then.
+ *
+ *
+ * Fields for tracking prefetched entries. Note that flushes and evictions
+ * of prefetched entries are tracked in the flushes and evictions arrays
+ * discused above.
+ *
+ * prefetches: Number of prefetched entries that are loaded to the
+ * cache.
+ *
+ * dirty_prefetches: Number of dirty prefetched entries that are loaded
+ * into the cache.
+ *
+ * prefetch_hits: Number of prefetched entries that are actually used.
+ *
*
* As entries are now capable of moving, loading, dirtying, and deleting
* other entries in their pre_serialize and serialize callbacks, it has
@@ -4372,6 +4640,11 @@ typedef struct H5C_tag_info_t {
* field is intended to allow marking of output of with
* the processes mpi rank.
*
+ * get_entry_ptr_from_addr_counter: Counter used to track the number of
+ * times the H5C_get_entry_ptr_from_addr() function has been
+ * called successfully. This field is only defined when
+ * NDEBUG is not #defined.
+ *
****************************************************************************/
struct H5C_t {
uint32_t magic;
@@ -4382,7 +4655,7 @@ struct H5C_t {
FILE * log_file_ptr;
void * aux_ptr;
int32_t max_type_id;
- const char * (* type_name_table_ptr);
+ const H5C_class_t * const *class_table_ptr;
size_t max_cache_size;
size_t min_clean_size;
H5C_write_permitted_func_t check_write_permitted;
@@ -4392,16 +4665,16 @@ struct H5C_t {
hbool_t close_warning_received;
/* Fields for maintaining [hash table] index of entries */
- int32_t index_len;
+ uint32_t index_len;
size_t index_size;
- int32_t index_ring_len[H5C_RING_NTYPES];
+ uint32_t index_ring_len[H5C_RING_NTYPES];
size_t index_ring_size[H5C_RING_NTYPES];
size_t clean_index_size;
size_t clean_index_ring_size[H5C_RING_NTYPES];
size_t dirty_index_size;
size_t dirty_index_ring_size[H5C_RING_NTYPES];
H5C_cache_entry_t * index[H5C__HASH_TABLE_LEN];
- int32_t il_len;
+ uint32_t il_len;
size_t il_size;
H5C_cache_entry_t * il_head;
H5C_cache_entry_t * il_tail;
@@ -4413,12 +4686,12 @@ struct H5C_t {
/* Fields for maintaining list of in-order entries, for flushing */
hbool_t slist_changed;
- int32_t slist_len;
+ uint32_t slist_len;
size_t slist_size;
- int32_t slist_ring_len[H5C_RING_NTYPES];
+ uint32_t slist_ring_len[H5C_RING_NTYPES];
size_t slist_ring_size[H5C_RING_NTYPES];
H5SL_t * slist_ptr;
- int32_t num_last_entries;
+ uint32_t num_last_entries;
#if H5C_DO_SANITY_CHECKS
int64_t slist_len_increase;
int64_t slist_size_increase;
@@ -4429,38 +4702,38 @@ struct H5C_t {
hbool_t ignore_tags;
/* Fields for tracking protected entries */
- int32_t pl_len;
+ uint32_t pl_len;
size_t pl_size;
H5C_cache_entry_t * pl_head_ptr;
H5C_cache_entry_t * pl_tail_ptr;
/* Fields for tracking pinned entries */
- int32_t pel_len;
+ uint32_t pel_len;
size_t pel_size;
H5C_cache_entry_t * pel_head_ptr;
H5C_cache_entry_t * pel_tail_ptr;
/* Fields for complete LRU list of entries */
- int32_t LRU_list_len;
+ uint32_t LRU_list_len;
size_t LRU_list_size;
H5C_cache_entry_t * LRU_head_ptr;
H5C_cache_entry_t * LRU_tail_ptr;
/* Fields for clean LRU list of entries */
- int32_t cLRU_list_len;
+ uint32_t cLRU_list_len;
size_t cLRU_list_size;
H5C_cache_entry_t * cLRU_head_ptr;
H5C_cache_entry_t * cLRU_tail_ptr;
/* Fields for dirty LRU list of entries */
- int32_t dLRU_list_len;
+ uint32_t dLRU_list_len;
size_t dLRU_list_size;
H5C_cache_entry_t * dLRU_head_ptr;
H5C_cache_entry_t * dLRU_tail_ptr;
#ifdef H5_HAVE_PARALLEL
/* Fields for collective metadata reads */
- int32_t coll_list_len;
+ uint32_t coll_list_len;
size_t coll_list_size;
H5C_cache_entry_t * coll_head_ptr;
H5C_cache_entry_t * coll_tail_ptr;
@@ -4477,6 +4750,7 @@ struct H5C_t {
hbool_t resize_enabled;
hbool_t cache_full;
hbool_t size_decreased;
+ hbool_t resize_in_progress;
H5C_auto_size_ctl_t resize_ctl;
/* Fields for epoch markers used in automatic cache size adjustment */
@@ -4492,9 +4766,23 @@ struct H5C_t {
int64_t cache_hits;
int64_t cache_accesses;
+ /* fields supporting generation of a cache image on file close */
+ H5C_cache_image_ctl_t image_ctl;
+ hbool_t serialization_in_progress;
+ hbool_t load_image;
+ hbool_t image_loaded;
+ hbool_t delete_image;
+ haddr_t image_addr;
+ hsize_t image_len;
+ hsize_t image_data_len;
int64_t entries_loaded_counter;
int64_t entries_inserted_counter;
int64_t entries_relocated_counter;
+ int64_t entry_fd_height_change_counter;
+ uint32_t num_entries_in_image;
+ H5C_image_entry_t * image_entries;
+ void * image_buffer;
+
/* Free Space Manager Related fields */
hbool_t rdfsm_settled;
hbool_t mdfsm_settled;
@@ -4532,21 +4820,21 @@ struct H5C_t {
int64_t total_successful_ht_search_depth;
int64_t failed_ht_searches;
int64_t total_failed_ht_search_depth;
- int32_t max_index_len;
+ uint32_t max_index_len;
size_t max_index_size;
size_t max_clean_index_size;
size_t max_dirty_index_size;
/* Fields for in-order skip list */
- int32_t max_slist_len;
+ uint32_t max_slist_len;
size_t max_slist_size;
/* Fields for protected entry list */
- int32_t max_pl_len;
+ uint32_t max_pl_len;
size_t max_pl_size;
/* Fields for pinned entry list */
- int32_t max_pel_len;
+ uint32_t max_pel_len;
size_t max_pel_size;
/* Fields for tracking 'make space in cache' (msic) operations */
@@ -4562,6 +4850,16 @@ struct H5C_t {
int64_t LRU_scan_restarts;
int64_t index_scan_restarts;
+ /* Fields for tracking cache image operations */
+ int32_t images_created;
+ int32_t images_loaded;
+ hsize_t last_image_size;
+
+ /* Fields for tracking prefetched entries */
+ int64_t prefetches;
+ int64_t dirty_prefetches;
+ int64_t prefetch_hits;
+
#if H5C_COLLECT_CACHE_ENTRY_STATS
int32_t max_accesses[H5C__MAX_NUM_TYPE_IDS + 1];
int32_t min_accesses[H5C__MAX_NUM_TYPE_IDS + 1];
@@ -4573,6 +4871,10 @@ struct H5C_t {
#endif /* H5C_COLLECT_CACHE_STATS */
char prefix[H5C__PREFIX_LEN];
+
+#ifndef NDEBUG
+ int64_t get_entry_ptr_from_addr_counter;
+#endif /* NDEBUG */
};
/* Define typedef for tagged cache entry iteration callbacks */
@@ -4583,19 +4885,24 @@ typedef int (*H5C_tag_iter_cb_t)(H5C_cache_entry_t *entry, void *ctx);
/* Package Private Variables */
/*****************************/
-/* Metadata cache epoch class */
-H5_DLLVAR const H5C_class_t H5C__epoch_marker_class;
-
/******************************/
/* Package Private Prototypes */
/******************************/
+H5_DLL herr_t H5C__prep_image_for_file_close(H5F_t *f, hid_t dxpl_id);
+H5_DLL herr_t H5C__deserialize_prefetched_entry(H5F_t * f, hid_t dxpl_id,
+ H5C_t * cache_ptr, H5C_cache_entry_t** entry_ptr_ptr,
+ const H5C_class_t * type, haddr_t addr, void * udata);
/* General routines */
H5_DLL herr_t H5C__flush_single_entry(H5F_t *f, hid_t dxpl_id,
H5C_cache_entry_t *entry_ptr, unsigned flags);
+H5_DLL herr_t H5C__generate_cache_image(H5F_t *f, hid_t dxpl_id, H5C_t *cache_ptr);
+H5_DLL herr_t H5C__load_cache_image(H5F_t *f, hid_t dxpl_id);
H5_DLL herr_t H5C__mark_flush_dep_serialized(H5C_cache_entry_t * entry_ptr);
H5_DLL herr_t H5C__mark_flush_dep_unserialized(H5C_cache_entry_t * entry_ptr);
+H5_DLL herr_t H5C__make_space_in_cache(H5F_t * f, hid_t dxpl_id,
+ size_t space_needed, hbool_t write_permitted);
H5_DLL herr_t H5C__flush_marked_entries(H5F_t * f, hid_t dxpl_id);
H5_DLL herr_t H5C__iter_tagged_entries(H5C_t *cache, haddr_t tag, hbool_t match_global,
H5C_tag_iter_cb_t cb, void *cb_ctx);
diff --git a/src/H5Cprefetched.c b/src/H5Cprefetched.c
new file mode 100644
index 0000000..9b11006
--- /dev/null
+++ b/src/H5Cprefetched.c
@@ -0,0 +1,352 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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: H5Cprefetched.c
+ * December 28 2016
+ * Quincey Koziol
+ *
+ * Purpose: Metadata cache prefetched entry callbacks.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+/***********/
+/* Headers */
+/***********/
+#include "H5private.h" /* Generic Functions */
+#include "H5ACprivate.h" /* Metadata cache */
+#include "H5FLprivate.h" /* Free Lists */
+#include "H5MMprivate.h" /* Memory management */
+
+
+/****************/
+/* Local Macros */
+/****************/
+
+
+/******************/
+/* Local Typedefs */
+/******************/
+
+
+/********************/
+/* Local Prototypes */
+/********************/
+
+/****************************************************************************
+ *
+ * Declarations for prefetched cache entry callbacks.
+ *
+ ****************************************************************************/
+static herr_t H5C__prefetched_entry_get_initial_load_size(void *udata_ptr,
+ size_t *image_len_ptr);
+static herr_t H5C__prefetched_entry_get_final_load_size(const void *image_ptr,
+ size_t image_len, void *udata_ptr, size_t *actual_len_ptr);
+static htri_t H5C__prefetched_entry_verify_chksum(const void *image_ptr,
+ size_t len, void *udata_ptr);
+static void * H5C__prefetched_entry_deserialize(const void *image_ptr,
+ size_t len, void *udata, hbool_t *dirty_ptr);
+static herr_t H5C__prefetched_entry_image_len(const void *thing,
+ size_t *image_len_ptr);
+static herr_t H5C__prefetched_entry_pre_serialize(H5F_t *f,
+ hid_t dxpl_id, void *thing, haddr_t addr, size_t len,
+ haddr_t *new_addr_ptr, size_t *new_len_ptr, unsigned *flags_ptr);
+static herr_t H5C__prefetched_entry_serialize(const H5F_t *f, void *image_ptr,
+ size_t len, void *thing);
+static herr_t H5C__prefetched_entry_notify(H5C_notify_action_t action,
+ void *thing);
+static herr_t H5C__prefetched_entry_free_icr(void *thing);
+static herr_t H5C__prefetched_entry_fsf_size(const void *thing,
+ size_t *fsf_size_ptr);
+
+
+/*********************/
+/* Package Variables */
+/*********************/
+
+/* Declare external the free list for H5C_cache_entry_t's */
+H5FL_EXTERN(H5C_cache_entry_t);
+
+
+/*****************************/
+/* Library Private Variables */
+/*****************************/
+
+
+/*******************/
+/* Local Variables */
+/*******************/
+
+
+const H5AC_class_t H5AC_PREFETCHED_ENTRY[1] = {{
+ /* id = */ H5AC_PREFETCHED_ENTRY_ID,
+ /* name = */ "prefetched entry",
+ /* mem_type = */ H5FD_MEM_DEFAULT, /* value doesn't matter */
+ /* flags = */ H5AC__CLASS_NO_FLAGS_SET,
+ /* get_initial_load_size = */ H5C__prefetched_entry_get_initial_load_size,
+ /* get_final_load_size = */ H5C__prefetched_entry_get_final_load_size,
+ /* verify_chksum = */ H5C__prefetched_entry_verify_chksum,
+ /* deserialize = */ H5C__prefetched_entry_deserialize,
+ /* image_len = */ H5C__prefetched_entry_image_len,
+ /* pre_serialize = */ H5C__prefetched_entry_pre_serialize,
+ /* serialize = */ H5C__prefetched_entry_serialize,
+ /* notify = */ H5C__prefetched_entry_notify,
+ /* free_icr = */ H5C__prefetched_entry_free_icr,
+ /* fsf_size = */ H5C__prefetched_entry_fsf_size,
+}};
+
+
+
+/***************************************************************************
+ * With two exceptions, these functions should never be called, and thus
+ * there is little point in documenting them separately as they all simply
+ * throw an error.
+ *
+ * See header comments for the two exceptions (free_icr and notify).
+ *
+ * JRM - 8/13/15
+ *
+ ***************************************************************************/
+
+static herr_t
+H5C__prefetched_entry_get_initial_load_size(void H5_ATTR_UNUSED *udata_ptr,
+ size_t H5_ATTR_UNUSED *image_len_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
+
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__prefetched_entry_get_initial_load_size() */
+
+static herr_t
+H5C__prefetched_entry_get_final_load_size(const void H5_ATTR_UNUSED *image_ptr,
+ size_t H5_ATTR_UNUSED image_len, void H5_ATTR_UNUSED *udata_ptr,
+ size_t H5_ATTR_UNUSED *actual_len_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
+
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__prefetched_entry_get_final_load_size() */
+
+static htri_t
+H5C__prefetched_entry_verify_chksum(const void H5_ATTR_UNUSED *image_ptr,
+ size_t H5_ATTR_UNUSED len, void H5_ATTR_UNUSED *udata_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
+
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__prefetched_verify_chksum() */
+
+
+static void *
+H5C__prefetched_entry_deserialize(const void H5_ATTR_UNUSED * image_ptr,
+ size_t H5_ATTR_UNUSED len, void H5_ATTR_UNUSED * udata,
+ hbool_t H5_ATTR_UNUSED * dirty_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
+
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(NULL)
+} /* end H5C__prefetched_entry_deserialize() */
+
+
+static herr_t
+H5C__prefetched_entry_image_len(const void H5_ATTR_UNUSED *thing,
+ size_t H5_ATTR_UNUSED *image_len_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
+
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__prefetched_entry_image_len() */
+
+
+static herr_t
+H5C__prefetched_entry_pre_serialize(H5F_t H5_ATTR_UNUSED *f,
+ hid_t H5_ATTR_UNUSED dxpl_id, void H5_ATTR_UNUSED *thing,
+ haddr_t H5_ATTR_UNUSED addr, size_t H5_ATTR_UNUSED len,
+ haddr_t H5_ATTR_UNUSED *new_addr_ptr,
+ size_t H5_ATTR_UNUSED *new_len_ptr,
+ unsigned H5_ATTR_UNUSED *flags_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
+
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__prefetched_entry_pre_serialize() */
+
+
+static herr_t
+H5C__prefetched_entry_serialize(const H5F_t H5_ATTR_UNUSED *f,
+ void H5_ATTR_UNUSED *image_ptr,
+ 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 */
+
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__prefetched_entry_serialize() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__prefetched_entry_notify
+ *
+ * Purpose: On H5AC_NOTIFY_ACTION_BEFORE_EVICT, check to see if the
+ * target entry is a child in a flush dependency relationship.
+ * If it is, destroy that flush dependency relationship.
+ *
+ * Ignore on all other notifications.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: John Mainzer
+ * 8/13/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__prefetched_entry_notify(H5C_notify_action_t action, void *_thing)
+{
+ H5C_cache_entry_t * entry_ptr = (H5C_cache_entry_t *)_thing;
+ unsigned u;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(entry_ptr->prefetched);
+
+ switch(action) {
+ case H5C_NOTIFY_ACTION_AFTER_INSERT:
+ case H5C_NOTIFY_ACTION_AFTER_LOAD:
+ case H5C_NOTIFY_ACTION_AFTER_FLUSH:
+ case H5C_NOTIFY_ACTION_ENTRY_DIRTIED:
+ case H5C_NOTIFY_ACTION_ENTRY_CLEANED:
+ case H5C_NOTIFY_ACTION_CHILD_DIRTIED:
+ case H5C_NOTIFY_ACTION_CHILD_CLEANED:
+ case H5C_NOTIFY_ACTION_CHILD_UNSERIALIZED:
+ case H5C_NOTIFY_ACTION_CHILD_SERIALIZED:
+ /* do nothing */
+ break;
+
+ case H5C_NOTIFY_ACTION_BEFORE_EVICT:
+ for(u = 0; u < entry_ptr->flush_dep_nparents; u++) {
+ H5C_cache_entry_t * parent_ptr;
+
+ /* Sanity checks */
+ HDassert(entry_ptr->flush_dep_parent);
+ parent_ptr = entry_ptr->flush_dep_parent[u];
+ HDassert(parent_ptr);
+ HDassert(parent_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(parent_ptr->flush_dep_nchildren > 0);
+
+ /* Destroy flush dependency with flush dependency parent */
+ if(H5C_destroy_flush_dependency(parent_ptr, entry_ptr) < 0)
+ HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "unable to destroy prefetched entry flush dependency")
+
+ if(parent_ptr->prefetched) {
+ /* In prefetched entries, the fd_child_count field is
+ * used in sanity checks elsewhere. Thus update this
+ * field to reflect the destruction of the flush
+ * dependency relationship.
+ */
+ HDassert(parent_ptr->fd_child_count > 0);
+ (parent_ptr->fd_child_count)--;
+ } /* end if */
+ } /* end for */
+ break;
+
+ default:
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unknown action from metadata cache")
+ break;
+ } /* end switch */
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5C__prefetched_entry_notify() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5C__prefetched_entry_free_icr
+ *
+ * Purpose: Free the in core representation of the prefetched entry.
+ * Verify that the image buffer associated with the entry
+ * has been either transferred or freed.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: John Mainzer
+ * 8/13/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5C__prefetched_entry_free_icr(void *_thing)
+{
+ H5C_cache_entry_t *entry_ptr = (H5C_cache_entry_t *)_thing;
+ herr_t ret_value = SUCCEED;
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(entry_ptr);
+ HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC);
+ HDassert(entry_ptr->prefetched);
+
+ /* Release array for flush dependency parent addresses */
+ if(entry_ptr->fd_parent_addrs != NULL) {
+ HDassert(entry_ptr->fd_parent_count > 0);
+ entry_ptr->fd_parent_addrs = (haddr_t *)H5MM_xfree((void *)entry_ptr->fd_parent_addrs);
+ } /* end if */
+ else
+ HDassert(entry_ptr->fd_parent_count == 0);
+
+ if(entry_ptr->image_ptr != NULL)
+ HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "prefetched entry image buffer still attatched?")
+
+ entry_ptr = H5FL_FREE(H5C_cache_entry_t, entry_ptr);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5C__prefetched_entry_free_icr() */
+
+
+static herr_t
+H5C__prefetched_entry_fsf_size(const void H5_ATTR_UNUSED *thing,
+ size_t H5_ATTR_UNUSED *fsf_size_ptr)
+{
+ FUNC_ENTER_STATIC_NOERR /* Yes, even though this pushes an error on the stack */
+
+ HERROR(H5E_CACHE, H5E_SYSTEM, "called unreachable fcn.");
+
+ FUNC_LEAVE_NOAPI(FAIL)
+} /* end H5C__prefetched_entry_fsf_size() */
+
diff --git a/src/H5Cprivate.h b/src/H5Cprivate.h
index 654ce35..47573f9 100644
--- a/src/H5Cprivate.h
+++ b/src/H5Cprivate.h
@@ -114,6 +114,7 @@
/* Cache configuration versions */
#define H5C__CURR_AUTO_SIZE_CTL_VER 1
#define H5C__CURR_AUTO_RESIZE_RPT_FCN_VER 1
+#define H5C__CURR_CACHE_IMAGE_CTL_VER 1
/* Default configuration settings */
#define H5C__DEF_AR_UPPER_THRESHHOLD 0.9999f
@@ -994,16 +995,16 @@ typedef int H5C_ring_t;
* just before the entry is freed.
*
* This is necessary, as the LRU list can be changed out
- * from under H5C_make_space_in_cache() by the serialize
+ * from under H5C__make_space_in_cache() by the serialize
* callback which may change the size of an existing entry,
* and/or load a new entry while serializing the target entry.
*
* This in turn can cause a recursive call to
- * H5C_make_space_in_cache() which may either flush or evict
+ * H5C__make_space_in_cache() which may either flush or evict
* the next entry that the first invocation of that function
* was about to examine.
*
- * The magic field allows H5C_make_space_in_cache() to
+ * The magic field allows H5C__make_space_in_cache() to
* detect this case, and re-start its scan from the bottom
* of the LRU when this situation occurs.
*
@@ -1338,6 +1339,187 @@ typedef int H5C_ring_t;
* In either case, when there is no previous item, it should
* be NULL.
*
+ * Fields supporting the cache image feature:
+ *
+ * The following fields are used to store data about the entry which must
+ * be stored in the cache image block, but which will typically be either
+ * lost or heavily altered in the process of serializing the cache and
+ * preparing its contents to be copied into the cache image block.
+ *
+ * Some fields are also used in loading the contents of the metadata cache
+ * image back into the cache, and in managing such entries until they are
+ * either protected by the library (at which point they become regular
+ * entries) or are evicted. See discussion of the prefetched field for
+ * further details.
+ *
+ * include_in_image: Boolean flag indicating whether this entry should
+ * be included in the metadata cache image. This field should
+ * always be false prior to the H5C_prep_for_file_close() call.
+ * During that call, it should be set to TRUE for all entries
+ * that are to be included in the metadata cache image. At
+ * present, only the superblock, the superblock extension
+ * object header and its chunks (if any) are omitted from
+ * the image.
+ *
+ * lru_rank: Rank of the entry in the LRU just prior to file close.
+ *
+ * Note that the first entry on the LRU has lru_rank 1,
+ * and that entries not on the LRU at that time will have
+ * either lru_rank -1 (if pinned) or 0 (if loaded during
+ * the process of flushing the cache.
+ *
+ * image_dirty: Boolean flag indicating whether the entry should be marked
+ * as dirty in the metadata cache image. The flag is set to
+ * TRUE iff the entry is dirty when H5C_prep_for_file_close()
+ * is called.
+ *
+ * fd_parent_count: If the entry is a child in one or more flush dependency
+ * relationships, this field contains the number of flush
+ * dependency parents.
+ *
+ * In all other cases, the field is set to zero.
+ *
+ * Note that while this count is initially taken from the
+ * flush dependency fields above, if the entry is in the
+ * cache image (i.e. include_in_image is TRUE), any parents
+ * that are not in the image are removed from this count and
+ * from the fd_parent_addrs array below.
+ *
+ * Finally observe that if the entry is dirty and in the
+ * cache image, and its parent is dirty and not in the cache
+ * image, then the entry must be removed from the cache image
+ * to avoid violating the flush dependency flush ordering.
+ *
+ * fd_parent_addrs: If the entry is a child in one or more flush dependency
+ * relationship when H5C_prep_for_file_close() is called, this
+ * field must contain a pointer to an array of size
+ * fd_parent_count containing the on disk addresses of the
+ * parent.
+ *
+ * In all other cases, the field is set to NULL.
+ *
+ * Note that while this list of addresses is initially taken
+ * from the flush dependency fields above, if the entry is in the
+ * cache image (i.e. include_in_image is TRUE), any parents
+ * that are not in the image are removed from this list, and
+ * and from the fd_parent_count above.
+ *
+ * Finally observe that if the entry is dirty and in the
+ * cache image, and its parent is dirty and not in the cache
+ * image, then the entry must be removed from the cache image
+ * to avoid violating the flush dependency flush ordering.
+ *
+ * fd_child_count: If the entry is a parent in a flush dependency
+ * relationship, this field contains the number of flush
+ * dependency children.
+ *
+ * In all other cases, the field is set to zero.
+ *
+ * Note that while this count is initially taken from the
+ * flush dependency fields above, if the entry is in the
+ * cache image (i.e. include_in_image is TRUE), any children
+ * that are not in the image are removed from this count.
+ *
+ * fd_dirty_child_count: If the entry is a parent in a flush dependency
+ * relationship, this field contains the number of dirty flush
+ * dependency children.
+ *
+ * In all other cases, the field is set to zero.
+ *
+ * Note that while this count is initially taken from the
+ * flush dependency fields above, if the entry is in the
+ * cache image (i.e. include_in_image is TRUE), any dirty
+ * children that are not in the image are removed from this
+ * count.
+ *
+ * image_fd_height: Flush dependency height of the entry in the cache image.
+ *
+ * The flush dependency height of any entry involved in a
+ * flush dependency relationship is defined to be the
+ * longest flush dependency path from that entry to an entry
+ * with no flush depenency children.
+ *
+ * Since the image_fd_height is used to order entries in the
+ * cache image so that fd parents preceed fd children, for
+ * purposes of this field, and entry is at flush dependency
+ * level 0 if it either has no children, or if all of its
+ * children are not in the cache image.
+ *
+ * Note that if a child in a flush dependency relationship is
+ * dirty and in the cache image, and its parent is dirty and
+ * not in the cache image, then the child must be excluded
+ * from the cache image to maintain flush ordering.
+ *
+ * prefetched: Boolean flag indicating that the on disk image of the entry
+ * has been loaded into the cache prior any request for the
+ * entry by the rest of the library.
+ *
+ * As of this writing (8/10/15), this can only happen through
+ * the load of a cache image block, although other scenarios
+ * are contemplated for the use of this feature. Note that
+ * unlike the usual prefetch situation, this means that a
+ * pre fetched entry can be dirty, and/or can be a party to
+ * flush dependency relationship(s). This complicates matters
+ * somewhat.
+ *
+ * The essential feature of a pre-fetched entry is that it
+ * consists only of a buffer containing the on disk image of
+ * the entry. Thus it must be deserialized before it can
+ * be passed back to the library on a protect call. This
+ * task is handled by H5C_deserialized_prefetched_entry().
+ * In essence, this routine calls the deserialize callback
+ * provided in the protect call with the on disk image,
+ * deletes the prefetched entry from the cache, and replaces
+ * it with the deserialized entry returned by the deserialize
+ * callback.
+ *
+ * Further, if the prefetched entry is a flush dependency parent,
+ * all its flush dependency children (which must also be
+ * pre-fetched entries), must be tranfered to the new cache
+ * entry returned by the deserailization callback.
+ *
+ * Finally, if the prefetched entry is a flush dependency child,
+ * this flush dependency must be destroyed prior to the
+ * deserialize call.
+ *
+ * In addition to the above special processing on the first
+ * protect call on a prefetched entry (after which is no longer
+ * a prefetched entry), prefetched entries also require special
+ * tretment on flush and evict.
+ *
+ * On flush, a dirty prefetched entry must simply be written
+ * to disk and marked clean without any call to any client
+ * callback.
+ *
+ * On eviction, if a prefetched entry is a flush dependency
+ * child, that flush dependency relationship must be destroyed
+ * just prior to the eviction. If the flush dependency code
+ * is working properly, it should be impossible for any entry
+ * that is a flush dependency parent to be evicted.
+ *
+ * prefetch_type_id: Integer field containing the type ID of the prefetched
+ * entry. This ID must match the ID of the type provided in any
+ * protect call on the prefetched entry.
+ *
+ * The value of this field is undefined in prefetched is FALSE.
+ *
+ * age: Number of times a prefetched entry has appeared in
+ * subsequent cache images. The field exists to allow
+ * imposition of a limit on how many times a prefetched
+ * entry can appear in subsequent cache images without being
+ * converted to a regular entry.
+ *
+ * This field must be zero if prefetched is FALSE.
+ *
+ * serialization_count: Integer field used to maintain a count of the
+ * number of times each entry is serialized during cache
+ * serialization. While no entry should be serialized more than
+ * once in any serialization call, throw an assertion if any
+ * flush depencency parent is serialized more than once during
+ * a single cache serialization.
+ *
+ * This is a debugging field, and thus is maintained only if
+ * NDEBUG is undefined.
*
* Fields supporting tagged entries:
*
@@ -1433,6 +1615,23 @@ typedef struct H5C_cache_entry_t {
struct H5C_cache_entry_t *coll_prev;
#endif /* H5_HAVE_PARALLEL */
+ /* fields supporting cache image */
+ hbool_t include_in_image;
+ int32_t lru_rank;
+ hbool_t image_dirty;
+ uint64_t fd_parent_count;
+ haddr_t *fd_parent_addrs;
+ uint64_t fd_child_count;
+ uint64_t fd_dirty_child_count;
+ uint32_t image_fd_height;
+ hbool_t prefetched;
+ int prefetch_type_id;
+ int32_t age;
+
+#ifndef NDEBUG /* debugging field */
+ int serialization_count;
+#endif /* NDEBUG */
+
/* fields supporting tag lists */
struct H5C_cache_entry_t *tl_next;
struct H5C_cache_entry_t *tl_prev;
@@ -1447,6 +1646,168 @@ typedef struct H5C_cache_entry_t {
#endif /* H5C_COLLECT_CACHE_ENTRY_STATS */
} H5C_cache_entry_t;
+
+/****************************************************************************
+ *
+ * structure H5C_image_entry_t
+ *
+ * Instances of the H5C_image_entry_t structure are used to store data on
+ * metadata cache entries used in the construction of the metadata cache
+ * image block. In essence this structure is a greatly simplified version
+ * of H5C_cache_entry_t.
+ *
+ * The fields of this structure are discussed individually below:
+ *
+ * JRM - 8/5/15
+ *
+ * magic: Unsigned 32 bit integer that must always be set to
+ * H5C__H5C_IMAGE_ENTRY_T_MAGIC when the entry is valid.
+ * The field must be set to H5C__H5C_IMAGE__ENTRY_T_BAD_MAGIC
+ * just before the entry is freed.
+ *
+ * addr: Base address of the cache entry on disk.
+ *
+ * size: Length of the cache entry on disk in bytes.
+ *
+ * ring: Instance of H5C_ring_t indicating the flush ordering ring
+ * to which this entry is assigned.
+ *
+ * age: Number of times this prefetech entry has appeared in
+ * the current sequence of cache images. This field is
+ * initialized to 0 if the instance of H5C_image_entry_t
+ * is constructed from a regular entry.
+ *
+ * If the instance is constructed from a prefetched entry
+ * currently residing in the metadata cache, the field is
+ * set to 1 + the age of the prefetched entry, or to
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX if that sum exceeds
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX.
+ *
+ * type_id: Integer field containing the type ID of the entry.
+ *
+ * lru_rank: Rank of the entry in the LRU just prior to file close.
+ *
+ * Note that the first entry on the LRU has lru_rank 1,
+ * and that entries not on the LRU at that time will have
+ * either lru_rank -1 (if pinned) or 0 (if loaded during
+ * the process of flushing the cache.
+ *
+ * is_dirty: Boolean flag indicating whether the contents of the cache
+ * entry has been modified since the last time it was written
+ * to disk as a regular piece of metadata.
+ *
+ * image_fd_height: Flush dependency height of the entry in the cache image.
+ *
+ * The flush dependency height of any entry involved in a
+ * flush dependency relationship is defined to be the
+ * longest flush dependency path from that entry to an entry
+ * with no flush depenency children.
+ *
+ * Since the image_fd_height is used to order entries in the
+ * cache image so that fd parents preceed fd children, for
+ * purposes of this field, an entry is at flush dependency
+ * level 0 if it either has no children, or if all of its
+ * children are not in the cache image.
+ *
+ * Note that if a child in a flush dependency relationship is
+ * dirty and in the cache image, and its parent is dirty and
+ * not in the cache image, then the child must be excluded
+ * from the cache image to maintain flush ordering.
+ *
+ * fd_parent_count: If the entry is a child in one or more flush dependency
+ * relationships, this field contains the number of flush
+ * dependency parents.
+ *
+ * In all other cases, the field is set to zero.
+ *
+ * Note that while this count is initially taken from the
+ * flush dependency fields in the associated instance of
+ * H5C_cache_entry_t, if the entry is in the cache image
+ * (i.e. include_in_image is TRUE), any parents that are
+ * not in the image are removed from this count and
+ * from the fd_parent_addrs array below.
+ *
+ * Finally observe that if the entry is dirty and in the
+ * cache image, and its parent is dirty and not in the cache
+ * image, then the entry must be removed from the cache image
+ * to avoid violating the flush dependency flush ordering.
+ * This should have happened before the construction of
+ * the instance of H5C_image_entry_t.
+ *
+ * fd_parent_addrs: If the entry is a child in one or more flush dependency
+ * relationship when H5C_prep_for_file_close() is called, this
+ * field must contain a pointer to an array of size
+ * fd_parent_count containing the on disk addresses of the
+ * parents.
+ *
+ * In all other cases, the field is set to NULL.
+ *
+ * Note that while this list of addresses is initially taken
+ * from the flush dependency fields in the associated instance of
+ * H5C_cache_entry_t, if the entry is in the cache image
+ * (i.e. include_in_image is TRUE), any parents that are not
+ * in the image are removed from this list, and from the
+ * fd_parent_count above.
+ *
+ * Finally observe that if the entry is dirty and in the
+ * cache image, and its parent is dirty and not in the cache
+ * image, then the entry must be removed from the cache image
+ * to avoid violating the flush dependency flush ordering.
+ * This should have happened before the construction of
+ * the instance of H5C_image_entry_t.
+ *
+ * fd_child_count: If the entry is a parent in a flush dependency
+ * relationship, this field contains the number of flush
+ * dependency children.
+ *
+ * In all other cases, the field is set to zero.
+ *
+ * Note that while this count is initially taken from the
+ * flush dependency fields in the associated instance of
+ * H5C_cache_entry_t, if the entry is in the cache image
+ * (i.e. include_in_image is TRUE), any children
+ * that are not in the image are removed from this count.
+ *
+ * fd_dirty_child_count: If the entry is a parent in a flush dependency
+ * relationship, this field contains the number of dirty flush
+ * dependency children.
+ *
+ * In all other cases, the field is set to zero.
+ *
+ * Note that while this count is initially taken from the
+ * flush dependency fields in the associated instance of
+ * H5C_cache_entry_t, if the entry is in the cache image
+ * (i.e. include_in_image is TRUE), any dirty children
+ * that are not in the image are removed from this count.
+ *
+ * image_ptr: Pointer to void. When not NULL, this field points to a
+ * dynamically allocated block of size bytes in which the
+ * on disk image of the metadata cache entry is stored.
+ *
+ * If the entry is dirty, the pre-serialize and serialize
+ * callbacks must be used to update this image before it is
+ * written to disk
+ *
+ *
+ ****************************************************************************/
+
+typedef struct H5C_image_entry_t {
+ uint32_t magic;
+ haddr_t addr;
+ size_t size;
+ H5C_ring_t ring;
+ int32_t age;
+ int32_t type_id;
+ int32_t lru_rank;
+ hbool_t is_dirty;
+ unsigned image_fd_height;
+ uint64_t fd_parent_count;
+ haddr_t *fd_parent_addrs;
+ uint64_t fd_child_count;
+ uint64_t fd_dirty_child_count;
+ void *image_ptr;
+} H5C_image_entry_t;
+
/****************************************************************************
*
* structure H5C_auto_size_ctl_t
@@ -1736,12 +2097,98 @@ typedef struct H5C_auto_size_ctl_t {
double empty_reserve;
} H5C_auto_size_ctl_t;
+/****************************************************************************
+ *
+ * structure H5C_cache_image_ctl_t
+ *
+ * Instances of H5C_image_ctl_t are used to get and set the control
+ * fields for generation of a metadata cache image on file close.
+ *
+ * At present control of construction of a cache image is via a FAPL
+ * property at file open / create.
+ *
+ * The fields of the structure are discussed individually below:
+ *
+ * version: Integer field containing the version number of this version
+ * of the H5C_image_ctl_t structure. Any instance of
+ * H5C_image_ctl_t passed to the cache must have a known
+ * version number, or an error will be flagged.
+ *
+ * generate_image: Boolean flag indicating whether a cache image should
+ * be created on file close.
+ *
+ * save_resize_status: Boolean flag indicating whether the cache image
+ * should include the adaptive cache resize configuration and status.
+ * Note that this field is ignored at present.
+ *
+ * entry_ageout: Integer field indicating the maximum number of
+ * times a prefetched entry can appear in subsequent cache images.
+ * This field exists to allow the user to avoid the buildup of
+ * infrequently used entries in long sequences of cache images.
+ *
+ * The value of this field must lie in the range
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE (-1) to
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX (100).
+ *
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE means that no limit
+ * is imposed on number of times a prefeteched entry can appear
+ * in subsequent cache images.
+ *
+ * A value of 0 prevents prefetched entries from being included
+ * in cache images.
+ *
+ * Positive integers restrict prefetched entries to the specified
+ * number of appearances.
+ *
+ * Note that the number of subsequent cache images that a prefetched
+ * entry has appeared in is tracked in an 8 bit field. Thus, while
+ * H5AC__CACHE_IMAGE__ENTRY_AGEOUT__MAX can be increased from its
+ * current value, any value in excess of 255 will be the functional
+ * equivalent of H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE.
+ *
+ * flags: Unsigned integer containing flags controling which aspects of the
+ * cache image functinality is actually executed. The primary impetus
+ * behind this field is to allow developement of tests for partial
+ * implementations that will require little if any modification to run
+ * with the full implementation. In normal operation, all flags should
+ * be set.
+ *
+ ****************************************************************************/
+
+#define H5C_CI__GEN_MDCI_SBE_MESG ((unsigned)0x0001)
+#define H5C_CI__GEN_MDC_IMAGE_BLK ((unsigned)0x0002)
+#define H5C_CI__SUPRESS_ENTRY_WRITES ((unsigned)0x0004)
+#define H5C_CI__WRITE_CACHE_IMAGE ((unsigned)0x0008)
+
+/* This #define must set all defined H5C_CI flags. It is
+ * used in the default value for instances of H5C_cache_image_ctl_t.
+ * This value will only be modified in test code.
+ */
+#define H5C_CI__ALL_FLAGS ((unsigned)0x000F)
+
+#define H5C__DEFAULT_CACHE_IMAGE_CTL \
+{ \
+ /* version = */ H5C__CURR_CACHE_IMAGE_CTL_VER, \
+ /* generate_image = */ FALSE, \
+ /* save_resize_status = */ FALSE, \
+ /* entry_ageout = */ H5AC__CACHE_IMAGE__ENTRY_AGEOUT__NONE, \
+ /* flags = */ H5C_CI__ALL_FLAGS \
+}
+
+typedef struct H5C_cache_image_ctl_t {
+ int32_t version;
+ hbool_t generate_image;
+ hbool_t save_resize_status;
+ int32_t entry_ageout;
+ unsigned flags;
+} H5C_cache_image_ctl_t;
+
/***************************************/
/* Library-private Function Prototypes */
/***************************************/
H5_DLL H5C_t *H5C_create(size_t max_cache_size, size_t min_clean_size,
- int max_type_id, const char *(*type_name_table_ptr),
+ int max_type_id, const H5C_class_t * const *class_table_ptr,
H5C_write_permitted_func_t check_write_permitted, hbool_t write_permitted,
H5C_log_flush_func_t log_flush, void *aux_ptr);
H5_DLL herr_t H5C_set_up_logging(H5C_t *cache_ptr, const char log_location[], hbool_t start_immediately);
@@ -1770,9 +2217,11 @@ herr_t H5C_verify_tag(int id, haddr_t tag);
H5_DLL herr_t H5C_flush_to_min_clean(H5F_t *f, hid_t dxpl_id);
H5_DLL herr_t H5C_get_cache_auto_resize_config(const H5C_t *cache_ptr,
H5C_auto_size_ctl_t *config_ptr);
+H5_DLL herr_t H5C_get_cache_image_config(const H5C_t * cache_ptr,
+ H5C_cache_image_ctl_t *config_ptr);
H5_DLL herr_t H5C_get_cache_size(H5C_t *cache_ptr, size_t *max_size_ptr,
size_t *min_clean_size_ptr, size_t *cur_size_ptr,
- int32_t *cur_num_entries_ptr);
+ uint32_t *cur_num_entries_ptr);
H5_DLL herr_t H5C_get_cache_hit_rate(H5C_t *cache_ptr, double *hit_rate_ptr);
H5_DLL herr_t H5C_get_entry_status(const H5F_t *f, haddr_t addr,
size_t *size_ptr, hbool_t *in_cache_ptr, hbool_t *is_dirty_ptr,
@@ -1783,8 +2232,11 @@ H5_DLL herr_t H5C_get_evictions_enabled(const H5C_t *cache_ptr, hbool_t *evictio
H5_DLL void * H5C_get_aux_ptr(const H5C_t *cache_ptr);
H5_DLL FILE *H5C_get_trace_file_ptr(const H5C_t *cache_ptr);
H5_DLL FILE *H5C_get_trace_file_ptr_from_entry(const H5C_cache_entry_t *entry_ptr);
+H5_DLL herr_t H5C_image_stats(H5C_t * cache_ptr, hbool_t print_header);
H5_DLL herr_t H5C_insert_entry(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type,
haddr_t addr, void *thing, unsigned int flags);
+H5_DLL herr_t H5C_load_cache_image_on_next_protect(H5F_t *f, haddr_t addr,
+ hsize_t len, hbool_t rw);
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_mark_entry_unserialized(void *thing);
@@ -1799,6 +2251,8 @@ H5_DLL void * H5C_protect(H5F_t *f, hid_t dxpl_id, const H5C_class_t *type,
H5_DLL herr_t H5C_reset_cache_hit_rate_stats(H5C_t *cache_ptr);
H5_DLL herr_t H5C_resize_entry(void *thing, size_t new_size);
H5_DLL herr_t H5C_set_cache_auto_resize_config(H5C_t *cache_ptr, H5C_auto_size_ctl_t *config_ptr);
+H5_DLL herr_t H5C_set_cache_image_config(const H5F_t *f, H5C_t *cache_ptr,
+ H5C_cache_image_ctl_t *config_ptr);
H5_DLL herr_t H5C_set_evictions_enabled(H5C_t *cache_ptr, hbool_t evictions_enabled);
H5_DLL herr_t H5C_set_prefix(H5C_t *cache_ptr, char *prefix);
H5_DLL herr_t H5C_set_trace_file_ptr(H5C_t *cache_ptr, FILE *trace_file_ptr);
@@ -1810,6 +2264,7 @@ H5_DLL herr_t H5C_unpin_entry(void *thing);
H5_DLL herr_t H5C_destroy_flush_dependency(void *parent_thing, void *child_thing);
H5_DLL herr_t H5C_unprotect(H5F_t *f, hid_t dxpl_id, haddr_t addr, void *thing,
unsigned int flags);
+H5_DLL herr_t H5C_validate_cache_image_config(H5C_cache_image_ctl_t * ctl_ptr);
H5_DLL herr_t H5C_validate_resize_config(H5C_auto_size_ctl_t *config_ptr,
unsigned int tests);
H5_DLL herr_t H5C_ignore_tags(H5C_t *cache_ptr);
@@ -1819,6 +2274,8 @@ H5_DLL herr_t H5C_cork(H5C_t *cache_ptr, haddr_t obj_addr, unsigned action, hboo
H5_DLL herr_t H5C_get_entry_ring(const H5F_t *f, haddr_t addr, H5C_ring_t *ring);
H5_DLL herr_t H5C_unsettle_entry_ring(void *thing);
H5_DLL herr_t H5C_remove_entry(void *thing);
+H5_DLL herr_t H5C_cache_image_status(H5F_t * f, hbool_t *load_ci_ptr,
+ hbool_t *write_ci_ptr);
#ifdef H5_HAVE_PARALLEL
H5_DLL herr_t H5C_apply_candidate_list(H5F_t *f, hid_t dxpl_id,
@@ -1832,7 +2289,16 @@ H5_DLL herr_t H5C_mark_entries_as_clean(H5F_t *f, hid_t dxpl_id, int32_t ce_arra
#endif /* H5_HAVE_PARALLEL */
#ifndef NDEBUG /* debugging functions */
+H5_DLL hbool_t H5C_get_serialization_in_progress(const H5C_t *cache_ptr);
H5_DLL hbool_t H5C_cache_is_clean(const H5C_t *cache_ptr, H5C_ring_t inner_ring);
+H5_DLL herr_t H5C_get_entry_ptr_from_addr(H5C_t *cache_ptr, haddr_t addr,
+ void **entry_ptr_ptr);
+H5_DLL herr_t H5C_flush_dependency_exists(H5C_t *cache_ptr, haddr_t parent_addr,
+ haddr_t child_addr, hbool_t *fd_exists_ptr);
+H5_DLL herr_t H5C_verify_entry_type(H5C_t *cache_ptr, haddr_t addr,
+ const H5C_class_t *expected_type, hbool_t *in_cache_ptr,
+ hbool_t *type_ok_ptr);
+H5_DLL herr_t H5C_validate_index_list(H5C_t *cache_ptr);
#endif /* NDEBUG */
#endif /* !_H5Cprivate_H */
diff --git a/src/H5Cquery.c b/src/H5Cquery.c
index f5409f7..03aadb0 100644
--- a/src/H5Cquery.c
+++ b/src/H5Cquery.c
@@ -131,7 +131,7 @@ H5C_get_cache_size(H5C_t * cache_ptr,
size_t * max_size_ptr,
size_t * min_clean_size_ptr,
size_t * cur_size_ptr,
- int32_t * cur_num_entries_ptr)
+ uint32_t * cur_num_entries_ptr)
{
herr_t ret_value = SUCCEED; /* Return value */
diff --git a/src/H5EApkg.h b/src/H5EApkg.h
index 093403c..7540ff2 100644
--- a/src/H5EApkg.h
+++ b/src/H5EApkg.h
@@ -378,21 +378,6 @@ typedef struct H5EA__ctx_cb_t {
/* Package Private Variables */
/*****************************/
-/* H5EA header inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_EARRAY_HDR[1];
-
-/* H5EA index block inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_EARRAY_IBLOCK[1];
-
-/* H5EA index block inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_EARRAY_SBLOCK[1];
-
-/* H5EA data block inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_EARRAY_DBLOCK[1];
-
-/* H5EA data block page inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_EARRAY_DBLK_PAGE[1];
-
/* Internal extensible array testing class */
H5_DLLVAR const H5EA_class_t H5EA_CLS_TEST[1];
diff --git a/src/H5F.c b/src/H5F.c
index a43009b..d3e365a 100644
--- a/src/H5F.c
+++ b/src/H5F.c
@@ -445,6 +445,8 @@ done:
hid_t
H5Fcreate(const char *filename, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
{
+ hbool_t ci_load = FALSE; /* whether MDC ci load requested */
+ hbool_t ci_write = FALSE; /* whether MDC CI write requested */
H5F_t *new_file = NULL; /*file struct for new file */
hid_t dxpl_id = H5AC_ind_read_dxpl_id; /*dxpl used by library */
hid_t ret_value; /*return value */
@@ -490,6 +492,14 @@ H5Fcreate(const char *filename, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
if(NULL == (new_file = H5F_open(filename, flags, fcpl_id, fapl_id, dxpl_id)))
HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "unable to create file")
+ /* check to see if both SWMR and cache image are requested. Fail if so */
+ if ( H5C_cache_image_status(new_file, &ci_load, &ci_write) < 0 )
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "can't get MDC CI status")
+
+ if ( ( ( ci_load ) || ( ci_write ) ) &&
+ ( flags & (H5F_ACC_SWMR_READ | H5F_ACC_SWMR_WRITE) ) )
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "can't have both SWMR and cache image.")
+
/* Get an atom for the file */
if((ret_value = H5I_register(H5I_FILE, new_file, TRUE)) < 0)
HGOTO_ERROR(H5E_ATOM, H5E_CANTREGISTER, FAIL, "unable to atomize file")
@@ -498,9 +508,29 @@ H5Fcreate(const char *filename, unsigned flags, hid_t fcpl_id, hid_t fapl_id)
new_file->file_id = ret_value;
done:
+#if 0
+ /* Quincy: please review this. With the original cleanup code (below)
+ * my code (above) to disable the combination of SWMR and cache image,
+ * triggering results in an assertion failure in H5F_close() either
+ * immediately or on library shutdown depending on the exact location
+ * of my code.
+ *
+ * I noticed that the issue did not appear in H5Fopen(). As that function
+ * calls H5F_try_close() instead of H5F_close(), I tried copying that
+ * cleanup code here. It seems to work -- but since I'm not familiar
+ * with this section of the code, it would be good if you would check me.
+ *
+ * Please delete the old version if you buy the fix.
+ *
+ * JRM -- 12/29/16
+ */
if(ret_value < 0 && new_file)
if(H5F_close(new_file) < 0)
HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "problems closing file")
+#else
+ if(ret_value < 0 && new_file && H5F_try_close(new_file, NULL) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "problems closing file")
+#endif
FUNC_LEAVE_API(ret_value)
} /* end H5Fcreate() */
@@ -549,6 +579,8 @@ done:
hid_t
H5Fopen(const char *filename, unsigned flags, hid_t fapl_id)
{
+ hbool_t ci_load = FALSE; /* whether MDC ci load requested */
+ hbool_t ci_write = FALSE; /* whether MDC CI write requested */
H5F_t *new_file = NULL; /*file struct for new file */
hid_t dxpl_id = H5AC_ind_read_dxpl_id; /*dxpl used by library */
hid_t ret_value; /*return value */
@@ -578,6 +610,14 @@ H5Fopen(const char *filename, unsigned flags, hid_t fapl_id)
if(NULL == (new_file = H5F_open(filename, flags, H5P_FILE_CREATE_DEFAULT, fapl_id, dxpl_id)))
HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "unable to open file")
+ /* check to see if both SWMR and cache image are requested. Fail if so */
+ if ( H5C_cache_image_status(new_file, &ci_load, &ci_write) < 0 )
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "can't get MDC CI status")
+
+ if ( ( ( ci_load ) || ( ci_write ) ) &&
+ ( flags & (H5F_ACC_SWMR_READ | H5F_ACC_SWMR_WRITE) ) )
+ HGOTO_ERROR(H5E_FILE, H5E_CANTOPENFILE, FAIL, "can't have both SWMR and cache image")
+
/* Get an atom for the file */
if((ret_value = H5I_register(H5I_FILE, new_file, TRUE)) < 0)
HGOTO_ERROR(H5E_ATOM, H5E_CANTREGISTER, FAIL, "unable to atomize file handle")
@@ -1177,7 +1217,7 @@ H5Fget_mdc_size(hid_t file_id, size_t *max_size_ptr, size_t *min_clean_size_ptr,
size_t *cur_size_ptr, int *cur_num_entries_ptr)
{
H5F_t *file; /* File object for file ID */
- int32_t cur_num_entries;
+ uint32_t cur_num_entries;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_API(FAIL)
@@ -1593,6 +1633,8 @@ done:
herr_t
H5Fstart_swmr_write(hid_t file_id)
{
+ hbool_t ci_load = FALSE; /* whether MDC ci load requested */
+ hbool_t ci_write = FALSE; /* whether MDC CI write requested */
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 */
@@ -1626,6 +1668,13 @@ H5Fstart_swmr_write(hid_t file_id)
HDassert(file->shared->sblock->status_flags & H5F_SUPER_WRITE_ACCESS);
+ /* check to see if cache image is enabled. Fail if so */
+ if ( H5C_cache_image_status(file, &ci_load, &ci_write) < 0 )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "can't get MDC CI status")
+
+ if ( ( ci_load ) || ( ci_write ) )
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "can't have both SWMR and MDC cache image.")
+
/* 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")
@@ -1868,7 +1917,48 @@ H5Fget_mdc_logging_status(hid_t file_id, hbool_t *is_enabled,
done:
FUNC_LEAVE_API(ret_value)
-} /* H5Fstop_mdc_logging() */
+} /* H5Fget_mdc_logging_status() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5Fset_latest_format
+ *
+ * Purpose: Enable switching the "latest format" flag while a file is open.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * Monday, September 21, 2015
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Fset_latest_format(hid_t file_id, hbool_t latest_format)
+{
+ H5F_t *f; /* File */
+ unsigned latest_flags; /* Latest format flags for file */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE2("e", "ib", file_id, latest_format);
+
+ /* Check args */
+ if(NULL == (f = (H5F_t *)H5I_object_verify(file_id, H5I_FILE)))
+ HGOTO_ERROR(H5E_FILE, H5E_BADVALUE, FAIL, "not a file ID")
+
+ /* Check if the value is changing */
+ latest_flags = H5F_USE_LATEST_FLAGS(f, H5F_LATEST_ALL_FLAGS);
+ if(latest_format != (H5F_LATEST_ALL_FLAGS == latest_flags)) {
+ /* Call the flush routine, for this file */
+ if(H5F_flush(f, H5AC_ind_read_dxpl_id, FALSE) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTFLUSH, FAIL, "unable to flush file's cached information")
+
+ /* Toggle the 'latest format' flag */
+ H5F_SET_LATEST_FLAGS(f, latest_format ? H5F_LATEST_ALL_FLAGS : 0);
+ } /* end if */
+
+done:
+ FUNC_LEAVE_API(ret_value)
+} /* end H5Fset_latest_format() */
/*-------------------------------------------------------------------------
diff --git a/src/H5FApkg.h b/src/H5FApkg.h
index ccef562..63eacff 100644
--- a/src/H5FApkg.h
+++ b/src/H5FApkg.h
@@ -249,15 +249,6 @@ typedef struct H5FA_dblk_page_cache_ud_t {
/* Package Private Variables */
/*****************************/
-/* H5FA header inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_FARRAY_HDR[1];
-
-/* H5FA data block inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_FARRAY_DBLOCK[1];
-
-/* H5FA data block page inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_FARRAY_DBLK_PAGE[1];
-
/* Internal fixed array testing class */
H5_DLLVAR const H5FA_class_t H5FA_CLS_TEST[1];
diff --git a/src/H5FSpkg.h b/src/H5FSpkg.h
index f07ffad..4411236 100644
--- a/src/H5FSpkg.h
+++ b/src/H5FSpkg.h
@@ -199,12 +199,6 @@ struct H5FS_t {
/* Package Private Variables */
/*****************************/
-/* H5FS header inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_FSPACE_HDR[1];
-
-/* H5FS section info inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_FSPACE_SINFO[1];
-
/* Declare a free list to manage the H5FS_node_t struct */
H5FL_EXTERN(H5FS_node_t);
diff --git a/src/H5Fint.c b/src/H5Fint.c
index 8ad97a8..4783a36 100644
--- a/src/H5Fint.c
+++ b/src/H5Fint.c
@@ -188,6 +188,8 @@ H5F_get_access_plist(H5F_t *f, hbool_t app_ref)
if(H5P_set(new_plist, H5F_ACS_COLL_MD_WRITE_FLAG_NAME, &(f->coll_md_write)) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't set collective metadata read flag")
#endif /* H5_HAVE_PARALLEL */
+ if(H5P_set(new_plist, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, &(f->shared->mdc_initCacheImageCfg)) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set initial metadata cache resize config.")
/* Prepare the driver property */
driver_prop.driver_id = f->shared->lf->driver_id;
@@ -667,6 +669,8 @@ H5F_new(H5F_file_t *shared, unsigned flags, hid_t fcpl_id, hid_t fapl_id, H5FD_t
if(H5P_get(plist, H5F_ACS_COLL_MD_WRITE_FLAG_NAME, &(f->coll_md_write)) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get collective metadata write flag")
#endif /* H5_HAVE_PARALLEL */
+ if(H5P_get(plist, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, &(f->shared->mdc_initCacheImageCfg)) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't get initial metadata cache resize config")
/* Get the VFD values to cache */
f->shared->maxaddr = H5FD_get_maxaddr(lf);
@@ -750,7 +754,7 @@ H5F_new(H5F_file_t *shared, unsigned flags, hid_t fcpl_id, hid_t fapl_id, H5FD_t
* The cache might be created with a different number of elements and
* the access property list should be updated to reflect that.
*/
- if(H5AC_create(f, &(f->shared->mdc_initCacheCfg)) < 0)
+ if(H5AC_create(f, &(f->shared->mdc_initCacheCfg), &(f->shared->mdc_initCacheImageCfg)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTINIT, NULL, "unable to create metadata cache")
/* Create the file's "open object" information */
@@ -2671,3 +2675,33 @@ H5F_set_coll_md_read(H5F_t *f, H5P_coll_md_read_flag_t cmr)
} /* H5F_set_coll_md_read() */
#endif /* H5_HAVE_PARALLEL */
+
+/*-------------------------------------------------------------------------
+ * Function: H5F_set_latest_flags
+ *
+ * Purpose: Set the latest_flags field with a new value.
+ *
+ * Return: Success: SUCCEED
+ * Failure: FAIL
+ *
+ * Programmer: Quincey Koziol
+ * 4/26/16
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5F_set_latest_flags(H5F_t *f, unsigned flags)
+{
+ /* Use FUNC_ENTER_NOAPI_NOINIT_NOERR here to avoid performance issues */
+ FUNC_ENTER_NOAPI_NOINIT_NOERR
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(f->shared);
+ HDassert(0 == ((~flags) & H5F_LATEST_ALL_FLAGS));
+
+ f->shared->latest_flags = flags;
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* H5F_set_latest_flags() */
+
diff --git a/src/H5Fio.c b/src/H5Fio.c
index e215666..afe1278 100644
--- a/src/H5Fio.c
+++ b/src/H5Fio.c
@@ -302,7 +302,7 @@ H5F__evict_cache_entries(H5F_t *f, hid_t dxpl_id)
#ifndef NDEBUG
{
unsigned status = 0;
- int32_t cur_num_entries;
+ uint32_t cur_num_entries;
/* Retrieve status of the superblock */
if(H5AC_get_entry_status(f, (haddr_t)0, &status) < 0)
diff --git a/src/H5Fpkg.h b/src/H5Fpkg.h
index 11665f4..93a3978 100644
--- a/src/H5Fpkg.h
+++ b/src/H5Fpkg.h
@@ -287,6 +287,12 @@ struct H5F_file_t {
/* metadata cache. This structure is */
/* fixed at creation time and should */
/* not change thereafter. */
+ H5AC_cache_image_config_t
+ mdc_initCacheImageCfg; /* initial configuration for the */
+ /* generate metadata cache image on */
+ /* close option. This structure is */
+ /* fixed at creation time and should */
+ /* not change thereafter. */
hbool_t use_mdc_logging; /* Set when metadata logging is desired */
hbool_t start_mdc_log_on_access; /* set when mdc logging should */
/* begin on file access/create */
@@ -369,9 +375,6 @@ H5FL_EXTERN(H5F_t);
/* Declare a free list to manage the H5F_file_t struct */
H5FL_EXTERN(H5F_file_t);
-H5_DLLVAR const H5AC_class_t H5AC_SUPERBLOCK[1];
-H5_DLLVAR const H5AC_class_t H5AC_DRVRINFO[1];
-
/******************************/
/* Package Private Prototypes */
@@ -400,7 +403,8 @@ H5_DLL herr_t H5F__super_free(H5F_super_t *sblock);
/* Superblock extension related routines */
H5_DLL herr_t H5F_super_ext_open(H5F_t *f, haddr_t ext_addr, H5O_loc_t *ext_ptr);
-H5_DLL herr_t H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg, hbool_t may_create);
+H5_DLL herr_t H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id,
+ void *mesg, hbool_t may_create, unsigned mesg_flags);
H5_DLL herr_t H5F_super_ext_remove_msg(H5F_t *f, hid_t dxpl_id, unsigned id);
H5_DLL herr_t H5F_super_ext_close(H5F_t *f, H5O_loc_t *ext_ptr, hid_t dxpl_id,
hbool_t was_created);
diff --git a/src/H5Fprivate.h b/src/H5Fprivate.h
index bcc56c6..7d288fa 100644
--- a/src/H5Fprivate.h
+++ b/src/H5Fprivate.h
@@ -317,6 +317,7 @@
#define H5F_SET_GRP_BTREE_SHARED(F, RC) (((F)->shared->grp_btree_shared = (RC)) ? SUCCEED : FAIL)
#define H5F_USE_TMP_SPACE(F) ((F)->shared->use_tmp_space)
#define H5F_IS_TMP_ADDR(F, ADDR) (H5F_addr_le((F)->shared->tmp_addr, (ADDR)))
+#define H5F_SET_LATEST_FLAGS(F, FL) ((F)->shared->latest_flags = (FL))
#ifdef H5_HAVE_PARALLEL
#define H5F_COLL_MD_READ(F) ((F)->coll_md_read)
#endif /* H5_HAVE_PARALLEL */
@@ -367,6 +368,7 @@
#define H5F_SET_GRP_BTREE_SHARED(F, RC) (H5F_set_grp_btree_shared((F), (RC)))
#define H5F_USE_TMP_SPACE(F) (H5F_use_tmp_space(F))
#define H5F_IS_TMP_ADDR(F, ADDR) (H5F_is_tmp_addr((F), (ADDR)))
+#define H5F_SET_LATEST_FLAGS(F, FL) (H5F_set_latest_flags((F), (FL)))
#ifdef H5_HAVE_PARALLEL
#define H5F_COLL_MD_READ(F) (H5F_coll_md_read(F))
#endif /* H5_HAVE_PARALLEL */
@@ -481,6 +483,7 @@
#define H5F_ACS_EVICT_ON_CLOSE_FLAG_NAME "evict_on_close_flag" /* Whether or not the metadata cache will evict objects on close */
#define H5F_ACS_CORE_WRITE_TRACKING_PAGE_SIZE_NAME "core_write_tracking_page_size" /* The page size in kiB when core VFD write tracking is enabled */
#define H5F_ACS_COLL_MD_WRITE_FLAG_NAME "collective_metadata_write" /* property indicating whether metadata writes are done collectively or not */
+#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME "mdc_initCacheImageCfg" /* Initial metadata cache image creation configuration */
/* ======================== File Mount properties ====================*/
#define H5F_MNT_SYM_LOCAL_NAME "local" /* Whether absolute symlinks local to file. */
@@ -700,6 +703,7 @@ H5_DLL struct H5UC_t *H5F_grp_btree_shared(const H5F_t *f);
H5_DLL herr_t H5F_set_grp_btree_shared(H5F_t *f, struct H5UC_t *rc);
H5_DLL hbool_t H5F_use_tmp_space(const H5F_t *f);
H5_DLL hbool_t H5F_is_tmp_addr(const H5F_t *f, haddr_t addr);
+H5_DLL herr_t H5F_set_latest_flags(H5F_t *f, unsigned flags);
#ifdef H5_HAVE_PARALLEL
H5_DLL H5P_coll_md_read_flag_t H5F_coll_md_read(const H5F_t *f);
H5_DLL void H5F_set_coll_md_read(H5F_t *f, H5P_coll_md_read_flag_t flag);
diff --git a/src/H5Fpublic.h b/src/H5Fpublic.h
index a79da75..c57a821 100644
--- a/src/H5Fpublic.h
+++ b/src/H5Fpublic.h
@@ -246,6 +246,7 @@ 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);
+H5_DLL herr_t H5Fset_latest_format(hid_t file_id, hbool_t latest_format);
H5_DLL herr_t H5Fstart_mdc_logging(hid_t file_id);
H5_DLL herr_t H5Fstop_mdc_logging(hid_t file_id);
H5_DLL herr_t H5Fget_mdc_logging_status(hid_t file_id,
diff --git a/src/H5Fsuper.c b/src/H5Fsuper.c
index 893ce26..e203c38 100644
--- a/src/H5Fsuper.c
+++ b/src/H5Fsuper.c
@@ -290,7 +290,7 @@ H5F__update_super_ext_driver_msg(H5F_t *f, hid_t dxpl_id)
*/
drvinfo.len = driver_size;
drvinfo.buf = dbuf;
- if(H5F_super_ext_write_msg(f, dxpl_id, H5O_DRVINFO_ID, &drvinfo, FALSE) < 0)
+ if(H5F_super_ext_write_msg(f, dxpl_id, H5O_DRVINFO_ID, &drvinfo, FALSE, H5O_MSG_NO_FLAGS_SET) < 0)
HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "unable to update driver info header message")
} /* end if driver_size > 0 */
} /* end if !H5F_HAS_FEATURE(f, H5FD_FEAT_IGNORE_DRVRINFO) */
@@ -692,6 +692,34 @@ H5F__super_read(H5F_t *f, hid_t dxpl_id, hbool_t initial_read)
f->shared->fs_addr[u] = fsinfo.fs_addr[u-1];
} /* end if */
+ /* Check for the extension having a 'metadata cache image' message */
+ if((status = H5O_msg_exists(&ext_loc, H5O_MDCI_MSG_ID, dxpl_id)) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_EXISTS, FAIL, "unable to read object header")
+ if(status) {
+ hbool_t rw = ((rw_flags & H5AC__READ_ONLY_FLAG) == 0);
+ H5O_mdci_t mdci_msg;
+
+ /* if the metadata cache image superblock extension message exists,
+ * read its contents and pass the data on to the metadata cache.
+ * Given this data, the cache will load and decode the metadata
+ * cache image block, decoded it and load its contents into the
+ * the cache on the test protect call.
+ *
+ * Further, if the file is opened R/W, the metadata cache will
+ * delete the metadata cache image superblock extension and free
+ * the cache image block. Don't do this now as f->shared
+ * is not fully setup, which complicates matters.
+ */
+
+ /* Retrieve the 'metadata cache image message' structure */
+ if(NULL == H5O_msg_read(&ext_loc, H5O_MDCI_MSG_ID, &mdci_msg, dxpl_id))
+ HGOTO_ERROR(H5E_FILE, H5E_CANTGET, FAIL, "unable to get metadata cache image message")
+
+ /* Indicate to the cache that there's an image to load on first protect call */
+ if(H5AC_load_cache_image_on_next_protect(f, mdci_msg.addr, mdci_msg.size, rw) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTLOAD, FAIL, "call to H5AC_load_cache_image_on_next_protect failed");
+ } /* end if */
+
/* Close superblock extension */
if(H5F_super_ext_close(f, &ext_loc, dxpl_id, FALSE) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTCLOSEOBJ, FAIL, "unable to close file's superblock extension")
@@ -737,7 +765,7 @@ H5F__super_read(H5F_t *f, hid_t dxpl_id, hbool_t initial_read)
HDassert(f->shared->sblock == NULL);
f->shared->sblock = sblock;
#endif /* JRM */
- if(H5F_super_ext_write_msg(f, dxpl_id, H5O_DRVINFO_ID, &drvinfo, FALSE) < 0)
+ if(H5F_super_ext_write_msg(f, dxpl_id, H5O_DRVINFO_ID, &drvinfo, FALSE, H5O_MSG_NO_FLAGS_SET) < 0)
HGOTO_ERROR(H5E_FILE, H5E_WRITEERROR, FAIL, "error in writing message to superblock extension")
#if 1 /* bug fix test code -- tidy this up if all goes well */ /* JRM */
@@ -1365,7 +1393,8 @@ done:
*-------------------------------------------------------------------------
*/
herr_t
-H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg, hbool_t may_create)
+H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg,
+ hbool_t may_create, unsigned mesg_flags)
{
H5P_genplist_t *dxpl = NULL; /* DXPL for setting ring */
H5AC_ring_t orig_ring = H5AC_RING_INV; /* Original ring value */
@@ -1410,7 +1439,7 @@ H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg, hbool_
HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "Message should not exist")
/* Create the message with ID in the superblock extension */
- if(H5O_msg_create(&ext_loc, id, H5O_MSG_FLAG_DONTSHARE, H5O_UPDATE_TIME, mesg, dxpl_id) < 0)
+ if(H5O_msg_create(&ext_loc, id, (mesg_flags | H5O_MSG_FLAG_DONTSHARE), H5O_UPDATE_TIME, mesg, dxpl_id) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to create the message in object header")
} /* end if */
else {
@@ -1418,7 +1447,7 @@ H5F_super_ext_write_msg(H5F_t *f, hid_t dxpl_id, unsigned id, void *mesg, hbool_
HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "Message should exist")
/* Update the message with ID in the superblock extension */
- if(H5O_msg_write(&ext_loc, id, H5O_MSG_FLAG_DONTSHARE, H5O_UPDATE_TIME, mesg, dxpl_id) < 0)
+ if(H5O_msg_write(&ext_loc, id, (mesg_flags | H5O_MSG_FLAG_DONTSHARE), H5O_UPDATE_TIME, mesg, dxpl_id) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to write the message in object header")
} /* end else */
diff --git a/src/H5Gpkg.h b/src/H5Gpkg.h
index c994da3..1e8ad31 100644
--- a/src/H5Gpkg.h
+++ b/src/H5Gpkg.h
@@ -312,9 +312,6 @@ typedef struct H5G_copy_file_ud_t {
*/
H5_DLLVAR H5B_class_t H5B_SNODE[1];
-/* The cache subclass */
-H5_DLLVAR const H5AC_class_t H5AC_SNODE[1];
-
/* The v2 B-tree class for indexing 'name' field on links */
H5_DLLVAR const H5B2_class_t H5G_BT2_NAME[1];
diff --git a/src/H5HFcache.c b/src/H5HFcache.c
index 302fe04..8e5ed76 100644
--- a/src/H5HFcache.c
+++ b/src/H5HFcache.c
@@ -116,14 +116,17 @@ static herr_t H5HF__cache_dblock_free_icr(void *thing);
/* Debugging Function Prototypes */
#ifndef NDEBUG
-static herr_t H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr,
- hbool_t *clean);
-static herr_t H5HF__cache_verify_iblock_descendants_clean(H5F_t *f,
- H5HF_indirect_t *iblock, unsigned *iblock_status, hbool_t *clean);
-static herr_t H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f,
- H5HF_indirect_t *iblock, hbool_t *clean, hbool_t *has_dblocks);
-static herr_t H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f,
- H5HF_indirect_t *iblock, hbool_t *clean, hbool_t *has_iblocks);
+static herr_t H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, hid_t dxpl_id,
+ H5HF_hdr_t * hdr, hbool_t *fd_clean, hbool_t *clean);
+static herr_t H5HF__cache_verify_iblock_descendants_clean(H5F_t *f,
+ hid_t dxpl_id, haddr_t fd_parent_addr, H5HF_indirect_t *iblock,
+ unsigned *iblock_status, hbool_t * fd_clean, hbool_t *clean);
+static herr_t H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f,
+ haddr_t fd_parent_addr, H5HF_indirect_t *iblock, hbool_t *fd_clean,
+ hbool_t *clean, hbool_t *has_dblocks);
+static herr_t H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f,
+ hid_t dxpl_id, haddr_t fd_parent_addr, H5HF_indirect_t *iblock,
+ hbool_t *fd_clean, hbool_t *clean, hbool_t *has_iblocks);
#endif /* NDEBUG */
@@ -687,6 +690,7 @@ H5HF__cache_hdr_pre_serialize(H5F_t *f, hid_t H5_ATTR_UNUSED dxpl_id,
#ifndef NDEBUG
{
hbool_t descendants_clean = TRUE;
+ hbool_t fd_children_clean = TRUE;
/* Verify that flush dependencies are working correctly. Do this
* by verifying that either:
@@ -701,10 +705,22 @@ H5HF__cache_hdr_pre_serialize(H5F_t *f, hid_t H5_ATTR_UNUSED dxpl_id,
* constraint is met by default.
*
* Do this with a call to H5HF__cache_verify_hdr_descendants_clean().
+ *
+ * Note that decendants need not be clean if the pre_serialize call
+ * is made during a cache serialization instead of an entry or cache
+ * flush.
+ *
+ * Note also that with the recent change in the definition of flush
+ * dependency, not all decendants need be clean -- only direct flush
+ * dependency children.
+ *
+ * Finally, observe that the H5HF__cache_verify_hdr_descendants_clean()
+ * call still looks for dirty descendants. At present we do not check
+ * this value.
*/
- if(H5HF__cache_verify_hdr_descendants_clean((H5F_t *)f, hdr, &descendants_clean) < 0)
+ if(H5HF__cache_verify_hdr_descendants_clean((H5F_t *)f, dxpl_id, hdr, &fd_children_clean, &descendants_clean) < 0)
HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify hdr descendants clean.")
- HDassert(descendants_clean);
+ HDassert(fd_children_clean);
}
#endif /* NDEBUG */
@@ -1176,8 +1192,9 @@ H5HF__cache_iblock_image_len(const void *_thing, size_t *image_len)
* and if so, to move it to real file space before the entry is
* serialized.
*
- * In debug compiles, this function also verifies that all children
- * of this indirect block are either clean or are not in cache.
+ * In debug compiles, this function also verifies that all
+ * immediate flush dependency children of this indirect block
+ * are either clean or are not in cache.
*
* Return: Success: SUCCEED
* Failure: FAIL
@@ -1217,10 +1234,12 @@ H5HF__cache_iblock_pre_serialize(H5F_t *f, hid_t dxpl_id, void *_thing,
#ifndef NDEBUG
{
hbool_t descendants_clean = TRUE;
+ hbool_t fd_children_clean = TRUE;
unsigned iblock_status = 0;
/* verify that flush dependencies are working correctly. Do this
- * by verifying that all children of this iblock are clean.
+ * by verifying that all immediate flush dependency children of this
+ * iblock are clean.
*/
if(H5AC_get_entry_status(f, iblock->addr, &iblock_status) < 0)
HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get iblock status")
@@ -1230,9 +1249,9 @@ H5HF__cache_iblock_pre_serialize(H5F_t *f, hid_t dxpl_id, void *_thing,
* there is no need to check to see if it is pinned or protected, or to
* protect it if it is not.
*/
- if(H5HF__cache_verify_iblock_descendants_clean((H5F_t *)f, iblock, &iblock_status, &descendants_clean) < 0)
+ if(H5HF__cache_verify_iblock_descendants_clean((H5F_t *)f, dxpl_id, iblock->addr, iblock, &iblock_status, &fd_children_clean, &descendants_clean) < 0)
HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify descendants clean.")
- HDassert(descendants_clean);
+ HDassert(fd_children_clean);
}
#endif /* NDEBUG */
@@ -2585,6 +2604,54 @@ done:
* instance of H5HF_hdr_t are clean. Set *clean to
* TRUE if this is the case, and to FALSE otherwise.
*
+ * Update -- 8/24/15
+ *
+ * With the advent of the metadata cache image feature, it is
+ * possible for the pre-serialize and serialize calls to be
+ * invoked outside of a flush. While this serialization
+ * observes flush dependencies for the order of serialization,
+ * the entries are not written to disk, and hence dirty entries
+ * remain dirty.
+ *
+ * To address this, updated the sanity checks in this function
+ * to treat entries whose images are up to date as clean if
+ * a cache serialization is in progress.
+ *
+ * Update -- 9/29/16
+ *
+ * The implementation of flush dependencies has been changed.
+ * Prior to this change, a flush dependency parent could be
+ * flushed if and only if all its flush dependency decendants
+ * were clean. In the new definition, a flush dependency
+ * parent can be flushed if all its immediate flush dependency
+ * children are clean, regardless of any other dirty
+ * decendants.
+ *
+ * Further, metadata cache entries are now allowed to have
+ * multiple flush dependency parents.
+ *
+ * This means that the fractal heap is no longer ncessarily
+ * flushed from the bottom up.
+ *
+ * For example, it is now possible for a dirty fractal heap
+ * header to be flushed before a dirty dblock, as long as the
+ * there in an interviening iblock, and the header has no
+ * dirty immediate flush dependency children.
+ *
+ * Also, I gather that under some circumstances, a dblock
+ * will be direct a flush dependency child both of the iblock
+ * that points to it, and of the fractal heap header.
+ *
+ * As a result of these changes, the functionality of these
+ * sanity checking routines has been modified significantly.
+ * Instead of scanning the fractal heap from a starting point
+ * down, and verifying that there were no dirty entries, the
+ * functions now scan downward from the starting point and
+ * verify that there are no dirty flush dependency children
+ * of the specified flush dependency parent. In passing,
+ * they also walk the data structure, and verify it.
+ *
+ *
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
@@ -2594,9 +2661,10 @@ done:
*/
#ifndef NDEBUG
static herr_t
-H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr,
- hbool_t *clean)
+H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, hid_t dxpl_id,
+ H5HF_hdr_t *hdr, hbool_t *fd_clean, hbool_t *clean)
{
+ hbool_t fd_exists = FALSE; /* whether flush dependency exists. */
haddr_t hdr_addr; /* Address of header */
unsigned hdr_status = 0; /* Header cache entry status */
herr_t ret_value = SUCCEED; /* Return value */
@@ -2608,6 +2676,7 @@ H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr,
HDassert(hdr);
HDassert(hdr->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(hdr->cache_info.type == H5AC_FHEAP_HDR);
+ HDassert(fd_clean);
HDassert(clean);
hdr_addr = hdr->cache_info.addr;
HDassert(hdr_addr == hdr->heap_addr);
@@ -2672,15 +2741,165 @@ H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr,
root_iblock_in_cache = ( (root_iblock_status & H5AC_ES__IN_CACHE) != 0);
HDassert(root_iblock_in_cache || (root_iblock == NULL));
- if(!root_iblock_in_cache) /* we are done */
- *clean = TRUE;
- else if(root_iblock_status & H5AC_ES__IS_DIRTY)
- *clean = FALSE;
+ if(!root_iblock_in_cache) { /* we are done */
+ *clean = TRUE;
+ *fd_clean = TRUE;
+ } /* end if */
+ else if((root_iblock_status & H5AC_ES__IS_DIRTY) &&
+ (((root_iblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) ||
+ (!H5AC_get_serialization_in_progress(f)))) {
+ *clean = FALSE;
+
+ /* verify that a flush dependency exists between the header and
+ * the root inode.
+ */
+ if(H5AC_flush_dependency_exists(f, hdr->heap_addr, root_iblock_addr, &fd_exists) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency")
+ HDassert(fd_exists);
+
+ *fd_clean = FALSE;
+ } /* end else-if */
+ else { /* must examine children */
+ hbool_t unprotect_root_iblock = FALSE;
+
+ /* At this point, the root iblock may be pinned, protected,
+ * both, or neither, and we may or may not have a pointer
+ * to root iblock in memory.
+ *
+ * Before we call H5HF__cache_verify_iblock_descendants_clean(),
+ * we must ensure that the root iblock is either pinned or
+ * protected or both, and that we have a pointer to it.
+ * Do this as follows:
+ */
+ if(root_iblock == NULL) { /* we don't have ptr to root iblock */
+ if(0 == (root_iblock_status & H5AC_ES__IS_PROTECTED)) {
+ /* just protect the root iblock -- this will give us
+ * the pointer we need to proceed, and ensure that
+ * it is locked into the metadata cache for the
+ * duration.
+ *
+ * Note that the udata is only used in the load callback.
+ * While the fractal heap makes heavy use of the udata
+ * in this case, since we know that the entry is in cache,
+ * we can pass NULL udata.
+ *
+ * The tag specified in the dxpl we received
+ * as a parameter (via dxpl_id) may not be correct.
+ * Grab the (hopefully) correct tag from the header,
+ * and load it into the dxpl via the H5_BEGIN_TAG and
+ * H5_END_TAG macros. Note that any error bracked by
+ * these macros must be reported with HGOTO_ERROR_TAG.
+ */
+ H5_BEGIN_TAG(dxpl_id, hdr->heap_addr, FAIL)
+
+ if(NULL == (root_iblock = (H5HF_indirect_t *)H5AC_protect(f, dxpl_id, H5AC_FHEAP_IBLOCK, root_iblock_addr, NULL, H5AC__READ_ONLY_FLAG)))
+ HGOTO_ERROR_TAG(H5E_HEAP, H5E_CANTPROTECT, FAIL, "H5AC_protect() faild.")
+
+ H5_END_TAG(FAIL)
+
+ unprotect_root_iblock = TRUE;
+ } /* end if */
+ else {
+ /* the root iblock is protected, and we have no
+ * legitimate way of getting a pointer to it.
+ *
+ * We square this circle by using the
+ * H5AC_get_entry_ptr_from_addr() to get the needed
+ * pointer.
+ *
+ * WARNING: This call should be used only in debugging
+ * routines, and it should be avoided there when
+ * possible.
+ *
+ * Further, if we ever multi-thread the cache,
+ * this routine will have to be either discarded
+ * or heavily re-worked.
+ *
+ * Finally, keep in mind that the entry whose
+ * pointer is obtained in this fashion may not
+ * be in a stable state.
+ *
+ * Assuming that the flush dependency code is working
+ * as it should, the only reason for the root iblock to
+ * be unpinned is if none of its children are in cache.
+ * This unfortunately means that if it is protected and
+ * not pinned, the fractal heap is in the process of loading
+ * or inserting one of its children. The obvious
+ * implication is that there is a significant chance that
+ * the root iblock is in an unstable state.
+ *
+ * All this suggests that using
+ * H5AC_get_entry_ptr_from_addr() to obtain the pointer
+ * to the protected root iblock is questionable here.
+ * However, since this is test/debugging code, I expect
+ * that we will use this approach until it causes problems,
+ * or we think of a better way.
+ */
+ if(H5AC_get_entry_ptr_from_addr(f, root_iblock_addr, (void **)(&root_iblock)) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "H5AC_get_entry_ptr_from_addr() failed.")
+ HDassert(root_iblock);
+ } /* end else */
+ } /* end if */
+ else { /* root_iblock != NULL */
+ /* we have the pointer to the root iblock. Protect it
+ * if it is neither pinned nor protected -- otherwise we
+ * are ready to go.
+ */
+ H5HF_indirect_t * iblock = NULL;
+
+ if(((root_iblock_status & H5AC_ES__IS_PINNED) == 0) &&
+ ((root_iblock_status & H5AC_ES__IS_PROTECTED) == 0)) {
+ /* the root iblock is neither pinned nor protected -- hence
+ * we must protect it before we proceed
+ *
+ * Note that the udata is only used in the load callback.
+ * While the fractal heap makes heavy use of the udata
+ * in this case, since we know that the entry is in cache,
+ * we can pass NULL udata.
+ *
+ * The tag associated specified in the dxpl we received
+ * as a parameter (via dxpl_id) may not be correct.
+ * Grab the (hopefully) correct tag from the header,
+ * and load it into the dxpl via the H5_BEGIN_TAG and
+ * H5_END_TAG macros. Note that any error bracked by
+ * these macros must be reported with HGOTO_ERROR_TAG.
+ */
+ H5_BEGIN_TAG(dxpl_id, hdr->heap_addr, FAIL)
+
+ if(NULL == (iblock = (H5HF_indirect_t *)H5AC_protect(f, dxpl_id, H5AC_FHEAP_IBLOCK, root_iblock_addr, NULL, H5AC__READ_ONLY_FLAG)))
+ HGOTO_ERROR_TAG(H5E_HEAP, H5E_CANTPROTECT, FAIL, "H5AC_protect() faild.")
+
+ H5_END_TAG(FAIL)
+
+ unprotect_root_iblock = TRUE;
+ HDassert(iblock == root_iblock);
+ } /* end if */
+ } /* end else */
+
+ /* at this point, one way or another, the root iblock is locked
+ * in memory for the duration of the call. Do some sanity checks,
+ * and then call H5HF__cache_verify_iblock_descendants_clean().
+ */
+ HDassert(root_iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(root_iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
+
+ if(H5HF__cache_verify_iblock_descendants_clean(f, dxpl_id, hdr->heap_addr, root_iblock, &root_iblock_status, fd_clean, clean) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify root iblock & descendants clean.")
+
+ /* Unprotect the root indirect block if required */
+ if(unprotect_root_iblock) {
+ HDassert(root_iblock);
+ if(H5AC_unprotect(f, dxpl_id, H5AC_FHEAP_IBLOCK, root_iblock_addr, root_iblock, H5AC__NO_FLAGS_SET) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "H5AC_unprotect() faild.")
+ } /* end if */
+ } /* end else */
} /* end if */
else if((hdr->man_dtable.curr_root_rows == 0) &&
(HADDR_UNDEF != hdr->man_dtable.table_addr)) {
haddr_t root_dblock_addr;
unsigned root_dblock_status = 0;
+ hbool_t in_cache;
+ hbool_t type_ok;
/* this is scenario 2 -- we have a root dblock */
root_dblock_addr = hdr->man_dtable.table_addr;
@@ -2688,25 +2907,48 @@ H5HF__cache_verify_hdr_descendants_clean(H5F_t *f, H5HF_hdr_t *hdr,
HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get root dblock status")
if(root_dblock_status & H5AC_ES__IN_CACHE) {
+ if(H5AC_verify_entry_type(f, root_dblock_addr, &H5AC_FHEAP_DBLOCK[0], &in_cache, &type_ok) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check dblock type")
+ HDassert(in_cache);
+ if(!type_ok)
+ HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock addr doesn't refer to a dblock?!?")
+
/* If a root dblock is in cache, it must have a flush
- * dependency relationship with the header.
+ * dependency relationship with the header, and it
+ * may not be the parent in any flush dependency
+ * relationship.
+ *
+ * We don't test this fully, but we will verify that
+ * the root iblock is a child in a flush dependency
+ * relationship with the header.
*/
- if(0 == (root_dblock_status & H5AC_ES__IS_FLUSH_DEP_CHILD))
- HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock in cache and not a flush dep child.")
+ if(H5AC_flush_dependency_exists(f, hdr->heap_addr, root_dblock_addr, &fd_exists) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency")
+ if(!fd_exists)
+ HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock is not a flush dep parent of header.")
+
if(0 != (root_dblock_status & H5AC_ES__IS_FLUSH_DEP_PARENT))
HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "root dblock in cache and is a flush dep parent.")
- if(root_dblock_status & H5AC_ES__IS_DIRTY)
- *clean = FALSE;
- } /* end if */
- else /* root dblock not in cache */
- *clean = TRUE;
+ *clean = !((root_dblock_status & H5AC_ES__IS_DIRTY) &&
+ (((root_dblock_status &
+ H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) ||
+ (!H5AC_get_serialization_in_progress(f))));
+
+ *fd_clean = *clean;
+ } /* end if */
+ else { /* root dblock not in cache */
+ *fd_clean = TRUE;
+ *clean = TRUE;
+ } /* end else */
} /* end else-if */
- else
- /* this is scenario 3 -- the fractal heap is empty, and we
- * have nothing to do.
- */
- *clean = TRUE;
+ else {
+ /* this is scenario 3 -- the fractal heap is empty, and we
+ * have nothing to do.
+ */
+ *fd_clean = TRUE;
+ *clean = TRUE;
+ } /* end else */
done:
FUNC_LEAVE_NOAPI(ret_value)
@@ -2741,6 +2983,40 @@ done:
* H5HF__cache_verify_descendant_iblocks_clean() are
* recursive co-routines.
*
+ * Update -- 9/29/16
+ *
+ * The implementation of flush dependencies has been changed.
+ * Prior to this change, a flush dependency parent could be
+ * flushed if and only if all its flush dependency decendants
+ * were clean. In the new definition, a flush dependency
+ * parent can be flushed if all its immediate flush dependency
+ * children are clean, regardless of any other dirty
+ * decendants.
+ *
+ * Further, metadata cache entries are now allowed to have
+ * multiple flush dependency parents.
+ *
+ * This means that the fractal heap is no longer ncessarily
+ * flushed from the bottom up.
+ *
+ * For example, it is now possible for a dirty fractal heap
+ * header to be flushed before a dirty dblock, as long as the
+ * there in an interviening iblock, and the header has no
+ * dirty immediate flush dependency children.
+ *
+ * Also, I gather that under some circumstances, a dblock
+ * will be direct a flush dependency child both of the iblock
+ * that points to it, and of the fractal heap header.
+ *
+ * As a result of these changes, the functionality of these
+ * sanity checking routines has been modified significantly.
+ * Instead of scanning the fractal heap from a starting point
+ * down, and verifying that there were no dirty entries, the
+ * functions now scan downward from the starting point and
+ * verify that there are no dirty flush dependency children
+ * of the specified flush dependency parent. In passing,
+ * they also walk the data structure, and verify it.
+ *
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
@@ -2750,8 +3026,9 @@ done:
*/
#ifndef NDEBUG
static herr_t
-H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, H5HF_indirect_t *iblock,
- unsigned *iblock_status, hbool_t *clean)
+H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, hid_t dxpl_id,
+ haddr_t fd_parent_addr, H5HF_indirect_t *iblock, unsigned *iblock_status,
+ hbool_t * fd_clean, hbool_t *clean)
{
hbool_t has_dblocks = FALSE;
hbool_t has_iblocks = FALSE;
@@ -2761,17 +3038,19 @@ H5HF__cache_verify_iblock_descendants_clean(H5F_t *f, H5HF_indirect_t *iblock,
/* Sanity checks */
HDassert(f);
+ HDassert(H5F_addr_defined(fd_parent_addr));
HDassert(iblock);
HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
HDassert(iblock_status);
- HDassert(clean);
- HDassert(*clean);
+ HDassert(fd_clean);
+ HDassert(*fd_clean);
+ HDassert(clean); /* note that *clean need not be TRUE */
- if((*clean) && H5HF__cache_verify_iblocks_dblocks_clean(f, iblock, clean, &has_dblocks) < 0)
+ if((*fd_clean) && H5HF__cache_verify_iblocks_dblocks_clean(f, fd_parent_addr, iblock, fd_clean, clean, &has_dblocks) < 0)
HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify dblocks clean.")
- if((*clean) && H5HF__cache_verify_descendant_iblocks_clean(f, iblock, clean, &has_iblocks) < 0)
+ if((*fd_clean) && H5HF__cache_verify_descendant_iblocks_clean(f, dxpl_id, fd_parent_addr, iblock, fd_clean, clean, &has_iblocks) < 0)
HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify iblocks clean.")
/* verify that flush dependency setup is plausible */
@@ -2808,6 +3087,53 @@ done:
* during the call. Caller must ensure that this is
* the case before the call.
*
+ * Update -- 8/24/15
+ *
+ * With the advent of the metadata cache image feature, it is
+ * possible for the pre-serialize and serialize calls to be
+ * invoked outside of a flush. While this serialization
+ * observes flush dependencies for the order of serialization,
+ * the entries are not written to disk, and hence dirty entries
+ * remain dirty.
+ *
+ * To address this, updated the sanity checks in this function
+ * to treat entries whose images are up to date as clean if
+ * a cache serialization is in progress.
+ *
+ * Update -- 9/29/16
+ *
+ * The implementation of flush dependencies has been changed.
+ * Prior to this change, a flush dependency parent could be
+ * flushed if and only if all its flush dependency decendants
+ * were clean. In the new definition, a flush dependency
+ * parent can be flushed if all its immediate flush dependency
+ * children are clean, regardless of any other dirty
+ * decendants.
+ *
+ * Further, metadata cache entries are now allowed to have
+ * multiple flush dependency parents.
+ *
+ * This means that the fractal heap is no longer ncessarily
+ * flushed from the bottom up.
+ *
+ * For example, it is now possible for a dirty fractal heap
+ * header to be flushed before a dirty dblock, as long as the
+ * there in an interviening iblock, and the header has no
+ * dirty immediate flush dependency children.
+ *
+ * Also, I gather that under some circumstances, a dblock
+ * will be direct a flush dependency child both of the iblock
+ * that points to it, and of the fractal heap header.
+ *
+ * As a result of these changes, the functionality of these
+ * sanity checking routines has been modified significantly.
+ * Instead of scanning the fractal heap from a starting point
+ * down, and verifying that there were no dirty entries, the
+ * functions now scan downward from the starting point and
+ * verify that there are no dirty flush dependency children
+ * of the specified flush dependency parent. In passing,
+ * they also walk the data structure, and verify it.
+ *
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
@@ -2817,53 +3143,82 @@ done:
*/
#ifndef NDEBUG
static herr_t
-H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f, H5HF_indirect_t *iblock,
- hbool_t *clean, hbool_t *has_dblocks)
+H5HF__cache_verify_iblocks_dblocks_clean(H5F_t *f, haddr_t fd_parent_addr,
+ H5HF_indirect_t *iblock, hbool_t *fd_clean, hbool_t *clean,
+ hbool_t *has_dblocks)
{
unsigned num_direct_rows;
unsigned max_dblock_index;
unsigned i;
+ haddr_t iblock_addr;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* Sanity checks */
HDassert(f);
+ HDassert(H5F_addr_defined(fd_parent_addr));
HDassert(iblock);
HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
- HDassert(clean);
- HDassert(*clean);
+ HDassert(fd_clean);
+ HDassert(*fd_clean);
+ HDassert(clean); /* note that *clean need not be true */
HDassert(has_dblocks);
i = 0;
num_direct_rows = MIN(iblock->nrows, iblock->hdr->man_dtable.max_direct_rows);
HDassert(num_direct_rows <= iblock->nrows);
max_dblock_index = (num_direct_rows * iblock->hdr->man_dtable.cparam.width) - 1;
- while((*clean) && (i <= max_dblock_index)) {
+ iblock_addr = iblock->addr;
+ HDassert(H5F_addr_defined(iblock_addr));
+
+ while((*fd_clean) && (i <= max_dblock_index)) {
haddr_t dblock_addr;
dblock_addr = iblock->ents[i].addr;
if(H5F_addr_defined(dblock_addr)) {
- unsigned dblock_status = 0;
+ hbool_t in_cache;
+ hbool_t type_ok;
+
+ if(H5AC_verify_entry_type(f, dblock_addr, &H5AC_FHEAP_DBLOCK[0], &in_cache, &type_ok) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check dblock type")
+
+ if(in_cache) { /* dblock is in cache */
+ hbool_t fd_exists;
+ unsigned dblock_status = 0;
+
+ if(!type_ok)
+ HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "dblock addr doesn't refer to a dblock?!?")
+
+ if(H5AC_get_entry_status(f, dblock_addr, &dblock_status) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get dblock status")
+
+ HDassert(dblock_status & H5AC_ES__IN_CACHE);
- if(H5AC_get_entry_status(f, dblock_addr, &dblock_status) < 0)
- HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get dblock status")
- if(dblock_status & H5AC_ES__IN_CACHE) {
*has_dblocks = TRUE;
- if(dblock_status & H5AC_ES__IS_DIRTY)
+ if((dblock_status & H5AC_ES__IS_DIRTY) &&
+ (((dblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) ||
+ (!H5AC_get_serialization_in_progress(f)))) {
*clean = FALSE;
+
+ if(H5AC_flush_dependency_exists(f, fd_parent_addr, dblock_addr, &fd_exists) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency")
+
+ if(fd_exists)
+ *fd_clean = FALSE;
+ } /* end if */
- /* If a child dblock is in cache, it must have a flush
- * dependency relationship with this iblock, and it
- * may not be the parent in any flush dependency
- * relationship.
+ /* If a child dblock is in cache, it must have a flush
+ * dependency relationship with this iblock. Test this
+ * here.
*/
- if(0 == (dblock_status & H5AC_ES__IS_FLUSH_DEP_CHILD))
- HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "dblock in cache and not a flush dep child.")
- if(0 != (dblock_status & H5AC_ES__IS_FLUSH_DEP_PARENT))
- HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "dblock in cache and is a flush dep parent.")
+ if(H5AC_flush_dependency_exists(f, iblock_addr, dblock_addr, &fd_exists) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency")
+
+ if(!fd_exists)
+ HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "dblock in cache and not a flush dep child of iblock.")
} /* end if */
} /* end if */
@@ -2896,6 +3251,54 @@ done:
* during the call. Caller must ensure that this is
* the case before the call.
*
+ * Update -- 8/24/15
+ *
+ * With the advent of the metadata cache image feature, it is
+ * possible for the pre-serialize and serialize calls to be
+ * invoked outside of a flush. While this serialization
+ * observes flush dependencies for the order of serialization,
+ * the entries are not written to disk, and hence dirty entries
+ * remain dirty.
+ *
+ * To address this, updated the sanity checks in this function
+ * to treat entries whose images are up to date as clean if
+ * a cache serialization is in progress.
+ *
+ * Update -- 9/29/16
+ *
+ * The implementation of flush dependencies has been changed.
+ * Prior to this change, a flush dependency parent could be
+ * flushed if and only if all its flush dependency decendants
+ * were clean. In the new definition, a flush dependency
+ * parent can be flushed if all its immediate flush dependency
+ * children are clean, regardless of any other dirty
+ * decendants.
+ *
+ * Further, metadata cache entries are now allowed to have
+ * multiple flush dependency parents.
+ *
+ * This means that the fractal heap is no longer ncessarily
+ * flushed from the bottom up.
+ *
+ * For example, it is now possible for a dirty fractal heap
+ * header to be flushed before a dirty dblock, as long as the
+ * there in an interviening iblock, and the header has no
+ * dirty immediate flush dependency children.
+ *
+ * Also, I gather that under some circumstances, a dblock
+ * will be direct a flush dependency child both of the iblock
+ * that points to it, and of the fractal heap header.
+ *
+ * As a result of these changes, the functionality of these
+ * sanity checking routines has been modified significantly.
+ * Instead of scanning the fractal heap from a starting point
+ * down, and verifying that there were no dirty entries, the
+ * functions now scan downward from the starting point and
+ * verify that there are no dirty flush dependency children
+ * of the specified flush dependency parent. In passing,
+ * they also walk the data structure, and verify it.
+ *
+ *
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
@@ -2905,33 +3308,38 @@ done:
*/
#ifndef NDEBUG
static herr_t
-H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, H5HF_indirect_t *iblock,
+H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, hid_t dxpl_id,
+ haddr_t fd_parent_addr, H5HF_indirect_t *iblock, hbool_t *fd_clean,
hbool_t *clean, hbool_t *has_iblocks)
{
unsigned first_iblock_index;
unsigned last_iblock_index;
unsigned num_direct_rows;
unsigned i;
+ haddr_t iblock_addr;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* Sanity checks */
HDassert(f);
+ HDassert(H5F_addr_defined(fd_parent_addr));
HDassert(iblock);
HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
- HDassert(clean);
- HDassert(*clean);
+ HDassert(fd_clean);
+ HDassert(*fd_clean);
+ HDassert(clean); /* note that *clean need not be true */
HDassert(has_iblocks);
num_direct_rows = MIN(iblock->nrows, iblock->hdr->man_dtable.max_direct_rows);
HDassert(num_direct_rows <= iblock->nrows);
+ iblock_addr = iblock->addr;
first_iblock_index = num_direct_rows * iblock->hdr->man_dtable.cparam.width;
last_iblock_index = (iblock->nrows * iblock->hdr->man_dtable.cparam.width) - 1;
i = first_iblock_index;
- while((*clean) && (i <= last_iblock_index)) {
+ while((*fd_clean) && (i <= last_iblock_index)) {
haddr_t child_iblock_addr = iblock->ents[i].addr;
if(H5F_addr_defined(child_iblock_addr)) {
@@ -2941,9 +3349,157 @@ H5HF__cache_verify_descendant_iblocks_clean(H5F_t *f, H5HF_indirect_t *iblock,
HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get iblock status")
if(child_iblock_status & H5AC_ES__IN_CACHE) {
+ hbool_t fd_exists;
+
*has_iblocks = TRUE;
- if(child_iblock_status & H5AC_ES__IS_DIRTY)
- *clean = FALSE;
+
+ if((child_iblock_status & H5AC_ES__IS_DIRTY) &&
+ (((child_iblock_status & H5AC_ES__IMAGE_IS_UP_TO_DATE) == 0) ||
+ (!H5AC_get_serialization_in_progress(f)))) {
+
+ *clean = FALSE;
+
+ if(H5AC_flush_dependency_exists(f, fd_parent_addr, child_iblock_addr, &fd_exists) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency")
+
+ if(fd_exists)
+ *fd_clean = FALSE;
+ } /* end if */
+
+ /* if the child iblock is in cache and *fd_clean is TRUE,
+ * we must continue to explore down the fractal heap tree
+ * structure to verify that all descendant blocks that are
+ * flush dependency children of the entry at parent_addr are
+ * either clean, or not in the metadata cache. We do this
+ * with a recursive call to
+ * H5HF__cache_verify_iblock_descendants_clean().
+ * However, we can't make this call unless the child iblock
+ * is somehow locked into the cache -- typically via either
+ * pinning or protecting.
+ *
+ * If the child iblock is pinned, we can look up its pointer
+ * on the current iblock's pinned child iblock list, and
+ * and use that pointer in the recursive call.
+ *
+ * If the entry is unprotected and unpinned, we simply
+ * protect it.
+ *
+ * If, however, the the child iblock is already protected,
+ * but not pinned, we have a bit of a problem, as we have
+ * no legitimate way of looking up its pointer in memory.
+ *
+ * To solve this problem, I have added a new metadata cache
+ * call to obtain the pointer.
+ *
+ * WARNING: This call should be used only in debugging
+ * routines, and it should be avoided there when
+ * possible.
+ *
+ * Further, if we ever multi-thread the cache,
+ * this routine will have to be either discarded
+ * or heavily re-worked.
+ *
+ * Finally, keep in mind that the entry whose
+ * pointer is obtained in this fashion may not
+ * be in a stable state.
+ *
+ * Assuming that the flush dependency code is working
+ * as it should, the only reason for the child entry to
+ * be unpinned is if none of its children are in cache.
+ * This unfortunately means that if it is protected and
+ * not pinned, the fractal heap is in the process of loading
+ * or inserting one of its children. The obvious implication
+ * is that there is a significant chance that the child
+ * iblock is in an unstable state.
+ *
+ * All this suggests that using the new call to obtain the
+ * pointer to the protected child iblock is questionable
+ * here. However, since this is test/debugging code, I
+ * expect that we will use this approach until it causes
+ * problems, or we think of a better way.
+ */
+ if(*fd_clean) {
+ H5HF_indirect_t *child_iblock = NULL;
+ hbool_t unprotect_child_iblock = FALSE;
+
+ if(0 == (child_iblock_status & H5AC_ES__IS_PINNED)) {
+ /* child iblock is not pinned */
+ if(0 == (child_iblock_status & H5AC_ES__IS_PROTECTED)) {
+ /* child iblock is unprotected, and unpinned */
+ /* protect it. Note that the udata is only */
+ /* used in the load callback. While the */
+ /* fractal heap makes heavy use of the udata */
+ /* in this case, since we know that the */
+ /* entry is in cache, we can pass NULL udata */
+ /* */
+ /* The tag associated specified in the dxpl */
+ /* we received as a parameter (via dxpl_id) */
+ /* may not be correct. */
+ /* */
+ /* Grab the (hopefully) correct tag from the */
+ /* parent iblock, and load it into the dxpl */
+ /* via the H5_BEGIN_TAG and H5_END_TAG */
+ /* macros. Note that any error bracked by */
+ /* these macros must be reported with */
+ /* HGOTO_ERROR_TAG. */
+
+ H5_BEGIN_TAG(dxpl_id, iblock->hdr->heap_addr, FAIL)
+
+ if(NULL == (child_iblock = (H5HF_indirect_t *) H5AC_protect(f, dxpl_id, H5AC_FHEAP_IBLOCK, child_iblock_addr, NULL, H5AC__READ_ONLY_FLAG)))
+ HGOTO_ERROR_TAG(H5E_HEAP, H5E_CANTPROTECT, FAIL, "H5AC_protect() faild.")
+
+ H5_END_TAG(FAIL)
+
+ unprotect_child_iblock = TRUE;
+ } /* end if */
+ else {
+ /* child iblock is protected -- use */
+ /* H5AC_get_entry_ptr_from_addr() to get a */
+ /* pointer to the entry. This is very slimy -- */
+ /* come up with a better solution. */
+ if(H5AC_get_entry_ptr_from_addr(f, child_iblock_addr, (void **)(&child_iblock)) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "H5AC_get_entry_ptr_from_addr() faild.")
+ HDassert(child_iblock);
+ } /* end else */
+ } /* end if */
+ else {
+ /* child iblock is pinned -- look it up in the */
+ /* parent iblocks child_iblocks array. */
+ HDassert(iblock->child_iblocks);
+ child_iblock = iblock->child_iblocks[i - first_iblock_index];
+ } /* end else */
+
+ /* At this point, one way or another we should have
+ * a pointer to the child iblock. Verify that we
+ * that we have the correct one.
+ */
+ HDassert(child_iblock);
+ HDassert(child_iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
+ HDassert(child_iblock->cache_info.type == H5AC_FHEAP_IBLOCK);
+ HDassert(child_iblock->addr == child_iblock_addr);
+
+ /* now make the recursive call */
+ if(H5HF__cache_verify_iblock_descendants_clean(f, dxpl_id, fd_parent_addr, child_iblock, &child_iblock_status, fd_clean, clean) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify child iblock clean.")
+
+ /* if iblock_addr != fd_parent_addr, verify that a flush
+ * dependency relationship exists between iblock and
+ * the child iblock.
+ */
+ if(fd_parent_addr != iblock_addr) {
+ if(H5AC_flush_dependency_exists(f, iblock_addr, child_iblock_addr, &fd_exists) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't check flush dependency")
+
+ if(!fd_exists)
+ HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "iblock is not a flush dep parent of child_iblock.")
+ } /* end if */
+
+ /* if we protected the child iblock, unprotect it now */
+ if(unprotect_child_iblock) {
+ if(H5AC_unprotect(f, dxpl_id, H5AC_FHEAP_IBLOCK, child_iblock_addr, child_iblock, H5AC__NO_FLAGS_SET) < 0)
+ HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, "H5AC_unprotect() faild.")
+ } /* end if */
+ } /* end if */
} /* end if */
} /* end if */
diff --git a/src/H5HFpkg.h b/src/H5HFpkg.h
index 6abae65..9c1d9a6 100644
--- a/src/H5HFpkg.h
+++ b/src/H5HFpkg.h
@@ -559,15 +559,6 @@ typedef struct H5HF_dblock_cache_ud_t {
/* Package Private Variables */
/*****************************/
-/* H5HF header inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_FHEAP_HDR[1];
-
-/* H5HF indirect block inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_FHEAP_IBLOCK[1];
-
-/* H5HF direct block inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_FHEAP_DBLOCK[1];
-
/* The v2 B-tree class for tracking indirectly accessed 'huge' objects */
H5_DLLVAR const H5B2_class_t H5HF_HUGE_BT2_INDIR[1];
diff --git a/src/H5HGpkg.h b/src/H5HGpkg.h
index e566ece..a5c80ea 100644
--- a/src/H5HGpkg.h
+++ b/src/H5HGpkg.h
@@ -40,9 +40,6 @@
/* Package Private Variables */
/*****************************/
-/* The cache subclass */
-H5_DLLVAR const H5AC_class_t H5AC_GHEAP[1];
-
/* Declare extern the free list to manage the H5HG_t struct */
H5FL_EXTERN(H5HG_heap_t);
diff --git a/src/H5HLpkg.h b/src/H5HLpkg.h
index 7075b2a..06db696 100644
--- a/src/H5HLpkg.h
+++ b/src/H5HLpkg.h
@@ -39,12 +39,6 @@
/* Package Private Variables */
/*****************************/
-/* The local heap prefix cache subclass */
-H5_DLLVAR const H5AC_class_t H5AC_LHEAP_PRFX[1];
-
-/* The local heap data block cache subclass */
-H5_DLLVAR const H5AC_class_t H5AC_LHEAP_DBLK[1];
-
/* Declare extern the free list to manage the H5HL_free_t struct */
H5FL_EXTERN(H5HL_free_t);
diff --git a/src/H5MF.c b/src/H5MF.c
index 3ed6d28..3a6a953 100644
--- a/src/H5MF.c
+++ b/src/H5MF.c
@@ -1489,7 +1489,7 @@ H5MF_settle_raw_data_fsm(H5F_t *f, hid_t dxpl_id, hbool_t *fsm_settled)
fsinfo.fs_addr[type-1] = HADDR_UNDEF;
fsinfo.strategy = f->shared->fs_strategy;
fsinfo.threshold = f->shared->fs_threshold;
- if(H5F_super_ext_write_msg(f, dxpl_id, H5O_FSINFO_ID, &fsinfo, TRUE) < 0)
+ if(H5F_super_ext_write_msg(f, dxpl_id, H5O_FSINFO_ID, &fsinfo, TRUE, H5O_MSG_NO_FLAGS_SET) < 0)
HGOTO_ERROR(H5E_FSPACE, H5E_WRITEERROR, FAIL, "error in writing message to superblock extension")
@@ -2113,7 +2113,7 @@ HDfprintf(stderr, "%s: Entering\n", FUNC);
fsinfo.threshold = f->shared->fs_threshold;
/* Write the free space manager message -- message must already exist */
- if(H5F_super_ext_write_msg(f, dxpl_id, H5O_FSINFO_ID, &fsinfo, FALSE) < 0)
+ if(H5F_super_ext_write_msg(f, dxpl_id, H5O_FSINFO_ID, &fsinfo, FALSE, H5O_MSG_NO_FLAGS_SET) < 0)
HGOTO_ERROR(H5E_RESOURCE, H5E_WRITEERROR, FAIL, "error in writing message to superblock extension")
/* Final close of free-space managers */
diff --git a/src/H5O.c b/src/H5O.c
index e0c0f0b..1b58703 100644
--- a/src/H5O.c
+++ b/src/H5O.c
@@ -130,11 +130,12 @@ const H5O_msg_class_t *const H5O_msg_class_g[] = {
H5O_MSG_AINFO, /*0x0015 Attribute information */
H5O_MSG_REFCOUNT, /*0x0016 Object's ref. count */
H5O_MSG_FSINFO, /*0x0017 Free-space manager info */
- H5O_MSG_UNKNOWN, /*0x0018 Placeholder for unknown message */
+ H5O_MSG_MDCI, /*0x0018 Metadata cache image */
+ H5O_MSG_UNKNOWN, /*0x0019 Placeholder for unknown message */
#ifdef H5O_ENABLE_BOGUS
- H5O_MSG_BOGUS_INVALID, /*0x0019 "Bogus invalid" (for testing) */
+ H5O_MSG_BOGUS_INVALID, /*0x001A "Bogus invalid" (for testing) */
#else /* H5O_ENABLE_BOGUS */
- NULL, /*0x0019 "Bogus invalid" (for testing) */
+ NULL, /*0x001A "Bogus invalid" (for testing) */
#endif /* H5O_ENABLE_BOGUS */
};
diff --git a/src/H5Ocache_image.c b/src/H5Ocache_image.c
new file mode 100644
index 0000000..65f6aa2
--- /dev/null
+++ b/src/H5Ocache_image.c
@@ -0,0 +1,337 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * 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: H5Ocache_image.c
+ * June 21, 2015
+ * John Mainzer
+ *
+ * Purpose: A message indicating that a metadata cache image block
+ * of the indicated length exists at the specified offset
+ * in the HDF5 file.
+ *
+ * The mdci_msg only appears in the superblock extension.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "H5Omodule.h" /* This source code file is part of the H5O module */
+
+
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5FLprivate.h" /* Free Lists */
+#include "H5Opkg.h" /* Object headers */
+#include "H5MFprivate.h" /* File space management */
+
+/* Callbacks for message class */
+static void *H5O__mdci_decode(H5F_t *f, hid_t dxpl_id, H5O_t *open_oh,
+ unsigned mesg_flags, unsigned *ioflags, const uint8_t *p);
+static herr_t H5O__mdci_encode(H5F_t *f, hbool_t disable_shared,
+ uint8_t *p, const void *_mesg);
+static void *H5O__mdci_copy(const void *_mesg, void *_dest);
+static size_t H5O__mdci_size(const H5F_t *f, hbool_t disable_shared,
+ const void *_mesg);
+static herr_t H5O__mdci_free(void *mesg);
+static herr_t H5O__mdci_delete(H5F_t *f, hid_t dxpl_id, H5O_t *open_oh,
+ void *_mesg);
+static herr_t H5O__mdci_debug(H5F_t *f, hid_t dxpl_id, const void *_mesg,
+ FILE *stream, int indent, int fwidth);
+
+/* This message derives from H5O message class */
+const H5O_msg_class_t H5O_MSG_MDCI[1] = {{
+ H5O_MDCI_MSG_ID, /* message id number */
+ "mdci", /* message name for debugging */
+ sizeof(H5O_mdci_t), /* native message size */
+ 0, /* messages are sharable? */
+ H5O__mdci_decode, /* decode message */
+ H5O__mdci_encode, /* encode message */
+ H5O__mdci_copy, /* copy method */
+ H5O__mdci_size, /* size of mdc image message */
+ NULL, /* reset method */
+ H5O__mdci_free, /* free method */
+ H5O__mdci_delete, /* file delete method */
+ NULL, /* link method */
+ NULL, /* set share method */
+ NULL, /* can share method */
+ NULL, /* pre copy native value to file */
+ NULL, /* copy native value to file */
+ NULL, /* post copy native value to file */
+ NULL, /* get creation index */
+ NULL, /* set creation index */
+ H5O__mdci_debug /* debugging */
+}};
+
+/* Only one version of the metadata cache image message at present */
+#define H5O_MDCI_VERSION_0 0
+
+/* Declare the free list for H5O_mdci_t's */
+H5FL_DEFINE(H5O_mdci_t);
+
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5O__mdci_decode
+ *
+ * Purpose: Decode a metadata cache image message and return a
+ * pointer to a newly allocated H5O_mdci_t struct.
+ *
+ * Return: Success: Ptr to new message in native struct.
+ * Failure: NULL
+ *
+ * Programmer: John Mainzer
+ * 6/22/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static void *
+H5O__mdci_decode(H5F_t *f, hid_t H5_ATTR_UNUSED dxpl_id,
+ H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNUSED mesg_flags,
+ unsigned H5_ATTR_UNUSED *ioflags, const uint8_t *p)
+{
+ H5O_mdci_t *mesg; /* Native message */
+ void *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(p);
+
+ /* Version of message */
+ if(*p++ != H5O_MDCI_VERSION_0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "bad version number for message")
+
+ /* Allocate space for message */
+ if(NULL == (mesg = (H5O_mdci_t *)H5FL_MALLOC(H5O_mdci_t)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for metadata cache image message")
+
+ /* Decode */
+ H5F_addr_decode(f, &p, &(mesg->addr));
+ H5F_DECODE_LENGTH(f, p, mesg->size);
+
+ /* Set return value */
+ ret_value = (void *)mesg;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5O__mdci_decode() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5O__mdci_encode
+ *
+ * Purpose: Encode metadata cache image message
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/22/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5O__mdci_encode(H5F_t *f, hbool_t H5_ATTR_UNUSED disable_shared,
+ uint8_t *p, const void *_mesg)
+{
+ const H5O_mdci_t *mesg = (const H5O_mdci_t *)_mesg;
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Sanity check */
+ HDassert(f);
+ HDassert(p);
+ HDassert(mesg);
+
+ /* encode */
+ *p++ = H5O_MDCI_VERSION_0;
+ H5F_addr_encode(f, &p, mesg->addr);
+ H5F_ENCODE_LENGTH(f, p, mesg->size);
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5O__mdci_encode() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5O__mdci_copy
+ *
+ * Purpose: Copies a message from _MESG to _DEST, allocating _DEST if
+ * necessary.
+ *
+ * Return: Success: Ptr to _DEST
+ * Failure: NULL
+ *
+ * Programmer: John Mainzer
+ * 6/22/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static void *
+H5O__mdci_copy(const void *_mesg, void *_dest)
+{
+ const H5O_mdci_t *mesg = (const H5O_mdci_t *)_mesg;
+ H5O_mdci_t *dest = (H5O_mdci_t *) _dest;
+ void *ret_value = NULL; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* check args */
+ HDassert(mesg);
+ if(!dest && NULL == (dest = H5FL_MALLOC(H5O_mdci_t)))
+ HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed")
+
+ /* copy */
+ *dest = *mesg;
+
+ /* Set return value */
+ ret_value = dest;
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5O_mdci__copy() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5O__mdci_size
+ *
+ * Purpose: Returns the size of the raw message in bytes not counting
+ * the message type or size fields, but only the data fields.
+ * This function doesn't take into account alignment.
+ *
+ * Return: Success: Message data size in bytes without alignment.
+ *
+ * Failure: zero
+ *
+ * Programmer: John Mainzer
+ * 6/22/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static size_t
+H5O__mdci_size(const H5F_t *f, hbool_t H5_ATTR_UNUSED disable_shared,
+ const void H5_ATTR_UNUSED *_mesg)
+{
+ size_t ret_value = 0; /* Return value */
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Set return value */
+ ret_value = (size_t)( 1 + /* Version number */
+ H5F_SIZEOF_ADDR(f) + /* addr of metadata cache */
+ /* image block */
+ H5F_SIZEOF_SIZE(f) ); /* length of metadata cache */
+ /* image block */
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5O__mdci_size() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5O__mdci_free
+ *
+ * Purpose: Free the message
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/22/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5O__mdci_free(void *mesg)
+{
+ FUNC_ENTER_STATIC_NOERR
+
+ HDassert(mesg);
+
+ mesg = H5FL_FREE(H5O_mdci_t, mesg);
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5O__mdci_free() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5O__mdci_delete
+ *
+ * Purpose: Free file space referenced by message
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * Wednesday, March 19, 2003
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5O__mdci_delete(H5F_t *f, hid_t dxpl_id, H5O_t *open_oh, void *_mesg)
+{
+ H5O_mdci_t *mesg = (H5O_mdci_t *)_mesg;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* check args */
+ HDassert(f);
+ HDassert(open_oh);
+ HDassert(mesg);
+
+ /* Free file space for cache image */
+ if(H5F_addr_defined(mesg->addr))
+ if(H5MF_xfree(f, H5FD_MEM_SUPER, dxpl_id, mesg->addr, mesg->size) < 0)
+ HGOTO_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to free file space for cache image block")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5O__mdci_delete() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5O__mdci_debug
+ *
+ * Purpose: Prints debugging info.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: John Mainzer
+ * 6/22/15
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5O__mdci_debug(H5F_t H5_ATTR_UNUSED *f, hid_t H5_ATTR_UNUSED dxpl_id,
+ const void *_mesg, FILE * stream, int indent, int fwidth)
+{
+ const H5O_mdci_t *mdci = (const H5O_mdci_t *) _mesg;
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* check args */
+ HDassert(f);
+ HDassert(mdci);
+ HDassert(stream);
+ HDassert(indent >= 0);
+ HDassert(fwidth >= 0);
+
+ HDfprintf(stream, "%*s%-*s %a\n", indent, "", fwidth,
+ "Metadata Cache Image Block address:", mdci->addr);
+
+ HDfprintf(stream, "%*s%-*s %Hu\n", indent, "", fwidth,
+ "Metadata Cache Image Block size in bytes:", mdci->size);
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5O__mdci_debug() */
+
diff --git a/src/H5Opkg.h b/src/H5Opkg.h
index ef49535..e1fa0be 100644
--- a/src/H5Opkg.h
+++ b/src/H5Opkg.h
@@ -31,7 +31,7 @@
#define H5O_NMESGS 8 /*initial number of messages */
#define H5O_NCHUNKS 2 /*initial number of chunks */
#define H5O_MIN_SIZE 22 /* Min. obj header data size (must be big enough for a message prefix and a continuation message) */
-#define H5O_MSG_TYPES 26 /* # of types of messages */
+#define H5O_MSG_TYPES 27 /* # of types of messages */
#define H5O_MAX_CRT_ORDER_IDX 65535 /* Max. creation order index value */
/* Versions of object header structure */
@@ -419,12 +419,6 @@ typedef struct H5O_chk_cache_ud_t {
} H5O_chk_cache_ud_t;
-/* H5O object header inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_OHDR[1];
-
-/* H5O object header chunk inherits cache-like properties from H5AC */
-H5_DLLVAR const H5AC_class_t H5AC_OHDR_CHK[1];
-
/* Header message ID to class mapping */
H5_DLLVAR const H5O_msg_class_t *const H5O_msg_class_g[H5O_MSG_TYPES];
@@ -544,7 +538,10 @@ H5_DLLVAR const H5O_msg_class_t H5O_MSG_REFCOUNT[1];
/* Free-space Manager Info message. (0x0017) */
H5_DLLVAR const H5O_msg_class_t H5O_MSG_FSINFO[1];
-/* Placeholder for unknown message. (0x0018) */
+/* Metadata Cache Image message. (0x0018) */
+H5_DLLVAR const H5O_msg_class_t H5O_MSG_MDCI[1];
+
+/* Placeholder for unknown message. (0x0019) */
H5_DLLVAR const H5O_msg_class_t H5O_MSG_UNKNOWN[1];
diff --git a/src/H5Oprivate.h b/src/H5Oprivate.h
index f6df874..71c512c 100644
--- a/src/H5Oprivate.h
+++ b/src/H5Oprivate.h
@@ -69,6 +69,7 @@ typedef struct H5O_t H5O_t;
#define H5O_FIRST (-2) /* Operate on first message of type */
/* Flags needed when encoding messages */
+#define H5O_MSG_NO_FLAGS_SET 0x00u
#define H5O_MSG_FLAG_CONSTANT 0x01u
#define H5O_MSG_FLAG_SHARED 0x02u
#define H5O_MSG_FLAG_DONTSHARE 0x04u
@@ -204,7 +205,8 @@ typedef struct H5O_copy_t {
#define H5O_AINFO_ID 0x0015 /* Attribute info message. */
#define H5O_REFCOUNT_ID 0x0016 /* Reference count message. */
#define H5O_FSINFO_ID 0x0017 /* File space info message. */
-#define H5O_UNKNOWN_ID 0x0018 /* Placeholder message ID for unknown message. */
+#define H5O_MDCI_MSG_ID 0x0018 /* Metadata Cache Image Message */
+#define H5O_UNKNOWN_ID 0x0019 /* Placeholder message ID for unknown message. */
/* (this should never exist in a file) */
/*
* Note: Must increment H5O_MSG_TYPES in H5Opkg.h and update H5O_msg_class_g
@@ -214,7 +216,7 @@ typedef struct H5O_copy_t {
*
* (this should never exist in a file)
*/
-#define H5O_BOGUS_INVALID_ID 0x0019 /* "Bogus invalid" Message. */
+#define H5O_BOGUS_INVALID_ID 0x001A /* "Bogus invalid" Message. */
/* Shared object message types.
* Shared objects can be committed, in which case the shared message contains
@@ -794,6 +796,16 @@ typedef struct H5O_fsinfo_t {
haddr_t fs_addr[H5FD_MEM_NTYPES-1]; /* Addresses of free space managers */
} H5O_fsinfo_t;
+/*
+ * Metadata Cache Image Message.
+ * Contains base address and length of the metadata cache image.
+ * (Data structure in memory)
+ */
+typedef struct H5O_mdci_t {
+ haddr_t addr; /* address of MDC image block */
+ hsize_t size; /* size of MDC image block */
+} H5O_mdci_t;
+
/* Typedef for "application" iteration operations */
typedef herr_t (*H5O_operator_t)(const void *mesg/*in*/, unsigned idx,
void *operator_data/*in,out*/);
diff --git a/src/H5Pfapl.c b/src/H5Pfapl.c
index 91a0e03..7ea0fe0 100644
--- a/src/H5Pfapl.c
+++ b/src/H5Pfapl.c
@@ -225,6 +225,13 @@
#define H5F_ACS_COLL_MD_WRITE_FLAG_ENC H5P__encode_hbool_t
#define H5F_ACS_COLL_MD_WRITE_FLAG_DEC H5P__decode_hbool_t
#endif /* H5_HAVE_PARALLEL */
+/* Definitions for the initial metadata cache image configuration */
+#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_SIZE sizeof(H5AC_cache_image_config_t)
+#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_DEF H5AC__DEFAULT_CACHE_IMAGE_CONFIG
+#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_ENC H5P__facc_cache_image_config_enc
+#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_DEC H5P__facc_cache_image_config_dec
+#define H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_CMP H5P__facc_cache_image_config_cmp
+
/******************/
/* Local Typedefs */
@@ -279,6 +286,11 @@ static herr_t H5P_facc_mdc_log_location_copy(const char *name, size_t size, void
static int H5P_facc_mdc_log_location_cmp(const void *value1, const void *value2, size_t size);
static herr_t H5P_facc_mdc_log_location_close(const char *name, size_t size, void *value);
+/* Metadata cache image property callbacks */
+static int H5P__facc_cache_image_config_cmp(const void *_config1, const void *_config2, size_t H5_ATTR_UNUSED size);
+static herr_t H5P__facc_cache_image_config_enc(const void *value, void **_pp, size_t *size);
+static herr_t H5P__facc_cache_image_config_dec(const void **_pp, void *_value);
+
/*********************/
/* Package Variables */
@@ -346,6 +358,7 @@ static const hbool_t H5F_def_evict_on_close_flag_g = H5F_ACS_EVICT_ON_CLOSE_FLAG
static const H5P_coll_md_read_flag_t H5F_def_coll_md_read_flag_g = H5F_ACS_COLL_MD_READ_FLAG_DEF; /* Default setting for the collective metedata read flag */
static const hbool_t H5F_def_coll_md_write_flag_g = H5F_ACS_COLL_MD_WRITE_FLAG_DEF; /* Default setting for the collective metedata write flag */
#endif /* H5_HAVE_PARALLEL */
+static const H5AC_cache_image_config_t H5F_def_mdc_initCacheImageCfg_g = H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_DEF; /* Default metadata cache image settings */
/*-------------------------------------------------------------------------
@@ -555,6 +568,12 @@ H5P__facc_reg_prop(H5P_genclass_t *pclass)
HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class")
#endif /* H5_HAVE_PARALLEL */
+ /* Register the initial metadata cache image configuration */
+ if(H5P_register_real(pclass, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_SIZE, &H5F_def_mdc_initCacheImageCfg_g,
+ NULL, NULL, NULL, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_ENC, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_DEC,
+ NULL, NULL, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_CMP, NULL) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class")
+
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5P__facc_reg_prop() */
@@ -1543,6 +1562,101 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5Pset_mdc_image_config
+ *
+ * Purpose: Set the initial metadata cache image configuration in the
+ * target FAPL.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: J. Mainzer
+ * Thursday, June 25, 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Pset_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t *config_ptr)
+{
+ H5P_genplist_t *plist; /* Property list pointer */
+ herr_t ret_value = SUCCEED; /* return value */
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE2("e", "i*x", plist_id, config_ptr);
+
+ /* Get the plist structure */
+ if(NULL == (plist = H5P_object_verify(plist_id,H5P_FILE_ACCESS)))
+ HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID")
+
+ /* validate the new configuration */
+ if(H5AC_validate_cache_image_config(config_ptr) < 0)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid metadata cache image configuration")
+
+ /* set the modified metadata cache image config */
+
+ /* If we ever support multiple versions of H5AC_cache_image_config_t, we
+ * will have to test the version and do translation here.
+ */
+
+ if(H5P_set(plist, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, config_ptr) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set metadata cache image initial config")
+
+done:
+ FUNC_LEAVE_API(ret_value)
+} /* H5Pset_mdc_image_config() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5Pget_mdc_image_config
+ *
+ * Purpose: Retrieve the metadata cache initial image configuration
+ * from the target FAPL.
+ *
+ * Observe that the function will fail if config_ptr is
+ * NULL, or if config_ptr->version specifies an unknown
+ * version of H5AC_cache_image_config_t.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: J. Mainzer
+ * Friday, June 26, 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5Pget_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t *config_ptr)
+{
+ H5P_genplist_t *plist; /* Property list pointer */
+ herr_t ret_value = SUCCEED; /* return value */
+
+ FUNC_ENTER_API(FAIL)
+ H5TRACE2("e", "i*x", plist_id, config_ptr);
+
+ /* Get the plist structure */
+ if(NULL == (plist = H5P_object_verify(plist_id,H5P_FILE_ACCESS)))
+ HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID")
+
+ /* validate the config_ptr */
+ if(config_ptr == NULL)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL config_ptr on entry.")
+
+ if(config_ptr->version != H5AC__CURR_CACHE_IMAGE_CONFIG_VERSION)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Unknown image config version.")
+
+ /* If we ever support multiple versions of H5AC_cache_config_t, we
+ * will have to get the cannonical version here, and then translate
+ * to the version of the structure supplied.
+ */
+
+ /* Get the current initial metadata cache resize configuration */
+ if(H5P_get(plist, H5F_ACS_META_CACHE_INIT_IMAGE_CONFIG_NAME, config_ptr) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET,FAIL, "can't get metadata cache initial image config")
+
+done:
+ FUNC_LEAVE_API(ret_value)
+} /* H5Pget_mdc_image_config() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5Pset_mdc_config
*
* Purpose: Set the initial metadata cache resize configuration in the
@@ -2754,6 +2868,147 @@ done:
/*-------------------------------------------------------------------------
+ * Function: H5P__facc_cache_image_config_cmp
+ *
+ * Purpose: Compare two cache image configurations.
+ *
+ * Return: positive if VALUE1 is greater than VALUE2, negative if VALUE2 is
+ * greater than VALUE1 and zero if VALUE1 and VALUE2 are equal.
+ *
+ * Programmer: John Mainzer
+ * June 26, 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+static int
+H5P__facc_cache_image_config_cmp(const void *_config1, const void *_config2, size_t H5_ATTR_UNUSED size)
+{
+ const H5AC_cache_image_config_t *config1 = (const H5AC_cache_image_config_t *)_config1; /* Create local aliases for values */
+ const H5AC_cache_image_config_t *config2 = (const H5AC_cache_image_config_t *)_config2; /* Create local aliases for values */
+ int ret_value = 0; /* Return value */
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Check for a property being set */
+ if(config1 == NULL && config2 != NULL) HGOTO_DONE(-1);
+ if(config1 != NULL && config2 == NULL) HGOTO_DONE(1);
+
+ if(config1->version < config2->version) HGOTO_DONE(-1);
+ if(config1->version > config2->version) HGOTO_DONE(1);
+
+ if(config1->generate_image < config2->generate_image) HGOTO_DONE(-1);
+ if(config1->generate_image > config2->generate_image) HGOTO_DONE(1);
+
+ if(config1->save_resize_status < config2->save_resize_status) HGOTO_DONE(-1);
+ if(config1->save_resize_status > config2->save_resize_status) HGOTO_DONE(1);
+
+ if(config1->entry_ageout < config2->entry_ageout) HGOTO_DONE(-1);
+ if(config1->entry_ageout > config2->entry_ageout) HGOTO_DONE(1);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5P__facc_cache_image_config_cmp() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5P__facc_cache_image_config_enc
+ *
+ * Purpose: Callback routine which is called whenever the default
+ * cache image config property in the file creation
+ * property list is encoded.
+ *
+ * Return: Success: Non-negative
+ * Failure: Negative
+ *
+ * Programmer: John Mainzer
+ * June 26, 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5P__facc_cache_image_config_enc(const void *value, void **_pp, size_t *size)
+{
+ const H5AC_cache_image_config_t *config = (const H5AC_cache_image_config_t *)value; /* Create local aliases for value */
+ uint8_t **pp = (uint8_t **)_pp;
+
+ FUNC_ENTER_STATIC_NOERR
+
+ /* Sanity check */
+ HDassert(value);
+
+ if(NULL != *pp) {
+ /* Encode type sizes (as a safety check) */
+ *(*pp)++ = (uint8_t)sizeof(unsigned);
+
+ INT32ENCODE(*pp, (int32_t)config->version);
+
+ H5_ENCODE_UNSIGNED(*pp, config->generate_image);
+
+ H5_ENCODE_UNSIGNED(*pp, config->save_resize_status);
+
+ INT32ENCODE(*pp, (int32_t)config->entry_ageout);
+ } /* end if */
+
+ /* Compute encoded size of fixed-size values */
+ *size += (1 + (2 * sizeof(unsigned)) + (2 * sizeof(int32_t)));
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5P__facc_cache_image_config_enc() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5P__facc_cache_image_config_dec
+ *
+ * Purpose: Callback routine which is called whenever the default
+ * cache image config property in the file creation property
+ * list is decoded.
+ *
+ * Return: Success: Non-negative
+ * Failure: Negative
+ *
+ * Programmer: John Mainzer
+ * June 26, 2015
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5P__facc_cache_image_config_dec(const void **_pp, void *_value)
+{
+ H5AC_cache_image_config_t *config = (H5AC_cache_image_config_t *)_value;
+ const uint8_t **pp = (const uint8_t **)_pp;
+ unsigned enc_size;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_STATIC
+
+ /* Sanity checks */
+ HDassert(pp);
+ HDassert(*pp);
+ HDassert(config);
+ HDcompile_assert(sizeof(size_t) <= sizeof(uint64_t));
+
+ /* Set property to default value */
+ HDmemcpy(config, &H5F_def_mdc_initCacheImageCfg_g, sizeof(H5AC_cache_image_config_t));
+
+ /* Decode type sizes */
+ enc_size = *(*pp)++;
+ if(enc_size != sizeof(unsigned))
+ HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "unsigned value can't be decoded")
+
+ INT32DECODE(*pp, config->version);
+
+ H5_DECODE_UNSIGNED(*pp, config->generate_image);
+
+ H5_DECODE_UNSIGNED(*pp, config->save_resize_status);
+
+ INT32DECODE(*pp, config->entry_ageout);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5P__facc_cache_image_config_dec() */
+
+
+/*-------------------------------------------------------------------------
* Function: H5P__facc_file_image_info_set
*
* Purpose: Copies a file image property when it's set for a property list
diff --git a/src/H5Ppublic.h b/src/H5Ppublic.h
index c736d7b..5aa8301 100644
--- a/src/H5Ppublic.h
+++ b/src/H5Ppublic.h
@@ -365,6 +365,8 @@ H5_DLL herr_t H5Pget_all_coll_metadata_ops(hid_t plist_id, hbool_t *is_collectiv
H5_DLL herr_t H5Pset_coll_metadata_write(hid_t plist_id, hbool_t is_collective);
H5_DLL herr_t H5Pget_coll_metadata_write(hid_t plist_id, hbool_t *is_collective);
#endif /* H5_HAVE_PARALLEL */
+H5_DLL herr_t H5Pset_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t *config_ptr);
+H5_DLL herr_t H5Pget_mdc_image_config(hid_t plist_id, H5AC_cache_image_config_t *config_ptr /*out*/);
/* Dataset creation property list (DCPL) routines */
H5_DLL herr_t H5Pset_layout(hid_t plist_id, H5D_layout_t layout);
diff --git a/src/H5SMpkg.h b/src/H5SMpkg.h
index 3b13e23..342543d 100644
--- a/src/H5SMpkg.h
+++ b/src/H5SMpkg.h
@@ -256,8 +256,6 @@ H5FL_ARR_EXTERN(H5SM_index_header_t);
H5FL_EXTERN(H5SM_list_t);
H5FL_ARR_EXTERN(H5SM_sohm_t);
-H5_DLLVAR const H5AC_class_t H5AC_SOHM_TABLE[1];
-H5_DLLVAR const H5AC_class_t H5AC_SOHM_LIST[1];
H5_DLLVAR const H5B2_class_t H5SM_INDEX[1];
/****************************/
diff --git a/src/Makefile.am b/src/Makefile.am
index 939e151..69b54b4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -46,7 +46,8 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5system.c H5timer.c H5trace.c \
H5B.c H5Bcache.c H5Bdbg.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 \
+ H5C.c H5Cdbg.c H5Cepoch.c H5Cimage.c H5Clog.c H5Cprefetched.c \
+ H5Cquery.c H5Ctag.c H5Ctest.c \
H5CS.c \
H5D.c H5Dbtree.c H5Dbtree2.c H5Dchunk.c H5Dcompact.c H5Dcontig.c H5Ddbg.c \
H5Ddeprec.c H5Dearray.c H5Defl.c H5Dfarray.c H5Dfill.c H5Dint.c \
@@ -81,7 +82,8 @@ libhdf5_la_SOURCES= H5.c H5checksum.c H5dbg.c H5system.c H5timer.c H5trace.c \
H5MF.c H5MFaggr.c H5MFdbg.c H5MFsection.c \
H5MM.c H5MP.c H5MPtest.c \
H5O.c H5Oainfo.c H5Oalloc.c H5Oattr.c \
- H5Oattribute.c H5Obogus.c H5Obtreek.c H5Ocache.c H5Ochunk.c \
+ H5Oattribute.c H5Obogus.c H5Obtreek.c H5Ocache.c H5Ocache_image.c \
+ H5Ochunk.c \
H5Ocont.c H5Ocopy.c H5Odbg.c H5Odrvinfo.c H5Odtype.c H5Oefl.c \
H5Ofill.c H5Oflush.c H5Ofsinfo.c H5Oginfo.c \
H5Olayout.c \