diff options
author | John Mainzer <mainzer@hdfgroup.org> | 2014-06-18 18:18:36 (GMT) |
---|---|---|
committer | John Mainzer <mainzer@hdfgroup.org> | 2014-06-18 18:18:36 (GMT) |
commit | 12ee3deb82f879cd4cf40e1604442e41ab2359bf (patch) | |
tree | 455b95f4dd1abb0873fa677ed0c372a64ed56e92 /src | |
parent | 471aedb1e85ba6b4c5b829373963c21d77ce0835 (diff) | |
download | hdf5-12ee3deb82f879cd4cf40e1604442e41ab2359bf.zip hdf5-12ee3deb82f879cd4cf40e1604442e41ab2359bf.tar.gz hdf5-12ee3deb82f879cd4cf40e1604442e41ab2359bf.tar.bz2 |
[svn-r25321] Reworked and enhanced extreme sanity check in H5C.c, fixing an
uninitialized variable bug in passing.
Added the H5C_get_entry_ptr_from_addr() and H5C_verify_entry_type()
in H5C.c, along with their pass through functions in H5AC.c. Note
that H5C_get_entry_ptr_from_addr() is quite slimey, and should be
avoided if possible.
Updated fractal heap to use flush ordering to force children
in the hdr / iblock / dblock tree to be flushed prior to parents.
This is part of a port of the fractal heap to the V3 cache, although
I gather that it will also be useful for SWMR.
Added sanity checking code to the fractal heap to verify that direct
blocks are flushed before their parent indirect blocks, child indirect
block are flushed prior to their parent indirect blocks, and that the
root iblock or dblock is flushed prior to the header
Tested on:
Jam: Serial and parallel
Phoenix (Debian): serial, debug and production
amazon (MacOS): serial
Diffstat (limited to 'src')
-rw-r--r-- | src/H5AC.c | 131 | ||||
-rw-r--r-- | src/H5ACprivate.h | 12 | ||||
-rw-r--r-- | src/H5C.c | 700 | ||||
-rw-r--r-- | src/H5Cpkg.h | 13 | ||||
-rw-r--r-- | src/H5Cprivate.h | 12 | ||||
-rw-r--r-- | src/H5HFcache.c | 1114 | ||||
-rw-r--r-- | src/H5HFdblock.c | 1 | ||||
-rw-r--r-- | src/H5HFiblock.c | 32 | ||||
-rw-r--r-- | src/H5HFpkg.h | 13 |
9 files changed, 1938 insertions, 90 deletions
@@ -2592,6 +2592,137 @@ done: /*************************************************************************/ +/*************************** Debugging Functions: ************************/ +/*************************************************************************/ + +/*------------------------------------------------------------------------- + * + * 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 + * + * Changes: + * + * None. + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5AC_get_entry_ptr_from_addr(const H5F_t *f, + haddr_t addr, + void ** entry_ptr_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + if ( H5C_get_entry_ptr_from_addr(f, 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_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. + * + *------------------------------------------------------------------------- + */ +#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) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + if ( H5C_verify_entry_type(f, addr, (const H5C_class_t *)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 */ + + + +/*************************************************************************/ /**************************** Private Functions: *************************/ /*************************************************************************/ diff --git a/src/H5ACprivate.h b/src/H5ACprivate.h index ca6dcbf..623f502 100644 --- a/src/H5ACprivate.h +++ b/src/H5ACprivate.h @@ -416,5 +416,17 @@ H5_DLL herr_t H5AC_ignore_tags(H5F_t * f); H5_DLL herr_t H5AC_add_candidate(H5AC_t * cache_ptr, haddr_t addr); #endif /* H5_HAVE_PARALLEL */ +#ifndef NDEBUG /* debugging functions */ + +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_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); + +#endif /* NDEBUG */ /* end debugging functions */ + #endif /* !_H5ACprivate_H */ @@ -186,8 +186,8 @@ static herr_t H5C_verify_tag(int id, haddr_t tag); #if H5C_DO_EXTREME_SANITY_CHECKS static herr_t H5C_validate_lru_list(H5C_t * cache_ptr); -static herr_t H5C_verify_not_in_index(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr); +static herr_t H5C_validate_pinned_entry_list(H5C_t * cache_ptr); +static herr_t H5C_validate_protected_entry_list(H5C_t * cache_ptr); #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ @@ -1285,6 +1285,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; @@ -1543,6 +1547,23 @@ H5C_dest(H5F_t * f, cache_ptr->slist_ptr = NULL; } /* end if */ + /* Only display count of number of calls to H5C_get_entry_ptr_from_add() + * if NDEBUG is undefined, and H5C_DO_SANITY_CHECKS is defined. Need + * this as the print statement will upset windows, and we frequently + * run debug builds there. + * + * Note that the count is still kept whenever NDEBUG is undefined, and + * is reasonably accessible via debugger. + */ +#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 */ +#endif /* NDEBUG */ + cache_ptr->magic = 0; cache_ptr = H5FL_FREE(H5C_t, cache_ptr); @@ -1599,7 +1620,7 @@ H5C_expunge_entry(H5F_t * f, if ( H5C_validate_lru_list(cache_ptr) < 0 ) { HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "LRU sanity check failed.\n"); + "LRU extreme sanity check failed on entry.\n"); } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ @@ -1654,7 +1675,7 @@ done: if ( H5C_validate_lru_list(cache_ptr) < 0 ) { HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "LRU sanity check failed.\n"); + "LRU extreme sanity check failed on exit.\n"); } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ @@ -1719,6 +1740,16 @@ H5C_flush_cache(H5F_t *f, hid_t primary_dxpl_id, hid_t secondary_dxpl_id, unsign HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); HDassert( cache_ptr->slist_ptr ); +#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 */ + ignore_protected = ( (flags & H5C__FLUSH_IGNORE_PROTECTED_FLAG) != 0 ); destroy = ( (flags & H5C__FLUSH_INVALIDATE_FLAG) != 0 ); @@ -2006,7 +2037,8 @@ end_of_inner_loop: HDassert( (initial_slist_len + cache_ptr->slist_len_increase - flushed_entries_count) == cache_ptr->slist_len ); - HDassert( (initial_slist_size + cache_ptr->slist_size_increase - + HDassert( (initial_slist_size + + (size_t)(cache_ptr->slist_size_increase) - flushed_entries_size) == cache_ptr->slist_size ); #endif /* H5C_DO_SANITY_CHECKS */ @@ -2682,17 +2714,15 @@ H5C_insert_entry(H5F_t * f, HDassert( thing ); #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_verify_not_in_index(cache_ptr, (H5C_cache_entry_t *)thing) < 0 ) { + /* no need to verify that entry is not already in the index as */ + /* we already make that check below. */ - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "thing already in index.\n"); - } -#endif /* H5C_DO_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 ) ) { -#if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { - - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "LRU sanity check failed.\n"); + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on entry.\n"); } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ @@ -2742,6 +2772,7 @@ H5C_insert_entry(H5F_t * f, entry_ptr->is_pinned = insert_pinned; entry_ptr->pinned_from_client = insert_pinned; + entry_ptr->pinned_from_cache = FALSE; entry_ptr->flush_me_last = flush_last; #ifdef H5_HAVE_PARALLEL entry_ptr->flush_me_collectively = flush_collectively; @@ -2911,10 +2942,12 @@ 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_lru_list(cache_ptr) < 0 ) { + 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, \ - "LRU sanity check failed.\n"); + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed just before done.\n"); } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ @@ -2930,10 +2963,12 @@ H5C_insert_entry(H5F_t * f, done: #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + 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, \ - "LRU sanity check failed.\n"); + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on exit.\n"); } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ @@ -3010,12 +3045,13 @@ H5C_mark_entries_as_clean(H5F_t * f, HDassert( ce_array_ptr != NULL ); #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || + ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || + ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - HDassert(0); - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "LRU sanity check failed.\n"); - } + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on entry.\n"); + } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ for ( i = 0; i < ce_array_len; i++ ) @@ -3042,11 +3078,12 @@ H5C_mark_entries_as_clean(H5F_t * f, } #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || + ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || + ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - HDassert(0); - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "LRU sanity check failed.\n"); + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed in for loop.\n"); } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ #endif /* H5C_DO_SANITY_CHECKS */ @@ -3220,12 +3257,13 @@ H5C_mark_entries_as_clean(H5F_t * f, done: #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || + ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || + ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - HDassert(0); - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "LRU sanity check failed.\n"); - } + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on exit.\n"); + } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) @@ -3359,11 +3397,13 @@ H5C_move_entry(H5C_t * cache_ptr, HDassert( H5F_addr_ne(old_addr, new_addr) ); #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + 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, \ - "LRU sanity check failed.\n"); - } + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on entry.\n"); + } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ H5C__SEARCH_INDEX(cache_ptr, old_addr, entry_ptr, FAIL) @@ -3463,10 +3503,11 @@ H5C_move_entry(H5C_t * cache_ptr, * the sanity checks. */ HDassert( cache_ptr->slist_len_increase > 1 ); - HDassert( cache_ptr->slist_size_increase > entry_ptr->size ); + HDassert( cache_ptr->slist_size_increase > + (int64_t)(entry_ptr->size) ); cache_ptr->slist_len_increase -= 1; - cache_ptr->slist_size_increase -= entry_ptr->size; + cache_ptr->slist_size_increase -= (int64_t)(entry_ptr->size); } #endif /* H5C_DO_SANITY_CHECKS */ @@ -3480,11 +3521,13 @@ H5C_move_entry(H5C_t * cache_ptr, done: #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + 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, \ - "LRU sanity check failed.\n"); - } + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on exit.\n"); + } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) @@ -3530,6 +3573,15 @@ H5C_resize_entry(void *thing, size_t new_size) if(!(entry_ptr->is_pinned || entry_ptr->is_protected)) 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"); + } +#endif /* H5C_DO_EXTREME_SANITY_CHECKS */ + /* update for change in entry size if necessary */ if ( entry_ptr->size != new_size ) { hbool_t was_clean; @@ -3594,6 +3646,16 @@ 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"); + } +#endif /* H5C_DO_EXTREME_SANITY_CHECKS */ + FUNC_LEAVE_NOAPI(ret_value) } /* H5C_resize_entry() */ @@ -3608,6 +3670,9 @@ done: * Programmer: Quincey Koziol * 3/26/09 * + * Changes: Added sanity checks to clarify the circumstances under + * which an entry can be pinned. JRM -- 4/27/14 + * *------------------------------------------------------------------------- */ #ifndef NDEBUG @@ -3627,6 +3692,7 @@ H5C_pin_entry_from_client(H5C_t UNUSED * cache_ptr, /* Sanity checks */ HDassert( cache_ptr ); HDassert( entry_ptr ); + HDassert( entry_ptr->is_protected ); /* Check if the entry is already pinned */ if(entry_ptr->is_pinned) { @@ -3644,7 +3710,9 @@ H5C_pin_entry_from_client(H5C_t UNUSED * cache_ptr, entry_ptr->pinned_from_client = TRUE; done: + FUNC_LEAVE_NOAPI(ret_value) + } /* H5C_pin_entry_from_client() */ @@ -3659,6 +3727,9 @@ done: * Programmer: John Mainzer * 4/26/06 * + * Changes: Added extreme sanity checks on entry and exit. + * JRM -- 4/26/14 + * *------------------------------------------------------------------------- */ herr_t @@ -3677,6 +3748,17 @@ H5C_pin_protected_entry(void *thing) HDassert(cache_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"); + } +#endif /* H5C_DO_EXTREME_SANITY_CHECKS */ + + /* Only protected entries can be pinned */ if(!entry_ptr->is_protected) HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "Entry isn't protected") @@ -3686,6 +3768,17 @@ 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"); + } +#endif /* H5C_DO_EXTREME_SANITY_CHECKS */ + FUNC_LEAVE_NOAPI(ret_value) } /* H5C_pin_protected_entry() */ @@ -3779,11 +3872,12 @@ H5C_protect(H5F_t * f, HDassert( H5F_addr_defined(addr) ); #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + if ( ( H5C_validate_protected_entry_list(cache_ptr) < 0 ) || + ( H5C_validate_pinned_entry_list(cache_ptr) < 0 ) || + ( H5C_validate_lru_list(cache_ptr) < 0 ) ) { - HDassert(0); - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, \ - "LRU sanity check failed.\n"); + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on entry.\n"); } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ @@ -4138,11 +4232,13 @@ H5C_protect(H5F_t * f, done: #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + 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, \ - "LRU sanity check failed.\n"); - } + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on exit.\n"); + } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) @@ -5353,7 +5449,9 @@ H5C_unpin_entry_from_client(H5C_t * cache_ptr, entry_ptr->pinned_from_client = FALSE; done: + FUNC_LEAVE_NOAPI(ret_value) + } /* H5C_unpin_entry_from_client() */ @@ -5368,6 +5466,9 @@ done: * Programmer: John Mainzer * 3/22/06 * + * Changes: Added extreme sanity checks on entry and exit. + JRM -- 4/26/14 + * *------------------------------------------------------------------------- */ herr_t @@ -5385,12 +5486,35 @@ H5C_unpin_entry(void *_entry_ptr) HDassert(cache_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"); + } +#endif /* H5C_DO_EXTREME_SANITY_CHECKS */ + + /* Unpin the entry */ if(H5C_unpin_entry_from_client(cache_ptr, entry_ptr, TRUE) < 0) 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"); + } +#endif /* H5C_DO_EXTREME_SANITY_CHECKS */ + FUNC_LEAVE_NOAPI(ret_value) + } /* H5C_unpin_entry() */ @@ -5495,14 +5619,15 @@ H5C_unprotect(H5F_t * f, was_clean = ! ( entry_ptr->is_dirty ); #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + 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, \ - "LRU sanity check failed.\n"); - } + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on entry.\n"); + } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ - /* if the entry has multiple read only protects, just decrement * the ro_ref_counter. Don't actually unprotect until the ref count * drops to zero. @@ -5735,11 +5860,13 @@ H5C_unprotect(H5F_t * f, done: #if H5C_DO_EXTREME_SANITY_CHECKS - if ( H5C_validate_lru_list(cache_ptr) < 0 ) { + 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, \ - "LRU sanity check failed.\n"); - } + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ + "an extreme sanity check failed on exit.\n"); + } #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ FUNC_LEAVE_NOAPI(ret_value) @@ -7958,7 +8085,8 @@ H5C_flush_invalidate_cache(H5F_t * f, HDassert( (actual_slist_len + cache_ptr->slist_len) == (initial_slist_len + cache_ptr->slist_len_increase) ); HDassert( (actual_slist_size + cache_ptr->slist_size) == - (initial_slist_size + cache_ptr->slist_size_increase) ); + (initial_slist_size + + (size_t)(cache_ptr->slist_size_increase)) ); } #endif /* H5C_DO_SANITY_CHECKS */ @@ -8590,6 +8718,8 @@ H5C_flush_single_entry(H5F_t * f, } done: + HDassert( ( destroy ) || + ( ( entry_ptr ) && ( ! entry_ptr->flush_in_progress ) ) ); FUNC_LEAVE_NOAPI(ret_value) } /* H5C_flush_single_entry() */ @@ -9083,6 +9213,11 @@ done: * * Programmer: John Mainzer, 7/14/05 * + * Changes: + * + * Added code to verify that the LRU contains no pinned + * entries. JRM -- 4/25/14 + * *------------------------------------------------------------------------- */ #if H5C_DO_EXTREME_SANITY_CHECKS @@ -9162,6 +9297,13 @@ H5C_validate_lru_list(H5C_t * cache_ptr) HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 6 failed") } + if ( ( entry_ptr->is_pinned ) || + ( entry_ptr->pinned_from_client ) || + ( entry_ptr->pinned_from_cache ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 7 failed") + } + len++; size += entry_ptr->size; entry_ptr = entry_ptr->next; @@ -9170,7 +9312,7 @@ H5C_validate_lru_list(H5C_t * cache_ptr) if ( ( cache_ptr->LRU_list_len != len ) || ( cache_ptr->LRU_list_size != size ) ) { - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 7 failed") + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 8 failed") } done: @@ -9189,11 +9331,10 @@ done: /*------------------------------------------------------------------------- * - * Function: H5C_verify_not_in_index + * Function: H5C_validate_pinned_entry_list * - * Purpose: Debugging function that scans the hash table to verify - * that the specified instance of H5C_cache_entry_t is not - * present. + * Purpose: Debugging function that scans the pinned entry list for + * errors. * * If an error is detected, the function generates a * diagnostic and returns FAIL. If no error is detected, @@ -9201,42 +9342,245 @@ done: * * Return: FAIL if error is detected, SUCCEED otherwise. * - * Programmer: John Mainzer, 7/14/05 + * Programmer: John Mainzer, 4/25/14 + * + * Changes: + * + * None. * *------------------------------------------------------------------------- */ #if H5C_DO_EXTREME_SANITY_CHECKS static herr_t -H5C_verify_not_in_index(H5C_t * cache_ptr, - H5C_cache_entry_t * entry_ptr) +H5C_validate_pinned_entry_list(H5C_t * cache_ptr) { herr_t ret_value = SUCCEED; /* Return value */ - int32_t i; - int32_t depth; - H5C_cache_entry_t * scan_ptr = NULL; + int32_t len = 0; + size_t size = 0; + H5C_cache_entry_t * entry_ptr = NULL; FUNC_ENTER_NOAPI_NOINIT - HDassert( cache_ptr != NULL ); + HDassert( cache_ptr ); HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); - HDassert( entry_ptr != NULL ); - for ( i = 0; i < H5C__HASH_TABLE_LEN; i++ ) + if ( ( ( cache_ptr->pel_head_ptr == NULL ) + || + ( cache_ptr->pel_tail_ptr == NULL ) + ) + && + ( cache_ptr->pel_head_ptr != cache_ptr->pel_tail_ptr ) + ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 1 failed") + } + + if ( ( cache_ptr->pel_len < 0 ) || ( cache_ptr->pel_size < 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 2 failed") + } + + if ( ( cache_ptr->pel_len == 1 ) + && + ( ( cache_ptr->pel_head_ptr != cache_ptr->pel_tail_ptr ) + || + ( cache_ptr->pel_head_ptr == NULL ) + || + ( cache_ptr->pel_head_ptr->size != cache_ptr->pel_size ) + ) + ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 3 failed") + } + + if ( ( cache_ptr->pel_len >= 1 ) + && + ( ( cache_ptr->pel_head_ptr == NULL ) + || + ( cache_ptr->pel_head_ptr->prev != NULL ) + || + ( cache_ptr->pel_tail_ptr == NULL ) + || + ( cache_ptr->pel_tail_ptr->next != NULL ) + ) + ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 4 failed") + } + + entry_ptr = cache_ptr->pel_head_ptr; + while ( entry_ptr != NULL ) { - depth = 0; - scan_ptr = cache_ptr->index[i]; - while ( scan_ptr != NULL ) - { - if ( scan_ptr == entry_ptr ) { + if ( ( entry_ptr != cache_ptr->pel_head_ptr ) && + ( ( entry_ptr->prev == NULL ) || + ( entry_ptr->prev->next != entry_ptr ) ) ) { - HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, \ - "Entry already in index.") - } - depth++; - scan_ptr = scan_ptr->ht_next; + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 5 failed") + } + + if ( ( entry_ptr != cache_ptr->pel_tail_ptr ) && + ( ( entry_ptr->next == NULL ) || + ( entry_ptr->next->prev != entry_ptr ) ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 6 failed") + } + + if ( ! entry_ptr->is_pinned ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 7 failed") + } + + if ( ! ( ( entry_ptr->pinned_from_client ) || + ( entry_ptr->pinned_from_cache ) ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 8 failed") + } + + len++; + size += entry_ptr->size; + entry_ptr = entry_ptr->next; + } + + if ( ( cache_ptr->pel_len != len ) || + ( cache_ptr->pel_size != size ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 9 failed") + } + +done: + + if ( ret_value != SUCCEED ) { + + HDassert(0); + } + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_validate_pinned_entry_list() */ + +#endif /* H5C_DO_EXTREME_SANITY_CHECKS */ + + +/*------------------------------------------------------------------------- + * + * Function: H5C_validate_protected_entry_list + * + * Purpose: Debugging function that scans the protected entry 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, 4/25/14 + * + * Changes: + * + * None. + * + *------------------------------------------------------------------------- + */ +#if H5C_DO_EXTREME_SANITY_CHECKS + +static herr_t +H5C_validate_protected_entry_list(H5C_t * cache_ptr) +{ + herr_t ret_value = SUCCEED; /* Return value */ + int32_t len = 0; + size_t size = 0; + H5C_cache_entry_t * entry_ptr = NULL; + + FUNC_ENTER_NOAPI_NOINIT + + HDassert( cache_ptr ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + + if ( ( ( cache_ptr->pl_head_ptr == NULL ) + || + ( cache_ptr->pl_tail_ptr == NULL ) + ) + && + ( cache_ptr->pl_head_ptr != cache_ptr->pl_tail_ptr ) + ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 1 failed") + } + + if ( ( cache_ptr->pl_len < 0 ) || ( cache_ptr->pl_size < 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 2 failed") + } + + if ( ( cache_ptr->pl_len == 1 ) + && + ( ( cache_ptr->pl_head_ptr != cache_ptr->pl_tail_ptr ) + || + ( cache_ptr->pl_head_ptr == NULL ) + || + ( cache_ptr->pl_head_ptr->size != cache_ptr->pl_size ) + ) + ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 3 failed") + } + + if ( ( cache_ptr->pl_len >= 1 ) + && + ( ( cache_ptr->pl_head_ptr == NULL ) + || + ( cache_ptr->pl_head_ptr->prev != NULL ) + || + ( cache_ptr->pl_tail_ptr == NULL ) + || + ( cache_ptr->pl_tail_ptr->next != NULL ) + ) + ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 4 failed") + } + + entry_ptr = cache_ptr->pl_head_ptr; + while ( entry_ptr != NULL ) + { + + if ( ( entry_ptr != cache_ptr->pl_head_ptr ) && + ( ( entry_ptr->prev == NULL ) || + ( entry_ptr->prev->next != entry_ptr ) ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 5 failed") + } + + if ( ( entry_ptr != cache_ptr->pl_tail_ptr ) && + ( ( entry_ptr->next == NULL ) || + ( entry_ptr->next->prev != entry_ptr ) ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 6 failed") + } + + if ( ! entry_ptr->is_protected ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 7 failed") } + + if ( ( entry_ptr->is_read_only ) && + ( entry_ptr->ro_ref_count <= 0 ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 8 failed") + } + + len++; + size += entry_ptr->size; + entry_ptr = entry_ptr->next; + } + + if ( ( cache_ptr->pl_len != len ) || + ( cache_ptr->pl_size != size ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 9 failed") } done: @@ -9248,13 +9592,195 @@ done: FUNC_LEAVE_NOAPI(ret_value) -} /* H5C_verify_not_in_index() */ +} /* H5C_validate_protected_entry_list() */ #endif /* H5C_DO_EXTREME_SANITY_CHECKS */ /*------------------------------------------------------------------------- * + * 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 + * + * Changes: + * + * None. + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5C_get_entry_ptr_from_addr(const H5F_t *f, + haddr_t addr, + void ** entry_ptr_ptr) +{ + H5C_t * cache_ptr; + H5C_cache_entry_t * entry_ptr = NULL; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + HDassert( f ); + HDassert( f->shared ); + + cache_ptr = f->shared->cache; + + HDassert( cache_ptr != NULL ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( H5F_addr_defined(addr) ); + HDassert( entry_ptr_ptr != NULL ); + + /* this test duplicates two of the above asserts, but we need an + * invocation of HGOTO_ERROR to keep the compiler happy. + */ + if ( ( cache_ptr == NULL ) || ( cache_ptr->magic != H5C__H5C_T_MAGIC ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.") + } + + 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)++; + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_get_entry_ptr_from_addr() */ + +#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 + * + * Changes: + * + * None. + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +herr_t +H5C_verify_entry_type(const H5F_t *f, + haddr_t addr, + const H5C_class_t * expected_type, + hbool_t * in_cache_ptr, + hbool_t * type_ok_ptr) +{ + H5C_t * cache_ptr; + H5C_cache_entry_t * entry_ptr = NULL; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + HDassert( f ); + HDassert( f->shared ); + + cache_ptr = f->shared->cache; + + HDassert( cache_ptr != NULL ); + HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC ); + HDassert( H5F_addr_defined(addr) ); + HDassert( in_cache_ptr != NULL ); + HDassert( type_ok_ptr != NULL ); + + /* this test duplicates two of the above asserts, but we need an + * invocation of HGOTO_ERROR to keep the compiler happy. + */ + if ( ( cache_ptr == NULL ) || ( cache_ptr->magic != H5C__H5C_T_MAGIC ) ) { + + HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry.") + } + + 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; + *type_ok_ptr = (expected_type == entry_ptr->type); + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5C_verify_entry_type() */ + +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------- + * * Function: H5C_ignore_tags * * Purpose: Override all assertion frameworks associated with making diff --git a/src/H5Cpkg.h b/src/H5Cpkg.h index 909578b..0fdaa79 100644 --- a/src/H5Cpkg.h +++ b/src/H5Cpkg.h @@ -846,6 +846,11 @@ * 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. + * ****************************************************************************/ #define H5C__HASH_TABLE_LEN (64 * 1024) /* must be a power of 2 */ @@ -1007,6 +1012,12 @@ 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 */ }; @@ -2198,7 +2209,7 @@ if ( (cache_ptr)->index_size != \ (cache_ptr)->slist_len++; \ (cache_ptr)->slist_size += (entry_ptr)->size; \ (cache_ptr)->slist_len_increase++; \ - (cache_ptr)->slist_size_increase += (entry_ptr)->size; \ + (cache_ptr)->slist_size_increase += (int64_t)((entry_ptr)->size); \ \ HDassert( (cache_ptr)->slist_len > 0 ); \ HDassert( (cache_ptr)->slist_size > 0 ); \ diff --git a/src/H5Cprivate.h b/src/H5Cprivate.h index 433411f..38b9469 100644 --- a/src/H5Cprivate.h +++ b/src/H5Cprivate.h @@ -1223,5 +1223,17 @@ H5_DLL herr_t H5C_ignore_tags(H5C_t * cache_ptr); H5_DLL void H5C_retag_copied_metadata(H5C_t * cache_ptr, haddr_t metadata_tag); +#ifndef NDEBUG /* debugging functions */ + +H5_DLL herr_t H5C_get_entry_ptr_from_addr(const H5F_t *f, haddr_t addr, + void ** entry_ptr_ptr); + +H5_DLL herr_t H5C_verify_entry_type(const H5F_t * f, haddr_t addr, + const H5C_class_t * expected_type, + hbool_t * in_cache_ptr, + hbool_t * type_ok_ptr); + +#endif /* NDEBUG */ + #endif /* !_H5Cprivate_H */ diff --git a/src/H5HFcache.c b/src/H5HFcache.c index 030927d..0f73c68 100644 --- a/src/H5HFcache.c +++ b/src/H5HFcache.c @@ -88,14 +88,41 @@ static H5HF_indirect_t *H5HF_cache_iblock_load(H5F_t *f, hid_t dxpl_id, haddr_t static herr_t H5HF_cache_iblock_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5HF_indirect_t *iblock, unsigned UNUSED * flags_ptr); static herr_t H5HF_cache_iblock_dest(H5F_t *f, H5HF_indirect_t *iblock); static herr_t H5HF_cache_iblock_clear(H5F_t *f, H5HF_indirect_t *iblock, hbool_t destroy); +static herr_t H5HF_cache_iblock_notify(H5C_notify_action_t action, H5HF_indirect_t *iblock); static herr_t H5HF_cache_iblock_size(const H5F_t *f, const H5HF_indirect_t *iblock, size_t *size_ptr); static H5HF_direct_t *H5HF_cache_dblock_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, void *udata); static herr_t H5HF_cache_dblock_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5HF_direct_t *dblock, unsigned UNUSED * flags_ptr); static herr_t H5HF_cache_dblock_dest(H5F_t *f, H5HF_direct_t *dblock); static herr_t H5HF_cache_dblock_clear(H5F_t *f, H5HF_direct_t *dblock, hbool_t destroy); +static herr_t H5HF_cache_dblock_notify(H5C_notify_action_t action, H5HF_direct_t *dblock); static herr_t H5HF_cache_dblock_size(const H5F_t *f, const H5HF_direct_t *dblock, size_t *size_ptr); +/*********************************/ +/* Debugging Function Prototypes */ +/*********************************/ +#ifndef NDEBUG +static herr_t H5HF_cache_verify_hdr_descendants_clean(H5F_t *f, + hid_t dxpl_id, + H5HF_hdr_t * hdr, + hbool_t *clean_ptr); +static herr_t H5HF_cache_verify_iblock_descendants_clean(H5F_t *f, + hid_t dxpl_id, + H5HF_indirect_t * iblock, + unsigned * iblock_status_ptr, + hbool_t *clean_ptr); +static herr_t H5HF_cache_verify_iblocks_dblocks_clean(H5F_t *f, + H5HF_indirect_t * iblock, + hbool_t *clean_ptr, + hbool_t *has_dblocks_ptr); +static herr_t H5HF_cache_verify_descendant_iblocks_clean(H5F_t *f, + hid_t dxpl_id, + H5HF_indirect_t * iblock, + hbool_t *clean_ptr, + hbool_t *has_iblocks_ptr); +#endif /* NDEBUG */ + + /*********************/ /* Package Variables */ /*********************/ @@ -118,7 +145,7 @@ const H5AC_class_t H5AC_FHEAP_IBLOCK[1] = {{ (H5AC_flush_func_t)H5HF_cache_iblock_flush, (H5AC_dest_func_t)H5HF_cache_iblock_dest, (H5AC_clear_func_t)H5HF_cache_iblock_clear, - (H5AC_notify_func_t)NULL, + (H5AC_notify_func_t)H5HF_cache_iblock_notify, (H5AC_size_func_t)H5HF_cache_iblock_size, }}; @@ -129,7 +156,7 @@ const H5AC_class_t H5AC_FHEAP_DBLOCK[1] = {{ (H5AC_flush_func_t)H5HF_cache_dblock_flush, (H5AC_dest_func_t)H5HF_cache_dblock_dest, (H5AC_clear_func_t)H5HF_cache_dblock_clear, - (H5AC_notify_func_t)NULL, + (H5AC_notify_func_t)H5HF_cache_dblock_notify, (H5AC_size_func_t)H5HF_cache_dblock_size, }}; @@ -471,6 +498,33 @@ H5HF_cache_hdr_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5H uint8_t heap_flags; /* Status flags for heap */ uint32_t metadata_chksum; /* Computed metadata checksum value */ +#ifndef NDEBUG + /* verify that flush dependencies are working correctly. Do this + * by verifying that either: + * + * 1) the header has a root iblock, and that the root iblock and all + * of its children are clean, or + * + * 2) The header has a root dblock, which is clean, or + * + * 3) The heap is empty, and thus the header has neither a root + * iblock no a root dblock. In this case, the flush ordering + * constraint is met by default. + * + * Do this with a call to H5HF_cache_verify_hdr_descendants_clean(). + */ + hbool_t descendants_clean = TRUE; + + if ( H5HF_cache_verify_hdr_descendants_clean(f, dxpl_id, hdr, + &descendants_clean) < 0 ) + + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, \ + "can't verify hdr descendants clean.") + + HDassert( descendants_clean ); + +#endif /* NDEBUG */ + /* Set the shared heap header's file context for this operation */ hdr->f = f; @@ -778,6 +832,10 @@ H5HF_cache_iblock_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, void *_udata) /* Address of parent block */ iblock->parent = udata->par_info->iblock; + /* this copy of the parent pointer is needed by the notify callback so */ + /* that it can take down flush dependencies on eviction even if */ + /* the parent pointer has been nulled out. JRM -- 5/18/14 */ + iblock->fd_parent = udata->par_info->iblock; iblock->par_entry = udata->par_info->entry; if(iblock->parent) { /* Share parent block */ @@ -927,6 +985,34 @@ H5HF_cache_iblock_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, uint32_t metadata_chksum; /* Computed metadata checksum value */ size_t u; /* Local index variable */ +#ifndef NDEBUG + /* verify that flush dependencies are working correctly. Do this + * by verifying that all children of this iblock are clean. + */ + hbool_t descendants_clean = TRUE; + unsigned iblock_status; + + if ( H5AC_get_entry_status(f, iblock->addr, &iblock_status) < 0 ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get iblock status") + + /* since the current iblock is the guest of honor in a flush, we know + * that it is locked into the cache for the duration of the call. Hence + * 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(f, dxpl_id, + iblock, &iblock_status, + &descendants_clean) < 0 ) + + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, \ + "can't verify descendants clean.") + + HDassert(descendants_clean); + +#endif /* NDEBUG */ + /* Get the pointer to the shared heap header */ hdr = iblock->hdr; @@ -1165,6 +1251,131 @@ done: /*------------------------------------------------------------------------- + * Function: H5HF_cache_iblock_notify + * + * Purpose: Setup / takedown flush dependencies as indirect blocks + * are loaded / inserted and evicted from the metadata cache. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/17/14 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5HF_cache_iblock_notify(H5C_notify_action_t action, H5HF_indirect_t *iblock) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT + + /* + * Check arguments. + */ + HDassert(iblock); + HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(iblock->hdr); + if ( action == H5C_NOTIFY_ACTION_BEFORE_EVICT ) + HDassert((iblock->parent == iblock->fd_parent) || + ((NULL == iblock->parent) && (iblock->fd_parent))); + else + HDassert(iblock->parent == iblock->fd_parent); + + /* further sanity checks */ + if ( iblock->parent == NULL ) { + + /* Either this is the root iblock, or the parent pointer is */ + /* invalid. Since we save a copy of the parent pointer on */ + /* the insertion event, it doesn't matter if the parent pointer */ + /* is invalid just before eviction. However, we will not be */ + /* able to function if it is invalid on the insertion event. */ + /* Scream and die if this is the case. */ + + HDassert((action == H5C_NOTIFY_ACTION_BEFORE_EVICT) || + (iblock->block_off == 0)); + + /* pointer from hdr to root iblock will not be set up unless */ + /* the fractal heap has already pinned the hdr. Do what */ + /* sanity checking we can. */ + + if ( ( iblock->block_off == 0 ) && + ( iblock->hdr->root_iblock_flags & H5HF_ROOT_IBLOCK_PINNED ) ) + HDassert(iblock->hdr->root_iblock == iblock); + + } else { + /* if this is a child iblock, verify that the pointers are */ + /* either uninitialized or set up correctly. */ + H5HF_indirect_t *par_iblock = iblock->parent; + unsigned indir_idx; /* Index in parent's child iblock pointer array */ + + /* Sanity check */ + HDassert(par_iblock->child_iblocks); + HDassert(iblock->par_entry >= (iblock->hdr->man_dtable.max_direct_rows + * iblock->hdr->man_dtable.cparam.width)); + + /* Compute index in parent's child iblock pointer array */ + indir_idx = iblock->par_entry - + (iblock->hdr->man_dtable.max_direct_rows + * iblock->hdr->man_dtable.cparam.width); + + /* The pointer to iblock in the parent may not be set yet -- */ + /* verify that it is either NULL, or that it has been set to */ + /* iblock. */ + HDassert((NULL == par_iblock->child_iblocks[indir_idx]) || + (par_iblock->child_iblocks[indir_idx] == iblock)); + } + + switch ( action ) + { + case H5C_NOTIFY_ACTION_AFTER_INSERT: + if ( iblock->parent ) /* this is a child iblock */ + { + /* create flush dependency with parent iblock */ + if(H5AC_create_flush_dependency(iblock->parent, iblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, \ + "unable to create flush dependency") + } + else /* this is the root iblock */ + { + /* create flush dependency with header */ + if(H5AC_create_flush_dependency(iblock->hdr, iblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, \ + "unable to create flush dependency") + } + break; + + case H5C_NOTIFY_ACTION_BEFORE_EVICT: + if ( iblock->fd_parent ) /* this is a child iblock */ + { + /* destroy flush dependency with parent iblock */ + if(H5AC_destroy_flush_dependency(iblock->fd_parent, + iblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, \ + "unable to destroy flush dependency") + } + else /* this is the root iblock */ + { + /* destroy flush dependency with header */ + if(H5AC_destroy_flush_dependency(iblock->hdr, iblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, \ + "unable to destroy flush dependency") + + } + break; + + default: + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \ + "unknown action from metadata cache") + break; + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5HF_cache_iblock_notify() */ + + +/*------------------------------------------------------------------------- * Function: H5HF_cache_iblock_size * * Purpose: Compute the size in bytes of a fractal heap indirect block @@ -1328,6 +1539,7 @@ H5HF_cache_dblock_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, void *_udata) /* Address of parent block */ dblock->parent = par_info->iblock; + dblock->fd_parent = par_info->iblock; dblock->par_entry = par_info->entry; if(dblock->parent) { /* Share parent block */ @@ -1736,6 +1948,89 @@ done: /*------------------------------------------------------------------------- + * Function: H5HF_cache_dblock_notify + * + * Purpose: Setup / takedown flush dependencies as direct blocks + * are loaded / inserted and evicted from the metadata cache. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/17/14 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5HF_cache_dblock_notify(H5C_notify_action_t action, H5HF_direct_t *dblock) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT + + /* + * Check arguments. + */ + HDassert(dblock); + HDassert(dblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(dblock->hdr); + HDassert((dblock->fd_parent) || + ((dblock->hdr->man_dtable.curr_root_rows == 0) && + (dblock->block_off == (hsize_t)0))); + + switch ( action ) + { + case H5C_NOTIFY_ACTION_AFTER_INSERT: + HDassert(dblock->parent == dblock->fd_parent); + + if ( dblock->parent ) /* this is a leaf dblock */ + { + /* create flush dependency with parent iblock */ + if(H5AC_create_flush_dependency(dblock->parent, dblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, \ + "unable to create flush dependency") + } + else /* this is a root dblock */ + { + /* create flush dependency with header */ + if(H5AC_create_flush_dependency(dblock->hdr, dblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, \ + "unable to create flush dependency") + } + break; + + case H5C_NOTIFY_ACTION_BEFORE_EVICT: + HDassert((dblock->parent == dblock->fd_parent) || + ((NULL == dblock->parent) && (dblock->fd_parent))); + if ( dblock->fd_parent ) /* this is a leaf dblock */ + { + /* destroy flush dependency with parent iblock */ + if(H5AC_destroy_flush_dependency(dblock->fd_parent, + dblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, \ + "unable to destroy flush dependency") + } + else /* this is a root dblock */ + { + /* destroy flush dependency with header */ + if(H5AC_destroy_flush_dependency(dblock->hdr, dblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, \ + "unable to destroy flush dependency") + + } + break; + + default: + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, \ + "unknown action from metadata cache") + break; + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5HF_cache_dblock_notify() */ + + +/*------------------------------------------------------------------------- * Function: H5HF_cache_dblock_size * * Purpose: Compute the size in bytes of a fractal heap direct block @@ -1765,3 +2060,818 @@ H5HF_cache_dblock_size(const H5F_t UNUSED *f, const H5HF_direct_t *dblock, size_ FUNC_LEAVE_NOAPI(SUCCEED) } /* H5HF_cache_dblock_size() */ + + +/*------------------------------------------------------------------------ + * Function: H5HF_cache_verify_hdr_descendants_clean + * + * Purpose: Sanity checking routine that verifies that all indirect + * and direct blocks that are descendants of the supplied + * instance of H5HF_hdr_t are clean. Set *clean_ptr to + * TRUE if this is the case, and to FALSE otherwise. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/25/14 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +static herr_t +H5HF_cache_verify_hdr_descendants_clean(H5F_t *f, + hid_t dxpl_id, + H5HF_hdr_t * hdr, + hbool_t *clean_ptr) +{ + hbool_t in_cache; + hbool_t type_ok; + hbool_t root_iblock_in_cache = FALSE; + hbool_t unprotect_root_iblock = FALSE; + unsigned hdr_status = 0; + unsigned root_iblock_status = 0; + unsigned root_dblock_status = 0; + H5HF_indirect_t * root_iblock = NULL; + haddr_t hdr_addr; + haddr_t root_iblock_addr = HADDR_UNDEF; + haddr_t root_dblock_addr; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + HDassert(f); + HDassert(hdr); + HDassert(hdr->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert((const H5AC_class_t *)(hdr->cache_info.type) == \ + &(H5AC_FHEAP_HDR[0])); + HDassert(clean_ptr); + + hdr_addr = hdr->cache_info.addr; + + HDassert(hdr_addr == hdr->heap_addr); + + if ( H5AC_get_entry_status(f, hdr_addr, &hdr_status) < 0 ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get hdr status") + + HDassert(hdr_status & H5AC_ES__IN_CACHE); + + /* We have three basic scenarios we have to deal with: + * + * The first, and most common case, is that there is a root iblock. + * In this case we need to verify that the root iblock and all its + * children are clean. + * + * The second, and much less common case, is that in which the + * the fractal heap contains only one direct block, which is + * pointed to by hdr->man_dtable.table_addr. In this case, all we + * need to do is verify that the root direct block is clean. + * + * Finally, it is possible that the fractal heap is empty, and + * has neither a root indirect block nor a root direct block. + * In this case, we have nothing to do. + */ + + /* There are two ways in which we can arrive at the first scenario. + * + * By far the most common is when hdr->root_iblock contains a pointer + * to the root iblock -- in this case the root iblock is almost certainly + * pinned, although we can't count on that. + * + * However, it is also possible that there is a root iblock that + * is no longer pointed to by the header. In this case, the on + * disk address of the iblock will be in hdr->man_dtable.table_addr + * and hdr->man_dtable.curr_root_rows will contain a positive value. + * + * Since the former case is far and away the most common, we don't + * worry too much about efficiency in the second case. + */ + + if ( ( hdr->root_iblock ) || + ( ( hdr->man_dtable.curr_root_rows > 0 ) && + ( HADDR_UNDEF != hdr->man_dtable.table_addr ) ) ) { + + root_iblock = hdr->root_iblock; + + /* make note of the on disk address of the root iblock */ + + if ( root_iblock == NULL ) { + + /* hdr->man_dtable.table_addr must contain address of root + * iblock. Check to see if it is in cache. If it is, + * protect it and put its address in root_iblock. + */ + root_iblock_addr = hdr->man_dtable.table_addr; + + } else { + + root_iblock_addr = root_iblock->addr; + } + + /* get the status of the root iblock */ + HDassert(root_iblock_addr != HADDR_UNDEF); + + if ( H5AC_get_entry_status(f, root_iblock_addr, + &root_iblock_status) < 0 ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, \ + "can't get root iblock status") + + 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_ptr = TRUE; + + } else if ( root_iblock_status & H5AC_ES__IS_DIRTY ) { + + *clean_ptr = FALSE; + + } else { /* must examine children */ + + /* 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. + */ + + root_iblock = (H5HF_indirect_t *)H5AC_protect(f, dxpl_id, + H5AC_FHEAP_IBLOCK, + root_iblock_addr, + NULL, H5AC_READ); + + if ( NULL == root_iblock ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, \ + "H5AC_protect() faild.") + + unprotect_root_iblock = TRUE; + + } 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); + } + } 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. + */ + + iblock = (H5HF_indirect_t *)H5AC_protect(f, dxpl_id, + H5AC_FHEAP_IBLOCK, + root_iblock_addr, + NULL, H5AC_READ); + + if ( NULL == iblock ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, \ + "H5AC_protect() faild.") + + unprotect_root_iblock = TRUE; + + HDassert(iblock == root_iblock); + + } + } + + /* 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(hdr->root_iblock->cache_info.magic == \ + H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert((const H5AC_class_t *)(hdr->root_iblock->cache_info.type) \ + == &(H5AC_FHEAP_IBLOCK[0])); + + if ( H5HF_cache_verify_iblock_descendants_clean(f, dxpl_id, + root_iblock, &root_iblock_status, + clean_ptr) < 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.") + } + } + } else if ( ( hdr->man_dtable.curr_root_rows == 0 ) && + ( HADDR_UNDEF != hdr->man_dtable.table_addr ) ) { + + /* this is scenario 2 -- we have a root dblock */ + + root_dblock_addr = hdr->man_dtable.table_addr; + + if ( H5AC_get_entry_status(f, root_dblock_addr, + &root_dblock_status) < 0 ) + + 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, 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 some flush dependency + * relationship. + */ + 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 ( 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.") + + + *clean_ptr = ! (root_dblock_status & H5AC_ES__IS_DIRTY); + + } else { /* root dblock not in cache */ + + *clean_ptr = TRUE; + } + } else { + /* this is scenario 3 -- the fractal heap is empty, and we + * have nothing to do. + */ + *clean_ptr = TRUE; + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5HF_cache_verify_hdr_descendants_clean() */ + +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------ + * Function: H5HF_cache_verify_iblock_descendants_clean + * + * Purpose: Sanity checking routine that verifies that all indirect + * and direct blocks that are decendents of the supplied + * instance of H5HF_indirect_t are clean. Set *clean_ptr + * to TRUE if this is the case, and to FALSE otherwise. + * + * In passing, the function also does a cursory check to + * spot any obvious errors in the flush dependency setup. + * If any problems are found, the function returns failure. + * Note that these checks are not exhaustive, thus passing + * them does not mean that the flush dependencies are + * correct -- only that there is nothing obviously wrong + * with them. + * + * WARNING: At its top level call, this function is + * intended to be called from H5HF_cache_iblock_flush(), + * and thus presumes that the supplied indirect block + * is in cache. Any other use of this function and + * its descendants must insure that this assumption is + * met. + * + * Note that this function and + * H5HF_cache_verify_descendant_iblocks_clean() are + * recursive co-routines. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/25/14 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +static herr_t +H5HF_cache_verify_iblock_descendants_clean(H5F_t *f, + hid_t dxpl_id, + H5HF_indirect_t * iblock, + unsigned * iblock_status_ptr, + hbool_t *clean_ptr) +{ + hbool_t has_dblocks = FALSE; + hbool_t has_iblocks = FALSE; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + HDassert(f); + HDassert(iblock); + HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert((const H5AC_class_t *)(iblock->cache_info.type) == \ + &(H5AC_FHEAP_IBLOCK[0])); + HDassert(iblock_status_ptr); + HDassert(clean_ptr); + HDassert(*clean_ptr); + + if ( ( *clean_ptr ) && + ( H5HF_cache_verify_iblocks_dblocks_clean(f, iblock, clean_ptr, + &has_dblocks) < 0 ) ) + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify dblocks clean.") + + if ( ( *clean_ptr ) && + ( H5HF_cache_verify_descendant_iblocks_clean(f, dxpl_id, iblock, + clean_ptr, &has_iblocks) < 0 ) ) + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, "can't verify iblocks clean.") + + if ( ( NULL == iblock_status_ptr ) && + ( H5AC_get_entry_status(f, iblock->addr, iblock_status_ptr) < 0 ) ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, "can't get iblock status") + + /* verify that flush dependency setup is plausible */ + + if ( 0 == (*iblock_status_ptr & H5AC_ES__IS_FLUSH_DEP_CHILD) ) + + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, \ + "iblock is not a flush dep child.") + + if ( ( ( has_dblocks || has_iblocks ) ) && + ( 0 == (*iblock_status_ptr & H5AC_ES__IS_FLUSH_DEP_PARENT) ) ) + + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, \ + "iblock has children and is not a flush dep parent.") + + if ( ( ( has_dblocks || has_iblocks ) ) && + ( 0 == (*iblock_status_ptr & H5AC_ES__IS_PINNED) ) ) + + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, \ + "iblock has children and is not pinned.") + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5HF_cache_verify_iblock_descendants_clean() */ + +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------ + * Function: H5HF_cache_verify_iblocks_dblocks_clean + * + * Purpose: Sanity checking routine that attempts to verify that all + * direct blocks pointed to by the supplied indirect block + * are either clean, or not in the cache. + * + * In passing, the function also does a cursory check to + * spot any obvious errors in the flush dependency setup. + * If any problems are found, the function returns failure. + * Note that these checks are not exhaustive, thus passing + * them does not mean that the flush dependencies are + * correct -- only that there is nothing obviously wrong + * with them. + * + * WARNING: This function presumes that the supplied + * iblock is in the cache, and will not be removed + * during the call. Caller must ensure that this is + * the case before the call. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/25/14 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +static herr_t +H5HF_cache_verify_iblocks_dblocks_clean(H5F_t *f, + H5HF_indirect_t * iblock, + hbool_t *clean_ptr, + hbool_t *has_dblocks_ptr) +{ + hbool_t in_cache; + hbool_t type_ok; + unsigned i; + unsigned num_direct_rows; + unsigned max_dblock_index; + haddr_t dblock_addr; + unsigned dblock_status = 0; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + HDassert(f); + HDassert(iblock); + HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(iblock->cache_info.type == H5AC_FHEAP_IBLOCK); + HDassert(clean_ptr); + HDassert(*clean_ptr); + HDassert(has_dblocks_ptr); + + 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_ptr ) && ( i <= max_dblock_index ) ) { + + dblock_addr = iblock->ents[i].addr; + + if ( H5F_addr_defined(dblock_addr) ) { + + 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 */ + + 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 ); + + *has_dblocks_ptr = TRUE; + + if ( dblock_status & H5AC_ES__IS_DIRTY ) { + + *clean_ptr = FALSE; + } + + /* 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. + * + * We don't test this fully, but we will verify that + * the child iblock is a child in some flush dependency + * relationship. + */ + 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.") + + } + } + + i++; + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5HF_cache_verify_iblocks_dblocks_clean() */ + +#endif /* NDEBUG */ + + +/*------------------------------------------------------------------------ + * Function: H5HF_cache_verify_descendant_iblocks_clean + * + * Purpose: Sanity checking routine that attempts to verify that all + * direct blocks pointed to by the supplied indirect block + * are either clean, or not in the cache. + * + * In passing, the function also does a cursory check to + * spot any obvious errors in the flush dependency setup. + * If any problems are found, the function returns failure. + * Note that these checks are not exhaustive, thus passing + * them does not mean that the flush dependencies are + * correct -- only that there is nothing obviously wrong + * with them. + * + * WARNING: This function presumes that the supplied + * iblock is in the cache, and will not be removed + * during the call. Caller must ensure that this is + * the case before the call. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: John Mainzer + * 5/25/14 + * + *------------------------------------------------------------------------- + */ +#ifndef NDEBUG +static herr_t +H5HF_cache_verify_descendant_iblocks_clean(H5F_t *f, + hid_t dxpl_id, + H5HF_indirect_t * iblock, + hbool_t *clean_ptr, + hbool_t *has_iblocks_ptr) +{ + hbool_t unprotect_child_iblock; + unsigned i; + unsigned first_iblock_index; + unsigned last_iblock_index; + unsigned num_direct_rows; + unsigned child_iblock_status = 0; + haddr_t child_iblock_addr; + H5HF_indirect_t * child_iblock_ptr; + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(FAIL) + + HDassert(f); + HDassert(iblock); + HDassert(iblock->cache_info.magic == H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(iblock->cache_info.type == &(H5AC_FHEAP_IBLOCK[0])); + HDassert(clean_ptr); + HDassert(*clean_ptr); + HDassert(has_iblocks_ptr); + + num_direct_rows = + MIN(iblock->nrows, iblock->hdr->man_dtable.max_direct_rows); + + HDassert(num_direct_rows <= iblock->nrows ); + + 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_ptr ) && ( i <= last_iblock_index ) ) { + + child_iblock_addr = iblock->ents[i].addr; + + if ( H5F_addr_defined(child_iblock_addr) ) { + + if ( H5AC_get_entry_status(f, child_iblock_addr, + &child_iblock_status) < 0 ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, \ + "can't get iblock status") + + if ( child_iblock_status & H5AC_ES__IN_CACHE ) { + + *has_iblocks_ptr = TRUE; + + if ( child_iblock_status & H5AC_ES__IS_DIRTY ) { + + *clean_ptr = FALSE; + } + + /* if the child iblock is in cache and *clean_ptr is TRUE, + * we must continue to explore down the fractal heap tree + * structure to verify that all descendant blocks 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 ( *clean_ptr ) { + + child_iblock_ptr = NULL; + 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 */ + child_iblock_ptr = (H5HF_indirect_t *) + H5AC_protect(f, dxpl_id, H5AC_FHEAP_IBLOCK, + child_iblock_addr, + NULL, H5AC_READ); + + if ( NULL == child_iblock_ptr ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTPROTECT, FAIL, \ + "H5AC_protect() faild.") + + unprotect_child_iblock = TRUE; + + } 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_ptr)) < 0 ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTGET, FAIL, \ + "H5AC_get_entry_ptr_from_addr() faild.") + + HDassert ( child_iblock_ptr ); + } + } else { + /* child iblock is pinned -- look it up in the */ + /* parent iblocks child_iblocks array. */ + + HDassert(iblock->child_iblocks); + + child_iblock_ptr = + iblock->child_iblocks[i - first_iblock_index]; + } + + /* 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_ptr); + HDassert(child_iblock_ptr->cache_info.magic == + H5C__H5C_CACHE_ENTRY_T_MAGIC); + HDassert(child_iblock_ptr->cache_info.type == + H5AC_FHEAP_IBLOCK); + HDassert(child_iblock_ptr->addr == child_iblock_addr); + + /* now make the recursive call */ + if ( H5HF_cache_verify_iblock_descendants_clean(f, dxpl_id, + child_iblock_ptr, &child_iblock_status, + clean_ptr) < 0 ) + + HGOTO_ERROR(H5E_HEAP, H5E_SYSTEM, FAIL, \ + "can't verify child iblock clean.") + + /* 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_ptr, + H5AC__NO_FLAGS_SET) < 0 ) + + HGOTO_ERROR(H5E_HEAP, H5E_CANTUNPROTECT, FAIL, \ + "H5AC_unprotect() faild.") + + } + } + } + } + + i++; + } + +done: + + FUNC_LEAVE_NOAPI(ret_value) + +} /* H5HF_cache_verify_descendant_iblocks_clean() */ + +#endif /* NDEBUG */ + diff --git a/src/H5HFdblock.c b/src/H5HFdblock.c index dd36613..c4ae573 100644 --- a/src/H5HFdblock.c +++ b/src/H5HFdblock.c @@ -161,6 +161,7 @@ HDmemset(dblock->blk, 0, dblock->size); /* Attach to parent indirect block, if there is one */ dblock->parent = par_iblock; + dblock->fd_parent = par_iblock; if(dblock->parent) if(H5HF_man_iblock_attach(dblock->parent, par_entry, dblock_addr) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTATTACH, FAIL, "can't attach direct block to parent indirect block") diff --git a/src/H5HFiblock.c b/src/H5HFiblock.c index c989bfc..4473803 100644 --- a/src/H5HFiblock.c +++ b/src/H5HFiblock.c @@ -438,7 +438,20 @@ H5HF_man_iblock_root_create(H5HF_hdr_t *hdr, hid_t dxpl_id, size_t min_dblock_si /* Attach direct block to new root indirect block */ dblock->parent = iblock; + dblock->fd_parent = iblock; dblock->par_entry = 0; + + /* destroy flush dependency between direct block and header */ + if(H5AC_destroy_flush_dependency(dblock->hdr, dblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, \ + "unable to destroy flush dependency") + + /* create flush dependency between direct block and new root indirect block */ + if(H5AC_create_flush_dependency(dblock->parent, dblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, \ + "unable to create flush dependency") + + if(H5HF_man_iblock_attach(iblock, 0, hdr->man_dtable.table_addr) < 0) HGOTO_ERROR(H5E_HEAP, H5E_CANTATTACH, FAIL, "can't attach root direct block to parent indirect block") @@ -884,6 +897,19 @@ H5HF_man_iblock_root_revert(H5HF_indirect_t *root_iblock, hid_t dxpl_id) dblock->parent = NULL; dblock->par_entry = 0; + /* destroy flush dependency between old root iblock and new root direct block*/ + if(H5AC_destroy_flush_dependency(dblock->fd_parent, dblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTUNDEPEND, FAIL, \ + "unable to destroy flush dependency") + + + /* create flush dependency between header and new root direct block */ + if(H5AC_create_flush_dependency(dblock->hdr, dblock) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTDEPEND, FAIL, \ + "unable to create flush dependency") + + dblock->fd_parent = NULL; + /* Point root at direct block */ hdr->man_dtable.curr_root_rows = 0; hdr->man_dtable.table_addr = dblock_addr; @@ -1078,6 +1104,12 @@ H5HF_man_iblock_create(H5HF_hdr_t *hdr, hid_t dxpl_id, H5HF_indirect_t *par_iblo /* Attach to parent indirect block, if there is one */ iblock->parent = par_iblock; + iblock->fd_parent = par_iblock; /* this copy of the parent pointer is */ + /* needed by the notify callback so */ + /* that it can take down flush */ + /* dependencies on eviction even if */ + /* the parent pointer has been nulled */ + /* out. JRM -- 5/18/14 */ iblock->par_entry = par_entry; if(iblock->parent) { /* Attach new block to parent */ diff --git a/src/H5HFpkg.h b/src/H5HFpkg.h index a17088b..63c1a3e 100644 --- a/src/H5HFpkg.h +++ b/src/H5HFpkg.h @@ -384,6 +384,13 @@ struct H5HF_indirect_t { size_t rc; /* Reference count of objects using this block */ H5HF_hdr_t *hdr; /* Shared heap header info */ struct H5HF_indirect_t *parent; /* Shared parent indirect block info */ + struct H5HF_indirect_t + *fd_parent; /* Saved copy of the parent pointer -- this */ + /* necessary as the parent field is sometimes */ + /* nulled out before the eviction notify call */ + /* is made from the metadata cache. Since */ + /* this call cancels flush dependencies, it */ + /* needs this information. */ unsigned par_entry; /* Entry in parent's table */ haddr_t addr; /* Address of this indirect block on disk */ size_t size; /* Size of indirect block on disk */ @@ -407,6 +414,12 @@ typedef struct H5HF_direct_t { /* Internal heap information */ H5HF_hdr_t *hdr; /* Shared heap header info */ H5HF_indirect_t *parent; /* Shared parent indirect block info */ + H5HF_indirect_t *fd_parent; /* Saved copy of the parent pointer -- this */ + /* necessary as the parent field is sometimes */ + /* nulled out before the eviction notify call */ + /* is made from the metadata cache. Since */ + /* this call cancels flush dependencies, it */ + /* needs this information. */ unsigned par_entry; /* Entry in parent's table */ size_t size; /* Size of direct block */ hsize_t file_size; /* Size of direct block in file (only valid when block's space is being freed) */ |