diff options
-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) */ |