Files
hdf5/src/H5C.c
2020-01-13 13:20:19 -06:00

8791 lines
329 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright by The HDF Group. *
* Copyright by the Board of Trustees of the University of Illinois. *
* All rights reserved. *
* *
* This file is part of HDF5. The full HDF5 copyright notice, including *
* terms governing use, modification, and redistribution, is contained in *
* the COPYING file, which can be found at the root of the source code *
* distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*-------------------------------------------------------------------------
*
* Created: H5C.c
* June 1 2004
* John Mainzer
*
* Purpose: Functions in this file implement a generic cache for
* things which exist on disk, and which may be
* unambiguously referenced by their disk addresses.
*
* The code in this module was initially written in
* support of a complete re-write of the metadata cache
* in H5AC.c However, other uses for the cache code
* suggested themselves, and thus this file was created
* in an attempt to support re-use.
*
* For a detailed overview of the cache, please see the
* header comment for H5C_t in H5Cpkg.h.
*
*-------------------------------------------------------------------------
*/
/**************************************************************************
*
* To Do:
*
* Code Changes:
*
* - Remove extra functionality in H5C__flush_single_entry()?
*
* - Change protect/unprotect to lock/unlock.
*
* - Flush entries in increasing address order in
* H5C__make_space_in_cache().
*
* - Also in H5C__make_space_in_cache(), use high and low water marks
* to reduce the number of I/O calls.
*
* - When flushing, attempt to combine contiguous entries to reduce
* I/O overhead. Can't do this just yet as some entries are not
* contiguous. Do this in parallel only or in serial as well?
*
* - Create MPI type for dirty objects when flushing in parallel.
*
* - Now that TBBT routines aren't used, fix nodes in memory to
* point directly to the skip list node from the LRU list, eliminating
* skip list lookups when evicting objects from the cache.
*
* Tests:
*
* - Trim execution time. (This is no longer a major issue with the
* shift from the TBBT to a hash table for indexing.)
*
* - Add random tests.
*
**************************************************************************/
/****************/
/* Module Setup */
/****************/
#include "H5Cmodule.h" /* This source code file is part of the H5C module */
#define H5F_FRIEND /* suppress error about including H5Fpkg */
/***********/
/* Headers */
/***********/
#include "H5private.h" /* Generic Functions */
#include "H5Cpkg.h" /* Cache */
#include "H5CXprivate.h" /* API Contexts */
#include "H5Eprivate.h" /* Error handling */
#include "H5Fpkg.h" /* Files */
#include "H5FLprivate.h" /* Free Lists */
#include "H5Iprivate.h" /* IDs */
#include "H5MFprivate.h" /* File memory management */
#include "H5MMprivate.h" /* Memory management */
#include "H5Pprivate.h" /* Property lists */
/****************/
/* Local Macros */
/****************/
#if H5C_DO_MEMORY_SANITY_CHECKS
#define H5C_IMAGE_EXTRA_SPACE 8
#define H5C_IMAGE_SANITY_VALUE "DeadBeef"
#else /* H5C_DO_MEMORY_SANITY_CHECKS */
#define H5C_IMAGE_EXTRA_SPACE 0
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
/******************/
/* Local Typedefs */
/******************/
/* Alias for pointer to cache entry, for use when allocating sequences of them */
typedef H5C_cache_entry_t *H5C_cache_entry_ptr_t;
/********************/
/* Local Prototypes */
/********************/
static herr_t H5C__pin_entry_from_client(H5C_t *cache_ptr,
H5C_cache_entry_t *entry_ptr);
static herr_t H5C__unpin_entry_real(H5C_t *cache_ptr,
H5C_cache_entry_t *entry_ptr, hbool_t update_rp);
static herr_t H5C__unpin_entry_from_client(H5C_t *cache_ptr,
H5C_cache_entry_t *entry_ptr, hbool_t update_rp);
static herr_t H5C__auto_adjust_cache_size(H5F_t *f, hbool_t write_permitted);
static herr_t H5C__autoadjust__ageout(H5F_t * f,
double hit_rate,
enum H5C_resize_status * status_ptr,
size_t * new_max_cache_size_ptr,
hbool_t write_permitted);
static herr_t H5C__autoadjust__ageout__cycle_epoch_marker(H5C_t * cache_ptr);
static herr_t H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t * f,
hbool_t write_permitted);
static herr_t H5C__autoadjust__ageout__insert_new_marker(H5C_t * cache_ptr);
static herr_t H5C__autoadjust__ageout__remove_all_markers(H5C_t * cache_ptr);
static herr_t H5C__autoadjust__ageout__remove_excess_markers(H5C_t * cache_ptr);
static herr_t H5C__flash_increase_cache_size(H5C_t * cache_ptr,
size_t old_entry_size, size_t new_entry_size);
static herr_t H5C__flush_invalidate_cache(H5F_t *f, unsigned flags);
static herr_t H5C_flush_invalidate_ring(H5F_t *f, H5C_ring_t ring, unsigned flags);
static herr_t H5C__flush_ring(H5F_t *f, H5C_ring_t ring, unsigned flags);
static void * H5C_load_entry(H5F_t * f,
#ifdef H5_HAVE_PARALLEL
hbool_t coll_access,
#endif /* H5_HAVE_PARALLEL */
const H5C_class_t * type,
haddr_t addr,
void * udata);
static herr_t H5C__mark_flush_dep_dirty(H5C_cache_entry_t * entry);
static herr_t H5C__mark_flush_dep_clean(H5C_cache_entry_t * entry);
static herr_t H5C__serialize_ring(H5F_t *f, H5C_ring_t ring);
static herr_t H5C__serialize_single_entry(H5F_t *f, H5C_t *cache_ptr,
H5C_cache_entry_t *entry_ptr);
static herr_t H5C__verify_len_eoa(H5F_t *f, const H5C_class_t * type,
haddr_t addr, size_t *len, hbool_t actual);
#if H5C_DO_SLIST_SANITY_CHECKS
static hbool_t H5C_entry_in_skip_list(H5C_t * cache_ptr,
H5C_cache_entry_t *target_ptr);
#endif /* H5C_DO_SLIST_SANITY_CHECKS */
#if H5C_DO_EXTREME_SANITY_CHECKS
static herr_t H5C_validate_lru_list(H5C_t * cache_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 */
#ifndef NDEBUG
static void H5C__assert_flush_dep_nocycle(const H5C_cache_entry_t * entry,
const H5C_cache_entry_t * base_entry);
#endif /* NDEBUG */
/*********************/
/* Package Variables */
/*********************/
/* Package initialization variable */
hbool_t H5_PKG_INIT_VAR = FALSE;
/* Declare a free list to manage the tag info struct */
H5FL_DEFINE(H5C_tag_info_t);
/*****************************/
/* Library Private Variables */
/*****************************/
/*******************/
/* Local Variables */
/*******************/
/* Declare a free list to manage the H5C_t struct */
H5FL_DEFINE_STATIC(H5C_t);
/* Declare a free list to manage arrays of cache entries */
H5FL_SEQ_DEFINE_STATIC(H5C_cache_entry_ptr_t);
/*-------------------------------------------------------------------------
* Function: H5C_create
*
* Purpose: Allocate, initialize, and return the address of a new
* instance of H5C_t.
*
* In general, the max_cache_size parameter must be positive,
* and the min_clean_size parameter must lie in the closed
* interval [0, max_cache_size].
*
* The check_write_permitted parameter must either be NULL,
* or point to a function of type H5C_write_permitted_func_t.
* If it is NULL, the cache will use the write_permitted
* flag to determine whether writes are permitted.
*
* Return: Success: Pointer to the new instance.
*
* Failure: NULL
*
* Programmer: John Mainzer
* 6/2/04
*
*-------------------------------------------------------------------------
*/
H5C_t *
H5C_create(size_t max_cache_size,
size_t min_clean_size,
int max_type_id,
const H5C_class_t * const * class_table_ptr,
H5C_write_permitted_func_t check_write_permitted,
hbool_t write_permitted,
H5C_log_flush_func_t log_flush,
void * aux_ptr)
{
int i;
H5C_t * cache_ptr = NULL;
H5C_t * ret_value = NULL; /* Return value */
FUNC_ENTER_NOAPI(NULL)
HDassert( max_cache_size >= H5C__MIN_MAX_CACHE_SIZE );
HDassert( max_cache_size <= H5C__MAX_MAX_CACHE_SIZE );
HDassert( min_clean_size <= max_cache_size );
HDassert( max_type_id >= 0 );
HDassert( max_type_id < H5C__MAX_NUM_TYPE_IDS );
HDassert( class_table_ptr );
for ( i = 0; i <= max_type_id; i++ ) {
HDassert( (class_table_ptr)[i] );
HDassert(HDstrlen((class_table_ptr)[i]->name) > 0);
} /* end for */
if(NULL == (cache_ptr = H5FL_CALLOC(H5C_t)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed")
if(NULL == (cache_ptr->slist_ptr = H5SL_create(H5SL_TYPE_HADDR, NULL)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, NULL, "can't create skip list")
if(NULL == (cache_ptr->tag_list = H5SL_create(H5SL_TYPE_HADDR, NULL)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, NULL, "can't create skip list for tagged entry addresses")
/* If we get this far, we should succeed. Go ahead and initialize all
* the fields.
*/
cache_ptr->magic = H5C__H5C_T_MAGIC;
cache_ptr->flush_in_progress = FALSE;
if(NULL == (cache_ptr->log_info = (H5C_log_info_t *)H5MM_calloc(sizeof(H5C_log_info_t))))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "memory allocation failed")
cache_ptr->aux_ptr = aux_ptr;
cache_ptr->max_type_id = max_type_id;
cache_ptr->class_table_ptr = class_table_ptr;
cache_ptr->max_cache_size = max_cache_size;
cache_ptr->min_clean_size = min_clean_size;
cache_ptr->check_write_permitted = check_write_permitted;
cache_ptr->write_permitted = write_permitted;
cache_ptr->log_flush = log_flush;
cache_ptr->evictions_enabled = TRUE;
cache_ptr->close_warning_received = FALSE;
cache_ptr->index_len = 0;
cache_ptr->index_size = (size_t)0;
cache_ptr->clean_index_size = (size_t)0;
cache_ptr->dirty_index_size = (size_t)0;
for(i = 0; i < H5C_RING_NTYPES; i++) {
cache_ptr->index_ring_len[i] = 0;
cache_ptr->index_ring_size[i] = (size_t)0;
cache_ptr->clean_index_ring_size[i] = (size_t)0;
cache_ptr->dirty_index_ring_size[i] = (size_t)0;
cache_ptr->slist_ring_len[i] = 0;
cache_ptr->slist_ring_size[i] = (size_t)0;
} /* end for */
for(i = 0; i < H5C__HASH_TABLE_LEN; i++)
(cache_ptr->index)[i] = NULL;
cache_ptr->il_len = 0;
cache_ptr->il_size = (size_t)0;
cache_ptr->il_head = NULL;
cache_ptr->il_tail = NULL;
/* Tagging Field Initializations */
cache_ptr->ignore_tags = FALSE;
cache_ptr->num_objs_corked = 0;
cache_ptr->slist_changed = FALSE;
cache_ptr->slist_len = 0;
cache_ptr->slist_size = (size_t)0;
#if H5C_DO_SANITY_CHECKS
cache_ptr->slist_len_increase = 0;
cache_ptr->slist_size_increase = 0;
#endif /* H5C_DO_SANITY_CHECKS */
cache_ptr->entries_removed_counter = 0;
cache_ptr->last_entry_removed_ptr = NULL;
cache_ptr->entry_watched_for_removal = NULL;
cache_ptr->pl_len = 0;
cache_ptr->pl_size = (size_t)0;
cache_ptr->pl_head_ptr = NULL;
cache_ptr->pl_tail_ptr = NULL;
cache_ptr->pel_len = 0;
cache_ptr->pel_size = (size_t)0;
cache_ptr->pel_head_ptr = NULL;
cache_ptr->pel_tail_ptr = NULL;
cache_ptr->LRU_list_len = 0;
cache_ptr->LRU_list_size = (size_t)0;
cache_ptr->LRU_head_ptr = NULL;
cache_ptr->LRU_tail_ptr = NULL;
#ifdef H5_HAVE_PARALLEL
cache_ptr->coll_list_len = 0;
cache_ptr->coll_list_size = (size_t)0;
cache_ptr->coll_head_ptr = NULL;
cache_ptr->coll_tail_ptr = NULL;
cache_ptr->coll_write_list = NULL;
#endif /* H5_HAVE_PARALLEL */
#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
cache_ptr->cLRU_list_len = 0;
cache_ptr->cLRU_list_size = (size_t)0;
cache_ptr->cLRU_head_ptr = NULL;
cache_ptr->cLRU_tail_ptr = NULL;
cache_ptr->dLRU_list_len = 0;
cache_ptr->dLRU_list_size = (size_t)0;
cache_ptr->dLRU_head_ptr = NULL;
cache_ptr->dLRU_tail_ptr = NULL;
#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
cache_ptr->size_increase_possible = FALSE;
cache_ptr->flash_size_increase_possible = FALSE;
cache_ptr->flash_size_increase_threshold = 0;
cache_ptr->size_decrease_possible = FALSE;
cache_ptr->resize_enabled = FALSE;
cache_ptr->cache_full = FALSE;
cache_ptr->size_decreased = FALSE;
cache_ptr->resize_in_progress = FALSE;
cache_ptr->msic_in_progress = FALSE;
(cache_ptr->resize_ctl).version = H5C__CURR_AUTO_SIZE_CTL_VER;
(cache_ptr->resize_ctl).rpt_fcn = NULL;
(cache_ptr->resize_ctl).set_initial_size = FALSE;
(cache_ptr->resize_ctl).initial_size = H5C__DEF_AR_INIT_SIZE;
(cache_ptr->resize_ctl).min_clean_fraction = H5C__DEF_AR_MIN_CLEAN_FRAC;
(cache_ptr->resize_ctl).max_size = H5C__DEF_AR_MAX_SIZE;
(cache_ptr->resize_ctl).min_size = H5C__DEF_AR_MIN_SIZE;
(cache_ptr->resize_ctl).epoch_length = H5C__DEF_AR_EPOCH_LENGTH;
(cache_ptr->resize_ctl).incr_mode = H5C_incr__off;
(cache_ptr->resize_ctl).lower_hr_threshold = H5C__DEF_AR_LOWER_THRESHHOLD;
(cache_ptr->resize_ctl).increment = H5C__DEF_AR_INCREMENT;
(cache_ptr->resize_ctl).apply_max_increment = TRUE;
(cache_ptr->resize_ctl).max_increment = H5C__DEF_AR_MAX_INCREMENT;
(cache_ptr->resize_ctl).flash_incr_mode = H5C_flash_incr__off;
(cache_ptr->resize_ctl).flash_multiple = 1.0f;
(cache_ptr->resize_ctl).flash_threshold = 0.25f;
(cache_ptr->resize_ctl).decr_mode = H5C_decr__off;
(cache_ptr->resize_ctl).upper_hr_threshold = H5C__DEF_AR_UPPER_THRESHHOLD;
(cache_ptr->resize_ctl).decrement = H5C__DEF_AR_DECREMENT;
(cache_ptr->resize_ctl).apply_max_decrement = TRUE;
(cache_ptr->resize_ctl).max_decrement = H5C__DEF_AR_MAX_DECREMENT;
(cache_ptr->resize_ctl).epochs_before_eviction = H5C__DEF_AR_EPCHS_B4_EVICT;
(cache_ptr->resize_ctl).apply_empty_reserve = TRUE;
(cache_ptr->resize_ctl).empty_reserve = H5C__DEF_AR_EMPTY_RESERVE;
cache_ptr->epoch_markers_active = 0;
/* no need to initialize the ring buffer itself */
cache_ptr->epoch_marker_ringbuf_first = 1;
cache_ptr->epoch_marker_ringbuf_last = 0;
cache_ptr->epoch_marker_ringbuf_size = 0;
/* Initialize all epoch marker entries' fields to zero/FALSE/NULL */
HDmemset(cache_ptr->epoch_markers, 0, sizeof(cache_ptr->epoch_markers));
/* Set non-zero/FALSE/NULL fields for epoch markers */
for ( i = 0; i < H5C__MAX_EPOCH_MARKERS; i++ )
{
((cache_ptr->epoch_markers)[i]).magic =
H5C__H5C_CACHE_ENTRY_T_MAGIC;
((cache_ptr->epoch_markers)[i]).addr = (haddr_t)i;
((cache_ptr->epoch_markers)[i]).type = H5AC_EPOCH_MARKER;
}
/* Initialize cache image generation on file close related fields.
* Initial value of image_ctl must match H5C__DEFAULT_CACHE_IMAGE_CTL
* in H5Cprivate.h.
*/
cache_ptr->image_ctl.version = H5C__CURR_CACHE_IMAGE_CTL_VER;
cache_ptr->image_ctl.generate_image = FALSE;
cache_ptr->image_ctl.save_resize_status = FALSE;
cache_ptr->image_ctl.entry_ageout = -1;
cache_ptr->image_ctl.flags = H5C_CI__ALL_FLAGS;
cache_ptr->serialization_in_progress= FALSE;
cache_ptr->load_image = FALSE;
cache_ptr->image_loaded = FALSE;
cache_ptr->delete_image = FALSE;
cache_ptr->image_addr = HADDR_UNDEF;
cache_ptr->image_len = 0;
cache_ptr->image_data_len = 0;
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
cache_ptr->entry_fd_height_change_counter = 0;
cache_ptr->num_entries_in_image = 0;
cache_ptr->image_entries = NULL;
cache_ptr->image_buffer = NULL;
/* initialize free space manager related fields: */
cache_ptr->rdfsm_settled = FALSE;
cache_ptr->mdfsm_settled = FALSE;
if(H5C_reset_cache_hit_rate_stats(cache_ptr) < 0)
/* this should be impossible... */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "H5C_reset_cache_hit_rate_stats failed")
H5C_stats__reset(cache_ptr);
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;
done:
if(NULL == ret_value) {
if(cache_ptr != NULL) {
if(cache_ptr->slist_ptr != NULL)
H5SL_close(cache_ptr->slist_ptr);
if(cache_ptr->tag_list != NULL)
H5SL_close(cache_ptr->tag_list);
if(cache_ptr->log_info != NULL)
H5MM_xfree(cache_ptr->log_info);
cache_ptr->magic = 0;
cache_ptr = H5FL_FREE(H5C_t, cache_ptr);
} /* end if */
} /* end if */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_create() */
/*-------------------------------------------------------------------------
* Function: H5C_def_auto_resize_rpt_fcn
*
* Purpose: Print results of a automatic cache resize.
*
* This function should only be used where HDprintf() behaves
* well -- i.e. not on Windows.
*
* Return: void
*
* Programmer: John Mainzer
* 10/27/04
*
*-------------------------------------------------------------------------
*/
void
H5C_def_auto_resize_rpt_fcn(H5C_t * cache_ptr,
#ifndef NDEBUG
int32_t version,
#else /* NDEBUG */
int32_t H5_ATTR_UNUSED version,
#endif /* NDEBUG */
double hit_rate,
enum H5C_resize_status status,
size_t old_max_cache_size,
size_t new_max_cache_size,
size_t old_min_clean_size,
size_t new_min_clean_size)
{
HDassert( cache_ptr != NULL );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( version == H5C__CURR_AUTO_RESIZE_RPT_FCN_VER );
switch ( status )
{
case in_spec:
HDfprintf(stdout,
"%sAuto cache resize -- no change. (hit rate = %lf)\n",
cache_ptr->prefix, hit_rate);
break;
case increase:
HDassert( hit_rate < (cache_ptr->resize_ctl).lower_hr_threshold );
HDassert( old_max_cache_size < new_max_cache_size );
HDfprintf(stdout,
"%sAuto cache resize -- hit rate (%lf) out of bounds low (%6.5lf).\n",
cache_ptr->prefix, hit_rate,
(cache_ptr->resize_ctl).lower_hr_threshold);
HDfprintf(stdout,
"%s cache size increased from (%Zu/%Zu) to (%Zu/%Zu).\n",
cache_ptr->prefix,
old_max_cache_size,
old_min_clean_size,
new_max_cache_size,
new_min_clean_size);
break;
case flash_increase:
HDassert( old_max_cache_size < new_max_cache_size );
HDfprintf(stdout,
"%sflash cache resize(%d) -- size threshold = %Zu.\n",
cache_ptr->prefix,
(int)((cache_ptr->resize_ctl).flash_incr_mode),
cache_ptr->flash_size_increase_threshold);
HDfprintf(stdout,
"%s cache size increased from (%Zu/%Zu) to (%Zu/%Zu).\n",
cache_ptr->prefix,
old_max_cache_size,
old_min_clean_size,
new_max_cache_size,
new_min_clean_size);
break;
case decrease:
HDassert( old_max_cache_size > new_max_cache_size );
switch ( (cache_ptr->resize_ctl).decr_mode )
{
case H5C_decr__off:
HDfprintf(stdout,
"%sAuto cache resize -- decrease off. HR = %lf\n",
cache_ptr->prefix, hit_rate);
break;
case H5C_decr__threshold:
HDassert( hit_rate >
(cache_ptr->resize_ctl).upper_hr_threshold );
HDfprintf(stdout,
"%sAuto cache resize -- decrease by threshold. HR = %lf > %6.5lf\n",
cache_ptr->prefix, hit_rate,
(cache_ptr->resize_ctl).upper_hr_threshold);
HDfprintf(stdout, "%sout of bounds high (%6.5lf).\n",
cache_ptr->prefix,
(cache_ptr->resize_ctl).upper_hr_threshold);
break;
case H5C_decr__age_out:
HDfprintf(stdout,
"%sAuto cache resize -- decrease by ageout. HR = %lf\n",
cache_ptr->prefix, hit_rate);
break;
case H5C_decr__age_out_with_threshold:
HDassert( hit_rate >
(cache_ptr->resize_ctl).upper_hr_threshold );
HDfprintf(stdout,
"%sAuto cache resize -- decrease by ageout with threshold. HR = %lf > %6.5lf\n",
cache_ptr->prefix, hit_rate,
(cache_ptr->resize_ctl).upper_hr_threshold);
break;
default:
HDfprintf(stdout,
"%sAuto cache resize -- decrease by unknown mode. HR = %lf\n",
cache_ptr->prefix, hit_rate);
}
HDfprintf(stdout,
"%s cache size decreased from (%Zu/%Zu) to (%Zu/%Zu).\n",
cache_ptr->prefix,
old_max_cache_size,
old_min_clean_size,
new_max_cache_size,
new_min_clean_size);
break;
case at_max_size:
HDfprintf(stdout,
"%sAuto cache resize -- hit rate (%lf) out of bounds low (%6.5lf).\n",
cache_ptr->prefix, hit_rate,
(cache_ptr->resize_ctl).lower_hr_threshold);
HDfprintf(stdout,
"%s cache already at maximum size so no change.\n",
cache_ptr->prefix);
break;
case at_min_size:
HDfprintf(stdout,
"%sAuto cache resize -- hit rate (%lf) -- can't decrease.\n",
cache_ptr->prefix, hit_rate);
HDfprintf(stdout, "%s cache already at minimum size.\n",
cache_ptr->prefix);
break;
case increase_disabled:
HDfprintf(stdout,
"%sAuto cache resize -- increase disabled -- HR = %lf.",
cache_ptr->prefix, hit_rate);
break;
case decrease_disabled:
HDfprintf(stdout,
"%sAuto cache resize -- decrease disabled -- HR = %lf.\n",
cache_ptr->prefix, hit_rate);
break;
case not_full:
HDassert( hit_rate < (cache_ptr->resize_ctl).lower_hr_threshold );
HDfprintf(stdout,
"%sAuto cache resize -- hit rate (%lf) out of bounds low (%6.5lf).\n",
cache_ptr->prefix, hit_rate,
(cache_ptr->resize_ctl).lower_hr_threshold);
HDfprintf(stdout,
"%s cache not full so no increase in size.\n",
cache_ptr->prefix);
break;
default:
HDfprintf(stdout, "%sAuto cache resize -- unknown status code.\n",
cache_ptr->prefix);
break;
}
return;
} /* H5C_def_auto_resize_rpt_fcn() */
/*-------------------------------------------------------------------------
* Function: H5C_free_tag_list_cb
*
* Purpose: Callback function to free tag nodes from the skip list.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Vailin Choi
* January 2014
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C_free_tag_list_cb(void *_item, void H5_ATTR_UNUSED *key, void H5_ATTR_UNUSED *op_data)
{
H5C_tag_info_t *tag_info = (H5C_tag_info_t *)_item;
FUNC_ENTER_NOAPI_NOINIT_NOERR
HDassert(tag_info);
/* Release the item */
tag_info = H5FL_FREE(H5C_tag_info_t, tag_info);
FUNC_LEAVE_NOAPI(0)
} /* H5C_free_tag_list_cb() */
/*-------------------------------------------------------------------------
*
* Function: H5C_prep_for_file_close
*
* Purpose: This function should be called just prior to the cache
* flushes at file close. There should be no protected
* entries in the cache at this point.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 7/3/15
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_prep_for_file_close(H5F_t *f)
{
H5C_t * cache_ptr;
hbool_t image_generated = FALSE; /* Whether a cache image was generated */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(f);
HDassert(f->shared);
HDassert(f->shared->cache);
cache_ptr = f->shared->cache;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
/* For now at least, it is possible to receive the
* close warning more than once -- the following
* if statement handles this.
*/
if(cache_ptr->close_warning_received)
HGOTO_DONE(SUCCEED)
cache_ptr->close_warning_received = TRUE;
/* Make certain there aren't any protected entries */
HDassert(cache_ptr->pl_len == 0);
/* Prepare cache image */
if(H5C__prep_image_for_file_close(f, &image_generated) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "can't create cache image")
#ifdef H5_HAVE_PARALLEL
if ( ( H5F_INTENT(f) & H5F_ACC_RDWR ) &&
( ! image_generated ) &&
( cache_ptr->aux_ptr != NULL ) &&
( f->shared->fs_persist ) ) {
/* If persistent free space managers are enabled, flushing the
* metadata cache may result in the deletion, insertion, and/or
* dirtying of entries.
*
* This is a problem in PHDF5, as it breaks two invariants of
* our management of the metadata cache across all processes:
*
* 1) Entries will not be dirtied, deleted, inserted, or moved
* during flush in the parallel case.
*
* 2) All processes contain the same set of dirty metadata
* entries on entry to a sync point.
*
* To solve this problem for the persistent free space managers,
* serialize the metadata cache on all processes prior to the
* first sync point on file shutdown. The shutdown warning is
* a convenient location for this call.
*
* This is sufficient since:
*
* 1) FSM settle routines are only invoked on file close. Since
* serialization make the same settle calls as flush on file
* close, and since the close warning is issued after all
* non FSM related space allocations and just before the
* first sync point on close, this call will leave the caches
* in a consistent state across the processes if they were
* consistent before.
*
* 2) Since the FSM settle routines are only invoked once during
* file close, invoking them now will prevent their invocation
* during a flush, and thus avoid any resulting entrie dirties,
* deletions, insertion, or moves during the flush.
*/
if(H5C__serialize_cache(f) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "serialization of the cache failed")
} /* end if */
#endif /* H5_HAVE_PARALLEL */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_prep_for_file_close() */
/*-------------------------------------------------------------------------
* Function: H5C_dest
*
* Purpose: Flush all data to disk and destroy the cache.
*
* This function fails if any object are protected since the
* resulting file might not be consistent.
*
* Note that *cache_ptr has been freed upon successful return.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 6/2/04
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_dest(H5F_t * f)
{
H5C_t * cache_ptr = f->shared->cache;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity check */
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr->close_warning_received);
#if H5AC_DUMP_IMAGE_STATS_ON_CLOSE
if(H5C_image_stats(cache_ptr, TRUE) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't display cache image stats")
#endif /* H5AC_DUMP_IMAGE_STATS_ON_CLOSE */
/* Flush and invalidate all cache entries */
if(H5C__flush_invalidate_cache(f, H5C__NO_FLAGS_SET) < 0 )
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush cache")
/* Generate & write cache image if requested */
if(cache_ptr->image_ctl.generate_image)
if(H5C__generate_cache_image(f, cache_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTCREATE, FAIL, "Can't generate metadata cache image")
if(cache_ptr->slist_ptr != NULL) {
H5SL_close(cache_ptr->slist_ptr);
cache_ptr->slist_ptr = NULL;
} /* end if */
if(cache_ptr->tag_list != NULL) {
H5SL_destroy(cache_ptr->tag_list, H5C_free_tag_list_cb, NULL);
cache_ptr->tag_list = NULL;
} /* end if */
if(cache_ptr->log_info != NULL)
H5MM_xfree(cache_ptr->log_info);
#ifndef NDEBUG
#if H5C_DO_SANITY_CHECKS
if(cache_ptr->get_entry_ptr_from_addr_counter > 0)
HDfprintf(stdout, "*** %ld calls to H5C_get_entry_ptr_from_add(). ***\n",
cache_ptr->get_entry_ptr_from_addr_counter);
#endif /* H5C_DO_SANITY_CHECKS */
cache_ptr->magic = 0;
#endif /* NDEBUG */
cache_ptr = H5FL_FREE(H5C_t, cache_ptr);
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_dest() */
/*-------------------------------------------------------------------------
* Function: H5C_evict
*
* Purpose: Evict all except pinned entries in the cache
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Vailin Choi
* Dec 2013
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_evict(H5F_t * f)
{
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity check */
HDassert(f);
/* Flush and invalidate all cache entries except the pinned entries */
if(H5C__flush_invalidate_cache(f, H5C__EVICT_ALLOW_LAST_PINS_FLAG) < 0 )
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to evict entries in the cache")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_evict() */
/*-------------------------------------------------------------------------
* Function: H5C_expunge_entry
*
* Purpose: Use this function to tell the cache to expunge an entry
* from the cache without writing it to disk even if it is
* dirty. The entry may not be either pinned or protected.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 6/29/06
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_expunge_entry(H5F_t *f, const H5C_class_t *type, haddr_t addr, unsigned flags)
{
H5C_t * cache_ptr;
H5C_cache_entry_t * entry_ptr = NULL;
unsigned flush_flags = (H5C__FLUSH_INVALIDATE_FLAG | H5C__FLUSH_CLEAR_ONLY_FLAG);
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
HDassert(f);
HDassert(f->shared);
cache_ptr = f->shared->cache;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(type);
HDassert(H5F_addr_defined(addr));
#if H5C_DO_EXTREME_SANITY_CHECKS
if(H5C_validate_lru_list(cache_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/* Look for entry in cache */
H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, FAIL)
if((entry_ptr == NULL) || (entry_ptr->type != type))
/* the target doesn't exist in the cache, so we are done. */
HGOTO_DONE(SUCCEED)
HDassert(entry_ptr->addr == addr);
HDassert(entry_ptr->type == type);
/* Check for entry being pinned or protected */
if(entry_ptr->is_protected)
HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, "Target entry is protected")
if(entry_ptr->is_pinned)
HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, "Target entry is pinned")
/* If we get this far, call H5C__flush_single_entry() with the
* H5C__FLUSH_INVALIDATE_FLAG and the H5C__FLUSH_CLEAR_ONLY_FLAG.
* This will clear the entry, and then delete it from the cache.
*/
/* Pass along 'free file space' flag */
flush_flags |= (flags & H5C__FREE_FILE_SPACE_FLAG);
/* Delete the entry from the skip list on destroy */
flush_flags |= H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG;
if(H5C__flush_single_entry(f, entry_ptr, flush_flags) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTEXPUNGE, FAIL, "can't flush entry")
done:
#if H5C_DO_EXTREME_SANITY_CHECKS
if(H5C_validate_lru_list(cache_ptr) < 0)
HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "LRU extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_expunge_entry() */
/*-------------------------------------------------------------------------
* Function: H5C_flush_cache
*
* Purpose: Flush (and possibly destroy) the entries contained in the
* specified cache.
*
* If the cache contains protected entries, the function will
* fail, as protected entries cannot be flushed. However
* all unprotected entries should be flushed before the
* function returns failure.
*
* Return: Non-negative on success/Negative on failure or if there was
* a request to flush all items and something was protected.
*
* Programmer: John Mainzer
* 6/2/04
*
* Changes: Modified function to test for slist chamges in
* pre_serialize and serialize callbacks, and re-start
* scans through the slist when such changes occur.
*
* This has been a potential problem for some time,
* and there has been code in this function to deal
* with elements of this issue. However the shift
* to the V3 cache in combination with the activities
* of some of the cache clients (in particular the
* free space manager and the fractal heap) have
* made this re-work necessary.
*
* JRM -- 12/13/14
*
* Modified function to support rings. Basic idea is that
* every entry in the cache is assigned to a ring. Entries
* in the outermost ring are flushed first, followed by
* those in the next outermost ring, and so on until the
* innermost ring is flushed. See header comment on
* H5C_ring_t in H5Cprivate.h for a more detailed
* discussion.
*
* JRM -- 8/30/15
*
* Modified function to call the free space manager
* settling functions.
* JRM -- 6/9/16
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_flush_cache(H5F_t *f, unsigned flags)
{
#if H5C_DO_SANITY_CHECKS
int i;
uint32_t index_len = 0;
size_t index_size = (size_t)0;
size_t clean_index_size = (size_t)0;
size_t dirty_index_size = (size_t)0;
size_t slist_size = (size_t)0;
uint32_t slist_len = 0;
#endif /* H5C_DO_SANITY_CHECKS */
H5C_ring_t ring;
H5C_t * cache_ptr;
hbool_t destroy;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI(FAIL)
HDassert(f);
HDassert(f->shared);
cache_ptr = f->shared->cache;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr->slist_ptr);
#if H5C_DO_SANITY_CHECKS
HDassert(cache_ptr->index_ring_len[H5C_RING_UNDEFINED] == 0);
HDassert(cache_ptr->index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
HDassert(cache_ptr->clean_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
HDassert(cache_ptr->dirty_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
HDassert(cache_ptr->slist_ring_len[H5C_RING_UNDEFINED] == 0);
HDassert(cache_ptr->slist_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
for(i = H5C_RING_USER; i < H5C_RING_NTYPES; i++) {
index_len += cache_ptr->index_ring_len[i];
index_size += cache_ptr->index_ring_size[i];
clean_index_size += cache_ptr->clean_index_ring_size[i];
dirty_index_size += cache_ptr->dirty_index_ring_size[i];
slist_len += cache_ptr->slist_ring_len[i];
slist_size += cache_ptr->slist_ring_size[i];
} /* end for */
HDassert(cache_ptr->index_len == index_len);
HDassert(cache_ptr->index_size == index_size);
HDassert(cache_ptr->clean_index_size == clean_index_size);
HDassert(cache_ptr->dirty_index_size == dirty_index_size);
HDassert(cache_ptr->slist_len == slist_len);
HDassert(cache_ptr->slist_size == slist_size);
#endif /* H5C_DO_SANITY_CHECKS */
#if H5C_DO_EXTREME_SANITY_CHECKS
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
destroy = ( (flags & H5C__FLUSH_INVALIDATE_FLAG) != 0 );
HDassert( ! ( destroy && ( (flags & H5C__FLUSH_IGNORE_PROTECTED_FLAG) != 0 )) );
HDassert( ! ( cache_ptr->flush_in_progress ) );
cache_ptr->flush_in_progress = TRUE;
if(destroy) {
if(H5C__flush_invalidate_cache(f, flags) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "flush invalidate failed")
} /* end if */
else {
/* flush each ring, starting from the outermost ring and
* working inward.
*/
ring = H5C_RING_USER;
while(ring < H5C_RING_NTYPES) {
/* Only call the free space manager settle routines when close
* warning has been received.
*/
if(cache_ptr->close_warning_received) {
switch(ring) {
case H5C_RING_USER:
break;
case H5C_RING_RDFSM:
/* Settle raw data FSM */
if(!cache_ptr->rdfsm_settled)
if(H5MF_settle_raw_data_fsm(f, &cache_ptr->rdfsm_settled) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "RD FSM settle failed")
break;
case H5C_RING_MDFSM:
/* Settle metadata FSM */
if(!cache_ptr->mdfsm_settled)
if(H5MF_settle_meta_data_fsm(f, &cache_ptr->mdfsm_settled) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "MD FSM settle failed")
break;
case H5C_RING_SBE:
case H5C_RING_SB:
break;
default:
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown ring?!?!")
break;
} /* end switch */
} /* end if */
if(H5C__flush_ring(f, ring, flags) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "flush ring failed")
ring++;
} /* end while */
} /* end else */
done:
cache_ptr->flush_in_progress = FALSE;
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_flush_cache() */
/*-------------------------------------------------------------------------
* Function: H5C_flush_to_min_clean
*
* Purpose: Flush dirty entries until the caches min clean size is
* attained.
*
* This function is used in the implementation of the
* metadata cache in PHDF5. To avoid "messages from the
* future", the cache on process 0 can't be allowed to
* flush entries until the other processes have reached
* the same point in the calculation. If this constraint
* is not met, it is possible that the other processes will
* read metadata generated at a future point in the
* computation.
*
*
* Return: Non-negative on success/Negative on failure or if
* write is not permitted.
*
* Programmer: John Mainzer
* 9/16/05
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_flush_to_min_clean(H5F_t * f)
{
H5C_t * cache_ptr;
hbool_t write_permitted;
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI(FAIL)
HDassert( f );
HDassert( f->shared );
cache_ptr = f->shared->cache;
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
if(cache_ptr->check_write_permitted != NULL) {
if((cache_ptr->check_write_permitted)(f, &write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "can't get write_permitted")
} /* end if */
else
write_permitted = cache_ptr->write_permitted;
if(!write_permitted)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "cache write is not permitted!?!")
if(H5C__make_space_in_cache(f, (size_t)0, write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C__make_space_in_cache failed")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_flush_to_min_clean() */
/*-------------------------------------------------------------------------
* Function: H5C_insert_entry
*
* Purpose: Adds the specified thing to the cache. The thing need not
* exist on disk yet, but it must have an address and disk
* space reserved.
*
* Observe that this function cannot occasion a read.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 6/2/04
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_insert_entry(H5F_t * f,
const H5C_class_t * type,
haddr_t addr,
void * thing,
unsigned int flags)
{
H5C_t *cache_ptr;
H5AC_ring_t ring = H5C_RING_UNDEFINED;
hbool_t insert_pinned;
hbool_t flush_last;
#ifdef H5_HAVE_PARALLEL
hbool_t coll_access = FALSE; /* whether access to the cache entry is done collectively */
#endif /* H5_HAVE_PARALLEL */
hbool_t set_flush_marker;
hbool_t write_permitted = TRUE;
size_t empty_space;
H5C_cache_entry_t *entry_ptr = NULL;
H5C_cache_entry_t *test_entry_ptr;
hbool_t entry_tagged = FALSE;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
HDassert( f );
HDassert( f->shared );
cache_ptr = f->shared->cache;
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( type );
HDassert( type->mem_type == cache_ptr->class_table_ptr[type->id]->mem_type );
HDassert( type->image_len );
HDassert( H5F_addr_defined(addr) );
HDassert( thing );
#if H5C_DO_EXTREME_SANITY_CHECKS
/* no need to verify that entry is not already in the index as */
/* we already make that check below. */
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
set_flush_marker = ( (flags & H5C__SET_FLUSH_MARKER_FLAG) != 0 );
insert_pinned = ( (flags & H5C__PIN_ENTRY_FLAG) != 0 );
flush_last = ( (flags & H5C__FLUSH_LAST_FLAG) != 0 );
/* Get the ring type from the API context */
ring = H5CX_get_ring();
entry_ptr = (H5C_cache_entry_t *)thing;
/* verify that the new entry isn't already in the hash table -- scream
* and die if it is.
*/
H5C__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL)
if(test_entry_ptr != NULL) {
if(test_entry_ptr == entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "entry already in cache")
else
HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "duplicate entry in cache")
} /* end if */
entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC;
entry_ptr->cache_ptr = cache_ptr;
entry_ptr->addr = addr;
entry_ptr->type = type;
entry_ptr->image_ptr = NULL;
entry_ptr->image_up_to_date = FALSE;
entry_ptr->is_protected = FALSE;
entry_ptr->is_read_only = FALSE;
entry_ptr->ro_ref_count = 0;
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;
/* newly inserted entries are assumed to be dirty */
entry_ptr->is_dirty = TRUE;
/* not protected, so can't be dirtied */
entry_ptr->dirtied = FALSE;
/* Retrieve the size of the thing */
if((type->image_len)(thing, &(entry_ptr->size)) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTGETSIZE, FAIL, "can't get size of thing")
HDassert(entry_ptr->size > 0 && entry_ptr->size < H5C_MAX_ENTRY_SIZE);
entry_ptr->in_slist = FALSE;
#ifdef H5_HAVE_PARALLEL
entry_ptr->clear_on_unprotect = FALSE;
entry_ptr->flush_immediately = FALSE;
#endif /* H5_HAVE_PARALLEL */
entry_ptr->flush_in_progress = FALSE;
entry_ptr->destroy_in_progress = FALSE;
entry_ptr->ring = ring;
/* Initialize flush dependency fields */
entry_ptr->flush_dep_parent = NULL;
entry_ptr->flush_dep_nparents = 0;
entry_ptr->flush_dep_parent_nalloc = 0;
entry_ptr->flush_dep_nchildren = 0;
entry_ptr->flush_dep_ndirty_children = 0;
entry_ptr->flush_dep_nunser_children = 0;
entry_ptr->ht_next = NULL;
entry_ptr->ht_prev = NULL;
entry_ptr->il_next = NULL;
entry_ptr->il_prev = NULL;
entry_ptr->next = NULL;
entry_ptr->prev = NULL;
#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
entry_ptr->aux_next = NULL;
entry_ptr->aux_prev = NULL;
#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
#ifdef H5_HAVE_PARALLEL
entry_ptr->coll_next = NULL;
entry_ptr->coll_prev = NULL;
#endif /* H5_HAVE_PARALLEL */
/* initialize cache image related fields */
entry_ptr->include_in_image = FALSE;
entry_ptr->lru_rank = 0;
entry_ptr->image_dirty = FALSE;
entry_ptr->fd_parent_count = 0;
entry_ptr->fd_parent_addrs = NULL;
entry_ptr->fd_child_count = 0;
entry_ptr->fd_dirty_child_count = 0;
entry_ptr->image_fd_height = 0;
entry_ptr->prefetched = FALSE;
entry_ptr->prefetch_type_id = 0;
entry_ptr->age = 0;
entry_ptr->prefetched_dirty = FALSE;
#ifndef NDEBUG /* debugging field */
entry_ptr->serialization_count = 0;
#endif /* NDEBUG */
entry_ptr->tl_next = NULL;
entry_ptr->tl_prev = NULL;
entry_ptr->tag_info = NULL;
/* Apply tag to newly inserted entry */
if(H5C__tag_entry(cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "Cannot tag metadata entry")
entry_tagged = TRUE;
H5C__RESET_CACHE_ENTRY_STATS(entry_ptr)
if(cache_ptr->flash_size_increase_possible &&
(entry_ptr->size > cache_ptr->flash_size_increase_threshold))
if(H5C__flash_increase_cache_size(cache_ptr, 0, entry_ptr->size) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C__flash_increase_cache_size failed")
if(cache_ptr->index_size >= cache_ptr->max_cache_size)
empty_space = 0;
else
empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
if(cache_ptr->evictions_enabled &&
(((cache_ptr->index_size + entry_ptr->size) > cache_ptr->max_cache_size)
||
(((empty_space + cache_ptr->clean_index_size) < cache_ptr->min_clean_size)))) {
size_t space_needed;
if(empty_space <= entry_ptr->size)
cache_ptr->cache_full = TRUE;
if(cache_ptr->check_write_permitted != NULL) {
if((cache_ptr->check_write_permitted)(f, &write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "Can't get write_permitted")
} /* end if */
else
write_permitted = cache_ptr->write_permitted;
HDassert(entry_ptr->size <= H5C_MAX_ENTRY_SIZE);
space_needed = entry_ptr->size;
if(space_needed > cache_ptr->max_cache_size)
space_needed = cache_ptr->max_cache_size;
/* Note that space_needed is just the amount of space that
* needed to insert the new entry without exceeding the cache
* size limit. The subsequent call to H5C__make_space_in_cache()
* may evict the entries required to free more or less space
* depending on conditions. It MAY be less if the cache is
* currently undersized, or more if the cache is oversized.
*
* The cache can exceed its maximum size limit via the following
* mechanisms:
*
* First, it is possible for the cache to grow without
* bound as long as entries are protected and not unprotected.
*
* Second, when writes are not permitted it is also possible
* for the cache to grow without bound.
*
* Finally, we usually don't check to see if the cache is
* oversized at the end of an unprotect. As a result, it is
* possible to have a vastly oversized cache with no protected
* entries as long as all the protects preceed the unprotects.
*
* Since items 1 and 2 are not changing any time soon, I see
* no point in worrying about the third.
*/
if(H5C__make_space_in_cache(f, space_needed, write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTINS, FAIL, "H5C__make_space_in_cache failed")
} /* end if */
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL)
/* New entries are presumed to be dirty */
HDassert(entry_ptr->is_dirty);
entry_ptr->flush_marker = set_flush_marker;
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, FAIL)
#if H5C_DO_EXTREME_SANITY_CHECKS
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed just before done")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/* If the entry's type has a 'notify' callback send a 'after insertion'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_AFTER_INSERT, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry inserted into cache")
H5C__UPDATE_STATS_FOR_INSERTION(cache_ptr, entry_ptr)
#ifdef H5_HAVE_PARALLEL
if(H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI))
coll_access = H5CX_get_coll_metadata_read();
entry_ptr->coll_access = coll_access;
if(coll_access) {
H5C__INSERT_IN_COLL_LIST(cache_ptr, entry_ptr, FAIL)
/* Make sure the size of the collective entries in the cache remain in check */
if(cache_ptr->max_cache_size * 80 < cache_ptr->coll_list_size * 100)
if(H5C_clear_coll_entries(cache_ptr, TRUE) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "can't clear collective metadata entries")
} /* end if */
#endif
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))
HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
if(ret_value < 0 && entry_tagged)
if(H5C__untag_entry(cache_ptr, entry_ptr) < 0)
HDONE_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry from tag list")
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_insert_entry() */
/*-------------------------------------------------------------------------
* Function: H5C_mark_entry_dirty
*
* Purpose: Mark a pinned or protected entry as dirty. The target entry
* MUST be either pinned or protected, and MAY be both.
*
* In the protected case, this call is the functional
* equivalent of setting the H5C__DIRTIED_FLAG on an unprotect
* call.
*
* In the pinned but not protected case, if the entry is not
* already dirty, the function places function marks the entry
* dirty and places it on the skip list.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 5/15/06
*
* JRM -- 11/5/08
* Added call to H5C__UPDATE_INDEX_FOR_ENTRY_DIRTY() to
* update the new clean_index_size and dirty_index_size
* fields of H5C_t in the case that the entry was clean
* prior to this call, and is pinned and not protected.
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_mark_entry_dirty(void *thing)
{
H5C_t * cache_ptr;
H5C_cache_entry_t * entry_ptr = (H5C_cache_entry_t *)thing;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(entry_ptr);
HDassert(H5F_addr_defined(entry_ptr->addr));
cache_ptr = entry_ptr->cache_ptr;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
if ( entry_ptr->is_protected ) {
HDassert( ! ((entry_ptr)->is_read_only) );
/* set the dirtied flag */
entry_ptr->dirtied = TRUE;
/* reset image_up_to_date */
if(entry_ptr->image_up_to_date) {
entry_ptr->image_up_to_date = FALSE;
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_unserialized(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate serialization status to fd parents")
}/* end if */
} /* end if */
else if ( entry_ptr->is_pinned ) {
hbool_t was_clean; /* Whether the entry was previously clean */
hbool_t image_was_up_to_date;
/* Remember previous dirty status */
was_clean = !entry_ptr->is_dirty;
/* Check if image is up to date */
image_was_up_to_date = entry_ptr->image_up_to_date;
/* Mark the entry as dirty if it isn't already */
entry_ptr->is_dirty = TRUE;
entry_ptr->image_up_to_date = FALSE;
/* Modify cache data structures */
if(was_clean)
H5C__UPDATE_INDEX_FOR_ENTRY_DIRTY(cache_ptr, entry_ptr)
if(!entry_ptr->in_slist)
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
/* Update stats for entry being marked dirty */
H5C__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr)
/* Check for entry changing status and do notifications, etc. */
if(was_clean) {
/* If the entry's type has a 'notify' callback send a 'entry dirtied'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_DIRTIED, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag set")
/* Propagate the dirty flag up the flush dependency chain if appropriate */
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_dirty(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, "Can't propagate flush dep dirty flag")
} /* end if */
if(image_was_up_to_date)
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_unserialized(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate serialization status to fd parents")
} /* end if */
else
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, "Entry is neither pinned nor protected??")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_mark_entry_dirty() */
/*-------------------------------------------------------------------------
* Function: H5C_mark_entry_clean
*
* Purpose: Mark a pinned entry as clean. The target entry MUST be pinned.
*
* If the entry is not
* already clean, the function places function marks the entry
* clean and removes it from the skip list.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* 7/23/16
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_mark_entry_clean(void *_thing)
{
H5C_t * cache_ptr;
H5C_cache_entry_t * entry_ptr = (H5C_cache_entry_t *)_thing;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(entry_ptr);
HDassert(H5F_addr_defined(entry_ptr->addr));
cache_ptr = entry_ptr->cache_ptr;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
/* Operate on pinned entry */
if(entry_ptr->is_protected)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKCLEAN, FAIL, "entry is protected")
else if(entry_ptr->is_pinned) {
hbool_t was_dirty; /* Whether the entry was previously dirty */
/* Remember previous dirty status */
was_dirty = entry_ptr->is_dirty;
/* Mark the entry as clean if it isn't already */
entry_ptr->is_dirty = FALSE;
/* Also reset the 'flush_marker' flag, since the entry shouldn't be flushed now */
entry_ptr->flush_marker = FALSE;
/* Modify cache data structures */
if(was_dirty)
H5C__UPDATE_INDEX_FOR_ENTRY_CLEAN(cache_ptr, entry_ptr)
if(entry_ptr->in_slist)
H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE)
/* Update stats for entry being marked clean */
H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr)
/* Check for entry changing status and do notifications, etc. */
if(was_dirty) {
/* If the entry's type has a 'notify' callback send a 'entry cleaned'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_CLEANED, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag cleared")
/* Propagate the clean up the flush dependency chain, if appropriate */
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_clean(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKCLEAN, FAIL, "Can't propagate flush dep clean")
} /* end if */
} /* end if */
else
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKCLEAN, FAIL, "Entry is not pinned??")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_mark_entry_clean() */
/*-------------------------------------------------------------------------
* Function: H5C_mark_entry_unserialized
*
* Purpose: Mark a pinned or protected entry as unserialized. The target
* entry MUST be either pinned or protected, and MAY be both.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* 12/23/16
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_mark_entry_unserialized(void *thing)
{
H5C_cache_entry_t *entry = (H5C_cache_entry_t *)thing;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(entry);
HDassert(H5F_addr_defined(entry->addr));
if(entry->is_protected || entry->is_pinned) {
HDassert(!entry->is_read_only);
/* Reset image_up_to_date */
if(entry->image_up_to_date) {
entry->image_up_to_date = FALSE;
if(entry->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_unserialized(entry) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTSET, FAIL, "Can't propagate serialization status to fd parents")
}/* end if */
} /* end if */
else
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKUNSERIALIZED, FAIL, "Entry to unserialize is neither pinned nor protected??")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_mark_entry_unserialized() */
/*-------------------------------------------------------------------------
* Function: H5C_mark_entry_serialized
*
* Purpose: Mark a pinned entry as serialized. The target entry MUST be
* pinned.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* 12/23/16
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_mark_entry_serialized(void *_thing)
{
H5C_cache_entry_t *entry = (H5C_cache_entry_t *)_thing;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(entry);
HDassert(H5F_addr_defined(entry->addr));
/* Operate on pinned entry */
if(entry->is_protected)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKSERIALIZED, FAIL, "entry is protected")
else if(entry->is_pinned) {
/* Check for entry changing status and do notifications, etc. */
if(!entry->image_up_to_date) {
/* Set the image_up_to_date flag */
entry->image_up_to_date = TRUE;
/* Propagate the serialize up the flush dependency chain, if appropriate */
if(entry->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_serialized(entry) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKSERIALIZED, FAIL, "Can't propagate flush dep serialize")
} /* end if */
} /* end if */
else
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKSERIALIZED, FAIL, "Entry is not pinned??")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_mark_entry_serialized() */
/*-------------------------------------------------------------------------
*
* Function: H5C_move_entry
*
* Purpose: Use this function to notify the cache that an entry's
* file address changed.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 6/2/04
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_move_entry(H5C_t * cache_ptr,
const H5C_class_t * type,
haddr_t old_addr,
haddr_t new_addr)
{
H5C_cache_entry_t * entry_ptr = NULL;
H5C_cache_entry_t * test_entry_ptr = NULL;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(type);
HDassert(H5F_addr_defined(old_addr));
HDassert(H5F_addr_defined(new_addr));
HDassert(H5F_addr_ne(old_addr, new_addr));
#if H5C_DO_EXTREME_SANITY_CHECKS
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
H5C__SEARCH_INDEX(cache_ptr, old_addr, entry_ptr, FAIL)
if(entry_ptr == NULL || entry_ptr->type != type)
/* the old item doesn't exist in the cache, so we are done. */
HGOTO_DONE(SUCCEED)
HDassert(entry_ptr->addr == old_addr);
HDassert(entry_ptr->type == type);
/* Check for R/W status, otherwise error */
/* (Moving a R/O entry would mark it dirty, which shouldn't
* happen. QAK - 2016/12/02)
*/
if(entry_ptr->is_read_only)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMOVE, FAIL, "can't move R/O entry")
H5C__SEARCH_INDEX(cache_ptr, new_addr, test_entry_ptr, FAIL)
if(test_entry_ptr != NULL) { /* we are hosed */
if(test_entry_ptr->type == type)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMOVE, FAIL, "target already moved & reinserted???")
else
HGOTO_ERROR(H5E_CACHE, H5E_CANTMOVE, FAIL, "new address already in use?")
} /* end if */
/* If we get this far we have work to do. Remove *entry_ptr from
* the hash table (and skip list if necessary), change its address to the
* new address, mark it as dirty (if it isn't already) and then re-insert.
*
* Update the replacement policy for a hit to avoid an eviction before
* the moved entry is touched. Update stats for a move.
*
* Note that we do not check the size of the cache, or evict anything.
* Since this is a simple re-name, cache size should be unaffected.
*
* Check to see if the target entry is in the process of being destroyed
* before we delete from the index, etc. If it is, all we do is
* change the addr. If the entry is only in the process of being flushed,
* don't mark it as dirty either, lest we confuse the flush call back.
*/
if(!entry_ptr->destroy_in_progress) {
H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr, FAIL)
if(entry_ptr->in_slist) {
HDassert(cache_ptr->slist_ptr);
H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE)
} /* end if */
} /* end if */
entry_ptr->addr = new_addr;
if(!entry_ptr->destroy_in_progress) {
hbool_t was_dirty; /* Whether the entry was previously dirty */
/* Remember previous dirty status */
was_dirty = entry_ptr->is_dirty;
/* Mark the entry as dirty if it isn't already */
entry_ptr->is_dirty = TRUE;
/* This shouldn't be needed, but it keeps the test code happy */
if(entry_ptr->image_up_to_date) {
entry_ptr->image_up_to_date = FALSE;
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_unserialized(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate serialization status to fd parents")
} /* end if */
/* Modify cache data structures */
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL)
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
/* Skip some actions if we're in the middle of flushing the entry */
if(!entry_ptr->flush_in_progress) {
/* Update the replacement policy for the entry */
H5C__UPDATE_RP_FOR_MOVE(cache_ptr, entry_ptr, was_dirty, FAIL)
/* Check for entry changing status and do notifications, etc. */
if(!was_dirty) {
/* If the entry's type has a 'notify' callback send a 'entry dirtied'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_DIRTIED, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag set")
/* Propagate the dirty flag up the flush dependency chain if appropriate */
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_dirty(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, "Can't propagate flush dep dirty flag")
} /* end if */
} /* end if */
} /* end if */
H5C__UPDATE_STATS_FOR_MOVE(cache_ptr, entry_ptr)
done:
#if H5C_DO_EXTREME_SANITY_CHECKS
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_move_entry() */
/*-------------------------------------------------------------------------
* Function: H5C_resize_entry
*
* Purpose: Resize a pinned or protected entry.
*
* Resizing an entry dirties it, so if the entry is not
* already dirty, the function places the entry on the
* skip list.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 7/5/06
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_resize_entry(void *thing, size_t new_size)
{
H5C_t * cache_ptr;
H5C_cache_entry_t * entry_ptr = (H5C_cache_entry_t *)thing;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(entry_ptr);
HDassert(H5F_addr_defined(entry_ptr->addr));
cache_ptr = entry_ptr->cache_ptr;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
/* Check for usage errors */
if(new_size <= 0)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "New size is non-positive")
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")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/* update for change in entry size if necessary */
if ( entry_ptr->size != new_size ) {
hbool_t was_clean;
/* make note of whether the entry was clean to begin with */
was_clean = !entry_ptr->is_dirty;
/* mark the entry as dirty if it isn't already */
entry_ptr->is_dirty = TRUE;
/* Reset the image up-to-date status */
if(entry_ptr->image_up_to_date) {
entry_ptr->image_up_to_date = FALSE;
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_unserialized(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate serialization status to fd parents")
} /* end if */
/* Release the current image */
if(entry_ptr->image_ptr)
entry_ptr->image_ptr = H5MM_xfree(entry_ptr->image_ptr);
/* do a flash cache size increase if appropriate */
if ( cache_ptr->flash_size_increase_possible ) {
if ( new_size > entry_ptr->size ) {
size_t size_increase;
size_increase = new_size - entry_ptr->size;
if(size_increase >= cache_ptr->flash_size_increase_threshold) {
if(H5C__flash_increase_cache_size(cache_ptr, entry_ptr->size, new_size) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTRESIZE, FAIL, "flash cache increase failed")
}
}
}
/* update the pinned and/or protected entry list */
if(entry_ptr->is_pinned) {
H5C__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr->pel_len), \
(cache_ptr->pel_size), \
(entry_ptr->size), (new_size))
} /* end if */
if(entry_ptr->is_protected) {
H5C__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr->pl_len), \
(cache_ptr->pl_size), \
(entry_ptr->size), (new_size))
} /* end if */
#ifdef H5_HAVE_PARALLEL
if(entry_ptr->coll_access) {
H5C__DLL_UPDATE_FOR_SIZE_CHANGE((cache_ptr->coll_list_len), \
(cache_ptr->coll_list_size), \
(entry_ptr->size), (new_size))
} /* end if */
#endif /* H5_HAVE_PARALLEL */
/* update statistics just before changing the entry size */
H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_size);
/* update the hash table */
H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, \
new_size, entry_ptr, was_clean);
/* if the entry is in the skip list, update that too */
if(entry_ptr->in_slist)
H5C__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_size);
/* finally, update the entry size proper */
entry_ptr->size = new_size;
if(!entry_ptr->in_slist)
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
if(entry_ptr->is_pinned)
H5C__UPDATE_STATS_FOR_DIRTY_PIN(cache_ptr, entry_ptr)
/* Check for entry changing status and do notifications, etc. */
if(was_clean) {
/* If the entry's type has a 'notify' callback send a 'entry dirtied'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_DIRTIED, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag set")
/* Propagate the dirty flag up the flush dependency chain if appropriate */
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_dirty(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, "Can't propagate flush dep dirty flag")
} /* end if */
} /* 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))
HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_resize_entry() */
/*-------------------------------------------------------------------------
* Function: H5C_pin_protected_entry()
*
* Purpose: Pin a protected cache entry. The entry must be protected
* at the time of call, and must be unpinned.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 4/26/06
*
* Changes: Added extreme sanity checks on entry and exit.
* JRM -- 4/26/14
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_pin_protected_entry(void *thing)
{
H5C_t * cache_ptr;
H5C_cache_entry_t * entry_ptr = (H5C_cache_entry_t *)thing; /* Pointer to entry to pin */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(entry_ptr);
HDassert(H5F_addr_defined(entry_ptr->addr));
cache_ptr = entry_ptr->cache_ptr;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
#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")
#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")
/* Pin the entry from a client */
if(H5C__pin_entry_from_client(cache_ptr, entry_ptr) < 0)
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))
HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_pin_protected_entry() */
/*-------------------------------------------------------------------------
* Function: H5C_protect
*
* Purpose: If the target entry is not in the cache, load it. If
* necessary, attempt to evict one or more entries to keep
* the cache within its maximum size.
*
* Mark the target entry as protected, and return its address
* to the caller. The caller must call H5C_unprotect() when
* finished with the entry.
*
* While it is protected, the entry may not be either evicted
* or flushed -- nor may it be accessed by another call to
* H5C_protect. Any attempt to do so will result in a failure.
*
* Return: Success: Ptr to the desired entry
* Failure: NULL
*
* Programmer: John Mainzer - 6/2/04
*
*-------------------------------------------------------------------------
*/
void *
H5C_protect(H5F_t * f,
const H5C_class_t * type,
haddr_t addr,
void * udata,
unsigned flags)
{
H5C_t * cache_ptr;
H5AC_ring_t ring = H5C_RING_UNDEFINED;
hbool_t hit;
hbool_t have_write_permitted = FALSE;
hbool_t read_only = FALSE;
hbool_t flush_last;
#ifdef H5_HAVE_PARALLEL
hbool_t coll_access = FALSE; /* whether access to the cache entry is done collectively */
#endif /* H5_HAVE_PARALLEL */
hbool_t write_permitted;
hbool_t was_loaded = FALSE; /* Whether the entry was loaded as a result of the protect */
size_t empty_space;
void * thing;
H5C_cache_entry_t * entry_ptr;
void * ret_value = NULL; /* Return value */
FUNC_ENTER_NOAPI(NULL)
/* check args */
HDassert( f );
HDassert( f->shared );
cache_ptr = f->shared->cache;
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( type );
HDassert( type->mem_type == cache_ptr->class_table_ptr[type->id]->mem_type );
HDassert( H5F_addr_defined(addr) );
#if H5C_DO_EXTREME_SANITY_CHECKS
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/* Load the cache image, if requested */
if(cache_ptr->load_image) {
cache_ptr->load_image = FALSE;
if(H5C__load_cache_image(f) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "Can't load cache image")
} /* end if */
read_only = ( (flags & H5C__READ_ONLY_FLAG) != 0 );
flush_last = ( (flags & H5C__FLUSH_LAST_FLAG) != 0 );
/* Get the ring type from the API context */
ring = H5CX_get_ring();
#ifdef H5_HAVE_PARALLEL
if(H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI))
coll_access = H5CX_get_coll_metadata_read();
#endif /* H5_HAVE_PARALLEL */
/* first check to see if the target is in cache */
H5C__SEARCH_INDEX(cache_ptr, addr, entry_ptr, NULL)
if(entry_ptr != NULL) {
if(entry_ptr->ring != ring)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "ring type mismatch occurred for cache entry")
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
if(entry_ptr->prefetched) {
/* This call removes the prefetched entry from the cache,
* and replaces it with an entry deserialized from the
* image of the prefetched entry.
*/
if(H5C__deserialize_prefetched_entry(f, cache_ptr, &entry_ptr, type, addr, udata) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't deserialize prefetched entry")
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(!entry_ptr->prefetched);
HDassert(entry_ptr->addr == addr);
} /* end if */
/* Check for trying to load the wrong type of entry from an address */
if(entry_ptr->type != type)
HGOTO_ERROR(H5E_CACHE, H5E_BADTYPE, NULL, "incorrect cache entry type")
/* if this is a collective metadata read, the entry is not
marked as collective, and is clean, it is possible that
other processes will not have it in its cache and will
expect a bcast of the entry from process 0. So process 0
will bcast the entry to all other ranks. Ranks that _do_ have
the entry in their cache still have to participate in the
bcast. */
#ifdef H5_HAVE_PARALLEL
if(coll_access) {
if(!(entry_ptr->is_dirty) && !(entry_ptr->coll_access)) {
MPI_Comm comm; /* File MPI Communicator */
int mpi_code; /* MPI error code */
int buf_size;
if(MPI_COMM_NULL == (comm = H5F_mpi_get_comm(f)))
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "get_comm request failed")
if(entry_ptr->image_ptr == NULL) {
int mpi_rank;
if((mpi_rank = H5F_mpi_get_rank(f)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "Can't get MPI rank")
if(NULL == (entry_ptr->image_ptr = H5MM_malloc(entry_ptr->size + H5C_IMAGE_EXTRA_SPACE)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "memory allocation failed for on disk image buffer")
#if H5C_DO_MEMORY_SANITY_CHECKS
H5MM_memcpy(((uint8_t *)entry_ptr->image_ptr) + entry_ptr->size, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
if(0 == mpi_rank)
if(H5C__generate_image(f, cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, NULL, "can't generate entry's image")
} /* end if */
HDassert(entry_ptr->image_ptr);
H5_CHECKED_ASSIGN(buf_size, int, entry_ptr->size, size_t);
if(MPI_SUCCESS != (mpi_code = MPI_Bcast(entry_ptr->image_ptr, buf_size, MPI_BYTE, 0, comm)))
HMPI_GOTO_ERROR(NULL, "MPI_Bcast failed", mpi_code)
/* Mark the entry as collective and insert into the collective list */
entry_ptr->coll_access = TRUE;
H5C__INSERT_IN_COLL_LIST(cache_ptr, entry_ptr, NULL)
} /* end if */
else if(entry_ptr->coll_access) {
H5C__MOVE_TO_TOP_IN_COLL_LIST(cache_ptr, entry_ptr, NULL)
} /* end else-if */
} /* end if */
#endif /* H5_HAVE_PARALLEL */
#if H5C_DO_TAGGING_SANITY_CHECKS
{
/* Verify tag value */
if(cache_ptr->ignore_tags != TRUE) {
haddr_t tag; /* Tag value */
/* The entry is already in the cache, but make sure that the tag value
* is still legal. This will ensure that had the entry NOT been in the
* cache, tagging was still set up correctly and it would have received
* a legal tag value after getting loaded from disk.
*/
/* Get the tag */
tag = H5CX_get_tag();
if(H5C_verify_tag(entry_ptr->type->id, tag) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, NULL, "tag verification failed")
} /* end if */
}
#endif
hit = TRUE;
thing = (void *)entry_ptr;
} else {
/* must try to load the entry from disk. */
hit = FALSE;
if(NULL == (thing = H5C_load_entry(f,
#ifdef H5_HAVE_PARALLEL
coll_access,
#endif /* H5_HAVE_PARALLEL */
type, addr, udata)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't load entry")
entry_ptr = (H5C_cache_entry_t *)thing;
cache_ptr->entries_loaded_counter++;
entry_ptr->ring = ring;
#ifdef H5_HAVE_PARALLEL
if(H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI) && entry_ptr->coll_access)
H5C__INSERT_IN_COLL_LIST(cache_ptr, entry_ptr, NULL)
#endif /* H5_HAVE_PARALLEL */
/* Apply tag to newly protected entry */
if(H5C__tag_entry(cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, NULL, "Cannot tag metadata entry")
/* If the entry is very large, and we are configured to allow it,
* we may wish to perform a flash cache size increase.
*/
if ( ( cache_ptr->flash_size_increase_possible ) &&
( entry_ptr->size > cache_ptr->flash_size_increase_threshold ) ) {
if(H5C__flash_increase_cache_size(cache_ptr, 0, entry_ptr->size) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C__flash_increase_cache_size failed")
}
if(cache_ptr->index_size >= cache_ptr->max_cache_size)
empty_space = 0;
else
empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
/* try to free up if necceary and if evictions are permitted. Note
* that if evictions are enabled, we will call H5C__make_space_in_cache()
* regardless if the min_free_space requirement is not met.
*/
if ( ( cache_ptr->evictions_enabled ) &&
( ( (cache_ptr->index_size + entry_ptr->size) >
cache_ptr->max_cache_size)
||
( ( empty_space + cache_ptr->clean_index_size ) <
cache_ptr->min_clean_size )
)
) {
size_t space_needed;
if(empty_space <= entry_ptr->size)
cache_ptr->cache_full = TRUE;
if(cache_ptr->check_write_permitted != NULL) {
if((cache_ptr->check_write_permitted)(f, &write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "Can't get write_permitted 1")
else
have_write_permitted = TRUE;
} /* end if */
else {
write_permitted = cache_ptr->write_permitted;
have_write_permitted = TRUE;
} /* end else */
HDassert(entry_ptr->size <= H5C_MAX_ENTRY_SIZE);
space_needed = entry_ptr->size;
if(space_needed > cache_ptr->max_cache_size)
space_needed = cache_ptr->max_cache_size;
/* Note that space_needed is just the amount of space that
* needed to insert the new entry without exceeding the cache
* size limit. The subsequent call to H5C__make_space_in_cache()
* may evict the entries required to free more or less space
* depending on conditions. It MAY be less if the cache is
* currently undersized, or more if the cache is oversized.
*
* The cache can exceed its maximum size limit via the following
* mechanisms:
*
* First, it is possible for the cache to grow without
* bound as long as entries are protected and not unprotected.
*
* Second, when writes are not permitted it is also possible
* for the cache to grow without bound.
*
* Third, the user may choose to disable evictions -- causing
* the cache to grow without bound until evictions are
* re-enabled.
*
* Finally, we usually don't check to see if the cache is
* oversized at the end of an unprotect. As a result, it is
* possible to have a vastly oversized cache with no protected
* entries as long as all the protects preceed the unprotects.
*
* Since items 1, 2, and 3 are not changing any time soon, I
* see no point in worrying about the fourth.
*/
if(H5C__make_space_in_cache(f, space_needed, write_permitted) < 0 )
HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C__make_space_in_cache failed")
} /* end if */
/* Insert the entry in the hash table. It can't be dirty yet, so
* we don't even check to see if it should go in the skip list.
*
* This is no longer true -- due to a bug fix, we may modify
* data on load to repair a file.
*
* *******************************************
*
* Set the flush_last field
* of the newly loaded entry before inserting it into the
* index. Must do this, as the index tracked the number of
* entries with the flush_last field set, but assumes that
* the field will not change after insertion into the index.
*
* Note that this means that the H5C__FLUSH_LAST_FLAG flag
* is ignored if the entry is already in cache.
*/
entry_ptr->flush_me_last = flush_last;
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, NULL)
if ( ( entry_ptr->is_dirty ) && ( ! (entry_ptr->in_slist) ) ) {
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, NULL)
}
/* insert the entry in the data structures used by the replacement
* policy. We are just going to take it out again when we update
* the replacement policy for a protect, but this simplifies the
* code. If we do this often enough, we may want to optimize this.
*/
H5C__UPDATE_RP_FOR_INSERTION(cache_ptr, entry_ptr, NULL)
/* Record that the entry was loaded, to trigger a notify callback later */
/* (After the entry is fully added to the cache) */
was_loaded = TRUE;
} /* end else */
HDassert(entry_ptr->addr == addr);
HDassert(entry_ptr->type == type);
if(entry_ptr->is_protected) {
if(read_only && entry_ptr->is_read_only) {
HDassert(entry_ptr->ro_ref_count > 0);
(entry_ptr->ro_ref_count)++;
} /* end if */
else
HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "Target already protected & not read only?!?")
} /* end if */
else {
H5C__UPDATE_RP_FOR_PROTECT(cache_ptr, entry_ptr, NULL)
entry_ptr->is_protected = TRUE;
if ( read_only ) {
entry_ptr->is_read_only = TRUE;
entry_ptr->ro_ref_count = 1;
} /* end if */
entry_ptr->dirtied = FALSE;
} /* end else */
H5C__UPDATE_CACHE_HIT_RATE_STATS(cache_ptr, hit)
H5C__UPDATE_STATS_FOR_PROTECT(cache_ptr, entry_ptr, hit)
ret_value = thing;
if ( ( cache_ptr->evictions_enabled ) &&
( ( cache_ptr->size_decreased ) ||
( ( cache_ptr->resize_enabled ) &&
( cache_ptr->cache_accesses >=
(cache_ptr->resize_ctl).epoch_length ) ) ) ) {
if ( ! have_write_permitted ) {
if ( cache_ptr->check_write_permitted != NULL ) {
if((cache_ptr->check_write_permitted)(f, &write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "Can't get write_permitted")
else
have_write_permitted = TRUE;
} else {
write_permitted = cache_ptr->write_permitted;
have_write_permitted = TRUE;
}
}
if(cache_ptr->resize_enabled &&
(cache_ptr->cache_accesses >= (cache_ptr->resize_ctl).epoch_length)) {
if(H5C__auto_adjust_cache_size(f, write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "Cache auto-resize failed")
} /* end if */
if(cache_ptr->size_decreased) {
cache_ptr->size_decreased = FALSE;
/* check to see if the cache is now oversized due to the cache
* size reduction. If it is, try to evict enough entries to
* bring the cache size down to the current maximum cache size.
*
* Also, if the min_clean_size requirement is not met, we
* should also call H5C__make_space_in_cache() to bring us
* into complience.
*/
if(cache_ptr->index_size >= cache_ptr->max_cache_size)
empty_space = 0;
else
empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
if ( ( cache_ptr->index_size > cache_ptr->max_cache_size )
||
( ( empty_space + cache_ptr->clean_index_size ) <
cache_ptr->min_clean_size) ) {
if(cache_ptr->index_size > cache_ptr->max_cache_size)
cache_ptr->cache_full = TRUE;
if(H5C__make_space_in_cache(f, (size_t)0, write_permitted) < 0 )
HGOTO_ERROR(H5E_CACHE, H5E_CANTPROTECT, NULL, "H5C__make_space_in_cache failed")
}
} /* end if */
}
/* If we loaded the entry and the entry's type has a 'notify' callback, send
* an 'after load' notice now that the entry is fully integrated into
* the cache and protected. We must wait until it is protected so it is not
* evicted during the notify callback.
*/
if(was_loaded) {
/* If the entry's type has a 'notify' callback send a 'after load'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_AFTER_LOAD, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, NULL, "can't notify client about entry inserted into cache")
} /* end if */
#ifdef H5_HAVE_PARALLEL
/* Make sure the size of the collective entries in the cache remain in check */
if(coll_access)
if(cache_ptr->max_cache_size * 80 < cache_ptr->coll_list_size * 100)
if(H5C_clear_coll_entries(cache_ptr, TRUE) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, NULL, "can't clear collective metadata entries")
#endif /* H5_HAVE_PARALLEL */
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))
HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, NULL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_protect() */
/*-------------------------------------------------------------------------
*
* Function: H5C_reset_cache_hit_rate_stats()
*
* Purpose: Reset the cache hit rate computation fields.
*
* Return: SUCCEED on success, and FAIL on failure.
*
* Programmer: John Mainzer, 10/5/04
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_reset_cache_hit_rate_stats(H5C_t * cache_ptr)
{
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
if((cache_ptr == NULL) || (cache_ptr->magic != H5C__H5C_T_MAGIC))
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "bad cache_ptr on entry")
cache_ptr->cache_hits = 0;
cache_ptr->cache_accesses = 0;
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_reset_cache_hit_rate_stats() */
/*-------------------------------------------------------------------------
* Function: H5C_set_cache_auto_resize_config
*
* Purpose: Set the cache automatic resize configuration to the
* provided values if they are in range, and fail if they
* are not.
*
* If the new configuration enables automatic cache resizing,
* coerce the cache max size and min clean size into agreement
* with the new policy and re-set the full cache hit rate
* stats.
*
* Return: SUCCEED on success, and FAIL on failure.
*
* Programmer: John Mainzer
* 10/8/04
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_set_cache_auto_resize_config(H5C_t *cache_ptr,
H5C_auto_size_ctl_t *config_ptr)
{
size_t new_max_cache_size;
size_t new_min_clean_size;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
if((cache_ptr == NULL) || (cache_ptr->magic != H5C__H5C_T_MAGIC))
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "bad cache_ptr on entry")
if(config_ptr == NULL)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "NULL config_ptr on entry")
if(config_ptr->version != H5C__CURR_AUTO_SIZE_CTL_VER)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "unknown config version")
/* check general configuration section of the config: */
if(H5C_validate_resize_config(config_ptr, H5C_RESIZE_CFG__VALIDATE_GENERAL) < 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "error in general configuration fields of new config")
/* check size increase control fields of the config: */
if(H5C_validate_resize_config(config_ptr, H5C_RESIZE_CFG__VALIDATE_INCREMENT) < 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "error in the size increase control fields of new config")
/* check size decrease control fields of the config: */
if(H5C_validate_resize_config(config_ptr, H5C_RESIZE_CFG__VALIDATE_DECREMENT) < 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "error in the size decrease control fields of new config")
/* check for conflicts between size increase and size decrease controls: */
if(H5C_validate_resize_config(config_ptr, H5C_RESIZE_CFG__VALIDATE_INTERACTIONS) < 0)
HGOTO_ERROR(H5E_ARGS, H5E_BADRANGE, FAIL, "conflicting threshold fields in new config")
/* will set the increase possible fields to FALSE later if needed */
cache_ptr->size_increase_possible = TRUE;
cache_ptr->flash_size_increase_possible = TRUE;
cache_ptr->size_decrease_possible = TRUE;
switch(config_ptr->incr_mode) {
case H5C_incr__off:
cache_ptr->size_increase_possible = FALSE;
break;
case H5C_incr__threshold:
if((config_ptr->lower_hr_threshold <= (double)0.0f) ||
(config_ptr->increment <= (double)1.0f) ||
((config_ptr->apply_max_increment) && (config_ptr->max_increment <= 0)))
cache_ptr->size_increase_possible = FALSE;
break;
default: /* should be unreachable */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown incr_mode?!?!?")
} /* end switch */
/* logically, this is were configuration for flash cache size increases
* should go. However, this configuration depends on max_cache_size, so
* we wait until the end of the function, when this field is set.
*/
switch(config_ptr->decr_mode) {
case H5C_decr__off:
cache_ptr->size_decrease_possible = FALSE;
break;
case H5C_decr__threshold:
if((config_ptr->upper_hr_threshold >= (double)1.0f) ||
(config_ptr->decrement >= (double)1.0f) ||
((config_ptr->apply_max_decrement) && (config_ptr->max_decrement <= 0)))
cache_ptr->size_decrease_possible = FALSE;
break;
case H5C_decr__age_out:
if(((config_ptr->apply_empty_reserve) && (config_ptr->empty_reserve >= (double)1.0f)) ||
((config_ptr->apply_max_decrement) && (config_ptr->max_decrement <= 0)))
cache_ptr->size_decrease_possible = FALSE;
break;
case H5C_decr__age_out_with_threshold:
if(((config_ptr->apply_empty_reserve) && (config_ptr->empty_reserve >= (double)1.0f)) ||
((config_ptr->apply_max_decrement) && (config_ptr->max_decrement <= 0)) ||
(config_ptr->upper_hr_threshold >= (double)1.0f))
cache_ptr->size_decrease_possible = FALSE;
break;
default: /* should be unreachable */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown decr_mode?!?!?")
} /* end switch */
if(config_ptr->max_size == config_ptr->min_size) {
cache_ptr->size_increase_possible = FALSE;
cache_ptr->flash_size_increase_possible = FALSE;
cache_ptr->size_decrease_possible = FALSE;
} /* end if */
/* flash_size_increase_possible is intentionally omitted from the
* following:
*/
cache_ptr->resize_enabled = cache_ptr->size_increase_possible ||
cache_ptr->size_decrease_possible;
cache_ptr->resize_ctl = *config_ptr;
/* Resize the cache to the supplied initial value if requested, or as
* necessary to force it within the bounds of the current automatic
* cache resizing configuration.
*
* Note that the min_clean_fraction may have changed, so we
* go through the exercise even if the current size is within
* range and an initial size has not been provided.
*/
if(cache_ptr->resize_ctl.set_initial_size)
new_max_cache_size = cache_ptr->resize_ctl.initial_size;
else if(cache_ptr->max_cache_size > cache_ptr->resize_ctl.max_size)
new_max_cache_size = cache_ptr->resize_ctl.max_size;
else if(cache_ptr->max_cache_size < cache_ptr->resize_ctl.min_size)
new_max_cache_size = cache_ptr->resize_ctl.min_size;
else
new_max_cache_size = cache_ptr->max_cache_size;
new_min_clean_size = (size_t)((double)new_max_cache_size *
((cache_ptr->resize_ctl).min_clean_fraction));
/* since new_min_clean_size is of type size_t, we have
*
* ( 0 <= new_min_clean_size )
*
* by definition.
*/
HDassert(new_min_clean_size <= new_max_cache_size);
HDassert(cache_ptr->resize_ctl.min_size <= new_max_cache_size);
HDassert(new_max_cache_size <= cache_ptr->resize_ctl.max_size);
if(new_max_cache_size < cache_ptr->max_cache_size)
cache_ptr->size_decreased = TRUE;
cache_ptr->max_cache_size = new_max_cache_size;
cache_ptr->min_clean_size = new_min_clean_size;
if(H5C_reset_cache_hit_rate_stats(cache_ptr) < 0)
/* this should be impossible... */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_reset_cache_hit_rate_stats failed")
/* remove excess epoch markers if any */
if((config_ptr->decr_mode == H5C_decr__age_out_with_threshold) ||
(config_ptr->decr_mode == H5C_decr__age_out)) {
if(cache_ptr->epoch_markers_active > cache_ptr->resize_ctl.epochs_before_eviction)
if(H5C__autoadjust__ageout__remove_excess_markers(cache_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "can't remove excess epoch markers")
} /* end if */
else if(cache_ptr->epoch_markers_active > 0) {
if(H5C__autoadjust__ageout__remove_all_markers(cache_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "error removing all epoch markers")
}
/* configure flash size increase facility. We wait until the
* end of the function, as we need the max_cache_size set before
* we start to keep things simple.
*
* If we haven't already ruled out flash cache size increases above,
* go ahead and configure it.
*/
if(cache_ptr->flash_size_increase_possible) {
switch(config_ptr->flash_incr_mode) {
case H5C_flash_incr__off:
cache_ptr->flash_size_increase_possible = FALSE;
break;
case H5C_flash_incr__add_space:
cache_ptr->flash_size_increase_possible = TRUE;
cache_ptr->flash_size_increase_threshold = (size_t)(((double)(cache_ptr->max_cache_size)) *
((cache_ptr->resize_ctl).flash_threshold));
break;
default: /* should be unreachable */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown flash_incr_mode?!?!?")
break;
} /* end switch */
} /* end if */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_set_cache_auto_resize_config() */
/*-------------------------------------------------------------------------
* Function: H5C_set_evictions_enabled()
*
* Purpose: Set cache_ptr->evictions_enabled to the value of the
* evictions enabled parameter.
*
* Return: SUCCEED on success, and FAIL on failure.
*
* Programmer: John Mainzer
* 7/27/07
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_set_evictions_enabled(H5C_t *cache_ptr, hbool_t evictions_enabled)
{
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
if((cache_ptr == NULL) || (cache_ptr->magic != H5C__H5C_T_MAGIC))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Bad cache_ptr on entry")
/* There is no fundamental reason why we should not permit
* evictions to be disabled while automatic resize is enabled.
* However, I can't think of any good reason why one would
* want to, and allowing it would greatly complicate testing
* the feature. Hence the following:
*/
if((evictions_enabled != TRUE) &&
((cache_ptr->resize_ctl.incr_mode != H5C_incr__off) ||
(cache_ptr->resize_ctl.decr_mode != H5C_decr__off)))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't disable evictions when auto resize enabled")
cache_ptr->evictions_enabled = evictions_enabled;
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_set_evictions_enabled() */
/*-------------------------------------------------------------------------
* Function: H5C_unpin_entry()
*
* Purpose: Unpin a cache entry. The entry can be either protected or
* unprotected at the time of call, but must be pinned.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 3/22/06
*
* Changes: Added extreme sanity checks on entry and exit.
JRM -- 4/26/14
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_unpin_entry(void *_entry_ptr)
{
H5C_t * cache_ptr;
H5C_cache_entry_t * entry_ptr = (H5C_cache_entry_t *)_entry_ptr; /* Pointer to entry to unpin */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity check */
HDassert(entry_ptr);
cache_ptr = entry_ptr->cache_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")
#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))
HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_unpin_entry() */
/*-------------------------------------------------------------------------
* Function: H5C_unprotect
*
* Purpose: Undo an H5C_protect() call -- specifically, mark the
* entry as unprotected, remove it from the protected list,
* and give it back to the replacement policy.
*
* The TYPE and ADDR arguments must be the same as those in
* the corresponding call to H5C_protect() and the THING
* argument must be the value returned by that call to
* H5C_protect().
*
* Return: Non-negative on success/Negative on failure
*
* If the deleted flag is TRUE, simply remove the target entry
* from the cache, clear it, and free it without writing it to
* disk.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 6/2/04
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_unprotect(H5F_t *f, haddr_t addr, void *thing, unsigned flags)
{
H5C_t * cache_ptr;
hbool_t deleted;
hbool_t dirtied;
hbool_t set_flush_marker;
hbool_t pin_entry;
hbool_t unpin_entry;
hbool_t free_file_space;
hbool_t take_ownership;
hbool_t was_clean;
#ifdef H5_HAVE_PARALLEL
hbool_t clear_entry = FALSE;
#endif /* H5_HAVE_PARALLEL */
H5C_cache_entry_t * entry_ptr;
H5C_cache_entry_t * test_entry_ptr;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
deleted = ((flags & H5C__DELETED_FLAG) != 0);
dirtied = ((flags & H5C__DIRTIED_FLAG) != 0);
set_flush_marker = ((flags & H5C__SET_FLUSH_MARKER_FLAG) != 0);
pin_entry = ((flags & H5C__PIN_ENTRY_FLAG) != 0);
unpin_entry = ((flags & H5C__UNPIN_ENTRY_FLAG) != 0);
free_file_space = ((flags & H5C__FREE_FILE_SPACE_FLAG) != 0);
take_ownership = ((flags & H5C__TAKE_OWNERSHIP_FLAG) != 0);
HDassert( f );
HDassert( f->shared );
cache_ptr = f->shared->cache;
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( H5F_addr_defined(addr) );
HDassert( thing );
HDassert( ! ( pin_entry && unpin_entry ) );
HDassert( ( ! free_file_space ) || ( deleted ) ); /* deleted flag must accompany free_file_space */
HDassert( ( ! take_ownership ) || ( deleted ) ); /* deleted flag must accompany take_ownership */
HDassert( ! ( free_file_space && take_ownership ) ); /* can't have both free_file_space & take_ownership */
entry_ptr = (H5C_cache_entry_t *)thing;
HDassert( entry_ptr->addr == addr );
/* also set the dirtied variable if the dirtied field is set in
* the entry.
*/
dirtied |= entry_ptr->dirtied;
was_clean = ! ( entry_ptr->is_dirty );
#if H5C_DO_EXTREME_SANITY_CHECKS
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#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.
*/
if(entry_ptr->ro_ref_count > 1) {
/* Sanity check */
HDassert(entry_ptr->is_protected);
HDassert(entry_ptr->is_read_only);
if(dirtied)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Read only entry modified??")
/* Reduce the RO ref count */
(entry_ptr->ro_ref_count)--;
/* Pin or unpin the entry as requested. */
if(pin_entry) {
/* Pin the entry from a client */
if(H5C__pin_entry_from_client(cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "Can't pin entry by client")
} else if(unpin_entry) {
/* Unpin the entry from a client */
if(H5C__unpin_entry_from_client(cache_ptr, entry_ptr, FALSE) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "Can't unpin entry by client")
} /* end if */
} else {
if(entry_ptr->is_read_only) {
/* Sanity check */
HDassert(entry_ptr->ro_ref_count == 1);
if(dirtied)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Read only entry modified??")
entry_ptr->is_read_only = FALSE;
entry_ptr->ro_ref_count = 0;
} /* end if */
#ifdef H5_HAVE_PARALLEL
/* When the H5C code is used to implement the metadata cache in the
* PHDF5 case, only the cache on process 0 is allowed to write to file.
* All the other metadata caches must hold dirty entries until they
* are told that the entries are clean.
*
* The clear_on_unprotect flag in the H5C_cache_entry_t structure
* exists to deal with the case in which an entry is protected when
* its cache receives word that the entry is now clean. In this case,
* the clear_on_unprotect flag is set, and the entry is flushed with
* the H5C__FLUSH_CLEAR_ONLY_FLAG.
*
* All this is a bit awkward, but until the metadata cache entries
* are contiguous, with only one dirty flag, we have to let the supplied
* functions deal with the resetting the is_dirty flag.
*/
if(entry_ptr->clear_on_unprotect) {
/* Sanity check */
HDassert(entry_ptr->is_dirty);
entry_ptr->clear_on_unprotect = FALSE;
if(!dirtied)
clear_entry = TRUE;
} /* end if */
#endif /* H5_HAVE_PARALLEL */
if(!entry_ptr->is_protected)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Entry already unprotected??")
/* Mark the entry as dirty if appropriate */
entry_ptr->is_dirty = (entry_ptr->is_dirty || dirtied);
if(dirtied)
if(entry_ptr->image_up_to_date) {
entry_ptr->image_up_to_date = FALSE;
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_unserialized(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate serialization status to fd parents")
} /* end if */
/* Check for newly dirtied entry */
if(was_clean && entry_ptr->is_dirty) {
/* Update index for newly dirtied entry */
H5C__UPDATE_INDEX_FOR_ENTRY_DIRTY(cache_ptr, entry_ptr)
/* If the entry's type has a 'notify' callback send a 'entry dirtied'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_DIRTIED, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag set")
/* Propagate the flush dep dirty flag up the flush dependency chain
* if appropriate */
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_dirty(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, "Can't propagate flush dep dirty flag")
} /* end if */
/* Check for newly clean entry */
else if(!was_clean && !entry_ptr->is_dirty) {
/* If the entry's type has a 'notify' callback send a 'entry cleaned'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_CLEANED, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag cleared")
/* Propagate the flush dep clean flag up the flush dependency chain
* if appropriate */
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_clean(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKDIRTY, FAIL, "Can't propagate flush dep dirty flag")
} /* end else-if */
/* Pin or unpin the entry as requested. */
if(pin_entry) {
/* Pin the entry from a client */
if(H5C__pin_entry_from_client(cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "Can't pin entry by client")
} else if(unpin_entry) {
/* Unpin the entry from a client */
if(H5C__unpin_entry_from_client(cache_ptr, entry_ptr, FALSE) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "Can't unpin entry by client")
} /* end if */
/* H5C__UPDATE_RP_FOR_UNPROTECT will place the unprotected entry on
* the pinned entry list if entry_ptr->is_pinned is TRUE.
*/
H5C__UPDATE_RP_FOR_UNPROTECT(cache_ptr, entry_ptr, FAIL)
entry_ptr->is_protected = FALSE;
/* if the entry is dirty, 'or' its flush_marker with the set flush flag,
* and then add it to the skip list if it isn't there already.
*/
if(entry_ptr->is_dirty) {
entry_ptr->flush_marker |= set_flush_marker;
if(!entry_ptr->in_slist)
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL)
} /* end if */
/* this implementation of the "deleted" option is a bit inefficient, as
* we re-insert the entry to be deleted into the replacement policy
* data structures, only to remove them again. Depending on how often
* we do this, we may want to optimize a bit.
*
* On the other hand, this implementation is reasonably clean, and
* makes good use of existing code.
* JRM - 5/19/04
*/
if(deleted) {
unsigned flush_flags = (H5C__FLUSH_CLEAR_ONLY_FLAG |
H5C__FLUSH_INVALIDATE_FLAG);
/* verify that the target entry is in the cache. */
H5C__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL)
if(test_entry_ptr == NULL)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "entry not in hash table?!?")
else if(test_entry_ptr != entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "hash table contains multiple entries for addr?!?")
/* Set the 'free file space' flag for the flush, if needed */
if(free_file_space)
flush_flags |= H5C__FREE_FILE_SPACE_FLAG;
/* Set the "take ownership" flag for the flush, if needed */
if(take_ownership)
flush_flags |= H5C__TAKE_OWNERSHIP_FLAG;
/* Delete the entry from the skip list on destroy */
flush_flags |= H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG;
HDassert(((!was_clean) || dirtied) == entry_ptr->in_slist);
if(H5C__flush_single_entry(f, entry_ptr, flush_flags) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Can't flush entry")
} /* end if */
#ifdef H5_HAVE_PARALLEL
else if(clear_entry) {
/* verify that the target entry is in the cache. */
H5C__SEARCH_INDEX(cache_ptr, addr, test_entry_ptr, FAIL)
if(test_entry_ptr == NULL)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "entry not in hash table?!?")
else if(test_entry_ptr != entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "hash table contains multiple entries for addr?!?")
if(H5C__flush_single_entry(f, entry_ptr, H5C__FLUSH_CLEAR_ONLY_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPROTECT, FAIL, "Can't clear entry")
} /* end else if */
#endif /* H5_HAVE_PARALLEL */
}
H5C__UPDATE_STATS_FOR_UNPROTECT(cache_ptr)
done:
#if H5C_DO_EXTREME_SANITY_CHECKS
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0)) {
HDONE_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on exit")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_unprotect() */
/*-------------------------------------------------------------------------
*
* Function: H5C_unsettle_entry_ring
*
* Purpose: Advise the metadata cache that the specified entry's free space
* manager ring is no longer settled (if it was on entry).
*
* If the target free space manager ring is already
* unsettled, do nothing, and return SUCCEED.
*
* If the target free space manager ring is settled, and
* we are not in the process of a file shutdown, mark
* the ring as unsettled, and return SUCCEED.
*
* If the target free space manager is settled, and we
* are in the process of a file shutdown, post an error
* message, and return FAIL.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* January 3, 2017
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_unsettle_entry_ring(void *_entry)
{
H5C_cache_entry_t *entry = (H5C_cache_entry_t *)_entry; /* Entry whose ring to unsettle */
H5C_t *cache; /* Cache for file */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(entry);
HDassert(entry->ring != H5C_RING_UNDEFINED);
HDassert((H5C_RING_USER == entry->ring) || (H5C_RING_RDFSM == entry->ring) || (H5C_RING_MDFSM == entry->ring));
cache = entry->cache_ptr;
HDassert(cache);
HDassert(cache->magic == H5C__H5C_T_MAGIC);
switch(entry->ring) {
case H5C_RING_USER:
/* Do nothing */
break;
case H5C_RING_RDFSM:
if(cache->rdfsm_settled) {
if(cache->flush_in_progress || cache->close_warning_received)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unexpected rdfsm ring unsettle")
cache->rdfsm_settled = FALSE;
} /* end if */
break;
case H5C_RING_MDFSM:
if(cache->mdfsm_settled) {
if(cache->flush_in_progress || cache->close_warning_received)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unexpected mdfsm ring unsettle")
cache->mdfsm_settled = FALSE;
} /* end if */
break;
default:
HDassert(FALSE); /* this should be un-reachable */
break;
} /* end switch */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_unsettle_entry_ring() */
/*-------------------------------------------------------------------------
* Function: H5C_unsettle_ring()
*
* Purpose: Advise the metadata cache that the specified free space
* manager ring is no longer settled (if it was on entry).
*
* If the target free space manager ring is already
* unsettled, do nothing, and return SUCCEED.
*
* If the target free space manager ring is settled, and
* we are not in the process of a file shutdown, mark
* the ring as unsettled, and return SUCCEED.
*
* If the target free space manager is settled, and we
* are in the process of a file shutdown, post an error
* message, and return FAIL.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 10/15/16
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_unsettle_ring(H5F_t * f, H5C_ring_t ring)
{
H5C_t * cache_ptr;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(f);
HDassert(f->shared);
HDassert(f->shared->cache);
HDassert((H5C_RING_RDFSM == ring) || (H5C_RING_MDFSM == ring));
cache_ptr = f->shared->cache;
HDassert(H5C__H5C_T_MAGIC == cache_ptr->magic);
switch(ring) {
case H5C_RING_RDFSM:
if(cache_ptr->rdfsm_settled) {
if(cache_ptr->close_warning_received)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unexpected rdfsm ring unsettle")
cache_ptr->rdfsm_settled = FALSE;
} /* end if */
break;
case H5C_RING_MDFSM:
if(cache_ptr->mdfsm_settled) {
if(cache_ptr->close_warning_received)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unexpected mdfsm ring unsettle")
cache_ptr->mdfsm_settled = FALSE;
} /* end if */
break;
default:
HDassert(FALSE); /* this should be un-reachable */
break;
} /* end switch */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_unsettle_ring() */
/*-------------------------------------------------------------------------
* Function: H5C_validate_resize_config()
*
* Purpose: Run a sanity check on the specified sections of the
* provided instance of struct H5C_auto_size_ctl_t.
*
* Do nothing and return SUCCEED if no errors are detected,
* and flag an error and return FAIL otherwise.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 3/23/05
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_validate_resize_config(H5C_auto_size_ctl_t * config_ptr,
unsigned int tests)
{
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
if(config_ptr == NULL)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "NULL config_ptr on entry")
if(config_ptr->version != H5C__CURR_AUTO_SIZE_CTL_VER)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown config version")
if((tests & H5C_RESIZE_CFG__VALIDATE_GENERAL) != 0) {
if(config_ptr->max_size > H5C__MAX_MAX_CACHE_SIZE)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "max_size too big")
if(config_ptr->min_size < H5C__MIN_MAX_CACHE_SIZE)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "min_size too small")
if(config_ptr->min_size > config_ptr->max_size)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "min_size > max_size")
if(config_ptr->set_initial_size &&
((config_ptr->initial_size < config_ptr->min_size) ||
(config_ptr->initial_size > config_ptr->max_size)))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "initial_size must be in the interval [min_size, max_size]")
if((config_ptr->min_clean_fraction < (double)0.0f) ||
(config_ptr->min_clean_fraction > (double)1.0f))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "min_clean_fraction must be in the interval [0.0, 1.0]")
if(config_ptr->epoch_length < H5C__MIN_AR_EPOCH_LENGTH)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epoch_length too small")
if(config_ptr->epoch_length > H5C__MAX_AR_EPOCH_LENGTH)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epoch_length too big")
} /* H5C_RESIZE_CFG__VALIDATE_GENERAL */
if((tests & H5C_RESIZE_CFG__VALIDATE_INCREMENT) != 0) {
if((config_ptr->incr_mode != H5C_incr__off) &&
(config_ptr->incr_mode != H5C_incr__threshold))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Invalid incr_mode")
if(config_ptr->incr_mode == H5C_incr__threshold) {
if((config_ptr->lower_hr_threshold < (double)0.0f) ||
(config_ptr->lower_hr_threshold > (double)1.0f))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "lower_hr_threshold must be in the range [0.0, 1.0]")
if(config_ptr->increment < (double)1.0f)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "increment must be greater than or equal to 1.0")
/* no need to check max_increment, as it is a size_t,
* and thus must be non-negative.
*/
} /* H5C_incr__threshold */
switch(config_ptr->flash_incr_mode) {
case H5C_flash_incr__off:
/* nothing to do here */
break;
case H5C_flash_incr__add_space:
if((config_ptr->flash_multiple < (double)0.1f) ||
(config_ptr->flash_multiple > (double)10.0f))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "flash_multiple must be in the range [0.1, 10.0]")
if((config_ptr->flash_threshold < (double)0.1f) ||
(config_ptr->flash_threshold > (double)1.0f))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "flash_threshold must be in the range [0.1, 1.0]")
break;
default:
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Invalid flash_incr_mode")
break;
} /* end switch */
} /* H5C_RESIZE_CFG__VALIDATE_INCREMENT */
if ( (tests & H5C_RESIZE_CFG__VALIDATE_DECREMENT) != 0 ) {
if ( ( config_ptr->decr_mode != H5C_decr__off ) &&
( config_ptr->decr_mode != H5C_decr__threshold ) &&
( config_ptr->decr_mode != H5C_decr__age_out ) &&
( config_ptr->decr_mode != H5C_decr__age_out_with_threshold )
) {
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "Invalid decr_mode")
}
if ( config_ptr->decr_mode == H5C_decr__threshold ) {
if(config_ptr->upper_hr_threshold > (double)1.0f)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "upper_hr_threshold must be <= 1.0")
if((config_ptr->decrement > (double)1.0f) ||
(config_ptr->decrement < (double)0.0f))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "decrement must be in the interval [0.0, 1.0]")
/* no need to check max_decrement as it is a size_t
* and thus must be non-negative.
*/
} /* H5C_decr__threshold */
if((config_ptr->decr_mode == H5C_decr__age_out) ||
(config_ptr->decr_mode == H5C_decr__age_out_with_threshold)) {
if(config_ptr->epochs_before_eviction < 1)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epochs_before_eviction must be positive")
if(config_ptr->epochs_before_eviction > H5C__MAX_EPOCH_MARKERS)
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "epochs_before_eviction too big")
if((config_ptr->apply_empty_reserve) &&
((config_ptr->empty_reserve > (double)1.0f) ||
(config_ptr->empty_reserve < (double)0.0f)))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "empty_reserve must be in the interval [0.0, 1.0]")
/* no need to check max_decrement as it is a size_t
* and thus must be non-negative.
*/
} /* H5C_decr__age_out || H5C_decr__age_out_with_threshold */
if(config_ptr->decr_mode == H5C_decr__age_out_with_threshold) {
if((config_ptr->upper_hr_threshold > (double)1.0f) ||
(config_ptr->upper_hr_threshold < (double)0.0f))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "upper_hr_threshold must be in the interval [0.0, 1.0]")
} /* H5C_decr__age_out_with_threshold */
} /* H5C_RESIZE_CFG__VALIDATE_DECREMENT */
if ( (tests & H5C_RESIZE_CFG__VALIDATE_INTERACTIONS) != 0 ) {
if((config_ptr->incr_mode == H5C_incr__threshold)
&& ((config_ptr->decr_mode == H5C_decr__threshold) ||
(config_ptr->decr_mode == H5C_decr__age_out_with_threshold))
&& (config_ptr->lower_hr_threshold >= config_ptr->upper_hr_threshold))
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "conflicting threshold fields in config")
} /* H5C_RESIZE_CFG__VALIDATE_INTERACTIONS */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_validate_resize_config() */
/*-------------------------------------------------------------------------
* Function: H5C_create_flush_dependency()
*
* Purpose: Initiates a parent<->child entry flush dependency. The parent
* entry must be pinned or protected at the time of call, and must
* have all dependencies removed before the cache can shut down.
*
* Note: Flush dependencies in the cache indicate that a child entry
* must be flushed to the file before its parent. (This is
* currently used to implement Single-Writer/Multiple-Reader (SWMR)
* I/O access for data structures in the file).
*
* Creating a flush dependency between two entries will also pin
* the parent entry.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* 3/05/09
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_create_flush_dependency(void * parent_thing, void * child_thing)
{
H5C_t * cache_ptr;
H5C_cache_entry_t * parent_entry = (H5C_cache_entry_t *)parent_thing; /* Ptr to parent thing's entry */
H5C_cache_entry_t * child_entry = (H5C_cache_entry_t *)child_thing; /* Ptr to child thing's entry */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(parent_entry);
HDassert(parent_entry->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(H5F_addr_defined(parent_entry->addr));
HDassert(child_entry);
HDassert(child_entry->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(H5F_addr_defined(child_entry->addr));
cache_ptr = parent_entry->cache_ptr;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr == child_entry->cache_ptr);
#ifndef NDEBUG
/* Make sure the parent is not already a parent */
{
unsigned u;
for(u = 0; u < child_entry->flush_dep_nparents; u++)
HDassert(child_entry->flush_dep_parent[u] != parent_entry);
} /* end block */
#endif /* NDEBUG */
/* More sanity checks */
if(child_entry == parent_entry)
HGOTO_ERROR(H5E_CACHE, H5E_CANTDEPEND, FAIL, "Child entry flush dependency parent can't be itself")
if(!(parent_entry->is_protected || parent_entry->is_pinned))
HGOTO_ERROR(H5E_CACHE, H5E_CANTDEPEND, FAIL, "Parent entry isn't pinned or protected")
/* Check for parent not pinned */
if(!parent_entry->is_pinned) {
/* Sanity check */
HDassert(parent_entry->flush_dep_nchildren == 0);
HDassert(!parent_entry->pinned_from_client);
HDassert(!parent_entry->pinned_from_cache);
/* Pin the parent entry */
parent_entry->is_pinned = TRUE;
H5C__UPDATE_STATS_FOR_PIN(cache_ptr, parent_entry)
} /* end else */
/* Mark the entry as pinned from the cache's action (possibly redundantly) */
parent_entry->pinned_from_cache = TRUE;
/* Check if we need to resize the child's parent array */
if(child_entry->flush_dep_nparents >= child_entry->flush_dep_parent_nalloc) {
if(child_entry->flush_dep_parent_nalloc == 0) {
/* Array does not exist yet, allocate it */
HDassert(!child_entry->flush_dep_parent);
if(NULL == (child_entry->flush_dep_parent = H5FL_SEQ_MALLOC(H5C_cache_entry_ptr_t, H5C_FLUSH_DEP_PARENT_INIT)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for flush dependency parent list")
child_entry->flush_dep_parent_nalloc = H5C_FLUSH_DEP_PARENT_INIT;
} /* end if */
else {
/* Resize existing array */
HDassert(child_entry->flush_dep_parent);
if(NULL == (child_entry->flush_dep_parent = H5FL_SEQ_REALLOC(H5C_cache_entry_ptr_t, child_entry->flush_dep_parent, 2 * child_entry->flush_dep_parent_nalloc)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for flush dependency parent list")
child_entry->flush_dep_parent_nalloc *= 2;
} /* end else */
cache_ptr->entry_fd_height_change_counter++;
} /* end if */
/* Add the dependency to the child's parent array */
child_entry->flush_dep_parent[child_entry->flush_dep_nparents] = parent_entry;
child_entry->flush_dep_nparents++;
/* Increment parent's number of children */
parent_entry->flush_dep_nchildren++;
/* Adjust the number of dirty children */
if(child_entry->is_dirty) {
/* Sanity check */
HDassert(parent_entry->flush_dep_ndirty_children < parent_entry->flush_dep_nchildren);
parent_entry->flush_dep_ndirty_children++;
/* If the parent has a 'notify' callback, send a 'child entry dirtied' notice */
if(parent_entry->type->notify &&
(parent_entry->type->notify)(H5C_NOTIFY_ACTION_CHILD_DIRTIED, parent_entry) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify parent about child entry dirty flag set")
} /* end if */
/* adjust the parent's number of unserialized children. Note
* that it is possible for and entry to be clean and unserialized.
*/
if(!child_entry->image_up_to_date) {
HDassert(parent_entry->flush_dep_nunser_children < parent_entry->flush_dep_nchildren);
parent_entry->flush_dep_nunser_children++;
/* If the parent has a 'notify' callback, send a 'child entry unserialized' notice */
if(parent_entry->type->notify &&
(parent_entry->type->notify)(H5C_NOTIFY_ACTION_CHILD_UNSERIALIZED, parent_entry) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify parent about child entry serialized flag reset")
} /* end if */
/* Post-conditions, for successful operation */
HDassert(parent_entry->is_pinned);
HDassert(parent_entry->flush_dep_nchildren > 0);
HDassert(child_entry->flush_dep_parent);
HDassert(child_entry->flush_dep_nparents > 0);
HDassert(child_entry->flush_dep_parent_nalloc > 0);
#ifndef NDEBUG
H5C__assert_flush_dep_nocycle(parent_entry, child_entry);
#endif /* NDEBUG */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_create_flush_dependency() */
/*-------------------------------------------------------------------------
* Function: H5C_destroy_flush_dependency()
*
* Purpose: Terminates a parent<-> child entry flush dependency. The
* parent entry must be pinned.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* 3/05/09
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_destroy_flush_dependency(void *parent_thing, void * child_thing)
{
H5C_t * cache_ptr;
H5C_cache_entry_t * parent_entry = (H5C_cache_entry_t *)parent_thing; /* Ptr to parent entry */
H5C_cache_entry_t * child_entry = (H5C_cache_entry_t *)child_thing; /* Ptr to child entry */
unsigned u; /* Local index variable */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(parent_entry);
HDassert(parent_entry->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(H5F_addr_defined(parent_entry->addr));
HDassert(child_entry);
HDassert(child_entry->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(H5F_addr_defined(child_entry->addr));
cache_ptr = parent_entry->cache_ptr;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr == child_entry->cache_ptr);
/* Usage checks */
if(!parent_entry->is_pinned)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "Parent entry isn't pinned")
if(NULL == child_entry->flush_dep_parent)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "Child entry doesn't have a flush dependency parent array")
if(0 == parent_entry->flush_dep_nchildren)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "Parent entry flush dependency ref. count has no child dependencies")
/* Search for parent in child's parent array. This is a linear search
* because we do not expect large numbers of parents. If this changes, we
* may wish to change the parent array to a skip list */
for(u = 0; u < child_entry->flush_dep_nparents; u++)
if(child_entry->flush_dep_parent[u] == parent_entry)
break;
if(u == child_entry->flush_dep_nparents)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNDEPEND, FAIL, "Parent entry isn't a flush dependency parent for child entry")
/* Remove parent entry from child's parent array */
if(u < (child_entry->flush_dep_nparents - 1))
HDmemmove(&child_entry->flush_dep_parent[u],
&child_entry->flush_dep_parent[u + 1],
(child_entry->flush_dep_nparents - u - 1) * sizeof(child_entry->flush_dep_parent[0]));
child_entry->flush_dep_nparents--;
/* Adjust parent entry's nchildren and unpin parent if it goes to zero */
parent_entry->flush_dep_nchildren--;
if(0 == parent_entry->flush_dep_nchildren) {
/* Sanity check */
HDassert(parent_entry->pinned_from_cache);
/* Check if we should unpin parent entry now */
if(!parent_entry->pinned_from_client)
if(H5C__unpin_entry_real(cache_ptr, parent_entry, TRUE) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "Can't unpin entry")
/* Mark the entry as unpinned from the cache's action */
parent_entry->pinned_from_cache = FALSE;
} /* end if */
/* Adjust parent entry's ndirty_children */
if(child_entry->is_dirty) {
/* Sanity check */
HDassert(parent_entry->flush_dep_ndirty_children > 0);
parent_entry->flush_dep_ndirty_children--;
/* If the parent has a 'notify' callback, send a 'child entry cleaned' notice */
if(parent_entry->type->notify &&
(parent_entry->type->notify)(H5C_NOTIFY_ACTION_CHILD_CLEANED, parent_entry) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify parent about child entry dirty flag reset")
} /* end if */
/* adjust parent entry's number of unserialized children */
if(!child_entry->image_up_to_date) {
HDassert(parent_entry->flush_dep_nunser_children > 0);
parent_entry->flush_dep_nunser_children--;
/* If the parent has a 'notify' callback, send a 'child entry serialized' notice */
if(parent_entry->type->notify &&
(parent_entry->type->notify)(H5C_NOTIFY_ACTION_CHILD_SERIALIZED, parent_entry) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify parent about child entry serialized flag set")
} /* end if */
/* Shrink or free the parent array if apporpriate */
if(child_entry->flush_dep_nparents == 0) {
child_entry->flush_dep_parent = H5FL_SEQ_FREE(H5C_cache_entry_ptr_t, child_entry->flush_dep_parent);
child_entry->flush_dep_parent_nalloc = 0;
} /* end if */
else if(child_entry->flush_dep_parent_nalloc > H5C_FLUSH_DEP_PARENT_INIT
&& child_entry->flush_dep_nparents <= (child_entry->flush_dep_parent_nalloc / 4)) {
if(NULL == (child_entry->flush_dep_parent = H5FL_SEQ_REALLOC(H5C_cache_entry_ptr_t, child_entry->flush_dep_parent, child_entry->flush_dep_parent_nalloc / 4)))
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed for flush dependency parent list")
child_entry->flush_dep_parent_nalloc /= 4;
} /* end if */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_destroy_flush_dependency() */
/*************************************************************************/
/**************************** Private Functions: *************************/
/*************************************************************************/
/*-------------------------------------------------------------------------
* Function: H5C__pin_entry_from_client()
*
* Purpose: Internal routine to pin a cache entry from a client action.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* 3/26/09
*
*-------------------------------------------------------------------------
*/
#if H5C_COLLECT_CACHE_STATS
static herr_t
H5C__pin_entry_from_client(H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr)
#else
static herr_t
H5C__pin_entry_from_client(H5C_t H5_ATTR_UNUSED *cache_ptr, H5C_cache_entry_t *entry_ptr)
#endif
{
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* 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) {
/* Check if the entry was pinned through an explicit pin from a client */
if(entry_ptr->pinned_from_client)
HGOTO_ERROR(H5E_CACHE, H5E_CANTPIN, FAIL, "entry is already pinned")
} /* end if */
else {
entry_ptr->is_pinned = TRUE;
H5C__UPDATE_STATS_FOR_PIN(cache_ptr, entry_ptr)
} /* end else */
/* Mark that the entry was pinned through an explicit pin from a client */
entry_ptr->pinned_from_client = TRUE;
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__pin_entry_from_client() */
/*-------------------------------------------------------------------------
* Function: H5C__unpin_entry_real()
*
* Purpose: Internal routine to unpin a cache entry.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* 1/6/18
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__unpin_entry_real(H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr,
hbool_t update_rp)
{
herr_t ret_value = SUCCEED; /* Return value */
#if H5C_DO_SANITY_CHECKS
FUNC_ENTER_STATIC
#else
FUNC_ENTER_STATIC_NOERR
#endif
/* Sanity checking */
HDassert(cache_ptr);
HDassert(entry_ptr);
HDassert(entry_ptr->is_pinned);
/* If requested, update the replacement policy if the entry is not protected */
if(update_rp && !entry_ptr->is_protected)
H5C__UPDATE_RP_FOR_UNPIN(cache_ptr, entry_ptr, FAIL)
/* Unpin the entry now */
entry_ptr->is_pinned = FALSE;
/* Update the stats for an unpin operation */
H5C__UPDATE_STATS_FOR_UNPIN(cache_ptr, entry_ptr)
#if H5C_DO_SANITY_CHECKS
done:
#endif
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__unpin_entry_real() */
/*-------------------------------------------------------------------------
* Function: H5C__unpin_entry_from_client()
*
* Purpose: Internal routine to unpin a cache entry from a client action.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* 3/24/09
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__unpin_entry_from_client(H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr,
hbool_t update_rp)
{
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* Sanity checking */
HDassert(cache_ptr);
HDassert(entry_ptr);
/* Error checking (should be sanity checks?) */
if(!entry_ptr->is_pinned)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "entry isn't pinned")
if(!entry_ptr->pinned_from_client)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "entry wasn't pinned by cache client")
/* Check if the entry is not pinned from a flush dependency */
if(!entry_ptr->pinned_from_cache)
if(H5C__unpin_entry_real(cache_ptr, entry_ptr, update_rp) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNPIN, FAIL, "can't unpin entry")
/* Mark the entry as explicitly unpinned by the client */
entry_ptr->pinned_from_client = FALSE;
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__unpin_entry_from_client() */
/*-------------------------------------------------------------------------
*
* Function: H5C__auto_adjust_cache_size
*
* Purpose: Obtain the current full cache hit rate, and compare it
* with the hit rate thresholds for modifying cache size.
* If one of the thresholds has been crossed, adjusts the
* size of the cache accordingly.
*
* The function then resets the full cache hit rate
* statistics, and exits.
*
* Return: Non-negative on success/Negative on failure or if there was
* an attempt to flush a protected item.
*
*
* Programmer: John Mainzer, 10/7/04
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__auto_adjust_cache_size(H5F_t *f, hbool_t write_permitted)
{
H5C_t * cache_ptr = f->shared->cache;
hbool_t reentrant_call = FALSE;
hbool_t inserted_epoch_marker = FALSE;
size_t new_max_cache_size = 0;
size_t old_max_cache_size = 0;
size_t new_min_clean_size = 0;
size_t old_min_clean_size = 0;
double hit_rate;
enum H5C_resize_status status = in_spec; /* will change if needed */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI_NOINIT
HDassert( f );
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( cache_ptr->cache_accesses >=
(cache_ptr->resize_ctl).epoch_length );
HDassert( (double)0.0f <= (cache_ptr->resize_ctl).min_clean_fraction );
HDassert( (cache_ptr->resize_ctl).min_clean_fraction <= (double)100.0f );
/* check to see if cache_ptr->resize_in_progress is TRUE. If it, this
* is a re-entrant call via a client callback called in the resize
* process. To avoid an infinite recursion, set reentrant_call to
* TRUE, and goto done.
*/
if(cache_ptr->resize_in_progress) {
reentrant_call = TRUE;
HGOTO_DONE(SUCCEED)
} /* end if */
cache_ptr->resize_in_progress = TRUE;
if(!cache_ptr->resize_enabled)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Auto cache resize disabled")
HDassert(((cache_ptr->resize_ctl).incr_mode != H5C_incr__off) || \
((cache_ptr->resize_ctl).decr_mode != H5C_decr__off));
if(H5C_get_cache_hit_rate(cache_ptr, &hit_rate) != SUCCEED)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't get hit rate")
HDassert( ( (double)0.0f <= hit_rate ) && ( hit_rate <= (double)1.0f ) );
switch((cache_ptr->resize_ctl).incr_mode) {
case H5C_incr__off:
if(cache_ptr->size_increase_possible)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "size_increase_possible but H5C_incr__off?!?!?")
break;
case H5C_incr__threshold:
if ( hit_rate < (cache_ptr->resize_ctl).lower_hr_threshold ) {
if ( ! cache_ptr->size_increase_possible ) {
status = increase_disabled;
} else if ( cache_ptr->max_cache_size >=
(cache_ptr->resize_ctl).max_size ) {
HDassert( cache_ptr->max_cache_size == \
(cache_ptr->resize_ctl).max_size );
status = at_max_size;
} else if ( ! cache_ptr->cache_full ) {
status = not_full;
} else {
new_max_cache_size = (size_t)
(((double)(cache_ptr->max_cache_size)) *
(cache_ptr->resize_ctl).increment);
/* clip to max size if necessary */
if ( new_max_cache_size >
(cache_ptr->resize_ctl).max_size ) {
new_max_cache_size = (cache_ptr->resize_ctl).max_size;
}
/* clip to max increment if necessary */
if ( ( (cache_ptr->resize_ctl).apply_max_increment ) &&
( (cache_ptr->max_cache_size +
(cache_ptr->resize_ctl).max_increment) <
new_max_cache_size ) ) {
new_max_cache_size = cache_ptr->max_cache_size +
(cache_ptr->resize_ctl).max_increment;
}
status = increase;
}
}
break;
default:
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unknown incr_mode")
}
/* If the decr_mode is either age out or age out with threshold, we
* must run the marker maintenance code, whether we run the size
* reduction code or not. We do this in two places -- here we
* insert a new marker if the number of active epoch markers is
* is less than the the current epochs before eviction, and after
* the ageout call, we cycle the markers.
*
* However, we can't call the ageout code or cycle the markers
* unless there was a full complement of markers in place on
* entry. The inserted_epoch_marker flag is used to track this.
*/
if ( ( ( (cache_ptr->resize_ctl).decr_mode == H5C_decr__age_out )
||
( (cache_ptr->resize_ctl).decr_mode ==
H5C_decr__age_out_with_threshold
)
)
&&
( cache_ptr->epoch_markers_active <
(cache_ptr->resize_ctl).epochs_before_eviction
)
) {
if(H5C__autoadjust__ageout__insert_new_marker(cache_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "can't insert new epoch marker")
inserted_epoch_marker = TRUE;
}
/* don't run the cache size decrease code unless the cache size
* increase code is disabled, or the size increase code sees no need
* for action. In either case, status == in_spec at this point.
*/
if ( status == in_spec ) {
switch ( (cache_ptr->resize_ctl).decr_mode )
{
case H5C_decr__off:
break;
case H5C_decr__threshold:
if ( hit_rate > (cache_ptr->resize_ctl).upper_hr_threshold ) {
if ( ! cache_ptr->size_decrease_possible ) {
status = decrease_disabled;
} else if ( cache_ptr->max_cache_size <=
(cache_ptr->resize_ctl).min_size ) {
HDassert( cache_ptr->max_cache_size ==
(cache_ptr->resize_ctl).min_size );
status = at_min_size;
} else {
new_max_cache_size = (size_t)
(((double)(cache_ptr->max_cache_size)) *
(cache_ptr->resize_ctl).decrement);
/* clip to min size if necessary */
if ( new_max_cache_size <
(cache_ptr->resize_ctl).min_size ) {
new_max_cache_size =
(cache_ptr->resize_ctl).min_size;
}
/* clip to max decrement if necessary */
if ( ( (cache_ptr->resize_ctl).apply_max_decrement ) &&
( ((cache_ptr->resize_ctl).max_decrement +
new_max_cache_size) <
cache_ptr->max_cache_size ) ) {
new_max_cache_size = cache_ptr->max_cache_size -
(cache_ptr->resize_ctl).max_decrement;
}
status = decrease;
}
}
break;
case H5C_decr__age_out_with_threshold:
case H5C_decr__age_out:
if(!inserted_epoch_marker) {
if(!cache_ptr->size_decrease_possible)
status = decrease_disabled;
else {
if(H5C__autoadjust__ageout(f, hit_rate, &status, &new_max_cache_size, write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ageout code failed")
} /* end else */
} /* end if */
break;
default:
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unknown incr_mode")
}
}
/* cycle the epoch markers here if appropriate */
if ( ( ( (cache_ptr->resize_ctl).decr_mode == H5C_decr__age_out )
||
( (cache_ptr->resize_ctl).decr_mode ==
H5C_decr__age_out_with_threshold
)
)
&&
( ! inserted_epoch_marker )
) {
/* move last epoch marker to the head of the LRU list */
if(H5C__autoadjust__ageout__cycle_epoch_marker(cache_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "error cycling epoch marker")
}
if ( ( status == increase ) || ( status == decrease ) ) {
old_max_cache_size = cache_ptr->max_cache_size;
old_min_clean_size = cache_ptr->min_clean_size;
new_min_clean_size = (size_t)
((double)new_max_cache_size *
((cache_ptr->resize_ctl).min_clean_fraction));
/* new_min_clean_size is of size_t, and thus must be non-negative.
* Hence we have
*
* ( 0 <= new_min_clean_size ).
*
* by definition.
*/
HDassert( new_min_clean_size <= new_max_cache_size );
HDassert( (cache_ptr->resize_ctl).min_size <= new_max_cache_size );
HDassert( new_max_cache_size <= (cache_ptr->resize_ctl).max_size );
cache_ptr->max_cache_size = new_max_cache_size;
cache_ptr->min_clean_size = new_min_clean_size;
if ( status == increase ) {
cache_ptr->cache_full = FALSE;
} else if ( status == decrease ) {
cache_ptr->size_decreased = TRUE;
}
/* update flash cache size increase fields as appropriate */
if ( cache_ptr->flash_size_increase_possible ) {
switch ( (cache_ptr->resize_ctl).flash_incr_mode )
{
case H5C_flash_incr__off:
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "flash_size_increase_possible but H5C_flash_incr__off?!")
break;
case H5C_flash_incr__add_space:
cache_ptr->flash_size_increase_threshold =
(size_t)
(((double)(cache_ptr->max_cache_size)) *
((cache_ptr->resize_ctl).flash_threshold));
break;
default: /* should be unreachable */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown flash_incr_mode?!?!?")
break;
}
}
}
if ( (cache_ptr->resize_ctl).rpt_fcn != NULL ) {
(*((cache_ptr->resize_ctl).rpt_fcn))
(cache_ptr,
H5C__CURR_AUTO_RESIZE_RPT_FCN_VER,
hit_rate,
status,
old_max_cache_size,
new_max_cache_size,
old_min_clean_size,
new_min_clean_size);
}
if(H5C_reset_cache_hit_rate_stats(cache_ptr) < 0)
/* this should be impossible... */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_reset_cache_hit_rate_stats failed")
done:
/* Sanity checks */
HDassert(cache_ptr->resize_in_progress);
if(!reentrant_call)
cache_ptr->resize_in_progress = FALSE;
HDassert((!reentrant_call) || (cache_ptr->resize_in_progress));
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__auto_adjust_cache_size() */
/*-------------------------------------------------------------------------
*
* Function: H5C__autoadjust__ageout
*
* Purpose: Implement the ageout automatic cache size decrement
* algorithm. Note that while this code evicts aged out
* entries, the code does not change the maximum cache size.
* Instead, the function simply computes the new value (if
* any change is indicated) and reports this value in
* *new_max_cache_size_ptr.
*
* Return: Non-negative on success/Negative on failure or if there was
* an attempt to flush a protected item.
*
*
* Programmer: John Mainzer, 11/18/04
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__autoadjust__ageout(H5F_t * f, double hit_rate, enum H5C_resize_status * status_ptr,
size_t * new_max_cache_size_ptr, hbool_t write_permitted)
{
H5C_t * cache_ptr = f->shared->cache;
size_t test_size;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI_NOINIT
HDassert( f );
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( ( status_ptr ) && ( *status_ptr == in_spec ) );
HDassert( ( new_max_cache_size_ptr ) && ( *new_max_cache_size_ptr == 0 ) );
/* remove excess epoch markers if any */
if(cache_ptr->epoch_markers_active > (cache_ptr->resize_ctl).epochs_before_eviction)
if(H5C__autoadjust__ageout__remove_excess_markers(cache_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "can't remove excess epoch markers")
if ( ( (cache_ptr->resize_ctl).decr_mode == H5C_decr__age_out )
||
( ( (cache_ptr->resize_ctl).decr_mode ==
H5C_decr__age_out_with_threshold
)
&&
( hit_rate >= (cache_ptr->resize_ctl).upper_hr_threshold )
)
) {
if ( cache_ptr->max_cache_size > (cache_ptr->resize_ctl).min_size ){
/* evict aged out cache entries if appropriate... */
if(H5C__autoadjust__ageout__evict_aged_out_entries(f, write_permitted) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "error flushing aged out entries")
/* ... and then reduce cache size if appropriate */
if ( cache_ptr->index_size < cache_ptr->max_cache_size ) {
if ( (cache_ptr->resize_ctl).apply_empty_reserve ) {
test_size = (size_t)(((double)cache_ptr->index_size) /
(1 - (cache_ptr->resize_ctl).empty_reserve));
if ( test_size < cache_ptr->max_cache_size ) {
*status_ptr = decrease;
*new_max_cache_size_ptr = test_size;
}
} else {
*status_ptr = decrease;
*new_max_cache_size_ptr = cache_ptr->index_size;
}
if ( *status_ptr == decrease ) {
/* clip to min size if necessary */
if ( *new_max_cache_size_ptr <
(cache_ptr->resize_ctl).min_size ) {
*new_max_cache_size_ptr =
(cache_ptr->resize_ctl).min_size;
}
/* clip to max decrement if necessary */
if ( ( (cache_ptr->resize_ctl).apply_max_decrement ) &&
( ((cache_ptr->resize_ctl).max_decrement +
*new_max_cache_size_ptr) <
cache_ptr->max_cache_size ) ) {
*new_max_cache_size_ptr = cache_ptr->max_cache_size -
(cache_ptr->resize_ctl).max_decrement;
}
}
}
} else {
*status_ptr = at_min_size;
}
}
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__autoadjust__ageout() */
/*-------------------------------------------------------------------------
*
* Function: H5C__autoadjust__ageout__cycle_epoch_marker
*
* Purpose: Remove the oldest epoch marker from the LRU list,
* and reinsert it at the head of the LRU list. Also
* remove the epoch marker's index from the head of the
* ring buffer, and re-insert it at the tail of the ring
* buffer.
*
* Return: SUCCEED on success/FAIL on failure.
*
* Programmer: John Mainzer, 11/22/04
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__autoadjust__ageout__cycle_epoch_marker(H5C_t * cache_ptr)
{
herr_t ret_value = SUCCEED; /* Return value */
int i;
FUNC_ENTER_NOAPI_NOINIT
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
if(cache_ptr->epoch_markers_active <= 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "No active epoch markers on entry?!?!?")
/* remove the last marker from both the ring buffer and the LRU list */
i = cache_ptr->epoch_marker_ringbuf[cache_ptr->epoch_marker_ringbuf_first];
cache_ptr->epoch_marker_ringbuf_first =
(cache_ptr->epoch_marker_ringbuf_first + 1) %
(H5C__MAX_EPOCH_MARKERS + 1);
cache_ptr->epoch_marker_ringbuf_size -= 1;
if(cache_ptr->epoch_marker_ringbuf_size < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer underflow")
if((cache_ptr->epoch_marker_active)[i] != TRUE)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unused marker in LRU?!?")
H5C__DLL_REMOVE((&((cache_ptr->epoch_markers)[i])), \
(cache_ptr)->LRU_head_ptr, \
(cache_ptr)->LRU_tail_ptr, \
(cache_ptr)->LRU_list_len, \
(cache_ptr)->LRU_list_size, \
(FAIL))
/* now, re-insert it at the head of the LRU list, and at the tail of
* the ring buffer.
*/
HDassert(((cache_ptr->epoch_markers)[i]).addr == (haddr_t)i);
HDassert(((cache_ptr->epoch_markers)[i]).next == NULL);
HDassert(((cache_ptr->epoch_markers)[i]).prev == NULL);
cache_ptr->epoch_marker_ringbuf_last =
(cache_ptr->epoch_marker_ringbuf_last + 1) %
(H5C__MAX_EPOCH_MARKERS + 1);
(cache_ptr->epoch_marker_ringbuf)[cache_ptr->epoch_marker_ringbuf_last] = i;
cache_ptr->epoch_marker_ringbuf_size += 1;
if(cache_ptr->epoch_marker_ringbuf_size > H5C__MAX_EPOCH_MARKERS)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer overflow")
H5C__DLL_PREPEND((&((cache_ptr->epoch_markers)[i])), \
(cache_ptr)->LRU_head_ptr, \
(cache_ptr)->LRU_tail_ptr, \
(cache_ptr)->LRU_list_len, \
(cache_ptr)->LRU_list_size, \
(FAIL))
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__autoadjust__ageout__cycle_epoch_marker() */
/*-------------------------------------------------------------------------
*
* Function: H5C__autoadjust__ageout__evict_aged_out_entries
*
* Purpose: Evict clean entries in the cache that haven't
* been accessed for at least
* (cache_ptr->resize_ctl).epochs_before_eviction epochs,
* and flush dirty entries that haven't been accessed for
* that amount of time.
*
* Depending on configuration, the function will either
* flush or evict all such entries, or all such entries it
* encounters until it has freed the maximum amount of space
* allowed under the maximum decrement.
*
* If we are running in parallel mode, writes may not be
* permitted. If so, the function simply skips any dirty
* entries it may encounter.
*
* The function makes no attempt to maintain the minimum
* clean size, as there is no guarantee that the cache size
* will be changed.
*
* If there is no cache size change, the minimum clean size
* constraint will be met through a combination of clean
* entries and free space in the cache.
*
* If there is a cache size reduction, the minimum clean size
* will be re-calculated, and will be enforced the next time
* we have to make space in the cache.
*
* Observe that this function cannot occasion a read.
*
* Return: Non-negative on success/Negative on failure.
*
* Programmer: John Mainzer, 11/22/04
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__autoadjust__ageout__evict_aged_out_entries(H5F_t *f, hbool_t write_permitted)
{
H5C_t * cache_ptr = f->shared->cache;
size_t eviction_size_limit;
size_t bytes_evicted = 0;
hbool_t prev_is_dirty = FALSE;
hbool_t restart_scan;
H5C_cache_entry_t * entry_ptr;
H5C_cache_entry_t * next_ptr;
H5C_cache_entry_t * prev_ptr;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI_NOINIT
HDassert( f );
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
/* if there is a limit on the amount that the cache size can be decrease
* in any one round of the cache size reduction algorithm, load that
* limit into eviction_size_limit. Otherwise, set eviction_size_limit
* to the equivalent of infinity. The current size of the index will
* do nicely.
*/
if ( (cache_ptr->resize_ctl).apply_max_decrement ) {
eviction_size_limit = (cache_ptr->resize_ctl).max_decrement;
} else {
eviction_size_limit = cache_ptr->index_size; /* i.e. infinity */
}
if ( write_permitted ) {
restart_scan = FALSE;
entry_ptr = cache_ptr->LRU_tail_ptr;
while ( ( entry_ptr != NULL ) &&
( (entry_ptr->type)->id != H5AC_EPOCH_MARKER_ID ) &&
( bytes_evicted < eviction_size_limit ) )
{
hbool_t skipping_entry = FALSE;
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert( ! (entry_ptr->is_protected) );
HDassert( ! (entry_ptr->is_read_only) );
HDassert( (entry_ptr->ro_ref_count) == 0 );
next_ptr = entry_ptr->next;
prev_ptr = entry_ptr->prev;
if(prev_ptr != NULL)
prev_is_dirty = prev_ptr->is_dirty;
if(entry_ptr->is_dirty ) {
HDassert(!entry_ptr->prefetched_dirty);
/* dirty corked entry is skipped */
if(entry_ptr->tag_info && entry_ptr->tag_info->corked)
skipping_entry = TRUE;
else {
/* reset entries_removed_counter and
* last_entry_removed_ptr prior to the call to
* H5C__flush_single_entry() so that we can spot
* unexpected removals of entries from the cache,
* and set the restart_scan flag if proceeding
* would be likely to cause us to scan an entry
* that is no longer in the cache.
*/
cache_ptr->entries_removed_counter = 0;
cache_ptr->last_entry_removed_ptr = NULL;
if(H5C__flush_single_entry(f, entry_ptr, H5C__NO_FLAGS_SET) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry")
if(cache_ptr->entries_removed_counter > 1 || cache_ptr->last_entry_removed_ptr == prev_ptr)
restart_scan = TRUE;
} /* end else */
} /* end if */
else if(!entry_ptr->prefetched_dirty) {
bytes_evicted += entry_ptr->size;
if(H5C__flush_single_entry(f, entry_ptr, H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) < 0 )
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry")
} /* end else-if */
else {
HDassert(!entry_ptr->is_dirty);
HDassert(entry_ptr->prefetched_dirty);
skipping_entry = TRUE;
} /* end else */
if(prev_ptr != NULL) {
if(skipping_entry)
entry_ptr = prev_ptr;
else if(restart_scan || (prev_ptr->is_dirty != prev_is_dirty)
|| (prev_ptr->next != next_ptr)
|| (prev_ptr->is_protected)
|| (prev_ptr->is_pinned)) {
/* Something has happened to the LRU -- start over
* from the tail.
*/
restart_scan = FALSE;
entry_ptr = cache_ptr->LRU_tail_ptr;
H5C__UPDATE_STATS_FOR_LRU_SCAN_RESTART(cache_ptr)
} /* end else-if */
else
entry_ptr = prev_ptr;
} /* end if */
else
entry_ptr = NULL;
} /* end while */
/* for now at least, don't bother to maintain the minimum clean size,
* as the cache should now be less than its maximum size. Due to
* the vaguries of the cache size reduction algorthim, we may not
* reduce the size of the cache.
*
* If we do, we will calculate a new minimum clean size, which will
* be enforced the next time we try to make space in the cache.
*
* If we don't, no action is necessary, as we have just evicted and/or
* or flushed a bunch of entries and therefore the sum of the clean
* and free space in the cache must be greater than or equal to the
* min clean space requirement (assuming that requirement was met on
* entry).
*/
} /* end if */
else /* ! write_permitted */ {
/* Since we are not allowed to write, all we can do is evict
* any clean entries that we may encounter before we either
* hit the eviction size limit, or encounter the epoch marker.
*
* If we are operating read only, this isn't an issue, as there
* will not be any dirty entries.
*
* If we are operating in R/W mode, all the dirty entries we
* skip will be flushed the next time we attempt to make space
* when writes are permitted. This may have some local
* performance implications, but it shouldn't cause any net
* slowdown.
*/
HDassert(H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS);
entry_ptr = cache_ptr->LRU_tail_ptr;
while(entry_ptr != NULL &&
((entry_ptr->type)->id != H5AC_EPOCH_MARKER_ID) &&
(bytes_evicted < eviction_size_limit)) {
HDassert(!(entry_ptr->is_protected));
prev_ptr = entry_ptr->prev;
if(!(entry_ptr->is_dirty) && !(entry_ptr->prefetched_dirty))
if(H5C__flush_single_entry(f, entry_ptr, H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush clean entry")
/* just skip the entry if it is dirty, as we can't do
* anything with it now since we can't write.
*
* Since all entries are clean, serialize() will not be called,
* and thus we needn't test to see if the LRU has been changed
* out from under us.
*/
entry_ptr = prev_ptr;
} /* end while */
} /* end else */
if(cache_ptr->index_size < cache_ptr->max_cache_size)
cache_ptr->cache_full = FALSE;
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__autoadjust__ageout__evict_aged_out_entries() */
/*-------------------------------------------------------------------------
*
* Function: H5C__autoadjust__ageout__insert_new_marker
*
* Purpose: Find an unused marker cache entry, mark it as used, and
* insert it at the head of the LRU list. Also add the
* marker's index in the epoch_markers array.
*
* Return: SUCCEED on success/FAIL on failure.
*
* Programmer: John Mainzer, 11/19/04
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__autoadjust__ageout__insert_new_marker(H5C_t * cache_ptr)
{
herr_t ret_value = SUCCEED; /* Return value */
int i;
FUNC_ENTER_NOAPI_NOINIT
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
if(cache_ptr->epoch_markers_active >= (cache_ptr->resize_ctl).epochs_before_eviction)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Already have a full complement of markers")
/* find an unused marker */
i = 0;
while((cache_ptr->epoch_marker_active)[i] && i < H5C__MAX_EPOCH_MARKERS)
i++;
if(i >= H5C__MAX_EPOCH_MARKERS)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't find unused marker")
HDassert( ((cache_ptr->epoch_markers)[i]).addr == (haddr_t)i );
HDassert( ((cache_ptr->epoch_markers)[i]).next == NULL );
HDassert( ((cache_ptr->epoch_markers)[i]).prev == NULL );
(cache_ptr->epoch_marker_active)[i] = TRUE;
cache_ptr->epoch_marker_ringbuf_last =
(cache_ptr->epoch_marker_ringbuf_last + 1) %
(H5C__MAX_EPOCH_MARKERS + 1);
(cache_ptr->epoch_marker_ringbuf)[cache_ptr->epoch_marker_ringbuf_last] = i;
cache_ptr->epoch_marker_ringbuf_size += 1;
if ( cache_ptr->epoch_marker_ringbuf_size > H5C__MAX_EPOCH_MARKERS ) {
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer overflow")
}
H5C__DLL_PREPEND((&((cache_ptr->epoch_markers)[i])), \
(cache_ptr)->LRU_head_ptr, \
(cache_ptr)->LRU_tail_ptr, \
(cache_ptr)->LRU_list_len, \
(cache_ptr)->LRU_list_size, \
(FAIL))
cache_ptr->epoch_markers_active += 1;
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__autoadjust__ageout__insert_new_marker() */
/*-------------------------------------------------------------------------
*
* Function: H5C__autoadjust__ageout__remove_all_markers
*
* Purpose: Remove all epoch markers from the LRU list and mark them
* as inactive.
*
* Return: SUCCEED on success/FAIL on failure.
*
* Programmer: John Mainzer, 11/22/04
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__autoadjust__ageout__remove_all_markers(H5C_t * cache_ptr)
{
herr_t ret_value = SUCCEED; /* Return value */
int i;
int ring_buf_index;
FUNC_ENTER_NOAPI_NOINIT
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
while ( cache_ptr->epoch_markers_active > 0 )
{
/* get the index of the last epoch marker in the LRU list
* and remove it from the ring buffer.
*/
ring_buf_index = cache_ptr->epoch_marker_ringbuf_first;
i = (cache_ptr->epoch_marker_ringbuf)[ring_buf_index];
cache_ptr->epoch_marker_ringbuf_first =
(cache_ptr->epoch_marker_ringbuf_first + 1) %
(H5C__MAX_EPOCH_MARKERS + 1);
cache_ptr->epoch_marker_ringbuf_size -= 1;
if(cache_ptr->epoch_marker_ringbuf_size < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer underflow")
if((cache_ptr->epoch_marker_active)[i] != TRUE)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unused marker in LRU?!?")
/* remove the epoch marker from the LRU list */
H5C__DLL_REMOVE((&((cache_ptr->epoch_markers)[i])), \
(cache_ptr)->LRU_head_ptr, \
(cache_ptr)->LRU_tail_ptr, \
(cache_ptr)->LRU_list_len, \
(cache_ptr)->LRU_list_size, \
(FAIL))
/* mark the epoch marker as unused. */
(cache_ptr->epoch_marker_active)[i] = FALSE;
HDassert( ((cache_ptr->epoch_markers)[i]).addr == (haddr_t)i );
HDassert( ((cache_ptr->epoch_markers)[i]).next == NULL );
HDassert( ((cache_ptr->epoch_markers)[i]).prev == NULL );
/* decrement the number of active epoch markers */
cache_ptr->epoch_markers_active -= 1;
HDassert( cache_ptr->epoch_markers_active == \
cache_ptr->epoch_marker_ringbuf_size );
}
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__autoadjust__ageout__remove_all_markers() */
/*-------------------------------------------------------------------------
*
* Function: H5C__autoadjust__ageout__remove_excess_markers
*
* Purpose: Remove epoch markers from the end of the LRU list and
* mark them as inactive until the number of active markers
* equals the the current value of
* (cache_ptr->resize_ctl).epochs_before_eviction.
*
* Return: SUCCEED on success/FAIL on failure.
*
* Programmer: John Mainzer, 11/19/04
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__autoadjust__ageout__remove_excess_markers(H5C_t * cache_ptr)
{
herr_t ret_value = SUCCEED; /* Return value */
int i;
int ring_buf_index;
FUNC_ENTER_NOAPI_NOINIT
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
if(cache_ptr->epoch_markers_active <= (cache_ptr->resize_ctl).epochs_before_eviction)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "no excess markers on entry")
while(cache_ptr->epoch_markers_active > (cache_ptr->resize_ctl).epochs_before_eviction) {
/* get the index of the last epoch marker in the LRU list
* and remove it from the ring buffer.
*/
ring_buf_index = cache_ptr->epoch_marker_ringbuf_first;
i = (cache_ptr->epoch_marker_ringbuf)[ring_buf_index];
cache_ptr->epoch_marker_ringbuf_first =
(cache_ptr->epoch_marker_ringbuf_first + 1) %
(H5C__MAX_EPOCH_MARKERS + 1);
cache_ptr->epoch_marker_ringbuf_size -= 1;
if(cache_ptr->epoch_marker_ringbuf_size < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "ring buffer underflow")
if((cache_ptr->epoch_marker_active)[i] != TRUE)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "unused marker in LRU?!?")
/* remove the epoch marker from the LRU list */
H5C__DLL_REMOVE((&((cache_ptr->epoch_markers)[i])), \
(cache_ptr)->LRU_head_ptr, \
(cache_ptr)->LRU_tail_ptr, \
(cache_ptr)->LRU_list_len, \
(cache_ptr)->LRU_list_size, \
(FAIL))
/* mark the epoch marker as unused. */
(cache_ptr->epoch_marker_active)[i] = FALSE;
HDassert( ((cache_ptr->epoch_markers)[i]).addr == (haddr_t)i );
HDassert( ((cache_ptr->epoch_markers)[i]).next == NULL );
HDassert( ((cache_ptr->epoch_markers)[i]).prev == NULL );
/* decrement the number of active epoch markers */
cache_ptr->epoch_markers_active -= 1;
HDassert( cache_ptr->epoch_markers_active == \
cache_ptr->epoch_marker_ringbuf_size );
}
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__autoadjust__ageout__remove_excess_markers() */
/*-------------------------------------------------------------------------
*
* Function: H5C__flash_increase_cache_size
*
* Purpose: If there is not at least new_entry_size - old_entry_size
* bytes of free space in the cache and the current
* max_cache_size is less than (cache_ptr->resize_ctl).max_size,
* perform a flash increase in the cache size and then reset
* the full cache hit rate statistics, and exit.
*
* Return: Non-negative on success/Negative on failure.
*
* Programmer: John Mainzer, 12/31/07
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__flash_increase_cache_size(H5C_t * cache_ptr,
size_t old_entry_size,
size_t new_entry_size)
{
size_t new_max_cache_size = 0;
size_t old_max_cache_size = 0;
size_t new_min_clean_size = 0;
size_t old_min_clean_size = 0;
size_t space_needed;
enum H5C_resize_status status = flash_increase; /* may change */
double hit_rate;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI_NOINIT
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( cache_ptr->flash_size_increase_possible );
HDassert( new_entry_size > cache_ptr->flash_size_increase_threshold );
HDassert( old_entry_size < new_entry_size );
if(old_entry_size >= new_entry_size)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "old_entry_size >= new_entry_size")
space_needed = new_entry_size - old_entry_size;
if ( ( (cache_ptr->index_size + space_needed) >
cache_ptr->max_cache_size ) &&
( cache_ptr->max_cache_size < (cache_ptr->resize_ctl).max_size ) ) {
/* we have work to do */
switch ( (cache_ptr->resize_ctl).flash_incr_mode )
{
case H5C_flash_incr__off:
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "flash_size_increase_possible but H5C_flash_incr__off?!")
break;
case H5C_flash_incr__add_space:
if ( cache_ptr->index_size < cache_ptr->max_cache_size ) {
HDassert( (cache_ptr->max_cache_size - cache_ptr->index_size)
< space_needed );
space_needed -= cache_ptr->max_cache_size -
cache_ptr->index_size;
}
space_needed =
(size_t)(((double)space_needed) *
(cache_ptr->resize_ctl).flash_multiple);
new_max_cache_size = cache_ptr->max_cache_size + space_needed;
break;
default: /* should be unreachable */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown flash_incr_mode?!?!?")
break;
}
if ( new_max_cache_size > (cache_ptr->resize_ctl).max_size ) {
new_max_cache_size = (cache_ptr->resize_ctl).max_size;
}
HDassert( new_max_cache_size > cache_ptr->max_cache_size );
new_min_clean_size = (size_t)
((double)new_max_cache_size *
((cache_ptr->resize_ctl).min_clean_fraction));
HDassert( new_min_clean_size <= new_max_cache_size );
old_max_cache_size = cache_ptr->max_cache_size;
old_min_clean_size = cache_ptr->min_clean_size;
cache_ptr->max_cache_size = new_max_cache_size;
cache_ptr->min_clean_size = new_min_clean_size;
/* update flash cache size increase fields as appropriate */
HDassert ( cache_ptr->flash_size_increase_possible );
switch ( (cache_ptr->resize_ctl).flash_incr_mode )
{
case H5C_flash_incr__off:
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "flash_size_increase_possible but H5C_flash_incr__off?!")
break;
case H5C_flash_incr__add_space:
cache_ptr->flash_size_increase_threshold =
(size_t)
(((double)(cache_ptr->max_cache_size)) *
((cache_ptr->resize_ctl).flash_threshold));
break;
default: /* should be unreachable */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown flash_incr_mode?!?!?")
break;
}
/* note that we don't cycle the epoch markers. We can
* argue either way as to whether we should, but for now
* we don't.
*/
if ( (cache_ptr->resize_ctl).rpt_fcn != NULL ) {
/* get the hit rate for the reporting function. Should still
* be good as we haven't reset the hit rate statistics.
*/
if(H5C_get_cache_hit_rate(cache_ptr, &hit_rate) != SUCCEED)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Can't get hit rate")
(*((cache_ptr->resize_ctl).rpt_fcn))
(cache_ptr,
H5C__CURR_AUTO_RESIZE_RPT_FCN_VER,
hit_rate,
status,
old_max_cache_size,
new_max_cache_size,
old_min_clean_size,
new_min_clean_size);
}
if(H5C_reset_cache_hit_rate_stats(cache_ptr) < 0)
/* this should be impossible... */
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "H5C_reset_cache_hit_rate_stats failed")
}
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__flash_increase_cache_size() */
/*-------------------------------------------------------------------------
* Function: H5C__flush_invalidate_cache
*
* Purpose: Flush and destroy the entries contained in the target
* cache.
*
* If the cache contains protected entries, the function will
* fail, as protected entries cannot be either flushed or
* destroyed. However all unprotected entries should be
* flushed and destroyed before the function returns failure.
*
* While pinned entries can usually be flushed, they cannot
* be destroyed. However, they should be unpinned when all
* the entries that reference them have been destroyed (thus
* reduding the pinned entry's reference count to 0, allowing
* it to be unpinned).
*
* If pinned entries are present, the function makes repeated
* passes through the cache, flushing all dirty entries
* (including the pinned dirty entries where permitted) and
* destroying all unpinned entries. This process is repeated
* until either the cache is empty, or the number of pinned
* entries stops decreasing on each pass.
*
* Return: Non-negative on success/Negative on failure or if there was
* a request to flush all items and something was protected.
*
* Programmer: John Mainzer
* 3/24/065
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__flush_invalidate_cache(H5F_t *f, unsigned flags)
{
H5C_t * cache_ptr;
H5C_ring_t ring;
herr_t ret_value = SUCCEED;
FUNC_ENTER_STATIC
HDassert(f);
HDassert(f->shared);
cache_ptr = f->shared->cache;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr->slist_ptr);
#if H5C_DO_SANITY_CHECKS
{
int32_t i;
uint32_t index_len = 0;
uint32_t slist_len = 0;
size_t index_size = (size_t)0;
size_t clean_index_size = (size_t)0;
size_t dirty_index_size = (size_t)0;
size_t slist_size = (size_t)0;
HDassert(cache_ptr->index_ring_len[H5C_RING_UNDEFINED] == 0);
HDassert(cache_ptr->index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
HDassert(cache_ptr->clean_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
HDassert(cache_ptr->dirty_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
HDassert(cache_ptr->slist_ring_len[H5C_RING_UNDEFINED] == 0);
HDassert(cache_ptr->slist_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
for(i = H5C_RING_USER; i < H5C_RING_NTYPES; i++) {
index_len += cache_ptr->index_ring_len[i];
index_size += cache_ptr->index_ring_size[i];
clean_index_size += cache_ptr->clean_index_ring_size[i];
dirty_index_size += cache_ptr->dirty_index_ring_size[i];
slist_len += cache_ptr->slist_ring_len[i];
slist_size += cache_ptr->slist_ring_size[i];
} /* end for */
HDassert(cache_ptr->index_len == index_len);
HDassert(cache_ptr->index_size == index_size);
HDassert(cache_ptr->clean_index_size == clean_index_size);
HDassert(cache_ptr->dirty_index_size == dirty_index_size);
HDassert(cache_ptr->slist_len == slist_len);
HDassert(cache_ptr->slist_size == slist_size);
}
#endif /* H5C_DO_SANITY_CHECKS */
/* remove ageout markers if present */
if(cache_ptr->epoch_markers_active > 0)
if(H5C__autoadjust__ageout__remove_all_markers(cache_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "error removing all epoch markers")
/* flush invalidate each ring, starting from the outermost ring and
* working inward.
*/
ring = H5C_RING_USER;
while(ring < H5C_RING_NTYPES) {
if(H5C_flush_invalidate_ring(f, ring, flags) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "flush invalidate ring failed")
ring++;
} /* end while */
/* Invariants, after destroying all entries in the hash table */
if(!(flags & H5C__EVICT_ALLOW_LAST_PINS_FLAG)) {
HDassert(cache_ptr->index_size == 0);
HDassert(cache_ptr->clean_index_size == 0);
HDassert(cache_ptr->pel_len == 0);
HDassert(cache_ptr->pel_size == 0);
} /* end if */
else {
H5C_cache_entry_t *entry_ptr; /* Cache entry */
unsigned u; /* Local index variable */
/* All rings except ring 4 should be empty now */
/* (Ring 4 has the superblock) */
for(u = H5C_RING_USER; u < H5C_RING_SB; u++) {
HDassert(cache_ptr->index_ring_len[u] == 0);
HDassert(cache_ptr->index_ring_size[u] == 0);
HDassert(cache_ptr->clean_index_ring_size[u] == 0);
} /* end for */
/* Check that any remaining pinned entries are in the superblock ring */
entry_ptr = cache_ptr->pel_head_ptr;
while(entry_ptr) {
/* Check ring */
HDassert(entry_ptr->ring == H5C_RING_SB);
/* Advance to next entry in pinned entry list */
entry_ptr = entry_ptr->next;
} /* end while */
} /* end else */
HDassert(cache_ptr->dirty_index_size == 0);
HDassert(cache_ptr->slist_len == 0);
HDassert(cache_ptr->slist_size == 0);
HDassert(cache_ptr->pl_len == 0);
HDassert(cache_ptr->pl_size == 0);
HDassert(cache_ptr->LRU_list_len == 0);
HDassert(cache_ptr->LRU_list_size == 0);
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__flush_invalidate_cache() */
/*-------------------------------------------------------------------------
* Function: H5C_flush_invalidate_ring
*
* Purpose: Flush and destroy the entries contained in the target
* cache and ring.
*
* If the ring contains protected entries, the function will
* fail, as protected entries cannot be either flushed or
* destroyed. However all unprotected entries should be
* flushed and destroyed before the function returns failure.
*
* While pinned entries can usually be flushed, they cannot
* be destroyed. However, they should be unpinned when all
* the entries that reference them have been destroyed (thus
* reduding the pinned entry's reference count to 0, allowing
* it to be unpinned).
*
* If pinned entries are present, the function makes repeated
* passes through the cache, flushing all dirty entries
* (including the pinned dirty entries where permitted) and
* destroying all unpinned entries. This process is repeated
* until either the cache is empty, or the number of pinned
* entries stops decreasing on each pass.
*
* If flush dependencies appear in the target ring, the
* function makes repeated passes through the cache flushing
* entries in flush dependency order.
*
* Return: Non-negative on success/Negative on failure or if there was
* a request to flush all items and something was protected.
*
* Programmer: John Mainzer
* 9/1/15
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C_flush_invalidate_ring(H5F_t * f, H5C_ring_t ring, unsigned flags)
{
H5C_t *cache_ptr;
hbool_t restart_slist_scan;
uint32_t protected_entries = 0;
int32_t i;
int32_t cur_ring_pel_len;
int32_t old_ring_pel_len;
unsigned cooked_flags;
unsigned evict_flags;
H5SL_node_t *node_ptr = NULL;
H5C_cache_entry_t *entry_ptr = NULL;
H5C_cache_entry_t *next_entry_ptr = NULL;
#if H5C_DO_SANITY_CHECKS
uint32_t initial_slist_len = 0;
size_t initial_slist_size = 0;
#endif /* H5C_DO_SANITY_CHECKS */
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI(FAIL)
HDassert(f);
HDassert(f->shared);
cache_ptr = f->shared->cache;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr->slist_ptr);
HDassert(ring > H5C_RING_UNDEFINED);
HDassert(ring < H5C_RING_NTYPES);
HDassert(cache_ptr->epoch_markers_active == 0);
/* Filter out the flags that are not relevant to the flush/invalidate.
*/
cooked_flags = flags & H5C__FLUSH_CLEAR_ONLY_FLAG;
evict_flags = flags & H5C__EVICT_ALLOW_LAST_PINS_FLAG;
/* The flush procedure here is a bit strange.
*
* In the outer while loop we make at least one pass through the
* cache, and then repeat until either all the pinned entries in
* the ring unpin themselves, or until the number of pinned entries
* in the ring stops declining. In this later case, we scream and die.
*
* Since the fractal heap can dirty, resize, and/or move entries
* in is flush callback, it is possible that the cache will still
* contain dirty entries at this point. If so, we must make more
* passes through the skip list to allow it to empty.
*
* Further, since clean entries can be dirtied, resized, and/or moved
* as the result of a flush call back (either the entries own, or that
* for some other cache entry), we can no longer promise to flush
* the cache entries in increasing address order.
*
* Instead, we just do the best we can -- making a pass through
* the skip list, and then a pass through the "clean" entries, and
* then repeating as needed. Thus it is quite possible that an
* entry will be evicted from the cache only to be re-loaded later
* in the flush process (From what Quincey tells me, the pin
* mechanism makes this impossible, but even it it is true now,
* we shouldn't count on it in the future.)
*
* The bottom line is that entries will probably be flushed in close
* to increasing address order, but there are no guarantees.
*/
/* compute the number of pinned entries in this ring */
entry_ptr = cache_ptr->pel_head_ptr;
cur_ring_pel_len = 0;
while(entry_ptr != NULL) {
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->ring >= ring);
if(entry_ptr->ring == ring)
cur_ring_pel_len++;
entry_ptr = entry_ptr->next;
} /* end while */
old_ring_pel_len = cur_ring_pel_len;
while(cache_ptr->index_ring_len[ring] > 0) {
/* first, try to flush-destroy any dirty entries. Do this by
* making a scan through the slist. Note that new dirty entries
* may be created by the flush call backs. Thus it is possible
* that the slist will not be empty after we finish the scan.
*/
#if H5C_DO_SANITY_CHECKS
/* Depending on circumstances, H5C__flush_single_entry() will
* remove dirty entries from the slist as it flushes them.
* Thus for sanity checks we must make note of the initial
* slist length and size before we do any flushes.
*/
initial_slist_len = cache_ptr->slist_len;
initial_slist_size = cache_ptr->slist_size;
/* There is also the possibility that entries will be
* dirtied, resized, moved, and/or removed from the cache
* as the result of calls to the flush callbacks. We use
* the slist_len_increase and slist_size_increase increase
* fields in struct H5C_t to track these changes for purpose
* of sanity checking.
*
* To this end, we must zero these fields before we start
* the pass through the slist.
*/
cache_ptr->slist_len_increase = 0;
cache_ptr->slist_size_increase = 0;
#endif /* H5C_DO_SANITY_CHECKS */
/* Set the cache_ptr->slist_changed to false.
*
* This flag is set to TRUE by H5C__flush_single_entry if the slist
* is modified by a pre_serialize, serialize, or notify callback.
*
* H5C_flush_invalidate_ring() uses this flag to detect any
* modifications to the slist that might corrupt the scan of
* the slist -- and restart the scan in this event.
*/
cache_ptr->slist_changed = FALSE;
/* this done, start the scan of the slist */
restart_slist_scan = TRUE;
while(restart_slist_scan || (node_ptr != NULL)) {
if(restart_slist_scan) {
restart_slist_scan = FALSE;
/* Start at beginning of skip list */
node_ptr = H5SL_first(cache_ptr->slist_ptr);
if(node_ptr == NULL)
/* the slist is empty -- break out of inner loop */
break;
/* Get cache entry for this node */
next_entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
if(NULL == next_entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "next_entry_ptr == NULL ?!?!")
HDassert(next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(next_entry_ptr->is_dirty);
HDassert(next_entry_ptr->in_slist);
HDassert(next_entry_ptr->ring >= ring);
} /* end if */
entry_ptr = next_entry_ptr;
/* It is possible that entries will be dirtied, resized,
* flushed, or removed from the cache via the take ownership
* flag as the result of pre_serialize or serialized callbacks.
*
* This in turn can corrupt the scan through the slist.
*
* We test for slist modifications in the pre_serialize
* and serialize callbacks, and restart the scan of the
* slist if we find them. However, best we do some extra
* sanity checking just in case.
*/
HDassert(entry_ptr != NULL);
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->in_slist);
HDassert(entry_ptr->is_dirty);
HDassert(entry_ptr->ring >= ring);
/* increment node pointer now, before we delete its target
* from the slist.
*/
node_ptr = H5SL_next(node_ptr);
if(node_ptr != NULL) {
next_entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
if(NULL == next_entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "next_entry_ptr == NULL ?!?!")
HDassert(next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(next_entry_ptr->is_dirty);
HDassert(next_entry_ptr->in_slist);
HDassert(next_entry_ptr->ring >= ring);
HDassert(entry_ptr != next_entry_ptr);
} /* end if */
else
next_entry_ptr = NULL;
/* Note that we now remove nodes from the slist as we flush
* the associated entries, instead of leaving them there
* until we are done, and then destroying all nodes in
* the slist.
*
* While this optimization used to be easy, with the possibility
* of new entries being added to the slist in the midst of the
* flush, we must keep the slist in canonical form at all
* times.
*/
if(((!entry_ptr->flush_me_last) ||
((entry_ptr->flush_me_last) &&
(cache_ptr->num_last_entries >= cache_ptr->slist_len))) &&
(entry_ptr->flush_dep_nchildren == 0) &&
(entry_ptr->ring == ring)) {
if(entry_ptr->is_protected) {
/* we have major problems -- but lets flush
* everything we can before we flag an error.
*/
protected_entries++;
} /* end if */
else if(entry_ptr->is_pinned) {
if(H5C__flush_single_entry(f, entry_ptr, H5C__DURING_FLUSH_FLAG) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "dirty pinned entry flush failed")
if(cache_ptr->slist_changed) {
/* The slist has been modified by something
* other than the simple removal of the
* of the flushed entry after the flush.
*
* This has the potential to corrupt the
* scan through the slist, so restart it.
*/
restart_slist_scan = TRUE;
cache_ptr->slist_changed = FALSE;
H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr);
} /* end if */
} /* end else-if */
else {
if(H5C__flush_single_entry(f, entry_ptr, (cooked_flags | H5C__DURING_FLUSH_FLAG | H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG)) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "dirty entry flush destroy failed")
if(cache_ptr->slist_changed) {
/* The slist has been modified by something
* other than the simple removal of the
* of the flushed entry after the flush.
*
* This has the potential to corrupt the
* scan through the slist, so restart it.
*/
restart_slist_scan = TRUE;
cache_ptr->slist_changed = FALSE;
H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr)
} /* end if */
} /* end else */
} /* end if */
} /* end while loop scanning skip list */
#if H5C_DO_SANITY_CHECKS
/* It is possible that entries were added to the slist during
* the scan, either before or after scan pointer. The following
* asserts take this into account.
*
* Don't bother with the sanity checks if node_ptr != NULL, as
* in this case we broke out of the loop because it got changed
* out from under us.
*/
if(node_ptr == NULL) {
HDassert(cache_ptr->slist_len == (uint32_t)((int32_t)initial_slist_len + cache_ptr->slist_len_increase));
HDassert(cache_ptr->slist_size == (size_t)((ssize_t)initial_slist_size + cache_ptr->slist_size_increase));
} /* end if */
#endif /* H5C_DO_SANITY_CHECKS */
/* Since we are doing a destroy, we must make a pass through
* the hash table and try to flush - destroy all entries that
* remain.
*
* It used to be that all entries remaining in the cache at
* this point had to be clean, but with the fractal heap mods
* this may not be the case. If so, we will flush entries out
* in increasing address order.
*
* Writes to disk are possible here.
*/
/* reset the counters so that we can detect insertions, loads,
* and moves caused by the pre_serialize and serialize calls.
*/
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
next_entry_ptr = cache_ptr->il_head;
while(next_entry_ptr != NULL) {
entry_ptr = next_entry_ptr;
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->ring >= ring);
next_entry_ptr = entry_ptr->il_next;
HDassert((next_entry_ptr == NULL) ||
(next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC));
if((!entry_ptr->flush_me_last || (entry_ptr->flush_me_last && cache_ptr->num_last_entries >= cache_ptr->slist_len))
&& entry_ptr->flush_dep_nchildren == 0 && entry_ptr->ring == ring) {
if(entry_ptr->is_protected) {
/* we have major problems -- but lets flush and
* destroy everything we can before we flag an
* error.
*/
protected_entries++;
if(!entry_ptr->in_slist)
HDassert(!(entry_ptr->is_dirty));
} /* end if */
else if(!(entry_ptr->is_pinned)) {
/* if *entry_ptr is dirty, it is possible
* that one or more other entries may be
* either removed from the cache, loaded
* into the cache, or moved to a new location
* in the file as a side effect of the flush.
*
* It's also possible that removing a clean
* entry will remove the last child of a proxy
* entry, allowing it to be removed also and
* invalidating the next_entry_ptr.
*
* If either of these happen, and one of the target
* or proxy entries happens to be the next entry in
* the hash bucket, we could either find ourselves
* either scanning a non-existant entry, scanning
* through a different bucket, or skipping an entry.
*
* Neither of these are good, so restart the
* the scan at the head of the hash bucket
* after the flush if we detect that the next_entry_ptr
* becomes invalid.
*
* This is not as inefficient at it might seem,
* as hash buckets typically have at most two
* or three entries.
*/
cache_ptr->entry_watched_for_removal = next_entry_ptr;
if(H5C__flush_single_entry(f, entry_ptr, (cooked_flags | H5C__DURING_FLUSH_FLAG | H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG)) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Entry flush destroy failed")
/* Restart the index list scan if necessary. Must
* do this if the next entry is evicted, and also if
* one or more entries are inserted, loaded, or moved
* as these operations can result in part of the scan
* being skipped -- which can cause a spurious failure
* if this results in the size of the pinned entry
* failing to decline during the pass.
*/
if((NULL != next_entry_ptr && NULL == cache_ptr->entry_watched_for_removal)
|| (cache_ptr->entries_loaded_counter > 0)
|| (cache_ptr->entries_inserted_counter > 0)
|| (cache_ptr->entries_relocated_counter > 0)) {
next_entry_ptr = cache_ptr->il_head;
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr)
} /* end if */
else
cache_ptr->entry_watched_for_removal = NULL;
} /* end if */
} /* end if */
} /* end for loop scanning hash table */
/* We can't do anything if entries are pinned. The
* hope is that the entries will be unpinned as the
* result of destroys of entries that reference them.
*
* We detect this by noting the change in the number
* of pinned entries from pass to pass. If it stops
* shrinking before it hits zero, we scream and die.
*/
old_ring_pel_len = cur_ring_pel_len;
entry_ptr = cache_ptr->pel_head_ptr;
cur_ring_pel_len = 0;
while(entry_ptr != NULL) {
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->ring >= ring);
if(entry_ptr->ring == ring)
cur_ring_pel_len++;
entry_ptr = entry_ptr->next;
} /* end while */
/* Check if the number of pinned entries in the ring is positive, and
* it is not declining. Scream and die if so.
*/
if(cur_ring_pel_len > 0 && cur_ring_pel_len >= old_ring_pel_len) {
/* Don't error if allowed to have pinned entries remaining */
if(evict_flags)
HGOTO_DONE(TRUE)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Pinned entry count not decreasing, cur_ring_pel_len = %d, old_ring_pel_len = %d, ring = %d", (int)cur_ring_pel_len, (int)old_ring_pel_len, (int)ring)
} /* end if */
HDassert(protected_entries == cache_ptr->pl_len);
if(protected_entries > 0 && protected_entries == cache_ptr->index_len)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Only protected entries left in cache, protected_entries = %d", (int)protected_entries)
} /* main while loop */
/* Invariants, after destroying all entries in the ring */
for(i = (int)H5C_RING_UNDEFINED; i <= (int)ring; i++) {
HDassert(cache_ptr->index_ring_len[i] == 0);
HDassert(cache_ptr->index_ring_size[i] == (size_t)0);
HDassert(cache_ptr->clean_index_ring_size[i] == (size_t)0);
HDassert(cache_ptr->dirty_index_ring_size[i] == (size_t)0);
HDassert(cache_ptr->slist_ring_len[i] == 0);
HDassert(cache_ptr->slist_ring_size[i] == (size_t)0);
} /* end for */
HDassert(protected_entries <= cache_ptr->pl_len);
if(protected_entries > 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Cache has protected entries")
else if(cur_ring_pel_len > 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't unpin all pinned entries in ring")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_flush_invalidate_ring() */
/*-------------------------------------------------------------------------
* Function: H5C__flush_ring
*
* Purpose: Flush the entries contained in the specified cache and
* ring. All entries in rings outside the specified ring
* must have been flushed on entry.
*
* If the cache contains protected entries in the specified
* ring, the function will fail, as protected entries cannot
* be flushed. However all unprotected entries in the target
* ring should be flushed before the function returns failure.
*
* If flush dependencies appear in the target ring, the
* function makes repeated passes through the slist flushing
* entries in flush dependency order.
*
* Return: Non-negative on success/Negative on failure or if there was
* a request to flush all items and something was protected.
*
* Programmer: John Mainzer
* 9/1/15
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__flush_ring(H5F_t *f, H5C_ring_t ring, unsigned flags)
{
H5C_t * cache_ptr = f->shared->cache;
hbool_t flushed_entries_last_pass;
hbool_t flush_marked_entries;
hbool_t ignore_protected;
hbool_t tried_to_flush_protected_entry = FALSE;
hbool_t restart_slist_scan;
uint32_t protected_entries = 0;
H5SL_node_t * node_ptr = NULL;
H5C_cache_entry_t * entry_ptr = NULL;
H5C_cache_entry_t * next_entry_ptr = NULL;
#if H5C_DO_SANITY_CHECKS
uint32_t initial_slist_len = 0;
size_t initial_slist_size = 0;
#endif /* H5C_DO_SANITY_CHECKS */
int i;
herr_t ret_value = SUCCEED;
FUNC_ENTER_STATIC
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr->slist_ptr);
HDassert((flags & H5C__FLUSH_INVALIDATE_FLAG) == 0);
HDassert(ring > H5C_RING_UNDEFINED);
HDassert(ring < H5C_RING_NTYPES);
#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")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
ignore_protected = ( (flags & H5C__FLUSH_IGNORE_PROTECTED_FLAG) != 0 );
flush_marked_entries = ( (flags & H5C__FLUSH_MARKED_ENTRIES_FLAG) != 0 );
if(!flush_marked_entries)
for(i = (int)H5C_RING_UNDEFINED; i < (int)ring; i++)
HDassert(cache_ptr->slist_ring_len[i] == 0);
HDassert(cache_ptr->flush_in_progress);
/* When we are only flushing marked entries, the slist will usually
* still contain entries when we have flushed everything we should.
* Thus we track whether we have flushed any entries in the last
* pass, and terminate if we haven't.
*/
flushed_entries_last_pass = TRUE;
/* Set the cache_ptr->slist_changed to false.
*
* This flag is set to TRUE by H5C__flush_single_entry if the
* slist is modified by a pre_serialize, serialize, or notify callback.
* H5C_flush_cache uses this flag to detect any modifications
* to the slist that might corrupt the scan of the slist -- and
* restart the scan in this event.
*/
cache_ptr->slist_changed = FALSE;
while((cache_ptr->slist_ring_len[ring] > 0) &&
(protected_entries == 0) &&
(flushed_entries_last_pass)) {
flushed_entries_last_pass = FALSE;
#if H5C_DO_SANITY_CHECKS
/* For sanity checking, try to verify that the skip list has
* the expected size and number of entries at the end of each
* internal while loop (see below).
*
* Doing this get a bit tricky, as depending on flags, we may
* or may not flush all the entries in the slist.
*
* To make things more entertaining, with the advent of the
* fractal heap, the entry serialize callback can cause entries
* to be dirtied, resized, and/or moved. Also, the
* pre_serialize callback can result in an entry being
* removed from the cache via the take ownership flag.
*
* To deal with this, we first make note of the initial
* skip list length and size:
*/
initial_slist_len = cache_ptr->slist_len;
initial_slist_size = cache_ptr->slist_size;
/* As mentioned above, there is the possibility that
* entries will be dirtied, resized, flushed, or removed
* from the cache via the take ownership flag during
* our pass through the skip list. To capture the number
* of entries added, and the skip list size delta,
* zero the slist_len_increase and slist_size_increase of
* the cache's instance of H5C_t. These fields will be
* updated elsewhere to account for slist insertions and/or
* dirty entry size changes.
*/
cache_ptr->slist_len_increase = 0;
cache_ptr->slist_size_increase = 0;
/* at the end of the loop, use these values to compute the
* expected slist length and size and compare this with the
* value recorded in the cache's instance of H5C_t.
*/
#endif /* H5C_DO_SANITY_CHECKS */
restart_slist_scan = TRUE;
while((restart_slist_scan ) || (node_ptr != NULL)) {
if(restart_slist_scan) {
restart_slist_scan = FALSE;
/* Start at beginning of skip list */
node_ptr = H5SL_first(cache_ptr->slist_ptr);
if(node_ptr == NULL)
/* the slist is empty -- break out of inner loop */
break;
/* Get cache entry for this node */
next_entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
if(NULL == next_entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "next_entry_ptr == NULL ?!?!")
HDassert(next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(next_entry_ptr->is_dirty);
HDassert(next_entry_ptr->in_slist);
} /* end if */
entry_ptr = next_entry_ptr;
/* With the advent of the fractal heap, the free space
* manager, and the version 3 cache, it is possible
* that the pre-serialize or serialize callback will
* dirty, resize, or take ownership of other entries
* in the cache.
*
* To deal with this, I have inserted code to detect any
* change in the skip list not directly under the control
* of this function. If such modifications are detected,
* we must re-start the scan of the skip list to avoid
* the possibility that the target of the next_entry_ptr
* may have been flushed or deleted from the cache.
*
* To verify that all such possibilities have been dealt
* with, we do a bit of extra sanity checking on
* entry_ptr.
*/
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->in_slist);
HDassert(entry_ptr->is_dirty);
if(!flush_marked_entries || entry_ptr->flush_marker)
HDassert(entry_ptr->ring >= ring);
/* Advance node pointer now, before we delete its target
* from the slist.
*/
node_ptr = H5SL_next(node_ptr);
if(node_ptr != NULL) {
next_entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
if(NULL == next_entry_ptr)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "next_entry_ptr == NULL ?!?!")
HDassert(next_entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(next_entry_ptr->is_dirty);
HDassert(next_entry_ptr->in_slist);
if(!flush_marked_entries || next_entry_ptr->flush_marker)
HDassert(next_entry_ptr->ring >= ring);
HDassert(entry_ptr != next_entry_ptr);
} /* end if */
else
next_entry_ptr = NULL;
if((!flush_marked_entries || entry_ptr->flush_marker)
&& (!entry_ptr->flush_me_last ||
(entry_ptr->flush_me_last
&& (cache_ptr->num_last_entries >= cache_ptr->slist_len
|| (flush_marked_entries && entry_ptr->flush_marker))))
&& (entry_ptr->flush_dep_nchildren == 0
|| entry_ptr->flush_dep_ndirty_children == 0)
&& entry_ptr->ring == ring) {
HDassert(entry_ptr->flush_dep_nunser_children == 0);
if(entry_ptr->is_protected) {
/* we probably have major problems -- but lets
* flush everything we can before we decide
* whether to flag an error.
*/
tried_to_flush_protected_entry = TRUE;
protected_entries++;
} /* end if */
else {
if(H5C__flush_single_entry(f, entry_ptr, (flags | H5C__DURING_FLUSH_FLAG)) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't flush entry")
if(cache_ptr->slist_changed) {
/* The slist has been modified by something
* other than the simple removal of the
* of the flushed entry after the flush.
*
* This has the potential to corrupt the
* scan through the slist, so restart it.
*/
restart_slist_scan = TRUE;
cache_ptr->slist_changed = FALSE;
H5C__UPDATE_STATS_FOR_SLIST_SCAN_RESTART(cache_ptr)
} /* end if */
flushed_entries_last_pass = TRUE;
} /* end else */
} /* end if */
} /* while ( ( restart_slist_scan ) || ( node_ptr != NULL ) ) */
#if H5C_DO_SANITY_CHECKS
/* Verify that the slist size and length are as expected. */
HDassert((uint32_t)((int32_t)initial_slist_len + cache_ptr->slist_len_increase) == cache_ptr->slist_len);
HDassert((size_t)((ssize_t)initial_slist_size + cache_ptr->slist_size_increase) == cache_ptr->slist_size);
#endif /* H5C_DO_SANITY_CHECKS */
} /* while */
HDassert(protected_entries <= cache_ptr->pl_len);
if(((cache_ptr->pl_len > 0) && (!ignore_protected)) || (tried_to_flush_protected_entry))
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "cache has protected items")
#if H5C_DO_SANITY_CHECKS
if(!flush_marked_entries) {
HDassert(cache_ptr->slist_ring_len[ring] == 0);
HDassert(cache_ptr->slist_ring_size[ring] == 0);
} /* end if */
#endif /* H5C_DO_SANITY_CHECKS */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__flush_ring() */
/*-------------------------------------------------------------------------
*
* Function: H5C__flush_single_entry
*
* Purpose: Flush or clear (and evict if requested) the cache entry
* with the specified address and type. If the type is NULL,
* any unprotected entry at the specified address will be
* flushed (and possibly evicted).
*
* Attempts to flush a protected entry will result in an
* error.
*
* If the H5C__FLUSH_INVALIDATE_FLAG flag is set, the entry will
* be cleared and not flushed, and the call can't be part of a
* sequence of flushes.
*
* If the caller knows the address of the skip list node at
* which the target entry resides, it can avoid a lookup
* by supplying that address in the tgt_node_ptr parameter.
* If this parameter is NULL, the function will do a skip list
* search for the entry instead.
*
* The function does nothing silently if there is no entry
* at the supplied address, or if the entry found has the
* wrong type.
*
* Return: Non-negative on success/Negative on failure or if there was
* an attempt to flush a protected item.
*
* Programmer: John Mainzer, 5/5/04
*
*-------------------------------------------------------------------------
*/
herr_t
H5C__flush_single_entry(H5F_t *f, H5C_cache_entry_t *entry_ptr, unsigned flags)
{
H5C_t * cache_ptr; /* Cache for file */
hbool_t destroy; /* external flag */
hbool_t clear_only; /* external flag */
hbool_t free_file_space; /* external flag */
hbool_t take_ownership; /* external flag */
hbool_t del_from_slist_on_destroy; /* external flag */
hbool_t during_flush; /* external flag */
hbool_t write_entry; /* internal flag */
hbool_t destroy_entry; /* internal flag */
hbool_t generate_image; /* internal flag */
hbool_t update_page_buffer; /* internal flag */
hbool_t was_dirty;
hbool_t suppress_image_entry_writes = FALSE;
hbool_t suppress_image_entry_frees = FALSE;
haddr_t entry_addr = HADDR_UNDEF;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_PACKAGE
HDassert(f);
cache_ptr = f->shared->cache;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(entry_ptr);
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->ring != H5C_RING_UNDEFINED);
HDassert(entry_ptr->type);
/* setup external flags from the flags parameter */
destroy = ((flags & H5C__FLUSH_INVALIDATE_FLAG) != 0);
clear_only = ((flags & H5C__FLUSH_CLEAR_ONLY_FLAG) != 0);
free_file_space = ((flags & H5C__FREE_FILE_SPACE_FLAG) != 0);
take_ownership = ((flags & H5C__TAKE_OWNERSHIP_FLAG) != 0);
del_from_slist_on_destroy = ((flags & H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) != 0);
during_flush = ((flags & H5C__DURING_FLUSH_FLAG) != 0);
generate_image = ((flags & H5C__GENERATE_IMAGE_FLAG) != 0);
update_page_buffer = ((flags & H5C__UPDATE_PAGE_BUFFER_FLAG) != 0);
/* Set the flag for destroying the entry, based on the 'take ownership'
* and 'destroy' flags
*/
if(take_ownership)
destroy_entry = FALSE;
else
destroy_entry = destroy;
/* we will write the entry to disk if it exists, is dirty, and if the
* clear only flag is not set.
*/
if(entry_ptr->is_dirty && !clear_only)
write_entry = TRUE;
else
write_entry = FALSE;
/* if we have received close warning, and we have been instructed to
* generate a metadata cache image, and we have actually constructed
* the entry images, set suppress_image_entry_frees to TRUE.
*
* Set suppress_image_entry_writes to TRUE if indicated by the
* image_ctl flags.
*/
if(cache_ptr->close_warning_received && cache_ptr->image_ctl.generate_image
&& cache_ptr->num_entries_in_image > 0 && cache_ptr->image_entries) {
/* Sanity checks */
HDassert(entry_ptr->image_up_to_date || !(entry_ptr->include_in_image));
HDassert(entry_ptr->image_ptr || !(entry_ptr->include_in_image));
HDassert((!clear_only) || !(entry_ptr->include_in_image));
HDassert((!take_ownership) || !(entry_ptr->include_in_image));
HDassert((!free_file_space) || !(entry_ptr->include_in_image));
suppress_image_entry_frees = TRUE;
if(cache_ptr->image_ctl.flags & H5C_CI__SUPRESS_ENTRY_WRITES)
suppress_image_entry_writes = TRUE;
} /* end if */
/* run initial sanity checks */
#if H5C_DO_SANITY_CHECKS
if(entry_ptr->in_slist) {
HDassert(entry_ptr->is_dirty);
if((entry_ptr->flush_marker) && (!entry_ptr->is_dirty))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "entry in slist failed sanity checks")
} /* end if */
else {
HDassert(!entry_ptr->is_dirty);
HDassert(!entry_ptr->flush_marker);
if((entry_ptr->is_dirty) || (entry_ptr->flush_marker))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "entry failed sanity checks")
} /* end else */
#endif /* H5C_DO_SANITY_CHECKS */
if(entry_ptr->is_protected) {
HDassert(!entry_ptr->is_protected);
/* Attempt to flush a protected entry -- scream and die. */
HGOTO_ERROR(H5E_CACHE, H5E_PROTECT, FAIL, "Attempt to flush a protected entry")
} /* end if */
/* Set entry_ptr->flush_in_progress = TRUE and set
* entry_ptr->flush_marker = FALSE
*
* We will set flush_in_progress back to FALSE at the end if the
* entry still exists at that point.
*/
entry_ptr->flush_in_progress = TRUE;
entry_ptr->flush_marker = FALSE;
/* Preserve current dirty state for later */
was_dirty = entry_ptr->is_dirty;
/* The entry is dirty, and we are doing a flush, a flush destroy or have
* been requested to generate an image. In those cases, serialize the
* entry.
*/
if(write_entry || generate_image) {
HDassert(entry_ptr->is_dirty);
if(NULL == entry_ptr->image_ptr) {
if(NULL == (entry_ptr->image_ptr = H5MM_malloc(entry_ptr->size + H5C_IMAGE_EXTRA_SPACE)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer")
#if H5C_DO_MEMORY_SANITY_CHECKS
H5MM_memcpy(((uint8_t *)entry_ptr->image_ptr) + entry_ptr->size, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
} /* end if */
if(!(entry_ptr->image_up_to_date)) {
/* Sanity check */
HDassert(!entry_ptr->prefetched);
/* Generate the entry's image */
if(H5C__generate_image(f, cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, FAIL, "can't generate entry's image")
} /* end if ( ! (entry_ptr->image_up_to_date) ) */
} /* end if */
/* Finally, write the image to disk.
*
* Note that if the H5AC__CLASS_SKIP_WRITES flag is set in the
* in the entry's type, we silently skip the write. This
* flag should only be used in test code.
*/
if(write_entry) {
HDassert(entry_ptr->is_dirty);
#if H5C_DO_SANITY_CHECKS
if(cache_ptr->check_write_permitted && !(cache_ptr->write_permitted))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Write when writes are always forbidden!?!?!")
#endif /* H5C_DO_SANITY_CHECKS */
/* Write the image to disk unless the write is suppressed.
*
* This happens if both suppress_image_entry_writes and
* entry_ptr->include_in_image are TRUE, or if the
* H5AC__CLASS_SKIP_WRITES is set in the entry's type. This
* flag should only be used in test code
*/
if((!suppress_image_entry_writes || !entry_ptr->include_in_image)
&& (((entry_ptr->type->flags) & H5C__CLASS_SKIP_WRITES) == 0)) {
H5FD_mem_t mem_type = H5FD_MEM_DEFAULT;
#ifdef H5_HAVE_PARALLEL
if(cache_ptr->coll_write_list) {
if(H5SL_insert(cache_ptr->coll_write_list, entry_ptr, &entry_ptr->addr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTINSERT, FAIL, "unable to insert skip list item")
} /* end if */
else
{
#endif /* H5_HAVE_PARALLEL */
if(entry_ptr->prefetched) {
HDassert(entry_ptr->type->id == H5AC_PREFETCHED_ENTRY_ID);
mem_type = cache_ptr->
class_table_ptr[entry_ptr->prefetch_type_id]->
mem_type;
} /* end if */
else
mem_type = entry_ptr->type->mem_type;
if(H5F_block_write(f, mem_type, entry_ptr->addr, entry_ptr->size, entry_ptr->image_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't write image to file")
#ifdef H5_HAVE_PARALLEL
}
#endif /* H5_HAVE_PARALLEL */
} /* end if */
/* if the entry has a notify callback, notify it that we have
* just flushed the entry.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_AFTER_FLUSH, entry_ptr) < 0 )
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client of entry flush")
} /* if ( write_entry ) */
/* At this point, all pre-serialize and serialize calls have been
* made if it was appropriate to make them. Similarly, the entry
* has been written to disk if desired.
*
* Thus it is now safe to update the cache data structures for the
* flush.
*/
/* start by updating the statistics */
if(clear_only) {
/* only log a clear if the entry was dirty */
if(was_dirty) {
H5C__UPDATE_STATS_FOR_CLEAR(cache_ptr, entry_ptr)
} /* end if */
} else if(write_entry) {
HDassert(was_dirty);
/* only log a flush if we actually wrote to disk */
H5C__UPDATE_STATS_FOR_FLUSH(cache_ptr, entry_ptr)
} /* end else if */
/* Note that the algorithm below is (very) similar to the set of operations
* in H5C_remove_entry() and should be kept in sync with changes
* to that code. - QAK, 2016/11/30
*/
/* Update the cache internal data structures. */
if(destroy) {
/* Sanity checks */
if(take_ownership)
HDassert(!destroy_entry);
else
HDassert(destroy_entry);
HDassert(!entry_ptr->is_pinned);
/* Update stats, while entry is still in the cache */
H5C__UPDATE_STATS_FOR_EVICTION(cache_ptr, entry_ptr, take_ownership)
/* If the entry's type has a 'notify' callback and the entry is about
* to be removed from the cache, send a 'before eviction' notice while
* the entry is still fully integrated in the cache.
*/
if(entry_ptr->type->notify && (entry_ptr->type->notify)(H5C_NOTIFY_ACTION_BEFORE_EVICT, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry to evict")
/* Update the cache internal data structures as appropriate
* for a destroy. Specifically:
*
* 1) Delete it from the index
*
* 2) Delete it from the skip list if requested.
*
* 3) Delete it from the collective read access list.
*
* 4) Update the replacement policy for eviction
*
* 5) Remove it from the tag list for this object
*
* Finally, if the destroy_entry flag is set, discard the
* entry.
*/
H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr, FAIL)
if(entry_ptr->in_slist && del_from_slist_on_destroy)
H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, during_flush)
#ifdef H5_HAVE_PARALLEL
/* Check for collective read access flag */
if(entry_ptr->coll_access) {
entry_ptr->coll_access = FALSE;
H5C__REMOVE_FROM_COLL_LIST(cache_ptr, entry_ptr, FAIL)
} /* end if */
#endif /* H5_HAVE_PARALLEL */
H5C__UPDATE_RP_FOR_EVICTION(cache_ptr, entry_ptr, FAIL)
/* Remove entry from tag list */
if(H5C__untag_entry(cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry from tag list")
/* verify that the entry is no longer part of any flush dependencies */
HDassert(entry_ptr->flush_dep_nparents == 0);
HDassert(entry_ptr->flush_dep_nchildren == 0);
} /* end if */
else {
HDassert(clear_only || write_entry);
HDassert(entry_ptr->is_dirty);
HDassert(entry_ptr->in_slist);
/* We are either doing a flush or a clear.
*
* A clear and a flush are the same from the point of
* view of the replacement policy and the slist.
* Hence no differentiation between them.
*
* JRM -- 7/7/07
*/
H5C__UPDATE_RP_FOR_FLUSH(cache_ptr, entry_ptr, FAIL)
H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, during_flush)
/* mark the entry as clean and update the index for
* entry clean. Also, call the clear callback
* if defined.
*/
entry_ptr->is_dirty = FALSE;
H5C__UPDATE_INDEX_FOR_ENTRY_CLEAN(cache_ptr, entry_ptr);
/* Check for entry changing status and do notifications, etc. */
if(was_dirty) {
/* If the entry's type has a 'notify' callback send a 'entry cleaned'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_CLEANED, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag cleared")
/* Propagate the clean flag up the flush dependency chain if appropriate */
if(entry_ptr->flush_dep_ndirty_children != 0)
HDassert(entry_ptr->flush_dep_ndirty_children == 0);
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_clean(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTMARKCLEAN, FAIL, "Can't propagate flush dep clean flag")
} /* end if */
} /* end else */
/* reset the flush_in progress flag */
entry_ptr->flush_in_progress = FALSE;
/* capture the cache entry address for the log_flush call at the
end before the entry_ptr gets freed */
entry_addr = entry_ptr->addr;
/* Internal cache data structures should now be up to date, and
* consistent with the status of the entry.
*
* Now discard the entry if appropriate.
*/
if(destroy) {
/* Sanity check */
HDassert(0 == entry_ptr->flush_dep_nparents);
/* if both suppress_image_entry_frees and entry_ptr->include_in_image
* are true, simply set entry_ptr->image_ptr to NULL, as we have
* another pointer to the buffer in an instance of H5C_image_entry_t
* in cache_ptr->image_entries.
*
* Otherwise, free the buffer if it exists.
*/
if(suppress_image_entry_frees && entry_ptr->include_in_image)
entry_ptr->image_ptr = NULL;
else if(entry_ptr->image_ptr != NULL)
entry_ptr->image_ptr = H5MM_xfree(entry_ptr->image_ptr);
/* If the entry is not a prefetched entry, verify that the flush
* dependency parents addresses array has been transferred.
*
* If the entry is prefetched, the free_isr routine will dispose of
* the flush dependency parents addresses array if necessary.
*/
if(!entry_ptr->prefetched) {
HDassert(0 == entry_ptr->fd_parent_count);
HDassert(NULL == entry_ptr->fd_parent_addrs);
} /* end if */
/* Check whether we should free the space in the file that
* the entry occupies
*/
if(free_file_space) {
hsize_t fsf_size;
/* Sanity checks */
HDassert(H5F_addr_defined(entry_ptr->addr));
HDassert(!H5F_IS_TMP_ADDR(f, entry_ptr->addr));
#ifndef NDEBUG
{
size_t curr_len;
/* Get the actual image size for the thing again */
entry_ptr->type->image_len((void *)entry_ptr, &curr_len);
HDassert(curr_len == entry_ptr->size);
}
#endif /* NDEBUG */
/* If the file space free size callback is defined, use
* it to get the size of the block of file space to free.
* Otherwise use entry_ptr->size.
*/
if(entry_ptr->type->fsf_size) {
if((entry_ptr->type->fsf_size)((void *)entry_ptr, &fsf_size) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to get file space free size")
} /* end if */
else /* no file space free size callback -- use entry size */
fsf_size = entry_ptr->size;
/* Release the space on disk */
if(H5MF_xfree(f, entry_ptr->type->mem_type, entry_ptr->addr, fsf_size) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFREE, FAIL, "unable to free file space for cache entry")
} /* end if ( free_file_space ) */
/* Reset the pointer to the cache the entry is within. -QAK */
entry_ptr->cache_ptr = NULL;
/* increment entries_removed_counter and set
* last_entry_removed_ptr. As we are likely abuut to
* free the entry, recall that last_entry_removed_ptr
* must NEVER be dereferenced.
*
* Recall that these fields are maintained to allow functions
* that perform scans of lists of entries to detect the
* unexpected removal of entries (via expunge, eviction,
* or take ownership at present), so that they can re-start
* their scans if necessary.
*
* Also check if the entry we are watching for removal is being
* removed (usually the 'next' entry for an iteration) and reset
* it to indicate that it was removed.
*/
cache_ptr->entries_removed_counter++;
cache_ptr->last_entry_removed_ptr = entry_ptr;
if(entry_ptr == cache_ptr->entry_watched_for_removal)
cache_ptr->entry_watched_for_removal = NULL;
/* Check for actually destroying the entry in memory */
/* (As opposed to taking ownership of it) */
if(destroy_entry) {
if(entry_ptr->is_dirty) {
/* Reset dirty flag */
entry_ptr->is_dirty = FALSE;
/* If the entry's type has a 'notify' callback send a 'entry cleaned'
* notice now that the entry is fully integrated into the cache.
*/
if(entry_ptr->type->notify &&
(entry_ptr->type->notify)(H5C_NOTIFY_ACTION_ENTRY_CLEANED, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry dirty flag cleared")
} /* end if */
/* we are about to discard the in core representation --
* set the magic field to bad magic so we can detect a
* freed entry if we see one.
*/
entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC;
/* verify that the image has been freed */
HDassert(entry_ptr->image_ptr == NULL);
if(entry_ptr->type->free_icr((void *)entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "free_icr callback failed")
} /* end if */
else {
HDassert(take_ownership);
/* client is taking ownership of the entry.
* set bad magic here too so the cache will choke
* unless the entry is re-inserted properly
*/
entry_ptr->magic = H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC;
} /* end else */
} /* if (destroy) */
/* Check if we have to update the page buffer with cleared entries
* so it doesn't go out of date
*/
if(update_page_buffer) {
/* Sanity check */
HDassert(!destroy);
HDassert(entry_ptr->image_ptr);
if(f->shared->page_buf && f->shared->page_buf->page_size >= entry_ptr->size)
if(H5PB_update_entry(f->shared->page_buf, entry_ptr->addr, entry_ptr->size, entry_ptr->image_ptr) > 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Failed to update PB with metadata cache")
} /* end if */
if(cache_ptr->log_flush)
if((cache_ptr->log_flush)(cache_ptr, entry_addr, was_dirty, flags) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "log_flush callback failed")
done:
HDassert( ( ret_value != SUCCEED ) || ( destroy_entry ) ||
( ! entry_ptr->flush_in_progress ) );
HDassert( ( ret_value != SUCCEED ) || ( destroy_entry ) ||
( take_ownership ) || ( ! entry_ptr->is_dirty ) );
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__flush_single_entry() */
/*-------------------------------------------------------------------------
*
* Function: H5C__verify_len_eoa
*
* Purpose: Verify that 'len' does not exceed eoa when 'actual' is
* false i.e. 'len" is the initial speculative length from
* get_load_size callback with null image pointer.
* If exceed, adjust 'len' accordingly.
*
* Verify that 'len' should not exceed eoa when 'actual' is
* true i.e. 'len' is the actual length from get_load_size
* callback with non-null image pointer.
* If exceed, return error.
*
* Return: FAIL if error is detected, SUCCEED otherwise.
*
* Programmer: Vailin Choi
* 9/6/15
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__verify_len_eoa(H5F_t *f, const H5C_class_t *type, haddr_t addr,
size_t *len, hbool_t actual)
{
H5FD_mem_t cooked_type; /* Modified type, accounting for switching global heaps */
haddr_t eoa; /* End-of-allocation in the file */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* if type == H5FD_MEM_GHEAP, H5F_block_read() forces
* type to H5FD_MEM_DRAW via its call to H5F__accum_read().
* Thus we do the same for purposes of computing the EOA
* for sanity checks.
*/
cooked_type = (type->mem_type == H5FD_MEM_GHEAP) ? H5FD_MEM_DRAW : type->mem_type;
/* Get the file's end-of-allocation value */
eoa = H5F_get_eoa(f, cooked_type);
if(!H5F_addr_defined(eoa))
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "invalid EOA address for file")
/* Check for bad address in general */
if(H5F_addr_gt(addr, eoa))
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "address of object past end of allocation")
/* Check if the amount of data to read will be past the EOA */
if(H5F_addr_gt((addr + *len), eoa)) {
if(actual)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "actual len exceeds EOA")
else
/* Trim down the length of the metadata */
*len = (size_t)(eoa - addr);
} /* end if */
if(*len <= 0)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, FAIL, "len not positive after adjustment for EOA")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__verify_len_eoa() */
/*-------------------------------------------------------------------------
*
* Function: H5C_load_entry
*
* Purpose: Attempt to load the entry at the specified disk address
* and with the specified type into memory. If successful.
* return the in memory address of the entry. Return NULL
* on failure.
*
* Note that this function simply loads the entry into
* core. It does not insert it into the cache.
*
* Return: Non-NULL on success / NULL on failure.
*
* Programmer: John Mainzer, 5/18/04
*
*-------------------------------------------------------------------------
*/
static void *
H5C_load_entry(H5F_t * f,
#ifdef H5_HAVE_PARALLEL
hbool_t coll_access,
#endif /* H5_HAVE_PARALLEL */
const H5C_class_t * type,
haddr_t addr,
void * udata)
{
hbool_t dirty = FALSE; /* Flag indicating whether thing was dirtied during deserialize */
uint8_t * image = NULL; /* Buffer for disk image */
void * thing = NULL; /* Pointer to thing loaded */
H5C_cache_entry_t *entry = NULL; /* Alias for thing loaded, as cache entry */
size_t len; /* Size of image in file */
#ifdef H5_HAVE_PARALLEL
int mpi_rank = 0; /* MPI process rank */
MPI_Comm comm = MPI_COMM_NULL; /* File MPI Communicator */
int mpi_code; /* MPI error code */
#endif /* H5_HAVE_PARALLEL */
void * ret_value = NULL; /* Return value */
FUNC_ENTER_NOAPI_NOINIT
/* Sanity checks */
HDassert(f);
HDassert(f->shared);
HDassert(f->shared->cache);
HDassert(type);
HDassert(H5F_addr_defined(addr));
HDassert(type->get_initial_load_size);
if(type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG)
HDassert(type->get_final_load_size);
else
HDassert(NULL == type->get_final_load_size);
HDassert(type->deserialize);
/* Can't see how skip reads could be usefully combined with
* the speculative read flag. Hence disallow.
*/
HDassert(!((type->flags & H5C__CLASS_SKIP_READS) &&
(type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG)));
/* Call the get_initial_load_size callback, to retrieve the initial size of image */
if(type->get_initial_load_size(udata, &len) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, NULL, "can't retrieve image size")
HDassert(len > 0);
/* Check for possible speculative read off the end of the file */
if(type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG)
if(H5C__verify_len_eoa(f, type, addr, &len, FALSE) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "invalid len with respect to EOA")
/* Allocate the buffer for reading the on-disk entry image */
if(NULL == (image = (uint8_t *)H5MM_malloc(len + H5C_IMAGE_EXTRA_SPACE)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "memory allocation failed for on disk image buffer")
#if H5C_DO_MEMORY_SANITY_CHECKS
H5MM_memcpy(image + len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
#ifdef H5_HAVE_PARALLEL
if(H5F_HAS_FEATURE(f, H5FD_FEAT_HAS_MPI)) {
if((mpi_rank = H5F_mpi_get_rank(f)) < 0)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "Can't get MPI rank")
if((comm = H5F_mpi_get_comm(f)) == MPI_COMM_NULL)
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "get_comm request failed")
} /* end if */
#endif /* H5_HAVE_PARALLEL */
/* Get the on-disk entry image */
if(0 == (type->flags & H5C__CLASS_SKIP_READS)) {
unsigned tries, max_tries; /* The # of read attempts */
unsigned retries; /* The # of retries */
htri_t chk_ret; /* return from verify_chksum callback */
size_t actual_len = len; /* The actual length, after speculative reads have been resolved */
uint64_t nanosec = 1; /* # of nanoseconds to sleep between retries */
void *new_image; /* Pointer to image */
hbool_t len_changed = TRUE; /* Whether to re-check speculative entries */
/* Get the # of read attempts */
max_tries = tries = H5F_GET_READ_ATTEMPTS(f);
/*
* This do/while loop performs the following till the metadata checksum
* is correct or the file's number of allowed read attempts are reached.
* --read the metadata
* --determine the actual size of the metadata
* --perform checksum verification
*/
do {
if(actual_len != len) {
if(NULL == (new_image = H5MM_realloc(image, len + H5C_IMAGE_EXTRA_SPACE)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "image null after H5MM_realloc()")
image = (uint8_t *)new_image;
#if H5C_DO_MEMORY_SANITY_CHECKS
H5MM_memcpy(image + len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
} /* end if */
#ifdef H5_HAVE_PARALLEL
if(!coll_access || 0 == mpi_rank) {
#endif /* H5_HAVE_PARALLEL */
if(H5F_block_read(f, type->mem_type, addr, len, image) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_READERROR, NULL, "Can't read image*")
#ifdef H5_HAVE_PARALLEL
} /* end if */
/* if the collective metadata read optimization is turned on,
* bcast the metadata read from process 0 to all ranks in the file
* communicator
*/
if(coll_access) {
int buf_size;
H5_CHECKED_ASSIGN(buf_size, int, len, size_t);
if(MPI_SUCCESS != (mpi_code = MPI_Bcast(image, buf_size, MPI_BYTE, 0, comm)))
HMPI_GOTO_ERROR(NULL, "MPI_Bcast failed", mpi_code)
} /* end if */
#endif /* H5_HAVE_PARALLEL */
/* If the entry could be read speculatively and the length is still
* changing, check for updating the actual size
*/
if((type->flags & H5C__CLASS_SPECULATIVE_LOAD_FLAG) && len_changed) {
/* Retrieve the actual length */
actual_len = len;
if(type->get_final_load_size(image, len, udata, &actual_len) < 0)
continue; /* Transfer control to while() and count towards retries */
/* Check for the length changing */
if(actual_len != len) {
/* Verify that the length isn't past the EOA for the file */
if(H5C__verify_len_eoa(f, type, addr, &actual_len, TRUE) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "actual_len exceeds EOA")
/* Expand buffer to new size */
if(NULL == (new_image = H5MM_realloc(image, actual_len + H5C_IMAGE_EXTRA_SPACE)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, NULL, "image null after H5MM_realloc()")
image = (uint8_t *)new_image;
#if H5C_DO_MEMORY_SANITY_CHECKS
H5MM_memcpy(image + actual_len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
if(actual_len > len) {
#ifdef H5_HAVE_PARALLEL
if(!coll_access || 0 == mpi_rank) {
#endif /* H5_HAVE_PARALLEL */
/* If the thing's image needs to be bigger for a speculatively
* loaded thing, go get the on-disk image again (the extra portion).
*/
if(H5F_block_read(f, type->mem_type, addr + len, actual_len - len, image + len) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "can't read image")
#ifdef H5_HAVE_PARALLEL
}
/* If the collective metadata read optimization is turned on,
* Bcast the metadata read from process 0 to all ranks in the file
* communicator */
if(coll_access) {
int buf_size;
H5_CHECKED_ASSIGN(buf_size, int, actual_len - len, size_t);
if(MPI_SUCCESS != (mpi_code = MPI_Bcast(image + len, buf_size, MPI_BYTE, 0, comm)))
HMPI_GOTO_ERROR(NULL, "MPI_Bcast failed", mpi_code)
} /* end if */
#endif /* H5_HAVE_PARALLEL */
} /* end if */
} /* end if (actual_len != len) */
else {
/* The length has stabilized */
len_changed = FALSE;
/* Set the final length */
len = actual_len;
} /* else */
} /* end if */
/* If there's no way to verify the checksum for a piece of metadata
* (usually because there's no checksum in the file), leave now
*/
if(type->verify_chksum == NULL)
break;
/* Verify the checksum for the metadata image */
if((chk_ret = type->verify_chksum(image, actual_len, udata)) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTGET, NULL, "failure from verify_chksum callback")
if(chk_ret == TRUE)
break;
/* Sleep for some time */
H5_nanosleep(nanosec);
nanosec *= 2; /* Double the sleep time next time */
} while(--tries);
/* Check for too many tries */
if(tries == 0)
HGOTO_ERROR(H5E_CACHE, H5E_READERROR, NULL, "incorrect metadatda checksum after all read attempts")
/* Calculate and track the # of retries */
retries = max_tries - tries;
if(retries) /* Does not track 0 retry */
if(H5F_track_metadata_read_retries(f, (unsigned)type->mem_type, retries) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_BADVALUE, NULL, "cannot track read tries = %u ", retries)
/* Set the final length (in case it wasn't set earlier) */
len = actual_len;
} /* end if !H5C__CLASS_SKIP_READS */
/* Deserialize the on-disk image into the native memory form */
if(NULL == (thing = type->deserialize(image, len, udata, &dirty)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTLOAD, NULL, "Can't deserialize image")
entry = (H5C_cache_entry_t *)thing;
/* In general, an entry should be clean just after it is loaded.
*
* However, when this code is used in the metadata cache, it is
* possible that object headers will be dirty at this point, as
* the deserialize function will alter object headers if necessary to
* fix an old bug.
*
* In the following assert:
*
* HDassert( ( dirty == FALSE ) || ( type->id == 5 || type->id == 6 ) );
*
* note that type ids 5 & 6 are associated with object headers in the
* metadata cache.
*
* When we get to using H5C for other purposes, we may wish to
* tighten up the assert so that the loophole only applies to the
* metadata cache.
*/
HDassert( ( dirty == FALSE ) || ( type->id == 5 || type->id == 6) );
entry->magic = H5C__H5C_CACHE_ENTRY_T_MAGIC;
entry->cache_ptr = f->shared->cache;
entry->addr = addr;
entry->size = len;
HDassert(entry->size < H5C_MAX_ENTRY_SIZE);
entry->image_ptr = image;
entry->image_up_to_date = !dirty;
entry->type = type;
entry->is_dirty = dirty;
entry->dirtied = FALSE;
entry->is_protected = FALSE;
entry->is_read_only = FALSE;
entry->ro_ref_count = 0;
entry->is_pinned = FALSE;
entry->in_slist = FALSE;
entry->flush_marker = FALSE;
#ifdef H5_HAVE_PARALLEL
entry->clear_on_unprotect = FALSE;
entry->flush_immediately = FALSE;
entry->coll_access = coll_access;
#endif /* H5_HAVE_PARALLEL */
entry->flush_in_progress = FALSE;
entry->destroy_in_progress = FALSE;
entry->ring = H5C_RING_UNDEFINED;
/* Initialize flush dependency fields */
entry->flush_dep_parent = NULL;
entry->flush_dep_nparents = 0;
entry->flush_dep_parent_nalloc = 0;
entry->flush_dep_nchildren = 0;
entry->flush_dep_ndirty_children = 0;
entry->flush_dep_nunser_children = 0;
entry->ht_next = NULL;
entry->ht_prev = NULL;
entry->il_next = NULL;
entry->il_prev = NULL;
entry->next = NULL;
entry->prev = NULL;
#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
entry->aux_next = NULL;
entry->aux_prev = NULL;
#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
#ifdef H5_HAVE_PARALLEL
entry->coll_next = NULL;
entry->coll_prev = NULL;
#endif /* H5_HAVE_PARALLEL */
/* initialize cache image related fields */
entry->include_in_image = FALSE;
entry->lru_rank = 0;
entry->image_dirty = FALSE;
entry->fd_parent_count = 0;
entry->fd_parent_addrs = NULL;
entry->fd_child_count = 0;
entry->fd_dirty_child_count = 0;
entry->image_fd_height = 0;
entry->prefetched = FALSE;
entry->prefetch_type_id = 0;
entry->age = 0;
entry->prefetched_dirty = FALSE;
#ifndef NDEBUG /* debugging field */
entry->serialization_count = 0;
#endif /* NDEBUG */
entry->tl_next = NULL;
entry->tl_prev = NULL;
entry->tag_info = NULL;
H5C__RESET_CACHE_ENTRY_STATS(entry);
ret_value = thing;
done:
/* Cleanup on error */
if(NULL == ret_value) {
/* Release resources */
if(thing && type->free_icr(thing) < 0)
HDONE_ERROR(H5E_CACHE, H5E_CANTFLUSH, NULL, "free_icr callback failed")
if(image)
image = (uint8_t *)H5MM_xfree(image);
} /* end if */
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_load_entry() */
/*-------------------------------------------------------------------------
*
* Function: H5C__make_space_in_cache
*
* Purpose: Attempt to evict cache entries until the index_size
* is at least needed_space below max_cache_size.
*
* In passing, also attempt to bring cLRU_list_size to a
* value greater than min_clean_size.
*
* Depending on circumstances, both of these goals may
* be impossible, as in parallel mode, we must avoid generating
* a write as part of a read (to avoid deadlock in collective
* I/O), and in all cases, it is possible (though hopefully
* highly unlikely) that the protected list may exceed the
* maximum size of the cache.
*
* Thus the function simply does its best, returning success
* unless an error is encountered.
*
* Observe that this function cannot occasion a read.
*
* Return: Non-negative on success/Negative on failure.
*
* Programmer: John Mainzer, 5/14/04
*
*-------------------------------------------------------------------------
*/
herr_t
H5C__make_space_in_cache(H5F_t *f, size_t space_needed, hbool_t write_permitted)
{
H5C_t * cache_ptr = f->shared->cache;
#if H5C_COLLECT_CACHE_STATS
int32_t clean_entries_skipped = 0;
int32_t dirty_pf_entries_skipped = 0;
int32_t total_entries_scanned = 0;
#endif /* H5C_COLLECT_CACHE_STATS */
uint32_t entries_examined = 0;
uint32_t initial_list_len;
size_t empty_space;
hbool_t reentrant_call = FALSE;
hbool_t prev_is_dirty = FALSE;
hbool_t didnt_flush_entry = FALSE;
hbool_t restart_scan;
H5C_cache_entry_t * entry_ptr;
H5C_cache_entry_t * prev_ptr;
H5C_cache_entry_t * next_ptr;
uint32_t num_corked_entries = 0;
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_PACKAGE
/* Sanity checks */
HDassert(f);
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr->index_size == (cache_ptr->clean_index_size + cache_ptr->dirty_index_size));
/* check to see if cache_ptr->msic_in_progress is TRUE. If it, this
* is a re-entrant call via a client callback called in the make
* space in cache process. To avoid an infinite recursion, set
* reentrant_call to TRUE, and goto done.
*/
if(cache_ptr->msic_in_progress) {
reentrant_call = TRUE;
HGOTO_DONE(SUCCEED);
} /* end if */
cache_ptr->msic_in_progress = TRUE;
if ( write_permitted ) {
restart_scan = FALSE;
initial_list_len = cache_ptr->LRU_list_len;
entry_ptr = cache_ptr->LRU_tail_ptr;
if(cache_ptr->index_size >= cache_ptr->max_cache_size)
empty_space = 0;
else
empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
while ( ( ( (cache_ptr->index_size + space_needed)
>
cache_ptr->max_cache_size
)
||
(
( empty_space + cache_ptr->clean_index_size )
<
( cache_ptr->min_clean_size )
)
)
&&
( entries_examined <= (2 * initial_list_len) )
&&
( entry_ptr != NULL )
)
{
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert( !(entry_ptr->is_protected) );
HDassert( ! (entry_ptr->is_read_only) );
HDassert( (entry_ptr->ro_ref_count) == 0 );
next_ptr = entry_ptr->next;
prev_ptr = entry_ptr->prev;
if(prev_ptr != NULL)
prev_is_dirty = prev_ptr->is_dirty;
if(entry_ptr->is_dirty &&
(entry_ptr->tag_info && entry_ptr->tag_info->corked)) {
/* Skip "dirty" corked entries. */
++num_corked_entries;
didnt_flush_entry = TRUE;
} else if ( ( (entry_ptr->type)->id != H5AC_EPOCH_MARKER_ID ) &&
( ! entry_ptr->flush_in_progress ) &&
( ! entry_ptr->prefetched_dirty ) ) {
didnt_flush_entry = FALSE;
if ( entry_ptr->is_dirty ) {
#if H5C_COLLECT_CACHE_STATS
if ( (cache_ptr->index_size + space_needed)
>
cache_ptr->max_cache_size ) {
cache_ptr->entries_scanned_to_make_space++;
}
#endif /* H5C_COLLECT_CACHE_STATS */
/* reset entries_removed_counter and
* last_entry_removed_ptr prior to the call to
* H5C__flush_single_entry() so that we can spot
* unexpected removals of entries from the cache,
* and set the restart_scan flag if proceeding
* would be likely to cause us to scan an entry
* that is no longer in the cache.
*/
cache_ptr->entries_removed_counter = 0;
cache_ptr->last_entry_removed_ptr = NULL;
if(H5C__flush_single_entry(f, entry_ptr, H5C__NO_FLAGS_SET) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry")
if ( ( cache_ptr->entries_removed_counter > 1 ) ||
( cache_ptr->last_entry_removed_ptr == prev_ptr ) )
restart_scan = TRUE;
} else if ( (cache_ptr->index_size + space_needed) > cache_ptr->max_cache_size
#ifdef H5_HAVE_PARALLEL
&& !(entry_ptr->coll_access)
#endif /* H5_HAVE_PARALLEL */
) {
#if H5C_COLLECT_CACHE_STATS
cache_ptr->entries_scanned_to_make_space++;
#endif /* H5C_COLLECT_CACHE_STATS */
if(H5C__flush_single_entry(f, entry_ptr, H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry")
} else {
/* We have enough space so don't flush clean entry. */
#if H5C_COLLECT_CACHE_STATS
clean_entries_skipped++;
#endif /* H5C_COLLECT_CACHE_STATS */
didnt_flush_entry = TRUE;
}
#if H5C_COLLECT_CACHE_STATS
total_entries_scanned++;
#endif /* H5C_COLLECT_CACHE_STATS */
} else {
/* Skip epoch markers, entries that are in the process
* of being flushed, and entries marked as prefetched_dirty
* (occurs in the R/O case only).
*/
didnt_flush_entry = TRUE;
#if H5C_COLLECT_CACHE_STATS
if(entry_ptr->prefetched_dirty)
dirty_pf_entries_skipped++;
#endif /* H5C_COLLECT_CACHE_STATS */
}
if ( prev_ptr != NULL ) {
if ( didnt_flush_entry ) {
/* epoch markers don't get flushed, and we don't touch
* entries that are in the process of being flushed.
* Hence no need for sanity checks, as we haven't
* flushed anything. Thus just set entry_ptr to prev_ptr
* and go on.
*/
entry_ptr = prev_ptr;
} else if ( ( restart_scan )
||
( prev_ptr->is_dirty != prev_is_dirty )
||
( prev_ptr->next != next_ptr )
||
( prev_ptr->is_protected )
||
( prev_ptr->is_pinned ) ) {
/* something has happened to the LRU -- start over
* from the tail.
*/
restart_scan = FALSE;
entry_ptr = cache_ptr->LRU_tail_ptr;
H5C__UPDATE_STATS_FOR_LRU_SCAN_RESTART(cache_ptr)
} else {
entry_ptr = prev_ptr;
}
} else {
entry_ptr = NULL;
}
entries_examined++;
if ( cache_ptr->index_size >= cache_ptr->max_cache_size ) {
empty_space = 0;
} else {
empty_space = cache_ptr->max_cache_size - cache_ptr->index_size;
}
HDassert( cache_ptr->index_size ==
(cache_ptr->clean_index_size +
cache_ptr->dirty_index_size) );
}
#if H5C_COLLECT_CACHE_STATS
cache_ptr->calls_to_msic++;
cache_ptr->total_entries_skipped_in_msic += clean_entries_skipped;
cache_ptr->total_dirty_pf_entries_skipped_in_msic += dirty_pf_entries_skipped;
cache_ptr->total_entries_scanned_in_msic += total_entries_scanned;
if ( clean_entries_skipped > cache_ptr->max_entries_skipped_in_msic ) {
cache_ptr->max_entries_skipped_in_msic = clean_entries_skipped;
}
if(dirty_pf_entries_skipped > cache_ptr->max_dirty_pf_entries_skipped_in_msic)
cache_ptr->max_dirty_pf_entries_skipped_in_msic = dirty_pf_entries_skipped;
if ( total_entries_scanned > cache_ptr->max_entries_scanned_in_msic ) {
cache_ptr->max_entries_scanned_in_msic = total_entries_scanned;
}
#endif /* H5C_COLLECT_CACHE_STATS */
/* NEED: work on a better assert for corked entries */
HDassert( ( entries_examined > (2 * initial_list_len) ) ||
( (cache_ptr->pl_size + cache_ptr->pel_size + cache_ptr->min_clean_size) >
cache_ptr->max_cache_size ) ||
( ( cache_ptr->clean_index_size + empty_space )
>= cache_ptr->min_clean_size ) ||
( ( num_corked_entries )));
#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
HDassert( ( entries_examined > (2 * initial_list_len) ) ||
( cache_ptr->cLRU_list_size <= cache_ptr->clean_index_size ) );
HDassert( ( entries_examined > (2 * initial_list_len) ) ||
( cache_ptr->dLRU_list_size <= cache_ptr->dirty_index_size ) );
#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
} else {
HDassert( H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS );
#if H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS
initial_list_len = cache_ptr->cLRU_list_len;
entry_ptr = cache_ptr->cLRU_tail_ptr;
while ( ( (cache_ptr->index_size + space_needed)
>
cache_ptr->max_cache_size
)
&&
( entries_examined <= initial_list_len )
&&
( entry_ptr != NULL )
)
{
HDassert( ! (entry_ptr->is_protected) );
HDassert( ! (entry_ptr->is_read_only) );
HDassert( (entry_ptr->ro_ref_count) == 0 );
HDassert( ! (entry_ptr->is_dirty) );
prev_ptr = entry_ptr->aux_prev;
if ( ( !(entry_ptr->prefetched_dirty) )
#ifdef H5_HAVE_PARALLEL
&& ( ! (entry_ptr->coll_access) )
#endif /* H5_HAVE_PARALLEL */
) {
if(H5C__flush_single_entry(f, entry_ptr,
H5C__FLUSH_INVALIDATE_FLAG | H5C__DEL_FROM_SLIST_ON_DESTROY_FLAG) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to flush entry")
} /* end if */
/* we are scanning the clean LRU, so the serialize function
* will not be called on any entry -- thus there is no
* concern about the list being modified out from under
* this function.
*/
entry_ptr = prev_ptr;
entries_examined++;
}
#endif /* H5C_MAINTAIN_CLEAN_AND_DIRTY_LRU_LISTS */
}
done:
/* Sanity checks */
HDassert(cache_ptr->msic_in_progress);
if(!reentrant_call)
cache_ptr->msic_in_progress = FALSE;
HDassert((!reentrant_call) || (cache_ptr->msic_in_progress));
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__make_space_in_cache() */
/*-------------------------------------------------------------------------
*
* Function: H5C_validate_lru_list
*
* Purpose: Debugging function that scans the LRU 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, 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
static herr_t
H5C_validate_lru_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->LRU_head_ptr == NULL )
||
( cache_ptr->LRU_tail_ptr == NULL )
)
&&
( cache_ptr->LRU_head_ptr != cache_ptr->LRU_tail_ptr )
) {
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 1 failed")
}
if(cache_ptr->LRU_list_len < 0)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 2 failed")
if ( ( cache_ptr->LRU_list_len == 1 )
&&
( ( cache_ptr->LRU_head_ptr != cache_ptr->LRU_tail_ptr )
||
( cache_ptr->LRU_head_ptr == NULL )
||
( cache_ptr->LRU_head_ptr->size != cache_ptr->LRU_list_size )
)
) {
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 3 failed")
}
if ( ( cache_ptr->LRU_list_len >= 1 )
&&
( ( cache_ptr->LRU_head_ptr == NULL )
||
( cache_ptr->LRU_head_ptr->prev != NULL )
||
( cache_ptr->LRU_tail_ptr == NULL )
||
( cache_ptr->LRU_tail_ptr->next != NULL )
)
) {
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 4 failed")
}
entry_ptr = cache_ptr->LRU_head_ptr;
while ( entry_ptr != NULL )
{
if ( ( entry_ptr != cache_ptr->LRU_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->LRU_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 ) ||
( 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;
}
if ( ( cache_ptr->LRU_list_len != len ) ||
( cache_ptr->LRU_list_size != size ) ) {
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Check 8 failed")
}
done:
if ( ret_value != SUCCEED ) {
HDassert(0);
}
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_validate_lru_list() */
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/*-------------------------------------------------------------------------
*
* Function: H5C_validate_pinned_entry_list
*
* 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,
* 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_pinned_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->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)
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 )
{
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, "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)
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:
if ( ret_value != SUCCEED ) {
HDassert(0);
}
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_validate_protected_entry_list() */
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
/*-------------------------------------------------------------------------
*
* Function: H5C_entry_in_skip_list
*
* Purpose: Debugging function that scans skip list to see if it
* is in present. We need this, as it is possible for
* an entry to be in the skip list twice.
*
* Return: FALSE if the entry is not in the skip list, and TRUE
* if it is.
*
* Programmer: John Mainzer, 11/1/14
*
* Changes:
*
* None.
*
*-------------------------------------------------------------------------
*/
#if H5C_DO_SLIST_SANITY_CHECKS
static hbool_t
H5C_entry_in_skip_list(H5C_t * cache_ptr, H5C_cache_entry_t *target_ptr)
{
hbool_t in_slist = FALSE;
H5SL_node_t * node_ptr = NULL;
H5C_cache_entry_t * entry_ptr = NULL;
HDassert( cache_ptr );
HDassert( cache_ptr->magic == H5C__H5C_T_MAGIC );
HDassert( cache_ptr->slist_ptr );
node_ptr = H5SL_first(cache_ptr->slist_ptr);
while ( ( node_ptr != NULL ) && ( ! in_slist ) )
{
entry_ptr = (H5C_cache_entry_t *)H5SL_item(node_ptr);
HDassert( entry_ptr );
HDassert( entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC );
HDassert( entry_ptr->is_dirty );
HDassert( entry_ptr->in_slist );
if ( entry_ptr == target_ptr ) {
in_slist = TRUE;
} else {
node_ptr = H5SL_next(node_ptr);
}
}
return(in_slist);
} /* H5C_entry_in_skip_list() */
#endif /* H5C_DO_SLIST_SANITY_CHECKS */
/*-------------------------------------------------------------------------
*
* Function: H5C__flush_marked_entries
*
* Purpose: Flushes all marked entries in the cache.
*
* Return: FAIL if error is detected, SUCCEED otherwise.
*
* Programmer: Mike McGreevy
* November 3, 2010
*
*-------------------------------------------------------------------------
*/
herr_t
H5C__flush_marked_entries(H5F_t * f)
{
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE
/* Assertions */
HDassert(f != NULL);
/* Flush all marked entries */
if(H5C_flush_cache(f, H5C__FLUSH_MARKED_ENTRIES_FLAG | H5C__FLUSH_IGNORE_PROTECTED_FLAG) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "Can't flush cache")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__flush_marked_entries */
/*-------------------------------------------------------------------------
*
* Function: H5C_cork
*
* Purpose: To cork/uncork/get cork status of an object depending on "action":
* H5C__SET_CORK:
* To cork the object
* Return error if the object is already corked
* H5C__UNCORK:
* To uncork the obejct
* Return error if the object is not corked
* H5C__GET_CORKED:
* To retrieve the cork status of an object in
* the parameter "corked"
*
* Return: Success: Non-negative
* Failure: Negative
*
* Programmer: Vailin Choi
* January 2014
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_cork(H5C_t *cache_ptr, haddr_t obj_addr, unsigned action, hbool_t *corked)
{
H5C_tag_info_t *tag_info; /* Points to a tag info struct */
herr_t ret_value = SUCCEED;
FUNC_ENTER_NOAPI_NOINIT
/* Assertions */
HDassert(cache_ptr != NULL);
HDassert(H5F_addr_defined(obj_addr));
HDassert(action == H5C__SET_CORK || action == H5C__UNCORK || action == H5C__GET_CORKED);
/* Search the list of corked object addresses in the cache */
tag_info = (H5C_tag_info_t *)H5SL_search(cache_ptr->tag_list, &obj_addr);
if(H5C__GET_CORKED == action) {
HDassert(corked);
if(tag_info != NULL && tag_info->corked)
*corked = TRUE;
else
*corked = FALSE;
} /* end if */
else {
/* Sanity check */
HDassert(H5C__SET_CORK == action || H5C__UNCORK == action);
/* Perform appropriate action */
if(H5C__SET_CORK == action) {
/* Check if this is the first entry for this tagged object */
if(NULL == tag_info) {
/* Allocate new tag info struct */
if(NULL == (tag_info = H5FL_CALLOC(H5C_tag_info_t)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "can't allocate tag info for cache entry")
/* Set the tag for all entries */
tag_info->tag = obj_addr;
/* Insert tag info into skip list */
if(H5SL_insert(cache_ptr->tag_list, tag_info, &(tag_info->tag)) < 0 )
HGOTO_ERROR(H5E_CACHE, H5E_CANTINSERT, FAIL, "can't insert tag info in skip list")
} /* end if */
else {
/* Check for object already corked */
if(tag_info->corked)
HGOTO_ERROR(H5E_CACHE, H5E_CANTCORK, FAIL, "object already corked")
HDassert(tag_info->entry_cnt > 0 && tag_info->head);
} /* end else */
/* Set the corked status for the entire object */
tag_info->corked = TRUE;
cache_ptr->num_objs_corked++;
} /* end if */
else {
/* Sanity check */
HDassert(tag_info);
/* Check for already uncorked */
if(!tag_info->corked)
HGOTO_ERROR(H5E_CACHE, H5E_CANTUNCORK, FAIL, "object already uncorked")
/* Set the corked status for the entire object */
tag_info->corked = FALSE;
cache_ptr->num_objs_corked--;
/* Remove the tag info from the tag list, if there's no more entries with this tag */
if(0 == tag_info->entry_cnt) {
/* Sanity check */
HDassert(NULL == tag_info->head);
if(H5SL_remove(cache_ptr->tag_list, &(tag_info->tag)) != tag_info)
HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove tag info from list")
/* Release the tag info */
tag_info = H5FL_FREE(H5C_tag_info_t, tag_info);
} /* end if */
else
HDassert(NULL != tag_info->head);
} /* end else */
} /* end else */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C_cork() */
/*-------------------------------------------------------------------------
* Function: H5C__mark_flush_dep_dirty()
*
* Purpose: Recursively propagate the flush_dep_ndirty_children flag
* up the dependency chain in response to entry either
* becoming dirty or having its flush_dep_ndirty_children
* increased from 0.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Neil Fortner
* 11/13/12
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__mark_flush_dep_dirty(H5C_cache_entry_t * entry)
{
unsigned u; /* Local index variable */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* Sanity checks */
HDassert(entry);
/* Iterate over the parent entries, if any */
for(u = 0; u < entry->flush_dep_nparents; u++) {
/* Sanity check */
HDassert(entry->flush_dep_parent[u]->flush_dep_ndirty_children < entry->flush_dep_parent[u]->flush_dep_nchildren);
/* Adjust the parent's number of dirty children */
entry->flush_dep_parent[u]->flush_dep_ndirty_children++;
/* If the parent has a 'notify' callback, send a 'child entry dirtied' notice */
if(entry->flush_dep_parent[u]->type->notify &&
(entry->flush_dep_parent[u]->type->notify)(H5C_NOTIFY_ACTION_CHILD_DIRTIED, entry->flush_dep_parent[u]) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify parent about child entry dirty flag set")
} /* end for */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__mark_flush_dep_dirty() */
/*-------------------------------------------------------------------------
* Function: H5C__mark_flush_dep_clean()
*
* Purpose: Recursively propagate the flush_dep_ndirty_children flag
* up the dependency chain in response to entry either
* becoming clean or having its flush_dep_ndirty_children
* reduced to 0.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Neil Fortner
* 11/13/12
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__mark_flush_dep_clean(H5C_cache_entry_t * entry)
{
int i; /* Local index variable */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* Sanity checks */
HDassert(entry);
/* Iterate over the parent entries, if any */
/* Note reverse iteration order, in case the callback removes the flush
* dependency - QAK, 2017/08/12
*/
for(i = ((int)entry->flush_dep_nparents) - 1; i >= 0; i--) {
/* Sanity check */
HDassert(entry->flush_dep_parent[i]->flush_dep_ndirty_children > 0);
/* Adjust the parent's number of dirty children */
entry->flush_dep_parent[i]->flush_dep_ndirty_children--;
/* If the parent has a 'notify' callback, send a 'child entry cleaned' notice */
if(entry->flush_dep_parent[i]->type->notify &&
(entry->flush_dep_parent[i]->type->notify)(H5C_NOTIFY_ACTION_CHILD_CLEANED, entry->flush_dep_parent[i]) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify parent about child entry dirty flag reset")
} /* end for */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__mark_flush_dep_clean() */
/*-------------------------------------------------------------------------
* Function: H5C__mark_flush_dep_serialized()
*
* Purpose: Decrement the flush_dep_nunser_children fields of all the
* target entry's flush dependency parents in response to
* the target entry becoming serialized.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 8/30/16
*
*-------------------------------------------------------------------------
*/
herr_t
H5C__mark_flush_dep_serialized(H5C_cache_entry_t * entry_ptr)
{
int i; /* Local index variable */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* Sanity checks */
HDassert(entry_ptr);
/* Iterate over the parent entries, if any */
/* Note reverse iteration order, in case the callback removes the flush
* dependency - QAK, 2017/08/12
*/
for(i = ((int)entry_ptr->flush_dep_nparents) - 1; i >= 0; i--) {
/* Sanity checks */
HDassert(entry_ptr->flush_dep_parent);
HDassert(entry_ptr->flush_dep_parent[i]->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->flush_dep_parent[i]->flush_dep_nunser_children > 0);
/* decrement the parents number of unserialized children */
entry_ptr->flush_dep_parent[i]->flush_dep_nunser_children--;
/* If the parent has a 'notify' callback, send a 'child entry serialized' notice */
if(entry_ptr->flush_dep_parent[i]->type->notify &&
(entry_ptr->flush_dep_parent[i]->type->notify)(H5C_NOTIFY_ACTION_CHILD_SERIALIZED, entry_ptr->flush_dep_parent[i]) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify parent about child entry serialized flag set")
} /* end for */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__mark_flush_dep_serialized() */
/*-------------------------------------------------------------------------
* Function: H5C__mark_flush_dep_unserialized()
*
* Purpose: Increment the flush_dep_nunser_children fields of all the
* target entry's flush dependency parents in response to
* the target entry becoming unserialized.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer
* 8/30/16
*
*-------------------------------------------------------------------------
*/
herr_t
H5C__mark_flush_dep_unserialized(H5C_cache_entry_t * entry_ptr)
{
unsigned u; /* Local index variable */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* Sanity checks */
HDassert(entry_ptr);
/* Iterate over the parent entries, if any */
for(u = 0; u < entry_ptr->flush_dep_nparents; u++) {
/* Sanity check */
HDassert(entry_ptr->flush_dep_parent);
HDassert(entry_ptr->flush_dep_parent[u]->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->flush_dep_parent[u]->flush_dep_nunser_children <
entry_ptr->flush_dep_parent[u]->flush_dep_nchildren);
/* increment parents number of usserialized children */
entry_ptr->flush_dep_parent[u]->flush_dep_nunser_children++;
/* If the parent has a 'notify' callback, send a 'child entry unserialized' notice */
if(entry_ptr->flush_dep_parent[u]->type->notify &&
(entry_ptr->flush_dep_parent[u]->type->notify)(H5C_NOTIFY_ACTION_CHILD_UNSERIALIZED, entry_ptr->flush_dep_parent[u]) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify parent about child entry serialized flag reset")
} /* end for */
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__mark_flush_dep_unserialized() */
#ifndef NDEBUG
/*-------------------------------------------------------------------------
* Function: H5C__assert_flush_dep_nocycle()
*
* Purpose: Assert recursively that base_entry is not the same as
* entry, and perform the same assertion on all of entry's
* flush dependency parents. This is used to detect cycles
* created by flush dependencies.
*
* Return: void
*
* Programmer: Neil Fortner
* 12/10/12
*
*-------------------------------------------------------------------------
*/
static void
H5C__assert_flush_dep_nocycle(const H5C_cache_entry_t * entry,
const H5C_cache_entry_t * base_entry)
{
unsigned u; /* Local index variable */
FUNC_ENTER_STATIC_NOERR
/* Sanity checks */
HDassert(entry);
HDassert(base_entry);
/* Make sure the entries are not the same */
HDassert(base_entry != entry);
/* Iterate over entry's parents (if any) */
for(u = 0; u < entry->flush_dep_nparents; u++)
H5C__assert_flush_dep_nocycle(entry->flush_dep_parent[u], base_entry);
FUNC_LEAVE_NOAPI_VOID
} /* H5C__assert_flush_dep_nocycle() */
#endif /* NDEBUG */
/*-------------------------------------------------------------------------
* Function: H5C__serialize_cache
*
* Purpose: Serialize (i.e. construct an on disk image) for all entries
* in the metadata cache including clean entries.
*
* Note that flush dependencies and "flush me last" flags
* must be observed in the serialization process.
*
* Note also that entries may be loaded, flushed, evicted,
* expunged, relocated, resized, or removed from the cache
* during this process, just as these actions may occur during
* a regular flush.
*
* However, we are given that the cache will contain no protected
* entries on entry to this routine (although entries may be
* briefly protected and then unprotected during the serialize
* process).
*
* The objective of this routine is serialize all entries and
* to force all entries into their actual locations on disk.
*
* The initial need for this routine is to settle all entries
* in the cache prior to construction of the metadata cache
* image so that the size of the cache image can be calculated.
* However, I gather that other uses for the routine are
* under consideration.
*
* Return: Non-negative on success/Negative on failure or if there was
* a request to flush all items and something was protected.
*
* Programmer: John Mainzer
* 7/22/15
*
*-------------------------------------------------------------------------
*/
herr_t
H5C__serialize_cache(H5F_t *f)
{
#if H5C_DO_SANITY_CHECKS
int i;
uint32_t index_len = 0;
size_t index_size = (size_t)0;
size_t clean_index_size = (size_t)0;
size_t dirty_index_size = (size_t)0;
size_t slist_size = (size_t)0;
uint32_t slist_len = 0;
#endif /* H5C_DO_SANITY_CHECKS */
H5C_ring_t ring;
H5C_t * cache_ptr;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE
/* Sanity checks */
HDassert(f);
HDassert(f->shared);
cache_ptr = f->shared->cache;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(cache_ptr->slist_ptr);
#if H5C_DO_SANITY_CHECKS
HDassert(cache_ptr->index_ring_len[H5C_RING_UNDEFINED] == 0);
HDassert(cache_ptr->index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
HDassert(cache_ptr->clean_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
HDassert(cache_ptr->dirty_index_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
HDassert(cache_ptr->slist_ring_len[H5C_RING_UNDEFINED] == 0);
HDassert(cache_ptr->slist_ring_size[H5C_RING_UNDEFINED] == (size_t)0);
for(i = H5C_RING_USER; i < H5C_RING_NTYPES; i++) {
index_len += cache_ptr->index_ring_len[i];
index_size += cache_ptr->index_ring_size[i];
clean_index_size += cache_ptr->clean_index_ring_size[i];
dirty_index_size += cache_ptr->dirty_index_ring_size[i];
slist_len += cache_ptr->slist_ring_len[i];
slist_size += cache_ptr->slist_ring_size[i];
} /* end for */
HDassert(cache_ptr->index_len == index_len);
HDassert(cache_ptr->index_size == index_size);
HDassert(cache_ptr->clean_index_size == clean_index_size);
HDassert(cache_ptr->dirty_index_size == dirty_index_size);
HDassert(cache_ptr->slist_len == slist_len);
HDassert(cache_ptr->slist_size == slist_size);
#endif /* H5C_DO_SANITY_CHECKS */
#if H5C_DO_EXTREME_SANITY_CHECKS
if((H5C_validate_protected_entry_list(cache_ptr) < 0) ||
(H5C_validate_pinned_entry_list(cache_ptr) < 0) ||
(H5C_validate_lru_list(cache_ptr) < 0))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "an extreme sanity check failed on entry")
#endif /* H5C_DO_EXTREME_SANITY_CHECKS */
#ifndef NDEBUG
/* if this is a debug build, set the serialization_count field of
* each entry in the cache to zero before we start the serialization.
* This allows us to detect the case in which any entry is serialized
* more than once (a performance issues), and more importantly, the
* case is which any flush depencency parent is serializes more than
* once (a correctness issue).
*/
{
H5C_cache_entry_t * scan_ptr = NULL;
scan_ptr = cache_ptr->il_head;
while(scan_ptr != NULL) {
HDassert(scan_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
scan_ptr->serialization_count = 0;
scan_ptr = scan_ptr->il_next;
} /* end while */
} /* end block */
#endif /* NDEBUG */
/* set cache_ptr->serialization_in_progress to TRUE, and back
* to FALSE at the end of the function. Must maintain this flag
* to support H5C_get_serialization_in_progress(), which is in
* turn required to support sanity checking in some cache
* clients.
*/
HDassert(!cache_ptr->serialization_in_progress);
cache_ptr->serialization_in_progress = TRUE;
/* Serialize each ring, starting from the outermost ring and
* working inward.
*/
ring = H5C_RING_USER;
while(ring < H5C_RING_NTYPES) {
HDassert(cache_ptr->close_warning_received);
switch(ring) {
case H5C_RING_USER:
break;
case H5C_RING_RDFSM:
/* Settle raw data FSM */
if(!cache_ptr->rdfsm_settled)
if(H5MF_settle_raw_data_fsm(f, &cache_ptr->rdfsm_settled) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "RD FSM settle failed")
break;
case H5C_RING_MDFSM:
/* Settle metadata FSM */
if(!cache_ptr->mdfsm_settled)
if(H5MF_settle_meta_data_fsm(f, &cache_ptr->mdfsm_settled) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "MD FSM settle failed")
break;
case H5C_RING_SBE:
case H5C_RING_SB:
break;
default:
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "Unknown ring?!?!")
break;
} /* end switch */
if(H5C__serialize_ring(f, ring) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "serialize ring failed")
ring++;
} /* end while */
#ifndef NDEBUG
/* Verify that no entry has been serialized more than once.
* FD parents with multiple serializations should have been caught
* elsewhere, so no specific check for them here.
*/
{
H5C_cache_entry_t * scan_ptr = NULL;
scan_ptr = cache_ptr->il_head;
while(scan_ptr != NULL) {
HDassert(scan_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(scan_ptr->serialization_count <= 1);
scan_ptr = scan_ptr->il_next;
} /* end while */
} /* end block */
#endif /* NDEBUG */
done:
cache_ptr->serialization_in_progress = FALSE;
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__serialize_cache() */
/*-------------------------------------------------------------------------
* Function: H5C__serialize_ring
*
* Purpose: Serialize the entries contained in the specified cache and
* ring. All entries in rings outside the specified ring
* must have been serialized on entry.
*
* If the cache contains protected entries in the specified
* ring, the function will fail, as protected entries cannot
* be serialized. However all unprotected entries in the
* target ring should be serialized before the function
* returns failure.
*
* If flush dependencies appear in the target ring, the
* function makes repeated passes through the index list
* serializing entries in flush dependency order.
*
* All entries outside the H5C_RING_SBE are marked for
* inclusion in the cache image. Entries in H5C_RING_SBE
* and below are marked for exclusion from the image.
*
* Return: Non-negative on success/Negative on failure or if there was
* a request to flush all items and something was protected.
*
* Programmer: John Mainzer
* 9/11/15
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__serialize_ring(H5F_t *f, H5C_ring_t ring)
{
hbool_t done = FALSE;
H5C_t * cache_ptr;
H5C_cache_entry_t * entry_ptr;
herr_t ret_value = SUCCEED;
FUNC_ENTER_STATIC
/* Sanity checks */
HDassert(f);
HDassert(f->shared);
cache_ptr = f->shared->cache;
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(ring > H5C_RING_UNDEFINED);
HDassert(ring < H5C_RING_NTYPES);
HDassert(cache_ptr->serialization_in_progress);
/* The objective here is to serialize all entries in the cache ring
* in flush dependency order.
*
* The basic algorithm is to scan the cache index list looking for
* unserialized entries that are either not in a flush dependency
* relationship, or which have no unserialized children. Any such
* entry is serialized and its flush dependency parents (if any) are
* informed -- allowing them to decrement their userialized child counts.
*
* However, this algorithm is complicated by the ability
* of client serialization callbacks to perform operations on
* on the cache which can result in the insertion, deletion,
* relocation, resize, dirty, flush, eviction, or removal (via the
* take ownership flag) of entries. Changes in the flush dependency
* structure are also possible.
*
* On the other hand, the algorithm is simplified by the fact that
* we are serializing, not flushing. Thus, as long as all entries
* are serialized correctly, it doesn't matter if we have to go back
* and serialize an entry a second time.
*
* These possible actions result in the following modfications to
* tha basic algorithm:
*
* 1) In the event of an entry expunge, eviction or removal, we must
* restart the scan as it is possible that the next entry in our
* scan is no longer in the cache. Were we to examine this entry,
* we would be accessing deallocated memory.
*
* 2) A resize, dirty, or insertion of an entry may result in the
* the increment of a flush dependency parent's dirty and/or
* unserialized child count. In the context of serializing the
* the cache, this is a non-issue, as even if we have already
* serialized the parent, it will be marked dirty and its image
* marked out of date if appropriate when the child is serialized.
*
* However, this is a major issue for a flush, as were this to happen
* in a flush, it would violate the invariant that the flush dependency
* feature is intended to enforce. As the metadata cache has no
* control over the behavior of cache clients, it has no way of
* preventing this behaviour. However, it should detect it if at all
* possible.
*
* Do this by maintaining a count of the number of times each entry is
* serialized during a cache serialization. If any flush dependency
* parent is serialized more than once, throw an assertion failure.
*
* 3) An entry relocation will typically change the location of the
* entry in the index list. This shouldn't cause problems as we
* will scan the index list until we make a complete pass without
* finding anything to serialize -- making relocations of either
* the current or next entries irrelevant.
*
* Note that since a relocation may result in our skipping part of
* the index list, we must always do at least one more pass through
* the index list after an entry relocation.
*
* 4) Changes in the flush dependency structure are possible on
* entry insertion, load, expunge, evict, or remove. Destruction
* of a flush dependency has no effect, as it can only relax the
* flush dependencies. Creation of a flush dependency can create
* an unserialized child of a flush dependency parent where all
* flush dependency children were previously serialized. Should
* this child dirty the flush dependency parent when it is serialized,
* the parent will be re-serialized.
*
* Per the discussion of 2) above, this is a non issue for cache
* serialization, and a major problem for cache flush. Using the
* same detection mechanism, throw an assertion failure if this
* condition appears.
*
* Observe that either eviction or removal of entries as a result of
* a serialization is not a problem as long as the flush depencency
* tree does not change beyond the removal of a leaf.
*/
while(!done) {
/* Reset the counters so that we can detect insertions, loads,
* moves, and flush dependency height changes caused by the pre_serialize
* and serialize callbacks.
*/
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
done = TRUE; /* set to FALSE if any activity in inner loop */
entry_ptr = cache_ptr->il_head;
while(entry_ptr != NULL) {
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
/* Verify that either the entry is already serialized, or
* that it is assigned to either the target or an inner
* ring.
*/
HDassert((entry_ptr->ring >= ring) || (entry_ptr->image_up_to_date));
/* Skip flush me last entries or inner ring entries */
if(!entry_ptr->flush_me_last && entry_ptr->ring == ring) {
/* if we encounter an unserialized entry in the current
* ring that is not marked flush me last, we are not done.
*/
if(!entry_ptr->image_up_to_date)
done = FALSE;
/* Serialize the entry if its image is not up to date
* and it has no unserialized flush dependency children.
*/
if(!entry_ptr->image_up_to_date && entry_ptr->flush_dep_nunser_children == 0) {
HDassert(entry_ptr->serialization_count == 0);
/* Serialize the entry */
if(H5C__serialize_single_entry(f, cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "entry serialization failed")
HDassert(entry_ptr->flush_dep_nunser_children == 0);
HDassert(entry_ptr->serialization_count == 0);
#ifndef NDEBUG
/* Increment serialization counter (to detect multiple serializations) */
entry_ptr->serialization_count++;
#endif /* NDEBUG */
} /* end if */
} /* end if */
/* Check for the cache being perturbed during the entry serialize */
if((cache_ptr->entries_loaded_counter > 0) ||
(cache_ptr->entries_inserted_counter > 0) ||
(cache_ptr->entries_relocated_counter > 0)) {
#if H5C_COLLECT_CACHE_STATS
H5C__UPDATE_STATS_FOR_INDEX_SCAN_RESTART(cache_ptr);
#endif /* H5C_COLLECT_CACHE_STATS */
/* Reset the counters */
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
/* Restart scan */
entry_ptr = cache_ptr->il_head;
} /* end if */
else
/* Advance to next entry */
entry_ptr = entry_ptr->il_next;
} /* while ( entry_ptr != NULL ) */
} /* while ( ! done ) */
/* Reset the counters so that we can detect insertions, loads,
* moves, and flush dependency height changes caused by the pre_serialize
* and serialize callbacks.
*/
cache_ptr->entries_loaded_counter = 0;
cache_ptr->entries_inserted_counter = 0;
cache_ptr->entries_relocated_counter = 0;
/* At this point, all entries not marked "flush me last" and in
* the current ring or outside it should be serialized and have up
* to date images. Scan the index list again to serialize the
* "flush me last" entries (if they are in the current ring) and to
* verify that all other entries have up to date images.
*/
entry_ptr = cache_ptr->il_head;
while(entry_ptr != NULL) {
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(entry_ptr->ring > H5C_RING_UNDEFINED);
HDassert(entry_ptr->ring < H5C_RING_NTYPES);
HDassert((entry_ptr->ring >= ring) || (entry_ptr->image_up_to_date));
if(entry_ptr->ring == ring) {
if(entry_ptr->flush_me_last) {
if(!entry_ptr->image_up_to_date) {
HDassert(entry_ptr->serialization_count == 0);
HDassert(entry_ptr->flush_dep_nunser_children == 0);
/* Serialize the entry */
if(H5C__serialize_single_entry(f, cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "entry serialization failed")
/* Check for the cache changing */
if((cache_ptr->entries_loaded_counter > 0) ||
(cache_ptr->entries_inserted_counter > 0) ||
(cache_ptr->entries_relocated_counter > 0))
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "flush_me_last entry serialization triggered restart")
HDassert(entry_ptr->flush_dep_nunser_children == 0);
HDassert(entry_ptr->serialization_count == 0);
#ifndef NDEBUG
/* Increment serialization counter (to detect multiple serializations) */
entry_ptr->serialization_count++;
#endif /* NDEBUG */
} /* end if */
} /* end if */
else {
HDassert(entry_ptr->image_up_to_date);
HDassert(entry_ptr->serialization_count <= 1);
HDassert(entry_ptr->flush_dep_nunser_children == 0);
} /* end else */
} /* if ( entry_ptr->ring == ring ) */
entry_ptr = entry_ptr->il_next;
} /* while ( entry_ptr != NULL ) */
done:
HDassert(cache_ptr->serialization_in_progress);
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__serialize_ring() */
/*-------------------------------------------------------------------------
* Function: H5C__serialize_single_entry
*
* Purpose: Serialize the cache entry pointed to by the entry_ptr
* parameter.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: John Mainzer, 7/24/15
*
*-------------------------------------------------------------------------
*/
static herr_t
H5C__serialize_single_entry(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr)
{
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_STATIC
/* Sanity checks */
HDassert(f);
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(entry_ptr);
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(!entry_ptr->prefetched);
HDassert(!entry_ptr->image_up_to_date);
HDassert(entry_ptr->is_dirty);
HDassert(!entry_ptr->is_protected);
HDassert(!entry_ptr->flush_in_progress);
HDassert(entry_ptr->type);
/* Set entry_ptr->flush_in_progress to TRUE so the the target entry
* will not be evicted out from under us. Must set it back to FALSE
* when we are done.
*/
entry_ptr->flush_in_progress = TRUE;
/* Allocate buffer for the entry image if required. */
if(NULL == entry_ptr->image_ptr) {
HDassert(entry_ptr->size > 0);
if(NULL == (entry_ptr->image_ptr = H5MM_malloc(entry_ptr->size + H5C_IMAGE_EXTRA_SPACE)) )
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer")
#if H5C_DO_MEMORY_SANITY_CHECKS
H5MM_memcpy(((uint8_t *)entry_ptr->image_ptr) + image_size, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
} /* end if */
/* Generate image for entry */
if(H5C__generate_image(f, cache_ptr, entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTSERIALIZE, FAIL, "Can't generate image for cache entry")
/* Reset the flush_in progress flag */
entry_ptr->flush_in_progress = FALSE;
done:
HDassert((ret_value != SUCCEED) || (!entry_ptr->flush_in_progress));
HDassert((ret_value != SUCCEED) || (entry_ptr->image_up_to_date));
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__serialize_single_entry() */
/*-------------------------------------------------------------------------
* Function: H5C__generate_image
*
* Purpose: Serialize an entry and generate its image.
*
* Note: This may cause the entry to be re-sized and/or moved in
* the cache.
*
* As we will not update the metadata cache's data structures
* until we we finish the write, we must touch up these
* data structures for size and location changes even if we
* are about to delete the entry from the cache (i.e. on a
* flush destroy).
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Mohamad Chaarawi
* 2/10/16
*
*-------------------------------------------------------------------------
*/
herr_t
H5C__generate_image(H5F_t *f, H5C_t *cache_ptr, H5C_cache_entry_t *entry_ptr)
{
haddr_t new_addr = HADDR_UNDEF;
haddr_t old_addr = HADDR_UNDEF;
size_t new_len = 0;
unsigned serialize_flags = H5C__SERIALIZE_NO_FLAGS_SET;
herr_t ret_value = SUCCEED;
FUNC_ENTER_PACKAGE
/* Sanity check */
HDassert(f);
HDassert(cache_ptr);
HDassert(cache_ptr->magic == H5C__H5C_T_MAGIC);
HDassert(entry_ptr);
HDassert(entry_ptr->magic == H5C__H5C_CACHE_ENTRY_T_MAGIC);
HDassert(!entry_ptr->image_up_to_date);
HDassert(entry_ptr->is_dirty);
HDassert(!entry_ptr->is_protected);
HDassert(entry_ptr->type);
/* make note of the entry's current address */
old_addr = entry_ptr->addr;
/* Call client's pre-serialize callback, if there's one */
if(entry_ptr->type->pre_serialize &&
(entry_ptr->type->pre_serialize)(f, (void *)entry_ptr,
entry_ptr->addr, entry_ptr->size, &new_addr, &new_len, &serialize_flags) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to pre-serialize entry")
/* Check for any flags set in the pre-serialize callback */
if(serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET) {
/* Check for unexpected flags from serialize callback */
if(serialize_flags & ~(H5C__SERIALIZE_RESIZED_FLAG | H5C__SERIALIZE_MOVED_FLAG))
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unknown serialize flag(s)")
#ifdef H5_HAVE_PARALLEL
/* In the parallel case, resizes and moves in
* the serialize operation can cause problems.
* If they occur, scream and die.
*
* At present, in the parallel case, the aux_ptr
* will only be set if there is more than one
* process. Thus we can use this to detect
* the parallel case.
*
* This works for now, but if we start using the
* aux_ptr for other purposes, we will have to
* change this test accordingly.
*
* NB: While this test detects entryies that attempt
* to resize or move themselves during a flush
* in the parallel case, it will not detect an
* entry that dirties, resizes, and/or moves
* other entries during its flush.
*
* From what Quincey tells me, this test is
* sufficient for now, as any flush routine that
* does the latter will also do the former.
*
* If that ceases to be the case, further
* tests will be necessary.
*/
if(cache_ptr->aux_ptr != NULL)
HGOTO_ERROR(H5E_CACHE, H5E_SYSTEM, FAIL, "resize/move in serialize occurred in parallel case")
#endif
/* If required, resize the buffer and update the entry and the cache
* data structures */
if(serialize_flags & H5C__SERIALIZE_RESIZED_FLAG) {
/* Sanity check */
HDassert(new_len > 0);
/* Allocate a new image buffer */
if(NULL == (entry_ptr->image_ptr = H5MM_realloc(entry_ptr->image_ptr, new_len + H5C_IMAGE_EXTRA_SPACE)))
HGOTO_ERROR(H5E_CACHE, H5E_CANTALLOC, FAIL, "memory allocation failed for on disk image buffer")
#if H5C_DO_MEMORY_SANITY_CHECKS
H5MM_memcpy(((uint8_t *)entry_ptr->image_ptr) + new_len, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE);
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
/* Update statistics for resizing the entry */
H5C__UPDATE_STATS_FOR_ENTRY_SIZE_CHANGE(cache_ptr, entry_ptr, new_len);
/* Update the hash table for the size change */
H5C__UPDATE_INDEX_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len, entry_ptr, !(entry_ptr->is_dirty));
/* The entry can't be protected since we are in the process of
* flushing it. Thus we must update the replacement policy data
* structures for the size change. The macro deals with the pinned
* case.
*/
H5C__UPDATE_RP_FOR_SIZE_CHANGE(cache_ptr, entry_ptr, new_len);
/* As we haven't updated the cache data structures for
* for the flush or flush destroy yet, the entry should
* be in the slist. Thus update it for the size change.
*/
HDassert(entry_ptr->is_dirty);
HDassert(entry_ptr->in_slist);
H5C__UPDATE_SLIST_FOR_SIZE_CHANGE(cache_ptr, entry_ptr->size, new_len);
/* Finally, update the entry for its new size */
entry_ptr->size = new_len;
} /* end if */
/* If required, udate the entry and the cache data structures
* for a move
*/
if(serialize_flags & H5C__SERIALIZE_MOVED_FLAG) {
/* Update stats and entries relocated counter */
H5C__UPDATE_STATS_FOR_MOVE(cache_ptr, entry_ptr)
/* We must update cache data structures for the change in address */
if(entry_ptr->addr == old_addr) {
/* Delete the entry from the hash table and the slist */
H5C__DELETE_FROM_INDEX(cache_ptr, entry_ptr, FAIL);
H5C__REMOVE_ENTRY_FROM_SLIST(cache_ptr, entry_ptr, FALSE);
/* Update the entry for its new address */
entry_ptr->addr = new_addr;
/* And then reinsert in the index and slist */
H5C__INSERT_IN_INDEX(cache_ptr, entry_ptr, FAIL);
H5C__INSERT_ENTRY_IN_SLIST(cache_ptr, entry_ptr, FAIL);
} /* end if */
else /* move is already done for us -- just do sanity checks */
HDassert(entry_ptr->addr == new_addr);
} /* end if */
} /* end if(serialize_flags != H5C__SERIALIZE_NO_FLAGS_SET) */
/* Serialize object into buffer */
if(entry_ptr->type->serialize(f, entry_ptr->image_ptr, entry_ptr->size, (void *)entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTFLUSH, FAIL, "unable to serialize entry")
#if H5C_DO_MEMORY_SANITY_CHECKS
HDassert(0 == HDmemcmp(((uint8_t *)entry_ptr->image_ptr) + entry_ptr->size, H5C_IMAGE_SANITY_VALUE, H5C_IMAGE_EXTRA_SPACE));
#endif /* H5C_DO_MEMORY_SANITY_CHECKS */
entry_ptr->image_up_to_date = TRUE;
/* Propagate the fact that the entry is serialized up the
* flush dependency chain if appropriate. Since the image must
* have been out of date for this function to have been called
* (see assertion on entry), no need to check that -- only check
* for flush dependency parents.
*/
HDassert(entry_ptr->flush_dep_nunser_children == 0);
if(entry_ptr->flush_dep_nparents > 0)
if(H5C__mark_flush_dep_serialized(entry_ptr) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "Can't propagate serialization status to fd parents")
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__generate_image */
/*-------------------------------------------------------------------------
*
* Function: H5C_remove_entry
*
* Purpose: Remove an entry from the cache. Must be not protected, pinned,
* dirty, involved in flush dependencies, etc.
*
* Return: Non-negative on success/Negative on failure
*
* Programmer: Quincey Koziol
* September 17, 2016
*
*-------------------------------------------------------------------------
*/
herr_t
H5C_remove_entry(void *_entry)
{
H5C_cache_entry_t *entry = (H5C_cache_entry_t *)_entry; /* Entry to remove */
H5C_t *cache; /* Cache for file */
herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(FAIL)
/* Sanity checks */
HDassert(entry);
HDassert(entry->ring != H5C_RING_UNDEFINED);
cache = entry->cache_ptr;
HDassert(cache);
HDassert(cache->magic == H5C__H5C_T_MAGIC);
/* Check for error conditions */
if(entry->is_dirty)
HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove dirty entry from cache")
if(entry->is_protected)
HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove protected entry from cache")
if(entry->is_pinned)
HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove pinned entry from cache")
/* NOTE: If these two errors are getting tripped because the entry is
* in a flush dependency with a freedspace entry, move the checks
* after the "before evict" message is sent, and add the
* "child being evicted" message to the "before evict" notify
* section below. QAK - 2017/08/03
*/
if(entry->flush_dep_nparents > 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry with flush dependency parents from cache")
if(entry->flush_dep_nchildren > 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry with flush dependency children from cache")
/* Additional internal cache consistency checks */
HDassert(!entry->in_slist);
HDassert(!entry->flush_marker);
HDassert(!entry->flush_in_progress);
/* Note that the algorithm below is (very) similar to the set of operations
* in H5C__flush_single_entry() and should be kept in sync with changes
* to that code. - QAK, 2016/11/30
*/
/* Update stats, as if we are "destroying" and taking ownership of the entry */
H5C__UPDATE_STATS_FOR_EVICTION(cache, entry, TRUE)
/* If the entry's type has a 'notify' callback, send a 'before eviction'
* notice while the entry is still fully integrated in the cache.
*/
if(entry->type->notify && (entry->type->notify)(H5C_NOTIFY_ACTION_BEFORE_EVICT, entry) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTNOTIFY, FAIL, "can't notify client about entry to evict")
/* Update the cache internal data structures as appropriate for a destroy.
* Specifically:
* 1) Delete it from the index
* 2) Delete it from the collective read access list
* 3) Update the replacement policy for eviction
* 4) Remove it from the tag list for this object
*/
H5C__DELETE_FROM_INDEX(cache, entry, FAIL)
#ifdef H5_HAVE_PARALLEL
/* Check for collective read access flag */
if(entry->coll_access) {
entry->coll_access = FALSE;
H5C__REMOVE_FROM_COLL_LIST(cache, entry, FAIL)
} /* end if */
#endif /* H5_HAVE_PARALLEL */
H5C__UPDATE_RP_FOR_EVICTION(cache, entry, FAIL)
/* Remove entry from tag list */
if(H5C__untag_entry(cache, entry) < 0)
HGOTO_ERROR(H5E_CACHE, H5E_CANTREMOVE, FAIL, "can't remove entry from tag list")
/* Increment entries_removed_counter and set last_entry_removed_ptr.
* As we me be about to free the entry, recall that last_entry_removed_ptr
* must NEVER be dereferenced.
*
* Recall that these fields are maintained to allow functions that perform
* scans of lists of entries to detect the unexpected removal of entries
* (via expunge, eviction, or take ownership at present), so that they can
* re-start their scans if necessary.
*
* Also check if the entry we are watching for removal is being
* removed (usually the 'next' entry for an iteration) and reset
* it to indicate that it was removed.
*/
cache->entries_removed_counter++;
cache->last_entry_removed_ptr = entry;
if(entry == cache->entry_watched_for_removal)
cache->entry_watched_for_removal = NULL;
/* Internal cache data structures should now be up to date, and
* consistent with the status of the entry.
*
* Now clean up internal cache fields if appropriate.
*/
/* Free the buffer for the on disk image */
if(entry->image_ptr != NULL)
entry->image_ptr = H5MM_xfree(entry->image_ptr);
/* Reset the pointer to the cache the entry is within */
entry->cache_ptr = NULL;
/* Client is taking ownership of the entry. Set bad magic here so the
* cache will choke unless the entry is re-inserted properly
*/
entry->magic = H5C__H5C_CACHE_ENTRY_T_BAD_MAGIC;
done:
FUNC_LEAVE_NOAPI(ret_value)
} /* H5C__remove_entry() */