diff options
Diffstat (limited to 'src/H5HFcache.c')
-rw-r--r-- | src/H5HFcache.c | 819 |
1 files changed, 701 insertions, 118 deletions
diff --git a/src/H5HFcache.c b/src/H5HFcache.c index 04f1459..f957e2e 100644 --- a/src/H5HFcache.c +++ b/src/H5HFcache.c @@ -5,12 +5,10 @@ * * * 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. * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /*------------------------------------------------------------------------- @@ -69,6 +67,7 @@ /********************/ /* Local encode/decode routines */ +static herr_t H5HF__hdr_prefix_decode(H5HF_hdr_t *hdr, const uint8_t **image_ref); static herr_t H5HF__dtable_encode(H5F_t *f, uint8_t **pp, const H5HF_dtable_t *dtable); static herr_t H5HF__dtable_decode(H5F_t *f, const uint8_t **pp, H5HF_dtable_t *dtable); @@ -80,7 +79,7 @@ static htri_t H5HF__cache_hdr_verify_chksum(const void *image_ptr, size_t len, v static void *H5HF__cache_hdr_deserialize(const void *image, size_t len, void *udata, hbool_t *dirty); static herr_t H5HF__cache_hdr_image_len(const void *thing, size_t *image_len); -static herr_t H5HF__cache_hdr_pre_serialize(const H5F_t *f, hid_t dxpl_id, +static herr_t H5HF__cache_hdr_pre_serialize(H5F_t *f, hid_t dxpl_id, void *thing, haddr_t addr, size_t len, haddr_t *new_addr, size_t *new_len, unsigned *flags); static herr_t H5HF__cache_hdr_serialize(const H5F_t *f, void *image, @@ -92,12 +91,12 @@ static htri_t H5HF__cache_iblock_verify_chksum(const void *image_ptr, size_t len static void *H5HF__cache_iblock_deserialize(const void *image, size_t len, void *udata, hbool_t *dirty); static herr_t H5HF__cache_iblock_image_len(const void *thing, size_t *image_len); -static herr_t H5HF__cache_iblock_pre_serialize(const H5F_t *f, hid_t dxpl_id, +static herr_t H5HF__cache_iblock_pre_serialize(H5F_t *f, hid_t dxpl_id, void *thing, haddr_t addr, size_t len, haddr_t *new_addr, size_t *new_len, unsigned *flags); static herr_t H5HF__cache_iblock_serialize(const H5F_t *f, void *image, size_t len, void *thing); -static herr_t H5HF__cache_iblock_notify(H5C_notify_action_t action, void *thing); +static herr_t H5HF__cache_iblock_notify(H5AC_notify_action_t action, void *thing); static herr_t H5HF__cache_iblock_free_icr(void *thing); static herr_t H5HF__cache_dblock_get_initial_load_size(void *udata, size_t *image_len); @@ -105,24 +104,27 @@ static htri_t H5HF__cache_dblock_verify_chksum(const void *image_ptr, size_t len static void *H5HF__cache_dblock_deserialize(const void *image, size_t len, void *udata, hbool_t *dirty); static herr_t H5HF__cache_dblock_image_len(const void *thing, size_t *image_len); -static herr_t H5HF__cache_dblock_pre_serialize(const H5F_t *f, hid_t dxpl_id, +static herr_t H5HF__cache_dblock_pre_serialize(H5F_t *f, hid_t dxpl_id, void *thing, haddr_t addr, size_t len, haddr_t *new_addr, size_t *new_len, unsigned *flags); static herr_t H5HF__cache_dblock_serialize(const H5F_t *f, void *image, size_t len, void *thing); -static herr_t H5HF__cache_dblock_notify(H5C_notify_action_t action, void *thing); +static herr_t H5HF__cache_dblock_notify(H5AC_notify_action_t action, void *thing); 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 */ @@ -200,6 +202,52 @@ H5FL_BLK_DEFINE(direct_block); /*------------------------------------------------------------------------- + * Function: H5HF__hdr_prefix_decode() + * + * Purpose: Decode a fractal heap header's prefix + * + * Return: Success: SUCCEED + * Failure: FAIL + * + * Programmer: Quincey Koziol + * December 15, 2016 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5HF__hdr_prefix_decode(H5HF_hdr_t *hdr, const uint8_t **image_ref) +{ + const uint8_t *image = *image_ref; /* Pointer into into supplied image */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_STATIC + + /* Sanity checks */ + HDassert(hdr); + HDassert(image); + + /* Magic number */ + if(HDmemcmp(image, H5HF_HDR_MAGIC, (size_t)H5_SIZEOF_MAGIC)) + HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "wrong fractal heap header signature") + image += H5_SIZEOF_MAGIC; + + /* Version */ + if(*image++ != H5HF_HDR_VERSION) + HGOTO_ERROR(H5E_HEAP, H5E_VERSION, FAIL, "wrong fractal heap header version") + + /* General heap information */ + UINT16DECODE(image, hdr->id_len); /* Heap ID length */ + UINT16DECODE(image, hdr->filter_len); /* I/O filters' encoded length */ + + /* Update the image buffer pointer */ + *image_ref = image; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5HF__hdr_prefix_decode() */ + + +/*------------------------------------------------------------------------- * Function: H5HF__dtable_decode * * Purpose: Decodes the metadata for a doubling table @@ -360,10 +408,10 @@ static herr_t H5HF__cache_hdr_get_final_load_size(const void *_image, size_t image_len, void *_udata, size_t *actual_len) { - const uint8_t *image = (const uint8_t *)_image; /* Pointer into raw data buffer */ - H5HF_hdr_cache_ud_t *udata = (H5HF_hdr_cache_ud_t *)_udata; /* pointer to user data */ - unsigned filter_len; /* Size of I/O filter information (in bytes) */ - htri_t ret_value = SUCCEED; /* Return value */ + H5HF_hdr_t hdr; /* Temporary fractal heap header */ + const uint8_t *image = (const uint8_t *)_image; /* Pointer into into supplied image */ + H5HF_hdr_cache_ud_t *udata = (H5HF_hdr_cache_ud_t *)_udata; /* User data for callback */ + herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC @@ -373,25 +421,16 @@ H5HF__cache_hdr_get_final_load_size(const void *_image, size_t image_len, HDassert(actual_len); HDassert(*actual_len == image_len); - /* Magic number */ - if(HDmemcmp(image, H5HF_HDR_MAGIC, (size_t)H5_SIZEOF_MAGIC)) - HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, FAIL, "wrong fractal heap header signature") - image += H5_SIZEOF_MAGIC; - - /* Version */ - if(*image++ != H5HF_HDR_VERSION) - HGOTO_ERROR(H5E_HEAP, H5E_VERSION, FAIL, "wrong fractal heap header version") - - /* General heap information */ - image += 2; /* Heap ID length */ - UINT16DECODE(image, filter_len); /* I/O filters' encoded length */ + /* Deserialize the fractal heap header's prefix */ + if(H5HF__hdr_prefix_decode(&hdr, &image) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTDECODE, FAIL, "can't decode fractal heap header prefix") /* Check for I/O filter info on this heap */ - if(filter_len > 0) + if(hdr.filter_len > 0) /* Compute the extra heap header size */ *actual_len += (size_t)(H5F_SIZEOF_SIZE(udata->f) /* Size of size for filtered root direct block */ + (unsigned)4 /* Size of filter mask for filtered root direct block */ - + filter_len); /* Size of encoded I/O filter info */ + + hdr.filter_len); /* Size of encoded I/O filter info */ done: FUNC_LEAVE_NOAPI(ret_value) @@ -473,18 +512,9 @@ H5HF__cache_hdr_deserialize(const void *_image, size_t len, void *_udata, if(NULL == (hdr = H5HF_hdr_alloc(udata->f))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") - /* Magic number */ - if(HDmemcmp(image, H5HF_HDR_MAGIC, (size_t)H5_SIZEOF_MAGIC)) - HGOTO_ERROR(H5E_HEAP, H5E_BADVALUE, NULL, "wrong fractal heap header signature") - image += H5_SIZEOF_MAGIC; - - /* Version */ - if(*image++ != H5HF_HDR_VERSION) - HGOTO_ERROR(H5E_HEAP, H5E_VERSION, NULL, "wrong fractal heap header version") - - /* General heap information */ - UINT16DECODE(image, hdr->id_len); /* Heap ID length */ - UINT16DECODE(image, hdr->filter_len); /* I/O filters' encoded length */ + /* Deserialize the fractal heap header's prefix */ + if(H5HF__hdr_prefix_decode(hdr, &image) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTDECODE, NULL, "can't decode fractal heap header prefix") /* Heap status flags */ /* (bit 0: "huge" object IDs have wrapped) */ @@ -635,7 +665,7 @@ H5HF__cache_hdr_image_len(const void *_thing, size_t *image_len) *------------------------------------------------------------------------- */ static herr_t -H5HF__cache_hdr_pre_serialize(const H5F_t *f, hid_t H5_ATTR_UNUSED dxpl_id, +H5HF__cache_hdr_pre_serialize(H5F_t *f, hid_t H5_ATTR_UNUSED dxpl_id, void *_thing, haddr_t addr, size_t len, haddr_t H5_ATTR_UNUSED *new_addr, size_t H5_ATTR_UNUSED *new_len, unsigned *flags) { @@ -658,6 +688,7 @@ H5HF__cache_hdr_pre_serialize(const 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: @@ -672,10 +703,22 @@ H5HF__cache_hdr_pre_serialize(const 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 */ @@ -1147,8 +1190,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 @@ -1159,7 +1203,7 @@ H5HF__cache_iblock_image_len(const void *_thing, size_t *image_len) *------------------------------------------------------------------------- */ static herr_t -H5HF__cache_iblock_pre_serialize(const H5F_t *f, hid_t dxpl_id, void *_thing, +H5HF__cache_iblock_pre_serialize(H5F_t *f, hid_t dxpl_id, void *_thing, haddr_t addr, size_t H5_ATTR_UNUSED len, haddr_t *new_addr, size_t H5_ATTR_UNUSED *new_len, unsigned *flags) { @@ -1188,10 +1232,12 @@ H5HF__cache_iblock_pre_serialize(const 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") @@ -1201,9 +1247,9 @@ H5HF__cache_iblock_pre_serialize(const 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 */ @@ -1253,7 +1299,7 @@ H5HF__cache_iblock_pre_serialize(const H5F_t *f, hid_t dxpl_id, void *_thing, } /* end if */ *new_addr = iblock_addr; - *flags = H5C__SERIALIZE_MOVED_FLAG; + *flags = H5AC__SERIALIZE_MOVED_FLAG; } /* end if */ else *flags = 0; @@ -1400,7 +1446,7 @@ H5HF__cache_iblock_serialize(const H5F_t *f, void *_image, size_t len, *------------------------------------------------------------------------- */ static herr_t -H5HF__cache_iblock_notify(H5C_notify_action_t action, void *_thing) +H5HF__cache_iblock_notify(H5AC_notify_action_t action, void *_thing) { H5HF_indirect_t *iblock = (H5HF_indirect_t *)_thing; /* Indirect block info */ herr_t ret_value = SUCCEED; /* Return value */ @@ -1463,6 +1509,8 @@ H5HF__cache_iblock_notify(H5C_notify_action_t action, void *_thing) case H5AC_NOTIFY_ACTION_ENTRY_CLEANED: case H5AC_NOTIFY_ACTION_CHILD_DIRTIED: case H5AC_NOTIFY_ACTION_CHILD_CLEANED: + case H5AC_NOTIFY_ACTION_CHILD_UNSERIALIZED: + case H5AC_NOTIFY_ACTION_CHILD_SERIALIZED: /* do nothing */ break; @@ -1618,10 +1666,6 @@ H5HF__cache_dblock_verify_chksum(const void *_image, size_t len, void *_udata) hdr = par_info->hdr; HDassert(hdr); - /* Reset callback context info */ - udata->decompressed = FALSE; - udata->dblk = NULL; - /* Get out if data block is not checksummed */ if(!(hdr->checksum_dblocks)) HGOTO_DONE(TRUE); @@ -1728,7 +1772,7 @@ H5HF__cache_dblock_deserialize(const void *_image, size_t len, void *_udata, H5HF_dblock_cache_ud_t *udata = (H5HF_dblock_cache_ud_t *)_udata; /* User data for callback */ H5HF_parent_t *par_info; /* Pointer to parent information */ H5HF_direct_t *dblock = NULL; /* Direct block info */ - const uint8_t *image = _image;/* Pointer into raw data buffer */ + const uint8_t *image = (const uint8_t *)_image;/* Pointer into raw data buffer */ void *read_buf = NULL; /* Pointer to buffer to decompress */ haddr_t heap_addr; /* Address of heap header in the file */ void * ret_value = NULL; /* Return value */ @@ -2028,7 +2072,7 @@ H5HF__cache_dblock_image_len(const void *_thing, size_t *image_len) *------------------------------------------------------------------------- */ static herr_t -H5HF__cache_dblock_pre_serialize(const H5F_t *f, hid_t dxpl_id, void *_thing, +H5HF__cache_dblock_pre_serialize(H5F_t *f, hid_t dxpl_id, void *_thing, haddr_t addr, size_t len, haddr_t *new_addr, size_t *new_len, unsigned *flags) { hbool_t at_tmp_addr; /* Flag to indicate direct block is */ @@ -2345,12 +2389,12 @@ H5HF__cache_dblock_pre_serialize(const H5F_t *f, hid_t dxpl_id, void *_thing, /* finally, pass data back to the metadata cache as appropriate */ if(!H5F_addr_eq(addr, dblock_addr)) { - dblock_flags |= H5C__SERIALIZE_MOVED_FLAG; + dblock_flags |= H5AC__SERIALIZE_MOVED_FLAG; *new_addr = dblock_addr; } /* end if */ if((hdr->filter_len > 0) && (len != write_size)) { - dblock_flags |= H5C__SERIALIZE_RESIZED_FLAG; + dblock_flags |= H5AC__SERIALIZE_RESIZED_FLAG; *new_len = write_size; } /* end if */ @@ -2444,7 +2488,7 @@ H5HF__cache_dblock_serialize(const H5F_t *f, void *image, size_t len, *------------------------------------------------------------------------- */ static herr_t -H5HF__cache_dblock_notify(H5C_notify_action_t action, void *_thing) +H5HF__cache_dblock_notify(H5AC_notify_action_t action, void *_thing) { H5HF_direct_t *dblock = (H5HF_direct_t *)_thing; /* Fractal heap direct block */ herr_t ret_value = SUCCEED; /* Return value */ @@ -2480,6 +2524,8 @@ H5HF__cache_dblock_notify(H5C_notify_action_t action, void *_thing) case H5AC_NOTIFY_ACTION_ENTRY_CLEANED: case H5AC_NOTIFY_ACTION_CHILD_DIRTIED: case H5AC_NOTIFY_ACTION_CHILD_CLEANED: + case H5AC_NOTIFY_ACTION_CHILD_UNSERIALIZED: + case H5AC_NOTIFY_ACTION_CHILD_SERIALIZED: /* do nothing */ break; @@ -2556,6 +2602,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 @@ -2565,9 +2659,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 */ @@ -2579,6 +2674,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); @@ -2643,15 +2739,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; @@ -2659,25 +2905,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) @@ -2712,6 +2981,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 @@ -2721,8 +3024,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; @@ -2732,17 +3036,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 */ @@ -2779,6 +3085,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 @@ -2788,53 +3141,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 */ @@ -2867,6 +3249,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 @@ -2876,33 +3306,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)) { @@ -2912,9 +3347,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 */ |